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 : template <typename ostream>
22 5423 : static inline void base64_encode(ostream &output, std::span<const std::byte> input)
23 : {
24 : static constexpr char encode_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
25 :
26 5423 : const auto *p_char = reinterpret_cast<const uint8_t *>(input.data());
27 : //
28 : //- +3 means 3 bytes are being processed in one iteration (3 * 8 = 24 bits)
29 : //
30 2189314 : for (size_t idx = 3; idx <= input.size(); idx += 3)
31 : {
32 2183891 : auto temp = uint32_t(*p_char++) << 16U;
33 2183891 : temp += uint32_t(*p_char++) << 8U;
34 2183891 : temp += (*p_char++);
35 2183891 : output.write(encode_table[(temp & 0x00FC0000U) >> 18U]);
36 2183891 : output.write(encode_table[(temp & 0x0003F000U) >> 12U]);
37 2183891 : output.write(encode_table[(temp & 0x00000FC0U) >> 6U]);
38 2183891 : output.write(encode_table[(temp & 0x0000003FU)]);
39 : }
40 5423 : switch (input.size() % 3)
41 : {
42 1884 : case 1:
43 : {
44 1884 : auto temp = uint32_t(*p_char++) << 16U;
45 1884 : output.write(encode_table[(temp & 0x00FC0000U) >> 18U]);
46 1884 : output.write(encode_table[(temp & 0x0003F000U) >> 12U]);
47 1884 : output.write('=');
48 1884 : output.write('=');
49 : }
50 1884 : break;
51 1810 : case 2:
52 : {
53 1810 : auto temp = uint32_t(*p_char++) << 16U;
54 1810 : temp += uint32_t(*p_char++) << 8U;
55 1810 : output.write(encode_table[(temp & 0x00FC0000) >> 18]);
56 1810 : output.write(encode_table[(temp & 0x0003F000) >> 12]);
57 1810 : output.write(encode_table[(temp & 0x00000FC0) >> 6]);
58 1810 : output.write('=');
59 : }
60 1810 : break;
61 : }
62 5423 : }
63 :
64 : template <typename istream>
65 2715 : static inline void base64_decode_string(spb::detail::proto_field_bytes auto &output, istream &stream,
66 : size_t max_output_size = 0)
67 : {
68 : static constexpr uint8_t decode_table[256] = {
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, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
71 : 128, 128, 128, 62, 128, 128, 128, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128,
72 : 128, 128, 128, 128, 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
73 : 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 128, 128, 128, 128, 128, 128, 26, 27, 28,
74 : 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
75 : 49, 50, 51, 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, 128, 128, 128, 128,
81 : 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128};
82 :
83 : /*static constexpr uint8_t decode_table2[] = {
84 : 62, 128, 128, 128, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 128, 128, 128, 128, 128, 128,
85 : 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,
86 : 24, 25, 128, 128, 128, 128, 128, 128, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
87 : 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
88 : };*/
89 :
90 : if constexpr (spb::detail::proto_field_bytes_resizable<decltype(output)>)
91 2697 : output.clear();
92 :
93 2715 : if (stream.current_char() != '"') [[unlikely]]
94 9 : throw std::runtime_error("expecting '\"'");
95 :
96 2706 : stream.consume_current_char(false);
97 2706 : if (stream.consume('"'))
98 5 : return;
99 :
100 2701 : auto mask = uint8_t(0);
101 :
102 18795 : for (auto out_index = size_t(0);;)
103 : {
104 18795 : auto view = stream.view(UINT32_MAX);
105 18794 : auto length = view.find('"');
106 18794 : auto end_found = length < view.npos;
107 18794 : if ((end_found && length % 4 != 0) || view.size() <= 4) [[unlikely]]
108 7 : throw std::runtime_error("invalid base64");
109 :
110 18787 : length = std::min(length, view.size());
111 :
112 : //- align to 4 bytes
113 18787 : auto aligned_length = length & ~3;
114 18787 : if (aligned_length > 4) [[likely]]
115 : {
116 18691 : auto out_length = ((aligned_length - 4) / 4) * 3;
117 18691 : view = view.substr(0, aligned_length);
118 :
119 : if constexpr (spb::detail::proto_field_bytes_resizable<decltype(output)>)
120 : {
121 18660 : if (max_output_size && (output.size() + out_length > max_output_size)) [[unlikely]]
122 0 : throw std::length_error("bytes is too large");
123 :
124 18660 : output.resize(output.size() + out_length);
125 : }
126 : else
127 : {
128 31 : if (out_length > (output.size() - out_index)) [[unlikely]]
129 0 : throw std::runtime_error("too large base64");
130 : }
131 :
132 18691 : auto *p_out = output.data() + out_index;
133 18691 : const auto *p_in = reinterpret_cast<const uint8_t *>(view.data());
134 18691 : const auto *p_end = p_in + aligned_length - 4; //- exclude the last 4 chars (possible padding)
135 :
136 1110621 : while (p_in < p_end) [[likely]]
137 : {
138 1091930 : uint8_t v0 = decode_table[*p_in++];
139 1091930 : uint8_t v1 = decode_table[*p_in++];
140 1091930 : uint8_t v2 = decode_table[*p_in++];
141 1091930 : uint8_t v3 = decode_table[*p_in++];
142 1091930 : mask |= (v0 | v1 | v2 | v3);
143 :
144 1091930 : *p_out++ = std::byte((v0 << 2) | (v1 >> 4));
145 1091930 : *p_out++ = std::byte((v1 << 4) | (v2 >> 2));
146 1091930 : *p_out++ = std::byte((v2 << 6) | (v3));
147 :
148 1091930 : out_index += 3;
149 : }
150 18691 : auto consumed_bytes = p_in - reinterpret_cast<const uint8_t *>(view.data());
151 18691 : view.remove_prefix(consumed_bytes);
152 18691 : stream.skip(consumed_bytes);
153 : }
154 :
155 18787 : if (end_found)
156 : {
157 : //- handle padding
158 2693 : const auto *p_in = reinterpret_cast<const uint8_t *>(view.data());
159 :
160 2693 : uint8_t v0 = decode_table[*p_in++];
161 2693 : uint8_t v1 = decode_table[*p_in++];
162 2693 : auto i1 = *p_in++;
163 2693 : uint8_t v2 = i1 == '=' ? 0 : decode_table[i1];
164 2693 : auto i2 = *p_in++;
165 2693 : uint8_t v3 = i2 == '=' ? 0 : decode_table[i2];
166 2693 : mask |= (v0 | v1 | v2 | v3);
167 2693 : mask |= ((i1 == '=') & (i2 != '=')) ? 128 : 0;
168 2693 : if (mask & 128) [[unlikely]]
169 4 : throw std::runtime_error("invalid base64");
170 :
171 2689 : auto padding_size = (i1 == '=' ? 1 : 0) + (i2 == '=' ? 1 : 0);
172 2689 : auto consumed_bytes = 3 - padding_size;
173 : //- +1 is for "
174 2689 : stream.skip(5);
175 : if constexpr (spb::detail::proto_field_bytes_resizable<decltype(output)>)
176 : {
177 2671 : if (max_output_size && (output.size() + consumed_bytes > max_output_size))
178 8 : throw std::length_error("bytes is too large");
179 :
180 2663 : output.resize(output.size() + consumed_bytes);
181 : }
182 : else
183 : {
184 18 : if (output.size() != out_index + consumed_bytes) [[unlikely]]
185 3 : throw std::runtime_error("too large base64");
186 : }
187 2678 : auto *p_out = output.data() + out_index;
188 2678 : if (padding_size == 0)
189 : {
190 860 : *p_out++ = std::byte((v0 << 2) | (v1 >> 4));
191 860 : *p_out++ = std::byte((v1 << 4) | (v2 >> 2));
192 860 : *p_out++ = std::byte((v2 << 6) | (v3));
193 : }
194 1818 : else if (padding_size == 1)
195 : {
196 892 : *p_out++ = std::byte((v0 << 2) | (v1 >> 4));
197 892 : *p_out++ = std::byte((v1 << 4) | (v2 >> 2));
198 : }
199 926 : else if (padding_size == 2)
200 : {
201 926 : *p_out++ = std::byte((v0 << 2) | (v1 >> 4));
202 : }
203 2678 : return;
204 : }
205 : }
206 : }
207 : } // namespace spb::json::detail
|