simdjson  3.1.6
Ridiculously Fast JSON
simd.h
1 #ifndef SIMDJSON_ICELAKE_SIMD_H
2 #define SIMDJSON_ICELAKE_SIMD_H
3 
4 #include "simdjson/internal/simdprune_tables.h"
5 
6 
7 
8 #if defined(__GNUC__) && !defined(__clang__)
9 #if __GNUC__ == 8
10 #define SIMDJSON_GCC8 1
11 #endif // __GNUC__ == 8
12 #endif // defined(__GNUC__) && !defined(__clang__)
13 
14 #if SIMDJSON_GCC8
18 inline __m512i _mm512_set_epi8(uint8_t a0, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8, uint8_t a9, uint8_t a10, uint8_t a11, uint8_t a12, uint8_t a13, uint8_t a14, uint8_t a15, uint8_t a16, uint8_t a17, uint8_t a18, uint8_t a19, uint8_t a20, uint8_t a21, uint8_t a22, uint8_t a23, uint8_t a24, uint8_t a25, uint8_t a26, uint8_t a27, uint8_t a28, uint8_t a29, uint8_t a30, uint8_t a31, uint8_t a32, uint8_t a33, uint8_t a34, uint8_t a35, uint8_t a36, uint8_t a37, uint8_t a38, uint8_t a39, uint8_t a40, uint8_t a41, uint8_t a42, uint8_t a43, uint8_t a44, uint8_t a45, uint8_t a46, uint8_t a47, uint8_t a48, uint8_t a49, uint8_t a50, uint8_t a51, uint8_t a52, uint8_t a53, uint8_t a54, uint8_t a55, uint8_t a56, uint8_t a57, uint8_t a58, uint8_t a59, uint8_t a60, uint8_t a61, uint8_t a62, uint8_t a63) {
19  return _mm512_set_epi64(uint64_t(a7) + (uint64_t(a6) << 8) + (uint64_t(a5) << 16) + (uint64_t(a4) << 24) + (uint64_t(a3) << 32) + (uint64_t(a2) << 40) + (uint64_t(a1) << 48) + (uint64_t(a0) << 56),
20  uint64_t(a15) + (uint64_t(a14) << 8) + (uint64_t(a13) << 16) + (uint64_t(a12) << 24) + (uint64_t(a11) << 32) + (uint64_t(a10) << 40) + (uint64_t(a9) << 48) + (uint64_t(a8) << 56),
21  uint64_t(a23) + (uint64_t(a22) << 8) + (uint64_t(a21) << 16) + (uint64_t(a20) << 24) + (uint64_t(a19) << 32) + (uint64_t(a18) << 40) + (uint64_t(a17) << 48) + (uint64_t(a16) << 56),
22  uint64_t(a31) + (uint64_t(a30) << 8) + (uint64_t(a29) << 16) + (uint64_t(a28) << 24) + (uint64_t(a27) << 32) + (uint64_t(a26) << 40) + (uint64_t(a25) << 48) + (uint64_t(a24) << 56),
23  uint64_t(a39) + (uint64_t(a38) << 8) + (uint64_t(a37) << 16) + (uint64_t(a36) << 24) + (uint64_t(a35) << 32) + (uint64_t(a34) << 40) + (uint64_t(a33) << 48) + (uint64_t(a32) << 56),
24  uint64_t(a47) + (uint64_t(a46) << 8) + (uint64_t(a45) << 16) + (uint64_t(a44) << 24) + (uint64_t(a43) << 32) + (uint64_t(a42) << 40) + (uint64_t(a41) << 48) + (uint64_t(a40) << 56),
25  uint64_t(a55) + (uint64_t(a54) << 8) + (uint64_t(a53) << 16) + (uint64_t(a52) << 24) + (uint64_t(a51) << 32) + (uint64_t(a50) << 40) + (uint64_t(a49) << 48) + (uint64_t(a48) << 56),
26  uint64_t(a63) + (uint64_t(a62) << 8) + (uint64_t(a61) << 16) + (uint64_t(a60) << 24) + (uint64_t(a59) << 32) + (uint64_t(a58) << 40) + (uint64_t(a57) << 48) + (uint64_t(a56) << 56));
27 }
28 #endif // SIMDJSON_GCC8
29 
30 
31 
32 namespace simdjson {
33 namespace SIMDJSON_IMPLEMENTATION {
34 namespace {
35 namespace simd {
36 
37  // Forward-declared so they can be used by splat and friends.
38  template<typename Child>
39  struct base {
40  __m512i value;
41 
42  // Zero constructor
43  simdjson_inline base() : value{__m512i()} {}
44 
45  // Conversion from SIMD register
46  simdjson_inline base(const __m512i _value) : value(_value) {}
47 
48  // Conversion to SIMD register
49  simdjson_inline operator const __m512i&() const { return this->value; }
50  simdjson_inline operator __m512i&() { return this->value; }
51 
52  // Bit operations
53  simdjson_inline Child operator|(const Child other) const { return _mm512_or_si512(*this, other); }
54  simdjson_inline Child operator&(const Child other) const { return _mm512_and_si512(*this, other); }
55  simdjson_inline Child operator^(const Child other) const { return _mm512_xor_si512(*this, other); }
56  simdjson_inline Child bit_andnot(const Child other) const { return _mm512_andnot_si512(other, *this); }
57  simdjson_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
58  simdjson_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
59  simdjson_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
60  };
61 
62  // Forward-declared so they can be used by splat and friends.
63  template<typename T>
64  struct simd8;
65 
66  template<typename T, typename Mask=simd8<bool>>
67  struct base8: base<simd8<T>> {
68  typedef uint32_t bitmask_t;
69  typedef uint64_t bitmask2_t;
70 
71  simdjson_inline base8() : base<simd8<T>>() {}
72  simdjson_inline base8(const __m512i _value) : base<simd8<T>>(_value) {}
73 
74  friend simdjson_really_inline uint64_t operator==(const simd8<T> lhs, const simd8<T> rhs) {
75  return _mm512_cmpeq_epi8_mask(lhs, rhs);
76  }
77 
78  static const int SIZE = sizeof(base<T>::value);
79 
80  template<int N=1>
81  simdjson_inline simd8<T> prev(const simd8<T> prev_chunk) const {
82  // workaround for compilers unable to figure out that 16 - N is a constant (GCC 8)
83  constexpr int shift = 16 - N;
84  return _mm512_alignr_epi8(*this, _mm512_permutex2var_epi64(prev_chunk, _mm512_set_epi64(13, 12, 11, 10, 9, 8, 7, 6), *this), shift);
85  }
86  };
87 
88  // SIMD byte mask type (returned by things like eq and gt)
89  template<>
90  struct simd8<bool>: base8<bool> {
91  static simdjson_inline simd8<bool> splat(bool _value) { return _mm512_set1_epi8(uint8_t(-(!!_value))); }
92 
93  simdjson_inline simd8<bool>() : base8() {}
94  simdjson_inline simd8<bool>(const __m512i _value) : base8<bool>(_value) {}
95  // Splat constructor
96  simdjson_inline simd8<bool>(bool _value) : base8<bool>(splat(_value)) {}
97  simdjson_inline bool any() const { return !!_mm512_test_epi8_mask (*this, *this); }
98  simdjson_inline simd8<bool> operator~() const { return *this ^ true; }
99  };
100 
101  template<typename T>
102  struct base8_numeric: base8<T> {
103  static simdjson_inline simd8<T> splat(T _value) { return _mm512_set1_epi8(_value); }
104  static simdjson_inline simd8<T> zero() { return _mm512_setzero_si512(); }
105  static simdjson_inline simd8<T> load(const T values[64]) {
106  return _mm512_loadu_si512(reinterpret_cast<const __m512i *>(values));
107  }
108  // Repeat 16 values as many times as necessary (usually for lookup tables)
109  static simdjson_inline simd8<T> repeat_16(
110  T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
111  T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
112  ) {
113  return simd8<T>(
114  v0, v1, v2, v3, v4, v5, v6, v7,
115  v8, v9, v10,v11,v12,v13,v14,v15,
116  v0, v1, v2, v3, v4, v5, v6, v7,
117  v8, v9, v10,v11,v12,v13,v14,v15,
118  v0, v1, v2, v3, v4, v5, v6, v7,
119  v8, v9, v10,v11,v12,v13,v14,v15,
120  v0, v1, v2, v3, v4, v5, v6, v7,
121  v8, v9, v10,v11,v12,v13,v14,v15
122  );
123  }
124 
125  simdjson_inline base8_numeric() : base8<T>() {}
126  simdjson_inline base8_numeric(const __m512i _value) : base8<T>(_value) {}
127 
128  // Store to array
129  simdjson_inline void store(T dst[64]) const { return _mm512_storeu_si512(reinterpret_cast<__m512i *>(dst), *this); }
130 
131  // Addition/subtraction are the same for signed and unsigned
132  simdjson_inline simd8<T> operator+(const simd8<T> other) const { return _mm512_add_epi8(*this, other); }
133  simdjson_inline simd8<T> operator-(const simd8<T> other) const { return _mm512_sub_epi8(*this, other); }
134  simdjson_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
135  simdjson_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
136 
137  // Override to distinguish from bool version
138  simdjson_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
139 
140  // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
141  template<typename L>
142  simdjson_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
143  return _mm512_shuffle_epi8(lookup_table, *this);
144  }
145 
146  // Copies to 'output" all bytes corresponding to a 0 in the mask (interpreted as a bitset).
147  // Passing a 0 value for mask would be equivalent to writing out every byte to output.
148  // Only the first 32 - count_ones(mask) bytes of the result are significant but 32 bytes
149  // get written.
150  // Design consideration: it seems like a function with the
151  // signature simd8<L> compress(uint32_t mask) would be
152  // sensible, but the AVX ISA makes this kind of approach difficult.
153  template<typename L>
154  simdjson_inline void compress(uint64_t mask, L * output) const {
155  _mm512_mask_compressstoreu_epi8 (output,~mask,*this);
156  }
157 
158  template<typename L>
159  simdjson_inline simd8<L> lookup_16(
160  L replace0, L replace1, L replace2, L replace3,
161  L replace4, L replace5, L replace6, L replace7,
162  L replace8, L replace9, L replace10, L replace11,
163  L replace12, L replace13, L replace14, L replace15) const {
164  return lookup_16(simd8<L>::repeat_16(
165  replace0, replace1, replace2, replace3,
166  replace4, replace5, replace6, replace7,
167  replace8, replace9, replace10, replace11,
168  replace12, replace13, replace14, replace15
169  ));
170  }
171  };
172 
173  // Signed bytes
174  template<>
175  struct simd8<int8_t> : base8_numeric<int8_t> {
176  simdjson_inline simd8() : base8_numeric<int8_t>() {}
177  simdjson_inline simd8(const __m512i _value) : base8_numeric<int8_t>(_value) {}
178  // Splat constructor
179  simdjson_inline simd8(int8_t _value) : simd8(splat(_value)) {}
180  // Array constructor
181  simdjson_inline simd8(const int8_t values[64]) : simd8(load(values)) {}
182  // Member-by-member initialization
183  simdjson_inline simd8(
184  int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
185  int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
186  int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
187  int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31,
188  int8_t v32, int8_t v33, int8_t v34, int8_t v35, int8_t v36, int8_t v37, int8_t v38, int8_t v39,
189  int8_t v40, int8_t v41, int8_t v42, int8_t v43, int8_t v44, int8_t v45, int8_t v46, int8_t v47,
190  int8_t v48, int8_t v49, int8_t v50, int8_t v51, int8_t v52, int8_t v53, int8_t v54, int8_t v55,
191  int8_t v56, int8_t v57, int8_t v58, int8_t v59, int8_t v60, int8_t v61, int8_t v62, int8_t v63
192  ) : simd8(_mm512_set_epi8(
193  v63, v62, v61, v60, v59, v58, v57, v56,
194  v55, v54, v53, v52, v51, v50, v49, v48,
195  v47, v46, v45, v44, v43, v42, v41, v40,
196  v39, v38, v37, v36, v35, v34, v33, v32,
197  v31, v30, v29, v28, v27, v26, v25, v24,
198  v23, v22, v21, v20, v19, v18, v17, v16,
199  v15, v14, v13, v12, v11, v10, v9, v8,
200  v7, v6, v5, v4, v3, v2, v1, v0
201  )) {}
202 
203  // Repeat 16 values as many times as necessary (usually for lookup tables)
204  simdjson_inline static simd8<int8_t> repeat_16(
205  int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
206  int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
207  ) {
208  return simd8<int8_t>(
209  v0, v1, v2, v3, v4, v5, v6, v7,
210  v8, v9, v10,v11,v12,v13,v14,v15,
211  v0, v1, v2, v3, v4, v5, v6, v7,
212  v8, v9, v10,v11,v12,v13,v14,v15,
213  v0, v1, v2, v3, v4, v5, v6, v7,
214  v8, v9, v10,v11,v12,v13,v14,v15,
215  v0, v1, v2, v3, v4, v5, v6, v7,
216  v8, v9, v10,v11,v12,v13,v14,v15
217  );
218  }
219 
220  // Order-sensitive comparisons
221  simdjson_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm512_max_epi8(*this, other); }
222  simdjson_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm512_min_epi8(*this, other); }
223 
224  simdjson_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(*this, other),_mm512_set1_epi8(uint8_t(0x80))); }
225  simdjson_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm512_maskz_abs_epi8(_mm512_cmpgt_epi8_mask(other, *this),_mm512_set1_epi8(uint8_t(0x80))); }
226  };
227 
228  // Unsigned bytes
229  template<>
230  struct simd8<uint8_t>: base8_numeric<uint8_t> {
231  simdjson_inline simd8() : base8_numeric<uint8_t>() {}
232  simdjson_inline simd8(const __m512i _value) : base8_numeric<uint8_t>(_value) {}
233  // Splat constructor
234  simdjson_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
235  // Array constructor
236  simdjson_inline simd8(const uint8_t values[64]) : simd8(load(values)) {}
237  // Member-by-member initialization
238  simdjson_inline simd8(
239  uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
240  uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
241  uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
242  uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31,
243  uint8_t v32, uint8_t v33, uint8_t v34, uint8_t v35, uint8_t v36, uint8_t v37, uint8_t v38, uint8_t v39,
244  uint8_t v40, uint8_t v41, uint8_t v42, uint8_t v43, uint8_t v44, uint8_t v45, uint8_t v46, uint8_t v47,
245  uint8_t v48, uint8_t v49, uint8_t v50, uint8_t v51, uint8_t v52, uint8_t v53, uint8_t v54, uint8_t v55,
246  uint8_t v56, uint8_t v57, uint8_t v58, uint8_t v59, uint8_t v60, uint8_t v61, uint8_t v62, uint8_t v63
247  ) : simd8(_mm512_set_epi8(
248  v63, v62, v61, v60, v59, v58, v57, v56,
249  v55, v54, v53, v52, v51, v50, v49, v48,
250  v47, v46, v45, v44, v43, v42, v41, v40,
251  v39, v38, v37, v36, v35, v34, v33, v32,
252  v31, v30, v29, v28, v27, v26, v25, v24,
253  v23, v22, v21, v20, v19, v18, v17, v16,
254  v15, v14, v13, v12, v11, v10, v9, v8,
255  v7, v6, v5, v4, v3, v2, v1, v0
256  )) {}
257 
258  // Repeat 16 values as many times as necessary (usually for lookup tables)
259  simdjson_inline static simd8<uint8_t> repeat_16(
260  uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
261  uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
262  ) {
263  return simd8<uint8_t>(
264  v0, v1, v2, v3, v4, v5, v6, v7,
265  v8, v9, v10,v11,v12,v13,v14,v15,
266  v0, v1, v2, v3, v4, v5, v6, v7,
267  v8, v9, v10,v11,v12,v13,v14,v15,
268  v0, v1, v2, v3, v4, v5, v6, v7,
269  v8, v9, v10,v11,v12,v13,v14,v15,
270  v0, v1, v2, v3, v4, v5, v6, v7,
271  v8, v9, v10,v11,v12,v13,v14,v15
272  );
273  }
274 
275  // Saturated math
276  simdjson_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm512_adds_epu8(*this, other); }
277  simdjson_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm512_subs_epu8(*this, other); }
278 
279  // Order-specific operations
280  simdjson_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm512_max_epu8(*this, other); }
281  simdjson_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm512_min_epu8(other, *this); }
282  // Same as >, but only guarantees true is nonzero (< guarantees true = -1)
283  simdjson_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
284  // Same as <, but only guarantees true is nonzero (< guarantees true = -1)
285  simdjson_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
286  simdjson_inline uint64_t operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
287  simdjson_inline uint64_t operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
288  simdjson_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
289  simdjson_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
290 
291  // Bit-specific operations
292  simdjson_inline simd8<bool> bits_not_set() const { return _mm512_mask_blend_epi8(*this == uint8_t(0), _mm512_set1_epi8(0), _mm512_set1_epi8(-1)); }
293  simdjson_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
294  simdjson_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
295  simdjson_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
296 
297  simdjson_inline bool is_ascii() const { return _mm512_movepi8_mask(*this) == 0; }
298  simdjson_inline bool bits_not_set_anywhere() const {
299  return !_mm512_test_epi8_mask(*this, *this);
300  }
301  simdjson_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
302  simdjson_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return !_mm512_test_epi8_mask(*this, bits); }
303  simdjson_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
304  template<int N>
305  simdjson_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm512_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
306  template<int N>
307  simdjson_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm512_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
308  // Get one of the bits and make a bitmask out of it.
309  // e.g. value.get_bit<7>() gets the high bit
310  template<int N>
311  simdjson_inline uint64_t get_bit() const { return _mm512_movepi8_mask(_mm512_slli_epi16(*this, 7-N)); }
312  };
313 
314  template<typename T>
315  struct simd8x64 {
316  static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
317  static_assert(NUM_CHUNKS == 1, "Icelake kernel should use one register per 64-byte block.");
318  const simd8<T> chunks[NUM_CHUNKS];
319 
320  simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
321  simd8x64<T>& operator=(const simd8<T>& other) = delete; // no assignment allowed
322  simd8x64() = delete; // no default constructor allowed
323 
324  simdjson_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
325  simdjson_inline simd8x64(const simd8<T> chunk0) : chunks{chunk0} {}
326  simdjson_inline simd8x64(const T ptr[64]) : chunks{simd8<T>::load(ptr)} {}
327 
328  simdjson_inline uint64_t compress(uint64_t mask, T * output) const {
329  this->chunks[0].compress(mask, output);
330  return 64 - count_ones(mask);
331  }
332 
333  simdjson_inline void store(T ptr[64]) const {
334  this->chunks[0].store(ptr+sizeof(simd8<T>)*0);
335  }
336 
337  simdjson_inline simd8<T> reduce_or() const {
338  return this->chunks[0];
339  }
340 
341  simdjson_inline simd8x64<T> bit_or(const T m) const {
342  const simd8<T> mask = simd8<T>::splat(m);
343  return simd8x64<T>(
344  this->chunks[0] | mask
345  );
346  }
347 
348  simdjson_inline uint64_t eq(const T m) const {
349  const simd8<T> mask = simd8<T>::splat(m);
350  return this->chunks[0] == mask;
351  }
352 
353  simdjson_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
354  return this->chunks[0] == other.chunks[0];
355  }
356 
357  simdjson_inline uint64_t lteq(const T m) const {
358  const simd8<T> mask = simd8<T>::splat(m);
359  return this->chunks[0] <= mask;
360  }
361  }; // struct simd8x64<T>
362 
363 } // namespace simd
364 
365 } // unnamed namespace
366 } // namespace SIMDJSON_IMPLEMENTATION
367 } // namespace simdjson
368 
369 #endif // SIMDJSON_ICELAKE_SIMD_H
simdjson_unused simdjson_inline bool operator==(const raw_json_string &a, std::string_view c) noexcept
Comparisons between raw_json_string and std::string_view instances are potentially unsafe: the user i...
We want to support argument-dependent lookup (ADL).