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