DAW JSON Link
Loading...
Searching...
No Matches
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"
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
34namespace 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
#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:20