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