From a0be723a591189094f88a63aa72439035d6aa4d0 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Thu, 1 Apr 2021 23:06:48 +0200 Subject: [PATCH] [crispy] base64: provide encoding via state machine based API. --- src/crispy/base64.h | 128 +++++++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 42 deletions(-) diff --git a/src/crispy/base64.h b/src/crispy/base64.h index a3b8cf80cb..01d7f3cbe3 100644 --- a/src/crispy/base64.h +++ b/src/crispy/base64.h @@ -42,55 +42,99 @@ namespace detail }; } -template -std::string encode(Iterator begin, Iterator end, Alphabet alphabet) +struct EncoderState { - int const inputLength = static_cast(std::distance(begin, end)); - int const outputLength = ((inputLength + 2) / 3 * 4) + 1; + int modulo = 0; + uint8_t pending[3]; +}; - std::string output; - output.resize(static_cast(outputLength)); +template +constexpr void encode(uint8_t _byte, Alphabet const& alphabet, EncoderState& _state, Sink&& _sink) +{ + _state.pending[_state.modulo] = _byte; + if (++_state.modulo != 3) + return; + + _state.modulo = 0; + uint8_t const* input = _state.pending; + char const out[4] = { + alphabet[(input[0] >> 2) & 0x3F], + alphabet[((input[0] & 0x03) << 4) | ((uint8_t)(input[1] & 0xF0) >> 4)], + alphabet[((input[1] & 0x0F) << 2) | ((uint8_t)(input[2] & 0xC0) >> 6)], + alphabet[input[2] & 0x3F] + }; + _sink(std::string_view(out, 4)); +} - auto i = 0; - auto const e = inputLength - 2; - auto const input = begin; - auto out = output.begin(); +namespace detail +{ - while (i < e) - { - *out++ = alphabet[(input[i] >> 2) & 0x3F]; +template +constexpr void finish(Alphabet const& alphabet, EncoderState& _state, Sink&& _sink) +{ + if (_state.modulo == 0) + return; - *out++ = alphabet[((input[i] & 0x03) << 4) | - ((uint8_t)(input[i + 1] & 0xF0) >> 4)]; + auto const* input = _state.pending; - *out++ = alphabet[((input[i + 1] & 0x0F) << 2) | - ((uint8_t)(input[i + 2] & 0xC0) >> 6)]; + switch (_state.modulo) + { + case 2: + { + char const out[4] = { + alphabet[(input[0] >> 2) & 0x3F], + alphabet[((input[0] & 0x03) << 4) | ((uint8_t)(input[1] & 0xF0) >> 4)], + alphabet[((input[1] & 0x0F) << 2)], + '=' + }; + _sink(std::string_view{out}); + } + break; + case 1: + { + char const out[4] = { + alphabet[(input[0] >> 2) & 0x3F], + alphabet[((input[0] & 0x03) << 4)], + '=', + '=' + }; + _sink(std::string_view{out}); + } + break; + case 0: + break; + } +} - *out++ = alphabet[input[i + 2] & 0x3F]; +} - i += 3; - } +template +constexpr void encode(uint8_t _byte, EncoderState& _state, Sink _sink) +{ + constexpr char alphabet[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + return encode(_byte, alphabet, _state, std::forward(_sink)); + //return encode(_byte, detail::indexmap, _state, std::forward(_sink)); +} - if (i < inputLength) - { - *out++ = alphabet[(input[i] >> 2) & 0x3F]; +template +constexpr void finish(EncoderState& _state, Sink&& _sink) +{ + finish(detail::indexmap, _state, std::forward(_sink)); +} - if (i == (inputLength - 1)) - { - *out++ = alphabet[((input[i] & 0x03) << 4)]; - *out++ = '='; - } - else - { - *out++ = alphabet[((input[i] & 0x03) << 4) | - ((uint8_t)(input[i + 1] & 0xF0) >> 4)]; - *out++ = alphabet[((input[i + 1] & 0x0F) << 2)]; - } - *out++ = '='; - } +template +std::string encode(Iterator begin, Iterator end, Alphabet const& alphabet) +{ + std::string output; + output.reserve(((std::distance(begin, end) + 2) / 3 * 4) + 1); - auto const outlen = std::distance(output.begin(), out); - output.resize(static_cast(outlen)); + EncoderState state{}; + for (auto i = begin; i != end; ++i) + encode(*i, alphabet, state, [&](std::string_view _data) { output += _data; }); + detail::finish(alphabet, state, [&](std::string_view _data) { output += _data; }); return output; } @@ -106,9 +150,9 @@ std::string encode(Iterator begin, Iterator end) } -inline std::string encode(const std::string_view& value) +inline std::string encode(std::string_view _value) { - return encode(value.begin(), value.end()); + return encode(_value.begin(), _value.end()); } template @@ -195,12 +239,12 @@ size_t decode(Iterator begin, Iterator end, Output output) } template -size_t decode(std::string_view const& input, Output output) +size_t decode(std::string_view input, Output output) { return decode(input.begin(), input.end(), output); } -inline std::string decode(const std::string_view& input) +inline std::string decode(std::string_view input) { std::string output; output.resize(decodeLength(input));