DAW JSON Link
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 
43 namespace 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
58  to_string( std::optional<T> const &v ) -> 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(
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(
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>(
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>(
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 ) };
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 ) };
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 ) };
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 ) };
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 ==
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 {
972  it, typename JsonMember::to_converter_t{ }( value ) );
973  }
974  it.put( '"' );
975  return it;
976  } else {
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
This is in addition to the parse policy. Always do a full name match instead of sometimes relying on ...
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.
constexpr ymdhms time_point_to_civil(std::chrono::time_point< Clock, Duration > const &tp)
LiteralAsStringOpt
Controls the ability to parse numbers that are encoded as strings.
EightBitModes
Controls whether any string character has the high bit set. If restricted, the member will escape any...
constexpr decltype(auto) get(basic_json_pair< PolicyFlags, Allocator > const &parse_state)
constexpr bool nullable_value_has_value(T const &opt)
Check if nullable value has a value.
constexpr auto const & nullable_value_read(T const &opt)
Read value from a non-empty nullable value.
static constexpr WriteableType copy_to_iterator(WriteableType it, basic_json_value< P, A > const &jv)
static constexpr WriteableType integer_to_string(WriteableType it, Integer const &value)
Customization point traits.
DAW_JSON_REQUIRES(boost::describe::has_describe_members< T >::value and use_boost_describe_v< T >) struct json_data_contract< T >
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:25