qxLib
string_pool.inl
Go to the documentation of this file.
1 /**
2 
3  @file string_pool.inl
4  @author Khrapov
5  @date 17.01.2026
6  @copyright © Nick Khrapov, 2026. All right reserved.
7 
8 **/
9 
10 namespace qx
11 {
12 
13 template<size_t nSize>
14 string_pool<nSize>::string_pool()
15 {
16  // Pre-create empty strings with reserved capacity 32
17  for (string& s : m_Storage)
18  normalize(s);
19 
20  for (auto& b : m_FreeMask)
21  b.store(~0ull, std::memory_order_relaxed);
22 
23  // Disable unused bits in the last block if nSize is not a multiple of 64
24  constexpr size_t nRemove = nSize % bTotalBits;
25  if constexpr (nRemove != 0)
26  {
27  u64 nValid = (1ull << nRemove) - 1;
28  m_FreeMask[m_FreeMask.size() - 1].store(nValid, std::memory_order_relaxed);
29  }
30 }
31 
32 template<size_t nSize>
34 {
35  for (size_t nBlock = 0; nBlock < m_FreeMask.size(); ++nBlock)
36  {
37  u64 nMask = m_FreeMask[nBlock].load(std::memory_order_relaxed);
38 
39  while (nMask)
40  {
41  const u64 nBit = nMask & (~nMask + 1); // lowest set bit
42  const u64 nNewMask = nMask & ~nBit; // mark as used
43 
44  if (m_FreeMask[nBlock]
45  .compare_exchange_weak(nMask, nNewMask, std::memory_order_acquire, std::memory_order_relaxed))
46  {
47  const size_t nIndex = nBlock * bTotalBits + ctz64(nBit);
48  return item { std::move(m_Storage[nIndex]), static_cast<int>(nIndex) };
49  }
50  }
51  }
52 
53  // Pool is empty
54  return item { string(), nFreeString };
55 }
56 
57 template<size_t nSize>
58 void string_pool<nSize>::release(string sValue, int nIndex)
59 {
60  if (nIndex == nFreeString)
61  return;
62 
63  const size_t nSizeIndex = static_cast<size_t>(nIndex);
64  if (nSizeIndex >= nSize)
65  return;
66 
67  normalize(sValue);
68 
69  // Exclusive access is guaranteed by the occupancy bit
70  m_Storage[nSizeIndex] = std::move(sValue);
71 
72  const size_t nBlock = nSizeIndex / bTotalBits;
73  const size_t nOffset = nSizeIndex % bTotalBits;
74  const u64 nBit = 1ull << nOffset;
75 
76  // Make the slot visible as free
77  m_FreeMask[nBlock].fetch_or(nBit, std::memory_order_release);
78 }
79 
80 template<size_t nSize>
81 int string_pool<nSize>::ctz64(u64 nBit) noexcept
82 {
83 #if QX_GNU || QX_CLANG || QX_APPLE_CLANG
84  return __builtin_ctzll(nBit);
85 #else
86  int n = 0;
87  while ((nBit & 1) == 0)
88  {
89  nBit >>= 1;
90  ++n;
91  }
92  return n;
93 #endif
94 }
95 
96 template<size_t nSize>
97 void string_pool<nSize>::normalize(string& string)
98 {
99  string.clear();
100  string.reserve(bStringStartCapacity);
101 }
102 
103 } // namespace qx
Fixed-size atomic string pool.
Definition: string_pool.h:34
item acquire()
Acquire a string from the pool.
Definition: string_pool.inl:33
void release(string sValue, int nIndex)
Return a string back to the pool.
Definition: string_pool.inl:58
std::uint64_t u64
Definition: typedefs.h:26