diff --git a/CHANGELOG.md b/CHANGELOG.md index ce8c6220..600c5c60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ 1.0.0-b38 * Refactor static_string +* Refactor base64 -------------------------------------------------------------------------------- diff --git a/include/beast/core/detail/base64.hpp b/include/beast/core/detail/base64.hpp index 407b79db..3ddcf863 100644 --- a/include/beast/core/detail/base64.hpp +++ b/include/beast/core/detail/base64.hpp @@ -5,15 +5,6 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_DETAIL_BASE64_HPP -#define BEAST_DETAIL_BASE64_HPP - -#include -#include - -namespace beast { -namespace detail { - /* Portions from http://www.adp-gmbh.ch/cpp/common/base64.html Copyright notice: @@ -44,77 +35,195 @@ namespace detail { */ -template -std::string const& -base64_alphabet() +#ifndef BEAST_DETAIL_BASE64_HPP +#define BEAST_DETAIL_BASE64_HPP + +#include +#include +#include + +namespace beast { +namespace detail { + +namespace base64 { + +inline +char const* +get_alphabet() { - static std::string const alphabet = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - return alphabet; + static char constexpr tab[] = { + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + }; + return &tab[0]; } inline -bool -is_base64(unsigned char c) +signed char const* +get_inverse() { - return (std::isalnum(c) || (c == '+') || (c == '/')); + static signed char constexpr tab[] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0-15 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16-31 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, // 32-47 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 48-63 + -1, -1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 64-79 + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, // 80-95 + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 96-111 + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, // 112-127 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128-143 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 144-159 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 160-175 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 176-191 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 192-207 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 208-223 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 224-239 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 // 240-255 + }; + return &tab[0]; } -template -std::string -base64_encode (std::uint8_t const* data, - std::size_t in_len) + +/// Returns max chars needed to encode a base64 string +inline +std::size_t constexpr +encoded_size(std::size_t n) { + return 4 * ((n + 2) / 3); +} + +/// Returns max bytes needed to decode a base64 string +inline +std::size_t constexpr +decoded_size(std::size_t n) +{ + return n / 4 * 3; // requires n&3==0, smaller + //return 3 * n / 4; +} + +/** Encode a series of octets as a padded, base64 string. + + The resulting string will not be null terminated. + + @par Requires + + The memory pointed to by `out` points to valid memory + of at least `encoded_size(len)` bytes. + + @return The number of characters written to `out`. This + will exclude any null termination. +*/ +template +std::size_t +encode(void* dest, void const* src, std::size_t len) +{ + char* out = static_cast(dest); + char const* in = static_cast(src); + auto const tab = base64::get_alphabet(); + + for(auto n = len / 3; n--;) + { + *out++ = tab[ (in[0] & 0xfc) >> 2]; + *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; + *out++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)]; + *out++ = tab[ in[2] & 0x3f]; + in += 3; + } + + switch(len % 3) + { + case 2: + *out++ = tab[ (in[0] & 0xfc) >> 2]; + *out++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)]; + *out++ = tab[ (in[1] & 0x0f) << 2]; + *out++ = '='; + break; + + case 1: + *out++ = tab[ (in[0] & 0xfc) >> 2]; + *out++ = tab[((in[0] & 0x03) << 4)]; + *out++ = '='; + *out++ = '='; + break; + + case 0: + break; + } + + return out - static_cast(dest); +} + +/** Decode a padded base64 string into a series of octets. + + @par Requires + + The memory pointed to by `out` points to valid memory + of at least `decoded_size(len)` bytes. + + @return The number of octets written to `out`, and + the number of characters read from the input string, + expressed as a pair. +*/ +template +std::pair +decode(void* dest, char const* src, std::size_t len) +{ + char* out = static_cast(dest); + auto in = reinterpret_cast(src); unsigned char c3[3], c4[4]; int i = 0; int j = 0; - std::string ret; - ret.reserve (3 + in_len * 8 / 6); + auto const inverse = base64::get_inverse(); - char const* alphabet (base64_alphabet().data()); - - while(in_len--) + while(len-- && *in != '=') { - c3[i++] = *(data++); - if(i == 3) + auto const v = inverse[*in]; + if(v == -1) + break; + ++in; + c4[i] = v; + if(++i == 4) { - c4[0] = (c3[0] & 0xfc) >> 2; - c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4); - c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6); - c4[3] = c3[2] & 0x3f; - for(i = 0; (i < 4); i++) - ret += alphabet[c4[i]]; + c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); + c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); + c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; + + for(i = 0; i < 3; i++) + *out++ = c3[i]; i = 0; } } if(i) { - for(j = i; j < 3; j++) - c3[j] = '\0'; + c3[0] = ( c4[0] << 2) + ((c4[1] & 0x30) >> 4); + c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); + c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; - c4[0] = (c3[0] & 0xfc) >> 2; - c4[1] = ((c3[0] & 0x03) << 4) + ((c3[1] & 0xf0) >> 4); - c4[2] = ((c3[1] & 0x0f) << 2) + ((c3[2] & 0xc0) >> 6); - c4[3] = c3[2] & 0x3f; - - for(j = 0; (j < i + 1); j++) - ret += alphabet[c4[j]]; - - while((i++ < 3)) - ret += '='; + for(j = 0; j < i - 1; j++) + *out++ = c3[j]; } - return ret; - + return {out - static_cast(dest), + in - reinterpret_cast(src)}; } +} // base64 + template std::string -base64_encode (std::string const& s) +base64_encode (std::uint8_t const* data, + std::size_t len) +{ + std::string dest; + dest.resize(base64::encoded_size(len)); + dest.resize(base64::encode(&dest[0], data, len)); + return dest; +} + +inline +std::string +base64_encode(std::string const& s) { return base64_encode (reinterpret_cast < std::uint8_t const*> (s.data()), s.size()); @@ -124,52 +233,12 @@ template std::string base64_decode(std::string const& data) { - auto in_len = data.size(); - unsigned char c3[3], c4[4]; - int i = 0; - int j = 0; - int in_ = 0; - - std::string ret; - ret.reserve (in_len * 6 / 8); // ??? - - while(in_len-- && (data[in_] != '=') && - is_base64(data[in_])) - { - c4[i++] = data[in_]; in_++; - if(i == 4) { - for(i = 0; i < 4; i++) - c4[i] = static_cast( - base64_alphabet().find(c4[i])); - - c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); - c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); - c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; - - for(i = 0; (i < 3); i++) - ret += c3[i]; - i = 0; - } - } - - if(i) - { - for(j = i; j < 4; j++) - c4[j] = 0; - - for(j = 0; j < 4; j++) - c4[j] = static_cast( - base64_alphabet().find(c4[j])); - - c3[0] = (c4[0] << 2) + ((c4[1] & 0x30) >> 4); - c3[1] = ((c4[1] & 0xf) << 4) + ((c4[2] & 0x3c) >> 2); - c3[2] = ((c4[2] & 0x3) << 6) + c4[3]; - - for(j = 0; (j < i - 1); j++) - ret += c3[j]; - } - - return ret; + std::string dest; + dest.resize(base64::decoded_size(data.size())); + auto const result = base64::decode( + &dest[0], data.data(), data.size()); + dest.resize(result.first); + return dest; } } // detail