package peergos.shared.merklebtree;
import peergos.shared.cbor.*;
import peergos.shared.io.ipfs.multihash.*;
import java.io.*;
import java.util.*;
public class MerkleNode implements Cborable {
public final byte[] data;
public final List<Link> links;
public MerkleNode(byte[] data, List<Link> links) {
this.data = data;
this.links = links;
Collections.sort(this.links);
}
public MerkleNode(byte[] data) {
this(data, Collections.emptyList());
}
public static class Link implements Comparable<Link> {
public final String label;
public final Multihash target;
public Link(String label, Multihash target) {
this.label = label;
this.target = target;
}
@Override
public int compareTo(Link link) {
return label.compareTo(link.label);
}
}
public MerkleNode addLink(String label, Multihash linkTarget) {
List<Link> tmp = new ArrayList<>(links);
tmp.add(new Link(label, linkTarget));
return new MerkleNode(data, tmp);
}
public MerkleNode setData(byte[] newData) {
return new MerkleNode(newData, links);
}
public CborObject.CborMap toCbor() {
SortedMap<CborObject, CborObject> cbor = new TreeMap<>();
cbor.put(new CborObject.CborString("Data"), new CborObject.CborByteArray(data));
for (Link link: links) {
cbor.put(new CborObject.CborString(link.label), new CborObject.CborMerkleLink((link.target)));
}
return new CborObject.CborMap(cbor);
}
public static MerkleNode fromCbor(CborObject obj) {
CborObject.CborMap map = (CborObject.CborMap) obj;
CborObject.CborString dataLabel = new CborObject.CborString("Data");
CborObject.CborByteArray data = (CborObject.CborByteArray) map.values.get(dataLabel);
List<Link> links = new ArrayList<>(map.values.size() - 1);
for (Map.Entry<CborObject, CborObject> entry : map.values.entrySet()) {
if (entry.getKey().equals(dataLabel))
continue;
String label = ((CborObject.CborString) entry.getKey()).value;
Multihash value = ((CborObject.CborMerkleLink) entry.getValue()).target;
links.add(new Link(label, value));
}
return new MerkleNode(data.value, links);
}
/**
*
* @param in a CBOR encoding of a merkle node
* @return
* @throws IOException for an invalid encoding
*/
public static MerkleNode deserialize(byte[] in) {
CborDecoder decoder = new CborDecoder(new ByteArrayInputStream(in));
CborObject cbor = CborObject.deserialize(decoder);
return fromCbor(cbor);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MerkleNode that = (MerkleNode) o;
if (!Arrays.equals(data, that.data)) return false;
return links != null ? links.equals(that.links) : that.links == null;
}
@Override
public int hashCode() {
int result = Arrays.hashCode(data);
result = 31 * result + (links != null ? links.hashCode() : 0);
return result;
}
}