44 namespace json_details {
46 constexpr bool is_escaped( daw::not_null<char const *> ptr,
47 daw::not_null<char const *> min_ptr ) {
48 if( *( ptr - 1 ) !=
'\\' ) {
51 if( ( ptr - min_ptr ) < 2 ) {
54 return *( ptr - 2 ) !=
'\\';
57#if defined( DAW_JSON_HAS_INTEL_STRING_SIMD )
59 alignas( 16 )
bool values[256] = { };
61 constexpr bool operator[](
char idx )
const {
62 return values[
static_cast<unsigned char>( idx )];
66 template<
char... keys>
67 static constexpr inline key_table_t key_table =
68 [] DAW_CPP23_STATIC_CALL_OP {
69 auto result = key_table_t{ };
71 ( result.values[
static_cast<unsigned char>( keys )] =
true )... ) );
75#if not defined( DAW_HAS_MSVC_LIKE )
80 std::ptrdiff_t find_lsb_set( std::uint64_t value ) {
81#if DAW_HAS_BUILTIN( __builtin_ffsll )
82 return __builtin_ffsll(
static_cast<long long>( value ) ) - 1;
83#elif defined( DAW_HAS_MSVC_LIKE )
84 if( not DAW_IS_CONSTANT_EVALUATED( ) ) {
86#if defined( _M_X64 ) or defined( _M_ARM64 )
87 if( _BitScanForward64( &index, value ) != 0 ) {
88 return static_cast<std::ptrdiff_t
>( index );
91 auto const low =
static_cast<unsigned long>( value );
92 if( _BitScanForward( &index, low ) != 0 ) {
93 return static_cast<std::ptrdiff_t
>( index );
95 auto const high =
static_cast<unsigned long>( value >> 32U );
96 if( _BitScanForward( &index, high ) != 0 ) {
97 return static_cast<std::ptrdiff_t
>( index ) + 32;
103 std::ptrdiff_t result = 0;
107 while( ( value & 1 ) == 0 ) {
114 using char_simd_t = daw::simd::vec<char>;
115 static constexpr std::size_t char_simd_size = char_simd_t::size( );
116 static_assert( char_simd_size <= 64 );
118 DAW_ATTRIB_INLINE char_simd_t
119 load_char_data_simd( daw::not_null<char const *> ptr ) {
120 return daw::simd::unchecked_load<char_simd_t>(
121 daw::span( ptr.get( ), char_simd_size ) );
125 DAW_ATTRIB_INLINE std::uint64_t mem_find_eq_simd( char_simd_t block ) {
126 return ( block == char_simd_t( k ) ).to_ullong( );
129 template<
bool is_unchecked_input,
char... keys>
130 DAW_ATTRIB_INLINE daw::not_null<char const *>
131 mem_move_to_next_of_simd( daw::not_null<char const *> first,
132 daw::not_null<char const *>
const last ) {
134 while( last - first >=
static_cast<std::ptrdiff_t
>( char_simd_size ) ) {
135 auto const val0 = load_char_data_simd( first );
136 auto const key_positions = ( mem_find_eq_simd<keys>( val0 ) | ... );
137 if( key_positions != 0 ) {
138 return first + find_lsb_set( key_positions );
140 first += char_simd_size;
142 auto const max_pos = last - first;
146 auto const val1 = daw::simd::partial_load<char_simd_t>(
147 daw::span( first.get( ),
static_cast<std::size_t
>( max_pos ) ) );
148 auto const key_positions = ( mem_find_eq_simd<keys>( val1 ) | ... );
149 if( key_positions != 0 ) {
150 auto const offset = find_lsb_set( key_positions );
151 if( offset >= max_pos ) {
154 return first + offset;
161 DAW_ATTRIB_INLINE
constexpr std::uint64_t
162 find_escaped_branchless( std::uint64_t &prev_escaped,
163 std::uint64_t backslashes ) {
164 DAW_CPP23_STATIC_LOCAL
constexpr std::uint64_t odd_bits =
165 0xAAAA'AAAA'AAAA'AAAAULL;
166 DAW_CPP23_STATIC_LOCAL
constexpr auto valid_bits = [] {
167 if constexpr( char_simd_size == 64 ) {
168 return ~std::uint64_t{ 0 };
170 return ( std::uint64_t{ 1 } << char_simd_size ) - 1;
173 auto const potential_escape = backslashes & ~prev_escaped;
174 auto const maybe_escaped = potential_escape << 1U;
175 auto const escape_and_terminal =
176 ( ( maybe_escaped | odd_bits ) - potential_escape ) ^ odd_bits;
178 ( escape_and_terminal ^ ( backslashes | prev_escaped ) ) & valid_bits;
179 auto const escape_bits = escape_and_terminal & backslashes;
180 prev_escaped = ( escape_bits >> ( char_simd_size - 1U ) ) & 1U;
184 DAW_ATTRIB_INLINE
constexpr std::uint64_t
185 prefix_xor_simd( std::uint64_t bitmask ) {
186 bitmask ^= bitmask << 1U;
187 bitmask ^= bitmask << 2U;
188 bitmask ^= bitmask << 4U;
189 bitmask ^= bitmask << 8U;
190 bitmask ^= bitmask << 16U;
191 bitmask ^= bitmask << 32U;
195 template<
bool is_unchecked_input>
196 inline daw::not_null<char const *> mem_skip_until_end_of_string_simd(
197 daw::not_null<char const *> first,
198 daw::not_null<char const *>
const last ) {
199 std::uint64_t prev_escapes = 0;
200 while( last - first >=
static_cast<std::ptrdiff_t
>( char_simd_size ) ) {
201 auto const val0 = load_char_data_simd( first );
202 std::uint64_t
const backslashes = mem_find_eq_simd<'\\'>( val0 );
203 std::uint64_t
const escaped =
204 find_escaped_branchless( prev_escapes, backslashes );
205 std::uint64_t
const quotes =
206 mem_find_eq_simd<
'"'>( val0 ) & ( ~escaped );
207 std::uint64_t
const in_string = prefix_xor_simd( quotes );
208 if( in_string != 0 ) {
209 first += find_lsb_set( in_string );
212 first += char_simd_size;
214 if constexpr( is_unchecked_input ) {
215 while( *first !=
'"' ) {
216 while( not key_table<
'"',
'\\'>[*first] ) {
219 if( *first ==
'"' ) {
225 while( DAW_LIKELY( first < last ) and *first !=
'"' ) {
226 while( DAW_LIKELY( first < last ) and
227 not key_table<
'"',
'\\'>[*first] ) {
230 if( first >= last ) {
233 if( *first ==
'"' ) {
239 return ( is_unchecked_input or DAW_LIKELY( first < last ) ) ? first
243 template<
bool is_unchecked_input>
244 inline daw::not_null<char const *>
245 mem_skip_until_end_of_string_simd( daw::not_null<char const *> first,
246 daw::not_null<char const *>
const last,
247 std::ptrdiff_t &first_escape ) {
248 auto const first_first = first;
249 std::uint64_t prev_escapes = 0;
250 while( last - first >=
static_cast<std::ptrdiff_t
>( char_simd_size ) ) {
251 auto const val0 = load_char_data_simd( first );
252 std::uint64_t
const backslashes = mem_find_eq_simd<'\\'>( val0 );
253 std::uint64_t
const escaped =
254 find_escaped_branchless( prev_escapes, backslashes );
255 std::uint64_t
const quotes =
256 mem_find_eq_simd<
'"'>( val0 ) & ( ~escaped );
257 std::uint64_t
const in_string = prefix_xor_simd( quotes );
258 auto relevant_backslashes = backslashes;
259 if( in_string != 0 ) {
260 auto const quote_pos = find_lsb_set( in_string );
261 relevant_backslashes &=
262 ( std::uint64_t{ 1 } <<
static_cast<unsigned>( quote_pos ) ) - 1U;
263 if( ( relevant_backslashes != 0 ) & ( first_escape < 0 ) ) {
265 ( first - first_first ) + find_lsb_set( relevant_backslashes );
270 if( ( relevant_backslashes != 0 ) & ( first_escape < 0 ) ) {
272 ( first - first_first ) + find_lsb_set( relevant_backslashes );
274 first += char_simd_size;
276 if constexpr( is_unchecked_input ) {
277 while( *first !=
'"' ) {
278 while( not key_table<
'"',
'\\'>[*first] ) {
281 if( *first ==
'"' ) {
284 if( first_escape < 0 ) {
285 first_escape = first - first_first;
290 while( DAW_LIKELY( first < last ) and *first !=
'"' ) {
291 while( DAW_LIKELY( first < last ) and
292 not key_table<
'"',
'\\'>[*first] ) {
295 if( first >= last ) {
298 if( *first ==
'"' ) {
301 if( first_escape < 0 ) {
302 first_escape = first - first_first;
307 return ( is_unchecked_input or DAW_LIKELY( first < last ) ) ? first
312 template<
bool is_unchecked_input,
char... keys>
313 DAW_ATTRIB_INLINE daw::not_null<char const *>
314 mem_move_to_next_of_runtime( daw::not_null<char const *> first,
315 daw::not_null<char const *> last ) {
316 if constexpr(
sizeof...( keys ) == 1 ) {
317 char const key[]{ keys... };
318 char const *ptr =
static_cast<char const *
>( std::memchr(
319 first, key[0],
static_cast<std::size_t
>( last - first ) ) );
320 if( ptr ==
nullptr ) {
325 DAW_CPP23_STATIC_LOCAL
constexpr auto eq =
329 while( is_unchecked_input or first < last ) {
330 char const c = *first;
331 if( nsc_or( eq( c, keys )... ) ) {
339 template<
bool is_unchecked_input,
typename ExecTag,
char... keys>
340 DAW_ATTRIB_FLATTEN daw::not_null<char const *>
341 mem_move_to_next_of( daw::not_null<char const *> first,
342 daw::not_null<char const *> last ) {
344#if defined( DAW_JSON_HAS_INTEL_STRING_SIMD )
345 if( not std::is_same_v<runtime_exec_tag, ExecTag> ) {
346 return mem_move_to_next_of_simd<is_unchecked_input, keys...>( first,
350 return mem_move_to_next_of_runtime<is_unchecked_input, keys...>( first,
354 template<
bool is_unchecked_input>
355 DAW_ATTRIB_INLINE daw::not_null<char const *>
356 mem_skip_until_end_of_string_runtime(
357 daw::not_null<char const *> first,
358 daw::not_null<char const *>
const last ) {
359 if constexpr( not is_unchecked_input ) {
365 while( is_unchecked_input or first < last ) {
374 first = mem_move_to_next_of<is_unchecked_input,
382 template<
bool is_unchecked_input>
383 DAW_ATTRIB_INLINE
constexpr daw::not_null<char const *>
384 mem_skip_until_end_of_string_constexpr(
385 daw::not_null<char const *> first,
386 daw::not_null<char const *>
const last ) {
387 if( first == last ) {
390 using char_t = std::remove_const_t<char const>;
393 if( is_unchecked_input or
394 DAW_LIKELY( *( last - 1 ) != char_t{
'\\' } ) ) {
395 while( is_unchecked_input or DAW_UNLIKELY( first < last ) ) {
396 char const c = *first;
397 if( c == char_t{
'"' } ) {
400 if( c == char_t{
'\\' } ) {
409 while( is_unchecked_input or DAW_UNLIKELY( first < last ) ) {
410 char const c = *first;
411 if( c == char_t{
'"' } ) {
414 if( c == char_t{
'\\' } ) {
415 if( DAW_LIKELY( first + 1 < last ) ) {
429 template<
bool is_unchecked_input,
typename ExecTag>
431 daw::not_null<char const *>
constexpr mem_skip_until_end_of_string(
432 daw::not_null<char const *> first,
433 daw::not_null<char const *>
const last ) {
434 if( use_constexpr_exec_mode<ExecTag>( ) ) {
435 return mem_skip_until_end_of_string_constexpr<is_unchecked_input>(
438#if defined( DAW_JSON_HAS_INTEL_STRING_SIMD )
439 if( not std::is_same_v<runtime_exec_tag, ExecTag> ) {
440 return mem_skip_until_end_of_string_simd<is_unchecked_input>( first,
444 return mem_skip_until_end_of_string_runtime<is_unchecked_input>( first,
448 template<
bool is_unchecked_input>
449 DAW_ATTRIB_INLINE daw::not_null<char const *>
450 mem_skip_until_end_of_string_runtime(
451 daw::not_null<char const *> first,
452 daw::not_null<char const *>
const last, std::ptrdiff_t &first_escape ) {
453 auto first_first = first;
454 if constexpr( not is_unchecked_input ) {
460 while( is_unchecked_input or first < last ) {
465 if( first_escape < 0 ) {
466 first_escape = first - first_first;
468 if constexpr( is_unchecked_input ) {
471 first +=
static_cast<int>(
static_cast<bool>( last - first ) );
476 first = mem_move_to_next_of<is_unchecked_input,
484 template<
bool is_unchecked_input,
typename ExecTag>
485 DAW_ATTRIB_FLATINLINE
inline daw::not_null<char const *>
486 mem_skip_until_end_of_string( daw::not_null<char const *> first,
487 daw::not_null<char const *>
const last,
488 std::ptrdiff_t &first_escape ) {
489#if defined( DAW_JSON_HAS_INTEL_STRING_SIMD )
490 if( not std::is_same_v<runtime_exec_tag, ExecTag> ) {
491 return mem_skip_until_end_of_string_simd<is_unchecked_input>(
492 first, last, first_escape );
495 return mem_skip_until_end_of_string_runtime<is_unchecked_input>(
496 first, last, first_escape );