diff --git a/src/string.cpp b/src/string.cpp index 493c9abf..d980370c 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -187,62 +187,53 @@ std::string toBase64(const std::vector& data) { std::vector fromBase64(const std::string& str) { - size_t strLen = str.size(); - if (strLen % 4 != 0) - throw std::runtime_error("String length not a factor of 4"); - - size_t numBlocks = strLen / 4; - size_t len = numBlocks * 3; - if (strLen >= 4) { - if (str[strLen - 1] == '=') - len--; - if (str[strLen - 2] == '=') - len--; - } + std::vector data; + uint32_t block = 0; + int i = 0; + int padding = 0; - std::vector data(len); + for (char c : str) { + uint8_t d = 0; - for (size_t b = 0; b < numBlocks; b++) { - // Encode block - uint32_t block = 0; - size_t i; - for (i = 0; i < 4; i++) { - uint8_t c = str[4 * b + i]; - uint8_t d = 0; - - if ('A' <= c && c <= 'Z') { - d = c - 'A'; - } - else if ('a' <= c && c <= 'z') { - d = c - 'a' + 26; - } - else if ('0' <= c && c <= '9') { - d = c - '0' + 52; - } - else if (c == '+') { - d = 62; - } - else if (c == '/') { - d = 63; - } - else if (c == '=') { - // Padding ends block encoding - break; - } - else { - // Since we assumed the string was a factor of 4 bytes, fail if an invalid character, such as whitespace, was found. - throw std::runtime_error("String contains non-base64 character"); - } - - block |= uint32_t(d) << (6 * (3 - i)); + if ('A' <= c && c <= 'Z') { + d = c - 'A'; + } + else if ('a' <= c && c <= 'z') { + d = c - 'a' + 26; + } + else if ('0' <= c && c <= '9') { + d = c - '0' + 52; + } + else if (c == '+') { + d = 62; + } + else if (c == '/') { + d = 63; + } + else if (c == '=') { + padding++; + } + else { + // Ignore whitespace and non-base64 characters + continue; } - // Decode block - for (size_t k = 0; k < i - 1; k++) { - data[3 * b + k] = (block >> (8 * (2 - k))) & 0xff; + block |= uint32_t(d) << (6 * (3 - i)); + i++; + + if (i >= 4) { + // Decode block + data.push_back((block >> (8 * (2 - 0))) & 0xff); + if (padding < 2) + data.push_back((block >> (8 * (2 - 1))) & 0xff); + if (padding < 1) + data.push_back((block >> (8 * (2 - 2))) & 0xff); + // Reset block + block = 0; + i = 0; + padding = 0; } } - return data; }