/** * Returns {@code true} if {@code object} is a {@link HashCode} instance with the identical byte * representation to this hash code. * * <p><b>Security note:</b> this method uses a constant-time (not short-circuiting) implementation * to protect against <a href="http://en.wikipedia.org/wiki/Timing_attack">timing attacks</a>. */ @Override public final boolean equals(@Nullable Object object) { if (object instanceof HashCode) { HashCode that = (HashCode) object; return bits() == that.bits() && equalsSameBits(that); } return false; }
/** * Returns a hash code, having the same bit length as each of the input hash codes, that combines * the information of these hash codes in an unordered fashion. That is, whenever two equal hash * codes are produced by two calls to this method, it is <i>as likely as possible</i> that each * was computed from the <i>same</i> input hash codes in <i>some</i> order. * * @throws IllegalArgumentException if {@code hashCodes} is empty, or the hash codes do not all * have the same bit length */ public static HashCode combineUnordered(Iterable<HashCode> hashCodes) { Iterator<HashCode> iterator = hashCodes.iterator(); checkArgument(iterator.hasNext(), "Must be at least 1 hash code to combine."); byte[] resultBytes = new byte[iterator.next().bits() / 8]; for (HashCode hashCode : hashCodes) { byte[] nextBytes = hashCode.asBytes(); checkArgument( nextBytes.length == resultBytes.length, "All hashcodes must have the same bit length."); for (int i = 0; i < nextBytes.length; i++) { resultBytes[i] += nextBytes[i]; } } return HashCode.fromBytesNoCopy(resultBytes); }
/** * Returns a hash code, having the same bit length as each of the input hash codes, that combines * the information of these hash codes in an ordered fashion. That is, whenever two equal hash * codes are produced by two calls to this method, it is <i>as likely as possible</i> that each * was computed from the <i>same</i> input hash codes in the <i>same</i> order. * * @throws IllegalArgumentException if {@code hashCodes} is empty, or the hash codes do not all * have the same bit length */ public static HashCode combineOrdered(Iterable<HashCode> hashCodes) { Iterator<HashCode> iterator = hashCodes.iterator(); checkArgument(iterator.hasNext(), "Must be at least 1 hash code to combine."); int bits = iterator.next().bits(); byte[] resultBytes = new byte[bits / 8]; for (HashCode hashCode : hashCodes) { byte[] nextBytes = hashCode.asBytes(); checkArgument( nextBytes.length == resultBytes.length, "All hashcodes must have the same bit length."); for (int i = 0; i < nextBytes.length; i++) { resultBytes[i] = (byte) (resultBytes[i] * 37 ^ nextBytes[i]); } } return HashCode.fromBytesNoCopy(resultBytes); }
/** * Returns {@code true} if {@code object} is a {@link HashCode} instance with the identical byte * representation to this hash code. * * <p><b>Security note:</b> this method uses a constant-time (not short-circuiting) implementation * to protect against <a href="http://en.wikipedia.org/wiki/Timing_attack">timing attacks</a>. */ @Override public final boolean equals(@NullableDecl Object object) { if (object instanceof HashCode) { HashCode that = (HashCode) object; return bits() == that.bits() && equalsSameBits(that); } return false; }
@Override HashCode makeHash(Hasher[] hashers) { byte[] bytes = new byte[bits() / 8]; int i = 0; for (Hasher hasher : hashers) { HashCode newHash = hasher.hash(); i += newHash.writeBytesTo(bytes, i, newHash.bits() / 8); } return HashCode.fromBytesNoCopy(bytes); }
/** * Returns a hash code, having the same bit length as each of the input hash codes, that combines * the information of these hash codes in an unordered fashion. That is, whenever two equal hash * codes are produced by two calls to this method, it is <i>as likely as possible</i> that each * was computed from the <i>same</i> input hash codes in <i>some</i> order. * * @throws IllegalArgumentException if {@code hashCodes} is empty, or the hash codes do not all * have the same bit length */ public static HashCode combineUnordered(Iterable<HashCode> hashCodes) { Iterator<HashCode> iterator = hashCodes.iterator(); checkArgument(iterator.hasNext(), "Must be at least 1 hash code to combine."); byte[] resultBytes = new byte[iterator.next().bits() / 8]; for (HashCode hashCode : hashCodes) { byte[] nextBytes = hashCode.asBytes(); checkArgument( nextBytes.length == resultBytes.length, "All hashcodes must have the same bit length."); for (int i = 0; i < nextBytes.length; i++) { resultBytes[i] += nextBytes[i]; } } return HashCode.fromBytesNoCopy(resultBytes); }
/** * Returns a hash code, having the same bit length as each of the input hash codes, that combines * the information of these hash codes in an ordered fashion. That is, whenever two equal hash * codes are produced by two calls to this method, it is <i>as likely as possible</i> that each * was computed from the <i>same</i> input hash codes in the <i>same</i> order. * * @throws IllegalArgumentException if {@code hashCodes} is empty, or the hash codes do not all * have the same bit length */ public static HashCode combineOrdered(Iterable<HashCode> hashCodes) { Iterator<HashCode> iterator = hashCodes.iterator(); checkArgument(iterator.hasNext(), "Must be at least 1 hash code to combine."); int bits = iterator.next().bits(); byte[] resultBytes = new byte[bits / 8]; for (HashCode hashCode : hashCodes) { byte[] nextBytes = hashCode.asBytes(); checkArgument( nextBytes.length == resultBytes.length, "All hashcodes must have the same bit length."); for (int i = 0; i < nextBytes.length; i++) { resultBytes[i] = (byte) (resultBytes[i] * 37 ^ nextBytes[i]); } } return HashCode.fromBytesNoCopy(resultBytes); }
/** * Returns {@code true} if {@code object} is a {@link HashCode} instance with the identical byte * representation to this hash code. * * <p><b>Security note:</b> this method uses a constant-time (not short-circuiting) implementation * to protect against <a href="http://en.wikipedia.org/wiki/Timing_attack">timing attacks</a>. */ @Override public final boolean equals(@NullableDecl Object object) { if (object instanceof HashCode) { HashCode that = (HashCode) object; return bits() == that.bits() && equalsSameBits(that); } return false; }
/** * Returns a "Java hash code" for this {@code HashCode} instance; this is well-defined (so, for * example, you can safely put {@code HashCode} instances into a {@code HashSet}) but is otherwise * probably not what you want to use. */ @Override public final int hashCode() { // If we have at least 4 bytes (32 bits), just take the first 4 bytes. Since this is // already a (presumably) high-quality hash code, any four bytes of it will do. if (bits() >= 32) { return asInt(); } // If we have less than 4 bytes, use them all. byte[] bytes = getBytesInternal(); int val = (bytes[0] & 0xFF); for (int i = 1; i < bytes.length; i++) { val |= ((bytes[i] & 0xFF) << (i * 8)); } return val; }
/** * Returns a hash code, having the same bit length as each of the input hash codes, that combines * the information of these hash codes in an unordered fashion. That is, whenever two equal hash * codes are produced by two calls to this method, it is <i>as likely as possible</i> that each * was computed from the <i>same</i> input hash codes in <i>some</i> order. * * @throws IllegalArgumentException if {@code hashCodes} is empty, or the hash codes do not all * have the same bit length */ public static HashCode combineUnordered(Iterable<HashCode> hashCodes) { Iterator<HashCode> iterator = hashCodes.iterator(); checkArgument(iterator.hasNext(), "Must be at least 1 hash code to combine."); byte[] resultBytes = new byte[iterator.next().bits() / 8]; for (HashCode hashCode : hashCodes) { byte[] nextBytes = hashCode.asBytes(); checkArgument( nextBytes.length == resultBytes.length, "All hashcodes must have the same bit length."); for (int i = 0; i < nextBytes.length; i++) { resultBytes[i] += nextBytes[i]; } } return HashCode.fromBytesNoCopy(resultBytes); }
private static void assertReadableBytes(HashCode hashCode) { assertTrue(hashCode.bits() >= 32); // sanity byte[] hashBytes = hashCode.asBytes(); int totalBytes = hashCode.bits() / 8; for (int bytes = 0; bytes < totalBytes; bytes++) { byte[] bb = new byte[bytes]; hashCode.writeBytesTo(bb, 0, bb.length); assertTrue(Arrays.equals(Arrays.copyOf(hashBytes, bytes), bb)); } }
@Override HashCode makeHash(Hasher[] hashers) { byte[] bytes = new byte[bits() / 8]; int i = 0; for (Hasher hasher : hashers) { HashCode newHash = hasher.hash(); i += newHash.writeBytesTo(bytes, i, newHash.bits() / 8); } return HashCode.fromBytesNoCopy(bytes); }
/** * Returns a "Java hash code" for this {@code HashCode} instance; this is well-defined (so, for * example, you can safely put {@code HashCode} instances into a {@code HashSet}) but is otherwise * probably not what you want to use. */ @Override public final int hashCode() { // If we have at least 4 bytes (32 bits), just take the first 4 bytes. Since this is // already a (presumably) high-quality hash code, any four bytes of it will do. if (bits() >= 32) { return asInt(); } // If we have less than 4 bytes, use them all. byte[] bytes = getBytesInternal(); int val = (bytes[0] & 0xFF); for (int i = 1; i < bytes.length; i++) { val |= ((bytes[i] & 0xFF) << (i * 8)); } return val; }
@Override HashCode makeHash(Hasher[] hashers) { byte[] bytes = new byte[bits() / 8]; int i = 0; for (Hasher hasher : hashers) { HashCode newHash = hasher.hash(); i += newHash.writeBytesTo(bytes, i, newHash.bits() / 8); } return HashCode.fromBytesNoCopy(bytes); }
/** * Copies bytes from this hash code into {@code dest}. * * @param dest the byte array into which the hash code will be written * @param offset the start offset in the data * @param maxLength the maximum number of bytes to write * @return the number of bytes written to {@code dest} * @throws IndexOutOfBoundsException if there is not enough room in {@code dest} */ @CanIgnoreReturnValue public int writeBytesTo(byte[] dest, int offset, int maxLength) { maxLength = Ints.min(maxLength, bits() / 8); Preconditions.checkPositionIndexes(offset, offset + maxLength, dest.length); writeBytesToImpl(dest, offset, maxLength); return maxLength; }
/** * Returns a "Java hash code" for this {@code HashCode} instance; this is well-defined (so, for * example, you can safely put {@code HashCode} instances into a {@code HashSet}) but is otherwise * probably not what you want to use. */ @Override public final int hashCode() { // If we have at least 4 bytes (32 bits), just take the first 4 bytes. Since this is // already a (presumably) high-quality hash code, any four bytes of it will do. if (bits() >= 32) { return asInt(); } // If we have less than 4 bytes, use them all. byte[] bytes = getBytesInternal(); int val = (bytes[0] & 0xFF); for (int i = 1; i < bytes.length; i++) { val |= ((bytes[i] & 0xFF) << (i * 8)); } return val; }
/** * Copies bytes from this hash code into {@code dest}. * * @param dest the byte array into which the hash code will be written * @param offset the start offset in the data * @param maxLength the maximum number of bytes to write * @return the number of bytes written to {@code dest} * @throws IndexOutOfBoundsException if there is not enough room in {@code dest} */ @CanIgnoreReturnValue public int writeBytesTo(byte[] dest, int offset, int maxLength) { maxLength = Ints.min(maxLength, bits() / 8); Preconditions.checkPositionIndexes(offset, offset + maxLength, dest.length); writeBytesToImpl(dest, offset, maxLength); return maxLength; }
/** * Copies bytes from this hash code into {@code dest}. * * @param dest the byte array into which the hash code will be written * @param offset the start offset in the data * @param maxLength the maximum number of bytes to write * @return the number of bytes written to {@code dest} * @throws IndexOutOfBoundsException if there is not enough room in {@code dest} */ @CanIgnoreReturnValue public int writeBytesTo(byte[] dest, int offset, int maxLength) { maxLength = Ints.min(maxLength, bits() / 8); Preconditions.checkPositionIndexes(offset, offset + maxLength, dest.length); writeBytesToImpl(dest, offset, maxLength); return maxLength; }
/** * Checks that a Hasher returns the same HashCode when given the same input, and also that the * collision rate looks sane. */ static void assertInvariants(HashFunction hashFunction) { int objects = 100; Set<HashCode> hashcodes = Sets.newHashSetWithExpectedSize(objects); Random random = new Random(314159); for (int i = 0; i < objects; i++) { int value = random.nextInt(); HashCode hashcode1 = hashFunction.hashInt(value); HashCode hashcode2 = hashFunction.hashInt(value); Assert.assertEquals(hashcode1, hashcode2); // idempotent Assert.assertEquals(hashFunction.bits(), hashcode1.bits()); Assert.assertEquals(hashFunction.bits(), hashcode1.asBytes().length * 8); hashcodes.add(hashcode1); } Assert.assertTrue(hashcodes.size() > objects * 0.95); // quite relaxed test assertHashBytesThrowsCorrectExceptions(hashFunction); assertIndependentHashers(hashFunction); assertShortcutsAreEquivalent(hashFunction, 512); }
private static void assertExpectedHashCode(ExpectedHashCode expectedHashCode, HashCode hash) { assertTrue(Arrays.equals(expectedHashCode.bytes, hash.asBytes())); byte[] bb = new byte[hash.bits() / 8]; hash.writeBytesTo(bb, 0, bb.length); assertTrue(Arrays.equals(expectedHashCode.bytes, bb)); assertEquals(expectedHashCode.asInt, hash.asInt()); if (expectedHashCode.asLong == null) { try { hash.asLong(); fail(); } catch (IllegalStateException expected) { } } else { assertEquals(expectedHashCode.asLong.longValue(), hash.asLong()); } assertEquals(expectedHashCode.toString, hash.toString()); assertSideEffectFree(hash); assertReadableBytes(hash); }