/* * Copyright 2009 Thomas Bocek * * 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 net.tomp2p.peers; import java.util.Random; import net.tomp2p.utils.Utils; /** * This class represents a 160 bit number. This class is preferred over BigInteger as we always have 160bit, and thus, * methods can be optimized. * * @author Thomas Bocek */ public final class Number160 extends Number implements Comparable<Number160> { private static final long serialVersionUID = -6386562272459272306L; // This key has *always* 160 bit. Do not change. public static final int BITS = 160; public static final Number160 MAX_VALUE = new Number160(new int[] {-1, -1, -1, -1, -1 }); private static final long LONG_MASK = 0xffffffffL; private static final int BYTE_MASK = 0xff; private static final int CHAR_MASK = 0xf; private static final int STRING_LENGTH = 42; // a map used for String <-> Key conversion private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; // size of the backing integer array public static final int INT_ARRAY_SIZE = BITS / Integer.SIZE; // size of a byte array public static final int BYTE_ARRAY_SIZE = BITS / Byte.SIZE; public static final int CHARS_PER_INT = 8; // backing integer array private final int[] val; // constants public static final Number160 ZERO = new Number160(0); public static final Number160 ONE = new Number160(1); /** * Create a Key with value 0. */ public Number160() { this.val = new int[INT_ARRAY_SIZE]; } /** * Create an instance with an integer array. This integer array will be copied into the backing array. * * @param val * The value to copy to the backing array. Since this class stores 160bit numbers, the array needs to be * of size 5 or smaller. */ public Number160(final int... val) { if (val.length > INT_ARRAY_SIZE) { throw new IllegalArgumentException("Can only deal with arrays of smaller or equal " + INT_ARRAY_SIZE + ". Your array has " + val.length); } this.val = new int[INT_ARRAY_SIZE]; final int len = val.length; for (int i = len - 1, j = INT_ARRAY_SIZE - 1; i >= 0; i--, j--) { this.val[j] = val[i]; } } /** * Create a Key from a string. The string has to be of length 40 to fit into the backing array. Note that this * string is *always* in hexadecimal, there is no 0x... required before the number. * * @param val * The characters allowed are [0-9a-f], which is in hexadecimal */ public Number160(final String val) { if (val.length() > STRING_LENGTH) { throw new IllegalArgumentException( "Can only deal with strings of size smaller or equal than 42. Your string has " + val.length()); } if (val.indexOf("0x") != 0) { throw new IllegalArgumentException(val + " is not in hexadecimal form. Decimal form is not supported yet"); } this.val = new int[INT_ARRAY_SIZE]; final char[] tmp = val.toCharArray(); final int len = tmp.length; for (int i = STRING_LENGTH - len, j = 2; i < (STRING_LENGTH - 2); i++, j++) { // CHECKSTYLE:OFF this.val[i >> 3] <<= 4; int digit = Character.digit(tmp[j], 16); if (digit < 0) { throw new RuntimeException("Not a hexadecimal number \"" + tmp[j] + "\". The range is [0-9a-f]"); } // += or |= does not matter here this.val[i >> 3] += digit & CHAR_MASK; // CHECKSTYLE:ON } } /** * Creates a Key with the integer value. * * @param val * integer value */ public Number160(final int val) { this.val = new int[INT_ARRAY_SIZE]; this.val[INT_ARRAY_SIZE - 1] = val; } /** * Creates a Key with the long value. * * @param val * long value */ public Number160(final long val) { this.val = new int[INT_ARRAY_SIZE]; this.val[INT_ARRAY_SIZE - 1] = (int) val; this.val[INT_ARRAY_SIZE - 2] = (int) (val >> Integer.SIZE); } /** * Creates a new Key using the byte array. The array is copied to the backing int[] * * @param val * byte array */ public Number160(final byte[] val) { this(val, 0, val.length); } /** * Creates a new Key using the byte array. The array is copied to the backing int[] starting at the given offest. * * @param val * byte array * @param offset * The offset where to start * @param length * the length to read */ public Number160(final byte[] val, final int offset, final int length) { if (length > BYTE_ARRAY_SIZE) { throw new IllegalArgumentException( "Can only deal with byte arrays of size smaller or equal than 20. Your array has " + length); } this.val = new int[INT_ARRAY_SIZE]; for (int i = length + offset - 1, j = BYTE_ARRAY_SIZE - 1, k = 0; i >= offset; i--, j--, k++) { // += or |= does not matter here // CHECKSTYLE:OFF this.val[j >> 2] |= (val[i] & BYTE_MASK) << ((k % 4) << 3); // CHECKSTYLE:ON } } /** * Creates a new Key with random values in it. * * @param random * The object to create pseudo random numbers. For testing and debugging, the seed in the random class * can be set to make the random values repeatable. */ public Number160(final Random random) { this.val = new int[INT_ARRAY_SIZE]; for (int i = 0; i < INT_ARRAY_SIZE; i++) { this.val[i] = random.nextInt(); } } /** * Xor operation. This operation is ~2.5 times faster than with BigInteger * * @param key * The second operand for the xor operation * @return A new key with the resurt of the xor operation */ public Number160 xor(final Number160 key) { final int[] result = new int[INT_ARRAY_SIZE]; for (int i = 0; i < INT_ARRAY_SIZE; i++) { result[i] = this.val[i] ^ key.val[i]; } return new Number160(result); } /** * Returns a copy of the backing array, which is always of size 5. * * @return a copy of the backing array */ public int[] toIntArray() { final int[] retVal = new int[INT_ARRAY_SIZE]; for (int i = 0; i < INT_ARRAY_SIZE; i++) { retVal[i] = this.val[i]; } return retVal; } /** * Fills the byte array with this number. * * @param me * the byte array * @param offset * where to start in the byte array * @return the offset we have read */ public int toByteArray(final byte[] me, final int offset) { if (offset + BYTE_ARRAY_SIZE > me.length) { throw new RuntimeException("array too small"); } for (int i = 0; i < INT_ARRAY_SIZE; i++) { // multiply by four final int idx = offset + (i << 2); // CHECKSTYLE:OFF me[idx + 0] = (byte) (val[i] >> 24); me[idx + 1] = (byte) (val[i] >> 16); me[idx + 2] = (byte) (val[i] >> 8); me[idx + 3] = (byte) (val[i]); // CHECKSTYLE:ON } return offset + BYTE_ARRAY_SIZE; } /** * Returns a byte array, which is always of size 20. * * @return a byte array */ public byte[] toByteArray() { final byte[] retVal = new byte[BYTE_ARRAY_SIZE]; toByteArray(retVal, 0); return retVal; } /** * Shows the content in a human readable manner. * * @param removeLeadingZero * Indicates of leading zeros should be removed * @return A human readable representation of this key */ public String toString(final boolean removeLeadingZero) { boolean removeZero = removeLeadingZero; final StringBuilder sb = new StringBuilder("0x"); for (int i = 0; i < INT_ARRAY_SIZE; i++) { toHex(val[i], removeZero, sb); if (removeZero && val[i] != 0) { removeZero = false; } } return sb.toString(); } /** * Checks if this number is zero. * * @return True if this number is zero, false otherwise */ public boolean isZero() { for (int i = 0; i < INT_ARRAY_SIZE; i++) { if (this.val[i] != 0) { return false; } } return true; } /** * Calculates the number of bits used to represent this number. All leading (leftmost) zero bits are ignored * * @return The bits used */ public int bitLength() { int bits = 0; for (int i = 0; i < INT_ARRAY_SIZE; i++) { if (this.val[i] != 0) { bits += Integer.SIZE - Integer.numberOfLeadingZeros(this.val[i]); bits += Integer.SIZE * (INT_ARRAY_SIZE - ++i); break; } } return bits; } @Override public String toString() { return toString(true); } @Override public double doubleValue() { double d = 0; for (int i = 0; i < INT_ARRAY_SIZE; i++) { d *= LONG_MASK + 1; d += this.val[i] & LONG_MASK; } return d; } @Override public float floatValue() { return (float) doubleValue(); } @Override public int intValue() { return this.val[INT_ARRAY_SIZE - 1]; } /** * For debugging... * * @param pos * the position in the internal array * @return the long of the unsigned int */ long unsignedInt(final int pos) { return this.val[pos] & LONG_MASK; } @Override public long longValue() { return ((this.val[INT_ARRAY_SIZE - 1] & LONG_MASK) << Integer.SIZE) + (this.val[INT_ARRAY_SIZE - 2] & LONG_MASK); } @Override public int compareTo(final Number160 o) { for (int i = 0; i < INT_ARRAY_SIZE; i++) { long b1 = val[i] & LONG_MASK; long b2 = o.val[i] & LONG_MASK; if (b1 < b2) { return -1; } else if (b1 > b2) { return 1; } } return 0; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof Number160)) { return false; } final Number160 key = (Number160) obj; for (int i = 0; i < INT_ARRAY_SIZE; i++) { if (key.val[i] != val[i]) { return false; } } return true; } @Override public int hashCode() { int hashCode = 0; for (int i = 0; i < INT_ARRAY_SIZE; i++) { // CHECKSTYLE:OFF hashCode = (int) (31 * hashCode + (val[i] & LONG_MASK)); // CHECKSTYLE:ON } return hashCode; } /** * Convert an integer to hex value. * * @param integer2 * The integer to convert * @param removeLeadingZero * idicate if leading zeros should be ignored * @param sb * The string bulider where to store the result */ private static void toHex(final int integer2, final boolean removeLeadingZero, final StringBuilder sb) { // 4 bits form a char, thus we have 160/4=40 chars in a key, with an // integer array size of 5, this gives 8 chars per integer final char[] buf = new char[CHARS_PER_INT]; int charPos = CHARS_PER_INT; int integer = integer2; for (int i = 0; i < CHARS_PER_INT && !(removeLeadingZero && integer == 0); i++) { buf[--charPos] = DIGITS[integer & CHAR_MASK]; // for hexadecimal, we have 4 bits per char, which ranges from // [0-9a-f] // CHECKSTYLE:OFF integer >>>= 4; // CHECKSTYLE:ON } sb.append(buf, charPos, (CHARS_PER_INT - charPos)); } /** * Create a new Number160 from the integer, which fills all the 160bits. A new random object will be created. * * @param integerValue * The value to hash from * @return A hash from based on pseudo random, to fill the 160bits */ public static Number160 createHash(final int integerValue) { Random r = new Random(integerValue); return new Number160(r); } /** * Create a new Number160 from the long, which fills all the 160bit. A new random object will be created, thus, its * thread safe * * @param longValue * The value to hash from * @return A hash based on pseudo random, to fill the 160bits */ public static Number160 createHash(final long longValue) { Random r = new Random(longValue); return new Number160(r); } /** * Create a new Number160 using SHA1 on the string. * * @param string * The value to hash from * @return A hash based on SHA1 of the string */ public static Number160 createHash(final String string) { return Utils.makeSHAHash(string); } }