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 ())
return;
SHAMapTreeNode newNode(
auto newNode = SHAMapAbstractNode::make(
Blob (node.nodedata().begin(), node.nodedata().end()),
0, snfWIRE, uZero, false);
s.erase();
newNode.addRaw(s, snfPREFIX);
newNode->addRaw(s, snfPREFIX);
auto blob = std::make_shared<Blob> (s.begin(), s.end());
getApp().getOPs().addFetchPack (newNode.getNodeHash(), blob);
getApp().getOPs().addFetchPack (newNode->getNodeHash(), blob);
}
}
catch (...)

View File

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

View File

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

View File

@@ -83,7 +83,7 @@ private:
beast::Journal journal_;
std::uint32_t seq_;
std::uint32_t ledgerSeq_; // sequence number of ledger this is part of
std::shared_ptr<SHAMapTreeNode> root_;
std::shared_ptr<SHAMapAbstractNode> root_;
SHAMapState state_;
SHAMapType type_;
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> 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;
// comparison/sync functions
@@ -192,7 +192,7 @@ public:
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,
std::function<void (uint256 const&, const Blob&)>) const;
@@ -203,30 +203,30 @@ public:
private:
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&,
std::shared_ptr<SHAMapItem> const&>;
int unshare ();
// tree node cache operations
std::shared_ptr<SHAMapTreeNode> getCache (uint256 const& hash) const;
void canonicalize (uint256 const& hash, std::shared_ptr<SHAMapTreeNode>&) const;
std::shared_ptr<SHAMapAbstractNode> getCache (uint256 const& hash) const;
void canonicalize (uint256 const& hash, std::shared_ptr<SHAMapAbstractNode>&) const;
// database operations
std::shared_ptr<SHAMapTreeNode> fetchNodeFromDB (uint256 const& hash) const;
std::shared_ptr<SHAMapTreeNode> fetchNodeNT (uint256 const& hash) const;
std::shared_ptr<SHAMapTreeNode> fetchNodeNT (
std::shared_ptr<SHAMapAbstractNode> fetchNodeFromDB (uint256 const& hash) const;
std::shared_ptr<SHAMapAbstractNode> fetchNodeNT (uint256 const& hash) const;
std::shared_ptr<SHAMapAbstractNode> fetchNodeNT (
SHAMapNodeID const& id,
uint256 const& hash,
SHAMapSyncFilter *filter) const;
std::shared_ptr<SHAMapTreeNode> fetchNode (uint256 const& hash) const;
std::shared_ptr<SHAMapTreeNode> checkFilter (uint256 const& hash, SHAMapNodeID const& id,
SHAMapSyncFilter* filter) const;
std::shared_ptr<SHAMapAbstractNode> fetchNode (uint256 const& hash) const;
std::shared_ptr<SHAMapAbstractNode> checkFilter(uint256 const& hash,
SHAMapNodeID const& id, SHAMapSyncFilter* filter) const;
/** Update hashes up to the root */
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 */
SharedPtrNodeStack
@@ -236,44 +236,50 @@ private:
SHAMapTreeNode* walkToPointer (uint256 const& id) const;
/** 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 */
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 */
void writeNode (NodeObjectType t, std::uint32_t seq,
std::shared_ptr<SHAMapTreeNode>& node) const;
std::shared_ptr<SHAMapAbstractNode>
writeNode(NodeObjectType t, std::uint32_t seq,
std::shared_ptr<SHAMapAbstractNode> node) const;
SHAMapTreeNode* firstBelow (SHAMapTreeNode*) const;
SHAMapTreeNode* lastBelow (SHAMapTreeNode*) const;
SHAMapTreeNode* firstBelow (SHAMapAbstractNode*) const;
SHAMapTreeNode* lastBelow (SHAMapAbstractNode*) const;
// Simple descent
// Get a child of the specified node
SHAMapTreeNode* descend (SHAMapTreeNode*, int branch) const;
SHAMapTreeNode* descendThrow (SHAMapTreeNode*, int branch) const;
std::shared_ptr<SHAMapTreeNode> descend (std::shared_ptr<SHAMapTreeNode> const&, int branch) const;
std::shared_ptr<SHAMapTreeNode> descendThrow (std::shared_ptr<SHAMapTreeNode> const&, int branch) const;
SHAMapAbstractNode* descend (SHAMapInnerNode*, int branch) const;
SHAMapAbstractNode* descendThrow (SHAMapInnerNode*, int branch) const;
std::shared_ptr<SHAMapAbstractNode> descend (std::shared_ptr<SHAMapInnerNode> const&, int branch) const;
std::shared_ptr<SHAMapAbstractNode> descendThrow (std::shared_ptr<SHAMapInnerNode> const&, int branch) const;
// Descend with filter
SHAMapTreeNode* descendAsync (SHAMapTreeNode* parent, int branch,
SHAMapAbstractNode* descendAsync (SHAMapInnerNode* parent, int branch,
SHAMapNodeID const& childID, SHAMapSyncFilter* filter, bool& pending) const;
std::pair <SHAMapTreeNode*, SHAMapNodeID>
descend (SHAMapTreeNode* parent, SHAMapNodeID const& parentID,
std::pair <SHAMapAbstractNode*, SHAMapNodeID>
descend (SHAMapInnerNode* parent, SHAMapNodeID const& parentID,
int branch, SHAMapSyncFilter* filter) const;
// Non-storing
// 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 */
std::shared_ptr<SHAMapItem> onlyBelow (SHAMapTreeNode*) const;
std::shared_ptr<SHAMapItem> onlyBelow (SHAMapAbstractNode*) const;
bool hasInnerNode (SHAMapNodeID const& nodeID, 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,
Delta & differences, int & maxCount) const;
int walkSubTree (bool doWrite, NodeObjectType t, std::uint32_t seq);

View File

@@ -39,7 +39,7 @@ enum SHANodeFormat
snfHASH = 3, // just the hash
};
class SHAMapTreeNode
class SHAMapAbstractNode
{
public:
enum TNType
@@ -51,112 +51,164 @@ public:
tnACCOUNT_STATE = 4
};
private:
uint256 mHash;
uint256 mHashes[16];
std::shared_ptr<SHAMapTreeNode> mChildren[16];
std::shared_ptr<SHAMapItem> mItem;
std::uint32_t mSeq;
protected:
TNType mType;
int mIsBranch;
std::uint32_t mFullBelowGen;
uint256 mHash;
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;
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:
SHAMapTreeNode (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 (Blob const & data, std::uint32_t seq,
SHANodeFormat format, uint256 const& hash, bool hashValid);
SHAMapTreeNode(std::shared_ptr<SHAMapItem> const& item, TNType type,
std::uint32_t seq, uint256 const& hash);
std::shared_ptr<SHAMapAbstractNode> clone(std::uint32_t seq) const override;
void addRaw (Serializer&, SHANodeFormat format);
uint256 const& getNodeHash () const;
void addRaw (Serializer&, SHANodeFormat format) override;
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
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
bool hasItem () const;
std::shared_ptr<SHAMapItem> const& peekItem () const;
bool setItem (std::shared_ptr<SHAMapItem> const& i, TNType type);
// sync functions
bool isFullBelow (std::uint32_t generation) const;
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;
std::string getString (SHAMapNodeID const&) const override;
bool updateHash () override;
};
// 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
std::uint32_t
SHAMapTreeNode::getSeq () const
SHAMapAbstractNode::getSeq () const
{
return mSeq;
}
inline
void
SHAMapTreeNode::setSeq (std::uint32_t s)
SHAMapAbstractNode::setSeq (std::uint32_t s)
{
mSeq = s;
}
inline
uint256 const&
SHAMapTreeNode::getNodeHash () const
SHAMapAbstractNode::getNodeHash () const
{
return mHash;
}
inline
SHAMapTreeNode::TNType
SHAMapTreeNode::getType () const
SHAMapAbstractNode::TNType
SHAMapAbstractNode::getType () const
{
return mType;
}
inline
bool
SHAMapTreeNode::isLeaf () const
SHAMapAbstractNode::isLeaf () const
{
return (mType == tnTRANSACTION_NM) || (mType == tnTRANSACTION_MD) ||
(mType == tnACCOUNT_STATE);
@@ -164,69 +216,72 @@ SHAMapTreeNode::isLeaf () const
inline
bool
SHAMapTreeNode::isInner () const
SHAMapAbstractNode::isInner () const
{
return mType == tnINNER;
}
inline
bool
SHAMapTreeNode::isInBounds (SHAMapNodeID const &id) const
{
// Nodes at depth 64 must be leaves
return (!isInner() || (id.getDepth() < 64));
}
inline
bool
SHAMapTreeNode::isValid () const
SHAMapAbstractNode::isValid () const
{
return mType != tnERROR;
}
inline
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
bool
SHAMapTreeNode::hasMetaData () 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
SHAMapInnerNode::isEmptyBranch (int m) const
{
return (mIsBranch & (1 << m)) == 0;
}
inline
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];
}
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
bool
SHAMapTreeNode::hasItem () const
@@ -241,20 +296,6 @@ SHAMapTreeNode::peekItem () const
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
#endif

View File

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

View File

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

View File

@@ -30,13 +30,13 @@ namespace ripple {
// makes no sense at all. (And our sync algorithm will avoid
// synchronizing matching branches too.)
bool SHAMap::walkBranch (SHAMapTreeNode* node,
bool SHAMap::walkBranch (SHAMapAbstractNode* node,
std::shared_ptr<SHAMapItem> const& otherMapItem, bool isFirstMap,
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
std::stack <SHAMapTreeNode*, std::vector<SHAMapTreeNode*>> nodeStack;
nodeStack.push ({node});
std::stack <SHAMapAbstractNode*, std::vector<SHAMapAbstractNode*>> nodeStack;
nodeStack.push (node);
bool emptyBranch = !otherMapItem;
@@ -48,14 +48,15 @@ bool SHAMap::walkBranch (SHAMapTreeNode* node,
if (node->isInner ())
{
// This is an inner node, add all non-empty branches
auto inner = static_cast<SHAMapInnerNode*>(node);
for (int i = 0; i < 16; ++i)
if (!node->isEmptyBranch (i))
nodeStack.push ({descendThrow (node, i)});
if (!inner->isEmptyBranch (i))
nodeStack.push ({descendThrow (inner, i)});
}
else
{
// 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 ()))
{
@@ -126,14 +127,14 @@ SHAMap::compare (std::shared_ptr<SHAMap> const& otherMap,
if (getHash () == otherMap->getHash ())
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
nodeStack.push ({root_.get(), otherMap->root_.get()});
while (!nodeStack.empty ())
{
SHAMapTreeNode* ourNode = nodeStack.top().first;
SHAMapTreeNode* otherNode = nodeStack.top().second;
SHAMapAbstractNode* ourNode = nodeStack.top().first;
SHAMapAbstractNode* otherNode = nodeStack.top().second;
nodeStack.pop ();
if (!ourNode || !otherNode)
@@ -145,71 +146,79 @@ SHAMap::compare (std::shared_ptr<SHAMap> const& otherMap,
if (ourNode->isLeaf () && otherNode->isLeaf ())
{
// 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 (),
DeltaRef (ourNode->peekItem (),
otherNode->peekItem ())));
differences.insert (std::make_pair (ours->peekItem()->getTag (),
DeltaRef (ours->peekItem (),
other->peekItem ())));
if (--maxCount <= 0)
return false;
}
}
else
{
differences.insert (std::make_pair(ourNode->peekItem()->getTag (),
DeltaRef(ourNode->peekItem(),
differences.insert (std::make_pair(ours->peekItem()->getTag (),
DeltaRef(ours->peekItem(),
std::shared_ptr<SHAMapItem> ())));
if (--maxCount <= 0)
return false;
differences.insert(std::make_pair(otherNode->peekItem()->getTag (),
differences.insert(std::make_pair(other->peekItem()->getTag (),
DeltaRef(std::shared_ptr<SHAMapItem>(),
otherNode->peekItem ())));
other->peekItem ())));
if (--maxCount <= 0)
return false;
}
}
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))
return false;
}
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))
return false;
}
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)
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
SHAMapTreeNode* iNode = descendThrow (ourNode, i);
SHAMapAbstractNode* iNode = descendThrow (ours, i);
if (!walkBranch (iNode,
std::shared_ptr<SHAMapItem> (), true,
differences, maxCount))
return false;
}
else if (ourNode->isEmptyBranch (i))
else if (ours->isEmptyBranch (i))
{
// The other tree has a branch, we do not
SHAMapTreeNode* iNode =
otherMap->descendThrow(otherNode, i);
SHAMapAbstractNode* iNode =
otherMap->descendThrow(other, i);
if (!otherMap->walkBranch (iNode,
std::shared_ptr<SHAMapItem>(),
false, differences, maxCount))
return false;
}
else // The two trees have different non-empty branches
nodeStack.push ({descendThrow (ourNode, i),
otherMap->descendThrow (otherNode, i)});
nodeStack.push ({descendThrow (ours, i),
otherMap->descendThrow (other, i)});
}
}
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
return;
using StackEntry = std::shared_ptr<SHAMapTreeNode>;
using StackEntry = std::shared_ptr<SHAMapInnerNode>;
std::stack <StackEntry, std::vector <StackEntry>> nodeStack;
nodeStack.push (root_);
nodeStack.push (std::static_pointer_cast<SHAMapInnerNode>(root_));
while (!nodeStack.empty ())
{
std::shared_ptr<SHAMapTreeNode> node = std::move (nodeStack.top());
std::shared_ptr<SHAMapInnerNode> node = std::move (nodeStack.top());
nodeStack.pop ();
for (int i = 0; i < 16; ++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->isInner ())
nodeStack.push (std::move (nextNode));
nodeStack.push(
std::static_pointer_cast<SHAMapInnerNode>(nextNode));
}
else
{

View File

@@ -30,27 +30,29 @@ static const uint256 uZero;
static bool visitLeavesHelper (
std::function <void (std::shared_ptr<SHAMapItem> const&)> const& function,
SHAMapTreeNode& node)
SHAMapAbstractNode& node)
{
// Adapt visitNodes to visitLeaves
if (!node.isInner ())
function (node.peekItem ());
function (static_cast<SHAMapTreeNode&>(node).peekItem ());
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,
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
assert (root_->isValid ());
if (!root_ || root_->isEmpty ())
if (!root_)
return;
function (*root_);
@@ -58,10 +60,10 @@ void SHAMap::visitNodes(std::function<bool (SHAMapTreeNode&)> const& function) c
if (!root_->isInner ())
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::shared_ptr<SHAMapTreeNode> node = root_;
auto node = std::static_pointer_cast<SHAMapInnerNode>(root_);
int pos = 0;
while (1)
@@ -71,7 +73,7 @@ void SHAMap::visitNodes(std::function<bool (SHAMapTreeNode&)> const& function) c
uint256 childHash;
if (!node->isEmptyBranch (pos))
{
std::shared_ptr<SHAMapTreeNode> child = descendNoStore (node, pos);
std::shared_ptr<SHAMapAbstractNode> child = descendNoStore (node, pos);
if (function (*child))
return;
@@ -90,7 +92,7 @@ void SHAMap::visitNodes(std::function<bool (SHAMapTreeNode&)> const& function) c
}
// descend to the child's first position
node = child;
node = std::static_pointer_cast<SHAMapInnerNode>(child);
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
nodes that are not permanently stored locally
*/
void SHAMap::getMissingNodes (std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes, int max,
SHAMapSyncFilter* filter)
void
SHAMap::getMissingNodes(std::vector<SHAMapNodeID>& nodeIDs, std::vector<uint256>& hashes,
int max, SHAMapSyncFilter* filter)
{
assert (root_->isValid ());
assert (root_->getNodeHash().isNonZero ());
std::uint32_t generation = f_.fullbelow().getGeneration();
if (root_->isFullBelow (generation))
{
clearSynching ();
return;
}
if (!root_->isInner ())
{
if (journal_.warning) journal_.warning <<
if (generation == 0)
clearSynching();
else if (journal_.warning) journal_.warning <<
"synching empty tree";
return;
}
if (std::static_pointer_cast<SHAMapInnerNode>(root_)->isFullBelow (generation))
{
clearSynching ();
return;
}
int const maxDefer = f_.db().getDesiredAsyncReadCount ();
// 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)
{
std::vector <std::tuple <SHAMapTreeNode*, int, SHAMapNodeID>> deferredReads;
std::vector <std::tuple <SHAMapInnerNode*, int, SHAMapNodeID>> deferredReads;
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;
// Traverse the map without blocking
SHAMapTreeNode *node = root_.get ();
auto node = static_cast<SHAMapInnerNode*>(root_.get());
SHAMapNodeID nodeID;
// 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);
bool pending = false;
SHAMapTreeNode* d = descendAsync (node, branch, childID, filter, pending);
auto d = descendAsync (node, branch, childID, filter, pending);
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
}
else if (d->isInner () && !d->isFullBelow (generation))
else if (d->isInner() &&
!static_cast<SHAMapInnerNode*>(d)->isFullBelow(generation))
{
stack.push (std::make_tuple (node, nodeID,
firstChild, currentChild, fullBelow));
// Switch to processing the child node
node = d;
node = static_cast<SHAMapInnerNode*>(d);
nodeID = childID;
firstChild = rand() % 256;
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& nodeHash = parent->getChildHash (branch);
std::shared_ptr<SHAMapTreeNode> nodePtr = fetchNodeNT (nodeID, nodeHash, filter);
auto nodePtr = fetchNodeNT(nodeID, nodeHash, filter);
if (nodePtr)
{
++hits;
if (backed_)
canonicalize (nodeHash, nodePtr);
parent->canonicalizeChild (branch, nodePtr);
nodePtr = parent->canonicalizeChild (branch, std::move(nodePtr));
}
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
// to a specified depth
SHAMapTreeNode* node = root_.get ();
auto node = root_.get();
SHAMapNodeID nodeID;
while (node && node->isInner () && (nodeID.getDepth() < wanted.getDepth()))
{
int branch = nodeID.selectBranch (wanted.getNodeID());
if (node->isEmptyBranch (branch))
auto inner = static_cast<SHAMapInnerNode*>(node);
if (inner->isEmptyBranch (branch))
return false;
node = descendThrow (node, branch);
node = descendThrow(inner, branch);
nodeID = nodeID.getChildNodeID (branch);
}
@@ -331,14 +338,14 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted,
return false;
}
if (node->isInner () && node->isEmpty ())
if (node->isInner() && static_cast<SHAMapInnerNode*>(node)->isEmpty())
{
if (journal_.warning) journal_.warning <<
"peer requests empty node";
return false;
}
std::stack<std::tuple <SHAMapTreeNode*, SHAMapNodeID, int>> stack;
std::stack<std::tuple <SHAMapAbstractNode*, SHAMapNodeID, int>> stack;
stack.emplace (node, nodeID, depth);
while (! stack.empty ())
@@ -356,16 +363,17 @@ bool SHAMap::getNodeFat (SHAMapNodeID wanted,
{
// We descend inner nodes with only a single child
// without decrementing the depth
int bc = node->getBranchCount();
auto inner = static_cast<SHAMapInnerNode*>(node);
int bc = inner->getBranchCount();
if ((depth > 0) || (bc == 1))
{
// We need to process this node's children
for (int i = 0; i < 16; ++i)
{
if (! node->isEmptyBranch (i))
if (! inner->isEmptyBranch (i))
{
SHAMapNodeID childID = nodeID.getChildNodeID (i);
SHAMapTreeNode* childNode = descendThrow (node, i);
auto childID = nodeID.getChildNodeID (i);
auto childNode = descendThrow (inner, i);
if (childNode->isInner () &&
((depth > 1) || (bc == 1)))
@@ -410,9 +418,7 @@ SHAMapAddNode SHAMap::addRootNode (Blob const& rootNode,
}
assert (seq_ >= 1);
auto node = std::make_shared<SHAMapTreeNode> (rootNode, 0,
format, uZero, false);
auto node = SHAMapAbstractNode::make(rootNode, 0, format, uZero, false);
if (!node)
return SHAMapAddNode::invalid ();
@@ -452,10 +458,7 @@ SHAMapAddNode SHAMap::addRootNode (uint256 const& hash, Blob const& rootNode, SH
}
assert (seq_ >= 1);
std::shared_ptr<SHAMapTreeNode> node =
std::make_shared<SHAMapTreeNode> (rootNode, 0,
format, uZero, false);
auto node = SHAMapAbstractNode::make(rootNode, 0, format, uZero, false);
if (!node || node->getNodeHash () != hash)
return SHAMapAddNode::invalid ();
@@ -494,27 +497,28 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
std::uint32_t generation = f_.fullbelow().getGeneration();
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 ()))
{
int branch = iNodeID.selectBranch (node.getNodeID ());
assert (branch >= 0);
if (iNode->isEmptyBranch (branch))
auto inner = static_cast<SHAMapInnerNode*>(iNode);
if (inner->isEmptyBranch (branch))
{
if (journal_.warning) journal_.warning <<
"Add known node for empty branch" << node;
return SHAMapAddNode::invalid ();
}
uint256 childHash = iNode->getChildHash (branch);
uint256 childHash = inner->getChildHash (branch);
if (f_.fullbelow().touch_if_exists (childHash))
return SHAMapAddNode::duplicate ();
SHAMapTreeNode* prevNode = iNode;
std::tie (iNode, iNodeID) = descend (iNode, iNodeID, branch, filter);
auto prevNode = inner;
std::tie(iNode, iNodeID) = descend(inner, iNodeID, branch, filter);
if (!iNode)
{
@@ -531,8 +535,7 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
return SHAMapAddNode::invalid ();
}
auto newNode = std::make_shared<SHAMapTreeNode>(rawNode, 0, snfWIRE,
uZero, false);
auto newNode = SHAMapAbstractNode::make(rawNode, 0, snfWIRE, uZero, false);
if (!newNode->isInBounds (iNodeID))
{
@@ -551,7 +554,7 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
if (backed_)
canonicalize (childHash, newNode);
prevNode->canonicalizeChild (branch, newNode);
newNode = prevNode->canonicalizeChild (branch, std::move(newNode));
if (filter)
{
@@ -573,13 +576,14 @@ SHAMap::addKnownNode (const SHAMapNodeID& node, Blob const& rawNode,
bool SHAMap::deepCompare (SHAMap& other) const
{
// 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()});
while (!stack.empty ())
{
SHAMapTreeNode *node, *otherNode;
SHAMapAbstractNode* node;
SHAMapAbstractNode* otherNode;
std::tie(node, otherNode) = stack.top ();
stack.pop ();
@@ -600,8 +604,8 @@ bool SHAMap::deepCompare (SHAMap& other) const
{
if (!otherNode->isLeaf ())
return false;
auto nodePeek = node->peekItem();
auto otherNodePeek = otherNode->peekItem();
auto& nodePeek = static_cast<SHAMapTreeNode*>(node)->peekItem();
auto& otherNodePeek = static_cast<SHAMapTreeNode*>(otherNode)->peekItem();
if (nodePeek->getTag() != otherNodePeek->getTag())
return false;
if (nodePeek->peekData() != otherNodePeek->peekData())
@@ -611,21 +615,22 @@ bool SHAMap::deepCompare (SHAMap& other) const
{
if (!otherNode->isInner ())
return false;
auto node_inner = static_cast<SHAMapInnerNode*>(node);
auto other_inner = static_cast<SHAMapInnerNode*>(otherNode);
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;
}
else
{
if (otherNode->isEmptyBranch (i))
if (other_inner->isEmptyBranch (i))
return false;
SHAMapTreeNode *next = descend (node, i);
SHAMapTreeNode *otherNext = other.descend (otherNode, i);
auto next = descend(node_inner, i);
auto otherNext = other.descend(other_inner, i);
if (!next || !otherNext)
{
if (journal_.warning) journal_.warning <<
@@ -647,17 +652,17 @@ bool
SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID,
uint256 const& targetNodeHash) const
{
SHAMapTreeNode* node = root_.get ();
auto node = root_.get();
SHAMapNodeID nodeID;
while (node->isInner () && (nodeID.getDepth () < targetNodeID.getDepth ()))
{
int branch = nodeID.selectBranch (targetNodeID.getNodeID ());
if (node->isEmptyBranch (branch))
auto inner = static_cast<SHAMapInnerNode*>(node);
if (inner->isEmptyBranch (branch))
return false;
node = descendThrow (node, branch);
node = descendThrow (inner, branch);
nodeID = nodeID.getChildNodeID (branch);
}
@@ -669,7 +674,7 @@ SHAMap::hasInnerNode (SHAMapNodeID const& targetNodeID,
bool
SHAMap::hasLeafNode (uint256 const& tag, uint256 const& targetNodeHash) const
{
SHAMapTreeNode* node = root_.get ();
auto node = root_.get();
SHAMapNodeID nodeID;
if (!node->isInner()) // only one leaf node in the tree
@@ -678,14 +683,14 @@ SHAMap::hasLeafNode (uint256 const& tag, uint256 const& targetNodeHash) const
do
{
int branch = nodeID.selectBranch (tag);
if (node->isEmptyBranch (branch))
auto inner = static_cast<SHAMapInnerNode*>(node);
if (inner->isEmptyBranch (branch))
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;
node = descendThrow (node, branch);
node = descendThrow(inner, branch);
nodeID = nodeID.getChildNodeID (branch);
}
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
{
visitDifferences (have,
[includeLeaves, &max, &func] (SHAMapTreeNode& smn) -> bool
[includeLeaves, &max, &func] (SHAMapAbstractNode& smn) -> bool
{
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
// in the specified SHAMap
@@ -734,20 +741,21 @@ void SHAMap::visitDifferences (SHAMap* have, std::function <bool (SHAMapTreeNode
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_);
return;
}
// 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;
stack.push ({root_.get(), SHAMapNodeID{}});
stack.push ({static_cast<SHAMapInnerNode*>(root_.get()), SHAMapNodeID{}});
while (!stack.empty())
{
SHAMapTreeNode* node;
SHAMapInnerNode* node;
SHAMapNodeID nodeID;
std::tie (node, nodeID) = stack.top ();
stack.pop ();
@@ -763,14 +771,16 @@ void SHAMap::visitDifferences (SHAMap* have, std::function <bool (SHAMapTreeNode
{
uint256 const& childHash = node->getChildHash (i);
SHAMapNodeID childID = nodeID.getChildNodeID (i);
SHAMapTreeNode* next = descendThrow (node, i);
auto next = descendThrow(node, i);
if (next->isInner ())
{
if (! have || ! have->hasInnerNode (childID, childHash))
stack.push ({next, childID});
if (!have || !have->hasInnerNode(childID, childHash))
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))
return;

View File

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

View File

@@ -95,6 +95,45 @@ public:
unexpected (!sMap.delItem (sMap.peekFirstItem ()->getTag ()), "bad mod");
unexpected (sMap.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");
}
}
};