1// Copyright 2011 Google Inc. All Rights Reserved.
2
3package com.google.common.hash;
4
5import com.google.common.primitives.Ints;
6
7import org.junit.Assert;
8
9import java.nio.ByteBuffer;
10import java.nio.ByteOrder;
11import java.util.Arrays;
12import java.util.Random;
13
14/**
15 * @author andreou@google.com (Dimitris Andreou)
16 */
17class 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