This is an automated email from the ASF dual-hosted git repository.

ggregory 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 2bcdfd8  CODEC-236: MurmurHash2 and MurmurHash3 implementations. (#22)
2bcdfd8 is described below

commit 2bcdfd86643761c239ec40e6e1e2f05855a07f95
Author: Melloware <[email protected]>
AuthorDate: Fri Jul 12 09:01:32 2019 -0400

    CODEC-236: MurmurHash2 and MurmurHash3 implementations. (#22)
    
    * CODEC-236: MurmurHash2 and MurmurHash3 implementations.
    
    * CODEC-236: Removed author tag.
    
    * CODEC-236: Added javadoc and increased unit test coverage.
---
 src/changes/changes.xml                            |   5 +-
 .../apache/commons/codec/digest/MurmurHash2.java   | 214 +++++++
 .../apache/commons/codec/digest/MurmurHash3.java   | 625 +++++++++++++++++++++
 .../commons/codec/digest/MurmurHash2Test.java      | 145 +++++
 .../commons/codec/digest/MurmurHash3Test.java      | 227 ++++++++
 5 files changed, 1214 insertions(+), 2 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index d759821..ee05160 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -46,8 +46,9 @@ The <action> type attribute can be add,update,fix,remove.
     <release version="1.13" date="YYYY-MM-DD" description="TBD">
       <action issue="CODEC-255" dev="sebb" due-to="Holger Grote" 
type="fix">ColognePhonetic handles x incorrectly</action>      
       <action issue="CODEC-254" dev="sebb" due-to="Holger Grote" 
type="fix">ColognePhonetic does not treat the letter H correctly</action>      
-      <action issue="CODEC-257" dev="ggregory" type="update">Update from Java 
7 to Java 8</action>      
-      <action issue="CODEC-134" dev="tmousaw-ptc" type="fix">Reject any decode 
request for a value that is impossible to encode to for Base32/Base64 rather 
than blindly decoding.</action>      
+      <action issue="CODEC-134" dev="tmousaw-ptc" type="fix">Reject any decode 
request for a value that is impossible to encode to for Base32/Base64 rather 
than blindly decoding.</action>
+      <action issue="CODEC-236" dev="melloware" due-to="Viliam Holub" 
type="add">MurmurHash2 for 32-bit or 64-bit value.</action>
+      <action issue="CODEC-236" dev="melloware" due-to="Austin Appleby" 
type="add">MurmurHash3 for 32-bit or 128-bit value.</action>
     </release>
 
     <release version="1.12" date="2019-02-04" description="Feature and fix 
release.">
diff --git a/src/main/java/org/apache/commons/codec/digest/MurmurHash2.java 
b/src/main/java/org/apache/commons/codec/digest/MurmurHash2.java
new file mode 100644
index 0000000..36a92db
--- /dev/null
+++ b/src/main/java/org/apache/commons/codec/digest/MurmurHash2.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.codec.digest;
+
+/**
+ * MurmurHash2 yields a 32-bit or 64-bit value.
+ * 
+ * MurmurHash is a non-cryptographic hash function suitable for general
+ * hash-based lookup. The name comes from two basic operations, multiply (MU)
+ * and rotate (R), used in its inner loop. Unlike cryptographic hash functions,
+ * it is not specifically designed to be difficult to reverse by an adversary,
+ * making it unsuitable for cryptographic purposes.
+ * 
+ * This is a re-implementation of the original C code plus some additional
+ * features.
+ * 
+ * Public domain.
+ * 
+ * @see <a href="https://en.wikipedia.org/wiki/MurmurHash";>MurmurHash</a>
+ * @since 1.13
+ */
+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, int length, 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;
+               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, 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, int from, 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, int length, int seed) {
+               final long m = 0xc6a4a7935bd1e995L;
+               final int r = 47;
+
+               long h = (seed & 0xffffffffl) ^ (length * m);
+
+               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 ^= (long) (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, 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, int from, 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
new file mode 100644
index 0000000..e9cd2c9
--- /dev/null
+++ b/src/main/java/org/apache/commons/codec/digest/MurmurHash3.java
@@ -0,0 +1,625 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.codec.digest;
+
+/**
+ * MurmurHash3 yields a 32-bit or 128-bit value.
+ * 
+ * MurmurHash is a non-cryptographic hash function suitable for general
+ * hash-based lookup. The name comes from two basic operations, multiply (MU)
+ * and rotate (R), used in its inner loop. Unlike cryptographic hash functions,
+ * it is not specifically designed to be difficult to reverse by an adversary,
+ * making it unsuitable for cryptographic purposes.
+ * 
+ * 32-bit Java port of
+ * https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp#94
+ * 128-bit Java port of
+ * https://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp#255
+ *
+ * This is a public domain code with no copyrights. From homepage of MurmurHash
+ * (https://code.google.com/p/smhasher/), "All MurmurHash versions are public
+ * domain software, and the author disclaims all copyright to their code."
+ * 
+ * Copied from Apache Hive:
+ * 
https://github.com/apache/hive/blob/master/storage-api/src/java/org/apache/hive/common/util/Murmur3.java
+ * 
+ * @see <a href="https://en.wikipedia.org/wiki/MurmurHash";>MurmurHash</a>
+ * @since 1.13
+ */
+public final class MurmurHash3 {
+
+       // 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(long l0, 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(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(long l0, 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(long l0, long l1, 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(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(String data) {
+               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(byte[] data, 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(byte[] data, int length, 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(byte[] data, int offset, int length, int seed) 
{
+               int hash = seed;
+               final int nblocks = length >> 2;
+
+               // body
+               for (int i = 0; i < nblocks; i++) {
+                       int i_4 = i << 2;
+                       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
+               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(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(long data) {
+               long hash = DEFAULT_SEED;
+               long k = Long.reverseBytes(data);
+               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(int data) {
+               long k1 = Integer.reverseBytes(data) & (-1L >>> 32);
+               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(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(byte[] data, int offset, 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(byte[] data, int offset, int length, 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;
+               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(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(String data) {
+               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(byte[] data, int offset, int length, 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;
+               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 ^= (long) (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 ^= (long) (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(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(int hash) {
+                       tailLen = totalLen = 0;
+                       this.hash = hash;
+               }
+
+               public final void add(byte[] data, int offset, 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;
+                       }
+                       int length2 = length - offset2;
+                       offset += offset2;
+                       final int nblocks = length2 >> 2;
+
+                       for (int i = 0; i < nblocks; i++) {
+                               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;
+                       }
+
+                       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(byte b1, byte b2, byte b3, 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
new file mode 100644
index 0000000..1c4b002
--- /dev/null
+++ b/src/test/java/org/apache/commons/codec/digest/MurmurHash2Test.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.codec.digest;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+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++) {
+                       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]));
+               }
+       }
+
+       @Test
+       public void testHash32ByteArrayInt() {
+               for (int i = 0; i < input.length; i++) {
+                       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]));
+               }
+       }
+
+       @Test
+       public void testHash32String() {
+               int hash = MurmurHash2.hash32(text);
+               assertTrue(hash == 0xb3bf597e);
+       }
+
+       @Test
+       public void testHash32StringIntInt() {
+               int hash = MurmurHash2.hash32(text, 2, text.length() - 4);
+               assertTrue(hash == 0x4d666d90);
+       }
+
+       @Test
+       public void testHash64ByteArrayIntInt() {
+               for (int i = 0; i < input.length; i++) {
+                       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++) {
+                       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() {
+               long hash = MurmurHash2.hash64(text);
+               assertTrue(hash == 0x0920e0c1b7eeb261l);
+       }
+
+       @Test
+       public void testHash64StringIntInt() {
+               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
new file mode 100644
index 0000000..15ac552
--- /dev/null
+++ b/src/test/java/org/apache/commons/codec/digest/MurmurHash3Test.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.codec.digest;
+
+import static org.junit.Assert.assertEquals;
+
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Random;
+
+import org.apache.commons.codec.digest.MurmurHash3.IncrementalHash32;
+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
+               String origin = TEST;
+               
+               // Act
+               int result = MurmurHash3.hash32(origin);
+
+               // Assert
+               assertEquals(-436507231, result);
+       }
+       
+       @Test
+       public void testHashCodeM3_64() {
+               byte[] origin =TEST.getBytes();
+               long hash = MurmurHash3.hash64(origin, 0, origin.length);
+               assertEquals(5785358552565094607L, hash);
+
+               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;
+               ByteBuffer SHORT_BUFFER = ByteBuffer.allocate(Short.BYTES);
+               ByteBuffer INT_BUFFER = ByteBuffer.allocate(Integer.BYTES);
+               ByteBuffer LONG_BUFFER = ByteBuffer.allocate(Long.BYTES);
+               Random rdm = new Random(seed);
+               for (int i = 0; i < iters; ++i) {
+                       long ln = rdm.nextLong();
+                       int in = rdm.nextInt();
+                       short sn = (short) (rdm.nextInt(2 * Short.MAX_VALUE - 
1) - Short.MAX_VALUE);
+                       float fn = rdm.nextFloat();
+                       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
+               ByteBuffer BUFFER = ByteBuffer.allocate(Short.BYTES);
+               BUFFER.putShort(0, (short) 2);
+
+               // Act
+               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
+               ByteBuffer BUFFER = ByteBuffer.allocate(Integer.BYTES);
+               BUFFER.putInt(0, 3);
+
+               // Act
+               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
+               ByteBuffer BUFFER = ByteBuffer.allocate(Long.BYTES);
+               BUFFER.putLong(0, 8675309L);
+
+               // Act
+               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
+               ByteBuffer BUFFER = ByteBuffer.allocate(Double.BYTES);
+               BUFFER.putDouble(0, 456.987);
+
+               // Act
+               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
+               String origin = TEST;
+               
+               // Act
+               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;
+               byte[] bytes = new byte[arraySize];
+               new Random(seed).nextBytes(bytes);
+               int expected = MurmurHash3.hash32(bytes, arraySize);
+               MurmurHash3.IncrementalHash32 same = new IncrementalHash32(), 
diff = new IncrementalHash32();
+               for (int blockSize = 1; blockSize <= arraySize; ++blockSize) {
+                       byte[] block = new byte[blockSize];
+                       same.start(MurmurHash3.DEFAULT_SEED);
+                       diff.start(MurmurHash3.DEFAULT_SEED);
+                       for (int offset = 0; offset < arraySize; offset += 
blockSize) {
+                               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() {
+               ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * 2);
+               for (long i = 0; i < 1000; i++) {
+                       for (long j = 0; j < 1000; j++) {
+                               buffer.putLong(0, i);
+                               buffer.putLong(Long.BYTES, j);
+                               
assertEquals(MurmurHash3.hash32(buffer.array()), MurmurHash3.hash32(i, j));
+                       }
+               }
+       }
+
+       @Test
+       public void testTwoLongRandom() {
+               ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES * 2);
+               Random random = new Random();
+               for (long i = 0; i < 1000; i++) {
+                       for (long j = 0; j < 1000; j++) {
+                               long x = random.nextLong();
+                               long y = random.nextLong();
+                               buffer.putLong(0, x);
+                               buffer.putLong(Long.BYTES, y);
+                               
assertEquals(MurmurHash3.hash32(buffer.array()), MurmurHash3.hash32(x, y));
+                       }
+               }
+       }
+
+       @Test
+       public void testSingleLongOrdered() {
+               ByteBuffer buffer = ByteBuffer.allocate(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() {
+               ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
+               Random random = new Random();
+               for (long i = 0; i < 1000; i++) {
+                       long x = random.nextLong();
+                       buffer.putLong(0, x);
+                       assertEquals(MurmurHash3.hash32(buffer.array()), 
MurmurHash3.hash32(x));
+               }
+       }
+
+}

Reply via email to