#pragma once #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { class SHAMapInnerNode final : public SHAMapTreeNode, public CountedObject { public: /** * Each inner node has 16 children (the 'radix tree' part of the map) */ static constexpr unsigned int kBranchFactor = 16; private: /** * Opaque type that contains the `hashes` array (array of type * `SHAMapHash`) and the `children` array (array of type * `intr_ptr::SharedPtr`). */ TaggedPointer hashesAndChildren_; std::uint32_t fullBelowGen_ = 0; std::uint16_t isBranch_ = 0; /** * A bitlock for the children of this node, with one bit per child */ mutable std::atomic lock_ = 0; /** * Convert arrays stored in `hashesAndChildren_` so they can store the * requested number of children. * * @param toAllocate allocate space for at least this number of children * (must be <= branchFactor) * * @note the arrays may allocate more than the requested value in * `toAllocate`. This is due to the implementation of TagPointer, which * only supports allocating arrays of 4 different sizes. */ void resizeChildArrays(std::uint8_t toAllocate); /** * Get the child's index inside the `hashes` or `children` array (stored in * `hashesAndChildren_`. * * These arrays may or may not be sparse). The optional will be empty is an * empty branch is requested and the arrays are sparse. * * @param i index of the requested child */ std::optional getChildIndex(int i) const; /** * Call the `f` callback for all 16 (branchFactor) branches - even if * the branch is empty. * * @param f a one parameter callback function. The parameter is the * child's hash. */ template void iterChildren(F&& f) const; /** * Call the `f` callback for all non-empty branches. * * @param f a two parameter callback function. The first parameter is * the branch number, the second parameter is the index into the array. * For dense formats these are the same, for sparse they may be * different. */ template void iterNonEmptyChildIndexes(F&& f) const; public: explicit SHAMapInnerNode(std::uint32_t cowid, std::uint8_t numAllocatedChildren = 2); SHAMapInnerNode(SHAMapInnerNode const&) = delete; SHAMapInnerNode& operator=(SHAMapInnerNode const&) = delete; ~SHAMapInnerNode() override; // Needed to support intrusive weak pointers void partialDestructor() override; SHAMapTreeNodePtr clone(std::uint32_t cowid) const override; SHAMapNodeType getType() const override { return SHAMapNodeType::TnInner; } bool isLeaf() const override { return false; } bool isInner() const override { return true; } bool isEmpty() const; bool isEmptyBranch(int m) const; int getBranchCount() const; SHAMapHash const& getChildHash(int m) const; void setChild(int m, SHAMapTreeNodePtr child); void shareChild(int m, SHAMapTreeNodePtr const& child); SHAMapTreeNode* getChildPointer(int branch); SHAMapTreeNodePtr getChild(int branch); SHAMapTreeNodePtr canonicalizeChild(int branch, SHAMapTreeNodePtr node); // sync functions bool isFullBelow(std::uint32_t generation) const; void setFullBelowGen(std::uint32_t gen); void updateHash() override; /** * Recalculate the hash of all children and this node. */ void updateHashDeep(); void serializeForWire(Serializer&) const override; void serializeWithPrefix(Serializer&) const override; std::string getString(SHAMapNodeID const&) const override; void invariants(bool isRoot = false) const override; static SHAMapTreeNodePtr makeFullInner(Slice data, SHAMapHash const& hash, bool hashValid); static SHAMapTreeNodePtr makeCompressedInner(Slice data); }; inline bool SHAMapInnerNode::isEmpty() const { return isBranch_ == 0; } inline bool SHAMapInnerNode::isEmptyBranch(int m) const { return (isBranch_ & (1 << m)) == 0; } inline int SHAMapInnerNode::getBranchCount() const { return popcnt16(isBranch_); } inline bool SHAMapInnerNode::isFullBelow(std::uint32_t generation) const { return fullBelowGen_ == generation; } inline void SHAMapInnerNode::setFullBelowGen(std::uint32_t gen) { fullBelowGen_ = gen; } } // namespace xrpl