View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.codec.digest;
19  
20  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21  import static org.junit.jupiter.api.Assertions.assertEquals;
22  import static org.junit.jupiter.api.Assertions.assertNotEquals;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  import static org.junit.jupiter.api.Assumptions.assumeTrue;
25  
26  import java.nio.ByteBuffer;
27  import java.util.Arrays;
28  import java.util.concurrent.ThreadLocalRandom;
29  
30  import org.apache.commons.codec.binary.StringUtils;
31  import org.apache.commons.codec.digest.MurmurHash3.IncrementalHash32;
32  import org.apache.commons.codec.digest.MurmurHash3.IncrementalHash32x86;
33  import org.junit.jupiter.api.Test;
34  
35  /**
36   * Test for {@link MurmurHash3}.
37   */
38  @SuppressWarnings("deprecation")
39  class MurmurHash3Test {
40  
41      /** Test data for the hash64 method. */
42      private static final String TEST_HASH64 = "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 perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur?";
43  
44      /**
45       * 256 integers in the range [0,255] arranged in random order.
46       * This ensures all bytes are covered in a full hash of the bytes.
47       *
48       * <p>
49       * To create test data using the Python library mmh3 (which invokes the C++ code):
50       * </p>
51       *
52       * <pre>
53       * import mmh3
54       * import numpy as np
55       * # Put random bytes in here:
56       * bytes = np.uint8([46,246,249,184,247,84,99,144,62,77,195,220,92,20,150,159,38,40,124,252,185,28,63,13,213,172,85,198,118,74,109,157,132,216,76,177,173,23,140,86,146,95,54,176,114,179,234,174,183,141,122,12,60,116,200,142,6,167,59,240,33,29,165,111,243,30,219,110,255,53,32,35,64,225,96,152,70,41,133,80,244,127,57,199,5,164,151,49,26,180,203,83,108,39,126,208,42,206,178,19,69,223,71,231,250,125,211,232,189,55,44,82,48,221,43,192,241,103,155,27,51,163,21,169,91,94,217,191,78,72,93,102,104,105,8,113,100,143,89,245,227,120,160,251,153,145,45,218,168,233,229,253,67,22,182,98,137,128,135,11,214,66,73,171,188,170,131,207,79,106,24,75,237,194,7,129,215,81,248,242,16,25,136,147,156,97,52,10,181,17,205,58,101,68,230,1,37,0,222,88,130,148,224,47,50,197,34,212,196,209,14,36,139,228,154,31,175,202,236,161,3,162,190,254,134,119,4,61,65,117,186,107,204,9,187,201,90,149,226,56,239,238,235,112,87,18,121,115,138,123,210,2,193,166,158,15])
57       * # Seed as appropriate. The default seed in mmh3 is zero.
58       * seed = 789
59       * # Output data. Adjust as appropriate.
60       * for x in range(0, 256):
61       *   # 32-bit hash
62       *   mmh3.hash(bytes[:x])
63       *   mmh3.hash(bytes[:x], seed)
64       *   # 128-bit hash as two 64 bit signed ints
65       *   mmh3.hash64(bytes[:x])
66       *   mmh3.hash64(bytes[:x], seed)
67       * # Sub-arrays: lower inclusive, upper exclusive:
68       * mmh3.hash(bytes[13:15])
69       * </pre>
70       */
71      private static final int[] RANDOM_INTS = { 46, 246, 249, 184, 247, 84, 99, 144, 62, 77, 195, 220, 92, 20, 150, 159, 38, 40, 124, 252, 185, 28, 63, 13, 213,
72              172, 85, 198, 118, 74, 109, 157, 132, 216, 76, 177, 173, 23, 140, 86, 146, 95, 54, 176, 114, 179, 234, 174, 183, 141, 122, 12, 60, 116, 200, 142, 6,
73              167, 59, 240, 33, 29, 165, 111, 243, 30, 219, 110, 255, 53, 32, 35, 64, 225, 96, 152, 70, 41, 133, 80, 244, 127, 57, 199, 5, 164, 151, 49, 26, 180,
74              203, 83, 108, 39, 126, 208, 42, 206, 178, 19, 69, 223, 71, 231, 250, 125, 211, 232, 189, 55, 44, 82, 48, 221, 43, 192, 241, 103, 155, 27, 51, 163,
75              21, 169, 91, 94, 217, 191, 78, 72, 93, 102, 104, 105, 8, 113, 100, 143, 89, 245, 227, 120, 160, 251, 153, 145, 45, 218, 168, 233, 229, 253, 67, 22,
76              182, 98, 137, 128, 135, 11, 214, 66, 73, 171, 188, 170, 131, 207, 79, 106, 24, 75, 237, 194, 7, 129, 215, 81, 248, 242, 16, 25, 136, 147, 156, 97,
77              52, 10, 181, 17, 205, 58, 101, 68, 230, 1, 37, 0, 222, 88, 130, 148, 224, 47, 50, 197, 34, 212, 196, 209, 14, 36, 139, 228, 154, 31, 175, 202, 236,
78              161, 3, 162, 190, 254, 134, 119, 4, 61, 65, 117, 186, 107, 204, 9, 187, 201, 90, 149, 226, 56, 239, 238, 235, 112, 87, 18, 121, 115, 138, 123, 210,
79              2, 193, 166, 158, 15 };
80  
81      /**
82       * 256 bytes in the range [0,255] arranged in random order.
83       * This ensures all bytes are covered in a full hash of the bytes.
84       */
85      private static final byte[] RANDOM_BYTES;
86  
87      static {
88          RANDOM_BYTES = new byte[RANDOM_INTS.length];
89          for (int i = 0; i < RANDOM_BYTES.length; i++) {
90              RANDOM_BYTES[i] = (byte) RANDOM_INTS[i];
91          }
92      }
93  
94      // Tests match the declared order of the class:
95      // hash32
96      // hash64
97      // hash128
98      // IncrementalHash32
99      //
100     // There is no reference for the hash64 method. It is not in the original murmurhash3 code.
101     // Thus hash64 is not extensively tested and the reference results are assumed to be from the
102     // Apache Hive project which was the source of the method.
103 
104     /**
105      * Assert {@link IncrementalHash32} returns the same values as
106      * {@link MurmurHash3#hash32(byte[], int, int, int)}.
107      *
108      * <p>
109      * The bytes are added to the incremental hash in the given blocks.
110      * </p>
111      *
112      * @param bytes The bytes
113      * @param seed The seed
114      * @param blocks The blocks
115      */
116     private static void assertIncrementalHash32(final byte[] bytes, final int seed, final int... blocks) {
117         int offset = 0;
118         int total = 0;
119         final IncrementalHash32 inc = new IncrementalHash32();
120         inc.start(seed);
121         for (final int block : blocks) {
122             total += block;
123             final int h1 = MurmurHash3.hash32(bytes, 0, total, seed);
124             inc.add(bytes, offset, block);
125             offset += block;
126             final int h2 = inc.end();
127             assertEquals(h1, h2, "Hashes differ");
128             assertEquals(h1, inc.end(), "Hashes differ after no additional data");
129         }
130     }
131 
132     /**
133      * Assert {@link IncrementalHash32x86} returns the same values as
134      * {@link MurmurHash3#hash32x86(byte[], int, int, int)}.
135      *
136      * <p>
137      * The bytes are added to the incremental hash in the given blocks.
138      * </p>
139      *
140      * @param bytes The bytes
141      * @param seed The seed
142      * @param blocks The blocks
143      */
144     private static void assertIncrementalHash32x86(final byte[] bytes, final int seed, final int... blocks) {
145         int offset = 0;
146         int total = 0;
147         final IncrementalHash32x86 inc = new IncrementalHash32x86();
148         inc.start(seed);
149         for (final int block : blocks) {
150             total += block;
151             final int h1 = MurmurHash3.hash32x86(bytes, 0, total, seed);
152             inc.add(bytes, offset, block);
153             offset += block;
154             final int h2 = inc.end();
155             assertEquals(h1, h2, "Hashes differ");
156             assertEquals(h1, inc.end(), "Hashes differ after no additional data");
157         }
158     }
159 
160     /**
161      * Creates a set of long values to test the hash utility methods.
162      *
163      * @return The data
164      */
165     private static long[] createLongTestData() {
166         final long[] data = new long[100];
167         // Edge cases:
168         data[0] = 0;
169         data[1] = Long.MIN_VALUE;
170         data[2] = Long.MAX_VALUE;
171         data[3] = -1L;
172         for (int i = 4; i < data.length; i++) {
173             data[i] = ThreadLocalRandom.current().nextLong();
174         }
175         return data;
176     }
177 
178     /**
179      * Creates the random blocks of data to process up to max length.
180      *
181      * @param maxLength The max length
182      * @return The blocks
183      */
184     private static int[] createRandomBlocks(final int maxLength) {
185         final int[] blocks = new int[20];
186         int count = 0;
187         int length = 0;
188         while (count < blocks.length && length < maxLength) {
189             // range of 1 to 8 for up to two 4 byte blocks
190             final int size = ThreadLocalRandom.current().nextInt(1, 9);
191             blocks[count++] = size;
192             length += size;
193         }
194         return Arrays.copyOf(blocks, count);
195     }
196 
197     /**
198      * Check if the bytes are negative in the given range.
199      *
200      * @param bytes The bytes
201      * @param start The start
202      * @param length The length
203      * @return true, if negative bytes exist
204      */
205     private static boolean negativeBytes(final byte[] bytes, final int start, final int length) {
206         for (int i = start; i < start + length; i++) {
207             if (bytes[i] < 0) {
208                 return true;
209             }
210         }
211         return false;
212     }
213 
214     /**
215      * Test the {@link MurmurHash3#hash128(byte[])} algorithm.
216      *
217      * <p>
218      * Reference data is taken from the Python library {@code mmh3}.
219      * </p>
220      *
221      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
222      */
223     @Test
224     void testHash128() {
225         // mmh3.hash64(bytes, 104729)
226         assertArrayEquals(new long[] {-5614308156300707300L, -4165733009867452172L},
227             MurmurHash3.hash128(RANDOM_BYTES));
228 
229         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
230         // 15 bytes remaining.
231         // for x in range(0, 32):
232         //   print(mmh3.hash64(bytes[:x], 104729), ',')
233         final long[][] answers = {
234             {-7122613646888064702L, -8341524471658347240L}, {5659994275039884826L, -962335545730945195L},
235             {-7641758224504050283L, 4083131074855072837L}, {-9123564721037921804L, -3321998102976419641L},
236             {-7999620158275145567L, -7769992740725283391L}, {2419143614837736468L, -5474637306496300103L},
237             {7781591175729494939L, -9023178611551692650L}, {-3431043156265556247L, -6589990064676612981L},
238             {6315693694262400182L, -6219942557302821890L}, {-8249934145502892979L, -5646083202776239948L},
239             {7500109050276796947L, 5350477981718987260L}, {-6102338673930022315L, 3413065069102535261L},
240             {-6440683413407781313L, -2374360388921904146L}, {-3071236194203069122L, 7531604855739305895L},
241             {-7629408037856591130L, -4070301959951145257L}, {860008171111471563L, -9026008285726889896L},
242             {8059667613600971894L, 3236009436748930210L}, {1833746055900036985L, 1418052485321768916L},
243             {8161230977297923537L, -2668130155009407119L}, {3653111839268905630L, 5525563908135615453L},
244             {-9163026480602019754L, 6819447647029564735L}, {1102346610654592779L, -6881918401879761029L},
245             {-3109499571828331931L, -3782255367340446228L}, {-7467915444290531104L, 4704551260862209500L},
246             {1237530251176898868L, 6144786892208594932L}, {2347717913548230384L, -7461066668225718223L},
247             {-7963311463560798404L, 8435801462986138227L}, {-7493166089060196513L, 8163503673197886404L},
248             {6807249306539951962L, -1438886581269648819L}, {6752656991043418179L, 6334147827922066123L},
249             {-4534351735605790331L, -4530801663887858236L}, {-7886946241830957955L, -6261339648449285315L}
250         };
251         for (int i = 0; i < answers.length; i++) {
252             final byte[] bytes = Arrays.copyOf(RANDOM_BYTES, i);
253             assertArrayEquals(answers[i], MurmurHash3.hash128(bytes));
254         }
255     }
256 
257     /**
258      * Test the {@link MurmurHash3#hash128(String)} algorithm. This only tests it can return
259      * the same value as {@link MurmurHash3#hash128(byte[], int, int, int)} if the string
260      * is converted to bytes using the method {@link String#getBytes()}.
261      *
262      * <p>
263      * The test uses random strings created with random Unicode code points.
264      * </p>
265      */
266     @Test
267     void testHash128String() {
268         final int seed = 104729;
269         // Range is end exclusive so this is random strings of length 1-10
270         final int minSize = 1;
271         final int maxSize = 11;
272         // The Unicode Standard, Version 7.0, contains 112,956 characters
273         final int codePoints = 112956;
274         final char[] chars = new char[(maxSize - minSize) * 2];
275         for (int i = 0; i < 1000; i++) {
276             int pos = 0;
277             final int size = ThreadLocalRandom.current().nextInt(minSize, maxSize);
278             for (int j = 0; j < size; j++) {
279                 final int codePoint = ThreadLocalRandom.current().nextInt(codePoints);
280                 pos += Character.toChars(codePoint, chars, pos);
281             }
282             final String text = String.copyValueOf(chars, 0, pos);
283             final byte[] bytes = StringUtils.getBytesUtf8(text);
284             final long[] h1 = MurmurHash3.hash128(bytes, 0, bytes.length, seed);
285             final long[] h2 = MurmurHash3.hash128(text);
286             assertArrayEquals(h1, h2);
287         }
288     }
289 
290     /**
291      * Test the {@link MurmurHash3#hash128(byte[], int, int, int)} algorithm.
292      *
293      * <p>
294      * Explicit test for a negative seed. The original implementation has a sign extension error
295      * for negative seeds. This test is here to maintain behavioral compatibility of the
296      * broken deprecated method.
297      * </p>
298      */
299     @Test
300     void testHash128WithOffsetLengthAndNegativeSeed() {
301         // Seed can be negative
302         final int seed = -42;
303         final int offset = 13;
304 
305         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
306         // 15 bytes remaining.
307         final long[][] answers = {
308                 {5954234972212089025L, 3342108296337967352L},
309                 {8501094764898402923L, 7873951092908129427L}, {-3334322685492296196L, -2842715922956549478L},
310                 {-2177918982459519644L, -1612349961980368636L}, {4172870320608886992L, -4177375712254136503L},
311                 {7546965006884307324L, -5222114032564054641L}, {-2885083166621537267L, -2069868899915344482L},
312                 {-2397098497873118851L, 4990578036999888806L}, {-706479374719025018L, 7531201699171849870L},
313                 {6487943141157228609L, 3576221902299447884L}, {6671331646806999453L, -3428049860825046360L},
314                 {-8700221138601237020L, -2748450904559980545L}, {-9028762509863648063L, 6130259301750313402L},
315                 {729958512305702590L, -36367317333638988L}, {-3803232241584178983L, -4257744207892929651L},
316                 {5734013720237474696L, -760784490666078990L}, {-6097477411153026656L, 625288777006549065L},
317                 {1320365359996757504L, -2251971390373072541L}, {5551441703887653022L, -3516892619809375941L},
318                 {698875391638415033L, 3456972931370496131L}, {5874956830271303805L, -6074126509360777023L},
319                 {-7030758398537734781L, -3174643657101295554L}, {6835789852786226556L, 7245353136839389595L},
320                 {-7755767580598793204L, -6680491060945077989L}, {-3099789923710523185L, -2751080516924952518L},
321                 {-7772046549951435453L, 5263206145535830491L}, {7458715941971015543L, 5470582752171544854L},
322                 {-7753394773760064468L, -2330157750295630617L}, {-5899278942232791979L, 6235686401271389982L},
323                 {4881732293467626532L, 2617335658565007304L}, {-5722863941703478257L, -5424475653939430258L},
324                 {-3703319768293496315L, -2124426428486426443L}
325         };
326         for (int i = 0; i < answers.length; i++) {
327             assertArrayEquals(answers[i], MurmurHash3.hash128(RANDOM_BYTES, offset, i, seed), "Length: " + i);
328         }
329     }
330 
331     /**
332      * Test the {@link MurmurHash3#hash128(byte[], int, int, int)} algorithm.
333      *
334      * <p>
335      * Reference data is taken from the Python library {@code mmh3}.
336      * </p>
337      *
338      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
339      */
340     @Test
341     void testHash128WithOffsetLengthAndSeed() {
342         // Seed must be positive
343         final int seed = 42;
344         final int offset = 13;
345 
346         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
347         // 15 bytes remaining.
348         // for x in range(0, 32):
349         //   print(mmh3.hash64(bytes[13:x+13], 42), ',')
350         final long[][] answers = {
351             {-1140915396076141277L, -3386313222241793095L},
352             {2745805417334040752L, -3045882272665292331L}, {6807939080212835946L, -1975749467247671127L},
353             {-7924884987449335214L, -4468571497642087939L}, {3005389733967167773L, -5809440073240597398L},
354             {8032745196600164727L, 4545709434702374224L}, {2095398623732573832L, 1778447136435513908L},
355             {4492723708121417255L, -7411125500882394867L}, {8467397417110552178L, -1503802302645548949L},
356             {4189760269121918355L, -8004336343217265057L}, {4939298084211301953L, -8419135013628844658L},
357             {5497136916151148085L, -394028342910298191L}, {3405983294878231737L, -3216533807498089078L},
358             {5833223403351466775L, -1792451370239813325L}, {7730583391236194819L, 5356157313842354092L},
359             {3111977482488580945L, -3119414725698132191L}, {3314524606404365027L, -1923219843083192742L},
360             {7299569240140613949L, 4176392429810027494L}, {6398084683727166117L, 7703960505857395788L},
361             {-8594572031068184774L, 4394224719145783692L}, {-7589785442804461713L, 4110439243215224554L},
362             {-5343610105946840628L, -4423992782020122809L}, {-522490326525787270L, 289136460641968781L},
363             {-5320637070354802556L, -7845553044730489027L}, {1344456408744313334L, 3803048032054968586L},
364             {1131205296221907191L, -6256656049039287019L}, {8583339267101027117L, 8934225022848628726L},
365             {-6379552869905441749L, 8973517768420051734L}, {5076646564516328801L, 8561479196844000567L},
366             {-4610341636137642517L, -6694266039505142069L}, {-758896383254029789L, 4050360662271552727L},
367             {-6123628195475753507L, 4283875822581966645L}
368         };
369         for (int i = 0; i < answers.length; i++) {
370             assertArrayEquals(answers[i], MurmurHash3.hash128(RANDOM_BYTES, offset, i, seed), "Length: " + i);
371         }
372     }
373 
374     /**
375      * Test the {@link MurmurHash3#hash128x64(byte[])} algorithm.
376      *
377      * <p>
378      * Reference data is taken from the Python library {@code mmh3}.
379      * </p>
380      *
381      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
382      */
383     @Test
384     void testHash128x64() {
385         // Note: Default seed is zero.
386 
387         // mmh3.hash64(bytes, 0)
388         assertArrayEquals(new long[] {1972113670104592209L, 5171809317673151911L},
389             MurmurHash3.hash128x64(RANDOM_BYTES));
390 
391         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
392         // 15 bytes remaining.
393         // for x in range(0, 32):
394         //   print(mmh3.hash64(bytes[:x], 0), ',')
395         final long[][] answers = {
396             {0L, 0L}, {-2808653841080383123L, -2531784594030660343L},
397             {-1284575471001240306L, -8226941173794461820L}, {1645529003294647142L, 4109127559758330427L},
398             {-4117979116203940765L, -8362902660322042742L}, {2559943399590596158L, 4738005461125350075L},
399             {-1651760031591552651L, -5386079254924224461L}, {-6208043960690815609L, 7862371518025305074L},
400             {-5150023478423646337L, 8346305334874564507L}, {7658274117911906792L, -4962914659382404165L},
401             {1309458104226302269L, 570003296096149119L}, {7440169453173347487L, -3489345781066813740L},
402             {-5698784298612201352L, 3595618450161835420L}, {-3822574792738072442L, 6878153771369862041L},
403             {3705084673301918328L, 3202155281274291907L}, {-6797166743928506931L, -4447271093653551597L},
404             {5240533565589385084L, -5575481185288758327L}, {-8467620131382649428L, -6450630367251114468L},
405             {3632866961828686471L, -5957695976089313500L}, {-6450283648077271139L, -7908632714374518059L},
406             {226350826556351719L, 8225586794606475685L}, {-2382996224496980401L, 2188369078123678011L},
407             {-1337544762358780825L, 7004253486151757299L}, {2889033453638709716L, -4099509333153901374L},
408             {-8644950936809596954L, -5144522919639618331L}, {-5628571865255520773L, -839021001655132087L},
409             {-5226774667293212446L, -505255961194269502L}, {1337107025517938142L, 3260952073019398505L},
410             {9149852874328582511L, 1880188360994521535L}, {-4035957988359881846L, -7709057850766490780L},
411             {-3842593823306330815L, 3805147088291453755L}, {4030161393619149616L, -2813603781312455238L}
412         };
413         for (int i = 0; i < answers.length; i++) {
414             final byte[] bytes = Arrays.copyOf(RANDOM_BYTES, i);
415             assertArrayEquals(answers[i], MurmurHash3.hash128x64(bytes));
416         }
417     }
418 
419     /**
420      * Test the {@link MurmurHash3#hash128x64(byte[], int, int, int)} algorithm.
421      *
422      * <p>
423      * Explicit test for a negative seed. The original implementation has a sign extension error
424      * for negative seeds.
425      * </p>
426      *
427      * <p>
428      * Reference data is taken from the Python library {@code mmh3}.
429      * </p>
430      *
431      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
432      */
433     @Test
434     void testHash128x64WithOffsetLengthAndNegativeSeed() {
435         // Seed can be negative
436         final int seed = -42;
437         final int offset = 13;
438 
439         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
440         // 15 bytes remaining.
441         // for x in range(0, 32):
442         //   print(mmh3.hash64(bytes[13:x+13], -42), ',')
443         final long[][] answers = {
444             {7182599573337898253L, -6490979146667806054L},
445             {-461284136738605467L, 7073284964362976233L}, {-3090354666589400212L, 2978755180788824810L},
446             {5052807367580803906L, -4497188744879598335L}, {5003711854877353474L, -6616808651483337088L},
447             {2043501804923817748L, -760668448196918637L}, {6813003268375229932L, -1818545210475363684L},
448             {4488070015393027916L, 8520186429078977003L}, {4709278711722456062L, -2262080641289046033L},
449             {-5944514262756048380L, 5968714500873552518L}, {-2304397529301122510L, 6451500469518446488L},
450             {-1054078041081348909L, -915114408909600132L}, {1300471646869277217L, -399493387666437046L},
451             {-2821780479886030222L, -9061571187511294733L}, {8005764841242557505L, 4135287855434326053L},
452             {318307346637037498L, -5355856739901286522L}, {3380719536119187025L, 1890890833937151467L},
453             {2691044185935730001L, 7963546423617895734L}, {-5277462388534000227L, 3613853764390780573L},
454             {8504421722476165699L, 2058020162708103700L}, {-6578421288092422241L, 3311200163790829579L},
455             {-5915037218487974215L, -7385137075895184179L}, {659642911937398022L, 854071824595671049L},
456             {-7007237968866727198L, 1372258010932080058L}, {622891376282772539L, -4140783491297489868L},
457             {8357110718969014985L, -4737117827581590306L}, {2208857857926305405L, -8360240839768465042L},
458             {858120048221036376L, -5822288789703639119L}, {-1988334009458340679L, 1262479472434068698L},
459             {-8580307083590783934L, 3634449965473715778L}, {6705664584730187559L, 5192304951463791556L},
460             {-6426410954037604142L, -1579122709247558101L}
461         };
462         for (int i = 0; i < answers.length; i++) {
463             assertArrayEquals(answers[i], MurmurHash3.hash128x64(RANDOM_BYTES, offset, i, seed), "Length: " + i);
464         }
465     }
466 
467     /**
468      * Test the {@link MurmurHash3#hash128x64(byte[], int, int, int)} algorithm.
469      *
470      * <p>
471      * Reference data is taken from the Python library {@code mmh3}.
472      * </p>
473      *
474      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
475      */
476     @Test
477     void testHash128x64WithOffsetLengthAndSeed() {
478         // Seed can be positive
479         final int seed = 42;
480         final int offset = 13;
481 
482         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
483         // 15 bytes remaining.
484         // for x in range(0, 32):
485         //   print(mmh3.hash64(bytes[13:x+13], 42), ',')
486         final long[][] answers = {
487             {-1140915396076141277L, -3386313222241793095L},
488             {2745805417334040752L, -3045882272665292331L}, {6807939080212835946L, -1975749467247671127L},
489             {-7924884987449335214L, -4468571497642087939L}, {3005389733967167773L, -5809440073240597398L},
490             {8032745196600164727L, 4545709434702374224L}, {2095398623732573832L, 1778447136435513908L},
491             {4492723708121417255L, -7411125500882394867L}, {8467397417110552178L, -1503802302645548949L},
492             {4189760269121918355L, -8004336343217265057L}, {4939298084211301953L, -8419135013628844658L},
493             {5497136916151148085L, -394028342910298191L}, {3405983294878231737L, -3216533807498089078L},
494             {5833223403351466775L, -1792451370239813325L}, {7730583391236194819L, 5356157313842354092L},
495             {3111977482488580945L, -3119414725698132191L}, {3314524606404365027L, -1923219843083192742L},
496             {7299569240140613949L, 4176392429810027494L}, {6398084683727166117L, 7703960505857395788L},
497             {-8594572031068184774L, 4394224719145783692L}, {-7589785442804461713L, 4110439243215224554L},
498             {-5343610105946840628L, -4423992782020122809L}, {-522490326525787270L, 289136460641968781L},
499             {-5320637070354802556L, -7845553044730489027L}, {1344456408744313334L, 3803048032054968586L},
500             {1131205296221907191L, -6256656049039287019L}, {8583339267101027117L, 8934225022848628726L},
501             {-6379552869905441749L, 8973517768420051734L}, {5076646564516328801L, 8561479196844000567L},
502             {-4610341636137642517L, -6694266039505142069L}, {-758896383254029789L, 4050360662271552727L},
503             {-6123628195475753507L, 4283875822581966645L}
504         };
505         for (int i = 0; i < answers.length; i++) {
506             assertArrayEquals(answers[i], MurmurHash3.hash128x64(RANDOM_BYTES, offset, i, seed), "Length: " + i);
507         }
508     }
509 
510     /**
511      * Test the {@link MurmurHash3#hash32(byte[])} algorithm.
512      *
513      * <p>
514      * Reference data is taken from the Python library {@code mmh3}.
515      * </p>
516      *
517      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
518      */
519     @Test
520     void testHash32() {
521         // mmh3.hash(bytes, 104729)
522         assertEquals(1905657630, MurmurHash3.hash32(RANDOM_BYTES));
523 
524         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
525         // 15 bytes remaining.
526         // for x in range(0, 32):
527         // print(mmh3.hash(bytes[:x], 104729), ',')
528         final int[] answers = { -965378730, 418246248, 1175981702, -616767012, -12304673, 1697005142, -1212417875, -420043393, -826068069, -1721451528,
529                 -544986914, 892942691, 27535194, 974863697, 1835661694, -894915836, 1826914566, -677571679, 1218764493, -375719050, -1320048170, -503583763,
530                 1321750696, -175065786, -496878386, -12065683, 512351473, 716560510, -1944803590, 10253199, 1105638211, 525704533, };
531         for (int i = 0; i < answers.length; i++) {
532             final byte[] bytes = Arrays.copyOf(RANDOM_BYTES, i);
533             // Known bug: Incorrect result for non modulus of 4 byte arrays if there are
534             // negative bytes
535             if (i % 4 == 0 || !negativeBytes(bytes, i / 4 * 4, i % 4)) {
536                 assertEquals(answers[i], MurmurHash3.hash32(bytes));
537             } else {
538                 assertNotEquals(answers[i], MurmurHash3.hash32(bytes));
539             }
540         }
541     }
542 
543     /**
544      * Test the convenience method {@link MurmurHash3#hash32(long)} works as documented.
545      */
546     @Test
547     void testHash32Long() {
548         // As described in the Javadoc
549         final int offset = 0;
550         final int seed = 104729;
551 
552         final int length = Long.BYTES;
553         final ByteBuffer buffer = ByteBuffer.allocate(length);
554         final byte[] bytes = buffer.array();
555         final long[] data = createLongTestData();
556         for (final long i : data) {
557             buffer.putLong(0, i);
558             assertEquals(MurmurHash3.hash32x86(bytes, offset, length, seed), MurmurHash3.hash32(i));
559         }
560     }
561 
562     /**
563      * Test the convenience method {@link MurmurHash3#hash32(long, long)} works as documented.
564      */
565     @Test
566     void testHash32LongLong() {
567         // As described in the Javadoc
568         final int offset = 0;
569         final int seed = 104729;
570 
571         final int length = Long.BYTES * 2;
572         final ByteBuffer buffer = ByteBuffer.allocate(length);
573         final byte[] bytes = buffer.array();
574         final long[] data = createLongTestData();
575         for (final long i : data) {
576             for (final long j : data) {
577                 buffer.putLong(0, i);
578                 buffer.putLong(Long.BYTES, j);
579                 assertEquals(MurmurHash3.hash32x86(bytes, offset, length, seed), MurmurHash3.hash32(i, j));
580             }
581         }
582     }
583 
584     /**
585      * Test the convenience method {@link MurmurHash3#hash32(long, long, int)} works as documented.
586      */
587     @Test
588     void testHash32LongLongSeed() {
589         // As described in the Javadoc
590         final int offset = 0;
591         final int seed = 104729;
592 
593         final int length = Long.BYTES * 2;
594         final ByteBuffer buffer = ByteBuffer.allocate(length);
595         final byte[] bytes = buffer.array();
596         final long[] data = createLongTestData();
597         for (final long i : data) {
598             for (final long j : data) {
599                 buffer.putLong(0, i);
600                 buffer.putLong(Long.BYTES, j);
601                 assertEquals(MurmurHash3.hash32x86(bytes, offset, length, seed), MurmurHash3.hash32(i, j, seed));
602             }
603         }
604     }
605 
606     /**
607      * Test the convenience method {@link MurmurHash3#hash32(long, int)} works as documented.
608      */
609     @Test
610     void testHash32LongSeed() {
611         // As described in the Javadoc
612         final int offset = 0;
613         final int seed = 104729;
614 
615         final int length = Long.BYTES;
616         final ByteBuffer buffer = ByteBuffer.allocate(length);
617         final byte[] bytes = buffer.array();
618         final long[] data = createLongTestData();
619         for (final long i : data) {
620             buffer.putLong(0, i);
621             assertEquals(MurmurHash3.hash32x86(bytes, offset, length, seed), MurmurHash3.hash32(i, seed));
622         }
623     }
624 
625     /**
626      * Test the {@link MurmurHash3#hash32(String)} algorithm. This only tests it can return the same value as {@link MurmurHash3#hash32(byte[], int, int, int)}
627      * if the string is converted to bytes using the method {@link String#getBytes()}.
628      *
629      * <p>
630      * The test uses random strings created with random Unicode code points.
631      * </p>
632      */
633     @Test
634     void testHash32String() {
635         final int seed = 104729;
636         // Range is end exclusive so this is random strings of length 1-10
637         final int minSize = 1;
638         final int maxSize = 11;
639         // The Unicode Standard, Version 7.0, contains 112,956 characters
640         final int codePoints = 112956;
641         final char[] chars = new char[(maxSize - minSize) * 2];
642         for (int i = 0; i < 1000; i++) {
643             int pos = 0;
644             final int size = ThreadLocalRandom.current().nextInt(minSize, maxSize);
645             for (int j = 0; j < size; j++) {
646                 final int codePoint = ThreadLocalRandom.current().nextInt(codePoints);
647                 pos += Character.toChars(codePoint, chars, pos);
648             }
649             final String text = String.copyValueOf(chars, 0, pos);
650             final byte[] bytes = StringUtils.getBytesUtf8(text);
651             final int h1 = MurmurHash3.hash32(bytes, 0, bytes.length, seed);
652             final int h2 = MurmurHash3.hash32(text);
653             assertEquals(h1, h2);
654         }
655     }
656 
657     /**
658      * Test the {@link MurmurHash3#hash32(byte[], int)} algorithm.
659      *
660      * <p>
661      * Reference data is taken from the Python library {@code mmh3}.
662      * </p>
663      *
664      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
665      */
666     @Test
667     void testHash32WithLength() {
668         // mmh3.hash(bytes, 104729)
669         assertEquals(1905657630, MurmurHash3.hash32(RANDOM_BYTES, RANDOM_BYTES.length));
670 
671         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
672         // 15 bytes remaining.
673         // for x in range(0, 32):
674         // print(mmh3.hash(bytes[:x], 104729), ',')
675         final int[] answers = { -965378730, 418246248, 1175981702, -616767012, -12304673, 1697005142, -1212417875, -420043393, -826068069, -1721451528,
676                 -544986914, 892942691, 27535194, 974863697, 1835661694, -894915836, 1826914566, -677571679, 1218764493, -375719050, -1320048170, -503583763,
677                 1321750696, -175065786, -496878386, -12065683, 512351473, 716560510, -1944803590, 10253199, 1105638211, 525704533, };
678         for (int i = 0; i < answers.length; i++) {
679             // Known bug: Incorrect result for non modulus of 4 byte arrays if there are
680             // negative bytes
681             if (i % 4 == 0 || !negativeBytes(RANDOM_BYTES, i / 4 * 4, i % 4)) {
682                 assertEquals(answers[i], MurmurHash3.hash32(RANDOM_BYTES, i));
683             } else {
684                 assertNotEquals(answers[i], MurmurHash3.hash32(RANDOM_BYTES, i));
685             }
686         }
687     }
688 
689     /**
690      * Test the {@link MurmurHash3#hash32(byte[], int, int)} algorithm.
691      *
692      * <p>
693      * Reference data is taken from the Python library {@code mmh3}.
694      * </p>
695      *
696      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
697      */
698     @Test
699     void testHash32WithLengthAndSeed() {
700         final int seed = -42;
701         // mmh3.hash(bytes, -42)
702         assertEquals(1693958011, MurmurHash3.hash32(RANDOM_BYTES, RANDOM_BYTES.length, seed));
703 
704         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
705         // 15 bytes remaining.
706         // for x in range(0, 32):
707         // print(mmh3.hash(bytes[:x], -42), ',')
708         final int[] answers = { 192929823, 7537536, -99368911, -1261039957, -1719251056, -399594848, 372285930, -80756529, 1770924588, -1071759082, 1832217706,
709                 1921413466, 1701676113, 675584253, 1620634486, 427719405, -973727623, 533209078, 136016960, 1947798330, 428635832, -1125743884, 793211715,
710                 -2068889169, -136818786, -720841364, -891446378, 1990860976, -710528065, -1602505694, -1493714677, 1911121524, };
711         for (int i = 0; i < answers.length; i++) {
712             // Known bug: Incorrect result for non modulus of 4 byte arrays if there are
713             // negative bytes
714             if (i % 4 == 0 || !negativeBytes(RANDOM_BYTES, i / 4 * 4, i % 4)) {
715                 assertEquals(answers[i], MurmurHash3.hash32(RANDOM_BYTES, i, seed));
716             } else {
717                 assertNotEquals(answers[i], MurmurHash3.hash32(RANDOM_BYTES, i, seed));
718             }
719         }
720     }
721 
722     /**
723      * Test the {@link MurmurHash3#hash32(byte[], int, int, int)} algorithm.
724      *
725      * <p>
726      * Reference data is taken from the Python library {@code mmh3}.
727      * </p>
728      *
729      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
730      */
731     @Test
732     void testHash32WithOffsetLengthAndSeed() {
733         final int seed = -42;
734         final int offset = 13;
735 
736         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
737         // 15 bytes remaining.
738         // for x in range(0, 32):
739         // print(mmh3.hash(bytes[13:x+13], -42), ',')
740         final int[] answers = { 192929823, -27171978, -1282326280, -816314453, -1176217753, -1904531247, 1962794233, -1302316624, -1151850323, -1464386748,
741                 -369299427, 972232488, 1747314487, 2137398916, 690986564, -1985866226, -678669121, -2123325690, -253319081, 46181235, 656058278, 1401175653,
742                 1750113912, -1567219725, 2032742772, -2024269989, -305340794, 1161737942, -661265418, 172838872, -650122718, -1934812417, };
743         for (int i = 0; i < answers.length; i++) {
744             // Known bug: Incorrect result for non modulus of 4 byte arrays if there are
745             // negative bytes
746             if (i % 4 == 0 || !negativeBytes(RANDOM_BYTES, offset + i / 4 * 4, i % 4)) {
747                 assertEquals(answers[i], MurmurHash3.hash32(RANDOM_BYTES, offset, i, seed));
748             } else {
749                 assertNotEquals(answers[i], MurmurHash3.hash32(RANDOM_BYTES, offset, i, seed));
750             }
751         }
752     }
753 
754     /**
755      * Test to demonstrate the errors in {@link MurmurHash3#hash32(byte[], int, int, int)} if the final 1, 2, or 3 bytes are negative.
756      */
757     @Test
758     void testHash32WithTrailingNegativeSignedBytesIsInvalid() {
759         // import mmh3
760         // import numpy as np
761         // mmh3.hash(np.uint8([-1]))
762         // mmh3.hash(np.uint8([0, -1]))
763         // mmh3.hash(np.uint8([0, 0, -1]))
764         // mmh3.hash(np.uint8([-1, 0]))
765         // mmh3.hash(np.uint8([-1, 0, 0]))
766         // mmh3.hash(np.uint8([0, -1, 0]))
767         assertNotEquals(-43192051, MurmurHash3.hash32(new byte[] { -1 }, 0, 1, 0));
768         assertNotEquals(-582037868, MurmurHash3.hash32(new byte[] { 0, -1 }, 0, 2, 0));
769         assertNotEquals(922088087, MurmurHash3.hash32(new byte[] { 0, 0, -1 }, 0, 3, 0));
770         assertNotEquals(-1309567588, MurmurHash3.hash32(new byte[] { -1, 0 }, 0, 2, 0));
771         assertNotEquals(-363779670, MurmurHash3.hash32(new byte[] { -1, 0, 0 }, 0, 3, 0));
772         assertNotEquals(-225068062, MurmurHash3.hash32(new byte[] { 0, -1, 0 }, 0, 3, 0));
773     }
774 
775     /**
776      * Test the {@link MurmurHash3#hash32x86(byte[])} algorithm.
777      *
778      * <p>
779      * Reference data is taken from the Python library {@code mmh3}.
780      * </p>
781      *
782      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
783      */
784     @Test
785     void testHash32x86() {
786         // Note: Default seed is zero.
787 
788         // mmh3.hash(bytes, 0)
789         assertEquals(1546271276, MurmurHash3.hash32x86(RANDOM_BYTES));
790 
791         // Test with all sizes up to 31 bytes. This ensures a full round of 16-bytes plus up to
792         // 15 bytes remaining.
793         // for x in range(0, 32):
794         // print(mmh3.hash(bytes[:x], 0), ',')
795         final int[] answers = { 0, -1353253853, 915381745, -734983419, 1271125654, -1042265893, -1204521619, 735845843, 138310876, -1918938664, 1399647898,
796                 -1126342309, 2067593280, 1220975287, 1941281084, -1289513180, 942412060, -618173583, -269546647, -1645631262, 1162379906, -1960125577,
797                 -1856773195, 1980513522, 1174612855, 905810751, 1044578220, -1758486689, -491393913, 839836946, -435014415, 2044851178 };
798         for (int i = 0; i < answers.length; i++) {
799             final byte[] bytes = Arrays.copyOf(RANDOM_BYTES, i);
800             assertEquals(answers[i], MurmurHash3.hash32x86(bytes));
801         }
802     }
803 
804     /**
805      * Test the {@link MurmurHash3#hash32x86(byte[], int, int, int)} algorithm.
806      *
807      * <p>
808      * Reference data is taken from the Python library {@code mmh3}.
809      * </p>
810      *
811      * @see <a href="https://pypi.org/project/mmh3/">mmh3</a>
812      */
813     @Test
814     void testHash32x86WithOffsetLengthAndSeed() {
815         // Data as above for testing MurmurHash3.hash32(byte[], int, int, int).
816         final int seed = -42;
817         final int offset = 13;
818         final int[] answers = { 192929823, -27171978, -1282326280, -816314453, -1176217753, -1904531247, 1962794233, -1302316624, -1151850323, -1464386748,
819                 -369299427, 972232488, 1747314487, 2137398916, 690986564, -1985866226, -678669121, -2123325690, -253319081, 46181235, 656058278, 1401175653,
820                 1750113912, -1567219725, 2032742772, -2024269989, -305340794, 1161737942, -661265418, 172838872, -650122718, -1934812417 };
821         for (int i = 0; i < answers.length; i++) {
822             assertEquals(answers[i], MurmurHash3.hash32x86(RANDOM_BYTES, offset, i, seed));
823         }
824     }
825 
826     /**
827      * Test to demonstrate {@link MurmurHash3#hash32x86(byte[], int, int, int)} is OK if the final 1, 2, or 3 bytes are negative.
828      */
829     @Test
830     void testHash32x86WithTrailingNegativeSignedBytes() {
831         // Data as above for testing MurmurHash3.hash32(byte[], int, int, int).
832         // This test uses assertEquals().
833         assertEquals(-43192051, MurmurHash3.hash32x86(new byte[] { -1 }, 0, 1, 0));
834         assertEquals(-582037868, MurmurHash3.hash32x86(new byte[] { 0, -1 }, 0, 2, 0));
835         assertEquals(922088087, MurmurHash3.hash32x86(new byte[] { 0, 0, -1 }, 0, 3, 0));
836         assertEquals(-1309567588, MurmurHash3.hash32x86(new byte[] { -1, 0 }, 0, 2, 0));
837         assertEquals(-363779670, MurmurHash3.hash32x86(new byte[] { -1, 0, 0 }, 0, 3, 0));
838         assertEquals(-225068062, MurmurHash3.hash32x86(new byte[] { 0, -1, 0 }, 0, 3, 0));
839     }
840 
841     /**
842      * Test the {@link MurmurHash3#hash64(byte[])} algorithm. Unknown origin of test data. It may be from the Apache Hive project.
843      */
844     @Test
845     void testHash64() {
846         final byte[] origin = StringUtils.getBytesUtf8(TEST_HASH64);
847         final long hash = MurmurHash3.hash64(origin);
848         assertEquals(5785358552565094607L, hash);
849     }
850 
851     /**
852      * Test the {@link MurmurHash3#hash64(byte[])} method is Murmur3-like but does not match the bits returned from {@link MurmurHash3#hash128(byte[])}.
853      *
854      * <p>
855      * The hash64 method is not in the MurmurHash3 reference code and has been inherited from the port from Apache Hive.
856      * <p>
857      */
858     @Test
859     void testHash64InNotEqualToHash128() {
860         for (int i = 0; i < 32; i++) {
861             final byte[] bytes = Arrays.copyOf(RANDOM_BYTES, i);
862             final long h1 = MurmurHash3.hash64(bytes);
863             final long[] hash = MurmurHash3.hash128(bytes);
864             assertNotEquals(hash[0], h1, "Did not expect hash64 to match upper bits of hash128");
865             assertNotEquals(hash[1], h1, "Did not expect hash64 to match lower bits of hash128");
866         }
867     }
868 
869     /**
870      * Test the {@link MurmurHash3#hash64(byte[], int, int)} algorithm. Unknown origin of test data. It may be from the Apache Hive project.
871      */
872     @Test
873     void testHash64WithOffsetAndLength() {
874         final byte[] origin = StringUtils.getBytesUtf8(TEST_HASH64);
875         final byte[] originOffset = new byte[origin.length + 150];
876         Arrays.fill(originOffset, (byte) 123);
877         System.arraycopy(origin, 0, originOffset, 150, origin.length);
878         final long hash = MurmurHash3.hash64(originOffset, 150, origin.length);
879         assertEquals(5785358552565094607L, hash);
880     }
881 
882     /**
883      * Test the hash64() helper methods that work directly on primitives work as documented. This test the methods return the same value as
884      * {@link MurmurHash3#hash64(byte[])} with the byte[] created from the same primitive data via a {@link ByteBuffer}.
885      */
886     @Test
887     void testHash64WithPrimitives() {
888         // As described in the Javadoc
889         final int offset = 0;
890         final int seed = 104729;
891 
892         final int iters = 1000;
893         final ByteBuffer shortBuffer = ByteBuffer.allocate(Short.BYTES);
894         final ByteBuffer intBuffer = ByteBuffer.allocate(Integer.BYTES);
895         final ByteBuffer longBuffer = ByteBuffer.allocate(Long.BYTES);
896         final byte[] shortBytes = shortBuffer.array();
897         final byte[] intBytes = intBuffer.array();
898         final byte[] longBytes = longBuffer.array();
899         for (int i = 0; i < iters; ++i) {
900             final long ln = ThreadLocalRandom.current().nextLong();
901             // Shift bits to bet different bytes
902             final int in = (int) (ln >>> 3);
903             final short sn = (short) (ln >>> 5);
904             shortBuffer.putShort(0, sn);
905             assertEquals(MurmurHash3.hash64(shortBytes, offset, shortBytes.length, seed), MurmurHash3.hash64(sn));
906             intBuffer.putInt(0, in);
907             assertEquals(MurmurHash3.hash64(intBytes, offset, intBytes.length, seed), MurmurHash3.hash64(in));
908             longBuffer.putLong(0, ln);
909             assertEquals(MurmurHash3.hash64(longBytes, offset, longBytes.length, seed), MurmurHash3.hash64(ln));
910         }
911     }
912 
913     /**
914      * Test {@link IncrementalHash32} returns the same values as {@link MurmurHash3#hash32(byte[], int, int, int)}.
915      */
916     @Test
917     void testIncrementalHash32() {
918         final byte[] bytes = new byte[1023];
919         ThreadLocalRandom.current().nextBytes(bytes);
920         // The seed does not matter
921         for (final int seed : new int[] { -567, 0, 6787990 }) {
922             // Cases are constructed to hit all edge cases of processing:
923             // Nothing added
924             assertIncrementalHash32(bytes, seed, 0, 0);
925             // Add single bytes
926             assertIncrementalHash32(bytes, seed, 1, 1, 1, 1, 1, 1, 1, 1);
927             // Leading unprocessed 1, 2, 3
928             assertIncrementalHash32(bytes, seed, 1, 4);
929             assertIncrementalHash32(bytes, seed, 2, 4);
930             assertIncrementalHash32(bytes, seed, 3, 4);
931             // Trailing unprocessed 1, 2, 3
932             assertIncrementalHash32(bytes, seed, 4, 1);
933             assertIncrementalHash32(bytes, seed, 4, 2);
934             assertIncrementalHash32(bytes, seed, 4, 3);
935             // Complete blocks
936             assertIncrementalHash32(bytes, seed, 4, 16, 64);
937             // Some random blocks
938             for (int i = 0; i < 10; i++) {
939                 assertIncrementalHash32x86(bytes, seed, createRandomBlocks(bytes.length));
940             }
941         }
942     }
943 
944     /**
945      * Test {@link IncrementalHash32x86} returns the same values as {@link MurmurHash3#hash32x86(byte[], int, int, int)}.
946      */
947     @Test
948     void testIncrementalHash32x86() {
949         final byte[] bytes = new byte[1023];
950         ThreadLocalRandom.current().nextBytes(bytes);
951         // The seed does not matter
952         for (final int seed : new int[] { -567, 0, 6787990 }) {
953             // Cases are constructed to hit all edge cases of processing:
954             // Nothing added
955             assertIncrementalHash32x86(bytes, seed, 0, 0);
956             // Add single bytes
957             assertIncrementalHash32x86(bytes, seed, 1, 1, 1, 1, 1, 1, 1, 1);
958             // Leading unprocessed 1, 2, 3
959             assertIncrementalHash32x86(bytes, seed, 1, 4);
960             assertIncrementalHash32x86(bytes, seed, 2, 4);
961             assertIncrementalHash32x86(bytes, seed, 3, 4);
962             // Trailing unprocessed 1, 2, 3
963             assertIncrementalHash32x86(bytes, seed, 4, 1);
964             assertIncrementalHash32x86(bytes, seed, 4, 2);
965             assertIncrementalHash32x86(bytes, seed, 4, 3);
966             // Complete blocks
967             assertIncrementalHash32x86(bytes, seed, 4, 16, 64);
968             // Some random blocks
969             for (int i = 0; i < 10; i++) {
970                 assertIncrementalHash32x86(bytes, seed, createRandomBlocks(bytes.length));
971             }
972         }
973     }
974 
975     /**
976      * This test hits an edge case where a very large number of bytes is added to the incremental hash. The data is constructed so that an integer counter of
977      * unprocessed bytes will overflow. If this is not handled correctly then the code throws an exception when it copies more data into the unprocessed bytes
978      * array.
979      */
980     @Test
981     void testIncrementalHashWithUnprocessedBytesAndHugeLengthArray() {
982         // Assert the test precondition that a large array added to unprocessed bytes
983         // will overflow an integer counter. We use the smallest hugeLength possible
984         // as some VMs cannot allocate maximum length arrays.
985         final int unprocessedSize = 3;
986         final int hugeLength = Integer.MAX_VALUE - 2;
987         assertTrue(unprocessedSize + hugeLength < 4, "This should overflow to negative");
988 
989         // Check the test can be run
990         byte[] bytes = null;
991         try {
992             bytes = new byte[hugeLength];
993         } catch (final OutOfMemoryError ignore) {
994             // Some VMs cannot allocate an array this large.
995             // Some test environments may not have enough available memory for this.
996         }
997         assumeTrue(bytes != null, "Cannot allocate array of length " + hugeLength);
998 
999         final IncrementalHash32x86 inc = new IncrementalHash32x86();
1000         inc.start(0);
1001         // Add bytes that should be unprocessed
1002         inc.add(bytes, 0, unprocessedSize);
1003         // Add a huge number of bytes to overflow an integer counter of unprocessed bytes.
1004         inc.add(bytes, 0, hugeLength);
1005     }
1006 }