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"
15 #include "daw_json_parse_digit.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 
31 namespace 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 
64  daw_json_assert_weak( parse_state.has_more( ),
65  ErrorReason::InvalidString, parse_state );
66  return skip_string_nq( 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( parse_state.is_at_token_after_value( ),
84  ErrorReason::InvalidEndOfValue, parse_state );
85  result.counter = static_cast<bool>( true );
86  return result;
87  }
88 
89  template<typename ParseState>
90  [[nodiscard]] constexpr ParseState skip_false( ParseState &parse_state ) {
91  auto result = parse_state;
92  if constexpr( ( ParseState::is_zero_terminated_string or
93  ParseState::is_unchecked_input ) ) {
94  parse_state.remove_prefix( 5 );
95  } else {
96  parse_state.remove_prefix( );
97  daw_json_assert( parse_state.starts_with( "alse" ),
98  ErrorReason::InvalidFalse, parse_state );
99  parse_state.remove_prefix( 4 );
100  }
101  result.last = parse_state.first;
102  parse_state.trim_left( );
103  daw_json_assert_weak( parse_state.is_at_token_after_value( ),
104  ErrorReason::InvalidEndOfValue, parse_state );
105  result.counter = static_cast<bool>( false );
106  return result;
107  }
108 
109  template<typename ParseState>
110  [[nodiscard]] constexpr ParseState skip_null( ParseState &parse_state ) {
111  if constexpr( ( ParseState::is_zero_terminated_string or
112  ParseState::is_unchecked_input ) ) {
113  parse_state.remove_prefix( 4 );
114  } else {
115  parse_state.remove_prefix( );
116  daw_json_assert( parse_state.starts_with( "ull" ),
117  ErrorReason::InvalidNull, parse_state );
118  parse_state.remove_prefix( 3 );
119  }
120  daw_json_assert_weak( parse_state.has_more( ),
121  ErrorReason::UnexpectedEndOfData, parse_state );
122  parse_state.trim_left( );
123  daw_json_assert_weak( parse_state.is_at_token_after_value( ),
124  ErrorReason::UnexpectedEndOfData, parse_state );
125  auto result = parse_state;
126  result.first = nullptr;
127  result.last = nullptr;
128  return result;
129  }
130 
131  template<bool skip_end_check, typename CharT>
132  DAW_ATTRIB_FLATINLINE [[nodiscard]] inline constexpr CharT *
133  skip_digits( CharT *first, CharT *const last ) {
134  (void)last; // only used inside if constexpr and gcc9 warns
135  unsigned dig = parse_digit( *first );
136  while( dig < 10 ) {
137  ++first;
138  if constexpr( not skip_end_check ) {
139  if( DAW_UNLIKELY( first >= last ) ) {
140  break;
141  }
142  }
143  dig = parse_digit( *first );
144  }
145  return first;
146  }
147  /***
148  * Skip a number and store the position of it's components in the returned
149  * ParseState
150  */
151 
152 #if false and defined( DAW_CX_BIT_CAST )
153  template<typename ParseState,
154  std::enable_if_t<( ParseState::is_unchecked_input or
155  ParseState::is_zero_terminated_string ),
156  std::nullptr_t> = nullptr>
157  [[nodiscard]] constexpr ParseState
158  skip_number( ParseState &parse_state ) {
159  using CharT = typename ParseState::CharT;
160 
161  auto result = parse_state;
162  CharT *first = parse_state.first;
163  CharT *const last = parse_state.last;
164 
165  if( *first == '-' ) {
166  ++first;
167  }
168 
169  first = count_digits( first, last );
170 
171  CharT *decimal = nullptr;
172  if( *first == '.' ) {
173  decimal = first++;
174  first = count_digits( first, last );
175  }
176 
177  CharT *exp = nullptr;
178  char const maybe_e = *first;
179  if( ( maybe_e == 'e' ) | ( maybe_e == 'E' ) ) {
180  exp = ++first;
181  char const maybe_sign = *first;
182  if( ( maybe_sign == '+' ) | ( maybe_sign == '-' ) ) {
183  ++first;
184  }
185  first = count_digits( first, last );
186  }
187 
188  daw_json_assert_weak( first <= last, ErrorReason::UnexpectedEndOfData );
189 
190  parse_state.first = first;
191  result.last = first;
192  result.class_first = decimal;
193  result.class_last = exp;
194  return result;
195  }
196 
197  template<typename ParseState,
198  std::enable_if_t<not( ParseState::is_unchecked_input or
199  ParseState::is_zero_terminated_string ),
200  std::nullptr_t> = nullptr>
201 #else
202  template<typename ParseState>
203 #endif
204  [[nodiscard]] constexpr ParseState
205  skip_number( ParseState &parse_state ) {
206  using CharT = typename ParseState::CharT;
207  daw_json_assert_weak( parse_state.has_more( ),
208  ErrorReason::UnexpectedEndOfData, parse_state );
209 
210  auto result = parse_state;
211  CharT *first = parse_state.first;
212  CharT *const last = parse_state.last;
213  if constexpr( ParseState::allow_leading_zero_plus ) {
214  if( *first == '-' ) {
215  ++first;
216  }
217  } else {
218  switch( *first ) {
219  case '-':
220  ++first;
221  break;
222  case '+':
223  daw_json_error( ErrorReason::InvalidNumberStart, parse_state );
224  case '0':
225  if( last - first > 1 ) {
227  not parse_policy_details::is_number( *std::next( first ) ),
228  ErrorReason::InvalidNumberStart, parse_state );
229  }
230  break;
231  }
232  }
233 
234  if( DAW_LIKELY( first < last ) ) {
235  first =
236  skip_digits<( ParseState::is_zero_terminated_string or
237  ParseState::is_unchecked_input )>( first, last );
238  }
239 
240  CharT *decimal = nullptr;
241  if( ( ( ParseState::is_zero_terminated_string or
242  ParseState::is_unchecked_input ) or
243  first < last ) and
244  ( *first == '.' ) ) {
245  decimal = first;
246  ++first;
247  if( DAW_LIKELY( first < last ) ) {
248  first =
249  skip_digits<( ParseState::is_zero_terminated_string or
250  ParseState::is_unchecked_input )>( first, last );
251  }
252  }
253  CharT *exp = nullptr;
254  unsigned dig = parse_digit( *first );
255  if( ( ( ParseState::is_zero_terminated_string or
256  ParseState::is_unchecked_input ) or
257  ( first < last ) ) &
258  ( ( dig == parsed_constants::e_char ) |
259  ( dig == parsed_constants::E_char ) ) ) {
260  exp = first;
261  ++first;
262  daw_json_assert_weak( first < last, ErrorReason::UnexpectedEndOfData,
263  [&] {
264  auto r = parse_state;
265  r.first = first;
266  return r;
267  }( ) );
268  dig = parse_digit( *first );
269  if( ( dig == parsed_constants::plus_char ) |
270  ( dig == parsed_constants::minus_char ) ) {
271  ++first;
272  }
273  daw_json_assert_weak( first < last and parse_digit( *first ) < 10U,
274  ErrorReason::InvalidNumber );
275 
276  if( DAW_LIKELY( first < last ) ) {
277  first =
278  skip_digits<( ParseState::is_zero_terminated_string or
279  ParseState::is_unchecked_input )>( first, last );
280  }
281  }
282 
283  parse_state.first = first;
284  result.last = first;
285  result.class_first = decimal;
286  result.class_last = exp;
287  return result;
288  }
289 
290  /***
291  * When we don't know ahead of time what we are skipping switch on the
292  * first value and call that types specific skipper
293  * TODO: Investigate if there is a difference for the times we know what
294  * the member should be if that can increase performance
295  */
296  template<typename ParseState>
297  [[nodiscard]] inline constexpr ParseState
298  skip_value( ParseState &parse_state ) {
299  daw_json_assert_weak( parse_state.has_more( ),
300  ErrorReason::UnexpectedEndOfData, parse_state );
301 
302  // reset counter
303  parse_state.counter = 0;
304  switch( parse_state.front( ) ) {
305  case '"':
306  return skip_string( parse_state );
307  case '[':
308  return parse_state.skip_array( );
309  case '{':
310  return parse_state.skip_class( );
311  case 't':
312  return skip_true( parse_state );
313  case 'f':
314  return skip_false( parse_state );
315  case 'n':
316  return skip_null( parse_state );
317  case '-':
318  case '0':
319  case '1':
320  case '2':
321  case '3':
322  case '4':
323  case '5':
324  case '6':
325  case '7':
326  case '8':
327  case '9':
328  return skip_number( parse_state );
329  case '\0':
330  daw_json_error( ErrorReason::InvalidStartOfValue, parse_state );
331  }
332  if constexpr( ParseState::is_unchecked_input ) {
333  DAW_UNREACHABLE( );
334  } else {
335  daw_json_error( ErrorReason::InvalidStartOfValue, parse_state );
336  }
337  }
338 
339  /***
340  * Used in json_array_iterator::operator++( ) as we know the type we are
341  * skipping
342  */
343  template<typename JsonMember, typename ParseState>
344  [[nodiscard]] DAW_ATTRIB_FLATINLINE inline constexpr ParseState
345  skip_known_value( ParseState &parse_state ) {
346  daw_json_assert_weak( parse_state.has_more( ),
347  ErrorReason::UnexpectedEndOfData, parse_state );
348  if constexpr( JsonMember::expected_type == JsonParseTypes::Date or
349  JsonMember::expected_type == JsonParseTypes::StringRaw or
350  JsonMember::expected_type ==
352  JsonMember::expected_type == JsonParseTypes::Custom ) {
353  // json string encodings
354  daw_json_assert_weak( parse_state.front( ) == '"',
355  ErrorReason::InvalidString, parse_state );
356  parse_state.remove_prefix( );
358  } else if constexpr( JsonMember::expected_type ==
360  JsonMember::expected_type ==
362  JsonMember::expected_type ==
364  JsonMember::expected_type ==
366  JsonMember::expected_type ==
368  // All literals
369  return skip_number( parse_state );
370  } else if constexpr( JsonMember::expected_type ==
372  daw_json_assert_weak( parse_state.is_opening_bracket_checked( ),
373  ErrorReason::InvalidArrayStart, parse_state );
374  return parse_state.skip_array( );
375  } else if constexpr( JsonMember::expected_type ==
377  daw_json_assert_weak( parse_state.is_opening_brace_checked( ),
378  ErrorReason::InvalidClassStart, parse_state );
379  return parse_state.skip_class( );
380  } else {
381  return skip_value( parse_state );
382  }
383  }
384 
385  template<typename ParseState>
386  [[nodiscard]] inline constexpr ParseState
387  skip_literal( ParseState &parse_state ) {
388  daw_json_assert_weak( parse_state.has_more( ),
389  ErrorReason::UnexpectedEndOfData, parse_state );
390 
391  // reset counter
392  parse_state.counter = 0;
393  switch( parse_state.front( ) ) {
394  case 't':
395  return skip_true( parse_state );
396  case 'f':
397  return skip_false( parse_state );
398  case 'n':
399  return skip_null( parse_state );
400  case '-':
401  case '0':
402  case '1':
403  case '2':
404  case '3':
405  case '4':
406  case '5':
407  case '6':
408  case '7':
409  case '8':
410  case '9':
411  return skip_number( parse_state );
412  case '\0':
413  daw_json_error( ErrorReason::InvalidStartOfValue, parse_state );
414  }
415  if constexpr( ParseState::is_unchecked_input ) {
416  DAW_UNREACHABLE( );
417  } else {
418  daw_json_error( ErrorReason::InvalidStartOfValue, parse_state );
419  }
420  }
421  } // namespace json_details
422  } // namespace DAW_JSON_VER
423 } // namespace daw::json
#define daw_json_assert_weak(Bool,...)
Definition: daw_json_assert.h:189
#define daw_json_assert(Bool,...)
Definition: daw_json_assert.h:178
ParseState & parse_state
Definition: daw_json_parse_class.h:201
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:90
constexpr ParseState skip_value(ParseState &parse_state)
Definition: daw_json_skip.h:298
constexpr DAW_ATTRIB_FLATTEN CharT * count_digits(CharT *first, CharT *last)
Definition: daw_count_digits.h:63
constexpr ParseState skip_literal(ParseState &parse_state)
Definition: daw_json_skip.h:387
constexpr ParseState skip_number(ParseState &parse_state)
Definition: daw_json_skip.h:205
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:110
constexpr DAW_ATTRIB_FLATINLINE ParseState skip_string_nq(ParseState &parse_state)
Definition: daw_json_skip.h:39
constexpr DAW_ATTRIB_FLATINLINE CharT * skip_digits(CharT *first, CharT *const last)
Definition: daw_json_skip.h:133
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:345
constexpr DAW_ATTRIB_FLATINLINE bool is_number(char c)
Definition: daw_json_parse_policy_policy_details.h:34
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
Definition: version.h:11