Coverage Summary for Class: CompactEncoder (org.ethereum.util)

Class Class, % Method, % Line, %
CompactEncoder 0% (0/1) 0% (0/6) 0% (0/54)


1 /* 2  * This file is part of RskJ 3  * Copyright (C) 2017 RSK Labs Ltd. 4  * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>) 5  * 6  * This program is free software: you can redistribute it and/or modify 7  * it under the terms of the GNU Lesser General Public License as published by 8  * the Free Software Foundation, either version 3 of the License, or 9  * (at your option) any later version. 10  * 11  * This program is distributed in the hope that it will be useful, 12  * but WITHOUT ANY WARRANTY; without even the implied warranty of 13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14  * GNU Lesser General Public License for more details. 15  * 16  * You should have received a copy of the GNU Lesser General Public License 17  * along with this program. If not, see <http://www.gnu.org/licenses/>. 18  */ 19  20 package org.ethereum.util; 21  22 import java.io.ByteArrayOutputStream; 23 import java.util.Arrays; 24 import java.util.HashMap; 25 import java.util.Map; 26  27 import static java.util.Arrays.copyOf; 28 import static java.util.Arrays.copyOfRange; 29 import static org.ethereum.util.ByteUtil.appendByte; 30 import static org.bouncycastle.util.Arrays.concatenate; 31 import static org.bouncycastle.util.encoders.Hex.encode; 32  33 /** 34  * Compact encoding of hex sequence with optional terminator 35  * 36  * The traditional compact way of encoding a hex string is to convert it into binary 37  * - that is, a string like 0f1248 would become three bytes 15, 18, 72. However, 38  * this approach has one slight problem: what if the length of the hex string is odd? 39  * In that case, there is no way to distinguish between, say, 0f1248 and f1248. 40  * 41  * Additionally, our application in the Merkle Patricia tree requires the additional feature 42  * that a hex string can also have a special "terminator symbol" at the end (denoted by the 'T'). 43  * A terminator symbol can occur only once, and only at the end. 44  * 45  * An alternative way of thinking about this to not think of there being a terminator symbol, 46  * but instead treat bit specifying the existence of the terminator symbol as a bit specifying 47  * that the given node encodes a final node, where the value is an actual value, rather than 48  * the hash of yet another node. 49  * 50  * To solve both of these issues, we force the first nibble of the final byte-stream to encode 51  * two flags, specifying oddness of length (ignoring the 'T' symbol) and terminator status; 52  * these are placed, respectively, into the two lowest significant bits of the first nibble. 53  * In the case of an even-length hex string, we must introduce a second nibble (of value zero) 54  * to ensure the hex-string is even in length and thus is representable by a whole number of bytes. 55  * 56  * Examples: 57  * &gt; [ 1, 2, 3, 4, 5 ] 58  * '\x11\x23\x45' 59  * &gt; [ 0, 1, 2, 3, 4, 5 ] 60  * '\x00\x01\x23\x45' 61  * &gt; [ 0, 15, 1, 12, 11, 8, T ] 62  * '\x20\x0f\x1c\xb8' 63  * &gt; [ 15, 1, 12, 11, 8, T ] 64  * '\x3f\x1c\xb8' 65  */ 66 public class CompactEncoder { 67  68  private static final byte TERMINATOR = 16; 69  private static final Map<Character, Byte> hexMap = new HashMap<>(); 70  71  static { 72  hexMap.put('0', (byte) 0x0); 73  hexMap.put('1', (byte) 0x1); 74  hexMap.put('2', (byte) 0x2); 75  hexMap.put('3', (byte) 0x3); 76  hexMap.put('4', (byte) 0x4); 77  hexMap.put('5', (byte) 0x5); 78  hexMap.put('6', (byte) 0x6); 79  hexMap.put('7', (byte) 0x7); 80  hexMap.put('8', (byte) 0x8); 81  hexMap.put('9', (byte) 0x9); 82  hexMap.put('a', (byte) 0xa); 83  hexMap.put('b', (byte) 0xb); 84  hexMap.put('c', (byte) 0xc); 85  hexMap.put('d', (byte) 0xd); 86  hexMap.put('e', (byte) 0xe); 87  hexMap.put('f', (byte) 0xf); 88  } 89  90  /** 91  * Pack nibbles to binary 92  * 93  * @param nibbles sequence. may have a terminator 94  * @return hex-encoded byte array 95  */ 96  public static byte[] packNibbles(byte[] nibbles) { 97  int terminator = 0; 98  99  if (nibbles[nibbles.length - 1] == TERMINATOR) { 100  terminator = 1; 101  nibbles = copyOf(nibbles, nibbles.length - 1); 102  } 103  int oddlen = nibbles.length % 2; 104  int flag = 2 * terminator + oddlen; 105  if (oddlen != 0) { 106  byte[] flags = new byte[]{(byte) flag}; 107  nibbles = concatenate(flags, nibbles); 108  } else { 109  byte[] flags = new byte[]{(byte) flag, 0}; 110  nibbles = concatenate(flags, nibbles); 111  } 112  ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 113  for (int i = 0; i < nibbles.length; i += 2) { 114  buffer.write(16 * nibbles[i] + nibbles[i + 1]); //lgtm [java/index-out-of-bounds] 115  } 116  return buffer.toByteArray(); 117  } 118  119  /** 120  * Unpack a binary string to its nibbles equivalent 121  * 122  * @param str of binary data 123  * @return array of nibbles in byte-format 124  */ 125  public static byte[] unpackToNibbles(byte[] str) { 126  byte[] base = binToNibbles(str); 127  base = copyOf(base, base.length - 1); 128  if (base[0] >= 2) { 129  base = appendByte(base, TERMINATOR); 130  } 131  if (base[0] % 2 == 1) { 132  base = copyOfRange(base, 1, base.length); 133  } else { 134  base = copyOfRange(base, 2, base.length); 135  } 136  return base; 137  } 138  139  /** 140  * Transforms a binary array to hexadecimal format + terminator 141  * 142  * @param str byte[] 143  * @return array with each individual nibble adding a terminator at the end 144  */ 145  public static byte[] binToNibbles(byte[] str) { 146  147  byte[] hexEncoded = encode(str); 148  byte[] hexEncodedTerminated = Arrays.copyOf(hexEncoded, hexEncoded.length + 1); 149  150  for (int i = 0; i < hexEncoded.length; ++i){ 151  byte b = hexEncodedTerminated[i]; 152  hexEncodedTerminated[i] = hexMap.get((char) b); 153  } 154  155  hexEncodedTerminated[hexEncodedTerminated.length - 1] = TERMINATOR; 156  return hexEncodedTerminated; 157  } 158  159  160  public static byte[] binToNibblesNoTerminator(byte[] str) { 161  162  byte[] hexEncoded = encode(str); 163  164  for (int i = 0; i < hexEncoded.length; ++i){ 165  byte b = hexEncoded[i]; 166  hexEncoded[i] = hexMap.get((char) b); 167  } 168  169  return hexEncoded; 170  } 171 }