@nxtedition/rocksdb 15.1.2 → 15.1.4

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.
Files changed (154) hide show
  1. package/.claude/settings.local.json +15 -0
  2. package/binding.cc +79 -38
  3. package/build.sh +1 -2
  4. package/deps/rocksdb/rocksdb/BUCK +10 -8
  5. package/deps/rocksdb/rocksdb/CMakeLists.txt +27 -2
  6. package/deps/rocksdb/rocksdb/Makefile +27 -116
  7. package/deps/rocksdb/rocksdb/cache/cache_bench_tool.cc +1 -1
  8. package/deps/rocksdb/rocksdb/cache/clock_cache.cc +101 -124
  9. package/deps/rocksdb/rocksdb/cache/clock_cache.h +47 -30
  10. package/deps/rocksdb/rocksdb/db/c.cc +793 -131
  11. package/deps/rocksdb/rocksdb/db/c_test.c +571 -0
  12. package/deps/rocksdb/rocksdb/db/compact_files_test.cc +226 -0
  13. package/deps/rocksdb/rocksdb/db/compaction/compaction.h +4 -0
  14. package/deps/rocksdb/rocksdb/db/compaction/compaction_job.cc +95 -59
  15. package/deps/rocksdb/rocksdb/db/compaction/compaction_job.h +2 -2
  16. package/deps/rocksdb/rocksdb/db/compaction/compaction_job_test.cc +45 -35
  17. package/deps/rocksdb/rocksdb/db/compaction/compaction_outputs.cc +8 -4
  18. package/deps/rocksdb/rocksdb/db/compaction/compaction_outputs.h +1 -1
  19. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker.cc +11 -6
  20. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker.h +8 -2
  21. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_test.cc +47 -0
  22. package/deps/rocksdb/rocksdb/db/compaction/compaction_picker_universal.cc +12 -2
  23. package/deps/rocksdb/rocksdb/db/compaction/compaction_service_test.cc +82 -0
  24. package/deps/rocksdb/rocksdb/db/compaction/subcompaction_state.cc +2 -2
  25. package/deps/rocksdb/rocksdb/db/compaction/subcompaction_state.h +1 -1
  26. package/deps/rocksdb/rocksdb/db/db_basic_test.cc +69 -24
  27. package/deps/rocksdb/rocksdb/db/db_bloom_filter_test.cc +9 -1
  28. package/deps/rocksdb/rocksdb/db/db_compaction_test.cc +65 -0
  29. package/deps/rocksdb/rocksdb/db/db_etc3_test.cc +161 -0
  30. package/deps/rocksdb/rocksdb/db/db_filesnapshot.cc +1 -0
  31. package/deps/rocksdb/rocksdb/db/db_impl/db_impl.cc +20 -7
  32. package/deps/rocksdb/rocksdb/db/db_impl/db_impl.h +13 -0
  33. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_compaction_flush.cc +114 -39
  34. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_files.cc +3 -0
  35. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_follower.cc +3 -3
  36. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_open.cc +1 -1
  37. package/deps/rocksdb/rocksdb/db/db_impl/db_impl_secondary.cc +39 -25
  38. package/deps/rocksdb/rocksdb/db/db_iterator_test.cc +361 -0
  39. package/deps/rocksdb/rocksdb/db/db_options_test.cc +35 -0
  40. package/deps/rocksdb/rocksdb/db/db_range_del_test.cc +83 -0
  41. package/deps/rocksdb/rocksdb/db/db_test.cc +249 -4
  42. package/deps/rocksdb/rocksdb/db/db_test2.cc +3 -0
  43. package/deps/rocksdb/rocksdb/db/db_test_util.cc +2 -1
  44. package/deps/rocksdb/rocksdb/db/db_wal_test.cc +3 -2
  45. package/deps/rocksdb/rocksdb/db/flush_job_test.cc +7 -7
  46. package/deps/rocksdb/rocksdb/db/listener_test.cc +7 -17
  47. package/deps/rocksdb/rocksdb/db/memtable_list_test.cc +4 -2
  48. package/deps/rocksdb/rocksdb/db/obsolete_files_test.cc +41 -0
  49. package/deps/rocksdb/rocksdb/db/repair.cc +2 -2
  50. package/deps/rocksdb/rocksdb/db/version_edit.h +7 -4
  51. package/deps/rocksdb/rocksdb/db/version_set.cc +299 -90
  52. package/deps/rocksdb/rocksdb/db/version_set.h +56 -9
  53. package/deps/rocksdb/rocksdb/db/version_set_test.cc +41 -39
  54. package/deps/rocksdb/rocksdb/db/version_util.h +3 -2
  55. package/deps/rocksdb/rocksdb/db/wal_manager.cc +7 -1
  56. package/deps/rocksdb/rocksdb/db/wal_manager_test.cc +48 -10
  57. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_common.h +1 -0
  58. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_gflags.cc +5 -1
  59. package/deps/rocksdb/rocksdb/db_stress_tool/db_stress_test_base.cc +16 -5
  60. package/deps/rocksdb/rocksdb/env/env_test.cc +126 -41
  61. package/deps/rocksdb/rocksdb/env/fs_posix.cc +14 -7
  62. package/deps/rocksdb/rocksdb/env/io_posix.cc +304 -112
  63. package/deps/rocksdb/rocksdb/env/io_posix.h +16 -4
  64. package/deps/rocksdb/rocksdb/env/io_posix_test.cc +43 -0
  65. package/deps/rocksdb/rocksdb/folly.mk +148 -0
  66. package/deps/rocksdb/rocksdb/include/rocksdb/advanced_compression.h +29 -3
  67. package/deps/rocksdb/rocksdb/include/rocksdb/advanced_options.h +73 -0
  68. package/deps/rocksdb/rocksdb/include/rocksdb/c.h +246 -0
  69. package/deps/rocksdb/rocksdb/include/rocksdb/compaction_filter.h +0 -2
  70. package/deps/rocksdb/rocksdb/include/rocksdb/data_structure.h +15 -9
  71. package/deps/rocksdb/rocksdb/include/rocksdb/db.h +19 -9
  72. package/deps/rocksdb/rocksdb/include/rocksdb/env.h +1 -1
  73. package/deps/rocksdb/rocksdb/include/rocksdb/file_system.h +6 -4
  74. package/deps/rocksdb/rocksdb/include/rocksdb/metadata.h +14 -0
  75. package/deps/rocksdb/rocksdb/include/rocksdb/options.h +67 -6
  76. package/deps/rocksdb/rocksdb/include/rocksdb/sst_file_writer.h +1 -7
  77. package/deps/rocksdb/rocksdb/include/rocksdb/statistics.h +3 -0
  78. package/deps/rocksdb/rocksdb/include/rocksdb/thread_status.h +6 -14
  79. package/deps/rocksdb/rocksdb/include/rocksdb/utilities/backup_engine.h +8 -1
  80. package/deps/rocksdb/rocksdb/include/rocksdb/utilities/env_mirror.h +2 -2
  81. package/deps/rocksdb/rocksdb/include/rocksdb/utilities/ldb_cmd_execute_result.h +0 -4
  82. package/deps/rocksdb/rocksdb/include/rocksdb/utilities/option_change_migration.h +33 -5
  83. package/deps/rocksdb/rocksdb/include/rocksdb/utilities/stackable_db.h +6 -0
  84. package/deps/rocksdb/rocksdb/include/rocksdb/version.h +2 -2
  85. package/deps/rocksdb/rocksdb/monitoring/statistics.cc +2 -0
  86. package/deps/rocksdb/rocksdb/monitoring/thread_status_impl.cc +5 -2
  87. package/deps/rocksdb/rocksdb/monitoring/thread_status_updater.cc +2 -2
  88. package/deps/rocksdb/rocksdb/monitoring/thread_status_updater.h +6 -6
  89. package/deps/rocksdb/rocksdb/monitoring/thread_status_updater_debug.cc +2 -2
  90. package/deps/rocksdb/rocksdb/monitoring/thread_status_util.cc +10 -5
  91. package/deps/rocksdb/rocksdb/monitoring/thread_status_util.h +2 -2
  92. package/deps/rocksdb/rocksdb/options/cf_options.cc +15 -3
  93. package/deps/rocksdb/rocksdb/options/cf_options.h +7 -0
  94. package/deps/rocksdb/rocksdb/options/db_options.cc +27 -36
  95. package/deps/rocksdb/rocksdb/options/db_options.h +3 -2
  96. package/deps/rocksdb/rocksdb/options/options.cc +4 -0
  97. package/deps/rocksdb/rocksdb/options/options_helper.cc +8 -2
  98. package/deps/rocksdb/rocksdb/options/options_settable_test.cc +4 -1
  99. package/deps/rocksdb/rocksdb/options/options_test.cc +19 -3
  100. package/deps/rocksdb/rocksdb/src.mk +1 -1
  101. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_builder.cc +155 -32
  102. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_builder.h +7 -3
  103. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_iterator.cc +169 -125
  104. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_iterator.h +22 -7
  105. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_reader.cc +43 -24
  106. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_reader.h +9 -5
  107. package/deps/rocksdb/rocksdb/table/block_based/block_based_table_reader_test.cc +9 -8
  108. package/deps/rocksdb/rocksdb/table/block_based/filter_block.h +17 -0
  109. package/deps/rocksdb/rocksdb/table/block_based/filter_policy.cc +15 -5
  110. package/deps/rocksdb/rocksdb/table/block_based/filter_policy_internal.h +13 -18
  111. package/deps/rocksdb/rocksdb/table/block_based/full_filter_block.cc +29 -0
  112. package/deps/rocksdb/rocksdb/table/block_based/full_filter_block.h +6 -0
  113. package/deps/rocksdb/rocksdb/table/block_based/full_filter_block_test.cc +15 -0
  114. package/deps/rocksdb/rocksdb/table/block_based/index_builder.cc +79 -19
  115. package/deps/rocksdb/rocksdb/table/block_based/index_builder.h +48 -20
  116. package/deps/rocksdb/rocksdb/table/block_based/partitioned_filter_block.cc +51 -0
  117. package/deps/rocksdb/rocksdb/table/block_based/partitioned_filter_block.h +19 -0
  118. package/deps/rocksdb/rocksdb/table/block_based/user_defined_index_wrapper.h +1 -1
  119. package/deps/rocksdb/rocksdb/table/external_table.cc +2 -2
  120. package/deps/rocksdb/rocksdb/table/sst_file_dumper.cc +3 -2
  121. package/deps/rocksdb/rocksdb/table/sst_file_dumper.h +3 -1
  122. package/deps/rocksdb/rocksdb/table/table_builder.h +5 -0
  123. package/deps/rocksdb/rocksdb/table/table_reader.h +4 -2
  124. package/deps/rocksdb/rocksdb/table/table_test.cc +48 -39
  125. package/deps/rocksdb/rocksdb/test_util/sync_point.cc +4 -0
  126. package/deps/rocksdb/rocksdb/test_util/sync_point.h +32 -0
  127. package/deps/rocksdb/rocksdb/test_util/testutil.h +6 -2
  128. package/deps/rocksdb/rocksdb/tools/db_bench_tool.cc +14 -4
  129. package/deps/rocksdb/rocksdb/tools/ldb_cmd.cc +8 -5
  130. package/deps/rocksdb/rocksdb/tools/ldb_cmd_test.cc +3 -2
  131. package/deps/rocksdb/rocksdb/tools/sst_dump_tool.cc +63 -12
  132. package/deps/rocksdb/rocksdb/util/auto_tune_compressor.cc +16 -1
  133. package/deps/rocksdb/rocksdb/util/auto_tune_compressor.h +5 -1
  134. package/deps/rocksdb/rocksdb/util/bit_fields.h +133 -23
  135. package/deps/rocksdb/rocksdb/util/bloom_test.cc +2 -5
  136. package/deps/rocksdb/rocksdb/util/compression.cc +51 -23
  137. package/deps/rocksdb/rocksdb/util/compression_test.cc +525 -270
  138. package/deps/rocksdb/rocksdb/util/filter_bench.cc +3 -4
  139. package/deps/rocksdb/rocksdb/util/simple_mixed_compressor.cc +11 -2
  140. package/deps/rocksdb/rocksdb/util/simple_mixed_compressor.h +4 -1
  141. package/deps/rocksdb/rocksdb/util/slice_test.cc +92 -0
  142. package/deps/rocksdb/rocksdb/util/thread_list_test.cc +2 -2
  143. package/deps/rocksdb/rocksdb/util/thread_operation.h +2 -2
  144. package/deps/rocksdb/rocksdb/util/threadpool_imp.cc +2 -2
  145. package/deps/rocksdb/rocksdb/utilities/backup/backup_engine.cc +19 -2
  146. package/deps/rocksdb/rocksdb/utilities/backup/backup_engine_test.cc +75 -0
  147. package/deps/rocksdb/rocksdb/utilities/checkpoint/checkpoint_test.cc +1 -0
  148. package/deps/rocksdb/rocksdb/utilities/option_change_migration/option_change_migration.cc +303 -111
  149. package/deps/rocksdb/rocksdb/utilities/option_change_migration/option_change_migration_test.cc +379 -0
  150. package/deps/rocksdb/rocksdb.gyp +1 -0
  151. package/iterator.js +66 -70
  152. package/package.json +6 -6
  153. package/prebuilds/darwin-arm64/@nxtedition+rocksdb.node +0 -0
  154. package/deps/rocksdb/rocksdb/table/block_based/index_builder_test.cc +0 -183
@@ -1752,26 +1752,6 @@ inline void GetHomeIndexAndShift(uint64_t length_info, uint64_t hash,
1752
1752
  assert(*home < LengthInfoToUsedLength(length_info));
1753
1753
  }
1754
1754
 
1755
- inline int GetShiftFromNextWithShift(uint64_t next_with_shift) {
1756
- return BitwiseAnd(next_with_shift,
1757
- AutoHyperClockTable::HandleImpl::kShiftMask);
1758
- }
1759
-
1760
- inline size_t GetNextFromNextWithShift(uint64_t next_with_shift) {
1761
- return static_cast<size_t>(next_with_shift >>
1762
- AutoHyperClockTable::HandleImpl::kNextShift);
1763
- }
1764
-
1765
- inline uint64_t MakeNextWithShift(size_t next, int shift) {
1766
- return (uint64_t{next} << AutoHyperClockTable::HandleImpl::kNextShift) |
1767
- static_cast<uint64_t>(shift);
1768
- }
1769
-
1770
- inline uint64_t MakeNextWithShiftEnd(size_t head, int shift) {
1771
- return AutoHyperClockTable::HandleImpl::kNextEndFlags |
1772
- MakeNextWithShift(head, shift);
1773
- }
1774
-
1775
1755
  // Helper function for Lookup
1776
1756
  inline bool MatchAndRef(const UniqueId64x2* hashed_key, const ClockHandle& h,
1777
1757
  int shift = 0, size_t home = 0,
@@ -1821,36 +1801,39 @@ inline bool MatchAndRef(const UniqueId64x2* hashed_key, const ClockHandle& h,
1821
1801
  }
1822
1802
  }
1823
1803
 
1804
+ using NextWithShift = AutoHyperClockTable::HandleImpl::NextWithShift;
1805
+
1824
1806
  // Assumes a chain rewrite lock prevents concurrent modification of
1825
1807
  // these chain pointers
1826
1808
  void UpgradeShiftsOnRange(AutoHyperClockTable::HandleImpl* arr,
1827
- size_t& frontier, uint64_t stop_before_or_new_tail,
1828
- int old_shift, int new_shift) {
1809
+ size_t& frontier,
1810
+ NextWithShift stop_before_or_new_tail, int old_shift,
1811
+ int new_shift) {
1829
1812
  assert(frontier != SIZE_MAX);
1830
1813
  assert(new_shift == old_shift + 1);
1831
1814
  (void)old_shift;
1832
1815
  (void)new_shift;
1833
- using HandleImpl = AutoHyperClockTable::HandleImpl;
1834
1816
  for (;;) {
1835
- uint64_t next_with_shift = arr[frontier].chain_next_with_shift.Load();
1836
- assert(GetShiftFromNextWithShift(next_with_shift) == old_shift);
1817
+ NextWithShift next_with_shift = arr[frontier].chain_next_with_shift.Load();
1818
+ assert(next_with_shift.GetShift() == old_shift);
1837
1819
  if (next_with_shift == stop_before_or_new_tail) {
1838
1820
  // Stopping at entry with pointer matching "stop before"
1839
- assert(!HandleImpl::IsEnd(next_with_shift));
1821
+ assert(!next_with_shift.IsEnd());
1840
1822
  return;
1841
1823
  }
1842
- if (HandleImpl::IsEnd(next_with_shift)) {
1824
+ if (next_with_shift.IsEnd()) {
1843
1825
  // Also update tail to new tail
1844
- assert(HandleImpl::IsEnd(stop_before_or_new_tail));
1826
+ assert(stop_before_or_new_tail.IsEnd());
1845
1827
  arr[frontier].chain_next_with_shift.Store(stop_before_or_new_tail);
1846
1828
  // Mark nothing left to upgrade
1847
1829
  frontier = SIZE_MAX;
1848
1830
  return;
1849
1831
  }
1850
1832
  // Next is another entry to process, so upgrade and advance frontier
1851
- arr[frontier].chain_next_with_shift.FetchAdd(1U);
1852
- assert(GetShiftFromNextWithShift(next_with_shift + 1) == new_shift);
1853
- frontier = GetNextFromNextWithShift(next_with_shift);
1833
+ arr[frontier].chain_next_with_shift.Apply(
1834
+ NextWithShift::Shift::PlusTransformPromiseNoOverflow(1U));
1835
+ assert(next_with_shift.GetShift() + 1 == new_shift);
1836
+ frontier = next_with_shift.GetNext();
1854
1837
  }
1855
1838
  }
1856
1839
 
@@ -1888,19 +1871,19 @@ class AutoHyperClockTable::ChainRewriteLock {
1888
1871
  // RAII wrap existing lock held (or end)
1889
1872
  explicit ChainRewriteLock(HandleImpl* h,
1890
1873
  RelaxedAtomic<uint64_t>& /*yield_count*/,
1891
- uint64_t already_locked_or_end)
1874
+ NextWithShift already_locked_or_end)
1892
1875
  : head_ptr_(&h->head_next_with_shift) {
1893
1876
  saved_head_ = already_locked_or_end;
1894
1877
  // already locked or end
1895
- assert(saved_head_ & HandleImpl::kHeadLocked);
1878
+ assert(saved_head_.IsLocked());
1896
1879
  }
1897
1880
 
1898
1881
  ~ChainRewriteLock() {
1899
1882
  if (!IsEnd()) {
1900
1883
  // Release lock
1901
- uint64_t old = head_ptr_->FetchAnd(~HandleImpl::kHeadLocked);
1902
- (void)old;
1903
- assert((old & HandleImpl::kNextEndFlags) == HandleImpl::kHeadLocked);
1884
+ NextWithShift old;
1885
+ head_ptr_->Apply(NextWithShift::LockedFlag::ClearTransform(), &old);
1886
+ assert(old.IsLockedNotEnd());
1904
1887
  }
1905
1888
  }
1906
1889
 
@@ -1910,12 +1893,13 @@ class AutoHyperClockTable::ChainRewriteLock {
1910
1893
  }
1911
1894
 
1912
1895
  // Expected current state, assuming no parallel updates.
1913
- uint64_t GetSavedHead() const { return saved_head_; }
1896
+ NextWithShift GetSavedHead() const { return saved_head_; }
1914
1897
 
1915
- bool CasUpdate(uint64_t next_with_shift,
1898
+ bool CasUpdate(NextWithShift next_with_shift,
1916
1899
  RelaxedAtomic<uint64_t>& yield_count) {
1917
- uint64_t new_head = next_with_shift | HandleImpl::kHeadLocked;
1918
- uint64_t expected = GetSavedHead();
1900
+ NextWithShift new_head =
1901
+ next_with_shift.With<NextWithShift::LockedFlag>(true);
1902
+ NextWithShift expected = GetSavedHead();
1919
1903
  bool success = head_ptr_->CasStrong(expected, new_head);
1920
1904
  if (success) {
1921
1905
  // Ensure IsEnd() is kept up-to-date, including for dtor
@@ -1924,7 +1908,7 @@ class AutoHyperClockTable::ChainRewriteLock {
1924
1908
  // Parallel update to head, such as Insert()
1925
1909
  if (IsEnd()) {
1926
1910
  // Didn't previously hold a lock
1927
- if (HandleImpl::IsEnd(expected)) {
1911
+ if (expected.IsEnd()) {
1928
1912
  // Still don't need to
1929
1913
  saved_head_ = expected;
1930
1914
  } else {
@@ -1933,28 +1917,25 @@ class AutoHyperClockTable::ChainRewriteLock {
1933
1917
  }
1934
1918
  } else {
1935
1919
  // Parallel update must preserve our lock
1936
- assert((expected & HandleImpl::kNextEndFlags) ==
1937
- HandleImpl::kHeadLocked);
1920
+ assert(expected.IsLockedNotEnd());
1938
1921
  saved_head_ = expected;
1939
1922
  }
1940
1923
  }
1941
1924
  return success;
1942
1925
  }
1943
1926
 
1944
- bool IsEnd() const { return HandleImpl::IsEnd(saved_head_); }
1927
+ bool IsEnd() const { return saved_head_.IsEnd(); }
1945
1928
 
1946
1929
  private:
1947
1930
  void Acquire(RelaxedAtomic<uint64_t>& yield_count) {
1948
1931
  for (;;) {
1949
1932
  // Acquire removal lock on the chain
1950
- uint64_t old_head = head_ptr_->FetchOr(HandleImpl::kHeadLocked);
1951
- if ((old_head & HandleImpl::kNextEndFlags) != HandleImpl::kHeadLocked) {
1933
+ NextWithShift old_head;
1934
+ head_ptr_->Apply(NextWithShift::LockedFlag::SetTransform(), &old_head,
1935
+ &saved_head_);
1936
+ if (!old_head.IsLockedNotEnd()) {
1952
1937
  // Either acquired the lock or lock not needed (end)
1953
- assert((old_head & HandleImpl::kNextEndFlags) == 0 ||
1954
- (old_head & HandleImpl::kNextEndFlags) ==
1955
- HandleImpl::kNextEndFlags);
1956
-
1957
- saved_head_ = old_head | HandleImpl::kHeadLocked;
1938
+ assert(old_head.IsEnd() == old_head.IsLocked());
1958
1939
  break;
1959
1940
  }
1960
1941
  // NOTE: one of the few yield-wait loops, which is rare enough in practice
@@ -1965,8 +1946,8 @@ class AutoHyperClockTable::ChainRewriteLock {
1965
1946
  }
1966
1947
  }
1967
1948
 
1968
- AcqRelAtomic<uint64_t>* head_ptr_;
1969
- uint64_t saved_head_;
1949
+ AcqRelBitFieldsAtomic<NextWithShift>* head_ptr_;
1950
+ NextWithShift saved_head_;
1970
1951
  };
1971
1952
 
1972
1953
  AutoHyperClockTable::AutoHyperClockTable(
@@ -2021,9 +2002,9 @@ AutoHyperClockTable::AutoHyperClockTable(
2021
2002
  #endif
2022
2003
  if (major + i < used_length) {
2023
2004
  array_[i].head_next_with_shift.StoreRelaxed(
2024
- MakeNextWithShiftEnd(i, max_shift));
2005
+ NextWithShift::MakeEnd(i, max_shift));
2025
2006
  array_[major + i].head_next_with_shift.StoreRelaxed(
2026
- MakeNextWithShiftEnd(major + i, max_shift));
2007
+ NextWithShift::MakeEnd(major + i, max_shift));
2027
2008
  #ifndef NDEBUG // Extra invariant checking
2028
2009
  GetHomeIndexAndShift(length_info, i, &home, &shift);
2029
2010
  assert(home == i);
@@ -2034,7 +2015,7 @@ AutoHyperClockTable::AutoHyperClockTable(
2034
2015
  #endif
2035
2016
  } else {
2036
2017
  array_[i].head_next_with_shift.StoreRelaxed(
2037
- MakeNextWithShiftEnd(i, min_shift));
2018
+ NextWithShift::MakeEnd(i, min_shift));
2038
2019
  #ifndef NDEBUG // Extra invariant checking
2039
2020
  GetHomeIndexAndShift(length_info, i, &home, &shift);
2040
2021
  assert(home == i);
@@ -2066,8 +2047,10 @@ AutoHyperClockTable::~AutoHyperClockTable() {
2066
2047
  // just a reasonable frontier past what we expect to have written.
2067
2048
  #ifdef MUST_FREE_HEAP_ALLOCATIONS
2068
2049
  for (size_t i = used_end; i < array_.Count() && i < used_end + 64U; i++) {
2069
- assert(array_[i].head_next_with_shift.LoadRelaxed() == 0);
2070
- assert(array_[i].chain_next_with_shift.LoadRelaxed() == 0);
2050
+ assert(array_[i].head_next_with_shift.LoadRelaxed() ==
2051
+ HandleImpl::kUnusedMarker);
2052
+ assert(array_[i].chain_next_with_shift.LoadRelaxed() ==
2053
+ HandleImpl::kUnusedMarker);
2071
2054
  assert(array_[i].meta.LoadRelaxed() == 0);
2072
2055
  }
2073
2056
  #endif // MUST_FREE_HEAP_ALLOCATIONS
@@ -2089,11 +2072,9 @@ AutoHyperClockTable::~AutoHyperClockTable() {
2089
2072
  usage_.FetchSubRelaxed(h.total_charge);
2090
2073
  occupancy_.FetchSubRelaxed(1U);
2091
2074
  was_populated[i] = true;
2092
- if (!HandleImpl::IsEnd(h.chain_next_with_shift.LoadRelaxed())) {
2093
- assert((h.chain_next_with_shift.LoadRelaxed() &
2094
- HandleImpl::kHeadLocked) == 0);
2095
- size_t next =
2096
- GetNextFromNextWithShift(h.chain_next_with_shift.LoadRelaxed());
2075
+ if (!h.chain_next_with_shift.LoadRelaxed().IsEnd()) {
2076
+ assert(!h.chain_next_with_shift.LoadRelaxed().IsLocked());
2077
+ size_t next = h.chain_next_with_shift.LoadRelaxed().GetNext();
2097
2078
  assert(!was_pointed_to[next]);
2098
2079
  was_pointed_to[next] = true;
2099
2080
  }
@@ -2105,9 +2086,8 @@ AutoHyperClockTable::~AutoHyperClockTable() {
2105
2086
  break;
2106
2087
  }
2107
2088
  #ifndef NDEBUG // Extra invariant checking
2108
- if (!HandleImpl::IsEnd(h.head_next_with_shift.LoadRelaxed())) {
2109
- size_t next =
2110
- GetNextFromNextWithShift(h.head_next_with_shift.LoadRelaxed());
2089
+ if (!h.head_next_with_shift.LoadRelaxed().IsEnd()) {
2090
+ size_t next = h.head_next_with_shift.LoadRelaxed().GetNext();
2111
2091
  assert(!was_pointed_to[next]);
2112
2092
  was_pointed_to[next] = true;
2113
2093
  }
@@ -2222,10 +2202,10 @@ bool AutoHyperClockTable::Grow(InsertState& state) {
2222
2202
  // chain rewrite lock has been released.
2223
2203
  size_t old_old_home = BottomNBits(grow_home, old_shift - 1);
2224
2204
  for (;;) {
2225
- uint64_t old_old_head = array_[old_old_home].head_next_with_shift.Load();
2226
- if (GetShiftFromNextWithShift(old_old_head) >= old_shift) {
2227
- if ((old_old_head & HandleImpl::kNextEndFlags) !=
2228
- HandleImpl::kHeadLocked) {
2205
+ NextWithShift old_old_head =
2206
+ array_[old_old_home].head_next_with_shift.Load();
2207
+ if (old_old_head.GetShift() >= old_shift) {
2208
+ if (!old_old_head.IsLockedNotEnd()) {
2229
2209
  break;
2230
2210
  }
2231
2211
  }
@@ -2285,8 +2265,7 @@ void AutoHyperClockTable::CatchUpLengthInfoNoWait(
2285
2265
  if (published_usable_size < known_usable_grow_home) {
2286
2266
  int old_shift = FloorLog2(next_usable_size - 1);
2287
2267
  size_t old_home = BottomNBits(published_usable_size, old_shift);
2288
- int shift = GetShiftFromNextWithShift(
2289
- array_[old_home].head_next_with_shift.Load());
2268
+ int shift = array_[old_home].head_next_with_shift.Load().GetShift();
2290
2269
  if (shift <= old_shift) {
2291
2270
  // Not ready
2292
2271
  break;
@@ -2437,9 +2416,10 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
2437
2416
  ChainRewriteLock zero_head_lock(&arr[old_home], yield_count_);
2438
2417
 
2439
2418
  // Used for locking the one chain below
2440
- uint64_t saved_one_head;
2419
+ NextWithShift saved_one_head;
2441
2420
  // One head has not been written to
2442
- assert(arr[grow_home].head_next_with_shift.Load() == 0);
2421
+ assert(arr[grow_home].head_next_with_shift.Load() ==
2422
+ HandleImpl::kUnusedMarker);
2443
2423
 
2444
2424
  // old_home will also the head of the new "zero chain" -- all entries in the
2445
2425
  // "from" chain whose next hash bit is 0. grow_home will be head of the new
@@ -2461,7 +2441,7 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
2461
2441
  assert(cur == SIZE_MAX);
2462
2442
  assert(chain_frontier_first == -1);
2463
2443
 
2464
- uint64_t next_with_shift = zero_head_lock.GetSavedHead();
2444
+ NextWithShift next_with_shift = zero_head_lock.GetSavedHead();
2465
2445
 
2466
2446
  // Find a single representative for each target chain, or scan the whole
2467
2447
  // chain if some target chain has no representative.
@@ -2474,16 +2454,16 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
2474
2454
  assert((cur == SIZE_MAX) == (zero_chain_frontier == SIZE_MAX &&
2475
2455
  one_chain_frontier == SIZE_MAX));
2476
2456
 
2477
- assert(GetShiftFromNextWithShift(next_with_shift) == old_shift);
2457
+ assert(next_with_shift.GetShift() == old_shift);
2478
2458
 
2479
2459
  // Check for end of original chain
2480
- if (HandleImpl::IsEnd(next_with_shift)) {
2460
+ if (next_with_shift.IsEnd()) {
2481
2461
  cur = SIZE_MAX;
2482
2462
  break;
2483
2463
  }
2484
2464
 
2485
2465
  // next_with_shift is not End
2486
- cur = GetNextFromNextWithShift(next_with_shift);
2466
+ cur = next_with_shift.GetNext();
2487
2467
 
2488
2468
  if (BottomNBits(arr[cur].hashed_key[1], new_shift) == old_home) {
2489
2469
  // Entry for zero chain
@@ -2522,10 +2502,10 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
2522
2502
  (zero_chain_frontier == SIZE_MAX && one_chain_frontier == SIZE_MAX));
2523
2503
 
2524
2504
  // Always update one chain's head first (safe), and mark it as locked
2525
- saved_one_head = HandleImpl::kHeadLocked |
2526
- (one_chain_frontier != SIZE_MAX
2527
- ? MakeNextWithShift(one_chain_frontier, new_shift)
2528
- : MakeNextWithShiftEnd(grow_home, new_shift));
2505
+ saved_one_head = one_chain_frontier != SIZE_MAX
2506
+ ? NextWithShift::Make(one_chain_frontier, new_shift)
2507
+ : NextWithShift::MakeEnd(grow_home, new_shift);
2508
+ saved_one_head.Set<NextWithShift::LockedFlag>(true);
2529
2509
  arr[grow_home].head_next_with_shift.Store(saved_one_head);
2530
2510
 
2531
2511
  // Make sure length_info_ hasn't been updated too early, as we're about
@@ -2535,8 +2515,8 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
2535
2515
  // Try to set zero's head.
2536
2516
  if (zero_head_lock.CasUpdate(
2537
2517
  zero_chain_frontier != SIZE_MAX
2538
- ? MakeNextWithShift(zero_chain_frontier, new_shift)
2539
- : MakeNextWithShiftEnd(old_home, new_shift),
2518
+ ? NextWithShift::Make(zero_chain_frontier, new_shift)
2519
+ : NextWithShift::MakeEnd(old_home, new_shift),
2540
2520
  yield_count_)) {
2541
2521
  // Both heads successfully updated to new shift
2542
2522
  break;
@@ -2570,10 +2550,10 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
2570
2550
  size_t& other_frontier = chain_frontier_first != 0
2571
2551
  ? /*&*/ zero_chain_frontier
2572
2552
  : /*&*/ one_chain_frontier;
2573
- uint64_t stop_before_or_new_tail =
2553
+ NextWithShift stop_before_or_new_tail =
2574
2554
  other_frontier != SIZE_MAX
2575
- ? /*stop before*/ MakeNextWithShift(other_frontier, old_shift)
2576
- : /*new tail*/ MakeNextWithShiftEnd(
2555
+ ? /*stop before*/ NextWithShift::Make(other_frontier, old_shift)
2556
+ : /*new tail*/ NextWithShift::MakeEnd(
2577
2557
  chain_frontier_first == 0 ? old_home : grow_home, new_shift);
2578
2558
  UpgradeShiftsOnRange(arr, first_frontier, stop_before_or_new_tail,
2579
2559
  old_shift, new_shift);
@@ -2599,20 +2579,19 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
2599
2579
  ? /*&*/ zero_chain_frontier
2600
2580
  : /*&*/ one_chain_frontier;
2601
2581
  assert(cur != first_frontier);
2602
- assert(GetNextFromNextWithShift(
2603
- arr[first_frontier].chain_next_with_shift.Load()) ==
2582
+ assert(arr[first_frontier].chain_next_with_shift.Load().GetNext() ==
2604
2583
  other_frontier);
2605
2584
 
2606
- uint64_t next_with_shift = arr[cur].chain_next_with_shift.Load();
2585
+ NextWithShift next_with_shift = arr[cur].chain_next_with_shift.Load();
2607
2586
 
2608
2587
  // Check for end of original chain
2609
- if (HandleImpl::IsEnd(next_with_shift)) {
2588
+ if (next_with_shift.IsEnd()) {
2610
2589
  // Can set upgraded tail on first chain
2611
- uint64_t first_new_tail = MakeNextWithShiftEnd(
2590
+ NextWithShift first_new_tail = NextWithShift::MakeEnd(
2612
2591
  chain_frontier_first == 0 ? old_home : grow_home, new_shift);
2613
2592
  arr[first_frontier].chain_next_with_shift.Store(first_new_tail);
2614
2593
  // And upgrade remainder of other chain
2615
- uint64_t other_new_tail = MakeNextWithShiftEnd(
2594
+ NextWithShift other_new_tail = NextWithShift::MakeEnd(
2616
2595
  chain_frontier_first != 0 ? old_home : grow_home, new_shift);
2617
2596
  UpgradeShiftsOnRange(arr, other_frontier, other_new_tail, old_shift,
2618
2597
  new_shift);
@@ -2621,7 +2600,7 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
2621
2600
  }
2622
2601
 
2623
2602
  // next_with_shift is not End
2624
- cur = GetNextFromNextWithShift(next_with_shift);
2603
+ cur = next_with_shift.GetNext();
2625
2604
 
2626
2605
  int target_chain;
2627
2606
  if (BottomNBits(arr[cur].hashed_key[1], new_shift) == old_home) {
@@ -2634,7 +2613,7 @@ void AutoHyperClockTable::SplitForGrow(size_t grow_home, size_t old_home,
2634
2613
  }
2635
2614
  if (target_chain == chain_frontier_first) {
2636
2615
  // Found next entry to skip to on the first chain
2637
- uint64_t skip_to = MakeNextWithShift(cur, new_shift);
2616
+ NextWithShift skip_to = NextWithShift::Make(cur, new_shift);
2638
2617
  arr[first_frontier].chain_next_with_shift.Store(skip_to);
2639
2618
  first_frontier = cur;
2640
2619
  // Upgrade other chain up to entry before that one
@@ -2675,17 +2654,17 @@ void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
2675
2654
 
2676
2655
  HandleImpl* const arr = array_.Get();
2677
2656
 
2678
- uint64_t next_with_shift = rewrite_lock.GetSavedHead();
2679
- assert(!HandleImpl::IsEnd(next_with_shift));
2680
- int home_shift = GetShiftFromNextWithShift(next_with_shift);
2657
+ NextWithShift next_with_shift = rewrite_lock.GetSavedHead();
2658
+ assert(!next_with_shift.IsEnd());
2659
+ int home_shift = next_with_shift.GetShift();
2681
2660
  (void)home;
2682
2661
  (void)home_shift;
2683
- size_t next = GetNextFromNextWithShift(next_with_shift);
2662
+ size_t next = next_with_shift.GetNext();
2684
2663
  assert(next < array_.Count());
2685
2664
  HandleImpl* h = &arr[next];
2686
2665
  HandleImpl* prev_to_keep = nullptr;
2687
2666
  #ifndef NDEBUG
2688
- uint64_t prev_to_keep_next_with_shift = 0;
2667
+ NextWithShift prev_to_keep_next_with_shift{};
2689
2668
  #endif
2690
2669
  // Whether there are entries between h and prev_to_keep that should be
2691
2670
  // purged from the chain.
@@ -2743,13 +2722,13 @@ void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
2743
2722
  // update any new entries just inserted in parallel.
2744
2723
  // Can simply restart (GetSavedHead() already updated from CAS failure).
2745
2724
  next_with_shift = rewrite_lock.GetSavedHead();
2746
- assert(!HandleImpl::IsEnd(next_with_shift));
2747
- next = GetNextFromNextWithShift(next_with_shift);
2725
+ assert(!next_with_shift.IsEnd());
2726
+ next = next_with_shift.GetNext();
2748
2727
  assert(next < array_.Count());
2749
2728
  h = &arr[next];
2750
2729
  pending_purge = false;
2751
2730
  assert(prev_to_keep == nullptr);
2752
- assert(GetShiftFromNextWithShift(next_with_shift) == home_shift);
2731
+ assert(next_with_shift.GetShift() == home_shift);
2753
2732
  continue;
2754
2733
  }
2755
2734
  pending_purge = false;
@@ -2771,13 +2750,13 @@ void AutoHyperClockTable::PurgeImplLocked(OpData* op_data,
2771
2750
  }
2772
2751
  #endif
2773
2752
 
2774
- assert(GetShiftFromNextWithShift(next_with_shift) == home_shift);
2753
+ assert(next_with_shift.GetShift() == home_shift);
2775
2754
 
2776
2755
  // Check for end marker
2777
- if (HandleImpl::IsEnd(next_with_shift)) {
2756
+ if (next_with_shift.IsEnd()) {
2778
2757
  h = nullptr;
2779
2758
  } else {
2780
- next = GetNextFromNextWithShift(next_with_shift);
2759
+ next = next_with_shift.GetNext();
2781
2760
  assert(next < array_.Count());
2782
2761
  h = &arr[next];
2783
2762
  assert(h != prev_to_keep);
@@ -2849,7 +2828,7 @@ void AutoHyperClockTable::PurgeImpl(OpData* op_data, size_t home,
2849
2828
  // Ensure we are at the correct home for the shift in effect for the
2850
2829
  // chain head.
2851
2830
  for (;;) {
2852
- int shift = GetShiftFromNextWithShift(rewrite_lock.GetSavedHead());
2831
+ int shift = rewrite_lock.GetSavedHead().GetShift();
2853
2832
 
2854
2833
  if (shift > home_shift) {
2855
2834
  // Found a newer shift at candidate head, which must apply to us.
@@ -3045,14 +3024,14 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::DoInsert(
3045
3024
  }
3046
3025
 
3047
3026
  // Now insert into chain using head pointer
3048
- uint64_t next_with_shift;
3027
+ NextWithShift next_with_shift;
3049
3028
  int home_shift = orig_home_shift;
3050
3029
 
3051
3030
  // Might need to retry
3052
3031
  for (int i = 0;; ++i) {
3053
3032
  CHECK_TOO_MANY_ITERATIONS(i);
3054
3033
  next_with_shift = arr[home].head_next_with_shift.Load();
3055
- int shift = GetShiftFromNextWithShift(next_with_shift);
3034
+ int shift = next_with_shift.GetShift();
3056
3035
 
3057
3036
  if (UNLIKELY(shift != home_shift)) {
3058
3037
  // NOTE: shift increases with table growth
@@ -3079,15 +3058,14 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::DoInsert(
3079
3058
  }
3080
3059
 
3081
3060
  // Values to update to
3082
- uint64_t head_next_with_shift = MakeNextWithShift(idx, home_shift);
3083
- uint64_t chain_next_with_shift = next_with_shift;
3061
+ NextWithShift head_next_with_shift = NextWithShift::Make(idx, home_shift);
3062
+ NextWithShift chain_next_with_shift = next_with_shift;
3084
3063
 
3085
3064
  // Preserve the locked state in head, without propagating to chain next
3086
3065
  // where it is meaningless (and not allowed)
3087
- if (UNLIKELY((next_with_shift & HandleImpl::kNextEndFlags) ==
3088
- HandleImpl::kHeadLocked)) {
3089
- head_next_with_shift |= HandleImpl::kHeadLocked;
3090
- chain_next_with_shift &= ~HandleImpl::kHeadLocked;
3066
+ if (UNLIKELY(next_with_shift.IsLockedNotEnd())) {
3067
+ head_next_with_shift.Set<NextWithShift::LockedFlag>(true);
3068
+ chain_next_with_shift.Set<NextWithShift::LockedFlag>(false);
3091
3069
  }
3092
3070
 
3093
3071
  arr[idx].chain_next_with_shift.Store(chain_next_with_shift);
@@ -3156,9 +3134,9 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
3156
3134
  // of a loop as possible.
3157
3135
 
3158
3136
  HandleImpl* const arr = array_.Get();
3159
- uint64_t next_with_shift = arr[home].head_next_with_shift.LoadRelaxed();
3160
- for (size_t i = 0; !HandleImpl::IsEnd(next_with_shift) && i < 10; ++i) {
3161
- HandleImpl* h = &arr[GetNextFromNextWithShift(next_with_shift)];
3137
+ NextWithShift next_with_shift = arr[home].head_next_with_shift.LoadRelaxed();
3138
+ for (size_t i = 0; !next_with_shift.IsEnd() && i < 10; ++i) {
3139
+ HandleImpl* h = &arr[next_with_shift.IsEnd()];
3162
3140
  // Attempt cheap key match without acquiring a read ref. This could give a
3163
3141
  // false positive, which is re-checked after acquiring read ref, or false
3164
3142
  // negative, which is re-checked in the full Lookup. Also, this is a
@@ -3203,7 +3181,7 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
3203
3181
  // Read head or chain pointer
3204
3182
  next_with_shift = h ? h->chain_next_with_shift.Load()
3205
3183
  : arr[home].head_next_with_shift.Load();
3206
- int shift = GetShiftFromNextWithShift(next_with_shift);
3184
+ int shift = next_with_shift.GetShift();
3207
3185
 
3208
3186
  // Make sure it's usable
3209
3187
  size_t effective_home = home;
@@ -3257,10 +3235,10 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
3257
3235
  }
3258
3236
 
3259
3237
  // Check for end marker
3260
- if (HandleImpl::IsEnd(next_with_shift)) {
3238
+ if (next_with_shift.IsEnd()) {
3261
3239
  // To ensure we didn't miss anything in the chain, the end marker must
3262
3240
  // point back to the correct home.
3263
- if (LIKELY(GetNextFromNextWithShift(next_with_shift) == effective_home)) {
3241
+ if (LIKELY(next_with_shift.GetNext() == effective_home)) {
3264
3242
  // Complete, clean iteration of the chain, not found.
3265
3243
  // Clean up.
3266
3244
  if (read_ref_on_chain) {
@@ -3276,7 +3254,7 @@ AutoHyperClockTable::HandleImpl* AutoHyperClockTable::Lookup(
3276
3254
  }
3277
3255
 
3278
3256
  // Follow the next and check for full key match, home match, or neither
3279
- h = &arr[GetNextFromNextWithShift(next_with_shift)];
3257
+ h = &arr[next_with_shift.GetNext()];
3280
3258
  bool full_match_or_unknown = false;
3281
3259
  if (MatchAndRef(&hashed_key, *h, shift, effective_home,
3282
3260
  &full_match_or_unknown)) {
@@ -3600,8 +3578,7 @@ size_t AutoHyperClockTable::CalcMaxUsableLength(
3600
3578
 
3601
3579
  namespace {
3602
3580
  bool IsHeadNonempty(const AutoHyperClockTable::HandleImpl& h) {
3603
- return !AutoHyperClockTable::HandleImpl::IsEnd(
3604
- h.head_next_with_shift.LoadRelaxed());
3581
+ return !h.head_next_with_shift.LoadRelaxed().IsEnd();
3605
3582
  }
3606
3583
  bool IsEntryAtHome(const AutoHyperClockTable::HandleImpl& h, int shift,
3607
3584
  size_t home) {
@@ -775,6 +775,7 @@ class AutoHyperClockTable : public BaseClockTable {
775
775
  // chain--specifically the next entry in the chain.
776
776
  // * The end of a chain is given a special "end" marker and refers back
777
777
  // to the head of the chain.
778
+ // These decorated pointers use the NextWithShift bit field struct below.
778
779
  //
779
780
  // Why do we need shift on each pointer? To make Lookup wait-free, we need
780
781
  // to be able to query a chain without missing anything, and preferably
@@ -794,47 +795,63 @@ class AutoHyperClockTable : public BaseClockTable {
794
795
  // it is normal to see "under construction" entries on the chain, and it
795
796
  // is not safe to read their hashed key without either a read reference
796
797
  // on the entry or a rewrite lock on the chain.
797
-
798
- // Marker in a "with_shift" head pointer for some thread owning writes
799
- // to the chain structure (except for inserts), but only if not an
800
- // "end" pointer. Also called the "rewrite lock."
801
- static constexpr uint64_t kHeadLocked = uint64_t{1} << 7;
802
-
803
- // Marker in a "with_shift" pointer for the end of a chain. Must also
804
- // point back to the head of the chain (with end marker removed).
805
- // Also includes the "locked" bit so that attempting to lock an empty
806
- // chain has no effect (not needed, as the lock is only needed for
807
- // removals).
808
- static constexpr uint64_t kNextEndFlags = (uint64_t{1} << 6) | kHeadLocked;
809
-
810
- static inline bool IsEnd(uint64_t next_with_shift) {
811
- // Assuming certain values never used, suffices to check this one bit
812
- constexpr auto kCheckBit = kNextEndFlags ^ kHeadLocked;
813
- return next_with_shift & kCheckBit;
814
- }
815
-
816
- // Bottom bits to right shift away to get an array index from a
817
- // "with_shift" pointer.
818
- static constexpr int kNextShift = 8;
819
-
820
- // A bit mask for the "shift" associated with each "with_shift" pointer.
821
- // Always bottommost bits.
822
- static constexpr int kShiftMask = 63;
798
+ struct NextWithShift : public BitFields<uint64_t, NextWithShift> {
799
+ // The "shift" associated with this decorated pointer (see description
800
+ // above).
801
+ using Shift = UnsignedBitField<NextWithShift, 6, NoPrevBitField>;
802
+ // Marker for the end of a chain. Must also (a) point back to the head of
803
+ // the chain (with end marker removed), and (b) set the LockedFlag
804
+ // (below), so that attempting to lock an empty chain has no effect (not
805
+ // needed, as the lock is only needed for removals).
806
+ using EndFlag = BoolBitField<NextWithShift, Shift>;
807
+ // Marker that some thread owning writes to the chain structure (except
808
+ // for inserts), but only if not an "end" pointer. Also called the
809
+ // "rewrite lock."
810
+ using LockedFlag = BoolBitField<NextWithShift, EndFlag>;
811
+ // The "next" associated with this decorated pointer, which is an index
812
+ // into the table's array_ (see description above).
813
+ using Next = UnsignedBitField<NextWithShift, 56, LockedFlag>;
814
+
815
+ bool IsLocked() const { return Get<LockedFlag>(); }
816
+ bool IsEnd() const {
817
+ // End flag should imply locked flag
818
+ assert(!Get<EndFlag>() || Get<LockedFlag>());
819
+ return Get<EndFlag>();
820
+ }
821
+ bool IsLockedNotEnd() const {
822
+ // NOTE: helping GCC to optimize this simpler code:
823
+ // return IsLocked() && !IsEnd();
824
+ constexpr U kEndFlag = U{1} << EndFlag::kBitOffset;
825
+ constexpr U kLockedFlag = U{1} << LockedFlag::kBitOffset;
826
+ return (underlying & (kEndFlag | kLockedFlag)) == kLockedFlag;
827
+ }
828
+ auto GetNext() const { return Get<Next>(); }
829
+ auto GetShift() const { return Get<Shift>(); }
830
+
831
+ static NextWithShift Make(size_t next, int shift) {
832
+ return NextWithShift{}.With<Next>(next).With<Shift>(
833
+ static_cast<uint8_t>(shift));
834
+ }
835
+
836
+ static NextWithShift MakeEnd(size_t next, int shift) {
837
+ return Make(next, shift).With<EndFlag>(true).With<LockedFlag>(true);
838
+ }
839
+ };
823
840
 
824
841
  // A marker for head_next_with_shift that indicates this HandleImpl is
825
842
  // heap allocated (standalone) rather than in the table.
826
- static constexpr uint64_t kStandaloneMarker = UINT64_MAX;
843
+ static constexpr NextWithShift kStandaloneMarker{UINT64_MAX};
827
844
 
828
845
  // A marker for head_next_with_shift indicating the head is not yet part
829
846
  // of the usable table, or for chain_next_with_shift indicating that the
830
847
  // entry is not present or is not yet part of a chain (must not be
831
848
  // "shareable" state).
832
- static constexpr uint64_t kUnusedMarker = 0;
849
+ static constexpr NextWithShift kUnusedMarker{0};
833
850
 
834
851
  // See above. The head pointer is logically independent of the rest of
835
852
  // the entry, including the chain next pointer.
836
- AcqRelAtomic<uint64_t> head_next_with_shift{kUnusedMarker};
837
- AcqRelAtomic<uint64_t> chain_next_with_shift{kUnusedMarker};
853
+ AcqRelBitFieldsAtomic<NextWithShift> head_next_with_shift{kUnusedMarker};
854
+ AcqRelBitFieldsAtomic<NextWithShift> chain_next_with_shift{kUnusedMarker};
838
855
 
839
856
  // For supporting CreateStandalone and some fallback cases.
840
857
  inline bool IsStandalone() const {