DAW JSON Link
Loading...
Searching...
No Matches
daw_json_simd_iterator_common.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
13
14#if defined( DAW_JSON_HAS_SIMD )
16
17#include <daw/daw_span.h>
18
19#include <array>
20#include <cstddef>
21#include <cstdint>
22#include <iterator>
23#include <string_view>
24#include <type_traits>
25
26namespace daw::json {
27 inline namespace DAW_JSON_VER {
28 namespace json_details::simd_details {
29 template<typename ParseState>
30 [[nodiscard]] constexpr std::string_view
31 find_array_range( std::string_view document,
32 daw::string_view start_path ) {
33 auto [result, is_found] = json_details::find_range<ParseState>(
34 { document.data( ), document.size( ) }, start_path );
35 daw_json_ensure( is_found, ErrorReason::JSONPathNotFound );
37 result.front( ) == '[', ErrorReason::InvalidArrayStart, result );
38 return { result.data( ), result.size( ) };
39 }
40
41 [[nodiscard]] constexpr std::uint64_t
42 prefix_xor( std::uint64_t bits ) noexcept {
43 bits ^= bits << 1U;
44 bits ^= bits << 2U;
45 bits ^= bits << 4U;
46 bits ^= bits << 8U;
47 bits ^= bits << 16U;
48 bits ^= bits << 32U;
49 return bits;
50 }
51
52 [[nodiscard]] constexpr std::uint64_t
53 low_bits( std::size_t count ) noexcept {
54 return count == 64 ? ~std::uint64_t{ 0 }
55 : ( std::uint64_t{ 1 } << count ) - 1;
56 }
57
58 [[nodiscard]] constexpr bool last_bit( std::uint64_t bits,
59 std::size_t count ) noexcept {
60 return count != 0 and
61 ( ( bits >> ( count - 1U ) ) & std::uint64_t{ 1 } ) != 0;
62 }
63
64 template<typename simd_type>
65 [[nodiscard]]
66#if defined( DAW_JSON_HAS_STD_SIMD )
67 consteval
68#else
69 DAW_ATTRIB_INLINE
70#endif
71 simd_type splat( char value ) noexcept {
72 return simd_type(
73 static_cast<typename simd_type::value_type>( value ) );
74 }
75
76 template<auto... values, typename simd_type>
77 [[nodiscard]] DAW_JSON_SIMD_CONSTEXPR auto one_of( simd_type value ) {
78 return ( ( value == splat<simd_type>( values ) ) | ... );
79 }
80
81 template<typename simd_type, std::size_t block_size, typename CharT,
82 typename Chr>
83 [[nodiscard]] DAW_JSON_SIMD_CONSTEXPR simd_type
84 load( Chr const *first, std::size_t count ) {
85 static constexpr auto flags = [] {
86 if constexpr( std::is_same_v<CharT, Chr> ) {
87 return daw::simd::flag_default;
88 } else {
89 return daw::simd::flag_convert;
90 }
91 }( );
92 if( count == block_size ) {
93 return daw::simd::unchecked_load<simd_type>(
94 daw::span( first, block_size ), flags );
95 }
96 return daw::simd::partial_load<simd_type>( daw::span( first, count ),
97 flags );
98 }
99
105 struct simd_json_classifier_state {
106 bool in_string = false;
107 bool escaped = false;
108 bool previous_scalar = false;
109 };
110
111 struct simd_array_grammar_state {
112 bool previous_event_was_value = false;
113 bool saw_value = false;
114 bool started = false;
115 bool ended = false;
116 };
117
118 template<typename CharT>
119 struct simd_json_block_base {
120 // Classify one native SIMD register at a time. The iterators aggregate
121 // results from as many blocks as are needed to fill their caches.
122 using simd_type = daw::simd::vec<CharT>;
123
124 static constexpr std::size_t block_size =
125 static_cast<std::size_t>( simd_type::size( ) );
126
127 char const *data = nullptr;
128 std::size_t size = 0;
129
130 std::uint64_t scalar_start = 0;
131 std::uint64_t comma = 0;
132 std::uint64_t array_end = 0;
133 };
134
135 template<typename ParseState>
136 constexpr void validate_array_events( char const *data, char const *last,
137 std::uint64_t value_starts,
138 std::uint64_t commas,
139 std::uint64_t array_end,
140 simd_array_grammar_state &state ) {
141 auto events = value_starts | commas | array_end;
142 while( events != 0 ) {
143 auto const lane = static_cast<std::size_t>(
144 daw::cxmath::count_trailing_zeros( events ) );
145 auto const bit = std::uint64_t{ 1 } << lane;
146 if( ( array_end & bit ) != 0 ) {
147 if( state.saw_value and not state.previous_event_was_value ) {
148 auto error_state = ParseState( data + lane, last );
149 daw_json_error( true, ErrorReason::TrailingComma, error_state );
150 }
151 state.ended = true;
152 } else if( ( commas & bit ) != 0 ) {
153 if( not state.previous_event_was_value ) {
154 auto error_state = ParseState( data + lane, last );
156 true, ErrorReason::InvalidStartOfValue, error_state );
157 }
158 state.previous_event_was_value = false;
159 } else {
160 if( state.previous_event_was_value ) {
161 auto error_state = ParseState( data + lane, last );
163 true, ErrorReason::InvalidEndOfValue, error_state );
164 }
165 state.previous_event_was_value = true;
166 state.saw_value = true;
167 }
168 events &= events - 1U;
169 }
170 }
171
172 template<typename ParseState>
173 constexpr void
174 validate_array_ended( char const *last,
175 simd_array_grammar_state const &state ) {
176 if( state.started and not state.ended ) {
177 auto error_state = ParseState( last, last );
178 daw_json_error( true, ErrorReason::UnexpectedEndOfData, error_state );
179 }
180 }
181
182 template<JsonBaseParseTypes ExpectedType, typename CharT>
183 struct simd_json_block;
184
185 template<typename CharT>
186 struct simd_json_block<JsonBaseParseTypes::Number, CharT>
187 : simd_json_block_base<CharT> {
188 static constexpr std::size_t number_span_capacity =
189 ( simd_json_block_base<CharT>::block_size + 1U ) / 2U;
190
191 std::uint64_t number_start = 0;
192 std::uint64_t number_characters = 0;
193 std::uint64_t decimal_points = 0;
194 std::uint64_t exponent_markers = 0;
195 std::uint64_t invalid_number_characters = 0;
196 std::size_t number_span_count = 0;
197 };
198
199 template<JsonParseTypes NumberType>
200 struct simd_number_span_types;
201
202 template<>
203 struct simd_number_span_types<JsonParseTypes::Real> {
204 using span = number_span;
205 using pending_span = pending_number_span;
206 };
207
208 template<>
209 struct simd_number_span_types<JsonParseTypes::Signed> {
210 using span = integer_span;
211 using pending_span = pending_integer_span;
212 };
213
214 template<>
215 struct simd_number_span_types<JsonParseTypes::Unsigned> {
216 using span = integer_span;
217 using pending_span = pending_integer_span;
218 };
219
220 template<typename CharT>
221 struct simd_json_block<JsonBaseParseTypes::Bool, CharT>
222 : simd_json_block_base<CharT> {
223 std::uint64_t boolean_start = 0;
224 std::uint64_t boolean_values = 0;
225 };
226
227 template<typename CharT>
228 struct simd_json_block<JsonBaseParseTypes::String, CharT>
229 : simd_json_block_base<CharT> {
230 std::uint64_t string_start = 0;
231 std::uint64_t string_end = 0;
232 std::uint64_t escape_characters = 0;
233 };
234
235 template<JsonBaseParseTypes ExpectedType, typename CharT>
236 class simd_json_classifier {
237 static_assert(
238 ExpectedType == JsonBaseParseTypes::Number or
239 ExpectedType == JsonBaseParseTypes::Bool or
240 ExpectedType == JsonBaseParseTypes::String,
241 "simd_json_classifier supports only Number, Bool, and String" );
242 using block_type = simd_json_block<ExpectedType, CharT>;
243 using simd_type = typename block_type::simd_type;
244 using simd_value_type = typename simd_type::value_type;
245
246 static constexpr std::size_t block_size = block_type::block_size;
247 static constexpr std::size_t number_span_capacity =
248 ( block_size + 1U ) / 2U;
249 static_assert( block_size <= 64,
250 "The classifier bit set stores at most 64 SIMD lanes" );
251
252 public:
253 using state_type = simd_json_classifier_state;
254
255 static DAW_JSON_SIMD_CONSTEXPR auto is_whitespace( simd_type input ) {
256 return simd_details::one_of<' ', '\t', '\n', '\r'>( input );
257 }
258
259 template<JsonParseTypes NumberType, bool ValidateStart = true,
260 std::size_t NumberSpanCapacity>
261 [[nodiscard]] static DAW_JSON_SIMD_CONSTEXPR block_type classify_number(
262 char const *first, std::size_t count, state_type &state,
263 std::array<typename simd_number_span_types<NumberType>::span,
264 NumberSpanCapacity> &number_spans,
265 typename simd_number_span_types<NumberType>::pending_span
266 &pending_number,
267 std::size_t number_span_offset = 0 ) {
268
269 static_assert( ExpectedType == JsonBaseParseTypes::Number );
270 static_assert( NumberType == JsonParseTypes::Real or
271 NumberType == JsonParseTypes::Signed or
272 NumberType == JsonParseTypes::Unsigned );
273 static_assert( NumberSpanCapacity >= number_span_capacity );
274 count = count < block_size ? count : block_size;
275 auto const input =
276 simd_details::load<simd_type, block_size, CharT>( first, count );
277 auto const comma = input == simd_details::splat<simd_type>( ',' );
278 auto const array_end = input == simd_details::splat<simd_type>( ']' );
279 auto const valid_bits = simd_details::low_bits( count );
280 auto const array_end_bits = array_end.to_ullong( ) & valid_bits;
281 auto const active_bits =
282 array_end_bits == 0
283 ? valid_bits
284 : simd_details::low_bits(
285 static_cast<std::size_t>( daw::cxmath::count_trailing_zeros(
286 static_cast<std::uint64_t>( array_end_bits ) ) ) );
287
288 auto const scalar = [&] {
289 if constexpr( ValidateStart ) {
290 auto const whitespace = is_whitespace( input );
291 auto const operators = simd_details::one_of<']', ','>( input );
292 return not( whitespace | operators );
293 } else {
294 // Unchecked parsing assumes valid JSON. All JSON whitespace is
295 // at or below space, so the four whitespace comparisons
296 // collapse to one while retaining array separators.
297 auto const separators =
298 simd_details::one_of<' ', ']', ','>( input );
299 return not separators;
300 }
301 }( );
302 auto const scalar_bits = scalar.to_ullong( ) & active_bits;
303 auto const digit_bits = [&] {
304 if constexpr( ValidateStart ) {
305 return ( ( input >= simd_details::splat<simd_type>( '0' ) ) &
306 ( input <= simd_details::splat<simd_type>( '9' ) ) )
307 .to_ullong( ) &
308 scalar_bits;
309 } else {
310 return std::uint64_t{ 0 };
311 }
312 }( );
313 auto const decimal_point_bits = [&] {
314 if constexpr( NumberType == JsonParseTypes::Real ) {
315 return ( input == simd_details::splat<simd_type>( '.' ) )
316 .to_ullong( ) &
317 scalar_bits;
318 } else {
319 return std::uint64_t{ 0 };
320 }
321 }( );
322 auto const exponent_marker_bits = [&] {
323 if constexpr( NumberType == JsonParseTypes::Real ) {
324 return simd_details::one_of<'e', 'E'>( input ).to_ullong( ) &
325 scalar_bits;
326 } else {
327 return std::uint64_t{ 0 };
328 }
329 }( );
330 auto const follows_scalar_bits =
331 ( scalar_bits << 1U ) |
332 ( state.previous_scalar ? std::uint64_t{ 1 } : 0 );
333 state.previous_scalar = simd_details::last_bit( scalar_bits, count );
334 auto const scalar_start_bits = scalar_bits & ~follows_scalar_bits;
335 auto const number_start_bits = [&] {
336 if constexpr( ValidateStart ) {
337 auto start_bits = digit_bits;
338 if constexpr( NumberType != JsonParseTypes::Unsigned ) {
339 start_bits |= ( input == simd_details::splat<simd_type>( '-' ) )
340 .to_ullong( );
341 }
342 return start_bits & scalar_start_bits & valid_bits;
343 } else {
344 return scalar_start_bits;
345 }
346 }( );
347
348 block_type result{ };
349 result.data = first;
350 result.size = count;
351 result.scalar_start = scalar_start_bits;
352 result.comma = comma.to_ullong( ) & active_bits;
353 result.array_end = array_end_bits;
354 result.number_start = number_start_bits;
355 result.number_characters = scalar_bits;
356 result.decimal_points = decimal_point_bits;
357 result.exponent_markers = exponent_marker_bits;
358 if constexpr( ValidateStart and NumberType != JsonParseTypes::Real ) {
359 auto valid_integer_characters = digit_bits;
360 if constexpr( NumberType == JsonParseTypes::Signed ) {
361 auto const minus_bits =
362 ( input == simd_details::splat<simd_type>( '-' ) ).to_ullong( );
363 valid_integer_characters |= minus_bits & number_start_bits;
364 }
365 result.invalid_number_characters =
366 scalar_bits & ~valid_integer_characters;
367 }
368
369 auto append_number_span = [&]( auto pending,
370 std::size_t first_lane ) {
371 auto const remaining = count - first_lane;
372 auto const remaining_mask = simd_details::low_bits( remaining );
373 auto const characters =
374 ( scalar_bits >> first_lane ) & remaining_mask;
375 auto const non_number_characters = ( ~characters ) & remaining_mask;
376 auto const length =
377 non_number_characters == 0
378 ? remaining
379 : static_cast<std::size_t>( daw::cxmath::count_trailing_zeros(
380 static_cast<std::uint64_t>( non_number_characters ) ) );
381 auto const number_mask = simd_details::low_bits( length )
382 << first_lane;
383
384 if constexpr( NumberType == JsonParseTypes::Real ) {
385 if( pending.decimal_point == nullptr ) {
386 auto const points = decimal_point_bits & number_mask;
387 if( points != 0 ) {
388 pending.decimal_point =
389 first + daw::cxmath::count_trailing_zeros(
390 static_cast<std::uint64_t>( points ) );
391 }
392 }
393 if( pending.exponent_marker == nullptr ) {
394 auto const markers = exponent_marker_bits & number_mask;
395 if( markers != 0 ) {
396 pending.exponent_marker =
397 first + daw::cxmath::count_trailing_zeros(
398 static_cast<std::uint64_t>( markers ) );
399 }
400 }
401 }
402
403 if( length != remaining or count < block_size ) {
404 if constexpr( NumberType == JsonParseTypes::Real ) {
405 number_spans[number_span_offset + result.number_span_count++] =
406 number_span{ pending.first,
407 first + first_lane + length,
408 pending.decimal_point,
409 pending.exponent_marker };
410 } else {
411 number_spans[number_span_offset + result.number_span_count++] =
412 integer_span{ pending.first, first + first_lane + length };
413 }
414 pending_number = { };
415 } else {
416 pending_number = pending;
417 }
418 };
419
420 if( pending_number.first != nullptr ) {
421 append_number_span( pending_number, 0 );
422 }
423
424 auto starts = number_start_bits;
425 while( starts != 0 ) {
426 auto const lane =
427 static_cast<std::size_t>( daw::cxmath::count_trailing_zeros(
428 static_cast<std::uint64_t>( starts ) ) );
429 if constexpr( NumberType == JsonParseTypes::Real ) {
430 append_number_span(
431 pending_number_span{ first + lane, nullptr, nullptr }, lane );
432 } else {
433 append_number_span( pending_integer_span{ first + lane }, lane );
434 }
435 starts &= starts - 1U;
436 }
437 return result;
438 }
439
440 template<bool ValidateStart = true>
441 [[nodiscard]] static DAW_JSON_SIMD_CONSTEXPR block_type classify_bool(
442 char const *first, std::size_t count, state_type &state ) {
443 static_assert( ExpectedType == JsonBaseParseTypes::Bool );
444 count = count < block_size ? count : block_size;
445 auto const input =
446 simd_details::load<simd_type, block_size, CharT>( first, count );
447 auto const comma = input == simd_details::splat<simd_type>( ',' );
448 auto const array_end = input == simd_details::splat<simd_type>( ']' );
449
450 auto const true_start =
451 input == simd_details::splat<simd_type>( 't' );
452 auto const boolean_start =
453 true_start | ( input == simd_details::splat<simd_type>( 'f' ) );
454 auto const valid_bits = simd_details::low_bits( count );
455 auto const array_end_bits = array_end.to_ullong( ) & valid_bits;
456 auto const active_bits =
457 array_end_bits == 0
458 ? valid_bits
459 : simd_details::low_bits(
460 static_cast<std::size_t>( daw::cxmath::count_trailing_zeros(
461 static_cast<std::uint64_t>( array_end_bits ) ) ) );
462 auto const boolean_bits = boolean_start.to_ullong( ) & active_bits;
463 auto const scalar_start_bits = [&] {
464 if constexpr( ValidateStart ) {
465 auto const whitespace = is_whitespace( input );
466 auto const operators = simd_details::one_of<']', ','>( input );
467 auto const scalar_bits =
468 ( not( whitespace | operators ) ).to_ullong( ) & active_bits;
469 auto const follows_scalar_bits =
470 ( scalar_bits << 1U ) |
471 ( state.previous_scalar ? std::uint64_t{ 1 } : 0 );
472 state.previous_scalar =
473 simd_details::last_bit( scalar_bits, count );
474 return scalar_bits & ~follows_scalar_bits;
475 } else {
476 return boolean_bits;
477 }
478 }( );
479 auto const boolean_start_bits = boolean_bits & scalar_start_bits;
480
481 block_type result{ };
482 result.data = first;
483 result.size = count;
484 result.scalar_start = scalar_start_bits;
485 result.comma = comma.to_ullong( ) & active_bits;
486 result.array_end = array_end_bits;
487 result.boolean_start = boolean_start_bits;
488 result.boolean_values =
489 daw::simd_impl::compress_bits( true_start, boolean_start_bits );
490 return result;
491 }
492
493 template<bool ValidateStart = true>
494 [[nodiscard]] static DAW_JSON_SIMD_CONSTEXPR block_type classify_string(
495 char const *first, std::size_t count, state_type &state ) {
496 count = count < block_size ? count : block_size;
497 auto const input =
498 simd_details::load<simd_type, block_size, CharT>( first, count );
499
500 auto const quote = input == simd_details::splat<simd_type>( '"' );
501 auto const backslash =
502 input == simd_details::splat<simd_type>( '\\' );
503 auto const comma = input == simd_details::splat<simd_type>( ',' );
504 auto const array_end = input == simd_details::splat<simd_type>( ']' );
505
506 auto const valid_bits = low_bits( count );
507 auto const backslash_bits = backslash.to_ullong( ) & valid_bits;
508
509 DAW_CPP23_STATIC_LOCAL constexpr std::uint64_t odd_bits =
510 0xAAAAAAAAAAAAAAAAULL;
511 auto const previous_escaped = state.escaped ? std::uint64_t{ 1 } : 0;
512 auto const potential_escape = backslash_bits & ~previous_escaped;
513 auto const maybe_escaped = potential_escape << 1U;
514 auto const escape_and_terminal =
515 ( ( maybe_escaped | odd_bits ) - potential_escape ) ^ odd_bits;
516 auto const escaped_bits =
517 ( escape_and_terminal ^ ( backslash_bits | previous_escaped ) ) &
518 valid_bits;
519 auto const escape_bits = escape_and_terminal & backslash_bits;
520 state.escaped = simd_details::last_bit( escape_bits, count );
521
522 auto const quote_bits =
523 quote.to_ullong( ) & ~escaped_bits & valid_bits;
524 auto const in_string_bits =
525 ( simd_details::prefix_xor( quote_bits ) ^
526 ( state.in_string ? valid_bits : std::uint64_t{ 0 } ) ) &
527 valid_bits;
528 state.in_string = simd_details::last_bit( in_string_bits, count );
529 auto const string_tail_bits = in_string_bits ^ quote_bits;
530 auto const outside_string_bits = ~string_tail_bits & valid_bits;
531 auto const array_end_bits =
532 array_end.to_ullong( ) & outside_string_bits;
533 auto const active_bits =
534 array_end_bits == 0
535 ? valid_bits
536 : simd_details::low_bits(
537 static_cast<std::size_t>( daw::cxmath::count_trailing_zeros(
538 static_cast<std::uint64_t>( array_end_bits ) ) ) );
539 auto value_start_bits = std::uint64_t{ 0 };
540 auto string_start_bits = quote_bits & in_string_bits & active_bits;
541 if constexpr( ValidateStart ) {
542 auto const whitespace = is_whitespace( input );
543 auto const operators = simd_details::one_of<']', ','>( input );
544 auto const scalar = not( whitespace | operators );
545 auto const operator_bits = operators.to_ullong( ) & active_bits;
546 auto const scalar_bits = scalar.to_ullong( ) & active_bits;
547 auto const nonquote_scalar_bits = scalar_bits & ~quote_bits;
548 auto const follows_scalar_bits =
549 ( nonquote_scalar_bits << 1U ) |
550 ( state.previous_scalar ? std::uint64_t{ 1 } : 0 );
551 state.previous_scalar =
552 simd_details::last_bit( nonquote_scalar_bits, count );
553 auto const scalar_start_bits = scalar_bits & ~follows_scalar_bits;
554 value_start_bits =
555 scalar_start_bits & ~string_tail_bits & active_bits;
556 auto const structural_bits = ( operator_bits | scalar_start_bits ) &
557 ~string_tail_bits & active_bits;
558 string_start_bits &= structural_bits;
559 }
560 auto const string_end_bits = quote_bits & ~in_string_bits;
561
562 block_type result{ };
563 result.data = first;
564 result.size = count;
565 result.scalar_start = value_start_bits;
566 result.comma = comma.to_ullong( ) & outside_string_bits & active_bits;
567 result.array_end = array_end_bits;
568 result.string_start = string_start_bits;
569 result.string_end = string_end_bits & active_bits;
570 result.escape_characters = backslash_bits & active_bits;
571 return result;
572 }
573 };
574 } // namespace json_details::simd_details
575 } // namespace DAW_JSON_VER
576} // namespace daw::json
577
578#endif
#define daw_json_ensure(Bool,...)
Ensure that Bool is true. If false pass rest of args to daw_json_error.
DAW_ATTRIB_NOINLINE void daw_json_error(bool b, ErrorReason reason)
JsonParseTypes
The tags used by the parser to determine what parser to call.
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20