|
|
|
@ -1,5 +1,7 @@
|
|
|
|
|
#include "stringconversion.h" |
|
|
|
|
|
|
|
|
|
#include "../misc/memory.h" |
|
|
|
|
|
|
|
|
|
#include <sstream> |
|
|
|
|
#include <iomanip> |
|
|
|
|
|
|
|
|
@ -85,101 +87,103 @@ string bitrateToString(double bitrateInKbitsPerSecond, bool useIecBinaryPrefixes
|
|
|
|
|
return res.str(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const char *base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|
|
|
|
const char *const base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
|
|
|
|
const char base64Pad = '='; |
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Encodes the specified data to a base64 string. |
|
|
|
|
* \brief Encodes the specified \a data to Base64. |
|
|
|
|
*/ |
|
|
|
|
LIB_EXPORT string encodeBase64(const vector<char> &bytes) |
|
|
|
|
LIB_EXPORT string encodeBase64(const byte *data, uint32 dataSize) |
|
|
|
|
{ |
|
|
|
|
string encoded; |
|
|
|
|
encoded.reserve(((bytes.size() / 3) + (bytes.size() % 3 > 0)) * 4); |
|
|
|
|
byte mod = dataSize % 3; |
|
|
|
|
encoded.reserve(((dataSize / 3) + (mod > 0)) * 4); |
|
|
|
|
uint32 temp; |
|
|
|
|
auto iterator = bytes.cbegin(); |
|
|
|
|
for(uint32 index = 0, maxIndex = bytes.size() / 3; index < maxIndex; ++iterator, ++index) { |
|
|
|
|
temp = (*iterator) << 16; |
|
|
|
|
temp += (*++iterator) << 8; |
|
|
|
|
temp += (*++iterator); |
|
|
|
|
encoded.append(1, base64Chars[(temp & 0x00FC0000) >> 18]); |
|
|
|
|
encoded.append(1, base64Chars[(temp & 0x0003F000) >> 12]); |
|
|
|
|
encoded.append(1, base64Chars[(temp & 0x00000FC0) >> 6 ]); |
|
|
|
|
encoded.append(1, base64Chars[(temp & 0x0000003F) ]); |
|
|
|
|
for(const byte *end = --data + dataSize - mod; data != end; ) { |
|
|
|
|
temp = *++data << 16; |
|
|
|
|
temp |= *++data << 8; |
|
|
|
|
temp |= *++data; |
|
|
|
|
encoded.push_back(base64Chars[(temp & 0x00FC0000) >> 18]); |
|
|
|
|
encoded.push_back(base64Chars[(temp & 0x0003F000) >> 12]); |
|
|
|
|
encoded.push_back(base64Chars[(temp & 0x00000FC0) >> 6 ]); |
|
|
|
|
encoded.push_back(base64Chars[(temp & 0x0000003F) ]); |
|
|
|
|
} |
|
|
|
|
switch(bytes.size() % 3) { |
|
|
|
|
switch(mod) { |
|
|
|
|
case 1: |
|
|
|
|
temp = (*++iterator) << 16; |
|
|
|
|
encoded.append(1, base64Chars[(temp & 0x00FC0000) >> 18]); |
|
|
|
|
encoded.append(1, base64Chars[(temp & 0x0003F000) >> 12]); |
|
|
|
|
encoded.append(2, base64Pad); |
|
|
|
|
temp = *++data << 16; |
|
|
|
|
encoded.push_back(base64Chars[(temp & 0x00FC0000) >> 18]); |
|
|
|
|
encoded.push_back(base64Chars[(temp & 0x0003F000) >> 12]); |
|
|
|
|
encoded.push_back(base64Pad); |
|
|
|
|
encoded.push_back(base64Pad); |
|
|
|
|
break; |
|
|
|
|
case 2: |
|
|
|
|
temp = (*++iterator) << 16; |
|
|
|
|
temp += (*++iterator) << 8; |
|
|
|
|
encoded.append(1, base64Chars[(temp & 0x00FC0000) >> 18]); |
|
|
|
|
encoded.append(1, base64Chars[(temp & 0x0003F000) >> 12]); |
|
|
|
|
encoded.append(1, base64Chars[(temp & 0x00000FC0) >> 6 ]); |
|
|
|
|
encoded.append(1, base64Pad); |
|
|
|
|
temp = *++data << 16; |
|
|
|
|
temp |= *++data << 8; |
|
|
|
|
encoded.push_back(base64Chars[(temp & 0x00FC0000) >> 18]); |
|
|
|
|
encoded.push_back(base64Chars[(temp & 0x0003F000) >> 12]); |
|
|
|
|
encoded.push_back(base64Chars[(temp & 0x00000FC0) >> 6 ]); |
|
|
|
|
encoded.push_back(base64Pad); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
return encoded; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Decodes the specified base64 encoded string. |
|
|
|
|
* \throw Throws a std::runtime_error if the specified string is no valid base64. |
|
|
|
|
* \brief Decodes the specified Base64 encoded string. |
|
|
|
|
* \throw Throws a ConversionException if the specified string is no valid Base64. |
|
|
|
|
*/ |
|
|
|
|
LIB_EXPORT vector<char> decodeBase64(const string &encoded) |
|
|
|
|
LIB_EXPORT pair<unique_ptr<byte[]>, uint32> decodeBase64(const char *encodedStr, const uint32 strSize) |
|
|
|
|
{ |
|
|
|
|
string::size_type len = encoded.length(); |
|
|
|
|
if(len % 4) { |
|
|
|
|
throw runtime_error("invalid size of base64"); |
|
|
|
|
} |
|
|
|
|
string::size_type pad = 0; |
|
|
|
|
if(len >= 1 && encoded[len - 1] == base64Pad) { |
|
|
|
|
++pad; |
|
|
|
|
if(strSize % 4) { |
|
|
|
|
throw ConversionException("invalid size of base64"); |
|
|
|
|
} |
|
|
|
|
if(len >= 2 && encoded[len - 2] == base64Pad) { |
|
|
|
|
++pad; |
|
|
|
|
uint32 decodedSize = (strSize / 4) * 3; |
|
|
|
|
const char *const end = encodedStr + strSize; |
|
|
|
|
if(strSize) { |
|
|
|
|
if(*(end - 1) == base64Pad) { |
|
|
|
|
--decodedSize; |
|
|
|
|
} |
|
|
|
|
if(*(end - 2) == base64Pad) { |
|
|
|
|
--decodedSize; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
vector<char> decoded; |
|
|
|
|
decoded.reserve(((len / 4) * 3) - pad); |
|
|
|
|
uint32 temp = 0; |
|
|
|
|
auto iterator = encoded.cbegin(), end = encoded.cend(); |
|
|
|
|
while(iterator < end) { |
|
|
|
|
for(uint32 quantumPos = 0; quantumPos < 4; ++quantumPos, ++iterator) { |
|
|
|
|
auto buffer = make_unique<byte[]>(decodedSize); |
|
|
|
|
auto *iter = buffer.get() - 1; |
|
|
|
|
while(encodedStr < end) { |
|
|
|
|
uint32 temp = 0; |
|
|
|
|
for(byte quantumPos = 0; quantumPos < 4; ++quantumPos, ++encodedStr) { |
|
|
|
|
temp <<= 6; |
|
|
|
|
if(*iterator >= 'A' && *iterator <= 'Z') { |
|
|
|
|
temp |= *iterator - 'A'; |
|
|
|
|
} else if(*iterator >= 'a' && *iterator <= 'z') { |
|
|
|
|
temp |= *iterator - 'a'; |
|
|
|
|
} else if(*iterator >= '0' && *iterator <= '9') { |
|
|
|
|
temp |= *iterator - '0'; |
|
|
|
|
} else if(*iterator == '+') { |
|
|
|
|
temp |= '>'; |
|
|
|
|
} else if(*iterator == '/') { |
|
|
|
|
temp |= '?'; |
|
|
|
|
} else if(*iterator == base64Pad) { |
|
|
|
|
switch(end - iterator) { |
|
|
|
|
if(*encodedStr >= 'A' && *encodedStr <= 'Z') { |
|
|
|
|
temp |= *encodedStr - 'A'; |
|
|
|
|
} else if(*encodedStr >= 'a' && *encodedStr <= 'z') { |
|
|
|
|
temp |= *encodedStr - 'a' + 26; |
|
|
|
|
} else if(*encodedStr >= '0' && *encodedStr <= '9') { |
|
|
|
|
temp |= *encodedStr - '0' + 2 * 26; |
|
|
|
|
} else if(*encodedStr == '+') { |
|
|
|
|
temp |= 2 * 26 + 10; |
|
|
|
|
} else if(*encodedStr == '/') { |
|
|
|
|
temp |= 2 * 26 + 10 + 1; |
|
|
|
|
} else if(*encodedStr == base64Pad) { |
|
|
|
|
switch(end - encodedStr) { |
|
|
|
|
case 1: |
|
|
|
|
decoded.push_back((temp >> 16) & 0x000000FF); |
|
|
|
|
decoded.push_back((temp >> 8 ) & 0x000000FF); |
|
|
|
|
return decoded; |
|
|
|
|
*++iter = (temp >> 16) & 0xFF; |
|
|
|
|
*++iter = (temp >> 8) & 0xFF; |
|
|
|
|
return make_pair(move(buffer), decodedSize); |
|
|
|
|
case 2: |
|
|
|
|
decoded.push_back((temp >> 10) & 0x000000FF); |
|
|
|
|
return decoded; |
|
|
|
|
*++iter = (temp >> 10) & 0xFF; |
|
|
|
|
return make_pair(move(buffer), decodedSize); |
|
|
|
|
default: |
|
|
|
|
throw runtime_error("invalid padding in base64"); |
|
|
|
|
throw ConversionException("invalid padding in base64"); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
throw runtime_error("invalid character in base64"); |
|
|
|
|
throw ConversionException("invalid character in base64"); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
decoded.push_back((temp >> 16) & 0x000000FF); |
|
|
|
|
decoded.push_back((temp >> 8 ) & 0x000000FF); |
|
|
|
|
decoded.push_back((temp ) & 0x000000FF); |
|
|
|
|
*++iter = (temp >> 16) & 0xFF; |
|
|
|
|
*++iter = (temp >> 8) & 0xFF; |
|
|
|
|
*++iter = (temp ) & 0xFF; |
|
|
|
|
} |
|
|
|
|
return decoded; |
|
|
|
|
return make_pair(move(buffer), decodedSize); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|