2012-12-31 60 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个字节的精确倍数。