blob: 4e2bb00e7824254ad32ef975ccb274feb00cb001 [file] [log] [blame]
/*
* 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;
}
}