// Copyright 2011 Google Inc. All Rights Reserved.
package com.google.common.hash;
import com.google.common.primitives.Ints;
import org.junit.Assert;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Random;
/**
* @author andreou@google.com (Dimitris Andreou)
*/
class HashTestUtils {
private HashTestUtils() {}
/**
* Converts a string, which should contain only ascii-representable characters, to a byte[].
*/
static byte[] ascii(String string) {
byte[] bytes = new byte[string.length()];
for (int i = 0; i < string.length(); i++) {
bytes[i] = (byte) string.charAt(i);
}
return bytes;
}
/**
* Returns a byte array representation for a sequence of longs, in big-endian order.
*/
static byte[] toBytes(ByteOrder bo, long... longs) {
ByteBuffer bb = ByteBuffer.wrap(new byte[longs.length * 8]).order(bo);
for (long x : longs) {
bb.putLong(x);
}
return bb.array();
}
interface HashFn {
byte[] hash(byte[] input, int seed);
}
static void verifyHashFunction(HashFn hashFunction, int hashbits, int expected) {
int hashBytes = hashbits / 8;
byte[] key = new byte[256];
byte[] hashes = new byte[hashBytes * 256];
// Hash keys of the form {}, {0}, {0,1}, {0,1,2}... up to N=255,using 256-N as the seed
for (int i = 0; i < 256; i++) {
key[i] = (byte) i;
int seed = 256 - i;
byte[] hash = hashFunction.hash(Arrays.copyOf(key, i), seed);
System.arraycopy(hash, 0, hashes, i * hashBytes, hash.length);
}
// Then hash the result array
byte[] result = hashFunction.hash(hashes, 0);
// interpreted in little-endian order.
int verification = Integer.reverseBytes(Ints.fromByteArray(result));
if (expected != verification) {
throw new AssertionError("Expected: " + Integer.toHexString(expected)
+ " got: " + Integer.toHexString(verification));
}
}
static void assertEqualHashes(byte[] expectedHash, byte[] actualHash) {
if (!Arrays.equals(expectedHash, actualHash)) {
Assert.fail(String.format("Should be: %x, was %x", expectedHash, actualHash));
}
}
static final Funnel<Object> BAD_FUNNEL = new Funnel<Object>() {
@Override public void funnel(Object object, Sink byteSink) {
byteSink.putInt(object.hashCode());
}
};
static enum RandomHasherAction {
PUT_BOOLEAN() {
@Override void performAction(Random random, Iterable<? extends Sink> sinks) {
boolean value = random.nextBoolean();
for (Sink sink : sinks) {
sink.putBoolean(value);
}
}
},
PUT_BYTE() {
@Override void performAction(Random random, Iterable<? extends Sink> sinks) {
int value = random.nextInt();
for (Sink sink : sinks) {
sink.putByte((byte) value);
}
}
},
PUT_SHORT() {
@Override void performAction(Random random, Iterable<? extends Sink> sinks) {
short value = (short) random.nextInt();
for (Sink sink : sinks) {
sink.putShort(value);
}
}
},
PUT_CHAR() {
@Override void performAction(Random random, Iterable<? extends Sink> sinks) {
char value = (char) random.nextInt();
for (Sink sink : sinks) {
sink.putChar(value);
}
}
},
PUT_INT() {
@Override void performAction(Random random, Iterable<? extends Sink> sinks) {
int value = random.nextInt();
for (Sink sink : sinks) {
sink.putInt(value);
}
}
},
PUT_LONG() {
@Override void performAction(Random random, Iterable<? extends Sink> sinks) {
long value = random.nextLong();
for (Sink sink : sinks) {
sink.putLong(value);
}
}
},
PUT_FLOAT() {
@Override void performAction(Random random, Iterable<? extends Sink> sinks) {
float value = random.nextFloat();
for (Sink sink : sinks) {
sink.putFloat(value);
}
}
},
PUT_DOUBLE() {
@Override void performAction(Random random, Iterable<? extends Sink> sinks) {
double value = random.nextDouble();
for (Sink sink : sinks) {
sink.putDouble(value);
}
}
},
PUT_BYTES() {
@Override void performAction(Random random, Iterable<? extends Sink> sinks) {
byte[] value = new byte[random.nextInt(128)];
random.nextBytes(value);
for (Sink sink : sinks) {
sink.putBytes(value);
}
}
},
PUT_BYTES_INT_INT() {
@Override void performAction(Random random, Iterable<? extends Sink> sinks) {
byte[] value = new byte[random.nextInt(128)];
random.nextBytes(value);
int off = random.nextInt(value.length + 1);
int len = random.nextInt(value.length - off + 1);
for (Sink sink : sinks) {
sink.putBytes(value);
}
}
};
abstract void performAction(Random random, Iterable<? extends Sink> sinks);
private static final RandomHasherAction[] actions = values();
static RandomHasherAction pickAtRandom(Random random) {
return actions[random.nextInt(actions.length)];
}
}
}