DAW JSON Link
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
11#include "version.h"
12
13#include "../daw_json_exception.h"
14#include "daw_json_assert.h"
18
19#include <daw/daw_attributes.h>
20#include <daw/daw_bit_cast.h>
21#include <daw/daw_likely.h>
22#include <daw/daw_unreachable.h>
23
24#if defined( DAW_CX_BIT_CAST )
25#include "daw_count_digits.h"
26#endif
27
28#include <ciso646>
29#include <iterator>
30
31namespace daw::json {
32 inline namespace DAW_JSON_VER {
33 namespace json_details {
34 /***
35 * Skip a string, after the initial quote has been skipped already
36 */
37 template<typename ParseState>
38 [[nodiscard]] DAW_ATTRIB_FLATINLINE inline constexpr ParseState
39 skip_string_nq( ParseState &parse_state ) {
40 auto result = parse_state;
41 result.counter =
43
44 daw_json_assert_weak( parse_state.front( ) == '"',
45 ErrorReason::InvalidString, 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<typename ParseState>
55 [[nodiscard]] DAW_ATTRIB_FLATINLINE inline constexpr ParseState
56 skip_string( ParseState &parse_state ) {
57 if( parse_state.empty( ) ) {
58 return parse_state;
59 }
60 daw_json_assert( parse_state.front( ) == '"',
61 ErrorReason::InvalidString, parse_state );
62 parse_state.remove_prefix( );
63
65 ErrorReason::InvalidString, parse_state );
67 }
68
69 template<typename ParseState>
70 [[nodiscard]] constexpr ParseState skip_true( ParseState &parse_state ) {
71 auto result = parse_state;
72 if constexpr( ( ParseState::is_zero_terminated_string or
73 ParseState::is_unchecked_input ) ) {
74 parse_state.remove_prefix( 4 );
75 } else {
76 parse_state.remove_prefix( );
77 daw_json_assert( parse_state.starts_with( "rue" ),
78 ErrorReason::InvalidTrue, parse_state );
79 parse_state.remove_prefix( 3 );
80 }
81 result.last = parse_state.first;
82 parse_state.trim_left( );
83 daw_json_assert_weak( not parse_state.has_more( ) or
84 parse_state.is_at_token_after_value( ),
85 ErrorReason::InvalidEndOfValue, parse_state );
86 result.counter = static_cast<bool>( true );
87 return result;
88 }
89
90 template<typename ParseState>
91 [[nodiscard]] constexpr ParseState skip_false( ParseState &parse_state ) {
92 auto result = parse_state;
93 if constexpr( ( ParseState::is_zero_terminated_string or
94 ParseState::is_unchecked_input ) ) {
95 parse_state.remove_prefix( 5 );
96 } else {
97 parse_state.remove_prefix( );
98 daw_json_assert( parse_state.starts_with( "alse" ),
99 ErrorReason::InvalidFalse, parse_state );
100 parse_state.remove_prefix( 4 );
101 }
102 result.last = parse_state.first;
103 parse_state.trim_left( );
104 daw_json_assert_weak( not parse_state.has_more( ) or
105 parse_state.is_at_token_after_value( ),
106 ErrorReason::InvalidEndOfValue, parse_state );
107 result.counter = static_cast<bool>( false );
108 return result;
109 }
110
111 template<typename ParseState>
112 [[nodiscard]] constexpr ParseState skip_null( ParseState &parse_state ) {
113 if constexpr( ( ParseState::is_zero_terminated_string or
114 ParseState::is_unchecked_input ) ) {
115 parse_state.remove_prefix( 4 );
116 } else {
117 parse_state.remove_prefix( );
118 daw_json_assert( parse_state.starts_with( "ull" ),
119 ErrorReason::InvalidNull, parse_state );
120 parse_state.remove_prefix( 3 );
121 }
123 ErrorReason::UnexpectedEndOfData, parse_state );
124 parse_state.trim_left( );
125 daw_json_assert_weak( not parse_state.has_more( ) or
126 parse_state.is_at_token_after_value( ),
127 ErrorReason::UnexpectedEndOfData, parse_state );
128 auto result = parse_state;
129 result.first = nullptr;
130 result.last = nullptr;
131 return result;
132 }
133
134 template<bool skip_end_check, typename CharT>
135 DAW_ATTRIB_FLATINLINE [[nodiscard]] inline constexpr CharT *
136 skip_digits( CharT *first, CharT *const last ) {
137 (void)last; // only used inside if constexpr and gcc9 warns
138 unsigned dig = parse_digit( *first );
139 while( dig < 10 ) {
140 ++first;
141 if constexpr( not skip_end_check ) {
142 if( DAW_UNLIKELY( first >= last ) ) {
143 break;
144 }
145 }
146 dig = parse_digit( *first );
147 }
148 return first;
149 }
150 /***
151 * Skip a number and store the position of it's components in the returned
152 * ParseState
153 */
154
155 // DAW TODO: This branch has a bug that shows up in twitter_test2
156#if false and defined( DAW_CX_BIT_CAST )
157 template<typename ParseState,
158 std::enable_if_t<( ParseState::is_unchecked_input or
159 ParseState::is_zero_terminated_string ),
160 std::nullptr_t> = nullptr>
161 [[nodiscard]] constexpr ParseState
162 skip_number( ParseState &parse_state ) {
163 using CharT = typename ParseState::CharT;
164
165 auto result = parse_state;
166 CharT *first = parse_state.first;
167 CharT *const last = parse_state.last;
168
169 if( *first == '-' ) {
170 ++first;
171 }
172
173 first = count_digits( first, last );
174
175 CharT *decimal = nullptr;
176 if( *first == '.' ) {
177 decimal = first++;
178 first = count_digits( first, last );
179 }
180
181 CharT *exp = nullptr;
182 char const maybe_e = *first;
183 if( ( maybe_e == 'e' ) | ( maybe_e == 'E' ) ) {
184 exp = ++first;
185 char const maybe_sign = *first;
186 if( ( maybe_sign == '+' ) | ( maybe_sign == '-' ) ) {
187 ++first;
188 }
189 first = count_digits( first, last );
190 }
191
192 daw_json_assert_weak( first <= last, ErrorReason::UnexpectedEndOfData );
193
194 parse_state.first = first;
195 result.last = first;
196 result.class_first = decimal;
197 result.class_last = exp;
198 return result;
199 }
200
201 template<typename ParseState,
202 std::enable_if_t<not( ParseState::is_unchecked_input or
203 ParseState::is_zero_terminated_string ),
204 std::nullptr_t> = nullptr>
205#else
206 template<typename ParseState>
207#endif
208 [[nodiscard]] constexpr ParseState
209 skip_number( ParseState &parse_state ) {
210 using CharT = typename ParseState::CharT;
212 ErrorReason::UnexpectedEndOfData, parse_state );
213
214 auto result = parse_state;
215 CharT *first = parse_state.first;
216 CharT *const last = parse_state.last;
217 if constexpr( ParseState::allow_leading_zero_plus ) {
218 if( *first == '-' ) {
219 ++first;
220 }
221 } else {
222 switch( *first ) {
223 case '-':
224 ++first;
225 break;
226 case '+':
227 daw_json_error( ErrorReason::InvalidNumberStart, parse_state );
228 case '0':
229 if( last - first > 1 ) {
231 not parse_policy_details::is_number( *std::next( first ) ),
232 ErrorReason::InvalidNumberStart, parse_state );
233 }
234 break;
235 }
236 }
237
238 if( DAW_LIKELY( first < last ) ) {
239 first =
240 skip_digits<( ParseState::is_zero_terminated_string or
241 ParseState::is_unchecked_input )>( first, last );
242 }
243
244 CharT *decimal = nullptr;
245 if( ( ( ParseState::is_zero_terminated_string or
246 ParseState::is_unchecked_input ) or
247 first < last ) and
248 ( *first == '.' ) ) {
249 decimal = first;
250 ++first;
251 if( DAW_LIKELY( first < last ) ) {
252 first =
253 skip_digits<( ParseState::is_zero_terminated_string or
254 ParseState::is_unchecked_input )>( first, last );
255 }
256 }
257 CharT *exp = nullptr;
258 if constexpr( not( ParseState::is_zero_terminated_string or
259 ParseState::is_unchecked_input ) ) {
260 daw_json_assert( first < last, ErrorReason::UnexpectedEndOfData,
261 parse_state );
262 }
263 unsigned dig = parse_digit( *first );
264 if( ( dig == parsed_constants::e_char ) |
265 ( dig == parsed_constants::E_char ) ) {
266 exp = first;
267 ++first;
268 daw_json_assert_weak( first < last, ErrorReason::UnexpectedEndOfData,
269 [&] {
270 auto r = parse_state;
271 r.first = first;
272 return r;
273 }( ) );
274 dig = parse_digit( *first );
275 if( ( dig == parsed_constants::plus_char ) |
276 ( dig == parsed_constants::minus_char ) ) {
277 ++first;
278 }
279 daw_json_assert_weak( first < last and parse_digit( *first ) < 10U,
280 ErrorReason::InvalidNumber );
281
282 if( DAW_LIKELY( first < last ) ) {
283 first =
284 skip_digits<( ParseState::is_zero_terminated_string or
285 ParseState::is_unchecked_input )>( first, last );
286 }
287 }
288
289 parse_state.first = first;
290 result.last = first;
291 result.class_first = decimal;
292 result.class_last = exp;
293 return result;
294 }
295
296 /***
297 * When we don't know ahead of time what we are skipping switch on the
298 * first value and call that types specific skipper
299 * TODO: Investigate if there is a difference for the times we know what
300 * the member should be if that can increase performance
301 */
302 template<typename ParseState>
303 [[nodiscard]] inline constexpr ParseState
304 skip_value( ParseState &parse_state ) {
306 ErrorReason::UnexpectedEndOfData, parse_state );
307
308 // reset counter
309 parse_state.counter = 0;
310 switch( parse_state.front( ) ) {
311 case '"':
312 return skip_string( parse_state );
313 case '[':
314 return parse_state.skip_array( );
315 case '{':
316 return parse_state.skip_class( );
317 case 't':
318 return skip_true( parse_state );
319 case 'f':
320 return skip_false( parse_state );
321 case 'n':
322 return skip_null( parse_state );
323 case '-':
324 case '0':
325 case '1':
326 case '2':
327 case '3':
328 case '4':
329 case '5':
330 case '6':
331 case '7':
332 case '8':
333 case '9':
334 return skip_number( parse_state );
335 case '\0':
336 daw_json_error( ErrorReason::InvalidStartOfValue, parse_state );
337 }
338 if constexpr( ParseState::is_unchecked_input ) {
339 DAW_UNREACHABLE( );
340 } else {
341 daw_json_error( ErrorReason::InvalidStartOfValue, parse_state );
342 }
343 }
344
345 /***
346 * Used in json_array_iterator::operator++( ) as we know the type we are
347 * skipping
348 */
349 template<typename JsonMember, typename ParseState>
350 [[nodiscard]] DAW_ATTRIB_FLATINLINE inline constexpr ParseState
353 ErrorReason::UnexpectedEndOfData, parse_state );
354 if constexpr( JsonMember::expected_type == JsonParseTypes::Date or
355 JsonMember::expected_type == JsonParseTypes::StringRaw or
356 JsonMember::expected_type ==
358 JsonMember::expected_type == JsonParseTypes::Custom ) {
359 // json string encodings
360 daw_json_assert_weak( parse_state.front( ) == '"',
361 ErrorReason::InvalidString, parse_state );
362 parse_state.remove_prefix( );
364 } else if constexpr( JsonMember::expected_type ==
366 JsonMember::expected_type ==
368 JsonMember::expected_type ==
370 JsonMember::expected_type ==
372 JsonMember::expected_type ==
374 // All literals
375 return skip_number( parse_state );
376 } else if constexpr( JsonMember::expected_type ==
378 daw_json_assert_weak( parse_state.is_opening_bracket_checked( ),
379 ErrorReason::InvalidArrayStart, parse_state );
380 return parse_state.skip_array( );
381 } else if constexpr( JsonMember::expected_type ==
383 daw_json_assert_weak( parse_state.is_opening_brace_checked( ),
384 ErrorReason::InvalidClassStart, parse_state );
385 return parse_state.skip_class( );
386 } else {
387 return skip_value( parse_state );
388 }
389 }
390
391 template<typename ParseState>
392 [[nodiscard]] inline constexpr ParseState
393 skip_literal( ParseState &parse_state ) {
395 ErrorReason::UnexpectedEndOfData, parse_state );
396
397 // reset counter
398 parse_state.counter = 0;
399 switch( parse_state.front( ) ) {
400 case 't':
401 return skip_true( parse_state );
402 case 'f':
403 return skip_false( parse_state );
404 case 'n':
405 return skip_null( parse_state );
406 case '-':
407 case '0':
408 case '1':
409 case '2':
410 case '3':
411 case '4':
412 case '5':
413 case '6':
414 case '7':
415 case '8':
416 case '9':
417 return skip_number( parse_state );
418 case '\0':
419 daw_json_error( ErrorReason::InvalidStartOfValue, parse_state );
420 }
421 if constexpr( ParseState::is_unchecked_input ) {
422 DAW_UNREACHABLE( );
423 } else {
424 daw_json_error( ErrorReason::InvalidStartOfValue, parse_state );
425 }
426 }
427 } // namespace json_details
428 } // namespace DAW_JSON_VER
429} // namespace daw::json
#define daw_json_assert_weak(Bool,...)
Definition: daw_json_assert.h:190
#define daw_json_assert(Bool,...)
Definition: daw_json_assert.h:179
ParseState & parse_state
Definition: daw_json_parse_class.h:182
static constexpr unsigned plus_char
Definition: daw_json_parse_digit.h:29
static constexpr unsigned e_char
Definition: daw_json_parse_digit.h:27
static constexpr unsigned minus_char
Definition: daw_json_parse_digit.h:30
static constexpr unsigned E_char
Definition: daw_json_parse_digit.h:28
constexpr ParseState skip_true(ParseState &parse_state)
Definition: daw_json_skip.h:70
constexpr ParseState skip_false(ParseState &parse_state)
Definition: daw_json_skip.h:91
constexpr DAW_ATTRIB_FLATTEN CharT * count_digits(CharT *first, CharT *last)
Definition: daw_count_digits.h:63
constexpr ParseState skip_value(ParseState &parse_state)
Definition: daw_json_skip.h:304
constexpr DAW_ATTRIB_FLATINLINE CharT * skip_digits(CharT *first, CharT *const last)
Definition: daw_json_skip.h:136
constexpr ParseState skip_literal(ParseState &parse_state)
Definition: daw_json_skip.h:393
constexpr ParseState skip_number(ParseState &parse_state)
Definition: daw_json_skip.h:209
constexpr DAW_ATTRIB_FLATINLINE ParseState skip_string(ParseState &parse_state)
Definition: daw_json_skip.h:56
constexpr ParseState skip_null(ParseState &parse_state)
Definition: daw_json_skip.h:112
constexpr DAW_ATTRIB_FLATINLINE ParseState skip_string_nq(ParseState &parse_state)
Definition: daw_json_skip.h:39
static constexpr DAW_ATTRIB_FLATINLINE unsigned parse_digit(char c)
Definition: daw_json_parse_digit.h:19
constexpr DAW_ATTRIB_FLATINLINE ParseState skip_known_value(ParseState &parse_state)
Definition: daw_json_skip.h:351
constexpr DAW_ATTRIB_FLATINLINE bool is_number(char c)
Definition: daw_json_parse_policy_policy_details.h:34
@ Array
A class type with named members.
@ Date
String - Fully processed string.
@ Signed
Number - Floating Point.
@ Custom
Array - Each element has a key and a value submember.
@ Unsigned
Number - Signed Integer.
@ Null
An array with a fixed size. This allows for size_t/ptr like combinations.
@ StringEscaped
String - A raw string as is. Escapes are left in.
@ Bool
Number - Unsigned Integer.
DAW_ATTRIB_NOINLINE void daw_json_error(ErrorReason reason)
Definition: daw_json_assert.h:39
Definition: daw_from_json.h:22
static constexpr auto parse_nq(ParseState &parse_state) -> std::enable_if_t< ParseState::is_unchecked_input, std::size_t >
Definition: daw_json_parse_string_quote.h:94
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition: version.h:16