8#ifndef GRIDFORMAT_COMMON_ENCODING_BASE64_HPP_
9#define GRIDFORMAT_COMMON_ENCODING_BASE64_HPP_
18#include <gridformat/common/exceptions.hpp>
19#include <gridformat/common/serialization.hpp>
20#include <gridformat/common/istream_helper.hpp>
21#include <gridformat/common/output_stream.hpp>
22#include <gridformat/common/concepts.hpp>
27namespace Base64Detail {
29static constexpr auto alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
31static constexpr std::array<unsigned char, 256> letter_to_index = [] {
32 std::array<unsigned char, 256> result;
33 std::ranges::fill(result, 0);
34 for (
int i = 0; i < 64; ++i)
35 result[
static_cast<unsigned>(alphabet[i])] = i;
47 throw SizeError(
"Given size is not a multiple of 4");
53 return 4*
static_cast<std::size_t
>(
54 std::ceil(
static_cast<double>(raw_size)/3.0)
64 Serialization decode_from(std::istream& stream, std::size_t target_num_decoded_bytes)
const {
65 InputStreamHelper helper{stream};
66 const auto encoded_size = Base64::encoded_size(target_num_decoded_bytes);
67 std::string chars = helper.read_until_any_of(
"=", encoded_size);
68 if (chars.size() != encoded_size)
69 chars += helper.read_until_not_any_of(
"=");
71 Serialization result{chars.size()};
72 auto result_chars = result.template as_span_of<char>();
73 std::ranges::move(std::move(chars), result_chars.begin());
74 result.resize(decode(result_chars));
78 template<std::
size_t s>
79 std::size_t decode(std::span<char, s> chars)
const {
80 if (chars.size() == 0)
82 if (chars.size()%4 != 0)
83 throw SizeError(
"Buffer size is not a multiple of 4");
85 std::size_t in_offset = 0;
86 std::size_t out_offset = 0;
87 while (in_offset < chars.size()) {
89 _decode_triplet(chars.data() + in_offset),
90 chars.data() + out_offset
96 const auto end_chars = chars.subspan(chars.size() - 3);
97 std::string_view end_str{end_chars.data(), end_chars.size()};
98 const auto num_padding_chars = std::ranges::count(end_str,
'=');
99 return out_offset - (num_padding_chars > 0 ? num_padding_chars : 0);
103 std::array<char, 3> _decode_triplet(
const char* in)
const {
104 using Base64Detail::letter_to_index;
105 std::array<char, 3> result;
106 result[0] = ((letter_to_index[in[0]] & 0b0011'1111) << 2) | ((letter_to_index[in[1]] & 0b0011'0000) >> 4);
107 result[1] = ((letter_to_index[in[1]] & 0b0000'1111) << 4) | ((letter_to_index[in[2]] & 0b0011'1100) >> 2);
108 result[2] = ((letter_to_index[in[2]] & 0b0000'0011) << 6) | ((letter_to_index[in[3]] & 0b0011'1111));
119template<
typename OStream>
122 static_assert(
sizeof(std::byte) ==
sizeof(Byte));
123 static constexpr int buffer_size = 3;
124 static constexpr int encoded_buffer_size = 4;
127 inline Byte _encode_sextet_0(
const Byte* buffer)
const {
128 return Base64Detail::alphabet[((buffer[0] & 0b1111'1100) >> 2)];
131 inline Byte _encode_sextet_1(
const Byte* buffer)
const {
132 return Base64Detail::alphabet[((buffer[0] & 0b0000'0011) << 4)
133 | ((buffer[1] & 0b1111'0000) >> 4)];
136 inline Byte _encode_sextet_2(
const Byte* buffer)
const {
137 return Base64Detail::alphabet[((buffer[1] & 0b0000'1111) << 2)
138 | ((buffer[2] & 0b1100'0000) >> 6)];
141 inline Byte _encode_sextet_3(
const Byte* buffer)
const {
142 return Base64Detail::alphabet[(buffer[2] & 0b0011'1111)];
147 : OutputStreamWrapperBase<OStream>(s)
148 , _opts{std::move(opts)}
151 template<
typename T, std::
size_t size>
152 void write(std::span<T, size> data) {
153 auto byte_span = std::as_bytes(data);
154 const Byte* bytes =
reinterpret_cast<const Byte*
>(byte_span.data());
155 _write(bytes, byte_span.size());
160 std::size_t _cache_size_out()
const {
return _opts.
num_cached_buffers*encoded_buffer_size; }
162 void _write(
const Byte* data, std::size_t size) {
163 const auto num_full_buffers = size/buffer_size;
165 for (
const auto i : std::views::iota(std::size_t{0}, num_full_caches))
166 _flush_full_cache(data + i*_cache_size_in());
168 const auto processed_bytes = num_full_caches*_cache_size_in();
169 if (size > processed_bytes)
170 _flush_cache(data + processed_bytes, size - processed_bytes);
173 void _flush_full_cache(
const Byte* data) {
174 std::vector<Byte> cache(_cache_size_out());
175 for (std::size_t i = 0; i < _cache_size_out()/encoded_buffer_size; ++i) {
176 const std::size_t in_offset = i*buffer_size;
177 const std::size_t out_offset = i*encoded_buffer_size;
178 cache[out_offset + 0] = _encode_sextet_0(data + in_offset);
179 cache[out_offset + 1] = _encode_sextet_1(data + in_offset);
180 cache[out_offset + 2] = _encode_sextet_2(data + in_offset);
181 cache[out_offset + 3] = _encode_sextet_3(data + in_offset);
183 this->_stream.write(std::span{cache});
186 void _flush_cache(
const Byte* data, std::size_t num_bytes_in) {
187 if (num_bytes_in == 0)
189 if (num_bytes_in > _cache_size_in())
190 throw SizeError(
"Number of bytes cannot be larger than cache size");
192 const std::size_t num_full_buffers = num_bytes_in/buffer_size;
193 const std::size_t residual = num_bytes_in%buffer_size;
195 std::vector<Byte> cache(_cache_size_out());
196 for (std::size_t i = 0; i < num_full_buffers; ++i) {
197 const std::size_t in_offset = i*buffer_size;
198 const std::size_t out_offset = i*encoded_buffer_size;
199 cache[out_offset + 0] = _encode_sextet_0(data + in_offset);
200 cache[out_offset + 1] = _encode_sextet_1(data + in_offset);
201 cache[out_offset + 2] = _encode_sextet_2(data + in_offset);
202 cache[out_offset + 3] = _encode_sextet_3(data + in_offset);
205 const std::size_t in_offset = num_full_buffers*buffer_size;
206 const std::size_t out_offset = num_full_buffers*encoded_buffer_size;
208 Byte last_buffer[buffer_size] = {
210 residual > 1 ? *(data + in_offset + 1) : Byte{0},
211 residual > 2 ? *(data + in_offset + 2) : Byte{0}
213 cache[out_offset] = _encode_sextet_0(last_buffer);
214 cache[out_offset + 1] = _encode_sextet_1(last_buffer);
215 cache[out_offset + 2] = residual > 1 ? _encode_sextet_2(last_buffer) :
'=';
216 cache[out_offset + 3] = residual > 2 ? _encode_sextet_3(last_buffer) :
'=';
217 this->_stream.write(std::span{cache.data(), out_offset + 4});
219 this->_stream.write(std::span{cache.data(), out_offset});
230namespace GridFormat::Encoding {
238 template<
typename Stream>
246 other._opts = std::move(opts);
253 enc._opts = std::move(opts);
std::size_t encoded_size(std::size_t raw_size)
Return the number of encoded bytes for the given number of raw bytes.
Definition: base64.hpp:52
std::size_t decoded_size(std::size_t encoded_size)
Return the number of decoded bytes for the given number of encoded bytes.
Definition: base64.hpp:45
constexpr Base64 base64
Instance of the base64 encoder.
Definition: base64.hpp:262