DAW JSON Link
Loading...
Searching...
No Matches
daw_json_parse_std_string.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
17
18#include <daw/algorithms/daw_algorithm_copy.h>
19#include <daw/algorithms/daw_algorithm_copy_n.h>
20#include <daw/daw_data_end.h>
21#include <daw/daw_likely.h>
22#include <daw/daw_not_null.h>
23#include <daw/daw_span.h>
24
25#include <cstddef>
26#include <daw/stdinc/data_access.h>
27#include <daw/stdinc/range_access.h>
28#include <type_traits>
29
30namespace daw::json {
31 inline namespace DAW_JSON_VER {
32 namespace json_details {
33 [[nodiscard]] static constexpr UInt8 to_nibble( unsigned char chr ) {
34 int const b = static_cast<int>( chr );
35 int const maskLetter = ( ( '9' - b ) >> 31 );
36 int const maskSmall = ( ( 'Z' - b ) >> 31 );
37 int const offset = '0' + ( maskLetter & int( 'A' - '0' - 10 ) ) +
38 ( maskSmall & int( 'a' - 'A' ) );
39 auto const result = static_cast<unsigned>( b - offset );
40 return to_uint8( result );
41 }
42
43 template<bool is_unchecked_input>
44 [[nodiscard]] static constexpr UInt16
45 byte_from_nibbles( daw::not_null<char const *> &first ) {
46 auto const n0 = to_nibble( static_cast<unsigned char>( *first++ ) );
47 auto const n1 = to_nibble( static_cast<unsigned char>( *first++ ) );
48 if constexpr( not is_unchecked_input ) {
49 daw_json_ensure( n0 < 16 and n1 < 16, ErrorReason::InvalidUTFEscape );
50 }
51 return to_uint16( ( n0 << 4U ) | n1 );
52 }
53
54 static constexpr char u32toC( UInt32 value ) {
55 return static_cast<char>( static_cast<unsigned char>( value ) );
56 }
57
58 template<typename ParseState>
59 [[nodiscard]] static constexpr daw::not_null<char *>
60 decode_utf16( ParseState &parse_state, daw::not_null<char *> it ) {
61 daw_json_assert_weak( parse_state.size( ) >= 5,
62 ErrorReason::UnexpectedEndOfData,
63 parse_state );
64 auto first = daw::not_null<char const *>( parse_state.first );
65 ++first;
66 UInt32 cp =
67 to_uint32(
68 byte_from_nibbles<ParseState::is_unchecked_input>( first ) )
69 << 8U;
70 cp |= byte_from_nibbles<ParseState::is_unchecked_input>( first );
71 if( cp <= 0x7FU ) {
72 *it++ = static_cast<char>( static_cast<unsigned char>( cp ) );
73 parse_state.first = input_pointer( parse_state.first, first.get( ) );
74 return it;
75 }
76
77 //******************************
79 cp < 0xDC00U or cp > 0xDFFFU, ErrorReason::InvalidUTFEscape,
80 parse_state ); // Lone/unpaired low surrogate
81 if( 0xD800U <= cp and cp <= 0xDBFFU ) {
82 cp = ( cp - 0xD800U ) * 0x400U;
84 *first == '\\', ErrorReason::InvalidUTFEscape,
85 parse_state ); // High surrogate must be followed by \\u
86 ++first;
88 ( parse_state.last - first >= 5 ) and *first == 'u',
89 ErrorReason::InvalidUTFEscape,
90 parse_state ); // Expected parse_state to start with a \\u
91 ++first;
92 auto trailing =
93 to_uint32(
94 byte_from_nibbles<ParseState::is_unchecked_input>( first ) )
95 << 8U;
96 trailing |=
97 byte_from_nibbles<ParseState::is_unchecked_input>( first );
99 0xDC00U <= trailing and trailing <= 0xDFFFU,
100 ErrorReason::InvalidUTFEscape,
101 parse_state ); // Expected a low surrogate to complete the pair
102 trailing -= 0xDC00U;
103 cp += trailing;
104 cp += 0x10000;
105 }
106 // UTF32-> UTF8
107 if( cp >= 0x10000U ) {
108 // 4 bytes
109 char const enc3 = u32toC( ( cp & 0b0011'1111U ) | 0b1000'0000U );
110 char const enc2 =
111 u32toC( ( ( cp >> 6U ) & 0b0011'1111U ) | 0b1000'0000U );
112 char const enc1 =
113 u32toC( ( ( cp >> 12U ) & 0b0011'1111U ) | 0b1000'0000U );
114 char const enc0 = u32toC( ( cp >> 18U ) | 0b1111'0000U );
115 *it++ = enc0;
116 *it++ = enc1;
117 *it++ = enc2;
118 *it++ = enc3;
119 parse_state.first = input_pointer( parse_state.first, first.get( ) );
120 return it;
121 }
122 //******************************
123 if( cp >= 0x800U ) {
124 // 3 bytes
125 char const enc2 = u32toC( ( cp & 0b0011'1111U ) | 0b1000'0000U );
126 char const enc1 =
127 u32toC( ( ( cp >> 6U ) & 0b0011'1111U ) | 0b1000'0000U );
128 char const enc0 = u32toC( ( cp >> 12U ) | 0b1110'0000U );
129 *it++ = enc0;
130 *it++ = enc1;
131 *it++ = enc2;
132 parse_state.first = input_pointer( parse_state.first, first.get( ) );
133 return it;
134 }
135 //******************************
136 // cp >= 0x80U
137 // 2 bytes
138 char const enc1 = u32toC( ( cp & 0b0011'1111U ) | 0b1000'0000U );
139 char const enc0 = u32toC( ( cp >> 6U ) | 0b1100'0000U );
140 *it++ = enc0;
141 *it++ = enc1;
142 parse_state.first = input_pointer( parse_state.first, first.get( ) );
143 return it;
144 }
145
146 template<typename ParseState, typename Appender>
147 static constexpr void decode_utf16( ParseState &parse_state,
148 Appender &app ) {
149 auto first = daw::not_null<char const *>( parse_state.first );
150 ++first;
151 UInt32 cp =
152 to_uint32(
153 byte_from_nibbles<ParseState::is_unchecked_input>( first ) )
154 << 8U;
155 cp |= byte_from_nibbles<ParseState::is_unchecked_input>( first );
156 if( cp <= 0x7FU ) {
157 app( u32toC( cp ) );
158 parse_state.first = input_pointer( parse_state.first, first.get( ) );
159 return;
160 }
162 cp < 0xDC00U or cp > 0xDFFFU, ErrorReason::InvalidUTFEscape,
163 parse_state ); // Lone/unpaired low surrogate
164 if( 0xD800U <= cp and cp <= 0xDBFFU ) {
165 cp = ( cp - 0xD800U ) * 0x400U;
167 *first == '\\', ErrorReason::InvalidUTFEscape,
168 parse_state ); // High surrogate must be followed by \\u
169 ++first;
171 *first == 'u', ErrorReason::InvalidUTFEscape, parse_state );
172 ++first;
173 auto trailing =
174 to_uint32(
175 byte_from_nibbles<ParseState::is_unchecked_input>( first ) )
176 << 8U;
177 trailing |=
178 byte_from_nibbles<ParseState::is_unchecked_input>( first );
180 0xDC00U <= trailing and trailing <= 0xDFFFU,
181 ErrorReason::InvalidUTFEscape,
182 parse_state ); // Expected a low surrogate to complete the pair
183 trailing -= 0xDC00U;
184 cp += trailing;
185 cp += 0x10000;
186 }
187 // UTF32-> UTF8
188 if( cp >= 0x10000U ) {
189 // 4 bytes
190 char const enc3 = u32toC( ( cp & 0b0011'1111U ) | 0b1000'0000U );
191 char const enc2 =
192 u32toC( ( ( cp >> 6U ) & 0b0011'1111U ) | 0b1000'0000U );
193 char const enc1 =
194 u32toC( ( ( cp >> 12U ) & 0b0011'1111U ) | 0b1000'0000U );
195 char const enc0 = u32toC( ( cp >> 18U ) | 0b1111'0000U );
196 app( enc0 );
197 app( enc1 );
198 app( enc2 );
199 app( enc3 );
200 parse_state.first = input_pointer( parse_state.first, first.get( ) );
201 return;
202 }
203 if( cp >= 0x800U ) {
204 // 3 bytes
205 char const enc2 = u32toC( ( cp & 0b0011'1111U ) | 0b1000'0000U );
206 char const enc1 =
207 u32toC( ( ( cp >> 6U ) & 0b0011'1111U ) | 0b1000'0000U );
208 char const enc0 = u32toC( ( cp >> 12U ) | 0b1110'0000U );
209 app( enc0 );
210 app( enc1 );
211 app( enc2 );
212 parse_state.first = input_pointer( parse_state.first, first.get( ) );
213 return;
214 }
215 // cp >= 0x80U
216 // 2 bytes
217 char const enc1 = u32toC( ( cp & 0b0011'1111U ) | 0b1000'0000U );
218 char const enc0 = u32toC( ( cp >> 6U ) | 0b1100'0000U );
219 app( enc0 );
220 app( enc1 );
221 parse_state.first = input_pointer( parse_state.first, first.get( ) );
222 }
223
224 namespace parse_tokens {
225 inline constexpr char escape_quotes[] = "\\\"";
226 }
227
228 // Fast path for parsing escaped strings to a std::string with the default
229 // appender
230 template<bool AllowHighEight, typename JsonMember, bool KnownBounds,
231 typename ParseState>
232 [[nodiscard]] constexpr json_result_t<JsonMember>
233 parse_string_known_stdstring( ParseState &parse_state ) {
234 DAW_CPP23_STATIC_LOCAL constexpr bool is_insitu =
235 JsonMember::expected_type == JsonParseTypes::StringInsitu;
236
237 static_assert(
238 not is_insitu or ParseState::allow_string_mutation,
239 "In-situ strings can only be parsed from writable input documents" );
240 static_assert( not is_insitu or
241 std::is_same_v<typename ParseState::iterator, char *>,
242 "In-situ strings require a mutable character buffer" );
243
244 bool const has_quote = parse_state.front( ) == '"';
245 if( has_quote ) {
246 parse_state.remove_prefix( );
247 }
248
249 using string_type = json_base_type_t<JsonMember>;
250 using result_t =
251 std::conditional_t<is_insitu, daw::span<char>, string_type>;
252 result_t result = [&] {
253 if constexpr( is_insitu ) {
254 return daw::span( std::data( parse_state ),
255 std::size( parse_state ) );
256 } else {
257 return string_type(
258 std::size( parse_state ) + 1U +
259 static_cast<unsigned>( has_quote ),
260 '\0',
261 parse_state.template get_allocator_for<char>( ) );
262 }
263 }( );
264 daw::not_null<char *> it = std::data( result );
265
266 if( auto const first_slash =
267 static_cast<std::ptrdiff_t>( parse_state.counter ) - 1;
268 first_slash > 1 ) {
269 if constexpr( is_insitu ) {
270 it += first_slash;
271 } else {
272 it =
273 daw::algorithm::copy_n( parse_state.first,
274 it.get( ),
275 static_cast<std::size_t>( first_slash ) )
276 .output;
277 }
278 parse_state.first += first_slash;
279 }
280
281 DAW_CPP23_STATIC_LOCAL constexpr auto in_json_string =
282 []( auto const &r ) DAW_JSON_CPP23_STATIC_CALL_OP -> bool {
283 if constexpr( KnownBounds or not ParseState::is_unchecked_input ) {
284 if( not DAW_LIKELY( r.has_more( ) ) ) {
285 return false;
286 }
287 }
288 return DAW_LIKELY( r.front( ) != '"' );
289 };
290
291 while( in_json_string( parse_state ) ) {
292 {
293 daw::not_null<char const *> first = parse_state.first;
294 daw::not_null<char const *> const last = parse_state.last;
295
296 if( not json_details::use_constexpr_exec_mode<
297 typename ParseState::exec_tag_t>( ) ) {
298 first = mem_move_to_next_of<
299 ( not KnownBounds and
300 ( ParseState::is_unchecked_input or
301 ParseState::is_zero_terminated_string ) ),
302 typename ParseState::exec_tag_t,
303 '"',
304 '\\'>( first, last );
305 } else {
306 while( first < last and *first != '"' and *first != '\\' ) {
307 ++first;
308 }
309 daw_json_assert_weak( KnownBounds or first < last,
310 ErrorReason::UnexpectedEndOfData,
311 parse_state );
312 }
313
314 auto const run_size = first.get( ) - parse_state.first;
316 static_cast<std::ptrdiff_t>( result.size( ) ) -
317 ( it.get( ) - result.data( ) ) >=
318 run_size,
319 ErrorReason::UnexpectedEndOfData );
320
321 if( it.get( ) == parse_state.first ) {
322 it += run_size;
323 } else {
324 it = daw::algorithm::copy(
325 parse_state.first, first.get( ), it.get( ) );
326 }
327 parse_state.first =
328 input_pointer( parse_state.first, first.get( ) );
329 }
330 if( parse_state.front( ) == '\\' ) {
331 parse_state.remove_prefix( );
332 daw_json_assert_weak( not parse_state.is_space_unchecked( ),
333 ErrorReason::InvalidUTFCodepoint,
334 parse_state );
335 switch( parse_state.front( ) ) {
336 case 'b':
337 *it++ = '\b';
338 parse_state.remove_prefix( );
339 break;
340 case 'f':
341 *it++ = '\f';
342 parse_state.remove_prefix( );
343 break;
344 case 'n':
345 *it++ = '\n';
346 parse_state.remove_prefix( );
347 break;
348 case 'r':
349 *it++ = '\r';
350 parse_state.remove_prefix( );
351 break;
352 case 't':
353 *it++ = '\t';
354 parse_state.remove_prefix( );
355 break;
356 case 'u':
357 it = decode_utf16( parse_state, it );
358 break;
359 case '/':
360 case '\\':
361 case '"':
362 *it++ = parse_state.front( );
363 parse_state.remove_prefix( );
364 break;
365 default:
366 // Every legal escape character (", \, /, b, f, n, r, t, u) is
367 // handled by an explicit case above; anything else following a
368 // backslash is not a valid JSON escape.
369 daw_json_assert_weak( false, ErrorReason::InvalidString,
370 parse_state );
371 *it++ = parse_state.front( );
372 parse_state.remove_prefix( );
373 }
374 } else {
375 daw_json_assert_weak( not has_quote or
376 parse_state.is_quotes_checked( ),
377 ErrorReason::InvalidString,
378 parse_state );
379 }
380 daw_json_assert_weak( not has_quote or parse_state.has_more( ),
381 ErrorReason::UnexpectedEndOfData,
382 parse_state );
383 }
384 auto const sz = static_cast<std::size_t>(
385 std::distance( std::data( result ), it.get( ) ) );
387 std::size( result ) >= sz, ErrorReason::InvalidString, parse_state );
388
389 if constexpr( is_insitu ) {
390 *it = '"';
391 ++it;
392 while( it != ( parse_state.last + 1 ) ) {
393 *it = ' ';
394 ++it;
395 }
396 result = result.subspan( 0, sz );
397 } else {
398 result.resize( sz );
399 }
400
401 if constexpr( not is_insitu and
402 std::is_convertible_v<result_t,
403 json_result_t<JsonMember>> ) {
404 return result;
405 } else {
406 using constructor_t = json_constructor_t<JsonMember>;
407 return construct_value<json_result_t<JsonMember>, constructor_t>(
408 parse_state, std::data( result ), std::size( result ) );
409 }
410 }
411 } // namespace json_details
412 } // namespace DAW_JSON_VER
413} // 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
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20