This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-codec.git
The following commit(s) were added to refs/heads/master by this push: new 40be3f7 Replaced tabs with 4 spaces 40be3f7 is described below commit 40be3f770c8a1e87342d14a6b730b7d99fef8e71 Author: Claude Warren <cla...@xenei.com> AuthorDate: Sun Nov 3 09:09:29 2019 +0000 Replaced tabs with 4 spaces --- .../apache/commons/codec/digest/MurmurHash2.java | 348 +++--- .../apache/commons/codec/digest/MurmurHash3.java | 1172 ++++++++++---------- .../commons/codec/digest/MurmurHash2Test.java | 230 ++-- .../commons/codec/digest/MurmurHash3Test.java | 390 +++---- 4 files changed, 1070 insertions(+), 1070 deletions(-) diff --git a/src/main/java/org/apache/commons/codec/digest/MurmurHash2.java b/src/main/java/org/apache/commons/codec/digest/MurmurHash2.java index c585d39..0e07bfe 100644 --- a/src/main/java/org/apache/commons/codec/digest/MurmurHash2.java +++ b/src/main/java/org/apache/commons/codec/digest/MurmurHash2.java @@ -36,178 +36,178 @@ package org.apache.commons.codec.digest; */ public final class MurmurHash2 { - // all methods static; private constructor. - private MurmurHash2() { - } - - /** - * Generates 32 bit hash from byte array of the given length and seed. - * - * @param data byte array to hash - * @param length length of the array to hash - * @param seed initial seed value - * @return 32 bit hash of the given array - */ - public static int hash32(final byte[] data, final int length, final int seed) { - // 'm' and 'r' are mixing constants generated offline. - // They're not really 'magic', they just happen to work well. - final int m = 0x5bd1e995; - final int r = 24; - - // Initialize the hash to a random value - int h = seed ^ length; - final int length4 = length / 4; - - for (int i = 0; i < length4; i++) { - final int i4 = i * 4; - int k = (data[i4 + 0] & 0xff) + ((data[i4 + 1] & 0xff) << 8) + ((data[i4 + 2] & 0xff) << 16) - + ((data[i4 + 3] & 0xff) << 24); - k *= m; - k ^= k >>> r; - k *= m; - h *= m; - h ^= k; - } - - // Handle the last few bytes of the input array - switch (length % 4) { - case 3: - h ^= (data[(length & ~3) + 2] & 0xff) << 16; - case 2: - h ^= (data[(length & ~3) + 1] & 0xff) << 8; - case 1: - h ^= (data[length & ~3] & 0xff); - h *= m; - } - - h ^= h >>> 13; - h *= m; - h ^= h >>> 15; - - return h; - } - - /** - * Generates 32 bit hash from byte array with default seed value. - * - * @param data byte array to hash - * @param length length of the array to hash - * @return 32 bit hash of the given array - */ - public static int hash32(final byte[] data, final int length) { - return hash32(data, length, 0x9747b28c); - } - - /** - * Generates 32 bit hash from a string. - * - * @param text string to hash - * @return 32 bit hash of the given string - */ - public static int hash32(final String text) { - final byte[] bytes = text.getBytes(); - return hash32(bytes, bytes.length); - } - - /** - * Generates 32 bit hash from a substring. - * - * @param text string to hash - * @param from starting index - * @param length length of the substring to hash - * @return 32 bit hash of the given string - */ - public static int hash32(final String text, final int from, final int length) { - return hash32(text.substring(from, from + length)); - } - - /** - * Generates 64 bit hash from byte array of the given length and seed. - * - * @param data byte array to hash - * @param length length of the array to hash - * @param seed initial seed value - * @return 64 bit hash of the given array - */ - public static long hash64(final byte[] data, final int length, final int seed) { - final long m = 0xc6a4a7935bd1e995L; - final int r = 47; - - long h = (seed & 0xffffffffl) ^ (length * m); - - final int length8 = length / 8; - - for (int i = 0; i < length8; i++) { - final int i8 = i * 8; - long k = ((long) data[i8 + 0] & 0xff) + (((long) data[i8 + 1] & 0xff) << 8) - + (((long) data[i8 + 2] & 0xff) << 16) + (((long) data[i8 + 3] & 0xff) << 24) - + (((long) data[i8 + 4] & 0xff) << 32) + (((long) data[i8 + 5] & 0xff) << 40) - + (((long) data[i8 + 6] & 0xff) << 48) + (((long) data[i8 + 7] & 0xff) << 56); - - k *= m; - k ^= k >>> r; - k *= m; - - h ^= k; - h *= m; - } - - switch (length % 8) { - case 7: - h ^= (long) (data[(length & ~7) + 6] & 0xff) << 48; - case 6: - h ^= (long) (data[(length & ~7) + 5] & 0xff) << 40; - case 5: - h ^= (long) (data[(length & ~7) + 4] & 0xff) << 32; - case 4: - h ^= (long) (data[(length & ~7) + 3] & 0xff) << 24; - case 3: - h ^= (long) (data[(length & ~7) + 2] & 0xff) << 16; - case 2: - h ^= (long) (data[(length & ~7) + 1] & 0xff) << 8; - case 1: - h ^= data[length & ~7] & 0xff; - h *= m; - } - - h ^= h >>> r; - h *= m; - h ^= h >>> r; - - return h; - } - - /** - * Generates 64 bit hash from byte array with default seed value. - * - * @param data byte array to hash - * @param length length of the array to hash - * @return 64 bit hash of the given string - */ - public static long hash64(final byte[] data, final int length) { - return hash64(data, length, 0xe17a1465); - } - - /** - * Generates 64 bit hash from a string. - * - * @param text string to hash - * @return 64 bit hash of the given string - */ - public static long hash64(final String text) { - final byte[] bytes = text.getBytes(); - return hash64(bytes, bytes.length); - } - - /** - * Generates 64 bit hash from a substring. - * - * @param text string to hash - * @param from starting index - * @param length length of the substring to hash - * @return 64 bit hash of the given array - */ - public static long hash64(final String text, final int from, final int length) { - return hash64(text.substring(from, from + length)); - } + // all methods static; private constructor. + private MurmurHash2() { + } + + /** + * Generates 32 bit hash from byte array of the given length and seed. + * + * @param data byte array to hash + * @param length length of the array to hash + * @param seed initial seed value + * @return 32 bit hash of the given array + */ + public static int hash32(final byte[] data, final int length, final int seed) { + // 'm' and 'r' are mixing constants generated offline. + // They're not really 'magic', they just happen to work well. + final int m = 0x5bd1e995; + final int r = 24; + + // Initialize the hash to a random value + int h = seed ^ length; + final int length4 = length / 4; + + for (int i = 0; i < length4; i++) { + final int i4 = i * 4; + int k = (data[i4 + 0] & 0xff) + ((data[i4 + 1] & 0xff) << 8) + ((data[i4 + 2] & 0xff) << 16) + + ((data[i4 + 3] & 0xff) << 24); + k *= m; + k ^= k >>> r; + k *= m; + h *= m; + h ^= k; + } + + // Handle the last few bytes of the input array + switch (length % 4) { + case 3: + h ^= (data[(length & ~3) + 2] & 0xff) << 16; + case 2: + h ^= (data[(length & ~3) + 1] & 0xff) << 8; + case 1: + h ^= (data[length & ~3] & 0xff); + h *= m; + } + + h ^= h >>> 13; + h *= m; + h ^= h >>> 15; + + return h; + } + + /** + * Generates 32 bit hash from byte array with default seed value. + * + * @param data byte array to hash + * @param length length of the array to hash + * @return 32 bit hash of the given array + */ + public static int hash32(final byte[] data, final int length) { + return hash32(data, length, 0x9747b28c); + } + + /** + * Generates 32 bit hash from a string. + * + * @param text string to hash + * @return 32 bit hash of the given string + */ + public static int hash32(final String text) { + final byte[] bytes = text.getBytes(); + return hash32(bytes, bytes.length); + } + + /** + * Generates 32 bit hash from a substring. + * + * @param text string to hash + * @param from starting index + * @param length length of the substring to hash + * @return 32 bit hash of the given string + */ + public static int hash32(final String text, final int from, final int length) { + return hash32(text.substring(from, from + length)); + } + + /** + * Generates 64 bit hash from byte array of the given length and seed. + * + * @param data byte array to hash + * @param length length of the array to hash + * @param seed initial seed value + * @return 64 bit hash of the given array + */ + public static long hash64(final byte[] data, final int length, final int seed) { + final long m = 0xc6a4a7935bd1e995L; + final int r = 47; + + long h = (seed & 0xffffffffl) ^ (length * m); + + final int length8 = length / 8; + + for (int i = 0; i < length8; i++) { + final int i8 = i * 8; + long k = ((long) data[i8 + 0] & 0xff) + (((long) data[i8 + 1] & 0xff) << 8) + + (((long) data[i8 + 2] & 0xff) << 16) + (((long) data[i8 + 3] & 0xff) << 24) + + (((long) data[i8 + 4] & 0xff) << 32) + (((long) data[i8 + 5] & 0xff) << 40) + + (((long) data[i8 + 6] & 0xff) << 48) + (((long) data[i8 + 7] & 0xff) << 56); + + k *= m; + k ^= k >>> r; + k *= m; + + h ^= k; + h *= m; + } + + switch (length % 8) { + case 7: + h ^= (long) (data[(length & ~7) + 6] & 0xff) << 48; + case 6: + h ^= (long) (data[(length & ~7) + 5] & 0xff) << 40; + case 5: + h ^= (long) (data[(length & ~7) + 4] & 0xff) << 32; + case 4: + h ^= (long) (data[(length & ~7) + 3] & 0xff) << 24; + case 3: + h ^= (long) (data[(length & ~7) + 2] & 0xff) << 16; + case 2: + h ^= (long) (data[(length & ~7) + 1] & 0xff) << 8; + case 1: + h ^= data[length & ~7] & 0xff; + h *= m; + } + + h ^= h >>> r; + h *= m; + h ^= h >>> r; + + return h; + } + + /** + * Generates 64 bit hash from byte array with default seed value. + * + * @param data byte array to hash + * @param length length of the array to hash + * @return 64 bit hash of the given string + */ + public static long hash64(final byte[] data, final int length) { + return hash64(data, length, 0xe17a1465); + } + + /** + * Generates 64 bit hash from a string. + * + * @param text string to hash + * @return 64 bit hash of the given string + */ + public static long hash64(final String text) { + final byte[] bytes = text.getBytes(); + return hash64(bytes, bytes.length); + } + + /** + * Generates 64 bit hash from a substring. + * + * @param text string to hash + * @param from starting index + * @param length length of the substring to hash + * @return 64 bit hash of the given array + */ + public static long hash64(final String text, final int from, final int length) { + return hash64(text.substring(from, from + length)); + } } diff --git a/src/main/java/org/apache/commons/codec/digest/MurmurHash3.java b/src/main/java/org/apache/commons/codec/digest/MurmurHash3.java index df46433..6a276f9 100644 --- a/src/main/java/org/apache/commons/codec/digest/MurmurHash3.java +++ b/src/main/java/org/apache/commons/codec/digest/MurmurHash3.java @@ -43,594 +43,594 @@ package org.apache.commons.codec.digest; */ public final class MurmurHash3 { - /** TODO Replace on Java 8 with Long.BYTES. */ - static final int LONG_BYTES = Long.SIZE / Byte.SIZE; - - /** TODO Replace on Java 8 with Integer.BYTES. */ - static final int INTEGER_BYTES = Integer.SIZE / Byte.SIZE; - - /** TODO Replace on Java 8 with Short.BYTES. */ - static final int SHORT_BYTES = Short.SIZE / Byte.SIZE; - - // from 64-bit linear congruential generator - public static final long NULL_HASHCODE = 2862933555777941757L; - - // Constants for 32 bit variant - private static final int C1_32 = 0xcc9e2d51; - private static final int C2_32 = 0x1b873593; - private static final int R1_32 = 15; - private static final int R2_32 = 13; - private static final int M_32 = 5; - private static final int N_32 = 0xe6546b64; - - // Constants for 128 bit variant - private static final long C1 = 0x87c37b91114253d5L; - private static final long C2 = 0x4cf5ad432745937fL; - private static final int R1 = 31; - private static final int R2 = 27; - private static final int R3 = 33; - private static final int M = 5; - private static final int N1 = 0x52dce729; - private static final int N2 = 0x38495ab5; - - public static final int DEFAULT_SEED = 104729; - - // all methods static; private constructor. - private MurmurHash3() { - } - - /** - * Generates 32 bit hash from two longs with default seed value. - * - * @param l0 long to hash - * @param l1 long to hash - * @return 32 bit hash - */ - public static int hash32(final long l0, final long l1) { - return hash32(l0, l1, DEFAULT_SEED); - } - - /** - * Generates 32 bit hash from a long with default seed value. - * - * @param l0 long to hash - * @return 32 bit hash - */ - public static int hash32(final long l0) { - return hash32(l0, DEFAULT_SEED); - } - - /** - * Generates 32 bit hash from a long with the given seed. - * - * @param l0 long to hash - * @param seed initial seed value - * @return 32 bit hash - */ - public static int hash32(final long l0, final int seed) { - int hash = seed; - final long r0 = Long.reverseBytes(l0); - - hash = mix32((int) r0, hash); - hash = mix32((int) (r0 >>> 32), hash); - - return fmix32(LONG_BYTES, hash); - } - - /** - * Generates 32 bit hash from two longs with the given seed. - * - * @param l0 long to hash - * @param l1 long to hash - * @param seed initial seed value - * @return 32 bit hash - */ - public static int hash32(final long l0, final long l1, final int seed) { - int hash = seed; - final long r0 = Long.reverseBytes(l0); - final long r1 = Long.reverseBytes(l1); - - hash = mix32((int) r0, hash); - hash = mix32((int) (r0 >>> 32), hash); - hash = mix32((int) (r1), hash); - hash = mix32((int) (r1 >>> 32), hash); - - return fmix32(LONG_BYTES * 2, hash); - } - - /** - * Generates 32 bit hash from byte array with the default seed. - * - * @param data - input byte array - * @return 32 bit hash - */ - public static int hash32(final byte[] data) { - return hash32(data, 0, data.length, DEFAULT_SEED); - } - - /** - * Generates 32 bit hash from a string with the default seed. - * - * @param data - input string - * @return 32 bit hash - */ - public static int hash32(final String data) { - final byte[] origin = data.getBytes(); - return hash32(origin, 0, origin.length, DEFAULT_SEED); - } - - /** - * Generates 32 bit hash from byte array with the default seed. - * - * @param data - input byte array - * @param length - length of array - * @return 32 bit hash - */ - public static int hash32(final byte[] data, final int length) { - return hash32(data, length, DEFAULT_SEED); - } - - /** - * Generates 32 bit hash from byte array with the given length and seed. - * - * @param data - input byte array - * @param length - length of array - * @param seed - seed. (default 0) - * @return 32 bit hash - */ - public static int hash32(final byte[] data, final int length, final int seed) { - return hash32(data, 0, length, seed); - } - - /** - * Generates 32 bit hash from byte array with the given length, offset and seed. - * - * @param data - input byte array - * @param offset - offset of data - * @param length - length of array - * @param seed - seed. (default 0) - * @return 32 bit hash - */ - public static int hash32(final byte[] data, final int offset, final int length, final int seed) { - int hash = seed; - final int nblocks = length >> 2; - - // body - for (int i = 0; i < nblocks; i++) { - final int i_4 = i << 2; - final int k = (data[offset + i_4] & 0xff) | ((data[offset + i_4 + 1] & 0xff) << 8) - | ((data[offset + i_4 + 2] & 0xff) << 16) | ((data[offset + i_4 + 3] & 0xff) << 24); - - hash = mix32(k, hash); - } - - // tail - final int idx = nblocks << 2; - int k1 = 0; - switch (length - idx) { - case 3: - k1 ^= data[offset + idx + 2] << 16; - case 2: - k1 ^= data[offset + idx + 1] << 8; - case 1: - k1 ^= data[offset + idx]; - - // mix functions - k1 *= C1_32; - k1 = Integer.rotateLeft(k1, R1_32); - k1 *= C2_32; - hash ^= k1; - } - - return fmix32(length, hash); - } - - /** - * Murmur3 64-bit variant. This is essentially MSB 8 bytes of Murmur3 128-bit - * variant. - * - * @param data - input byte array - * @return 64 bit hash - */ - public static long hash64(final byte[] data) { - return hash64(data, 0, data.length, DEFAULT_SEED); - } - - /** - * Murmur3 64-bit variant. This is essentially MSB 8 bytes of Murmur3 128-bit - * variant. - * - * @param data - input long - * @return 64 bit hash - */ - public static long hash64(final long data) { - long hash = DEFAULT_SEED; - long k = Long.reverseBytes(data); - final int length = LONG_BYTES; - // mix functions - k *= C1; - k = Long.rotateLeft(k, R1); - k *= C2; - hash ^= k; - hash = Long.rotateLeft(hash, R2) * M + N1; - // finalization - hash ^= length; - hash = fmix64(hash); - return hash; - } - - /** - * Murmur3 64-bit variant. This is essentially MSB 8 bytes of Murmur3 128-bit - * variant. - * - * @param data - input int - * @return 64 bit hash - */ - public static long hash64(final int data) { - long k1 = Integer.reverseBytes(data) & (-1L >>> 32); - final int length = INTEGER_BYTES; - long hash = DEFAULT_SEED; - k1 *= C1; - k1 = Long.rotateLeft(k1, R1); - k1 *= C2; - hash ^= k1; - // finalization - hash ^= length; - hash = fmix64(hash); - return hash; - } - - /** - * Murmur3 64-bit variant. This is essentially MSB 8 bytes of Murmur3 128-bit - * variant. - * - * @param data - input short - * @return 64 bit hash - */ - public static long hash64(final short data) { - long hash = DEFAULT_SEED; - long k1 = 0; - k1 ^= ((long) data & 0xff) << 8; - k1 ^= ((long) ((data & 0xFF00) >> 8) & 0xff); - k1 *= C1; - k1 = Long.rotateLeft(k1, R1); - k1 *= C2; - hash ^= k1; - - // finalization - hash ^= SHORT_BYTES; - hash = fmix64(hash); - return hash; - } - - /** - * Generates 64 bit hash from byte array with the given length, offset and - * default seed. - * - * @param data - input byte array - * @param offset - offset of data - * @param length - length of array - * @return 64 bit hash - */ - public static long hash64(final byte[] data, final int offset, final int length) { - return hash64(data, offset, length, DEFAULT_SEED); - } - - /** - * Generates 64 bit hash from byte array with the given length, offset and seed. - * - * @param data - input byte array - * @param offset - offset of data - * @param length - length of array - * @param seed - seed. (default 0) - * @return 64 bit hash - */ - public static long hash64(final byte[] data, final int offset, final int length, final int seed) { - long hash = seed; - final int nblocks = length >> 3; - - // body - for (int i = 0; i < nblocks; i++) { - final int i8 = i << 3; - long k = ((long) data[offset + i8] & 0xff) | (((long) data[offset + i8 + 1] & 0xff) << 8) - | (((long) data[offset + i8 + 2] & 0xff) << 16) | (((long) data[offset + i8 + 3] & 0xff) << 24) - | (((long) data[offset + i8 + 4] & 0xff) << 32) | (((long) data[offset + i8 + 5] & 0xff) << 40) - | (((long) data[offset + i8 + 6] & 0xff) << 48) | (((long) data[offset + i8 + 7] & 0xff) << 56); - - // mix functions - k *= C1; - k = Long.rotateLeft(k, R1); - k *= C2; - hash ^= k; - hash = Long.rotateLeft(hash, R2) * M + N1; - } - - // tail - long k1 = 0; - final int tailStart = nblocks << 3; - switch (length - tailStart) { - case 7: - k1 ^= ((long) data[offset + tailStart + 6] & 0xff) << 48; - case 6: - k1 ^= ((long) data[offset + tailStart + 5] & 0xff) << 40; - case 5: - k1 ^= ((long) data[offset + tailStart + 4] & 0xff) << 32; - case 4: - k1 ^= ((long) data[offset + tailStart + 3] & 0xff) << 24; - case 3: - k1 ^= ((long) data[offset + tailStart + 2] & 0xff) << 16; - case 2: - k1 ^= ((long) data[offset + tailStart + 1] & 0xff) << 8; - case 1: - k1 ^= ((long) data[offset + tailStart] & 0xff); - k1 *= C1; - k1 = Long.rotateLeft(k1, R1); - k1 *= C2; - hash ^= k1; - } - - // finalization - hash ^= length; - hash = fmix64(hash); - - return hash; - } - - /** - * Murmur3 128-bit variant. - * - * @param data - input byte array - * @return - 128 bit hash (2 longs) - */ - public static long[] hash128(final byte[] data) { - return hash128(data, 0, data.length, DEFAULT_SEED); - } - - /** - * Murmur3 128-bit variant. - * - * @param data - input String - * @return - 128 bit hash (2 longs) - */ - public static long[] hash128(final String data) { - final byte[] origin = data.getBytes(); - return hash128(origin, 0, origin.length, DEFAULT_SEED); - } - - /** - * Murmur3 128-bit variant. - * - * @param data - input byte array - * @param offset - the first element of array - * @param length - length of array - * @param seed - seed. (default is 0) - * @return - 128 bit hash (2 longs) - */ - public static long[] hash128(final byte[] data, final int offset, final int length, final int seed) { - long h1 = seed; - long h2 = seed; - final int nblocks = length >> 4; - - // body - for (int i = 0; i < nblocks; i++) { - final int i16 = i << 4; - long k1 = ((long) data[offset + i16] & 0xff) | (((long) data[offset + i16 + 1] & 0xff) << 8) - | (((long) data[offset + i16 + 2] & 0xff) << 16) | (((long) data[offset + i16 + 3] & 0xff) << 24) - | (((long) data[offset + i16 + 4] & 0xff) << 32) | (((long) data[offset + i16 + 5] & 0xff) << 40) - | (((long) data[offset + i16 + 6] & 0xff) << 48) | (((long) data[offset + i16 + 7] & 0xff) << 56); - - long k2 = ((long) data[offset + i16 + 8] & 0xff) | (((long) data[offset + i16 + 9] & 0xff) << 8) - | (((long) data[offset + i16 + 10] & 0xff) << 16) | (((long) data[offset + i16 + 11] & 0xff) << 24) - | (((long) data[offset + i16 + 12] & 0xff) << 32) | (((long) data[offset + i16 + 13] & 0xff) << 40) - | (((long) data[offset + i16 + 14] & 0xff) << 48) | (((long) data[offset + i16 + 15] & 0xff) << 56); - - // mix functions for k1 - k1 *= C1; - k1 = Long.rotateLeft(k1, R1); - k1 *= C2; - h1 ^= k1; - h1 = Long.rotateLeft(h1, R2); - h1 += h2; - h1 = h1 * M + N1; - - // mix functions for k2 - k2 *= C2; - k2 = Long.rotateLeft(k2, R3); - k2 *= C1; - h2 ^= k2; - h2 = Long.rotateLeft(h2, R1); - h2 += h1; - h2 = h2 * M + N2; - } - - // tail - long k1 = 0; - long k2 = 0; - final int tailStart = nblocks << 4; - switch (length - tailStart) { - case 15: - k2 ^= (long) (data[offset + tailStart + 14] & 0xff) << 48; - case 14: - k2 ^= (long) (data[offset + tailStart + 13] & 0xff) << 40; - case 13: - k2 ^= (long) (data[offset + tailStart + 12] & 0xff) << 32; - case 12: - k2 ^= (long) (data[offset + tailStart + 11] & 0xff) << 24; - case 11: - k2 ^= (long) (data[offset + tailStart + 10] & 0xff) << 16; - case 10: - k2 ^= (long) (data[offset + tailStart + 9] & 0xff) << 8; - case 9: - k2 ^= data[offset + tailStart + 8] & 0xff; - k2 *= C2; - k2 = Long.rotateLeft(k2, R3); - k2 *= C1; - h2 ^= k2; - - case 8: - k1 ^= (long) (data[offset + tailStart + 7] & 0xff) << 56; - case 7: - k1 ^= (long) (data[offset + tailStart + 6] & 0xff) << 48; - case 6: - k1 ^= (long) (data[offset + tailStart + 5] & 0xff) << 40; - case 5: - k1 ^= (long) (data[offset + tailStart + 4] & 0xff) << 32; - case 4: - k1 ^= (long) (data[offset + tailStart + 3] & 0xff) << 24; - case 3: - k1 ^= (long) (data[offset + tailStart + 2] & 0xff) << 16; - case 2: - k1 ^= (long) (data[offset + tailStart + 1] & 0xff) << 8; - case 1: - k1 ^= data[offset + tailStart] & 0xff; - k1 *= C1; - k1 = Long.rotateLeft(k1, R1); - k1 *= C2; - h1 ^= k1; - } - - // finalization - h1 ^= length; - h2 ^= length; - - h1 += h2; - h2 += h1; - - h1 = fmix64(h1); - h2 = fmix64(h2); - - h1 += h2; - h2 += h1; - - return new long[] { h1, h2 }; - } - - private static int mix32(int k, int hash) { - k *= C1_32; - k = Integer.rotateLeft(k, R1_32); - k *= C2_32; - hash ^= k; - return Integer.rotateLeft(hash, R2_32) * M_32 + N_32; - } - - private static int fmix32(final int length, int hash) { - hash ^= length; - hash ^= (hash >>> 16); - hash *= 0x85ebca6b; - hash ^= (hash >>> 13); - hash *= 0xc2b2ae35; - hash ^= (hash >>> 16); - - return hash; - } - - private static long fmix64(long h) { - h ^= (h >>> 33); - h *= 0xff51afd7ed558ccdL; - h ^= (h >>> 33); - h *= 0xc4ceb9fe1a85ec53L; - h ^= (h >>> 33); - return h; - } - - public static class IncrementalHash32 { - byte[] tail = new byte[3]; - int tailLen; - int totalLen; - int hash; - - public final void start(final int hash) { - tailLen = totalLen = 0; - this.hash = hash; - } - - public final void add(final byte[] data, int offset, final int length) { - if (length == 0) { + /** TODO Replace on Java 8 with Long.BYTES. */ + static final int LONG_BYTES = Long.SIZE / Byte.SIZE; + + /** TODO Replace on Java 8 with Integer.BYTES. */ + static final int INTEGER_BYTES = Integer.SIZE / Byte.SIZE; + + /** TODO Replace on Java 8 with Short.BYTES. */ + static final int SHORT_BYTES = Short.SIZE / Byte.SIZE; + + // from 64-bit linear congruential generator + public static final long NULL_HASHCODE = 2862933555777941757L; + + // Constants for 32 bit variant + private static final int C1_32 = 0xcc9e2d51; + private static final int C2_32 = 0x1b873593; + private static final int R1_32 = 15; + private static final int R2_32 = 13; + private static final int M_32 = 5; + private static final int N_32 = 0xe6546b64; + + // Constants for 128 bit variant + private static final long C1 = 0x87c37b91114253d5L; + private static final long C2 = 0x4cf5ad432745937fL; + private static final int R1 = 31; + private static final int R2 = 27; + private static final int R3 = 33; + private static final int M = 5; + private static final int N1 = 0x52dce729; + private static final int N2 = 0x38495ab5; + + public static final int DEFAULT_SEED = 104729; + + // all methods static; private constructor. + private MurmurHash3() { + } + + /** + * Generates 32 bit hash from two longs with default seed value. + * + * @param l0 long to hash + * @param l1 long to hash + * @return 32 bit hash + */ + public static int hash32(final long l0, final long l1) { + return hash32(l0, l1, DEFAULT_SEED); + } + + /** + * Generates 32 bit hash from a long with default seed value. + * + * @param l0 long to hash + * @return 32 bit hash + */ + public static int hash32(final long l0) { + return hash32(l0, DEFAULT_SEED); + } + + /** + * Generates 32 bit hash from a long with the given seed. + * + * @param l0 long to hash + * @param seed initial seed value + * @return 32 bit hash + */ + public static int hash32(final long l0, final int seed) { + int hash = seed; + final long r0 = Long.reverseBytes(l0); + + hash = mix32((int) r0, hash); + hash = mix32((int) (r0 >>> 32), hash); + + return fmix32(LONG_BYTES, hash); + } + + /** + * Generates 32 bit hash from two longs with the given seed. + * + * @param l0 long to hash + * @param l1 long to hash + * @param seed initial seed value + * @return 32 bit hash + */ + public static int hash32(final long l0, final long l1, final int seed) { + int hash = seed; + final long r0 = Long.reverseBytes(l0); + final long r1 = Long.reverseBytes(l1); + + hash = mix32((int) r0, hash); + hash = mix32((int) (r0 >>> 32), hash); + hash = mix32((int) (r1), hash); + hash = mix32((int) (r1 >>> 32), hash); + + return fmix32(LONG_BYTES * 2, hash); + } + + /** + * Generates 32 bit hash from byte array with the default seed. + * + * @param data - input byte array + * @return 32 bit hash + */ + public static int hash32(final byte[] data) { + return hash32(data, 0, data.length, DEFAULT_SEED); + } + + /** + * Generates 32 bit hash from a string with the default seed. + * + * @param data - input string + * @return 32 bit hash + */ + public static int hash32(final String data) { + final byte[] origin = data.getBytes(); + return hash32(origin, 0, origin.length, DEFAULT_SEED); + } + + /** + * Generates 32 bit hash from byte array with the default seed. + * + * @param data - input byte array + * @param length - length of array + * @return 32 bit hash + */ + public static int hash32(final byte[] data, final int length) { + return hash32(data, length, DEFAULT_SEED); + } + + /** + * Generates 32 bit hash from byte array with the given length and seed. + * + * @param data - input byte array + * @param length - length of array + * @param seed - seed. (default 0) + * @return 32 bit hash + */ + public static int hash32(final byte[] data, final int length, final int seed) { + return hash32(data, 0, length, seed); + } + + /** + * Generates 32 bit hash from byte array with the given length, offset and seed. + * + * @param data - input byte array + * @param offset - offset of data + * @param length - length of array + * @param seed - seed. (default 0) + * @return 32 bit hash + */ + public static int hash32(final byte[] data, final int offset, final int length, final int seed) { + int hash = seed; + final int nblocks = length >> 2; + + // body + for (int i = 0; i < nblocks; i++) { + final int i_4 = i << 2; + final int k = (data[offset + i_4] & 0xff) | ((data[offset + i_4 + 1] & 0xff) << 8) + | ((data[offset + i_4 + 2] & 0xff) << 16) | ((data[offset + i_4 + 3] & 0xff) << 24); + + hash = mix32(k, hash); + } + + // tail + final int idx = nblocks << 2; + int k1 = 0; + switch (length - idx) { + case 3: + k1 ^= data[offset + idx + 2] << 16; + case 2: + k1 ^= data[offset + idx + 1] << 8; + case 1: + k1 ^= data[offset + idx]; + + // mix functions + k1 *= C1_32; + k1 = Integer.rotateLeft(k1, R1_32); + k1 *= C2_32; + hash ^= k1; + } + + return fmix32(length, hash); + } + + /** + * Murmur3 64-bit variant. This is essentially MSB 8 bytes of Murmur3 128-bit + * variant. + * + * @param data - input byte array + * @return 64 bit hash + */ + public static long hash64(final byte[] data) { + return hash64(data, 0, data.length, DEFAULT_SEED); + } + + /** + * Murmur3 64-bit variant. This is essentially MSB 8 bytes of Murmur3 128-bit + * variant. + * + * @param data - input long + * @return 64 bit hash + */ + public static long hash64(final long data) { + long hash = DEFAULT_SEED; + long k = Long.reverseBytes(data); + final int length = LONG_BYTES; + // mix functions + k *= C1; + k = Long.rotateLeft(k, R1); + k *= C2; + hash ^= k; + hash = Long.rotateLeft(hash, R2) * M + N1; + // finalization + hash ^= length; + hash = fmix64(hash); + return hash; + } + + /** + * Murmur3 64-bit variant. This is essentially MSB 8 bytes of Murmur3 128-bit + * variant. + * + * @param data - input int + * @return 64 bit hash + */ + public static long hash64(final int data) { + long k1 = Integer.reverseBytes(data) & (-1L >>> 32); + final int length = INTEGER_BYTES; + long hash = DEFAULT_SEED; + k1 *= C1; + k1 = Long.rotateLeft(k1, R1); + k1 *= C2; + hash ^= k1; + // finalization + hash ^= length; + hash = fmix64(hash); + return hash; + } + + /** + * Murmur3 64-bit variant. This is essentially MSB 8 bytes of Murmur3 128-bit + * variant. + * + * @param data - input short + * @return 64 bit hash + */ + public static long hash64(final short data) { + long hash = DEFAULT_SEED; + long k1 = 0; + k1 ^= ((long) data & 0xff) << 8; + k1 ^= ((long) ((data & 0xFF00) >> 8) & 0xff); + k1 *= C1; + k1 = Long.rotateLeft(k1, R1); + k1 *= C2; + hash ^= k1; + + // finalization + hash ^= SHORT_BYTES; + hash = fmix64(hash); + return hash; + } + + /** + * Generates 64 bit hash from byte array with the given length, offset and + * default seed. + * + * @param data - input byte array + * @param offset - offset of data + * @param length - length of array + * @return 64 bit hash + */ + public static long hash64(final byte[] data, final int offset, final int length) { + return hash64(data, offset, length, DEFAULT_SEED); + } + + /** + * Generates 64 bit hash from byte array with the given length, offset and seed. + * + * @param data - input byte array + * @param offset - offset of data + * @param length - length of array + * @param seed - seed. (default 0) + * @return 64 bit hash + */ + public static long hash64(final byte[] data, final int offset, final int length, final int seed) { + long hash = seed; + final int nblocks = length >> 3; + + // body + for (int i = 0; i < nblocks; i++) { + final int i8 = i << 3; + long k = ((long) data[offset + i8] & 0xff) | (((long) data[offset + i8 + 1] & 0xff) << 8) + | (((long) data[offset + i8 + 2] & 0xff) << 16) | (((long) data[offset + i8 + 3] & 0xff) << 24) + | (((long) data[offset + i8 + 4] & 0xff) << 32) | (((long) data[offset + i8 + 5] & 0xff) << 40) + | (((long) data[offset + i8 + 6] & 0xff) << 48) | (((long) data[offset + i8 + 7] & 0xff) << 56); + + // mix functions + k *= C1; + k = Long.rotateLeft(k, R1); + k *= C2; + hash ^= k; + hash = Long.rotateLeft(hash, R2) * M + N1; + } + + // tail + long k1 = 0; + final int tailStart = nblocks << 3; + switch (length - tailStart) { + case 7: + k1 ^= ((long) data[offset + tailStart + 6] & 0xff) << 48; + case 6: + k1 ^= ((long) data[offset + tailStart + 5] & 0xff) << 40; + case 5: + k1 ^= ((long) data[offset + tailStart + 4] & 0xff) << 32; + case 4: + k1 ^= ((long) data[offset + tailStart + 3] & 0xff) << 24; + case 3: + k1 ^= ((long) data[offset + tailStart + 2] & 0xff) << 16; + case 2: + k1 ^= ((long) data[offset + tailStart + 1] & 0xff) << 8; + case 1: + k1 ^= ((long) data[offset + tailStart] & 0xff); + k1 *= C1; + k1 = Long.rotateLeft(k1, R1); + k1 *= C2; + hash ^= k1; + } + + // finalization + hash ^= length; + hash = fmix64(hash); + + return hash; + } + + /** + * Murmur3 128-bit variant. + * + * @param data - input byte array + * @return - 128 bit hash (2 longs) + */ + public static long[] hash128(final byte[] data) { + return hash128(data, 0, data.length, DEFAULT_SEED); + } + + /** + * Murmur3 128-bit variant. + * + * @param data - input String + * @return - 128 bit hash (2 longs) + */ + public static long[] hash128(final String data) { + final byte[] origin = data.getBytes(); + return hash128(origin, 0, origin.length, DEFAULT_SEED); + } + + /** + * Murmur3 128-bit variant. + * + * @param data - input byte array + * @param offset - the first element of array + * @param length - length of array + * @param seed - seed. (default is 0) + * @return - 128 bit hash (2 longs) + */ + public static long[] hash128(final byte[] data, final int offset, final int length, final int seed) { + long h1 = seed; + long h2 = seed; + final int nblocks = length >> 4; + + // body + for (int i = 0; i < nblocks; i++) { + final int i16 = i << 4; + long k1 = ((long) data[offset + i16] & 0xff) | (((long) data[offset + i16 + 1] & 0xff) << 8) + | (((long) data[offset + i16 + 2] & 0xff) << 16) | (((long) data[offset + i16 + 3] & 0xff) << 24) + | (((long) data[offset + i16 + 4] & 0xff) << 32) | (((long) data[offset + i16 + 5] & 0xff) << 40) + | (((long) data[offset + i16 + 6] & 0xff) << 48) | (((long) data[offset + i16 + 7] & 0xff) << 56); + + long k2 = ((long) data[offset + i16 + 8] & 0xff) | (((long) data[offset + i16 + 9] & 0xff) << 8) + | (((long) data[offset + i16 + 10] & 0xff) << 16) | (((long) data[offset + i16 + 11] & 0xff) << 24) + | (((long) data[offset + i16 + 12] & 0xff) << 32) | (((long) data[offset + i16 + 13] & 0xff) << 40) + | (((long) data[offset + i16 + 14] & 0xff) << 48) | (((long) data[offset + i16 + 15] & 0xff) << 56); + + // mix functions for k1 + k1 *= C1; + k1 = Long.rotateLeft(k1, R1); + k1 *= C2; + h1 ^= k1; + h1 = Long.rotateLeft(h1, R2); + h1 += h2; + h1 = h1 * M + N1; + + // mix functions for k2 + k2 *= C2; + k2 = Long.rotateLeft(k2, R3); + k2 *= C1; + h2 ^= k2; + h2 = Long.rotateLeft(h2, R1); + h2 += h1; + h2 = h2 * M + N2; + } + + // tail + long k1 = 0; + long k2 = 0; + final int tailStart = nblocks << 4; + switch (length - tailStart) { + case 15: + k2 ^= (long) (data[offset + tailStart + 14] & 0xff) << 48; + case 14: + k2 ^= (long) (data[offset + tailStart + 13] & 0xff) << 40; + case 13: + k2 ^= (long) (data[offset + tailStart + 12] & 0xff) << 32; + case 12: + k2 ^= (long) (data[offset + tailStart + 11] & 0xff) << 24; + case 11: + k2 ^= (long) (data[offset + tailStart + 10] & 0xff) << 16; + case 10: + k2 ^= (long) (data[offset + tailStart + 9] & 0xff) << 8; + case 9: + k2 ^= data[offset + tailStart + 8] & 0xff; + k2 *= C2; + k2 = Long.rotateLeft(k2, R3); + k2 *= C1; + h2 ^= k2; + + case 8: + k1 ^= (long) (data[offset + tailStart + 7] & 0xff) << 56; + case 7: + k1 ^= (long) (data[offset + tailStart + 6] & 0xff) << 48; + case 6: + k1 ^= (long) (data[offset + tailStart + 5] & 0xff) << 40; + case 5: + k1 ^= (long) (data[offset + tailStart + 4] & 0xff) << 32; + case 4: + k1 ^= (long) (data[offset + tailStart + 3] & 0xff) << 24; + case 3: + k1 ^= (long) (data[offset + tailStart + 2] & 0xff) << 16; + case 2: + k1 ^= (long) (data[offset + tailStart + 1] & 0xff) << 8; + case 1: + k1 ^= data[offset + tailStart] & 0xff; + k1 *= C1; + k1 = Long.rotateLeft(k1, R1); + k1 *= C2; + h1 ^= k1; + } + + // finalization + h1 ^= length; + h2 ^= length; + + h1 += h2; + h2 += h1; + + h1 = fmix64(h1); + h2 = fmix64(h2); + + h1 += h2; + h2 += h1; + + return new long[] { h1, h2 }; + } + + private static int mix32(int k, int hash) { + k *= C1_32; + k = Integer.rotateLeft(k, R1_32); + k *= C2_32; + hash ^= k; + return Integer.rotateLeft(hash, R2_32) * M_32 + N_32; + } + + private static int fmix32(final int length, int hash) { + hash ^= length; + hash ^= (hash >>> 16); + hash *= 0x85ebca6b; + hash ^= (hash >>> 13); + hash *= 0xc2b2ae35; + hash ^= (hash >>> 16); + + return hash; + } + + private static long fmix64(long h) { + h ^= (h >>> 33); + h *= 0xff51afd7ed558ccdL; + h ^= (h >>> 33); + h *= 0xc4ceb9fe1a85ec53L; + h ^= (h >>> 33); + return h; + } + + public static class IncrementalHash32 { + byte[] tail = new byte[3]; + int tailLen; + int totalLen; + int hash; + + public final void start(final int hash) { + tailLen = totalLen = 0; + this.hash = hash; + } + + public final void add(final byte[] data, int offset, final int length) { + if (length == 0) { return; } - totalLen += length; - if (tailLen + length < 4) { - System.arraycopy(data, offset, tail, tailLen, length); - tailLen += length; - return; - } - int offset2 = 0; - if (tailLen > 0) { - offset2 = (4 - tailLen); - int k = -1; - switch (tailLen) { - case 1: - k = orBytes(tail[0], data[offset], data[offset + 1], data[offset + 2]); - break; - case 2: - k = orBytes(tail[0], tail[1], data[offset], data[offset + 1]); - break; - case 3: - k = orBytes(tail[0], tail[1], tail[2], data[offset]); - break; - default: - throw new AssertionError(tailLen); - } - // mix functions - k *= C1_32; - k = Integer.rotateLeft(k, R1_32); - k *= C2_32; - hash ^= k; - hash = Integer.rotateLeft(hash, R2_32) * M_32 + N_32; - } - final int length2 = length - offset2; - offset += offset2; - final int nblocks = length2 >> 2; - - for (int i = 0; i < nblocks; i++) { - final int i_4 = (i << 2) + offset; - int k = orBytes(data[i_4], data[i_4 + 1], data[i_4 + 2], data[i_4 + 3]); - - // mix functions - k *= C1_32; - k = Integer.rotateLeft(k, R1_32); - k *= C2_32; - hash ^= k; - hash = Integer.rotateLeft(hash, R2_32) * M_32 + N_32; - } - - final int consumed = (nblocks << 2); - tailLen = length2 - consumed; - if (consumed == length2) { + totalLen += length; + if (tailLen + length < 4) { + System.arraycopy(data, offset, tail, tailLen, length); + tailLen += length; return; } - System.arraycopy(data, offset + consumed, tail, 0, tailLen); - } - - public final int end() { - int k1 = 0; - switch (tailLen) { - case 3: - k1 ^= tail[2] << 16; - case 2: - k1 ^= tail[1] << 8; - case 1: - k1 ^= tail[0]; - - // mix functions - k1 *= C1_32; - k1 = Integer.rotateLeft(k1, R1_32); - k1 *= C2_32; - hash ^= k1; - } - - // finalization - hash ^= totalLen; - hash ^= (hash >>> 16); - hash *= 0x85ebca6b; - hash ^= (hash >>> 13); - hash *= 0xc2b2ae35; - hash ^= (hash >>> 16); - return hash; - } - } - - private static int orBytes(final byte b1, final byte b2, final byte b3, final byte b4) { - return (b1 & 0xff) | ((b2 & 0xff) << 8) | ((b3 & 0xff) << 16) | ((b4 & 0xff) << 24); - } + int offset2 = 0; + if (tailLen > 0) { + offset2 = (4 - tailLen); + int k = -1; + switch (tailLen) { + case 1: + k = orBytes(tail[0], data[offset], data[offset + 1], data[offset + 2]); + break; + case 2: + k = orBytes(tail[0], tail[1], data[offset], data[offset + 1]); + break; + case 3: + k = orBytes(tail[0], tail[1], tail[2], data[offset]); + break; + default: + throw new AssertionError(tailLen); + } + // mix functions + k *= C1_32; + k = Integer.rotateLeft(k, R1_32); + k *= C2_32; + hash ^= k; + hash = Integer.rotateLeft(hash, R2_32) * M_32 + N_32; + } + final int length2 = length - offset2; + offset += offset2; + final int nblocks = length2 >> 2; + + for (int i = 0; i < nblocks; i++) { + final int i_4 = (i << 2) + offset; + int k = orBytes(data[i_4], data[i_4 + 1], data[i_4 + 2], data[i_4 + 3]); + + // mix functions + k *= C1_32; + k = Integer.rotateLeft(k, R1_32); + k *= C2_32; + hash ^= k; + hash = Integer.rotateLeft(hash, R2_32) * M_32 + N_32; + } + + final int consumed = (nblocks << 2); + tailLen = length2 - consumed; + if (consumed == length2) { + return; + } + System.arraycopy(data, offset + consumed, tail, 0, tailLen); + } + + public final int end() { + int k1 = 0; + switch (tailLen) { + case 3: + k1 ^= tail[2] << 16; + case 2: + k1 ^= tail[1] << 8; + case 1: + k1 ^= tail[0]; + + // mix functions + k1 *= C1_32; + k1 = Integer.rotateLeft(k1, R1_32); + k1 *= C2_32; + hash ^= k1; + } + + // finalization + hash ^= totalLen; + hash ^= (hash >>> 16); + hash *= 0x85ebca6b; + hash ^= (hash >>> 13); + hash *= 0xc2b2ae35; + hash ^= (hash >>> 16); + return hash; + } + } + + private static int orBytes(final byte b1, final byte b2, final byte b3, final byte b4) { + return (b1 & 0xff) | ((b2 & 0xff) << 8) | ((b3 & 0xff) << 16) | ((b4 & 0xff) << 24); + } } diff --git a/src/test/java/org/apache/commons/codec/digest/MurmurHash2Test.java b/src/test/java/org/apache/commons/codec/digest/MurmurHash2Test.java index 884bee6..a121909 100644 --- a/src/test/java/org/apache/commons/codec/digest/MurmurHash2Test.java +++ b/src/test/java/org/apache/commons/codec/digest/MurmurHash2Test.java @@ -24,124 +24,124 @@ import org.junit.Test; public class MurmurHash2Test { - /** Random input data with various length. */ - static final byte[][] input = { - { (byte) 0xed, (byte) 0x53, (byte) 0xc4, (byte) 0xa5, (byte) 0x3b, (byte) 0x1b, (byte) 0xbd, (byte) 0xc2, - (byte) 0x52, (byte) 0x7d, (byte) 0xc3, (byte) 0xef, (byte) 0x53, (byte) 0x5f, (byte) 0xae, - (byte) 0x3b }, - { (byte) 0x21, (byte) 0x65, (byte) 0x59, (byte) 0x4e, (byte) 0xd8, (byte) 0x12, (byte) 0xf9, (byte) 0x05, - (byte) 0x80, (byte) 0xe9, (byte) 0x1e, (byte) 0xed, (byte) 0xe4, (byte) 0x56, (byte) 0xbb }, - { (byte) 0x2b, (byte) 0x02, (byte) 0xb1, (byte) 0xd0, (byte) 0x3d, (byte) 0xce, (byte) 0x31, (byte) 0x3d, - (byte) 0x97, (byte) 0xc4, (byte) 0x91, (byte) 0x0d, (byte) 0xf7, (byte) 0x17 }, - { (byte) 0x8e, (byte) 0xa7, (byte) 0x9a, (byte) 0x02, (byte) 0xe8, (byte) 0xb9, (byte) 0x6a, (byte) 0xda, - (byte) 0x92, (byte) 0xad, (byte) 0xe9, (byte) 0x2d, (byte) 0x21 }, - { (byte) 0xa9, (byte) 0x6d, (byte) 0xea, (byte) 0x77, (byte) 0x06, (byte) 0xce, (byte) 0x1b, (byte) 0x85, - (byte) 0x48, (byte) 0x27, (byte) 0x4c, (byte) 0xfe }, - { (byte) 0xec, (byte) 0x93, (byte) 0xa0, (byte) 0x12, (byte) 0x60, (byte) 0xee, (byte) 0xc8, (byte) 0x0a, - (byte) 0xc5, (byte) 0x90, (byte) 0x62 }, - { (byte) 0x55, (byte) 0x6d, (byte) 0x93, (byte) 0x66, (byte) 0x14, (byte) 0x6d, (byte) 0xdf, (byte) 0x00, - (byte) 0x58, (byte) 0x99 }, - { (byte) 0x3c, (byte) 0x72, (byte) 0x20, (byte) 0x1f, (byte) 0xd2, (byte) 0x59, (byte) 0x19, (byte) 0xdb, - (byte) 0xa1 }, - { (byte) 0x23, (byte) 0xa8, (byte) 0xb1, (byte) 0x87, (byte) 0x55, (byte) 0xf7, (byte) 0x8a, (byte) 0x4b, - - }, { (byte) 0xe2, (byte) 0x42, (byte) 0x1c, (byte) 0x2d, (byte) 0xc1, (byte) 0xe4, (byte) 0x3e }, - { (byte) 0x66, (byte) 0xa6, (byte) 0xb5, (byte) 0x5a, (byte) 0x74, (byte) 0xd9 }, - { (byte) 0xe8, (byte) 0x76, (byte) 0xa8, (byte) 0x90, (byte) 0x76 }, - { (byte) 0xeb, (byte) 0x25, (byte) 0x3f, (byte) 0x87 }, { (byte) 0x37, (byte) 0xa0, (byte) 0xa9 }, - { (byte) 0x5b, (byte) 0x5d }, { (byte) 0x7e }, {} }; - - /* - * Expected results - from the original C implementation. - */ - - /** Murmur 32bit hash results, default library seed. */ - static final int[] results32_standard = { 0x96814fb3, 0x485dcaba, 0x331dc4ae, 0xc6a7bf2f, 0xcdf35de0, 0xd9dec7cc, - 0x63a7318a, 0xd0d3c2de, 0x90923aef, 0xaf35c1e2, 0x735377b2, 0x366c98f3, 0x9c48ee29, 0x0b615790, 0xb4308ac1, - 0xec98125a, 0x106e08d9 }; - - /** Murmur 32bit hash results, special test seed. */ - static final int[] results32_seed = { 0xd92e493e, 0x8b50903b, 0xc3372a7b, 0x48f07e9e, 0x8a5e4a6e, 0x57916df4, - 0xa346171f, 0x1e319c86, 0x9e1a03cd, 0x9f973e6c, 0x2d8c77f5, 0xabed8751, 0x296708b6, 0x24f8078b, 0x111b1553, - 0xa7da1996, 0xfe776c70 }; - - /** Murmur 64bit hash results, default library seed. */ - static final long[] results64_standard = { 0x4987cb15118a83d9l, 0x28e2a79e3f0394d9l, 0x8f4600d786fc5c05l, - 0xa09b27fea4b54af3l, 0x25f34447525bfd1el, 0x32fad4c21379c7bfl, 0x4b30b99a9d931921l, 0x4e5dab004f936cdbl, - 0x06825c27bc96cf40l, 0xff4bf2f8a4823905l, 0x7f7e950c064e6367l, 0x821ade90caaa5889l, 0x6d28c915d791686al, - 0x9c32649372163ba2l, 0xd66ae956c14d5212l, 0x38ed30ee5161200fl, 0x9bfae0a4e613fc3cl, }; - - /** Murmur 64bit hash results, special test seed. */ - static final long[] results64_seed = { 0x0822b1481a92e97bl, 0xf8a9223fef0822ddl, 0x4b49e56affae3a89l, - 0xc970296e32e1d1c1l, 0xe2f9f88789f1b08fl, 0x2b0459d9b4c10c61l, 0x377e97ea9197ee89l, 0xd2ccad460751e0e7l, - 0xff162ca8d6da8c47l, 0xf12e051405769857l, 0xdabba41293d5b035l, 0xacf326b0bb690d0el, 0x0617f431bc1a8e04l, - 0x15b81f28d576e1b2l, 0x28c1fe59e4f8e5bal, 0x694dd315c9354ca9l, 0xa97052a8f088ae6cl }; - - /** Dummy test text. */ - static final String text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; - - @Test - public void testHash32ByteArrayIntInt() { - for (int i = 0; i < input.length; i++) { - final int hash = MurmurHash2.hash32(input[i], input[i].length, 0x71b4954d); - if (hash != results32_seed[i]) { + /** Random input data with various length. */ + static final byte[][] input = { + { (byte) 0xed, (byte) 0x53, (byte) 0xc4, (byte) 0xa5, (byte) 0x3b, (byte) 0x1b, (byte) 0xbd, (byte) 0xc2, + (byte) 0x52, (byte) 0x7d, (byte) 0xc3, (byte) 0xef, (byte) 0x53, (byte) 0x5f, (byte) 0xae, + (byte) 0x3b }, + { (byte) 0x21, (byte) 0x65, (byte) 0x59, (byte) 0x4e, (byte) 0xd8, (byte) 0x12, (byte) 0xf9, (byte) 0x05, + (byte) 0x80, (byte) 0xe9, (byte) 0x1e, (byte) 0xed, (byte) 0xe4, (byte) 0x56, (byte) 0xbb }, + { (byte) 0x2b, (byte) 0x02, (byte) 0xb1, (byte) 0xd0, (byte) 0x3d, (byte) 0xce, (byte) 0x31, (byte) 0x3d, + (byte) 0x97, (byte) 0xc4, (byte) 0x91, (byte) 0x0d, (byte) 0xf7, (byte) 0x17 }, + { (byte) 0x8e, (byte) 0xa7, (byte) 0x9a, (byte) 0x02, (byte) 0xe8, (byte) 0xb9, (byte) 0x6a, (byte) 0xda, + (byte) 0x92, (byte) 0xad, (byte) 0xe9, (byte) 0x2d, (byte) 0x21 }, + { (byte) 0xa9, (byte) 0x6d, (byte) 0xea, (byte) 0x77, (byte) 0x06, (byte) 0xce, (byte) 0x1b, (byte) 0x85, + (byte) 0x48, (byte) 0x27, (byte) 0x4c, (byte) 0xfe }, + { (byte) 0xec, (byte) 0x93, (byte) 0xa0, (byte) 0x12, (byte) 0x60, (byte) 0xee, (byte) 0xc8, (byte) 0x0a, + (byte) 0xc5, (byte) 0x90, (byte) 0x62 }, + { (byte) 0x55, (byte) 0x6d, (byte) 0x93, (byte) 0x66, (byte) 0x14, (byte) 0x6d, (byte) 0xdf, (byte) 0x00, + (byte) 0x58, (byte) 0x99 }, + { (byte) 0x3c, (byte) 0x72, (byte) 0x20, (byte) 0x1f, (byte) 0xd2, (byte) 0x59, (byte) 0x19, (byte) 0xdb, + (byte) 0xa1 }, + { (byte) 0x23, (byte) 0xa8, (byte) 0xb1, (byte) 0x87, (byte) 0x55, (byte) 0xf7, (byte) 0x8a, (byte) 0x4b, + + }, { (byte) 0xe2, (byte) 0x42, (byte) 0x1c, (byte) 0x2d, (byte) 0xc1, (byte) 0xe4, (byte) 0x3e }, + { (byte) 0x66, (byte) 0xa6, (byte) 0xb5, (byte) 0x5a, (byte) 0x74, (byte) 0xd9 }, + { (byte) 0xe8, (byte) 0x76, (byte) 0xa8, (byte) 0x90, (byte) 0x76 }, + { (byte) 0xeb, (byte) 0x25, (byte) 0x3f, (byte) 0x87 }, { (byte) 0x37, (byte) 0xa0, (byte) 0xa9 }, + { (byte) 0x5b, (byte) 0x5d }, { (byte) 0x7e }, {} }; + + /* + * Expected results - from the original C implementation. + */ + + /** Murmur 32bit hash results, default library seed. */ + static final int[] results32_standard = { 0x96814fb3, 0x485dcaba, 0x331dc4ae, 0xc6a7bf2f, 0xcdf35de0, 0xd9dec7cc, + 0x63a7318a, 0xd0d3c2de, 0x90923aef, 0xaf35c1e2, 0x735377b2, 0x366c98f3, 0x9c48ee29, 0x0b615790, 0xb4308ac1, + 0xec98125a, 0x106e08d9 }; + + /** Murmur 32bit hash results, special test seed. */ + static final int[] results32_seed = { 0xd92e493e, 0x8b50903b, 0xc3372a7b, 0x48f07e9e, 0x8a5e4a6e, 0x57916df4, + 0xa346171f, 0x1e319c86, 0x9e1a03cd, 0x9f973e6c, 0x2d8c77f5, 0xabed8751, 0x296708b6, 0x24f8078b, 0x111b1553, + 0xa7da1996, 0xfe776c70 }; + + /** Murmur 64bit hash results, default library seed. */ + static final long[] results64_standard = { 0x4987cb15118a83d9l, 0x28e2a79e3f0394d9l, 0x8f4600d786fc5c05l, + 0xa09b27fea4b54af3l, 0x25f34447525bfd1el, 0x32fad4c21379c7bfl, 0x4b30b99a9d931921l, 0x4e5dab004f936cdbl, + 0x06825c27bc96cf40l, 0xff4bf2f8a4823905l, 0x7f7e950c064e6367l, 0x821ade90caaa5889l, 0x6d28c915d791686al, + 0x9c32649372163ba2l, 0xd66ae956c14d5212l, 0x38ed30ee5161200fl, 0x9bfae0a4e613fc3cl, }; + + /** Murmur 64bit hash results, special test seed. */ + static final long[] results64_seed = { 0x0822b1481a92e97bl, 0xf8a9223fef0822ddl, 0x4b49e56affae3a89l, + 0xc970296e32e1d1c1l, 0xe2f9f88789f1b08fl, 0x2b0459d9b4c10c61l, 0x377e97ea9197ee89l, 0xd2ccad460751e0e7l, + 0xff162ca8d6da8c47l, 0xf12e051405769857l, 0xdabba41293d5b035l, 0xacf326b0bb690d0el, 0x0617f431bc1a8e04l, + 0x15b81f28d576e1b2l, 0x28c1fe59e4f8e5bal, 0x694dd315c9354ca9l, 0xa97052a8f088ae6cl }; + + /** Dummy test text. */ + static final String text = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"; + + @Test + public void testHash32ByteArrayIntInt() { + for (int i = 0; i < input.length; i++) { + final int hash = MurmurHash2.hash32(input[i], input[i].length, 0x71b4954d); + if (hash != results32_seed[i]) { fail(String.format("Unexpected hash32 result for example %d: 0x%08x instead of 0x%08x", i, hash, - results32_seed[i])); + results32_seed[i])); } - } - } - - @Test - public void testHash32ByteArrayInt() { - for (int i = 0; i < input.length; i++) { - final int hash = MurmurHash2.hash32(input[i], input[i].length); - if (hash != results32_standard[i]) { + } + } + + @Test + public void testHash32ByteArrayInt() { + for (int i = 0; i < input.length; i++) { + final int hash = MurmurHash2.hash32(input[i], input[i].length); + if (hash != results32_standard[i]) { fail(String.format("Unexpected hash32 result for example %d: 0x%08x instead of 0x%08x", i, hash, - results32_standard[i])); + results32_standard[i])); } - } - } - - @Test - public void testHash32String() { - final int hash = MurmurHash2.hash32(text); - assertTrue(hash == 0xb3bf597e); - } - - @Test - public void testHash32StringIntInt() { - final int hash = MurmurHash2.hash32(text, 2, text.length() - 4); - assertTrue(hash == 0x4d666d90); - } - - @Test - public void testHash64ByteArrayIntInt() { - for (int i = 0; i < input.length; i++) { - final long hash = MurmurHash2.hash64(input[i], input[i].length, 0x344d1f5c); - assertTrue(String.format("Unexpected hash64 result for example %d: 0x%016x instead of 0x%016x", i, hash, - results64_seed[i]), hash == results64_seed[i]); - } - } - - @Test - public void testHash64ByteArrayInt() { - for (int i = 0; i < input.length; i++) { - final long hash = MurmurHash2.hash64(input[i], input[i].length); - assertTrue(String.format("Unexpected hash64 result for example %d: 0x%016x instead of 0x%016x", i, hash, - results64_standard[i]), hash == results64_standard[i]); - } - } - - @Test - public void testHash64String() { - final long hash = MurmurHash2.hash64(text); - assertTrue(hash == 0x0920e0c1b7eeb261l); - } - - @Test - public void testHash64StringIntInt() { - final long hash = MurmurHash2.hash64(text, 2, text.length() - 4); - assertTrue(hash == 0xa8b33145194985a2l); - } + } + } + + @Test + public void testHash32String() { + final int hash = MurmurHash2.hash32(text); + assertTrue(hash == 0xb3bf597e); + } + + @Test + public void testHash32StringIntInt() { + final int hash = MurmurHash2.hash32(text, 2, text.length() - 4); + assertTrue(hash == 0x4d666d90); + } + + @Test + public void testHash64ByteArrayIntInt() { + for (int i = 0; i < input.length; i++) { + final long hash = MurmurHash2.hash64(input[i], input[i].length, 0x344d1f5c); + assertTrue(String.format("Unexpected hash64 result for example %d: 0x%016x instead of 0x%016x", i, hash, + results64_seed[i]), hash == results64_seed[i]); + } + } + + @Test + public void testHash64ByteArrayInt() { + for (int i = 0; i < input.length; i++) { + final long hash = MurmurHash2.hash64(input[i], input[i].length); + assertTrue(String.format("Unexpected hash64 result for example %d: 0x%016x instead of 0x%016x", i, hash, + results64_standard[i]), hash == results64_standard[i]); + } + } + + @Test + public void testHash64String() { + final long hash = MurmurHash2.hash64(text); + assertTrue(hash == 0x0920e0c1b7eeb261l); + } + + @Test + public void testHash64StringIntInt() { + final long hash = MurmurHash2.hash64(text, 2, text.length() - 4); + assertTrue(hash == 0xa8b33145194985a2l); + } } diff --git a/src/test/java/org/apache/commons/codec/digest/MurmurHash3Test.java b/src/test/java/org/apache/commons/codec/digest/MurmurHash3Test.java index d239c16..eb526e2 100644 --- a/src/test/java/org/apache/commons/codec/digest/MurmurHash3Test.java +++ b/src/test/java/org/apache/commons/codec/digest/MurmurHash3Test.java @@ -28,200 +28,200 @@ import org.junit.Test; public class MurmurHash3Test { - private static final String TEST = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut pe [...] - - @Test - public void test32_String() { - // Arrange - final String origin = TEST; - - // Act - final int result = MurmurHash3.hash32(origin); - - // Assert - assertEquals(-436507231, result); - } - - @Test - public void testHashCodeM3_64() { - final byte[] origin =TEST.getBytes(); - long hash = MurmurHash3.hash64(origin, 0, origin.length); - assertEquals(5785358552565094607L, hash); - - final byte[] originOffset = new byte[origin.length + 150]; - Arrays.fill(originOffset, (byte) 123); - System.arraycopy(origin, 0, originOffset, 150, origin.length); - hash = MurmurHash3.hash64(originOffset, 150, origin.length); - assertEquals(5785358552565094607L, hash); - } - - @Test - public void test64() { - final int seed = 123, iters = 1000000; - final ByteBuffer SHORT_BUFFER = ByteBuffer.allocate(MurmurHash3.SHORT_BYTES); - final ByteBuffer INT_BUFFER = ByteBuffer.allocate(MurmurHash3.INTEGER_BYTES); - final ByteBuffer LONG_BUFFER = ByteBuffer.allocate(MurmurHash3.LONG_BYTES); - final Random rdm = new Random(seed); - for (int i = 0; i < iters; ++i) { - final long ln = rdm.nextLong(); - final int in = rdm.nextInt(); - final short sn = (short) (rdm.nextInt(2 * Short.MAX_VALUE - 1) - Short.MAX_VALUE); - final float fn = rdm.nextFloat(); - final double dn = rdm.nextDouble(); - SHORT_BUFFER.putShort(0, sn); - assertEquals(MurmurHash3.hash64(SHORT_BUFFER.array()), MurmurHash3.hash64(sn)); - INT_BUFFER.putInt(0, in); - assertEquals(MurmurHash3.hash64(INT_BUFFER.array()), MurmurHash3.hash64(in)); - LONG_BUFFER.putLong(0, ln); - assertEquals(MurmurHash3.hash64(LONG_BUFFER.array()), MurmurHash3.hash64(ln)); - INT_BUFFER.putFloat(0, fn); - assertEquals(MurmurHash3.hash64(INT_BUFFER.array()), MurmurHash3.hash64(Float.floatToIntBits(fn))); - LONG_BUFFER.putDouble(0, dn); - assertEquals(MurmurHash3.hash64(LONG_BUFFER.array()), MurmurHash3.hash64(Double.doubleToLongBits(dn))); - } - } - - @Test - public void test128_Short() { - // Arrange - final ByteBuffer BUFFER = ByteBuffer.allocate(MurmurHash3.SHORT_BYTES); - BUFFER.putShort(0, (short) 2); - - // Act - final long[] result = MurmurHash3.hash128(BUFFER.array()); - - // Assert - assertEquals(result.length, 2); - assertEquals(8673501042631707204L, result[0]); - assertEquals(491907755572407714L, result[1]); - } - - @Test - public void test128_Int() { - // Arrange - final ByteBuffer BUFFER = ByteBuffer.allocate(MurmurHash3.INTEGER_BYTES); - BUFFER.putInt(0, 3); - - // Act - final long[] result = MurmurHash3.hash128(BUFFER.array()); - - // Assert - assertEquals(result.length, 2); - assertEquals(2448828847287705405L, result[0]); - assertEquals(-4568642211270719983L, result[1]); - } - - @Test - public void test128_Long() { - // Arrange - final ByteBuffer BUFFER = ByteBuffer.allocate(MurmurHash3.LONG_BYTES); - BUFFER.putLong(0, 8675309L); - - // Act - final long[] result = MurmurHash3.hash128(BUFFER.array()); - - // Assert - assertEquals(result.length, 2); - assertEquals(2339756411022791995L, result[0]); - assertEquals(8242951144762217305L, result[1]); - } - - @Test - public void test128_Double() { - // Arrange - final ByteBuffer BUFFER = ByteBuffer.allocate(Double.SIZE / Byte.SIZE); - BUFFER.putDouble(0, 456.987); - - // Act - final long[] result = MurmurHash3.hash128(BUFFER.array()); - - // Assert - assertEquals(result.length, 2); - assertEquals(6877430437712399133L, result[0]); - assertEquals(-8576421050167250536L, result[1]); - } - - @Test - public void test128_String() { - // Arrange - final String origin = TEST; - - // Act - final long[] result = MurmurHash3.hash128(origin); - - // Assert - assertEquals(result.length, 2); - assertEquals(6409160382500807310L, result[0]); - assertEquals(-7835827609130513921L, result[1]); - } - - @Test - public void testIncremental() { - final int seed = 123, arraySize = 1023; - final byte[] bytes = new byte[arraySize]; - new Random(seed).nextBytes(bytes); - final int expected = MurmurHash3.hash32(bytes, arraySize); - final MurmurHash3.IncrementalHash32 same = new IncrementalHash32(), diff = new IncrementalHash32(); - for (int blockSize = 1; blockSize <= arraySize; ++blockSize) { - final byte[] block = new byte[blockSize]; - same.start(MurmurHash3.DEFAULT_SEED); - diff.start(MurmurHash3.DEFAULT_SEED); - for (int offset = 0; offset < arraySize; offset += blockSize) { - final int length = Math.min(arraySize - offset, blockSize); - same.add(bytes, offset, length); - System.arraycopy(bytes, offset, block, 0, length); - diff.add(block, 0, length); - } - assertEquals("Block size " + blockSize, expected, same.end()); - assertEquals("Block size " + blockSize, expected, diff.end()); - } - } - - @Test - public void testTwoLongOrdered() { - final ByteBuffer buffer = ByteBuffer.allocate(MurmurHash3.LONG_BYTES * 2); - for (long i = 0; i < 1000; i++) { - for (long j = 0; j < 1000; j++) { - buffer.putLong(0, i); - buffer.putLong(MurmurHash3.LONG_BYTES, j); - assertEquals(MurmurHash3.hash32(buffer.array()), MurmurHash3.hash32(i, j)); - } - } - } - - @Test - public void testTwoLongRandom() { - final ByteBuffer buffer = ByteBuffer.allocate(MurmurHash3.LONG_BYTES * 2); - final Random random = new Random(); - for (long i = 0; i < 1000; i++) { - for (long j = 0; j < 1000; j++) { - final long x = random.nextLong(); - final long y = random.nextLong(); - buffer.putLong(0, x); - buffer.putLong(MurmurHash3.LONG_BYTES, y); - assertEquals(MurmurHash3.hash32(buffer.array()), MurmurHash3.hash32(x, y)); - } - } - } - - @Test - public void testSingleLongOrdered() { - final ByteBuffer buffer = ByteBuffer.allocate(MurmurHash3.LONG_BYTES); - for (long i = 0; i < 1000; i++) { - buffer.putLong(0, i); - assertEquals(MurmurHash3.hash32(buffer.array()), MurmurHash3.hash32(i)); - } - } - - @Test - public void testSingleLongRandom() { - final ByteBuffer buffer = ByteBuffer.allocate(MurmurHash3.LONG_BYTES); - final Random random = new Random(); - for (long i = 0; i < 1000; i++) { - final long x = random.nextLong(); - buffer.putLong(0, x); - assertEquals(MurmurHash3.hash32(buffer.array()), MurmurHash3.hash32(x)); - } - } + private static final String TEST = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut [...] + + @Test + public void test32_String() { + // Arrange + final String origin = TEST; + + // Act + final int result = MurmurHash3.hash32(origin); + + // Assert + assertEquals(-436507231, result); + } + + @Test + public void testHashCodeM3_64() { + final byte[] origin =TEST.getBytes(); + long hash = MurmurHash3.hash64(origin, 0, origin.length); + assertEquals(5785358552565094607L, hash); + + final byte[] originOffset = new byte[origin.length + 150]; + Arrays.fill(originOffset, (byte) 123); + System.arraycopy(origin, 0, originOffset, 150, origin.length); + hash = MurmurHash3.hash64(originOffset, 150, origin.length); + assertEquals(5785358552565094607L, hash); + } + + @Test + public void test64() { + final int seed = 123, iters = 1000000; + final ByteBuffer SHORT_BUFFER = ByteBuffer.allocate(MurmurHash3.SHORT_BYTES); + final ByteBuffer INT_BUFFER = ByteBuffer.allocate(MurmurHash3.INTEGER_BYTES); + final ByteBuffer LONG_BUFFER = ByteBuffer.allocate(MurmurHash3.LONG_BYTES); + final Random rdm = new Random(seed); + for (int i = 0; i < iters; ++i) { + final long ln = rdm.nextLong(); + final int in = rdm.nextInt(); + final short sn = (short) (rdm.nextInt(2 * Short.MAX_VALUE - 1) - Short.MAX_VALUE); + final float fn = rdm.nextFloat(); + final double dn = rdm.nextDouble(); + SHORT_BUFFER.putShort(0, sn); + assertEquals(MurmurHash3.hash64(SHORT_BUFFER.array()), MurmurHash3.hash64(sn)); + INT_BUFFER.putInt(0, in); + assertEquals(MurmurHash3.hash64(INT_BUFFER.array()), MurmurHash3.hash64(in)); + LONG_BUFFER.putLong(0, ln); + assertEquals(MurmurHash3.hash64(LONG_BUFFER.array()), MurmurHash3.hash64(ln)); + INT_BUFFER.putFloat(0, fn); + assertEquals(MurmurHash3.hash64(INT_BUFFER.array()), MurmurHash3.hash64(Float.floatToIntBits(fn))); + LONG_BUFFER.putDouble(0, dn); + assertEquals(MurmurHash3.hash64(LONG_BUFFER.array()), MurmurHash3.hash64(Double.doubleToLongBits(dn))); + } + } + + @Test + public void test128_Short() { + // Arrange + final ByteBuffer BUFFER = ByteBuffer.allocate(MurmurHash3.SHORT_BYTES); + BUFFER.putShort(0, (short) 2); + + // Act + final long[] result = MurmurHash3.hash128(BUFFER.array()); + + // Assert + assertEquals(result.length, 2); + assertEquals(8673501042631707204L, result[0]); + assertEquals(491907755572407714L, result[1]); + } + + @Test + public void test128_Int() { + // Arrange + final ByteBuffer BUFFER = ByteBuffer.allocate(MurmurHash3.INTEGER_BYTES); + BUFFER.putInt(0, 3); + + // Act + final long[] result = MurmurHash3.hash128(BUFFER.array()); + + // Assert + assertEquals(result.length, 2); + assertEquals(2448828847287705405L, result[0]); + assertEquals(-4568642211270719983L, result[1]); + } + + @Test + public void test128_Long() { + // Arrange + final ByteBuffer BUFFER = ByteBuffer.allocate(MurmurHash3.LONG_BYTES); + BUFFER.putLong(0, 8675309L); + + // Act + final long[] result = MurmurHash3.hash128(BUFFER.array()); + + // Assert + assertEquals(result.length, 2); + assertEquals(2339756411022791995L, result[0]); + assertEquals(8242951144762217305L, result[1]); + } + + @Test + public void test128_Double() { + // Arrange + final ByteBuffer BUFFER = ByteBuffer.allocate(Double.SIZE / Byte.SIZE); + BUFFER.putDouble(0, 456.987); + + // Act + final long[] result = MurmurHash3.hash128(BUFFER.array()); + + // Assert + assertEquals(result.length, 2); + assertEquals(6877430437712399133L, result[0]); + assertEquals(-8576421050167250536L, result[1]); + } + + @Test + public void test128_String() { + // Arrange + final String origin = TEST; + + // Act + final long[] result = MurmurHash3.hash128(origin); + + // Assert + assertEquals(result.length, 2); + assertEquals(6409160382500807310L, result[0]); + assertEquals(-7835827609130513921L, result[1]); + } + + @Test + public void testIncremental() { + final int seed = 123, arraySize = 1023; + final byte[] bytes = new byte[arraySize]; + new Random(seed).nextBytes(bytes); + final int expected = MurmurHash3.hash32(bytes, arraySize); + final MurmurHash3.IncrementalHash32 same = new IncrementalHash32(), diff = new IncrementalHash32(); + for (int blockSize = 1; blockSize <= arraySize; ++blockSize) { + final byte[] block = new byte[blockSize]; + same.start(MurmurHash3.DEFAULT_SEED); + diff.start(MurmurHash3.DEFAULT_SEED); + for (int offset = 0; offset < arraySize; offset += blockSize) { + final int length = Math.min(arraySize - offset, blockSize); + same.add(bytes, offset, length); + System.arraycopy(bytes, offset, block, 0, length); + diff.add(block, 0, length); + } + assertEquals("Block size " + blockSize, expected, same.end()); + assertEquals("Block size " + blockSize, expected, diff.end()); + } + } + + @Test + public void testTwoLongOrdered() { + final ByteBuffer buffer = ByteBuffer.allocate(MurmurHash3.LONG_BYTES * 2); + for (long i = 0; i < 1000; i++) { + for (long j = 0; j < 1000; j++) { + buffer.putLong(0, i); + buffer.putLong(MurmurHash3.LONG_BYTES, j); + assertEquals(MurmurHash3.hash32(buffer.array()), MurmurHash3.hash32(i, j)); + } + } + } + + @Test + public void testTwoLongRandom() { + final ByteBuffer buffer = ByteBuffer.allocate(MurmurHash3.LONG_BYTES * 2); + final Random random = new Random(); + for (long i = 0; i < 1000; i++) { + for (long j = 0; j < 1000; j++) { + final long x = random.nextLong(); + final long y = random.nextLong(); + buffer.putLong(0, x); + buffer.putLong(MurmurHash3.LONG_BYTES, y); + assertEquals(MurmurHash3.hash32(buffer.array()), MurmurHash3.hash32(x, y)); + } + } + } + + @Test + public void testSingleLongOrdered() { + final ByteBuffer buffer = ByteBuffer.allocate(MurmurHash3.LONG_BYTES); + for (long i = 0; i < 1000; i++) { + buffer.putLong(0, i); + assertEquals(MurmurHash3.hash32(buffer.array()), MurmurHash3.hash32(i)); + } + } + + @Test + public void testSingleLongRandom() { + final ByteBuffer buffer = ByteBuffer.allocate(MurmurHash3.LONG_BYTES); + final Random random = new Random(); + for (long i = 0; i < 1000; i++) { + final long x = random.nextLong(); + buffer.putLong(0, x); + assertEquals(MurmurHash3.hash32(buffer.array()), MurmurHash3.hash32(x)); + } + } }