Skip to content

YexuanXiao/rfc4648-base64

Repository files navigation

rfc4648/base64/base32/base16-hex

A plain header-only RFC 4648 encoding/decoding library, but with the most modern C++ API design.

All variants of RFC 4648 are supported and the Crockford variant is available.

Support input from discontinuous multiple buffers.

Support non-padding for secure base64 url variant.

Support constexpr compile-time caculation.

C++23 required (std::byteswap).

Synopsis

enum class rfc4648_kind
{
    base64,
    base64_url,
    base32,
    base32_lower,
    base32_mixed,
    base32_hex,
    base32_hex_lower,
    base32_hex_mixed,
    base32_crockford,
    base32_crockford_lower,
    base32_crockford_mixed,
    base16,
    base16_lower,
    base16_mixed,
    hex = base16,
    hex_lower = base16_lower,
    hex_mixed = base16_mixed
};
// All special member functions are trivial and has non-trivial but noexcept default constructor
class rfc4648_context;
//
template <typename In, typename Out>
struct rfc4648_decode_result
{
    In end;
    Out out;
    // For rebinding via std::tie
    operator std::tuple<End &, Out &>() && noexcept;
};
// All functions are constexpr
// Encode
template <rfc4648_kind Kind = rfc4648_kind::base64, bool Padding = true, typename In, typename Out>
Out rfc4648_encode(In begin, In end, Out first);
template <rfc4648_kind Kind = rfc4648_kind::base64, bool Padding = true, typename R, typename Out>
Out rfc4648_encode(R&& r, Out first);
template <rfc4648_kind Kind = rfc4648_kind::base64, typename In, typename Out>
Out rfc4648_encode(rfc4648_context& ctx, In begin, In end, Out first);
template <rfc4648_kind Kind = rfc4648_kind::base64, typename R, typename Out>
Out rfc4648_encode(rfc4648_context& ctx, R&& r, Out first);
template <rfc4648_kind Kind = rfc4648_kind::base64, bool Padding = true, typename Out>
Out rfc4648_encode(rfc4648_context& ctx, Out first);
// Decode
template <rfc4648_kind Kind = rfc4648_kind::base64, bool Padding = true, typename In, typename Out>
rfc4648_decode_result<In, Out> rfc4648_decode(In begin, In end, Out first);
template <rfc4648_kind Kind = rfc4648_kind::base64, bool Padding = true, typename R, typename Out>
rfc4648_decode_result<In, Out> rfc4648_decode(R&& r, Out first);
template <rfc4648_kind Kind = rfc4648_kind::base64, typename In, typename Out>
rfc4648_decode_result<In, Out> rfc4648_decode(rfc4648_context& ctx, In begin, In end, Out first);
template <rfc4648_kind Kind = rfc4648_kind::base64, typename R, typename Out>
rfc4648_decode_result<In, Out> rfc4648_decode(rfc4648_context& ctx, R&& r, Out first);
template <rfc4648_kind Kind = rfc4648_kind::base64, bool Padding = true, typename In>
In rfc4648_decode(rfc4648_context& ctx, In begin, In end);
// Helper functions
template <rfc4648_kind kind = rfc4648_kind::base64>
std::size_t rfc4648_encode_length(std::size_t input) noexcept;
template <rfc4648_kind kind = rfc4648_kind::base64>
std::size_t rfc4648_decode_length(std::size_t input) noexcept;

R must model std::contiguous_range , In must satisfy ContinuousIterator and Out must satisfy OutputIterator.

Let n - 1 is the length of the output as specified by RFC 4648.

If [begin, end) is not a valid range, or [first, first + n) is not a valid range, or if [begin, end) and [first, first + n) overlap, or if r and [first, first + n) overlap, the behavior is undefined.

If the template parameter Padding is false then the padding character = is not written.

The decode functions will return immediately if there are invalid characters (including =) within the range [begin, end), then rfc4648_decode_result<In, Out>::end points to the first invalid character.

Throws any exceptions from increments and dereferences begin, end or first, no other exceptions will be thrown. After an exception is thrown, ctx will be in an unspecified state.

The rfc4648_encode_length and rfc4648_decode_length functions calculate the maximum number of characters/bytes needed for a given input length. The actual output number will be less than or equal to the returned number.

Example

#include "decode.hpp"
#include "encode.hpp"
#include <cassert>
#include <string>
#include <string_view>

#define test_str "ABCDEFGHIJKLMN"

int main()
{
    std::string_view src1{test_str};

    std::string encoded1;
    encoded1.resize(bizwen::rfc4648_encode_length(src1.size()));
    bizwen::rfc4648_encode(src1.begin(), src1.end(), encoded1.begin());

    std::string decoded1;
    decoded1.resize(src1.size());
    bizwen::rfc4648_decode(encoded1.begin(), encoded1.end(), decoded1.begin());

    assert(src1 == decoded1);

    std::string_view src2{test_str test_str test_str};
    std::string encoded2;
    encoded2.resize(bizwen::rfc4648_encode_length(src2.size()));
    bizwen::rfc4648_context ctx;

    auto eit = bizwen::rfc4648_encode(ctx, src1.begin(), src1.end(), encoded2.begin());
    eit = bizwen::rfc4648_encode(ctx, src1.begin(), src1.end(), eit);
    eit = bizwen::rfc4648_encode(ctx, src1.begin(), src1.end(), eit);
    // This overload handles remaining bits and outputs the padding characters
    bizwen::rfc4648_encode(ctx, eit);

    std::string decoded2;
    decoded2.resize(src2.size());
    auto [end, dit] = bizwen::rfc4648_decode(ctx, encoded2.begin(), encoded2.begin() + encoded2.size() / 3, decoded2.begin());
    // If there is an error in decoding, then the assertion fails
    assert(end == encoded2.begin() + encoded2.size() / 3);
    std::tie(end, dit) = bizwen::rfc4648_decode(ctx, end, end + encoded2.size() / 3, dit);
    // If it is the last input data, then the returned end may point to the first padding character
    // even if the input data is correct
    std::tie(end, dit) = bizwen::rfc4648_decode(ctx, end, end + (encoded2.size() - encoded2.size() / 3 * 2), dit);
    // It should not be asserted that the returned end is equal to the input end
    // This overload is used to check the padding characters
    end = bizwen::rfc4648_decode(ctx, end, encoded2.end());
    // The assertion can only be true when the last check is complete
    assert(end == encoded2.end());

    assert(src2 == decoded2);

    std::string encoded3;
    encoded3.resize(bizwen::rfc4648_encode_length(src1.size()));
    bizwen::rfc4648_encode(src1, encoded3.begin());

    std::wstring dest3;
    dest3.resize(bizwen::rfc4648_encode_length(src1.size()));
    bizwen::rfc4648_encode((std::byte *)src1.data(), (std::byte *)src1.data() + src1.size(), dest3.begin());
}

About

Modern C++ base64, base32 and base16 library

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published