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