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