package com.ripple.core.types.shamap;
import com.ripple.core.coretypes.hash.Hash256;
import java.util.TreeSet;
public class ShaMapDiff {
public ShaMap one, two;
public TreeSet<Hash256> modified = new TreeSet<Hash256>();
public TreeSet<Hash256> deleted = new TreeSet<Hash256>();
public TreeSet<Hash256> added = new TreeSet<Hash256>();
public ShaMapDiff(ShaMap one, ShaMap two) {
this.one = one;
this.two = two;
}
// Find what's added, modified and deleted in `two`
public void find() {
one.hash();
two.hash();
compare(one, two);
}
public ShaMapDiff inverted() {
ShaMapDiff shaMapDiff = new ShaMapDiff(two, one);
shaMapDiff.added = deleted;
shaMapDiff.modified = modified;
shaMapDiff.deleted = added;
return shaMapDiff;
}
public void apply(ShaMap sa) {
for (Hash256 mod : modified) {
boolean modded = sa.updateItem(mod, two.getItem(mod).copy());
if (!modded) throw new AssertionError();
}
for (Hash256 add : added) {
boolean added = sa.addItem(add, two.getItem(add).copy());
if (!added) throw new AssertionError();
}
for (Hash256 delete : deleted) {
boolean removed = sa.removeLeaf(delete);
if (!removed) throw new AssertionError();
}
}
private void compare(ShaMapInner a, ShaMapInner b) {
for (int i = 0; i < 16; i++) {
ShaMapNode aChild = a.getBranch(i);
ShaMapNode bChild = b.getBranch(i);
if (aChild == null && bChild != null) {
trackAdded(bChild);
// added in B
} else if (aChild != null && bChild == null) {
trackRemoved(aChild);
// removed from B
} else if (aChild != null && !aChild.hash().equals(bChild.hash())) {
boolean aleaf = aChild.isLeaf(),
bLeaf = bChild.isLeaf();
if (aleaf && bLeaf) {
ShaMapLeaf la = (ShaMapLeaf) aChild;
ShaMapLeaf lb = (ShaMapLeaf) bChild;
if (la.index.equals(lb.index)) {
modified.add(la.index);
} else {
deleted.add(la.index);
added.add(lb.index);
}
} else if (aleaf /*&& bInner*/) {
ShaMapLeaf la = (ShaMapLeaf) aChild;
ShaMapInner ib = (ShaMapInner) bChild;
trackAdded(ib);
if (ib.hasLeaf(la.index)) {
// because trackAdded would have added it
added.remove(la.index);
ShaMapLeaf leaf = ib.getLeaf(la.index);
if (!leaf.hash().equals(la.hash())) {
modified.add(la.index);
}
} else {
deleted.add(la.index);
}
} else if (bLeaf /*&& aInner*/) {
ShaMapLeaf lb = (ShaMapLeaf) bChild;
ShaMapInner ia = (ShaMapInner) aChild;
trackRemoved(ia);
if (ia.hasLeaf(lb.index)) {
// because trackRemoved would have deleted it
deleted.remove(lb.index);
ShaMapLeaf leaf = ia.getLeaf(lb.index);
if (!leaf.hash().equals(lb.hash())) {
modified.add(lb.index);
}
} else {
added.add(lb.index);
}
} else /*if (aInner && bInner)*/ {
compare((ShaMapInner) aChild, (ShaMapInner) bChild);
}
}
}
}
private void trackRemoved(ShaMapNode child) {
child.walkAnyLeaves(new LeafWalker() {
@Override
public void onLeaf(ShaMapLeaf leaf) {
deleted.add(leaf.index);
}
});
}
private void trackAdded(ShaMapNode child) {
child.walkAnyLeaves(new LeafWalker() {
@Override
public void onLeaf(ShaMapLeaf leaf) {
added.add(leaf.index);
}
});
}
}