Branch data Line data Source code
1 : : // Copyright 2011-2026 David Robillard <d@drobilla.net> 2 : : // SPDX-License-Identifier: ISC 3 : : 4 : : #include "char_utils.h" 5 : : #include "macros.h" 6 : : #include "read_utils.h" 7 : : #include "result.h" 8 : : #include "write_utils.h" 9 : : 10 : : #include <exess/exess.h> 11 : : 12 : : #include <assert.h> 13 : : #include <stdint.h> 14 : : #include <string.h> 15 : : 16 : : // Map a 6-bit base64 group to a base64 digit 17 : : EXESS_PURE_FUNC static inline uint8_t 18 : 43647 : map(const unsigned group) 19 : : { 20 [ - + ]: 43647 : assert(group < 64); 21 : : 22 : : // See <http://tools.ietf.org/html/rfc3548#section-3>. 23 : 43647 : EXESS_CONSTEXPR uint8_t b64_map[] = 24 : : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 25 : : 26 : 43647 : return b64_map[group]; 27 : : } 28 : : 29 : : // Unmap a base64 digit to the numeric value used for decoding 30 : : static inline uint8_t 31 : 65490 : unmap(const uint8_t in) 32 : : { 33 : : /* Table indexed by encoded characters that contains the numeric value used 34 : : for decoding, shifted up by 47 to be in the range of printable ASCII. A 35 : : '$' is a placeholder for characters not in the base64 alphabet. */ 36 : 65490 : EXESS_CONSTEXPR uint8_t b64_unmap[] = 37 : : "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$m$$$ncdefghijkl$$$$$$" 38 : : "$/0123456789:;<=>?@ABCDEFGH$$$$$$IJKLMNOPQRSTUVWXYZ[\\]^_`ab$$$$" 39 : : "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" 40 : : "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"; 41 : : 42 : 65490 : return (uint8_t)(b64_unmap[in] - 47U); 43 : : } 44 : : 45 : : EXESS_NONBLOCKING size_t 46 : 13 : exess_decoded_base64_size(const size_t length) 47 : : { 48 : 13 : return ((length * 3) / 4) + 2; 49 : : } 50 : : 51 : : EXESS_NONBLOCKING ExessVariableResult 52 : 296 : exess_read_base64(const char* const str, const size_t out_size, void* const out) 53 : : { 54 : 296 : uint8_t* const uout = (uint8_t*)out; 55 : 296 : const uint8_t* const ustr = (const uint8_t*)str; 56 : 296 : size_t i = skip_whitespace(str); 57 : 296 : size_t o = 0U; 58 : : 59 [ + + ]: 11307 : while (str[i]) { 60 : : // Read next chunk of 4 input characters 61 : 11026 : uint8_t in[] = {'=', '=', '=', '='}; 62 [ + + ]: 55100 : for (size_t j = 0; j < 4; ++j) { 63 [ + + ]: 44086 : if (!is_base64(str[i])) { 64 : 12 : return VRESULT(EXESS_EXPECTED_BASE64, i, o); 65 : : } 66 : : 67 : 44074 : in[j] = ustr[i++]; 68 : 44074 : i += skip_whitespace(str + i); 69 : : } 70 : : 71 [ + + + + : 11014 : if (in[0] == '=' || in[1] == '=' || (in[2] == '=' && in[3] != '=')) { + + + + ] 72 : 3 : return VRESULT(EXESS_BAD_VALUE, i, o); 73 : : } 74 : : 75 [ + + ]: 11011 : const size_t n_bytes = 1U + (in[2] != '=') + (in[3] != '='); 76 [ + + ]: 11011 : if (o + n_bytes > out_size) { 77 : 9 : o += n_bytes; 78 : : } else { 79 : 11002 : const uint8_t a1 = (uint8_t)(unmap(in[0]) << 2U); 80 : 11002 : const uint8_t a2 = unmap(in[1]) >> 4U; 81 : : 82 : 11002 : uout[o++] = a1 | a2; 83 : : 84 [ + + ]: 11002 : if (in[2] != '=') { 85 : 10916 : const uint8_t b1 = (uint8_t)(((unsigned)unmap(in[1]) << 4U) & 0xF0U); 86 : 10916 : const uint8_t b2 = unmap(in[2]) >> 2U; 87 : : 88 : 10916 : uout[o++] = b1 | b2; 89 : : } 90 : : 91 [ + + ]: 11002 : if (in[3] != '=') { 92 : 10827 : const uint8_t c1 = (uint8_t)(((unsigned)unmap(in[2]) << 6U) & 0xC0U); 93 : 10827 : const uint8_t c2 = unmap(in[3]); 94 : : 95 : 10827 : uout[o++] = c1 | c2; 96 : : } 97 : : } 98 : : } 99 : : 100 [ + + ]: 281 : return VRESULT(o <= out_size ? EXESS_SUCCESS : EXESS_NO_SPACE, i, o); 101 : : } 102 : : 103 : : EXESS_NONBLOCKING ExessResult 104 : 523 : exess_write_base64(const size_t data_size, 105 : : const void* const data, 106 : : const size_t buf_size, 107 : : char* const buf) 108 : : { 109 : 523 : const size_t length = (data_size + 2) / 3 * 4; 110 [ + + + + ]: 523 : if (!buf || length >= buf_size) { 111 : 259 : return end_write(EXESS_NO_SPACE, buf_size, buf, length); 112 : : } 113 : : 114 : 264 : uint8_t* const out = (uint8_t*)buf; 115 : : 116 : 264 : size_t o = 0; 117 [ + + ]: 11241 : for (size_t i = 0; i < data_size; i += 3, o += 4) { 118 : 10977 : uint8_t in[4] = {0, 0, 0, 0}; 119 : 10977 : const size_t n_in = MIN(3, data_size - i); 120 : 10977 : memcpy(in, (const uint8_t*)data + i, n_in); 121 : : 122 : 10977 : out[o] = map(in[0] >> 2U); 123 : 10977 : out[o + 1] = map(((in[0] & 0x03U) << 4U) | ((in[1] & 0xF0U) >> 4U)); 124 [ + + ]: 10977 : out[o + 2] = 125 : 10890 : ((n_in > 1U) ? map(((in[1] & 0x0FU) << 2U) | ((in[2] & 0xC0U) >> 6U)) 126 : : : '='); 127 : : 128 [ + + ]: 10977 : out[o + 3] = ((n_in > 2U) ? map(in[2] & 0x3FU) : '='); 129 : : } 130 : : 131 : 264 : return end_write(EXESS_SUCCESS, buf_size, buf, o); 132 : : }