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