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