stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
base64.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2016-2024 Amebis
4*/
5
6#pragma once
7
8#include "compat.hpp"
9#include "stream.hpp"
10#include <cstdint>
11#include <string>
12#include <vector>
13
14#if defined(__GNUC__)
15#pragma GCC diagnostic push
16#pragma GCC diagnostic ignored "-Wunknown-pragmas"
17#endif
18
19namespace stdex
20{
22 inline const char base64_enc_lookup[64] = {
23 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
24 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
25 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
26 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
27 };
28
29 inline const uint8_t base64_dec_lookup[256] = {
30 /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
31 /* 0 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
32 /* 1 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
33 /* 2 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
34 /* 3 */ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 64, 255, 255,
35 /* 4 */ 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
36 /* 5 */ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
37 /* 6 */ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
38 /* 7 */ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255,
39 /* 8 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
40 /* 9 */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
41 /* A */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
42 /* B */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
43 /* C */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
44 /* D */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
45 /* E */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
46 /* F */ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
47 };
49
54 {
55 public:
59 base64_enc() noexcept : m_num(0)
60 {
61 m_buf[0] = 0;
62 m_buf[1] = 0;
63 m_buf[2] = 0;
64 }
65
74 template<class T, class TR, class AX>
75 void encode(_Inout_ std::basic_string<T, TR, AX> &out, _In_bytecount_(size) const void *data, _In_ size_t size, _In_opt_ bool is_last = true)
76 {
77 _Assume_(data || !size);
78
79 // Preallocate output
80 out.reserve(out.size() + enc_size(size));
81
82 // Convert data character by character.
83 for (size_t i = 0;; i++) {
84 if (m_num >= 3) {
85 encode(out);
86 m_num = 0;
87 }
88
89 if (i >= size)
90 break;
91
92 m_buf[m_num++] = reinterpret_cast<const uint8_t*>(data)[i];
93 }
94
95 // If this is the last block, flush the buffer.
96 if (is_last && m_num) {
97 encode(out, m_num);
98 m_num = 0;
99 }
100 }
101
105 void clear() noexcept
106 {
107 m_num = 0;
108 }
109
117 size_t enc_size(_In_ size_t size) const noexcept
118 {
119 return ((m_num + size + 2)/3)*4;
120 }
121
122 protected:
126 template<class T, class TR, class AX>
127 void encode(_Inout_ std::basic_string<T, TR, AX> &out)
128 {
129 out += base64_enc_lookup[ m_buf[0] >> 2 ];
130 out += base64_enc_lookup[((m_buf[0] << 4) | (m_buf[1] >> 4)) & 0x3f];
131 out += base64_enc_lookup[((m_buf[1] << 2) | (m_buf[2] >> 6)) & 0x3f];
132 out += base64_enc_lookup[ m_buf[2] & 0x3f];
133 }
134
138 template<class T, class TR, class AX>
139 void encode(_Inout_ std::basic_string<T, TR, AX> &out, _In_ size_t size)
140 {
141 if (size > 0) {
142 out += base64_enc_lookup[m_buf[0] >> 2];
143 if (size > 1) {
144 out += base64_enc_lookup[((m_buf[0] << 4) | (m_buf[1] >> 4)) & 0x3f];
145 if (size > 2) {
146 out += base64_enc_lookup[((m_buf[1] << 2) | (m_buf[2] >> 6)) & 0x3f];
147 out += base64_enc_lookup[m_buf[2] & 0x3f];
148 } else {
149 out += base64_enc_lookup[(m_buf[1] << 2) & 0x3f];
150 out += '=';
151 }
152 } else {
153 out += base64_enc_lookup[(m_buf[0] << 4) & 0x3f];
154 out += '=';
155 out += '=';
156 }
157 } else {
158 out += '=';
159 out += '=';
160 out += '=';
161 out += '=';
162 }
163 }
164
165 protected:
166 uint8_t m_buf[3];
167 size_t m_num;
168 };
169
174 {
175 public:
176 base64_writer(_Inout_ stdex::stream::basic& source, _In_ size_t max_blocks = 19) :
178 m_max_blocks(max_blocks),
179 m_num_blocks(0)
180 {}
181
182 virtual ~base64_writer()
183 {
184 // Flush the buffer.
185 if (m_num) {
186 if (++m_num_blocks > m_max_blocks) {
187 *m_source << '\n';
188 m_num_blocks = 1;
189 }
190 encode(m_num);
191 }
192 }
193
194 virtual _Success_(return != 0) size_t write(
195 _In_reads_bytes_opt_(length) const void* data, _In_ size_t length)
196 {
197 _Assume_(data || !length);
198 for (size_t i = 0;; i++) {
199 if (m_num >= 3) {
200 if (++m_num_blocks > m_max_blocks) {
201 *m_source << '\n';
202 m_num_blocks = 1;
203 }
204 encode();
205 if (!m_source->ok()) _Unlikely_ {
206 m_state = m_source->state();
207 return length - i;
208 }
209 m_num = 0;
210 }
211 if (i >= length) {
212 m_state = stdex::stream::state_t::ok;
213 return length;
214 }
215 m_buf[m_num++] = reinterpret_cast<const uint8_t*>(data)[i];
216 }
217 }
218
219 protected:
223 void encode()
224 {
225 char out[4];
226 out[0] = base64_enc_lookup[ m_buf[0] >> 2 ];
227 out[1] = base64_enc_lookup[((m_buf[0] << 4) | (m_buf[1] >> 4)) & 0x3f];
228 out[2] = base64_enc_lookup[((m_buf[1] << 2) | (m_buf[2] >> 6)) & 0x3f];
229 out[3] = base64_enc_lookup[ m_buf[2] & 0x3f];
230 m_source->write_array(out, sizeof(*out), _countof(out));
231 }
232
236 void encode(_In_ size_t size)
237 {
238 char out[4];
239 if (size > 0) {
240 out[0] = base64_enc_lookup[m_buf[0] >> 2];
241 if (size > 1) {
242 out[1] = base64_enc_lookup[((m_buf[0] << 4) | (m_buf[1] >> 4)) & 0x3f];
243 if (size > 2) {
244 out[2] = base64_enc_lookup[((m_buf[1] << 2) | (m_buf[2] >> 6)) & 0x3f];
245 out[3] = base64_enc_lookup[m_buf[2] & 0x3f];
246 } else {
247 out[2] = base64_enc_lookup[(m_buf[1] << 2) & 0x3f];
248 out[3] = '=';
249 }
250 } else {
251 out[1] = base64_enc_lookup[(m_buf[0] << 4) & 0x3f];
252 out[2] = '=';
253 out[3] = '=';
254 }
255 } else {
256 out[0] = '=';
257 out[1] = '=';
258 out[2] = '=';
259 out[3] = '=';
260 }
261 m_source->write_array(out, sizeof(*out), _countof(out));
262 }
263
264 protected:
265 size_t
266 m_max_blocks,
268 };
269
274 {
275 public:
279 base64_dec() noexcept : m_num(0)
280 {
281 m_buf[0] = 0;
282 m_buf[1] = 0;
283 m_buf[2] = 0;
284 m_buf[3] = 0;
285 }
286
295 template<class T_to, class AX, class T_from>
296 void decode(_Inout_ std::vector<T_to, AX> &out, _Out_ bool &is_last, _In_z_count_(size) const T_from *data, _In_ size_t size)
297 {
298 is_last = false;
299
300 // Trim data size to first terminator.
301 for (size_t k = 0; k < size; k++)
302 if (!data[k]) { size = k; break; }
303
304 // Preallocate output
305 out.reserve(out.size() + dec_size(size));
306
307 for (size_t i = 0;; i++) {
308 if (m_num >= 4) {
309 // Buffer full; decode it.
310 size_t nibbles = decode(out);
311 if (nibbles < 3) {
312 is_last = true;
313 break;
314 }
315 }
316
317 if (i >= size)
318 break;
319
320 size_t x = static_cast<size_t>(data[i]);
321 if ((m_buf[m_num] = x < _countof(base64_dec_lookup) ? base64_dec_lookup[x] : 255) != 255)
322 m_num++;
323 }
324 }
325
329 void clear() noexcept
330 {
331 m_num = 0;
332 }
333
341 size_t dec_size(_In_ size_t size) const noexcept
342 {
343 return ((m_num + size + 3)/4)*3;
344 }
345
346 protected:
350 template<class T, class AX>
351 size_t decode(_Inout_ std::vector<T, AX> &out)
352 {
353 m_num = 0;
354 out.push_back((T)(((m_buf[0] << 2) | (m_buf[1] >> 4)) & 0xff));
355 if (m_buf[2] < 64) {
356 out.push_back((T)(((m_buf[1] << 4) | (m_buf[2] >> 2)) & 0xff));
357 if (m_buf[3] < 64) {
358 out.push_back((T)(((m_buf[2] << 6) | m_buf[3]) & 0xff));
359 return 3;
360 } else
361 return 2;
362 } else
363 return 1;
364 }
365
366 protected:
367 uint8_t m_buf[4];
368 size_t m_num;
369 };
370
371#pragma warning(push)
372#pragma warning(disable: 26495)
373
378 {
379 public:
380 base64_reader(_Inout_ stdex::stream::basic& source) :
382 m_temp_off(0),
383 m_temp_len(0)
384 {}
385
386#pragma warning(suppress: 6101) // See [1] below
387 virtual _Success_(return != 0 || length == 0) size_t read(
388 _Out_writes_bytes_to_opt_(length, return) void* data, _In_ size_t length)
389 {
390 _Assume_(data || !length);
391 for (size_t to_read = length;;) {
392 if (m_temp_len >= to_read) {
393 memcpy(data, m_temp + m_temp_off, to_read);
394 m_temp_off += to_read;
395 m_temp_len -= to_read;
396 m_state = stdex::stream::state_t::ok;
397 return length;
398 }
399 if (m_temp_len) {
400 memcpy(data, m_temp + m_temp_off, m_temp_len);
401 reinterpret_cast<uint8_t*&>(data) += m_temp_len;
402 to_read -= m_temp_len;
403 m_temp_off = 0;
404 m_temp_len = 0;
405 }
406 // Read one Base64 block (4 chars)
407 while (m_num < 4) {
408 uint8_t x;
409 *m_source >> x;
410 if (!m_source->ok()) _Unlikely_ {
411 m_state = m_source->state();
412 return length - to_read; // [1] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
413 }
414 if ((m_buf[m_num] = base64_dec_lookup[x]) != 255)
415 m_num++;
416 }
417 decode();
418 if (m_temp_len < 3 && to_read >= 3) {
419 // If Base64 indicates end of data, truncate read to hint the client, end of Base64 data has been reached.
420 memcpy(data, m_temp + m_temp_off, m_temp_len);
421 m_temp_off = 0;
422 m_temp_len = 0;
423 to_read -= m_temp_len;
424 m_state = stdex::stream::state_t::ok;
425 return length - to_read; // [1] Code analysis misses `length - to_read` bytes were written to data in previous loop iterations.
426 }
427 }
428 }
429
430 protected:
434 void decode()
435 {
436 m_num = 0;
437 m_temp_off = 0;
438 m_temp[0] = static_cast<char>(((m_buf[0] << 2) | (m_buf[1] >> 4)) & 0xff);
439 if (m_buf[2] < 64) {
440 m_temp[1] = static_cast<char>(((m_buf[1] << 4) | (m_buf[2] >> 2)) & 0xff);
441 if (m_buf[3] < 64) {
442 m_temp[2] = static_cast<char>(((m_buf[2] << 6) | m_buf[3]) & 0xff);
443 m_temp_len = 3;
444 } else
445 m_temp_len = 2;
446 } else
447 m_temp_len = 1;
448 }
449
450 protected:
451 char m_temp[3];
452 size_t
455 };
456
457#pragma warning(pop)
458}
459
460#if defined(__GNUC__)
461#pragma GCC diagnostic pop
462#endif
Base64 decoding session.
Definition base64.hpp:274
size_t m_num
Number of bytes used in m_buf
Definition base64.hpp:368
size_t decode(std::vector< T, AX > &out)
Decodes one complete internal buffer of data.
Definition base64.hpp:351
base64_dec() noexcept
Constructs blank decoding session.
Definition base64.hpp:279
size_t dec_size(size_t size) const noexcept
Returns maximum decoded size.
Definition base64.hpp:341
void clear() noexcept
Resets decoding session.
Definition base64.hpp:329
void decode(std::vector< T_to, AX > &out, bool &is_last, const T_from *data, size_t size)
Decodes one block of information, and appends it to the output.
Definition base64.hpp:296
uint8_t m_buf[4]
Internal buffer.
Definition base64.hpp:367
Base64 encoding session.
Definition base64.hpp:54
void encode(std::basic_string< T, TR, AX > &out, size_t size)
Encodes partial internal buffer of data.
Definition base64.hpp:139
void encode(std::basic_string< T, TR, AX > &out)
Encodes one complete internal buffer of data.
Definition base64.hpp:127
void encode(std::basic_string< T, TR, AX > &out, const void *data, size_t size, bool is_last=true)
Encodes one block of information, and appends it to the output.
Definition base64.hpp:75
size_t m_num
Number of bytes used in m_buf
Definition base64.hpp:167
uint8_t m_buf[3]
Internal buffer.
Definition base64.hpp:166
base64_enc() noexcept
Constructs blank encoding session.
Definition base64.hpp:59
void clear() noexcept
Resets encoding session.
Definition base64.hpp:105
size_t enc_size(size_t size) const noexcept
Returns maximum encoded size.
Definition base64.hpp:117
Converts from Base64 when reading from a stream.
Definition base64.hpp:378
void decode()
Decodes one complete internal buffer of data.
Definition base64.hpp:434
char m_temp[3]
Temporary buffer.
Definition base64.hpp:451
size_t m_temp_len
Number of bytes of data in m_temp
Definition base64.hpp:454
virtual size_t read(_Out_writes_bytes_to_opt_(length, return) void *data, size_t length)
Reads block of data from the stream.
Definition base64.hpp:387
size_t m_temp_off
Index of data start in m_temp
Definition base64.hpp:453
Converts to Base64 when writing to a stream.
Definition base64.hpp:174
size_t m_num_blocks
‍Maximum number of Base64 blocks (4 chars) to write without a line break (SIZE_MAX no line breaks)
Definition base64.hpp:267
void encode()
Encodes one complete internal buffer of data.
Definition base64.hpp:223
void encode(size_t size)
Encodes partial internal buffer of data.
Definition base64.hpp:236
virtual size_t write(_In_reads_bytes_opt_(length) const void *data, size_t length)
Writes block of data to the stream.
Definition base64.hpp:194
‍UTF-8 byte-order-mark
Definition stream.hpp:84
bool ok() const
Returns true if the stream state is clean i.e. previous operation was succesful.
Definition stream.hpp:180
state_t state() const
Returns stream state after last operation.
Definition stream.hpp:175
size_t write_array(_In_reads_bytes_opt_(size *count) const void *array, size_t size, size_t count)
Writes an array of data to the stream.
Definition stream.hpp:393
Modifies data on the fly when reading from/writing to a source stream. Could also be used to modify r...
Definition stream.hpp:1022