Home | History | Annotate | Download | only in hash
      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