DAW JSON Link
Loading...
Searching...
No Matches
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
12
15
16#include <daw/daw_traits.h>
17#include <daw/daw_uint_buffer.h>
18
19#include <cstddef>
20#include <type_traits>
21
22namespace daw::json {
23 inline namespace DAW_JSON_VER {
24 namespace json_details::string_quote {
25 template<std::size_t N, char c>
26 constexpr UInt8 test_at_byte( UInt64 b ) {
27 auto const lhs = b & ( daw::UInt64{0xFF} << ( N * 8U ) );
28 using rhs = daw::constant<to_uint64( static_cast<unsigned char>( c ) )
29 << ( N * 8U )>;
30 return to_uint8( not( lhs - rhs::value ) );
31 }
32
33 template<std::size_t N, char c>
34 DAW_ATTRIB_INLINE constexpr UInt8 test_at_byte( UInt32 b ) {
35 auto const lhs = b & ( daw::UInt32{0xFF} << ( N * 8U ) );
36 using rhs = daw::constant<to_uint32( static_cast<unsigned char>( c ) )
37 << ( N * 8U )>;
38 return to_uint8( not( lhs - rhs::value ) );
39 }
40
41 constexpr void skip_to_first8( daw::not_null<char const *> &first,
42 daw::not_null<char const *> const last ) {
43 bool keep_going = last - first >= 8;
44 while( keep_going ) {
45 auto buff = daw::to_uint64_buffer( first.get( ) );
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 constexpr void skip_to_first4( daw::not_null<char const *> &first,
72 daw::not_null<char const *> 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.get( ) );
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
92 namespace string_quote_parser {
93 template<typename ParseState>
94 [[nodiscard]] static constexpr std::size_t
95 parse_nq_uncheck( ParseState &parse_state ) {
96 std::ptrdiff_t need_slow_path = -1;
97 auto first =
98 daw::not_null<char const *>( daw::never_null, parse_state.first );
99 auto const last =
100 daw::not_null<char const *>( daw::never_null, parse_state.last );
101 // This is a logic error to happen.
102 // daw_json_assert_weak( first != '"', "Unexpected quote", parse_state
103 // );
104 if( not json_details::use_constexpr_exec_mode<
105 typename ParseState::exec_tag_t>( ) ) {
106 first =
107 mem_skip_until_end_of_string<true,
108 typename ParseState::exec_tag_t>(
109 first, last, need_slow_path );
110 } else {
111 {
112 auto const sz = last - first;
113 if( sz >= 8 ) {
114 skip_to_first8( first, last );
115 } else if( sz >= 4 ) {
116 skip_to_first4( first, last );
117 }
118 }
119 while( *first != '"' ) {
120 while( []( char c ) DAW_JSON_CPP23_STATIC_CALL_OP {
121 return daw::nsc_and( c != '"', c != '\\' );
122 }( *first ) ) {
123 ++first;
124 }
125 if( *first == '\\' ) {
126 if( need_slow_path < 0 ) {
127 need_slow_path = first - parse_state.first;
128 }
129 first += 2;
130 } else {
131 break;
132 }
133 }
134 }
135 if constexpr( std::is_same_v<char const *, typename ParseState::iterator> ) {
136 parse_state.first = first;
137 } else {
138 parse_state.first += first.get( ) - parse_state.first;
139 }
140 return static_cast<std::size_t>( need_slow_path );
141 }
142
143 template<typename ParseState>
144 [[nodiscard]] static constexpr std::size_t
145 parse_nq_check( ParseState &parse_state ) {
146
147 std::ptrdiff_t need_slow_path = -1;
148 auto first = daw::not_null<char const *>( parse_state.first );
149 auto const last =
150 daw::not_null<char const *>( parse_state.class_last );
151
152 // The SIMD scan does not validate control characters or escapes,
153 // strict parsing keeps the validating loops below
154 if( not ParseState::exclude_special_escapes and
155 not json_details::use_constexpr_exec_mode<
156 typename ParseState::exec_tag_t>( ) ) {
157 first =
158 mem_skip_until_end_of_string<false,
159 typename ParseState::exec_tag_t>(
160 first, last, need_slow_path );
161 } else {
162 if constexpr( not ParseState::exclude_special_escapes ) {
163 if( auto const l =
164 daw::not_null<char const *>( parse_state.last );
165 l - first >= 8 ) {
166 skip_to_first8( first, l );
167 } else if( last - first >= 4 ) {
168 skip_to_first4( first, l );
169 }
170 }
171 if constexpr( ParseState::is_zero_terminated_string ) {
172 if constexpr( ParseState::exclude_special_escapes ) {
173 while( *first != '\0' ) {
174 char c = *first;
175 daw_json_ensure( static_cast<unsigned char>( c ) >= 0x20U,
176 ErrorReason::InvalidString,
177 parse_state );
178 if( c == '\\' ) {
179 daw_json_ensure( last - first > 1,
180 ErrorReason::InvalidString,
181 parse_state );
182 if( need_slow_path < 0 ) {
183 need_slow_path = first - parse_state.first;
184 }
185 ++first;
186 c = *first;
187 switch( c ) {
188 case '"':
189 case '\\':
190 case '/':
191 case 'b':
192 case 'f':
193 case 'n':
194 case 'r':
195 case 't':
196 case 'u':
197 break;
198 default:
199 daw_json_error( true, ErrorReason::InvalidString, parse_state );
200 }
201 } else if( c == '"' ) {
202 break;
203 }
204 ++first;
205 }
206 } else {
207 while( daw::nsc_and( *first != 0, *first != '"' ) ) {
208 while( daw::nsc_and(
209 *first != 0, *first != '"', *first != '\\' ) ) {
210 ++first;
211 }
212
213 if( daw::nsc_and( *first != 0, *first == '\\' ) ) {
214 if( need_slow_path < 0 ) {
215 need_slow_path = first - parse_state.first;
216 }
217 first += 2;
218 } else {
219 break;
220 }
221 }
222 }
223 } else {
224 if constexpr( ParseState::exclude_special_escapes ) {
225 while( first < last ) {
226 char c = *first;
227 daw_json_ensure( static_cast<unsigned char>( c ) >= 0x20U,
228 ErrorReason::InvalidString,
229 parse_state );
230 if( c == '\\' ) {
231 daw_json_ensure( last - first > 1,
232 ErrorReason::InvalidString,
233 parse_state );
234 if( need_slow_path < 0 ) {
235 need_slow_path = first - parse_state.first;
236 }
237 ++first;
238 c = *first;
239 switch( c ) {
240 case '"':
241 case '\\':
242 case '/':
243 case 'b':
244 case 'f':
245 case 'n':
246 case 'r':
247 case 't':
248 case 'u':
249 break;
250 default:
251 daw_json_error( true, ErrorReason::InvalidString, parse_state );
252 }
253 } else if( c == '"' ) {
254 break;
255 }
256 ++first;
257 }
258 } else {
259 while( first < last and *first != '"' ) {
260 while( first < last and
261 daw::nsc_and( *first != '"', *first != '\\' ) ) {
262 ++first;
263 }
264
265 if( first < last and *first == '\\' ) {
266 if( need_slow_path < 0 ) {
267 need_slow_path = first - parse_state.first;
268 }
269 first += 2;
270 } else {
271 break;
272 }
273 }
274 }
275 }
276 }
277 if constexpr( ParseState::is_zero_terminated_string ) {
279 *first == '"', ErrorReason::InvalidString, parse_state );
280 } else {
281 daw_json_assert_weak( first < last and *first == '"',
282 ErrorReason::InvalidString,
283 parse_state );
284 }
285 if constexpr( std::is_same_v<char const *, typename ParseState::iterator> ) {
286 parse_state.first = first;
287 } else {
288 parse_state.first += first.get( ) - parse_state.first;
289 }
290 return static_cast<std::size_t>( need_slow_path );
291 }
292
293 template<typename ParseState>
294 [[nodiscard]] DAW_ATTRIB_FLATTEN static constexpr std::size_t
295 parse_nq( ParseState &parse_state ) {
296 if constexpr( ParseState::is_unchecked_input ) {
297 return parse_nq_uncheck( parse_state );
298 } else {
299 return parse_nq_check( parse_state );
300 }
301 }
302 } // namespace string_quote_parser
303 } // namespace json_details::string_quote
304 } // namespace DAW_JSON_VER
305} // 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.
#define DAW_JSON_CPP23_STATIC_CALL_OP
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