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