package io.blobkeeper.common.util; /* * Copyright (C) 2015 by Denis M. Gabaydulin * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import com.google.common.collect.BinaryTreeTraverser; import com.google.common.collect.Range; import com.google.common.primitives.Longs; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.Serializable; import java.util.*; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Range.openClosed; import static io.blobkeeper.common.util.GuavaCollectors.toImmutableList; import static io.blobkeeper.common.util.HashableNode.EMPTY_HASH; import static io.blobkeeper.common.util.Utils.midPoint; import static java.lang.Math.pow; public class MerkleTree implements Serializable { private static final long serialVersionUID = 5208542351647956821L; private static final Logger log = LoggerFactory.getLogger(MerkleTree.class); public static final int MAX_LEVEL = 64; private int maxDepth; private Range<Long> fullRange; private int size; private HashableNode root; public MerkleTree(Range<Long> fullRange, int maxDepth) { checkArgument(!fullRange.isEmpty(), "Range is empty!"); this.size = 0; this.fullRange = fullRange; this.maxDepth = maxDepth; init(); } public void calculate() { root.calculate(); } @Override public String toString() { return root.toString(); } public List<LeafNode> getLeafNodes() { TreeTraverser treeTraverser = new TreeTraverser(); return treeTraverser.breadthFirstTraversal(root) .toList() .stream() .filter(node -> node instanceof LeafNode) .map(node -> (LeafNode) node) .collect(toImmutableList()); } /** * {@param blocks} must be sorted */ public static void fillTree(@NotNull MerkleTree tree, @NotNull SortedMap<Long, Block> blocks) { List<LeafNode> nodes = tree.getLeafNodes(); Iterator<LeafNode> iterator = nodes.iterator(); LeafNode current = null; if (iterator.hasNext()) { current = iterator.next(); } for (Long hash : blocks.keySet()) { Block block = blocks.get(hash); while (current != null && !current.getRange().contains(hash) && iterator.hasNext()) { current = iterator.next(); if (!current.getRange().contains(hash)) { current.addHash(EMPTY_HASH, 0); } } if (current != null) { // FIXME current.addHash(block.toByteArray(), block.getLength()); } else { break; } } if (current != null && current.getHash() == null) { current.addHash(EMPTY_HASH, 0); } while (iterator.hasNext()) { LeafNode node = iterator.next(); node.addHash(EMPTY_HASH, 0); } } public static List<LeafNode> difference(@NotNull MerkleTree one, @Nullable MerkleTree two) { checkNotNull(one, "Expected tree is required!"); if (null == two) { return one.getLeafNodes(); } Deque<HashableNode> nodes1 = new ArrayDeque<>(); Deque<HashableNode> nodes2 = new ArrayDeque<>(); HashableNode current1 = one.root; HashableNode current2 = two.root; List<LeafNode> diff = new ArrayList<>(); while ((!nodes1.isEmpty() || current1 != null) && (!nodes2.isEmpty() || current2 != null)) { if (current1 != null && current2 != null && current1 instanceof LeafNode && current2 instanceof LeafNode && !current1.equals(current2)) { log.debug("Nodes are diff {} : {}", current1, current2); diff.add((LeafNode) current2); } if (current1 instanceof BranchNode) { HashableNode rightNode = ((BranchNode) current1).getRight(); if (rightNode != null) { nodes1.push(rightNode); } current1 = ((BranchNode) current1).getLeft(); } else { if (!nodes1.isEmpty()) { current1 = nodes1.pop(); } else { current1 = null; } } if (current2 instanceof BranchNode) { HashableNode rightNode = ((BranchNode) current2).getRight(); if (rightNode != null) { nodes2.push(rightNode); } current2 = ((BranchNode) current2).getLeft(); } else { if (!nodes2.isEmpty()) { current2 = nodes2.pop(); } else { current2 = null; } } } return diff; } private void init() { // determine the depth to which we can safely split the tree int depth = (int) (Math.log10(maxDepth) / Math.log10(2)); root = _init(fullRange.lowerEndpoint(), fullRange.upperEndpoint(), 0, depth); size = (int) pow(2, depth); } private HashableNode _init(Long left, Long right, int depth, int max) { if (depth == max) { return new LeafNode(openClosed(left, right)); } long midpoint = midPoint(left, right); log.trace("M: {}", midpoint); if (Objects.equals(left, midpoint) || Objects.equals(right, midpoint)) { return new LeafNode(openClosed(left, right)); } ++depth; HashableNode leftNode = _init(left, midpoint, depth, max); HashableNode rightNode = _init(midpoint, right, depth, max); log.trace("B: {} - {}, {} - {}", left, midpoint, midpoint, right); return new BranchNode(midpoint, leftNode, rightNode); } public HashableNode getRoot() { return root; } private static class TreeTraverser extends BinaryTreeTraverser<HashableNode> { @Override public com.google.common.base.Optional<HashableNode> leftChild(HashableNode parent) { if (parent instanceof BranchNode) { return com.google.common.base.Optional.fromNullable(((BranchNode) parent).getLeft()); } else { return com.google.common.base.Optional.absent(); } } @Override public com.google.common.base.Optional<HashableNode> rightChild(HashableNode parent) { if (parent instanceof BranchNode) { return com.google.common.base.Optional.fromNullable(((BranchNode) parent).getRight()); } else { return com.google.common.base.Optional.absent(); } } } }