@@ -635,7 +635,9 @@ namespace glz
635635 power_of_two, // Powers of 2 (flags): countr_zero(value)
636636 small_range, // Sparse lookup table for small ranges
637637 modular, // Perfect hash: (value * seed) % table_size
638- modular_shifted // Perfect hash with shift: ((value >> shift) * seed) % table_size
638+ modular_shifted, // Perfect hash with shift: ((value >> shift) * seed) % table_size
639+ linear_search, // Fallback: linear scan through values (N <= 16)
640+ binary_search // Fallback: binary search through sorted values (N > 16)
639641 };
640642
641643 template <size_t N, size_t TableSize>
@@ -852,20 +854,29 @@ namespace glz
852854 return std::pair{false , uint64_t {0 }};
853855 }();
854856
855- static_assert (shifted_info.first , " Failed to find perfect hash seed for enum" );
857+ if constexpr (shifted_info.first ) {
858+ int_keys_info_t <N, table_size> info{.type = int_hash_type::modular_shifted,
859+ .seed = shifted_info.second ,
860+ .table_size = table_size,
861+ .shift = common_shift};
862+ info.table .fill (static_cast <uint8_t >(N));
856863
857- int_keys_info_t <N, table_size> info{.type = int_hash_type::modular_shifted,
858- .seed = shifted_info.second ,
859- .table_size = table_size,
860- .shift = common_shift};
861- info.table .fill (static_cast <uint8_t >(N));
862-
863- for (size_t i = 0 ; i < N; ++i) {
864- const auto shifted = static_cast <uint64_t >(vals[i]) >> common_shift;
865- const auto h = (shifted * info.seed ) % table_size;
866- info.table [h] = static_cast <uint8_t >(i);
864+ for (size_t i = 0 ; i < N; ++i) {
865+ const auto shifted = static_cast <uint64_t >(vals[i]) >> common_shift;
866+ const auto h = (shifted * info.seed ) % table_size;
867+ info.table [h] = static_cast <uint8_t >(i);
868+ }
869+ return info;
870+ }
871+ else {
872+ // Fallback: linear search for small N, binary search for larger N
873+ if constexpr (N <= 16 ) {
874+ return int_keys_info_t <N, 0 >{.type = int_hash_type::linear_search};
875+ }
876+ else {
877+ return int_keys_info_t <N, 0 >{.type = int_hash_type::binary_search};
878+ }
867879 }
868- return info;
869880 }
870881 }
871882 }
@@ -924,11 +935,73 @@ namespace glz
924935 const auto h = (static_cast <uint64_t >(value) * Info.seed ) % Info.table_size ;
925936 return Info.table [h]; // Returns N if slot is empty
926937 }
927- else { // modular_shifted
938+ else if constexpr (Info. type == modular_shifted) {
928939 const auto shifted = static_cast <uint64_t >(value) >> Info.shift ;
929940 const auto h = (shifted * Info.seed ) % Info.table_size ;
930941 return Info.table [h]; // Returns N if slot is empty
931942 }
943+ else if constexpr (Info.type == linear_search) {
944+ // Linear scan through enum values
945+ constexpr auto & values = enum_values_array<T>;
946+ for (size_t i = 0 ; i < N; ++i) {
947+ if (values[i] == value) {
948+ return i;
949+ }
950+ }
951+ return N; // Not found
952+ }
953+ else { // binary_search
954+ // Binary search through sorted enum values
955+ // Compute sorted indices and values together to avoid capture issues
956+ constexpr auto sorted_data = []() {
957+ struct result_t {
958+ std::array<size_t , N> indices{};
959+ std::array<U, N> values{};
960+ };
961+ result_t result{};
962+
963+ // Initialize indices
964+ for (size_t i = 0 ; i < N; ++i) {
965+ result.indices [i] = i;
966+ }
967+
968+ // Sort indices by their corresponding values (bubble sort for constexpr)
969+ constexpr auto & src_values = enum_values_array<T>;
970+ for (size_t i = 0 ; i < N - 1 ; ++i) {
971+ for (size_t j = i + 1 ; j < N; ++j) {
972+ if (src_values[result.indices [j]] < src_values[result.indices [i]]) {
973+ auto tmp = result.indices [i];
974+ result.indices [i] = result.indices [j];
975+ result.indices [j] = tmp;
976+ }
977+ }
978+ }
979+
980+ // Build sorted values array
981+ for (size_t i = 0 ; i < N; ++i) {
982+ result.values [i] = src_values[result.indices [i]];
983+ }
984+
985+ return result;
986+ }();
987+
988+ // Binary search
989+ size_t left = 0 ;
990+ size_t right = N;
991+ while (left < right) {
992+ const size_t mid = left + (right - left) / 2 ;
993+ if (sorted_data.values [mid] < value) {
994+ left = mid + 1 ;
995+ }
996+ else {
997+ right = mid;
998+ }
999+ }
1000+ if (left < N && sorted_data.values [left] == value) {
1001+ return sorted_data.indices [left];
1002+ }
1003+ return N; // Not found
1004+ }
9321005 }
9331006 };
9341007
@@ -2204,8 +2277,9 @@ namespace glz
22042277
22052278 GLZ_ALWAYS_INLINE static constexpr size_t op (auto && it, auto end) noexcept
22062279 {
2207- // For JSON we require at a minimum ":1} characters after a key (1 being a single char number)
2208- // This means that we can require all these characters to exist for SWAR parsing
2280+ // Bounds checks ensure we can safely read the string content and determine its length.
2281+ // Note: This is used for both object keys and enum values, so we cannot assume
2282+ // extra characters exist after the closing quote (e.g., standalone enum: "value")
22092283
22102284 if constexpr (length_range == 0 ) {
22112285 if ((it + min_length) >= end) [[unlikely]] {
@@ -2217,7 +2291,9 @@ namespace glz
22172291 else {
22182292 if constexpr (length_range == 1 ) {
22192293 auto quote = it + min_length;
2220- if ((quote + 1 ) >= end) [[unlikely]] {
2294+ // Ensure we can read *quote to determine if string is min_length or max_length.
2295+ // The check (quote + 1) > end ensures quote < end, making *quote dereferenceable.
2296+ if ((quote + 1 ) > end) [[unlikely]] {
22212297 return N;
22222298 }
22232299
@@ -2673,20 +2749,29 @@ namespace glz
26732749 return std::pair{false , uint64_t {0 }};
26742750 }();
26752751
2676- static_assert (shifted_info.first , " Failed to find perfect hash seed for variant int IDs" );
2752+ if constexpr (shifted_info.first ) {
2753+ int_keys_info_t <N, table_size> info{.type = int_hash_type::modular_shifted,
2754+ .seed = shifted_info.second ,
2755+ .table_size = table_size,
2756+ .shift = common_shift};
2757+ info.table .fill (static_cast <uint8_t >(N));
26772758
2678- int_keys_info_t <N, table_size> info{.type = int_hash_type::modular_shifted,
2679- .seed = shifted_info.second ,
2680- .table_size = table_size,
2681- .shift = common_shift};
2682- info.table .fill (static_cast <uint8_t >(N));
2683-
2684- for (size_t i = 0 ; i < N; ++i) {
2685- const auto shifted = static_cast <uint64_t >(vals[i]) >> common_shift;
2686- const auto h = (shifted * info.seed ) % table_size;
2687- info.table [h] = static_cast <uint8_t >(i);
2759+ for (size_t i = 0 ; i < N; ++i) {
2760+ const auto shifted = static_cast <uint64_t >(vals[i]) >> common_shift;
2761+ const auto h = (shifted * info.seed ) % table_size;
2762+ info.table [h] = static_cast <uint8_t >(i);
2763+ }
2764+ return info;
2765+ }
2766+ else {
2767+ // Fallback: linear search for small N, binary search for larger N
2768+ if constexpr (N <= 16 ) {
2769+ return int_keys_info_t <N, 0 >{.type = int_hash_type::linear_search};
2770+ }
2771+ else {
2772+ return int_keys_info_t <N, 0 >{.type = int_hash_type::binary_search};
2773+ }
26882774 }
2689- return info;
26902775 }
26912776 }
26922777 }
@@ -2759,11 +2844,61 @@ namespace glz
27592844 const auto h = (static_cast <uint64_t >(id) * Info.seed ) % Info.table_size ;
27602845 return Info.table [h];
27612846 }
2762- else { // modular_shifted
2847+ else if constexpr (Info. type == modular_shifted) {
27632848 const auto shifted = static_cast <uint64_t >(id) >> Info.shift ;
27642849 const auto h = (shifted * Info.seed ) % Info.table_size ;
27652850 return Info.table [h];
27662851 }
2852+ else if constexpr (Info.type == linear_search) {
2853+ for (size_t i = 0 ; i < N; ++i) {
2854+ if (ids_v<T>[i] == id) {
2855+ return i;
2856+ }
2857+ }
2858+ return N;
2859+ }
2860+ else { // binary_search
2861+ constexpr auto sorted_data = []() {
2862+ struct result_t {
2863+ std::array<size_t , N> indices{};
2864+ std::array<U, N> values{};
2865+ };
2866+ result_t result{};
2867+
2868+ for (size_t i = 0 ; i < N; ++i) {
2869+ result.indices [i] = i;
2870+ }
2871+ for (size_t i = 0 ; i < N - 1 ; ++i) {
2872+ for (size_t j = i + 1 ; j < N; ++j) {
2873+ if (ids_v<T>[result.indices [j]] < ids_v<T>[result.indices [i]]) {
2874+ auto tmp = result.indices [i];
2875+ result.indices [i] = result.indices [j];
2876+ result.indices [j] = tmp;
2877+ }
2878+ }
2879+ }
2880+ for (size_t i = 0 ; i < N; ++i) {
2881+ result.values [i] = ids_v<T>[result.indices [i]];
2882+ }
2883+ return result;
2884+ }();
2885+
2886+ size_t left = 0 ;
2887+ size_t right = N;
2888+ while (left < right) {
2889+ const size_t mid = left + (right - left) / 2 ;
2890+ if (sorted_data.values [mid] < id) {
2891+ left = mid + 1 ;
2892+ }
2893+ else {
2894+ right = mid;
2895+ }
2896+ }
2897+ if (left < N && sorted_data.values [left] == id) {
2898+ return sorted_data.indices [left];
2899+ }
2900+ return N;
2901+ }
27672902 }
27682903 };
27692904
0 commit comments