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