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