package org.bitseal.util; /** * A utility class offering methods for encoding and decoding between * bytes and var_int encoded integers.<br><br> * * At the time of writing, PyBitmessages's implementation of this can * be found in the "addresses.py" file in the source code. <br><br> * * Note: In Protocol Version 3, 9-byte encoding is no longer valid. See * https://bitmessage.org/wiki/Protocol_specification_v3 <br><br> * * See https://bitmessage.org/wiki/Protocol_specification#Variable_length_integer * * @author Jonathan Coe */ public final class VarintEncoder { private VarintEncoder() { // The constructor of this class is private in order to prevent the class being instantiated } /** * Encodes a long into a var_int encoded byte[]. * * @param input - A long representing the value to encode * * @return A byte[] containing the var_int encoded bytes */ public static byte[] encode(long input) { // Credit to Sebastian Schmidt for this way of doing the encoding byte[] b = ByteUtils.longToBytes(input); byte[] varInt; if (input < 0) { throw new IllegalArgumentException("VarintEncoder.encode was called with a negative value as its parameter. This is " + "invalid. The parameter given was " + input + ". Throwing new IllegalArgumentException."); } else if (input < 0xfd) { varInt = new byte[] { b[7] }; } else if (input < 0xffff) { varInt = new byte[] { (byte) 0xfd, b[6], b[7] }; } else { varInt = new byte[] { (byte) 0xfe, b[4], b[5], b[6], b[7] }; } return varInt; } /** * Decodes a byte[] from var_int format into a long[] representing the value of the * var_int and the length of the var_int in its encoded byte form * * @param input - A byte[] containing the bytes to decode * * @return An long[] containing exactly 2 longs. The first long is the value of the var_int and * the second long is the length of the var_int in its encoded byte form */ public static long[] decode(byte[] input) { long encodedValue = 0; long encodedLength = 0; int firstByteValue = (int) (input[0] & 0xFF); // Variable length integer encoding uses unsigned bytes, so it is necessary to convert them for use in Java // In variable length integer encoding, the first byte is a special value. If the value encoded is less than 253 decimal, then the first byte is that value // (in unsigned byte form). If the encoded value is greater than or equal to 253, then the first byte is used as a flag to denote how many bytes have been used // to encode the value - 2, 4, or 8 bytes, also in unsigned form. Thus the total number of bytes used is 1, 3, 5, or 9. if (input.length == 0) { return new long[]{0,0}; } if (firstByteValue < 253) { encodedValue = firstByteValue; encodedLength = 1; } if (firstByteValue == 253) { encodedValue = ByteUtils.bytesToShort(ArrayCopier.copyOfRange(input, 1, 3)); encodedLength = 3; } if (firstByteValue == 254) { encodedValue = ByteUtils.bytesToInt(ArrayCopier.copyOfRange(input, 1, 5)); encodedLength = 5; } return new long[]{encodedValue, encodedLength}; } }