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