在C++中有免费的Base64解码代码片段可用吗?
在C++中有免费的Base64解码代码片段可用吗?
我首先制作了自己的版本,然后发现了这个主题。
为什么我的版本看起来比这里呈现的其他版本简单?我做错了什么吗?我没有测试它的速度。
inline char const* b64units = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
inline char* b64encode(void const* a, int64_t b) {
ASSERT(a != nullptr);
if (b > 0) {
uint8_t const* aa = static_cast<uint8_t const*>(a);
uint8_t v = 0;
int64_t bp = 0;
int64_t sb = 0;
int8_t off = 0;
int64_t nt = ((b + 2) / 3) * 4;
int64_t nd = (b * 8) / 6;
int64_t tl = ((b * 8) % 6) ? 1 : 0;
int64_t nf = nt - nd - tl;
int64_t ri = 0;
char* r = new char[nt + 1]();
for (int64_t i = 0; i < nd; i++) {
v = (aa[sb] << off) | (aa[sb + 1] >> (8 - off));
v >>= 2;
r[ri] = b64units[v];
ri += 1;
bp += 6;
sb = (bp / 8);
off = (bp % 8);
}
if (tl > 0) {
v = (aa[sb] << off);
v >>= 2;
r[ri] = b64units[v];
ri += 1;
}
for (int64_t i = 0; i < nf; i++) {
r[ri] = '=';
ri += 1;
}
return r;
} else return nullptr;
}
P.S.:我的方法效果很好。我用Node.js进行了测试:
let data = 'stackabuse.com';
let buff = new Buffer(data);
let base64data = buff.toString('base64');
这是我写的一个程序,它使用联合和位域以实现最大效率和可读性。
const char PADDING_CHAR = '=';
const char* ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const uint8_t DECODED_ALPHBET[128]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,0,0,0,0,0};
/**
* Given a string, this function will encode it in 64b (with padding)
*/
std::string encodeBase64(const std::string& binaryText)
{
std::string encoded((binaryText.size()/3 + (binaryText.size()%3 > 0)) << 2, PADDING_CHAR);
const char* bytes = binaryText.data();
union
{
uint32_t temp = 0;
struct
{
uint32_t first : 6, second : 6, third : 6, fourth : 6;
} tempBytes;
};
std::string::iterator currEncoding = encoded.begin();
for(uint32_t i = 0, lim = binaryText.size() / 3; i < lim; ++i, bytes+=3)
{
temp = bytes[0] << 16 | bytes[1] << 8 | bytes[2];
(*currEncoding++) = ALPHABET[tempBytes.fourth];
(*currEncoding++) = ALPHABET[tempBytes.third];
(*currEncoding++) = ALPHABET[tempBytes.second];
(*currEncoding++) = ALPHABET[tempBytes.first];
}
switch(binaryText.size() % 3)
{
case 1:
temp = bytes[0] << 16;
(*currEncoding++) = ALPHABET[tempBytes.fourth];
(*currEncoding++) = ALPHABET[tempBytes.third];
break;
case 2:
temp = bytes[0] << 16 | bytes[1] << 8;
(*currEncoding++) = ALPHABET[tempBytes.fourth];
(*currEncoding++) = ALPHABET[tempBytes.third];
(*currEncoding++) = ALPHABET[tempBytes.second];
break;
}
return encoded;
}
/**
* Given a 64b padding-encoded string, this function will decode it.
*/
std::string decodeBase64(const std::string& base64Text)
{
if( base64Text.empty() )
return "";
assert((base64Text.size()&3) == 0 && "The base64 text to be decoded must have a length devisible by 4!");
uint32_t numPadding = (*std::prev(base64Text.end(),1) == PADDING_CHAR) + (*std::prev(base64Text.end(),2) == PADDING_CHAR);
std::string decoded((base64Text.size()*3>>2) - numPadding, '.');
union
{
uint32_t temp;
char tempBytes[4];
};
const uint8_t* bytes = reinterpret_cast<const uint8_t*>(base64Text.data());
std::string::iterator currDecoding = decoded.begin();
for(uint32_t i = 0, lim = (base64Text.size() >> 2) - (numPadding!=0); i < lim; ++i, bytes+=4)
{
temp = DECODED_ALPHBET[bytes[0]] << 18 | DECODED_ALPHBET[bytes[1]] << 12 | DECODED_ALPHBET[bytes[2]] << 6 | DECODED_ALPHBET[bytes[3]];
(*currDecoding++) = tempBytes[2];
(*currDecoding++) = tempBytes[1];
(*currDecoding++) = tempBytes[0];
}
switch (numPadding)
{
case 2:
temp = DECODED_ALPHBET[bytes[0]] << 18 | DECODED_ALPHBET[bytes[1]] << 12;
(*currDecoding++) = tempBytes[2];
break;
case 1:
temp = DECODED_ALPHBET[bytes[0]] << 18 | DECODED_ALPHBET[bytes[1]] << 12 | DECODED_ALPHBET[bytes[2]] << 6;
(*currDecoding++) = tempBytes[2];
(*currDecoding++) = tempBytes[1];
break;
}
return decoded;
}
我喜欢GitHub上的这个解决方案。
它是一个单一的hpp文件,与被接受的答案不同,它使用vector<byte>类型来处理原始数据。
#pragma once
#include <string>
#include <vector>
#include <stdexcept>
#include <cstdint>
namespace base64
{
inline static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
inline static const char kPadCharacter = '=';
using byte = std::uint8_t;
inline std::string encode(const std::vector<byte>& input)
{
std::string encoded;
encoded.reserve(((input.size() / 3) + (input.size() % 3 > 0)) * 4);
std::uint32_t temp{};
auto it = input.begin();
for(std::size_t i = 0; i < input.size() / 3; ++i)
{
temp = (*it++) << 16;
temp += (*it++) << 8;
temp += (*it++);
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
encoded.append(1, kEncodeLookup[(temp & 0x0000003F) ]);
}
switch(input.size() % 3)
{
case 1:
temp = (*it++) << 16;
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(2, kPadCharacter);
break;
case 2:
temp = (*it++) << 16;
temp += (*it++) << 8;
encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]);
encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]);
encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6 ]);
encoded.append(1, kPadCharacter);
break;
}
return encoded;
}
std::vector<byte> decode(const std::string& input)
{
if(input.length() % 4)
throw std::runtime_error("Invalid base64 length!");
std::size_t padding{};
if(input.length())
{
if(input[input.length() - 1] == kPadCharacter) padding++;
if(input[input.length() - 2] == kPadCharacter) padding++;
}
std::vector<byte> decoded;
decoded.reserve(((input.length() / 4) * 3) - padding);
std::uint32_t temp{};
auto it = input.begin();
while(it < input.end())
{
for(std::size_t i = 0; i < 4; ++i)
{
temp <<= 6;
if (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41;
else if(*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47;
else if(*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04;
else if(*it == 0x2B) temp |= 0x3E;
else if(*it == 0x2F) temp |= 0x3F;
else if(*it == kPadCharacter)
{
switch(input.end() - it)
{
case 1:
decoded.push_back((temp >> 16) & 0x000000FF);
decoded.push_back((temp >> 8 ) & 0x000000FF);
return decoded;
case 2:
decoded.push_back((temp >> 10) & 0x000000FF);
return decoded;
default:
throw std::runtime_error("Invalid padding in base64!");
}
}
else throw std::runtime_error("Invalid character in base64!");
++it;
}
decoded.push_back((temp >> 16) & 0x000000FF);
decoded.push_back((temp >> 8 ) & 0x000000FF);
decoded.push_back((temp ) & 0x000000FF);
}
return decoded;
}
}