DAW JSON Link
Loading...
Searching...
No Matches
daw_json_skip.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
19
20#include <daw/daw_attributes.h>
21#include <daw/daw_bit_cast.h>
22#include <daw/daw_enable_requires.h>
23#include <daw/daw_is_any_of.h>
24#include <daw/daw_likely.h>
25#include <daw/daw_not_null.h>
26#include <daw/daw_unreachable.h>
27
28#include <iterator>
29
30namespace daw::json {
31 inline namespace DAW_JSON_VER {
32 namespace json_details {
33 /***
34 * Skip a string, after the initial quote has been skipped already
35 */
36 template<typename ParseState>
37 [[nodiscard]] DAW_ATTRIB_FLATINLINE static constexpr ParseState
38 skip_string_nq( ParseState &parse_state ) {
39 auto result = parse_state;
40 result.counter =
41 string_quote::string_quote_parser::parse_nq( parse_state );
42
43 daw_json_assert_weak( parse_state.front( ) == '"',
44 ErrorReason::InvalidString,
45 parse_state );
46 result.last = parse_state.first;
47 parse_state.remove_prefix( );
48 return result;
49 }
50
51 /***
52 * Skip a string and store the first escaped element's position, if any
53 */
54 template<bool KeepQuotes = false, typename ParseState>
55 [[nodiscard]] DAW_ATTRIB_FLATINLINE static constexpr ParseState
56 skip_string( ParseState &parse_state ) {
57 if( parse_state.empty( ) ) {
58 return parse_state;
59 }
60 daw_json_ensure( parse_state.front( ) == '"',
61 ErrorReason::InvalidString,
62 parse_state );
63 parse_state.remove_prefix( );
64
66 parse_state.has_more( ), ErrorReason::InvalidString, parse_state );
67 auto result = skip_string_nq( parse_state );
68 if constexpr( KeepQuotes ) {
69 --result.first;
70 ++result.last;
71 }
72 return result;
73 }
74
75 template<typename ParseState>
76 [[nodiscard]] static constexpr ParseState
77 skip_true( ParseState &parse_state ) {
78 auto result = parse_state;
79 if constexpr( ParseState::is_unchecked_input ) {
80 parse_state.remove_prefix( 4 );
81 } else {
82 parse_state.remove_prefix( );
83 daw_json_ensure( parse_state.starts_with( "rue" ),
84 ErrorReason::InvalidTrue,
85 parse_state );
86 parse_state.remove_prefix( 3 );
87 }
88 result.last = parse_state.first;
89 parse_state.trim_left( );
90 daw_json_assert_weak( not parse_state.has_more( ) or
91 parse_state.is_at_token_after_value( ),
92 ErrorReason::InvalidEndOfValue,
93 parse_state );
94 result.counter = static_cast<bool>( true );
95 return result;
96 }
97
98 template<typename ParseState>
99 [[nodiscard]] static constexpr ParseState
100 skip_false( ParseState &parse_state ) {
101 auto result = parse_state;
102 if constexpr( ParseState::is_unchecked_input ) {
103 parse_state.remove_prefix( 5 );
104 } else {
105 parse_state.remove_prefix( );
106 daw_json_ensure( parse_state.starts_with( "alse" ),
107 ErrorReason::InvalidFalse,
108 parse_state );
109 parse_state.remove_prefix( 4 );
110 }
111 result.last = parse_state.first;
112 parse_state.trim_left( );
113 daw_json_assert_weak( not parse_state.has_more( ) or
114 parse_state.is_at_token_after_value( ),
115 ErrorReason::InvalidEndOfValue,
116 parse_state );
117 result.counter = static_cast<bool>( false );
118 return result;
119 }
120
121 template<typename ParseState>
122 [[nodiscard]] static constexpr ParseState
123 skip_null( ParseState &parse_state ) {
124 if constexpr( ParseState::is_unchecked_input ) {
125 parse_state.remove_prefix( 4 );
126 } else {
127 parse_state.remove_prefix( );
128 daw_json_ensure( parse_state.starts_with( "ull" ),
129 ErrorReason::InvalidNull,
130 parse_state );
131 parse_state.remove_prefix( 3 );
132 }
133 daw_json_assert_weak( parse_state.has_more( ),
134 ErrorReason::UnexpectedEndOfData,
135 parse_state );
136 parse_state.trim_left( );
137 daw_json_assert_weak( not parse_state.has_more( ) or
138 parse_state.is_at_token_after_value( ),
139 ErrorReason::UnexpectedEndOfData,
140 parse_state );
141 auto result = parse_state;
142 result.first = nullptr;
143 result.last = nullptr;
144 return result;
145 }
146
147 template<bool skip_end_check, typename iterator>
148 DAW_ATTRIB_FLATINLINE [[nodiscard]] constexpr daw::not_null<iterator>
149 skip_digits( daw::not_null<iterator> first,
150 daw::not_null<iterator> const last ) {
151 (void)last; // only used inside if constexpr and gcc9 warns
152 unsigned dig = parse_digit( *first );
153 while( dig < 10 ) {
154 ++first;
155 if constexpr( not skip_end_check ) {
156 if( DAW_UNLIKELY( first >= last ) ) {
157 break;
158 }
159 }
160 dig = parse_digit( *first );
161 }
162 return first;
163 }
164 /***
165 * Skip a number and store the position of it's components in the returned
166 * ParseState
167 */
168 template<typename ParseState>
169 [[nodiscard]] static constexpr ParseState
170 skip_number( ParseState &parse_state ) {
171 daw_json_assert_weak( parse_state.has_more( ),
172 ErrorReason::UnexpectedEndOfData,
173 parse_state );
174
175 auto result = parse_state;
176 daw::not_null<char const *> first = parse_state.first;
177 daw::not_null<char const *> const last = parse_state.last;
178 if constexpr( ParseState::allow_leading_zero_plus ) {
179 if( *first == '-' ) {
180 ++first;
181 }
182 } else {
183 switch( *first ) {
184 case '-':
185 ++first;
186 break;
187 case '+':
189 true, ErrorReason::InvalidNumberStart, parse_state );
190 case '0':
191 if( last - first > 1 ) {
193 not parse_policy_details::is_number( *std::next( first ) ),
194 ErrorReason::InvalidNumberStart,
195 parse_state );
196 }
197 break;
198 }
199 }
200
201 if( DAW_LIKELY( first < last ) ) {
202 DAW_LIKELY_BRANCH
203 first = skip_digits<ParseState::is_unchecked_input>( first, last );
204 }
205
206 char const *decimal = nullptr;
207 if( ( ( ParseState::is_zero_terminated_string or
208 ParseState::is_unchecked_input ) or
209 first < last ) and
210 ( *first == '.' ) ) {
211 decimal = first;
212 ++first;
213 if( DAW_LIKELY( first < last ) ) {
214 DAW_LIKELY_BRANCH
215 first =
216 skip_digits<( ParseState::is_zero_terminated_string or
217 ParseState::is_unchecked_input )>( first, last );
218 }
219 }
220 char const *exp = nullptr;
221
222 unsigned dig = [&] {
223 if( ParseState::is_zero_terminated_string or first < last ) {
224 return parse_digit( *first );
225 }
226 // We are out of range and the exponent part is optional
227 return 0U;
228 }( );
229 if( ( dig == parsed_constants::e_char ) |
230 ( dig == parsed_constants::E_char ) ) {
231 exp = first;
232 ++first;
234 first < last, ErrorReason::UnexpectedEndOfData, [&] {
235 auto r = parse_state;
236 r.first = input_pointer( r.first, first.get( ) );
237 return r;
238 }( ) );
239 dig = parse_digit( *first );
240 if( ( dig == parsed_constants::plus_char ) |
241 ( dig == parsed_constants::minus_char ) ) {
242 ++first;
243 }
244 daw_json_assert_weak( first < last and parse_digit( *first ) < 10U,
245 ErrorReason::InvalidNumber );
246
247 if( DAW_LIKELY( first < last ) ) {
248 DAW_LIKELY_BRANCH
249 first =
250 skip_digits<( ParseState::is_zero_terminated_string or
251 ParseState::is_unchecked_input )>( first, last );
252 }
253 }
254
255 parse_state.first = input_pointer( parse_state.first, first.get( ) );
256 result.last = input_pointer( result.first, first.get( ) );
257 result.class_first = input_pointer( result.first, decimal );
258 result.class_last = input_pointer( result.first, exp );
259 return result;
260 }
261
262 /***
263 * When we don't know ahead of time what we are skipping switch on the
264 * first value and call that types specific skipper
265 * TODO: Investigate if there is a difference for the times we know
266 * what the member should be if that can increase performance
267 */
268 template<bool KeepInitialQuote = false, typename ParseState>
269 [[nodiscard]] DAW_ATTRIB_NOINLINE constexpr ParseState
270 skip_value( ParseState &parse_state ) {
271 daw_json_assert_weak( parse_state.has_more( ),
272 ErrorReason::UnexpectedEndOfData,
273 parse_state );
274
275 // reset counter
276 parse_state.counter = 0;
277 switch( parse_state.front( ) ) {
278 case '"':
279 return skip_string<KeepInitialQuote>( parse_state );
280 case '[':
281 return parse_state.skip_array( );
282 case '{':
283 return parse_state.skip_class( );
284 case 't':
285 return skip_true( parse_state );
286 case 'f':
287 return skip_false( parse_state );
288 case 'n':
289 return skip_null( parse_state );
290 case '-':
291 case '0':
292 case '1':
293 case '2':
294 case '3':
295 case '4':
296 case '5':
297 case '6':
298 case '7':
299 case '8':
300 case '9':
301 return skip_number( parse_state );
302 }
303 DAW_UNLIKELY_BRANCH
304 if constexpr( ParseState::is_unchecked_input ) {
305 if( DAW_UNLIKELY( parse_state.front( ) == '\0' ) ) {
307 true, ErrorReason::InvalidStartOfValue, parse_state );
308 }
309 DAW_UNREACHABLE( );
310 } else {
311 daw_json_error( true, ErrorReason::InvalidStartOfValue, parse_state );
312 }
313 }
314
315 /***
316 * Used in json_array_iterator::operator++( ) as we know the type we
317 * are skipping
318 */
319 template<typename JsonMember, typename ParseState>
320 [[nodiscard]] DAW_ATTRIB_FLATINLINE static constexpr ParseState
321 skip_known_value( ParseState &parse_state ) {
322 daw_json_assert_weak( parse_state.has_more( ),
323 ErrorReason::UnexpectedEndOfData,
324 parse_state );
325 if constexpr( JsonMember::expected_type == JsonParseTypes::Date or
326 JsonMember::expected_type == JsonParseTypes::StringRaw or
327 JsonMember::expected_type ==
328 JsonParseTypes::StringEscaped or
329 JsonMember::expected_type ==
330 JsonParseTypes::StringInsitu or
331 JsonMember::expected_type == JsonParseTypes::Custom ) {
332 // json string encodings
333 daw_json_assert_weak( parse_state.front( ) == '"',
334 ErrorReason::InvalidString,
335 parse_state );
336 parse_state.remove_prefix( );
337 return json_details::skip_string_nq( parse_state );
338 } else if constexpr( daw::is_any_of_v<JsonMember::expected_type,
339 JsonParseTypes::Real,
340 JsonParseTypes::Signed,
341 JsonParseTypes::Unsigned,
342 JsonParseTypes::Bool,
343 JsonParseTypes::Null> ) {
344 // All literals
345 return skip_number( parse_state );
346 } else if constexpr( JsonMember::expected_type ==
347 JsonParseTypes::Array ) {
348 daw_json_assert_weak( parse_state.is_opening_bracket_checked( ),
349 ErrorReason::InvalidArrayStart,
350 parse_state );
351 return parse_state.skip_array( );
352 } else if constexpr( JsonMember::expected_type ==
353 JsonParseTypes::Class ) {
354 daw_json_assert_weak( parse_state.is_opening_brace_checked( ),
355 ErrorReason::InvalidClassStart,
356 parse_state );
357 return parse_state.skip_class( );
358 } else {
359 return skip_value( parse_state );
360 }
361 }
362
363 template<typename ParseState>
364 [[nodiscard]] static constexpr ParseState
365 skip_literal( ParseState &parse_state ) {
366 daw_json_assert_weak( parse_state.has_more( ),
367 ErrorReason::UnexpectedEndOfData,
368 parse_state );
369
370 // reset counter
371 parse_state.counter = 0;
372 switch( parse_state.front( ) ) {
373 case 't':
374 return skip_true( parse_state );
375 case 'f':
376 return skip_false( parse_state );
377 case 'n':
378 return skip_null( parse_state );
379 case '-':
380 case '0':
381 case '1':
382 case '2':
383 case '3':
384 case '4':
385 case '5':
386 case '6':
387 case '7':
388 case '8':
389 case '9':
390 return skip_number( parse_state );
391 case '\0':
392 daw_json_error( true, ErrorReason::InvalidStartOfValue, parse_state );
393 }
394 if constexpr( ParseState::is_unchecked_input ) {
395 DAW_UNREACHABLE( );
396 } else {
397 daw_json_error( true, ErrorReason::InvalidStartOfValue, parse_state );
398 }
399 }
400 } // namespace json_details
401 } // namespace DAW_JSON_VER
402} // 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.
DAW_ATTRIB_NOINLINE void daw_json_error(bool b, ErrorReason reason)
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