• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.hash;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 
22 import com.google.common.base.Charsets;
23 import com.google.common.collect.ImmutableSet;
24 import com.google.common.collect.Sets;
25 import com.google.common.primitives.Ints;
26 import com.google.common.testing.EqualsTester;
27 import java.nio.ByteBuffer;
28 import java.nio.ByteOrder;
29 import java.nio.charset.Charset;
30 import java.util.Arrays;
31 import java.util.Random;
32 import java.util.Set;
33 import org.junit.Assert;
34 
35 /**
36  * Various utilities for testing {@link HashFunction}s.
37  *
38  * @author Dimitris Andreou
39  * @author Kurt Alfred Kluever
40  */
41 final class HashTestUtils {
HashTestUtils()42   private HashTestUtils() {}
43 
44   /** Converts a string, which should contain only ascii-representable characters, to a byte[]. */
ascii(String string)45   static byte[] ascii(String string) {
46     byte[] bytes = new byte[string.length()];
47     for (int i = 0; i < string.length(); i++) {
48       bytes[i] = (byte) string.charAt(i);
49     }
50     return bytes;
51   }
52 
53   interface HashFn {
hash(byte[] input, int seed)54     byte[] hash(byte[] input, int seed);
55   }
56 
verifyHashFunction(HashFn hashFunction, int hashbits, int expected)57   static void verifyHashFunction(HashFn hashFunction, int hashbits, int expected) {
58     int hashBytes = hashbits / 8;
59 
60     byte[] key = new byte[256];
61     byte[] hashes = new byte[hashBytes * 256];
62 
63     // Hash keys of the form {}, {0}, {0,1}, {0,1,2}... up to N=255,using 256-N as the seed
64     for (int i = 0; i < 256; i++) {
65       key[i] = (byte) i;
66       int seed = 256 - i;
67       byte[] hash = hashFunction.hash(Arrays.copyOf(key, i), seed);
68       System.arraycopy(hash, 0, hashes, i * hashBytes, hash.length);
69     }
70 
71     // Then hash the result array
72     byte[] result = hashFunction.hash(hashes, 0);
73 
74     // interpreted in little-endian order.
75     int verification = Integer.reverseBytes(Ints.fromByteArray(result));
76 
77     if (expected != verification) {
78       throw new AssertionError(
79           "Expected: "
80               + Integer.toHexString(expected)
81               + " got: "
82               + Integer.toHexString(verification));
83     }
84   }
85 
86   static final Funnel<Object> BAD_FUNNEL =
87       new Funnel<Object>() {
88         @Override
89         public void funnel(Object object, PrimitiveSink bytePrimitiveSink) {
90           bytePrimitiveSink.putInt(object.hashCode());
91         }
92       };
93 
94   enum RandomHasherAction {
PUT_BOOLEAN()95     PUT_BOOLEAN() {
96       @Override
97       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
98         boolean value = random.nextBoolean();
99         for (PrimitiveSink sink : sinks) {
100           sink.putBoolean(value);
101         }
102       }
103     },
PUT_BYTE()104     PUT_BYTE() {
105       @Override
106       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
107         int value = random.nextInt();
108         for (PrimitiveSink sink : sinks) {
109           sink.putByte((byte) value);
110         }
111       }
112     },
PUT_SHORT()113     PUT_SHORT() {
114       @Override
115       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
116         short value = (short) random.nextInt();
117         for (PrimitiveSink sink : sinks) {
118           sink.putShort(value);
119         }
120       }
121     },
PUT_CHAR()122     PUT_CHAR() {
123       @Override
124       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
125         char value = (char) random.nextInt();
126         for (PrimitiveSink sink : sinks) {
127           sink.putChar(value);
128         }
129       }
130     },
PUT_INT()131     PUT_INT() {
132       @Override
133       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
134         int value = random.nextInt();
135         for (PrimitiveSink sink : sinks) {
136           sink.putInt(value);
137         }
138       }
139     },
PUT_LONG()140     PUT_LONG() {
141       @Override
142       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
143         long value = random.nextLong();
144         for (PrimitiveSink sink : sinks) {
145           sink.putLong(value);
146         }
147       }
148     },
PUT_FLOAT()149     PUT_FLOAT() {
150       @Override
151       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
152         float value = random.nextFloat();
153         for (PrimitiveSink sink : sinks) {
154           sink.putFloat(value);
155         }
156       }
157     },
PUT_DOUBLE()158     PUT_DOUBLE() {
159       @Override
160       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
161         double value = random.nextDouble();
162         for (PrimitiveSink sink : sinks) {
163           sink.putDouble(value);
164         }
165       }
166     },
PUT_BYTES()167     PUT_BYTES() {
168       @Override
169       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
170         byte[] value = new byte[random.nextInt(128)];
171         random.nextBytes(value);
172         for (PrimitiveSink sink : sinks) {
173           sink.putBytes(value);
174         }
175       }
176     },
PUT_BYTES_INT_INT()177     PUT_BYTES_INT_INT() {
178       @Override
179       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
180         byte[] value = new byte[random.nextInt(128)];
181         random.nextBytes(value);
182         int off = random.nextInt(value.length + 1);
183         int len = random.nextInt(value.length - off + 1);
184         for (PrimitiveSink sink : sinks) {
185           sink.putBytes(value, off, len);
186         }
187       }
188     },
PUT_BYTE_BUFFER()189     PUT_BYTE_BUFFER() {
190       @Override
191       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
192         byte[] value = new byte[random.nextInt(128)];
193         random.nextBytes(value);
194         int pos = random.nextInt(value.length + 1);
195         int limit = pos + random.nextInt(value.length - pos + 1);
196         for (PrimitiveSink sink : sinks) {
197           ByteBuffer buffer = ByteBuffer.wrap(value);
198           buffer.position(pos);
199           buffer.limit(limit);
200           sink.putBytes(buffer);
201           assertEquals(limit, buffer.limit());
202           assertEquals(limit, buffer.position());
203         }
204       }
205     },
PUT_STRING()206     PUT_STRING() {
207       @Override
208       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
209         char[] value = new char[random.nextInt(128)];
210         for (int i = 0; i < value.length; i++) {
211           value[i] = (char) random.nextInt();
212         }
213         String s = new String(value);
214         for (PrimitiveSink sink : sinks) {
215           sink.putUnencodedChars(s);
216         }
217       }
218     },
PUT_STRING_LOW_SURROGATE()219     PUT_STRING_LOW_SURROGATE() {
220       @Override
221       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
222         String s = new String(new char[] {randomLowSurrogate(random)});
223         for (PrimitiveSink sink : sinks) {
224           sink.putUnencodedChars(s);
225         }
226       }
227     },
PUT_STRING_HIGH_SURROGATE()228     PUT_STRING_HIGH_SURROGATE() {
229       @Override
230       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
231         String s = new String(new char[] {randomHighSurrogate(random)});
232         for (PrimitiveSink sink : sinks) {
233           sink.putUnencodedChars(s);
234         }
235       }
236     },
PUT_STRING_LOW_HIGH_SURROGATE()237     PUT_STRING_LOW_HIGH_SURROGATE() {
238       @Override
239       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
240         String s = new String(new char[] {randomLowSurrogate(random), randomHighSurrogate(random)});
241         for (PrimitiveSink sink : sinks) {
242           sink.putUnencodedChars(s);
243         }
244       }
245     },
PUT_STRING_HIGH_LOW_SURROGATE()246     PUT_STRING_HIGH_LOW_SURROGATE() {
247       @Override
248       void performAction(Random random, Iterable<? extends PrimitiveSink> sinks) {
249         String s = new String(new char[] {randomHighSurrogate(random), randomLowSurrogate(random)});
250         for (PrimitiveSink sink : sinks) {
251           sink.putUnencodedChars(s);
252         }
253       }
254     };
255 
performAction(Random random, Iterable<? extends PrimitiveSink> sinks)256     abstract void performAction(Random random, Iterable<? extends PrimitiveSink> sinks);
257 
258     private static final RandomHasherAction[] actions = values();
259 
pickAtRandom(Random random)260     static RandomHasherAction pickAtRandom(Random random) {
261       return actions[random.nextInt(actions.length)];
262     }
263   }
264 
265   /**
266    * Test that the hash function contains no funnels. A funnel is a situation where a set of input
267    * (key) bits 'affects' a strictly smaller set of output bits. Funneling is bad because it can
268    * result in more-than-ideal collisions for a non-uniformly distributed key space. In practice,
269    * most key spaces are ANYTHING BUT uniformly distributed. A bit(i) in the input is said to
270    * 'affect' a bit(j) in the output if two inputs, identical but for bit(i), will differ at output
271    * bit(j) about half the time
272    *
273    * <p>Funneling is pretty simple to detect. The key idea is to find example keys which
274    * unequivocally demonstrate that funneling cannot be occurring. This is done bit-by-bit. For each
275    * input bit(i) and output bit(j), two pairs of keys must be found with all bits identical except
276    * bit(i). One pair must differ in output bit(j), and one pair must not. This proves that input
277    * bit(i) can alter output bit(j).
278    */
checkNoFunnels(HashFunction function)279   static void checkNoFunnels(HashFunction function) {
280     Random rand = new Random(0);
281     int keyBits = 32;
282     int hashBits = function.bits();
283 
284     // output loop tests input bit
285     for (int i = 0; i < keyBits; i++) {
286       int same = 0x0; // bitset for output bits with same values
287       int diff = 0x0; // bitset for output bits with different values
288       int count = 0;
289       // originally was 2 * Math.log(...), making it try more times to avoid flakiness issues
290       int maxCount = (int) (4 * Math.log(2 * keyBits * hashBits) + 1);
291       while (same != 0xffffffff || diff != 0xffffffff) {
292         int key1 = rand.nextInt();
293         // flip input bit for key2
294         int key2 = key1 ^ (1 << i);
295         // get hashes
296         int hash1 = function.hashInt(key1).asInt();
297         int hash2 = function.hashInt(key2).asInt();
298         // test whether the hash values have same output bits
299         same |= ~(hash1 ^ hash2);
300         // test whether the hash values have different output bits
301         diff |= (hash1 ^ hash2);
302 
303         count++;
304         // check whether we've exceeded the probabilistically
305         // likely number of trials to have proven no funneling
306         if (count > maxCount) {
307           Assert.fail(
308               "input bit("
309                   + i
310                   + ") was found not to affect all "
311                   + hashBits
312                   + " output bits; The unaffected bits are "
313                   + "as follows: "
314                   + ~(same & diff)
315                   + ". This was "
316                   + "determined after "
317                   + count
318                   + " trials.");
319         }
320       }
321     }
322   }
323 
324   /**
325    * Test for avalanche. Avalanche means that output bits differ with roughly 1/2 probability on
326    * different input keys. This test verifies that each possible 1-bit key delta achieves avalanche.
327    *
328    * <p>For more information: http://burtleburtle.net/bob/hash/avalanche.html
329    */
checkAvalanche(HashFunction function, int trials, double epsilon)330   static void checkAvalanche(HashFunction function, int trials, double epsilon) {
331     Random rand = new Random(0);
332     int keyBits = 32;
333     int hashBits = function.bits();
334     for (int i = 0; i < keyBits; i++) {
335       int[] same = new int[hashBits];
336       int[] diff = new int[hashBits];
337       // go through trials to compute probability
338       for (int j = 0; j < trials; j++) {
339         int key1 = rand.nextInt();
340         // flip input bit for key2
341         int key2 = key1 ^ (1 << i);
342         // compute hash values
343         int hash1 = function.hashInt(key1).asInt();
344         int hash2 = function.hashInt(key2).asInt();
345         for (int k = 0; k < hashBits; k++) {
346           if ((hash1 & (1 << k)) == (hash2 & (1 << k))) {
347             same[k] += 1;
348           } else {
349             diff[k] += 1;
350           }
351         }
352       }
353       // measure probability and assert it's within margin of error
354       for (int j = 0; j < hashBits; j++) {
355         double prob = (double) diff[j] / (double) (diff[j] + same[j]);
356         Assert.assertEquals(0.50d, prob, epsilon);
357       }
358     }
359   }
360 
361   /**
362    * Test for 2-bit characteristics. A characteristic is a delta in the input which is repeated in
363    * the output. For example, if f() is a block cipher and c is a characteristic, then f(x^c) =
364    * f(x)^c with greater than expected probability. The test for funneling is merely a test for
365    * 1-bit characteristics.
366    *
367    * <p>There is more general code provided by Bob Jenkins to test arbitrarily sized characteristics
368    * using the magic of gaussian elimination: http://burtleburtle.net/bob/crypto/findingc.html.
369    */
checkNo2BitCharacteristics(HashFunction function)370   static void checkNo2BitCharacteristics(HashFunction function) {
371     Random rand = new Random(0);
372     int keyBits = 32;
373 
374     // get every one of (keyBits choose 2) deltas:
375     for (int i = 0; i < keyBits; i++) {
376       for (int j = 0; j < keyBits; j++) {
377         if (j <= i) continue;
378         int count = 0;
379         int maxCount = 20; // the probability of error here is miniscule
380         boolean diff = false;
381 
382         while (!diff) {
383           int delta = (1 << i) | (1 << j);
384           int key1 = rand.nextInt();
385           // apply delta
386           int key2 = key1 ^ delta;
387 
388           // get hashes
389           int hash1 = function.hashInt(key1).asInt();
390           int hash2 = function.hashInt(key2).asInt();
391 
392           // this 2-bit candidate delta is not a characteristic
393           // if deltas are different
394           if ((hash1 ^ hash2) != delta) {
395             diff = true;
396             continue;
397           }
398 
399           // check if we've exceeded the probabilistically
400           // likely number of trials to have proven 2-bit candidate
401           // is not a characteristic
402           count++;
403           if (count > maxCount) {
404             Assert.fail(
405                 "2-bit delta ("
406                     + i
407                     + ", "
408                     + j
409                     + ") is likely a "
410                     + "characteristic for this hash. This was "
411                     + "determined after "
412                     + count
413                     + " trials");
414           }
415         }
416       }
417     }
418   }
419 
420   /**
421    * Test for avalanche with 2-bit deltas. Most probabilities of output bit(j) differing are well
422    * within 50%.
423    */
check2BitAvalanche(HashFunction function, int trials, double epsilon)424   static void check2BitAvalanche(HashFunction function, int trials, double epsilon) {
425     Random rand = new Random(0);
426     int keyBits = 32;
427     int hashBits = function.bits();
428     for (int bit1 = 0; bit1 < keyBits; bit1++) {
429       for (int bit2 = 0; bit2 < keyBits; bit2++) {
430         if (bit2 <= bit1) continue;
431         int delta = (1 << bit1) | (1 << bit2);
432         int[] same = new int[hashBits];
433         int[] diff = new int[hashBits];
434         // go through trials to compute probability
435         for (int j = 0; j < trials; j++) {
436           int key1 = rand.nextInt();
437           // flip input bit for key2
438           int key2 = key1 ^ delta;
439           // compute hash values
440           int hash1 = function.hashInt(key1).asInt();
441           int hash2 = function.hashInt(key2).asInt();
442           for (int k = 0; k < hashBits; k++) {
443             if ((hash1 & (1 << k)) == (hash2 & (1 << k))) {
444               same[k] += 1;
445             } else {
446               diff[k] += 1;
447             }
448           }
449         }
450         // measure probability and assert it's within margin of error
451         for (int j = 0; j < hashBits; j++) {
452           double prob = (double) diff[j] / (double) (diff[j] + same[j]);
453           Assert.assertEquals(0.50d, prob, epsilon);
454         }
455       }
456     }
457   }
458 
459   /**
460    * Checks that a Hasher returns the same HashCode when given the same input, and also that the
461    * collision rate looks sane.
462    */
assertInvariants(HashFunction hashFunction)463   static void assertInvariants(HashFunction hashFunction) {
464     int objects = 100;
465     Set<HashCode> hashcodes = Sets.newHashSetWithExpectedSize(objects);
466     Random random = new Random(314159);
467     for (int i = 0; i < objects; i++) {
468       int value = random.nextInt();
469       HashCode hashcode1 = hashFunction.hashInt(value);
470       HashCode hashcode2 = hashFunction.hashInt(value);
471       Assert.assertEquals(hashcode1, hashcode2); // idempotent
472       Assert.assertEquals(hashFunction.bits(), hashcode1.bits());
473       Assert.assertEquals(hashFunction.bits(), hashcode1.asBytes().length * 8);
474       hashcodes.add(hashcode1);
475     }
476     Assert.assertTrue(hashcodes.size() > objects * 0.95); // quite relaxed test
477 
478     assertHashBytesThrowsCorrectExceptions(hashFunction);
479     assertIndependentHashers(hashFunction);
480     assertShortcutsAreEquivalent(hashFunction, 512);
481   }
482 
assertHashByteBufferInvariants(HashFunction hashFunction)483   static void assertHashByteBufferInvariants(HashFunction hashFunction) {
484     assertHashByteBufferMatchesBytes(hashFunction);
485     assertHashByteBufferExhaustsBuffer(hashFunction);
486     assertHashByteBufferPreservesByteOrder(hashFunction);
487     assertHasherByteBufferPreservesByteOrder(hashFunction);
488   }
489 
assertHashByteBufferMatchesBytes(HashFunction hashFunction)490   static void assertHashByteBufferMatchesBytes(HashFunction hashFunction) {
491     Random rng = new Random(0L);
492     byte[] bytes = new byte[rng.nextInt(256) + 1];
493     rng.nextBytes(bytes);
494     assertEquals(hashFunction.hashBytes(bytes), hashFunction.hashBytes(ByteBuffer.wrap(bytes)));
495   }
496 
assertHashByteBufferExhaustsBuffer(HashFunction hashFunction)497   static void assertHashByteBufferExhaustsBuffer(HashFunction hashFunction) {
498     Random rng = new Random(0L);
499     byte[] bytes = new byte[rng.nextInt(256) + 1];
500     rng.nextBytes(bytes);
501     ByteBuffer buffer = ByteBuffer.wrap(bytes);
502     HashCode unused = hashFunction.hashBytes(buffer);
503     assertFalse(buffer.hasRemaining());
504   }
505 
assertHashByteBufferPreservesByteOrder(HashFunction hashFunction)506   static void assertHashByteBufferPreservesByteOrder(HashFunction hashFunction) {
507     Random rng = new Random(0L);
508     byte[] bytes = new byte[rng.nextInt(256) + 1];
509     rng.nextBytes(bytes);
510     ByteBuffer littleEndian = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
511     ByteBuffer bigEndian = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
512     assertEquals(hashFunction.hashBytes(littleEndian), hashFunction.hashBytes(bigEndian));
513     assertEquals(ByteOrder.LITTLE_ENDIAN, littleEndian.order());
514     assertEquals(ByteOrder.BIG_ENDIAN, bigEndian.order());
515   }
516 
assertHasherByteBufferPreservesByteOrder(HashFunction hashFunction)517   static void assertHasherByteBufferPreservesByteOrder(HashFunction hashFunction) {
518     Random rng = new Random(0L);
519     byte[] bytes = new byte[rng.nextInt(256) + 1];
520     rng.nextBytes(bytes);
521     ByteBuffer littleEndian = ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN);
522     ByteBuffer bigEndian = ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN);
523     assertEquals(
524         hashFunction.newHasher().putBytes(littleEndian).hash(),
525         hashFunction.newHasher().putBytes(bigEndian).hash());
526     assertEquals(ByteOrder.LITTLE_ENDIAN, littleEndian.order());
527     assertEquals(ByteOrder.BIG_ENDIAN, bigEndian.order());
528   }
529 
assertHashBytesThrowsCorrectExceptions(HashFunction hashFunction)530   static void assertHashBytesThrowsCorrectExceptions(HashFunction hashFunction) {
531     {
532       HashCode unused = hashFunction.hashBytes(new byte[64], 0, 0);
533     }
534 
535     try {
536       hashFunction.hashBytes(new byte[128], -1, 128);
537       Assert.fail();
538     } catch (IndexOutOfBoundsException expected) {
539     }
540     try {
541       hashFunction.hashBytes(new byte[128], 64, 256 /* too long len */);
542       Assert.fail();
543     } catch (IndexOutOfBoundsException expected) {
544     }
545     try {
546       hashFunction.hashBytes(new byte[64], 0, -1);
547       Assert.fail();
548     } catch (IndexOutOfBoundsException expected) {
549     }
550   }
551 
assertIndependentHashers(HashFunction hashFunction)552   static void assertIndependentHashers(HashFunction hashFunction) {
553     int numActions = 100;
554     // hashcodes from non-overlapping hash computations
555     HashCode expected1 = randomHash(hashFunction, new Random(1L), numActions);
556     HashCode expected2 = randomHash(hashFunction, new Random(2L), numActions);
557 
558     // equivalent, but overlapping, computations (should produce the same results as above)
559     Random random1 = new Random(1L);
560     Random random2 = new Random(2L);
561     Hasher hasher1 = hashFunction.newHasher();
562     Hasher hasher2 = hashFunction.newHasher();
563     for (int i = 0; i < numActions; i++) {
564       RandomHasherAction.pickAtRandom(random1).performAction(random1, ImmutableSet.of(hasher1));
565       RandomHasherAction.pickAtRandom(random2).performAction(random2, ImmutableSet.of(hasher2));
566     }
567 
568     Assert.assertEquals(expected1, hasher1.hash());
569     Assert.assertEquals(expected2, hasher2.hash());
570   }
571 
randomHash(HashFunction hashFunction, Random random, int numActions)572   static HashCode randomHash(HashFunction hashFunction, Random random, int numActions) {
573     Hasher hasher = hashFunction.newHasher();
574     for (int i = 0; i < numActions; i++) {
575       RandomHasherAction.pickAtRandom(random).performAction(random, ImmutableSet.of(hasher));
576     }
577     return hasher.hash();
578   }
579 
assertShortcutsAreEquivalent(HashFunction hashFunction, int trials)580   private static void assertShortcutsAreEquivalent(HashFunction hashFunction, int trials) {
581     Random random = new Random(42085L);
582     for (int i = 0; i < trials; i++) {
583       assertHashBytesEquivalence(hashFunction, random);
584       assertHashByteBufferEquivalence(hashFunction, random);
585       assertHashIntEquivalence(hashFunction, random);
586       assertHashLongEquivalence(hashFunction, random);
587       assertHashStringEquivalence(hashFunction, random);
588       assertHashStringWithSurrogatesEquivalence(hashFunction, random);
589     }
590   }
591 
assertHashBytesEquivalence(HashFunction hashFunction, Random random)592   private static void assertHashBytesEquivalence(HashFunction hashFunction, Random random) {
593     int size = random.nextInt(2048);
594     byte[] bytes = new byte[size];
595     random.nextBytes(bytes);
596     assertEquals(
597         hashFunction.hashBytes(bytes), hashFunction.newHasher(size).putBytes(bytes).hash());
598     int off = random.nextInt(size);
599     int len = random.nextInt(size - off);
600     assertEquals(
601         hashFunction.hashBytes(bytes, off, len),
602         hashFunction.newHasher(size).putBytes(bytes, off, len).hash());
603   }
604 
assertHashByteBufferEquivalence(HashFunction hashFunction, Random random)605   private static void assertHashByteBufferEquivalence(HashFunction hashFunction, Random random) {
606     int size = random.nextInt(2048);
607     byte[] bytes = new byte[size];
608     random.nextBytes(bytes);
609     assertEquals(
610         hashFunction.hashBytes(ByteBuffer.wrap(bytes)),
611         hashFunction.newHasher(size).putBytes(ByteBuffer.wrap(bytes)).hash());
612     int off = random.nextInt(size);
613     int len = random.nextInt(size - off);
614     assertEquals(
615         hashFunction.hashBytes(ByteBuffer.wrap(bytes, off, len)),
616         hashFunction.newHasher(size).putBytes(ByteBuffer.wrap(bytes, off, len)).hash());
617   }
618 
assertHashIntEquivalence(HashFunction hashFunction, Random random)619   private static void assertHashIntEquivalence(HashFunction hashFunction, Random random) {
620     int i = random.nextInt();
621     assertEquals(hashFunction.hashInt(i), hashFunction.newHasher().putInt(i).hash());
622   }
623 
assertHashLongEquivalence(HashFunction hashFunction, Random random)624   private static void assertHashLongEquivalence(HashFunction hashFunction, Random random) {
625     long l = random.nextLong();
626     assertEquals(hashFunction.hashLong(l), hashFunction.newHasher().putLong(l).hash());
627   }
628 
629   private static final ImmutableSet<Charset> CHARSETS =
630       ImmutableSet.of(
631           Charsets.ISO_8859_1,
632           Charsets.US_ASCII,
633           Charsets.UTF_16,
634           Charsets.UTF_16BE,
635           Charsets.UTF_16LE,
636           Charsets.UTF_8);
637 
assertHashStringEquivalence(HashFunction hashFunction, Random random)638   private static void assertHashStringEquivalence(HashFunction hashFunction, Random random) {
639     // Test that only data and data-order is important, not the individual operations.
640     new EqualsTester()
641         .addEqualityGroup(
642             hashFunction.hashUnencodedChars("abc"),
643             hashFunction.newHasher().putUnencodedChars("abc").hash(),
644             hashFunction.newHasher().putUnencodedChars("ab").putUnencodedChars("c").hash(),
645             hashFunction.newHasher().putUnencodedChars("a").putUnencodedChars("bc").hash(),
646             hashFunction
647                 .newHasher()
648                 .putUnencodedChars("a")
649                 .putUnencodedChars("b")
650                 .putUnencodedChars("c")
651                 .hash(),
652             hashFunction.newHasher().putChar('a').putUnencodedChars("bc").hash(),
653             hashFunction.newHasher().putUnencodedChars("ab").putChar('c').hash(),
654             hashFunction.newHasher().putChar('a').putChar('b').putChar('c').hash())
655         .testEquals();
656 
657     int size = random.nextInt(2048);
658     byte[] bytes = new byte[size];
659     random.nextBytes(bytes);
660     String string = new String(bytes, Charsets.US_ASCII);
661     assertEquals(
662         hashFunction.hashUnencodedChars(string),
663         hashFunction.newHasher().putUnencodedChars(string).hash());
664     for (Charset charset : CHARSETS) {
665       assertEquals(
666           hashFunction.hashString(string, charset),
667           hashFunction.newHasher().putString(string, charset).hash());
668     }
669   }
670 
671   /**
672    * This verifies that putUnencodedChars(String) and hashUnencodedChars(String) are equivalent,
673    * even for funny strings composed by (possibly unmatched, and mostly illegal) surrogate
674    * characters. (But doesn't test that they do the right thing - just their consistency).
675    */
assertHashStringWithSurrogatesEquivalence( HashFunction hashFunction, Random random)676   private static void assertHashStringWithSurrogatesEquivalence(
677       HashFunction hashFunction, Random random) {
678     int size = random.nextInt(8) + 1;
679     char[] chars = new char[size];
680     for (int i = 0; i < chars.length; i++) {
681       chars[i] = random.nextBoolean() ? randomLowSurrogate(random) : randomHighSurrogate(random);
682     }
683     String string = new String(chars);
684     assertEquals(
685         hashFunction.hashUnencodedChars(string),
686         hashFunction.newHasher().putUnencodedChars(string).hash());
687   }
688 
randomLowSurrogate(Random random)689   static char randomLowSurrogate(Random random) {
690     return (char)
691         (Character.MIN_LOW_SURROGATE
692             + random.nextInt(Character.MAX_LOW_SURROGATE - Character.MIN_LOW_SURROGATE + 1));
693   }
694 
randomHighSurrogate(Random random)695   static char randomHighSurrogate(Random random) {
696     return (char)
697         (Character.MIN_HIGH_SURROGATE
698             + random.nextInt(Character.MAX_HIGH_SURROGATE - Character.MIN_HIGH_SURROGATE + 1));
699   }
700 }
701