38 namespace json_details {
40 constexpr bool is_escaped( daw::not_null<char const *> ptr,
41 daw::not_null<char const *> min_ptr ) {
42 if( *( ptr - 1 ) !=
'\\' ) {
45 if( ( ptr - min_ptr ) < 2 ) {
48 return *( ptr - 2 ) !=
'\\';
51#if defined( DAW_JSON_HAS_SIMD )
52#if not defined( DAW_HAS_MSVC_LIKE )
57 std::ptrdiff_t find_lsb_set( std::uint64_t value ) {
58#if DAW_HAS_BUILTIN( __builtin_ffsll )
59 return __builtin_ffsll(
static_cast<long long>( value ) ) - 1;
60#elif defined( DAW_HAS_MSVC_LIKE )
61 if( not DAW_IS_CONSTANT_EVALUATED( ) ) {
63#if defined( _M_X64 ) or defined( _M_ARM64 )
64 if( _BitScanForward64( &index, value ) != 0 ) {
65 return static_cast<std::ptrdiff_t
>( index );
68 auto const low =
static_cast<unsigned long>( value );
69 if( _BitScanForward( &index, low ) != 0 ) {
70 return static_cast<std::ptrdiff_t
>( index );
72 auto const high =
static_cast<unsigned long>( value >> 32U );
73 if( _BitScanForward( &index, high ) != 0 ) {
74 return static_cast<std::ptrdiff_t
>( index ) + 32;
80 std::ptrdiff_t result = 0;
84 while( ( value & 1 ) == 0 ) {
91 using char_simd_t = daw::simd::vec<char>;
92 static constexpr std::size_t char_simd_size = char_simd_t::size( );
93 static_assert( char_simd_size <= 64 and
94 ( char_simd_size & ( char_simd_size - 1U ) ) == 0,
95 "Block size must be a power of two no larger than 64" );
102 [[nodiscard]] DAW_ATTRIB_INLINE std::uintptr_t
103 align_down_to_block( std::uintptr_t addr ) {
104 return addr &
~std::uintptr_t{ char_simd_size - 1U };
108 DAW_ATTRIB_INLINE std::uint64_t mem_find_eq_simd( char_simd_t block ) {
109 return ( block == char_simd_t( k ) ).to_ullong( );
112 template<
bool is_unchecked_input,
char... keys>
113 DAW_ATTRIB_INLINE daw::not_null<char const *>
114 mem_move_to_next_of_simd( daw::not_null<char const *> first,
115 daw::not_null<char const *>
const last ) {
116 if( first >= last ) {
119 auto const first_addr =
reinterpret_cast<std::uintptr_t
>( first.get( ) );
120 auto const last_addr =
reinterpret_cast<std::uintptr_t
>( last.get( ) );
121 auto const find_keys = []( char_simd_t block ) {
122 return ( mem_find_eq_simd<keys>( block ) | ... );
124 std::uintptr_t block_addr;
125 std::uint64_t key_positions;
126 if( last_addr - first_addr >= char_simd_size ) {
128 auto const key_positions0 =
129 find_keys( daw::simd::unchecked_load<char_simd_t>(
130 daw::span( first.get( ), char_simd_size ) ) );
131 if( key_positions0 != 0 ) {
132 return first + find_lsb_set( key_positions0 );
134 block_addr = align_down_to_block( first_addr + char_simd_size );
135 key_positions =
~std::uint64_t{ 0 }
136 << ( first_addr + char_simd_size - block_addr );
138 block_addr = align_down_to_block( first_addr );
139 key_positions =
~std::uint64_t{ 0 } << ( first_addr - block_addr );
141 if( block_addr >= last_addr ) {
144 key_positions &= find_keys(
145 load_aligned_block<char_simd_t>( block_addr, first_addr, last_addr ) );
146 while( key_positions == 0 ) {
147 block_addr += char_simd_size;
148 if( block_addr >= last_addr ) {
151 key_positions = find_keys(
152 load_aligned_block<char_simd_t>( block_addr, first_addr, last_addr ) );
154 auto const hit_addr =
156 static_cast<std::uintptr_t
>( find_lsb_set( key_positions ) );
157 if( hit_addr >= last_addr ) {
160 return first +
static_cast<std::ptrdiff_t
>( hit_addr - first_addr );
165 DAW_ATTRIB_INLINE
constexpr std::uint64_t
166 find_escaped_branchless( std::uint64_t &prev_escaped,
167 std::uint64_t backslashes ) {
168 DAW_CPP23_STATIC_LOCAL
constexpr std::uint64_t odd_bits =
169 0xAAAA'AAAA'AAAA'AAAAULL;
170 DAW_CPP23_STATIC_LOCAL
constexpr auto valid_bits = [] {
171 if constexpr( char_simd_size == 64 ) {
172 return ~std::uint64_t{ 0 };
174 return ( std::uint64_t{ 1 } << char_simd_size ) - 1;
177 auto const potential_escape = backslashes & ~prev_escaped;
178 auto const maybe_escaped = potential_escape << 1U;
179 auto const escape_and_terminal =
180 ( ( maybe_escaped | odd_bits ) - potential_escape ) ^ odd_bits;
182 ( escape_and_terminal ^ ( backslashes | prev_escaped ) ) & valid_bits;
183 auto const escape_bits = escape_and_terminal & backslashes;
184 prev_escaped = ( escape_bits >> ( char_simd_size - 1U ) ) & 1U;
195 template<
bool is_unchecked_input>
196 inline daw::not_null<char const *>
197 mem_skip_until_end_of_string_simd( daw::not_null<char const *> first,
198 daw::not_null<char const *>
const last,
199 std::ptrdiff_t *first_escape ) {
200 if( first >= last ) {
203 auto const first_addr =
reinterpret_cast<std::uintptr_t
>( first.get( ) );
204 auto const last_addr =
reinterpret_cast<std::uintptr_t
>( last.get( ) );
205 std::uint64_t prev_escapes = 0;
206 char const *result =
nullptr;
209 auto const scan = [&]( char_simd_t block, std::uintptr_t block_addr,
210 unsigned lead_shift ) {
211 auto const lead_mask =
~std::uint64_t{ 0 } << lead_shift;
212 auto const backslashes = mem_find_eq_simd<'\\'>( block ) & lead_mask;
213 prev_escapes <<= lead_shift;
215 find_escaped_branchless( prev_escapes, backslashes );
217 mem_find_eq_simd<
'"'>( block ) & lead_mask & ~escaped;
218 auto relevant_backslashes = backslashes;
220 auto const quote_pos = find_lsb_set( quotes );
221 relevant_backslashes &=
222 ( std::uint64_t{ 1 } <<
static_cast<unsigned>( quote_pos ) ) - 1U;
224 if( first_escape !=
nullptr and *first_escape < 0 and
225 relevant_backslashes != 0 ) {
226 auto const escape_addr =
227 block_addr +
static_cast<std::uintptr_t
>(
228 find_lsb_set( relevant_backslashes ) );
229 if( escape_addr < last_addr ) {
231 static_cast<std::ptrdiff_t
>( escape_addr - first_addr );
237 auto const quote_addr =
238 block_addr +
static_cast<std::uintptr_t
>( find_lsb_set( quotes ) );
239 result = quote_addr >= last_addr
242 static_cast<std::ptrdiff_t
>( quote_addr - first_addr );
245 std::uintptr_t block_addr;
247 if( last_addr - first_addr >= char_simd_size ) {
248 if( scan( daw::simd::unchecked_load<char_simd_t>(
249 daw::span( first.get( ), char_simd_size ) ),
253 block_addr = align_down_to_block( first_addr + char_simd_size );
254 lead_shift =
static_cast<unsigned>( first_addr + char_simd_size -
257 block_addr = align_down_to_block( first_addr );
258 lead_shift =
static_cast<unsigned>( first_addr - block_addr );
260 while( block_addr < last_addr ) {
262 load_aligned_block<char_simd_t>( block_addr, first_addr, last_addr ),
263 block_addr, lead_shift ) ) {
266 block_addr += char_simd_size;
272 template<
bool is_unchecked_input>
273 inline daw::not_null<char const *>
274 mem_skip_until_end_of_string_simd( daw::not_null<char const *> first,
275 daw::not_null<char const *>
const last ) {
276 return mem_skip_until_end_of_string_simd<is_unchecked_input>(
277 first, last,
nullptr );
280 template<
bool is_unchecked_input>
281 inline daw::not_null<char const *>
282 mem_skip_until_end_of_string_simd( daw::not_null<char const *> first,
283 daw::not_null<char const *>
const last,
284 std::ptrdiff_t &first_escape ) {
285 return mem_skip_until_end_of_string_simd<is_unchecked_input>(
286 first, last, &first_escape );
290 template<
bool is_unchecked_input,
char... keys>
291 DAW_ATTRIB_INLINE daw::not_null<char const *>
292 mem_move_to_next_of_runtime( daw::not_null<char const *> first,
293 daw::not_null<char const *> last ) {
294 if constexpr(
sizeof...( keys ) == 1 ) {
295 char const key[]{ keys... };
296 char const *ptr =
static_cast<char const *
>( std::memchr(
297 first, key[0],
static_cast<std::size_t
>( last - first ) ) );
298 if( ptr ==
nullptr ) {
303 DAW_CPP23_STATIC_LOCAL
constexpr auto eq =
307 while( is_unchecked_input or first < last ) {
308 char const c = *first;
309 if( nsc_or( eq( c, keys )... ) ) {
317 template<
bool is_unchecked_input,
typename ExecTag,
char... keys>
318 DAW_ATTRIB_FLATTEN daw::not_null<char const *>
319 mem_move_to_next_of( daw::not_null<char const *> first,
320 daw::not_null<char const *> last ) {
322#if defined( DAW_JSON_HAS_SIMD )
323 if( use_simd_exec_mode<ExecTag>( ) ) {
324 return mem_move_to_next_of_simd<is_unchecked_input, keys...>( first,
328 return mem_move_to_next_of_runtime<is_unchecked_input, keys...>( first,
332 template<
bool is_unchecked_input>
333 DAW_ATTRIB_INLINE daw::not_null<char const *>
334 mem_skip_until_end_of_string_runtime(
335 daw::not_null<char const *> first,
336 daw::not_null<char const *>
const last ) {
337 if constexpr( not is_unchecked_input ) {
343 while( is_unchecked_input or first < last ) {
352 first = mem_move_to_next_of<is_unchecked_input,
360 template<
bool is_unchecked_input>
361 DAW_ATTRIB_INLINE
constexpr daw::not_null<char const *>
362 mem_skip_until_end_of_string_constexpr(
363 daw::not_null<char const *> first,
364 daw::not_null<char const *>
const last ) {
365 if( first == last ) {
368 using char_t = std::remove_const_t<char const>;
371 if( is_unchecked_input or
372 DAW_LIKELY( *( last - 1 ) != char_t{
'\\' } ) ) {
373 while( is_unchecked_input or DAW_UNLIKELY( first < last ) ) {
374 char const c = *first;
375 if( c == char_t{
'"' } ) {
378 if( c == char_t{
'\\' } ) {
387 while( is_unchecked_input or DAW_UNLIKELY( first < last ) ) {
388 char const c = *first;
389 if( c == char_t{
'"' } ) {
392 if( c == char_t{
'\\' } ) {
393 if( DAW_LIKELY( first + 1 < last ) ) {
407 template<
bool is_unchecked_input,
typename ExecTag>
409 daw::not_null<char const *>
constexpr mem_skip_until_end_of_string(
410 daw::not_null<char const *> first,
411 daw::not_null<char const *>
const last ) {
412 if( use_constexpr_exec_mode<ExecTag>( ) ) {
413 return mem_skip_until_end_of_string_constexpr<is_unchecked_input>(
416#if defined( DAW_JSON_HAS_SIMD )
417 if( use_simd_exec_mode<ExecTag>( ) ) {
418 return mem_skip_until_end_of_string_simd<is_unchecked_input>( first,
422 return mem_skip_until_end_of_string_runtime<is_unchecked_input>( first,
426 template<
bool is_unchecked_input>
427 DAW_ATTRIB_INLINE daw::not_null<char const *>
428 mem_skip_until_end_of_string_runtime(
429 daw::not_null<char const *> first,
430 daw::not_null<char const *>
const last, std::ptrdiff_t &first_escape ) {
431 auto first_first = first;
432 if constexpr( not is_unchecked_input ) {
438 while( is_unchecked_input or first < last ) {
443 if( first_escape < 0 ) {
444 first_escape = first - first_first;
446 if constexpr( is_unchecked_input ) {
449 first +=
static_cast<int>(
static_cast<bool>( last - first ) );
454 first = mem_move_to_next_of<is_unchecked_input,
462 template<
bool is_unchecked_input,
typename ExecTag>
463 DAW_ATTRIB_FLATINLINE
inline daw::not_null<char const *>
464 mem_skip_until_end_of_string( daw::not_null<char const *> first,
465 daw::not_null<char const *>
const last,
466 std::ptrdiff_t &first_escape ) {
467#if defined( DAW_JSON_HAS_SIMD )
468 if( use_simd_exec_mode<ExecTag>( ) ) {
469 return mem_skip_until_end_of_string_simd<is_unchecked_input>(
470 first, last, first_escape );
473 return mem_skip_until_end_of_string_runtime<is_unchecked_input>(
474 first, last, first_escape );