DAW JSON Link
daw_json_parse_string_quote.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 "daw_json_assert.h"
13 #include "version.h"
14 
15 #include <daw/daw_traits.h>
16 #include <daw/daw_uint_buffer.h>
17 
18 #include <ciso646>
19 #include <cstddef>
20 #include <type_traits>
21 
22 namespace daw::json {
23  inline namespace DAW_JSON_VER {
24  namespace json_details::string_quote {
25  template<std::size_t N, char c>
26  inline constexpr UInt8 test_at_byte( UInt64 b ) {
27  auto const lhs = b & ( 0xFF_u64 << ( N * 8U ) );
28  constexpr auto rhs = to_uint64( static_cast<unsigned char>( c ) )
29  << ( N * 8U );
30  return to_uint8( not( lhs - rhs ) );
31  }
32 
33  template<std::size_t N, char c>
34  DAW_ATTRIB_INLINE inline constexpr UInt8 test_at_byte( UInt32 b ) {
35  auto const lhs = b & ( 0xFF_u32 << ( N * 8U ) );
36  constexpr auto rhs = to_uint32( static_cast<unsigned char>( c ) )
37  << ( N * 8U );
38  return to_uint8( not( lhs - rhs ) );
39  }
40 
41  template<typename CharT>
42  inline constexpr void skip_to_first8( CharT *&first, CharT *const last ) {
43  bool keep_going = last - first >= 8;
44  while( keep_going ) {
45  auto buff = daw::to_uint64_buffer( first );
46  auto const q7 = test_at_byte<7U, '"'>( buff );
47  auto const q6 = test_at_byte<6U, '"'>( buff );
48  auto const q5 = test_at_byte<5U, '"'>( buff );
49  auto const q4 = test_at_byte<4U, '"'>( buff );
50  auto const q3 = test_at_byte<3U, '"'>( buff );
51  auto const q2 = test_at_byte<2U, '"'>( buff );
52  auto const q1 = test_at_byte<1U, '"'>( buff );
53  auto const q0 = test_at_byte<0U, '"'>( buff );
54  auto const s7 = test_at_byte<7U, '\\'>( buff );
55  auto const s6 = test_at_byte<6U, '\\'>( buff );
56  auto const s5 = test_at_byte<5U, '\\'>( buff );
57  auto const s4 = test_at_byte<4U, '\\'>( buff );
58  auto const s3 = test_at_byte<3U, '\\'>( buff );
59  auto const s2 = test_at_byte<2U, '\\'>( buff );
60  auto const s1 = test_at_byte<1U, '\\'>( buff );
61  auto const s0 = test_at_byte<0U, '\\'>( buff );
62 
63  keep_going = not( q0 | q1 | q2 | q3 | q4 | q5 | q6 | q7 | s0 | s1 |
64  s2 | s3 | s4 | s5 | s6 | s7 );
65  keep_going = keep_going & static_cast<bool>( last - first >= 16 );
66  first += static_cast<int>( keep_going ) * 8;
67  }
68  first -= *( first - 1 ) == '\\' ? 1 : 0;
69  }
70 
71  template<typename CharT>
72  inline constexpr void skip_to_first4( CharT *&first, CharT *const last ) {
73  bool keep_going = last - first >= 4;
74  while( keep_going ) {
75  // Need to look for escapes as this is fast path
76  auto buff = daw::to_uint32_buffer( first );
77  auto const q3 = test_at_byte<3U, '"'>( buff );
78  auto const q2 = test_at_byte<2U, '"'>( buff );
79  auto const q1 = test_at_byte<1U, '"'>( buff );
80  auto const q0 = test_at_byte<0U, '"'>( buff );
81  auto const s3 = test_at_byte<3U, '\\'>( buff );
82  auto const s2 = test_at_byte<2U, '\\'>( buff );
83  auto const s1 = test_at_byte<1U, '\\'>( buff );
84  auto const s0 = test_at_byte<0U, '\\'>( buff );
85  keep_going = not( q0 | q1 | q2 | q3 | s0 | s1 | s2 | s3 );
86  keep_going = keep_going & static_cast<bool>( last - first >= 8 );
87  first += static_cast<int>( keep_going ) * 4;
88  }
89  first -= *( first - 1 ) == '\\' ? 1 : 0;
90  }
91 
93  template<typename ParseState>
94  [[nodiscard]] static constexpr auto parse_nq( ParseState &parse_state )
95  -> std::enable_if_t<ParseState::is_unchecked_input, std::size_t> {
96 
97  using CharT = typename ParseState::CharT;
98  std::ptrdiff_t need_slow_path = -1;
99  CharT *first = parse_state.first;
100  CharT *const last = parse_state.last;
101  // This is a logic error to happen.
102  // daw_json_assert_weak( first != '"', "Unexpected quote", parse_state
103  // );
104  if constexpr( traits::not_same_v<typename ParseState::exec_tag_t,
105  constexpr_exec_tag> ) {
106  first = mem_skip_until_end_of_string<true>(
107  ParseState::exec_tag, first, last, need_slow_path );
108  } else {
109 
110  {
111  auto const sz = last - first;
112  if( sz >= 8 ) {
113  skip_to_first8( first, last );
114  } else if( sz >= 4 ) {
115  skip_to_first4( first, last );
116  }
117  }
118  while( *first != '"' ) {
119  while( []( char c ) {
120  return ( c != '"' ) & ( c != '\\' );
121  }( *first ) ) {
122  ++first;
123  }
124  if( *first == '\\' ) {
125  if( need_slow_path < 0 ) {
126  need_slow_path = first - parse_state.first;
127  }
128  first += 2;
129  } else {
130  break;
131  }
132  }
133  }
134  parse_state.first = first;
135  return static_cast<std::size_t>( need_slow_path );
136  }
137 
138  template<typename ParseState>
139  [[nodiscard]] static constexpr auto parse_nq( ParseState &parse_state )
140  -> std::enable_if_t<not ParseState::is_unchecked_input, std::size_t> {
141 
142  using CharT = typename ParseState::CharT;
143  std::ptrdiff_t need_slow_path = -1;
144  CharT *first = parse_state.first;
145  CharT *const last = parse_state.class_last;
146  if constexpr( traits::not_same_v<typename ParseState::exec_tag_t,
147  constexpr_exec_tag> ) {
148  first = mem_skip_until_end_of_string<false>(
149  ParseState::exec_tag, first, last, need_slow_path );
150  } else {
151  if constexpr( not ParseState::exclude_special_escapes ) {
152  if( CharT *const l = parse_state.last; l - first >= 8 ) {
153  skip_to_first8( first, l );
154  } else if( last - first >= 4 ) {
155  skip_to_first4( first, l );
156  }
157  }
158  if constexpr( ParseState::is_zero_terminated_string ) {
159  if constexpr( ParseState::exclude_special_escapes ) {
160  while( *first != '\0' ) {
161  char c = *first;
162  daw_json_assert( static_cast<unsigned char>( c ) >= 0x20U,
163  ErrorReason::InvalidString, parse_state );
164  if( c == '\\' ) {
165  daw_json_assert( last - first > 1,
166  ErrorReason::InvalidString, parse_state );
167  if( need_slow_path < 0 ) {
168  need_slow_path = first - parse_state.first;
169  }
170  ++first;
171  c = *first;
172  switch( c ) {
173  case '"':
174  case '\\':
175  case '/':
176  case 'b':
177  case 'f':
178  case 'n':
179  case 'r':
180  case 't':
181  case 'u':
182  break;
183  default:
184  daw_json_error( ErrorReason::InvalidString, parse_state );
185  }
186  } else if( c == '"' ) {
187  break;
188  }
189  ++first;
190  }
191  } else {
192  while( ( *first != 0 ) & ( *first != '"' ) ) {
193  while( ( *first != 0 ) & ( *first != '"' ) &
194  ( *first != '\\' ) ) {
195  ++first;
196  }
197 
198  if( ( ( *first != 0 ) & ( *first == '\\' ) ) ) {
199  if( need_slow_path < 0 ) {
200  need_slow_path = first - parse_state.first;
201  }
202  first += 2;
203  } else {
204  break;
205  }
206  }
207  }
208  } else {
209  if constexpr( ParseState::exclude_special_escapes ) {
210  while( first < last ) {
211  char c = *first;
212  daw_json_assert( static_cast<unsigned char>( c ) >= 0x20U,
213  ErrorReason::InvalidString, parse_state );
214  if( c == '\\' ) {
215  daw_json_assert( last - first > 1,
216  ErrorReason::InvalidString, parse_state );
217  if( need_slow_path < 0 ) {
218  need_slow_path = first - parse_state.first;
219  }
220  ++first;
221  c = *first;
222  switch( c ) {
223  case '"':
224  case '\\':
225  case '/':
226  case 'b':
227  case 'f':
228  case 'n':
229  case 'r':
230  case 't':
231  case 'u':
232  break;
233  default:
234  daw_json_error( ErrorReason::InvalidString, parse_state );
235  }
236  } else if( c == '"' ) {
237  break;
238  }
239  ++first;
240  }
241  } else {
242  while( first < last and *first != '"' ) {
243  while( first < last and
244  ( ( *first != '"' ) & ( *first != '\\' ) ) ) {
245  ++first;
246  }
247 
248  if( first < last and *first == '\\' ) {
249  if( need_slow_path < 0 ) {
250  need_slow_path = first - parse_state.first;
251  }
252  first += 2;
253  } else {
254  break;
255  }
256  }
257  }
258  }
259  }
260  if constexpr( ParseState::is_zero_terminated_string ) {
261  daw_json_assert_weak( *first == '"', ErrorReason::InvalidString,
262  parse_state );
263  } else {
264  daw_json_assert_weak( first < last and *first == '"',
265  ErrorReason::InvalidString, parse_state );
266  }
267  parse_state.first = first;
268  return static_cast<std::size_t>( need_slow_path );
269  }
270  };
271  } // namespace json_details::string_quote
272  } // namespace DAW_JSON_VER
273 } // 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
constexpr void skip_to_first4(CharT *&first, CharT *const last)
Definition: daw_json_parse_string_quote.h:72
constexpr void skip_to_first8(CharT *&first, CharT *const last)
Definition: daw_json_parse_string_quote.h:42
constexpr UInt8 test_at_byte(UInt64 b)
Definition: daw_json_parse_string_quote.h:26
DAW_ATTRIB_NOINLINE void daw_json_error(ErrorReason reason)
Definition: daw_json_assert.h:39
Definition: daw_from_json.h:22
Definition: daw_json_exec_modes.h:19
static constexpr auto parse_nq(ParseState &parse_state) -> std::enable_if_t< not ParseState::is_unchecked_input, std::size_t >
Definition: daw_json_parse_string_quote.h:139
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