2012-12-31 59 views
0

這是我第四次嘗試做base64編碼。我的第一個嘗試工作,但它不是標準的。它也非常慢!我使用矢量和push_back並擦除了很多。Base 64編碼丟失數據

所以我決定重新寫它,這是非常非常快!除了它丟失數據。 -__- 我需要儘可能多的速度,因爲我正在壓縮像素緩衝區和base64編碼壓縮的字符串。我正在使用ZLib。圖像是1366 x 768所以是的。

我不想因爲......嗯,我喜歡寫東西自己網上覆制任何代碼,我發現,我不喜歡擔心版權的東西,或有遍佈把一噸來自不同來源的學分我的代碼..

不管怎樣,我的代碼如下所示。它非常簡短。

const static std::string Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz/"; 

inline bool IsBase64(std::uint8_t C) 
{ 
    return (isalnum(C) || (C == '+') || (C == '/')); 
} 

std::string Copy(std::string Str, int FirstChar, int Count) 
{ 
    if (FirstChar <= 0) 
     FirstChar = 0; 
    else 
     FirstChar -= 1; 
    return Str.substr(FirstChar, Count); 
} 

std::string DecToBinStr(int Num, int Padding) 
{ 
    int Bin = 0, Pos = 1; 
    std::stringstream SS; 
    while (Num > 0) 
    { 
     Bin += (Num % 2) * Pos; 
     Num /= 2; 
     Pos *= 10; 
    } 
    SS.fill('0'); 
    SS.width(Padding); 
    SS << Bin; 
    return SS.str(); 
} 

int DecToBinStr(std::string DecNumber) 
{ 
    int Bin = 0, Pos = 1; 
    int Dec = strtol(DecNumber.c_str(), NULL, 10); 

    while (Dec > 0) 
    { 
     Bin += (Dec % 2) * Pos; 
     Dec /= 2; 
     Pos *= 10; 
    } 
    return Bin; 
} 

int BinToDecStr(std::string BinNumber) 
{ 
    int Dec = 0; 
    int Bin = strtol(BinNumber.c_str(), NULL, 10); 

    for (int I = 0; Bin > 0; ++I) 
    { 
     if(Bin % 10 == 1) 
     { 
      Dec += (1 << I); 
     } 
     Bin /= 10; 
    } 
    return Dec; 
} 

std::string EncodeBase64(std::string Data) 
{ 
    std::string Binary = std::string(); 
    std::string Result = std::string(); 

    for (std::size_t I = 0; I < Data.size(); ++I) 
    { 
     Binary += DecToBinStr(Data[I], 8); 
    } 

    for (std::size_t I = 0; I < Binary.size(); I += 6) 
    { 
     Result += Base64Chars[BinToDecStr(Copy(Binary, I, 6))]; 
     if (I == 0) ++I; 
    } 

    int PaddingAmount = ((-Result.size() * 3) & 3); 
    for (int I = 0; I < PaddingAmount; ++I) 
     Result += '='; 

    return Result; 
} 

std::string DecodeBase64(std::string Data) 
{ 
    std::string Binary = std::string(); 
    std::string Result = std::string(); 

    for (std::size_t I = Data.size(); I > 0; --I) 
    { 
     if (Data[I - 1] != '=') 
     { 
      std::string Characters = Copy(Data, 0, I); 
      for (std::size_t J = 0; J < Characters.size(); ++J) 
       Binary += DecToBinStr(Base64Chars.find(Characters[J]), 6); 
      break; 
     } 
    } 

    for (std::size_t I = 0; I < Binary.size(); I += 8) 
    { 
     Result += (char)BinToDecStr(Copy(Binary, I, 8)); 
     if (I == 0) ++I; 
    } 

    return Result; 
} 

我一直在使用上述這樣的:

int main() 
{ 
    std::string Data = EncodeBase64("IMG." + ::ToString(677) + "*" + ::ToString(604)); //IMG.677*604 
    std::cout<<DecodeBase64(Data);  //Prints IMG.677*601 
} 

正如你可以在上面看到,它打印了錯誤的字符串。它相當接近,但由於某種原因,4變成了1!

現在,如果我做的:

int main() 
{ 
    std::string Data = EncodeBase64("IMG." + ::ToString(1366) + "*" + ::ToString(768)); //IMG.1366*768 
    std::cout<<DecodeBase64(Data);  //Prints IMG.1366*768 
} 

打印正確..我不知道是什麼原因在所有事情或者從哪裏開始尋找。

只是套內任何人是好奇,想看看我的其他嘗試(慢的):http://pastebin.com/Xcv03KwE

我真的希望有人能擺脫超速一些事情的補光燈,或者至少搞清楚什麼是錯的我的代碼:L

回答

2

主要編碼的問題是,你是不是佔數據不是6位的倍數。在這種情況下,最終4你已經被轉換成0100而不是010000因爲沒有更多的位閱讀。你應該填寫0 s。

將這樣的Copy更改後,最終的編碼字符爲Q,而不是原來的E

std::string data = Str.substr(FirstChar, Count); 
while(data.size() < Count) data += '0'; 
return data; 

而且,看來你對添加填充=邏輯是關閉的,因爲它是在這種情況下添加一個太多=

就速度方面的評論而言,我主要關注的是如何減少std::string的使用量。考慮到可以直接用位運算符來讀取數據源,您當前將數據轉換爲0和1的字符串的方式相當不便。

1
std::string EncodeBase64(std::string Data) 
{ 
    std::string Binary = std::string(); 
    std::string Result = std::string(); 

    for (std::size_t I = 0; I < Data.size(); ++I) 
    { 
     Binary += DecToBinStr(Data[I], 8); 
    } 

    if (Binary.size() % 6) 
    { 
     Binary.resize(Binary.size() + 6 - Binary.size() % 6, '0'); 
    } 

    for (std::size_t I = 0; I < Binary.size(); I += 6) 
    { 
     Result += Base64Chars[BinToDecStr(Copy(Binary, I, 6))]; 
     if (I == 0) ++I; 
    } 

    if (Result.size() % 4) 
    { 
     Result.resize(Result.size() + 4 - Result.size() % 4, '='); 
    } 

    return Result; 
} 
1

我不知道我是否能輕易拿出這樣做的Base-64轉換較慢方法。

代碼要求4周的頭文件(在Mac OS X 10.7.5與G ++ 4.7。1)和編譯器選項-std=c++11使#include <cstdint>可接受:

#include <string> 
#include <iostream> 
#include <sstream> 
#include <cstdint> 

它還要求沒有定義一個函數ToString();我創建:

std::string ToString(int value) 
{ 
    std::stringstream ss; 
    ss << value; 
    return ss.str(); 
} 

的代碼在你的main() - 這就是使用ToString()功能 - 有點奇怪:爲什麼你需要建立從片,而不是簡單地使用"IMG.677*604"一個字符串?

此外,值得一打印出中間結果:

這產生了:

SU1HLjY3Nyo2MDE=== 
IMG.677*601 

輸出字符串(SU1HLjY3Nyo2MDE===)是18個字節長;這必須是錯誤的,因爲有效的Base-64編碼的字符串必須是4個字節長的倍數(因爲三個8位字節被編碼成每個包含6位原始數據的四個字節)。這立即告訴我們有問題。您只能得到零個,一個或兩個墊(=)字符;從未三。這也證實存在問題。

刪除兩個填充字符會留下有效的Base-64字符串。當我用我自己的家釀造的Base-64編碼和解碼功能,以您的(截)解碼輸出,它給了我:

Base64: 
0x0000: SU1HLjY3Nyo2MDE= 
Binary: 
0x0000: 49 4D 47 2E 36 37 37 2A 36 30 31 00    IMG.677*601. 

如此看來你有編碼空終止字符串。當我編碼IMG.677*604,輸出我得到的是:

Binary: 
0x0000: 49 4D 47 2E 36 37 37 2A 36 30 34     IMG.677*604 
Base64: SU1HLjY3Nyo2MDQ= 

你說你想加快你的代碼。除了修復它以便它正確編碼之外(我沒有真正研究過解碼),您將希望避免您所做的所有字符串操作。這應該是一個操縱練習,而不是一個字符串操作練習。

我在我的代碼3點小編碼的例程,以編碼三聯體,雙重和單峯:

/* Encode 3 bytes of data into 4 */ 
static void encode_triplet(const char *triplet, char *quad) 
{ 
    quad[0] = base_64_map[(triplet[0] >> 2) & 0x3F]; 
    quad[1] = base_64_map[((triplet[0] & 0x03) << 4) | ((triplet[1] >> 4) & 0x0F)]; 
    quad[2] = base_64_map[((triplet[1] & 0x0F) << 2) | ((triplet[2] >> 6) & 0x03)]; 
    quad[3] = base_64_map[triplet[2] & 0x3F]; 
} 

/* Encode 2 bytes of data into 4 */ 
static void encode_doublet(const char *doublet, char *quad, char pad) 
{ 
    quad[0] = base_64_map[(doublet[0] >> 2) & 0x3F]; 
    quad[1] = base_64_map[((doublet[0] & 0x03) << 4) | ((doublet[1] >> 4) & 0x0F)]; 
    quad[2] = base_64_map[((doublet[1] & 0x0F) << 2)]; 
    quad[3] = pad; 
} 

/* Encode 1 byte of data into 4 */ 
static void encode_singlet(const char *singlet, char *quad, char pad) 
{ 
    quad[0] = base_64_map[(singlet[0] >> 2) & 0x3F]; 
    quad[1] = base_64_map[((singlet[0] & 0x03) << 4)]; 
    quad[2] = pad; 
    quad[3] = pad; 
} 

這被寫爲C代碼,而不是使用本地C++成語,但示出應該使用C編譯代碼++ (與來源中其他地方的C99初始化程序不同)。 base_64_map[]數組對應於您的Base64Chars字符串。傳入的pad字符通常是'=',但可以是'\0',因爲我所使用的系統具有關於不需要填充(預約我參與代碼,並使用非標準字母來引導)和代碼處理非標準和RFC 3548標準。

的驅動代碼是:

/* Encode input data as Base-64 string. Output length returned, or negative error */ 
static int base64_encode_internal(const char *data, size_t datalen, char *buffer, size_t buflen, char pad) 
{ 
    size_t outlen = BASE64_ENCLENGTH(datalen); 
    const char *bin_data = (const void *)data; 
    char *b64_data = (void *)buffer; 

    if (outlen > buflen) 
     return(B64_ERR_OUTPUT_BUFFER_TOO_SMALL); 
    while (datalen >= 3) 
    { 
     encode_triplet(bin_data, b64_data); 
     bin_data += 3; 
     b64_data += 4; 
     datalen -= 3; 
    } 
    b64_data[0] = '\0'; 

    if (datalen == 2) 
     encode_doublet(bin_data, b64_data, pad); 
    else if (datalen == 1) 
     encode_singlet(bin_data, b64_data, pad); 
    b64_data[4] = '\0'; 
    return((b64_data - buffer) + strlen(b64_data)); 
} 

/* Encode input data as Base-64 string. Output length returned, or negative error */ 
int base64_encode(const char *data, size_t datalen, char *buffer, size_t buflen) 
{ 
    return(base64_encode_internal(data, datalen, buffer, buflen, base64_pad)); 
} 

base64_pad常數是'=';還有一個base64_encode_nopad()函數,它提供了'\0'。這些錯誤有些隨意,但與代碼相關。

要從這個角度取消的主要觀點是,您應該進行位操作併爲給定的輸入構建一個字符串,它是4個字節的精確倍數。