All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.wan.impl.merkletree.ArrayMerkleTree Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed 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.
 */

package com.hazelcast.wan.impl.merkletree;

import com.hazelcast.internal.util.collection.OAHashSet;

import java.util.Arrays;

import static com.hazelcast.internal.nio.Bits.INT_SIZE_IN_BYTES;
import static com.hazelcast.internal.nio.Bits.LONG_SIZE_IN_BYTES;
import static com.hazelcast.internal.util.JVMUtil.REFERENCE_COST_IN_BYTES;

/**
 * A not thread-safe, array based {@link MerkleTree} implementation, which
 * represents each node of the tree as a single {@code int} containing
 * the node's hash.
 * 

* The array representation of the tree comes with optimal memory footprint * and memory layout. The memory layout is leveraged by the breadth-first * (and breadth-last) traversal of the tree such as recalculating the * hash for each non-leaf node. This traversal is optimal, since it is * a sequential memory walk coming with low CPU cache miss rates, which * in the case of a deep tree enables much better performance compared * to the linked representation of the tree. *

* The nodes in this implementation are referenced by their breadth-first * order in the tree. Using this order as reference comes with the * following properties: *

    *
  • The order of a node is unique for each node and refers to the node * on the same level and position in every binary tree. This makes a single * node easily referable even between clusters. *
  • The order of a node intrinsically encodes the level of the tree on * which the given node is. See {@link MerkleTreeUtil#getLevelOfNode(int)} *
  • The order of a node also intrinsically encodes the hash range that * the node covers since the followings are known based on the order: *
      *
    1. the level the node is on *
    2. the number of the nodes on the same level *
    3. the node's relative order from the leftmost node on the same * level *
    4. because of the points above and that the codomain of the hash * function is known (32 bit ints), the range of the hashes that a single * node on the given level covers is known, hence the range covered by a * concrete node can be calculated. * See {@link MerkleTreeUtil#getNodeHashRangeOnLevel(int)}, * {@link MerkleTreeUtil#getNodeRangeLow(int)}, * {@link MerkleTreeUtil#getNodeRangeHigh(int)} *
    *
  • The offset of a node can be calculated easily and cheaply with * as {@code offset = order * NODE_SIZE}. *
*

* Every leaf of the tree may cover multiple elements in the underlying * data structure. These elements are key-value pairs and the keys of the * elements are stored in a data structure that are referred to as data * blocks. This implementation stores the keys in {@link OAHashSet}s, one * set for each leaf. These sets are initialized in tree construction time * eagerly with initial capacity one. *

* This implementation updates the tree synchronously. It can do that, * since updating a leaf's hash doesn't need to access the values belong * to the leaf, thanks to the not order-sensitive hash function in use. * See {@link MerkleTreeUtil#addHash(int, int)}, * {@link MerkleTreeUtil#removeHash(int, int)}, * {@link MerkleTreeUtil#sumHash(int, int)} */ public class ArrayMerkleTree extends AbstractMerkleTreeView implements MerkleTree { private final int leafLevel; /** * Footprint holds the total memory footprint of the Merkle tree. */ private final long footprint; public ArrayMerkleTree(int depth) { super(depth); this.leafLevel = depth - 1; this.footprint = INT_SIZE_IN_BYTES * tree.length // reference to the tree + REFERENCE_COST_IN_BYTES // depth + INT_SIZE_IN_BYTES // leafLevelOrder + INT_SIZE_IN_BYTES // leafLevel + INT_SIZE_IN_BYTES // footprint; + LONG_SIZE_IN_BYTES; } @Override public void updateAdd(Object key, Object value) { int keyHash = key.hashCode(); int valueHash = value.hashCode(); int leafOrder = MerkleTreeUtil.getLeafOrderForHash(keyHash, leafLevel); int leafCurrentHash = getNodeHash(leafOrder); int leafNewHash = MerkleTreeUtil.addHash(leafCurrentHash, valueHash); setNodeHash(leafOrder, leafNewHash); updateBranch(leafOrder); } @Override public void updateReplace(Object key, Object oldValue, Object newValue) { int keyHash = key.hashCode(); int oldValueHash = oldValue.hashCode(); int newValueHash = newValue.hashCode(); int leafOrder = MerkleTreeUtil.getLeafOrderForHash(keyHash, leafLevel); int leafCurrentHash = getNodeHash(leafOrder); int leafNewHash = MerkleTreeUtil.removeHash(leafCurrentHash, oldValueHash); leafNewHash = MerkleTreeUtil.addHash(leafNewHash, newValueHash); setNodeHash(leafOrder, leafNewHash); updateBranch(leafOrder); } @Override public void updateRemove(Object key, Object removedValue) { int keyHash = key.hashCode(); int removedValueHash = removedValue.hashCode(); int leafOrder = MerkleTreeUtil.getLeafOrderForHash(keyHash, leafLevel); int leafCurrentHash = getNodeHash(leafOrder); int leafNewHash = MerkleTreeUtil.removeHash(leafCurrentHash, removedValueHash); setNodeHash(leafOrder, leafNewHash); updateBranch(leafOrder); } @Override public int getNodeHash(int nodeOrder) { return tree[nodeOrder]; } @Override public long footprint() { return footprint; } @Override public void clear() { Arrays.fill(tree, 0); } /** * Synchronously calculates the hash for all the parent nodes of the * node referenced by {@code leafOrder} up to the root node. * * @param leafOrder The order of node */ private void updateBranch(int leafOrder) { int nodeOrder = MerkleTreeUtil.getParentOrder(leafOrder); for (int level = leafLevel; level > 0; level--) { int leftChildOrder = MerkleTreeUtil.getLeftChildOrder(nodeOrder); int rightChildOrder = MerkleTreeUtil.getRightChildOrder(nodeOrder); int leftChildHash = getNodeHash(leftChildOrder); int rightChildHash = getNodeHash(rightChildOrder); int newNodeHash = MerkleTreeUtil.sumHash(leftChildHash, rightChildHash); setNodeHash(nodeOrder, newNodeHash); nodeOrder = MerkleTreeUtil.getParentOrder(nodeOrder); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy