| /* |
| * 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. |
| */ |
| package org.apache.tuweni.trie; |
| |
| import static com.google.common.base.Preconditions.checkArgument; |
| |
| import org.apache.tuweni.bytes.Bytes; |
| import org.apache.tuweni.bytes.MutableBytes; |
| |
| /** |
| * Compact (Hex-prefix) encoding and decoding. |
| * |
| * <p> |
| * An implementation of <a href= |
| * "https://github.com/ethereum/wiki/wiki/Patricia-Tree#specification-compact-encoding-of-hex-sequence-with-optional-terminator">Compact |
| * (Hex-prefix) encoding</a>. |
| */ |
| public final class CompactEncoding { |
| private CompactEncoding() {} |
| |
| public static final byte LEAF_TERMINATOR = 0x10; |
| |
| /** |
| * Calculate a RADIX-16 path for a given byte sequence. |
| * |
| * @param bytes The byte sequence to calculate the path for. |
| * @return The Radix-16 path. |
| */ |
| public static Bytes bytesToPath(Bytes bytes) { |
| MutableBytes path = MutableBytes.create(bytes.size() * 2 + 1); |
| int j = 0; |
| for (int i = 0; i < bytes.size(); i += 1, j += 2) { |
| byte b = bytes.get(i); |
| path.set(j, (byte) ((b >>> 4) & 0x0f)); |
| path.set(j + 1, (byte) (b & 0x0f)); |
| } |
| path.set(j, LEAF_TERMINATOR); |
| return path; |
| } |
| |
| /** |
| * Encode a Radix-16 path. |
| * |
| * @param path A Radix-16 path. |
| * @return A compact-encoded path. |
| */ |
| public static Bytes encode(Bytes path) { |
| int size = path.size(); |
| boolean isLeaf = size > 0 && path.get(size - 1) == LEAF_TERMINATOR; |
| if (isLeaf) { |
| size = size - 1; |
| } |
| |
| MutableBytes encoded = MutableBytes.create((size + 2) / 2); |
| int i = 0; |
| int j = 0; |
| |
| if (size % 2 == 1) { |
| // add first nibble to magic |
| byte high = (byte) (isLeaf ? 0x03 : 0x01); |
| byte low = path.get(i++); |
| if ((low & 0xf0) != 0) { |
| throw new IllegalArgumentException("Invalid path: contains elements larger than a nibble"); |
| } |
| encoded.set(j++, (byte) (high << 4 | low)); |
| } else { |
| byte high = (byte) (isLeaf ? 0x02 : 0x00); |
| encoded.set(j++, (byte) (high << 4)); |
| } |
| |
| while (i < size) { |
| byte high = path.get(i++); |
| byte low = path.get(i++); |
| if ((high & 0xf0) != 0 || (low & 0xf0) != 0) { |
| throw new IllegalArgumentException("Invalid path: contains elements larger than a nibble"); |
| } |
| encoded.set(j++, (byte) (high << 4 | low)); |
| } |
| |
| return encoded; |
| } |
| |
| /** |
| * Decode a compact-encoded path to Radix-16. |
| * |
| * @param encoded A compact-encoded path. |
| * @return A Radix-16 path. |
| */ |
| public static Bytes decode(Bytes encoded) { |
| int size = encoded.size(); |
| checkArgument(size > 0); |
| byte magic = encoded.get(0); |
| checkArgument((magic & 0xc0) == 0, "Invalid compact encoding"); |
| |
| boolean isLeaf = (magic & 0x20) != 0; |
| |
| int pathLength = ((size - 1) * 2) + (isLeaf ? 1 : 0); |
| MutableBytes path; |
| int i = 0; |
| |
| if ((magic & 0x10) != 0) { |
| // need to use lower nibble of magic |
| path = MutableBytes.create(pathLength + 1); |
| path.set(i++, (byte) (magic & 0x0f)); |
| } else { |
| path = MutableBytes.create(pathLength); |
| } |
| |
| for (int j = 1; j < size; j++) { |
| byte b = encoded.get(j); |
| path.set(i++, (byte) ((b >>> 4) & 0x0f)); |
| path.set(i++, (byte) (b & 0x0f)); |
| } |
| |
| if (isLeaf) { |
| path.set(i, LEAF_TERMINATOR); |
| } |
| |
| return path; |
| } |
| } |