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