Skip to content

Commit

Permalink
[crispy] base64: provide encoding via state machine based API.
Browse files Browse the repository at this point in the history
  • Loading branch information
christianparpart committed Jul 15, 2021
1 parent dfacb3d commit a0be723
Showing 1 changed file with 86 additions and 42 deletions.
128 changes: 86 additions & 42 deletions src/crispy/base64.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,55 +42,99 @@ namespace detail
};
}

template <typename Iterator, typename Alphabet>
std::string encode(Iterator begin, Iterator end, Alphabet alphabet)
struct EncoderState
{
int const inputLength = static_cast<int>(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<unsigned>(outputLength));
template <typename Alphabet, typename Sink>
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 <typename Alphabet, typename Sink>
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 <typename Sink>
constexpr void encode(uint8_t _byte, EncoderState& _state, Sink _sink)
{
constexpr char alphabet[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
return encode(_byte, alphabet, _state, std::forward<Sink>(_sink));
//return encode(_byte, detail::indexmap, _state, std::forward<Sink>(_sink));
}

if (i < inputLength)
{
*out++ = alphabet[(input[i] >> 2) & 0x3F];
template <typename Sink>
constexpr void finish(EncoderState& _state, Sink&& _sink)
{
finish(detail::indexmap, _state, std::forward<Sink>(_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 <typename Iterator, typename Alphabet>
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<unsigned>(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;
}
Expand All @@ -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 <typename Iterator, typename IndexTable>
Expand Down Expand Up @@ -195,12 +239,12 @@ size_t decode(Iterator begin, Iterator end, Output output)
}

template <typename Output>
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));
Expand Down

0 comments on commit a0be723

Please sign in to comment.