Bitcoin ABC 0.33.5
P2P Digital Currency
radix.h
Go to the documentation of this file.
1// Copyright (c) 2019 The Bitcoin developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#ifndef BITCOIN_RADIX_H
6#define BITCOIN_RADIX_H
7
8#include <rcu.h>
9
10#include <array>
11#include <atomic>
12#include <cstdint>
13#include <memory>
14#include <type_traits>
15
16template <typename T> struct PassthroughAdapter {
17 auto &&getId(const T &e) const { return e.getId(); }
18};
19
38template <typename T, typename Adapter = PassthroughAdapter<T>>
39struct RadixTree : private Adapter {
40private:
41 static const int BITS = 4;
42 static const int MASK = (1 << BITS) - 1;
43 static const size_t CHILD_PER_LEVEL = 1 << BITS;
44
45 using KeyType =
46 typename std::remove_reference<decltype(std::declval<Adapter &>().getId(
47 std::declval<T &>()))>::type;
48 static const size_t KEY_BITS = 8 * sizeof(KeyType);
49 static const uint32_t TOP_LEVEL = (KEY_BITS - 1) / BITS;
50
51 struct RadixElement;
52 struct RadixNode;
53
54 std::atomic<RadixElement> root;
55
56public:
58 ~RadixTree() { root.load().decrementRefCount(); }
59
63 RadixTree(const RadixTree &src) : RadixTree() {
64 {
65 RCULock lock;
66 RadixElement e = src.root.load();
68 root = e;
69 }
70
71 // Make sure we the writes in the tree are behind us so
72 // this copy won't mutate behind our back.
74 }
75
77 {
78 RCULock lock;
79 RadixElement e = rhs.root.load();
81 root.load().decrementRefCount();
82 root = e;
83 }
84
85 // Make sure we the writes in the tree are behind us so
86 // this copy won't mutate behind our back.
88
89 return *this;
90 }
91
95 RadixTree(RadixTree &&src) : RadixTree() { *this = std::move(src); }
97 {
98 RCULock lock;
99 RadixElement e = rhs.root.load();
100 rhs.root = root.load();
101 root = e;
102 }
103
104 return *this;
105 }
106
111 bool insert(const RCUPtr<T> &value) { return insert(getId(*value), value); }
112
117 RCUPtr<T> get(const KeyType &key) {
118 uint32_t level = TOP_LEVEL;
119
120 RCULock lock;
121 RadixElement e = root.load();
122
123 // Find a leaf.
124 while (e.isNode()) {
125 e = e.getNode()->get(level--, key)->load();
126 }
127
128 T *leaf = e.getLeaf();
129 if (leaf == nullptr || getId(*leaf) != key) {
130 // We failed to find the proper element.
131 return RCUPtr<T>();
132 }
133
134 // The leaf is non-null and the keys match. We have our guy.
135 return RCUPtr<T>::copy(leaf);
136 }
137
138 RCUPtr<const T> get(const KeyType &key) const {
139 T const *ptr = const_cast<RadixTree *>(this)->get(key).release();
140 return RCUPtr<const T>::acquire(ptr);
141 }
142
143 template <typename Callable> bool forEachLeaf(Callable &&func) const {
144 RCULock lock;
145 return forEachLeaf(root.load(), std::move(func));
146 }
147
148#define SEEK_LEAF_LOOP() \
149 RadixElement e = eptr->load(); \
150 \
151 /* Walk down the tree until we find a leaf for our node. */ \
152 do { \
153 while (e.isNode()) { \
154 Node: \
155 auto nptr = e.getNode(); \
156 if (!nptr->isShared()) { \
157 eptr = nptr->get(level--, key); \
158 e = eptr->load(); \
159 continue; \
160 } \
161 \
162 auto copy = std::make_unique<RadixNode>(*nptr); \
163 if (!eptr->compare_exchange_strong(e, RadixElement(copy.get()))) { \
164 /* We failed to insert our subtree, just try again. */ \
165 continue; \
166 } \
167 \
168 /* We have a subtree, resume normal operations from there. */ \
169 e.decrementRefCount(); \
170 eptr = copy->get(level--, key); \
171 e = eptr->load(); \
172 copy.release(); \
173 } \
174 } while (0)
175
181 uint32_t level = TOP_LEVEL;
182
183 RCULock lock;
184 std::atomic<RadixElement> *eptr = &root;
185
187
188 T *leaf = e.getLeaf();
189 if (leaf == nullptr || getId(*leaf) != key) {
190 // We failed to find the proper element.
191 return RCUPtr<T>();
192 }
193
194 // We have the proper element, try to delete it.
195 if (eptr->compare_exchange_strong(e, RadixElement())) {
196 return RCUPtr<T>::acquire(leaf);
197 }
198
199 // The element was replaced, either by a subtree or another element.
200 if (e.isNode()) {
201 goto Node;
202 }
203
204 // The element in the slot is not the one we are looking for.
205 return RCUPtr<T>();
206 }
207
208private:
209 KeyType getId(const T &value) const { return Adapter::getId(value); }
210
211 bool insert(const KeyType &key, RCUPtr<T> value) {
212 uint32_t level = TOP_LEVEL;
213
214 RCULock lock;
215 std::atomic<RadixElement> *eptr = &root;
216
217 while (true) {
219
220 // If the slot is empty, try to insert right there.
221 if (e.getLeaf() == nullptr) {
222 if (eptr->compare_exchange_strong(e,
223 RadixElement(value.get()))) {
224 value.release();
225 return true;
226 }
227
228 // CAS failed, we may have a node in there now.
229 if (e.isNode()) {
230 goto Node;
231 }
232 }
233
234 // The element was already in the tree.
235 const KeyType &leafKey = getId(*e.getLeaf());
236 if (key == leafKey) {
237 return false;
238 }
239
240 // There is an element there, but it isn't a subtree. We need to
241 // convert it into a subtree and resume insertion into that subtree.
242 auto newChild = std::make_unique<RadixNode>(level, leafKey, e);
243 if (eptr->compare_exchange_strong(e,
244 RadixElement(newChild.get()))) {
245 // We have a subtree, resume normal operations from there.
246 newChild.release();
247 } else {
248 // We failed to insert our subtree, clean it before it is freed.
249 newChild->get(level, leafKey)->store(RadixElement());
250 }
251 }
252 }
253
254#undef SEEK_LEAF_LOOP
255
256 template <typename Callable>
257 bool forEachLeaf(RadixElement e, Callable &&func) const {
258 if (e.isLeaf()) {
259 T *leaf = e.getLeaf();
260 if (leaf != nullptr) {
261 return func(RCUPtr<T>::copy(leaf));
262 }
263
264 return true;
265 }
266
267 return e.getNode()->forEachChild(
268 [&](const std::atomic<RadixElement> *pElement) {
269 return forEachLeaf(pElement->load(), func);
270 });
271 }
272
274 private:
275 union {
278 uintptr_t raw;
279 };
280
281 static const uintptr_t DISCRIMINANT = 0x01;
282 bool getDiscriminant() const { return (raw & DISCRIMINANT) != 0; }
283
284 public:
285 explicit RadixElement() noexcept : raw(DISCRIMINANT) {}
286 explicit RadixElement(RadixNode *nodeIn) noexcept : node(nodeIn) {}
287 explicit RadixElement(T *leafIn) noexcept : leaf(leafIn) {
288 raw |= DISCRIMINANT;
289 }
290
296 if (isNode()) {
298 } else {
300 }
301 }
302
304 if (isNode()) {
305 RadixNode *ptr = getNode();
307 } else {
308 T *ptr = getLeaf();
310 }
311 }
312
316 bool isNode() const { return !getDiscriminant(); }
317
319 assert(isNode());
320 return node;
321 }
322
323 const RadixNode *getNode() const {
324 assert(isNode());
325 return node;
326 }
327
331 bool isLeaf() const { return getDiscriminant(); }
332
333 T *getLeaf() {
334 assert(isLeaf());
335 return reinterpret_cast<T *>(raw & ~DISCRIMINANT);
336 }
337
338 const T *getLeaf() const {
339 assert(isLeaf());
340 return const_cast<RadixElement *>(this)->getLeaf();
341 }
342 };
343
344 struct RadixNode {
346
347 private:
348 union {
349 std::array<std::atomic<RadixElement>, CHILD_PER_LEVEL> children;
350 std::array<RadixElement, CHILD_PER_LEVEL>
352 };
353
354 public:
355 RadixNode(uint32_t level, const KeyType &key, RadixElement e)
357 get(level, key)->store(e);
358 }
359
362 e.decrementRefCount();
363 }
364 }
365
367 for (size_t i = 0; i < CHILD_PER_LEVEL; i++) {
368 auto e = rhs.children[i].load();
369 e.incrementRefCount();
371 }
372 }
373
374 RadixNode &operator=(const RadixNode &) = delete;
375
376 std::atomic<RadixElement> *get(uint32_t level, const KeyType &key) {
377 return &children[(key >> uint32_t(level * BITS)) & MASK];
378 }
379
380 bool isShared() const { return refcount > 0; }
381
382 template <typename Callable> bool forEachChild(Callable &&func) const {
383 for (size_t i = 0; i < CHILD_PER_LEVEL; i++) {
384 if (!func(&children[i])) {
385 return false;
386 }
387 }
388 return true;
389 }
390 };
391
392 // Make sure the alignment works for T and RadixElement.
393 static_assert(alignof(T) > 1, "T's alignment must be 2 or more.");
394 static_assert(alignof(RadixNode) > 1,
395 "RadixNode alignment must be 2 or more.");
396};
397
398#endif // BITCOIN_RADIX_H
Definition: rcu.h:61
static void synchronize()
Definition: rcu.h:82
Definition: rcu.h:85
T * get()
Get allows to access the undelying pointer.
Definition: rcu.h:170
static RCUPtr acquire(T *&ptrIn)
Acquire ownership of some pointer.
Definition: rcu.h:103
T * release()
Release transfers ownership of the pointer from RCUPtr to the caller.
Definition: rcu.h:176
static RCUPtr copy(T *ptr)
Construct a new RCUPtr without transferring owership.
Definition: rcu.h:119
#define SEEK_LEAF_LOOP()
Definition: radix.h:148
auto && getId(const T &e) const
Definition: radix.h:17
bool getDiscriminant() const
Definition: radix.h:282
bool isNode() const
Node features.
Definition: radix.h:316
static const uintptr_t DISCRIMINANT
Definition: radix.h:281
RadixNode * getNode()
Definition: radix.h:318
const T * getLeaf() const
Definition: radix.h:338
bool isLeaf() const
Leaf features.
Definition: radix.h:331
RadixElement(RadixNode *nodeIn) noexcept
Definition: radix.h:286
void incrementRefCount()
RadixElement is designed to be a dumb wrapper.
Definition: radix.h:295
void decrementRefCount()
Definition: radix.h:303
RadixNode * node
Definition: radix.h:276
const RadixNode * getNode() const
Definition: radix.h:323
RadixElement(T *leafIn) noexcept
Definition: radix.h:287
RadixElement() noexcept
Definition: radix.h:285
bool forEachChild(Callable &&func) const
Definition: radix.h:382
RadixNode & operator=(const RadixNode &)=delete
std::atomic< RadixElement > * get(uint32_t level, const KeyType &key)
Definition: radix.h:376
RadixNode(uint32_t level, const KeyType &key, RadixElement e)
Definition: radix.h:355
std::array< RadixElement, CHILD_PER_LEVEL > non_atomic_children_DO_NOT_USE
Definition: radix.h:351
bool isShared() const
Definition: radix.h:380
RadixNode(const RadixNode &rhs)
Definition: radix.h:366
std::array< std::atomic< RadixElement >, CHILD_PER_LEVEL > children
Definition: radix.h:349
IMPLEMENT_RCU_REFCOUNT(uint64_t)
This is a radix tree storing values identified by a unique key.
Definition: radix.h:39
static const int BITS
Definition: radix.h:41
static const uint32_t TOP_LEVEL
Definition: radix.h:49
RCUPtr< T > remove(const KeyType &key)
Remove an element from the tree.
Definition: radix.h:180
RadixTree()
Definition: radix.h:57
KeyType getId(const T &value) const
Definition: radix.h:209
static const size_t CHILD_PER_LEVEL
Definition: radix.h:43
~RadixTree()
Definition: radix.h:58
RadixTree(RadixTree &&src)
Move semantic.
Definition: radix.h:95
typename std::remove_reference< decltype(std::declval< Adapter & >().getId(std::declval< T & >()))>::type KeyType
Definition: radix.h:47
bool forEachLeaf(RadixElement e, Callable &&func) const
Definition: radix.h:257
RadixTree & operator=(const RadixTree &rhs)
Definition: radix.h:76
static const size_t KEY_BITS
Definition: radix.h:48
static const int MASK
Definition: radix.h:42
std::atomic< RadixElement > root
Definition: radix.h:54
RadixTree(const RadixTree &src)
Copy semantic.
Definition: radix.h:63
RCUPtr< const T > get(const KeyType &key) const
Definition: radix.h:138
bool insert(const KeyType &key, RCUPtr< T > value)
Definition: radix.h:211
RCUPtr< T > get(const KeyType &key)
Get the value corresponding to a key.
Definition: radix.h:117
RadixTree & operator=(RadixTree &&rhs)
Definition: radix.h:96
bool forEachLeaf(Callable &&func) const
Definition: radix.h:143
bool insert(const RCUPtr< T > &value)
Insert a value into the tree.
Definition: radix.h:111
assert(!tx.IsCoinBase())