mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-20 11:05:54 +00:00
The algorithm to find all the differences between two trees of transactions
or accounts. This is definitely either genius or madness. I'm just not quite sure which.
This commit is contained in:
139
SHAMapDiff.cpp
Normal file
139
SHAMapDiff.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "SHAMap.h"
|
||||
|
||||
#include <stack>
|
||||
|
||||
class SHAMapDiffNode
|
||||
{
|
||||
public:
|
||||
SHAMapNode mNodeID;
|
||||
uint256 mOurHash, mOtherHash;
|
||||
|
||||
SHAMapDiffNode(SHAMapNode id, const uint256& ourHash, const uint256& otherHash) :
|
||||
mNodeID(id), mOurHash(ourHash), mOtherHash(otherHash) { ; }
|
||||
};
|
||||
|
||||
bool SHAMap::compare(SHAMap::pointer otherMap, SHAMapDiff& differences, int maxCount)
|
||||
{ // compare two hash trees, add up to maxCount differences to the difference table
|
||||
// return value: true=complete table of differences given, false=too many differences
|
||||
// throws on corrupt tables or missing nodes
|
||||
|
||||
std::stack<SHAMapDiffNode> nodeStack; // track nodes we've pushed
|
||||
nodeStack.push(SHAMapDiffNode(SHAMapNode(), getHash(), otherMap->getHash()));
|
||||
|
||||
ScopedLock sl(Lock());
|
||||
while(nodeStack.size())
|
||||
{
|
||||
SHAMapDiffNode node(nodeStack.top());
|
||||
nodeStack.pop();
|
||||
if(node.mOurHash!=node.mOtherHash)
|
||||
{
|
||||
if(node.mNodeID.isLeaf())
|
||||
{
|
||||
if(!node.mOurHash)
|
||||
{ // leaf only in our tree
|
||||
SHAMapLeafNode::pointer thisNode=getLeaf(node.mNodeID, node.mOurHash, false);
|
||||
for(SHAMapItem::pointer item=thisNode->firstItem(); item; item=thisNode->nextItem(item->getTag()))
|
||||
{ // items in leaf only in our tree
|
||||
differences[item->getTag()]=
|
||||
std::pair<SHAMapItem::pointer, SHAMapItem::pointer>(item, SHAMapItem::pointer());
|
||||
if((--maxCount)<=0) return false;
|
||||
}
|
||||
}
|
||||
else if(!node.mOtherHash)
|
||||
{ // leaf only in other tree
|
||||
SHAMapLeafNode::pointer otherNode=otherMap->getLeaf(node.mNodeID, node.mOtherHash, false);
|
||||
for(SHAMapItem::pointer item=otherNode->firstItem(); item; item=otherNode->nextItem(item->getTag()))
|
||||
{ // items in leaf only in our tree
|
||||
differences[item->getTag()]=
|
||||
std::pair<SHAMapItem::pointer, SHAMapItem::pointer>(SHAMapItem::pointer(), item);
|
||||
if((--maxCount)<=0) return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // leaf in both trees, but differs
|
||||
SHAMapLeafNode::pointer thisNode=getLeaf(node.mNodeID, node.mOurHash, false);
|
||||
SHAMapLeafNode::pointer otherNode=otherMap->getLeaf(node.mNodeID, node.mOtherHash, false);
|
||||
SHAMapItem::pointer ourItem=thisNode->firstItem();
|
||||
SHAMapItem::pointer otherItem=otherNode->firstItem();
|
||||
while(ourItem || otherItem)
|
||||
{
|
||||
if(!otherItem)
|
||||
{ // we have items, other tree does not
|
||||
differences[otherItem->getTag()]=
|
||||
std::pair<SHAMapItem::pointer, SHAMapItem::pointer>(ourItem, otherItem);
|
||||
if((--maxCount)<=0) return false;
|
||||
otherItem=otherNode->nextItem(otherItem->getTag());
|
||||
}
|
||||
else if(!ourItem)
|
||||
{ // we have no items, other tree does
|
||||
differences[ourItem->getTag()]=
|
||||
std::pair<SHAMapItem::pointer, SHAMapItem::pointer>(ourItem, otherItem);
|
||||
if((--maxCount)<=0) return false;
|
||||
ourItem=thisNode->nextItem(ourItem->getTag());
|
||||
}
|
||||
else if(ourItem->getTag()==otherItem->getTag())
|
||||
{ // we have items with the same tag
|
||||
if(ourItem->getData()!=otherItem->getData())
|
||||
{ // different data
|
||||
differences[ourItem->getTag()]=
|
||||
std::pair<SHAMapItem::pointer, SHAMapItem::pointer>(ourItem, otherItem);
|
||||
if((--maxCount)<=0) return false;
|
||||
}
|
||||
ourItem=thisNode->nextItem(ourItem->getTag());
|
||||
otherItem=otherNode->nextItem(otherItem->getTag());
|
||||
}
|
||||
else if(ourItem->getTag()<otherItem->getTag())
|
||||
{ // our item comes first
|
||||
differences[ourItem->getTag()]=
|
||||
std::pair<SHAMapItem::pointer, SHAMapItem::pointer>(ourItem, SHAMapItem::pointer());
|
||||
if((--maxCount)<=0) return false;
|
||||
ourItem=thisNode->nextItem(ourItem->getTag());
|
||||
}
|
||||
else
|
||||
{ // other item comes first
|
||||
differences[otherItem->getTag()]=
|
||||
std::pair<SHAMapItem::pointer, SHAMapItem::pointer>(SHAMapItem::pointer(), otherItem);
|
||||
if((--maxCount)<=0) return false;
|
||||
otherItem=otherNode->nextItem(otherItem->getTag());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // inner node different in two trees
|
||||
if(!node.mOurHash)
|
||||
{ // node only exist in our tree
|
||||
SHAMapInnerNode::pointer thisNode=getInner(node.mNodeID, node.mOurHash, false);
|
||||
for(int i=0; i<32; i++)
|
||||
{ // push all existing branches onto the stack
|
||||
if(!!thisNode->getChildHash(i))
|
||||
nodeStack.push(SHAMapDiffNode(thisNode->getChildNodeID(i),
|
||||
thisNode->getChildHash(i), uint256()));
|
||||
}
|
||||
}
|
||||
else if(!node.mOtherHash)
|
||||
{ // node only exists in other tree
|
||||
SHAMapInnerNode::pointer otherNode=otherMap->getInner(node.mNodeID, node.mOtherHash, false);
|
||||
for(int i=0; i<32; i++)
|
||||
{ // push all existing branches onto the stack
|
||||
if(!!otherNode->getChildHash(i))
|
||||
nodeStack.push(SHAMapDiffNode(otherNode->getChildNodeID(i),
|
||||
uint256(), otherNode->getChildHash(i)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // node in both trees, but differs
|
||||
SHAMapInnerNode::pointer thisNode=getInner(node.mNodeID, node.mOurHash, false);
|
||||
SHAMapInnerNode::pointer otherNode=otherMap->getInner(node.mNodeID, node.mOtherHash, false);
|
||||
for(int i=0; i<32; i++)
|
||||
{ // push all differing branches onto the stack
|
||||
if(thisNode->getChildHash(i)!=otherNode->getChildHash(i))
|
||||
nodeStack.push(SHAMapDiffNode(thisNode->getChildNodeID(i),
|
||||
thisNode->getChildHash(i), otherNode->getChildHash(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user