1 // Copyright 2011 Google Inc. All Rights Reserved. 2 3 package com.google.common.hash; 4 5 import com.google.common.primitives.Ints; 6 7 import org.junit.Assert; 8 9 import java.nio.ByteBuffer; 10 import java.nio.ByteOrder; 11 import java.util.Arrays; 12 import java.util.Random; 13 14 /** 15 * @author andreou (at) google.com (Dimitris Andreou) 16 */ 17 class HashTestUtils { 18 private HashTestUtils() {} 19 20 /** 21 * Converts a string, which should contain only ascii-representable characters, to a byte[]. 22 */ 23 static byte[] ascii(String string) { 24 byte[] bytes = new byte[string.length()]; 25 for (int i = 0; i < string.length(); i++) { 26 bytes[i] = (byte) string.charAt(i); 27 } 28 return bytes; 29 } 30 31 /** 32 * Returns a byte array representation for a sequence of longs, in big-endian order. 33 */ 34 static byte[] toBytes(ByteOrder bo, long... longs) { 35 ByteBuffer bb = ByteBuffer.wrap(new byte[longs.length * 8]).order(bo); 36 for (long x : longs) { 37 bb.putLong(x); 38 } 39 return bb.array(); 40 } 41 42 interface HashFn { 43 byte[] hash(byte[] input, int seed); 44 } 45 46 static void verifyHashFunction(HashFn hashFunction, int hashbits, int expected) { 47 int hashBytes = hashbits / 8; 48 49 byte[] key = new byte[256]; 50 byte[] hashes = new byte[hashBytes * 256]; 51 52 // Hash keys of the form {}, {0}, {0,1}, {0,1,2}... up to N=255,using 256-N as the seed 53 for (int i = 0; i < 256; i++) { 54 key[i] = (byte) i; 55 int seed = 256 - i; 56 byte[] hash = hashFunction.hash(Arrays.copyOf(key, i), seed); 57 System.arraycopy(hash, 0, hashes, i * hashBytes, hash.length); 58 } 59 60 // Then hash the result array 61 byte[] result = hashFunction.hash(hashes, 0); 62 63 // interpreted in little-endian order. 64 int verification = Integer.reverseBytes(Ints.fromByteArray(result)); 65 66 if (expected != verification) { 67 throw new AssertionError("Expected: " + Integer.toHexString(expected) 68 + " got: " + Integer.toHexString(verification)); 69 } 70 } 71 72 static void assertEqualHashes(byte[] expectedHash, byte[] actualHash) { 73 if (!Arrays.equals(expectedHash, actualHash)) { 74 Assert.fail(String.format("Should be: %x, was %x", expectedHash, actualHash)); 75 } 76 } 77 78 static final Funnel<Object> BAD_FUNNEL = new Funnel<Object>() { 79 @Override public void funnel(Object object, Sink byteSink) { 80 byteSink.putInt(object.hashCode()); 81 } 82 }; 83 84 static enum RandomHasherAction { 85 PUT_BOOLEAN() { 86 @Override void performAction(Random random, Iterable<? extends Sink> sinks) { 87 boolean value = random.nextBoolean(); 88 for (Sink sink : sinks) { 89 sink.putBoolean(value); 90 } 91 } 92 }, 93 PUT_BYTE() { 94 @Override void performAction(Random random, Iterable<? extends Sink> sinks) { 95 int value = random.nextInt(); 96 for (Sink sink : sinks) { 97 sink.putByte((byte) value); 98 } 99 } 100 }, 101 PUT_SHORT() { 102 @Override void performAction(Random random, Iterable<? extends Sink> sinks) { 103 short value = (short) random.nextInt(); 104 for (Sink sink : sinks) { 105 sink.putShort(value); 106 } 107 } 108 }, 109 PUT_CHAR() { 110 @Override void performAction(Random random, Iterable<? extends Sink> sinks) { 111 char value = (char) random.nextInt(); 112 for (Sink sink : sinks) { 113 sink.putChar(value); 114 } 115 } 116 }, 117 PUT_INT() { 118 @Override void performAction(Random random, Iterable<? extends Sink> sinks) { 119 int value = random.nextInt(); 120 for (Sink sink : sinks) { 121 sink.putInt(value); 122 } 123 } 124 }, 125 PUT_LONG() { 126 @Override void performAction(Random random, Iterable<? extends Sink> sinks) { 127 long value = random.nextLong(); 128 for (Sink sink : sinks) { 129 sink.putLong(value); 130 } 131 } 132 }, 133 PUT_FLOAT() { 134 @Override void performAction(Random random, Iterable<? extends Sink> sinks) { 135 float value = random.nextFloat(); 136 for (Sink sink : sinks) { 137 sink.putFloat(value); 138 } 139 } 140 }, 141 PUT_DOUBLE() { 142 @Override void performAction(Random random, Iterable<? extends Sink> sinks) { 143 double value = random.nextDouble(); 144 for (Sink sink : sinks) { 145 sink.putDouble(value); 146 } 147 } 148 }, 149 PUT_BYTES() { 150 @Override void performAction(Random random, Iterable<? extends Sink> sinks) { 151 byte[] value = new byte[random.nextInt(128)]; 152 random.nextBytes(value); 153 for (Sink sink : sinks) { 154 sink.putBytes(value); 155 } 156 } 157 }, 158 PUT_BYTES_INT_INT() { 159 @Override void performAction(Random random, Iterable<? extends Sink> sinks) { 160 byte[] value = new byte[random.nextInt(128)]; 161 random.nextBytes(value); 162 int off = random.nextInt(value.length + 1); 163 int len = random.nextInt(value.length - off + 1); 164 for (Sink sink : sinks) { 165 sink.putBytes(value); 166 } 167 } 168 }; 169 170 abstract void performAction(Random random, Iterable<? extends Sink> sinks); 171 172 private static final RandomHasherAction[] actions = values(); 173 174 static RandomHasherAction pickAtRandom(Random random) { 175 return actions[random.nextInt(actions.length)]; 176 } 177 } 178 } 179