rippled
SHAMapInnerNode.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/shamap/SHAMapInnerNode.h>
21 
22 #include <ripple/basics/Log.h>
23 #include <ripple/basics/Slice.h>
24 #include <ripple/basics/contract.h>
25 #include <ripple/beast/core/LexicalCast.h>
26 #include <ripple/protocol/HashPrefix.h>
27 #include <ripple/protocol/digest.h>
28 #include <ripple/shamap/SHAMapTreeNode.h>
29 #include <ripple/shamap/impl/TaggedPointer.ipp>
30 
31 #include <openssl/sha.h>
32 
33 #include <algorithm>
34 #include <iterator>
35 #include <utility>
36 
37 #ifndef __aarch64__
38 // This is used for the _mm_pause instruction:
39 #include <immintrin.h>
40 #endif
41 
42 namespace ripple {
43 
53 {
54 private:
57 
58 public:
60  {
61  }
62 
64  : bits_(lock), mask_(1 << index)
65  {
66  assert(index >= 0 && index < 16);
67  }
68 
69  [[nodiscard]] bool
71  {
72  // If we want to grab all the individual bitlocks at once we cannot
73  // use `fetch_or`! To see why, imagine that `lock_ == 0x0020` which
74  // means that the `fetch_or` would return `0x0020` but all the bits
75  // would already be (incorrectly!) set. Oops!
76  std::uint16_t expected = 0;
77 
78  if (mask_ != 0xFFFF)
79  return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) ==
80  expected;
81 
83  expected,
84  mask_,
85  std::memory_order_acquire,
86  std::memory_order_relaxed);
87  }
88 
89  void
90  lock()
91  {
92  // Testing suggests that 99.9999% of the time this will succeed, so
93  // we try to optimize the fast path.
94  if (try_lock())
95  return;
96 
97  do
98  {
99  // We try to spin for a few times:
100  for (int i = 0; i != 100; ++i)
101  {
102  if (try_lock())
103  return;
104 
105 #ifndef __aarch64__
106  _mm_pause();
107 #endif
108  }
109 
111  } while ((bits_.load(std::memory_order_relaxed) & mask_) == 0);
112  }
113 
114  void
116  {
117  bits_.fetch_and(~mask_, std::memory_order_release);
118  }
119 };
120 
122  std::uint32_t cowid,
123  std::uint8_t numAllocatedChildren)
124  : SHAMapTreeNode(cowid), hashesAndChildren_(numAllocatedChildren)
125 {
126 }
127 
129 
130 template <class F>
131 void
133 {
134  hashesAndChildren_.iterChildren(isBranch_, std::forward<F>(f));
135 }
136 
137 template <class F>
138 void
140 {
142 }
143 
144 void
146 {
148  TaggedPointer(std::move(hashesAndChildren_), isBranch_, toAllocate);
149 }
150 
153 {
155 }
156 
159 {
160  auto const branchCount = getBranchCount();
161  auto const thisIsSparse = !hashesAndChildren_.isDense();
162  auto p = std::make_shared<SHAMapInnerNode>(cowid, branchCount);
163  p->hash_ = hash_;
164  p->isBranch_ = isBranch_;
165  p->fullBelowGen_ = fullBelowGen_;
166  SHAMapHash *cloneHashes, *thisHashes;
167  std::shared_ptr<SHAMapTreeNode>*cloneChildren, *thisChildren;
168  // structured bindings can't be captured in c++ 17; use tie instead
169  std::tie(std::ignore, cloneHashes, cloneChildren) =
170  p->hashesAndChildren_.getHashesAndChildren();
171  std::tie(std::ignore, thisHashes, thisChildren) =
173 
174  if (thisIsSparse)
175  {
176  int cloneChildIndex = 0;
177  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
178  cloneHashes[cloneChildIndex++] = thisHashes[indexNum];
179  });
180  }
181  else
182  {
183  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
184  cloneHashes[branchNum] = thisHashes[indexNum];
185  });
186  }
187 
188  SpinBitlock sl(lock_);
189  std::lock_guard lock(sl);
190 
191  if (thisIsSparse)
192  {
193  int cloneChildIndex = 0;
194  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
195  cloneChildren[cloneChildIndex++] = thisChildren[indexNum];
196  });
197  }
198  else
199  {
200  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
201  cloneChildren[branchNum] = thisChildren[indexNum];
202  });
203  }
204 
205  return p;
206 }
207 
210  Slice data,
211  SHAMapHash const& hash,
212  bool hashValid)
213 {
214  // A full inner node is serialized as 16 256-bit hashes, back to back:
215  if (data.size() != branchFactor * uint256::bytes)
216  Throw<std::runtime_error>("Invalid FI node");
217 
218  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
219 
220  SerialIter si(data);
221 
222  auto hashes = ret->hashesAndChildren_.getHashes();
223 
224  for (int i = 0; i < branchFactor; ++i)
225  {
226  hashes[i].as_uint256() = si.getBitString<256>();
227 
228  if (hashes[i].isNonZero())
229  ret->isBranch_ |= (1 << i);
230  }
231 
232  ret->resizeChildArrays(ret->getBranchCount());
233 
234  if (hashValid)
235  ret->hash_ = hash;
236  else
237  ret->updateHash();
238 
239  return ret;
240 }
241 
244 {
245  // A compressed inner node is serialized as a series of 33 byte chunks,
246  // representing a one byte "position" and a 256-bit hash:
247  constexpr std::size_t chunkSize = uint256::bytes + 1;
248 
249  if (auto const s = data.size();
250  (s % chunkSize != 0) || (s > chunkSize * branchFactor))
251  Throw<std::runtime_error>("Invalid CI node");
252 
253  SerialIter si(data);
254 
255  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
256 
257  auto hashes = ret->hashesAndChildren_.getHashes();
258 
259  while (!si.empty())
260  {
261  auto const hash = si.getBitString<256>();
262  auto const pos = si.get8();
263 
264  if (pos >= branchFactor)
265  Throw<std::runtime_error>("invalid CI node");
266 
267  hashes[pos].as_uint256() = hash;
268 
269  if (hashes[pos].isNonZero())
270  ret->isBranch_ |= (1 << pos);
271  }
272 
273  ret->resizeChildArrays(ret->getBranchCount());
274  ret->updateHash();
275  return ret;
276 }
277 
278 void
280 {
281  uint256 nh;
282  if (isBranch_ != 0)
283  {
285  using beast::hash_append;
287  iterChildren([&](SHAMapHash const& hh) { hash_append(h, hh); });
288  nh = static_cast<typename sha512_half_hasher::result_type>(h);
289  }
290  hash_ = SHAMapHash{nh};
291 }
292 
293 void
295 {
296  SHAMapHash* hashes;
298  // structured bindings can't be captured in c++ 17; use tie instead
299  std::tie(std::ignore, hashes, children) =
301  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
302  if (children[indexNum] != nullptr)
303  hashes[indexNum] = children[indexNum]->getHash();
304  });
305  updateHash();
306 }
307 
308 void
310 {
311  assert(!isEmpty());
312 
313  // If the node is sparse, then only send non-empty branches:
314  if (getBranchCount() < 12)
315  {
316  // compressed node
317  auto hashes = hashesAndChildren_.getHashes();
318  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
319  s.addBitString(hashes[indexNum].as_uint256());
320  s.add8(branchNum);
321  });
323  }
324  else
325  {
326  iterChildren(
327  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
328  s.add8(wireTypeInner);
329  }
330 }
331 
332 void
334 {
335  assert(!isEmpty());
336 
338  iterChildren(
339  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
340 }
341 
342 bool
344 {
345  return isBranch_ == 0;
346 }
347 
348 int
350 {
351  return popcnt16(isBranch_);
352 }
353 
356 {
358  auto hashes = hashesAndChildren_.getHashes();
359  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
360  ret += "\nb";
361  ret += std::to_string(branchNum);
362  ret += " = ";
363  ret += to_string(hashes[indexNum]);
364  });
365  return ret;
366 }
367 
368 // We are modifying an inner node
369 void
371 {
372  assert((m >= 0) && (m < branchFactor));
373  assert(cowid_ != 0);
374  assert(child.get() != this);
375 
376  auto const dstIsBranch = [&] {
377  if (child)
378  return isBranch_ | (1 << m);
379  else
380  return isBranch_ & ~(1 << m);
381  }();
382 
383  auto const dstToAllocate = popcnt16(dstIsBranch);
384  // change hashesAndChildren to remove the element, or make room for the
385  // added element, if necessary
387  std::move(hashesAndChildren_), isBranch_, dstIsBranch, dstToAllocate);
388 
389  isBranch_ = dstIsBranch;
390 
391  if (child)
392  {
393  auto const childIndex = *getChildIndex(m);
394  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
395  hashes[childIndex].zero();
396  children[childIndex] = child;
397  }
398 
399  hash_.zero();
400 
402 }
403 
404 // finished modifying, now make shareable
405 void
407 {
408  assert((m >= 0) && (m < branchFactor));
409  assert(cowid_ != 0);
410  assert(child);
411  assert(child.get() != this);
412 
413  assert(!isEmptyBranch(m));
415 }
416 
419 {
420  assert(branch >= 0 && branch < branchFactor);
421  assert(!isEmptyBranch(branch));
422 
423  auto const index = *getChildIndex(branch);
424 
425  SpinBitlock sl(lock_, index);
426  std::lock_guard lock(sl);
427  return hashesAndChildren_.getChildren()[index].get();
428 }
429 
432 {
433  assert(branch >= 0 && branch < branchFactor);
434  assert(!isEmptyBranch(branch));
435 
436  auto const index = *getChildIndex(branch);
437 
438  SpinBitlock sl(lock_, index);
439  std::lock_guard lock(sl);
440  return hashesAndChildren_.getChildren()[index];
441 }
442 
443 SHAMapHash const&
445 {
446  assert((m >= 0) && (m < branchFactor));
447  if (auto const i = getChildIndex(m))
448  return hashesAndChildren_.getHashes()[*i];
449 
450  return zeroSHAMapHash;
451 }
452 
455  int branch,
457 {
458  assert(branch >= 0 && branch < branchFactor);
459  assert(node);
460  assert(!isEmptyBranch(branch));
461  auto const childIndex = *getChildIndex(branch);
462  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
463  assert(node->getHash() == hashes[childIndex]);
464 
465  SpinBitlock sl(lock_, childIndex);
466  std::lock_guard lock(sl);
467 
468  if (children[childIndex])
469  {
470  // There is already a node hooked up, return it
471  node = children[childIndex];
472  }
473  else
474  {
475  // Hook this node up
476  children[childIndex] = node;
477  }
478  return node;
479 }
480 
481 void
482 SHAMapInnerNode::invariants(bool is_root) const
483 {
484  unsigned count = 0;
485  auto [numAllocated, hashes, children] =
487 
488  if (numAllocated != branchFactor)
489  {
490  auto const branchCount = getBranchCount();
491  for (int i = 0; i < branchCount; ++i)
492  {
493  assert(hashes[i].isNonZero());
494  if (children[i] != nullptr)
495  children[i]->invariants();
496  ++count;
497  }
498  }
499  else
500  {
501  for (int i = 0; i < branchFactor; ++i)
502  {
503  if (hashes[i].isNonZero())
504  {
505  assert((isBranch_ & (1 << i)) != 0);
506  if (children[i] != nullptr)
507  children[i]->invariants();
508  ++count;
509  }
510  else
511  {
512  assert((isBranch_ & (1 << i)) == 0);
513  }
514  }
515  }
516 
517  if (!is_root)
518  {
519  assert(hash_.isNonZero());
520  assert(count >= 1);
521  }
522  assert((count == 0) ? hash_.isZero() : hash_.isNonZero());
523 }
524 
525 } // namespace ripple
ripple::SHAMapTreeNode::cowid
std::uint32_t cowid() const
Returns the SHAMap that owns this node.
Definition: SHAMapTreeNode.h:116
ripple::SHAMapInnerNode::serializeWithPrefix
void serializeWithPrefix(Serializer &) const override
Serialize the node in a format appropriate for hashing.
Definition: SHAMapInnerNode.cpp:333
ripple::SHAMapInnerNode::setChild
void setChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:370
ripple::SpinBitlock::SpinBitlock
SpinBitlock(std::atomic< std::uint16_t > &lock, int index)
Definition: SHAMapInnerNode.cpp:63
ripple::SHAMapInnerNode::clone
std::shared_ptr< SHAMapTreeNode > clone(std::uint32_t cowid) const override
Make a copy of this node, setting the owner.
Definition: SHAMapInnerNode.cpp:158
ripple::TaggedPointer::getChildIndex
std::optional< int > getChildIndex(std::uint16_t isBranch, int i) const
Get the child's index inside the hashes or children array (which may or may not be sparse).
std::string
STL class.
std::shared_ptr
STL class.
utility
ripple::SHAMapInnerNode::hashesAndChildren_
TaggedPointer hashesAndChildren_
Opaque type that contains the hashes array (array of type SHAMapHash) and the children array (array o...
Definition: SHAMapInnerNode.h:53
ripple::detail::basic_sha512_half_hasher
Returns the SHA512-Half digest of a message.
Definition: digest.h:166
ripple::SHAMapInnerNode::getChild
std::shared_ptr< SHAMapTreeNode > getChild(int branch)
Definition: SHAMapInnerNode.cpp:431
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
ripple::SHAMapInnerNode::makeFullInner
static std::shared_ptr< SHAMapTreeNode > makeFullInner(Slice data, SHAMapHash const &hash, bool hashValid)
Definition: SHAMapInnerNode.cpp:209
ripple::SHAMapInnerNode::canonicalizeChild
std::shared_ptr< SHAMapTreeNode > canonicalizeChild(int branch, std::shared_ptr< SHAMapTreeNode > node)
Definition: SHAMapInnerNode.cpp:454
ripple::SHAMapTreeNode::cowid_
std::uint32_t cowid_
Determines the owning SHAMap, if any.
Definition: SHAMapTreeNode.h:64
ripple::Serializer::add8
int add8(unsigned char i)
Definition: Serializer.cpp:166
ripple::SHAMapInnerNode::updateHash
void updateHash() override
Recalculate the hash of this node.
Definition: SHAMapInnerNode.cpp:279
iterator
std::lock_guard
STL class.
ripple::SHAMapHash::isZero
bool isZero() const
Definition: SHAMapHash.h:53
ripple::SHAMapInnerNode::branchFactor
static constexpr unsigned int branchFactor
Each inner node has 16 children (the 'radix tree' part of the map)
Definition: SHAMapInnerNode.h:46
ripple::SHAMapInnerNode::shareChild
void shareChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:406
ripple::wireTypeInner
static constexpr unsigned const char wireTypeInner
Definition: SHAMapTreeNode.h:42
ripple::SHAMapNodeID
Identifies a node inside a SHAMap.
Definition: SHAMapNodeID.h:33
ripple::SHAMapHash::isNonZero
bool isNonZero() const
Definition: SHAMapHash.h:58
std::atomic::compare_exchange_weak
T compare_exchange_weak(T... args)
ripple::SHAMapTreeNode::getString
virtual std::string getString(SHAMapNodeID const &) const
Definition: SHAMapTreeNode.cpp:184
ripple::SHAMapTreeNode::hash_
SHAMapHash hash_
Definition: SHAMapTreeNode.h:56
ripple::SHAMapInnerNode::iterChildren
void iterChildren(F &&f) const
Call the f callback for all 16 (branchFactor) branches - even if the branch is empty.
Definition: SHAMapInnerNode.cpp:132
ripple::TaggedPointer
TaggedPointer is a combination of a pointer and a mask stored in the lowest two bits.
Definition: TaggedPointer.h:57
ripple::SHAMapHash
Definition: SHAMapHash.h:32
algorithm
ripple::SpinBitlock::SpinBitlock
SpinBitlock(std::atomic< std::uint16_t > &lock)
Definition: SHAMapInnerNode.cpp:59
ripple::SpinBitlock::try_lock
bool try_lock()
Definition: SHAMapInnerNode.cpp:70
std::tie
T tie(T... args)
ripple::SHAMapInnerNode::isEmptyBranch
bool isEmptyBranch(int m) const
Definition: SHAMapInnerNode.h:198
ripple::SHAMapInnerNode::iterNonEmptyChildIndexes
void iterNonEmptyChildIndexes(F &&f) const
Call the f callback for all non-empty branches.
Definition: SHAMapInnerNode.cpp:139
ripple::base_uint< 256 >
ripple::SHAMapInnerNode::getString
std::string getString(SHAMapNodeID const &) const override
Definition: SHAMapInnerNode.cpp:355
ripple::HashPrefix::innerNode
@ innerNode
inner node in V1 tree
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:444
ripple::base_uint< 256 >::bytes
static constexpr std::size_t bytes
Definition: base_uint.h:98
ripple::SerialIter::get8
unsigned char get8()
Definition: Serializer.cpp:362
std::atomic::load
T load(T... args)
ripple::SHAMapInnerNode::resizeChildArrays
void resizeChildArrays(std::uint8_t toAllocate)
Convert arrays stored in hashesAndChildren_ so they can store the requested number of children.
Definition: SHAMapInnerNode.cpp:145
ripple::SerialIter::empty
std::size_t empty() const noexcept
Definition: Serializer.h:332
ripple::TaggedPointer::getHashes
SHAMapHash * getHashes() const
Get the hashes array.
ripple::TaggedPointer::isDense
bool isDense() const
Check if the arrays have a dense format.
ripple::SHAMapTreeNode
Definition: SHAMapTreeNode.h:53
std::to_string
T to_string(T... args)
ripple::SHAMapInnerNode::getChildIndex
std::optional< int > getChildIndex(int i) const
Get the child's index inside the hashes or children array (stored in hashesAndChildren_.
Definition: SHAMapInnerNode.cpp:152
ripple::SpinBitlock
A specialized 16-way spinlock used to protect inner node branches.
Definition: SHAMapInnerNode.cpp:52
ripple::SHAMapInnerNode::updateHashDeep
void updateHashDeep()
Recalculate the hash of all children and this node.
Definition: SHAMapInnerNode.cpp:294
ripple::SHAMapInnerNode::SHAMapInnerNode
SHAMapInnerNode(std::uint32_t cowid, std::uint8_t numAllocatedChildren=2)
Definition: SHAMapInnerNode.cpp:121
ripple::SpinBitlock::bits_
std::atomic< std::uint16_t > & bits_
Definition: SHAMapInnerNode.cpp:55
ripple::SHAMapInnerNode::isBranch_
std::uint16_t isBranch_
Definition: SHAMapInnerNode.h:56
ripple::SerialIter
Definition: Serializer.h:310
ripple::SHAMapInnerNode::getBranchCount
int getBranchCount() const
Definition: SHAMapInnerNode.cpp:349
std::uint16_t
std::atomic< std::uint16_t >
std::atomic::fetch_and
T fetch_and(T... args)
ripple::SHAMapInnerNode::~SHAMapInnerNode
~SHAMapInnerNode()
ripple::TaggedPointer::iterNonEmptyChildIndexes
void iterNonEmptyChildIndexes(std::uint16_t isBranch, F &&f) const
Call the f callback for all non-empty branches.
ripple::SpinBitlock::mask_
std::uint16_t mask_
Definition: SHAMapInnerNode.cpp:56
ripple::SHAMapTreeNode::getHash
SHAMapHash const & getHash() const
Return the hash of this node.
Definition: SHAMapTreeNode.h:143
ripple::Serializer
Definition: Serializer.h:39
ripple::wireTypeCompressedInner
static constexpr unsigned const char wireTypeCompressedInner
Definition: SHAMapTreeNode.h:43
ripple::SerialIter::getBitString
base_uint< Bits, Tag > getBitString()
Definition: Serializer.h:414
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::SHAMapInnerNode::serializeForWire
void serializeForWire(Serializer &) const override
Serialize the node in a format appropriate for sending over the wire.
Definition: SHAMapInnerNode.cpp:309
ripple::Serializer::addBitString
int addBitString(base_uint< Bits, Tag > const &v)
Definition: Serializer.h:97
ripple::SHAMapHash::zero
void zero()
Definition: SHAMapHash.h:68
beast::hash_append
std::enable_if_t< is_contiguously_hashable< T, Hasher >::value > hash_append(Hasher &h, T const &t) noexcept
Logically concatenate input data to a Hasher.
Definition: hash_append.h:236
std::atomic::fetch_or
T fetch_or(T... args)
ripple::SpinBitlock::unlock
void unlock()
Definition: SHAMapInnerNode.cpp:115
std::optional< int >
ripple::TaggedPointer::getChildren
std::shared_ptr< SHAMapTreeNode > * getChildren() const
Get the children array.
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::Serializer::add32
int add32(std::uint32_t i)
Definition: Serializer.cpp:38
ripple::TaggedPointer::iterChildren
void iterChildren(std::uint16_t isBranch, F &&f) const
Call the f callback for all 16 (branchFactor) branches - even if the branch is empty.
ripple::SHAMapInnerNode::isEmpty
bool isEmpty() const
Definition: SHAMapInnerNode.cpp:343
ripple::SHAMapInnerNode::makeCompressedInner
static std::shared_ptr< SHAMapTreeNode > makeCompressedInner(Slice data)
Definition: SHAMapInnerNode.cpp:243
ripple::TaggedPointer::getHashesAndChildren
std::tuple< std::uint8_t, SHAMapHash *, std::shared_ptr< SHAMapTreeNode > * > getHashesAndChildren() const
Get the number of elements in each array and a pointer to the start of each array.
ripple::SHAMapHash::as_uint256
uint256 const & as_uint256() const
Definition: SHAMapHash.h:43
ripple::SpinBitlock::lock
void lock()
Definition: SHAMapInnerNode.cpp:90
ripple::SHAMapInnerNode::getChildPointer
SHAMapTreeNode * getChildPointer(int branch)
Definition: SHAMapInnerNode.cpp:418
ripple::TaggedPointer::capacity
std::uint8_t capacity() const
Get the number of elements allocated for each array.
ripple::hash_append
void hash_append(Hasher &h, ValidatorBlobInfo const &blobInfo)
Definition: ValidatorList.h:897
ripple::SHAMapInnerNode::lock_
std::atomic< std::uint16_t > lock_
A bitlock for the children of this node, with one bit per child.
Definition: SHAMapInnerNode.h:59
ripple::SHAMapInnerNode::invariants
void invariants(bool is_root=false) const override
Definition: SHAMapInnerNode.cpp:482
std::this_thread::yield
T yield(T... args)
ripple::SHAMapInnerNode::fullBelowGen_
std::uint32_t fullBelowGen_
Definition: SHAMapInnerNode.h:55