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