Split SHAMapTreeNode into leaf and inner nodes.

* This reduces the memory requirements of both leaf and inner nodes.
* The name SHAMapTreeNode is retained for leaf nodes so as to keep
  the public API of SHAMap stable.
This commit is contained in:
Howard Hinnant
2015-06-05 20:47:30 -04:00
committed by Nik Bougalis
parent e95ab65396
commit f875603525
11 changed files with 739 additions and 582 deletions

View File

@@ -235,16 +235,16 @@ public:
if (!node.has_nodeid () || !node.has_nodedata ()) if (!node.has_nodeid () || !node.has_nodedata ())
return; return;
SHAMapTreeNode newNode( auto newNode = SHAMapAbstractNode::make(
Blob (node.nodedata().begin(), node.nodedata().end()), Blob (node.nodedata().begin(), node.nodedata().end()),
0, snfWIRE, uZero, false); 0, snfWIRE, uZero, false);
s.erase(); s.erase();
newNode.addRaw(s, snfPREFIX); newNode->addRaw(s, snfPREFIX);
auto blob = std::make_shared<Blob> (s.begin(), s.end()); auto blob = std::make_shared<Blob> (s.begin(), s.end());
getApp().getOPs().addFetchPack (newNode.getNodeHash(), blob); getApp().getOPs().addFetchPack (newNode->getNodeHash(), blob);
} }
} }
catch (...) catch (...)

View File

@@ -245,7 +245,7 @@ SHAMapStoreImp::onLedgerClosed (Ledger::pointer validatedLedger)
bool bool
SHAMapStoreImp::copyNode (std::uint64_t& nodeCount, SHAMapStoreImp::copyNode (std::uint64_t& nodeCount,
SHAMapTreeNode const& node) SHAMapAbstractNode const& node)
{ {
// Copy a single record from node to database_ // Copy a single record from node to database_
database_->fetchNode (node.getNodeHash()); database_->fetchNode (node.getNodeHash());

View File

@@ -159,7 +159,7 @@ public:
private: private:
// callback for visitNodes // callback for visitNodes
bool copyNode (std::uint64_t& nodeCount, SHAMapTreeNode const &node); bool copyNode (std::uint64_t& nodeCount, SHAMapAbstractNode const &node);
void run(); void run();
void dbPaths(); void dbPaths();
std::shared_ptr <NodeStore::Backend> makeBackendRotating ( std::shared_ptr <NodeStore::Backend> makeBackendRotating (

View File

@@ -83,7 +83,7 @@ private:
beast::Journal journal_; beast::Journal journal_;
std::uint32_t seq_; std::uint32_t seq_;
std::uint32_t ledgerSeq_; // sequence number of ledger this is part of std::uint32_t ledgerSeq_; // sequence number of ledger this is part of
std::shared_ptr<SHAMapTreeNode> root_; std::shared_ptr<SHAMapAbstractNode> root_;
SHAMapState state_; SHAMapState state_;
SHAMapType type_; SHAMapType type_;
bool backed_ = true; // Map is backed by the database bool backed_ = true; // Map is backed by the database
@@ -153,7 +153,7 @@ public:
std::shared_ptr<SHAMapItem> peekNextItem (uint256 const& , SHAMapTreeNode::TNType & type) const; std::shared_ptr<SHAMapItem> peekNextItem (uint256 const& , SHAMapTreeNode::TNType & type) const;
std::shared_ptr<SHAMapItem> peekPrevItem (uint256 const& ) const; std::shared_ptr<SHAMapItem> peekPrevItem (uint256 const& ) const;
void visitNodes (std::function<bool (SHAMapTreeNode&)> const&) const; void visitNodes (std::function<bool (SHAMapAbstractNode&)> const&) const;
void visitLeaves(std::function<void (std::shared_ptr<SHAMapItem> const&)> const&) const; void visitLeaves(std::function<void (std::shared_ptr<SHAMapItem> const&)> const&) const;
// comparison/sync functions // comparison/sync functions
@@ -192,7 +192,7 @@ public:
using fetchPackEntry_t = std::pair <uint256, Blob>; using fetchPackEntry_t = std::pair <uint256, Blob>;
void visitDifferences (SHAMap * have, std::function<bool (SHAMapTreeNode&)>) const; void visitDifferences(SHAMap* have, std::function<bool(SHAMapAbstractNode&)>) const;
void getFetchPack (SHAMap * have, bool includeLeaves, int max, void getFetchPack (SHAMap * have, bool includeLeaves, int max,
std::function<void (uint256 const&, const Blob&)>) const; std::function<void (uint256 const&, const Blob&)>) const;
@@ -203,30 +203,30 @@ public:
private: private:
using SharedPtrNodeStack = using SharedPtrNodeStack =
std::stack<std::pair<std::shared_ptr<SHAMapTreeNode>, SHAMapNodeID>>; std::stack<std::pair<std::shared_ptr<SHAMapAbstractNode>, SHAMapNodeID>>;
using DeltaRef = std::pair<std::shared_ptr<SHAMapItem> const&, using DeltaRef = std::pair<std::shared_ptr<SHAMapItem> const&,
std::shared_ptr<SHAMapItem> const&>; std::shared_ptr<SHAMapItem> const&>;
int unshare (); int unshare ();
// tree node cache operations // tree node cache operations
std::shared_ptr<SHAMapTreeNode> getCache (uint256 const& hash) const; std::shared_ptr<SHAMapAbstractNode> getCache (uint256 const& hash) const;
void canonicalize (uint256 const& hash, std::shared_ptr<SHAMapTreeNode>&) const; void canonicalize (uint256 const& hash, std::shared_ptr<SHAMapAbstractNode>&) const;
// database operations // database operations
std::shared_ptr<SHAMapTreeNode> fetchNodeFromDB (uint256 const& hash) const; std::shared_ptr<SHAMapAbstractNode> fetchNodeFromDB (uint256 const& hash) const;
std::shared_ptr<SHAMapTreeNode> fetchNodeNT (uint256 const& hash) const; std::shared_ptr<SHAMapAbstractNode> fetchNodeNT (uint256 const& hash) const;
std::shared_ptr<SHAMapTreeNode> fetchNodeNT ( std::shared_ptr<SHAMapAbstractNode> fetchNodeNT (
SHAMapNodeID const& id, SHAMapNodeID const& id,
uint256 const& hash, uint256 const& hash,
SHAMapSyncFilter *filter) const; SHAMapSyncFilter *filter) const;
std::shared_ptr<SHAMapTreeNode> fetchNode (uint256 const& hash) const; std::shared_ptr<SHAMapAbstractNode> fetchNode (uint256 const& hash) const;
std::shared_ptr<SHAMapTreeNode> checkFilter (uint256 const& hash, SHAMapNodeID const& id, std::shared_ptr<SHAMapAbstractNode> checkFilter(uint256 const& hash,
SHAMapSyncFilter* filter) const; SHAMapNodeID const& id, SHAMapSyncFilter* filter) const;
/** Update hashes up to the root */ /** Update hashes up to the root */
void dirtyUp (SharedPtrNodeStack& stack, void dirtyUp (SharedPtrNodeStack& stack,
uint256 const& target, std::shared_ptr<SHAMapTreeNode> terminal); uint256 const& target, std::shared_ptr<SHAMapAbstractNode> terminal);
/** Get the path from the root to the specified node */ /** Get the path from the root to the specified node */
SharedPtrNodeStack SharedPtrNodeStack
@@ -236,44 +236,50 @@ private:
SHAMapTreeNode* walkToPointer (uint256 const& id) const; SHAMapTreeNode* walkToPointer (uint256 const& id) const;
/** Unshare the node, allowing it to be modified */ /** Unshare the node, allowing it to be modified */
void unshareNode (std::shared_ptr<SHAMapTreeNode>&, SHAMapNodeID const& nodeID); template <class Node>
std::shared_ptr<Node>
unshareNode(std::shared_ptr<Node>, SHAMapNodeID const& nodeID);
/** prepare a node to be modified before flushing */ /** prepare a node to be modified before flushing */
void preFlushNode (std::shared_ptr<SHAMapTreeNode>& node) const; template <class Node>
std::shared_ptr<Node>
preFlushNode(std::shared_ptr<Node> node) const;
/** write and canonicalize modified node */ /** write and canonicalize modified node */
void writeNode (NodeObjectType t, std::uint32_t seq, std::shared_ptr<SHAMapAbstractNode>
std::shared_ptr<SHAMapTreeNode>& node) const; writeNode(NodeObjectType t, std::uint32_t seq,
std::shared_ptr<SHAMapAbstractNode> node) const;
SHAMapTreeNode* firstBelow (SHAMapTreeNode*) const; SHAMapTreeNode* firstBelow (SHAMapAbstractNode*) const;
SHAMapTreeNode* lastBelow (SHAMapTreeNode*) const; SHAMapTreeNode* lastBelow (SHAMapAbstractNode*) const;
// Simple descent // Simple descent
// Get a child of the specified node // Get a child of the specified node
SHAMapTreeNode* descend (SHAMapTreeNode*, int branch) const; SHAMapAbstractNode* descend (SHAMapInnerNode*, int branch) const;
SHAMapTreeNode* descendThrow (SHAMapTreeNode*, int branch) const; SHAMapAbstractNode* descendThrow (SHAMapInnerNode*, int branch) const;
std::shared_ptr<SHAMapTreeNode> descend (std::shared_ptr<SHAMapTreeNode> const&, int branch) const; std::shared_ptr<SHAMapAbstractNode> descend (std::shared_ptr<SHAMapInnerNode> const&, int branch) const;
std::shared_ptr<SHAMapTreeNode> descendThrow (std::shared_ptr<SHAMapTreeNode> const&, int branch) const; std::shared_ptr<SHAMapAbstractNode> descendThrow (std::shared_ptr<SHAMapInnerNode> const&, int branch) const;
// Descend with filter // Descend with filter
SHAMapTreeNode* descendAsync (SHAMapTreeNode* parent, int branch, SHAMapAbstractNode* descendAsync (SHAMapInnerNode* parent, int branch,
SHAMapNodeID const& childID, SHAMapSyncFilter* filter, bool& pending) const; SHAMapNodeID const& childID, SHAMapSyncFilter* filter, bool& pending) const;
std::pair <SHAMapTreeNode*, SHAMapNodeID> std::pair <SHAMapAbstractNode*, SHAMapNodeID>
descend (SHAMapTreeNode* parent, SHAMapNodeID const& parentID, descend (SHAMapInnerNode* parent, SHAMapNodeID const& parentID,
int branch, SHAMapSyncFilter* filter) const; int branch, SHAMapSyncFilter* filter) const;
// Non-storing // Non-storing
// Does not hook the returned node to its parent // Does not hook the returned node to its parent
std::shared_ptr<SHAMapTreeNode> descendNoStore (std::shared_ptr<SHAMapTreeNode> const&, int branch) const; std::shared_ptr<SHAMapAbstractNode>
descendNoStore (std::shared_ptr<SHAMapInnerNode> const&, int branch) const;
/** If there is only one leaf below this node, get its contents */ /** If there is only one leaf below this node, get its contents */
std::shared_ptr<SHAMapItem> onlyBelow (SHAMapTreeNode*) const; std::shared_ptr<SHAMapItem> onlyBelow (SHAMapAbstractNode*) const;
bool hasInnerNode (SHAMapNodeID const& nodeID, uint256 const& hash) const; bool hasInnerNode (SHAMapNodeID const& nodeID, uint256 const& hash) const;
bool hasLeafNode (uint256 const& tag, uint256 const& hash) const; bool hasLeafNode (uint256 const& tag, uint256 const& hash) const;
bool walkBranch (SHAMapTreeNode* node, bool walkBranch (SHAMapAbstractNode* node,
std::shared_ptr<SHAMapItem> const& otherMapItem, bool isFirstMap, std::shared_ptr<SHAMapItem> const& otherMapItem, bool isFirstMap,
Delta & differences, int & maxCount) const; Delta & differences, int & maxCount) const;
int walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq); int walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq);

View File

@@ -39,7 +39,7 @@ enum SHANodeFormat
snfHASH = 3, // just the hash snfHASH = 3, // just the hash
}; };
class SHAMapTreeNode class SHAMapAbstractNode
{ {
public: public:
enum TNType enum TNType
@@ -51,112 +51,164 @@ public:
tnACCOUNT_STATE = 4 tnACCOUNT_STATE = 4
}; };
private: protected:
uint256 mHash;
uint256 mHashes[16];
std::shared_ptr<SHAMapTreeNode> mChildren[16];
std::shared_ptr<SHAMapItem> mItem;
std::uint32_t mSeq;
TNType mType; TNType mType;
int mIsBranch; uint256 mHash;
std::uint32_t mFullBelowGen; std::uint32_t mSeq;
protected:
virtual ~SHAMapAbstractNode() = 0;
SHAMapAbstractNode(SHAMapAbstractNode const&) = delete;
SHAMapAbstractNode& operator=(SHAMapAbstractNode const&) = delete;
SHAMapAbstractNode(TNType type, std::uint32_t seq);
SHAMapAbstractNode(TNType type, std::uint32_t seq, uint256 const& hash);
public:
std::uint32_t getSeq () const;
void setSeq (std::uint32_t s);
uint256 const& getNodeHash () const;
TNType getType () const;
bool isLeaf () const;
bool isInner () const;
bool isValid () const;
bool isInBounds (SHAMapNodeID const &id) const;
virtual bool updateHash () = 0;
virtual void addRaw (Serializer&, SHANodeFormat format) = 0;
virtual std::string getString (SHAMapNodeID const&) const;
virtual std::shared_ptr<SHAMapAbstractNode> clone(std::uint32_t seq) const = 0;
static std::shared_ptr<SHAMapAbstractNode>
make(Blob const& rawNode, std::uint32_t seq, SHANodeFormat format,
uint256 const& hash, bool hashValid);
// debugging
#ifdef BEAST_DEBUG
static void dump (SHAMapNodeID const&, beast::Journal journal);
#endif
};
class SHAMapInnerNode
: public SHAMapAbstractNode
{
uint256 mHashes[16];
std::shared_ptr<SHAMapAbstractNode> mChildren[16];
int mIsBranch = 0;
std::uint32_t mFullBelowGen = 0;
static std::mutex childLock; static std::mutex childLock;
public:
SHAMapInnerNode(std::uint32_t seq = 0);
std::shared_ptr<SHAMapAbstractNode> clone(std::uint32_t seq) const override;
bool isEmpty () const;
bool isEmptyBranch (int m) const;
int getBranchCount () const;
uint256 const& getChildHash (int m) const;
void setChild(int m, std::shared_ptr<SHAMapAbstractNode> const& child);
void shareChild (int m, std::shared_ptr<SHAMapAbstractNode> const& child);
SHAMapAbstractNode* getChildPointer (int branch);
std::shared_ptr<SHAMapAbstractNode> getChild (int branch);
std::shared_ptr<SHAMapAbstractNode>
canonicalizeChild (int branch, std::shared_ptr<SHAMapAbstractNode> node);
// sync functions
bool isFullBelow (std::uint32_t generation) const;
void setFullBelowGen (std::uint32_t gen);
bool updateHash () override;
void updateHashDeep();
void addRaw (Serializer&, SHANodeFormat format) override;
std::string getString (SHAMapNodeID const&) const override;
friend std::shared_ptr<SHAMapAbstractNode>
SHAMapAbstractNode::make(Blob const& rawNode, std::uint32_t seq,
SHANodeFormat format, uint256 const& hash, bool hashValid);
};
// SHAMapTreeNode represents a leaf, and may eventually be renamed to reflect that.
class SHAMapTreeNode
: public SHAMapAbstractNode
{
private:
std::shared_ptr<SHAMapItem> mItem;
public: public:
SHAMapTreeNode (const SHAMapTreeNode&) = delete; SHAMapTreeNode (const SHAMapTreeNode&) = delete;
SHAMapTreeNode& operator= (const SHAMapTreeNode&) = delete; SHAMapTreeNode& operator= (const SHAMapTreeNode&) = delete;
SHAMapTreeNode (std::uint32_t seq); // empty node
SHAMapTreeNode (const SHAMapTreeNode & node, std::uint32_t seq); // copy node from older tree
SHAMapTreeNode (std::shared_ptr<SHAMapItem> const& item, TNType type, std::uint32_t seq); SHAMapTreeNode (std::shared_ptr<SHAMapItem> const& item, TNType type, std::uint32_t seq);
SHAMapTreeNode (Blob const & data, std::uint32_t seq, SHAMapTreeNode(std::shared_ptr<SHAMapItem> const& item, TNType type,
SHANodeFormat format, uint256 const& hash, bool hashValid); std::uint32_t seq, uint256 const& hash);
std::shared_ptr<SHAMapAbstractNode> clone(std::uint32_t seq) const override;
void addRaw (Serializer&, SHANodeFormat format); void addRaw (Serializer&, SHANodeFormat format) override;
uint256 const& getNodeHash () const;
public: // public only to SHAMap public: // public only to SHAMap
void setChild (int m, std::shared_ptr<SHAMapTreeNode> const& child);
void shareChild (int m, std::shared_ptr<SHAMapTreeNode> const& child);
// node functions
std::uint32_t getSeq () const;
void setSeq (std::uint32_t s);
TNType getType () const;
// type functions
bool isLeaf () const;
bool isInner () const;
bool isInBounds (SHAMapNodeID const &id) const;
bool isValid () const;
// inner node functions // inner node functions
bool isInnerNode () const; bool isInnerNode () const;
bool isEmptyBranch (int m) const;
bool isEmpty () const;
int getBranchCount () const;
void makeInner ();
uint256 const& getChildHash (int m) const;
// item node function // item node function
bool hasItem () const; bool hasItem () const;
std::shared_ptr<SHAMapItem> const& peekItem () const; std::shared_ptr<SHAMapItem> const& peekItem () const;
bool setItem (std::shared_ptr<SHAMapItem> const& i, TNType type); bool setItem (std::shared_ptr<SHAMapItem> const& i, TNType type);
// sync functions std::string getString (SHAMapNodeID const&) const override;
bool isFullBelow (std::uint32_t generation) const; bool updateHash () override;
void setFullBelowGen (std::uint32_t gen);
SHAMapTreeNode* getChildPointer (int branch);
std::shared_ptr<SHAMapTreeNode> getChild (int branch);
void canonicalizeChild (int branch, std::shared_ptr<SHAMapTreeNode>& node);
// debugging
#ifdef BEAST_DEBUG
void dump (SHAMapNodeID const&, beast::Journal journal);
#endif
std::string getString (SHAMapNodeID const&) const;
bool updateHash ();
void updateHashDeep();
private:
bool isTransaction () const;
bool hasMetaData () const;
bool isAccountState () const;
}; };
// SHAMapAbstractNode
inline
SHAMapAbstractNode::SHAMapAbstractNode(TNType type, std::uint32_t seq)
: mType(type)
, mSeq(seq)
{
}
inline
SHAMapAbstractNode::SHAMapAbstractNode(TNType type, std::uint32_t seq,
uint256 const& hash)
: mType(type)
, mHash(hash)
, mSeq(seq)
{
}
inline inline
std::uint32_t std::uint32_t
SHAMapTreeNode::getSeq () const SHAMapAbstractNode::getSeq () const
{ {
return mSeq; return mSeq;
} }
inline inline
void void
SHAMapTreeNode::setSeq (std::uint32_t s) SHAMapAbstractNode::setSeq (std::uint32_t s)
{ {
mSeq = s; mSeq = s;
} }
inline inline
uint256 const& uint256 const&
SHAMapTreeNode::getNodeHash () const SHAMapAbstractNode::getNodeHash () const
{ {
return mHash; return mHash;
} }
inline inline
SHAMapTreeNode::TNType SHAMapAbstractNode::TNType
SHAMapTreeNode::getType () const SHAMapAbstractNode::getType () const
{ {
return mType; return mType;
} }
inline inline
bool bool
SHAMapTreeNode::isLeaf () const SHAMapAbstractNode::isLeaf () const
{ {
return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD) || return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD) ||
(mType == tnACCOUNT_STATE); (mType == tnACCOUNT_STATE);
@@ -164,69 +216,72 @@ SHAMapTreeNode::isLeaf () const
inline inline
bool bool
SHAMapTreeNode::isInner () const SHAMapAbstractNode::isInner () const
{ {
return mType == tnINNER; return mType == tnINNER;
} }
inline inline
bool bool
SHAMapTreeNode::isInBounds (SHAMapNodeID const &id) const SHAMapAbstractNode::isValid () const
{
// Nodes at depth 64 must be leaves
return (!isInner() || (id.getDepth() < 64));
}
inline
bool
SHAMapTreeNode::isValid () const
{ {
return mType != tnERROR; return mType != tnERROR;
} }
inline inline
bool bool
SHAMapTreeNode::isTransaction () const SHAMapAbstractNode::isInBounds (SHAMapNodeID const &id) const
{
// Nodes at depth 64 must be leaves
return (!isInner() || (id.getDepth() < 64));
}
// SHAMapInnerNode
inline
SHAMapInnerNode::SHAMapInnerNode(std::uint32_t seq)
: SHAMapAbstractNode(tnINNER, seq)
{ {
return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD);
} }
inline inline
bool bool
SHAMapTreeNode::hasMetaData () const SHAMapInnerNode::isEmptyBranch (int m) const
{
return mType == tnTRANSACTION_MD;
}
inline
bool
SHAMapTreeNode::isAccountState () const
{
return mType == tnACCOUNT_STATE;
}
inline
bool
SHAMapTreeNode::isInnerNode () const
{
return !mItem;
}
inline
bool
SHAMapTreeNode::isEmptyBranch (int m) const
{ {
return (mIsBranch & (1 << m)) == 0; return (mIsBranch & (1 << m)) == 0;
} }
inline inline
uint256 const& uint256 const&
SHAMapTreeNode::getChildHash (int m) const SHAMapInnerNode::getChildHash (int m) const
{ {
assert ((m >= 0) && (m < 16) && (mType == tnINNER)); assert ((m >= 0) && (m < 16) && (getType() == tnINNER));
return mHashes[m]; return mHashes[m];
} }
inline
bool
SHAMapInnerNode::isFullBelow (std::uint32_t generation) const
{
return mFullBelowGen == generation;
}
inline
void
SHAMapInnerNode::setFullBelowGen (std::uint32_t gen)
{
mFullBelowGen = gen;
}
// SHAMapTreeNode
inline
bool
SHAMapTreeNode::isInnerNode () const
{
return !mItem;
}
inline inline
bool bool
SHAMapTreeNode::hasItem () const SHAMapTreeNode::hasItem () const
@@ -241,20 +296,6 @@ SHAMapTreeNode::peekItem () const
return mItem; return mItem;
} }
inline
bool
SHAMapTreeNode::isFullBelow (std::uint32_t generation) const
{
return mFullBelowGen == generation;
}
inline
void
SHAMapTreeNode::setFullBelowGen (std::uint32_t gen)
{
mFullBelowGen = gen;
}
} // ripple } // ripple
#endif #endif

View File

@@ -24,9 +24,9 @@
namespace ripple { namespace ripple {
class SHAMapTreeNode; class SHAMapAbstractNode;
using TreeNodeCache = TaggedCache <uint256, SHAMapTreeNode>; using TreeNodeCache = TaggedCache <uint256, SHAMapAbstractNode>;
} // ripple } // ripple

View File

@@ -38,8 +38,7 @@ SHAMap::SHAMap (
{ {
assert (seq_ != 0); assert (seq_ != 0);
root_ = std::make_shared<SHAMapTreeNode> (seq_); root_ = std::make_shared<SHAMapInnerNode> (seq_);
root_->makeInner ();
} }
SHAMap::SHAMap ( SHAMap::SHAMap (
@@ -54,8 +53,7 @@ SHAMap::SHAMap (
, state_ (SHAMapState::Synching) , state_ (SHAMapState::Synching)
, type_ (t) , type_ (t)
{ {
root_ = std::make_shared<SHAMapTreeNode> (seq_); root_ = std::make_shared<SHAMapInnerNode> (seq_);
root_->makeInner ();
} }
SHAMap::~SHAMap () SHAMap::~SHAMap ()
@@ -91,7 +89,7 @@ SHAMap::getStack (uint256 const& id, bool include_nonmatching_leaf) const
// produce a stack of nodes along the way, with the terminal node at the top // produce a stack of nodes along the way, with the terminal node at the top
SharedPtrNodeStack stack; SharedPtrNodeStack stack;
std::shared_ptr<SHAMapTreeNode> node = root_; std::shared_ptr<SHAMapAbstractNode> node = root_;
SHAMapNodeID nodeID; SHAMapNodeID nodeID;
while (!node->isLeaf ()) while (!node->isLeaf ())
@@ -100,15 +98,16 @@ SHAMap::getStack (uint256 const& id, bool include_nonmatching_leaf) const
int branch = nodeID.selectBranch (id); int branch = nodeID.selectBranch (id);
assert (branch >= 0); assert (branch >= 0);
auto inner = std::static_pointer_cast<SHAMapInnerNode>(std::move(node));
if (node->isEmptyBranch (branch)) if (inner->isEmptyBranch (branch))
return stack; return stack;
node = descendThrow (node, branch); node = descendThrow (inner, branch);
nodeID = nodeID.getChildNodeID (branch); nodeID = nodeID.getChildNodeID (branch);
} }
if (include_nonmatching_leaf || (node->peekItem ()->getTag () == id)) if (include_nonmatching_leaf ||
(std::static_pointer_cast<SHAMapTreeNode>(node)->peekItem()->getTag() == id))
stack.push ({node, nodeID}); stack.push ({node, nodeID});
return stack; return stack;
@@ -116,7 +115,7 @@ SHAMap::getStack (uint256 const& id, bool include_nonmatching_leaf) const
void void
SHAMap::dirtyUp (SharedPtrNodeStack& stack, SHAMap::dirtyUp (SharedPtrNodeStack& stack,
uint256 const& target, std::shared_ptr<SHAMapTreeNode> child) uint256 const& target, std::shared_ptr<SHAMapAbstractNode> child)
{ {
// walk the tree up from through the inner nodes to the root_ // walk the tree up from through the inner nodes to the root_
// update hashes and links // update hashes and links
@@ -128,15 +127,15 @@ SHAMap::dirtyUp (SharedPtrNodeStack& stack,
while (!stack.empty ()) while (!stack.empty ())
{ {
std::shared_ptr<SHAMapTreeNode> node = stack.top ().first; auto node = std::dynamic_pointer_cast<SHAMapInnerNode>(stack.top ().first);
SHAMapNodeID nodeID = stack.top ().second; SHAMapNodeID nodeID = stack.top ().second;
stack.pop (); stack.pop ();
assert (node->isInnerNode ()); assert (node != nullptr);
int branch = nodeID.selectBranch (target); int branch = nodeID.selectBranch (target);
assert (branch >= 0); assert (branch >= 0);
unshareNode (node, nodeID); node = unshareNode(std::move(node), nodeID);
node->setChild (branch, child); node->setChild (branch, child);
#ifdef ST_DEBUG #ifdef ST_DEBUG
@@ -149,28 +148,29 @@ SHAMap::dirtyUp (SharedPtrNodeStack& stack,
SHAMapTreeNode* SHAMap::walkToPointer (uint256 const& id) const SHAMapTreeNode* SHAMap::walkToPointer (uint256 const& id) const
{ {
SHAMapTreeNode* inNode = root_.get (); auto inNode = root_.get();
SHAMapNodeID nodeID; SHAMapNodeID nodeID;
uint256 nodeHash; uint256 nodeHash;
while (inNode->isInner ()) while (inNode->isInner ())
{ {
int branch = nodeID.selectBranch (id); int branch = nodeID.selectBranch (id);
auto inner = static_cast<SHAMapInnerNode*>(inNode);
if (inNode->isEmptyBranch (branch)) if (inner->isEmptyBranch (branch))
return nullptr; return nullptr;
inNode = descendThrow (inNode, branch); inNode = descendThrow (inner, branch);
nodeID = nodeID.getChildNodeID (branch); nodeID = nodeID.getChildNodeID (branch);
} }
return (inNode->peekItem()->getTag () == id) ? inNode : nullptr; auto ret = static_cast<SHAMapTreeNode*>(inNode);
return ret->peekItem()->getTag() == id ? ret : nullptr;
} }
std::shared_ptr<SHAMapTreeNode> std::shared_ptr<SHAMapAbstractNode>
SHAMap::fetchNodeFromDB (uint256 const& hash) const SHAMap::fetchNodeFromDB (uint256 const& hash) const
{ {
std::shared_ptr<SHAMapTreeNode> node; std::shared_ptr<SHAMapAbstractNode> node;
if (backed_) if (backed_)
{ {
@@ -179,7 +179,7 @@ SHAMap::fetchNodeFromDB (uint256 const& hash) const
{ {
try try
{ {
node = std::make_shared <SHAMapTreeNode> (obj->getData(), node = SHAMapAbstractNode::make(obj->getData(),
0, snfPREFIX, hash, true); 0, snfPREFIX, hash, true);
canonicalize (hash, node); canonicalize (hash, node);
} }
@@ -201,36 +201,30 @@ SHAMap::fetchNodeFromDB (uint256 const& hash) const
} }
// See if a sync filter has a node // See if a sync filter has a node
std::shared_ptr<SHAMapTreeNode> SHAMap::checkFilter ( std::shared_ptr<SHAMapAbstractNode>
uint256 const& hash, SHAMap::checkFilter(uint256 const& hash, SHAMapNodeID const& id,
SHAMapNodeID const& id,
SHAMapSyncFilter* filter) const SHAMapSyncFilter* filter) const
{ {
std::shared_ptr<SHAMapTreeNode> node; std::shared_ptr<SHAMapAbstractNode> node;
Blob nodeData; Blob nodeData;
if (filter->haveNode (id, hash, nodeData)) if (filter->haveNode (id, hash, nodeData))
{ {
node = std::make_shared <SHAMapTreeNode> ( node = SHAMapAbstractNode::make(nodeData, 0, snfPREFIX, hash, true);
nodeData, 0, snfPREFIX, hash, true);
filter->gotNode (true, id, hash, nodeData, node->getType ()); filter->gotNode (true, id, hash, nodeData, node->getType ());
if (backed_) if (backed_)
canonicalize (hash, node); canonicalize (hash, node);
} }
return node; return node;
} }
// Get a node without throwing // Get a node without throwing
// Used on maps where missing nodes are expected // Used on maps where missing nodes are expected
std::shared_ptr<SHAMapTreeNode> SHAMap::fetchNodeNT( std::shared_ptr<SHAMapAbstractNode> SHAMap::fetchNodeNT(
SHAMapNodeID const& id, SHAMapNodeID const& id,
uint256 const& hash, uint256 const& hash,
SHAMapSyncFilter* filter) const SHAMapSyncFilter* filter) const
{ {
std::shared_ptr<SHAMapTreeNode> node = getCache (hash); std::shared_ptr<SHAMapAbstractNode> node = getCache (hash);
if (node) if (node)
return node; return node;
@@ -250,9 +244,9 @@ std::shared_ptr<SHAMapTreeNode> SHAMap::fetchNodeNT(
return node; return node;
} }
std::shared_ptr<SHAMapTreeNode> SHAMap::fetchNodeNT (uint256 const& hash) const std::shared_ptr<SHAMapAbstractNode> SHAMap::fetchNodeNT (uint256 const& hash) const
{ {
std::shared_ptr<SHAMapTreeNode> node = getCache (hash); auto node = getCache (hash);
if (!node && backed_) if (!node && backed_)
node = fetchNodeFromDB (hash); node = fetchNodeFromDB (hash);
@@ -261,9 +255,9 @@ std::shared_ptr<SHAMapTreeNode> SHAMap::fetchNodeNT (uint256 const& hash) const
} }
// Throw if the node is missing // Throw if the node is missing
std::shared_ptr<SHAMapTreeNode> SHAMap::fetchNode (uint256 const& hash) const std::shared_ptr<SHAMapAbstractNode> SHAMap::fetchNode (uint256 const& hash) const
{ {
std::shared_ptr<SHAMapTreeNode> node = fetchNodeNT (hash); auto node = fetchNodeNT (hash);
if (!node) if (!node)
throw SHAMapMissingNode (type_, hash); throw SHAMapMissingNode (type_, hash);
@@ -271,9 +265,9 @@ std::shared_ptr<SHAMapTreeNode> SHAMap::fetchNode (uint256 const& hash) const
return node; return node;
} }
SHAMapTreeNode* SHAMap::descendThrow (SHAMapTreeNode* parent, int branch) const SHAMapAbstractNode* SHAMap::descendThrow (SHAMapInnerNode* parent, int branch) const
{ {
SHAMapTreeNode* ret = descend (parent, branch); SHAMapAbstractNode* ret = descend (parent, branch);
if (! ret && ! parent->isEmptyBranch (branch)) if (! ret && ! parent->isEmptyBranch (branch))
throw SHAMapMissingNode (type_, parent->getChildHash (branch)); throw SHAMapMissingNode (type_, parent->getChildHash (branch));
@@ -281,10 +275,10 @@ SHAMapTreeNode* SHAMap::descendThrow (SHAMapTreeNode* parent, int branch) const
return ret; return ret;
} }
std::shared_ptr<SHAMapTreeNode> std::shared_ptr<SHAMapAbstractNode>
SHAMap::descendThrow (std::shared_ptr<SHAMapTreeNode> const& parent, int branch) const SHAMap::descendThrow (std::shared_ptr<SHAMapInnerNode> const& parent, int branch) const
{ {
std::shared_ptr<SHAMapTreeNode> ret = descend (parent, branch); std::shared_ptr<SHAMapAbstractNode> ret = descend (parent, branch);
if (! ret && ! parent->isEmptyBranch (branch)) if (! ret && ! parent->isEmptyBranch (branch))
throw SHAMapMissingNode (type_, parent->getChildHash (branch)); throw SHAMapMissingNode (type_, parent->getChildHash (branch));
@@ -292,24 +286,24 @@ SHAMap::descendThrow (std::shared_ptr<SHAMapTreeNode> const& parent, int branch)
return ret; return ret;
} }
SHAMapTreeNode* SHAMap::descend (SHAMapTreeNode* parent, int branch) const SHAMapAbstractNode* SHAMap::descend (SHAMapInnerNode* parent, int branch) const
{ {
SHAMapTreeNode* ret = parent->getChildPointer (branch); SHAMapAbstractNode* ret = parent->getChildPointer (branch);
if (ret || !backed_) if (ret || !backed_)
return ret; return ret;
std::shared_ptr<SHAMapTreeNode> node = fetchNodeNT (parent->getChildHash (branch)); std::shared_ptr<SHAMapAbstractNode> node = fetchNodeNT (parent->getChildHash (branch));
if (!node) if (!node)
return nullptr; return nullptr;
parent->canonicalizeChild (branch, node); node = parent->canonicalizeChild (branch, std::move(node));
return node.get (); return node.get ();
} }
std::shared_ptr<SHAMapTreeNode> std::shared_ptr<SHAMapAbstractNode>
SHAMap::descend (std::shared_ptr<SHAMapTreeNode> const& parent, int branch) const SHAMap::descend (std::shared_ptr<SHAMapInnerNode> const& parent, int branch) const
{ {
std::shared_ptr<SHAMapTreeNode> node = parent->getChild (branch); std::shared_ptr<SHAMapAbstractNode> node = parent->getChild (branch);
if (node || !backed_) if (node || !backed_)
return node; return node;
@@ -317,23 +311,23 @@ SHAMap::descend (std::shared_ptr<SHAMapTreeNode> const& parent, int branch) cons
if (!node) if (!node)
return nullptr; return nullptr;
parent->canonicalizeChild (branch, node); node = parent->canonicalizeChild (branch, std::move(node));
return node; return node;
} }
// Gets the node that would be hooked to this branch, // Gets the node that would be hooked to this branch,
// but doesn't hook it up. // but doesn't hook it up.
std::shared_ptr<SHAMapTreeNode> std::shared_ptr<SHAMapAbstractNode>
SHAMap::descendNoStore (std::shared_ptr<SHAMapTreeNode> const& parent, int branch) const SHAMap::descendNoStore (std::shared_ptr<SHAMapInnerNode> const& parent, int branch) const
{ {
std::shared_ptr<SHAMapTreeNode> ret = parent->getChild (branch); std::shared_ptr<SHAMapAbstractNode> ret = parent->getChild (branch);
if (!ret && backed_) if (!ret && backed_)
ret = fetchNode (parent->getChildHash (branch)); ret = fetchNode (parent->getChildHash (branch));
return ret; return ret;
} }
std::pair <SHAMapTreeNode*, SHAMapNodeID> std::pair <SHAMapAbstractNode*, SHAMapNodeID>
SHAMap::descend (SHAMapTreeNode * parent, SHAMapNodeID const& parentID, SHAMap::descend (SHAMapInnerNode * parent, SHAMapNodeID const& parentID,
int branch, SHAMapSyncFilter * filter) const int branch, SHAMapSyncFilter * filter) const
{ {
assert (parent->isInner ()); assert (parent->isInner ());
@@ -341,16 +335,16 @@ SHAMap::descend (SHAMapTreeNode * parent, SHAMapNodeID const& parentID,
assert (!parent->isEmptyBranch (branch)); assert (!parent->isEmptyBranch (branch));
SHAMapNodeID childID = parentID.getChildNodeID (branch); SHAMapNodeID childID = parentID.getChildNodeID (branch);
SHAMapTreeNode* child = parent->getChildPointer (branch); SHAMapAbstractNode* child = parent->getChildPointer (branch);
uint256 const& childHash = parent->getChildHash (branch); uint256 const& childHash = parent->getChildHash (branch);
if (!child) if (!child)
{ {
std::shared_ptr<SHAMapTreeNode> childNode = fetchNodeNT (childID, childHash, filter); std::shared_ptr<SHAMapAbstractNode> childNode = fetchNodeNT (childID, childHash, filter);
if (childNode) if (childNode)
{ {
parent->canonicalizeChild (branch, childNode); childNode = parent->canonicalizeChild (branch, std::move(childNode));
child = childNode.get (); child = childNode.get ();
} }
} }
@@ -358,18 +352,19 @@ SHAMap::descend (SHAMapTreeNode * parent, SHAMapNodeID const& parentID,
return std::make_pair (child, childID); return std::make_pair (child, childID);
} }
SHAMapTreeNode* SHAMap::descendAsync (SHAMapTreeNode* parent, int branch, SHAMapAbstractNode*
SHAMap::descendAsync (SHAMapInnerNode* parent, int branch,
SHAMapNodeID const& childID, SHAMapSyncFilter * filter, bool & pending) const SHAMapNodeID const& childID, SHAMapSyncFilter * filter, bool & pending) const
{ {
pending = false; pending = false;
SHAMapTreeNode* ret = parent->getChildPointer (branch); SHAMapAbstractNode* ret = parent->getChildPointer (branch);
if (ret) if (ret)
return ret; return ret;
uint256 const& hash = parent->getChildHash (branch); uint256 const& hash = parent->getChildHash (branch);
std::shared_ptr<SHAMapTreeNode> ptr = getCache (hash); std::shared_ptr<SHAMapAbstractNode> ptr = getCache (hash);
if (!ptr) if (!ptr)
{ {
if (filter) if (filter)
@@ -386,7 +381,7 @@ SHAMapTreeNode* SHAMap::descendAsync (SHAMapTreeNode* parent, int branch,
if (!obj) if (!obj)
return nullptr; return nullptr;
ptr = std::make_shared <SHAMapTreeNode> (obj->getData(), 0, snfPREFIX, hash, true); ptr = SHAMapAbstractNode::make(obj->getData(), 0, snfPREFIX, hash, true);
if (backed_) if (backed_)
canonicalize (hash, ptr); canonicalize (hash, ptr);
@@ -394,49 +389,49 @@ SHAMapTreeNode* SHAMap::descendAsync (SHAMapTreeNode* parent, int branch,
} }
if (ptr) if (ptr)
parent->canonicalizeChild (branch, ptr); ptr = parent->canonicalizeChild (branch, std::move(ptr));
return ptr.get (); return ptr.get ();
} }
void template <class Node>
SHAMap::unshareNode (std::shared_ptr<SHAMapTreeNode>& node, SHAMapNodeID const& nodeID) std::shared_ptr<Node>
SHAMap::unshareNode (std::shared_ptr<Node> node, SHAMapNodeID const& nodeID)
{ {
// make sure the node is suitable for the intended operation (copy on write) // make sure the node is suitable for the intended operation (copy on write)
assert (node->isValid ()); assert (node->isValid ());
assert (node->getSeq () <= seq_); assert (node->getSeq () <= seq_);
if (node->getSeq () != seq_) if (node->getSeq () != seq_)
{ {
// have a CoW // have a CoW
assert (state_ != SHAMapState::Immutable); assert (state_ != SHAMapState::Immutable);
node = std::static_pointer_cast<Node>(node->clone(seq_));
node = std::make_shared<SHAMapTreeNode> (*node, seq_); // here's to the new node, same as the old node
assert (node->isValid ()); assert (node->isValid ());
if (nodeID.isRoot ()) if (nodeID.isRoot ())
root_ = node; root_ = node;
} }
return node;
} }
SHAMapTreeNode* SHAMapTreeNode*
SHAMap::firstBelow (SHAMapTreeNode* node) const SHAMap::firstBelow (SHAMapAbstractNode* node) const
{ {
// Return the first item below this node // Return the first item below this node
do do
{ {
assert(node != nullptr); assert(node != nullptr);
if (node->hasItem ()) if (node->isLeaf ())
return node; return static_cast<SHAMapTreeNode*>(node);
// Walk down the tree // Walk down the tree
bool foundNode = false; bool foundNode = false;
auto inner = static_cast<SHAMapInnerNode*>(node);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
if (!node->isEmptyBranch (i)) if (!inner->isEmptyBranch (i))
{ {
node = descendThrow (node, i); node = descendThrow (inner, i);
foundNode = true; foundNode = true;
break; break;
} }
@@ -448,20 +443,21 @@ SHAMap::firstBelow (SHAMapTreeNode* node) const
} }
SHAMapTreeNode* SHAMapTreeNode*
SHAMap::lastBelow (SHAMapTreeNode* node) const SHAMap::lastBelow (SHAMapAbstractNode* node) const
{ {
do do
{ {
if (node->hasItem ()) if (node->isLeaf ())
return node; return static_cast<SHAMapTreeNode*>(node);
// Walk down the tree // Walk down the tree
bool foundNode = false; bool foundNode = false;
auto inner = static_cast<SHAMapInnerNode*>(node);
for (int i = 15; i >= 0; --i) for (int i = 15; i >= 0; --i)
{ {
if (!node->isEmptyBranch (i)) if (!inner->isEmptyBranch (i))
{ {
node = descendThrow (node, i); node = descendThrow (inner, i);
foundNode = true; foundNode = true;
break; break;
} }
@@ -473,21 +469,22 @@ SHAMap::lastBelow (SHAMapTreeNode* node) const
} }
std::shared_ptr<SHAMapItem> std::shared_ptr<SHAMapItem>
SHAMap::onlyBelow (SHAMapTreeNode* node) const SHAMap::onlyBelow (SHAMapAbstractNode* node) const
{ {
// If there is only one item below this node, return it // If there is only one item below this node, return it
while (!node->isLeaf ()) while (!node->isLeaf ())
{ {
SHAMapTreeNode* nextNode = nullptr; SHAMapAbstractNode* nextNode = nullptr;
auto inner = static_cast<SHAMapInnerNode*>(node);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
if (!node->isEmptyBranch (i)) if (!inner->isEmptyBranch (i))
{ {
if (nextNode) if (nextNode)
return std::shared_ptr<SHAMapItem> (); return std::shared_ptr<SHAMapItem> ();
nextNode = descendThrow (node, i); nextNode = descendThrow (inner, i);
} }
} }
@@ -502,9 +499,10 @@ SHAMap::onlyBelow (SHAMapTreeNode* node) const
// An inner node must have at least one leaf // An inner node must have at least one leaf
// below it, unless it's the root_ // below it, unless it's the root_
assert (node->hasItem () || (node == root_.get ())); auto leaf = static_cast<SHAMapTreeNode*>(node);
assert (leaf->hasItem () || (leaf == root_.get ()));
return node->peekItem (); return leaf->peekItem ();
} }
static std::shared_ptr< static std::shared_ptr<
@@ -559,7 +557,8 @@ std::shared_ptr<SHAMapItem> SHAMap::peekNextItem (uint256 const& id) const
return peekNextItem (id, type); return peekNextItem (id, type);
} }
std::shared_ptr<SHAMapItem> SHAMap::peekNextItem (uint256 const& id, SHAMapTreeNode::TNType& type) const std::shared_ptr<SHAMapItem>
SHAMap::peekNextItem (uint256 const& id, SHAMapTreeNode::TNType& type) const
{ {
// Get a pointer to the next item in the tree after a given item - item need not be in tree // Get a pointer to the next item in the tree after a given item - item need not be in tree
@@ -567,32 +566,34 @@ std::shared_ptr<SHAMapItem> SHAMap::peekNextItem (uint256 const& id, SHAMapTreeN
while (!stack.empty ()) while (!stack.empty ())
{ {
SHAMapTreeNode* node = stack.top().first.get(); auto node = stack.top().first.get();
SHAMapNodeID nodeID = stack.top().second; auto nodeID = stack.top().second;
stack.pop (); stack.pop ();
if (node->isLeaf ()) if (node->isLeaf ())
{ {
if (node->peekItem ()->getTag () > id) auto leaf = static_cast<SHAMapTreeNode*>(node);
if (leaf->peekItem ()->getTag () > id)
{ {
type = node->getType (); type = leaf->getType ();
return node->peekItem (); return leaf->peekItem ();
} }
} }
else else
{ {
// breadth-first // breadth-first
auto inner = static_cast<SHAMapInnerNode*>(node);
for (int i = nodeID.selectBranch (id) + 1; i < 16; ++i) for (int i = nodeID.selectBranch (id) + 1; i < 16; ++i)
if (!node->isEmptyBranch (i)) if (!inner->isEmptyBranch (i))
{ {
node = descendThrow (node, i); node = descendThrow (inner, i);
node = firstBelow (node); auto leaf = firstBelow (node);
if (!node || node->isInner ()) if (!leaf)
throw (std::runtime_error ("missing/corrupt node")); throw (std::runtime_error ("missing/corrupt node"));
type = node->getType (); type = leaf->getType ();
return node->peekItem (); return leaf->peekItem ();
} }
} }
} }
@@ -602,30 +603,33 @@ std::shared_ptr<SHAMapItem> SHAMap::peekNextItem (uint256 const& id, SHAMapTreeN
} }
// Get a pointer to the previous item in the tree after a given item - item need not be in tree // Get a pointer to the previous item in the tree after a given item - item need not be in tree
std::shared_ptr<SHAMapItem> SHAMap::peekPrevItem (uint256 const& id) const std::shared_ptr<SHAMapItem>
SHAMap::peekPrevItem (uint256 const& id) const
{ {
auto stack = getStack (id, true); auto stack = getStack (id, true);
while (!stack.empty ()) while (!stack.empty ())
{ {
SHAMapTreeNode* node = stack.top ().first.get(); auto node = stack.top ().first.get();
SHAMapNodeID nodeID = stack.top ().second; auto nodeID = stack.top ().second;
stack.pop (); stack.pop ();
if (node->isLeaf ()) if (node->isLeaf ())
{ {
if (node->peekItem ()->getTag () < id) auto leaf = static_cast<SHAMapTreeNode*>(node);
return node->peekItem (); if (leaf->peekItem ()->getTag () < id)
return leaf->peekItem ();
} }
else else
{ {
auto inner = static_cast<SHAMapInnerNode*>(node);
for (int i = nodeID.selectBranch (id) - 1; i >= 0; --i) for (int i = nodeID.selectBranch (id) - 1; i >= 0; --i)
{ {
if (!node->isEmptyBranch (i)) if (!inner->isEmptyBranch (i))
{ {
node = descendThrow (node, i); node = descendThrow (inner, i);
node = lastBelow (node); auto leaf = lastBelow (node);
return node->peekItem (); return leaf->peekItem ();
} }
} }
} }
@@ -685,10 +689,10 @@ bool SHAMap::delItem (uint256 const& id)
if (stack.empty ()) if (stack.empty ())
throw (std::runtime_error ("missing node")); throw (std::runtime_error ("missing node"));
std::shared_ptr<SHAMapTreeNode> leaf = stack.top ().first; auto leaf = std::dynamic_pointer_cast<SHAMapTreeNode>(stack.top ().first);
stack.pop (); stack.pop ();
if (!leaf || !leaf->hasItem () || (leaf->peekItem ()->getTag () != id)) if (!leaf || (leaf->peekItem ()->getTag () != id))
return false; return false;
SHAMapTreeNode::TNType type = leaf->getType (); SHAMapTreeNode::TNType type = leaf->getType ();
@@ -696,17 +700,17 @@ bool SHAMap::delItem (uint256 const& id)
// What gets attached to the end of the chain // What gets attached to the end of the chain
// (For now, nothing, since we deleted the leaf) // (For now, nothing, since we deleted the leaf)
uint256 prevHash; uint256 prevHash;
std::shared_ptr<SHAMapTreeNode> prevNode; std::shared_ptr<SHAMapAbstractNode> prevNode;
while (!stack.empty ()) while (!stack.empty ())
{ {
std::shared_ptr<SHAMapTreeNode> node = stack.top ().first; auto node = std::dynamic_pointer_cast<SHAMapInnerNode>(stack.top ().first);
SHAMapNodeID nodeID = stack.top ().second; SHAMapNodeID nodeID = stack.top ().second;
stack.pop (); stack.pop ();
assert (node->isInner ()); assert (node);
unshareNode (node, nodeID); node = unshareNode(std::move(node), nodeID);
node->setChild (nodeID.selectBranch (id), prevNode); node->setChild (nodeID.selectBranch (id), prevNode);
if (!nodeID.isRoot ()) if (!nodeID.isRoot ())
@@ -736,12 +740,15 @@ bool SHAMap::delItem (uint256 const& id)
break; break;
} }
} }
node->setItem (item, type); prevNode = std::make_shared<SHAMapTreeNode>(item, type, node->getSeq());
prevHash = prevNode->getNodeHash();
} }
else
{
prevHash = node->getNodeHash (); prevHash = node->getNodeHash ();
prevNode = std::move (node); prevNode = std::move (node);
} }
}
else else
{ {
// This node is now the end of the branch // This node is now the end of the branch
@@ -770,29 +777,34 @@ SHAMap::addGiveItem (std::shared_ptr<SHAMapItem> const& item,
if (stack.empty ()) if (stack.empty ())
throw (std::runtime_error ("missing node")); throw (std::runtime_error ("missing node"));
std::shared_ptr<SHAMapTreeNode> node = stack.top ().first; auto node = stack.top ().first;
SHAMapNodeID nodeID = stack.top ().second; auto nodeID = stack.top ().second;
stack.pop (); stack.pop ();
if (node->isLeaf () && (node->peekItem ()->getTag () == tag)) if (node->isLeaf())
{
auto leaf = std::static_pointer_cast<SHAMapTreeNode>(node);
if (leaf->peekItem()->getTag() == tag)
return false; return false;
}
unshareNode (node, nodeID); node = unshareNode(std::move(node), nodeID);
if (node->isInner ()) if (node->isInner ())
{ {
// easy case, we end on an inner node // easy case, we end on an inner node
auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
int branch = nodeID.selectBranch (tag); int branch = nodeID.selectBranch (tag);
assert (node->isEmptyBranch (branch)); assert (inner->isEmptyBranch (branch));
auto newNode = std::make_shared<SHAMapTreeNode> (item, type, seq_); auto newNode = std::make_shared<SHAMapTreeNode> (item, type, seq_);
node->setChild (branch, newNode); inner->setChild (branch, newNode);
} }
else else
{ {
// this is a leaf node that has to be made an inner node holding two items // this is a leaf node that has to be made an inner node holding two items
std::shared_ptr<SHAMapItem> otherItem = node->peekItem (); auto leaf = std::static_pointer_cast<SHAMapTreeNode>(node);
std::shared_ptr<SHAMapItem> otherItem = leaf->peekItem ();
assert (otherItem && (tag != otherItem->getTag ())); assert (otherItem && (tag != otherItem->getTag ()));
node->makeInner (); node = std::make_shared<SHAMapInnerNode>(node->getSeq());
int b1, b2; int b1, b2;
@@ -803,8 +815,7 @@ SHAMap::addGiveItem (std::shared_ptr<SHAMapItem> const& item,
// we need a new inner node, since both go on same branch at this level // we need a new inner node, since both go on same branch at this level
nodeID = nodeID.getChildNodeID (b1); nodeID = nodeID.getChildNodeID (b1);
node = std::make_shared<SHAMapTreeNode> (seq_); node = std::make_shared<SHAMapInnerNode> (seq_);
node->makeInner ();
} }
// we can add the two leaf nodes here // we can add the two leaf nodes here
@@ -813,11 +824,12 @@ SHAMap::addGiveItem (std::shared_ptr<SHAMapItem> const& item,
std::shared_ptr<SHAMapTreeNode> newNode = std::shared_ptr<SHAMapTreeNode> newNode =
std::make_shared<SHAMapTreeNode> (item, type, seq_); std::make_shared<SHAMapTreeNode> (item, type, seq_);
assert (newNode->isValid () && newNode->isLeaf ()); assert (newNode->isValid () && newNode->isLeaf ());
node->setChild (b1, newNode); auto inner = std::static_pointer_cast<SHAMapInnerNode>(node);
inner->setChild (b1, newNode);
newNode = std::make_shared<SHAMapTreeNode> (otherItem, type, seq_); newNode = std::make_shared<SHAMapTreeNode> (otherItem, type, seq_);
assert (newNode->isValid () && newNode->isLeaf ()); assert (newNode->isValid () && newNode->isLeaf ());
node->setChild (b2, newNode); inner->setChild (b2, newNode);
} }
dirtyUp (stack, tag, node); dirtyUp (stack, tag, node);
@@ -855,17 +867,17 @@ SHAMap::updateGiveItem (std::shared_ptr<SHAMapItem> const& item,
if (stack.empty ()) if (stack.empty ())
throw (std::runtime_error ("missing node")); throw (std::runtime_error ("missing node"));
std::shared_ptr<SHAMapTreeNode> node = stack.top ().first; auto node = std::dynamic_pointer_cast<SHAMapTreeNode>(stack.top().first);
SHAMapNodeID nodeID = stack.top ().second; auto nodeID = stack.top ().second;
stack.pop (); stack.pop ();
if (!node->isLeaf () || (node->peekItem ()->getTag () != tag)) if (!node || (node->peekItem ()->getTag () != tag))
{ {
assert (false); assert (false);
return false; return false;
} }
unshareNode (node, nodeID); node = unshareNode(std::move(node), nodeID);
if (!node->setItem (item, !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE : if (!node->setItem (item, !isTransaction ? SHAMapTreeNode::tnACCOUNT_STATE :
(hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM))) (hasMeta ? SHAMapTreeNode::tnTRANSACTION_MD : SHAMapTreeNode::tnTRANSACTION_NM)))
@@ -903,7 +915,7 @@ bool SHAMap::fetchRoot (uint256 const& hash, SHAMapSyncFilter* filter)
} }
} }
std::shared_ptr<SHAMapTreeNode> newRoot = fetchNodeNT (SHAMapNodeID(), hash, filter); auto newRoot = fetchNodeNT (SHAMapNodeID(), hash, filter);
if (newRoot) if (newRoot)
{ {
@@ -924,8 +936,9 @@ bool SHAMap::fetchRoot (uint256 const& hash, SHAMapSyncFilter* filter)
// //
// 2) An unshareable node is shared. This happens when you make // 2) An unshareable node is shared. This happens when you make
// a mutable snapshot of a mutable SHAMap. // a mutable snapshot of a mutable SHAMap.
void SHAMap::writeNode ( std::shared_ptr<SHAMapAbstractNode>
NodeObjectType t, std::uint32_t seq, std::shared_ptr<SHAMapTreeNode>& node) const SHAMap::writeNode (
NodeObjectType t, std::uint32_t seq, std::shared_ptr<SHAMapAbstractNode> node) const
{ {
// Node is ours, so we can just make it shareable // Node is ours, so we can just make it shareable
assert (node->getSeq() == seq_); assert (node->getSeq() == seq_);
@@ -938,12 +951,15 @@ void SHAMap::writeNode (
node->addRaw (s, snfPREFIX); node->addRaw (s, snfPREFIX);
f_.db().store (t, f_.db().store (t,
std::move (s.modData ()), node->getNodeHash ()); std::move (s.modData ()), node->getNodeHash ());
return node;
} }
// We can't modify an inner node someone else might have a // We can't modify an inner node someone else might have a
// pointer to because flushing modifies inner nodes -- it // pointer to because flushing modifies inner nodes -- it
// makes them point to canonical/shared nodes. // makes them point to canonical/shared nodes.
void SHAMap::preFlushNode (std::shared_ptr<SHAMapTreeNode>& node) const template <class Node>
std::shared_ptr<Node>
SHAMap::preFlushNode (std::shared_ptr<Node> node) const
{ {
// A shared node should never need to be flushed // A shared node should never need to be flushed
// because that would imply someone modified it // because that would imply someone modified it
@@ -953,8 +969,9 @@ void SHAMap::preFlushNode (std::shared_ptr<SHAMapTreeNode>& node) const
{ {
// Node is not uniquely ours, so unshare it before // Node is not uniquely ours, so unshare it before
// possibly modifying it // possibly modifying it
node = std::make_shared <SHAMapTreeNode> (*node, seq_); node = std::static_pointer_cast<Node>(node->clone(seq_));
} }
return node;
} }
int SHAMap::unshare () int SHAMap::unshare ()
@@ -975,24 +992,26 @@ SHAMap::walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq)
int flushed = 0; int flushed = 0;
Serializer s; Serializer s;
if (!root_ || (root_->getSeq() == 0) || root_->isEmpty ()) if (!root_ || (root_->getSeq() == 0))
return flushed; return flushed;
if (root_->isLeaf()) if (root_->isLeaf())
{ // special case -- root_ is leaf { // special case -- root_ is leaf
preFlushNode (root_); root_ = preFlushNode (std::move(root_));
if (doWrite && backed_) if (doWrite && backed_)
writeNode (t, seq, root_); root_ = writeNode(t, seq, std::move(root_));
return 1; return 1;
} }
auto node = std::static_pointer_cast<SHAMapInnerNode>(root_);
if (node->isEmpty())
return flushed;
// Stack of {parent,index,child} pointers representing // Stack of {parent,index,child} pointers representing
// inner nodes we are in the process of flushing // inner nodes we are in the process of flushing
using StackEntry = std::pair <std::shared_ptr<SHAMapTreeNode>, int>; using StackEntry = std::pair <std::shared_ptr<SHAMapInnerNode>, int>;
std::stack <StackEntry, std::vector<StackEntry>> stack; std::stack <StackEntry, std::vector<StackEntry>> stack;
std::shared_ptr<SHAMapTreeNode> node = root_; node = preFlushNode(std::move(node));
preFlushNode (node);
int pos = 0; int pos = 0;
@@ -1010,7 +1029,7 @@ SHAMap::walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq)
// No need to do I/O. If the node isn't linked, // No need to do I/O. If the node isn't linked,
// it can't need to be flushed // it can't need to be flushed
int branch = pos; int branch = pos;
std::shared_ptr<SHAMapTreeNode> child = node->getChild (pos++); auto child = node->getChild(pos++);
if (child && (child->getSeq() != 0)) if (child && (child->getSeq() != 0))
{ {
@@ -1019,11 +1038,11 @@ SHAMap::walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq)
if (child->isInner ()) if (child->isInner ())
{ {
// save our place and work on this node // save our place and work on this node
preFlushNode (child); child = preFlushNode(std::move(child));
stack.emplace (std::move (node), branch); stack.emplace (std::move (node), branch);
node = std::move (child); node = std::static_pointer_cast<SHAMapInnerNode>(std::move(child));
pos = 0; pos = 0;
} }
else else
@@ -1031,13 +1050,13 @@ SHAMap::walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq)
// flush this leaf // flush this leaf
++flushed; ++flushed;
preFlushNode (child); child = preFlushNode(std::move(child));
assert (node->getSeq() == seq_); assert (node->getSeq() == seq_);
child->updateHash(); child->updateHash();
if (doWrite && backed_) if (doWrite && backed_)
writeNode (t, seq, child); child = writeNode(t, seq, std::move(child));
node->shareChild (branch, child); node->shareChild (branch, child);
} }
@@ -1050,14 +1069,15 @@ SHAMap::walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq)
// This inner node can now be shared // This inner node can now be shared
if (doWrite && backed_) if (doWrite && backed_)
writeNode (t, seq, node); node = std::static_pointer_cast<SHAMapInnerNode>(writeNode(t, seq,
std::move(node)));
++flushed; ++flushed;
if (stack.empty ()) if (stack.empty ())
break; break;
std::shared_ptr<SHAMapTreeNode> parent = std::move (stack.top().first); auto parent = std::move (stack.top().first);
pos = stack.top().second; pos = stack.top().second;
stack.pop(); stack.pop();
@@ -1082,13 +1102,13 @@ void SHAMap::dump (bool hash) const
if (journal_.info) journal_.info << if (journal_.info) journal_.info <<
" MAP Contains"; " MAP Contains";
std::stack <std::pair <SHAMapTreeNode*, SHAMapNodeID> > stack; std::stack <std::pair <SHAMapAbstractNode*, SHAMapNodeID> > stack;
stack.push ({root_.get (), SHAMapNodeID ()}); stack.push ({root_.get (), SHAMapNodeID ()});
do do
{ {
SHAMapTreeNode* node = stack.top().first; auto node = stack.top().first;
SHAMapNodeID nodeID = stack.top().second; auto nodeID = stack.top().second;
stack.pop(); stack.pop();
if (journal_.info) journal_.info << if (journal_.info) journal_.info <<
@@ -1099,14 +1119,15 @@ void SHAMap::dump (bool hash) const
if (node->isInner ()) if (node->isInner ())
{ {
auto inner = static_cast<SHAMapInnerNode*>(node);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
if (!node->isEmptyBranch (i)) if (!inner->isEmptyBranch (i))
{ {
SHAMapTreeNode* child = node->getChildPointer (i); auto child = inner->getChildPointer (i);
if (child) if (child)
{ {
assert (child->getNodeHash() == node->getChildHash (i)); assert (child->getNodeHash() == inner->getChildHash (i));
stack.push ({child, nodeID.getChildNodeID (i)}); stack.push ({child, nodeID.getChildNodeID (i)});
} }
} }
@@ -1121,14 +1142,15 @@ void SHAMap::dump (bool hash) const
leafCount << " resident leaves"; leafCount << " resident leaves";
} }
std::shared_ptr<SHAMapTreeNode> SHAMap::getCache (uint256 const& hash) const std::shared_ptr<SHAMapAbstractNode> SHAMap::getCache (uint256 const& hash) const
{ {
std::shared_ptr<SHAMapTreeNode> ret = f_.treecache().fetch (hash); auto ret = f_.treecache().fetch (hash);
assert (!ret || !ret->getSeq()); assert (!ret || !ret->getSeq());
return ret; return ret;
} }
void SHAMap::canonicalize (uint256 const& hash, std::shared_ptr<SHAMapTreeNode>& node) const void
SHAMap::canonicalize(uint256 const& hash, std::shared_ptr<SHAMapAbstractNode>& node) const
{ {
assert (backed_); assert (backed_);
assert (node->getSeq() == 0); assert (node->getSeq() == 0);

View File

@@ -30,13 +30,13 @@ namespace ripple {
// makes no sense at all. (And our sync algorithm will avoid // makes no sense at all. (And our sync algorithm will avoid
// synchronizing matching branches too.) // synchronizing matching branches too.)
bool SHAMap::walkBranch (SHAMapTreeNode* node, bool SHAMap::walkBranch (SHAMapAbstractNode* node,
std::shared_ptr<SHAMapItem> const& otherMapItem, bool isFirstMap, std::shared_ptr<SHAMapItem> const& otherMapItem, bool isFirstMap,
Delta& differences, int& maxCount) const Delta& differences, int& maxCount) const
{ {
// Walk a branch of a SHAMap that's matched by an empty branch or single item in the other map // Walk a branch of a SHAMap that's matched by an empty branch or single item in the other map
std::stack <SHAMapTreeNode*, std::vector<SHAMapTreeNode*>> nodeStack; std::stack <SHAMapAbstractNode*, std::vector<SHAMapAbstractNode*>> nodeStack;
nodeStack.push ({node}); nodeStack.push (node);
bool emptyBranch = !otherMapItem; bool emptyBranch = !otherMapItem;
@@ -48,14 +48,15 @@ bool SHAMap::walkBranch (SHAMapTreeNode* node,
if (node->isInner ()) if (node->isInner ())
{ {
// This is an inner node, add all non-empty branches // This is an inner node, add all non-empty branches
auto inner = static_cast<SHAMapInnerNode*>(node);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
if (!node->isEmptyBranch (i)) if (!inner->isEmptyBranch (i))
nodeStack.push ({descendThrow (node, i)}); nodeStack.push ({descendThrow (inner, i)});
} }
else else
{ {
// This is a leaf node, process its item // This is a leaf node, process its item
std::shared_ptr<SHAMapItem> item = node->peekItem (); auto item = static_cast<SHAMapTreeNode*>(node)->peekItem();
if (emptyBranch || (item->getTag () != otherMapItem->getTag ())) if (emptyBranch || (item->getTag () != otherMapItem->getTag ()))
{ {
@@ -126,14 +127,14 @@ SHAMap::compare (std::shared_ptr<SHAMap> const& otherMap,
if (getHash () == otherMap->getHash ()) if (getHash () == otherMap->getHash ())
return true; return true;
using StackEntry = std::pair <SHAMapTreeNode*, SHAMapTreeNode*>; using StackEntry = std::pair <SHAMapAbstractNode*, SHAMapAbstractNode*>;
std::stack <StackEntry, std::vector<StackEntry>> nodeStack; // track nodes we've pushed std::stack <StackEntry, std::vector<StackEntry>> nodeStack; // track nodes we've pushed
nodeStack.push ({root_.get(), otherMap->root_.get()}); nodeStack.push ({root_.get(), otherMap->root_.get()});
while (!nodeStack.empty ()) while (!nodeStack.empty ())
{ {
SHAMapTreeNode* ourNode = nodeStack.top().first; SHAMapAbstractNode* ourNode = nodeStack.top().first;
SHAMapTreeNode* otherNode = nodeStack.top().second; SHAMapAbstractNode* otherNode = nodeStack.top().second;
nodeStack.pop (); nodeStack.pop ();
if (!ourNode || !otherNode) if (!ourNode || !otherNode)
@@ -145,71 +146,79 @@ SHAMap::compare (std::shared_ptr<SHAMap> const& otherMap,
if (ourNode->isLeaf () && otherNode->isLeaf ()) if (ourNode->isLeaf () && otherNode->isLeaf ())
{ {
// two leaves // two leaves
if (ourNode->peekItem()->getTag () == otherNode->peekItem()->getTag ()) auto ours = static_cast<SHAMapTreeNode*>(ourNode);
auto other = static_cast<SHAMapTreeNode*>(otherNode);
if (ours->peekItem()->getTag () == other->peekItem()->getTag ())
{ {
if (ourNode->peekItem()->peekData () != otherNode->peekItem()->peekData ()) if (ours->peekItem()->peekData () != other->peekItem()->peekData ())
{ {
differences.insert (std::make_pair (ourNode->peekItem()->getTag (), differences.insert (std::make_pair (ours->peekItem()->getTag (),
DeltaRef (ourNode->peekItem (), DeltaRef (ours->peekItem (),
otherNode->peekItem ()))); other->peekItem ())));
if (--maxCount <= 0) if (--maxCount <= 0)
return false; return false;
} }
} }
else else
{ {
differences.insert (std::make_pair(ourNode->peekItem()->getTag (), differences.insert (std::make_pair(ours->peekItem()->getTag (),
DeltaRef(ourNode->peekItem(), DeltaRef(ours->peekItem(),
std::shared_ptr<SHAMapItem> ()))); std::shared_ptr<SHAMapItem> ())));
if (--maxCount <= 0) if (--maxCount <= 0)
return false; return false;
differences.insert(std::make_pair(otherNode->peekItem()->getTag (), differences.insert(std::make_pair(other->peekItem()->getTag (),
DeltaRef(std::shared_ptr<SHAMapItem>(), DeltaRef(std::shared_ptr<SHAMapItem>(),
otherNode->peekItem ()))); other->peekItem ())));
if (--maxCount <= 0) if (--maxCount <= 0)
return false; return false;
} }
} }
else if (ourNode->isInner () && otherNode->isLeaf ()) else if (ourNode->isInner () && otherNode->isLeaf ())
{ {
if (!walkBranch (ourNode, otherNode->peekItem (), auto ours = static_cast<SHAMapInnerNode*>(ourNode);
auto other = static_cast<SHAMapTreeNode*>(otherNode);
if (!walkBranch (ours, other->peekItem (),
true, differences, maxCount)) true, differences, maxCount))
return false; return false;
} }
else if (ourNode->isLeaf () && otherNode->isInner ()) else if (ourNode->isLeaf () && otherNode->isInner ())
{ {
if (!otherMap->walkBranch (otherNode, ourNode->peekItem (), auto ours = static_cast<SHAMapTreeNode*>(ourNode);
auto other = static_cast<SHAMapInnerNode*>(otherNode);
if (!otherMap->walkBranch (other, ours->peekItem (),
false, differences, maxCount)) false, differences, maxCount))
return false; return false;
} }
else if (ourNode->isInner () && otherNode->isInner ()) else if (ourNode->isInner () && otherNode->isInner ())
{ {
auto ours = static_cast<SHAMapInnerNode*>(ourNode);
auto other = static_cast<SHAMapInnerNode*>(otherNode);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
if (ourNode->getChildHash (i) != otherNode->getChildHash (i)) if (ours->getChildHash (i) != other->getChildHash (i))
{ {
if (otherNode->isEmptyBranch (i)) if (other->isEmptyBranch (i))
{ {
// We have a branch, the other tree does not // We have a branch, the other tree does not
SHAMapTreeNode* iNode = descendThrow (ourNode, i); SHAMapAbstractNode* iNode = descendThrow (ours, i);
if (!walkBranch (iNode, if (!walkBranch (iNode,
std::shared_ptr<SHAMapItem> (), true, std::shared_ptr<SHAMapItem> (), true,
differences, maxCount)) differences, maxCount))
return false; return false;
} }
else if (ourNode->isEmptyBranch (i)) else if (ours->isEmptyBranch (i))
{ {
// The other tree has a branch, we do not // The other tree has a branch, we do not
SHAMapTreeNode* iNode = SHAMapAbstractNode* iNode =
otherMap->descendThrow(otherNode, i); otherMap->descendThrow(other, i);
if (!otherMap->walkBranch (iNode, if (!otherMap->walkBranch (iNode,
std::shared_ptr<SHAMapItem>(), std::shared_ptr<SHAMapItem>(),
false, differences, maxCount)) false, differences, maxCount))
return false; return false;
} }
else // The two trees have different non-empty branches else // The two trees have different non-empty branches
nodeStack.push ({descendThrow (ourNode, i), nodeStack.push ({descendThrow (ours, i),
otherMap->descendThrow (otherNode, i)}); otherMap->descendThrow (other, i)});
} }
} }
else else
@@ -224,26 +233,27 @@ void SHAMap::walkMap (std::vector<SHAMapMissingNode>& missingNodes, int maxMissi
if (!root_->isInner ()) // root_ is only node, and we have it if (!root_->isInner ()) // root_ is only node, and we have it
return; return;
using StackEntry = std::shared_ptr<SHAMapTreeNode>; using StackEntry = std::shared_ptr<SHAMapInnerNode>;
std::stack <StackEntry, std::vector <StackEntry>> nodeStack; std::stack <StackEntry, std::vector <StackEntry>> nodeStack;
nodeStack.push (root_); nodeStack.push (std::static_pointer_cast<SHAMapInnerNode>(root_));
while (!nodeStack.empty ()) while (!nodeStack.empty ())
{ {
std::shared_ptr<SHAMapTreeNode> node = std::move (nodeStack.top()); std::shared_ptr<SHAMapInnerNode> node = std::move (nodeStack.top());
nodeStack.pop (); nodeStack.pop ();
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
if (!node->isEmptyBranch (i)) if (!node->isEmptyBranch (i))
{ {
std::shared_ptr<SHAMapTreeNode> nextNode = descendNoStore (node, i); std::shared_ptr<SHAMapAbstractNode> nextNode = descendNoStore (node, i);
if (nextNode) if (nextNode)
{ {
if (nextNode->isInner ()) if (nextNode->isInner ())
nodeStack.push (std::move (nextNode)); nodeStack.push(
std::static_pointer_cast<SHAMapInnerNode>(nextNode));
} }
else else
{ {

View File

@@ -30,27 +30,29 @@ static const uint256 uZero;
static bool visitLeavesHelper ( static bool visitLeavesHelper (
std::function <void (std::shared_ptr<SHAMapItem> const&)> const& function, std::function <void (std::shared_ptr<SHAMapItem> const&)> const& function,
SHAMapTreeNode& node) SHAMapAbstractNode& node)
{ {
// Adapt visitNodes to visitLeaves // Adapt visitNodes to visitLeaves
if (!node.isInner ()) if (!node.isInner ())
function (node.peekItem ()); function (static_cast<SHAMapTreeNode&>(node).peekItem ());
return false; return false;
} }
void SHAMap::visitLeaves (std::function<void (std::shared_ptr<SHAMapItem> const& item)> const& leafFunction) const void
SHAMap::visitLeaves(
std::function<void(std::shared_ptr<SHAMapItem> const& item)> const& leafFunction) const
{ {
visitNodes (std::bind (visitLeavesHelper, visitNodes (std::bind (visitLeavesHelper,
std::cref (leafFunction), std::placeholders::_1)); std::cref (leafFunction), std::placeholders::_1));
} }
void SHAMap::visitNodes(std::function<bool (SHAMapTreeNode&)> const& function) const void SHAMap::visitNodes(std::function<bool (SHAMapAbstractNode&)> const& function) const
{ {
// Visit every node in a SHAMap // Visit every node in a SHAMap
assert (root_->isValid ()); assert (root_->isValid ());
if (!root_ || root_->isEmpty ()) if (!root_)
return; return;
function (*root_); function (*root_);
@@ -58,10 +60,10 @@ void SHAMap::visitNodes(std::function<bool (SHAMapTreeNode&)> const& function) c
if (!root_->isInner ()) if (!root_->isInner ())
return; return;
using StackEntry = std::pair <int, std::shared_ptr<SHAMapTreeNode>>; using StackEntry = std::pair <int, std::shared_ptr<SHAMapInnerNode>>;
std::stack <StackEntry, std::vector <StackEntry>> stack; std::stack <StackEntry, std::vector <StackEntry>> stack;
std::shared_ptr<SHAMapTreeNode> node = root_; auto node = std::static_pointer_cast<SHAMapInnerNode>(root_);
int pos = 0; int pos = 0;
while (1) while (1)
@@ -71,7 +73,7 @@ void SHAMap::visitNodes(std::function<bool (SHAMapTreeNode&)> const& function) c
uint256 childHash; uint256 childHash;
if (!node->isEmptyBranch (pos)) if (!node->isEmptyBranch (pos))
{ {
std::shared_ptr<SHAMapTreeNode> child = descendNoStore (node, pos); std::shared_ptr<SHAMapAbstractNode> child = descendNoStore (node, pos);
if (function (*child)) if (function (*child))
return; return;
@@ -90,7 +92,7 @@ void SHAMap::visitNodes(std::function<bool (SHAMapTreeNode&)> const& function) c
} }
// descend to the child's first position // descend to the child's first position
node = child; node = std::static_pointer_cast<SHAMapInnerNode>(child);
pos = 0; pos = 0;
} }
} }
@@ -112,26 +114,30 @@ void SHAMap::visitNodes(std::function<bool (SHAMapTreeNode&)> const& function) c
but not available locally. The filter can hold alternate sources of but not available locally. The filter can hold alternate sources of
nodes that are not permanently stored locally nodes that are not permanently stored locally
*/ */
void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max, void
SHAMapSyncFilter* filter) SHAMap::getMissingNodes(std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes,
int max, SHAMapSyncFilter* filter)
{ {
assert (root_->isValid ()); assert (root_->isValid ());
assert (root_->getNodeHash().isNonZero ()); assert (root_->getNodeHash().isNonZero ());
std::uint32_t generation = f_.fullbelow().getGeneration(); std::uint32_t generation = f_.fullbelow().getGeneration();
if (root_->isFullBelow (generation))
{
clearSynching ();
return;
}
if (!root_->isInner ()) if (!root_->isInner ())
{ {
if (journal_.warning) journal_.warning << if (generation == 0)
clearSynching();
else if (journal_.warning) journal_.warning <<
"synching empty tree"; "synching empty tree";
return; return;
} }
if (std::static_pointer_cast<SHAMapInnerNode>(root_)->isFullBelow (generation))
{
clearSynching ();
return;
}
int const maxDefer = f_.db().getDesiredAsyncReadCount (); int const maxDefer = f_.db().getDesiredAsyncReadCount ();
// Track the missing hashes we have found so far // Track the missing hashes we have found so far
@@ -140,15 +146,15 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
while (1) while (1)
{ {
std::vector <std::tuple <SHAMapTreeNode*, int, SHAMapNodeID>> deferredReads; std::vector <std::tuple <SHAMapInnerNode*, int, SHAMapNodeID>> deferredReads;
deferredReads.reserve (maxDefer + 16); deferredReads.reserve (maxDefer + 16);
using StackEntry = std::tuple<SHAMapTreeNode*, SHAMapNodeID, int, int, bool>; using StackEntry = std::tuple<SHAMapInnerNode*, SHAMapNodeID, int, int, bool>;
std::stack <StackEntry, std::vector<StackEntry>> stack; std::stack <StackEntry, std::vector<StackEntry>> stack;
// Traverse the map without blocking // Traverse the map without blocking
SHAMapTreeNode *node = root_.get (); auto node = static_cast<SHAMapInnerNode*>(root_.get());
SHAMapNodeID nodeID; SHAMapNodeID nodeID;
// The firstChild value is selected randomly so if multiple threads // The firstChild value is selected randomly so if multiple threads
@@ -177,7 +183,7 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
{ {
SHAMapNodeID childID = nodeID.getChildNodeID (branch); SHAMapNodeID childID = nodeID.getChildNodeID (branch);
bool pending = false; bool pending = false;
SHAMapTreeNode* d = descendAsync (node, branch, childID, filter, pending); auto d = descendAsync (node, branch, childID, filter, pending);
if (!d) if (!d)
{ {
@@ -197,13 +203,14 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
fullBelow = false; // This node is not known full below fullBelow = false; // This node is not known full below
} }
else if (d->isInner () && !d->isFullBelow (generation)) else if (d->isInner() &&
!static_cast<SHAMapInnerNode*>(d)->isFullBelow(generation))
{ {
stack.push (std::make_tuple (node, nodeID, stack.push (std::make_tuple (node, nodeID,
firstChild, currentChild, fullBelow)); firstChild, currentChild, fullBelow));
// Switch to processing the child node // Switch to processing the child node
node = d; node = static_cast<SHAMapInnerNode*>(d);
nodeID = childID; nodeID = childID;
firstChild = rand() % 256; firstChild = rand() % 256;
currentChild = 0; currentChild = 0;
@@ -256,13 +263,13 @@ void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<ui
auto const& nodeID = std::get<2>(node); auto const& nodeID = std::get<2>(node);
auto const& nodeHash = parent->getChildHash (branch); auto const& nodeHash = parent->getChildHash (branch);
std::shared_ptr<SHAMapTreeNode> nodePtr = fetchNodeNT (nodeID, nodeHash, filter); auto nodePtr = fetchNodeNT(nodeID, nodeHash, filter);
if (nodePtr) if (nodePtr)
{ {
++hits; ++hits;
if (backed_) if (backed_)
canonicalize (nodeHash, nodePtr); canonicalize (nodeHash, nodePtr);
parent->canonicalizeChild (branch, nodePtr); nodePtr = parent->canonicalizeChild (branch, std::move(nodePtr));
} }
else if ((max > 0) && (missingHashes.insert (nodeHash).second)) else if ((max > 0) && (missingHashes.insert (nodeHash).second))
{ {
@@ -310,17 +317,17 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted,
// Gets a node and some of its children // Gets a node and some of its children
// to a specified depth // to a specified depth
SHAMapTreeNode* node = root_.get (); auto node = root_.get();
SHAMapNodeID nodeID; SHAMapNodeID nodeID;
while (node && node->isInner () && (nodeID.getDepth() < wanted.getDepth())) while (node && node->isInner () && (nodeID.getDepth() < wanted.getDepth()))
{ {
int branch = nodeID.selectBranch (wanted.getNodeID()); int branch = nodeID.selectBranch (wanted.getNodeID());
auto inner = static_cast<SHAMapInnerNode*>(node);
if (node->isEmptyBranch (branch)) if (inner->isEmptyBranch (branch))
return false; return false;
node = descendThrow (node, branch); node = descendThrow(inner, branch);
nodeID = nodeID.getChildNodeID (branch); nodeID = nodeID.getChildNodeID (branch);
} }
@@ -331,14 +338,14 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted,
return false; return false;
} }
if (node->isInner () && node->isEmpty ()) if (node->isInner() && static_cast<SHAMapInnerNode*>(node)->isEmpty())
{ {
if (journal_.warning) journal_.warning << if (journal_.warning) journal_.warning <<
"peer requests empty node"; "peer requests empty node";
return false; return false;
} }
std::stack<std::tuple <SHAMapTreeNode*, SHAMapNodeID, int>> stack; std::stack<std::tuple <SHAMapAbstractNode*, SHAMapNodeID, int>> stack;
stack.emplace (node, nodeID, depth); stack.emplace (node, nodeID, depth);
while (! stack.empty ()) while (! stack.empty ())
@@ -356,16 +363,17 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted,
{ {
// We descend inner nodes with only a single child // We descend inner nodes with only a single child
// without decrementing the depth // without decrementing the depth
int bc = node->getBranchCount(); auto inner = static_cast<SHAMapInnerNode*>(node);
int bc = inner->getBranchCount();
if ((depth > 0) || (bc == 1)) if ((depth > 0) || (bc == 1))
{ {
// We need to process this node's children // We need to process this node's children
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
if (! node->isEmptyBranch (i)) if (! inner->isEmptyBranch (i))
{ {
SHAMapNodeID childID = nodeID.getChildNodeID (i); auto childID = nodeID.getChildNodeID (i);
SHAMapTreeNode* childNode = descendThrow (node, i); auto childNode = descendThrow (inner, i);
if (childNode->isInner () && if (childNode->isInner () &&
((depth > 1) || (bc == 1))) ((depth > 1) || (bc == 1)))
@@ -410,9 +418,7 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode,
} }
assert (seq_ >= 1); assert (seq_ >= 1);
auto node = std::make_shared<SHAMapTreeNode> (rootNode, 0, auto node = SHAMapAbstractNode::make(rootNode, 0, format, uZero, false);
format, uZero, false);
if (!node) if (!node)
return SHAMapAddNode::invalid (); return SHAMapAddNode::invalid ();
@@ -452,10 +458,7 @@ SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SH
} }
assert (seq_ >= 1); assert (seq_ >= 1);
std::shared_ptr<SHAMapTreeNode> node = auto node = SHAMapAbstractNode::make(rootNode, 0, format, uZero, false);
std::make_shared<SHAMapTreeNode> (rootNode, 0,
format, uZero, false);
if (!node || node->getNodeHash () != hash) if (!node || node->getNodeHash () != hash)
return SHAMapAddNode::invalid (); return SHAMapAddNode::invalid ();
@@ -494,27 +497,28 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
std::uint32_t generation = f_.fullbelow().getGeneration(); std::uint32_t generation = f_.fullbelow().getGeneration();
SHAMapNodeID iNodeID; SHAMapNodeID iNodeID;
SHAMapTreeNode* iNode = root_.get (); auto iNode = root_.get();
while (iNode->isInner () && !iNode->isFullBelow (generation) && while (iNode->isInner () &&
!static_cast<SHAMapInnerNode*>(iNode)->isFullBelow(generation) &&
(iNodeID.getDepth () < node.getDepth ())) (iNodeID.getDepth () < node.getDepth ()))
{ {
int branch = iNodeID.selectBranch (node.getNodeID ()); int branch = iNodeID.selectBranch (node.getNodeID ());
assert (branch >= 0); assert (branch >= 0);
auto inner = static_cast<SHAMapInnerNode*>(iNode);
if (iNode->isEmptyBranch (branch)) if (inner->isEmptyBranch (branch))
{ {
if (journal_.warning) journal_.warning << if (journal_.warning) journal_.warning <<
"Add known node for empty branch" << node; "Add known node for empty branch" << node;
return SHAMapAddNode::invalid (); return SHAMapAddNode::invalid ();
} }
uint256 childHash = iNode->getChildHash (branch); uint256 childHash = inner->getChildHash (branch);
if (f_.fullbelow().touch_if_exists (childHash)) if (f_.fullbelow().touch_if_exists (childHash))
return SHAMapAddNode::duplicate (); return SHAMapAddNode::duplicate ();
SHAMapTreeNode* prevNode = iNode; auto prevNode = inner;
std::tie (iNode, iNodeID) = descend (iNode, iNodeID, branch, filter); std::tie(iNode, iNodeID) = descend(inner, iNodeID, branch, filter);
if (!iNode) if (!iNode)
{ {
@@ -531,8 +535,7 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
return SHAMapAddNode::invalid (); return SHAMapAddNode::invalid ();
} }
auto newNode = std::make_shared<SHAMapTreeNode>(rawNode, 0, snfWIRE, auto newNode = SHAMapAbstractNode::make(rawNode, 0, snfWIRE, uZero, false);
uZero, false);
if (!newNode->isInBounds (iNodeID)) if (!newNode->isInBounds (iNodeID))
{ {
@@ -551,7 +554,7 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
if (backed_) if (backed_)
canonicalize (childHash, newNode); canonicalize (childHash, newNode);
prevNode->canonicalizeChild (branch, newNode); newNode = prevNode->canonicalizeChild (branch, std::move(newNode));
if (filter) if (filter)
{ {
@@ -573,13 +576,14 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
bool SHAMap::deepCompare (SHAMap& other) const bool SHAMap::deepCompare (SHAMap& other) const
{ {
// Intended for debug/test only // Intended for debug/test only
std::stack <std::pair <SHAMapTreeNode*, SHAMapTreeNode*> > stack; std::stack <std::pair <SHAMapAbstractNode*, SHAMapAbstractNode*> > stack;
stack.push ({root_.get(), other.root_.get()}); stack.push ({root_.get(), other.root_.get()});
while (!stack.empty ()) while (!stack.empty ())
{ {
SHAMapTreeNode *node, *otherNode; SHAMapAbstractNode* node;
SHAMapAbstractNode* otherNode;
std::tie(node, otherNode) = stack.top (); std::tie(node, otherNode) = stack.top ();
stack.pop (); stack.pop ();
@@ -600,8 +604,8 @@ bool SHAMap::deepCompare (SHAMap& other) const
{ {
if (!otherNode->isLeaf ()) if (!otherNode->isLeaf ())
return false; return false;
auto nodePeek = node->peekItem(); auto& nodePeek = static_cast<SHAMapTreeNode*>(node)->peekItem();
auto otherNodePeek = otherNode->peekItem(); auto& otherNodePeek = static_cast<SHAMapTreeNode*>(otherNode)->peekItem();
if (nodePeek->getTag() != otherNodePeek->getTag()) if (nodePeek->getTag() != otherNodePeek->getTag())
return false; return false;
if (nodePeek->peekData() != otherNodePeek->peekData()) if (nodePeek->peekData() != otherNodePeek->peekData())
@@ -611,21 +615,22 @@ bool SHAMap::deepCompare (SHAMap& other) const
{ {
if (!otherNode->isInner ()) if (!otherNode->isInner ())
return false; return false;
auto node_inner = static_cast<SHAMapInnerNode*>(node);
auto other_inner = static_cast<SHAMapInnerNode*>(otherNode);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
if (node->isEmptyBranch (i)) if (node_inner->isEmptyBranch (i))
{ {
if (!otherNode->isEmptyBranch (i)) if (!other_inner->isEmptyBranch (i))
return false; return false;
} }
else else
{ {
if (otherNode->isEmptyBranch (i)) if (other_inner->isEmptyBranch (i))
return false; return false;
SHAMapTreeNode *next = descend (node, i); auto next = descend(node_inner, i);
SHAMapTreeNode *otherNext = other.descend (otherNode, i); auto otherNext = other.descend(other_inner, i);
if (!next || !otherNext) if (!next || !otherNext)
{ {
if (journal_.warning) journal_.warning << if (journal_.warning) journal_.warning <<
@@ -647,17 +652,17 @@ bool
SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID, SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID,
uint256 const& targetNodeHash) const uint256 const& targetNodeHash) const
{ {
SHAMapTreeNode* node = root_.get (); auto node = root_.get();
SHAMapNodeID nodeID; SHAMapNodeID nodeID;
while (node->isInner () && (nodeID.getDepth () < targetNodeID.getDepth ())) while (node->isInner () && (nodeID.getDepth () < targetNodeID.getDepth ()))
{ {
int branch = nodeID.selectBranch (targetNodeID.getNodeID ()); int branch = nodeID.selectBranch (targetNodeID.getNodeID ());
auto inner = static_cast<SHAMapInnerNode*>(node);
if (node->isEmptyBranch (branch)) if (inner->isEmptyBranch (branch))
return false; return false;
node = descendThrow (node, branch); node = descendThrow (inner, branch);
nodeID = nodeID.getChildNodeID (branch); nodeID = nodeID.getChildNodeID (branch);
} }
@@ -669,7 +674,7 @@ SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID,
bool bool
SHAMap::hasLeafNode (uint256 const& tag, uint256 const& targetNodeHash) const SHAMap::hasLeafNode (uint256 const& tag, uint256 const& targetNodeHash) const
{ {
SHAMapTreeNode* node = root_.get (); auto node = root_.get();
SHAMapNodeID nodeID; SHAMapNodeID nodeID;
if (!node->isInner()) // only one leaf node in the tree if (!node->isInner()) // only one leaf node in the tree
@@ -678,14 +683,14 @@ SHAMap::hasLeafNode (uint256 const& tag, uint256 const& targetNodeHash) const
do do
{ {
int branch = nodeID.selectBranch (tag); int branch = nodeID.selectBranch (tag);
auto inner = static_cast<SHAMapInnerNode*>(node);
if (node->isEmptyBranch (branch)) if (inner->isEmptyBranch (branch))
return false; // Dead end, node must not be here return false; // Dead end, node must not be here
if (node->getChildHash (branch) == targetNodeHash) // Matching leaf, no need to retrieve it if (inner->getChildHash (branch) == targetNodeHash) // Matching leaf, no need to retrieve it
return true; return true;
node = descendThrow (node, branch); node = descendThrow(inner, branch);
nodeID = nodeID.getChildNodeID (branch); nodeID = nodeID.getChildNodeID (branch);
} }
while (node->isInner()); while (node->isInner());
@@ -706,7 +711,7 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
std::function<void (uint256 const&, const Blob&)> func) const std::function<void (uint256 const&, const Blob&)> func) const
{ {
visitDifferences (have, visitDifferences (have,
[includeLeaves, &max, &func] (SHAMapTreeNode& smn) -> bool [includeLeaves, &max, &func] (SHAMapAbstractNode& smn) -> bool
{ {
if (includeLeaves || smn.isInner ()) if (includeLeaves || smn.isInner ())
{ {
@@ -721,7 +726,9 @@ void SHAMap::getFetchPack (SHAMap* have, bool includeLeaves, int max,
}); });
} }
void SHAMap::visitDifferences (SHAMap* have, std::function <bool (SHAMapTreeNode&)> func) const void
SHAMap::visitDifferences(SHAMap* have,
std::function<bool (SHAMapAbstractNode&)> func) const
{ {
// Visit every node in this SHAMap that is not present // Visit every node in this SHAMap that is not present
// in the specified SHAMap // in the specified SHAMap
@@ -734,20 +741,21 @@ void SHAMap::visitDifferences (SHAMap* have, std::function <bool (SHAMapTreeNode
if (root_->isLeaf ()) if (root_->isLeaf ())
{ {
if (! have || ! have->hasLeafNode (root_->peekItem()->getTag (), root_->getNodeHash ())) auto leaf = std::static_pointer_cast<SHAMapTreeNode>(root_);
if (!have || !have->hasLeafNode(leaf->peekItem()->getTag(), leaf->getNodeHash()))
func (*root_); func (*root_);
return; return;
} }
// contains unexplored non-matching inner node entries // contains unexplored non-matching inner node entries
using StackEntry = std::pair <SHAMapTreeNode*, SHAMapNodeID>; using StackEntry = std::pair <SHAMapInnerNode*, SHAMapNodeID>;
std::stack <StackEntry, std::vector<StackEntry>> stack; std::stack <StackEntry, std::vector<StackEntry>> stack;
stack.push ({root_.get(), SHAMapNodeID{}}); stack.push ({static_cast<SHAMapInnerNode*>(root_.get()), SHAMapNodeID{}});
while (!stack.empty()) while (!stack.empty())
{ {
SHAMapTreeNode* node; SHAMapInnerNode* node;
SHAMapNodeID nodeID; SHAMapNodeID nodeID;
std::tie (node, nodeID) = stack.top (); std::tie (node, nodeID) = stack.top ();
stack.pop (); stack.pop ();
@@ -763,14 +771,16 @@ void SHAMap::visitDifferences (SHAMap* have, std::function <bool (SHAMapTreeNode
{ {
uint256 const& childHash = node->getChildHash (i); uint256 const& childHash = node->getChildHash (i);
SHAMapNodeID childID = nodeID.getChildNodeID (i); SHAMapNodeID childID = nodeID.getChildNodeID (i);
SHAMapTreeNode* next = descendThrow (node, i); auto next = descendThrow(node, i);
if (next->isInner ()) if (next->isInner ())
{ {
if (! have || ! have->hasInnerNode (childID, childHash)) if (!have || !have->hasInnerNode(childID, childHash))
stack.push ({next, childID}); stack.push ({static_cast<SHAMapInnerNode*>(next), childID});
} }
else if (! have || ! have->hasLeafNode (next->peekItem()->getTag(), childHash)) else if (!have || !have->hasLeafNode(
static_cast<SHAMapTreeNode*>(next)->peekItem()->getTag(),
childHash))
{ {
if (! func (*next)) if (! func (*next))
return; return;

View File

@@ -31,55 +31,50 @@
namespace ripple { namespace ripple {
std::mutex SHAMapTreeNode::childLock; std::mutex SHAMapInnerNode::childLock;
SHAMapTreeNode::SHAMapTreeNode (std::uint32_t seq) SHAMapAbstractNode::~SHAMapAbstractNode() = default;
: mSeq (seq)
, mType (tnERROR) std::shared_ptr<SHAMapAbstractNode>
, mIsBranch (0) SHAMapInnerNode::clone(std::uint32_t seq) const
, mFullBelowGen (0)
{ {
auto p = std::make_shared<SHAMapInnerNode>(seq);
p->mHash = mHash;
p->mIsBranch = mIsBranch;
p->mFullBelowGen = mFullBelowGen;
std::memcpy(p->mHashes, mHashes, sizeof(mHashes));
std::unique_lock <std::mutex> lock(childLock);
for (int i = 0; i < 16; ++i)
p->mChildren[i] = mChildren[i];
return std::move(p);
} }
SHAMapTreeNode::SHAMapTreeNode (const SHAMapTreeNode& node, std::uint32_t seq) std::shared_ptr<SHAMapAbstractNode>
: mHash (node.mHash) SHAMapTreeNode::clone(std::uint32_t seq) const
, mSeq (seq)
, mType (node.mType)
, mIsBranch (node.mIsBranch)
, mFullBelowGen (0)
{ {
if (node.mItem) return std::make_shared<SHAMapTreeNode>(mItem, mType, seq, mHash);
mItem = node.mItem;
else
{
memcpy (mHashes, node.mHashes, sizeof (mHashes));
std::unique_lock <std::mutex> lock (childLock);
for (int i = 0; i < 16; ++i)
mChildren[i] = node.mChildren[i];
}
} }
SHAMapTreeNode::SHAMapTreeNode (std::shared_ptr<SHAMapItem> const& item, SHAMapTreeNode::SHAMapTreeNode (std::shared_ptr<SHAMapItem> const& item,
TNType type, std::uint32_t seq) TNType type, std::uint32_t seq)
: mItem (item) : SHAMapAbstractNode(type, seq)
, mSeq (seq) , mItem (item)
, mType (type)
, mIsBranch (0)
, mFullBelowGen (0)
{ {
assert (item->peekData ().size () >= 12); assert (item->peekData ().size () >= 12);
updateHash (); updateHash();
} }
SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode, SHAMapTreeNode::SHAMapTreeNode (std::shared_ptr<SHAMapItem> const& item,
std::uint32_t seq, SHANodeFormat format, TNType type, std::uint32_t seq, uint256 const& hash)
: SHAMapAbstractNode(type, seq, hash)
, mItem (item)
{
assert (item->peekData ().size () >= 12);
}
std::shared_ptr<SHAMapAbstractNode>
SHAMapAbstractNode::make(Blob const& rawNode, std::uint32_t seq, SHANodeFormat format,
uint256 const& hash, bool hashValid) uint256 const& hash, bool hashValid)
: mSeq (seq)
, mType (tnERROR)
, mIsBranch (0)
, mFullBelowGen (0)
{ {
if (format == snfWIRE) if (format == snfWIRE)
{ {
@@ -110,11 +105,13 @@ SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode,
if (type == 0) if (type == 0)
{ {
// transaction // transaction
mItem = std::make_shared<SHAMapItem>( auto item = std::make_shared<SHAMapItem>(
sha512Half(HashPrefix::transactionID, sha512Half(HashPrefix::transactionID,
Slice(s.data(), s.size())), Slice(s.data(), s.size())),
s.peekData()); s.peekData());
mType = tnTRANSACTION_NM; if (hashValid)
return std::make_shared<SHAMapTreeNode>(item, tnTRANSACTION_NM, seq, hash);
return std::make_shared<SHAMapTreeNode>(item, tnTRANSACTION_NM, seq);
} }
else if (type == 1) else if (type == 1)
{ {
@@ -128,8 +125,10 @@ SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode,
if (u.isZero ()) throw std::runtime_error ("invalid AS node"); if (u.isZero ()) throw std::runtime_error ("invalid AS node");
mItem = std::make_shared<SHAMapItem> (u, s.peekData ()); auto item = std::make_shared<SHAMapItem> (u, s.peekData ());
mType = tnACCOUNT_STATE; if (hashValid)
return std::make_shared<SHAMapTreeNode>(item, tnACCOUNT_STATE, seq, hash);
return std::make_shared<SHAMapTreeNode>(item, tnACCOUNT_STATE, seq);
} }
else if (type == 2) else if (type == 2)
{ {
@@ -137,18 +136,23 @@ SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode,
if (len != 512) if (len != 512)
throw std::runtime_error ("invalid FI node"); throw std::runtime_error ("invalid FI node");
auto ret = std::make_shared<SHAMapInnerNode>(seq);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
s.get256 (mHashes[i], i * 32); s.get256 (ret->mHashes[i], i * 32);
if (mHashes[i].isNonZero ()) if (ret->mHashes[i].isNonZero ())
mIsBranch |= (1 << i); ret->mIsBranch |= (1 << i);
} }
if (hashValid)
mType = tnINNER; ret->mHash = hash;
else
ret->updateHash();
return ret;
} }
else if (type == 3) else if (type == 3)
{ {
auto ret = std::make_shared<SHAMapInnerNode>(seq);
// compressed inner // compressed inner
for (int i = 0; i < (len / 33); ++i) for (int i = 0; i < (len / 33); ++i)
{ {
@@ -157,13 +161,16 @@ SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode,
if ((pos < 0) || (pos >= 16)) throw std::runtime_error ("invalid CI node"); if ((pos < 0) || (pos >= 16)) throw std::runtime_error ("invalid CI node");
s.get256 (mHashes[pos], i * 33); s.get256 (ret->mHashes[pos], i * 33);
if (mHashes[pos].isNonZero ()) if (ret->mHashes[pos].isNonZero ())
mIsBranch |= (1 << pos); ret->mIsBranch |= (1 << pos);
} }
if (hashValid)
mType = tnINNER; ret->mHash = hash;
else
ret->updateHash();
return ret;
} }
else if (type == 4) else if (type == 4)
{ {
@@ -178,8 +185,10 @@ SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode,
if (u.isZero ()) if (u.isZero ())
throw std::runtime_error ("invalid TM node"); throw std::runtime_error ("invalid TM node");
mItem = std::make_shared<SHAMapItem> (u, s.peekData ()); auto item = std::make_shared<SHAMapItem> (u, s.peekData ());
mType = tnTRANSACTION_MD; if (hashValid)
return std::make_shared<SHAMapTreeNode>(item, tnTRANSACTION_MD, seq, hash);
return std::make_shared<SHAMapTreeNode>(item, tnTRANSACTION_MD, seq);
} }
} }
@@ -202,10 +211,12 @@ SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode,
if (prefix == HashPrefix::transactionID) if (prefix == HashPrefix::transactionID)
{ {
mItem = std::make_shared<SHAMapItem>( auto item = std::make_shared<SHAMapItem>(
sha512Half(make_Slice(rawNode)), sha512Half(make_Slice(rawNode)),
s.peekData ()); s.peekData ());
mType = tnTRANSACTION_NM; if (hashValid)
return std::make_shared<SHAMapTreeNode>(item, tnTRANSACTION_NM, seq, hash);
return std::make_shared<SHAMapTreeNode>(item, tnTRANSACTION_NM, seq);
} }
else if (prefix == HashPrefix::leafNode) else if (prefix == HashPrefix::leafNode)
{ {
@@ -222,23 +233,28 @@ SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode,
throw std::runtime_error ("invalid PLN node"); throw std::runtime_error ("invalid PLN node");
} }
mItem = std::make_shared<SHAMapItem> (u, s.peekData ()); auto item = std::make_shared<SHAMapItem> (u, s.peekData ());
mType = tnACCOUNT_STATE; if (hashValid)
return std::make_shared<SHAMapTreeNode>(item, tnACCOUNT_STATE, seq, hash);
return std::make_shared<SHAMapTreeNode>(item, tnACCOUNT_STATE, seq);
} }
else if (prefix == HashPrefix::innerNode) else if (prefix == HashPrefix::innerNode)
{ {
if (s.getLength () != 512) if (s.getLength () != 512)
throw std::runtime_error ("invalid PIN node"); throw std::runtime_error ("invalid PIN node");
auto ret = std::make_shared<SHAMapInnerNode>(seq);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{ {
s.get256 (mHashes[i], i * 32); s.get256 (ret->mHashes[i], i * 32);
if (mHashes[i].isNonZero ()) if (ret->mHashes[i].isNonZero ())
mIsBranch |= (1 << i); ret->mIsBranch |= (1 << i);
} }
if (hashValid)
mType = tnINNER; ret->mHash = hash;
else
ret->updateHash();
return ret;
} }
else if (prefix == HashPrefix::txNode) else if (prefix == HashPrefix::txNode)
{ {
@@ -249,8 +265,10 @@ SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode,
uint256 txID; uint256 txID;
s.get256 (txID, s.getLength () - 32); s.get256 (txID, s.getLength () - 32);
s.chop (32); s.chop (32);
mItem = std::make_shared<SHAMapItem> (txID, s.peekData ()); auto item = std::make_shared<SHAMapItem> (txID, s.peekData ());
mType = tnTRANSACTION_MD; if (hashValid)
return std::make_shared<SHAMapTreeNode>(item, tnTRANSACTION_MD, seq, hash);
return std::make_shared<SHAMapTreeNode>(item, tnTRANSACTION_MD, seq);
} }
else else
{ {
@@ -258,31 +276,14 @@ SHAMapTreeNode::SHAMapTreeNode (Blob const& rawNode,
throw std::runtime_error ("invalid node prefix"); throw std::runtime_error ("invalid node prefix");
} }
} }
else
{
assert (false); assert (false);
throw std::runtime_error ("Unknown format"); throw std::runtime_error ("Unknown format");
}
if (hashValid)
{
mHash = hash;
#if RIPPLE_VERIFY_NODEOBJECT_KEYS
updateHash ();
assert (mHash == hash);
#endif
}
else
updateHash ();
} }
bool SHAMapTreeNode::updateHash () bool
SHAMapInnerNode::updateHash()
{ {
uint256 nh; uint256 nh;
if (mType == tnINNER)
{
if (mIsBranch != 0) if (mIsBranch != 0)
{ {
// VFALCO This code assumes the layout of a base_uint // VFALCO This code assumes the layout of a base_uint
@@ -297,10 +298,28 @@ bool SHAMapTreeNode::updateHash ()
static_cast<uint256>(h))); static_cast<uint256>(h)));
#endif #endif
} }
else if (nh == mHash)
nh.zero (); return false;
mHash = nh;
return true;
}
void
SHAMapInnerNode::updateHashDeep()
{
for (auto pos = 0; pos < 16; ++pos)
{
if (mChildren[pos] != nullptr)
mHashes[pos] = mChildren[pos]->getNodeHash();
} }
else if (mType == tnTRANSACTION_NM) updateHash();
}
bool
SHAMapTreeNode::updateHash()
{
uint256 nh;
if (mType == tnTRANSACTION_NM)
{ {
nh = sha512Half(HashPrefix::transactionID, nh = sha512Half(HashPrefix::transactionID,
make_Slice(mItem->peekData())); make_Slice(mItem->peekData()));
@@ -328,17 +347,7 @@ bool SHAMapTreeNode::updateHash ()
} }
void void
SHAMapTreeNode::updateHashDeep() SHAMapInnerNode::addRaw(Serializer& s, SHANodeFormat format)
{
for (auto pos = 0; pos < 16; ++pos)
{
if (mChildren[pos] != nullptr)
mHashes[pos] = mChildren[pos]->mHash;
}
updateHash();
}
void SHAMapTreeNode::addRaw (Serializer& s, SHANodeFormat format)
{ {
assert ((format == snfPREFIX) || (format == snfWIRE) || (format == snfHASH)); assert ((format == snfPREFIX) || (format == snfWIRE) || (format == snfHASH));
@@ -383,6 +392,22 @@ void SHAMapTreeNode::addRaw (Serializer& s, SHANodeFormat format)
} }
} }
} }
else
assert (false);
}
void
SHAMapTreeNode::addRaw(Serializer& s, SHANodeFormat format)
{
assert ((format == snfPREFIX) || (format == snfWIRE) || (format == snfHASH));
if (mType == tnERROR)
throw std::runtime_error ("invalid I node type");
if (format == snfHASH)
{
s.add256 (getNodeHash ());
}
else if (mType == tnACCOUNT_STATE) else if (mType == tnACCOUNT_STATE)
{ {
if (format == snfPREFIX) if (format == snfPREFIX)
@@ -439,12 +464,12 @@ bool SHAMapTreeNode::setItem (std::shared_ptr<SHAMapItem> const& i, TNType type)
return updateHash (); return updateHash ();
} }
bool SHAMapTreeNode::isEmpty () const bool SHAMapInnerNode::isEmpty () const
{ {
return mIsBranch == 0; return mIsBranch == 0;
} }
int SHAMapTreeNode::getBranchCount () const int SHAMapInnerNode::getBranchCount () const
{ {
assert (isInner ()); assert (isInner ());
int count = 0; int count = 0;
@@ -456,18 +481,10 @@ int SHAMapTreeNode::getBranchCount () const
return count; return count;
} }
void SHAMapTreeNode::makeInner ()
{
mItem.reset ();
mIsBranch = 0;
memset (mHashes, 0, sizeof (mHashes));
mType = tnINNER;
mHash.zero ();
}
#ifdef BEAST_DEBUG #ifdef BEAST_DEBUG
void SHAMapTreeNode::dump (const SHAMapNodeID & id, beast::Journal journal) void
SHAMapAbstractNode::dump(const SHAMapNodeID & id, beast::Journal journal)
{ {
if (journal.debug) journal.debug << if (journal.debug) journal.debug <<
"SHAMapTreeNode(" << id.getNodeID () << ")"; "SHAMapTreeNode(" << id.getNodeID () << ")";
@@ -475,17 +492,23 @@ void SHAMapTreeNode::dump (const SHAMapNodeID & id, beast::Journal journal)
#endif // BEAST_DEBUG #endif // BEAST_DEBUG
std::string SHAMapTreeNode::getString (const SHAMapNodeID & id) const std::string
SHAMapAbstractNode::getString(const SHAMapNodeID & id) const
{ {
std::string ret = "NodeID("; std::string ret = "NodeID(";
ret += beast::lexicalCastThrow <std::string> (id.getDepth ()); ret += beast::lexicalCastThrow <std::string> (id.getDepth ());
ret += ","; ret += ",";
ret += to_string (id.getNodeID ()); ret += to_string (id.getNodeID ());
ret += ")"; ret += ")";
return ret;
}
if (isInner ()) std::string
{ SHAMapInnerNode::getString(const SHAMapNodeID & id) const
{
std::string ret = SHAMapAbstractNode::getString(id);
for (int i = 0; i < 16; ++i) for (int i = 0; i < 16; ++i)
{
if (!isEmptyBranch (i)) if (!isEmptyBranch (i))
{ {
ret += "\nb"; ret += "\nb";
@@ -494,9 +517,13 @@ std::string SHAMapTreeNode::getString (const SHAMapNodeID & id) const
ret += to_string (mHashes[i]); ret += to_string (mHashes[i]);
} }
} }
return ret;
}
if (isLeaf ()) std::string
{ SHAMapTreeNode::getString(const SHAMapNodeID & id) const
{
std::string ret = SHAMapAbstractNode::getString(id);
if (mType == tnTRANSACTION_NM) if (mType == tnTRANSACTION_NM)
ret += ",txn\n"; ret += ",txn\n";
else if (mType == tnTRANSACTION_MD) else if (mType == tnTRANSACTION_MD)
@@ -512,14 +539,12 @@ std::string SHAMapTreeNode::getString (const SHAMapNodeID & id) const
ret += to_string (mHash); ret += to_string (mHash);
ret += "/"; ret += "/";
ret += beast::lexicalCast <std::string> (mItem->size()); ret += beast::lexicalCast <std::string> (mItem->size());
}
return ret; return ret;
} }
// We are modifying an inner node // We are modifying an inner node
void void
SHAMapTreeNode::setChild (int m, std::shared_ptr<SHAMapTreeNode> const& child) SHAMapInnerNode::setChild(int m, std::shared_ptr<SHAMapAbstractNode> const& child)
{ {
assert ((m >= 0) && (m < 16)); assert ((m >= 0) && (m < 16));
assert (mType == tnINNER); assert (mType == tnINNER);
@@ -535,7 +560,7 @@ SHAMapTreeNode::setChild (int m, std::shared_ptr<SHAMapTreeNode> const& child)
} }
// finished modifying, now make shareable // finished modifying, now make shareable
void SHAMapTreeNode::shareChild (int m, std::shared_ptr<SHAMapTreeNode> const& child) void SHAMapInnerNode::shareChild (int m, std::shared_ptr<SHAMapAbstractNode> const& child)
{ {
assert ((m >= 0) && (m < 16)); assert ((m >= 0) && (m < 16));
assert (mType == tnINNER); assert (mType == tnINNER);
@@ -546,28 +571,31 @@ void SHAMapTreeNode::shareChild (int m, std::shared_ptr<SHAMapTreeNode> const& c
mChildren[m] = child; mChildren[m] = child;
} }
SHAMapTreeNode* SHAMapTreeNode::getChildPointer (int branch) SHAMapAbstractNode*
SHAMapInnerNode::getChildPointer (int branch)
{ {
assert (branch >= 0 && branch < 16); assert (branch >= 0 && branch < 16);
assert (isInnerNode ()); assert (isInner());
std::unique_lock <std::mutex> lock (childLock); std::unique_lock <std::mutex> lock (childLock);
return mChildren[branch].get (); return mChildren[branch].get ();
} }
std::shared_ptr<SHAMapTreeNode> SHAMapTreeNode::getChild (int branch) std::shared_ptr<SHAMapAbstractNode>
SHAMapInnerNode::getChild (int branch)
{ {
assert (branch >= 0 && branch < 16); assert (branch >= 0 && branch < 16);
assert (isInnerNode ()); assert (isInner());
std::unique_lock <std::mutex> lock (childLock); std::unique_lock <std::mutex> lock (childLock);
return mChildren[branch]; return mChildren[branch];
} }
void SHAMapTreeNode::canonicalizeChild (int branch, std::shared_ptr<SHAMapTreeNode>& node) std::shared_ptr<SHAMapAbstractNode>
SHAMapInnerNode::canonicalizeChild(int branch, std::shared_ptr<SHAMapAbstractNode> node)
{ {
assert (branch >= 0 && branch < 16); assert (branch >= 0 && branch < 16);
assert (isInnerNode ()); assert (isInner());
assert (node); assert (node);
assert (node->getNodeHash() == mHashes[branch]); assert (node->getNodeHash() == mHashes[branch]);
@@ -582,6 +610,7 @@ void SHAMapTreeNode::canonicalizeChild (int branch, std::shared_ptr<SHAMapTreeNo
// Hook this node up // Hook this node up
mChildren[branch] = node; mChildren[branch] = node;
} }
return node;
} }

View File

@@ -95,6 +95,45 @@ public:
unexpected (!sMap.delItem (sMap.peekFirstItem ()->getTag ()), "bad mod"); unexpected (!sMap.delItem (sMap.peekFirstItem ()->getTag ()), "bad mod");
unexpected (sMap.getHash () == mapHash, "bad snapshot"); unexpected (sMap.getHash () == mapHash, "bad snapshot");
unexpected (map2->getHash () != mapHash, "bad snapshot"); unexpected (map2->getHash () != mapHash, "bad snapshot");
testcase ("build/tear");
{
std::vector<uint256> keys(8);
keys[0].SetHex ("b92891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
keys[1].SetHex ("b92881fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
keys[2].SetHex ("b92691fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
keys[3].SetHex ("b92791fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
keys[4].SetHex ("b91891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
keys[5].SetHex ("b99891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
keys[6].SetHex ("f22891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
keys[7].SetHex ("292891fe4ef6cee585fdc6fda1e09eb4d386363158ec3321b8123e5a772c6ca8");
std::vector<uint256> hashes(8);
hashes[0].SetHex ("B7387CFEA0465759ADC718E8C42B52D2309D179B326E239EB5075C64B6281F7F");
hashes[1].SetHex ("FBC195A9592A54AB44010274163CB6BA95F497EC5BA0A8831845467FB2ECE266");
hashes[2].SetHex ("4E7D2684B65DFD48937FFB775E20175C43AF0C94066F7D5679F51AE756795B75");
hashes[3].SetHex ("7A2F312EB203695FFD164E038E281839EEF06A1B99BFC263F3CECC6C74F93E07");
hashes[4].SetHex ("395A6691A372387A703FB0F2C6D2C405DAF307D0817F8F0E207596462B0E3A3E");
hashes[5].SetHex ("D044C0A696DE3169CC70AE216A1564D69DE96582865796142CE7D98A84D9DDE4");
hashes[6].SetHex ("76DCC77C4027309B5A91AD164083264D70B77B5E43E08AEDA5EBF94361143615");
hashes[7].SetHex ("DF4220E93ADC6F5569063A01B4DC79F8DB9553B6A3222ADE23DEA02BBE7230E5");
SHAMap map (SHAMapType::FREE, f, beast::Journal());
expect (map.getHash() == uint256(), "bad initial empty map hash");
for (int i = 0; i < keys.size(); ++i)
{
SHAMapItem item (keys[i], IntToVUC (i));
map.addItem (item, true, false);
expect (map.getHash() == hashes[i], "bad buildup map hash");
}
for (int i = keys.size() - 1; i >= 0; --i)
{
expect (map.getHash() == hashes[i], "bad teardown hash");
map.delItem (keys[i]);
}
expect (map.getHash() == uint256(), "bad final empty map hash");
}
} }
}; };