14#if defined( DAW_JSON_HAS_SIMD )
17#include <daw/daw_span.h>
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 );
37 result.front( ) ==
'[', ErrorReason::InvalidArrayStart, result );
38 return { result.data( ), result.size( ) };
41 [[nodiscard]]
constexpr std::uint64_t
42 prefix_xor( std::uint64_t bits )
noexcept {
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;
58 [[nodiscard]]
constexpr bool last_bit( std::uint64_t bits,
59 std::size_t count )
noexcept {
61 ( ( bits >> ( count - 1U ) ) & std::uint64_t{ 1 } ) != 0;
64 template<
typename simd_type>
66#if defined( DAW_JSON_HAS_STD_SIMD )
71 simd_type splat(
char value )
noexcept {
73 static_cast<typename simd_type::value_type
>( value ) );
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 ) ) | ... );
81 template<
typename simd_type, std::size_t block_size,
typename CharT,
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;
89 return daw::simd::flag_convert;
92 if( count == block_size ) {
93 return daw::simd::unchecked_load<simd_type>(
94 daw::span( first, block_size ), flags );
96 return daw::simd::partial_load<simd_type>( daw::span( first, count ),
105 struct simd_json_classifier_state {
106 bool in_string =
false;
107 bool escaped =
false;
108 bool previous_scalar =
false;
111 struct simd_array_grammar_state {
112 bool previous_event_was_value =
false;
113 bool saw_value =
false;
114 bool started =
false;
118 template<
typename CharT>
119 struct simd_json_block_base {
122 using simd_type = daw::simd::vec<CharT>;
124 static constexpr std::size_t block_size =
125 static_cast<std::size_t
>( simd_type::size( ) );
127 char const *data =
nullptr;
128 std::size_t size = 0;
130 std::uint64_t scalar_start = 0;
131 std::uint64_t comma = 0;
132 std::uint64_t array_end = 0;
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 );
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 );
158 state.previous_event_was_value =
false;
160 if( state.previous_event_was_value ) {
161 auto error_state = ParseState( data + lane, last );
163 true, ErrorReason::InvalidEndOfValue, error_state );
165 state.previous_event_was_value =
true;
166 state.saw_value =
true;
168 events &= events - 1U;
172 template<
typename ParseState>
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 );
182 template<JsonBaseParseTypes ExpectedType,
typename CharT>
183 struct simd_json_block;
185 template<
typename 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;
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;
199 template<JsonParseTypes NumberType>
200 struct simd_number_span_types;
204 using span = number_span;
205 using pending_span = pending_number_span;
210 using span = integer_span;
211 using pending_span = pending_integer_span;
216 using span = integer_span;
217 using pending_span = pending_integer_span;
220 template<
typename CharT>
222 : simd_json_block_base<CharT> {
223 std::uint64_t boolean_start = 0;
224 std::uint64_t boolean_values = 0;
227 template<
typename 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;
235 template<JsonBaseParseTypes ExpectedType,
typename CharT>
236 class simd_json_classifier {
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;
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" );
253 using state_type = simd_json_classifier_state;
255 static DAW_JSON_SIMD_CONSTEXPR
auto is_whitespace( simd_type input ) {
256 return simd_details::one_of<' ', '\t', '\n', '\r'>( input );
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
267 std::size_t number_span_offset = 0 ) {
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;
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 =
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 ) ) ) );
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 );
297 auto const separators =
298 simd_details::one_of<' ', ']', ','>( input );
299 return not separators;
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' ) ) )
310 return std::uint64_t{ 0 };
313 auto const decimal_point_bits = [&] {
314 if constexpr( NumberType == JsonParseTypes::Real ) {
315 return ( input == simd_details::splat<simd_type>(
'.' ) )
319 return std::uint64_t{ 0 };
322 auto const exponent_marker_bits = [&] {
323 if constexpr( NumberType == JsonParseTypes::Real ) {
324 return simd_details::one_of<'e', 'E'>( input ).to_ullong( ) &
327 return std::uint64_t{ 0 };
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>(
'-' ) )
342 return start_bits & scalar_start_bits & valid_bits;
344 return scalar_start_bits;
348 block_type result{ };
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;
365 result.invalid_number_characters =
366 scalar_bits & ~valid_integer_characters;
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;
377 non_number_characters == 0
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 )
384 if constexpr( NumberType == JsonParseTypes::Real ) {
385 if( pending.decimal_point ==
nullptr ) {
386 auto const points = decimal_point_bits & number_mask;
388 pending.decimal_point =
389 first + daw::cxmath::count_trailing_zeros(
390 static_cast<std::uint64_t
>( points ) );
393 if( pending.exponent_marker ==
nullptr ) {
394 auto const markers = exponent_marker_bits & number_mask;
396 pending.exponent_marker =
397 first + daw::cxmath::count_trailing_zeros(
398 static_cast<std::uint64_t
>( markers ) );
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 };
411 number_spans[number_span_offset + result.number_span_count++] =
412 integer_span{ pending.first, first + first_lane + length };
414 pending_number = { };
416 pending_number = pending;
420 if( pending_number.first !=
nullptr ) {
421 append_number_span( pending_number, 0 );
424 auto starts = number_start_bits;
425 while( starts != 0 ) {
427 static_cast<std::size_t
>( daw::cxmath::count_trailing_zeros(
428 static_cast<std::uint64_t
>( starts ) ) );
429 if constexpr( NumberType == JsonParseTypes::Real ) {
431 pending_number_span{ first + lane,
nullptr,
nullptr }, lane );
433 append_number_span( pending_integer_span{ first + lane }, lane );
435 starts &= starts - 1U;
440 template<
bool Val
idateStart = 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;
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>(
']' );
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 =
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;
479 auto const boolean_start_bits = boolean_bits & scalar_start_bits;
481 block_type result{ };
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 );
493 template<
bool Val
idateStart = 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;
498 simd_details::load<simd_type, block_size, CharT>( first, count );
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>(
']' );
506 auto const valid_bits = low_bits( count );
507 auto const backslash_bits = backslash.to_ullong( ) & valid_bits;
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 ) ) &
519 auto const escape_bits = escape_and_terminal & backslash_bits;
520 state.escaped = simd_details::last_bit( escape_bits, count );
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 } ) ) &
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 =
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;
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;
560 auto const string_end_bits = quote_bits & ~in_string_bits;
562 block_type result{ };
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;
#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.
@ Signed
Number - Floating Point.
@ Unsigned
Number - Signed Integer.
@ Bool
Number - Unsigned Integer.
JsonBaseParseTypes
The fundamental JSON types.
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.