Line data Source code
1 : /***************************************************************************\
2 : * Name : serialize library for json *
3 : * Description : all json serialization functions *
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 :
15 : #include "../to_from_chars.h"
16 : #include "base64.h"
17 : #include "spb/json/deserialize.hpp"
18 : #include "spb/utf8.h"
19 : #include <algorithm>
20 : #include <array>
21 : #include <cctype>
22 : #include <charconv>
23 : #include <cinttypes>
24 : #include <cstddef>
25 : #include <cstring>
26 : #include <map>
27 : #include <memory>
28 : #include <spb/io/io.hpp>
29 : #include <stdexcept>
30 : #include <string>
31 : #include <string_view>
32 : #include <sys/types.h>
33 : #include <type_traits>
34 :
35 : namespace spb::json::detail
36 : {
37 : struct ostream
38 : {
39 : private:
40 : size_t bytes_written = 0;
41 : spb::io::writer on_write;
42 :
43 : public:
44 : //- flag if put ',' before value
45 : bool put_comma = false;
46 :
47 : /**
48 : * @brief Construct a new ostream object
49 : *
50 : * @param writer if null, stream will skip all writes but will still count number of written
51 : * chars
52 : */
53 6969 : explicit ostream( spb::io::writer writer ) noexcept
54 6969 : : on_write( writer )
55 : {
56 6969 : }
57 :
58 8756274 : void write( char c ) noexcept
59 : {
60 8756274 : if( on_write )
61 : {
62 4377405 : on_write( &c, sizeof( c ) );
63 : }
64 :
65 8756274 : bytes_written += sizeof( c );
66 8756274 : }
67 :
68 20 : void write_unicode( uint32_t codepoint )
69 : {
70 20 : if( codepoint <= 0xffff )
71 : {
72 15 : char buffer[ 8 ] = { };
73 15 : auto size = snprintf( buffer, sizeof( buffer ), "\\u%04" PRIx32, codepoint );
74 15 : return write( std::string_view( buffer, size ) );
75 : }
76 5 : if( codepoint <= 0x10FFFF )
77 : {
78 5 : codepoint -= 0x10000;
79 :
80 5 : auto high = static_cast< uint16_t >( ( codepoint >> 10 ) + 0xD800 );
81 5 : auto low = static_cast< uint16_t >( ( codepoint & 0x3FF ) + 0xDC00 );
82 5 : char buffer[ 16 ] = { };
83 : auto size =
84 5 : snprintf( buffer, sizeof( buffer ), "\\u%04" PRIx16 "\\u%04" PRIx16, high, low );
85 5 : return write( std::string_view( buffer, size ) );
86 : }
87 0 : throw std::invalid_argument( "invalid utf8" );
88 : }
89 :
90 4599 : void write( std::string_view str )
91 : {
92 4599 : if( on_write )
93 : {
94 1859 : on_write( str.data( ), str.size( ) );
95 : }
96 :
97 4599 : bytes_written += str.size( );
98 4599 : }
99 :
100 1688 : void write_escaped( std::string_view str )
101 : {
102 1688 : if( !has_escape_chars( str ) )
103 : {
104 1644 : write( str );
105 1644 : return;
106 : }
107 :
108 : using namespace std::literals;
109 44 : uint32_t codepoint = 0;
110 44 : uint32_t state = spb::detail::utf8::ok;
111 44 : bool decoding_utf8 = false;
112 285 : for( uint8_t c : str )
113 : {
114 241 : if( decoding_utf8 )
115 : {
116 30 : if( spb::detail::utf8::decode_point( &state, &codepoint, c ) ==
117 : spb::detail::utf8::ok )
118 : {
119 15 : write_unicode( codepoint );
120 15 : decoding_utf8 = false;
121 : }
122 30 : continue;
123 : }
124 211 : if( is_escape( c ) )
125 : {
126 128 : switch( c )
127 : {
128 21 : case '"':
129 21 : write( R"(\")"sv );
130 21 : break;
131 14 : case '\\':
132 14 : write( R"(\\)"sv );
133 14 : break;
134 9 : case '\b':
135 9 : write( R"(\b)"sv );
136 9 : break;
137 9 : case '\f':
138 9 : write( R"(\f)"sv );
139 9 : break;
140 14 : case '\n':
141 14 : write( R"(\n)"sv );
142 14 : break;
143 14 : case '\r':
144 14 : write( R"(\r)"sv );
145 14 : break;
146 26 : case '\t':
147 26 : write( R"(\t)"sv );
148 26 : break;
149 21 : default:
150 21 : decoding_utf8 = true;
151 21 : if( spb::detail::utf8::decode_point( &state, &codepoint, c ) ==
152 : spb::detail::utf8::ok )
153 : {
154 5 : write_unicode( codepoint );
155 5 : decoding_utf8 = false;
156 : }
157 : }
158 : }
159 : else
160 : {
161 83 : write( c );
162 : }
163 : }
164 44 : if( state != spb::detail::utf8::ok )
165 : {
166 1 : throw std::runtime_error( "invalid utf8" );
167 : }
168 : }
169 :
170 : void serialize( std::string_view key, const auto & value );
171 : void serialize( std::string_view value );
172 :
173 4407 : [[nodiscard]] auto size( ) const noexcept -> size_t
174 : {
175 4407 : return bytes_written;
176 : }
177 :
178 : private:
179 9275 : static auto is_escape( uint8_t c ) -> bool
180 : {
181 : static constexpr std::string_view escape_chars = "\\\"\b\f\n\r\t<>";
182 9275 : return c <= 0x1f || c >= 0x7f || escape_chars.find( c ) != std::string_view::npos;
183 : }
184 :
185 1688 : static auto has_escape_chars( std::string_view str ) -> bool
186 : {
187 1688 : return std::any_of( str.begin( ), str.end( ), is_escape );
188 : }
189 : };
190 :
191 : using namespace std::literals;
192 :
193 3939 : static inline void serialize_key( ostream & stream, std::string_view key )
194 : {
195 3939 : if( stream.put_comma )
196 : {
197 293 : stream.write( ',' );
198 : }
199 3939 : stream.put_comma = true;
200 :
201 3939 : if( !key.empty( ) )
202 : {
203 1425 : stream.write( '"' );
204 1425 : stream.write_escaped( key );
205 1425 : stream.write( R"(":)"sv );
206 : }
207 3939 : }
208 :
209 : static inline void serialize( ostream & stream, std::string_view key, const bool & value );
210 : static inline void serialize( ostream & stream, std::string_view key,
211 : const spb::detail::proto_field_int_or_float auto & value );
212 : static inline void serialize( ostream & stream, std::string_view key,
213 : const spb::detail::proto_message auto & value );
214 : static inline void serialize( ostream & stream, std::string_view key,
215 : const spb::detail::proto_enum auto & value );
216 : static inline void serialize( ostream & stream, std::string_view key,
217 : const spb::detail::proto_field_string auto & value );
218 : static inline void serialize( ostream & stream, std::string_view key,
219 : const spb::detail::proto_field_bytes auto & value );
220 : static inline void serialize( ostream & stream, std::string_view key,
221 : const spb::detail::proto_label_repeated auto & value );
222 : static inline void serialize( ostream & stream, std::string_view key,
223 : const spb::detail::proto_label_repeated_fixed_size auto & value );
224 : template < typename keyT, typename valueT >
225 : static inline void serialize( ostream & stream, std::string_view key,
226 : const std::map< keyT, valueT > & map );
227 :
228 : static inline void serialize( ostream & stream, bool value );
229 : static inline void serialize( ostream & stream, spb::detail::proto_field_int_or_float auto value );
230 : static inline void serialize( ostream & stream,
231 : const spb::detail::proto_field_string auto & value );
232 :
233 76 : static inline void serialize( ostream & stream, bool value )
234 : {
235 76 : stream.write( value ? "true"sv : "false"sv );
236 76 : }
237 :
238 263 : static inline void serialize( ostream & stream, const std::string_view & value )
239 : {
240 263 : stream.write( '"' );
241 263 : stream.write_escaped( value );
242 262 : stream.write( '"' );
243 262 : }
244 :
245 159 : static inline void serialize( ostream & stream, const spb::detail::proto_field_string auto & value )
246 : {
247 159 : serialize( stream, std::string_view( value.data( ), value.size( ) ) );
248 158 : }
249 :
250 1319 : static inline void serialize( ostream & stream, spb::detail::proto_field_int_or_float auto value )
251 : {
252 1319 : auto buffer = std::array< char, 32 >( );
253 :
254 3128 : auto result = spb_std_emu::to_chars( buffer.data( ), buffer.data( ) + sizeof( buffer ), value );
255 1319 : stream.write(
256 3957 : std::string_view( buffer.data( ), static_cast< size_t >( result.ptr - buffer.data( ) ) ) );
257 1319 : }
258 :
259 76 : static inline void serialize( ostream & stream, std::string_view key, const bool & value )
260 : {
261 76 : serialize_key( stream, key );
262 76 : serialize( stream, value );
263 76 : }
264 :
265 1311 : static inline void serialize( ostream & stream, std::string_view key,
266 : const spb::detail::proto_field_int_or_float auto & value )
267 : {
268 1311 : serialize_key( stream, key );
269 1311 : serialize( stream, value );
270 1311 : }
271 :
272 164 : static inline void serialize( ostream & stream, std::string_view key,
273 : const spb::detail::proto_field_string auto & value )
274 : {
275 164 : if( !value.empty( ) )
276 : {
277 159 : serialize_key( stream, key );
278 159 : serialize( stream, value );
279 : }
280 163 : }
281 :
282 134 : static inline void serialize( ostream & stream, std::string_view key,
283 : const spb::detail::proto_field_bytes auto & value )
284 : {
285 134 : if( !value.empty( ) )
286 : {
287 119 : serialize_key( stream, key );
288 119 : stream.write( '"' );
289 119 : base64_encode( stream, value );
290 119 : stream.write( '"' );
291 : }
292 134 : }
293 693 : static inline void serialize( ostream & stream, std::string_view key,
294 : const spb::detail::proto_label_repeated auto & value )
295 : {
296 693 : if( value.empty( ) )
297 : {
298 270 : return;
299 : }
300 :
301 423 : serialize_key( stream, key );
302 423 : stream.write( '[' );
303 423 : stream.put_comma = false;
304 1076 : for( const auto & v : value )
305 : {
306 : if constexpr( std::is_same_v< typename std::decay_t< decltype( value ) >::value_type,
307 : bool > )
308 : {
309 34 : serialize( stream, { }, bool( v ) );
310 : }
311 : else
312 : {
313 619 : serialize( stream, { }, v );
314 : }
315 : }
316 423 : stream.write( ']' );
317 423 : stream.put_comma = true;
318 : }
319 :
320 10 : static inline void serialize( ostream & stream, std::string_view key,
321 : const spb::detail::proto_label_repeated_fixed_size auto & value )
322 : {
323 10 : serialize_key( stream, key );
324 10 : stream.write( '[' );
325 10 : stream.put_comma = false;
326 60 : for( size_t i = 0; i < value.size( ); i++ )
327 : {
328 : if constexpr( std::is_same_v< typename std::decay_t< decltype( value ) >::value_type,
329 : bool > )
330 : {
331 : serialize( stream, { }, bool( value[ i ] ) );
332 : }
333 : else
334 : {
335 40 : serialize( stream, { }, value[ i ] );
336 : }
337 : }
338 10 : stream.write( ']' );
339 10 : stream.put_comma = true;
340 10 : }
341 :
342 : static constexpr std::string_view no_name = { };
343 :
344 : template < typename keyT, typename valueT >
345 20 : static inline void serialize( ostream & stream, std::string_view key,
346 : const std::map< keyT, valueT > & map )
347 : {
348 20 : if( map.empty( ) )
349 : {
350 8 : return;
351 : }
352 12 : serialize_key( stream, key );
353 12 : stream.write( '{' );
354 12 : stream.put_comma = false;
355 26 : for( auto & [ map_key, map_value ] : map )
356 : {
357 : if constexpr( std::is_same_v< keyT, std::string_view > ||
358 : std::is_same_v< keyT, std::string > )
359 : {
360 6 : serialize_key( stream, map_key );
361 : }
362 : else
363 : {
364 8 : if( stream.put_comma )
365 : {
366 2 : stream.write( ',' );
367 : }
368 :
369 8 : stream.write( '"' );
370 8 : serialize( stream, map_key );
371 8 : stream.write( R"(":)"sv );
372 : }
373 14 : stream.put_comma = false;
374 14 : serialize( stream, no_name, map_value );
375 14 : stream.put_comma = true;
376 : }
377 12 : stream.write( '}' );
378 12 : stream.put_comma = true;
379 : }
380 :
381 421 : static inline void serialize( ostream & stream, std::string_view key,
382 : const spb::detail::proto_label_optional auto & p_value )
383 : {
384 421 : if( p_value.has_value( ) )
385 : {
386 319 : return serialize( stream, key, *p_value );
387 : }
388 : }
389 :
390 : template < typename T >
391 0 : static inline void serialize( ostream & stream, std::string_view key,
392 : const std::unique_ptr< T > & p_value )
393 : {
394 0 : if( p_value )
395 : {
396 0 : return serialize( stream, key, *p_value );
397 : }
398 : }
399 :
400 1719 : static inline void serialize( ostream & stream, std::string_view key,
401 : const spb::detail::proto_message auto & value )
402 : {
403 1719 : serialize_key( stream, key );
404 1719 : stream.write( '{' );
405 1719 : stream.put_comma = false;
406 :
407 : //
408 : //- serialize_value is generated by the spb-protoc
409 : //
410 1719 : serialize_value( stream, value );
411 1719 : stream.write( '}' );
412 1719 : stream.put_comma = true;
413 1719 : }
414 :
415 104 : static inline void serialize( ostream & stream, std::string_view key,
416 : const spb::detail::proto_enum auto & value )
417 : {
418 104 : serialize_key( stream, key );
419 :
420 : //
421 : //- serialize_value is generated by the spb-protoc
422 : //
423 104 : serialize_value( stream, value );
424 104 : }
425 :
426 1847 : static inline auto serialize( const auto & value, spb::io::writer on_write ) -> size_t
427 : {
428 1847 : auto stream = ostream( on_write );
429 1847 : serialize( stream, no_name, value );
430 3692 : return stream.size( );
431 : }
432 :
433 1779 : void ostream::serialize( std::string_view key, const auto & value )
434 : {
435 1779 : detail::serialize( *this, key, value );
436 1779 : }
437 :
438 104 : inline void ostream::serialize( std::string_view value )
439 : {
440 104 : detail::serialize( *this, value );
441 104 : }
442 :
443 : }// namespace spb::json::detail
|