Java程序  |  179行  |  5.15 KB

// Copyright 2011 Google Inc. All Rights Reserved.

package com.google.common.hash;

import com.google.common.primitives.Ints;

import org.junit.Assert;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.Random;

/**
 * @author andreou@google.com (Dimitris Andreou)
 */
class HashTestUtils {
  private HashTestUtils() {}

  /**
   * Converts a string, which should contain only ascii-representable characters, to a byte[].
   */
  static byte[] ascii(String string) {
    byte[] bytes = new byte[string.length()];
    for (int i = 0; i < string.length(); i++) {
      bytes[i] = (byte) string.charAt(i);
    }
    return bytes;
  }

  /**
   * Returns a byte array representation for a sequence of longs, in big-endian order. 
   */
  static byte[] toBytes(ByteOrder bo, long... longs) {
    ByteBuffer bb = ByteBuffer.wrap(new byte[longs.length * 8]).order(bo);
    for (long x : longs) {
      bb.putLong(x);
    }
    return bb.array();
  }

  interface HashFn {
    byte[] hash(byte[] input, int seed);
  }

  static void verifyHashFunction(HashFn hashFunction, int hashbits, int expected) {
    int hashBytes = hashbits / 8;

    byte[] key = new byte[256];
    byte[] hashes = new byte[hashBytes * 256];

    // Hash keys of the form {}, {0}, {0,1}, {0,1,2}... up to N=255,using 256-N as the seed
    for (int i = 0; i < 256; i++) {
      key[i] = (byte) i;
      int seed = 256 - i;
      byte[] hash = hashFunction.hash(Arrays.copyOf(key, i), seed);
      System.arraycopy(hash, 0, hashes, i * hashBytes, hash.length);
    }
    
    // Then hash the result array
    byte[] result = hashFunction.hash(hashes, 0);
    
    // interpreted in little-endian order.
    int verification = Integer.reverseBytes(Ints.fromByteArray(result)); 

    if (expected != verification) {
      throw new AssertionError("Expected: " + Integer.toHexString(expected)
          + " got: " + Integer.toHexString(verification));
    }
  }
  
  static void assertEqualHashes(byte[] expectedHash, byte[] actualHash) {
    if (!Arrays.equals(expectedHash, actualHash)) {
      Assert.fail(String.format("Should be: %x, was %x", expectedHash, actualHash));
    }
  }
  
  static final Funnel<Object> BAD_FUNNEL = new Funnel<Object>() {
    @Override public void funnel(Object object, Sink byteSink) {
      byteSink.putInt(object.hashCode());
    }
  };
  
  static enum RandomHasherAction {
    PUT_BOOLEAN() {
      @Override void performAction(Random random, Iterable<? extends Sink> sinks) {
        boolean value = random.nextBoolean();
        for (Sink sink : sinks) {
          sink.putBoolean(value);
        }
      }
    },
    PUT_BYTE() {
      @Override void performAction(Random random, Iterable<? extends Sink> sinks) {
        int value = random.nextInt();
        for (Sink sink : sinks) {
          sink.putByte((byte) value);
        }
      }
    },
    PUT_SHORT() {
      @Override void performAction(Random random, Iterable<? extends Sink> sinks) {
        short value = (short) random.nextInt();
        for (Sink sink : sinks) {
          sink.putShort(value);
        }
      }
    },
    PUT_CHAR() {
      @Override void performAction(Random random, Iterable<? extends Sink> sinks) {
        char value = (char) random.nextInt();
        for (Sink sink : sinks) {
          sink.putChar(value);
        }
      }
    },
    PUT_INT() {
      @Override void performAction(Random random, Iterable<? extends Sink> sinks) {
        int value = random.nextInt();
        for (Sink sink : sinks) {
          sink.putInt(value);
        }
      }
    },
    PUT_LONG() {
      @Override void performAction(Random random, Iterable<? extends Sink> sinks) {
        long value = random.nextLong();
        for (Sink sink : sinks) {
          sink.putLong(value);
        }
      }
    },
    PUT_FLOAT() {
      @Override void performAction(Random random, Iterable<? extends Sink> sinks) {
        float value = random.nextFloat();
        for (Sink sink : sinks) {
          sink.putFloat(value);
        }
      }
    },
    PUT_DOUBLE() {
      @Override void performAction(Random random, Iterable<? extends Sink> sinks) {
        double value = random.nextDouble();
        for (Sink sink : sinks) {
          sink.putDouble(value);
        }
      }
    },
    PUT_BYTES() {
      @Override void performAction(Random random, Iterable<? extends Sink> sinks) {
        byte[] value = new byte[random.nextInt(128)];
        random.nextBytes(value);
        for (Sink sink : sinks) {
          sink.putBytes(value);
        }
      }
    },
    PUT_BYTES_INT_INT() {
      @Override void performAction(Random random, Iterable<? extends Sink> sinks) {
        byte[] value = new byte[random.nextInt(128)];
        random.nextBytes(value);
        int off = random.nextInt(value.length + 1);
        int len = random.nextInt(value.length - off + 1);
        for (Sink sink : sinks) {
          sink.putBytes(value);
        }
      }
    };
    
    abstract void performAction(Random random, Iterable<? extends Sink> sinks);
    
    private static final RandomHasherAction[] actions = values();
    
    static RandomHasherAction pickAtRandom(Random random) {
      return actions[random.nextInt(actions.length)];
    }
  }
}