| /* |
| * 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.datasketches.hash; |
| |
| import static java.lang.foreign.ValueLayout.JAVA_BYTE; |
| import static java.lang.foreign.ValueLayout.JAVA_CHAR_UNALIGNED; |
| import static java.lang.foreign.ValueLayout.JAVA_DOUBLE_UNALIGNED; |
| import static java.lang.foreign.ValueLayout.JAVA_FLOAT_UNALIGNED; |
| import static java.lang.foreign.ValueLayout.JAVA_INT_UNALIGNED; |
| import static java.lang.foreign.ValueLayout.JAVA_LONG_UNALIGNED; |
| import static java.lang.foreign.ValueLayout.JAVA_SHORT_UNALIGNED; |
| import static org.testng.Assert.assertEquals; |
| import static org.testng.Assert.assertTrue; |
| import static org.testng.Assert.fail; |
| |
| import java.lang.foreign.MemorySegment; |
| import java.util.Random; |
| |
| import org.testng.annotations.Test; |
| |
| /** |
| * @author Lee Rhodes |
| */ |
| public class MurmurHash3FFMTest { |
| private final Random rand = new Random(); |
| private static final int trials = 1 << 20; |
| |
| @Test |
| public void compareLongArrLong() { //long[] |
| final int arrLen = 3; |
| final int iPer = 8 / Long.BYTES; |
| final long[] key = new long[arrLen]; |
| for (int i = 0; i < trials; i++) { //trials |
| for (int j = 0; j < (arrLen / iPer); j++) { //longs |
| final long r = rand.nextLong(); |
| key[j] = r; |
| } |
| final long[] res1 = hashV1(key, 0); |
| final long[] res2 = hashV2(key, 0); |
| assertEquals(res2, res1); |
| } |
| } |
| |
| @Test |
| public void compareIntArr() { //int[] |
| final int bytes = Integer.BYTES; |
| final int arrLen = 6; |
| final int[] key = new int[arrLen]; |
| final int iPer = 8 / bytes; |
| final int nLongs = arrLen / iPer; |
| final int shift = 64 / iPer; |
| |
| for (int i = 0; i < trials; i++) { //trials |
| for (int j = 0; j < nLongs; j++) { //longs |
| final long r = rand.nextLong(); |
| for (int k = 0; k < iPer; k++) { //ints |
| final int shft = k * shift; |
| key[k] = (int) (r >>> shft); |
| } |
| } |
| final long[] res1 = hashV1(key, 0); |
| final long[] res2 = hashV2(key, 0); |
| assertEquals(res2, res1); |
| } |
| } |
| |
| @Test |
| public void compareCharArr() { //char[] |
| final int bytes = Character.BYTES; |
| final int arrLen = 12; |
| final char[] key = new char[arrLen]; |
| final int iPer = 8 / bytes; |
| final int nLongs = arrLen / iPer; |
| final int shift = 64 / iPer; |
| |
| for (int i = 0; i < trials; i++) { //trials |
| for (int j = 0; j < nLongs; j++) { //longs |
| final long r = rand.nextLong(); |
| for (int k = 0; k < iPer; k++) { //char |
| final int shft = k * shift; |
| key[k] = (char) (r >>> shft); |
| } |
| } |
| final long[] res1 = hashV1(key, 0); |
| final long[] res2 = hashV2(key, 0); |
| assertEquals(res2, res1); |
| } |
| } |
| |
| @Test |
| public void compareByteArr() { //byte[] |
| final int bytes = Byte.BYTES; |
| final int arrLen = 12; |
| final byte[] key = new byte[arrLen]; |
| final int iPer = 8 / bytes; |
| final int nLongs = arrLen / iPer; |
| final int shift = 64 / iPer; |
| |
| for (int i = 0; i < trials; i++) { //trials |
| for (int j = 0; j < nLongs; j++) { //longs |
| final long r = rand.nextLong(); |
| for (int k = 0; k < iPer; k++) { //bytes |
| final int shft = k * shift; |
| key[k] = (byte) (r >>> shft); |
| } |
| } |
| final long[] res1 = hashV1(key, 0); |
| final long[] res2 = hashV2(key, 0); |
| assertEquals(res2, res1); |
| } |
| } |
| |
| @Test |
| public void compareLongVsLongArr() { |
| final int arrLen = 1; |
| final long[] key = new long[arrLen]; |
| final long[] out = new long[2]; |
| for (int i = 0; i < trials; i++) { //trials |
| final long r = rand.nextLong(); |
| key[0] = r; |
| final long[] res1 = hashV1(key, 0); |
| final long[] res2 = hashV2(r, 0, out); |
| assertEquals(res2, res1); |
| } |
| } |
| |
| private static final long[] hashV1(final long[] key, final long seed) { |
| return MurmurHash3.hash(key, seed); |
| } |
| |
| private static final long[] hashV1(final int[] key, final long seed) { |
| return MurmurHash3.hash(key, seed); |
| } |
| |
| private static final long[] hashV1(final char[] key, final long seed) { |
| return MurmurHash3.hash(key, seed); |
| } |
| |
| private static final long[] hashV1(final byte[] key, final long seed) { |
| return MurmurHash3.hash(key, seed); |
| } |
| |
| private static final long[] hashV2(final long[] key, final long seed) { |
| return MurmurHash3FFM.hash(key, seed); |
| } |
| |
| private static final long[] hashV2(final int[] key2, final long seed) { |
| return MurmurHash3FFM.hash(key2, seed); |
| } |
| |
| private static final long[] hashV2(final char[] key, final long seed) { |
| return MurmurHash3FFM.hash(key, seed); |
| } |
| |
| private static final long[] hashV2(final byte[] key, final long seed) { |
| return MurmurHash3FFM.hash(key, seed); |
| } |
| |
| //V2 single primitives |
| |
| private static final long[] hashV2(final long key, final long seed, final long[] out) { |
| return MurmurHash3FFM.hash(key, seed, out); |
| } |
| |
| // private static final long[] hashV2(double key, long seed, long[] out) { |
| // return MurmurHash3v4.hash(key, seed, out); |
| // } |
| |
| // private static final long[] hashV2(String key, long seed, long[] out) { |
| // return MurmurHash3v4.hash(key, seed, out); |
| // } |
| |
| @Test |
| public void offsetChecks() { |
| final long seed = 12345; |
| final int blocks = 6; |
| final int cap = blocks * 16; |
| |
| long[] hash1 = new long[2]; |
| long[] hash2; |
| |
| final MemorySegment wseg = MemorySegment.ofArray(new byte[cap]); |
| for (int i = 0; i < cap; i++) { wseg.set(JAVA_BYTE, i, (byte)(-128 + i)); } |
| |
| for (int offset = 0; offset < 16; offset++) { |
| final int arrLen = cap - offset; |
| hash1 = MurmurHash3FFM.hash(wseg, offset, arrLen, seed, hash1); |
| final byte[] byteArr2 = new byte[arrLen]; |
| MemorySegment.copy(wseg, JAVA_BYTE, offset, byteArr2, 0, arrLen); |
| hash2 = MurmurHash3.hash(byteArr2, seed); |
| assertEquals(hash1, hash2); |
| } |
| } |
| |
| @Test |
| public void byteArrChecks() { |
| final long seed = 0; |
| final int offset = 0; |
| final int bytes = 1024; |
| |
| long[] hash2 = new long[2]; |
| |
| for (int j = 1; j < bytes; j++) { |
| final byte[] in = new byte[bytes]; |
| |
| final MemorySegment wseg = MemorySegment.ofArray(in); |
| for (int i = 0; i < j; i++) { wseg.set(JAVA_BYTE, i, (byte) (-128 + i)); } |
| |
| final long[] hash1 = MurmurHash3.hash(in, seed); |
| hash2 = MurmurHash3FFM.hash(wseg, offset, bytes, seed, hash2); |
| final long[] hash3 = MurmurHash3FFM.hash(in, seed); |
| |
| assertEquals(hash1, hash2); |
| assertEquals(hash1, hash3); |
| } |
| } |
| |
| @Test |
| public void charArrChecks() { |
| final long seed = 0; |
| final int offset = 0; |
| final int chars = 16; |
| final int bytes = chars << 1; |
| |
| long[] hash2 = new long[2]; |
| |
| for (int j = 1; j < chars; j++) { |
| final char[] in = new char[chars]; |
| |
| final MemorySegment wseg = MemorySegment.ofArray(in); |
| for (int i = 0; i < j; i++) { wseg.set(JAVA_INT_UNALIGNED, i, i); } |
| |
| final long[] hash1 = MurmurHash3.hash(in, 0); |
| hash2 = MurmurHash3FFM.hash(wseg, offset, bytes, seed, hash2); |
| final long[] hash3 = MurmurHash3FFM.hash(in, seed); |
| |
| assertEquals(hash1, hash2); |
| assertEquals(hash1, hash3); |
| } |
| } |
| |
| @Test |
| public void intArrChecks() { |
| final long seed = 0; |
| final int offset = 0; |
| final int ints = 16; |
| final int bytes = ints << 2; |
| |
| long[] hash2 = new long[2]; |
| |
| for (int j = 1; j < ints; j++) { |
| final int[] in = new int[ints]; |
| |
| final MemorySegment wseg = MemorySegment.ofArray(in); |
| for (int i = 0; i < j; i++) { wseg.set(JAVA_INT_UNALIGNED, i, i); } |
| |
| final long[] hash1 = MurmurHash3.hash(in, 0); |
| hash2 = MurmurHash3FFM.hash(wseg, offset, bytes, seed, hash2); |
| final long[] hash3 = MurmurHash3FFM.hash(in, seed); |
| |
| assertEquals(hash1, hash2); |
| assertEquals(hash1, hash3); |
| } |
| } |
| |
| @Test |
| public void longArrChecks() { |
| final long seed = 0; |
| final int offset = 0; |
| final int longs = 16; |
| final int bytes = longs << 3; |
| |
| long[] hash2 = new long[2]; |
| |
| for (int j = 1; j < longs; j++) { |
| final long[] in = new long[longs]; |
| |
| final MemorySegment wseg = MemorySegment.ofArray(in); |
| for (int i = 0; i < j; i++) { wseg.set(JAVA_LONG_UNALIGNED, i, i); } |
| |
| final long[] hash1 = MurmurHash3.hash(in, 0); |
| hash2 = MurmurHash3FFM.hash(wseg, offset, bytes, seed, hash2); |
| final long[] hash3 = MurmurHash3FFM.hash(in, seed); |
| |
| assertEquals(hash1, hash2); |
| assertEquals(hash1, hash3); |
| } |
| } |
| |
| @Test |
| public void longCheck() { |
| final long seed = 0; |
| final int offset = 0; |
| final int bytes = 8; |
| |
| long[] hash2 = new long[2]; |
| final long[] in = { 1 }; |
| final MemorySegment wseg = MemorySegment.ofArray(in); |
| |
| final long[] hash1 = MurmurHash3.hash(in, 0); |
| hash2 = MurmurHash3FFM.hash(wseg, offset, bytes, seed, hash2); |
| final long[] hash3 = MurmurHash3FFM.hash(in, seed); |
| |
| assertEquals(hash1, hash2); |
| assertEquals(hash1, hash3); |
| } |
| |
| @Test |
| public void checkEmptiesNulls() { |
| final long seed = 123; |
| final long[] hashOut = new long[2]; |
| try { |
| MurmurHash3FFM.hash(MemorySegment.ofArray(new long[0]), 0, 0, seed, hashOut); //seg empty |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| try { |
| final String s = ""; |
| MurmurHash3FFM.hash(s, seed, hashOut); //string empty |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| try { |
| final String s = null; |
| MurmurHash3FFM.hash(s, seed, hashOut); //string null |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| try { |
| final byte[] barr = {}; |
| MurmurHash3FFM.hash(barr, seed); //byte[] empty |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| try { |
| final byte[] barr = null; |
| MurmurHash3FFM.hash(barr, seed); //byte[] null |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| try { |
| final char[] carr = {}; |
| MurmurHash3FFM.hash(carr, seed); //char[] empty |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| try { |
| final char[] carr = null; |
| MurmurHash3FFM.hash(carr, seed); //char[] null |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| try { |
| final int[] iarr = {}; |
| MurmurHash3FFM.hash(iarr, seed); //int[] empty |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| try { |
| final int[] iarr = null; |
| MurmurHash3FFM.hash(iarr, seed); //int[] null |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| try { |
| final long[] larr = {}; |
| MurmurHash3FFM.hash(larr, seed); //long[] empty |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| try { |
| final long[] larr = null; |
| MurmurHash3FFM.hash(larr, seed); //long[] null |
| fail(); |
| } catch (final IllegalArgumentException e) { } //OK |
| } |
| |
| @Test |
| public void checkStringLong() { |
| final long seed = 123; |
| final long[] hashOut = new long[2]; |
| final String s = "123"; |
| assertTrue(MurmurHash3FFM.hash(s, seed, hashOut)[0] != 0); |
| final long v = 123; |
| assertTrue(MurmurHash3FFM.hash(v, seed, hashOut)[0] != 0); |
| } |
| |
| @Test |
| public void doubleCheck() { |
| long[] hash1 = checkDouble(-0.0); |
| long[] hash2 = checkDouble(0.0); |
| assertEquals(hash1, hash2); |
| hash1 = checkDouble(Double.NaN); |
| final long nan = (0x7FFL << 52) + 1L; |
| hash2 = checkDouble(Double.longBitsToDouble(nan)); |
| assertEquals(hash1, hash2); |
| checkDouble(1.0); |
| } |
| |
| private static long[] checkDouble(final double dbl) { |
| final long seed = 0; |
| final int offset = 0; |
| final int bytes = 8; |
| |
| long[] hash2 = new long[2]; |
| |
| final double d = (dbl == 0.0) ? 0.0 : dbl; // canonicalize -0.0, 0.0 |
| final long data = Double.doubleToLongBits(d);// canonicalize all NaN forms |
| final long[] dataArr = { data }; |
| |
| final MemorySegment wseg = MemorySegment.ofArray(dataArr); |
| final long[] hash1 = MurmurHash3.hash(dataArr, 0); |
| hash2 = MurmurHash3FFM.hash(wseg, offset, bytes, seed, hash2); |
| final long[] hash3 = MurmurHash3FFM.hash(dbl, seed, hash2); |
| |
| assertEquals(hash1, hash2); |
| assertEquals(hash1, hash3); |
| return hash1; |
| } |
| |
| } |