DAW JSON Link
Loading...
Searching...
No Matches
daw_not_const_ex_functions.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
16
17#include <daw/daw_attributes.h>
18#include <daw/daw_cpp_feature_check.h>
19#include <daw/daw_cxmath.h>
20#include <daw/daw_do_n.h>
21#include <daw/daw_likely.h>
22#include <daw/daw_logic.h>
23#include <daw/daw_not_null.h>
24#include <daw/daw_span.h>
25#include <daw/daw_uint_buffer.h>
26#include <daw/daw_unreachable.h>
27
28#if defined( DAW_HAS_MSVC_LIKE )
29#include <intrin.h>
30#endif
31
32#include <cstddef>
33#include <cstring>
34#include <type_traits>
35
36#if defined( DAW_JSON_HAS_SIMD ) and \
37 ( defined( __i386__ ) or defined( __x86_64__ ) or defined( _M_IX86 ) or \
38 defined( _M_X64 ) )
39#define DAW_JSON_HAS_INTEL_STRING_SIMD 1
40#endif
41
42namespace daw::json {
43 inline namespace DAW_JSON_VER {
44 namespace json_details {
45 DAW_ATTRIB_INLINE
46 constexpr bool is_escaped( daw::not_null<char const *> ptr,
47 daw::not_null<char const *> min_ptr ) {
48 if( *( ptr - 1 ) != '\\' ) {
49 return false;
50 }
51 if( ( ptr - min_ptr ) < 2 ) {
52 return false;
53 }
54 return *( ptr - 2 ) != '\\';
55 }
56
57#if defined( DAW_JSON_HAS_INTEL_STRING_SIMD )
58 struct key_table_t {
59 alignas( 16 ) bool values[256] = { };
60
61 constexpr bool operator[]( char idx ) const {
62 return values[static_cast<unsigned char>( idx )];
63 }
64 };
65
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{ };
70 (void)( daw::nsc_or(
71 ( result.values[static_cast<unsigned char>( keys )] = true )... ) );
72 return result;
73 }( );
74
75#if not defined( DAW_HAS_MSVC_LIKE )
76 constexpr
77#else
78 inline
79#endif
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( ) ) {
85 unsigned long index;
86#if defined( _M_X64 ) or defined( _M_ARM64 )
87 if( _BitScanForward64( &index, value ) != 0 ) {
88 return static_cast<std::ptrdiff_t>( index );
89 }
90#else
91 auto const low = static_cast<unsigned long>( value );
92 if( _BitScanForward( &index, low ) != 0 ) {
93 return static_cast<std::ptrdiff_t>( index );
94 }
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;
98 }
99#endif
100 return -1;
101 }
102#endif
103 std::ptrdiff_t result = 0;
104 if( value == 0 ) {
105 return -1;
106 }
107 while( ( value & 1 ) == 0 ) {
108 value >>= 1;
109 ++result;
110 }
111 return result;
112 }
113
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 );
117
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 ) );
122 }
123
124 template<char k>
125 DAW_ATTRIB_INLINE std::uint64_t mem_find_eq_simd( char_simd_t block ) {
126 return ( block == char_simd_t( k ) ).to_ullong( );
127 }
128
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 ) {
133
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 );
139 }
140 first += char_simd_size;
141 }
142 auto const max_pos = last - first;
143 if( max_pos == 0 ) {
144 return last;
145 }
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 ) {
152 return last;
153 }
154 return first + offset;
155 }
156 return last;
157 }
158
159 // Adapted from
160 // https://github.com/simdjson/simdjson/blob/master/src/generic/stage1/json_string_scanner.h#L79
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 };
169 } else {
170 return ( std::uint64_t{ 1 } << char_simd_size ) - 1;
171 }
172 }( );
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;
177 auto const escaped =
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;
181 return escaped;
182 }
183
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;
192 return bitmask;
193 }
194
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 );
210 return first;
211 }
212 first += char_simd_size;
213 }
214 if constexpr( is_unchecked_input ) {
215 while( *first != '"' ) {
216 while( not key_table<'"', '\\'>[*first] ) {
217 ++first;
218 }
219 if( *first == '"' ) {
220 return first;
221 }
222 first += 2;
223 }
224 } else {
225 while( DAW_LIKELY( first < last ) and *first != '"' ) {
226 while( DAW_LIKELY( first < last ) and
227 not key_table<'"', '\\'>[*first] ) {
228 ++first;
229 }
230 if( first >= last ) {
231 return last;
232 }
233 if( *first == '"' ) {
234 return first;
235 }
236 first += 2;
237 }
238 }
239 return ( is_unchecked_input or DAW_LIKELY( first < last ) ) ? first
240 : last;
241 }
242
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 ) ) {
264 first_escape =
265 ( first - first_first ) + find_lsb_set( relevant_backslashes );
266 }
267 first += quote_pos;
268 return first;
269 }
270 if( ( relevant_backslashes != 0 ) & ( first_escape < 0 ) ) {
271 first_escape =
272 ( first - first_first ) + find_lsb_set( relevant_backslashes );
273 }
274 first += char_simd_size;
275 }
276 if constexpr( is_unchecked_input ) {
277 while( *first != '"' ) {
278 while( not key_table<'"', '\\'>[*first] ) {
279 ++first;
280 }
281 if( *first == '"' ) {
282 return first;
283 }
284 if( first_escape < 0 ) {
285 first_escape = first - first_first;
286 }
287 first += 2;
288 }
289 } else {
290 while( DAW_LIKELY( first < last ) and *first != '"' ) {
291 while( DAW_LIKELY( first < last ) and
292 not key_table<'"', '\\'>[*first] ) {
293 ++first;
294 }
295 if( first >= last ) {
296 return last;
297 }
298 if( *first == '"' ) {
299 return first;
300 }
301 if( first_escape < 0 ) {
302 first_escape = first - first_first;
303 }
304 first += 2;
305 }
306 }
307 return ( is_unchecked_input or DAW_LIKELY( first < last ) ) ? first
308 : last;
309 }
310
311#endif
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 ) {
321 ptr = last;
322 }
323 return ptr;
324 } else {
325 DAW_CPP23_STATIC_LOCAL constexpr auto eq =
326 []( char l, char r ) DAW_JSON_CPP23_STATIC_CALL_OP {
327 return l == r;
328 };
329 while( is_unchecked_input or first < last ) {
330 char const c = *first;
331 if( nsc_or( eq( c, keys )... ) ) {
332 return first;
333 }
334 ++first;
335 }
336 return first;
337 }
338 }
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 ) {
343
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,
347 last );
348 }
349#endif
350 return mem_move_to_next_of_runtime<is_unchecked_input, keys...>( first,
351 last );
352 }
353
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 ) {
360 daw_json_ensure( first < last, ErrorReason::UnexpectedEndOfData );
361 }
362 first =
363 mem_move_to_next_of<is_unchecked_input, runtime_exec_tag, '\\', '"'>(
364 first, last );
365 while( is_unchecked_input or first < last ) {
366 switch( *first ) {
367 case '"':
368 return first;
369 case '\\':
370 ++first;
371 break;
372 }
373 ++first;
374 first = mem_move_to_next_of<is_unchecked_input,
376 '\\',
377 '"'>( first, last );
378 }
379 return first;
380 }
381
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 ) {
388 return first;
389 }
390 using char_t = std::remove_const_t<char const>;
391 // Check if the last valid char is a '\'. If not we can skip a check
392 // in the loop on escaped things
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{ '"' } ) {
398 break;
399 }
400 if( c == char_t{ '\\' } ) {
401 // We know that the last \ character is not the last character
402 // in range
403 first += 2;
404 } else {
405 ++first;
406 }
407 }
408 } else {
409 while( is_unchecked_input or DAW_UNLIKELY( first < last ) ) {
410 char const c = *first;
411 if( c == char_t{ '"' } ) {
412 break;
413 }
414 if( c == char_t{ '\\' } ) {
415 if( DAW_LIKELY( first + 1 < last ) ) {
416 first += 2;
417 } else {
418 first = last;
419 break;
420 }
421 } else {
422 ++first;
423 }
424 }
425 }
426 return first;
427 }
428
429 template<bool is_unchecked_input, typename ExecTag>
430 DAW_ATTRIB_INLINE
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>(
436 first, last );
437 }
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,
441 last );
442 }
443#endif
444 return mem_skip_until_end_of_string_runtime<is_unchecked_input>( first,
445 last );
446 }
447
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 ) {
455 daw_json_ensure( first < last, ErrorReason::UnexpectedEndOfData );
456 }
457 first =
458 mem_move_to_next_of<is_unchecked_input, runtime_exec_tag, '\\', '"'>(
459 first, last );
460 while( is_unchecked_input or first < last ) {
461 switch( *first ) {
462 case '"':
463 return first;
464 case '\\':
465 if( first_escape < 0 ) {
466 first_escape = first - first_first;
467 }
468 if constexpr( is_unchecked_input ) {
469 ++first;
470 } else {
471 first += static_cast<int>( static_cast<bool>( last - first ) );
472 }
473 break;
474 }
475 ++first;
476 first = mem_move_to_next_of<is_unchecked_input,
478 '\\',
479 '"'>( first, last );
480 }
481 return first;
482 }
483
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 );
493 }
494#endif
495 return mem_skip_until_end_of_string_runtime<is_unchecked_input>(
496 first, last, first_escape );
497 }
498 } // namespace json_details
499 } // namespace DAW_JSON_VER
500} // namespace daw::json
501
502#if defined( DAW_JSON_HAS_INTEL_STRING_SIMD )
503#undef DAW_JSON_HAS_INTEL_STRING_SIMD
504#endif
#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
Customization point traits.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20