DAW JSON Link
Loading...
Searching...
No Matches
to_daw_json_string.h
Go to the documentation of this file.
1// Copyright (c) Darrell Wright
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5//
6// Official repository: https://github.com/beached/daw_json_link
7//
8
9#pragma once
10
12
19
20#include <daw/daw_algorithm.h>
21#include <daw/traits/daw_traits_is_explicitly_convertible.h>
22#include <daw/daw_arith_traits.h>
23#include <daw/daw_callable.h>
24#include <daw/daw_constant.h>
25#include <daw/daw_cpp_feature_check.h>
26#include <daw/daw_cxmath.h>
27#include <daw/daw_enable_requires.h>
28#include <daw/daw_likely.h>
29#include <daw/daw_move.h>
30#include <daw/daw_simple_array.h>
31#include <daw/daw_traits.h>
32#include <daw/daw_utility.h>
33#include <daw/daw_visit.h>
34#include <daw/third_party/dragonbox/dragonbox.h>
35#include <daw/utf8/unchecked.h>
36
37#include <array>
38#if defined( DAW_HAS_CPP20_CONCEPTS )
39#include <concepts>
40#endif
41#include <daw/stdinc/move_fwd_exch.h>
42#include <daw/stdinc/tuple_traits.h>
43#include <optional>
44#include <sstream>
45#include <string>
46#include <string_view>
47#include <type_traits>
48#include <variant>
49
50namespace daw::json {
51 inline namespace DAW_JSON_VER {
52 namespace json_details {
53 template<options::FPOutputFormat fp_output_format,
54 unsigned Precision = daw::max_value<unsigned>, typename Real,
55 typename WriteableType>
56 constexpr WriteableType to_chars( Real const &value,
57 WriteableType out_it );
58 } // namespace json_details
59
60 namespace json_details::to_strings {
61 using std::to_string;
62 // Need to use ADL to_string in unevaluated contexts. Limiting to it's
63 // own namespace
64 template<typename T>
65 [[nodiscard]] static constexpr auto to_string( std::optional<T> const &v )
66 -> decltype( to_string( *v ) ) {
67 if( not has_value( v ) ) {
68 return { "null" };
69 }
70 return to_string( *v );
71 }
72
73 DAW_JSON_MAKE_REQ_TRAIT( has_to_string_v,
74 to_string( std::declval<T>( ) ) );
75
76 } // namespace json_details::to_strings
77 namespace json_details {
79 has_from_string_v, from_string( std::declval<daw::tag_t<T>>( ),
80 std::declval<std::string_view>( ) ) );
81
83 has_ostream_op_v, operator<<( std::declval<std::stringstream &>( ),
84 std::declval<T const &>( ) ) );
85
87 has_istream_op_v, operator>>( std::declval<std::stringstream &>( ),
88 std::declval<T const &>( ) ) );
89 } // namespace json_details
90
91 /***
92 * This is the default ToJsonConverter for json_custom. By default is will
93 * return the stringified version of the value if, to_string( T ) exists.
94 * Otherwise it will fallback to an std::ostream converter for T if it
95 * exists.
96 * @tparam T type of value to convert to a string
97 */
98 template<typename T>
100 template<typename U>
101 [[nodiscard]] static std::string use_stream( U const &v ) {
102 std::stringstream ss{ };
103 ss << v;
104 return std::move( ss ).str( );
105 }
106
107 template<typename U>
108 [[nodiscard]] DAW_JSON_CPP23_STATIC_CALL_OP constexpr auto
109 operator( )( U const &value ) DAW_JSON_CPP23_STATIC_CALL_OP_CONST {
110 if constexpr( json_details::is_string_view_like_v<U> ) {
111 return std::string_view( std::data( value ), std::size( value ) );
112 } else if constexpr( json_details::to_strings::has_to_string_v<U> ) {
113 using json_details::to_strings::to_string;
114 return to_string( value );
115 } else if constexpr( json_details::has_ostream_op_v<U> ) {
116 return use_stream( value );
117 } else if constexpr( std::is_convertible_v<U, std::string_view> ) {
118 return static_cast<std::string_view>( value );
119 } else if constexpr( std::is_convertible_v<U, std::string> ) {
120 return static_cast<std::string>( value );
121 } else if constexpr( daw::is_arithmetic_v<U> ) {
122 return to_string( value );
123 } else if constexpr( std::is_enum_v<U> ) {
124 return to_string( static_cast<std::underlying_type_t<U>>( value ) );
125 } else if constexpr( concepts::is_nullable_value_v<U> ) {
126 using value_type = concepts::nullable_value_type_t<U>;
127 if constexpr( json_details::is_string_view_like_v<value_type> ) {
128 if constexpr( std::is_reference_v<DAW_TYPEOF(
129 concepts::nullable_value_read( value ) )> ) {
130 if( concepts::nullable_value_has_value( value ) ) {
131 return daw::string_view(
132 concepts::nullable_value_read( value ) );
133 }
134 return daw::string_view( "null" );
135 } else {
136 if( concepts::nullable_value_has_value( value ) ) {
137 auto const &v = concepts::nullable_value_read( value );
138 return std::string( std::data( v ), std::size( v ) );
139 }
140 return std::string( "null", 4 );
141 }
142 } else if constexpr( json_details::to_strings::has_to_string_v<
143 value_type> ) {
144 if( concepts::nullable_value_has_value( value ) ) {
145 using json_details::to_strings::to_string;
146 return to_string( concepts::nullable_value_read( value ) );
147 } else {
148 using result_t = DAW_TYPEOF(
149 to_string( concepts::nullable_value_read( value ) ) );
150 return result_t{ "null" };
151 }
152 } else if constexpr( std::is_convertible_v<value_type,
153 std::string_view> ) {
154 if( concepts::nullable_value_has_value( value ) ) {
155 return static_cast<std::string_view>(
156 concepts::nullable_value_read( value ) );
157 }
158 return std::string_view{ "null" };
159 } else if constexpr( std::is_convertible_v<value_type,
160 std::string> ) {
161 if( concepts::nullable_value_has_value( value ) ) {
162 return static_cast<std::string>(
163 concepts::nullable_value_read( value ) );
164 }
165 return std::string( "null" );
166 } else {
167 if( concepts::nullable_value_has_value( value ) ) {
168 return use_stream( concepts::nullable_value_has_value( value ) );
169 } else {
170 return std::string( "null" );
171 }
172 }
173 }
174 }
175 };
176
177 namespace from_json_conv_details {
178 template<typename T>
179 [[nodiscard]] static auto use_stream( std::string_view sv ) {
180 std::stringstream ss{ };
181 ss << sv;
182 T result;
183 ss >> result;
184 return result;
185 }
186 } // namespace from_json_conv_details
187
188 template<typename T>
190 [[nodiscard]] DAW_JSON_CPP23_STATIC_CALL_OP constexpr decltype( auto )
191 operator( )( std::string_view sv ) DAW_JSON_CPP23_STATIC_CALL_OP_CONST {
192 if constexpr( std::is_same_v<T, std::string_view> or
193 std::is_same_v<T, std::optional<std::string_view>> ) {
194 return sv;
195 } else if constexpr( json_details::has_from_string_v<T> ) {
196 return from_string( daw::tag<T>, sv );
197 } else if constexpr( std::is_convertible_v<std::string_view, T> ) {
198 return static_cast<T>( sv );
199 } else if constexpr( std::is_convertible_v<std::string, T> ) {
200 return static_cast<T>( static_cast<std::string>( sv ) );
201 } else {
202 static_assert( json_details::has_istream_op_v<T>,
203 "Unsupported type in default to converter. Must "
204 "supply a custom one" );
205 static_assert( std::is_default_constructible_v<T>,
206 "Unsupported type in default to converter. Must "
207 "supply a custom one" );
208 return from_json_conv_details::use_stream<T>( sv );
209 }
210 }
211 };
212
213 namespace json_details {
214
215 template<typename Char>
216 static constexpr char to_nibble_char( Char c ) {
217 auto const u = static_cast<unsigned>( static_cast<unsigned char>( c ) );
218 daw_json_ensure( u < 16, ErrorReason::InvalidUTFEscape );
219 if( u < 10 ) {
220 return static_cast<char>( u + static_cast<unsigned char>( '0' ) );
221 } else {
222 return static_cast<char>( ( u - 10U ) +
223 static_cast<unsigned char>( 'A' ) );
224 }
225 }
226
227 template<typename WritableType>
228 static constexpr WritableType output_hex( std::uint16_t c,
229 WritableType it ) {
230 char const nibbles[] = { '\\',
231 'u',
232 to_nibble_char( ( c >> 12U ) & 0xFU ),
233 to_nibble_char( ( c >> 8U ) & 0xFU ),
234 to_nibble_char( ( c >> 4U ) & 0xFU ),
235 to_nibble_char( c & 0xFU ),
236 '\0' };
237
238 it.write( nibbles );
239 return it;
240 }
241
242 template<typename WritableType>
243 static constexpr void utf32_to_utf8( std::uint32_t cp,
244 WritableType &it ) {
245 if( cp <= 0x7FU ) {
246 it.put( static_cast<char>( cp ) );
247 return;
248 }
249 if( cp <= 0x7FFU ) {
250 char const tmp[] = {
251 static_cast<char>( ( cp >> 6U ) | 0b11000000U ),
252 static_cast<char>( ( cp & 0b00111111U ) | 0b10000000U ),
253 '\0' };
254 it.write( tmp );
255 return;
256 }
257 if( cp <= 0xFFFFU ) {
258 char const tmp[]{
259 static_cast<char>( ( cp >> 12U ) | 0b11100000U ),
260 static_cast<char>( ( ( cp >> 6U ) & 0b00111111U ) | 0b10000000U ),
261 static_cast<char>( ( cp & 0b00111111U ) | 0b10000000U ),
262 '\0' };
263 it.write( tmp );
264 return;
265 }
266 if( cp <= 0x10FFFFU ) {
267 char const tmp[]{
268 static_cast<char>( ( cp >> 18U ) | 0b11110000U ),
269 static_cast<char>( ( ( cp >> 12U ) & 0b00111111U ) | 0b10000000U ),
270 static_cast<char>( ( ( cp >> 6U ) & 0b00111111U ) | 0b10000000U ),
271 static_cast<char>( ( cp & 0b00111111U ) | 0b10000000U ),
272 '\0' };
273 it.write( tmp );
274 return;
275 }
276 daw_json_error( true, ErrorReason::InvalidUTFCodepoint );
277 }
278 } // namespace json_details
279
280 namespace utils {
281 template<bool RestrictHigh, typename WriteableType>
282 [[nodiscard]] static constexpr WriteableType
283 escape_codepoint_to_iterator( WriteableType it, std::uint32_t cp ) {
284 switch( cp ) {
285 case '"':
286 it.write( "\\\"" );
287 break;
288 case '\\':
289 it.write( "\\\\" );
290 break;
291 case '\b':
292 it.write( "\\b" );
293 break;
294 case '\f':
295 it.write( "\\f" );
296 break;
297 case '\n':
298 it.write( "\\n" );
299 break;
300 case '\r':
301 it.write( "\\r" );
302 break;
303 case '\t':
304 it.write( "\\t" );
305 break;
306 default:
307 if( cp < 0x20U ) {
308 it =
309 json_details::output_hex( static_cast<std::uint16_t>( cp ), it );
310 break;
311 }
312 if constexpr( RestrictHigh ) {
313 if( cp >= 0x80U and cp <= 0xFFFFU ) {
314 it = json_details::output_hex( static_cast<std::uint16_t>( cp ),
315 it );
316 break;
317 }
318 if( cp > 0xFFFFU ) {
319 it = json_details::output_hex(
320 static_cast<std::uint16_t>( 0xD7C0U + ( cp >> 10U ) ), it );
321 it = json_details::output_hex(
322 static_cast<std::uint16_t>( 0xDC00U + ( cp & 0x3FFU ) ), it );
323 break;
324 }
325 }
326 json_details::utf32_to_utf8( cp, it );
327 break;
328 }
329 return it;
330 }
331
332 template<
333 bool do_escape = false,
334 options::EightBitModes EightBitMode = options::EightBitModes::AllowFull,
335 bool use_scanned_write = false, typename WritableType,
336 typename Container DAW_ENABLEIF(
337 daw::traits::is_container_like_v<daw::remove_cvref_t<Container>> )>
339 daw::traits::is_container_like_v<daw::remove_cvref_t<Container>> )
340 [[nodiscard]] static constexpr WritableType
341 copy_to_iterator( WritableType it, Container const &container ) {
342 using restrict_high = std::bool_constant<
343 EightBitMode != options::EightBitModes::AllowFull or
344 ( WritableType::restricted_string_output ==
345 options::RestrictedStringOutput::OnlyAllow7bitStrings )>;
346 if constexpr( do_escape ) {
347 if constexpr( use_scanned_write and
348 json_details::is_string_view_like_v<Container> ) {
349 auto const *const data = std::data( container );
350 auto const size = std::size( container );
351 bool is_ascii = true;
352 for( std::size_t n = 0; n < size; ++n ) {
353 if( static_cast<unsigned char>( data[n] ) >= 0x80U ) {
354 is_ascii = false;
355 break;
356 }
357 }
358 if( is_ascii ) {
359 auto const *first = data;
360 auto const *pos = data;
361 auto const *const last = data + size;
362 while( pos != last ) {
363 auto const c = static_cast<unsigned char>( *pos );
364 if( c != '"' and c != '\\' and c >= 0x20U ) {
365 ++pos;
366 continue;
367 }
368 if( first != pos ) {
369 it.write( daw::string_view(
370 first, static_cast<std::size_t>( pos - first ) ) );
371 }
372 switch( c ) {
373 case '"':
374 it.write( "\\\"" );
375 break;
376 case '\\':
377 it.write( "\\\\" );
378 break;
379 case '\b':
380 it.write( "\\b" );
381 break;
382 case '\f':
383 it.write( "\\f" );
384 break;
385 case '\n':
386 it.write( "\\n" );
387 break;
388 case '\r':
389 it.write( "\\r" );
390 break;
391 case '\t':
392 it.write( "\\t" );
393 break;
394 default:
395 it = json_details::output_hex(
396 static_cast<std::uint16_t>( c ), it );
397 break;
398 }
399 ++pos;
400 first = pos;
401 }
402 if( first != last ) {
403 it.write( daw::string_view(
404 first, static_cast<std::size_t>( last - first ) ) );
405 }
406 return it;
407 }
408 }
409 using iter = DAW_TYPEOF( std::begin( container ) );
410 using it_t = utf8::unchecked::iterator<iter>;
411
412 auto first = it_t( std::begin( container ) );
413 auto const last = it_t( std::end( container ) );
414 while( first != last ) {
415 auto const last_it = first;
416 auto const cp = *first++;
417 if( last_it == first ) {
418 // Not a valid unicode cp
419 if constexpr( WritableType::restricted_string_output ==
420 options::RestrictedStringOutput::
421 ErrorInvalidUTF8 ) {
422 daw_json_error( true, ErrorReason::InvalidStringHighASCII );
423 } else {
424 first = it_t( std::next( first.base( ) ) );
425 }
426 }
427 it = escape_codepoint_to_iterator<restrict_high::value>( it, cp );
428 }
429 } else {
430 if constexpr( json_details::is_string_view_like_v<Container> ) {
431 if constexpr( restrict_high::value ) {
432 for( auto c : container ) {
433 daw_json_ensure( ( static_cast<unsigned char>( c ) >= 0x20U and
434 static_cast<unsigned char>( c ) <= 0x7FU ),
435 ErrorReason::InvalidStringHighASCII );
436 }
437 }
438 it.write( daw::string_view( std::data( container ),
439 std::size( container ) ) );
440 } else {
441 for( auto c : container ) {
442 if constexpr( restrict_high::value ) {
443 daw_json_ensure( ( static_cast<unsigned char>( c ) >= 0x20U and
444 static_cast<unsigned char>( c ) <= 0x7FU ),
445 ErrorReason::InvalidStringHighASCII );
446 }
447 it.put( c );
448 }
449 }
450 }
451 return it;
452 }
453
454 template<
455 bool do_escape = false,
456 options::EightBitModes EightBitMode = options::EightBitModes::AllowFull,
457 typename WriteableType>
458 [[nodiscard]] static constexpr WriteableType
459 copy_to_iterator( WriteableType it, char const *ptr ) {
460 if( ptr == nullptr ) {
461 return it;
462 }
463 using restrict_high = std::bool_constant<
464 EightBitMode != options::EightBitModes::AllowFull or
465 ( WriteableType::restricted_string_output ==
466 options::RestrictedStringOutput::OnlyAllow7bitStrings )>;
467
468 if constexpr( do_escape ) {
469 auto chr_it = utf8::unchecked::iterator<char const *>( ptr );
470 while( *chr_it.base( ) != '\0' ) {
471 auto const cp = *chr_it++;
472 it = escape_codepoint_to_iterator<restrict_high::value>( it, cp );
473 }
474 } else if constexpr( restrict_high::value ) {
475 auto const *const first = ptr;
476 while( *ptr != '\0' ) {
477 daw_json_ensure( ( static_cast<unsigned char>( *ptr ) >= 0x20U and
478 static_cast<unsigned char>( *ptr ) <= 0x7FU ),
479 ErrorReason::InvalidStringHighASCII );
480 ++ptr;
481 }
482 it.write( daw::string_view(
483 first, static_cast<std::size_t>( ptr - first ) ) );
484 } else {
485 it.write( ptr );
486 }
487 return it;
488 }
489
490 template<
491 bool do_escape = false,
492 options::EightBitModes EightBitMode = options::EightBitModes::AllowFull,
493 typename WriteableType, json_options_t P, typename A>
494 [[nodiscard]] static constexpr WriteableType
495 copy_to_iterator( WriteableType it, basic_json_value<P, A> const &jv ) {
496 if( jv.is_null( ) ) {
497 return copy_to_iterator<do_escape, EightBitMode>( it, "null" );
498 } else {
499 return copy_to_iterator<do_escape, EightBitMode>(
500 it, jv.get_string_view( ) );
501 }
502 }
503 } // namespace utils
504
505 namespace json_details {
506 template<typename JsonMember, JsonParseTypes Tag, typename WriteableType,
507 typename parse_to_t>
508 [[nodiscard]] DAW_ATTRIB_INLINE static constexpr WriteableType
509 to_daw_json_string( WriteableType it, parse_to_t const &value );
510
511 template<typename JsonMember, typename WriteableType, typename parse_to_t>
512 [[nodiscard]] static constexpr WriteableType
513 to_json_string_bool( WriteableType it, parse_to_t const &value ) {
514
515 if constexpr( JsonMember::literal_as_string ==
516 options::LiteralAsStringOpt::Always ) {
517 if( value ) {
518 it.write( "\"true\"" );
519 } else {
520 it.write( "\"false\"" );
521 }
522 } else {
523 if( value ) {
524 it.write( "true" );
525 } else {
526 it.write( "false" );
527 }
528 }
529 return it;
530 }
531
532 template<std::size_t idx, typename JsonMembers, typename WriteableType,
533 typename parse_to_t>
534 static constexpr void to_variant_string( WriteableType &it,
535 parse_to_t const &value ) {
536 if constexpr( idx < std::variant_size_v<parse_to_t> ) {
537 if( value.index( ) != idx ) {
538 to_variant_string<idx + 1, JsonMembers>( it, value );
539 return;
540 }
541 using element_t = typename JsonMembers::json_elements;
542 using JsonMember =
543 typename pack_element<idx, typename element_t::element_map_t>::type;
544 it = to_daw_json_string<JsonMember, JsonMember::expected_type>(
545 it, daw::get_nt<idx>( value ) );
546 }
547 }
548
549 template<typename JsonMember, typename WriteableType, typename parse_to_t>
550 [[nodiscard]] static constexpr WriteableType
551 to_json_string_variant( WriteableType it, parse_to_t const &value ) {
552
553 assert( value.index( ) >= 0 );
554 to_variant_string<0, JsonMember>( it, value );
555 return it;
556 }
557
558 template<typename JsonMember, typename WriteableType, typename parse_to_t>
559 [[nodiscard]] static constexpr WriteableType
560 to_json_string_variant_tagged( WriteableType it,
561 parse_to_t const &value ) {
562
563 to_variant_string<0, JsonMember>( it, value );
564 return it;
565 }
566
567 template<typename JsonMember, typename WriteableType, typename parse_to_t>
568 [[nodiscard]] static constexpr WriteableType
569 to_json_string_variant_intrusive( WriteableType it,
570 parse_to_t const &value ) {
571
572 to_variant_string<0, JsonMember>( it, value );
573 return it;
574 }
575
576 template<typename /*JsonMember*/, typename = void>
577 inline constexpr bool has_null_checker_v = false;
578
579 template<typename JsonMember>
580 inline constexpr bool has_null_checker_v<
581 JsonMember, std::void_t<typename JsonMember::is_null_checker>> =
582 not std::is_same_v<use_default, typename JsonMember::is_null_checker>;
583
584 template<typename JsonMember, typename WriteableType, typename Optional>
585 [[nodiscard]] static constexpr WriteableType
586 to_json_string_null( WriteableType it, Optional const &value ) {
587
588 if constexpr( has_null_checker_v<JsonMember> ) {
589 // Null checker specified
590 using is_null_checker_t = typename JsonMember::is_null_checker;
591 if( is_null_checker_t{ }( value ) ) {
592 it.write( "null" );
593 return it;
594 }
595 } else if constexpr( has_op_bool_v<Optional> ) {
596 if( not static_cast<bool>( value ) ) {
597 it.write( "null" );
598 return it;
599 }
600 } else {
601 // has_value defaults to true for non-nullable. This allows types
602 // like containers to accept null/missing on parsing but just be empty
603 // when serialized
604 if( not concepts::nullable_value_has_value( value ) ) {
605 it.write( "null" );
606 return it;
607 }
608 }
609 using member_type = typename JsonMember::member_type;
610 return to_daw_json_string<member_type, member_type::expected_type>(
611 it, [&] {
612 if constexpr( concepts::is_nullable_value_v<Optional> ) {
613 return concepts::nullable_value_traits<Optional>::read( value );
614 } else if constexpr( json_details::has_op_star_v<Optional> ) {
615 return *value;
616 } else {
617 return value;
618 }
619 }( ) );
620 }
621
622 DAW_JSON_MAKE_REQ_TRAIT( has_fp_precision_v, T::precision );
623
624 template<typename JsonMember, typename WriteableType, typename parse_to_t>
625 [[nodiscard]] static constexpr WriteableType
626 to_json_string_real( WriteableType it, parse_to_t const &value ) {
627
628 static_assert(
629 std::is_convertible_v<parse_to_t, json_result_t<JsonMember>>,
630 "value must be convertible to specified type in class contract" );
631
632 if constexpr( std::is_floating_point_v<json_result_t<JsonMember>> ) {
633 if( daw::cxmath::is_nan( value ) ) {
634 if constexpr( JsonMember::literal_as_string ==
635 options::LiteralAsStringOpt::Never or
636 JsonMember::allow_number_errors ==
637 options::JsonNumberErrors::None or
638 JsonMember::allow_number_errors ==
639 options::JsonNumberErrors::AllowInf ) {
640 daw_json_error( true, ErrorReason::NumberIsNaN );
641 } else {
642 it.write( "\"NaN\"" );
643 return it;
644 }
645 } else if( daw::cxmath::is_inf( value ) ) {
646 if constexpr( JsonMember::literal_as_string ==
647 options::LiteralAsStringOpt::Never or
648 JsonMember::allow_number_errors ==
649 options::JsonNumberErrors::None or
650 JsonMember::allow_number_errors ==
651 options::JsonNumberErrors::AllowNaN ) {
652 daw_json_error( true, ErrorReason::NumberIsInf );
653 } else {
654 if( value < 0 ) {
655 it.write( "\"-Infinity\"" );
656 } else {
657 it.write( "\"Infinity\"" );
658 }
659 return it;
660 }
661 }
662 }
663
664 if constexpr( JsonMember::literal_as_string ==
665 options::LiteralAsStringOpt::Always ) {
666 it.put( '"' );
667 }
668 if constexpr( daw::is_floating_point_v<parse_to_t> ) {
669 static_assert( sizeof( parse_to_t ) <= sizeof( double ) );
670 if constexpr( has_fp_precision_v<JsonMember> ) {
671 it = to_chars<JsonMember::fp_output_format, JsonMember::precision>(
672 value, it );
673 } else {
674 it = to_chars<JsonMember::fp_output_format>( value, it );
675 }
676 } else {
677 using std::to_string;
678 using to_strings::to_string;
679 it = utils::copy_to_iterator( it, to_string( value ) );
680 }
681 if constexpr( JsonMember::literal_as_string ==
682 options::LiteralAsStringOpt::Always ) {
683 it.put( '"' );
684 }
685 return it;
686 }
687
688 template<typename T>
689 using base_int_type_impl = std::underlying_type<T>;
690
691 template<typename T>
692 using base_int_type_t =
693 typename daw::conditional_t<std::is_enum_v<T>, base_int_type_impl<T>,
694 daw::traits::identity<T>>::type;
695
696 DAW_ATTRIB_INLINE DAW_CONSTEVAL std::array<char[2], 100>
697 make_digits100( ) {
698 auto result = std::array<char[2], 100>{ };
699 for( std::size_t n = 0; n < 100; ++n ) {
700 result[n][0] =
701 static_cast<char>( ( n % 10 ) + static_cast<unsigned char>( '0' ) );
702 result[n][1] =
703 static_cast<char>( ( n / 10 ) + static_cast<unsigned char>( '0' ) );
704 }
705 return result;
706 }
707 inline constexpr auto digits100 = make_digits100( );
708
709 template<typename T>
710 static constexpr void reverse( T *first, T *last ) {
711 // Assume preconditions. This helps on CE in codegen but may
712 // not matter here with inlining
713 DAW_ASSUME( first and last );
714 DAW_ASSUME( first <= last );
715 auto rpos = last - first;
716 auto lpos = 0;
717 while( lpos < rpos ) {
718 --rpos;
719 auto tmp = std::move( first[lpos] );
720 first[lpos] = std::move( first[rpos] );
721 first[rpos] = std::move( tmp );
722 ++lpos;
723 }
724 }
725
726 template<typename JsonMember, typename WriteableType, typename parse_to_t>
727 [[nodiscard]] static constexpr WriteableType
728 to_json_string_signed( WriteableType it, parse_to_t const &value ) {
729
730 static_assert(
731 daw::is_explicitly_convertible_v<parse_to_t,
732 json_base_type_t<JsonMember>>,
733 "value must be convertible to specified type in class contract" );
734
735 using std::to_string;
736 using to_strings::to_string;
737 using under_type = base_int_type_t<parse_to_t>;
738
739 if constexpr( std::is_enum_v<parse_to_t> or
740 daw::is_integral_v<parse_to_t> ) {
741 auto v = static_cast<under_type>( value );
742
743 char buff[daw::digits10<under_type> + 10]{ };
744 char *num_start = buff;
745 char *ptr = buff;
746 if constexpr( JsonMember::literal_as_string ==
747 options::LiteralAsStringOpt::Always ) {
748 *ptr++ = '"';
749 ++num_start;
750 }
751 if( v < 0 ) {
752 *ptr++ = '-';
753 ++num_start;
754 // Do 1 round here just in case we are
755 // daw::min_value<intmax_t> and cannot negate
756 // This is a subtraction because when v < 0, v % 100 is negative
757 auto const tmp = -static_cast<std::size_t>( v % 10 );
758 v /= -10;
759 *ptr++ = digits100[tmp][0];
760 if( v == 0 ) {
761 if constexpr( JsonMember::literal_as_string ==
762 options::LiteralAsStringOpt::Always ) {
763 *ptr++ = '"';
764 }
765 it.copy_buffer( buff, ptr );
766 return it;
767 }
768 }
769
770 if( v == 0 ) {
771 *ptr++ = '0';
772 }
773 while( v >= 10 ) {
774 auto const tmp = static_cast<std::size_t>( v % 100 );
775 v /= 100;
776 ptr[0] = digits100[tmp][0];
777 ptr[1] = digits100[tmp][1];
778 ptr += 2;
779 }
780 if( v > 0 ) {
781 *ptr++ = static_cast<char>( '0' + static_cast<char>( v ) );
782 }
783
784 reverse( num_start, ptr );
785 if constexpr( JsonMember::literal_as_string ==
786 options::LiteralAsStringOpt::Always ) {
787 *ptr++ = '"';
788 }
789 it.copy_buffer( buff, ptr );
790 return it;
791 } else {
792 if constexpr( JsonMember::literal_as_string ==
793 options::LiteralAsStringOpt::Always ) {
794 it.put( '"' );
795 }
796 // Fallback to ADL
797 it = utils::copy_to_iterator( it, to_string( value ) );
798 if constexpr( JsonMember::literal_as_string ==
799 options::LiteralAsStringOpt::Always ) {
800 it.put( '"' );
801 }
802 return it;
803 }
804 }
805
806 template<typename JsonMember, typename WriteableType, typename parse_to_t>
807 [[nodiscard]] static constexpr WriteableType
808 to_json_string_unsigned( WriteableType it, parse_to_t const &value ) {
809
810 static_assert(
811 daw::is_explicitly_convertible_v<parse_to_t,
812 json_result_t<JsonMember>>,
813 "value must be convertible to specified type in class contract" );
814
815 using std::to_string;
816 using to_strings::to_string;
817 using under_type = base_int_type_t<parse_to_t>;
818 if constexpr( JsonMember::literal_as_string ==
819 options::LiteralAsStringOpt::Always ) {
820 it.put( '"' );
821 }
822 if constexpr( std::is_same_v<under_type, bool> ) {
823 if( static_cast<bool>( value ) ) {
824 it.put( '1' );
825 } else {
826 it.put( '0' );
827 }
828 } else if constexpr( std::is_enum_v<parse_to_t> or
829 daw::is_integral_v<parse_to_t> ) {
830 auto v = static_cast<under_type>( value );
831
832 if( DAW_UNLIKELY( v == 0 ) ) {
833 it.put( '0' );
834 } else {
835 daw_json_ensure( v > 0, ErrorReason::NumberOutOfRange );
836 char buff[daw::digits10<under_type> + 10U]{ };
837 char *ptr = buff;
838 while( v >= 10 ) {
839 auto const tmp = static_cast<std::size_t>( v % 100U );
840 v /= 100U;
841 ptr[0] = digits100[tmp][0];
842 ptr[1] = digits100[tmp][1];
843 ptr += 2;
844 }
845 if( v > 0 ) {
846 *ptr++ = static_cast<char>( '0' + static_cast<char>( v ) );
847 }
848 reverse( buff, ptr );
849 it.copy_buffer( buff, ptr );
850 }
851 } else {
852 // Fallback to ADL
853 it = utils::copy_to_iterator( it, to_string( value ) );
854 }
855 if constexpr( JsonMember::literal_as_string ==
856 options::LiteralAsStringOpt::Always ) {
857 it.put( '"' );
858 }
859 return it;
860 }
861 } // namespace json_details
862
863 namespace utils {
864 namespace utils_details {
865 template<typename Integer>
866 struct number {
867 using parse_to_t = Integer;
868 using base_type = parse_to_t;
869 static constexpr options::LiteralAsStringOpt literal_as_string =
870 options::LiteralAsStringOpt::Never;
871 };
872 } // namespace utils_details
873
874 template<typename Integer, typename WriteableType>
875 static constexpr WriteableType integer_to_string( WriteableType it,
876 Integer const &value ) {
877 static_assert( daw::is_integral_v<Integer> );
878
879 if constexpr( daw::is_unsigned_v<Integer> ) {
880 return json_details::to_json_string_unsigned<
881 utils_details::number<Integer>>( it, value );
882 } else {
883 return json_details::to_json_string_signed<
884 utils_details::number<Integer>>( it, value );
885 }
886 }
887 } // namespace utils
888
889 namespace json_details {
890 template<typename JsonMember, typename WriteableType, typename parse_to_t>
891 [[nodiscard]] static constexpr WriteableType
892 to_json_string_string_raw( WriteableType it, parse_to_t const &value ) {
893
894 static_assert(
895 std::is_convertible_v<parse_to_t, json_result_t<JsonMember>>,
896 "Value must be convertible to specialized type in "
897 "json_data_contract" );
898
899 it.put( '"' );
900 if( std::size( value ) > 0U ) {
901 it = utils::copy_to_iterator<false, JsonMember::eight_bit_mode>(
902 it, value );
903 }
904 it.put( '"' );
905 return it;
906 }
907
908 template<typename JsonMember, typename = void>
909 inline constexpr options::EscapeValidUTF8 escape_output_v =
910 options::EscapeValidUTF8::Validate;
911
912 template<typename JsonMember>
913 inline constexpr options::EscapeValidUTF8 escape_output_v<
914 JsonMember, std::void_t<decltype( JsonMember::escape_output )>> =
915 JsonMember::escape_output;
916
917 template<typename JsonMember, typename WriteableType, typename parse_to_t>
918 [[nodiscard]] static constexpr WriteableType
919 to_json_string_string_escaped( WriteableType it,
920 parse_to_t const &value ) {
921 it.put( '"' );
922 it = utils::copy_to_iterator<escape_output_v<JsonMember> !=
923 options::EscapeValidUTF8::AssumeValid,
924 JsonMember::eight_bit_mode,
925 true>( it, value );
926 it.put( '"' );
927 return it;
928 }
929
930 template<typename T>
931 [[nodiscard]] static constexpr bool is_null( std::optional<T> const &v ) {
932 return not static_cast<bool>( v );
933 }
934
935 template<typename T>
936 [[nodiscard]] static constexpr bool is_null( T const & ) {
937 return false;
938 }
939
940 template<typename JsonMember, typename WriteableType, typename parse_to_t>
941 [[nodiscard]] static constexpr WriteableType
942 to_json_string_date( WriteableType it, parse_to_t const &value ) {
943
944 static_assert(
945 std::is_convertible_v<parse_to_t, json_result_t<JsonMember>>,
946 "value must be convertible to specified type in class contract" );
947
948 using json_details::is_null;
949 // TODO: document customization point
950 if( is_null( value ) ) {
951 it.write( "null" );
952 return it;
953 }
954 it.put( '"' );
955 datetime::ymdhms civil = datetime::time_point_to_civil( value );
956 it = utils::integer_to_string( it, civil.year );
957 it.put( '-' );
958 if( civil.month < 10 ) {
959 it.put( '0' );
960 }
961 it = utils::integer_to_string( it, civil.month );
962 it.put( '-' );
963 if( civil.day < 10 ) {
964 it.put( '0' );
965 }
966 it = utils::integer_to_string( it, civil.day );
967 it.put( 'T' );
968 if( civil.hour < 10 ) {
969 it.put( '0' );
970 }
971 it = utils::integer_to_string( it, civil.hour );
972 it.put( ':' );
973 if( civil.minute < 10 ) {
974 it.put( '0' );
975 }
976 it = utils::integer_to_string( it, civil.minute );
977 it.put( ':' );
978 if( civil.second < 10 ) {
979 it.put( '0' );
980 }
981 it = utils::integer_to_string( it, civil.second );
982 if( civil.attosecond > 0 ) {
983 auto fractional_digits = std::size_t{ 18 };
984 while( civil.attosecond % 10 == 0 ) {
985 civil.attosecond /= 10;
986 --fractional_digits;
987 }
988 it.put( '.' );
989 auto const digit_count = static_cast<std::size_t>(
990 daw::cxmath::count_digits( civil.attosecond ) );
991 while( digit_count < fractional_digits ) {
992 it.put( '0' );
993 --fractional_digits;
994 }
995 it = utils::integer_to_string( it, civil.attosecond );
996 }
997 it.write( "Z\"" );
998 return it;
999 }
1000
1001 template<typename JsonMember, typename WriteableType, typename parse_to_t>
1002 [[nodiscard]] static constexpr WriteableType
1003 to_json_string_unknown( WriteableType it, parse_to_t const &value ) {
1004
1005 return utils::copy_to_iterator<false, JsonMember::eight_bit_mode>(
1006 it, value );
1007 }
1008
1009 template<typename JsonMember, typename WriteableType, typename parse_to_t>
1010 [[nodiscard]] static constexpr WriteableType
1011 to_json_string_class( WriteableType it, parse_to_t const &value ) {
1012
1013 static_assert(
1014 std::is_convertible_v<parse_to_t, json_result_t<JsonMember>> or
1015 std::is_same_v<parse_to_t,
1016 json_result_t<JsonMember>>, // This is for
1017 // not-copy/movable
1018 // types
1019 "value must be convertible to specified type in class contract" );
1020
1021 if constexpr( has_json_to_json_data_v<parse_to_t> ) {
1022 return json_data_contract_trait_t<typename JsonMember::wrapped_type>::
1023 serialize(
1024 it,
1025 json_data_contract<
1026 typename JsonMember::wrapped_type>::to_json_data( value ),
1027 value );
1028 } else if constexpr( is_json_map_alias_v<parse_to_t> ) {
1029 return json_data_contract_trait_t<parse_to_t>::serialize(
1030 it, value, value );
1031 } else if constexpr( std::is_empty_v<parse_to_t> and
1032 std::is_default_constructible_v<parse_to_t> and
1033 not has_json_data_contract_trait_v<parse_to_t> ) {
1034 it.write( "{}" );
1035 return it;
1036 } else {
1037 static_assert( is_submember_tagged_variant_v<parse_to_t>,
1038 "Could not find appropriate mapping or to_json_data "
1039 "member of json_data_contract" );
1040 return json_data_contract_trait_t<parse_to_t>::serialize( it, value );
1041 }
1042 }
1043
1044 template<typename JsonMember, typename WriteableType, typename parse_to_t>
1045 [[nodiscard]] static constexpr WriteableType
1046 to_json_string_reflected_class( WriteableType it,
1047 parse_to_t const &value ) {
1048
1049 return JsonMember::serialize( it, value );
1050 }
1051
1052 template<typename JsonMember, typename WriteableType, typename parse_to_t>
1053 [[nodiscard]] static constexpr WriteableType
1054 to_json_string_custom( WriteableType it, parse_to_t const &value ) {
1055
1056 static_assert(
1057 std::is_convertible_v<parse_to_t, json_result_t<JsonMember>>,
1058 "value must be convertible to specified type in class contract" );
1059
1060 if constexpr( JsonMember::custom_json_type ==
1061 options::JsonCustomTypes::String ) {
1062 it.put( '"' );
1063 }
1064
1065 if constexpr( daw::is_callable_r_v<WriteableType,
1066 typename JsonMember::to_converter_t,
1067 WriteableType,
1068 parse_to_t> ) {
1069 it = typename JsonMember::to_converter_t{ }( it, value );
1070 } else {
1071 it = utils::copy_to_iterator(
1072 it, typename JsonMember::to_converter_t{ }( value ) );
1073 }
1074
1075 if constexpr( JsonMember::custom_json_type ==
1076 options::JsonCustomTypes::String ) {
1077 it.put( '"' );
1078 }
1079 return it;
1080 }
1081
1082 template<typename JsonMember, typename WriteableType,
1083 json_options_t SerializationOptions, typename parse_to_t,
1084 std::size_t... Is>
1085 DAW_ATTRIB_INLINE constexpr serialization_policy<WriteableType,
1086 SerializationOptions>
1087 to_daw_json_string_tuple(
1088 serialization_policy<WriteableType, SerializationOptions> it,
1089 parse_to_t const &value, std::index_sequence<Is...> ) {
1090
1091 auto const to_daw_json_string_help = [&]( auto Idx ) {
1092 using index = daw::remove_cvref_t<decltype( Idx )>;
1093 using pack_element = tuple_elements_pack<parse_to_t>;
1094 using T = std::tuple_element_t<index::value,
1095 typename JsonMember::sub_member_list>;
1096
1097 it = to_daw_json_string<T, T::expected_type>(
1098 it, pack_element::template get<index::value>( value ) );
1099 if constexpr( index::value + 1 < sizeof...( Is ) ) {
1100 it.put( ',' );
1101 it.next_member( );
1102 }
1103 };
1104 (void)to_daw_json_string_help;
1105
1106 daw::empty_t const expander[]{
1107 ( to_daw_json_string_help( daw::constant_v<Is> ),
1108 daw::empty_t{ } )...,
1109 daw::empty_t{ } };
1110 (void)expander;
1111
1112 return it;
1113 }
1114
1115 template<typename JsonMember, typename WriteableType,
1116 json_options_t SerializationOptions, typename parse_to_t>
1117 [[nodiscard]] static constexpr serialization_policy<WriteableType,
1118 SerializationOptions>
1119 to_json_string_tuple(
1120 serialization_policy<WriteableType, SerializationOptions> it,
1121 parse_to_t const &value ) {
1122
1123 using tuple_t = json_result_t<JsonMember>;
1124
1125 using element_pack = tuple_elements_pack<typename daw::conditional_t<
1126 is_tuple_v<tuple_t>,
1127 daw::traits::identity<tuple_t>,
1128 json_details::identity_parts<tp_from_struct_binding_result_t,
1129 parse_to_t>>::type>;
1130
1131 static_assert(
1132 std::is_convertible_v<parse_to_t, tuple_t>,
1133 "value must be convertible to specified type in class contract" );
1134
1135 it.put( '[' );
1136 it.add_indent( );
1137 it.next_member( );
1138 if constexpr( is_tuple_v<tuple_t> ) {
1139 it = to_daw_json_string_tuple<JsonMember>(
1140 it, value, std::make_index_sequence<element_pack::size>{ } );
1141 } else {
1142 auto value2 = to_tuple_impl( DAW_FWD( value ) );
1143
1144 using value2_t = tp_from_struct_binding_result_t<parse_to_t>;
1145 it = to_daw_json_string_tuple<json_base::json_tuple<value2_t>>(
1146 it, value2, std::make_index_sequence<element_pack::size>{ } );
1147 }
1148 it.del_indent( );
1149 if constexpr( element_pack::size > 0 ) {
1150 if constexpr( element_pack::size > 0 and
1151 it.output_trailing_comma ==
1152 options::OutputTrailingComma::Yes ) {
1153 it.put( ',' );
1154 }
1155 it.next_member( );
1156 }
1157 it.put( ']' );
1158 return it;
1159 }
1160
1162 is_view_like_v, ( (void)( std::begin( std::declval<T &>( ) ) ),
1163 (void)( std::end( std::declval<T &>( ) ) ),
1164 (void)( std::declval<typename T::value_type>( ) ) ) );
1165
1166 template<typename JsonMember, typename WriteableType,
1167 json_options_t SerializationOptions, typename parse_to_t>
1168 [[nodiscard]] static constexpr serialization_policy<WriteableType,
1169 SerializationOptions>
1170 to_json_string_array(
1171 serialization_policy<WriteableType, SerializationOptions> it,
1172 parse_to_t const &value ) {
1173
1174 using array_t = json_result_t<JsonMember>;
1175 if constexpr( is_view_like_v<array_t> ) {
1176 static_assert(
1177 std::is_convertible_v<parse_to_t, array_t>,
1178 "value must be convertible to specified type in class contract" );
1179 } else {
1180 static_assert(
1181 is_pointer_like_v<array_t>,
1182 "This is a special case for pointer like(T*, unique_ptr<T>, "
1183 "shared_ptr<T>) arrays. In the to_json_data it is required to "
1184 "encode the size of the data with the pointer. Will take any "
1185 "Container like type, but std::span like types work too" );
1186 static_assert(
1187 is_view_like_v<parse_to_t>,
1188 "This is a special case for pointer like(T*, unique_ptr<T>, "
1189 "shared_ptr<T>) arrays. In the to_json_data it is required to "
1190 "encode the size of the data with the pointer. Will take any "
1191 "Container like type, but std::span like types work too" );
1192 }
1193
1194 it.put( '[' );
1195 it.add_indent( );
1196 auto first = std::begin( value );
1197 auto last = std::end( value );
1198 bool const has_elements = first != last;
1199 while( first != last ) {
1200 it.next_member( );
1201 it = to_daw_json_string<typename JsonMember::json_element_t,
1202 JsonMember::json_element_t::expected_type>(
1203 it, *first );
1204 ++first;
1205 if( first != last ) {
1206 it.put( ',' );
1207 }
1208 }
1209 it.del_indent( );
1210 if constexpr( it.output_trailing_comma ==
1211 options::OutputTrailingComma::Yes ) {
1212 if( has_elements ) {
1213 it.put( ',' );
1214 }
1215 }
1216 if( has_elements ) {
1217 it.next_member( );
1218 }
1219 it.put( ']' );
1220 return it;
1221 }
1222
1223 template<typename JsonMember, typename WriteableType, typename parse_to_t>
1224 [[nodiscard]] static constexpr WriteableType
1225 to_json_string_sized_array( WriteableType it, parse_to_t const &value ) {
1226 return to_json_string_array<JsonMember>( it, value );
1227 }
1228
1229 template<typename Key, typename Value>
1230 static constexpr Key const &
1231 json_get_key( std::pair<Key, Value> const &kv ) {
1232 return kv.first;
1233 }
1234
1235 template<typename Key, typename Value>
1236 static constexpr Value const &
1237 json_get_value( std::pair<Key, Value> const &kv ) {
1238 return kv.second;
1239 }
1240
1241 template<typename JsonMember, typename WriteableType,
1242 json_options_t SerializeOptions, typename parse_to_t>
1243 [[nodiscard]] static constexpr serialization_policy<WriteableType,
1244 SerializeOptions>
1245 to_json_string_kv_array(
1246 serialization_policy<WriteableType, SerializeOptions> it,
1247 parse_to_t const &value ) {
1248
1249 static_assert(
1250 std::is_convertible_v<parse_to_t, json_result_t<JsonMember>>,
1251 "value must be convertible to specified type in class contract" );
1252 using key_t = typename JsonMember::json_key_t;
1253 using value_t = typename JsonMember::json_value_t;
1254 it.put( '[' );
1255 it.add_indent( );
1256 auto first = std::begin( value );
1257 auto last = std::end( value );
1258 bool const has_elements = first != last;
1259 while( first != last ) {
1260 it.next_member( );
1261 it.put( '{' );
1262 it.add_indent( );
1263 it.next_member( );
1264 // Append Key Name
1265 it.write( "\"", key_t::name, "\":", it.space );
1266 // Append Key Value
1267 it = to_daw_json_string<key_t, key_t::expected_type>(
1268 it, json_get_key( *first ) );
1269
1270 it.put( ',' );
1271 // Append Value Name
1272 it.next_member( );
1273 it.write( "\"", value_t::name, "\":", it.space );
1274 // Append Value Value
1275 it = to_daw_json_string<value_t, value_t::expected_type>(
1276 it, json_get_value( *first ) );
1277
1278 it.del_indent( );
1279 if constexpr( it.output_trailing_comma ==
1280 options::OutputTrailingComma::Yes ) {
1281 if( has_elements ) {
1282 it.put( ',' );
1283 }
1284 }
1285 it.next_member( );
1286 it.put( '}' );
1287 ++first;
1288 if( first != last ) {
1289 it.put( ',' );
1290 }
1291 }
1292 it.del_indent( );
1293 if constexpr( it.output_trailing_comma ==
1294 options::OutputTrailingComma::Yes ) {
1295 if( has_elements ) {
1296 it.put( ',' );
1297 }
1298 }
1299 if( has_elements ) {
1300 it.next_member( );
1301 }
1302 it.put( ']' );
1303 return it;
1304 }
1305
1306 template<typename JsonMember, typename WriteableType,
1307 json_options_t SerializationOptions, typename parse_to_t>
1308 [[nodiscard]] static constexpr serialization_policy<WriteableType,
1309 SerializationOptions>
1310 to_json_string_kv(
1311 serialization_policy<WriteableType, SerializationOptions> it,
1312 parse_to_t const &value ) {
1313
1314 it.put( '{' );
1315 it.add_indent( );
1316 auto first = std::begin( value );
1317 auto last = std::end( value );
1318 bool const has_elements = first != last;
1319 while( first != last ) {
1320 auto const &v = *first;
1321 it.next_member( );
1322 it = to_daw_json_string<typename JsonMember::json_key_t,
1323 JsonMember::json_key_t::expected_type>(
1324 it, json_get_key( v ) );
1325 it.write( ':', it.space );
1326 it = to_daw_json_string<typename JsonMember::json_element_t,
1327 JsonMember::json_element_t::expected_type>(
1328 it, json_get_value( v ) );
1329 ++first;
1330 if( first != last ) {
1331 it.put( ',' );
1332 }
1333 }
1334 it.del_indent( );
1335 if constexpr( it.output_trailing_comma ==
1336 options::OutputTrailingComma::Yes ) {
1337 if( has_elements ) {
1338 it.put( ',' );
1339 }
1340 }
1341 if( has_elements ) {
1342 it.next_member( );
1343 }
1344 it.put( '}' );
1345 return it;
1346 }
1347
1348 template<typename JsonMember, typename WriteableType, typename T>
1349 [[nodiscard]] static constexpr WriteableType
1350 to_json_string_submember( WriteableType it, daw::string_view path,
1351 T const &value );
1352
1353 template<typename JsonMember, typename WriteableType, typename T>
1354 [[nodiscard]] static constexpr WriteableType
1355 to_json_string_submember_array( WriteableType it, daw::string_view path,
1356 T const &value ) {
1357 auto const index = pop_json_path( path );
1358 // Only index zero can be reconstructed without inventing preceding
1359 // array elements. This is a runtime output error because serialization
1360 // is the operation that requires this stronger path invariant.
1361 daw_json_ensure( index.found_char == ']' and
1362 index.current.size( ) == 1 and
1363 index.current.front( ) == '0',
1364 ErrorReason::OutputError );
1365
1366 it.put( '[' );
1367 it.add_indent( );
1368 it.next_member( );
1369 if( path.empty( ) ) {
1370 using member_type = typename JsonMember::member_type;
1371 it = to_daw_json_string<member_type, member_type::expected_type>(
1372 it, value );
1373 } else {
1374 it = to_json_string_submember<JsonMember>( it, path, value );
1375 }
1376 it.del_indent( );
1377 if constexpr( it.output_trailing_comma ==
1378 options::OutputTrailingComma::Yes ) {
1379 it.put( ',' );
1380 }
1381 it.next_member( );
1382 it.put( ']' );
1383 return it;
1384 }
1385
1386 template<typename JsonMember, typename WriteableType, typename T>
1387 [[nodiscard]] static constexpr WriteableType
1388 to_json_string_submember( WriteableType it, daw::string_view path,
1389 T const &value ) {
1390 auto const path_item = pop_json_path( path );
1391 if( path_item.current.empty( ) ) {
1392 daw_json_ensure( path_item.found_char == '[',
1393 ErrorReason::OutputError );
1394 return to_json_string_submember_array<JsonMember>( it, path, value );
1395 }
1396
1397 it.put( '{' );
1398 it.add_indent( );
1399 it.next_member( );
1400 it.put( '"' );
1401 auto name = path_item.current;
1402 while( not name.empty( ) ) {
1403 if( name.front( ) == '\\' ) {
1404 name.remove_prefix( );
1405 daw_json_ensure( not name.empty( ), ErrorReason::OutputError );
1406 }
1407 it.put( name.front( ) );
1408 name.remove_prefix( );
1409 }
1410 it.write( "\":", it.space );
1411
1412 if( path_item.found_char == '[' ) {
1413 it = to_json_string_submember_array<JsonMember>( it, path, value );
1414 } else if( path.empty( ) ) {
1415 using member_type = typename JsonMember::member_type;
1416 it = to_daw_json_string<member_type, member_type::expected_type>(
1417 it, value );
1418 } else {
1419 it = to_json_string_submember<JsonMember>( it, path, value );
1420 }
1421 it.del_indent( );
1422 if constexpr( it.output_trailing_comma ==
1423 options::OutputTrailingComma::Yes ) {
1424 it.put( ',' );
1425 }
1426 it.next_member( );
1427 it.put( '}' );
1428 return it;
1429 }
1430
1431 template<typename JsonMember, JsonParseTypes Tag, typename WriteableType,
1432 typename parse_to_t>
1433 [[nodiscard]] DAW_ATTRIB_INLINE static constexpr WriteableType
1434 to_daw_json_string( WriteableType it, parse_to_t const &value ) {
1435 if constexpr( Tag == JsonParseTypes::Real ) {
1436 return to_json_string_real<JsonMember>( it, value );
1437 } else if constexpr( Tag == JsonParseTypes::Signed ) {
1438 return to_json_string_signed<JsonMember>( it, value );
1439 } else if constexpr( Tag == JsonParseTypes::Unsigned ) {
1440 return to_json_string_unsigned<JsonMember>( it, value );
1441 } else if constexpr( Tag == JsonParseTypes::Null ) {
1442 return to_json_string_null<JsonMember>( it, value );
1443 } else if constexpr( Tag == JsonParseTypes::Bool ) {
1444 return to_json_string_bool<JsonMember>( it, value );
1445 } else if constexpr( Tag == JsonParseTypes::StringRaw ) {
1446 return to_json_string_string_raw<JsonMember>( it, value );
1447 } else if constexpr( Tag == JsonParseTypes::StringEscaped or
1448 Tag == JsonParseTypes::StringInsitu ) {
1449 return to_json_string_string_escaped<JsonMember>( it, value );
1450 } else if constexpr( Tag == JsonParseTypes::Date ) {
1451 return to_json_string_date<JsonMember>( it, value );
1452 } else if constexpr( Tag == JsonParseTypes::Custom ) {
1453 return to_json_string_custom<JsonMember>( it, value );
1454 } else if constexpr( Tag == JsonParseTypes::Class ) {
1455 return to_json_string_class<JsonMember>( it, value );
1456 } else if constexpr( Tag == JsonParseTypes::KeyValue ) {
1457 return to_json_string_kv<JsonMember>( it, value );
1458 } else if constexpr( Tag == JsonParseTypes::KeyValueArray ) {
1459 return to_json_string_kv_array<JsonMember>( it, value );
1460 } else if constexpr( Tag == JsonParseTypes::Array ) {
1461 return to_json_string_array<JsonMember>( it, value );
1462 } else if constexpr( Tag == JsonParseTypes::SizedArray ) {
1463 return to_json_string_sized_array<JsonMember>( it, value );
1464 } else if constexpr( Tag == JsonParseTypes::Variant ) {
1465 return to_json_string_variant<JsonMember>( it, value );
1466 } else if constexpr( Tag == JsonParseTypes::VariantTagged ) {
1467 return to_json_string_variant_tagged<JsonMember>( it, value );
1468 } else if constexpr( Tag == JsonParseTypes::VariantIntrusive ) {
1469 return to_json_string_variant_intrusive<JsonMember>( it, value );
1470 } else if constexpr( Tag == JsonParseTypes::Tuple ) {
1471 return to_json_string_tuple<JsonMember>( it, value );
1472 } else if constexpr( Tag == JsonParseTypes::Submember ) {
1473 return to_json_string_submember<JsonMember>(
1474 it, JsonMember::json_path, value );
1475#if defined( DAW_JSON_HAS_REFLECTION )
1476 } else if constexpr( Tag == JsonParseTypes::ReflectedClass ) {
1477 return to_json_string_reflected_class<JsonMember>( it, value );
1478#endif
1479 } else /*if constexpr( Tag == JsonParseTypes::Unknown )*/ {
1480 static_assert( Tag == JsonParseTypes::Unknown,
1481 "Unexpected JsonParseType" );
1482 return to_json_string_unknown<JsonMember>( it, value );
1483 }
1484 }
1485
1486 template<typename JsonMember, typename WriteableType, typename T>
1487 [[nodiscard]] static constexpr WriteableType
1488 member_to_string( WriteableType it, T const &value ) {
1489 return to_daw_json_string<JsonMember, JsonMember::expected_type>(
1490 std::move( it ), value );
1491 }
1492
1493 template<typename>
1494 struct missing_required_mapping_for {};
1495
1496 // This is only ever called in a constant expression. But will not
1497 // compile if exceptions are disabled and it tries to throw
1498 template<typename Name>
1499 [[noreturn]] DAW_ATTRIB_NOINLINE void missing_required_mapping_error( ) {
1500#if defined( DAW_USE_EXCEPTIONS )
1501 throw missing_required_mapping_for<Name>{ };
1502#else
1503 std::terminate( );
1504#endif
1505 }
1506
1507 template<typename, typename...>
1508 struct find_names_in_pack;
1509
1510 template<typename Needle, typename... Haystack>
1511 struct find_names_in_pack<Needle, daw::fwd_pack<Haystack...>> {
1512 private:
1513 static constexpr daw::simple_array<daw::string_view,
1514 sizeof...( Haystack )>
1515 names = { Haystack::name... };
1516 static_assert( ( ( Haystack::name == Needle::name ) or ... ),
1517 "Name must exist" );
1518
1519 public:
1520 static DAW_CONSTEVAL std::size_t find_position( ) {
1521 std::size_t n = 0;
1522 for( ; n < sizeof...( Haystack ); ++n ) {
1523 if( Needle::name == names[n] ) {
1524 return n;
1525 }
1526 }
1527 if( n >= sizeof...( Haystack ) ) {
1528 missing_required_mapping_error<Needle>( );
1529 }
1530 DAW_UNREACHABLE( );
1531 }
1532
1533 static constexpr std::size_t value = find_position( );
1534 };
1535
1536 template<typename Needle, typename... Haystack>
1537 inline static constexpr std::size_t find_names_in_pack_v =
1538 find_names_in_pack<Needle, Haystack...>::value;
1539
1540 template<std::size_t pos, typename JsonMember, typename NamePack,
1541 typename WriteableType, json_options_t SerializationOptions,
1542 typename TpArgs, typename Value, typename VisitedMembers>
1543 static constexpr void dependent_member_to_json_str(
1544 bool &is_first,
1545 serialization_policy<WriteableType, SerializationOptions> it,
1546 TpArgs const &args, Value const &v, VisitedMembers &visited_members ) {
1547 if constexpr( not has_dependent_member_v<JsonMember> ) {
1548 (void)is_first;
1549 (void)it;
1550 (void)args;
1551 (void)v;
1552 (void)visited_members;
1553 return;
1554 } else {
1555 using base_member_t = typename daw::conditional_t<
1556 is_json_nullable_v<JsonMember>,
1557 ident_trait<json_nullable_member_type_t, JsonMember>,
1558 daw::traits::identity<JsonMember>>::type;
1559
1560 using dependent_member = dependent_member_t<base_member_t>;
1561
1562 using daw::get;
1563 using std::get;
1564 static_assert( is_a_json_type_v<JsonMember>,
1565 "Unsupported data type" );
1566 if constexpr( is_json_nullable_v<JsonMember> ) {
1567 if constexpr( JsonMember::nullable == JsonNullable::Nullable ) {
1568 // We have no requirement to output this member when it's null
1569 if( not get<pos>( args ) ) {
1570 return;
1571 }
1572 }
1573 }
1574 if( daw::algorithm::contains( std::data( visited_members ),
1575 daw::data_end( visited_members ),
1576 dependent_member::name ) ) {
1577 // Already outputted this member
1578 return;
1579 }
1580 visited_members.push_back( dependent_member::name );
1581 if( not is_first ) {
1582 it.put( ',' );
1583 }
1584 it.next_member( );
1585 is_first = false;
1586 it.write( '"', dependent_member::name, "\":", it.space );
1587
1588 if constexpr( has_switcher_v<base_member_t> ) {
1589 it = member_to_string<dependent_member>(
1590 it, typename base_member_t::switcher{ }( v ) );
1591 } else {
1592 using idx =
1593 daw::constant<find_names_in_pack_v<dependent_member, NamePack>>;
1594 it =
1595 member_to_string<dependent_member>( it, get<idx::value>( args ) );
1596 }
1597 (void)it;
1598 }
1599 }
1600
1601 template<std::size_t pos, typename JsonMember, typename WriteableType,
1602 json_options_t SerializationOptions, typename Tuple,
1603 typename Value, typename Visited>
1604 static constexpr void to_json_str(
1605 bool &is_first,
1606 serialization_policy<WriteableType, SerializationOptions> &it,
1607 Tuple const &tp, Value const &, Visited &visited_members ) {
1608 DAW_CPP23_STATIC_LOCAL constexpr auto json_member_name =
1609 daw::string_view( std::data( JsonMember::name ),
1610 std::size( JsonMember::name ) );
1611 if( daw::algorithm::contains( std::data( visited_members ),
1612 daw::data_end( visited_members ),
1613 json_member_name ) ) {
1614 return;
1615 }
1616 visited_members.push_back( json_member_name );
1617 static_assert( is_a_json_type_v<JsonMember>, "Unsupported data type" );
1618 if constexpr( is_json_nullable_v<JsonMember> ) {
1619 if constexpr( JsonMember::nullable == JsonNullable::Nullable ) {
1620 if( not concepts::nullable_value_has_value( get<pos>( tp ) ) ) {
1621 return;
1622 }
1623 if constexpr( has_null_checker_v<JsonMember> ) {
1624 if( typename JsonMember::is_null_checker{ }( get<pos>( tp ) ) ) {
1625 return;
1626 }
1627 }
1628 }
1629 }
1630 if( not is_first ) {
1631 it.put( ',' );
1632 }
1633 it.next_member( );
1634 is_first = false;
1635 it.write( '"', JsonMember::name, "\":", it.space );
1636
1637 it = member_to_string<JsonMember>( std::move( it ), get<pos>( tp ) );
1638 }
1639
1640 template<std::size_t TupleIdx, typename JsonMember,
1641 typename WriteableType, json_options_t SerializerOptions,
1642 template<class...> class Tuple, typename... Args>
1643 static constexpr void to_json_ordered_str(
1644 std::size_t &array_idx, std::size_t array_size,
1645 serialization_policy<WriteableType, SerializerOptions> &it,
1646 Tuple<Args...> const &tp ) {
1647
1648 using json_member_type = ordered_member_subtype_t<JsonMember>;
1649 static_assert( is_a_json_type_v<json_member_type>,
1650 "Unsupported data type" );
1651 // json_tagged_variant like members cannot work as we have no member
1652 // names to work with
1653 static_assert(
1654 not is_a_json_tagged_variant_v<json_member_type>,
1655 "JSON tagged variant types are not supported when inside an array "
1656 "as an ordered structure" );
1657
1658 if constexpr( is_an_ordered_member_v<JsonMember> ) {
1659 for( ; array_idx < JsonMember::member_index; ++array_idx ) {
1660 it.next_member( );
1661 it.write( "null," );
1662 it.next_member( );
1663 }
1664 }
1665 it = member_to_string<json_member_type>( it, get<TupleIdx>( tp ) );
1666 ++array_idx;
1667 if( array_idx < array_size ) {
1668 it.put( ',' );
1669 it.next_member( );
1670 }
1671 }
1672
1705
1706 template<options::FPOutputFormat fp_output_format, unsigned Precision,
1707 typename Real, typename WriteableType>
1708 constexpr WriteableType to_chars( Real const &value,
1709 WriteableType out_it ) {
1710 daw::jkj::dragonbox::unsigned_fp_t<Real> dec =
1711 daw::jkj::dragonbox::to_decimal(
1712 value, daw::jkj::dragonbox::policy::sign::ignore );
1713 auto const original_digits =
1714 daw::jkj::dragonbox::to_chars_detail::decimal_length(
1715 dec.significand );
1716 static_assert(
1717 daw::max_digits10<Real> < daw::digits10<std::uintmax_t>,
1718 "The integer pow10 table must cover every Dragonbox significand "
1719 "digit" );
1720 unsigned decimal_places = 0;
1721 unsigned scientific_places = 0;
1722 // Round a decimal significand to a requested number of significant
1723 // digits. Dragonbox gives us the exact shortest decimal, so doing
1724 // this here avoids a second (and potentially lossy) conversion.
1725 auto round_significand = [&]( unsigned digits ) constexpr {
1726 if( digits == 0 ) {
1727 digits = 1;
1728 }
1729 if( original_digits <= digits ) {
1730 return;
1731 }
1732 auto const remove = original_digits - digits;
1733 using carrier_t = decltype( dec.significand );
1734 auto const divisor =
1735 static_cast<carrier_t>( daw::cxmath::pow10( remove ) );
1736 auto quotient = dec.significand / divisor;
1737 auto const remainder = dec.significand % divisor;
1738 auto const halfway = divisor / 2;
1739 if( remainder > halfway or
1740 ( remainder == halfway and ( quotient & 1U ) != 0U ) ) {
1741 ++quotient;
1742 }
1743 dec.significand = quotient;
1744 dec.exponent += static_cast<int>( remove );
1745 if( daw::jkj::dragonbox::to_chars_detail::decimal_length(
1746 dec.significand ) > digits ) {
1747 // Rounding 9.99 to two digits, for example, produces 10.
1748 dec.significand /= 10;
1749 ++dec.exponent;
1750 }
1751 };
1752 if constexpr( Precision != daw::max_value<unsigned> ) {
1753 if constexpr( fp_output_format == options::FPOutputFormat::Auto ) {
1754 // trim sig digits to at most precision and adjust exponent as
1755 // needed auto chosen decimal/scientific
1756 round_significand( Precision );
1757 } else if constexpr( fp_output_format ==
1758 options::FPOutputFormat::Decimal ) {
1759 // fixed width/padded to Precisioin in Decimal notation
1760 decimal_places = Precision;
1761 if( dec.exponent < -static_cast<int>( Precision ) ) {
1762 using carrier_t = decltype( dec.significand );
1763 auto const remove = static_cast<unsigned>(
1764 -static_cast<long long>( dec.exponent ) - Precision );
1765 if( remove > original_digits ) {
1766 // The discarded power of ten is larger than the entire
1767 // significand, so the value is strictly below half of the
1768 // least retained decimal place. It rounds to zero without
1769 // constructing a power of ten that cannot fit in carrier_t.
1770 dec.significand = 0;
1771 } else {
1772 auto const divisor =
1773 static_cast<carrier_t>( daw::cxmath::pow10( remove ) );
1774 auto quotient = dec.significand / divisor;
1775 auto const remainder = dec.significand % divisor;
1776 auto const halfway = divisor / 2;
1777 if( remainder > halfway or
1778 ( remainder == halfway and ( quotient & 1U ) != 0U ) ) {
1779 ++quotient;
1780 }
1781 dec.significand = quotient;
1782 }
1783 dec.exponent = -static_cast<int>( Precision );
1784 }
1785 } else if constexpr( fp_output_format ==
1786 options::FPOutputFormat::Scientific ) {
1787 // fixed width/padded to Precisioin in Scientific
1788 scientific_places = Precision;
1789 round_significand( Precision + 1 );
1790 // Scientific output requires exactly Precision digits after
1791 // the point. Add insignificant zeroes to the significand.
1792 unsigned const wanted = Precision + 1;
1793 unsigned current =
1794 daw::jkj::dragonbox::to_chars_detail::decimal_length(
1795 dec.significand );
1796 while( current < wanted and current < 19 ) {
1797 dec.significand *= 10;
1798 --dec.exponent;
1799 ++current;
1800 }
1801 } else if constexpr( fp_output_format ==
1802 options::FPOutputFormat::Minimum ) {
1803 // trim to at most Precision significant digits. Use smallest rep
1804 round_significand( Precision );
1805 }
1806 }
1807 auto const digit_values =
1808 daw::jkj::dragonbox::to_chars_detail::decimal_length(
1809 dec.significand );
1810
1811 auto whole_dig =
1812 static_cast<std::int32_t>( digit_values ) + dec.exponent;
1813
1814 auto const br = [&] {
1815 if constexpr( std::is_same_v<Real, float> ) {
1816 return daw::jkj::dragonbox::ieee754_bits( value );
1817 } else {
1818 return daw::jkj::dragonbox::ieee754_bits(
1819 static_cast<double>( value ) );
1820 }
1821 }( );
1822 if( dec.significand == 0 ) {
1823 if( fp_output_format == options::FPOutputFormat::Decimal ) {
1824 out_it.put( '0' );
1825 if constexpr( Precision != daw::max_value<unsigned> ) {
1826 if( decimal_places != 0 ) {
1827 out_it.put( '.' );
1828 for( unsigned n = 0; n < decimal_places; ++n ) {
1829 out_it.put( '0' );
1830 }
1831 }
1832 } else {
1833 out_it.write( ".0" );
1834 }
1835 } else {
1836 out_it.put( '0' );
1837 }
1838 return out_it;
1839 }
1840 if( br.is_negative( ) ) {
1841 out_it.put( '-' );
1842 }
1843 if constexpr( fp_output_format ==
1844 options::FPOutputFormat::Scientific ) {
1845 char buff[50]{ };
1846 char *ptr = buff;
1847 ptr = daw::jkj::dragonbox::to_chars_detail::to_chars(
1848 dec, ptr, digit_values );
1849 if( scientific_places == 0 ) {
1850 out_it.copy_buffer( buff, ptr );
1851 } else {
1852 char const *epos = buff;
1853 while( epos != ptr and *epos != 'e' ) {
1854 ++epos;
1855 }
1856 char const *dot = buff;
1857 while( dot != epos and *dot != '.' ) {
1858 ++dot;
1859 }
1860 if( dot == epos ) {
1861 out_it.copy_buffer( buff, epos );
1862 out_it.put( '.' );
1863 } else {
1864 out_it.copy_buffer( buff, dot + 1 );
1865 out_it.copy_buffer( dot + 1, epos );
1866 }
1867 unsigned count =
1868 dot == epos ? 0U : static_cast<unsigned>( epos - dot - 1 );
1869 while( count < scientific_places ) {
1870 out_it.put( '0' );
1871 ++count;
1872 }
1873 out_it.copy_buffer( epos, ptr );
1874 }
1875 return out_it;
1876 } else if constexpr( fp_output_format ==
1877 options::FPOutputFormat::Auto ) {
1878 if( ( whole_dig < -4 ) | ( whole_dig > 6 ) ) {
1879 char buff[50]{ };
1880 char *ptr = buff;
1881 ptr = daw::jkj::dragonbox::to_chars_detail::to_chars(
1882 dec, ptr, digit_values );
1883 out_it.copy_buffer( buff, ptr );
1884 return out_it;
1885 }
1886 }
1887 if( dec.exponent < 0 ) {
1888 if( whole_dig < 0 ) {
1889 out_it.write( "0." );
1890 do {
1891 out_it.put( '0' );
1892 ++whole_dig;
1893 } while( whole_dig < 0 );
1894 out_it = utils::integer_to_string( out_it, dec.significand );
1895 if constexpr( Precision != daw::max_value<unsigned> ) {
1896 for( auto n = static_cast<unsigned>( -dec.exponent );
1897 n < decimal_places;
1898 ++n ) {
1899 out_it.put( '0' );
1900 }
1901 }
1902 return out_it;
1903 }
1904 // TODO allow for decimal output for all
1905 auto const p1pow =
1906 daw::cxmath::pow10( static_cast<std::size_t>( -dec.exponent ) );
1907 auto const p1val = dec.significand / p1pow;
1908 out_it = utils::integer_to_string( out_it, p1val );
1909 if( p1pow == 1 ) {
1910 return out_it;
1911 }
1912 out_it.put( '.' );
1913 auto const p2val = dec.significand - ( p1val * p1pow );
1914 // ensure we account for leading zeros
1915 {
1916 auto const l10_p1val = daw::cxmath::count_digits( p1val );
1917 auto const l10_p2val = daw::cxmath::count_digits( p2val );
1918 auto const extra_zeros =
1919 static_cast<int>( digit_values ) - ( l10_p2val + l10_p1val );
1920 for( int n = 0; n < extra_zeros; ++n ) {
1921 out_it.put( '0' );
1922 }
1923 }
1924 out_it = utils::integer_to_string( out_it, p2val );
1925 if constexpr( Precision != daw::max_value<unsigned> ) {
1926 for( auto n = static_cast<unsigned>( -dec.exponent );
1927 n < decimal_places;
1928 ++n ) {
1929 out_it.put( '0' );
1930 }
1931 }
1932 return out_it;
1933 }
1934 out_it = utils::integer_to_string( out_it, dec.significand );
1935
1936 while( dec.exponent > 0 ) {
1937 out_it.put( '0' );
1938 --dec.exponent;
1939 }
1940 if( fp_output_format == options::FPOutputFormat::Decimal ) {
1941 if constexpr( Precision == daw::max_value<unsigned> ) {
1942 out_it.put( '.' );
1943 out_it.put( '0' );
1944 } else {
1945 if( decimal_places != 0 ) {
1946 out_it.put( '.' );
1947 for( unsigned n = 0; n < decimal_places; ++n ) {
1948 out_it.put( '0' );
1949 }
1950 }
1951 }
1952 }
1953 return out_it;
1954 }
1955 } // namespace json_details
1956 } // namespace DAW_JSON_VER
1957} // namespace daw::json
#define daw_json_ensure(Bool,...)
Ensure that Bool is true. If false pass rest of args to daw_json_error.
DAW_REQUIRES(daw::json::json_details::is_container_opted_into_json_iostreams_v< Container >) std
An opt in ostream interface for containers of types that have JSON mappings.
#define DAW_JSON_MAKE_REQ_TRAIT(Name,...)
#define DAW_JSON_CPP23_STATIC_CALL_OP_CONST
#define DAW_JSON_CPP23_STATIC_CALL_OP
DAW_ATTRIB_NOINLINE void daw_json_error(bool b, ErrorReason reason)
constexpr std::string_view to_string(JsonBaseParseTypes pt)
JsonParseTypes
The tags used by the parser to determine what parser to call.
@ Tuple
A variant type where the Switcher is based on a submember of the class being parsed.
static constexpr WriteableType escape_codepoint_to_iterator(WriteableType it, std::uint32_t cp)
static constexpr WriteableType integer_to_string(WriteableType it, Integer const &value)
static constexpr WriteableType copy_to_iterator(WriteableType it, char const *ptr)
Customization point traits.
A non-owning container for arbitrary JSON values that allows movement/iteration through.
constexpr bool is_null() const
Is the JSON value a null literal.
constexpr std::string_view get_string_view() const
Construct a string range of the current value. Strings start inside the quotes.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20