re2 1.22.2 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -4
- package/binding.gyp +5 -1
- package/lib/addon.cc +4 -0
- package/lib/new.cc +1 -246
- package/lib/pattern.cc +252 -0
- package/lib/pattern.h +10 -0
- package/lib/set.cc +777 -0
- package/lib/wrapped_re2_set.h +42 -0
- package/package.json +21 -9
- package/re2.d.ts +46 -9
- package/vendor/abseil-cpp/CMake/AbseilDll.cmake +14 -24
- package/vendor/abseil-cpp/CMake/AbseilHelpers.cmake +3 -3
- package/vendor/abseil-cpp/CMake/README.md +2 -2
- package/vendor/abseil-cpp/CMakeLists.txt +3 -3
- package/vendor/abseil-cpp/MODULE.bazel +6 -9
- package/vendor/abseil-cpp/README.md +6 -8
- package/vendor/abseil-cpp/absl/abseil.podspec.gen.py +6 -4
- package/vendor/abseil-cpp/absl/algorithm/BUILD.bazel +3 -0
- package/vendor/abseil-cpp/absl/algorithm/CMakeLists.txt +1 -0
- package/vendor/abseil-cpp/absl/algorithm/container.h +2 -19
- package/vendor/abseil-cpp/absl/algorithm/container_test.cc +4 -11
- package/vendor/abseil-cpp/absl/base/BUILD.bazel +60 -45
- package/vendor/abseil-cpp/absl/base/CMakeLists.txt +57 -38
- package/vendor/abseil-cpp/absl/base/attributes.h +76 -7
- package/vendor/abseil-cpp/absl/base/attributes_test.cc +43 -0
- package/vendor/abseil-cpp/absl/base/call_once.h +11 -12
- package/vendor/abseil-cpp/absl/base/config.h +22 -129
- package/vendor/abseil-cpp/absl/base/exception_safety_testing_test.cc +0 -4
- package/vendor/abseil-cpp/absl/base/{internal/fast_type_id.h → fast_type_id.h} +11 -16
- package/vendor/abseil-cpp/absl/base/{internal/fast_type_id_test.cc → fast_type_id_test.cc} +34 -30
- package/vendor/abseil-cpp/absl/base/internal/cycleclock.cc +0 -5
- package/vendor/abseil-cpp/absl/base/internal/cycleclock_config.h +7 -7
- package/vendor/abseil-cpp/absl/base/internal/endian.h +34 -38
- package/vendor/abseil-cpp/absl/base/internal/iterator_traits.h +71 -0
- package/vendor/abseil-cpp/absl/base/internal/iterator_traits_test.cc +85 -0
- package/vendor/abseil-cpp/absl/base/internal/iterator_traits_test_helper.h +97 -0
- package/vendor/abseil-cpp/absl/base/internal/low_level_alloc.cc +39 -9
- package/vendor/abseil-cpp/absl/base/internal/low_level_alloc.h +6 -0
- package/vendor/abseil-cpp/absl/base/internal/poison.cc +7 -6
- package/vendor/abseil-cpp/absl/base/internal/spinlock.cc +15 -28
- package/vendor/abseil-cpp/absl/base/internal/spinlock.h +65 -35
- package/vendor/abseil-cpp/absl/base/internal/spinlock_benchmark.cc +2 -2
- package/vendor/abseil-cpp/absl/base/internal/sysinfo_test.cc +2 -2
- package/vendor/abseil-cpp/absl/base/internal/thread_identity_benchmark.cc +1 -1
- package/vendor/abseil-cpp/absl/base/internal/thread_identity_test.cc +4 -4
- package/vendor/abseil-cpp/absl/base/internal/unaligned_access.h +6 -6
- package/vendor/abseil-cpp/absl/base/internal/unscaledcycleclock.cc +4 -0
- package/vendor/abseil-cpp/absl/base/internal/unscaledcycleclock.h +8 -3
- package/vendor/abseil-cpp/absl/base/no_destructor.h +11 -32
- package/vendor/abseil-cpp/absl/base/no_destructor_test.cc +0 -4
- package/vendor/abseil-cpp/absl/base/nullability.h +83 -72
- package/vendor/abseil-cpp/absl/base/nullability_test.cc +25 -64
- package/vendor/abseil-cpp/absl/base/options.h +3 -80
- package/vendor/abseil-cpp/absl/base/policy_checks.h +7 -7
- package/vendor/abseil-cpp/absl/base/raw_logging_test.cc +15 -0
- package/vendor/abseil-cpp/absl/base/spinlock_test_common.cc +50 -30
- package/vendor/abseil-cpp/absl/cleanup/BUILD.bazel +2 -1
- package/vendor/abseil-cpp/absl/cleanup/CMakeLists.txt +0 -1
- package/vendor/abseil-cpp/absl/cleanup/cleanup.h +1 -3
- package/vendor/abseil-cpp/absl/cleanup/cleanup_test.cc +0 -2
- package/vendor/abseil-cpp/absl/cleanup/internal/cleanup.h +3 -4
- package/vendor/abseil-cpp/absl/container/BUILD.bazel +74 -1
- package/vendor/abseil-cpp/absl/container/CMakeLists.txt +73 -0
- package/vendor/abseil-cpp/absl/container/btree_benchmark.cc +51 -9
- package/vendor/abseil-cpp/absl/container/btree_map.h +8 -6
- package/vendor/abseil-cpp/absl/container/btree_set.h +8 -6
- package/vendor/abseil-cpp/absl/container/btree_test.cc +89 -4
- package/vendor/abseil-cpp/absl/container/fixed_array.h +7 -15
- package/vendor/abseil-cpp/absl/container/fixed_array_test.cc +17 -0
- package/vendor/abseil-cpp/absl/container/flat_hash_map.h +20 -15
- package/vendor/abseil-cpp/absl/container/flat_hash_map_test.cc +8 -14
- package/vendor/abseil-cpp/absl/container/flat_hash_set.h +19 -14
- package/vendor/abseil-cpp/absl/container/flat_hash_set_test.cc +46 -0
- package/vendor/abseil-cpp/absl/container/inlined_vector.h +7 -6
- package/vendor/abseil-cpp/absl/container/inlined_vector_test.cc +28 -0
- package/vendor/abseil-cpp/absl/container/internal/btree.h +132 -29
- package/vendor/abseil-cpp/absl/container/internal/btree_container.h +175 -71
- package/vendor/abseil-cpp/absl/container/internal/common.h +43 -0
- package/vendor/abseil-cpp/absl/container/internal/common_policy_traits.h +1 -2
- package/vendor/abseil-cpp/absl/container/internal/compressed_tuple.h +28 -24
- package/vendor/abseil-cpp/absl/container/internal/compressed_tuple_test.cc +4 -17
- package/vendor/abseil-cpp/absl/container/internal/container_memory.h +80 -17
- package/vendor/abseil-cpp/absl/container/internal/container_memory_test.cc +32 -2
- package/vendor/abseil-cpp/absl/container/internal/hash_function_defaults.h +13 -8
- package/vendor/abseil-cpp/absl/container/internal/hash_function_defaults_test.cc +1 -52
- package/vendor/abseil-cpp/absl/container/internal/hash_generator_testing.cc +9 -31
- package/vendor/abseil-cpp/absl/container/internal/hash_generator_testing.h +23 -32
- package/vendor/abseil-cpp/absl/container/internal/hash_policy_testing.h +5 -1
- package/vendor/abseil-cpp/absl/container/internal/hash_policy_traits.h +11 -23
- package/vendor/abseil-cpp/absl/container/internal/hash_policy_traits_test.cc +14 -9
- package/vendor/abseil-cpp/absl/container/internal/hashtable_control_bytes.h +516 -0
- package/vendor/abseil-cpp/absl/container/internal/hashtable_control_bytes_test.cc +259 -0
- package/vendor/abseil-cpp/absl/container/internal/hashtablez_sampler.cc +23 -6
- package/vendor/abseil-cpp/absl/container/internal/hashtablez_sampler.h +32 -13
- package/vendor/abseil-cpp/absl/container/internal/hashtablez_sampler_test.cc +8 -8
- package/vendor/abseil-cpp/absl/container/internal/inlined_vector.h +2 -7
- package/vendor/abseil-cpp/absl/container/internal/layout.h +26 -42
- package/vendor/abseil-cpp/absl/container/internal/raw_hash_map.h +199 -68
- package/vendor/abseil-cpp/absl/container/internal/raw_hash_set.cc +1506 -213
- package/vendor/abseil-cpp/absl/container/internal/raw_hash_set.h +1095 -1658
- package/vendor/abseil-cpp/absl/container/internal/raw_hash_set_allocator_test.cc +3 -2
- package/vendor/abseil-cpp/absl/container/internal/raw_hash_set_benchmark.cc +31 -29
- package/vendor/abseil-cpp/absl/container/internal/raw_hash_set_probe_benchmark.cc +51 -20
- package/vendor/abseil-cpp/absl/container/internal/raw_hash_set_resize_impl.h +79 -0
- package/vendor/abseil-cpp/absl/container/internal/raw_hash_set_resize_impl_test.cc +66 -0
- package/vendor/abseil-cpp/absl/container/internal/raw_hash_set_test.cc +707 -363
- package/vendor/abseil-cpp/absl/container/node_hash_map.h +20 -15
- package/vendor/abseil-cpp/absl/container/node_hash_map_test.cc +0 -3
- package/vendor/abseil-cpp/absl/container/node_hash_set.h +18 -13
- package/vendor/abseil-cpp/absl/container/sample_element_size_test.cc +3 -8
- package/vendor/abseil-cpp/absl/copts/AbseilConfigureCopts.cmake +1 -1
- package/vendor/abseil-cpp/absl/copts/GENERATED_AbseilCopts.cmake +9 -20
- package/vendor/abseil-cpp/absl/copts/GENERATED_copts.bzl +9 -20
- package/vendor/abseil-cpp/absl/copts/copts.py +24 -15
- package/vendor/abseil-cpp/absl/crc/BUILD.bazel +3 -0
- package/vendor/abseil-cpp/absl/crc/crc32c.cc +0 -4
- package/vendor/abseil-cpp/absl/crc/crc32c.h +7 -5
- package/vendor/abseil-cpp/absl/crc/crc32c_benchmark.cc +17 -4
- package/vendor/abseil-cpp/absl/crc/crc32c_test.cc +30 -0
- package/vendor/abseil-cpp/absl/crc/internal/cpu_detect.cc +17 -0
- package/vendor/abseil-cpp/absl/crc/internal/cpu_detect.h +7 -1
- package/vendor/abseil-cpp/absl/crc/internal/crc32_x86_arm_combined_simd.h +0 -22
- package/vendor/abseil-cpp/absl/crc/internal/crc_memcpy_x86_arm_combined.cc +5 -0
- package/vendor/abseil-cpp/absl/crc/internal/crc_x86_arm_combined.cc +136 -165
- package/vendor/abseil-cpp/absl/crc/internal/gen_crc32c_consts.py +90 -0
- package/vendor/abseil-cpp/absl/debugging/BUILD.bazel +7 -0
- package/vendor/abseil-cpp/absl/debugging/CMakeLists.txt +4 -0
- package/vendor/abseil-cpp/absl/debugging/internal/addresses.h +57 -0
- package/vendor/abseil-cpp/absl/debugging/internal/decode_rust_punycode.cc +1 -1
- package/vendor/abseil-cpp/absl/debugging/internal/decode_rust_punycode.h +5 -5
- package/vendor/abseil-cpp/absl/debugging/internal/demangle.cc +8 -35
- package/vendor/abseil-cpp/absl/debugging/internal/demangle_rust.cc +16 -16
- package/vendor/abseil-cpp/absl/debugging/internal/demangle_test.cc +11 -10
- package/vendor/abseil-cpp/absl/debugging/internal/stacktrace_aarch64-inl.inc +40 -37
- package/vendor/abseil-cpp/absl/debugging/internal/stacktrace_arm-inl.inc +16 -7
- package/vendor/abseil-cpp/absl/debugging/internal/stacktrace_config.h +6 -5
- package/vendor/abseil-cpp/absl/debugging/internal/stacktrace_emscripten-inl.inc +14 -5
- package/vendor/abseil-cpp/absl/debugging/internal/stacktrace_generic-inl.inc +10 -4
- package/vendor/abseil-cpp/absl/debugging/internal/stacktrace_powerpc-inl.inc +27 -16
- package/vendor/abseil-cpp/absl/debugging/internal/stacktrace_riscv-inl.inc +13 -4
- package/vendor/abseil-cpp/absl/debugging/internal/stacktrace_unimplemented-inl.inc +4 -3
- package/vendor/abseil-cpp/absl/debugging/internal/stacktrace_win32-inl.inc +15 -28
- package/vendor/abseil-cpp/absl/debugging/internal/stacktrace_x86-inl.inc +25 -14
- package/vendor/abseil-cpp/absl/debugging/internal/vdso_support.cc +4 -0
- package/vendor/abseil-cpp/absl/debugging/stacktrace.cc +161 -27
- package/vendor/abseil-cpp/absl/debugging/stacktrace.h +73 -5
- package/vendor/abseil-cpp/absl/debugging/stacktrace_test.cc +435 -1
- package/vendor/abseil-cpp/absl/debugging/symbolize_elf.inc +55 -63
- package/vendor/abseil-cpp/absl/debugging/symbolize_emscripten.inc +3 -2
- package/vendor/abseil-cpp/absl/debugging/symbolize_win32.inc +25 -6
- package/vendor/abseil-cpp/absl/flags/BUILD.bazel +6 -0
- package/vendor/abseil-cpp/absl/flags/CMakeLists.txt +3 -0
- package/vendor/abseil-cpp/absl/flags/commandlineflag.h +2 -2
- package/vendor/abseil-cpp/absl/flags/flag.h +4 -3
- package/vendor/abseil-cpp/absl/flags/internal/commandlineflag.h +2 -2
- package/vendor/abseil-cpp/absl/flags/internal/flag.cc +14 -13
- package/vendor/abseil-cpp/absl/flags/internal/flag.h +34 -34
- package/vendor/abseil-cpp/absl/flags/internal/program_name.cc +2 -2
- package/vendor/abseil-cpp/absl/flags/internal/registry.h +4 -3
- package/vendor/abseil-cpp/absl/flags/internal/usage.cc +2 -2
- package/vendor/abseil-cpp/absl/flags/parse.cc +10 -6
- package/vendor/abseil-cpp/absl/flags/reflection.cc +9 -7
- package/vendor/abseil-cpp/absl/flags/usage.cc +2 -2
- package/vendor/abseil-cpp/absl/flags/usage_config.cc +2 -2
- package/vendor/abseil-cpp/absl/functional/BUILD.bazel +7 -6
- package/vendor/abseil-cpp/absl/functional/CMakeLists.txt +2 -4
- package/vendor/abseil-cpp/absl/functional/any_invocable.h +15 -15
- package/vendor/abseil-cpp/absl/functional/any_invocable_test.cc +10 -42
- package/vendor/abseil-cpp/absl/functional/function_ref.h +2 -9
- package/vendor/abseil-cpp/absl/functional/function_ref_test.cc +10 -0
- package/vendor/abseil-cpp/absl/functional/function_type_benchmark.cc +1 -1
- package/vendor/abseil-cpp/absl/functional/internal/any_invocable.h +112 -227
- package/vendor/abseil-cpp/absl/functional/internal/front_binder.h +10 -12
- package/vendor/abseil-cpp/absl/functional/internal/function_ref.h +2 -5
- package/vendor/abseil-cpp/absl/functional/overload.h +0 -20
- package/vendor/abseil-cpp/absl/functional/overload_test.cc +1 -7
- package/vendor/abseil-cpp/absl/hash/BUILD.bazel +16 -9
- package/vendor/abseil-cpp/absl/hash/CMakeLists.txt +6 -9
- package/vendor/abseil-cpp/absl/hash/hash.h +18 -0
- package/vendor/abseil-cpp/absl/hash/hash_benchmark.cc +3 -0
- package/vendor/abseil-cpp/absl/hash/hash_instantiated_test.cc +1 -1
- package/vendor/abseil-cpp/absl/hash/hash_test.cc +131 -30
- package/vendor/abseil-cpp/absl/hash/hash_testing.h +20 -20
- package/vendor/abseil-cpp/absl/hash/internal/hash.cc +129 -17
- package/vendor/abseil-cpp/absl/hash/internal/hash.h +326 -362
- package/vendor/abseil-cpp/absl/hash/internal/low_level_hash_test.cc +54 -151
- package/vendor/abseil-cpp/absl/hash/internal/spy_hash_state.h +14 -2
- package/vendor/abseil-cpp/absl/{strings/cord_buffer.cc → hash/internal/weakly_mixed_integer.h} +14 -6
- package/vendor/abseil-cpp/absl/log/BUILD.bazel +4 -0
- package/vendor/abseil-cpp/absl/log/CMakeLists.txt +7 -0
- package/vendor/abseil-cpp/absl/log/check.h +2 -1
- package/vendor/abseil-cpp/absl/log/check_test_impl.inc +308 -14
- package/vendor/abseil-cpp/absl/log/die_if_null.h +2 -2
- package/vendor/abseil-cpp/absl/log/flags_test.cc +7 -0
- package/vendor/abseil-cpp/absl/log/globals.h +4 -5
- package/vendor/abseil-cpp/absl/log/internal/BUILD.bazel +13 -9
- package/vendor/abseil-cpp/absl/log/internal/append_truncated.h +28 -0
- package/vendor/abseil-cpp/absl/log/internal/check_op.cc +24 -22
- package/vendor/abseil-cpp/absl/log/internal/check_op.h +149 -94
- package/vendor/abseil-cpp/absl/log/internal/conditions.cc +5 -3
- package/vendor/abseil-cpp/absl/log/internal/conditions.h +7 -2
- package/vendor/abseil-cpp/absl/log/internal/fnmatch_test.cc +1 -0
- package/vendor/abseil-cpp/absl/log/internal/log_message.cc +85 -43
- package/vendor/abseil-cpp/absl/log/internal/log_message.h +84 -59
- package/vendor/abseil-cpp/absl/log/internal/log_sink_set.cc +4 -4
- package/vendor/abseil-cpp/absl/log/internal/nullstream.h +1 -0
- package/vendor/abseil-cpp/absl/log/internal/proto.cc +3 -2
- package/vendor/abseil-cpp/absl/log/internal/proto.h +3 -3
- package/vendor/abseil-cpp/absl/log/internal/strip.h +4 -12
- package/vendor/abseil-cpp/absl/log/internal/structured.h +3 -7
- package/vendor/abseil-cpp/absl/log/internal/vlog_config.cc +9 -9
- package/vendor/abseil-cpp/absl/log/internal/vlog_config.h +8 -6
- package/vendor/abseil-cpp/absl/log/internal/voidify.h +10 -4
- package/vendor/abseil-cpp/absl/log/log.h +48 -35
- package/vendor/abseil-cpp/absl/log/log_basic_test_impl.inc +45 -0
- package/vendor/abseil-cpp/absl/log/log_entry.cc +241 -19
- package/vendor/abseil-cpp/absl/log/log_entry.h +2 -0
- package/vendor/abseil-cpp/absl/log/log_format_test.cc +412 -6
- package/vendor/abseil-cpp/absl/log/log_modifier_methods_test.cc +20 -0
- package/vendor/abseil-cpp/absl/log/log_sink_registry.h +2 -2
- package/vendor/abseil-cpp/absl/log/log_streamer_test.cc +15 -2
- package/vendor/abseil-cpp/absl/log/scoped_mock_log.h +7 -1
- package/vendor/abseil-cpp/absl/log/structured_test.cc +1 -0
- package/vendor/abseil-cpp/absl/memory/BUILD.bazel +2 -0
- package/vendor/abseil-cpp/absl/meta/BUILD.bazel +2 -0
- package/vendor/abseil-cpp/absl/meta/type_traits.h +46 -175
- package/vendor/abseil-cpp/absl/meta/type_traits_test.cc +1 -478
- package/vendor/abseil-cpp/absl/numeric/BUILD.bazel +7 -3
- package/vendor/abseil-cpp/absl/numeric/CMakeLists.txt +2 -0
- package/vendor/abseil-cpp/absl/numeric/bits.h +68 -2
- package/vendor/abseil-cpp/absl/numeric/bits_benchmark.cc +1 -1
- package/vendor/abseil-cpp/absl/numeric/bits_test.cc +83 -0
- package/vendor/abseil-cpp/absl/numeric/int128.cc +0 -52
- package/vendor/abseil-cpp/absl/numeric/int128_benchmark.cc +14 -15
- package/vendor/abseil-cpp/absl/numeric/int128_test.cc +13 -8
- package/vendor/abseil-cpp/absl/numeric/internal/bits.h +39 -7
- package/vendor/abseil-cpp/absl/profiling/BUILD.bazel +47 -0
- package/vendor/abseil-cpp/absl/profiling/CMakeLists.txt +38 -0
- package/vendor/abseil-cpp/absl/profiling/hashtable.cc +124 -0
- package/vendor/abseil-cpp/absl/profiling/hashtable.h +40 -0
- package/vendor/abseil-cpp/absl/profiling/internal/exponential_biased.cc +1 -1
- package/vendor/abseil-cpp/absl/profiling/internal/profile_builder.cc +462 -0
- package/vendor/abseil-cpp/absl/profiling/internal/profile_builder.h +138 -0
- package/vendor/abseil-cpp/absl/profiling/internal/sample_recorder.h +9 -9
- package/vendor/abseil-cpp/absl/profiling/internal/sample_recorder_test.cc +7 -3
- package/vendor/abseil-cpp/absl/random/BUILD.bazel +6 -4
- package/vendor/abseil-cpp/absl/random/CMakeLists.txt +20 -19
- package/vendor/abseil-cpp/absl/random/benchmarks.cc +16 -23
- package/vendor/abseil-cpp/absl/random/bit_gen_ref.h +10 -11
- package/vendor/abseil-cpp/absl/random/bit_gen_ref_test.cc +7 -2
- package/vendor/abseil-cpp/absl/random/distributions.h +6 -8
- package/vendor/abseil-cpp/absl/random/gaussian_distribution.h +1 -1
- package/vendor/abseil-cpp/absl/random/internal/BUILD.bazel +19 -20
- package/vendor/abseil-cpp/absl/random/internal/distribution_caller.h +5 -6
- package/vendor/abseil-cpp/absl/random/internal/{pool_urbg.cc → entropy_pool.cc} +24 -92
- package/vendor/abseil-cpp/absl/{base/inline_variable_test_b.cc → random/internal/entropy_pool.h} +14 -6
- package/vendor/abseil-cpp/absl/random/internal/entropy_pool_test.cc +119 -0
- package/vendor/abseil-cpp/absl/random/internal/mock_helpers.h +6 -7
- package/vendor/abseil-cpp/absl/random/internal/nonsecure_base.h +5 -6
- package/vendor/abseil-cpp/absl/random/internal/nonsecure_base_test.cc +39 -0
- package/vendor/abseil-cpp/absl/random/internal/randen_benchmarks.cc +8 -6
- package/vendor/abseil-cpp/absl/random/internal/randen_detect.cc +1 -1
- package/vendor/abseil-cpp/absl/random/internal/seed_material.cc +20 -12
- package/vendor/abseil-cpp/absl/random/internal/seed_material.h +5 -5
- package/vendor/abseil-cpp/absl/random/internal/seed_material_test.cc +3 -0
- package/vendor/abseil-cpp/absl/random/mock_distributions_test.cc +5 -4
- package/vendor/abseil-cpp/absl/random/mocking_bit_gen.h +8 -10
- package/vendor/abseil-cpp/absl/random/random.h +88 -53
- package/vendor/abseil-cpp/absl/random/seed_sequences.cc +6 -2
- package/vendor/abseil-cpp/absl/status/BUILD.bazel +26 -0
- package/vendor/abseil-cpp/absl/status/internal/status_internal.cc +3 -4
- package/vendor/abseil-cpp/absl/status/internal/status_internal.h +3 -4
- package/vendor/abseil-cpp/absl/status/internal/status_matchers.cc +4 -3
- package/vendor/abseil-cpp/absl/status/internal/statusor_internal.h +194 -32
- package/vendor/abseil-cpp/absl/status/status.cc +4 -8
- package/vendor/abseil-cpp/absl/status/status.h +8 -8
- package/vendor/abseil-cpp/absl/{base/inline_variable_test_a.cc → status/status_benchmark.cc} +20 -10
- package/vendor/abseil-cpp/absl/status/status_matchers_test.cc +65 -0
- package/vendor/abseil-cpp/absl/status/status_payload_printer.h +2 -2
- package/vendor/abseil-cpp/absl/status/statusor.cc +2 -2
- package/vendor/abseil-cpp/absl/status/statusor.h +49 -102
- package/vendor/abseil-cpp/absl/status/statusor_benchmark.cc +480 -0
- package/vendor/abseil-cpp/absl/status/statusor_test.cc +323 -1
- package/vendor/abseil-cpp/absl/strings/BUILD.bazel +70 -34
- package/vendor/abseil-cpp/absl/strings/CMakeLists.txt +6 -3
- package/vendor/abseil-cpp/absl/strings/ascii.cc +9 -9
- package/vendor/abseil-cpp/absl/strings/ascii.h +18 -18
- package/vendor/abseil-cpp/absl/strings/ascii_benchmark.cc +5 -8
- package/vendor/abseil-cpp/absl/strings/charconv.cc +21 -22
- package/vendor/abseil-cpp/absl/strings/charconv.h +5 -5
- package/vendor/abseil-cpp/absl/strings/charconv_benchmark.cc +1 -2
- package/vendor/abseil-cpp/absl/strings/charset_benchmark.cc +1 -1
- package/vendor/abseil-cpp/absl/strings/cord.cc +54 -58
- package/vendor/abseil-cpp/absl/strings/cord.h +94 -84
- package/vendor/abseil-cpp/absl/strings/cord_analysis.cc +11 -11
- package/vendor/abseil-cpp/absl/strings/cord_analysis.h +3 -3
- package/vendor/abseil-cpp/absl/strings/cord_test.cc +23 -0
- package/vendor/abseil-cpp/absl/strings/cordz_test_helpers.h +4 -5
- package/vendor/abseil-cpp/absl/strings/escaping.cc +130 -149
- package/vendor/abseil-cpp/absl/strings/escaping.h +9 -10
- package/vendor/abseil-cpp/absl/strings/escaping_benchmark.cc +2 -3
- package/vendor/abseil-cpp/absl/strings/escaping_test.cc +19 -9
- package/vendor/abseil-cpp/absl/strings/internal/charconv_bigint.cc +1 -1
- package/vendor/abseil-cpp/absl/strings/internal/charconv_bigint_test.cc +1 -1
- package/vendor/abseil-cpp/absl/strings/internal/cord_internal.h +6 -10
- package/vendor/abseil-cpp/absl/strings/internal/cord_rep_btree.cc +0 -4
- package/vendor/abseil-cpp/absl/strings/internal/cordz_handle.cc +6 -6
- package/vendor/abseil-cpp/absl/strings/internal/cordz_info.cc +5 -9
- package/vendor/abseil-cpp/absl/strings/internal/cordz_info.h +2 -4
- package/vendor/abseil-cpp/absl/strings/internal/damerau_levenshtein_distance_benchmark.cc +56 -0
- package/vendor/abseil-cpp/absl/strings/internal/memutil_benchmark.cc +2 -3
- package/vendor/abseil-cpp/absl/strings/internal/ostringstream_benchmark.cc +1 -2
- package/vendor/abseil-cpp/absl/strings/internal/str_format/arg.cc +7 -63
- package/vendor/abseil-cpp/absl/strings/internal/str_format/arg.h +1 -11
- package/vendor/abseil-cpp/absl/strings/internal/str_format/convert_test.cc +1 -6
- package/vendor/abseil-cpp/absl/strings/internal/str_format/extension.cc +0 -22
- package/vendor/abseil-cpp/absl/strings/internal/str_format/extension_test.cc +3 -2
- package/vendor/abseil-cpp/absl/strings/internal/str_format/output.cc +5 -3
- package/vendor/abseil-cpp/absl/strings/internal/str_format/parser.h +4 -2
- package/vendor/abseil-cpp/absl/strings/internal/str_join_internal.h +3 -3
- package/vendor/abseil-cpp/absl/strings/internal/str_split_internal.h +7 -2
- package/vendor/abseil-cpp/absl/strings/internal/string_constant.h +0 -5
- package/vendor/abseil-cpp/absl/strings/internal/utf8.cc +96 -1
- package/vendor/abseil-cpp/absl/strings/internal/utf8.h +15 -1
- package/vendor/abseil-cpp/absl/strings/internal/utf8_test.cc +196 -3
- package/vendor/abseil-cpp/absl/strings/numbers.cc +53 -32
- package/vendor/abseil-cpp/absl/strings/numbers.h +87 -58
- package/vendor/abseil-cpp/absl/strings/numbers_benchmark.cc +1 -1
- package/vendor/abseil-cpp/absl/strings/numbers_test.cc +634 -120
- package/vendor/abseil-cpp/absl/strings/str_cat.cc +6 -7
- package/vendor/abseil-cpp/absl/strings/str_cat.h +32 -32
- package/vendor/abseil-cpp/absl/strings/str_cat_benchmark.cc +25 -1
- package/vendor/abseil-cpp/absl/strings/str_cat_test.cc +2 -7
- package/vendor/abseil-cpp/absl/strings/str_format.h +18 -18
- package/vendor/abseil-cpp/absl/strings/str_format_test.cc +8 -14
- package/vendor/abseil-cpp/absl/strings/str_join_benchmark.cc +2 -3
- package/vendor/abseil-cpp/absl/strings/str_replace.cc +3 -3
- package/vendor/abseil-cpp/absl/strings/str_replace.h +6 -6
- package/vendor/abseil-cpp/absl/strings/str_replace_benchmark.cc +2 -3
- package/vendor/abseil-cpp/absl/strings/str_split.h +2 -2
- package/vendor/abseil-cpp/absl/strings/str_split_benchmark.cc +2 -3
- package/vendor/abseil-cpp/absl/strings/string_view.cc +4 -9
- package/vendor/abseil-cpp/absl/strings/string_view.h +38 -39
- package/vendor/abseil-cpp/absl/strings/string_view_benchmark.cc +4 -6
- package/vendor/abseil-cpp/absl/strings/string_view_test.cc +2 -50
- package/vendor/abseil-cpp/absl/strings/strip.h +4 -4
- package/vendor/abseil-cpp/absl/strings/substitute.cc +5 -4
- package/vendor/abseil-cpp/absl/strings/substitute.h +66 -64
- package/vendor/abseil-cpp/absl/strings/substitute_benchmark.cc +158 -0
- package/vendor/abseil-cpp/absl/synchronization/BUILD.bazel +6 -1
- package/vendor/abseil-cpp/absl/synchronization/CMakeLists.txt +2 -1
- package/vendor/abseil-cpp/absl/synchronization/barrier.cc +1 -1
- package/vendor/abseil-cpp/absl/synchronization/barrier_test.cc +3 -3
- package/vendor/abseil-cpp/absl/synchronization/blocking_counter.cc +2 -2
- package/vendor/abseil-cpp/absl/synchronization/internal/create_thread_identity.cc +3 -3
- package/vendor/abseil-cpp/absl/synchronization/internal/futex_waiter.cc +0 -4
- package/vendor/abseil-cpp/absl/synchronization/internal/graphcycles.cc +30 -33
- package/vendor/abseil-cpp/absl/synchronization/internal/graphcycles_benchmark.cc +2 -3
- package/vendor/abseil-cpp/absl/synchronization/internal/graphcycles_test.cc +6 -5
- package/vendor/abseil-cpp/absl/synchronization/internal/kernel_timeout.cc +0 -5
- package/vendor/abseil-cpp/absl/synchronization/internal/pthread_waiter.cc +0 -4
- package/vendor/abseil-cpp/absl/synchronization/internal/sem_waiter.cc +0 -4
- package/vendor/abseil-cpp/absl/synchronization/internal/stdcpp_waiter.cc +0 -4
- package/vendor/abseil-cpp/absl/synchronization/internal/thread_pool.h +3 -3
- package/vendor/abseil-cpp/absl/synchronization/internal/waiter_base.cc +0 -4
- package/vendor/abseil-cpp/absl/synchronization/internal/waiter_test.cc +12 -3
- package/vendor/abseil-cpp/absl/synchronization/internal/win32_waiter.cc +0 -4
- package/vendor/abseil-cpp/absl/synchronization/lifetime_test.cc +4 -4
- package/vendor/abseil-cpp/absl/synchronization/mutex.cc +27 -29
- package/vendor/abseil-cpp/absl/synchronization/mutex.h +205 -126
- package/vendor/abseil-cpp/absl/synchronization/mutex_benchmark.cc +13 -31
- package/vendor/abseil-cpp/absl/synchronization/mutex_test.cc +183 -169
- package/vendor/abseil-cpp/absl/synchronization/notification.cc +5 -5
- package/vendor/abseil-cpp/absl/synchronization/notification.h +1 -1
- package/vendor/abseil-cpp/absl/synchronization/notification_test.cc +3 -3
- package/vendor/abseil-cpp/absl/time/BUILD.bazel +9 -1
- package/vendor/abseil-cpp/absl/time/CMakeLists.txt +3 -1
- package/vendor/abseil-cpp/absl/time/civil_time.cc +1 -0
- package/vendor/abseil-cpp/absl/time/civil_time_test.cc +134 -0
- package/vendor/abseil-cpp/absl/time/clock.cc +11 -14
- package/vendor/abseil-cpp/absl/time/duration.cc +14 -9
- package/vendor/abseil-cpp/absl/time/duration_test.cc +6 -7
- package/vendor/abseil-cpp/absl/time/internal/cctz/BUILD.bazel +14 -3
- package/vendor/abseil-cpp/absl/time/internal/cctz/include/cctz/civil_time_detail.h +12 -0
- package/vendor/abseil-cpp/absl/time/internal/cctz/include/cctz/time_zone.h +1 -1
- package/vendor/abseil-cpp/absl/time/internal/cctz/src/cctz_benchmark.cc +4 -490
- package/vendor/abseil-cpp/absl/time/internal/cctz/src/test_time_zone_names.cc +515 -0
- package/vendor/abseil-cpp/absl/time/internal/cctz/src/test_time_zone_names.h +33 -0
- package/vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_format.cc +41 -4
- package/vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_format_test.cc +22 -23
- package/vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup.cc +90 -111
- package/vendor/abseil-cpp/absl/time/internal/cctz/src/time_zone_lookup_test.cc +1 -488
- package/vendor/abseil-cpp/absl/time/internal/cctz/testdata/version +1 -1
- package/vendor/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/America/Coyhaique +0 -0
- package/vendor/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Asia/Tehran +0 -0
- package/vendor/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/Iran +0 -0
- package/vendor/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/zone1970.tab +2 -1
- package/vendor/abseil-cpp/absl/time/internal/cctz/testdata/zoneinfo/zonenow.tab +1 -1
- package/vendor/abseil-cpp/absl/time/time.h +24 -18
- package/vendor/abseil-cpp/absl/time/time_test.cc +26 -0
- package/vendor/abseil-cpp/absl/types/BUILD.bazel +11 -164
- package/vendor/abseil-cpp/absl/types/CMakeLists.txt +23 -167
- package/vendor/abseil-cpp/absl/types/any.h +9 -484
- package/vendor/abseil-cpp/absl/types/optional.h +7 -747
- package/vendor/abseil-cpp/absl/types/span.h +46 -19
- package/vendor/abseil-cpp/absl/types/span_test.cc +27 -0
- package/vendor/abseil-cpp/absl/types/variant.h +5 -784
- package/vendor/abseil-cpp/absl/types/variant_test.cc +43 -2597
- package/vendor/abseil-cpp/absl/utility/BUILD.bazel +1 -41
- package/vendor/abseil-cpp/absl/utility/CMakeLists.txt +0 -40
- package/vendor/abseil-cpp/absl/utility/utility.h +10 -185
- package/vendor/abseil-cpp/ci/absl_alternate_options.h +2 -3
- package/vendor/abseil-cpp/ci/cmake_common.sh +2 -2
- package/vendor/abseil-cpp/ci/linux_arm_clang-latest_libcxx_bazel.sh +12 -13
- package/vendor/abseil-cpp/ci/linux_clang-latest_libcxx_asan_bazel.sh +24 -21
- package/vendor/abseil-cpp/ci/linux_clang-latest_libcxx_bazel.sh +12 -12
- package/vendor/abseil-cpp/ci/linux_clang-latest_libcxx_tsan_bazel.sh +23 -22
- package/vendor/abseil-cpp/ci/linux_clang-latest_libstdcxx_bazel.sh +20 -19
- package/vendor/abseil-cpp/ci/linux_docker_containers.sh +4 -4
- package/vendor/abseil-cpp/ci/linux_gcc-floor_libstdcxx_bazel.sh +17 -17
- package/vendor/abseil-cpp/ci/linux_gcc-latest_libstdcxx_bazel.sh +10 -10
- package/vendor/abseil-cpp/ci/linux_gcc-latest_libstdcxx_cmake.sh +1 -1
- package/vendor/abseil-cpp/ci/linux_gcc_alpine_cmake.sh +1 -1
- package/vendor/abseil-cpp/ci/macos_xcode_bazel.sh +9 -10
- package/vendor/abseil-cpp/ci/macos_xcode_cmake.sh +9 -1
- package/vendor/abseil-cpp/ci/windows_clangcl_bazel.bat +14 -6
- package/vendor/abseil-cpp/ci/windows_msvc_bazel.bat +14 -6
- package/vendor/abseil-cpp/ci/windows_msvc_cmake.bat +1 -1
- package/vendor/re2/.bazelrc +4 -4
- package/vendor/re2/.bcr/metadata.template.json +16 -0
- package/vendor/re2/.bcr/presubmit.yml +57 -0
- package/vendor/re2/.bcr/source.template.json +5 -0
- package/vendor/re2/.github/bazel.sh +1 -7
- package/vendor/re2/.github/workflows/ci-bazel.yml +5 -5
- package/vendor/re2/.github/workflows/ci-cmake.yml +4 -4
- package/vendor/re2/.github/workflows/ci.yml +5 -6
- package/vendor/re2/.github/workflows/pages.yml +3 -3
- package/vendor/re2/.github/workflows/python.yml +29 -24
- package/vendor/re2/.github/workflows/release-bazel.yml +42 -0
- package/vendor/re2/.github/workflows/release.yml +15 -4
- package/vendor/re2/BUILD.bazel +25 -0
- package/vendor/re2/CMakeLists.txt +100 -85
- package/vendor/re2/CONTRIBUTING.md +0 -1
- package/vendor/re2/MODULE.bazel +10 -10
- package/vendor/re2/Makefile +1 -1
- package/vendor/re2/README.md +259 -0
- package/vendor/re2/python/BUILD.bazel +8 -0
- package/vendor/re2/python/re2.py +1 -1
- package/vendor/re2/python/re2_test.py +6 -0
- package/vendor/re2/python/setup.py +3 -3
- package/vendor/re2/re2/bitmap256.cc +3 -4
- package/vendor/re2/re2/bitstate.cc +15 -10
- package/vendor/re2/re2/dfa.cc +1 -2
- package/vendor/re2/re2/parse.cc +3 -4
- package/vendor/re2/re2/prog.cc +1 -2
- package/vendor/re2/re2/prog.h +1 -0
- package/vendor/re2/re2/re2.cc +5 -0
- package/vendor/re2/re2/re2.h +9 -9
- package/vendor/re2/re2/set.cc +6 -0
- package/vendor/re2/re2/set.h +5 -0
- package/vendor/re2/re2/testing/re2_arg_test.cc +3 -3
- package/vendor/re2/re2/testing/re2_test.cc +8 -0
- package/vendor/re2/re2/testing/set_test.cc +5 -0
- package/vendor/re2/re2/walker-inl.h +1 -1
- package/vendor/abseil-cpp/WORKSPACE +0 -76
- package/vendor/abseil-cpp/WORKSPACE.bzlmod +0 -19
- package/vendor/abseil-cpp/absl/base/inline_variable_test.cc +0 -64
- package/vendor/abseil-cpp/absl/base/internal/inline_variable.h +0 -108
- package/vendor/abseil-cpp/absl/base/internal/inline_variable_testing.h +0 -46
- package/vendor/abseil-cpp/absl/base/internal/invoke.h +0 -241
- package/vendor/abseil-cpp/absl/base/internal/nullability_impl.h +0 -69
- package/vendor/abseil-cpp/absl/base/invoke_test.cc +0 -331
- package/vendor/abseil-cpp/absl/hash/internal/low_level_hash.cc +0 -148
- package/vendor/abseil-cpp/absl/hash/internal/low_level_hash.h +0 -54
- package/vendor/abseil-cpp/absl/random/internal/pool_urbg.h +0 -131
- package/vendor/abseil-cpp/absl/random/internal/pool_urbg_test.cc +0 -182
- package/vendor/abseil-cpp/absl/types/any_exception_safety_test.cc +0 -173
- package/vendor/abseil-cpp/absl/types/any_test.cc +0 -778
- package/vendor/abseil-cpp/absl/types/bad_any_cast.cc +0 -64
- package/vendor/abseil-cpp/absl/types/bad_any_cast.h +0 -75
- package/vendor/abseil-cpp/absl/types/bad_optional_access.cc +0 -66
- package/vendor/abseil-cpp/absl/types/bad_optional_access.h +0 -78
- package/vendor/abseil-cpp/absl/types/bad_variant_access.cc +0 -82
- package/vendor/abseil-cpp/absl/types/bad_variant_access.h +0 -82
- package/vendor/abseil-cpp/absl/types/internal/optional.h +0 -352
- package/vendor/abseil-cpp/absl/types/internal/variant.h +0 -1622
- package/vendor/abseil-cpp/absl/types/optional_exception_safety_test.cc +0 -292
- package/vendor/abseil-cpp/absl/types/optional_test.cc +0 -1615
- package/vendor/abseil-cpp/absl/types/variant_benchmark.cc +0 -222
- package/vendor/abseil-cpp/absl/types/variant_exception_safety_test.cc +0 -532
- package/vendor/abseil-cpp/absl/utility/internal/if_constexpr.h +0 -70
- package/vendor/abseil-cpp/absl/utility/internal/if_constexpr_test.cc +0 -79
- package/vendor/abseil-cpp/absl/utility/utility_test.cc +0 -239
- package/vendor/re2/.github/workflows/pr.yml +0 -34
- package/vendor/re2/README +0 -47
|
@@ -150,11 +150,11 @@
|
|
|
150
150
|
// To `insert`, we compose `unchecked_insert` with `find`. We compute `h(x)` and
|
|
151
151
|
// perform a `find` to see if it's already present; if it is, we're done. If
|
|
152
152
|
// it's not, we may decide the table is getting overcrowded (i.e. the load
|
|
153
|
-
// factor is greater than 7/8 for big tables;
|
|
154
|
-
// factor of 1); in this case, we allocate a bigger array,
|
|
155
|
-
// each element of the table into the new array (we know that
|
|
156
|
-
// will insert an already-present value), and discard the old
|
|
157
|
-
// this point, we may `unchecked_insert` the value `x`.
|
|
153
|
+
// factor is greater than 7/8 for big tables; tables smaller than one probing
|
|
154
|
+
// group use a max load factor of 1); in this case, we allocate a bigger array,
|
|
155
|
+
// `unchecked_insert` each element of the table into the new array (we know that
|
|
156
|
+
// no insertion here will insert an already-present value), and discard the old
|
|
157
|
+
// backing array. At this point, we may `unchecked_insert` the value `x`.
|
|
158
158
|
//
|
|
159
159
|
// Below, `unchecked_insert` is partly implemented by `prepare_insert`, which
|
|
160
160
|
// presents a viable, initialized slot pointee to the caller.
|
|
@@ -194,8 +194,10 @@
|
|
|
194
194
|
#include <utility>
|
|
195
195
|
|
|
196
196
|
#include "absl/base/attributes.h"
|
|
197
|
+
#include "absl/base/casts.h"
|
|
197
198
|
#include "absl/base/config.h"
|
|
198
199
|
#include "absl/base/internal/endian.h"
|
|
200
|
+
#include "absl/base/internal/iterator_traits.h"
|
|
199
201
|
#include "absl/base/internal/raw_logging.h"
|
|
200
202
|
#include "absl/base/macros.h"
|
|
201
203
|
#include "absl/base/optimization.h"
|
|
@@ -208,30 +210,17 @@
|
|
|
208
210
|
#include "absl/container/internal/container_memory.h"
|
|
209
211
|
#include "absl/container/internal/hash_function_defaults.h"
|
|
210
212
|
#include "absl/container/internal/hash_policy_traits.h"
|
|
213
|
+
#include "absl/container/internal/hashtable_control_bytes.h"
|
|
211
214
|
#include "absl/container/internal/hashtable_debug_hooks.h"
|
|
212
215
|
#include "absl/container/internal/hashtablez_sampler.h"
|
|
216
|
+
#include "absl/functional/function_ref.h"
|
|
213
217
|
#include "absl/hash/hash.h"
|
|
218
|
+
#include "absl/hash/internal/weakly_mixed_integer.h"
|
|
214
219
|
#include "absl/memory/memory.h"
|
|
215
220
|
#include "absl/meta/type_traits.h"
|
|
216
221
|
#include "absl/numeric/bits.h"
|
|
217
222
|
#include "absl/utility/utility.h"
|
|
218
223
|
|
|
219
|
-
#ifdef ABSL_INTERNAL_HAVE_SSE2
|
|
220
|
-
#include <emmintrin.h>
|
|
221
|
-
#endif
|
|
222
|
-
|
|
223
|
-
#ifdef ABSL_INTERNAL_HAVE_SSSE3
|
|
224
|
-
#include <tmmintrin.h>
|
|
225
|
-
#endif
|
|
226
|
-
|
|
227
|
-
#ifdef _MSC_VER
|
|
228
|
-
#include <intrin.h>
|
|
229
|
-
#endif
|
|
230
|
-
|
|
231
|
-
#ifdef ABSL_INTERNAL_HAVE_ARM_NEON
|
|
232
|
-
#include <arm_neon.h>
|
|
233
|
-
#endif
|
|
234
|
-
|
|
235
224
|
namespace absl {
|
|
236
225
|
ABSL_NAMESPACE_BEGIN
|
|
237
226
|
namespace container_internal {
|
|
@@ -278,6 +267,15 @@ constexpr bool SwisstableGenerationsEnabled() { return false; }
|
|
|
278
267
|
constexpr size_t NumGenerationBytes() { return 0; }
|
|
279
268
|
#endif
|
|
280
269
|
|
|
270
|
+
// Returns true if we should assert that the table is not accessed after it has
|
|
271
|
+
// been destroyed or during the destruction of the table.
|
|
272
|
+
constexpr bool SwisstableAssertAccessToDestroyedTable() {
|
|
273
|
+
#ifndef NDEBUG
|
|
274
|
+
return true;
|
|
275
|
+
#endif
|
|
276
|
+
return SwisstableGenerationsEnabled();
|
|
277
|
+
}
|
|
278
|
+
|
|
281
279
|
template <typename AllocType>
|
|
282
280
|
void SwapAlloc(AllocType& lhs, AllocType& rhs,
|
|
283
281
|
std::true_type /* propagate_on_container_swap */) {
|
|
@@ -285,10 +283,8 @@ void SwapAlloc(AllocType& lhs, AllocType& rhs,
|
|
|
285
283
|
swap(lhs, rhs);
|
|
286
284
|
}
|
|
287
285
|
template <typename AllocType>
|
|
288
|
-
void SwapAlloc(AllocType& lhs, AllocType& rhs,
|
|
286
|
+
void SwapAlloc([[maybe_unused]] AllocType& lhs, [[maybe_unused]] AllocType& rhs,
|
|
289
287
|
std::false_type /* propagate_on_container_swap */) {
|
|
290
|
-
(void)lhs;
|
|
291
|
-
(void)rhs;
|
|
292
288
|
assert(lhs == rhs &&
|
|
293
289
|
"It's UB to call swap with unequal non-propagating allocators.");
|
|
294
290
|
}
|
|
@@ -372,176 +368,7 @@ struct IsDecomposable<
|
|
|
372
368
|
std::declval<Ts>()...))>,
|
|
373
369
|
Policy, Hash, Eq, Ts...> : std::true_type {};
|
|
374
370
|
|
|
375
|
-
|
|
376
|
-
template <class T>
|
|
377
|
-
constexpr bool IsNoThrowSwappable(std::true_type = {} /* is_swappable */) {
|
|
378
|
-
using std::swap;
|
|
379
|
-
return noexcept(swap(std::declval<T&>(), std::declval<T&>()));
|
|
380
|
-
}
|
|
381
|
-
template <class T>
|
|
382
|
-
constexpr bool IsNoThrowSwappable(std::false_type /* is_swappable */) {
|
|
383
|
-
return false;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
template <typename T>
|
|
387
|
-
uint32_t TrailingZeros(T x) {
|
|
388
|
-
ABSL_ASSUME(x != 0);
|
|
389
|
-
return static_cast<uint32_t>(countr_zero(x));
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// 8 bytes bitmask with most significant bit set for every byte.
|
|
393
|
-
constexpr uint64_t kMsbs8Bytes = 0x8080808080808080ULL;
|
|
394
|
-
|
|
395
|
-
// An abstract bitmask, such as that emitted by a SIMD instruction.
|
|
396
|
-
//
|
|
397
|
-
// Specifically, this type implements a simple bitset whose representation is
|
|
398
|
-
// controlled by `SignificantBits` and `Shift`. `SignificantBits` is the number
|
|
399
|
-
// of abstract bits in the bitset, while `Shift` is the log-base-two of the
|
|
400
|
-
// width of an abstract bit in the representation.
|
|
401
|
-
// This mask provides operations for any number of real bits set in an abstract
|
|
402
|
-
// bit. To add iteration on top of that, implementation must guarantee no more
|
|
403
|
-
// than the most significant real bit is set in a set abstract bit.
|
|
404
|
-
template <class T, int SignificantBits, int Shift = 0>
|
|
405
|
-
class NonIterableBitMask {
|
|
406
|
-
public:
|
|
407
|
-
explicit NonIterableBitMask(T mask) : mask_(mask) {}
|
|
408
|
-
|
|
409
|
-
explicit operator bool() const { return this->mask_ != 0; }
|
|
410
|
-
|
|
411
|
-
// Returns the index of the lowest *abstract* bit set in `self`.
|
|
412
|
-
uint32_t LowestBitSet() const {
|
|
413
|
-
return container_internal::TrailingZeros(mask_) >> Shift;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// Returns the index of the highest *abstract* bit set in `self`.
|
|
417
|
-
uint32_t HighestBitSet() const {
|
|
418
|
-
return static_cast<uint32_t>((bit_width(mask_) - 1) >> Shift);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Returns the number of trailing zero *abstract* bits.
|
|
422
|
-
uint32_t TrailingZeros() const {
|
|
423
|
-
return container_internal::TrailingZeros(mask_) >> Shift;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
// Returns the number of leading zero *abstract* bits.
|
|
427
|
-
uint32_t LeadingZeros() const {
|
|
428
|
-
constexpr int total_significant_bits = SignificantBits << Shift;
|
|
429
|
-
constexpr int extra_bits = sizeof(T) * 8 - total_significant_bits;
|
|
430
|
-
return static_cast<uint32_t>(
|
|
431
|
-
countl_zero(static_cast<T>(mask_ << extra_bits))) >>
|
|
432
|
-
Shift;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
T mask_;
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
// Mask that can be iterable
|
|
439
|
-
//
|
|
440
|
-
// For example, when `SignificantBits` is 16 and `Shift` is zero, this is just
|
|
441
|
-
// an ordinary 16-bit bitset occupying the low 16 bits of `mask`. When
|
|
442
|
-
// `SignificantBits` is 8 and `Shift` is 3, abstract bits are represented as
|
|
443
|
-
// the bytes `0x00` and `0x80`, and it occupies all 64 bits of the bitmask.
|
|
444
|
-
// If NullifyBitsOnIteration is true (only allowed for Shift == 3),
|
|
445
|
-
// non zero abstract bit is allowed to have additional bits
|
|
446
|
-
// (e.g., `0xff`, `0x83` and `0x9c` are ok, but `0x6f` is not).
|
|
447
|
-
//
|
|
448
|
-
// For example:
|
|
449
|
-
// for (int i : BitMask<uint32_t, 16>(0b101)) -> yields 0, 2
|
|
450
|
-
// for (int i : BitMask<uint64_t, 8, 3>(0x0000000080800000)) -> yields 2, 3
|
|
451
|
-
template <class T, int SignificantBits, int Shift = 0,
|
|
452
|
-
bool NullifyBitsOnIteration = false>
|
|
453
|
-
class BitMask : public NonIterableBitMask<T, SignificantBits, Shift> {
|
|
454
|
-
using Base = NonIterableBitMask<T, SignificantBits, Shift>;
|
|
455
|
-
static_assert(std::is_unsigned<T>::value, "");
|
|
456
|
-
static_assert(Shift == 0 || Shift == 3, "");
|
|
457
|
-
static_assert(!NullifyBitsOnIteration || Shift == 3, "");
|
|
458
|
-
|
|
459
|
-
public:
|
|
460
|
-
explicit BitMask(T mask) : Base(mask) {
|
|
461
|
-
if (Shift == 3 && !NullifyBitsOnIteration) {
|
|
462
|
-
ABSL_SWISSTABLE_ASSERT(this->mask_ == (this->mask_ & kMsbs8Bytes));
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
// BitMask is an iterator over the indices of its abstract bits.
|
|
466
|
-
using value_type = int;
|
|
467
|
-
using iterator = BitMask;
|
|
468
|
-
using const_iterator = BitMask;
|
|
469
|
-
|
|
470
|
-
BitMask& operator++() {
|
|
471
|
-
if (Shift == 3 && NullifyBitsOnIteration) {
|
|
472
|
-
this->mask_ &= kMsbs8Bytes;
|
|
473
|
-
}
|
|
474
|
-
this->mask_ &= (this->mask_ - 1);
|
|
475
|
-
return *this;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
uint32_t operator*() const { return Base::LowestBitSet(); }
|
|
479
|
-
|
|
480
|
-
BitMask begin() const { return *this; }
|
|
481
|
-
BitMask end() const { return BitMask(0); }
|
|
482
|
-
|
|
483
|
-
private:
|
|
484
|
-
friend bool operator==(const BitMask& a, const BitMask& b) {
|
|
485
|
-
return a.mask_ == b.mask_;
|
|
486
|
-
}
|
|
487
|
-
friend bool operator!=(const BitMask& a, const BitMask& b) {
|
|
488
|
-
return a.mask_ != b.mask_;
|
|
489
|
-
}
|
|
490
|
-
};
|
|
491
|
-
|
|
492
|
-
using h2_t = uint8_t;
|
|
493
|
-
|
|
494
|
-
// The values here are selected for maximum performance. See the static asserts
|
|
495
|
-
// below for details.
|
|
496
|
-
|
|
497
|
-
// A `ctrl_t` is a single control byte, which can have one of four
|
|
498
|
-
// states: empty, deleted, full (which has an associated seven-bit h2_t value)
|
|
499
|
-
// and the sentinel. They have the following bit patterns:
|
|
500
|
-
//
|
|
501
|
-
// empty: 1 0 0 0 0 0 0 0
|
|
502
|
-
// deleted: 1 1 1 1 1 1 1 0
|
|
503
|
-
// full: 0 h h h h h h h // h represents the hash bits.
|
|
504
|
-
// sentinel: 1 1 1 1 1 1 1 1
|
|
505
|
-
//
|
|
506
|
-
// These values are specifically tuned for SSE-flavored SIMD.
|
|
507
|
-
// The static_asserts below detail the source of these choices.
|
|
508
|
-
//
|
|
509
|
-
// We use an enum class so that when strict aliasing is enabled, the compiler
|
|
510
|
-
// knows ctrl_t doesn't alias other types.
|
|
511
|
-
enum class ctrl_t : int8_t {
|
|
512
|
-
kEmpty = -128, // 0b10000000
|
|
513
|
-
kDeleted = -2, // 0b11111110
|
|
514
|
-
kSentinel = -1, // 0b11111111
|
|
515
|
-
};
|
|
516
|
-
static_assert(
|
|
517
|
-
(static_cast<int8_t>(ctrl_t::kEmpty) &
|
|
518
|
-
static_cast<int8_t>(ctrl_t::kDeleted) &
|
|
519
|
-
static_cast<int8_t>(ctrl_t::kSentinel) & 0x80) != 0,
|
|
520
|
-
"Special markers need to have the MSB to make checking for them efficient");
|
|
521
|
-
static_assert(
|
|
522
|
-
ctrl_t::kEmpty < ctrl_t::kSentinel && ctrl_t::kDeleted < ctrl_t::kSentinel,
|
|
523
|
-
"ctrl_t::kEmpty and ctrl_t::kDeleted must be smaller than "
|
|
524
|
-
"ctrl_t::kSentinel to make the SIMD test of IsEmptyOrDeleted() efficient");
|
|
525
|
-
static_assert(
|
|
526
|
-
ctrl_t::kSentinel == static_cast<ctrl_t>(-1),
|
|
527
|
-
"ctrl_t::kSentinel must be -1 to elide loading it from memory into SIMD "
|
|
528
|
-
"registers (pcmpeqd xmm, xmm)");
|
|
529
|
-
static_assert(ctrl_t::kEmpty == static_cast<ctrl_t>(-128),
|
|
530
|
-
"ctrl_t::kEmpty must be -128 to make the SIMD check for its "
|
|
531
|
-
"existence efficient (psignb xmm, xmm)");
|
|
532
|
-
static_assert(
|
|
533
|
-
(~static_cast<int8_t>(ctrl_t::kEmpty) &
|
|
534
|
-
~static_cast<int8_t>(ctrl_t::kDeleted) &
|
|
535
|
-
static_cast<int8_t>(ctrl_t::kSentinel) & 0x7F) != 0,
|
|
536
|
-
"ctrl_t::kEmpty and ctrl_t::kDeleted must share an unset bit that is not "
|
|
537
|
-
"shared by ctrl_t::kSentinel to make the scalar test for "
|
|
538
|
-
"MaskEmptyOrDeleted() efficient");
|
|
539
|
-
static_assert(ctrl_t::kDeleted == static_cast<ctrl_t>(-2),
|
|
540
|
-
"ctrl_t::kDeleted must be -2 to make the implementation of "
|
|
541
|
-
"ConvertSpecialToEmptyAndFullToDeleted efficient");
|
|
542
|
-
|
|
543
|
-
// See definition comment for why this is size 32.
|
|
544
|
-
ABSL_DLL extern const ctrl_t kEmptyGroup[32];
|
|
371
|
+
ABSL_DLL extern ctrl_t kDefaultIterControl;
|
|
545
372
|
|
|
546
373
|
// We use these sentinel capacity values in debug mode to indicate different
|
|
547
374
|
// classes of bugs.
|
|
@@ -555,17 +382,14 @@ enum InvalidCapacity : size_t {
|
|
|
555
382
|
kSelfMovedFrom,
|
|
556
383
|
};
|
|
557
384
|
|
|
558
|
-
// Returns a pointer to a control byte
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
// to it because it is only used for empty tables.
|
|
562
|
-
return const_cast<ctrl_t*>(kEmptyGroup + 16);
|
|
563
|
-
}
|
|
385
|
+
// Returns a pointer to a control byte that can be used by default-constructed
|
|
386
|
+
// iterators. We don't expect this pointer to be dereferenced.
|
|
387
|
+
inline ctrl_t* DefaultIterControl() { return &kDefaultIterControl; }
|
|
564
388
|
|
|
565
389
|
// For use in SOO iterators.
|
|
566
390
|
// TODO(b/289225379): we could potentially get rid of this by adding an is_soo
|
|
567
391
|
// bit in iterators. This would add branches but reduce cache misses.
|
|
568
|
-
ABSL_DLL extern const ctrl_t kSooControl[
|
|
392
|
+
ABSL_DLL extern const ctrl_t kSooControl[2];
|
|
569
393
|
|
|
570
394
|
// Returns a pointer to a full byte followed by a sentinel byte.
|
|
571
395
|
inline ctrl_t* SooControl() {
|
|
@@ -585,360 +409,135 @@ inline bool IsEmptyGeneration(const GenerationType* generation) {
|
|
|
585
409
|
return *generation == SentinelEmptyGeneration();
|
|
586
410
|
}
|
|
587
411
|
|
|
588
|
-
//
|
|
589
|
-
//
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
//
|
|
605
|
-
|
|
606
|
-
// the group.
|
|
607
|
-
// TODO(kfm,sbenza): revisit after we do unconditional mixing
|
|
608
|
-
template <class Mask>
|
|
609
|
-
ABSL_ATTRIBUTE_ALWAYS_INLINE inline auto GetInsertionOffset(
|
|
610
|
-
Mask mask, ABSL_ATTRIBUTE_UNUSED size_t capacity,
|
|
611
|
-
ABSL_ATTRIBUTE_UNUSED size_t hash,
|
|
612
|
-
ABSL_ATTRIBUTE_UNUSED const ctrl_t* ctrl) {
|
|
613
|
-
#if defined(NDEBUG)
|
|
614
|
-
return mask.LowestBitSet();
|
|
615
|
-
#else
|
|
616
|
-
return ShouldInsertBackwardsForDebug(capacity, hash, ctrl)
|
|
617
|
-
? mask.HighestBitSet()
|
|
618
|
-
: mask.LowestBitSet();
|
|
619
|
-
#endif
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
// Returns a per-table, hash salt, which changes on resize. This gets mixed into
|
|
623
|
-
// H1 to randomize iteration order per-table.
|
|
624
|
-
//
|
|
625
|
-
// The seed consists of the ctrl_ pointer, which adds enough entropy to ensure
|
|
626
|
-
// non-determinism of iteration order in most cases.
|
|
627
|
-
inline size_t PerTableSalt(const ctrl_t* ctrl) {
|
|
628
|
-
// The low bits of the pointer have little or no entropy because of
|
|
629
|
-
// alignment. We shift the pointer to try to use higher entropy bits. A
|
|
630
|
-
// good number seems to be 12 bits, because that aligns with page size.
|
|
631
|
-
return reinterpret_cast<uintptr_t>(ctrl) >> 12;
|
|
632
|
-
}
|
|
633
|
-
// Extracts the H1 portion of a hash: 57 bits mixed with a per-table salt.
|
|
634
|
-
inline size_t H1(size_t hash, const ctrl_t* ctrl) {
|
|
635
|
-
return (hash >> 7) ^ PerTableSalt(ctrl);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
// Extracts the H2 portion of a hash: the 7 bits not used for H1.
|
|
639
|
-
//
|
|
640
|
-
// These are used as an occupied control byte.
|
|
641
|
-
inline h2_t H2(size_t hash) { return hash & 0x7F; }
|
|
642
|
-
|
|
643
|
-
// Helpers for checking the state of a control byte.
|
|
644
|
-
inline bool IsEmpty(ctrl_t c) { return c == ctrl_t::kEmpty; }
|
|
645
|
-
inline bool IsFull(ctrl_t c) {
|
|
646
|
-
// Cast `c` to the underlying type instead of casting `0` to `ctrl_t` as `0`
|
|
647
|
-
// is not a value in the enum. Both ways are equivalent, but this way makes
|
|
648
|
-
// linters happier.
|
|
649
|
-
return static_cast<std::underlying_type_t<ctrl_t>>(c) >= 0;
|
|
650
|
-
}
|
|
651
|
-
inline bool IsDeleted(ctrl_t c) { return c == ctrl_t::kDeleted; }
|
|
652
|
-
inline bool IsEmptyOrDeleted(ctrl_t c) { return c < ctrl_t::kSentinel; }
|
|
653
|
-
|
|
654
|
-
#ifdef ABSL_INTERNAL_HAVE_SSE2
|
|
655
|
-
// Quick reference guide for intrinsics used below:
|
|
656
|
-
//
|
|
657
|
-
// * __m128i: An XMM (128-bit) word.
|
|
658
|
-
//
|
|
659
|
-
// * _mm_setzero_si128: Returns a zero vector.
|
|
660
|
-
// * _mm_set1_epi8: Returns a vector with the same i8 in each lane.
|
|
661
|
-
//
|
|
662
|
-
// * _mm_subs_epi8: Saturating-subtracts two i8 vectors.
|
|
663
|
-
// * _mm_and_si128: Ands two i128s together.
|
|
664
|
-
// * _mm_or_si128: Ors two i128s together.
|
|
665
|
-
// * _mm_andnot_si128: And-nots two i128s together.
|
|
666
|
-
//
|
|
667
|
-
// * _mm_cmpeq_epi8: Component-wise compares two i8 vectors for equality,
|
|
668
|
-
// filling each lane with 0x00 or 0xff.
|
|
669
|
-
// * _mm_cmpgt_epi8: Same as above, but using > rather than ==.
|
|
670
|
-
//
|
|
671
|
-
// * _mm_loadu_si128: Performs an unaligned load of an i128.
|
|
672
|
-
// * _mm_storeu_si128: Performs an unaligned store of an i128.
|
|
673
|
-
//
|
|
674
|
-
// * _mm_sign_epi8: Retains, negates, or zeroes each i8 lane of the first
|
|
675
|
-
// argument if the corresponding lane of the second
|
|
676
|
-
// argument is positive, negative, or zero, respectively.
|
|
677
|
-
// * _mm_movemask_epi8: Selects the sign bit out of each i8 lane and produces a
|
|
678
|
-
// bitmask consisting of those bits.
|
|
679
|
-
// * _mm_shuffle_epi8: Selects i8s from the first argument, using the low
|
|
680
|
-
// four bits of each i8 lane in the second argument as
|
|
681
|
-
// indices.
|
|
682
|
-
|
|
683
|
-
// https://github.com/abseil/abseil-cpp/issues/209
|
|
684
|
-
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87853
|
|
685
|
-
// _mm_cmpgt_epi8 is broken under GCC with -funsigned-char
|
|
686
|
-
// Work around this by using the portable implementation of Group
|
|
687
|
-
// when using -funsigned-char under GCC.
|
|
688
|
-
inline __m128i _mm_cmpgt_epi8_fixed(__m128i a, __m128i b) {
|
|
689
|
-
#if defined(__GNUC__) && !defined(__clang__)
|
|
690
|
-
if (std::is_unsigned<char>::value) {
|
|
691
|
-
const __m128i mask = _mm_set1_epi8(0x80);
|
|
692
|
-
const __m128i diff = _mm_subs_epi8(b, a);
|
|
693
|
-
return _mm_cmpeq_epi8(_mm_and_si128(diff, mask), mask);
|
|
694
|
-
}
|
|
695
|
-
#endif
|
|
696
|
-
return _mm_cmpgt_epi8(a, b);
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
struct GroupSse2Impl {
|
|
700
|
-
static constexpr size_t kWidth = 16; // the number of slots per group
|
|
701
|
-
|
|
702
|
-
explicit GroupSse2Impl(const ctrl_t* pos) {
|
|
703
|
-
ctrl = _mm_loadu_si128(reinterpret_cast<const __m128i*>(pos));
|
|
704
|
-
}
|
|
412
|
+
// We only allow a maximum of 1 SOO element, which makes the implementation
|
|
413
|
+
// much simpler. Complications with multiple SOO elements include:
|
|
414
|
+
// - Satisfying the guarantee that erasing one element doesn't invalidate
|
|
415
|
+
// iterators to other elements means we would probably need actual SOO
|
|
416
|
+
// control bytes.
|
|
417
|
+
// - In order to prevent user code from depending on iteration order for small
|
|
418
|
+
// tables, we would need to randomize the iteration order somehow.
|
|
419
|
+
constexpr size_t SooCapacity() { return 1; }
|
|
420
|
+
// Sentinel type to indicate SOO CommonFields construction.
|
|
421
|
+
struct soo_tag_t {};
|
|
422
|
+
// Sentinel type to indicate SOO CommonFields construction with full size.
|
|
423
|
+
struct full_soo_tag_t {};
|
|
424
|
+
// Sentinel type to indicate non-SOO CommonFields construction.
|
|
425
|
+
struct non_soo_tag_t {};
|
|
426
|
+
// Sentinel value to indicate an uninitialized value explicitly.
|
|
427
|
+
struct uninitialized_tag_t {};
|
|
428
|
+
// Sentinel value to indicate creation of an empty table without a seed.
|
|
429
|
+
struct no_seed_empty_tag_t {};
|
|
705
430
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
431
|
+
// Per table hash salt. This gets mixed into H1 to randomize iteration order
|
|
432
|
+
// per-table.
|
|
433
|
+
// The seed is needed to ensure non-determinism of iteration order.
|
|
434
|
+
class PerTableSeed {
|
|
435
|
+
public:
|
|
436
|
+
// The number of bits in the seed.
|
|
437
|
+
// It is big enough to ensure non-determinism of iteration order.
|
|
438
|
+
// We store the seed inside a uint64_t together with size and other metadata.
|
|
439
|
+
// Using 16 bits allows us to save one `and` instruction in H1 (we use
|
|
440
|
+
// sign-extended move instead of mov+and).
|
|
441
|
+
static constexpr size_t kBitCount = 16;
|
|
442
|
+
static constexpr size_t kSignBit = uint64_t{1} << (kBitCount - 1);
|
|
443
|
+
|
|
444
|
+
// Returns the seed for the table.
|
|
445
|
+
size_t seed() const {
|
|
446
|
+
// We use a sign-extended load to ensure high bits are non-zero.
|
|
447
|
+
int16_t seed_signed = absl::bit_cast<int16_t>(seed_);
|
|
448
|
+
auto seed_sign_extended =
|
|
449
|
+
static_cast<std::make_signed_t<size_t>>(seed_signed);
|
|
450
|
+
return absl::bit_cast<size_t>(seed_sign_extended);
|
|
711
451
|
}
|
|
712
452
|
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
return NonIterableBitMask<uint16_t, kWidth>(
|
|
718
|
-
static_cast<uint16_t>(_mm_movemask_epi8(_mm_sign_epi8(ctrl, ctrl))));
|
|
719
|
-
#else
|
|
720
|
-
auto match = _mm_set1_epi8(static_cast<char>(ctrl_t::kEmpty));
|
|
721
|
-
return NonIterableBitMask<uint16_t, kWidth>(
|
|
722
|
-
static_cast<uint16_t>(_mm_movemask_epi8(_mm_cmpeq_epi8(match, ctrl))));
|
|
723
|
-
#endif
|
|
453
|
+
private:
|
|
454
|
+
friend class HashtableSize;
|
|
455
|
+
explicit PerTableSeed(uint16_t seed) : seed_(seed) {
|
|
456
|
+
ABSL_SWISSTABLE_ASSERT((seed & kSignBit) != 0 || seed == 0);
|
|
724
457
|
}
|
|
725
458
|
|
|
726
|
-
//
|
|
727
|
-
//
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
return BitMask<uint16_t, kWidth>(
|
|
731
|
-
static_cast<uint16_t>(_mm_movemask_epi8(ctrl) ^ 0xffff));
|
|
732
|
-
}
|
|
459
|
+
// The most significant bit of the seed is always 1 when there is a non-zero
|
|
460
|
+
// seed. This way, when sign-extended the seed has non-zero high bits.
|
|
461
|
+
const uint16_t seed_;
|
|
462
|
+
};
|
|
733
463
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
464
|
+
// Returns next per-table seed.
|
|
465
|
+
inline uint16_t NextSeed() {
|
|
466
|
+
static_assert(PerTableSeed::kBitCount == 16);
|
|
467
|
+
thread_local uint16_t seed =
|
|
468
|
+
static_cast<uint16_t>(reinterpret_cast<uintptr_t>(&seed));
|
|
469
|
+
seed += uint16_t{0xad53};
|
|
470
|
+
return seed;
|
|
471
|
+
}
|
|
741
472
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
473
|
+
// The size and also has additionally
|
|
474
|
+
// 1) one bit that stores whether we have infoz.
|
|
475
|
+
// 2) PerTableSeed::kBitCount bits for the seed.
|
|
476
|
+
class HashtableSize {
|
|
477
|
+
public:
|
|
478
|
+
static constexpr size_t kSizeBitCount = 64 - PerTableSeed::kBitCount - 1;
|
|
748
479
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
return TrailingZeros(static_cast<uint32_t>(
|
|
753
|
-
_mm_movemask_epi8(_mm_cmpgt_epi8_fixed(special, ctrl)) + 1));
|
|
754
|
-
}
|
|
480
|
+
explicit HashtableSize(uninitialized_tag_t) {}
|
|
481
|
+
explicit HashtableSize(no_seed_empty_tag_t) : data_(0) {}
|
|
482
|
+
explicit HashtableSize(full_soo_tag_t) : data_(kSizeOneNoMetadata) {}
|
|
755
483
|
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
#else
|
|
762
|
-
auto zero = _mm_setzero_si128();
|
|
763
|
-
auto special_mask = _mm_cmpgt_epi8_fixed(zero, ctrl);
|
|
764
|
-
auto res = _mm_or_si128(msbs, _mm_andnot_si128(special_mask, x126));
|
|
765
|
-
#endif
|
|
766
|
-
_mm_storeu_si128(reinterpret_cast<__m128i*>(dst), res);
|
|
484
|
+
// Returns actual size of the table.
|
|
485
|
+
size_t size() const { return static_cast<size_t>(data_ >> kSizeShift); }
|
|
486
|
+
void increment_size() { data_ += kSizeOneNoMetadata; }
|
|
487
|
+
void increment_size(size_t size) {
|
|
488
|
+
data_ += static_cast<uint64_t>(size) * kSizeOneNoMetadata;
|
|
767
489
|
}
|
|
490
|
+
void decrement_size() { data_ -= kSizeOneNoMetadata; }
|
|
491
|
+
// Returns true if the table is empty.
|
|
492
|
+
bool empty() const { return data_ < kSizeOneNoMetadata; }
|
|
493
|
+
// Sets the size to zero, but keeps all the metadata bits.
|
|
494
|
+
void set_size_to_zero_keep_metadata() { data_ = data_ & kMetadataMask; }
|
|
768
495
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
#endif // ABSL_INTERNAL_RAW_HASH_SET_HAVE_SSE2
|
|
772
|
-
|
|
773
|
-
#if defined(ABSL_INTERNAL_HAVE_ARM_NEON) && defined(ABSL_IS_LITTLE_ENDIAN)
|
|
774
|
-
struct GroupAArch64Impl {
|
|
775
|
-
static constexpr size_t kWidth = 8;
|
|
776
|
-
|
|
777
|
-
explicit GroupAArch64Impl(const ctrl_t* pos) {
|
|
778
|
-
ctrl = vld1_u8(reinterpret_cast<const uint8_t*>(pos));
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
auto Match(h2_t hash) const {
|
|
782
|
-
uint8x8_t dup = vdup_n_u8(hash);
|
|
783
|
-
auto mask = vceq_u8(ctrl, dup);
|
|
784
|
-
return BitMask<uint64_t, kWidth, /*Shift=*/3,
|
|
785
|
-
/*NullifyBitsOnIteration=*/true>(
|
|
786
|
-
vget_lane_u64(vreinterpret_u64_u8(mask), 0));
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
NonIterableBitMask<uint64_t, kWidth, 3> MaskEmpty() const {
|
|
790
|
-
uint64_t mask =
|
|
791
|
-
vget_lane_u64(vreinterpret_u64_u8(vceq_s8(
|
|
792
|
-
vdup_n_s8(static_cast<int8_t>(ctrl_t::kEmpty)),
|
|
793
|
-
vreinterpret_s8_u8(ctrl))),
|
|
794
|
-
0);
|
|
795
|
-
return NonIterableBitMask<uint64_t, kWidth, 3>(mask);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
// Returns a bitmask representing the positions of full slots.
|
|
799
|
-
// Note: for `is_small()` tables group may contain the "same" slot twice:
|
|
800
|
-
// original and mirrored.
|
|
801
|
-
auto MaskFull() const {
|
|
802
|
-
uint64_t mask = vget_lane_u64(
|
|
803
|
-
vreinterpret_u64_u8(vcge_s8(vreinterpret_s8_u8(ctrl),
|
|
804
|
-
vdup_n_s8(static_cast<int8_t>(0)))),
|
|
805
|
-
0);
|
|
806
|
-
return BitMask<uint64_t, kWidth, /*Shift=*/3,
|
|
807
|
-
/*NullifyBitsOnIteration=*/true>(mask);
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
// Returns a bitmask representing the positions of non full slots.
|
|
811
|
-
// Note: this includes: kEmpty, kDeleted, kSentinel.
|
|
812
|
-
// It is useful in contexts when kSentinel is not present.
|
|
813
|
-
auto MaskNonFull() const {
|
|
814
|
-
uint64_t mask = vget_lane_u64(
|
|
815
|
-
vreinterpret_u64_u8(vclt_s8(vreinterpret_s8_u8(ctrl),
|
|
816
|
-
vdup_n_s8(static_cast<int8_t>(0)))),
|
|
817
|
-
0);
|
|
818
|
-
return BitMask<uint64_t, kWidth, /*Shift=*/3,
|
|
819
|
-
/*NullifyBitsOnIteration=*/true>(mask);
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
NonIterableBitMask<uint64_t, kWidth, 3> MaskEmptyOrDeleted() const {
|
|
823
|
-
uint64_t mask =
|
|
824
|
-
vget_lane_u64(vreinterpret_u64_u8(vcgt_s8(
|
|
825
|
-
vdup_n_s8(static_cast<int8_t>(ctrl_t::kSentinel)),
|
|
826
|
-
vreinterpret_s8_u8(ctrl))),
|
|
827
|
-
0);
|
|
828
|
-
return NonIterableBitMask<uint64_t, kWidth, 3>(mask);
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
uint32_t CountLeadingEmptyOrDeleted() const {
|
|
832
|
-
uint64_t mask =
|
|
833
|
-
vget_lane_u64(vreinterpret_u64_u8(vcle_s8(
|
|
834
|
-
vdup_n_s8(static_cast<int8_t>(ctrl_t::kSentinel)),
|
|
835
|
-
vreinterpret_s8_u8(ctrl))),
|
|
836
|
-
0);
|
|
837
|
-
// Similar to MaskEmptyorDeleted() but we invert the logic to invert the
|
|
838
|
-
// produced bitfield. We then count number of trailing zeros.
|
|
839
|
-
// Clang and GCC optimize countr_zero to rbit+clz without any check for 0,
|
|
840
|
-
// so we should be fine.
|
|
841
|
-
return static_cast<uint32_t>(countr_zero(mask)) >> 3;
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
void ConvertSpecialToEmptyAndFullToDeleted(ctrl_t* dst) const {
|
|
845
|
-
uint64_t mask = vget_lane_u64(vreinterpret_u64_u8(ctrl), 0);
|
|
846
|
-
constexpr uint64_t slsbs = 0x0202020202020202ULL;
|
|
847
|
-
constexpr uint64_t midbs = 0x7e7e7e7e7e7e7e7eULL;
|
|
848
|
-
auto x = slsbs & (mask >> 6);
|
|
849
|
-
auto res = (x + midbs) | kMsbs8Bytes;
|
|
850
|
-
little_endian::Store64(dst, res);
|
|
851
|
-
}
|
|
852
|
-
|
|
853
|
-
uint8x8_t ctrl;
|
|
854
|
-
};
|
|
855
|
-
#endif // ABSL_INTERNAL_HAVE_ARM_NEON && ABSL_IS_LITTLE_ENDIAN
|
|
856
|
-
|
|
857
|
-
struct GroupPortableImpl {
|
|
858
|
-
static constexpr size_t kWidth = 8;
|
|
859
|
-
|
|
860
|
-
explicit GroupPortableImpl(const ctrl_t* pos)
|
|
861
|
-
: ctrl(little_endian::Load64(pos)) {}
|
|
862
|
-
|
|
863
|
-
BitMask<uint64_t, kWidth, 3> Match(h2_t hash) const {
|
|
864
|
-
// For the technique, see:
|
|
865
|
-
// http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord
|
|
866
|
-
// (Determine if a word has a byte equal to n).
|
|
867
|
-
//
|
|
868
|
-
// Caveat: there are false positives but:
|
|
869
|
-
// - they only occur if there is a real match
|
|
870
|
-
// - they never occur on ctrl_t::kEmpty, ctrl_t::kDeleted, ctrl_t::kSentinel
|
|
871
|
-
// - they will be handled gracefully by subsequent checks in code
|
|
872
|
-
//
|
|
873
|
-
// Example:
|
|
874
|
-
// v = 0x1716151413121110
|
|
875
|
-
// hash = 0x12
|
|
876
|
-
// retval = (v - lsbs) & ~v & msbs = 0x0000000080800000
|
|
877
|
-
constexpr uint64_t lsbs = 0x0101010101010101ULL;
|
|
878
|
-
auto x = ctrl ^ (lsbs * hash);
|
|
879
|
-
return BitMask<uint64_t, kWidth, 3>((x - lsbs) & ~x & kMsbs8Bytes);
|
|
496
|
+
PerTableSeed seed() const {
|
|
497
|
+
return PerTableSeed(static_cast<size_t>(data_) & kSeedMask);
|
|
880
498
|
}
|
|
881
499
|
|
|
882
|
-
|
|
883
|
-
return NonIterableBitMask<uint64_t, kWidth, 3>((ctrl & ~(ctrl << 6)) &
|
|
884
|
-
kMsbs8Bytes);
|
|
885
|
-
}
|
|
500
|
+
void generate_new_seed() { set_seed(NextSeed()); }
|
|
886
501
|
|
|
887
|
-
//
|
|
888
|
-
//
|
|
889
|
-
|
|
890
|
-
BitMask<uint64_t, kWidth, 3> MaskFull() const {
|
|
891
|
-
return BitMask<uint64_t, kWidth, 3>((ctrl ^ kMsbs8Bytes) & kMsbs8Bytes);
|
|
892
|
-
}
|
|
502
|
+
// We need to use a constant seed when the table is sampled so that sampled
|
|
503
|
+
// hashes use the same seed and can e.g. identify stuck bits accurately.
|
|
504
|
+
void set_sampled_seed() { set_seed(PerTableSeed::kSignBit); }
|
|
893
505
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
// It is useful in contexts when kSentinel is not present.
|
|
897
|
-
auto MaskNonFull() const {
|
|
898
|
-
return BitMask<uint64_t, kWidth, 3>(ctrl & kMsbs8Bytes);
|
|
506
|
+
bool is_sampled_seed() const {
|
|
507
|
+
return (data_ & kSeedMask) == PerTableSeed::kSignBit;
|
|
899
508
|
}
|
|
900
509
|
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
510
|
+
// Returns true if the table has infoz.
|
|
511
|
+
bool has_infoz() const {
|
|
512
|
+
return ABSL_PREDICT_FALSE((data_ & kHasInfozMask) != 0);
|
|
904
513
|
}
|
|
905
514
|
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
// kDeleted. We lower all other bits and count number of trailing zeros.
|
|
909
|
-
constexpr uint64_t bits = 0x0101010101010101ULL;
|
|
910
|
-
return static_cast<uint32_t>(countr_zero((ctrl | ~(ctrl >> 7)) & bits) >>
|
|
911
|
-
3);
|
|
912
|
-
}
|
|
515
|
+
// Sets the has_infoz bit.
|
|
516
|
+
void set_has_infoz() { data_ |= kHasInfozMask; }
|
|
913
517
|
|
|
914
|
-
void
|
|
915
|
-
constexpr uint64_t lsbs = 0x0101010101010101ULL;
|
|
916
|
-
auto x = ctrl & kMsbs8Bytes;
|
|
917
|
-
auto res = (~x + (x >> 7)) & ~lsbs;
|
|
918
|
-
little_endian::Store64(dst, res);
|
|
919
|
-
}
|
|
518
|
+
void set_no_seed_for_testing() { data_ &= ~kSeedMask; }
|
|
920
519
|
|
|
921
|
-
|
|
520
|
+
private:
|
|
521
|
+
void set_seed(uint16_t seed) {
|
|
522
|
+
data_ = (data_ & ~kSeedMask) | (seed | PerTableSeed::kSignBit);
|
|
523
|
+
}
|
|
524
|
+
static constexpr size_t kSizeShift = 64 - kSizeBitCount;
|
|
525
|
+
static constexpr uint64_t kSizeOneNoMetadata = uint64_t{1} << kSizeShift;
|
|
526
|
+
static constexpr uint64_t kMetadataMask = kSizeOneNoMetadata - 1;
|
|
527
|
+
static constexpr uint64_t kSeedMask =
|
|
528
|
+
(uint64_t{1} << PerTableSeed::kBitCount) - 1;
|
|
529
|
+
// The next bit after the seed.
|
|
530
|
+
static constexpr uint64_t kHasInfozMask = kSeedMask + 1;
|
|
531
|
+
uint64_t data_;
|
|
922
532
|
};
|
|
923
533
|
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
//
|
|
930
|
-
|
|
931
|
-
// between data GPRs and Neon registers when it does not provide a benefit.
|
|
932
|
-
// Using Neon is profitable when we call Match(), but is not when we don't,
|
|
933
|
-
// which is the case when we do *EmptyOrDeleted and MaskFull operations.
|
|
934
|
-
// It is difficult to make a similar approach beneficial on other architectures
|
|
935
|
-
// such as x86 since they have much lower GPR <-> vector register transfer
|
|
936
|
-
// latency and 16-wide Groups.
|
|
937
|
-
using GroupFullEmptyOrDeleted = GroupPortableImpl;
|
|
938
|
-
#else
|
|
939
|
-
using Group = GroupPortableImpl;
|
|
940
|
-
using GroupFullEmptyOrDeleted = GroupPortableImpl;
|
|
941
|
-
#endif
|
|
534
|
+
// H1 is just the low bits of the hash.
|
|
535
|
+
inline size_t H1(size_t hash) { return hash; }
|
|
536
|
+
|
|
537
|
+
// Extracts the H2 portion of a hash: the 7 most significant bits.
|
|
538
|
+
//
|
|
539
|
+
// These are used as an occupied control byte.
|
|
540
|
+
inline h2_t H2(size_t hash) { return hash >> (sizeof(size_t) * 8 - 7); }
|
|
942
541
|
|
|
943
542
|
// When there is an insertion with no reserved growth, we rehash with
|
|
944
543
|
// probability `min(1, RehashProbabilityConstant() / capacity())`. Using a
|
|
@@ -974,11 +573,9 @@ class CommonFieldsGenerationInfoEnabled {
|
|
|
974
573
|
// references. We rehash on the first insertion after reserved_growth_ reaches
|
|
975
574
|
// 0 after a call to reserve. We also do a rehash with low probability
|
|
976
575
|
// whenever reserved_growth_ is zero.
|
|
977
|
-
bool should_rehash_for_bug_detection_on_insert(
|
|
978
|
-
size_t capacity) const;
|
|
576
|
+
bool should_rehash_for_bug_detection_on_insert(size_t capacity) const;
|
|
979
577
|
// Similar to above, except that we don't depend on reserved_growth_.
|
|
980
|
-
bool should_rehash_for_bug_detection_on_move(
|
|
981
|
-
size_t capacity) const;
|
|
578
|
+
bool should_rehash_for_bug_detection_on_move(size_t capacity) const;
|
|
982
579
|
void maybe_increment_generation_on_insert() {
|
|
983
580
|
if (reserved_growth_ == kReservedGrowthJustRanOut) reserved_growth_ = 0;
|
|
984
581
|
|
|
@@ -1031,12 +628,8 @@ class CommonFieldsGenerationInfoDisabled {
|
|
|
1031
628
|
CommonFieldsGenerationInfoDisabled& operator=(
|
|
1032
629
|
CommonFieldsGenerationInfoDisabled&&) = default;
|
|
1033
630
|
|
|
1034
|
-
bool should_rehash_for_bug_detection_on_insert(
|
|
1035
|
-
|
|
1036
|
-
}
|
|
1037
|
-
bool should_rehash_for_bug_detection_on_move(const ctrl_t*, size_t) const {
|
|
1038
|
-
return false;
|
|
1039
|
-
}
|
|
631
|
+
bool should_rehash_for_bug_detection_on_insert(size_t) const { return false; }
|
|
632
|
+
bool should_rehash_for_bug_detection_on_move(size_t) const { return false; }
|
|
1040
633
|
void maybe_increment_generation_on_insert() {}
|
|
1041
634
|
void increment_generation() {}
|
|
1042
635
|
void reset_reserved_growth(size_t, size_t) {}
|
|
@@ -1127,9 +720,9 @@ class GrowthInfo {
|
|
|
1127
720
|
}
|
|
1128
721
|
|
|
1129
722
|
// Overwrites several empty slots with full slots.
|
|
1130
|
-
void OverwriteManyEmptyAsFull(size_t
|
|
1131
|
-
ABSL_SWISSTABLE_ASSERT(GetGrowthLeft() >=
|
|
1132
|
-
growth_left_info_ -=
|
|
723
|
+
void OverwriteManyEmptyAsFull(size_t count) {
|
|
724
|
+
ABSL_SWISSTABLE_ASSERT(GetGrowthLeft() >= count);
|
|
725
|
+
growth_left_info_ -= count;
|
|
1133
726
|
}
|
|
1134
727
|
|
|
1135
728
|
// Overwrites specified control element with full slot.
|
|
@@ -1154,7 +747,14 @@ class GrowthInfo {
|
|
|
1154
747
|
// 2. There is no growth left.
|
|
1155
748
|
bool HasNoGrowthLeftAndNoDeleted() const { return growth_left_info_ == 0; }
|
|
1156
749
|
|
|
1157
|
-
// Returns true if
|
|
750
|
+
// Returns true if GetGrowthLeft() == 0, but must be called only if
|
|
751
|
+
// HasNoDeleted() is false. It is slightly more efficient.
|
|
752
|
+
bool HasNoGrowthLeftAssumingMayHaveDeleted() const {
|
|
753
|
+
ABSL_SWISSTABLE_ASSERT(!HasNoDeleted());
|
|
754
|
+
return growth_left_info_ == kDeletedBit;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Returns true if table guaranteed to have no kDeleted slots.
|
|
1158
758
|
bool HasNoDeleted() const {
|
|
1159
759
|
return static_cast<std::make_signed_t<size_t>>(growth_left_info_) >= 0;
|
|
1160
760
|
}
|
|
@@ -1175,7 +775,10 @@ static_assert(alignof(GrowthInfo) == alignof(size_t), "");
|
|
|
1175
775
|
// Returns whether `n` is a valid capacity (i.e., number of slots).
|
|
1176
776
|
//
|
|
1177
777
|
// A valid capacity is a non-zero integer `2^m - 1`.
|
|
1178
|
-
|
|
778
|
+
constexpr bool IsValidCapacity(size_t n) { return ((n + 1) & n) == 0 && n > 0; }
|
|
779
|
+
|
|
780
|
+
// Whether a table is small enough that we don't need to hash any keys.
|
|
781
|
+
constexpr bool IsSmallCapacity(size_t capacity) { return capacity <= 1; }
|
|
1179
782
|
|
|
1180
783
|
// Returns the number of "cloned control bytes".
|
|
1181
784
|
//
|
|
@@ -1186,31 +789,40 @@ constexpr size_t NumClonedBytes() { return Group::kWidth - 1; }
|
|
|
1186
789
|
|
|
1187
790
|
// Returns the number of control bytes including cloned.
|
|
1188
791
|
constexpr size_t NumControlBytes(size_t capacity) {
|
|
1189
|
-
return capacity + 1 + NumClonedBytes();
|
|
792
|
+
return IsSmallCapacity(capacity) ? 0 : capacity + 1 + NumClonedBytes();
|
|
1190
793
|
}
|
|
1191
794
|
|
|
1192
795
|
// Computes the offset from the start of the backing allocation of control.
|
|
1193
796
|
// infoz and growth_info are stored at the beginning of the backing array.
|
|
1194
|
-
|
|
797
|
+
constexpr size_t ControlOffset(bool has_infoz) {
|
|
1195
798
|
return (has_infoz ? sizeof(HashtablezInfoHandle) : 0) + sizeof(GrowthInfo);
|
|
1196
799
|
}
|
|
1197
800
|
|
|
801
|
+
// Returns the offset of the next item after `offset` that is aligned to `align`
|
|
802
|
+
// bytes. `align` must be a power of two.
|
|
803
|
+
constexpr size_t AlignUpTo(size_t offset, size_t align) {
|
|
804
|
+
return (offset + align - 1) & (~align + 1);
|
|
805
|
+
}
|
|
806
|
+
|
|
1198
807
|
// Helper class for computing offsets and allocation size of hash set fields.
|
|
1199
808
|
class RawHashSetLayout {
|
|
1200
809
|
public:
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
810
|
+
// TODO(b/413062340): maybe don't allocate growth info for capacity 1 tables.
|
|
811
|
+
// Doing so may require additional branches/complexity so it might not be
|
|
812
|
+
// worth it.
|
|
813
|
+
explicit RawHashSetLayout(size_t capacity, size_t slot_size,
|
|
814
|
+
size_t slot_align, bool has_infoz)
|
|
815
|
+
: control_offset_(ControlOffset(has_infoz)),
|
|
1204
816
|
generation_offset_(control_offset_ + NumControlBytes(capacity)),
|
|
1205
817
|
slot_offset_(
|
|
1206
|
-
(generation_offset_ + NumGenerationBytes()
|
|
1207
|
-
|
|
818
|
+
AlignUpTo(generation_offset_ + NumGenerationBytes(), slot_align)),
|
|
819
|
+
alloc_size_(slot_offset_ + capacity * slot_size) {
|
|
1208
820
|
ABSL_SWISSTABLE_ASSERT(IsValidCapacity(capacity));
|
|
821
|
+
ABSL_SWISSTABLE_ASSERT(
|
|
822
|
+
slot_size <=
|
|
823
|
+
((std::numeric_limits<size_t>::max)() - slot_offset_) / capacity);
|
|
1209
824
|
}
|
|
1210
825
|
|
|
1211
|
-
// Returns the capacity of a table.
|
|
1212
|
-
size_t capacity() const { return capacity_; }
|
|
1213
|
-
|
|
1214
826
|
// Returns precomputed offset from the start of the backing allocation of
|
|
1215
827
|
// control.
|
|
1216
828
|
size_t control_offset() const { return control_offset_; }
|
|
@@ -1225,101 +837,59 @@ class RawHashSetLayout {
|
|
|
1225
837
|
|
|
1226
838
|
// Given the capacity of a table, computes the total size of the backing
|
|
1227
839
|
// array.
|
|
1228
|
-
size_t alloc_size(
|
|
1229
|
-
ABSL_SWISSTABLE_ASSERT(
|
|
1230
|
-
slot_size <=
|
|
1231
|
-
((std::numeric_limits<size_t>::max)() - slot_offset_) / capacity_);
|
|
1232
|
-
return slot_offset_ + capacity_ * slot_size;
|
|
1233
|
-
}
|
|
840
|
+
size_t alloc_size() const { return alloc_size_; }
|
|
1234
841
|
|
|
1235
842
|
private:
|
|
1236
|
-
size_t capacity_;
|
|
1237
843
|
size_t control_offset_;
|
|
1238
844
|
size_t generation_offset_;
|
|
1239
845
|
size_t slot_offset_;
|
|
846
|
+
size_t alloc_size_;
|
|
1240
847
|
};
|
|
1241
848
|
|
|
1242
849
|
struct HashtableFreeFunctionsAccess;
|
|
1243
850
|
|
|
1244
|
-
// We only allow a maximum of 1 SOO element, which makes the implementation
|
|
1245
|
-
// much simpler. Complications with multiple SOO elements include:
|
|
1246
|
-
// - Satisfying the guarantee that erasing one element doesn't invalidate
|
|
1247
|
-
// iterators to other elements means we would probably need actual SOO
|
|
1248
|
-
// control bytes.
|
|
1249
|
-
// - In order to prevent user code from depending on iteration order for small
|
|
1250
|
-
// tables, we would need to randomize the iteration order somehow.
|
|
1251
|
-
constexpr size_t SooCapacity() { return 1; }
|
|
1252
|
-
// Sentinel type to indicate SOO CommonFields construction.
|
|
1253
|
-
struct soo_tag_t {};
|
|
1254
|
-
// Sentinel type to indicate SOO CommonFields construction with full size.
|
|
1255
|
-
struct full_soo_tag_t {};
|
|
1256
|
-
// Sentinel type to indicate non-SOO CommonFields construction.
|
|
1257
|
-
struct non_soo_tag_t {};
|
|
1258
|
-
// Sentinel value to indicate an uninitialized CommonFields for use in swapping.
|
|
1259
|
-
struct uninitialized_tag_t {};
|
|
1260
|
-
|
|
1261
|
-
// Suppress erroneous uninitialized memory errors on GCC. For example, GCC
|
|
1262
|
-
// thinks that the call to slot_array() in find_or_prepare_insert() is reading
|
|
1263
|
-
// uninitialized memory, but slot_array is only called there when the table is
|
|
1264
|
-
// non-empty and this memory is initialized when the table is non-empty.
|
|
1265
|
-
#if !defined(__clang__) && defined(__GNUC__)
|
|
1266
|
-
#define ABSL_SWISSTABLE_IGNORE_UNINITIALIZED(x) \
|
|
1267
|
-
_Pragma("GCC diagnostic push") \
|
|
1268
|
-
_Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") \
|
|
1269
|
-
_Pragma("GCC diagnostic ignored \"-Wuninitialized\"") x; \
|
|
1270
|
-
_Pragma("GCC diagnostic pop")
|
|
1271
|
-
#define ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(x) \
|
|
1272
|
-
ABSL_SWISSTABLE_IGNORE_UNINITIALIZED(return x)
|
|
1273
|
-
#else
|
|
1274
|
-
#define ABSL_SWISSTABLE_IGNORE_UNINITIALIZED(x) x
|
|
1275
|
-
#define ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(x) return x
|
|
1276
|
-
#endif
|
|
1277
|
-
|
|
1278
851
|
// This allows us to work around an uninitialized memory warning when
|
|
1279
852
|
// constructing begin() iterators in empty hashtables.
|
|
853
|
+
template <typename T>
|
|
1280
854
|
union MaybeInitializedPtr {
|
|
1281
|
-
|
|
1282
|
-
void set(
|
|
855
|
+
T* get() const { ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(p); }
|
|
856
|
+
void set(T* ptr) { p = ptr; }
|
|
1283
857
|
|
|
1284
|
-
|
|
858
|
+
T* p;
|
|
1285
859
|
};
|
|
1286
860
|
|
|
1287
861
|
struct HeapPtrs {
|
|
1288
|
-
HeapPtrs() = default;
|
|
1289
|
-
explicit HeapPtrs(ctrl_t* c) : control(c) {}
|
|
1290
|
-
|
|
1291
862
|
// The control bytes (and, also, a pointer near to the base of the backing
|
|
1292
863
|
// array).
|
|
1293
864
|
//
|
|
1294
|
-
// This contains `capacity + 1 + NumClonedBytes()` entries
|
|
1295
|
-
// when the table is empty (hence EmptyGroup).
|
|
865
|
+
// This contains `capacity + 1 + NumClonedBytes()` entries.
|
|
1296
866
|
//
|
|
1297
867
|
// Note that growth_info is stored immediately before this pointer.
|
|
1298
|
-
// May be uninitialized for
|
|
1299
|
-
ctrl_t
|
|
868
|
+
// May be uninitialized for small tables.
|
|
869
|
+
MaybeInitializedPtr<ctrl_t> control;
|
|
1300
870
|
|
|
1301
871
|
// The beginning of the slots, located at `SlotOffset()` bytes after
|
|
1302
872
|
// `control`. May be uninitialized for empty tables.
|
|
1303
873
|
// Note: we can't use `slots` because Qt defines "slots" as a macro.
|
|
1304
|
-
MaybeInitializedPtr slot_array;
|
|
874
|
+
MaybeInitializedPtr<void> slot_array;
|
|
1305
875
|
};
|
|
1306
876
|
|
|
877
|
+
// Returns the maximum size of the SOO slot.
|
|
878
|
+
constexpr size_t MaxSooSlotSize() { return sizeof(HeapPtrs); }
|
|
879
|
+
|
|
1307
880
|
// Manages the backing array pointers or the SOO slot. When raw_hash_set::is_soo
|
|
1308
881
|
// is true, the SOO slot is stored in `soo_data`. Otherwise, we use `heap`.
|
|
1309
882
|
union HeapOrSoo {
|
|
1310
|
-
|
|
1311
|
-
explicit HeapOrSoo(ctrl_t* c) : heap(c) {}
|
|
1312
|
-
|
|
1313
|
-
ctrl_t*& control() {
|
|
883
|
+
MaybeInitializedPtr<ctrl_t>& control() {
|
|
1314
884
|
ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(heap.control);
|
|
1315
885
|
}
|
|
1316
|
-
ctrl_t
|
|
886
|
+
MaybeInitializedPtr<ctrl_t> control() const {
|
|
1317
887
|
ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(heap.control);
|
|
1318
888
|
}
|
|
1319
|
-
MaybeInitializedPtr
|
|
889
|
+
MaybeInitializedPtr<void>& slot_array() {
|
|
1320
890
|
ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(heap.slot_array);
|
|
1321
891
|
}
|
|
1322
|
-
MaybeInitializedPtr slot_array() const {
|
|
892
|
+
MaybeInitializedPtr<void> slot_array() const {
|
|
1323
893
|
ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(heap.slot_array);
|
|
1324
894
|
}
|
|
1325
895
|
void* get_soo_data() {
|
|
@@ -1330,26 +900,43 @@ union HeapOrSoo {
|
|
|
1330
900
|
}
|
|
1331
901
|
|
|
1332
902
|
HeapPtrs heap;
|
|
1333
|
-
unsigned char soo_data[
|
|
903
|
+
unsigned char soo_data[MaxSooSlotSize()];
|
|
1334
904
|
};
|
|
1335
905
|
|
|
906
|
+
// Returns a reference to the GrowthInfo object stored immediately before
|
|
907
|
+
// `control`.
|
|
908
|
+
inline GrowthInfo& GetGrowthInfoFromControl(ctrl_t* control) {
|
|
909
|
+
auto* gl_ptr = reinterpret_cast<GrowthInfo*>(control) - 1;
|
|
910
|
+
ABSL_SWISSTABLE_ASSERT(
|
|
911
|
+
reinterpret_cast<uintptr_t>(gl_ptr) % alignof(GrowthInfo) == 0);
|
|
912
|
+
return *gl_ptr;
|
|
913
|
+
}
|
|
914
|
+
|
|
1336
915
|
// CommonFields hold the fields in raw_hash_set that do not depend
|
|
1337
916
|
// on template parameters. This allows us to conveniently pass all
|
|
1338
917
|
// of this state to helper functions as a single argument.
|
|
1339
918
|
class CommonFields : public CommonFieldsGenerationInfo {
|
|
1340
919
|
public:
|
|
1341
|
-
explicit CommonFields(soo_tag_t)
|
|
920
|
+
explicit CommonFields(soo_tag_t)
|
|
921
|
+
: capacity_(SooCapacity()), size_(no_seed_empty_tag_t{}) {}
|
|
1342
922
|
explicit CommonFields(full_soo_tag_t)
|
|
1343
|
-
: capacity_(SooCapacity()), size_(
|
|
923
|
+
: capacity_(SooCapacity()), size_(full_soo_tag_t{}) {}
|
|
1344
924
|
explicit CommonFields(non_soo_tag_t)
|
|
1345
|
-
: capacity_(0), size_(
|
|
925
|
+
: capacity_(0), size_(no_seed_empty_tag_t{}) {}
|
|
1346
926
|
// For use in swapping.
|
|
1347
|
-
explicit CommonFields(uninitialized_tag_t) {}
|
|
927
|
+
explicit CommonFields(uninitialized_tag_t) : size_(uninitialized_tag_t{}) {}
|
|
1348
928
|
|
|
1349
929
|
// Not copyable
|
|
1350
930
|
CommonFields(const CommonFields&) = delete;
|
|
1351
931
|
CommonFields& operator=(const CommonFields&) = delete;
|
|
1352
932
|
|
|
933
|
+
// Copy with guarantee that it is not SOO.
|
|
934
|
+
CommonFields(non_soo_tag_t, const CommonFields& that)
|
|
935
|
+
: capacity_(that.capacity_),
|
|
936
|
+
size_(that.size_),
|
|
937
|
+
heap_or_soo_(that.heap_or_soo_) {
|
|
938
|
+
}
|
|
939
|
+
|
|
1353
940
|
// Movable
|
|
1354
941
|
CommonFields(CommonFields&& that) = default;
|
|
1355
942
|
CommonFields& operator=(CommonFields&&) = default;
|
|
@@ -1364,44 +951,68 @@ class CommonFields : public CommonFieldsGenerationInfo {
|
|
|
1364
951
|
const void* soo_data() const { return heap_or_soo_.get_soo_data(); }
|
|
1365
952
|
void* soo_data() { return heap_or_soo_.get_soo_data(); }
|
|
1366
953
|
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
954
|
+
ctrl_t* control() const {
|
|
955
|
+
ABSL_SWISSTABLE_ASSERT(capacity() > 0);
|
|
956
|
+
// Assume that the control bytes don't alias `this`.
|
|
957
|
+
ctrl_t* ctrl = heap_or_soo_.control().get();
|
|
958
|
+
[[maybe_unused]] size_t num_control_bytes = NumControlBytes(capacity());
|
|
959
|
+
ABSL_ASSUME(reinterpret_cast<uintptr_t>(ctrl + num_control_bytes) <=
|
|
960
|
+
reinterpret_cast<uintptr_t>(this) ||
|
|
961
|
+
reinterpret_cast<uintptr_t>(this + 1) <=
|
|
962
|
+
reinterpret_cast<uintptr_t>(ctrl));
|
|
963
|
+
ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(ctrl);
|
|
1377
964
|
}
|
|
1378
965
|
|
|
966
|
+
void set_control(ctrl_t* c) { heap_or_soo_.control().set(c); }
|
|
967
|
+
|
|
1379
968
|
// Note: we can't use slots() because Qt defines "slots" as a macro.
|
|
1380
969
|
void* slot_array() const { return heap_or_soo_.slot_array().get(); }
|
|
1381
|
-
MaybeInitializedPtr slots_union() const {
|
|
970
|
+
MaybeInitializedPtr<void> slots_union() const {
|
|
971
|
+
return heap_or_soo_.slot_array();
|
|
972
|
+
}
|
|
1382
973
|
void set_slots(void* s) { heap_or_soo_.slot_array().set(s); }
|
|
1383
974
|
|
|
1384
975
|
// The number of filled slots.
|
|
1385
|
-
size_t size() const { return size_
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
}
|
|
976
|
+
size_t size() const { return size_.size(); }
|
|
977
|
+
// Sets the size to zero, but keeps hashinfoz bit and seed.
|
|
978
|
+
void set_size_to_zero() { size_.set_size_to_zero_keep_metadata(); }
|
|
1389
979
|
void set_empty_soo() {
|
|
1390
980
|
AssertInSooMode();
|
|
1391
|
-
size_ =
|
|
981
|
+
size_ = HashtableSize(no_seed_empty_tag_t{});
|
|
1392
982
|
}
|
|
1393
983
|
void set_full_soo() {
|
|
1394
984
|
AssertInSooMode();
|
|
1395
|
-
size_ =
|
|
985
|
+
size_ = HashtableSize(full_soo_tag_t{});
|
|
1396
986
|
}
|
|
1397
987
|
void increment_size() {
|
|
1398
988
|
ABSL_SWISSTABLE_ASSERT(size() < capacity());
|
|
1399
|
-
size_
|
|
989
|
+
size_.increment_size();
|
|
990
|
+
}
|
|
991
|
+
void increment_size(size_t n) {
|
|
992
|
+
ABSL_SWISSTABLE_ASSERT(size() + n <= capacity());
|
|
993
|
+
size_.increment_size(n);
|
|
1400
994
|
}
|
|
1401
995
|
void decrement_size() {
|
|
1402
|
-
ABSL_SWISSTABLE_ASSERT(
|
|
1403
|
-
size_
|
|
996
|
+
ABSL_SWISSTABLE_ASSERT(!empty());
|
|
997
|
+
size_.decrement_size();
|
|
998
|
+
}
|
|
999
|
+
bool empty() const { return size_.empty(); }
|
|
1000
|
+
|
|
1001
|
+
// The seed used for the hash function.
|
|
1002
|
+
PerTableSeed seed() const { return size_.seed(); }
|
|
1003
|
+
// Generates a new seed the hash function.
|
|
1004
|
+
// The table will be invalidated if `!empty()` because hash is being changed.
|
|
1005
|
+
// In such cases, we will need to rehash the table.
|
|
1006
|
+
void generate_new_seed(bool has_infoz) {
|
|
1007
|
+
// Note: we can't use has_infoz() here because we set has_infoz later than
|
|
1008
|
+
// we generate the seed.
|
|
1009
|
+
if (ABSL_PREDICT_FALSE(has_infoz)) {
|
|
1010
|
+
size_.set_sampled_seed();
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
size_.generate_new_seed();
|
|
1404
1014
|
}
|
|
1015
|
+
void set_no_seed_for_testing() { size_.set_no_seed_for_testing(); }
|
|
1405
1016
|
|
|
1406
1017
|
// The total number of available slots.
|
|
1407
1018
|
size_t capacity() const { return capacity_; }
|
|
@@ -1411,6 +1022,7 @@ class CommonFields : public CommonFieldsGenerationInfo {
|
|
|
1411
1022
|
c > kAboveMaxValidCapacity);
|
|
1412
1023
|
capacity_ = c;
|
|
1413
1024
|
}
|
|
1025
|
+
bool is_small() const { return IsSmallCapacity(capacity_); }
|
|
1414
1026
|
|
|
1415
1027
|
// The number of slots we can still fill without needing to rehash.
|
|
1416
1028
|
// This is stored in the heap allocation before the control bytes.
|
|
@@ -1419,39 +1031,49 @@ class CommonFields : public CommonFieldsGenerationInfo {
|
|
|
1419
1031
|
size_t growth_left() const { return growth_info().GetGrowthLeft(); }
|
|
1420
1032
|
|
|
1421
1033
|
GrowthInfo& growth_info() {
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
reinterpret_cast<uintptr_t>(gl_ptr) % alignof(GrowthInfo) == 0);
|
|
1425
|
-
return *gl_ptr;
|
|
1034
|
+
ABSL_SWISSTABLE_ASSERT(!is_small());
|
|
1035
|
+
return GetGrowthInfoFromControl(control());
|
|
1426
1036
|
}
|
|
1427
1037
|
GrowthInfo growth_info() const {
|
|
1428
1038
|
return const_cast<CommonFields*>(this)->growth_info();
|
|
1429
1039
|
}
|
|
1430
1040
|
|
|
1431
|
-
bool has_infoz() const {
|
|
1432
|
-
|
|
1041
|
+
bool has_infoz() const { return size_.has_infoz(); }
|
|
1042
|
+
void set_has_infoz() {
|
|
1043
|
+
ABSL_SWISSTABLE_ASSERT(size_.is_sampled_seed());
|
|
1044
|
+
size_.set_has_infoz();
|
|
1433
1045
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1046
|
+
|
|
1047
|
+
HashtablezInfoHandle* infoz_ptr() const {
|
|
1048
|
+
// growth_info is stored before control bytes.
|
|
1049
|
+
ABSL_SWISSTABLE_ASSERT(
|
|
1050
|
+
reinterpret_cast<uintptr_t>(control()) % alignof(size_t) == 0);
|
|
1051
|
+
ABSL_SWISSTABLE_ASSERT(has_infoz());
|
|
1052
|
+
return reinterpret_cast<HashtablezInfoHandle*>(
|
|
1053
|
+
control() - ControlOffset(/*has_infoz=*/true));
|
|
1436
1054
|
}
|
|
1437
1055
|
|
|
1438
1056
|
HashtablezInfoHandle infoz() {
|
|
1439
|
-
return has_infoz()
|
|
1440
|
-
? *reinterpret_cast<HashtablezInfoHandle*>(backing_array_start())
|
|
1441
|
-
: HashtablezInfoHandle();
|
|
1057
|
+
return has_infoz() ? *infoz_ptr() : HashtablezInfoHandle();
|
|
1442
1058
|
}
|
|
1443
1059
|
void set_infoz(HashtablezInfoHandle infoz) {
|
|
1444
1060
|
ABSL_SWISSTABLE_ASSERT(has_infoz());
|
|
1445
|
-
*
|
|
1061
|
+
*infoz_ptr() = infoz;
|
|
1446
1062
|
}
|
|
1447
1063
|
|
|
1448
1064
|
bool should_rehash_for_bug_detection_on_insert() const {
|
|
1065
|
+
if constexpr (!SwisstableGenerationsEnabled()) {
|
|
1066
|
+
return false;
|
|
1067
|
+
}
|
|
1068
|
+
// As an optimization, we avoid calling ShouldRehashForBugDetection if we
|
|
1069
|
+
// will end up rehashing anyways.
|
|
1070
|
+
if (growth_left() == 0) return false;
|
|
1449
1071
|
return CommonFieldsGenerationInfo::
|
|
1450
|
-
should_rehash_for_bug_detection_on_insert(
|
|
1072
|
+
should_rehash_for_bug_detection_on_insert(capacity());
|
|
1451
1073
|
}
|
|
1452
1074
|
bool should_rehash_for_bug_detection_on_move() const {
|
|
1453
1075
|
return CommonFieldsGenerationInfo::should_rehash_for_bug_detection_on_move(
|
|
1454
|
-
|
|
1076
|
+
capacity());
|
|
1455
1077
|
}
|
|
1456
1078
|
void reset_reserved_growth(size_t reservation) {
|
|
1457
1079
|
CommonFieldsGenerationInfo::reset_reserved_growth(reservation, size());
|
|
@@ -1459,8 +1081,8 @@ class CommonFields : public CommonFieldsGenerationInfo {
|
|
|
1459
1081
|
|
|
1460
1082
|
// The size of the backing array allocation.
|
|
1461
1083
|
size_t alloc_size(size_t slot_size, size_t slot_align) const {
|
|
1462
|
-
return RawHashSetLayout(capacity(), slot_align, has_infoz())
|
|
1463
|
-
.alloc_size(
|
|
1084
|
+
return RawHashSetLayout(capacity(), slot_size, slot_align, has_infoz())
|
|
1085
|
+
.alloc_size();
|
|
1464
1086
|
}
|
|
1465
1087
|
|
|
1466
1088
|
// Move fields other than heap_or_soo_.
|
|
@@ -1513,11 +1135,10 @@ class CommonFields : public CommonFieldsGenerationInfo {
|
|
|
1513
1135
|
// regressions, presumably because we need capacity to do find operations.
|
|
1514
1136
|
size_t capacity_;
|
|
1515
1137
|
|
|
1516
|
-
// The size and also has one bit that stores whether we have infoz.
|
|
1517
1138
|
// TODO(b/289225379): we could put size_ into HeapOrSoo and make capacity_
|
|
1518
1139
|
// encode the size in SOO case. We would be making size()/capacity() more
|
|
1519
1140
|
// expensive in order to have more SOO space.
|
|
1520
|
-
|
|
1141
|
+
HashtableSize size_;
|
|
1521
1142
|
|
|
1522
1143
|
// Either the control/slots pointers or the SOO slot.
|
|
1523
1144
|
HeapOrSoo heap_or_soo_;
|
|
@@ -1527,11 +1148,17 @@ template <class Policy, class Hash, class Eq, class Alloc>
|
|
|
1527
1148
|
class raw_hash_set;
|
|
1528
1149
|
|
|
1529
1150
|
// Returns the next valid capacity after `n`.
|
|
1530
|
-
|
|
1151
|
+
constexpr size_t NextCapacity(size_t n) {
|
|
1531
1152
|
ABSL_SWISSTABLE_ASSERT(IsValidCapacity(n) || n == 0);
|
|
1532
1153
|
return n * 2 + 1;
|
|
1533
1154
|
}
|
|
1534
1155
|
|
|
1156
|
+
// Returns the previous valid capacity before `n`.
|
|
1157
|
+
constexpr size_t PreviousCapacity(size_t n) {
|
|
1158
|
+
ABSL_SWISSTABLE_ASSERT(IsValidCapacity(n));
|
|
1159
|
+
return n / 2;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1535
1162
|
// Applies the following mapping to every byte in the control array:
|
|
1536
1163
|
// * kDeleted -> kEmpty
|
|
1537
1164
|
// * kEmpty -> kEmpty
|
|
@@ -1543,19 +1170,10 @@ inline size_t NextCapacity(size_t n) {
|
|
|
1543
1170
|
void ConvertDeletedToEmptyAndFullToDeleted(ctrl_t* ctrl, size_t capacity);
|
|
1544
1171
|
|
|
1545
1172
|
// Converts `n` into the next valid capacity, per `IsValidCapacity`.
|
|
1546
|
-
|
|
1173
|
+
constexpr size_t NormalizeCapacity(size_t n) {
|
|
1547
1174
|
return n ? ~size_t{} >> countl_zero(n) : 1;
|
|
1548
1175
|
}
|
|
1549
1176
|
|
|
1550
|
-
template <size_t kSlotSize>
|
|
1551
|
-
size_t MaxValidCapacity() {
|
|
1552
|
-
return NormalizeCapacity((std::numeric_limits<size_t>::max)() / 4 /
|
|
1553
|
-
kSlotSize);
|
|
1554
|
-
}
|
|
1555
|
-
|
|
1556
|
-
// Use a non-inlined function to avoid code bloat.
|
|
1557
|
-
[[noreturn]] void HashTableSizeOverflow();
|
|
1558
|
-
|
|
1559
1177
|
// General notes on capacity/growth methods below:
|
|
1560
1178
|
// - We use 7/8th as maximum load factor. For 16-wide groups, that gives an
|
|
1561
1179
|
// average of two empty slots per group.
|
|
@@ -1566,7 +1184,7 @@ size_t MaxValidCapacity() {
|
|
|
1566
1184
|
|
|
1567
1185
|
// Given `capacity`, applies the load factor; i.e., it returns the maximum
|
|
1568
1186
|
// number of values we should put into the table before a resizing rehash.
|
|
1569
|
-
|
|
1187
|
+
constexpr size_t CapacityToGrowth(size_t capacity) {
|
|
1570
1188
|
ABSL_SWISSTABLE_ASSERT(IsValidCapacity(capacity));
|
|
1571
1189
|
// `capacity*7/8`
|
|
1572
1190
|
if (Group::kWidth == 8 && capacity == 7) {
|
|
@@ -1576,18 +1194,32 @@ inline size_t CapacityToGrowth(size_t capacity) {
|
|
|
1576
1194
|
return capacity - capacity / 8;
|
|
1577
1195
|
}
|
|
1578
1196
|
|
|
1579
|
-
// Given `
|
|
1197
|
+
// Given `size`, "unapplies" the load factor to find how large the capacity
|
|
1580
1198
|
// should be to stay within the load factor.
|
|
1581
1199
|
//
|
|
1582
|
-
//
|
|
1583
|
-
//
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1200
|
+
// For size == 0, returns 0.
|
|
1201
|
+
// For other values, returns the same as `NormalizeCapacity(size*8/7)`.
|
|
1202
|
+
constexpr size_t SizeToCapacity(size_t size) {
|
|
1203
|
+
if (size == 0) {
|
|
1204
|
+
return 0;
|
|
1205
|
+
}
|
|
1206
|
+
// The minimum possible capacity is NormalizeCapacity(size).
|
|
1207
|
+
// Shifting right `~size_t{}` by `leading_zeros` yields
|
|
1208
|
+
// NormalizeCapacity(size).
|
|
1209
|
+
int leading_zeros = absl::countl_zero(size);
|
|
1210
|
+
constexpr size_t kLast3Bits = size_t{7} << (sizeof(size_t) * 8 - 3);
|
|
1211
|
+
// max_size_for_next_capacity = max_load_factor * next_capacity
|
|
1212
|
+
// = (7/8) * (~size_t{} >> leading_zeros)
|
|
1213
|
+
// = (7/8*~size_t{}) >> leading_zeros
|
|
1214
|
+
// = kLast3Bits >> leading_zeros
|
|
1215
|
+
size_t max_size_for_next_capacity = kLast3Bits >> leading_zeros;
|
|
1216
|
+
// Decrease shift if size is too big for the minimum capacity.
|
|
1217
|
+
leading_zeros -= static_cast<int>(size > max_size_for_next_capacity);
|
|
1218
|
+
if constexpr (Group::kWidth == 8) {
|
|
1219
|
+
// Formula doesn't work when size==7 for 8-wide groups.
|
|
1220
|
+
leading_zeros -= (size == 7);
|
|
1221
|
+
}
|
|
1222
|
+
return (~size_t{}) >> leading_zeros;
|
|
1591
1223
|
}
|
|
1592
1224
|
|
|
1593
1225
|
template <class InputIter>
|
|
@@ -1596,12 +1228,9 @@ size_t SelectBucketCountForIterRange(InputIter first, InputIter last,
|
|
|
1596
1228
|
if (bucket_count != 0) {
|
|
1597
1229
|
return bucket_count;
|
|
1598
1230
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
InputIterCategory>::value) {
|
|
1603
|
-
return GrowthToLowerboundCapacity(
|
|
1604
|
-
static_cast<size_t>(std::distance(first, last)));
|
|
1231
|
+
if (base_internal::IsAtLeastIterator<std::random_access_iterator_tag,
|
|
1232
|
+
InputIter>()) {
|
|
1233
|
+
return SizeToCapacity(static_cast<size_t>(std::distance(first, last)));
|
|
1605
1234
|
}
|
|
1606
1235
|
return 0;
|
|
1607
1236
|
}
|
|
@@ -1627,7 +1256,7 @@ inline void AssertIsFull(const ctrl_t* ctrl, GenerationType generation,
|
|
|
1627
1256
|
if (ABSL_PREDICT_FALSE(ctrl == nullptr)) {
|
|
1628
1257
|
ABSL_RAW_LOG(FATAL, "%s called on end() iterator.", operation);
|
|
1629
1258
|
}
|
|
1630
|
-
if (ABSL_PREDICT_FALSE(ctrl ==
|
|
1259
|
+
if (ABSL_PREDICT_FALSE(ctrl == DefaultIterControl())) {
|
|
1631
1260
|
ABSL_RAW_LOG(FATAL, "%s called on default-constructed iterator.",
|
|
1632
1261
|
operation);
|
|
1633
1262
|
}
|
|
@@ -1662,7 +1291,7 @@ inline void AssertIsValidForComparison(const ctrl_t* ctrl,
|
|
|
1662
1291
|
const GenerationType* generation_ptr) {
|
|
1663
1292
|
if (!SwisstableDebugEnabled()) return;
|
|
1664
1293
|
const bool ctrl_is_valid_for_comparison =
|
|
1665
|
-
ctrl == nullptr || ctrl ==
|
|
1294
|
+
ctrl == nullptr || ctrl == DefaultIterControl() || IsFull(*ctrl);
|
|
1666
1295
|
if (SwisstableGenerationsEnabled()) {
|
|
1667
1296
|
if (ABSL_PREDICT_FALSE(generation != *generation_ptr)) {
|
|
1668
1297
|
ABSL_RAW_LOG(FATAL,
|
|
@@ -1674,7 +1303,7 @@ inline void AssertIsValidForComparison(const ctrl_t* ctrl,
|
|
|
1674
1303
|
FATAL, "Invalid iterator comparison. The element was likely erased.");
|
|
1675
1304
|
}
|
|
1676
1305
|
} else {
|
|
1677
|
-
|
|
1306
|
+
ABSL_HARDENING_ASSERT_SLOW(
|
|
1678
1307
|
ctrl_is_valid_for_comparison &&
|
|
1679
1308
|
"Invalid iterator comparison. The element might have been erased or "
|
|
1680
1309
|
"the table might have rehashed. Consider running with --config=asan to "
|
|
@@ -1728,8 +1357,8 @@ inline void AssertSameContainer(const ctrl_t* ctrl_a, const ctrl_t* ctrl_b,
|
|
|
1728
1357
|
}
|
|
1729
1358
|
};
|
|
1730
1359
|
|
|
1731
|
-
const bool a_is_default = ctrl_a ==
|
|
1732
|
-
const bool b_is_default = ctrl_b ==
|
|
1360
|
+
const bool a_is_default = ctrl_a == DefaultIterControl();
|
|
1361
|
+
const bool b_is_default = ctrl_b == DefaultIterControl();
|
|
1733
1362
|
if (a_is_default && b_is_default) return;
|
|
1734
1363
|
fail_if(a_is_default != b_is_default,
|
|
1735
1364
|
"Comparing default-constructed hashtable iterator with a "
|
|
@@ -1737,13 +1366,6 @@ inline void AssertSameContainer(const ctrl_t* ctrl_a, const ctrl_t* ctrl_b,
|
|
|
1737
1366
|
|
|
1738
1367
|
if (SwisstableGenerationsEnabled()) {
|
|
1739
1368
|
if (ABSL_PREDICT_TRUE(generation_ptr_a == generation_ptr_b)) return;
|
|
1740
|
-
// Users don't need to know whether the tables are SOO so don't mention SOO
|
|
1741
|
-
// in the debug message.
|
|
1742
|
-
const bool a_is_soo = IsSooControl(ctrl_a);
|
|
1743
|
-
const bool b_is_soo = IsSooControl(ctrl_b);
|
|
1744
|
-
fail_if(a_is_soo != b_is_soo || (a_is_soo && b_is_soo),
|
|
1745
|
-
"Comparing iterators from different hashtables.");
|
|
1746
|
-
|
|
1747
1369
|
const bool a_is_empty = IsEmptyGeneration(generation_ptr_a);
|
|
1748
1370
|
const bool b_is_empty = IsEmptyGeneration(generation_ptr_b);
|
|
1749
1371
|
fail_if(a_is_empty != b_is_empty,
|
|
@@ -1772,87 +1394,85 @@ struct FindInfo {
|
|
|
1772
1394
|
size_t probe_length;
|
|
1773
1395
|
};
|
|
1774
1396
|
|
|
1775
|
-
// Whether a table is "small". A small table fits entirely into a probing
|
|
1776
|
-
// group, i.e., has a capacity < `Group::kWidth`.
|
|
1777
|
-
//
|
|
1778
|
-
// In small mode we are able to use the whole capacity. The extra control
|
|
1779
|
-
// bytes give us at least one "empty" control byte to stop the iteration.
|
|
1780
|
-
// This is important to make 1 a valid capacity.
|
|
1781
|
-
//
|
|
1782
|
-
// In small mode only the first `capacity` control bytes after the sentinel
|
|
1783
|
-
// are valid. The rest contain dummy ctrl_t::kEmpty values that do not
|
|
1784
|
-
// represent a real slot. This is important to take into account on
|
|
1785
|
-
// `find_first_non_full()`, where we never try
|
|
1786
|
-
// `ShouldInsertBackwards()` for small tables.
|
|
1787
|
-
inline bool is_small(size_t capacity) { return capacity < Group::kWidth - 1; }
|
|
1788
|
-
|
|
1789
1397
|
// Whether a table fits entirely into a probing group.
|
|
1790
1398
|
// Arbitrary order of elements in such tables is correct.
|
|
1791
|
-
|
|
1399
|
+
constexpr bool is_single_group(size_t capacity) {
|
|
1792
1400
|
return capacity <= Group::kWidth;
|
|
1793
1401
|
}
|
|
1794
1402
|
|
|
1795
1403
|
// Begins a probing operation on `common.control`, using `hash`.
|
|
1796
|
-
inline probe_seq<Group::kWidth>
|
|
1797
|
-
|
|
1798
|
-
|
|
1404
|
+
inline probe_seq<Group::kWidth> probe_h1(size_t capacity, size_t h1) {
|
|
1405
|
+
return probe_seq<Group::kWidth>(h1, capacity);
|
|
1406
|
+
}
|
|
1407
|
+
inline probe_seq<Group::kWidth> probe(size_t capacity, size_t hash) {
|
|
1408
|
+
return probe_h1(capacity, H1(hash));
|
|
1799
1409
|
}
|
|
1800
1410
|
inline probe_seq<Group::kWidth> probe(const CommonFields& common, size_t hash) {
|
|
1801
|
-
return probe(common.
|
|
1411
|
+
return probe(common.capacity(), hash);
|
|
1802
1412
|
}
|
|
1803
1413
|
|
|
1804
|
-
|
|
1805
|
-
|
|
1414
|
+
constexpr size_t kProbedElementIndexSentinel = ~size_t{};
|
|
1415
|
+
|
|
1416
|
+
// Implementation detail of transfer_unprobed_elements_to_next_capacity_fn.
|
|
1417
|
+
// Tries to find the new index for an element whose hash corresponds to
|
|
1418
|
+
// `h1` for growth to the next capacity.
|
|
1419
|
+
// Returns kProbedElementIndexSentinel if full probing is required.
|
|
1806
1420
|
//
|
|
1807
|
-
//
|
|
1421
|
+
// If element is located in the first probing group in the table before growth,
|
|
1422
|
+
// returns one of two positions: `old_index` or `old_index + old_capacity + 1`.
|
|
1808
1423
|
//
|
|
1809
|
-
//
|
|
1810
|
-
//
|
|
1424
|
+
// Otherwise, we will try to insert it into the first probe group of the new
|
|
1425
|
+
// table. We only attempt to do so if the first probe group is already
|
|
1426
|
+
// initialized.
|
|
1811
1427
|
template <typename = void>
|
|
1812
|
-
inline
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1428
|
+
inline size_t TryFindNewIndexWithoutProbing(size_t h1, size_t old_index,
|
|
1429
|
+
size_t old_capacity,
|
|
1430
|
+
ctrl_t* new_ctrl,
|
|
1431
|
+
size_t new_capacity) {
|
|
1432
|
+
size_t index_diff = old_index - h1;
|
|
1433
|
+
// The first probe group starts with h1 & capacity.
|
|
1434
|
+
// All following groups start at (h1 + Group::kWidth * K) & capacity.
|
|
1435
|
+
// We can find an index within the floating group as index_diff modulo
|
|
1436
|
+
// Group::kWidth.
|
|
1437
|
+
// Both old and new capacity are larger than Group::kWidth so we can avoid
|
|
1438
|
+
// computing `& capacity`.
|
|
1439
|
+
size_t in_floating_group_index = index_diff & (Group::kWidth - 1);
|
|
1440
|
+
// By subtracting we will get the difference between the first probe group
|
|
1441
|
+
// and the probe group corresponding to old_index.
|
|
1442
|
+
index_diff -= in_floating_group_index;
|
|
1443
|
+
if (ABSL_PREDICT_TRUE((index_diff & old_capacity) == 0)) {
|
|
1444
|
+
size_t new_index = (h1 + in_floating_group_index) & new_capacity;
|
|
1445
|
+
ABSL_ASSUME(new_index != kProbedElementIndexSentinel);
|
|
1446
|
+
return new_index;
|
|
1447
|
+
}
|
|
1448
|
+
ABSL_SWISSTABLE_ASSERT(((old_index - h1) & old_capacity) >= Group::kWidth);
|
|
1449
|
+
// Try to insert element into the first probe group.
|
|
1450
|
+
// new_ctrl is not yet fully initialized so we can't use regular search via
|
|
1451
|
+
// find_first_non_full.
|
|
1452
|
+
|
|
1453
|
+
// We can search in the first probe group only if it is located in already
|
|
1454
|
+
// initialized part of the table.
|
|
1455
|
+
if (ABSL_PREDICT_FALSE((h1 & old_capacity) >= old_index)) {
|
|
1456
|
+
return kProbedElementIndexSentinel;
|
|
1457
|
+
}
|
|
1458
|
+
size_t offset = h1 & new_capacity;
|
|
1459
|
+
Group new_g(new_ctrl + offset);
|
|
1460
|
+
if (auto mask = new_g.MaskNonFull(); ABSL_PREDICT_TRUE(mask)) {
|
|
1461
|
+
size_t result = offset + mask.LowestBitSet();
|
|
1462
|
+
ABSL_ASSUME(result != kProbedElementIndexSentinel);
|
|
1463
|
+
return result;
|
|
1464
|
+
}
|
|
1465
|
+
return kProbedElementIndexSentinel;
|
|
1830
1466
|
}
|
|
1831
1467
|
|
|
1832
|
-
// Extern template for inline function
|
|
1468
|
+
// Extern template for inline function keeps possibility of inlining.
|
|
1833
1469
|
// When compiler decided to not inline, no symbols will be added to the
|
|
1834
1470
|
// corresponding translation unit.
|
|
1835
|
-
extern template
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
inline void ResetGrowthLeft(CommonFields& common) {
|
|
1842
|
-
common.growth_info().InitGrowthLeftNoDeleted(
|
|
1843
|
-
CapacityToGrowth(common.capacity()) - common.size());
|
|
1844
|
-
}
|
|
1845
|
-
|
|
1846
|
-
// Sets `ctrl` to `{kEmpty, kSentinel, ..., kEmpty}`, marking the entire
|
|
1847
|
-
// array as marked as empty.
|
|
1848
|
-
inline void ResetCtrl(CommonFields& common, size_t slot_size) {
|
|
1849
|
-
const size_t capacity = common.capacity();
|
|
1850
|
-
ctrl_t* ctrl = common.control();
|
|
1851
|
-
std::memset(ctrl, static_cast<int8_t>(ctrl_t::kEmpty),
|
|
1852
|
-
capacity + 1 + NumClonedBytes());
|
|
1853
|
-
ctrl[capacity] = ctrl_t::kSentinel;
|
|
1854
|
-
SanitizerPoisonMemoryRegion(common.slot_array(), slot_size * capacity);
|
|
1855
|
-
}
|
|
1471
|
+
extern template size_t TryFindNewIndexWithoutProbing(size_t h1,
|
|
1472
|
+
size_t old_index,
|
|
1473
|
+
size_t old_capacity,
|
|
1474
|
+
ctrl_t* new_ctrl,
|
|
1475
|
+
size_t new_capacity);
|
|
1856
1476
|
|
|
1857
1477
|
// Sets sanitizer poisoning for slot corresponding to control byte being set.
|
|
1858
1478
|
inline void DoSanitizeOnSetCtrl(const CommonFields& c, size_t i, ctrl_t h,
|
|
@@ -1872,6 +1492,7 @@ inline void DoSanitizeOnSetCtrl(const CommonFields& c, size_t i, ctrl_t h,
|
|
|
1872
1492
|
// mirror the value to the cloned tail if necessary.
|
|
1873
1493
|
inline void SetCtrl(const CommonFields& c, size_t i, ctrl_t h,
|
|
1874
1494
|
size_t slot_size) {
|
|
1495
|
+
ABSL_SWISSTABLE_ASSERT(!c.is_small());
|
|
1875
1496
|
DoSanitizeOnSetCtrl(c, i, h, slot_size);
|
|
1876
1497
|
ctrl_t* ctrl = c.control();
|
|
1877
1498
|
ctrl[i] = h;
|
|
@@ -1887,6 +1508,7 @@ inline void SetCtrl(const CommonFields& c, size_t i, h2_t h, size_t slot_size) {
|
|
|
1887
1508
|
// setting the cloned control byte.
|
|
1888
1509
|
inline void SetCtrlInSingleGroupTable(const CommonFields& c, size_t i, ctrl_t h,
|
|
1889
1510
|
size_t slot_size) {
|
|
1511
|
+
ABSL_SWISSTABLE_ASSERT(!c.is_small());
|
|
1890
1512
|
ABSL_SWISSTABLE_ASSERT(is_single_group(c.capacity()));
|
|
1891
1513
|
DoSanitizeOnSetCtrl(c, i, h, slot_size);
|
|
1892
1514
|
ctrl_t* ctrl = c.control();
|
|
@@ -1899,6 +1521,22 @@ inline void SetCtrlInSingleGroupTable(const CommonFields& c, size_t i, h2_t h,
|
|
|
1899
1521
|
SetCtrlInSingleGroupTable(c, i, static_cast<ctrl_t>(h), slot_size);
|
|
1900
1522
|
}
|
|
1901
1523
|
|
|
1524
|
+
// Like SetCtrl, but in a table with capacity >= Group::kWidth - 1,
|
|
1525
|
+
// we can save some operations when setting the cloned control byte.
|
|
1526
|
+
inline void SetCtrlInLargeTable(const CommonFields& c, size_t i, ctrl_t h,
|
|
1527
|
+
size_t slot_size) {
|
|
1528
|
+
ABSL_SWISSTABLE_ASSERT(c.capacity() >= Group::kWidth - 1);
|
|
1529
|
+
DoSanitizeOnSetCtrl(c, i, h, slot_size);
|
|
1530
|
+
ctrl_t* ctrl = c.control();
|
|
1531
|
+
ctrl[i] = h;
|
|
1532
|
+
ctrl[((i - NumClonedBytes()) & c.capacity()) + NumClonedBytes()] = h;
|
|
1533
|
+
}
|
|
1534
|
+
// Overload for setting to an occupied `h2_t` rather than a special `ctrl_t`.
|
|
1535
|
+
inline void SetCtrlInLargeTable(const CommonFields& c, size_t i, h2_t h,
|
|
1536
|
+
size_t slot_size) {
|
|
1537
|
+
SetCtrlInLargeTable(c, i, static_cast<ctrl_t>(h), slot_size);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1902
1540
|
// growth_info (which is a size_t) is stored with the backing array.
|
|
1903
1541
|
constexpr size_t BackingArrayAlignment(size_t align_of_slot) {
|
|
1904
1542
|
return (std::max)(align_of_slot, alignof(GrowthInfo));
|
|
@@ -1911,423 +1549,260 @@ inline void* SlotAddress(void* slot_array, size_t slot, size_t slot_size) {
|
|
|
1911
1549
|
(slot * slot_size));
|
|
1912
1550
|
}
|
|
1913
1551
|
|
|
1914
|
-
// Iterates over all full slots and calls `cb(const ctrl_t*,
|
|
1915
|
-
// No insertion to the table allowed during
|
|
1552
|
+
// Iterates over all full slots and calls `cb(const ctrl_t*, void*)`.
|
|
1553
|
+
// No insertion to the table is allowed during `cb` call.
|
|
1916
1554
|
// Erasure is allowed only for the element passed to the callback.
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
const size_t cap = c.capacity();
|
|
1921
|
-
const ctrl_t* ctrl = c.control();
|
|
1922
|
-
if (is_small(cap)) {
|
|
1923
|
-
// Mirrored/cloned control bytes in small table are also located in the
|
|
1924
|
-
// first group (starting from position 0). We are taking group from position
|
|
1925
|
-
// `capacity` in order to avoid duplicates.
|
|
1926
|
-
|
|
1927
|
-
// Small tables capacity fits into portable group, where
|
|
1928
|
-
// GroupPortableImpl::MaskFull is more efficient for the
|
|
1929
|
-
// capacity <= GroupPortableImpl::kWidth.
|
|
1930
|
-
ABSL_SWISSTABLE_ASSERT(cap <= GroupPortableImpl::kWidth &&
|
|
1931
|
-
"unexpectedly large small capacity");
|
|
1932
|
-
static_assert(Group::kWidth >= GroupPortableImpl::kWidth,
|
|
1933
|
-
"unexpected group width");
|
|
1934
|
-
// Group starts from kSentinel slot, so indices in the mask will
|
|
1935
|
-
// be increased by 1.
|
|
1936
|
-
const auto mask = GroupPortableImpl(ctrl + cap).MaskFull();
|
|
1937
|
-
--ctrl;
|
|
1938
|
-
--slot;
|
|
1939
|
-
for (uint32_t i : mask) {
|
|
1940
|
-
cb(ctrl + i, slot + i);
|
|
1941
|
-
}
|
|
1942
|
-
return;
|
|
1943
|
-
}
|
|
1944
|
-
size_t remaining = c.size();
|
|
1945
|
-
ABSL_ATTRIBUTE_UNUSED const size_t original_size_for_assert = remaining;
|
|
1946
|
-
while (remaining != 0) {
|
|
1947
|
-
for (uint32_t i : GroupFullEmptyOrDeleted(ctrl).MaskFull()) {
|
|
1948
|
-
ABSL_SWISSTABLE_ASSERT(IsFull(ctrl[i]) &&
|
|
1949
|
-
"hash table was modified unexpectedly");
|
|
1950
|
-
cb(ctrl + i, slot + i);
|
|
1951
|
-
--remaining;
|
|
1952
|
-
}
|
|
1953
|
-
ctrl += Group::kWidth;
|
|
1954
|
-
slot += Group::kWidth;
|
|
1955
|
-
ABSL_SWISSTABLE_ASSERT(
|
|
1956
|
-
(remaining == 0 || *(ctrl - 1) != ctrl_t::kSentinel) &&
|
|
1957
|
-
"hash table was modified unexpectedly");
|
|
1958
|
-
}
|
|
1959
|
-
// NOTE: erasure of the current element is allowed in callback for
|
|
1960
|
-
// absl::erase_if specialization. So we use `>=`.
|
|
1961
|
-
ABSL_SWISSTABLE_ASSERT(original_size_for_assert >= c.size() &&
|
|
1962
|
-
"hash table was modified unexpectedly");
|
|
1963
|
-
}
|
|
1555
|
+
// The table must not be in SOO mode.
|
|
1556
|
+
void IterateOverFullSlots(const CommonFields& c, size_t slot_size,
|
|
1557
|
+
absl::FunctionRef<void(const ctrl_t*, void*)> cb);
|
|
1964
1558
|
|
|
1965
1559
|
template <typename CharAlloc>
|
|
1966
|
-
constexpr bool
|
|
1560
|
+
constexpr bool ShouldSampleHashtablezInfoForAlloc() {
|
|
1967
1561
|
// Folks with custom allocators often make unwarranted assumptions about the
|
|
1968
1562
|
// behavior of their classes vis-a-vis trivial destructability and what
|
|
1969
1563
|
// calls they will or won't make. Avoid sampling for people with custom
|
|
1970
1564
|
// allocators to get us out of this mess. This is not a hard guarantee but
|
|
1971
1565
|
// a workaround while we plan the exact guarantee we want to provide.
|
|
1972
|
-
return std::
|
|
1566
|
+
return std::is_same_v<CharAlloc, std::allocator<char>>;
|
|
1973
1567
|
}
|
|
1974
1568
|
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
HashtablezInfoHandle forced_infoz,
|
|
1980
|
-
CommonFields& c) {
|
|
1981
|
-
if (forced_infoz.IsSampled()) return forced_infoz;
|
|
1982
|
-
// In SOO, we sample on the first insertion so if this is an empty SOO case
|
|
1983
|
-
// (e.g. when reserve is called), then we still need to sample.
|
|
1984
|
-
if (kSooEnabled && was_soo && c.size() == 0) {
|
|
1985
|
-
return Sample(sizeof_slot, sizeof_key, sizeof_value, SooCapacity());
|
|
1986
|
-
}
|
|
1987
|
-
// For non-SOO cases, we sample whenever the capacity is increasing from zero
|
|
1988
|
-
// to non-zero.
|
|
1989
|
-
if (!kSooEnabled && old_capacity == 0) {
|
|
1990
|
-
return Sample(sizeof_slot, sizeof_key, sizeof_value, 0);
|
|
1991
|
-
}
|
|
1992
|
-
return c.infoz();
|
|
1569
|
+
// Allocates `n` bytes for a backing array.
|
|
1570
|
+
template <size_t AlignOfBackingArray, typename Alloc>
|
|
1571
|
+
ABSL_ATTRIBUTE_NOINLINE void* AllocateBackingArray(void* alloc, size_t n) {
|
|
1572
|
+
return Allocate<AlignOfBackingArray>(static_cast<Alloc*>(alloc), n);
|
|
1993
1573
|
}
|
|
1994
1574
|
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
was_soo_(was_soo),
|
|
2006
|
-
had_soo_slot_(had_soo_slot),
|
|
2007
|
-
forced_infoz_(forced_infoz) {}
|
|
2008
|
-
|
|
2009
|
-
// Optimized for small groups version of `find_first_non_full`.
|
|
2010
|
-
// Beneficial only right after calling `raw_hash_set::resize`.
|
|
2011
|
-
// It is safe to call in case capacity is big or was not changed, but there
|
|
2012
|
-
// will be no performance benefit.
|
|
2013
|
-
// It has implicit assumption that `resize` will call
|
|
2014
|
-
// `GrowSizeIntoSingleGroup*` in case `IsGrowingIntoSingleGroupApplicable`.
|
|
2015
|
-
// Falls back to `find_first_non_full` in case of big groups.
|
|
2016
|
-
static FindInfo FindFirstNonFullAfterResize(const CommonFields& c,
|
|
2017
|
-
size_t old_capacity, size_t hash);
|
|
2018
|
-
|
|
2019
|
-
HeapOrSoo& old_heap_or_soo() { return old_heap_or_soo_; }
|
|
2020
|
-
void* old_soo_data() { return old_heap_or_soo_.get_soo_data(); }
|
|
2021
|
-
ctrl_t* old_ctrl() const {
|
|
2022
|
-
ABSL_SWISSTABLE_ASSERT(!was_soo_);
|
|
2023
|
-
return old_heap_or_soo_.control();
|
|
2024
|
-
}
|
|
2025
|
-
void* old_slots() const {
|
|
2026
|
-
ABSL_SWISSTABLE_ASSERT(!was_soo_);
|
|
2027
|
-
return old_heap_or_soo_.slot_array().get();
|
|
2028
|
-
}
|
|
2029
|
-
size_t old_capacity() const { return old_capacity_; }
|
|
2030
|
-
|
|
2031
|
-
// Returns the index of the SOO slot when growing from SOO to non-SOO in a
|
|
2032
|
-
// single group. See also InitControlBytesAfterSoo(). It's important to use
|
|
2033
|
-
// index 1 so that when resizing from capacity 1 to 3, we can still have
|
|
2034
|
-
// random iteration order between the first two inserted elements.
|
|
2035
|
-
// I.e. it allows inserting the second element at either index 0 or 2.
|
|
2036
|
-
static size_t SooSlotIndex() { return 1; }
|
|
2037
|
-
|
|
2038
|
-
// Allocates a backing array for the hashtable.
|
|
2039
|
-
// Reads `capacity` and updates all other fields based on the result of
|
|
2040
|
-
// the allocation.
|
|
2041
|
-
//
|
|
2042
|
-
// It also may do the following actions:
|
|
2043
|
-
// 1. initialize control bytes
|
|
2044
|
-
// 2. initialize slots
|
|
2045
|
-
// 3. deallocate old slots.
|
|
2046
|
-
//
|
|
2047
|
-
// We are bundling a lot of functionality
|
|
2048
|
-
// in one ABSL_ATTRIBUTE_NOINLINE function in order to minimize binary code
|
|
2049
|
-
// duplication in raw_hash_set<>::resize.
|
|
2050
|
-
//
|
|
2051
|
-
// `c.capacity()` must be nonzero.
|
|
2052
|
-
// POSTCONDITIONS:
|
|
2053
|
-
// 1. CommonFields is initialized.
|
|
2054
|
-
//
|
|
2055
|
-
// if IsGrowingIntoSingleGroupApplicable && TransferUsesMemcpy
|
|
2056
|
-
// Both control bytes and slots are fully initialized.
|
|
2057
|
-
// old_slots are deallocated.
|
|
2058
|
-
// infoz.RecordRehash is called.
|
|
2059
|
-
//
|
|
2060
|
-
// if IsGrowingIntoSingleGroupApplicable && !TransferUsesMemcpy
|
|
2061
|
-
// Control bytes are fully initialized.
|
|
2062
|
-
// infoz.RecordRehash is called.
|
|
2063
|
-
// GrowSizeIntoSingleGroup must be called to finish slots initialization.
|
|
2064
|
-
//
|
|
2065
|
-
// if !IsGrowingIntoSingleGroupApplicable
|
|
2066
|
-
// Control bytes are initialized to empty table via ResetCtrl.
|
|
2067
|
-
// raw_hash_set<>::resize must insert elements regularly.
|
|
2068
|
-
// infoz.RecordRehash is called if old_capacity == 0.
|
|
2069
|
-
//
|
|
2070
|
-
// Returns IsGrowingIntoSingleGroupApplicable result to avoid recomputation.
|
|
2071
|
-
template <typename Alloc, size_t SizeOfSlot, bool TransferUsesMemcpy,
|
|
2072
|
-
bool SooEnabled, size_t AlignOfSlot>
|
|
2073
|
-
ABSL_ATTRIBUTE_NOINLINE bool InitializeSlots(CommonFields& c, Alloc alloc,
|
|
2074
|
-
ctrl_t soo_slot_h2,
|
|
2075
|
-
size_t key_size,
|
|
2076
|
-
size_t value_size) {
|
|
2077
|
-
ABSL_SWISSTABLE_ASSERT(c.capacity());
|
|
2078
|
-
HashtablezInfoHandle infoz =
|
|
2079
|
-
ShouldSampleHashtablezInfo<Alloc>()
|
|
2080
|
-
? SampleHashtablezInfo<SooEnabled>(SizeOfSlot, key_size, value_size,
|
|
2081
|
-
old_capacity_, was_soo_,
|
|
2082
|
-
forced_infoz_, c)
|
|
2083
|
-
: HashtablezInfoHandle{};
|
|
2084
|
-
|
|
2085
|
-
const bool has_infoz = infoz.IsSampled();
|
|
2086
|
-
RawHashSetLayout layout(c.capacity(), AlignOfSlot, has_infoz);
|
|
2087
|
-
char* mem = static_cast<char*>(Allocate<BackingArrayAlignment(AlignOfSlot)>(
|
|
2088
|
-
&alloc, layout.alloc_size(SizeOfSlot)));
|
|
2089
|
-
const GenerationType old_generation = c.generation();
|
|
2090
|
-
c.set_generation_ptr(
|
|
2091
|
-
reinterpret_cast<GenerationType*>(mem + layout.generation_offset()));
|
|
2092
|
-
c.set_generation(NextGeneration(old_generation));
|
|
2093
|
-
c.set_control(reinterpret_cast<ctrl_t*>(mem + layout.control_offset()));
|
|
2094
|
-
c.set_slots(mem + layout.slot_offset());
|
|
2095
|
-
ResetGrowthLeft(c);
|
|
2096
|
-
|
|
2097
|
-
const bool grow_single_group =
|
|
2098
|
-
IsGrowingIntoSingleGroupApplicable(old_capacity_, layout.capacity());
|
|
2099
|
-
if (SooEnabled && was_soo_ && grow_single_group) {
|
|
2100
|
-
InitControlBytesAfterSoo(c.control(), soo_slot_h2, layout.capacity());
|
|
2101
|
-
if (TransferUsesMemcpy && had_soo_slot_) {
|
|
2102
|
-
TransferSlotAfterSoo(c, SizeOfSlot);
|
|
2103
|
-
}
|
|
2104
|
-
// SooEnabled implies that old_capacity_ != 0.
|
|
2105
|
-
} else if ((SooEnabled || old_capacity_ != 0) && grow_single_group) {
|
|
2106
|
-
if (TransferUsesMemcpy) {
|
|
2107
|
-
GrowSizeIntoSingleGroupTransferable(c, SizeOfSlot);
|
|
2108
|
-
DeallocateOld<AlignOfSlot>(alloc, SizeOfSlot);
|
|
2109
|
-
} else {
|
|
2110
|
-
GrowIntoSingleGroupShuffleControlBytes(c.control(), layout.capacity());
|
|
2111
|
-
}
|
|
2112
|
-
} else {
|
|
2113
|
-
ResetCtrl(c, SizeOfSlot);
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
c.set_has_infoz(has_infoz);
|
|
2117
|
-
if (has_infoz) {
|
|
2118
|
-
infoz.RecordStorageChanged(c.size(), layout.capacity());
|
|
2119
|
-
if ((SooEnabled && was_soo_) || grow_single_group || old_capacity_ == 0) {
|
|
2120
|
-
infoz.RecordRehash(0);
|
|
2121
|
-
}
|
|
2122
|
-
c.set_infoz(infoz);
|
|
2123
|
-
}
|
|
2124
|
-
return grow_single_group;
|
|
2125
|
-
}
|
|
2126
|
-
|
|
2127
|
-
// Relocates slots into new single group consistent with
|
|
2128
|
-
// GrowIntoSingleGroupShuffleControlBytes.
|
|
2129
|
-
//
|
|
2130
|
-
// PRECONDITIONS:
|
|
2131
|
-
// 1. GrowIntoSingleGroupShuffleControlBytes was already called.
|
|
2132
|
-
template <class PolicyTraits, class Alloc>
|
|
2133
|
-
void GrowSizeIntoSingleGroup(CommonFields& c, Alloc& alloc_ref) {
|
|
2134
|
-
ABSL_SWISSTABLE_ASSERT(old_capacity_ < Group::kWidth / 2);
|
|
2135
|
-
ABSL_SWISSTABLE_ASSERT(
|
|
2136
|
-
IsGrowingIntoSingleGroupApplicable(old_capacity_, c.capacity()));
|
|
2137
|
-
using slot_type = typename PolicyTraits::slot_type;
|
|
2138
|
-
ABSL_SWISSTABLE_ASSERT(is_single_group(c.capacity()));
|
|
2139
|
-
|
|
2140
|
-
auto* new_slots = static_cast<slot_type*>(c.slot_array()) + 1;
|
|
2141
|
-
auto* old_slots_ptr = static_cast<slot_type*>(old_slots());
|
|
2142
|
-
auto* old_ctrl_ptr = old_ctrl();
|
|
2143
|
-
|
|
2144
|
-
for (size_t i = 0; i < old_capacity_; ++i, ++new_slots) {
|
|
2145
|
-
if (IsFull(old_ctrl_ptr[i])) {
|
|
2146
|
-
SanitizerUnpoisonMemoryRegion(new_slots, sizeof(slot_type));
|
|
2147
|
-
PolicyTraits::transfer(&alloc_ref, new_slots, old_slots_ptr + i);
|
|
2148
|
-
}
|
|
2149
|
-
}
|
|
2150
|
-
PoisonSingleGroupEmptySlots(c, sizeof(slot_type));
|
|
2151
|
-
}
|
|
2152
|
-
|
|
2153
|
-
// Deallocates old backing array.
|
|
2154
|
-
template <size_t AlignOfSlot, class CharAlloc>
|
|
2155
|
-
void DeallocateOld(CharAlloc alloc_ref, size_t slot_size) {
|
|
2156
|
-
SanitizerUnpoisonMemoryRegion(old_slots(), slot_size * old_capacity_);
|
|
2157
|
-
auto layout = RawHashSetLayout(old_capacity_, AlignOfSlot, had_infoz_);
|
|
2158
|
-
Deallocate<BackingArrayAlignment(AlignOfSlot)>(
|
|
2159
|
-
&alloc_ref, old_ctrl() - layout.control_offset(),
|
|
2160
|
-
layout.alloc_size(slot_size));
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
private:
|
|
2164
|
-
// Returns true if `GrowSizeIntoSingleGroup` can be used for resizing.
|
|
2165
|
-
static bool IsGrowingIntoSingleGroupApplicable(size_t old_capacity,
|
|
2166
|
-
size_t new_capacity) {
|
|
2167
|
-
// NOTE that `old_capacity < new_capacity` in order to have
|
|
2168
|
-
// `old_capacity < Group::kWidth / 2` to make faster copies of 8 bytes.
|
|
2169
|
-
return is_single_group(new_capacity) && old_capacity < new_capacity;
|
|
2170
|
-
}
|
|
2171
|
-
|
|
2172
|
-
// Relocates control bytes and slots into new single group for
|
|
2173
|
-
// transferable objects.
|
|
2174
|
-
// Must be called only if IsGrowingIntoSingleGroupApplicable returned true.
|
|
2175
|
-
void GrowSizeIntoSingleGroupTransferable(CommonFields& c, size_t slot_size);
|
|
2176
|
-
|
|
2177
|
-
// If there was an SOO slot and slots are transferable, transfers the SOO slot
|
|
2178
|
-
// into the new heap allocation. Must be called only if
|
|
2179
|
-
// IsGrowingIntoSingleGroupApplicable returned true.
|
|
2180
|
-
void TransferSlotAfterSoo(CommonFields& c, size_t slot_size);
|
|
2181
|
-
|
|
2182
|
-
// Shuffle control bits deterministically to the next capacity.
|
|
2183
|
-
// Returns offset for newly added element with given hash.
|
|
2184
|
-
//
|
|
2185
|
-
// PRECONDITIONs:
|
|
2186
|
-
// 1. new_ctrl is allocated for new_capacity,
|
|
2187
|
-
// but not initialized.
|
|
2188
|
-
// 2. new_capacity is a single group.
|
|
2189
|
-
// 3. old_capacity > 0.
|
|
2190
|
-
//
|
|
2191
|
-
// All elements are transferred into the first `old_capacity + 1` positions
|
|
2192
|
-
// of the new_ctrl. Elements are shifted by 1 in order to keep a space at the
|
|
2193
|
-
// beginning for the new element.
|
|
2194
|
-
// Position of the new added element will be based on `H1` and is not
|
|
2195
|
-
// deterministic.
|
|
2196
|
-
//
|
|
2197
|
-
// Examples:
|
|
2198
|
-
// S = kSentinel, E = kEmpty
|
|
2199
|
-
//
|
|
2200
|
-
// old_ctrl = 0SEEEEEEE...
|
|
2201
|
-
// new_ctrl = E0ESE0EEE...
|
|
2202
|
-
//
|
|
2203
|
-
// old_ctrl = 012S012EEEEEEEEE...
|
|
2204
|
-
// new_ctrl = E012EEESE012EEE...
|
|
2205
|
-
//
|
|
2206
|
-
// old_ctrl = 0123456S0123456EEEEEEEEEEE...
|
|
2207
|
-
// new_ctrl = E0123456EEEEEESE0123456EEE...
|
|
2208
|
-
void GrowIntoSingleGroupShuffleControlBytes(ctrl_t* new_ctrl,
|
|
2209
|
-
size_t new_capacity) const;
|
|
2210
|
-
|
|
2211
|
-
// If the table was SOO, initializes new control bytes. `h2` is the control
|
|
2212
|
-
// byte corresponding to the full slot. Must be called only if
|
|
2213
|
-
// IsGrowingIntoSingleGroupApplicable returned true.
|
|
2214
|
-
// Requires: `had_soo_slot_ || h2 == ctrl_t::kEmpty`.
|
|
2215
|
-
void InitControlBytesAfterSoo(ctrl_t* new_ctrl, ctrl_t h2,
|
|
2216
|
-
size_t new_capacity);
|
|
2217
|
-
|
|
2218
|
-
// Shuffle trivially transferable slots in the way consistent with
|
|
2219
|
-
// GrowIntoSingleGroupShuffleControlBytes.
|
|
2220
|
-
//
|
|
2221
|
-
// PRECONDITIONs:
|
|
2222
|
-
// 1. old_capacity must be non-zero.
|
|
2223
|
-
// 2. new_ctrl is fully initialized using
|
|
2224
|
-
// GrowIntoSingleGroupShuffleControlBytes.
|
|
2225
|
-
// 3. new_slots is allocated and *not* poisoned.
|
|
2226
|
-
//
|
|
2227
|
-
// POSTCONDITIONS:
|
|
2228
|
-
// 1. new_slots are transferred from old_slots_ consistent with
|
|
2229
|
-
// GrowIntoSingleGroupShuffleControlBytes.
|
|
2230
|
-
// 2. Empty new_slots are *not* poisoned.
|
|
2231
|
-
void GrowIntoSingleGroupShuffleTransferableSlots(void* new_slots,
|
|
2232
|
-
size_t slot_size) const;
|
|
2233
|
-
|
|
2234
|
-
// Poison empty slots that were transferred using the deterministic algorithm
|
|
2235
|
-
// described above.
|
|
2236
|
-
// PRECONDITIONs:
|
|
2237
|
-
// 1. new_ctrl is fully initialized using
|
|
2238
|
-
// GrowIntoSingleGroupShuffleControlBytes.
|
|
2239
|
-
// 2. new_slots is fully initialized consistent with
|
|
2240
|
-
// GrowIntoSingleGroupShuffleControlBytes.
|
|
2241
|
-
void PoisonSingleGroupEmptySlots(CommonFields& c, size_t slot_size) const {
|
|
2242
|
-
// poison non full items
|
|
2243
|
-
for (size_t i = 0; i < c.capacity(); ++i) {
|
|
2244
|
-
if (!IsFull(c.control()[i])) {
|
|
2245
|
-
SanitizerPoisonMemoryRegion(SlotAddress(c.slot_array(), i, slot_size),
|
|
2246
|
-
slot_size);
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
|
-
}
|
|
2250
|
-
|
|
2251
|
-
HeapOrSoo old_heap_or_soo_;
|
|
2252
|
-
size_t old_capacity_;
|
|
2253
|
-
bool had_infoz_;
|
|
2254
|
-
bool was_soo_;
|
|
2255
|
-
bool had_soo_slot_;
|
|
2256
|
-
// Either null infoz or a pre-sampled forced infoz for SOO tables.
|
|
2257
|
-
HashtablezInfoHandle forced_infoz_;
|
|
2258
|
-
};
|
|
2259
|
-
|
|
2260
|
-
inline void PrepareInsertCommon(CommonFields& common) {
|
|
2261
|
-
common.increment_size();
|
|
2262
|
-
common.maybe_increment_generation_on_insert();
|
|
1575
|
+
template <size_t AlignOfBackingArray, typename Alloc>
|
|
1576
|
+
ABSL_ATTRIBUTE_NOINLINE void DeallocateBackingArray(
|
|
1577
|
+
void* alloc, size_t capacity, ctrl_t* ctrl, size_t slot_size,
|
|
1578
|
+
size_t slot_align, bool had_infoz) {
|
|
1579
|
+
RawHashSetLayout layout(capacity, slot_size, slot_align, had_infoz);
|
|
1580
|
+
void* backing_array = ctrl - layout.control_offset();
|
|
1581
|
+
// Unpoison before returning the memory to the allocator.
|
|
1582
|
+
SanitizerUnpoisonMemoryRegion(backing_array, layout.alloc_size());
|
|
1583
|
+
Deallocate<AlignOfBackingArray>(static_cast<Alloc*>(alloc), backing_array,
|
|
1584
|
+
layout.alloc_size());
|
|
2263
1585
|
}
|
|
2264
1586
|
|
|
2265
|
-
// Like prepare_insert, but for the case of inserting into a full SOO table.
|
|
2266
|
-
size_t PrepareInsertAfterSoo(size_t hash, size_t slot_size,
|
|
2267
|
-
CommonFields& common);
|
|
2268
|
-
|
|
2269
1587
|
// PolicyFunctions bundles together some information for a particular
|
|
2270
1588
|
// raw_hash_set<T, ...> instantiation. This information is passed to
|
|
2271
1589
|
// type-erased functions that want to do small amounts of type-specific
|
|
2272
1590
|
// work.
|
|
2273
1591
|
struct PolicyFunctions {
|
|
2274
|
-
|
|
1592
|
+
uint32_t key_size;
|
|
1593
|
+
uint32_t value_size;
|
|
1594
|
+
uint32_t slot_size;
|
|
1595
|
+
uint16_t slot_align;
|
|
1596
|
+
bool soo_enabled;
|
|
1597
|
+
bool is_hashtablez_eligible;
|
|
2275
1598
|
|
|
2276
1599
|
// Returns the pointer to the hash function stored in the set.
|
|
2277
|
-
|
|
1600
|
+
void* (*hash_fn)(CommonFields& common);
|
|
2278
1601
|
|
|
2279
1602
|
// Returns the hash of the pointed-to slot.
|
|
2280
|
-
|
|
1603
|
+
HashSlotFn hash_slot;
|
|
2281
1604
|
|
|
2282
|
-
// Transfers the contents of src_slot to dst_slot.
|
|
2283
|
-
|
|
1605
|
+
// Transfers the contents of `count` slots from src_slot to dst_slot.
|
|
1606
|
+
// We use ability to transfer several slots in single group table growth.
|
|
1607
|
+
void (*transfer_n)(void* set, void* dst_slot, void* src_slot, size_t count);
|
|
2284
1608
|
|
|
2285
|
-
//
|
|
2286
|
-
void (*
|
|
1609
|
+
// Returns the pointer to the CharAlloc stored in the set.
|
|
1610
|
+
void* (*get_char_alloc)(CommonFields& common);
|
|
2287
1611
|
|
|
2288
|
-
//
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
1612
|
+
// Allocates n bytes for the backing store for common.
|
|
1613
|
+
void* (*alloc)(void* alloc, size_t n);
|
|
1614
|
+
|
|
1615
|
+
// Deallocates the backing store from common.
|
|
1616
|
+
void (*dealloc)(void* alloc, size_t capacity, ctrl_t* ctrl, size_t slot_size,
|
|
1617
|
+
size_t slot_align, bool had_infoz);
|
|
1618
|
+
|
|
1619
|
+
// Implementation detail of GrowToNextCapacity.
|
|
1620
|
+
// Iterates over all full slots and transfers unprobed elements.
|
|
1621
|
+
// Initializes the new control bytes except mirrored bytes and kSentinel.
|
|
1622
|
+
// Caller must finish the initialization.
|
|
1623
|
+
// All slots corresponding to the full control bytes are transferred.
|
|
1624
|
+
// Probed elements are reported by `encode_probed_element` callback.
|
|
1625
|
+
// encode_probed_element may overwrite old_ctrl buffer till source_offset.
|
|
1626
|
+
// Different encoding is used depending on the capacity of the table.
|
|
1627
|
+
// See ProbedItem*Bytes classes for details.
|
|
1628
|
+
void (*transfer_unprobed_elements_to_next_capacity)(
|
|
1629
|
+
CommonFields& common, const ctrl_t* old_ctrl, void* old_slots,
|
|
1630
|
+
// TODO(b/382423690): Try to use absl::FunctionRef here.
|
|
1631
|
+
void* probed_storage,
|
|
1632
|
+
void (*encode_probed_element)(void* probed_storage, h2_t h2,
|
|
1633
|
+
size_t source_offset, size_t h1));
|
|
1634
|
+
|
|
1635
|
+
uint8_t soo_capacity() const {
|
|
1636
|
+
return static_cast<uint8_t>(soo_enabled ? SooCapacity() : 0);
|
|
1637
|
+
}
|
|
2292
1638
|
};
|
|
2293
1639
|
|
|
1640
|
+
// Returns the maximum valid size for a table with 1-byte slots.
|
|
1641
|
+
// This function is an utility shared by MaxValidSize and IsAboveValidSize.
|
|
1642
|
+
// Template parameter is only used to enable testing.
|
|
1643
|
+
template <size_t kSizeOfSizeT = sizeof(size_t)>
|
|
1644
|
+
constexpr size_t MaxValidSizeFor1ByteSlot() {
|
|
1645
|
+
if constexpr (kSizeOfSizeT == 8) {
|
|
1646
|
+
return CapacityToGrowth(
|
|
1647
|
+
static_cast<size_t>(uint64_t{1} << HashtableSize::kSizeBitCount) - 1);
|
|
1648
|
+
} else {
|
|
1649
|
+
static_assert(kSizeOfSizeT == 4);
|
|
1650
|
+
return CapacityToGrowth((size_t{1} << (kSizeOfSizeT * 8 - 2)) - 1);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
// Returns the maximum valid size for a table with provided slot size.
|
|
1655
|
+
// Template parameter is only used to enable testing.
|
|
1656
|
+
template <size_t kSizeOfSizeT = sizeof(size_t)>
|
|
1657
|
+
constexpr size_t MaxValidSize(size_t slot_size) {
|
|
1658
|
+
if constexpr (kSizeOfSizeT == 8) {
|
|
1659
|
+
// For small slot sizes we are limited by HashtableSize::kSizeBitCount.
|
|
1660
|
+
if (slot_size < size_t{1} << (64 - HashtableSize::kSizeBitCount)) {
|
|
1661
|
+
return MaxValidSizeFor1ByteSlot<kSizeOfSizeT>();
|
|
1662
|
+
}
|
|
1663
|
+
return (size_t{1} << (kSizeOfSizeT * 8 - 2)) / slot_size;
|
|
1664
|
+
} else {
|
|
1665
|
+
return MaxValidSizeFor1ByteSlot<kSizeOfSizeT>() / slot_size;
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
// Returns true if size is larger than the maximum valid size.
|
|
1670
|
+
// It is an optimization to avoid the division operation in the common case.
|
|
1671
|
+
// Template parameter is only used to enable testing.
|
|
1672
|
+
template <size_t kSizeOfSizeT = sizeof(size_t)>
|
|
1673
|
+
constexpr bool IsAboveValidSize(size_t size, size_t slot_size) {
|
|
1674
|
+
if constexpr (kSizeOfSizeT == 8) {
|
|
1675
|
+
// For small slot sizes we are limited by HashtableSize::kSizeBitCount.
|
|
1676
|
+
if (ABSL_PREDICT_TRUE(slot_size <
|
|
1677
|
+
(size_t{1} << (64 - HashtableSize::kSizeBitCount)))) {
|
|
1678
|
+
return size > MaxValidSizeFor1ByteSlot<kSizeOfSizeT>();
|
|
1679
|
+
}
|
|
1680
|
+
return size > MaxValidSize<kSizeOfSizeT>(slot_size);
|
|
1681
|
+
} else {
|
|
1682
|
+
return uint64_t{size} * slot_size >
|
|
1683
|
+
MaxValidSizeFor1ByteSlot<kSizeOfSizeT>();
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
// Returns the index of the SOO slot when growing from SOO to non-SOO in a
|
|
1688
|
+
// single group. See also InitializeSmallControlBytesAfterSoo(). It's important
|
|
1689
|
+
// to use index 1 so that when resizing from capacity 1 to 3, we can still have
|
|
1690
|
+
// random iteration order between the first two inserted elements.
|
|
1691
|
+
// I.e. it allows inserting the second element at either index 0 or 2.
|
|
1692
|
+
constexpr size_t SooSlotIndex() { return 1; }
|
|
1693
|
+
|
|
1694
|
+
// Maximum capacity for the algorithm for small table after SOO.
|
|
1695
|
+
// Note that typical size after SOO is 3, but we allow up to 7.
|
|
1696
|
+
// Allowing till 16 would require additional store that can be avoided.
|
|
1697
|
+
constexpr size_t MaxSmallAfterSooCapacity() { return 7; }
|
|
1698
|
+
|
|
1699
|
+
// Type erased version of raw_hash_set::reserve.
|
|
1700
|
+
// Requires: `new_size > policy.soo_capacity`.
|
|
1701
|
+
void ReserveTableToFitNewSize(CommonFields& common,
|
|
1702
|
+
const PolicyFunctions& policy, size_t new_size);
|
|
1703
|
+
|
|
1704
|
+
// Resizes empty non-allocated table to the next valid capacity after
|
|
1705
|
+
// `bucket_count`. Requires:
|
|
1706
|
+
// 1. `c.capacity() == policy.soo_capacity`.
|
|
1707
|
+
// 2. `c.empty()`.
|
|
1708
|
+
// 3. `new_size > policy.soo_capacity`.
|
|
1709
|
+
// The table will be attempted to be sampled.
|
|
1710
|
+
void ReserveEmptyNonAllocatedTableToFitBucketCount(
|
|
1711
|
+
CommonFields& common, const PolicyFunctions& policy, size_t bucket_count);
|
|
1712
|
+
|
|
1713
|
+
// Type erased version of raw_hash_set::rehash.
|
|
1714
|
+
void Rehash(CommonFields& common, const PolicyFunctions& policy, size_t n);
|
|
1715
|
+
|
|
1716
|
+
// Type erased version of copy constructor.
|
|
1717
|
+
void Copy(CommonFields& common, const PolicyFunctions& policy,
|
|
1718
|
+
const CommonFields& other,
|
|
1719
|
+
absl::FunctionRef<void(void*, const void*)> copy_fn);
|
|
1720
|
+
|
|
1721
|
+
// Returns the optimal size for memcpy when transferring SOO slot.
|
|
1722
|
+
// Otherwise, returns the optimal size for memcpy SOO slot transfer
|
|
1723
|
+
// to SooSlotIndex().
|
|
1724
|
+
// At the destination we are allowed to copy upto twice more bytes,
|
|
1725
|
+
// because there is at least one more slot after SooSlotIndex().
|
|
1726
|
+
// The result must not exceed MaxSooSlotSize().
|
|
1727
|
+
// Some of the cases are merged to minimize the number of function
|
|
1728
|
+
// instantiations.
|
|
1729
|
+
constexpr size_t OptimalMemcpySizeForSooSlotTransfer(
|
|
1730
|
+
size_t slot_size, size_t max_soo_slot_size = MaxSooSlotSize()) {
|
|
1731
|
+
static_assert(MaxSooSlotSize() >= 8, "unexpectedly small SOO slot size");
|
|
1732
|
+
if (slot_size == 1) {
|
|
1733
|
+
return 1;
|
|
1734
|
+
}
|
|
1735
|
+
if (slot_size <= 3) {
|
|
1736
|
+
return 4;
|
|
1737
|
+
}
|
|
1738
|
+
// We are merging 4 and 8 into one case because we expect them to be the
|
|
1739
|
+
// hottest cases. Copying 8 bytes is as fast on common architectures.
|
|
1740
|
+
if (slot_size <= 8) {
|
|
1741
|
+
return 8;
|
|
1742
|
+
}
|
|
1743
|
+
if (max_soo_slot_size <= 16) {
|
|
1744
|
+
return max_soo_slot_size;
|
|
1745
|
+
}
|
|
1746
|
+
if (slot_size <= 16) {
|
|
1747
|
+
return 16;
|
|
1748
|
+
}
|
|
1749
|
+
if (max_soo_slot_size <= 24) {
|
|
1750
|
+
return max_soo_slot_size;
|
|
1751
|
+
}
|
|
1752
|
+
static_assert(MaxSooSlotSize() <= 24, "unexpectedly large SOO slot size");
|
|
1753
|
+
return 24;
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
// Resizes SOO table to the NextCapacity(SooCapacity()) and prepares insert for
|
|
1757
|
+
// the given new_hash. Returns the offset of the new element.
|
|
1758
|
+
// All possible template combinations are defined in cc file to improve
|
|
1759
|
+
// compilation time.
|
|
1760
|
+
template <size_t SooSlotMemcpySize, bool TransferUsesMemcpy>
|
|
1761
|
+
size_t GrowSooTableToNextCapacityAndPrepareInsert(
|
|
1762
|
+
CommonFields& common, const PolicyFunctions& policy,
|
|
1763
|
+
absl::FunctionRef<size_t(size_t)> get_hash, bool force_sampling);
|
|
1764
|
+
|
|
1765
|
+
// PrepareInsert for small tables (is_small()==true).
|
|
1766
|
+
// Returns the new control and the new slot.
|
|
1767
|
+
// Hash is only computed if the table is sampled or grew to large size
|
|
1768
|
+
// (is_small()==false).
|
|
1769
|
+
std::pair<ctrl_t*, void*> PrepareInsertSmallNonSoo(
|
|
1770
|
+
CommonFields& common, const PolicyFunctions& policy,
|
|
1771
|
+
absl::FunctionRef<size_t(size_t)> get_hash);
|
|
1772
|
+
|
|
1773
|
+
// Resizes table with allocated slots and change the table seed.
|
|
1774
|
+
// Tables with SOO enabled must have capacity > policy.soo_capacity.
|
|
1775
|
+
// No sampling will be performed since table is already allocated.
|
|
1776
|
+
void ResizeAllocatedTableWithSeedChange(CommonFields& common,
|
|
1777
|
+
const PolicyFunctions& policy,
|
|
1778
|
+
size_t new_capacity);
|
|
1779
|
+
|
|
2294
1780
|
// ClearBackingArray clears the backing array, either modifying it in place,
|
|
2295
1781
|
// or creating a new one based on the value of "reuse".
|
|
2296
1782
|
// REQUIRES: c.capacity > 0
|
|
2297
1783
|
void ClearBackingArray(CommonFields& c, const PolicyFunctions& policy,
|
|
2298
|
-
bool reuse, bool soo_enabled);
|
|
2299
|
-
|
|
2300
|
-
// Type-erased
|
|
2301
|
-
void
|
|
2302
|
-
|
|
2303
|
-
// Function to place in PolicyFunctions::dealloc for raw_hash_sets
|
|
2304
|
-
// that are using std::allocator. This allows us to share the same
|
|
2305
|
-
// function body for raw_hash_set instantiations that have the
|
|
2306
|
-
// same slot alignment.
|
|
2307
|
-
template <size_t AlignOfSlot>
|
|
2308
|
-
ABSL_ATTRIBUTE_NOINLINE void DeallocateStandard(CommonFields& common,
|
|
2309
|
-
const PolicyFunctions& policy) {
|
|
2310
|
-
// Unpoison before returning the memory to the allocator.
|
|
2311
|
-
SanitizerUnpoisonMemoryRegion(common.slot_array(),
|
|
2312
|
-
policy.slot_size * common.capacity());
|
|
2313
|
-
|
|
2314
|
-
std::allocator<char> alloc;
|
|
2315
|
-
common.infoz().Unregister();
|
|
2316
|
-
Deallocate<BackingArrayAlignment(AlignOfSlot)>(
|
|
2317
|
-
&alloc, common.backing_array_start(),
|
|
2318
|
-
common.alloc_size(policy.slot_size, AlignOfSlot));
|
|
2319
|
-
}
|
|
1784
|
+
void* alloc, bool reuse, bool soo_enabled);
|
|
1785
|
+
|
|
1786
|
+
// Type-erased versions of raw_hash_set::erase_meta_only_{small,large}.
|
|
1787
|
+
void EraseMetaOnlySmall(CommonFields& c, bool soo_enabled, size_t slot_size);
|
|
1788
|
+
void EraseMetaOnlyLarge(CommonFields& c, const ctrl_t* ctrl, size_t slot_size);
|
|
2320
1789
|
|
|
2321
1790
|
// For trivially relocatable types we use memcpy directly. This allows us to
|
|
2322
1791
|
// share the same function body for raw_hash_set instantiations that have the
|
|
2323
1792
|
// same slot size as long as they are relocatable.
|
|
1793
|
+
// Separate function for relocating single slot cause significant binary bloat.
|
|
2324
1794
|
template <size_t SizeOfSlot>
|
|
2325
|
-
ABSL_ATTRIBUTE_NOINLINE void
|
|
2326
|
-
|
|
1795
|
+
ABSL_ATTRIBUTE_NOINLINE void TransferNRelocatable(void*, void* dst, void* src,
|
|
1796
|
+
size_t count) {
|
|
1797
|
+
// TODO(b/382423690): Experiment with making specialization for power of 2 and
|
|
1798
|
+
// non power of 2. This would require passing the size of the slot.
|
|
1799
|
+
memcpy(dst, src, SizeOfSlot * count);
|
|
2327
1800
|
}
|
|
2328
1801
|
|
|
2329
|
-
//
|
|
2330
|
-
|
|
1802
|
+
// Returns a pointer to `common`. This is used to implement type erased
|
|
1803
|
+
// raw_hash_set::get_hash_ref_fn and raw_hash_set::get_alloc_ref_fn for the
|
|
1804
|
+
// empty class cases.
|
|
1805
|
+
void* GetRefForEmptyClass(CommonFields& common);
|
|
2331
1806
|
|
|
2332
1807
|
// Given the hash of a value not currently in the table and the first empty
|
|
2333
1808
|
// slot in the probe sequence, finds a viable slot index to insert it at.
|
|
@@ -2341,11 +1816,17 @@ const void* GetHashRefForEmptyHasher(const CommonFields& common);
|
|
|
2341
1816
|
// When the table has deleted slots (according to GrowthInfo), the target
|
|
2342
1817
|
// position will be searched one more time using `find_first_non_full`.
|
|
2343
1818
|
//
|
|
2344
|
-
// REQUIRES:
|
|
1819
|
+
// REQUIRES: `!common.is_small()`.
|
|
2345
1820
|
// REQUIRES: At least one non-full slot available.
|
|
2346
1821
|
// REQUIRES: `target` is a valid empty position to insert.
|
|
2347
|
-
size_t
|
|
2348
|
-
|
|
1822
|
+
size_t PrepareInsertLarge(CommonFields& common, const PolicyFunctions& policy,
|
|
1823
|
+
size_t hash, FindInfo target);
|
|
1824
|
+
|
|
1825
|
+
// Same as above, but with generations enabled, we may end up changing the seed,
|
|
1826
|
+
// which means we need to be able to recompute the hash.
|
|
1827
|
+
size_t PrepareInsertLargeGenerationsEnabled(
|
|
1828
|
+
CommonFields& common, const PolicyFunctions& policy, size_t hash,
|
|
1829
|
+
FindInfo target, absl::FunctionRef<size_t(size_t)> recompute_hash);
|
|
2349
1830
|
|
|
2350
1831
|
// A SwissTable.
|
|
2351
1832
|
//
|
|
@@ -2376,9 +1857,6 @@ class raw_hash_set {
|
|
|
2376
1857
|
public:
|
|
2377
1858
|
using init_type = typename PolicyTraits::init_type;
|
|
2378
1859
|
using key_type = typename PolicyTraits::key_type;
|
|
2379
|
-
// TODO(sbenza): Hide slot_type as it is an implementation detail. Needs user
|
|
2380
|
-
// code fixes!
|
|
2381
|
-
using slot_type = typename PolicyTraits::slot_type;
|
|
2382
1860
|
using allocator_type = Alloc;
|
|
2383
1861
|
using size_type = size_t;
|
|
2384
1862
|
using difference_type = ptrdiff_t;
|
|
@@ -2393,6 +1871,7 @@ class raw_hash_set {
|
|
|
2393
1871
|
using const_pointer = typename absl::allocator_traits<
|
|
2394
1872
|
allocator_type>::template rebind_traits<value_type>::const_pointer;
|
|
2395
1873
|
|
|
1874
|
+
private:
|
|
2396
1875
|
// Alias used for heterogeneous lookup functions.
|
|
2397
1876
|
// `key_arg<K>` evaluates to `K` when the functors are transparent and to
|
|
2398
1877
|
// `key_type` otherwise. It permits template argument deduction on `K` for the
|
|
@@ -2400,7 +1879,12 @@ class raw_hash_set {
|
|
|
2400
1879
|
template <class K>
|
|
2401
1880
|
using key_arg = typename KeyArgImpl::template type<K, key_type>;
|
|
2402
1881
|
|
|
2403
|
-
|
|
1882
|
+
using slot_type = typename PolicyTraits::slot_type;
|
|
1883
|
+
|
|
1884
|
+
constexpr static bool kIsDefaultHash =
|
|
1885
|
+
std::is_same_v<hasher, absl::Hash<key_type>> ||
|
|
1886
|
+
std::is_same_v<hasher, absl::container_internal::StringHash>;
|
|
1887
|
+
|
|
2404
1888
|
// TODO(b/289225379): we could add extra SOO space inside raw_hash_set
|
|
2405
1889
|
// after CommonFields to allow inlining larger slot_types (e.g. std::string),
|
|
2406
1890
|
// but it's a bit complicated if we want to support incomplete mapped_type in
|
|
@@ -2425,10 +1909,19 @@ class raw_hash_set {
|
|
|
2425
1909
|
bool is_soo() const { return fits_in_soo(capacity()); }
|
|
2426
1910
|
bool is_full_soo() const { return is_soo() && !empty(); }
|
|
2427
1911
|
|
|
1912
|
+
bool is_small() const { return common().is_small(); }
|
|
1913
|
+
|
|
2428
1914
|
// Give an early error when key_type is not hashable/eq.
|
|
2429
1915
|
auto KeyTypeCanBeHashed(const Hash& h, const key_type& k) -> decltype(h(k));
|
|
2430
1916
|
auto KeyTypeCanBeEq(const Eq& eq, const key_type& k) -> decltype(eq(k, k));
|
|
2431
1917
|
|
|
1918
|
+
// Try to be helpful when the hasher returns an unreasonable type.
|
|
1919
|
+
using key_hash_result =
|
|
1920
|
+
absl::remove_cvref_t<decltype(std::declval<const Hash&>()(
|
|
1921
|
+
std::declval<const key_type&>()))>;
|
|
1922
|
+
static_assert(sizeof(key_hash_result) >= sizeof(size_t),
|
|
1923
|
+
"`Hash::operator()` should return a `size_t`");
|
|
1924
|
+
|
|
2432
1925
|
using AllocTraits = absl::allocator_traits<allocator_type>;
|
|
2433
1926
|
using SlotAlloc = typename absl::allocator_traits<
|
|
2434
1927
|
allocator_type>::template rebind_alloc<slot_type>;
|
|
@@ -2495,19 +1988,19 @@ class raw_hash_set {
|
|
|
2495
1988
|
|
|
2496
1989
|
// PRECONDITION: not an end() iterator.
|
|
2497
1990
|
reference operator*() const {
|
|
2498
|
-
|
|
1991
|
+
assert_is_full("operator*()");
|
|
2499
1992
|
return unchecked_deref();
|
|
2500
1993
|
}
|
|
2501
1994
|
|
|
2502
1995
|
// PRECONDITION: not an end() iterator.
|
|
2503
1996
|
pointer operator->() const {
|
|
2504
|
-
|
|
1997
|
+
assert_is_full("operator->");
|
|
2505
1998
|
return &operator*();
|
|
2506
1999
|
}
|
|
2507
2000
|
|
|
2508
2001
|
// PRECONDITION: not an end() iterator.
|
|
2509
2002
|
iterator& operator++() {
|
|
2510
|
-
|
|
2003
|
+
assert_is_full("operator++");
|
|
2511
2004
|
++ctrl_;
|
|
2512
2005
|
++slot_;
|
|
2513
2006
|
skip_empty_or_deleted();
|
|
@@ -2545,7 +2038,7 @@ class raw_hash_set {
|
|
|
2545
2038
|
// This constructor is used in begin() to avoid an MSan
|
|
2546
2039
|
// use-of-uninitialized-value error. Delegating from this constructor to
|
|
2547
2040
|
// the previous one doesn't avoid the error.
|
|
2548
|
-
iterator(ctrl_t* ctrl, MaybeInitializedPtr slot,
|
|
2041
|
+
iterator(ctrl_t* ctrl, MaybeInitializedPtr<void> slot,
|
|
2549
2042
|
const GenerationType* generation_ptr)
|
|
2550
2043
|
: HashSetIteratorGenerationInfo(generation_ptr),
|
|
2551
2044
|
ctrl_(ctrl),
|
|
@@ -2558,38 +2051,42 @@ class raw_hash_set {
|
|
|
2558
2051
|
explicit iterator(const GenerationType* generation_ptr)
|
|
2559
2052
|
: HashSetIteratorGenerationInfo(generation_ptr), ctrl_(nullptr) {}
|
|
2560
2053
|
|
|
2054
|
+
void assert_is_full(const char* operation) const {
|
|
2055
|
+
AssertIsFull(ctrl_, generation(), generation_ptr(), operation);
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2561
2058
|
// Fixes up `ctrl_` to point to a full or sentinel by advancing `ctrl_` and
|
|
2562
2059
|
// `slot_` until they reach one.
|
|
2563
2060
|
void skip_empty_or_deleted() {
|
|
2564
2061
|
while (IsEmptyOrDeleted(*ctrl_)) {
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
ctrl_ += shift;
|
|
2568
|
-
slot_ += shift;
|
|
2062
|
+
++ctrl_;
|
|
2063
|
+
++slot_;
|
|
2569
2064
|
}
|
|
2570
2065
|
}
|
|
2571
2066
|
|
|
2572
|
-
ctrl_t* control() const { return ctrl_; }
|
|
2573
|
-
slot_type* slot() const { return slot_; }
|
|
2574
|
-
|
|
2575
|
-
// We use EmptyGroup() for default-constructed iterators so that they can
|
|
2576
|
-
// be distinguished from end iterators, which have nullptr ctrl_.
|
|
2577
|
-
ctrl_t* ctrl_ = EmptyGroup();
|
|
2578
|
-
// To avoid uninitialized member warnings, put slot_ in an anonymous union.
|
|
2579
|
-
// The member is not initialized on singleton and end iterators.
|
|
2580
|
-
union {
|
|
2581
|
-
slot_type* slot_;
|
|
2582
|
-
};
|
|
2583
|
-
|
|
2584
2067
|
// An equality check which skips ABSL Hardening iterator invalidation
|
|
2585
2068
|
// checks.
|
|
2586
2069
|
// Should be used when the lifetimes of the iterators are well-enough
|
|
2587
2070
|
// understood to prove that they cannot be invalid.
|
|
2588
|
-
bool unchecked_equals(const iterator& b) {
|
|
2071
|
+
bool unchecked_equals(const iterator& b) const {
|
|
2072
|
+
return ctrl_ == b.control();
|
|
2073
|
+
}
|
|
2589
2074
|
|
|
2590
2075
|
// Dereferences the iterator without ABSL Hardening iterator invalidation
|
|
2591
2076
|
// checks.
|
|
2592
2077
|
reference unchecked_deref() const { return PolicyTraits::element(slot_); }
|
|
2078
|
+
|
|
2079
|
+
ctrl_t* control() const { return ctrl_; }
|
|
2080
|
+
slot_type* slot() const { return slot_; }
|
|
2081
|
+
|
|
2082
|
+
// We use DefaultIterControl() for default-constructed iterators so that
|
|
2083
|
+
// they can be distinguished from end iterators, which have nullptr ctrl_.
|
|
2084
|
+
ctrl_t* ctrl_ = DefaultIterControl();
|
|
2085
|
+
// To avoid uninitialized member warnings, put slot_ in an anonymous union.
|
|
2086
|
+
// The member is not initialized on singleton and end iterators.
|
|
2087
|
+
union {
|
|
2088
|
+
slot_type* slot_;
|
|
2089
|
+
};
|
|
2593
2090
|
};
|
|
2594
2091
|
|
|
2595
2092
|
class const_iterator {
|
|
@@ -2630,14 +2127,13 @@ class raw_hash_set {
|
|
|
2630
2127
|
const GenerationType* gen)
|
|
2631
2128
|
: inner_(const_cast<ctrl_t*>(ctrl), const_cast<slot_type*>(slot), gen) {
|
|
2632
2129
|
}
|
|
2130
|
+
bool unchecked_equals(const const_iterator& b) const {
|
|
2131
|
+
return inner_.unchecked_equals(b.inner_);
|
|
2132
|
+
}
|
|
2633
2133
|
ctrl_t* control() const { return inner_.control(); }
|
|
2634
2134
|
slot_type* slot() const { return inner_.slot(); }
|
|
2635
2135
|
|
|
2636
2136
|
iterator inner_;
|
|
2637
|
-
|
|
2638
|
-
bool unchecked_equals(const const_iterator& b) {
|
|
2639
|
-
return inner_.unchecked_equals(b.inner_);
|
|
2640
|
-
}
|
|
2641
2137
|
};
|
|
2642
2138
|
|
|
2643
2139
|
using node_type = node_handle<Policy, hash_policy_traits<Policy>, Alloc>;
|
|
@@ -2650,18 +2146,15 @@ class raw_hash_set {
|
|
|
2650
2146
|
std::is_nothrow_default_constructible<key_equal>::value &&
|
|
2651
2147
|
std::is_nothrow_default_constructible<allocator_type>::value) {}
|
|
2652
2148
|
|
|
2653
|
-
|
|
2149
|
+
explicit raw_hash_set(
|
|
2654
2150
|
size_t bucket_count, const hasher& hash = hasher(),
|
|
2655
2151
|
const key_equal& eq = key_equal(),
|
|
2656
2152
|
const allocator_type& alloc = allocator_type())
|
|
2657
2153
|
: settings_(CommonFields::CreateDefault<SooEnabled()>(), hash, eq,
|
|
2658
2154
|
alloc) {
|
|
2659
2155
|
if (bucket_count > DefaultCapacity()) {
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
HashTableSizeOverflow();
|
|
2663
|
-
}
|
|
2664
|
-
resize(NormalizeCapacity(bucket_count));
|
|
2156
|
+
ReserveEmptyNonAllocatedTableToFitBucketCount(
|
|
2157
|
+
common(), GetPolicyFunctions(), bucket_count);
|
|
2665
2158
|
}
|
|
2666
2159
|
}
|
|
2667
2160
|
|
|
@@ -2762,74 +2255,20 @@ class raw_hash_set {
|
|
|
2762
2255
|
|
|
2763
2256
|
raw_hash_set(const raw_hash_set& that)
|
|
2764
2257
|
: raw_hash_set(that, AllocTraits::select_on_container_copy_construction(
|
|
2765
|
-
that.
|
|
2258
|
+
allocator_type(that.char_alloc_ref()))) {}
|
|
2766
2259
|
|
|
2767
2260
|
raw_hash_set(const raw_hash_set& that, const allocator_type& a)
|
|
2768
|
-
: raw_hash_set(
|
|
2769
|
-
that.eq_ref(), a) {
|
|
2261
|
+
: raw_hash_set(0, that.hash_ref(), that.eq_ref(), a) {
|
|
2770
2262
|
that.AssertNotDebugCapacity();
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
emplace_at(soo_iterator(), *that.begin());
|
|
2781
|
-
const HashtablezInfoHandle infoz = try_sample_soo();
|
|
2782
|
-
if (infoz.IsSampled()) resize_with_soo_infoz(infoz);
|
|
2783
|
-
return;
|
|
2784
|
-
}
|
|
2785
|
-
ABSL_SWISSTABLE_ASSERT(!that.is_soo());
|
|
2786
|
-
const size_t cap = capacity();
|
|
2787
|
-
// Note about single group tables:
|
|
2788
|
-
// 1. It is correct to have any order of elements.
|
|
2789
|
-
// 2. Order has to be non deterministic.
|
|
2790
|
-
// 3. We are assigning elements with arbitrary `shift` starting from
|
|
2791
|
-
// `capacity + shift` position.
|
|
2792
|
-
// 4. `shift` must be coprime with `capacity + 1` in order to be able to use
|
|
2793
|
-
// modular arithmetic to traverse all positions, instead if cycling
|
|
2794
|
-
// through a subset of positions. Odd numbers are coprime with any
|
|
2795
|
-
// `capacity + 1` (2^N).
|
|
2796
|
-
size_t offset = cap;
|
|
2797
|
-
const size_t shift =
|
|
2798
|
-
is_single_group(cap) ? (PerTableSalt(control()) | 1) : 0;
|
|
2799
|
-
IterateOverFullSlots(
|
|
2800
|
-
that.common(), that.slot_array(),
|
|
2801
|
-
[&](const ctrl_t* that_ctrl,
|
|
2802
|
-
slot_type* that_slot) ABSL_ATTRIBUTE_ALWAYS_INLINE {
|
|
2803
|
-
if (shift == 0) {
|
|
2804
|
-
// Big tables case. Position must be searched via probing.
|
|
2805
|
-
// The table is guaranteed to be empty, so we can do faster than
|
|
2806
|
-
// a full `insert`.
|
|
2807
|
-
const size_t hash = PolicyTraits::apply(
|
|
2808
|
-
HashElement{hash_ref()}, PolicyTraits::element(that_slot));
|
|
2809
|
-
FindInfo target = find_first_non_full_outofline(common(), hash);
|
|
2810
|
-
infoz().RecordInsert(hash, target.probe_length);
|
|
2811
|
-
offset = target.offset;
|
|
2812
|
-
} else {
|
|
2813
|
-
// Small tables case. Next position is computed via shift.
|
|
2814
|
-
offset = (offset + shift) & cap;
|
|
2815
|
-
}
|
|
2816
|
-
const h2_t h2 = static_cast<h2_t>(*that_ctrl);
|
|
2817
|
-
ABSL_SWISSTABLE_ASSERT( // We rely that hash is not changed for small
|
|
2818
|
-
// tables.
|
|
2819
|
-
H2(PolicyTraits::apply(HashElement{hash_ref()},
|
|
2820
|
-
PolicyTraits::element(that_slot))) == h2 &&
|
|
2821
|
-
"hash function value changed unexpectedly during the copy");
|
|
2822
|
-
SetCtrl(common(), offset, h2, sizeof(slot_type));
|
|
2823
|
-
emplace_at(iterator_at(offset), PolicyTraits::element(that_slot));
|
|
2824
|
-
common().maybe_increment_generation_on_insert();
|
|
2825
|
-
});
|
|
2826
|
-
if (shift != 0) {
|
|
2827
|
-
// On small table copy we do not record individual inserts.
|
|
2828
|
-
// RecordInsert requires hash, but it is unknown for small tables.
|
|
2829
|
-
infoz().RecordStorageChanged(size, cap);
|
|
2830
|
-
}
|
|
2831
|
-
common().set_size(size);
|
|
2832
|
-
growth_info().OverwriteManyEmptyAsFull(size);
|
|
2263
|
+
if (that.empty()) return;
|
|
2264
|
+
Copy(common(), GetPolicyFunctions(), that.common(),
|
|
2265
|
+
[this](void* dst, const void* src) {
|
|
2266
|
+
// TODO(b/413598253): type erase for trivially copyable types via
|
|
2267
|
+
// PolicyTraits.
|
|
2268
|
+
construct(to_slot(dst),
|
|
2269
|
+
PolicyTraits::element(
|
|
2270
|
+
static_cast<slot_type*>(const_cast<void*>(src))));
|
|
2271
|
+
});
|
|
2833
2272
|
}
|
|
2834
2273
|
|
|
2835
2274
|
ABSL_ATTRIBUTE_NOINLINE raw_hash_set(raw_hash_set&& that) noexcept(
|
|
@@ -2843,7 +2282,7 @@ class raw_hash_set {
|
|
|
2843
2282
|
settings_(PolicyTraits::transfer_uses_memcpy() || !that.is_full_soo()
|
|
2844
2283
|
? std::move(that.common())
|
|
2845
2284
|
: CommonFields{full_soo_tag_t{}},
|
|
2846
|
-
that.hash_ref(), that.eq_ref(), that.
|
|
2285
|
+
that.hash_ref(), that.eq_ref(), that.char_alloc_ref()) {
|
|
2847
2286
|
if (!PolicyTraits::transfer_uses_memcpy() && that.is_full_soo()) {
|
|
2848
2287
|
transfer(soo_slot(), that.soo_slot());
|
|
2849
2288
|
}
|
|
@@ -2854,7 +2293,7 @@ class raw_hash_set {
|
|
|
2854
2293
|
raw_hash_set(raw_hash_set&& that, const allocator_type& a)
|
|
2855
2294
|
: settings_(CommonFields::CreateDefault<SooEnabled()>(), that.hash_ref(),
|
|
2856
2295
|
that.eq_ref(), a) {
|
|
2857
|
-
if (a == that.
|
|
2296
|
+
if (CharAlloc(a) == that.char_alloc_ref()) {
|
|
2858
2297
|
swap_common(that);
|
|
2859
2298
|
annotate_for_bug_detection_on_move(that);
|
|
2860
2299
|
} else {
|
|
@@ -2871,13 +2310,15 @@ class raw_hash_set {
|
|
|
2871
2310
|
// is an exact match for that.size(). If this->capacity() is too big, then
|
|
2872
2311
|
// it would make iteration very slow to reuse the allocation. Maybe we can
|
|
2873
2312
|
// do the same heuristic as clear() and reuse if it's small enough.
|
|
2874
|
-
|
|
2313
|
+
allocator_type alloc(propagate_alloc ? that.char_alloc_ref()
|
|
2314
|
+
: char_alloc_ref());
|
|
2315
|
+
raw_hash_set tmp(that, alloc);
|
|
2875
2316
|
// NOLINTNEXTLINE: not returning *this for performance.
|
|
2876
2317
|
return assign_impl<propagate_alloc>(std::move(tmp));
|
|
2877
2318
|
}
|
|
2878
2319
|
|
|
2879
2320
|
raw_hash_set& operator=(raw_hash_set&& that) noexcept(
|
|
2880
|
-
|
|
2321
|
+
AllocTraits::is_always_equal::value &&
|
|
2881
2322
|
std::is_nothrow_move_assignable<hasher>::value &&
|
|
2882
2323
|
std::is_nothrow_move_assignable<key_equal>::value) {
|
|
2883
2324
|
// TODO(sbenza): We should only use the operations from the noexcept clause
|
|
@@ -2890,14 +2331,14 @@ class raw_hash_set {
|
|
|
2890
2331
|
|
|
2891
2332
|
~raw_hash_set() {
|
|
2892
2333
|
destructor_impl();
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2334
|
+
if constexpr (SwisstableAssertAccessToDestroyedTable()) {
|
|
2335
|
+
common().set_capacity(InvalidCapacity::kDestroyed);
|
|
2336
|
+
}
|
|
2896
2337
|
}
|
|
2897
2338
|
|
|
2898
2339
|
iterator begin() ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
2899
2340
|
if (ABSL_PREDICT_FALSE(empty())) return end();
|
|
2900
|
-
if (
|
|
2341
|
+
if (is_small()) return single_iterator();
|
|
2901
2342
|
iterator it = {control(), common().slots_union(),
|
|
2902
2343
|
common().generation_ptr()};
|
|
2903
2344
|
it.skip_empty_or_deleted();
|
|
@@ -2928,14 +2369,12 @@ class raw_hash_set {
|
|
|
2928
2369
|
size_t capacity() const {
|
|
2929
2370
|
const size_t cap = common().capacity();
|
|
2930
2371
|
// Compiler complains when using functions in ASSUME so use local variable.
|
|
2931
|
-
|
|
2372
|
+
[[maybe_unused]] static constexpr size_t kDefaultCapacity =
|
|
2932
2373
|
DefaultCapacity();
|
|
2933
2374
|
ABSL_ASSUME(cap >= kDefaultCapacity);
|
|
2934
2375
|
return cap;
|
|
2935
2376
|
}
|
|
2936
|
-
size_t max_size() const {
|
|
2937
|
-
return CapacityToGrowth(MaxValidCapacity<sizeof(slot_type)>());
|
|
2938
|
-
}
|
|
2377
|
+
size_t max_size() const { return MaxValidSize(sizeof(slot_type)); }
|
|
2939
2378
|
|
|
2940
2379
|
ABSL_ATTRIBUTE_REINITIALIZES void clear() {
|
|
2941
2380
|
if (SwisstableGenerationsEnabled() &&
|
|
@@ -2953,13 +2392,14 @@ class raw_hash_set {
|
|
|
2953
2392
|
const size_t cap = capacity();
|
|
2954
2393
|
if (cap == 0) {
|
|
2955
2394
|
// Already guaranteed to be empty; so nothing to do.
|
|
2956
|
-
} else if (
|
|
2957
|
-
if (!empty())
|
|
2958
|
-
|
|
2395
|
+
} else if (is_small()) {
|
|
2396
|
+
if (!empty()) {
|
|
2397
|
+
destroy(single_slot());
|
|
2398
|
+
decrement_small_size();
|
|
2399
|
+
}
|
|
2959
2400
|
} else {
|
|
2960
2401
|
destroy_slots();
|
|
2961
|
-
|
|
2962
|
-
SooEnabled());
|
|
2402
|
+
clear_backing_array(/*reuse=*/cap < 128);
|
|
2963
2403
|
}
|
|
2964
2404
|
common().set_reserved_growth(0);
|
|
2965
2405
|
common().set_reservation_size(0);
|
|
@@ -2971,15 +2411,15 @@ class raw_hash_set {
|
|
|
2971
2411
|
// flat_hash_map<std::string, int> m;
|
|
2972
2412
|
// m.insert(std::make_pair("abc", 42));
|
|
2973
2413
|
template <class T,
|
|
2974
|
-
std::enable_if_t<IsDecomposableAndInsertable<T>::value &&
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2414
|
+
int = std::enable_if_t<IsDecomposableAndInsertable<T>::value &&
|
|
2415
|
+
IsNotBitField<T>::value &&
|
|
2416
|
+
!IsLifetimeBoundAssignmentFrom<T>::value,
|
|
2417
|
+
int>()>
|
|
2978
2418
|
std::pair<iterator, bool> insert(T&& value) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
2979
2419
|
return emplace(std::forward<T>(value));
|
|
2980
2420
|
}
|
|
2981
2421
|
|
|
2982
|
-
template <class T,
|
|
2422
|
+
template <class T, int&...,
|
|
2983
2423
|
std::enable_if_t<IsDecomposableAndInsertable<T>::value &&
|
|
2984
2424
|
IsNotBitField<T>::value &&
|
|
2985
2425
|
IsLifetimeBoundAssignmentFrom<T>::value,
|
|
@@ -2987,7 +2427,7 @@ class raw_hash_set {
|
|
|
2987
2427
|
std::pair<iterator, bool> insert(
|
|
2988
2428
|
T&& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this))
|
|
2989
2429
|
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
2990
|
-
return
|
|
2430
|
+
return this->template insert<T, 0>(std::forward<T>(value));
|
|
2991
2431
|
}
|
|
2992
2432
|
|
|
2993
2433
|
// This overload kicks in when the argument is a bitfield or an lvalue of
|
|
@@ -3001,22 +2441,22 @@ class raw_hash_set {
|
|
|
3001
2441
|
// const char* p = "hello";
|
|
3002
2442
|
// s.insert(p);
|
|
3003
2443
|
//
|
|
3004
|
-
template <class T, std::enable_if_t<
|
|
2444
|
+
template <class T, int = std::enable_if_t<
|
|
3005
2445
|
IsDecomposableAndInsertable<const T&>::value &&
|
|
3006
2446
|
!IsLifetimeBoundAssignmentFrom<const T&>::value,
|
|
3007
|
-
int>
|
|
2447
|
+
int>()>
|
|
3008
2448
|
std::pair<iterator, bool> insert(const T& value)
|
|
3009
2449
|
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
3010
2450
|
return emplace(value);
|
|
3011
2451
|
}
|
|
3012
|
-
template <class T,
|
|
2452
|
+
template <class T, int&...,
|
|
3013
2453
|
std::enable_if_t<IsDecomposableAndInsertable<const T&>::value &&
|
|
3014
2454
|
IsLifetimeBoundAssignmentFrom<const T&>::value,
|
|
3015
2455
|
int> = 0>
|
|
3016
2456
|
std::pair<iterator, bool> insert(
|
|
3017
2457
|
const T& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this))
|
|
3018
2458
|
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
3019
|
-
return
|
|
2459
|
+
return this->template insert<T, 0>(value);
|
|
3020
2460
|
}
|
|
3021
2461
|
|
|
3022
2462
|
// This overload kicks in when the argument is an rvalue of init_type. Its
|
|
@@ -3043,21 +2483,22 @@ class raw_hash_set {
|
|
|
3043
2483
|
#endif
|
|
3044
2484
|
|
|
3045
2485
|
template <class T,
|
|
3046
|
-
std::enable_if_t<IsDecomposableAndInsertable<T>::value &&
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
2486
|
+
int = std::enable_if_t<IsDecomposableAndInsertable<T>::value &&
|
|
2487
|
+
IsNotBitField<T>::value &&
|
|
2488
|
+
!IsLifetimeBoundAssignmentFrom<T>::value,
|
|
2489
|
+
int>()>
|
|
3050
2490
|
iterator insert(const_iterator, T&& value) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
3051
2491
|
return insert(std::forward<T>(value)).first;
|
|
3052
2492
|
}
|
|
3053
|
-
template <class T,
|
|
2493
|
+
template <class T, int&...,
|
|
3054
2494
|
std::enable_if_t<IsDecomposableAndInsertable<T>::value &&
|
|
3055
2495
|
IsNotBitField<T>::value &&
|
|
3056
2496
|
IsLifetimeBoundAssignmentFrom<T>::value,
|
|
3057
2497
|
int> = 0>
|
|
3058
|
-
iterator insert(const_iterator,
|
|
3059
|
-
|
|
3060
|
-
|
|
2498
|
+
iterator insert(const_iterator hint,
|
|
2499
|
+
T&& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this))
|
|
2500
|
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
2501
|
+
return this->template insert<T, 0>(hint, std::forward<T>(value));
|
|
3061
2502
|
}
|
|
3062
2503
|
|
|
3063
2504
|
template <class T, std::enable_if_t<
|
|
@@ -3198,7 +2639,8 @@ class raw_hash_set {
|
|
|
3198
2639
|
auto res = find_or_prepare_insert(key);
|
|
3199
2640
|
if (res.second) {
|
|
3200
2641
|
slot_type* slot = res.first.slot();
|
|
3201
|
-
|
|
2642
|
+
allocator_type alloc(char_alloc_ref());
|
|
2643
|
+
std::forward<F>(f)(constructor(&alloc, &slot));
|
|
3202
2644
|
ABSL_SWISSTABLE_ASSERT(!slot);
|
|
3203
2645
|
}
|
|
3204
2646
|
return res.first;
|
|
@@ -3230,35 +2672,31 @@ class raw_hash_set {
|
|
|
3230
2672
|
// This overload is necessary because otherwise erase<K>(const K&) would be
|
|
3231
2673
|
// a better match if non-const iterator is passed as an argument.
|
|
3232
2674
|
void erase(iterator it) {
|
|
2675
|
+
ABSL_SWISSTABLE_ASSERT(capacity() > 0);
|
|
3233
2676
|
AssertNotDebugCapacity();
|
|
3234
|
-
|
|
2677
|
+
it.assert_is_full("erase()");
|
|
3235
2678
|
destroy(it.slot());
|
|
3236
|
-
|
|
3237
|
-
common().set_empty_soo();
|
|
3238
|
-
} else {
|
|
3239
|
-
erase_meta_only(it);
|
|
3240
|
-
}
|
|
2679
|
+
erase_meta_only(it);
|
|
3241
2680
|
}
|
|
3242
2681
|
|
|
3243
2682
|
iterator erase(const_iterator first,
|
|
3244
2683
|
const_iterator last) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
3245
2684
|
AssertNotDebugCapacity();
|
|
3246
|
-
// We check for empty first because
|
|
2685
|
+
// We check for empty first because clear_backing_array requires that
|
|
3247
2686
|
// capacity() > 0 as a precondition.
|
|
3248
2687
|
if (empty()) return end();
|
|
3249
2688
|
if (first == last) return last.inner_;
|
|
3250
|
-
if (
|
|
3251
|
-
destroy(
|
|
3252
|
-
|
|
2689
|
+
if (is_small()) {
|
|
2690
|
+
destroy(single_slot());
|
|
2691
|
+
erase_meta_only_small();
|
|
3253
2692
|
return end();
|
|
3254
2693
|
}
|
|
3255
2694
|
if (first == begin() && last == end()) {
|
|
3256
2695
|
// TODO(ezb): we access control bytes in destroy_slots so it could make
|
|
3257
|
-
// sense to combine destroy_slots and
|
|
2696
|
+
// sense to combine destroy_slots and clear_backing_array to avoid cache
|
|
3258
2697
|
// misses when the table is large. Note that we also do this in clear().
|
|
3259
2698
|
destroy_slots();
|
|
3260
|
-
|
|
3261
|
-
SooEnabled());
|
|
2699
|
+
clear_backing_array(/*reuse=*/true);
|
|
3262
2700
|
common().set_reserved_growth(common().reservation_size());
|
|
3263
2701
|
return end();
|
|
3264
2702
|
}
|
|
@@ -3282,14 +2720,15 @@ class raw_hash_set {
|
|
|
3282
2720
|
.second;
|
|
3283
2721
|
};
|
|
3284
2722
|
|
|
3285
|
-
if (src.
|
|
2723
|
+
if (src.is_small()) {
|
|
3286
2724
|
if (src.empty()) return;
|
|
3287
|
-
if (insert_slot(src.
|
|
2725
|
+
if (insert_slot(src.single_slot()))
|
|
2726
|
+
src.erase_meta_only_small();
|
|
3288
2727
|
return;
|
|
3289
2728
|
}
|
|
3290
2729
|
for (auto it = src.begin(), e = src.end(); it != e;) {
|
|
3291
2730
|
auto next = std::next(it);
|
|
3292
|
-
if (insert_slot(it.slot())) src.
|
|
2731
|
+
if (insert_slot(it.slot())) src.erase_meta_only_large(it);
|
|
3293
2732
|
it = next;
|
|
3294
2733
|
}
|
|
3295
2734
|
}
|
|
@@ -3301,14 +2740,10 @@ class raw_hash_set {
|
|
|
3301
2740
|
|
|
3302
2741
|
node_type extract(const_iterator position) {
|
|
3303
2742
|
AssertNotDebugCapacity();
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
auto node = CommonAccess::Transfer<node_type>(
|
|
3307
|
-
|
|
3308
|
-
common().set_empty_soo();
|
|
3309
|
-
} else {
|
|
3310
|
-
erase_meta_only(position);
|
|
3311
|
-
}
|
|
2743
|
+
position.inner_.assert_is_full("extract()");
|
|
2744
|
+
allocator_type alloc(char_alloc_ref());
|
|
2745
|
+
auto node = CommonAccess::Transfer<node_type>(alloc, position.slot());
|
|
2746
|
+
erase_meta_only(position);
|
|
3312
2747
|
return node;
|
|
3313
2748
|
}
|
|
3314
2749
|
|
|
@@ -3320,82 +2755,25 @@ class raw_hash_set {
|
|
|
3320
2755
|
}
|
|
3321
2756
|
|
|
3322
2757
|
void swap(raw_hash_set& that) noexcept(
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
2758
|
+
AllocTraits::is_always_equal::value &&
|
|
2759
|
+
std::is_nothrow_swappable<hasher>::value &&
|
|
2760
|
+
std::is_nothrow_swappable<key_equal>::value) {
|
|
3326
2761
|
AssertNotDebugCapacity();
|
|
3327
2762
|
that.AssertNotDebugCapacity();
|
|
3328
2763
|
using std::swap;
|
|
3329
2764
|
swap_common(that);
|
|
3330
2765
|
swap(hash_ref(), that.hash_ref());
|
|
3331
2766
|
swap(eq_ref(), that.eq_ref());
|
|
3332
|
-
SwapAlloc(
|
|
2767
|
+
SwapAlloc(char_alloc_ref(), that.char_alloc_ref(),
|
|
3333
2768
|
typename AllocTraits::propagate_on_container_swap{});
|
|
3334
2769
|
}
|
|
3335
2770
|
|
|
3336
|
-
void rehash(size_t n) {
|
|
3337
|
-
const size_t cap = capacity();
|
|
3338
|
-
if (n == 0) {
|
|
3339
|
-
if (cap == 0 || is_soo()) return;
|
|
3340
|
-
if (empty()) {
|
|
3341
|
-
ClearBackingArray(common(), GetPolicyFunctions(), /*reuse=*/false,
|
|
3342
|
-
SooEnabled());
|
|
3343
|
-
return;
|
|
3344
|
-
}
|
|
3345
|
-
if (fits_in_soo(size())) {
|
|
3346
|
-
// When the table is already sampled, we keep it sampled.
|
|
3347
|
-
if (infoz().IsSampled()) {
|
|
3348
|
-
const size_t kInitialSampledCapacity = NextCapacity(SooCapacity());
|
|
3349
|
-
if (capacity() > kInitialSampledCapacity) {
|
|
3350
|
-
resize(kInitialSampledCapacity);
|
|
3351
|
-
}
|
|
3352
|
-
// This asserts that we didn't lose sampling coverage in `resize`.
|
|
3353
|
-
ABSL_SWISSTABLE_ASSERT(infoz().IsSampled());
|
|
3354
|
-
return;
|
|
3355
|
-
}
|
|
3356
|
-
alignas(slot_type) unsigned char slot_space[sizeof(slot_type)];
|
|
3357
|
-
slot_type* tmp_slot = to_slot(slot_space);
|
|
3358
|
-
transfer(tmp_slot, begin().slot());
|
|
3359
|
-
ClearBackingArray(common(), GetPolicyFunctions(), /*reuse=*/false,
|
|
3360
|
-
SooEnabled());
|
|
3361
|
-
transfer(soo_slot(), tmp_slot);
|
|
3362
|
-
common().set_full_soo();
|
|
3363
|
-
return;
|
|
3364
|
-
}
|
|
3365
|
-
}
|
|
3366
|
-
|
|
3367
|
-
// bitor is a faster way of doing `max` here. We will round up to the next
|
|
3368
|
-
// power-of-2-minus-1, so bitor is good enough.
|
|
3369
|
-
auto m = NormalizeCapacity(n | GrowthToLowerboundCapacity(size()));
|
|
3370
|
-
// n == 0 unconditionally rehashes as per the standard.
|
|
3371
|
-
if (n == 0 || m > cap) {
|
|
3372
|
-
if (ABSL_PREDICT_FALSE(m > MaxValidCapacity<sizeof(slot_type)>())) {
|
|
3373
|
-
HashTableSizeOverflow();
|
|
3374
|
-
}
|
|
3375
|
-
resize(m);
|
|
3376
|
-
|
|
3377
|
-
// This is after resize, to ensure that we have completed the allocation
|
|
3378
|
-
// and have potentially sampled the hashtable.
|
|
3379
|
-
infoz().RecordReservation(n);
|
|
3380
|
-
}
|
|
3381
|
-
}
|
|
2771
|
+
void rehash(size_t n) { Rehash(common(), GetPolicyFunctions(), n); }
|
|
3382
2772
|
|
|
3383
2773
|
void reserve(size_t n) {
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
if (n > max_size_before_growth) {
|
|
3387
|
-
if (ABSL_PREDICT_FALSE(n > max_size())) {
|
|
3388
|
-
HashTableSizeOverflow();
|
|
3389
|
-
}
|
|
3390
|
-
size_t m = GrowthToLowerboundCapacity(n);
|
|
3391
|
-
resize(NormalizeCapacity(m));
|
|
3392
|
-
|
|
3393
|
-
// This is after resize, to ensure that we have completed the allocation
|
|
3394
|
-
// and have potentially sampled the hashtable.
|
|
3395
|
-
infoz().RecordReservation(n);
|
|
2774
|
+
if (ABSL_PREDICT_TRUE(n > DefaultCapacity())) {
|
|
2775
|
+
ReserveTableToFitNewSize(common(), GetPolicyFunctions(), n);
|
|
3396
2776
|
}
|
|
3397
|
-
common().reset_reserved_growth(n);
|
|
3398
|
-
common().set_reservation_size(n);
|
|
3399
2777
|
}
|
|
3400
2778
|
|
|
3401
2779
|
// Extension API: support for heterogeneous keys.
|
|
@@ -3418,13 +2796,13 @@ class raw_hash_set {
|
|
|
3418
2796
|
// NOTE: This is a very low level operation and should not be used without
|
|
3419
2797
|
// specific benchmarks indicating its importance.
|
|
3420
2798
|
template <class K = key_type>
|
|
3421
|
-
void prefetch(const key_arg<K>& key) const {
|
|
2799
|
+
void prefetch([[maybe_unused]] const key_arg<K>& key) const {
|
|
3422
2800
|
if (capacity() == DefaultCapacity()) return;
|
|
3423
|
-
(void)key;
|
|
3424
2801
|
// Avoid probing if we won't be able to prefetch the addresses received.
|
|
3425
2802
|
#ifdef ABSL_HAVE_PREFETCH
|
|
3426
2803
|
prefetch_heap_block();
|
|
3427
|
-
|
|
2804
|
+
if (is_small()) return;
|
|
2805
|
+
auto seq = probe(common(), hash_of(key));
|
|
3428
2806
|
PrefetchToLocalCache(control() + seq.offset());
|
|
3429
2807
|
PrefetchToLocalCache(slot_array() + seq.offset());
|
|
3430
2808
|
#endif // ABSL_HAVE_PREFETCH
|
|
@@ -3441,9 +2819,9 @@ class raw_hash_set {
|
|
|
3441
2819
|
template <class K = key_type>
|
|
3442
2820
|
iterator find(const key_arg<K>& key) ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
3443
2821
|
AssertOnFind(key);
|
|
3444
|
-
if (
|
|
2822
|
+
if (is_small()) return find_small(key);
|
|
3445
2823
|
prefetch_heap_block();
|
|
3446
|
-
return
|
|
2824
|
+
return find_large(key, hash_of(key));
|
|
3447
2825
|
}
|
|
3448
2826
|
|
|
3449
2827
|
template <class K = key_type>
|
|
@@ -3493,7 +2871,9 @@ class raw_hash_set {
|
|
|
3493
2871
|
|
|
3494
2872
|
hasher hash_function() const { return hash_ref(); }
|
|
3495
2873
|
key_equal key_eq() const { return eq_ref(); }
|
|
3496
|
-
allocator_type get_allocator() const {
|
|
2874
|
+
allocator_type get_allocator() const {
|
|
2875
|
+
return allocator_type(char_alloc_ref());
|
|
2876
|
+
}
|
|
3497
2877
|
|
|
3498
2878
|
friend bool operator==(const raw_hash_set& a, const raw_hash_set& b) {
|
|
3499
2879
|
if (a.size() != b.size()) return false;
|
|
@@ -3525,7 +2905,7 @@ class raw_hash_set {
|
|
|
3525
2905
|
H>::type
|
|
3526
2906
|
AbslHashValue(H h, const raw_hash_set& s) {
|
|
3527
2907
|
return H::combine(H::combine_unordered(std::move(h), s.begin(), s.end()),
|
|
3528
|
-
s.size());
|
|
2908
|
+
hash_internal::WeaklyMixedInteger{s.size()});
|
|
3529
2909
|
}
|
|
3530
2910
|
|
|
3531
2911
|
friend void swap(raw_hash_set& a,
|
|
@@ -3548,24 +2928,6 @@ class raw_hash_set {
|
|
|
3548
2928
|
const raw_hash_set& s;
|
|
3549
2929
|
};
|
|
3550
2930
|
|
|
3551
|
-
struct HashElement {
|
|
3552
|
-
template <class K, class... Args>
|
|
3553
|
-
size_t operator()(const K& key, Args&&...) const {
|
|
3554
|
-
return h(key);
|
|
3555
|
-
}
|
|
3556
|
-
const hasher& h;
|
|
3557
|
-
};
|
|
3558
|
-
|
|
3559
|
-
template <class K1>
|
|
3560
|
-
struct EqualElement {
|
|
3561
|
-
template <class K2, class... Args>
|
|
3562
|
-
bool operator()(const K2& lhs, Args&&...) const {
|
|
3563
|
-
return eq(lhs, rhs);
|
|
3564
|
-
}
|
|
3565
|
-
const K1& rhs;
|
|
3566
|
-
const key_equal& eq;
|
|
3567
|
-
};
|
|
3568
|
-
|
|
3569
2931
|
struct EmplaceDecomposable {
|
|
3570
2932
|
template <class K, class... Args>
|
|
3571
2933
|
std::pair<iterator, bool> operator()(const K& key, Args&&... args) const {
|
|
@@ -3598,40 +2960,44 @@ class raw_hash_set {
|
|
|
3598
2960
|
template <typename... Args>
|
|
3599
2961
|
inline void construct(slot_type* slot, Args&&... args) {
|
|
3600
2962
|
common().RunWithReentrancyGuard([&] {
|
|
3601
|
-
|
|
2963
|
+
allocator_type alloc(char_alloc_ref());
|
|
2964
|
+
PolicyTraits::construct(&alloc, slot, std::forward<Args>(args)...);
|
|
3602
2965
|
});
|
|
3603
2966
|
}
|
|
3604
2967
|
inline void destroy(slot_type* slot) {
|
|
3605
|
-
common().RunWithReentrancyGuard(
|
|
3606
|
-
|
|
2968
|
+
common().RunWithReentrancyGuard([&] {
|
|
2969
|
+
allocator_type alloc(char_alloc_ref());
|
|
2970
|
+
PolicyTraits::destroy(&alloc, slot);
|
|
2971
|
+
});
|
|
3607
2972
|
}
|
|
3608
2973
|
inline void transfer(slot_type* to, slot_type* from) {
|
|
3609
|
-
common().RunWithReentrancyGuard(
|
|
3610
|
-
|
|
2974
|
+
common().RunWithReentrancyGuard([&] {
|
|
2975
|
+
allocator_type alloc(char_alloc_ref());
|
|
2976
|
+
PolicyTraits::transfer(&alloc, to, from);
|
|
2977
|
+
});
|
|
3611
2978
|
}
|
|
3612
2979
|
|
|
3613
2980
|
// TODO(b/289225379): consider having a helper class that has the impls for
|
|
3614
2981
|
// SOO functionality.
|
|
3615
2982
|
template <class K = key_type>
|
|
3616
|
-
iterator
|
|
3617
|
-
ABSL_SWISSTABLE_ASSERT(
|
|
3618
|
-
return empty() || !
|
|
3619
|
-
PolicyTraits::element(soo_slot()))
|
|
3620
|
-
? end()
|
|
3621
|
-
: soo_iterator();
|
|
2983
|
+
iterator find_small(const key_arg<K>& key) {
|
|
2984
|
+
ABSL_SWISSTABLE_ASSERT(is_small());
|
|
2985
|
+
return empty() || !equal_to(key, single_slot()) ? end() : single_iterator();
|
|
3622
2986
|
}
|
|
3623
2987
|
|
|
3624
2988
|
template <class K = key_type>
|
|
3625
|
-
iterator
|
|
3626
|
-
ABSL_SWISSTABLE_ASSERT(!
|
|
2989
|
+
iterator find_large(const key_arg<K>& key, size_t hash) {
|
|
2990
|
+
ABSL_SWISSTABLE_ASSERT(!is_small());
|
|
3627
2991
|
auto seq = probe(common(), hash);
|
|
2992
|
+
const h2_t h2 = H2(hash);
|
|
3628
2993
|
const ctrl_t* ctrl = control();
|
|
3629
2994
|
while (true) {
|
|
2995
|
+
#ifndef ABSL_HAVE_MEMORY_SANITIZER
|
|
2996
|
+
absl::PrefetchToLocalCache(slot_array() + seq.offset());
|
|
2997
|
+
#endif
|
|
3630
2998
|
Group g{ctrl + seq.offset()};
|
|
3631
|
-
for (uint32_t i : g.Match(
|
|
3632
|
-
if (ABSL_PREDICT_TRUE(
|
|
3633
|
-
EqualElement<K>{key, eq_ref()},
|
|
3634
|
-
PolicyTraits::element(slot_array() + seq.offset(i)))))
|
|
2999
|
+
for (uint32_t i : g.Match(h2)) {
|
|
3000
|
+
if (ABSL_PREDICT_TRUE(equal_to(key, slot_array() + seq.offset(i))))
|
|
3635
3001
|
return iterator_at(seq.offset(i));
|
|
3636
3002
|
}
|
|
3637
3003
|
if (ABSL_PREDICT_TRUE(g.MaskEmpty())) return end();
|
|
@@ -3640,48 +3006,62 @@ class raw_hash_set {
|
|
|
3640
3006
|
}
|
|
3641
3007
|
}
|
|
3642
3008
|
|
|
3643
|
-
//
|
|
3644
|
-
// insertion into an empty SOO table and in copy
|
|
3645
|
-
// can fit in SOO capacity.
|
|
3646
|
-
|
|
3009
|
+
// Returns true if the table needs to be sampled.
|
|
3010
|
+
// This should be called on insertion into an empty SOO table and in copy
|
|
3011
|
+
// construction when the size can fit in SOO capacity.
|
|
3012
|
+
bool should_sample_soo() const {
|
|
3647
3013
|
ABSL_SWISSTABLE_ASSERT(is_soo());
|
|
3648
|
-
if (!
|
|
3649
|
-
return
|
|
3650
|
-
SooCapacity());
|
|
3014
|
+
if (!ShouldSampleHashtablezInfoForAlloc<CharAlloc>()) return false;
|
|
3015
|
+
return ABSL_PREDICT_FALSE(ShouldSampleNextTable());
|
|
3651
3016
|
}
|
|
3652
3017
|
|
|
3653
|
-
|
|
3654
|
-
ABSL_SWISSTABLE_ASSERT(
|
|
3018
|
+
void clear_backing_array(bool reuse) {
|
|
3019
|
+
ABSL_SWISSTABLE_ASSERT(capacity() > DefaultCapacity());
|
|
3020
|
+
ClearBackingArray(common(), GetPolicyFunctions(), &char_alloc_ref(), reuse,
|
|
3021
|
+
SooEnabled());
|
|
3022
|
+
}
|
|
3023
|
+
|
|
3024
|
+
void destroy_slots() {
|
|
3025
|
+
ABSL_SWISSTABLE_ASSERT(!is_small());
|
|
3655
3026
|
if (PolicyTraits::template destroy_is_trivial<Alloc>()) return;
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3027
|
+
auto destroy_slot = [&](const ctrl_t*, void* slot) {
|
|
3028
|
+
this->destroy(static_cast<slot_type*>(slot));
|
|
3029
|
+
};
|
|
3030
|
+
if constexpr (SwisstableAssertAccessToDestroyedTable()) {
|
|
3031
|
+
CommonFields common_copy(non_soo_tag_t{}, this->common());
|
|
3032
|
+
common().set_capacity(InvalidCapacity::kDestroyed);
|
|
3033
|
+
IterateOverFullSlots(common_copy, sizeof(slot_type), destroy_slot);
|
|
3034
|
+
common().set_capacity(common_copy.capacity());
|
|
3035
|
+
} else {
|
|
3036
|
+
IterateOverFullSlots(common(), sizeof(slot_type), destroy_slot);
|
|
3037
|
+
}
|
|
3660
3038
|
}
|
|
3661
3039
|
|
|
3662
|
-
|
|
3663
|
-
ABSL_SWISSTABLE_ASSERT(capacity()
|
|
3040
|
+
void dealloc() {
|
|
3041
|
+
ABSL_SWISSTABLE_ASSERT(capacity() > DefaultCapacity());
|
|
3664
3042
|
// Unpoison before returning the memory to the allocator.
|
|
3665
3043
|
SanitizerUnpoisonMemoryRegion(slot_array(), sizeof(slot_type) * capacity());
|
|
3666
3044
|
infoz().Unregister();
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3045
|
+
DeallocateBackingArray<BackingArrayAlignment(alignof(slot_type)),
|
|
3046
|
+
CharAlloc>(&char_alloc_ref(), capacity(), control(),
|
|
3047
|
+
sizeof(slot_type), alignof(slot_type),
|
|
3048
|
+
common().has_infoz());
|
|
3670
3049
|
}
|
|
3671
3050
|
|
|
3672
|
-
|
|
3051
|
+
void destructor_impl() {
|
|
3673
3052
|
if (SwisstableGenerationsEnabled() &&
|
|
3674
3053
|
capacity() >= InvalidCapacity::kMovedFrom) {
|
|
3675
3054
|
return;
|
|
3676
3055
|
}
|
|
3677
3056
|
if (capacity() == 0) return;
|
|
3678
|
-
if (
|
|
3057
|
+
if (is_small()) {
|
|
3679
3058
|
if (!empty()) {
|
|
3680
|
-
ABSL_SWISSTABLE_IGNORE_UNINITIALIZED(destroy(
|
|
3059
|
+
ABSL_SWISSTABLE_IGNORE_UNINITIALIZED(destroy(single_slot()));
|
|
3681
3060
|
}
|
|
3682
|
-
return;
|
|
3061
|
+
if constexpr (SooEnabled()) return;
|
|
3062
|
+
} else {
|
|
3063
|
+
destroy_slots();
|
|
3683
3064
|
}
|
|
3684
|
-
destroy_slots();
|
|
3685
3065
|
dealloc();
|
|
3686
3066
|
}
|
|
3687
3067
|
|
|
@@ -3690,133 +3070,44 @@ class raw_hash_set {
|
|
|
3690
3070
|
// This merely updates the pertinent control byte. This can be used in
|
|
3691
3071
|
// conjunction with Policy::transfer to move the object to another place.
|
|
3692
3072
|
void erase_meta_only(const_iterator it) {
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3073
|
+
if (is_small()) {
|
|
3074
|
+
erase_meta_only_small();
|
|
3075
|
+
return;
|
|
3076
|
+
}
|
|
3077
|
+
erase_meta_only_large(it);
|
|
3078
|
+
}
|
|
3079
|
+
void erase_meta_only_small() {
|
|
3080
|
+
EraseMetaOnlySmall(common(), SooEnabled(), sizeof(slot_type));
|
|
3081
|
+
}
|
|
3082
|
+
void erase_meta_only_large(const_iterator it) {
|
|
3083
|
+
EraseMetaOnlyLarge(common(), it.control(), sizeof(slot_type));
|
|
3696
3084
|
}
|
|
3697
3085
|
|
|
3698
|
-
|
|
3699
|
-
|
|
3086
|
+
template <class K>
|
|
3087
|
+
ABSL_ATTRIBUTE_ALWAYS_INLINE bool equal_to(const K& key,
|
|
3088
|
+
slot_type* slot) const {
|
|
3089
|
+
return PolicyTraits::apply(EqualElement<K, key_equal>{key, eq_ref()},
|
|
3700
3090
|
PolicyTraits::element(slot));
|
|
3701
3091
|
}
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
void resize(size_t new_capacity) {
|
|
3712
|
-
raw_hash_set::resize_impl(common(), new_capacity, HashtablezInfoHandle{});
|
|
3713
|
-
}
|
|
3714
|
-
|
|
3715
|
-
// As above, except that we also accept a pre-sampled, forced infoz for
|
|
3716
|
-
// SOO tables, since they need to switch from SOO to heap in order to
|
|
3717
|
-
// store the infoz.
|
|
3718
|
-
void resize_with_soo_infoz(HashtablezInfoHandle forced_infoz) {
|
|
3719
|
-
ABSL_SWISSTABLE_ASSERT(forced_infoz.IsSampled());
|
|
3720
|
-
raw_hash_set::resize_impl(common(), NextCapacity(SooCapacity()),
|
|
3721
|
-
forced_infoz);
|
|
3722
|
-
}
|
|
3723
|
-
|
|
3724
|
-
// Resizes set to the new capacity.
|
|
3725
|
-
// It is a static function in order to use its pointer in GetPolicyFunctions.
|
|
3726
|
-
ABSL_ATTRIBUTE_NOINLINE static void resize_impl(
|
|
3727
|
-
CommonFields& common, size_t new_capacity,
|
|
3728
|
-
HashtablezInfoHandle forced_infoz) {
|
|
3729
|
-
raw_hash_set* set = reinterpret_cast<raw_hash_set*>(&common);
|
|
3730
|
-
ABSL_SWISSTABLE_ASSERT(IsValidCapacity(new_capacity));
|
|
3731
|
-
ABSL_SWISSTABLE_ASSERT(!set->fits_in_soo(new_capacity));
|
|
3732
|
-
const bool was_soo = set->is_soo();
|
|
3733
|
-
const bool had_soo_slot = was_soo && !set->empty();
|
|
3734
|
-
const ctrl_t soo_slot_h2 =
|
|
3735
|
-
had_soo_slot ? static_cast<ctrl_t>(H2(set->hash_of(set->soo_slot())))
|
|
3736
|
-
: ctrl_t::kEmpty;
|
|
3737
|
-
HashSetResizeHelper resize_helper(common, was_soo, had_soo_slot,
|
|
3738
|
-
forced_infoz);
|
|
3739
|
-
// Initialize HashSetResizeHelper::old_heap_or_soo_. We can't do this in
|
|
3740
|
-
// HashSetResizeHelper constructor because it can't transfer slots when
|
|
3741
|
-
// transfer_uses_memcpy is false.
|
|
3742
|
-
// TODO(b/289225379): try to handle more of the SOO cases inside
|
|
3743
|
-
// InitializeSlots. See comment on cl/555990034 snapshot #63.
|
|
3744
|
-
if (PolicyTraits::transfer_uses_memcpy() || !had_soo_slot) {
|
|
3745
|
-
resize_helper.old_heap_or_soo() = common.heap_or_soo();
|
|
3746
|
-
} else {
|
|
3747
|
-
set->transfer(set->to_slot(resize_helper.old_soo_data()),
|
|
3748
|
-
set->soo_slot());
|
|
3749
|
-
}
|
|
3750
|
-
common.set_capacity(new_capacity);
|
|
3751
|
-
// Note that `InitializeSlots` does different number initialization steps
|
|
3752
|
-
// depending on the values of `transfer_uses_memcpy` and capacities.
|
|
3753
|
-
// Refer to the comment in `InitializeSlots` for more details.
|
|
3754
|
-
const bool grow_single_group =
|
|
3755
|
-
resize_helper.InitializeSlots<CharAlloc, sizeof(slot_type),
|
|
3756
|
-
PolicyTraits::transfer_uses_memcpy(),
|
|
3757
|
-
SooEnabled(), alignof(slot_type)>(
|
|
3758
|
-
common, CharAlloc(set->alloc_ref()), soo_slot_h2, sizeof(key_type),
|
|
3759
|
-
sizeof(value_type));
|
|
3760
|
-
|
|
3761
|
-
// In the SooEnabled() case, capacity is never 0 so we don't check.
|
|
3762
|
-
if (!SooEnabled() && resize_helper.old_capacity() == 0) {
|
|
3763
|
-
// InitializeSlots did all the work including infoz().RecordRehash().
|
|
3764
|
-
return;
|
|
3765
|
-
}
|
|
3766
|
-
ABSL_SWISSTABLE_ASSERT(resize_helper.old_capacity() > 0);
|
|
3767
|
-
// Nothing more to do in this case.
|
|
3768
|
-
if (was_soo && !had_soo_slot) return;
|
|
3769
|
-
|
|
3770
|
-
slot_type* new_slots = set->slot_array();
|
|
3771
|
-
if (grow_single_group) {
|
|
3772
|
-
if (PolicyTraits::transfer_uses_memcpy()) {
|
|
3773
|
-
// InitializeSlots did all the work.
|
|
3774
|
-
return;
|
|
3775
|
-
}
|
|
3776
|
-
if (was_soo) {
|
|
3777
|
-
set->transfer(new_slots + resize_helper.SooSlotIndex(),
|
|
3778
|
-
to_slot(resize_helper.old_soo_data()));
|
|
3779
|
-
return;
|
|
3780
|
-
} else {
|
|
3781
|
-
// We want GrowSizeIntoSingleGroup to be called here in order to make
|
|
3782
|
-
// InitializeSlots not depend on PolicyTraits.
|
|
3783
|
-
resize_helper.GrowSizeIntoSingleGroup<PolicyTraits>(common,
|
|
3784
|
-
set->alloc_ref());
|
|
3785
|
-
}
|
|
3786
|
-
} else {
|
|
3787
|
-
// InitializeSlots prepares control bytes to correspond to empty table.
|
|
3788
|
-
const auto insert_slot = [&](slot_type* slot) {
|
|
3789
|
-
size_t hash = PolicyTraits::apply(HashElement{set->hash_ref()},
|
|
3790
|
-
PolicyTraits::element(slot));
|
|
3791
|
-
auto target = find_first_non_full(common, hash);
|
|
3792
|
-
SetCtrl(common, target.offset, H2(hash), sizeof(slot_type));
|
|
3793
|
-
set->transfer(new_slots + target.offset, slot);
|
|
3794
|
-
return target.probe_length;
|
|
3795
|
-
};
|
|
3796
|
-
if (was_soo) {
|
|
3797
|
-
insert_slot(to_slot(resize_helper.old_soo_data()));
|
|
3798
|
-
return;
|
|
3799
|
-
} else {
|
|
3800
|
-
auto* old_slots = static_cast<slot_type*>(resize_helper.old_slots());
|
|
3801
|
-
size_t total_probe_length = 0;
|
|
3802
|
-
for (size_t i = 0; i != resize_helper.old_capacity(); ++i) {
|
|
3803
|
-
if (IsFull(resize_helper.old_ctrl()[i])) {
|
|
3804
|
-
total_probe_length += insert_slot(old_slots + i);
|
|
3805
|
-
}
|
|
3806
|
-
}
|
|
3807
|
-
common.infoz().RecordRehash(total_probe_length);
|
|
3808
|
-
}
|
|
3809
|
-
}
|
|
3810
|
-
resize_helper.DeallocateOld<alignof(slot_type)>(CharAlloc(set->alloc_ref()),
|
|
3811
|
-
sizeof(slot_type));
|
|
3092
|
+
template <class K>
|
|
3093
|
+
ABSL_ATTRIBUTE_ALWAYS_INLINE size_t hash_of(const K& key) const {
|
|
3094
|
+
return HashElement<hasher, kIsDefaultHash>{hash_ref(),
|
|
3095
|
+
common().seed().seed()}(key);
|
|
3096
|
+
}
|
|
3097
|
+
ABSL_ATTRIBUTE_ALWAYS_INLINE size_t hash_of(slot_type* slot) const {
|
|
3098
|
+
return PolicyTraits::apply(
|
|
3099
|
+
HashElement<hasher, kIsDefaultHash>{hash_ref(), common().seed().seed()},
|
|
3100
|
+
PolicyTraits::element(slot));
|
|
3812
3101
|
}
|
|
3813
3102
|
|
|
3814
3103
|
// Casting directly from e.g. char* to slot_type* can cause compilation errors
|
|
3815
3104
|
// on objective-C. This function converts to void* first, avoiding the issue.
|
|
3816
|
-
static slot_type* to_slot(void* buf) {
|
|
3105
|
+
static ABSL_ATTRIBUTE_ALWAYS_INLINE slot_type* to_slot(void* buf) {
|
|
3106
|
+
return static_cast<slot_type*>(buf);
|
|
3107
|
+
}
|
|
3817
3108
|
|
|
3818
3109
|
// Requires that lhs does not have a full SOO slot.
|
|
3819
|
-
static void move_common(bool rhs_is_full_soo,
|
|
3110
|
+
static void move_common(bool rhs_is_full_soo, CharAlloc& rhs_alloc,
|
|
3820
3111
|
CommonFields& lhs, CommonFields&& rhs) {
|
|
3821
3112
|
if (PolicyTraits::transfer_uses_memcpy() || !rhs_is_full_soo) {
|
|
3822
3113
|
lhs = std::move(rhs);
|
|
@@ -3841,14 +3132,15 @@ class raw_hash_set {
|
|
|
3841
3132
|
}
|
|
3842
3133
|
CommonFields tmp = CommonFields(uninitialized_tag_t{});
|
|
3843
3134
|
const bool that_is_full_soo = that.is_full_soo();
|
|
3844
|
-
move_common(that_is_full_soo, that.
|
|
3135
|
+
move_common(that_is_full_soo, that.char_alloc_ref(), tmp,
|
|
3845
3136
|
std::move(that.common()));
|
|
3846
|
-
move_common(is_full_soo(),
|
|
3847
|
-
|
|
3137
|
+
move_common(is_full_soo(), char_alloc_ref(), that.common(),
|
|
3138
|
+
std::move(common()));
|
|
3139
|
+
move_common(that_is_full_soo, that.char_alloc_ref(), common(),
|
|
3140
|
+
std::move(tmp));
|
|
3848
3141
|
}
|
|
3849
3142
|
|
|
3850
|
-
void annotate_for_bug_detection_on_move(
|
|
3851
|
-
ABSL_ATTRIBUTE_UNUSED raw_hash_set& that) {
|
|
3143
|
+
void annotate_for_bug_detection_on_move([[maybe_unused]] raw_hash_set& that) {
|
|
3852
3144
|
// We only enable moved-from validation when generations are enabled (rather
|
|
3853
3145
|
// than using NDEBUG) to avoid issues in which NDEBUG is enabled in some
|
|
3854
3146
|
// translation units but not in others.
|
|
@@ -3862,7 +3154,8 @@ class raw_hash_set {
|
|
|
3862
3154
|
}
|
|
3863
3155
|
common().increment_generation();
|
|
3864
3156
|
if (!empty() && common().should_rehash_for_bug_detection_on_move()) {
|
|
3865
|
-
|
|
3157
|
+
ResizeAllocatedTableWithSeedChange(common(), GetPolicyFunctions(),
|
|
3158
|
+
capacity());
|
|
3866
3159
|
}
|
|
3867
3160
|
}
|
|
3868
3161
|
|
|
@@ -3871,11 +3164,11 @@ class raw_hash_set {
|
|
|
3871
3164
|
// We don't bother checking for this/that aliasing. We just need to avoid
|
|
3872
3165
|
// breaking the invariants in that case.
|
|
3873
3166
|
destructor_impl();
|
|
3874
|
-
move_common(that.is_full_soo(), that.
|
|
3167
|
+
move_common(that.is_full_soo(), that.char_alloc_ref(), common(),
|
|
3875
3168
|
std::move(that.common()));
|
|
3876
3169
|
hash_ref() = that.hash_ref();
|
|
3877
3170
|
eq_ref() = that.eq_ref();
|
|
3878
|
-
CopyAlloc(
|
|
3171
|
+
CopyAlloc(char_alloc_ref(), that.char_alloc_ref(),
|
|
3879
3172
|
std::integral_constant<bool, propagate_alloc>());
|
|
3880
3173
|
that.common() = CommonFields::CreateDefault<SooEnabled()>();
|
|
3881
3174
|
annotate_for_bug_detection_on_move(that);
|
|
@@ -3902,7 +3195,7 @@ class raw_hash_set {
|
|
|
3902
3195
|
}
|
|
3903
3196
|
raw_hash_set& move_assign(raw_hash_set&& that,
|
|
3904
3197
|
std::false_type /*propagate_alloc*/) {
|
|
3905
|
-
if (
|
|
3198
|
+
if (char_alloc_ref() == that.char_alloc_ref()) {
|
|
3906
3199
|
return assign_impl<false>(std::move(that));
|
|
3907
3200
|
}
|
|
3908
3201
|
// Aliasing can't happen here because allocs would compare equal above.
|
|
@@ -3918,48 +3211,76 @@ class raw_hash_set {
|
|
|
3918
3211
|
|
|
3919
3212
|
template <class K>
|
|
3920
3213
|
std::pair<iterator, bool> find_or_prepare_insert_soo(const K& key) {
|
|
3214
|
+
ABSL_SWISSTABLE_ASSERT(is_soo());
|
|
3215
|
+
bool force_sampling;
|
|
3921
3216
|
if (empty()) {
|
|
3922
|
-
|
|
3923
|
-
if (infoz.IsSampled()) {
|
|
3924
|
-
resize_with_soo_infoz(infoz);
|
|
3925
|
-
} else {
|
|
3217
|
+
if (!should_sample_soo()) {
|
|
3926
3218
|
common().set_full_soo();
|
|
3927
|
-
return {
|
|
3219
|
+
return {single_iterator(), true};
|
|
3928
3220
|
}
|
|
3929
|
-
|
|
3930
|
-
|
|
3931
|
-
return {
|
|
3221
|
+
force_sampling = true;
|
|
3222
|
+
} else if (equal_to(key, single_slot())) {
|
|
3223
|
+
return {single_iterator(), false};
|
|
3932
3224
|
} else {
|
|
3933
|
-
|
|
3934
|
-
}
|
|
3935
|
-
|
|
3936
|
-
|
|
3225
|
+
force_sampling = false;
|
|
3226
|
+
}
|
|
3227
|
+
ABSL_SWISSTABLE_ASSERT(capacity() == 1);
|
|
3228
|
+
constexpr bool kUseMemcpy =
|
|
3229
|
+
PolicyTraits::transfer_uses_memcpy() && SooEnabled();
|
|
3230
|
+
size_t index = GrowSooTableToNextCapacityAndPrepareInsert<
|
|
3231
|
+
kUseMemcpy ? OptimalMemcpySizeForSooSlotTransfer(sizeof(slot_type)) : 0,
|
|
3232
|
+
kUseMemcpy>(common(), GetPolicyFunctions(),
|
|
3233
|
+
HashKey<hasher, K, kIsDefaultHash>{hash_ref(), key},
|
|
3234
|
+
force_sampling);
|
|
3937
3235
|
return {iterator_at(index), true};
|
|
3938
3236
|
}
|
|
3939
3237
|
|
|
3940
3238
|
template <class K>
|
|
3941
|
-
std::pair<iterator, bool>
|
|
3239
|
+
std::pair<iterator, bool> find_or_prepare_insert_small(const K& key) {
|
|
3240
|
+
ABSL_SWISSTABLE_ASSERT(is_small());
|
|
3241
|
+
if constexpr (SooEnabled()) {
|
|
3242
|
+
return find_or_prepare_insert_soo(key);
|
|
3243
|
+
}
|
|
3244
|
+
if (!empty()) {
|
|
3245
|
+
if (equal_to(key, single_slot())) {
|
|
3246
|
+
return {single_iterator(), false};
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
return {iterator_at_ptr(PrepareInsertSmallNonSoo(
|
|
3250
|
+
common(), GetPolicyFunctions(),
|
|
3251
|
+
HashKey<hasher, K, kIsDefaultHash>{hash_ref(), key})),
|
|
3252
|
+
true};
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
template <class K>
|
|
3256
|
+
std::pair<iterator, bool> find_or_prepare_insert_large(const K& key) {
|
|
3942
3257
|
ABSL_SWISSTABLE_ASSERT(!is_soo());
|
|
3943
3258
|
prefetch_heap_block();
|
|
3944
|
-
|
|
3259
|
+
const size_t hash = hash_of(key);
|
|
3945
3260
|
auto seq = probe(common(), hash);
|
|
3261
|
+
const h2_t h2 = H2(hash);
|
|
3946
3262
|
const ctrl_t* ctrl = control();
|
|
3947
3263
|
while (true) {
|
|
3264
|
+
#ifndef ABSL_HAVE_MEMORY_SANITIZER
|
|
3265
|
+
absl::PrefetchToLocalCache(slot_array() + seq.offset());
|
|
3266
|
+
#endif
|
|
3948
3267
|
Group g{ctrl + seq.offset()};
|
|
3949
|
-
for (uint32_t i : g.Match(
|
|
3950
|
-
if (ABSL_PREDICT_TRUE(
|
|
3951
|
-
EqualElement<K>{key, eq_ref()},
|
|
3952
|
-
PolicyTraits::element(slot_array() + seq.offset(i)))))
|
|
3268
|
+
for (uint32_t i : g.Match(h2)) {
|
|
3269
|
+
if (ABSL_PREDICT_TRUE(equal_to(key, slot_array() + seq.offset(i))))
|
|
3953
3270
|
return {iterator_at(seq.offset(i)), false};
|
|
3954
3271
|
}
|
|
3955
3272
|
auto mask_empty = g.MaskEmpty();
|
|
3956
3273
|
if (ABSL_PREDICT_TRUE(mask_empty)) {
|
|
3957
|
-
size_t target = seq.offset(
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3274
|
+
size_t target = seq.offset(mask_empty.LowestBitSet());
|
|
3275
|
+
size_t index =
|
|
3276
|
+
SwisstableGenerationsEnabled()
|
|
3277
|
+
? PrepareInsertLargeGenerationsEnabled(
|
|
3278
|
+
common(), GetPolicyFunctions(), hash,
|
|
3279
|
+
FindInfo{target, seq.index()},
|
|
3280
|
+
HashKey<hasher, K, kIsDefaultHash>{hash_ref(), key})
|
|
3281
|
+
: PrepareInsertLarge(common(), GetPolicyFunctions(), hash,
|
|
3282
|
+
FindInfo{target, seq.index()});
|
|
3283
|
+
return {iterator_at(index), true};
|
|
3963
3284
|
}
|
|
3964
3285
|
seq.next();
|
|
3965
3286
|
ABSL_SWISSTABLE_ASSERT(seq.index() <= capacity() && "full table!");
|
|
@@ -3969,13 +3290,18 @@ class raw_hash_set {
|
|
|
3969
3290
|
protected:
|
|
3970
3291
|
// Asserts for correctness that we run on find/find_or_prepare_insert.
|
|
3971
3292
|
template <class K>
|
|
3972
|
-
void AssertOnFind(
|
|
3293
|
+
void AssertOnFind([[maybe_unused]] const K& key) {
|
|
3973
3294
|
AssertHashEqConsistent(key);
|
|
3974
3295
|
AssertNotDebugCapacity();
|
|
3975
3296
|
}
|
|
3976
3297
|
|
|
3977
3298
|
// Asserts that the capacity is not a sentinel invalid value.
|
|
3978
3299
|
void AssertNotDebugCapacity() const {
|
|
3300
|
+
#ifdef NDEBUG
|
|
3301
|
+
if (!SwisstableGenerationsEnabled()) {
|
|
3302
|
+
return;
|
|
3303
|
+
}
|
|
3304
|
+
#endif
|
|
3979
3305
|
if (ABSL_PREDICT_TRUE(capacity() <
|
|
3980
3306
|
InvalidCapacity::kAboveMaxValidCapacity)) {
|
|
3981
3307
|
return;
|
|
@@ -3983,8 +3309,11 @@ class raw_hash_set {
|
|
|
3983
3309
|
assert(capacity() != InvalidCapacity::kReentrance &&
|
|
3984
3310
|
"Reentrant container access during element construction/destruction "
|
|
3985
3311
|
"is not allowed.");
|
|
3986
|
-
|
|
3987
|
-
|
|
3312
|
+
if constexpr (SwisstableAssertAccessToDestroyedTable()) {
|
|
3313
|
+
if (capacity() == InvalidCapacity::kDestroyed) {
|
|
3314
|
+
ABSL_RAW_LOG(FATAL, "Use of destroyed hash table.");
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3988
3317
|
if (SwisstableGenerationsEnabled() &&
|
|
3989
3318
|
ABSL_PREDICT_FALSE(capacity() >= InvalidCapacity::kMovedFrom)) {
|
|
3990
3319
|
if (capacity() == InvalidCapacity::kSelfMovedFrom) {
|
|
@@ -4015,29 +3344,25 @@ class raw_hash_set {
|
|
|
4015
3344
|
}
|
|
4016
3345
|
if (empty()) return;
|
|
4017
3346
|
|
|
4018
|
-
const size_t hash_of_arg =
|
|
4019
|
-
const auto assert_consistent = [&](const ctrl_t*,
|
|
4020
|
-
const
|
|
4021
|
-
const bool is_key_equal =
|
|
4022
|
-
PolicyTraits::apply(EqualElement<K>{key, eq_ref()}, element);
|
|
3347
|
+
const size_t hash_of_arg = hash_of(key);
|
|
3348
|
+
const auto assert_consistent = [&](const ctrl_t*, void* slot) {
|
|
3349
|
+
const bool is_key_equal = equal_to(key, to_slot(slot));
|
|
4023
3350
|
if (!is_key_equal) return;
|
|
4024
3351
|
|
|
4025
|
-
const
|
|
4026
|
-
|
|
4027
|
-
ABSL_ATTRIBUTE_UNUSED const bool is_hash_equal =
|
|
4028
|
-
hash_of_arg == hash_of_slot;
|
|
3352
|
+
[[maybe_unused]] const bool is_hash_equal =
|
|
3353
|
+
hash_of_arg == hash_of(to_slot(slot));
|
|
4029
3354
|
assert((!is_key_equal || is_hash_equal) &&
|
|
4030
3355
|
"eq(k1, k2) must imply that hash(k1) == hash(k2). "
|
|
4031
3356
|
"hash/eq functors are inconsistent.");
|
|
4032
3357
|
};
|
|
4033
3358
|
|
|
4034
|
-
if (
|
|
4035
|
-
assert_consistent(/*unused*/ nullptr,
|
|
3359
|
+
if (is_small()) {
|
|
3360
|
+
assert_consistent(/*unused*/ nullptr, single_slot());
|
|
4036
3361
|
return;
|
|
4037
3362
|
}
|
|
4038
3363
|
// We only do validation for small tables so that it's constant time.
|
|
4039
3364
|
if (capacity() > 16) return;
|
|
4040
|
-
IterateOverFullSlots(common(),
|
|
3365
|
+
IterateOverFullSlots(common(), sizeof(slot_type), assert_consistent);
|
|
4041
3366
|
}
|
|
4042
3367
|
|
|
4043
3368
|
// Attempts to find `key` in the table; if it isn't found, returns an iterator
|
|
@@ -4046,8 +3371,8 @@ class raw_hash_set {
|
|
|
4046
3371
|
template <class K>
|
|
4047
3372
|
std::pair<iterator, bool> find_or_prepare_insert(const K& key) {
|
|
4048
3373
|
AssertOnFind(key);
|
|
4049
|
-
if (
|
|
4050
|
-
return
|
|
3374
|
+
if (is_small()) return find_or_prepare_insert_small(key);
|
|
3375
|
+
return find_or_prepare_insert_large(key);
|
|
4051
3376
|
}
|
|
4052
3377
|
|
|
4053
3378
|
// Constructs the value in the space pointed by the iterator. This only works
|
|
@@ -4062,7 +3387,10 @@ class raw_hash_set {
|
|
|
4062
3387
|
void emplace_at(iterator iter, Args&&... args) {
|
|
4063
3388
|
construct(iter.slot(), std::forward<Args>(args)...);
|
|
4064
3389
|
|
|
4065
|
-
|
|
3390
|
+
// When is_small, find calls find_small and if size is 0, then it will
|
|
3391
|
+
// return an end iterator. This can happen in the raw_hash_set copy ctor.
|
|
3392
|
+
assert((is_small() ||
|
|
3393
|
+
PolicyTraits::apply(FindElement{*this}, *iter) == iter) &&
|
|
4066
3394
|
"constructed value does not match the lookup key");
|
|
4067
3395
|
}
|
|
4068
3396
|
|
|
@@ -4072,6 +3400,10 @@ class raw_hash_set {
|
|
|
4072
3400
|
const_iterator iterator_at(size_t i) const ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
4073
3401
|
return const_cast<raw_hash_set*>(this)->iterator_at(i);
|
|
4074
3402
|
}
|
|
3403
|
+
iterator iterator_at_ptr(std::pair<ctrl_t*, void*> ptrs)
|
|
3404
|
+
ABSL_ATTRIBUTE_LIFETIME_BOUND {
|
|
3405
|
+
return {ptrs.first, to_slot(ptrs.second), common().generation_ptr()};
|
|
3406
|
+
}
|
|
4075
3407
|
|
|
4076
3408
|
reference unchecked_deref(iterator it) { return it.unchecked_deref(); }
|
|
4077
3409
|
|
|
@@ -4089,16 +3421,13 @@ class raw_hash_set {
|
|
|
4089
3421
|
//
|
|
4090
3422
|
// See `CapacityToGrowth()`.
|
|
4091
3423
|
size_t growth_left() const {
|
|
4092
|
-
ABSL_SWISSTABLE_ASSERT(!is_soo());
|
|
4093
3424
|
return common().growth_left();
|
|
4094
3425
|
}
|
|
4095
3426
|
|
|
4096
3427
|
GrowthInfo& growth_info() {
|
|
4097
|
-
ABSL_SWISSTABLE_ASSERT(!is_soo());
|
|
4098
3428
|
return common().growth_info();
|
|
4099
3429
|
}
|
|
4100
3430
|
GrowthInfo growth_info() const {
|
|
4101
|
-
ABSL_SWISSTABLE_ASSERT(!is_soo());
|
|
4102
3431
|
return common().growth_info();
|
|
4103
3432
|
}
|
|
4104
3433
|
|
|
@@ -4125,16 +3454,32 @@ class raw_hash_set {
|
|
|
4125
3454
|
}
|
|
4126
3455
|
slot_type* soo_slot() {
|
|
4127
3456
|
ABSL_SWISSTABLE_ASSERT(is_soo());
|
|
4128
|
-
|
|
3457
|
+
ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(
|
|
3458
|
+
static_cast<slot_type*>(common().soo_data()));
|
|
4129
3459
|
}
|
|
4130
3460
|
const slot_type* soo_slot() const {
|
|
4131
|
-
|
|
3461
|
+
ABSL_SWISSTABLE_IGNORE_UNINITIALIZED_RETURN(
|
|
3462
|
+
const_cast<raw_hash_set*>(this)->soo_slot());
|
|
4132
3463
|
}
|
|
4133
|
-
|
|
4134
|
-
|
|
3464
|
+
slot_type* single_slot() {
|
|
3465
|
+
ABSL_SWISSTABLE_ASSERT(is_small());
|
|
3466
|
+
return SooEnabled() ? soo_slot() : slot_array();
|
|
4135
3467
|
}
|
|
4136
|
-
|
|
4137
|
-
return const_cast<raw_hash_set*>(this)->
|
|
3468
|
+
const slot_type* single_slot() const {
|
|
3469
|
+
return const_cast<raw_hash_set*>(this)->single_slot();
|
|
3470
|
+
}
|
|
3471
|
+
void decrement_small_size() {
|
|
3472
|
+
ABSL_SWISSTABLE_ASSERT(is_small());
|
|
3473
|
+
SooEnabled() ? common().set_empty_soo() : common().decrement_size();
|
|
3474
|
+
if (!SooEnabled()) {
|
|
3475
|
+
SanitizerPoisonObject(single_slot());
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
iterator single_iterator() {
|
|
3479
|
+
return {SooControl(), single_slot(), common().generation_ptr()};
|
|
3480
|
+
}
|
|
3481
|
+
const_iterator single_iterator() const {
|
|
3482
|
+
return const_cast<raw_hash_set*>(this)->single_iterator();
|
|
4138
3483
|
}
|
|
4139
3484
|
HashtablezInfoHandle infoz() {
|
|
4140
3485
|
ABSL_SWISSTABLE_ASSERT(!is_soo());
|
|
@@ -4145,49 +3490,116 @@ class raw_hash_set {
|
|
|
4145
3490
|
const hasher& hash_ref() const { return settings_.template get<1>(); }
|
|
4146
3491
|
key_equal& eq_ref() { return settings_.template get<2>(); }
|
|
4147
3492
|
const key_equal& eq_ref() const { return settings_.template get<2>(); }
|
|
4148
|
-
|
|
4149
|
-
const
|
|
3493
|
+
CharAlloc& char_alloc_ref() { return settings_.template get<3>(); }
|
|
3494
|
+
const CharAlloc& char_alloc_ref() const {
|
|
4150
3495
|
return settings_.template get<3>();
|
|
4151
3496
|
}
|
|
4152
3497
|
|
|
4153
|
-
static
|
|
4154
|
-
auto* h = reinterpret_cast<
|
|
4155
|
-
return &h->
|
|
3498
|
+
static void* get_char_alloc_ref_fn(CommonFields& common) {
|
|
3499
|
+
auto* h = reinterpret_cast<raw_hash_set*>(&common);
|
|
3500
|
+
return &h->char_alloc_ref();
|
|
3501
|
+
}
|
|
3502
|
+
static void* get_hash_ref_fn(CommonFields& common) {
|
|
3503
|
+
auto* h = reinterpret_cast<raw_hash_set*>(&common);
|
|
3504
|
+
// TODO(b/397453582): Remove support for const hasher.
|
|
3505
|
+
return const_cast<std::remove_const_t<hasher>*>(&h->hash_ref());
|
|
4156
3506
|
}
|
|
4157
|
-
static void
|
|
3507
|
+
static void transfer_n_slots_fn(void* set, void* dst, void* src,
|
|
3508
|
+
size_t count) {
|
|
3509
|
+
auto* src_slot = to_slot(src);
|
|
3510
|
+
auto* dst_slot = to_slot(dst);
|
|
3511
|
+
|
|
4158
3512
|
auto* h = static_cast<raw_hash_set*>(set);
|
|
4159
|
-
|
|
3513
|
+
for (; count > 0; --count, ++src_slot, ++dst_slot) {
|
|
3514
|
+
h->transfer(dst_slot, src_slot);
|
|
3515
|
+
}
|
|
4160
3516
|
}
|
|
4161
|
-
// Note: dealloc_fn will only be used if we have a non-standard allocator.
|
|
4162
|
-
static void dealloc_fn(CommonFields& common, const PolicyFunctions&) {
|
|
4163
|
-
auto* set = reinterpret_cast<raw_hash_set*>(&common);
|
|
4164
3517
|
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
3518
|
+
// TODO(b/382423690): Try to type erase entire function or at least type erase
|
|
3519
|
+
// by GetKey + Hash for memcpyable types.
|
|
3520
|
+
// TODO(b/382423690): Try to type erase for big slots: sizeof(slot_type) > 16.
|
|
3521
|
+
static void transfer_unprobed_elements_to_next_capacity_fn(
|
|
3522
|
+
CommonFields& common, const ctrl_t* old_ctrl, void* old_slots,
|
|
3523
|
+
void* probed_storage,
|
|
3524
|
+
void (*encode_probed_element)(void* probed_storage, h2_t h2,
|
|
3525
|
+
size_t source_offset, size_t h1)) {
|
|
3526
|
+
const size_t new_capacity = common.capacity();
|
|
3527
|
+
const size_t old_capacity = PreviousCapacity(new_capacity);
|
|
3528
|
+
ABSL_ASSUME(old_capacity + 1 >= Group::kWidth);
|
|
3529
|
+
ABSL_ASSUME((old_capacity + 1) % Group::kWidth == 0);
|
|
4168
3530
|
|
|
4169
|
-
common
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
3531
|
+
auto* set = reinterpret_cast<raw_hash_set*>(&common);
|
|
3532
|
+
slot_type* old_slots_ptr = to_slot(old_slots);
|
|
3533
|
+
ctrl_t* new_ctrl = common.control();
|
|
3534
|
+
slot_type* new_slots = set->slot_array();
|
|
3535
|
+
|
|
3536
|
+
for (size_t group_index = 0; group_index < old_capacity;
|
|
3537
|
+
group_index += Group::kWidth) {
|
|
3538
|
+
GroupFullEmptyOrDeleted old_g(old_ctrl + group_index);
|
|
3539
|
+
std::memset(new_ctrl + group_index, static_cast<int8_t>(ctrl_t::kEmpty),
|
|
3540
|
+
Group::kWidth);
|
|
3541
|
+
std::memset(new_ctrl + group_index + old_capacity + 1,
|
|
3542
|
+
static_cast<int8_t>(ctrl_t::kEmpty), Group::kWidth);
|
|
3543
|
+
// TODO(b/382423690): try to type erase everything outside of the loop.
|
|
3544
|
+
// We will share a lot of code in expense of one function call per group.
|
|
3545
|
+
for (auto in_fixed_group_index : old_g.MaskFull()) {
|
|
3546
|
+
size_t old_index = group_index + in_fixed_group_index;
|
|
3547
|
+
slot_type* old_slot = old_slots_ptr + old_index;
|
|
3548
|
+
// TODO(b/382423690): try to avoid entire hash calculation since we need
|
|
3549
|
+
// only one new bit of h1.
|
|
3550
|
+
size_t hash = set->hash_of(old_slot);
|
|
3551
|
+
size_t h1 = H1(hash);
|
|
3552
|
+
h2_t h2 = H2(hash);
|
|
3553
|
+
size_t new_index = TryFindNewIndexWithoutProbing(
|
|
3554
|
+
h1, old_index, old_capacity, new_ctrl, new_capacity);
|
|
3555
|
+
// Note that encode_probed_element is allowed to use old_ctrl buffer
|
|
3556
|
+
// till and included the old_index.
|
|
3557
|
+
if (ABSL_PREDICT_FALSE(new_index == kProbedElementIndexSentinel)) {
|
|
3558
|
+
encode_probed_element(probed_storage, h2, old_index, h1);
|
|
3559
|
+
continue;
|
|
3560
|
+
}
|
|
3561
|
+
ABSL_SWISSTABLE_ASSERT((new_index & old_capacity) <= old_index);
|
|
3562
|
+
ABSL_SWISSTABLE_ASSERT(IsEmpty(new_ctrl[new_index]));
|
|
3563
|
+
new_ctrl[new_index] = static_cast<ctrl_t>(h2);
|
|
3564
|
+
auto* new_slot = new_slots + new_index;
|
|
3565
|
+
SanitizerUnpoisonMemoryRegion(new_slot, sizeof(slot_type));
|
|
3566
|
+
set->transfer(new_slot, old_slot);
|
|
3567
|
+
SanitizerPoisonMemoryRegion(old_slot, sizeof(slot_type));
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
4173
3570
|
}
|
|
4174
3571
|
|
|
4175
3572
|
static const PolicyFunctions& GetPolicyFunctions() {
|
|
3573
|
+
static_assert(sizeof(slot_type) <= (std::numeric_limits<uint32_t>::max)(),
|
|
3574
|
+
"Slot size is too large. Use std::unique_ptr for value type "
|
|
3575
|
+
"or use absl::node_hash_{map,set}.");
|
|
3576
|
+
static_assert(alignof(slot_type) <=
|
|
3577
|
+
size_t{(std::numeric_limits<uint16_t>::max)()});
|
|
3578
|
+
static_assert(sizeof(key_type) <=
|
|
3579
|
+
size_t{(std::numeric_limits<uint32_t>::max)()});
|
|
3580
|
+
static_assert(sizeof(value_type) <=
|
|
3581
|
+
size_t{(std::numeric_limits<uint32_t>::max)()});
|
|
3582
|
+
static constexpr size_t kBackingArrayAlignment =
|
|
3583
|
+
BackingArrayAlignment(alignof(slot_type));
|
|
4176
3584
|
static constexpr PolicyFunctions value = {
|
|
4177
|
-
sizeof(
|
|
3585
|
+
static_cast<uint32_t>(sizeof(key_type)),
|
|
3586
|
+
static_cast<uint32_t>(sizeof(value_type)),
|
|
3587
|
+
static_cast<uint32_t>(sizeof(slot_type)),
|
|
3588
|
+
static_cast<uint16_t>(alignof(slot_type)), SooEnabled(),
|
|
3589
|
+
ShouldSampleHashtablezInfoForAlloc<CharAlloc>(),
|
|
4178
3590
|
// TODO(b/328722020): try to type erase
|
|
4179
3591
|
// for standard layout and alignof(Hash) <= alignof(CommonFields).
|
|
4180
|
-
std::
|
|
4181
|
-
|
|
4182
|
-
PolicyTraits::template get_hash_slot_fn<hasher>(),
|
|
3592
|
+
std::is_empty_v<hasher> ? &GetRefForEmptyClass
|
|
3593
|
+
: &raw_hash_set::get_hash_ref_fn,
|
|
3594
|
+
PolicyTraits::template get_hash_slot_fn<hasher, kIsDefaultHash>(),
|
|
4183
3595
|
PolicyTraits::transfer_uses_memcpy()
|
|
4184
|
-
?
|
|
4185
|
-
: &raw_hash_set::
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
&
|
|
4190
|
-
|
|
3596
|
+
? TransferNRelocatable<sizeof(slot_type)>
|
|
3597
|
+
: &raw_hash_set::transfer_n_slots_fn,
|
|
3598
|
+
std::is_empty_v<Alloc> ? &GetRefForEmptyClass
|
|
3599
|
+
: &raw_hash_set::get_char_alloc_ref_fn,
|
|
3600
|
+
&AllocateBackingArray<kBackingArrayAlignment, CharAlloc>,
|
|
3601
|
+
&DeallocateBackingArray<kBackingArrayAlignment, CharAlloc>,
|
|
3602
|
+
&raw_hash_set::transfer_unprobed_elements_to_next_capacity_fn};
|
|
4191
3603
|
return value;
|
|
4192
3604
|
}
|
|
4193
3605
|
|
|
@@ -4195,9 +3607,9 @@ class raw_hash_set {
|
|
|
4195
3607
|
// CompressedTuple will ensure that sizeof is not affected by any of the empty
|
|
4196
3608
|
// fields that occur after CommonFields.
|
|
4197
3609
|
absl::container_internal::CompressedTuple<CommonFields, hasher, key_equal,
|
|
4198
|
-
|
|
3610
|
+
CharAlloc>
|
|
4199
3611
|
settings_{CommonFields::CreateDefault<SooEnabled()>(), hasher{},
|
|
4200
|
-
key_equal{},
|
|
3612
|
+
key_equal{}, CharAlloc{}};
|
|
4201
3613
|
};
|
|
4202
3614
|
|
|
4203
3615
|
// Friend access for free functions in raw_hash_set.h.
|
|
@@ -4207,25 +3619,27 @@ struct HashtableFreeFunctionsAccess {
|
|
|
4207
3619
|
if (c->empty()) {
|
|
4208
3620
|
return 0;
|
|
4209
3621
|
}
|
|
4210
|
-
if (c->
|
|
4211
|
-
auto it = c->
|
|
3622
|
+
if (c->is_small()) {
|
|
3623
|
+
auto it = c->single_iterator();
|
|
4212
3624
|
if (!pred(*it)) {
|
|
4213
3625
|
ABSL_SWISSTABLE_ASSERT(c->size() == 1 &&
|
|
4214
3626
|
"hash table was modified unexpectedly");
|
|
4215
3627
|
return 0;
|
|
4216
3628
|
}
|
|
4217
3629
|
c->destroy(it.slot());
|
|
4218
|
-
c->
|
|
3630
|
+
c->erase_meta_only_small();
|
|
4219
3631
|
return 1;
|
|
4220
3632
|
}
|
|
4221
|
-
|
|
3633
|
+
[[maybe_unused]] const size_t original_size_for_assert = c->size();
|
|
4222
3634
|
size_t num_deleted = 0;
|
|
3635
|
+
using SlotType = typename Set::slot_type;
|
|
4223
3636
|
IterateOverFullSlots(
|
|
4224
|
-
c->common(),
|
|
3637
|
+
c->common(), sizeof(SlotType),
|
|
3638
|
+
[&](const ctrl_t* ctrl, void* slot_void) {
|
|
3639
|
+
auto* slot = static_cast<SlotType*>(slot_void);
|
|
4225
3640
|
if (pred(Set::PolicyTraits::element(slot))) {
|
|
4226
3641
|
c->destroy(slot);
|
|
4227
|
-
|
|
4228
|
-
sizeof(*slot));
|
|
3642
|
+
EraseMetaOnlyLarge(c->common(), ctrl, sizeof(*slot));
|
|
4229
3643
|
++num_deleted;
|
|
4230
3644
|
}
|
|
4231
3645
|
});
|
|
@@ -4242,14 +3656,16 @@ struct HashtableFreeFunctionsAccess {
|
|
|
4242
3656
|
if (c->empty()) {
|
|
4243
3657
|
return;
|
|
4244
3658
|
}
|
|
4245
|
-
if (c->
|
|
4246
|
-
cb(*c->
|
|
3659
|
+
if (c->is_small()) {
|
|
3660
|
+
cb(*c->single_iterator());
|
|
4247
3661
|
return;
|
|
4248
3662
|
}
|
|
3663
|
+
using SlotType = typename Set::slot_type;
|
|
4249
3664
|
using ElementTypeWithConstness = decltype(*c->begin());
|
|
4250
3665
|
IterateOverFullSlots(
|
|
4251
|
-
c->common(),
|
|
4252
|
-
ElementTypeWithConstness& element =
|
|
3666
|
+
c->common(), sizeof(SlotType), [&cb](const ctrl_t*, void* slot) {
|
|
3667
|
+
ElementTypeWithConstness& element =
|
|
3668
|
+
Set::PolicyTraits::element(static_cast<SlotType*>(slot));
|
|
4253
3669
|
cb(element);
|
|
4254
3670
|
});
|
|
4255
3671
|
}
|
|
@@ -4278,20 +3694,20 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> {
|
|
|
4278
3694
|
using Traits = typename Set::PolicyTraits;
|
|
4279
3695
|
using Slot = typename Traits::slot_type;
|
|
4280
3696
|
|
|
3697
|
+
constexpr static bool kIsDefaultHash = Set::kIsDefaultHash;
|
|
3698
|
+
|
|
4281
3699
|
static size_t GetNumProbes(const Set& set,
|
|
4282
3700
|
const typename Set::key_type& key) {
|
|
4283
|
-
if (set.
|
|
3701
|
+
if (set.is_small()) return 0;
|
|
4284
3702
|
size_t num_probes = 0;
|
|
4285
|
-
size_t hash = set.
|
|
3703
|
+
const size_t hash = set.hash_of(key);
|
|
4286
3704
|
auto seq = probe(set.common(), hash);
|
|
3705
|
+
const h2_t h2 = H2(hash);
|
|
4287
3706
|
const ctrl_t* ctrl = set.control();
|
|
4288
3707
|
while (true) {
|
|
4289
3708
|
container_internal::Group g{ctrl + seq.offset()};
|
|
4290
|
-
for (uint32_t i : g.Match(
|
|
4291
|
-
if (
|
|
4292
|
-
typename Set::template EqualElement<typename Set::key_type>{
|
|
4293
|
-
key, set.eq_ref()},
|
|
4294
|
-
Traits::element(set.slot_array() + seq.offset(i))))
|
|
3709
|
+
for (uint32_t i : g.Match(h2)) {
|
|
3710
|
+
if (set.equal_to(key, set.slot_array() + seq.offset(i)))
|
|
4295
3711
|
return num_probes;
|
|
4296
3712
|
++num_probes;
|
|
4297
3713
|
}
|
|
@@ -4320,6 +3736,27 @@ struct HashtableDebugAccess<Set, absl::void_t<typename Set::raw_hash_set>> {
|
|
|
4320
3736
|
};
|
|
4321
3737
|
|
|
4322
3738
|
} // namespace hashtable_debug_internal
|
|
3739
|
+
|
|
3740
|
+
// Extern template instantiations reduce binary size and linker input size.
|
|
3741
|
+
// Function definition is in raw_hash_set.cc.
|
|
3742
|
+
extern template size_t GrowSooTableToNextCapacityAndPrepareInsert<0, false>(
|
|
3743
|
+
CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
|
|
3744
|
+
bool);
|
|
3745
|
+
extern template size_t GrowSooTableToNextCapacityAndPrepareInsert<1, true>(
|
|
3746
|
+
CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
|
|
3747
|
+
bool);
|
|
3748
|
+
extern template size_t GrowSooTableToNextCapacityAndPrepareInsert<4, true>(
|
|
3749
|
+
CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
|
|
3750
|
+
bool);
|
|
3751
|
+
extern template size_t GrowSooTableToNextCapacityAndPrepareInsert<8, true>(
|
|
3752
|
+
CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
|
|
3753
|
+
bool);
|
|
3754
|
+
#if UINTPTR_MAX == UINT64_MAX
|
|
3755
|
+
extern template size_t GrowSooTableToNextCapacityAndPrepareInsert<16, true>(
|
|
3756
|
+
CommonFields&, const PolicyFunctions&, absl::FunctionRef<size_t(size_t)>,
|
|
3757
|
+
bool);
|
|
3758
|
+
#endif
|
|
3759
|
+
|
|
4323
3760
|
} // namespace container_internal
|
|
4324
3761
|
ABSL_NAMESPACE_END
|
|
4325
3762
|
} // namespace absl
|