diff --git a/SHAMapDiff.cpp b/SHAMapDiff.cpp new file mode 100644 index 0000000000..eb63b031af --- /dev/null +++ b/SHAMapDiff.cpp @@ -0,0 +1,139 @@ +#include "SHAMap.h" + +#include + +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 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(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(), 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(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(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(ourItem, otherItem); + if((--maxCount)<=0) return false; + } + ourItem=thisNode->nextItem(ourItem->getTag()); + otherItem=otherNode->nextItem(otherItem->getTag()); + } + else if(ourItem->getTag()getTag()) + { // our item comes first + differences[ourItem->getTag()]= + std::pair(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(), 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; +}