1#ifndef SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
2#define SIMDJSON_GENERIC_ONDEMAND_KEY_SELECTOR_H
4#ifndef SIMDJSON_CONDITIONAL_INCLUDE
5#include "simdjson/base.h"
6#include "simdjson/common_defs.h"
7#include "simdjson/constevalutil.h"
8#include "simdjson/generic/ondemand/raw_json_string.h"
22#if defined(__aarch64__)
24 #define SIMDJSON_KEY_SELECTOR_HAS_NEON 1
26 #define SIMDJSON_KEY_SELECTOR_HAS_NEON 0
29 #include <emmintrin.h>
30 #define SIMDJSON_KEY_SELECTOR_HAS_SSE2 1
32 #define SIMDJSON_KEY_SELECTOR_HAS_SSE2 0
34#if defined(__loongarch_sx)
35 #include <lsxintrin.h>
36 #define SIMDJSON_KEY_SELECTOR_HAS_LSX 1
38 #define SIMDJSON_KEY_SELECTOR_HAS_LSX 0
41#if SIMDJSON_SUPPORTS_CONCEPTS
44namespace SIMDJSON_IMPLEMENTATION {
47namespace key_selector_detail {
60static constexpr std::size_t MAX_POSITIONS = 16;
62static constexpr std::size_t LAST_CHAR = std::size_t(-1);
64static constexpr std::uint8_t POS_LAST_CHAR = 0xFF;
65static constexpr std::uint8_t HD_MODE = 0xFF;
67static constexpr std::size_t HD_HASH_2BYTE_FLAG = 2;
68static constexpr std::size_t HD_HASH_4BYTE_FLAG = 4;
70constexpr std::size_t next_power_of_2(std::size_t n)
noexcept {
71 if (n == 0) {
return 1; }
73 while (p < n) { p <<= 1; }
79constexpr std::size_t char_at(std::string_view key, std::size_t pos)
noexcept {
80 if (pos == LAST_CHAR) {
81 if (key.empty()) {
return 256; }
82 return static_cast<unsigned char>(key[key.size() - 1]);
84 if (pos >= key.size()) {
return 256; }
85 return static_cast<unsigned char>(key[pos]);
91template <std::
size_t N>
92consteval std::size_t count_undistinguished_pairs(
93 const std::array<std::string_view, N>& keys,
94 const std::size_t* positions,
95 std::size_t num_positions,
96 std::size_t modulus) {
97 std::size_t count = 0;
98 for (std::size_t i = 0; i < N; ++i) {
99 for (std::size_t j = i + 1; j < N; ++j) {
100 if (keys[i].size() % modulus != keys[j].size() % modulus) {
continue; }
101 bool distinguished =
false;
102 for (std::size_t p = 0; p < num_positions; ++p) {
103 if (char_at(keys[i], positions[p]) != char_at(keys[j], positions[p])) {
104 distinguished =
true;
108 if (!distinguished) { ++count; }
114template <std::
size_t N>
115consteval bool positions_distinguish(
116 const std::array<std::string_view, N>& keys,
117 const std::size_t* positions,
118 std::size_t num_positions,
119 std::size_t modulus) {
120 return count_undistinguished_pairs<N>(keys, positions, num_positions, modulus) == 0;
124template <std::
size_t N>
125consteval std::size_t discriminating_power(
126 const std::array<std::string_view, N>& keys,
128 std::size_t modulus) {
129 struct pair { std::size_t len_mod; std::size_t ch; };
130 std::array<pair, N> pairs{};
131 for (std::size_t i = 0; i < N; ++i) {
132 pairs[i] = {keys[i].size() % modulus, char_at(keys[i], pos)};
134 std::size_t count = 0;
135 for (std::size_t i = 0; i < N; ++i) {
137 for (std::size_t j = 0; j < i; ++j) {
138 if (pairs[i].len_mod == pairs[j].len_mod && pairs[i].ch == pairs[j].ch) {
143 if (!dup) { ++count; }
148template <std::
size_t N>
149consteval std::size_t max_key_length(
const std::array<std::string_view, N>& keys) {
151 for (std::size_t i = 0; i < N; ++i) {
152 if (keys[i].size() > m) { m = keys[i].size(); }
158template <std::
size_t N>
159consteval bool backtracking_search(
160 const std::array<std::string_view, N>& keys,
161 const std::size_t* candidates,
162 std::size_t num_candidates,
163 std::size_t* positions,
164 std::size_t& num_positions_out,
166 std::size_t modulus) {
167 constexpr std::size_t MAX_DEPTH = 8;
168 std::size_t breadth = num_candidates < 20 ? num_candidates : 20;
170 struct frame { std::size_t depth; std::size_t next_ci; std::size_t parent_count; };
171 std::array<frame, MAX_DEPTH + 1> stack{};
174 std::size_t initial_count = count_undistinguished_pairs<N>(keys, positions, 0, modulus);
175 if (budget > 0) { --budget; }
176 if (initial_count == 0) { num_positions_out = 0;
return true; }
178 stack[0] = {0, 0, initial_count};
181 if (sp > MAX_DEPTH) {
182 if (sp == 0) {
break; }
188 if (f.next_ci >= breadth) {
189 if (sp == 0) {
break; }
194 positions[sp] = candidates[f.next_ci];
196 std::size_t new_count = count_undistinguished_pairs<N>(keys, positions, sp + 1, modulus);
197 if (new_count == 0) { num_positions_out = sp + 1;
return true; }
198 if (new_count < f.parent_count && sp + 1 < MAX_DEPTH) {
199 stack[sp + 1] = {sp + 1, f.next_ci + 1, new_count};
209template <std::
size_t N>
210consteval std::size_t select_positions(
211 const std::array<std::string_view, N>& keys,
212 std::array<std::size_t, MAX_POSITIONS>& positions,
213 std::size_t modulus) {
214 if (positions_distinguish<N>(keys, positions.data(), 0, modulus)) {
return 0; }
216 std::size_t max_len = max_key_length(keys);
217 constexpr std::size_t MAX_CANDIDATES = 256;
218 std::array<std::size_t, MAX_CANDIDATES> candidates{};
219 std::array<std::size_t, MAX_CANDIDATES> powers{};
220 std::size_t num_candidates = 0;
221 for (std::size_t p = 0; p < max_len && num_candidates < MAX_CANDIDATES - 1; ++p) {
222 candidates[num_candidates] = p;
223 powers[num_candidates] = discriminating_power(keys, p, modulus);
226 if (num_candidates < MAX_CANDIDATES) {
227 candidates[num_candidates] = LAST_CHAR;
228 powers[num_candidates] = discriminating_power(keys, LAST_CHAR, modulus);
231 for (std::size_t i = 0; i < num_candidates; ++i) {
232 for (std::size_t j = i + 1; j < num_candidates; ++j) {
233 if (powers[j] > powers[i]) {
234 auto tc = candidates[i]; candidates[i] = candidates[j]; candidates[j] = tc;
235 auto tp = powers[i]; powers[i] = powers[j]; powers[j] = tp;
240 positions[0] = candidates[0];
241 if (positions_distinguish<N>(keys, positions.data(), 1, modulus)) {
return 1; }
244 positions[1] = LAST_CHAR;
245 if (positions_distinguish<N>(keys, positions.data(), 2, modulus)) {
return 2; }
248 std::size_t budget = 5000;
249 std::size_t num_found = 0;
250 if (backtracking_search<N>(keys, candidates.data(), num_candidates,
251 positions.data(), num_found, budget, modulus)) {
256 std::size_t num_pos = 0;
257 for (std::size_t ci = 0; ci < num_candidates && num_pos < MAX_POSITIONS; ++ci) {
258 bool already =
false;
259 for (std::size_t p = 0; p < num_pos; ++p) {
260 if (positions[p] == candidates[ci]) { already =
true;
break; }
262 if (already) {
continue; }
263 positions[num_pos] = candidates[ci];
265 if (positions_distinguish<N>(keys, positions.data(), num_pos, modulus)) {
return num_pos; }
268 throw "Failed to find distinguishing positions for perfect hash";
273template <std::
size_t N>
276 static constexpr std::size_t MAX_TABLE_SIZE = next_power_of_2(N) * 8;
277 std::size_t table_size{};
278 std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso_values{};
279 std::size_t num_positions{};
280 std::array<std::size_t, MAX_POSITIONS> positions{};
281 std::array<std::size_t, MAX_TABLE_SIZE> slot_to_key{};
287template <std::
size_t N, std::
size_t M>
288consteval bool try_generate_gperf(
289 const std::array<std::string_view, N>& keys,
290 std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
291 std::size_t& num_positions,
292 std::array<std::size_t, MAX_POSITIONS>& positions,
293 std::array<std::size_t, M>& slot_to_key) {
294 num_positions = select_positions<N>(keys, positions, M);
296 for (std::size_t p = 0; p < MAX_POSITIONS; ++p) {
297 for (std::size_t c = 0; c < 256; ++c) { asso_values[p][c] = 0; }
300 if (num_positions == 0) {
301 for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
302 for (std::size_t i = 0; i < N; ++i) {
303 std::size_t slot = keys[i].size() % M;
304 if (slot_to_key[slot] != N) {
return false; }
305 slot_to_key[slot] = i;
310 std::array<std::array<std::size_t, MAX_POSITIONS>, N> kchars{};
311 for (std::size_t k = 0; k < N; ++k) {
312 for (std::size_t p = 0; p < num_positions; ++p) {
313 kchars[k][p] = char_at(keys[k], positions[p]);
317 struct sym_t { std::size_t pos; std::size_t ch; std::size_t freq; };
318 constexpr std::size_t MAX_SYMS = MAX_POSITIONS * 256;
319 std::array<sym_t, MAX_SYMS> syms{};
320 std::size_t nsyms = 0;
321 for (std::size_t p = 0; p < num_positions; ++p) {
322 std::array<std::size_t, 256> freq{};
323 for (std::size_t k = 0; k < N; ++k) {
324 std::size_t c = kchars[k][p];
325 if (c < 256) { freq[c]++; }
327 for (std::size_t c = 0; c < 256; ++c) {
328 if (freq[c] > 0) { syms[nsyms++] = {p, c, freq[c]}; }
331 for (std::size_t i = 0; i < nsyms; ++i) {
332 for (std::size_t j = i + 1; j < nsyms; ++j) {
333 if (syms[j].freq > syms[i].freq) {
334 auto tmp = syms[i]; syms[i] = syms[j]; syms[j] = tmp;
339 std::array<std::size_t, N> phash{};
340 for (std::size_t k = 0; k < N; ++k) { phash[k] = keys[k].size(); }
342 std::array<std::array<uint64_t, 256>, MAX_POSITIONS> salt{};
344 uint64_t s = 0x9e3779b97f4a7c15ULL;
345 for (std::size_t p = 0; p < num_positions; ++p) {
346 for (std::size_t c = 0; c < 256; ++c) {
347 s = s * 6364136223846793005ULL + 1442695040888963407ULL;
352 std::array<uint64_t, N> sig{};
353 for (std::size_t k = 0; k < N; ++k) {
355 for (std::size_t p = 0; p < num_positions; ++p) {
356 std::size_t c = kchars[k][p];
357 if (c < 256) { s ^= salt[p][c]; }
361 std::array<std::size_t, N> order{};
362 for (std::size_t k = 0; k < N; ++k) { order[k] = k; }
364 std::array<std::size_t, M> slot_gen{};
367 std::size_t search_limit = next_power_of_2(M);
368 if (search_limit < 32) { search_limit = 32; }
370 for (std::size_t si = 0; si < nsyms; ++si) {
371 std::size_t sp = syms[si].pos;
372 std::size_t sc = syms[si].ch;
374 uint64_t sp_salt = salt[sp][sc];
375 for (std::size_t k = 0; k < N; ++k) {
376 if (kchars[k][sp] == sc) { sig[k] ^= sp_salt; }
379 for (std::size_t i = 1; i < N; ++i) {
380 std::size_t x = order[i];
381 uint64_t xs = sig[x];
383 while (j > 0 && sig[order[j - 1]] > xs) {
384 order[j] = order[j - 1];
391 for (std::size_t v = 0; v < search_limit && !found; ++v) {
392 bool collision =
false;
394 while (ci < N && !collision) {
395 uint64_t class_sig = sig[order[ci]];
397 while (cj < N && sig[order[cj]] == class_sig) { ++cj; }
400 for (std::size_t x = ci; x < cj; ++x) {
401 std::size_t k = order[x];
402 std::size_t h = phash[k];
403 if (kchars[k][sp] == sc) { h += v; }
405 if (slot_gen[h] == gen) { collision =
true;
break; }
412 asso_values[sp][sc] = v;
413 for (std::size_t k = 0; k < N; ++k) {
414 if (kchars[k][sp] == sc) { phash[k] += v; }
419 if (!found) {
return false; }
422 for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
423 for (std::size_t i = 0; i < N; ++i) {
424 std::size_t slot = phash[i] % M;
425 if (slot_to_key[slot] != N) {
return false; }
426 slot_to_key[slot] = i;
428 std::size_t filled = 0;
429 for (std::size_t i = 0; i < M; ++i) {
430 if (slot_to_key[i] != N) { ++filled; }
435template <std::
size_t N, std::
size_t M>
436consteval bool try_compute_phf(
const std::array<std::string_view, N>& keys, phf_result<N>& result) {
437 static_assert(M <= phf_result<N>::MAX_TABLE_SIZE,
"Table size M exceeds maximum");
438 std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
440 std::array<std::size_t, MAX_POSITIONS> pos{};
441 std::array<std::size_t, M> s2k{};
442 if (try_generate_gperf<N, M>(keys, asso, npos, pos, s2k)) {
443 result.table_size = M;
444 result.asso_values = asso;
445 result.num_positions = npos;
446 result.positions = pos;
447 for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
448 for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
454template <std::
size_t N, std::
size_t M, std::
size_t MaxM>
455consteval bool try_gperf_po2(
const std::array<std::string_view, N>& keys, phf_result<N>& result) {
456 if (try_compute_phf<N, M>(keys, result)) {
return true; }
457 constexpr std::size_t NextM = M * 2;
458 if constexpr (NextM <= MaxM) {
return try_gperf_po2<N, NextM, MaxM>(keys, result); }
464constexpr std::size_t hd_bucket_hash(std::string_view key)
noexcept {
465 std::size_t c0 = key.empty() ? 0 :
static_cast<unsigned char>(key[0]);
466 std::size_t c1 = key.empty() ? 0 :
static_cast<unsigned char>(key[key.size() - 1]);
467 return (c0 + c1 * 3 + key.size() * 17) & 0xFF;
469constexpr std::size_t hd_safe_char(
const char* p, std::size_t len, std::size_t idx)
noexcept {
470 std::size_t has =
static_cast<std::size_t
>(idx < len);
471 std::size_t si = idx & (std::size_t{0} - has);
472 return static_cast<unsigned char>(p[si]) & (std::size_t{0} - has);
474constexpr std::size_t hd_key_hash_2(std::string_view key)
noexcept {
475 std::size_t kc = key.size();
476 kc = kc * 31 +
static_cast<unsigned char>(key[0]);
477 kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
480constexpr std::size_t hd_key_hash_4(std::string_view key)
noexcept {
481 std::size_t kc = key.size();
482 kc = kc * 31 +
static_cast<unsigned char>(key[0]);
483 kc = kc * 31 + hd_safe_char(key.data(), key.size(), 1);
484 kc = kc * 31 + hd_safe_char(key.data(), key.size(), 2);
485 kc = kc * 31 + hd_safe_char(key.data(), key.size(), 3);
489template <std::
size_t N, std::
size_t M>
490consteval bool try_hash_and_displace(
491 const std::array<std::string_view, N>& keys,
492 std::array<std::array<std::size_t, 256>, MAX_POSITIONS>& asso_values,
493 std::size_t& num_positions,
494 std::array<std::size_t, MAX_POSITIONS>& positions,
495 std::array<std::size_t, M>& slot_to_key) {
496 num_positions = select_positions<N>(keys, positions, M);
498 if (num_positions == 0) {
499 for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
500 for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
501 for (std::size_t i = 0; i < N; ++i) {
502 std::size_t slot = keys[i].size() % M;
503 if (slot_to_key[slot] != N) {
return false; }
504 slot_to_key[slot] = i;
509 for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
510 num_positions = HD_MODE;
512 positions[1] = LAST_CHAR;
514 std::array<std::size_t, N> key_bucket{};
515 for (std::size_t i = 0; i < N; ++i) { key_bucket[i] = hd_bucket_hash(keys[i]); }
517 struct bucket_info { std::size_t ch; std::size_t count; };
518 std::array<bucket_info, N> buckets{};
519 std::size_t num_buckets = 0;
520 for (std::size_t i = 0; i < N; ++i) {
521 std::size_t bk = key_bucket[i];
523 for (std::size_t b = 0; b < num_buckets; ++b) {
524 if (buckets[b].ch == bk) { ++buckets[b].count; found =
true;
break; }
526 if (!found) { buckets[num_buckets++] = {bk, 1}; }
528 for (std::size_t i = 0; i < num_buckets; ++i) {
529 for (std::size_t j = i + 1; j < num_buckets; ++j) {
530 if (buckets[j].count > buckets[i].count) {
531 auto tmp = buckets[i]; buckets[i] = buckets[j]; buckets[j] = tmp;
536 auto try_placement = [&](
auto key_hash_fn) ->
bool {
537 for (std::size_t i = 0; i < M; ++i) { slot_to_key[i] = N; }
538 for (std::size_t i = 0; i < 256; ++i) { asso_values[0][i] = 0; }
539 for (std::size_t b = 0; b < num_buckets; ++b) {
540 std::size_t ch = buckets[b].ch;
541 std::array<std::size_t, N> bucket_keys{};
542 std::size_t bk_count = 0;
543 for (std::size_t i = 0; i < N; ++i) {
544 if (key_bucket[i] == ch) { bucket_keys[bk_count++] = i; }
547 std::size_t max_d = M < 255 ? M : 255;
548 for (std::size_t d = 0; d < max_d; ++d) {
550 std::array<std::size_t, N> bucket_slots{};
551 for (std::size_t k = 0; k < bk_count; ++k) {
552 std::size_t slot = (d + key_hash_fn(keys[bucket_keys[k]])) % M;
553 if (slot_to_key[slot] != N) { ok =
false;
break; }
554 for (std::size_t k2 = 0; k2 < k; ++k2) {
555 if (bucket_slots[k2] == slot) { ok =
false;
break; }
558 bucket_slots[k] = slot;
561 asso_values[0][ch] = d;
562 for (std::size_t k = 0; k < bk_count; ++k) {
563 slot_to_key[bucket_slots[k]] = bucket_keys[k];
569 if (!placed) {
return false; }
571 std::size_t filled = 0;
572 for (std::size_t i = 0; i < M; ++i) {
573 if (slot_to_key[i] != N) { ++filled; }
578 if (try_placement([](std::string_view k) {
return hd_key_hash_2(k); })) {
579 positions[2] = HD_HASH_2BYTE_FLAG;
582 if (try_placement([](std::string_view k) {
return hd_key_hash_4(k); })) {
583 positions[2] = HD_HASH_4BYTE_FLAG;
589template <std::
size_t N, std::
size_t M>
590consteval bool try_compute_phf_hd(
const std::array<std::string_view, N>& keys, phf_result<N>& result) {
591 static_assert(M <= phf_result<N>::MAX_TABLE_SIZE,
"Table size M exceeds maximum");
592 std::array<std::array<std::size_t, 256>, MAX_POSITIONS> asso{};
594 std::array<std::size_t, MAX_POSITIONS> pos{};
595 std::array<std::size_t, M> s2k{};
596 if (try_hash_and_displace<N, M>(keys, asso, npos, pos, s2k)) {
597 result.table_size = M;
598 result.asso_values = asso;
599 result.num_positions = npos;
600 result.positions = pos;
601 for (std::size_t i = 0; i < M; ++i) { result.slot_to_key[i] = s2k[i]; }
602 for (std::size_t i = M; i < phf_result<N>::MAX_TABLE_SIZE; ++i) { result.slot_to_key[i] = N; }
608template <std::
size_t N, std::
size_t M>
609consteval phf_result<N> compute_phf_hd_po2(
const std::array<std::string_view, N>& keys) {
610 static_assert(M <= phf_result<N>::MAX_TABLE_SIZE,
"Table size M exceeds maximum");
611 phf_result<N> result{};
612 if (try_compute_phf_hd<N, M>(keys, result)) {
return result; }
613 constexpr std::size_t NextM = M * 2;
614 if constexpr (NextM <= phf_result<N>::MAX_TABLE_SIZE) {
615 return compute_phf_hd_po2<N, NextM>(keys);
617 throw "Hash-and-Displace: failed to find valid table size";
623template <std::
size_t N>
624consteval phf_result<N> compute_phf(
const std::array<std::string_view, N>& keys) {
625 constexpr std::size_t StartM = next_power_of_2(N);
626 constexpr std::size_t GPERF_MAX_TABLE =
627 phf_result<N>::MAX_TABLE_SIZE < 256 ? phf_result<N>::MAX_TABLE_SIZE : 256;
628 if constexpr (StartM <= GPERF_MAX_TABLE) {
629 phf_result<N> result{};
630 if (try_gperf_po2<N, StartM, GPERF_MAX_TABLE>(keys, result)) {
return result; }
632 return compute_phf_hd_po2<N, StartM>(keys);
639template <std::
size_t N, std::
size_t TableSize, std::
size_t MaxKeyLen>
641 std::array<std::array<std::uint8_t, 256>, MAX_POSITIONS> asso_values{};
642 std::array<std::uint8_t, MAX_POSITIONS> positions{};
643 std::uint8_t num_positions{};
644 std::uint8_t hd_hash_variant{};
645 std::array<std::uint8_t, TableSize> slot_to_key{};
648 std::array<std::array<char, ((MaxKeyLen + 15) / 16) * 16>, TableSize> slot_key_bytes{};
649 std::array<std::uint8_t, TableSize> slot_key_len{};
652template <std::
size_t N>
653constexpr std::size_t compute_max_key_len(
const std::array<std::string_view, N>& keys)
noexcept {
655 for (std::size_t i = 0; i < N; ++i) {
if (keys[i].size() > m) { m = keys[i].size(); } }
660template <std::
size_t N, std::
size_t TableSize, std::
size_t MaxKeyLen>
661consteval phf_data<N, TableSize, MaxKeyLen>
662build_phf_data(
const std::array<std::string_view, N>& keys,
const phf_result<N>& result) {
663 for (std::size_t i = 0; i < N; ++i) {
664 if (keys[i].empty()) {
throw "empty keys are not allowed in key_selector"; }
665 if (keys[i].size() > MaxKeyLen) {
throw "key length exceeds MaxKeyLen"; }
666 for (
char c : keys[i]) {
667 if (c ==
'\\') {
throw "backslash not allowed in key_selector keys"; }
668 if (c ==
'"') {
throw "quote not allowed in key_selector keys"; }
669 if (c ==
'\0') {
throw "null byte not allowed in key_selector keys"; }
671 for (std::size_t j = i + 1; j < N; ++j) {
672 if (keys[i] == keys[j]) {
throw "duplicate keys in key_selector"; }
676 phf_data<N, TableSize, MaxKeyLen> out{};
678 if (result.num_positions == HD_MODE) {
680 for (std::size_t c = 0; c < 256; ++c) {
681 out.asso_values[0][c] =
static_cast<std::uint8_t
>(result.asso_values[0][c]);
683 out.num_positions =
static_cast<std::uint8_t
>(HD_MODE);
684 out.hd_hash_variant =
static_cast<std::uint8_t
>(result.positions[2]);
686 for (std::size_t pi = 0; pi < result.num_positions; ++pi) {
687 for (std::size_t c = 0; c < 256; ++c) {
688 out.asso_values[pi][c] =
static_cast<std::uint8_t
>(result.asso_values[pi][c] % TableSize);
691 out.num_positions =
static_cast<std::uint8_t
>(result.num_positions);
692 for (std::size_t i = 0; i < result.num_positions; ++i) {
693 out.positions[i] = (result.positions[i] == LAST_CHAR)
695 : static_cast<std::uint8_t>(result.positions[i]);
699 for (std::size_t s = 0; s < TableSize; ++s) {
700 out.slot_to_key[s] =
static_cast<std::uint8_t
>(result.slot_to_key[s]);
703 for (std::size_t s = 0; s < TableSize; ++s) {
704 std::size_t ki = result.slot_to_key[s];
707 out.slot_key_len[s] =
static_cast<std::uint8_t
>(k.size());
708 for (std::size_t c = 0; c < k.size(); ++c) { out.slot_key_bytes[s][c] = k[c]; }
710 out.slot_key_len[s] = 0;
723 "key_selector requires SIMDJSON_PADDING > 63 for its SIMD key reads");
725#if SIMDJSON_KEY_SELECTOR_HAS_NEON
730simdjson_really_inline
bool neon_all_bytes_zero(uint8x16_t diff)
noexcept {
731 return vmaxvq_u32(vreinterpretq_u32_u8(diff)) == 0;
738template <std::
size_t MaxKeyLen>
739simdjson_really_inline std::size_t scan_key_length(
const char* p)
noexcept {
744 static_assert(MaxKeyLen <= 63,
"MaxKeyLen must be <= 63 for current SIMD implementations");
746 [[maybe_unused]]
constexpr std::size_t num_blocks = MaxKeyLen / 16 + 1;
747#if SIMDJSON_KEY_SELECTOR_HAS_NEON
748 for (std::size_t b = 0; b < num_blocks; ++b) {
749 uint8x16_t v = vld1q_u8(
reinterpret_cast<const uint8_t*
>(p) + b * 16);
750 uint8x16_t cmp = vceqq_u8(v, vdupq_n_u8(
'"'));
751 uint64_t m = vget_lane_u64(
752 vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(cmp), 4)), 0);
753 if (simdjson_likely(m != 0)) {
return b * 16 + (std::size_t(__builtin_ctzll(m)) >> 2); }
755 return MaxKeyLen + 1;
756#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
757 for (std::size_t b = 0; b < num_blocks; ++b) {
758 __m128i v = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(p + b * 16));
759 __m128i cmp = _mm_cmpeq_epi8(v, _mm_set1_epi8(
'"'));
760 unsigned m =
static_cast<unsigned>(_mm_movemask_epi8(cmp));
761 if (simdjson_likely(m != 0)) {
return b * 16 + std::size_t(__builtin_ctz(m)); }
763 return MaxKeyLen + 1;
764#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
765 for (std::size_t b = 0; b < num_blocks; ++b) {
766 __m128i v = __lsx_vld(
reinterpret_cast<const void*
>(p + b * 16), 0);
767 __m128i cmp = __lsx_vseq_b(v, __lsx_vreplgr2vr_b(
'"'));
770 unsigned m =
static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(cmp), 0)) & 0xFFFFu;
771 if (simdjson_likely(m != 0)) {
return b * 16 + std::size_t(__builtin_ctz(m)); }
773 return MaxKeyLen + 1;
775 for (std::size_t i = 0; i <= MaxKeyLen; ++i)
776 if (p[i] ==
'"')
return i;
777 return MaxKeyLen + 1;
784template <std::
size_t MaxKeyLen>
785simdjson_really_inline
bool compare_key_bytes(
786 const char* p,
const char* stored, std::size_t len)
noexcept {
789 [[maybe_unused]]
alignas(16)
static constexpr uint8_t idx16[16] =
790 {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
791 if constexpr (MaxKeyLen <= 16) {
792#if SIMDJSON_KEY_SELECTOR_HAS_NEON
793 uint8x16_t vp = vld1q_u8(
reinterpret_cast<const uint8_t*
>(p));
794 uint8x16_t vs = vld1q_u8(
reinterpret_cast<const uint8_t*
>(stored));
795 uint8x16_t mask = vcltq_u8(vld1q_u8(idx16), vdupq_n_u8(
static_cast<uint8_t
>(len)));
796 uint8x16_t diff = veorq_u8(vandq_u8(vp, mask), vs);
797 return neon_all_bytes_zero(diff);
798#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
799 __m128i vp = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(p));
800 __m128i vs = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(stored));
801 __m128i idx = _mm_load_si128(
reinterpret_cast<const __m128i*
>(idx16));
802 __m128i mask = _mm_cmplt_epi8(idx, _mm_set1_epi8(
static_cast<char>(len)));
803 __m128i eq = _mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs);
804 return _mm_movemask_epi8(eq) == 0xFFFF;
805#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
806 __m128i vp = __lsx_vld(
reinterpret_cast<const void*
>(p), 0);
807 __m128i vs = __lsx_vld(
reinterpret_cast<const void*
>(stored), 0);
808 __m128i idx = __lsx_vld(
reinterpret_cast<const void*
>(idx16), 0);
809 __m128i mask = __lsx_vslt_b(idx, __lsx_vreplgr2vr_b(
static_cast<int>(len)));
810 __m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
811 return (
static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu) == 0xFFFFu;
813 for (std::size_t i = 0; i < len; ++i)
814 if (p[i] != stored[i])
return false;
817 }
else if constexpr (MaxKeyLen <= 32) {
818 [[maybe_unused]]
alignas(16)
static constexpr uint8_t idx32_hi[16] =
819 {16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};
820#if SIMDJSON_KEY_SELECTOR_HAS_NEON
821 uint8x16_t vp_lo = vld1q_u8(
reinterpret_cast<const uint8_t*
>(p));
822 uint8x16_t vp_hi = vld1q_u8(
reinterpret_cast<const uint8_t*
>(p) + 16);
823 uint8x16_t vs_lo = vld1q_u8(
reinterpret_cast<const uint8_t*
>(stored));
824 uint8x16_t vs_hi = vld1q_u8(
reinterpret_cast<const uint8_t*
>(stored) + 16);
825 uint8x16_t lenv = vdupq_n_u8(
static_cast<uint8_t
>(len));
826 uint8x16_t m_lo = vcltq_u8(vld1q_u8(idx16), lenv);
827 uint8x16_t m_hi = vcltq_u8(vld1q_u8(idx32_hi), lenv);
828 uint8x16_t d_lo = veorq_u8(vandq_u8(vp_lo, m_lo), vs_lo);
829 uint8x16_t d_hi = veorq_u8(vandq_u8(vp_hi, m_hi), vs_hi);
830 return neon_all_bytes_zero(vorrq_u8(d_lo, d_hi));
831#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
832 __m128i vp_lo = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(p));
833 __m128i vp_hi = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(p + 16));
834 __m128i vs_lo = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(stored));
835 __m128i vs_hi = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(stored + 16));
836 __m128i lenv = _mm_set1_epi8(
static_cast<char>(len));
837 __m128i m_lo = _mm_cmplt_epi8(_mm_load_si128(
reinterpret_cast<const __m128i*
>(idx16)), lenv);
838 __m128i m_hi = _mm_cmplt_epi8(_mm_load_si128(
reinterpret_cast<const __m128i*
>(idx32_hi)), lenv);
839 __m128i eq_lo = _mm_cmpeq_epi8(_mm_and_si128(vp_lo, m_lo), vs_lo);
840 __m128i eq_hi = _mm_cmpeq_epi8(_mm_and_si128(vp_hi, m_hi), vs_hi);
841 return (_mm_movemask_epi8(eq_lo) & _mm_movemask_epi8(eq_hi)) == 0xFFFF;
842#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
843 __m128i lenv = __lsx_vreplgr2vr_b(
static_cast<int>(len));
844 __m128i vp_lo = __lsx_vld(
reinterpret_cast<const void*
>(p), 0);
845 __m128i vp_hi = __lsx_vld(
reinterpret_cast<const void*
>(p + 16), 0);
846 __m128i vs_lo = __lsx_vld(
reinterpret_cast<const void*
>(stored), 0);
847 __m128i vs_hi = __lsx_vld(
reinterpret_cast<const void*
>(stored + 16), 0);
848 __m128i m_lo = __lsx_vslt_b(__lsx_vld(
reinterpret_cast<const void*
>(idx16), 0), lenv);
849 __m128i m_hi = __lsx_vslt_b(__lsx_vld(
reinterpret_cast<const void*
>(idx32_hi), 0), lenv);
850 __m128i eq_lo = __lsx_vseq_b(__lsx_vand_v(vp_lo, m_lo), vs_lo);
851 __m128i eq_hi = __lsx_vseq_b(__lsx_vand_v(vp_hi, m_hi), vs_hi);
852 unsigned mlo =
static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_lo), 0)) & 0xFFFFu;
853 unsigned mhi =
static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq_hi), 0)) & 0xFFFFu;
854 return (mlo & mhi) == 0xFFFFu;
856 for (std::size_t i = 0; i < len; ++i)
857 if (p[i] != stored[i])
return false;
860 }
else if constexpr (MaxKeyLen <= 64) {
864 [[maybe_unused]]
constexpr std::size_t num_blocks = (MaxKeyLen + 15) / 16;
865#if SIMDJSON_KEY_SELECTOR_HAS_NEON
866 uint8x16_t base = vld1q_u8(idx16);
867 uint8x16_t lenv = vdupq_n_u8(
static_cast<uint8_t
>(len));
868 uint8x16_t acc = vdupq_n_u8(0);
869 for (std::size_t b = 0; b < num_blocks; ++b) {
870 uint8x16_t vp = vld1q_u8(
reinterpret_cast<const uint8_t*
>(p) + b * 16);
871 uint8x16_t vs = vld1q_u8(
reinterpret_cast<const uint8_t*
>(stored) + b * 16);
872 uint8x16_t idxv = vaddq_u8(base, vdupq_n_u8(
static_cast<uint8_t
>(b * 16)));
873 uint8x16_t mask = vcltq_u8(idxv, lenv);
874 acc = vorrq_u8(acc, veorq_u8(vandq_u8(vp, mask), vs));
876 return neon_all_bytes_zero(acc);
877#elif SIMDJSON_KEY_SELECTOR_HAS_SSE2
878 __m128i base = _mm_load_si128(
reinterpret_cast<const __m128i*
>(idx16));
879 __m128i lenv = _mm_set1_epi8(
static_cast<char>(len));
881 for (std::size_t b = 0; b < num_blocks; ++b) {
882 __m128i vp = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(p + b * 16));
883 __m128i vs = _mm_loadu_si128(
reinterpret_cast<const __m128i*
>(stored + b * 16));
884 __m128i idxv = _mm_add_epi8(base, _mm_set1_epi8(
static_cast<char>(b * 16)));
885 __m128i mask = _mm_cmplt_epi8(idxv, lenv);
886 eq &= _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_and_si128(vp, mask), vs));
889#elif SIMDJSON_KEY_SELECTOR_HAS_LSX
890 __m128i base = __lsx_vld(
reinterpret_cast<const void*
>(idx16), 0);
891 __m128i lenv = __lsx_vreplgr2vr_b(
static_cast<int>(len));
892 unsigned acc = 0xFFFFu;
893 for (std::size_t b = 0; b < num_blocks; ++b) {
894 __m128i vp = __lsx_vld(
reinterpret_cast<const void*
>(p + b * 16), 0);
895 __m128i vs = __lsx_vld(
reinterpret_cast<const void*
>(stored + b * 16), 0);
896 __m128i idxv = __lsx_vadd_b(base, __lsx_vreplgr2vr_b(
static_cast<int>(b * 16)));
897 __m128i mask = __lsx_vslt_b(idxv, lenv);
898 __m128i eq = __lsx_vseq_b(__lsx_vand_v(vp, mask), vs);
899 acc &=
static_cast<unsigned>(__lsx_vpickve2gr_w(__lsx_vmskltz_b(eq), 0)) & 0xFFFFu;
901 return acc == 0xFFFFu;
903 for (std::size_t i = 0; i < len; ++i)
904 if (p[i] != stored[i])
return false;
908 for (std::size_t i = 0; i < len; ++i)
909 if (p[i] != stored[i])
return false;
938template <std::
size_t N, std::
size_t MaxKeyLen>
940 static constexpr std::size_t KEY_STRIDE = ((MaxKeyLen + 15) / 16) * 16;
942 std::uint8_t byte_offset{0};
943 std::uint8_t shift{0};
944 std::array<std::uint8_t, 256> window_to_key{};
945 std::array<std::uint8_t, N> key_len{};
946 std::array<std::array<char, KEY_STRIDE>, N> key_bytes{};
951template <std::
size_t N>
952constexpr unsigned window_byte_at(
const std::array<std::string_view, N>& keys,
953 std::size_t i, std::size_t idx)
noexcept {
954 if (idx < keys[i].size()) {
return static_cast<unsigned char>(keys[i][idx]); }
955 return static_cast<unsigned>(
'"');
961template <std::
size_t N>
962constexpr unsigned window_value(
const std::array<std::string_view, N>& keys,
963 std::size_t i, std::size_t byte_offset, std::size_t shift)
noexcept {
964 unsigned lo = window_byte_at<N>(keys, i, byte_offset);
965 unsigned hi = window_byte_at<N>(keys, i, byte_offset + 1);
966 return ((lo | (hi << 8)) >> shift) & 0xFFu;
969template <std::
size_t N, std::
size_t MaxKeyLen>
970consteval window_data<N, MaxKeyLen>
971compute_window(
const std::array<std::string_view, N>& keys) {
972 window_data<N, MaxKeyLen> out{};
974 std::size_t min_len = keys[0].size();
975 for (std::size_t i = 1; i < N; ++i) {
976 if (keys[i].size() < min_len) { min_len = keys[i].size(); }
980 for (std::size_t off = 0; off <= min_len; ++off) {
981 for (std::size_t shift = 0; shift < 8; ++shift) {
985 if (shift != 0 && off + 1 > min_len) {
continue; }
987 bool distinct =
true;
988 for (std::size_t i = 0; i < N && distinct; ++i) {
989 for (std::size_t j = i + 1; j < N; ++j) {
990 if (window_value<N>(keys, i, off, shift) == window_value<N>(keys, j, off, shift)) {
996 if (!distinct) {
continue; }
999 out.byte_offset =
static_cast<std::uint8_t
>(off);
1000 out.shift =
static_cast<std::uint8_t
>(shift);
1001 for (std::size_t b = 0; b < 256; ++b) { out.window_to_key[b] =
static_cast<std::uint8_t
>(N); }
1002 for (std::size_t i = 0; i < N; ++i) {
1003 out.window_to_key[window_value<N>(keys, i, off, shift)] =
static_cast<std::uint8_t
>(i);
1004 out.key_len[i] =
static_cast<std::uint8_t
>(keys[i].size());
1005 for (std::size_t c = 0; c < keys[i].size(); ++c) { out.key_bytes[i][c] = keys[i][c]; }
1014simdjson_really_inline std::uint8_t read_window(
const char* p, std::size_t byte_offset,
1015 std::size_t shift)
noexcept {
1019 std::memcpy(&w, p + byte_offset,
sizeof(w));
1020#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
1021 w =
static_cast<std::uint16_t
>((w >> 8) | (w << 8));
1023 return static_cast<std::uint8_t
>((w >> shift) & 0xFFu);
1034template <std::size_t N, std::size_t MaxKeyLen, std::size_t... Is>
1035simdjson_really_inline std::size_t
1036match_window_candidate(
const char* p, std::uint8_t ki,
1037 const window_data<N, MaxKeyLen>& w,
1038 std::index_sequence<Is...>)
noexcept {
1039 std::size_t result = N;
1040 auto try_match = [&](
auto Ic) {
1041 constexpr std::size_t i =
decltype(Ic)::value;
1042 if (ki == i && p[w.key_len[i]] ==
'"' &&
1043 key_selector_detail::compare_key_bytes<MaxKeyLen>(
1044 p, w.key_bytes[i].data(), w.key_len[i])) {
1048 (try_match(std::integral_constant<std::size_t, Is>{}), ...);
1055SIMDJSON_CONSTEXPR_STRING
void append_uint(std::string& s, std::size_t v) {
1056 if (v == 0) { s.push_back(
'0');
return; }
1059 while (v > 0) { buf[n++] =
static_cast<char>(
'0' + (v % 10)); v /= 10; }
1060 while (n > 0) { s.push_back(buf[--n]); }
1065SIMDJSON_CONSTEXPR_STRING
void append_byte(std::string& s,
unsigned b) {
1067 if (b >= 0x20 && b < 0x7f) {
1069 s.push_back(
static_cast<char>(b));
1096template <constevalutil::fixed_string... Keys>
1097struct key_selector {
1098 static constexpr std::size_t N =
sizeof...(Keys);
1099 static_assert(N > 0,
"key_selector requires at least one key");
1100 static_assert(N <= 255,
"key_selector supports at most 255 keys");
1102 static constexpr std::array<std::string_view, N> keys{ Keys.view()... };
1103 static constexpr std::size_t max_key_len = key_selector_detail::compute_max_key_len<N>(keys);
1105 "key longer than SIMDJSON_PADDING is not supported");
1110 static_assert(max_key_len <= 63,
1111 "key_selector keys must be at most 63 characters long");
1113 static constexpr auto result = key_selector_detail::compute_phf<N>(keys);
1114 static constexpr std::size_t table_size = result.table_size;
1116 static constexpr auto phf =
1117 key_selector_detail::build_phf_data<N, table_size, max_key_len>(keys, result);
1123 static constexpr auto window =
1124 key_selector_detail::compute_window<N, max_key_len>(keys);
1126 static constexpr std::size_t size() noexcept {
return N; }
1138 static simdjson_really_inline std::size_t match_raw(
const char* p, std::size_t len)
noexcept {
1139 if (len == 0 || len > max_key_len) {
return N; }
1141 if constexpr (window.ok) {
1147 std::uint8_t ki = window.window_to_key[
1148 key_selector_detail::read_window(p, window.byte_offset, window.shift)];
1149 if (ki >= N) {
return N; }
1150 return key_selector_detail::match_window_candidate(
1151 p, ki, window, std::make_index_sequence<N>{});
1155 if (phf.num_positions == key_selector_detail::HD_MODE) {
1157 std::string_view key(p, len);
1158 std::size_t bucket = key_selector_detail::hd_bucket_hash(key);
1159 std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
1160 ? key_selector_detail::hd_key_hash_2(key)
1161 : key_selector_detail::hd_key_hash_4(key);
1162 slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
1169 std::size_t h = len;
1170 for (std::uint8_t i = 0; i < phf.num_positions; ++i) {
1171 std::uint8_t pos = phf.positions[i];
1172 std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
1173 ? (len - std::size_t{1})
1174 : static_cast<std::size_t>(pos);
1176 h += phf.asso_values[i][
static_cast<unsigned char>(p[idx])];
1179 slot = h & (table_size - 1);
1182 std::uint8_t ki = phf.slot_to_key[slot];
1183 if (ki >= N) {
return N; }
1184 if (phf.slot_key_len[slot] != len) {
return N; }
1185 if (!key_selector_detail::compare_key_bytes<max_key_len>(
1186 p, phf.slot_key_bytes[slot].data(), len)) {
return N; }
1196 static simdjson_really_inline std::size_t match_raw(raw_json_string rjs)
noexcept {
1197 const char* p = rjs.raw();
1198 if constexpr (window.ok) {
1203 std::uint8_t ki = window.window_to_key[
1204 key_selector_detail::read_window(p, window.byte_offset, window.shift)];
1205 if (ki >= N) {
return N; }
1206 return key_selector_detail::match_window_candidate(
1207 p, ki, window, std::make_index_sequence<N>{});
1209 return match_raw(p, key_selector_detail::scan_key_length<max_key_len>(p));
1213 static constexpr std::string_view key_at(std::size_t i)
noexcept {
1234 static SIMDJSON_CONSTEXPR_STRING std::string describe() {
1236 s +=
"key_selector: ";
1237 key_selector_detail::append_uint(s, N);
1238 s +=
" keys, max key length ";
1239 key_selector_detail::append_uint(s, max_key_len);
1241 for (std::size_t i = 0; i < N; ++i) {
1243 key_selector_detail::append_uint(s, i);
1247 key_selector_detail::append_uint(s, keys[i].size());
1250 if constexpr (window.ok) {
1252 s +=
"algorithm: single 8-bit window\n";
1253 s +=
" step 1: read 2 bytes at offset ";
1254 key_selector_detail::append_uint(s, window.byte_offset);
1255 s +=
", interpret them as a little-endian 16-bit value, shift right by ";
1256 key_selector_detail::append_uint(s, window.shift);
1257 s +=
" bits, and keep the low 8 bits\n";
1258 s +=
" step 2: map that byte through a 256-entry table to a key index (";
1259 key_selector_detail::append_uint(s, N);
1260 s +=
" means no match):\n";
1261 for (std::size_t b = 0; b < 256; ++b) {
1262 if (window.window_to_key[b] < N) {
1264 key_selector_detail::append_byte(s,
static_cast<unsigned>(b));
1266 key_selector_detail::append_uint(s, window.window_to_key[b]);
1270 s +=
" step 3: confirm the candidate by checking the closing quote sits at the key's length and comparing the key bytes\n";
1273 if constexpr (phf.num_positions == key_selector_detail::HD_MODE) {
1274 s +=
"algorithm: hash-and-displace perfect hash\n";
1275 s +=
" step 1: bucket = (first_byte + last_byte*3 + length*17) mod 256\n";
1276 s +=
" step 2: keyhash = base-31 rolling hash of the length and the first ";
1277 key_selector_detail::append_uint(s, phf.hd_hash_variant);
1279 s +=
" step 3: slot = (displacement[bucket] + keyhash) mod ";
1280 key_selector_detail::append_uint(s, table_size);
1281 s +=
"\n step 4: slot_to_key[slot] gives the candidate key index\n";
1282 s +=
" per-key derivation:\n";
1283 for (std::size_t i = 0; i < N; ++i) {
1284 std::string_view k = keys[i];
1285 std::size_t bucket = key_selector_detail::hd_bucket_hash(k);
1286 std::size_t kh = (phf.hd_hash_variant == key_selector_detail::HD_HASH_2BYTE_FLAG)
1287 ? key_selector_detail::hd_key_hash_2(k)
1288 : key_selector_detail::hd_key_hash_4(k);
1289 std::size_t slot = (phf.asso_values[0][bucket] + kh) & (table_size - 1);
1293 key_selector_detail::append_uint(s, bucket);
1294 s +=
" displacement=";
1295 key_selector_detail::append_uint(s, phf.asso_values[0][bucket]);
1297 key_selector_detail::append_uint(s, kh);
1299 key_selector_detail::append_uint(s, slot);
1303 s +=
"algorithm: gperf-style perfect hash over ";
1304 key_selector_detail::append_uint(s, phf.num_positions);
1305 s +=
" character position(s)\n";
1306 s +=
" step 1: h = key length\n";
1307 s +=
" step 2: for each position below, add its association value for the key byte there (a position past the key's length contributes 0):\n";
1308 for (std::size_t i = 0; i < phf.num_positions; ++i) {
1310 if (phf.positions[i] == key_selector_detail::POS_LAST_CHAR) {
1311 s +=
"last character";
1314 key_selector_detail::append_uint(s, phf.positions[i]);
1318 s +=
" step 3: slot = h mod ";
1319 key_selector_detail::append_uint(s, table_size);
1320 s +=
" (a power of two, applied as a bitmask)\n";
1321 s +=
" step 4: slot_to_key[slot] gives the candidate key index\n";
1322 s +=
" per-key derivation:\n";
1323 for (std::size_t i = 0; i < N; ++i) {
1324 std::string_view k = keys[i];
1325 std::size_t h = k.size();
1326 for (std::size_t pi = 0; pi < phf.num_positions; ++pi) {
1327 std::size_t pos = phf.positions[pi];
1328 std::size_t idx = (pos == key_selector_detail::POS_LAST_CHAR)
1329 ? (k.size() - 1) : pos;
1330 if (idx < k.size()) {
1331 h += phf.asso_values[pi][
static_cast<unsigned char>(k[idx])];
1334 std::size_t slot = h & (table_size - 1);
1338 key_selector_detail::append_uint(s, h);
1340 key_selector_detail::append_uint(s, slot);
1344 s +=
" occupied slots (slot -> key):\n";
1345 for (std::size_t slot = 0; slot < table_size; ++slot) {
1346 if (phf.slot_to_key[slot] < N) {
1348 key_selector_detail::append_uint(s, slot);
1350 key_selector_detail::append_uint(s, phf.slot_to_key[slot]);
1352 s += keys[phf.slot_to_key[slot]];
1354 key_selector_detail::append_uint(s, phf.slot_key_len[slot]);
1358 s +=
" confirm the candidate by checking the key length matches and comparing the key bytes\n";
1364namespace key_selector_detail {
1365template <
typename>
struct is_key_selector : std::false_type {};
1366template <constevalutil::fixed_string... Keys>
1367struct is_key_selector<key_selector<Keys...>> : std::true_type {};
1375template <
typename T>
1376concept key_selector_type = key_selector_detail::is_key_selector<T>::value;
The top level simdjson namespace, containing everything the library provides.
constexpr size_t SIMDJSON_PADDING
The amount of padding needed in a buffer to parse JSON.