DAW JSON Link
daw_json_location_info.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"
14 #include "daw_json_find_result.h"
15 #include "daw_murmur3.h"
16 
17 #include <daw/algorithms/daw_algorithm_adjacent_find.h>
18 #include <daw/daw_consteval.h>
19 #include <daw/daw_likely.h>
20 #include <daw/daw_logic.h>
21 #include <daw/daw_sort_n.h>
22 #include <daw/daw_string_view.h>
23 #include <daw/daw_traits.h>
24 #include <daw/daw_uint_buffer.h>
25 
26 #include <cstddef>
27 #include <daw/stdinc/data_access.h>
28 
29 #if defined( DAW_JSON_PARSER_DIAGNOSTICS )
30 #include <cmath>
31 #include <iostream>
32 #endif
33 
34 namespace daw::json {
35  inline namespace DAW_JSON_VER {
36  namespace json_details {
37  template<bool FullNameMatch, typename CharT>
38  struct location_info_t {
39  daw::string_view name;
40  CharT *first = nullptr;
41  CharT *last = nullptr;
42  CharT *class_first = nullptr;
43  CharT *class_last = nullptr;
44  std::size_t counter = 0;
45 
46  [[nodiscard]] inline constexpr bool missing( ) const {
47  return first == nullptr;
48  }
49 
50  template<typename ParseState>
51  constexpr void set_range( ParseState const &parse_state ) {
52  first = parse_state.first;
53  last = parse_state.last;
54  class_first = parse_state.class_first;
55  class_last = parse_state.class_last;
56  counter = parse_state.counter;
57  }
58 
59  template<typename ParseState>
60  constexpr auto get_range( ) const {
61  using range_t = typename ParseState::without_allocator_type;
62  auto result = range_t( first, last, class_first, class_last );
63  result.counter = counter;
64  return result;
65  }
66  };
67 
68  template<typename CharT>
69  struct location_info_t<false, CharT> {
70  CharT *first = nullptr;
71  CharT *last = nullptr;
72  CharT *class_first = nullptr;
73  CharT *class_last = nullptr;
74  std::size_t counter = 0;
75 
76  [[nodiscard]] inline constexpr bool missing( ) const {
77  return first == nullptr;
78  }
79 
80  template<typename ParseState>
81  constexpr void set_range( ParseState const &parse_state ) {
82  first = parse_state.first;
83  last = parse_state.last;
84  class_first = parse_state.class_first;
85  class_last = parse_state.class_last;
86  counter = parse_state.counter;
87  }
88 
89  template<typename ParseState>
90  constexpr auto get_range( ) const {
91  // Not copying allocator as it may contain state that needs copying in
92  using range_t = typename ParseState::without_allocator_type;
93  auto result = range_t( first, last, class_first, class_last );
94  result.counter = counter;
95  return result;
96  }
97  };
98 
99  /***
100  * Contains an array of member location_info mapped in a json_class
101  * @tparam MemberCount Number of mapped members from json_class
102  */
103  template<std::size_t MemberCount, typename CharT,
104  bool DoFullNameMatch = true>
105  struct locations_info_t {
106  using value_type = location_info_t<DoFullNameMatch, CharT>;
107  using reference = value_type &;
108  using const_reference = value_type const &;
109  static constexpr bool do_full_name_match = DoFullNameMatch;
110  daw::UInt32 hashes[MemberCount];
111  value_type names[MemberCount];
112 
113  constexpr const_reference operator[]( std::size_t idx ) const {
114  daw_json_ensure( idx < MemberCount, ErrorReason::NumberOutOfRange );
115  return names[idx];
116  }
117 
118  constexpr reference operator[]( std::size_t idx ) {
119  daw_json_ensure( idx < MemberCount, ErrorReason::NumberOutOfRange );
120  return names[idx];
121  }
122 
123  static constexpr std::size_t size( ) {
124  return MemberCount;
125  }
126 
127  template<bool expect_long_strings, std::size_t start_pos>
128  [[nodiscard]] DAW_ATTRIB_INLINE constexpr std::size_t
129  find_name( daw::string_view key ) const {
130  UInt32 const hash = name_hash<expect_long_strings>( key );
131 #if defined( DAW_JSON_BUGFIX_MSVC_EVAL_ORDER_002 )
132  (void)start_pos;
133  for( std::size_t n = 0; n < MemberCount; ++n ) {
134 #else
135  for( std::size_t n = start_pos; n < MemberCount; ++n ) {
136 #endif
137  if( hashes[n] == hash ) {
138  if constexpr( do_full_name_match ) {
139  if( DAW_UNLIKELY( key != names[n].name ) ) {
140  continue;
141  }
142  }
143  return n;
144  }
145  }
146  return MemberCount;
147  }
148  };
149 
150  // Should never be called outside a consteval context
151  template<typename... MemberNames>
152  static inline DAW_CONSTEVAL bool do_hashes_collide( ) {
153  daw::UInt32 hashes[sizeof...( MemberNames )]{
154  name_hash<false>( MemberNames::name )... };
155 
156  daw::sort( std::data( hashes ), daw::data_end( hashes ) );
157  return daw::algorithm::adjacent_find(
158  std::data( hashes ), daw::data_end( hashes ),
159  []( UInt32 l, UInt32 r ) DAW_JSON_CPP23_STATIC_CALL_OP {
160  return l == r;
161  } ) != daw::data_end( hashes );
162  }
163 
164  // Should never be called outside a consteval context
165  template<typename ParseState, typename... JsonMembers>
166  DAW_ATTRIB_FLATINLINE static inline DAW_JSON_MAKE_LOC_INFO_CONSTEVAL auto
167  make_locations_info( ) {
168  using CharT = typename ParseState::CharT;
169 #if defined( DAW_JSON_ALWAYS_FULL_NAME_MATCH )
170  constexpr bool do_full_name_match = true;
171  return locations_info_t<sizeof...( JsonMembers ), CharT,
172  do_full_name_match>{
173  { daw::name_hash<false>( JsonMembers::name )... },
174  { location_info_t<do_full_name_match, CharT>{
175  JsonMembers::name }... } };
176 #else
177  // DAW
178  constexpr bool do_full_name_match =
179  ParseState::force_name_equal_check or
180  do_hashes_collide<JsonMembers...>( );
181  if constexpr( do_full_name_match ) {
182  return locations_info_t<sizeof...( JsonMembers ), CharT,
183  do_full_name_match>{
184  { daw::name_hash<false>( JsonMembers::name )... },
185  { location_info_t<do_full_name_match, CharT>{
186  JsonMembers::name }... } };
187  } else {
188  return locations_info_t<sizeof...( JsonMembers ), CharT,
189  do_full_name_match>{
190  { daw::name_hash<false>( JsonMembers::name )... }, {} };
191  }
192 #endif
193  }
194 
195  /***
196  * Get the position from already seen JSON members or move the parser
197  * forward until we reach the end of the class or the member.
198  * @tparam N Number of members in json_class
199  * @tparam ParseState see IteratorRange
200  * @param locations members location and names
201  * @param parse_state Current JSON data
202  * @return IteratorRange with begin( ) being start of value
203  */
204  enum class AllMembersMustExist { yes, no };
205 
206  template<std::size_t pos, AllMembersMustExist must_exist,
207  bool from_start = false, std::size_t N, typename ParseState,
208  bool B, typename CharT>
209  [[nodiscard]] DAW_ATTRIB_INLINE static constexpr find_result<ParseState>
210  find_class_member( ParseState &parse_state,
211  locations_info_t<N, CharT, B> &locations,
212  bool is_nullable, daw::string_view member_name ) {
213 
214  // silencing gcc9 warning as these are selectively used
215  (void)is_nullable;
216  (void)member_name;
217 
219  nsc_or( is_nullable, ( not locations[pos].missing( ) ),
220  ( not parse_state.is_closing_brace_checked( ) ) ),
221  missing_member( member_name ), parse_state );
222 
223  parse_state.trim_left_unchecked( );
224  bool known = not locations[pos].missing( );
225  while( nsc_and(
226  locations[pos].missing( ),
227  ( not parse_state.empty( ) and parse_state.front( ) != '}' ) ) ) {
228  // TODO: fully unescape name
229  // parse_name checks if we have more and are quotes
230  auto const name = parse_name( parse_state );
231  auto const name_pos =
232  locations.template find_name<ParseState::expect_long_strings,
233  ( from_start ? 0 : pos )>( name );
234  if constexpr( must_exist == AllMembersMustExist::yes ) {
235  daw_json_assert_weak( name_pos < std::size( locations ),
236  ErrorReason::UnknownMember, parse_state );
237  } else {
238 #if defined( DAW_JSON_PARSER_DIAGNOSTICS )
239  std::cerr << "DEBUG: Unknown member '" << name << '\n';
240 #endif
241  if( name_pos >= std::size( locations ) ) {
242  // This is not a member we are concerned with
243  (void)skip_value( parse_state );
244  parse_state.move_next_member_or_end( );
245  continue;
246  }
247  }
248  if( name_pos == pos ) {
249  locations[pos].set_range( parse_state );
250  break;
251  } else {
252 #if defined( DAW_JSON_PARSER_DIAGNOSTICS )
253  std::cerr << "DEBUG: Out of order member '"
254  << locations.names[name_pos].name
255  << "' found, looking for '" << locations.names[pos].name
256  << ". It is "
257  << std::abs( static_cast<long long>( pos ) -
258  static_cast<long long>( name_pos ) )
259  << " members ahead in constructor\n";
260 #endif
261  // We are out of order, store position for later
262  // OLDTODO: use type knowledge to speed up skip
263  // OLDTODO: on skipped classes see if way to store
264  // member positions so that we don't have to
265  // re-parse them after
266  // RESULT: storing preparsed is slower, don't try 3 times
267  // it also limits the type of things we can parse potentially
268  // Using locations to switch on BaseType is slower too
269  locations[name_pos].set_range( skip_value( parse_state ) );
270 
271  if constexpr( ParseState::is_unchecked_input ) {
272  if( name_pos + 1 < std::size( locations ) ) {
273  parse_state.move_next_member( );
274  } else {
275  parse_state.move_next_member_or_end( );
276  }
277  } else {
278  parse_state.move_next_member_or_end( );
279  }
280  }
281  }
282  if( locations[pos].missing( ) ) {
283  known = true;
284  }
285  if constexpr( ParseState::has_allocator ) {
286  return find_result{
287  locations[pos].template get_range<ParseState>( ).with_allocator(
288  parse_state ),
289  known };
290  } else {
291  return find_result<ParseState>{
292  locations[pos].template get_range<ParseState>( ), known };
293  }
294  }
295  } // namespace json_details
296  } // namespace DAW_JSON_VER
297 } // namespace daw::json
#define daw_json_assert_weak(Bool,...)
Assert that Bool is true when in Checked Input mode If false pass rest of args to daw_json_error.
#define daw_json_ensure(Bool,...)
Ensure that Bool is true. If false pass rest of args to daw_json_error.
#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 ...
#define DAW_JSON_MAKE_LOC_INFO_CONSTEVAL
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition: version.h:25