Line data Source code
1 : /***************************************************************************\
2 : * Name : base64 library for json *
3 : * Description : RFC 4648 base64 decoder and encoder *
4 : * Author : antonin.kriz@gmail.com *
5 : * ------------------------------------------------------------------------- *
6 : * This is free software; you can redistribute it and/or modify it under the *
7 : * terms of the MIT license. A copy of the license can be found in the file *
8 : * "LICENSE" at the root of this distribution. *
9 : \***************************************************************************/
10 :
11 : #pragma once
12 :
13 : #include "../concepts.h"
14 : #include <cstddef>
15 : #include <cstdint>
16 : #include <span>
17 : #include <stdexcept>
18 :
19 : namespace spb::json::detail
20 : {
21 5502 : void base64_encode(auto &output, std::span<const std::byte> input)
22 : {
23 : static constexpr char encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
24 :
25 5502 : const auto *p_char = reinterpret_cast<const uint8_t *>(input.data());
26 : //
27 : //- +3 means 3 bytes are being processed in one iteration (3 * 8 = 24 bits)
28 : //
29 2189432 : for (size_t idx = 3; idx <= input.size(); idx += 3)
30 : {
31 2183930 : auto temp = uint32_t(*p_char++) << 16U;
32 2183930 : temp += uint32_t(*p_char++) << 8U;
33 2183930 : temp += (*p_char++);
34 2183930 : output.write(encode_table[(temp & 0x00FC0000U) >> 18U]);
35 2183930 : output.write(encode_table[(temp & 0x0003F000U) >> 12U]);
36 2183930 : output.write(encode_table[(temp & 0x00000FC0U) >> 6U]);
37 2183930 : output.write(encode_table[(temp & 0x0000003FU)]);
38 : }
39 5502 : switch (input.size() % 3)
40 : {
41 1928 : case 1:
42 : {
43 1928 : auto temp = uint32_t(*p_char++) << 16U;
44 1928 : output.write(encode_table[(temp & 0x00FC0000U) >> 18U]);
45 1928 : output.write(encode_table[(temp & 0x0003F000U) >> 12U]);
46 1928 : output.write('=');
47 1928 : output.write('=');
48 : }
49 1928 : break;
50 1832 : case 2:
51 : {
52 1832 : auto temp = uint32_t(*p_char++) << 16U;
53 1832 : temp += uint32_t(*p_char++) << 8U;
54 1832 : output.write(encode_table[(temp & 0x00FC0000) >> 18]);
55 1832 : output.write(encode_table[(temp & 0x0003F000) >> 12]);
56 1832 : output.write(encode_table[(temp & 0x00000FC0) >> 6]);
57 1832 : output.write('=');
58 : }
59 1832 : break;
60 : }
61 5502 : }
62 :
63 : template <typename istream>
64 2756 : void base64_decode_string(spb::detail::proto_field_bytes auto &output, istream &stream,
65 : size_t max_output_size = 0)
66 : {
67 : static constexpr uint8_t decode_table[256] = {
68 : 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
69 : 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
70 : 128, 128, 128, 62, 128, 128, 128, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128,
71 : 128, 128, 128, 128, 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
72 : 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 128, 128, 128, 128, 128, 128, 26, 27, 28,
73 : 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
74 : 49, 50, 51, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
75 : 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
76 : 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
77 : 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
78 : 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
79 : 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
80 : 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128};
81 :
82 : /*static constexpr uint8_t decode_table2[] = {
83 : 62, 128, 128, 128, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 128, 128, 128,
84 : 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
85 : 24, 25, 128, 128, 128, 128, 128, 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
86 : 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
87 : };*/
88 :
89 : if constexpr (spb::detail::proto_field_bytes_resizable<decltype(output)>)
90 2732 : output.clear();
91 :
92 2756 : if (!stream.consume('"')) [[unlikely]]
93 9 : throw std::runtime_error("expecting '\"'");
94 :
95 2747 : if (stream.consume_and_skip_white_space('"'))
96 5 : return;
97 :
98 2742 : auto mask = uint8_t(0);
99 :
100 2742 : for (auto out_index = size_t(0);;)
101 : {
102 2742 : auto view = stream.view(1, UINT32_MAX);
103 2741 : auto length = view.find('"');
104 2741 : auto end_found = length < view.npos;
105 2741 : if ((end_found && length % 4 != 0) || view.size() <= 4) [[unlikely]]
106 7 : throw std::runtime_error("invalid base64");
107 :
108 2734 : length = std::min(length, view.size());
109 :
110 : //- align to 4 bytes
111 2734 : auto aligned_length = length & ~3;
112 2734 : if (aligned_length > 4) [[likely]]
113 : {
114 2658 : auto out_length = ((aligned_length - 4) / 4) * 3;
115 2658 : view = view.substr(0, aligned_length);
116 :
117 : if constexpr (spb::detail::proto_field_bytes_resizable<decltype(output)>)
118 : {
119 2634 : if (max_output_size && (output.size() + out_length > max_output_size)) [[unlikely]]
120 0 : throw std::length_error("bytes is too large");
121 :
122 2634 : output.resize(output.size() + out_length);
123 : }
124 : else
125 : {
126 24 : if (out_length > (output.size() - out_index)) [[unlikely]]
127 0 : throw std::runtime_error("too large base64");
128 : }
129 :
130 2658 : auto *p_out = output.data() + out_index;
131 2658 : const auto *p_in = reinterpret_cast<const uint8_t *>(view.data());
132 2658 : const auto *p_end = p_in + aligned_length - 4; //- exclude the last 4 chars (possible padding)
133 :
134 1094616 : while (p_in < p_end) [[likely]]
135 : {
136 1091958 : uint8_t v0 = decode_table[*p_in++];
137 1091958 : uint8_t v1 = decode_table[*p_in++];
138 1091958 : uint8_t v2 = decode_table[*p_in++];
139 1091958 : uint8_t v3 = decode_table[*p_in++];
140 1091958 : mask |= (v0 | v1 | v2 | v3);
141 :
142 1091958 : *p_out++ = std::byte((v0 << 2) | (v1 >> 4));
143 1091958 : *p_out++ = std::byte((v1 << 4) | (v2 >> 2));
144 1091958 : *p_out++ = std::byte((v2 << 6) | (v3));
145 :
146 1091958 : out_index += 3;
147 : }
148 2658 : auto consumed_bytes = p_in - reinterpret_cast<const uint8_t *>(view.data());
149 2658 : view.remove_prefix(consumed_bytes);
150 2658 : stream.skip(consumed_bytes);
151 : }
152 :
153 2734 : if (end_found)
154 : {
155 : //- handle padding
156 2734 : const auto *p_in = reinterpret_cast<const uint8_t *>(view.data());
157 :
158 2734 : uint8_t v0 = decode_table[*p_in++];
159 2734 : uint8_t v1 = decode_table[*p_in++];
160 2734 : auto i1 = *p_in++;
161 2734 : uint8_t v2 = i1 == '=' ? 0 : decode_table[i1];
162 2734 : auto i2 = *p_in++;
163 2734 : uint8_t v3 = i2 == '=' ? 0 : decode_table[i2];
164 2734 : mask |= (v0 | v1 | v2 | v3);
165 2734 : mask |= ((i1 == '=') & (i2 != '=')) ? 128 : 0;
166 2734 : if (mask & 128) [[unlikely]]
167 4 : throw std::runtime_error("invalid base64");
168 :
169 2730 : auto padding_size = (i1 == '=' ? 1 : 0) + (i2 == '=' ? 1 : 0);
170 2730 : auto consumed_bytes = 3 - padding_size;
171 : //- +1 is for "
172 2730 : stream.skip(5);
173 : if constexpr (spb::detail::proto_field_bytes_resizable<decltype(output)>)
174 : {
175 2706 : if (max_output_size && (output.size() + consumed_bytes > max_output_size))
176 8 : throw std::length_error("bytes is too large");
177 :
178 2698 : output.resize(output.size() + consumed_bytes);
179 : }
180 : else
181 : {
182 24 : if (output.size() != out_index + consumed_bytes) [[unlikely]]
183 3 : throw std::runtime_error("too large base64");
184 : }
185 2719 : auto *p_out = output.data() + out_index;
186 2719 : if (padding_size == 0)
187 : {
188 863 : *p_out++ = std::byte((v0 << 2) | (v1 >> 4));
189 863 : *p_out++ = std::byte((v1 << 4) | (v2 >> 2));
190 863 : *p_out++ = std::byte((v2 << 6) | (v3));
191 : }
192 1856 : else if (padding_size == 1)
193 : {
194 906 : *p_out++ = std::byte((v0 << 2) | (v1 >> 4));
195 906 : *p_out++ = std::byte((v1 << 4) | (v2 >> 2));
196 : }
197 950 : else if (padding_size == 2)
198 : {
199 950 : *p_out++ = std::byte((v0 << 2) | (v1 >> 4));
200 : }
201 2719 : return;
202 : }
203 : }
204 : }
205 : } // namespace spb::json::detail
|