This commit is contained in:
Looly 2023-03-13 11:15:47 +08:00
parent 48eec2a8ae
commit 9ab3397a2f
11 changed files with 163 additions and 225 deletions

View File

@ -17,6 +17,9 @@ import java.util.Arrays;
* @since 5.2.5 * @since 5.2.5
*/ */
public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>{ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>{
/**
* 单例
*/
public static CityHash INSTANCE = new CityHash(); public static CityHash INSTANCE = new CityHash();
// Some primes between 2^63 and 2^64 for various uses. // Some primes between 2^63 and 2^64 for various uses.
@ -50,46 +53,46 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
// len > 24 // len > 24
int h = len, g = c1 * len, f = g; int h = len, g = c1 * len, f = g;
int a0 = rotate32(fetch32(data, len - 4) * c1, 17) * c2; int a0 = Integer.rotateRight(fetch32(data, len - 4) * c1, 17) * c2;
int a1 = rotate32(fetch32(data, len - 8) * c1, 17) * c2; int a1 = Integer.rotateRight(fetch32(data, len - 8) * c1, 17) * c2;
int a2 = rotate32(fetch32(data, len - 16) * c1, 17) * c2; int a2 = Integer.rotateRight(fetch32(data, len - 16) * c1, 17) * c2;
int a3 = rotate32(fetch32(data, len - 12) * c1, 17) * c2; int a3 = Integer.rotateRight(fetch32(data, len - 12) * c1, 17) * c2;
int a4 = rotate32(fetch32(data, len - 20) * c1, 17) * c2; int a4 = Integer.rotateRight(fetch32(data, len - 20) * c1, 17) * c2;
h ^= a0; h ^= a0;
h = rotate32(h, 19); h = Integer.rotateRight(h, 19);
h = h * 5 + 0xe6546b64; h = h * 5 + 0xe6546b64;
h ^= a2; h ^= a2;
h = rotate32(h, 19); h = Integer.rotateRight(h, 19);
h = h * 5 + 0xe6546b64; h = h * 5 + 0xe6546b64;
g ^= a1; g ^= a1;
g = rotate32(g, 19); g = Integer.rotateRight(g, 19);
g = g * 5 + 0xe6546b64; g = g * 5 + 0xe6546b64;
g ^= a3; g ^= a3;
g = rotate32(g, 19); g = Integer.rotateRight(g, 19);
g = g * 5 + 0xe6546b64; g = g * 5 + 0xe6546b64;
f += a4; f += a4;
f = rotate32(f, 19); f = Integer.rotateRight(f, 19);
f = f * 5 + 0xe6546b64; f = f * 5 + 0xe6546b64;
int iters = (len - 1) / 20; int iters = (len - 1) / 20;
int pos = 0; int pos = 0;
do { do {
a0 = rotate32(fetch32(data, pos) * c1, 17) * c2; a0 = Integer.rotateRight(fetch32(data, pos) * c1, 17) * c2;
a1 = fetch32(data, pos + 4); a1 = fetch32(data, pos + 4);
a2 = rotate32(fetch32(data, pos + 8) * c1, 17) * c2; a2 = Integer.rotateRight(fetch32(data, pos + 8) * c1, 17) * c2;
a3 = rotate32(fetch32(data, pos + 12) * c1, 17) * c2; a3 = Integer.rotateRight(fetch32(data, pos + 12) * c1, 17) * c2;
a4 = fetch32(data, pos + 16); a4 = fetch32(data, pos + 16);
h ^= a0; h ^= a0;
h = rotate32(h, 18); h = Integer.rotateRight(h, 18);
h = h * 5 + 0xe6546b64; h = h * 5 + 0xe6546b64;
f += a1; f += a1;
f = rotate32(f, 19); f = Integer.rotateRight(f, 19);
f = f * c1; f = f * c1;
g += a2; g += a2;
g = rotate32(g, 18); g = Integer.rotateRight(g, 18);
g = g * 5 + 0xe6546b64; g = g * 5 + 0xe6546b64;
h ^= a3 + a1; h ^= a3 + a1;
h = rotate32(h, 19); h = Integer.rotateRight(h, 19);
h = h * 5 + 0xe6546b64; h = h * 5 + 0xe6546b64;
g ^= a4; g ^= a4;
g = Integer.reverseBytes(g) * 5; g = Integer.reverseBytes(g) * 5;
@ -104,16 +107,16 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
pos += 20; pos += 20;
} while (--iters != 0); } while (--iters != 0);
g = rotate32(g, 11) * c1; g = Integer.rotateRight(g, 11) * c1;
g = rotate32(g, 17) * c1; g = Integer.rotateRight(g, 17) * c1;
f = rotate32(f, 11) * c1; f = Integer.rotateRight(f, 11) * c1;
f = rotate32(f, 17) * c1; f = Integer.rotateRight(f, 17) * c1;
h = rotate32(h + g, 19); h = Integer.rotateRight(h + g, 19);
h = h * 5 + 0xe6546b64; h = h * 5 + 0xe6546b64;
h = rotate32(h, 17) * c1; h = Integer.rotateRight(h, 17) * c1;
h = rotate32(h + f, 19); h = Integer.rotateRight(h + f, 19);
h = h * 5 + 0xe6546b64; h = h * 5 + 0xe6546b64;
h = rotate32(h, 17) * c1; h = Integer.rotateRight(h, 17) * c1;
return h; return h;
} }
@ -149,11 +152,11 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
len = (len - 1) & ~63; len = (len - 1) & ~63;
int pos = 0; int pos = 0;
do { do {
x = rotate64(x + y + v.getLowValue() + fetch64(data, pos + 8), 37) * k1; x = Long.rotateRight(x + y + v.getLowValue() + fetch64(data, pos + 8), 37) * k1;
y = rotate64(y + v.getHighValue() + fetch64(data, pos + 48), 42) * k1; y = Long.rotateRight(y + v.getHighValue() + fetch64(data, pos + 48), 42) * k1;
x ^= w.getHighValue(); x ^= w.getHighValue();
y += v.getLowValue() + fetch64(data, pos + 40); y += v.getLowValue() + fetch64(data, pos + 40);
z = rotate64(z + w.getLowValue(), 33) * k1; z = Long.rotateRight(z + w.getLowValue(), 33) * k1;
v = weakHashLen32WithSeeds(data, pos, v.getHighValue() * k1, x + w.getLowValue()); v = weakHashLen32WithSeeds(data, pos, v.getHighValue() * k1, x + w.getLowValue());
w = weakHashLen32WithSeeds(data, pos + 32, z + w.getHighValue(), y + fetch64(data, pos + 16)); w = weakHashLen32WithSeeds(data, pos + 32, z + w.getHighValue(), y + fetch64(data, pos + 16));
// swap z,x value // swap z,x value
@ -231,19 +234,19 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
long x = seed.getLowValue(); long x = seed.getLowValue();
long y = seed.getHighValue(); long y = seed.getHighValue();
long z = len * k1; long z = len * k1;
v.setLowValue(rotate64(y ^ k1, 49) * k1 + fetch64(byteArray, start)); v.setLowValue(Long.rotateRight(y ^ k1, 49) * k1 + fetch64(byteArray, start));
v.setHighValue(rotate64(v.getLowValue(), 42) * k1 + fetch64(byteArray, start + 8)); v.setHighValue(Long.rotateRight(v.getLowValue(), 42) * k1 + fetch64(byteArray, start + 8));
w.setLowValue(rotate64(y + z, 35) * k1 + x); w.setLowValue(Long.rotateRight(y + z, 35) * k1 + x);
w.setHighValue(rotate64(x + fetch64(byteArray, start + 88), 53) * k1); w.setHighValue(Long.rotateRight(x + fetch64(byteArray, start + 88), 53) * k1);
// This is the same inner loop as CityHash64(), manually unrolled. // This is the same inner loop as CityHash64(), manually unrolled.
int pos = start; int pos = start;
do { do {
x = rotate64(x + y + v.getLowValue() + fetch64(byteArray, pos + 8), 37) * k1; x = Long.rotateRight(x + y + v.getLowValue() + fetch64(byteArray, pos + 8), 37) * k1;
y = rotate64(y + v.getHighValue() + fetch64(byteArray, pos + 48), 42) * k1; y = Long.rotateRight(y + v.getHighValue() + fetch64(byteArray, pos + 48), 42) * k1;
x ^= w.getHighValue(); x ^= w.getHighValue();
y += v.getLowValue() + fetch64(byteArray, pos + 40); y += v.getLowValue() + fetch64(byteArray, pos + 40);
z = rotate64(z + w.getLowValue(), 33) * k1; z = Long.rotateRight(z + w.getLowValue(), 33) * k1;
v = weakHashLen32WithSeeds(byteArray, pos, v.getHighValue() * k1, x + w.getLowValue()); v = weakHashLen32WithSeeds(byteArray, pos, v.getHighValue() * k1, x + w.getLowValue());
w = weakHashLen32WithSeeds(byteArray, pos + 32, z + w.getHighValue(), y + fetch64(byteArray, pos + 16)); w = weakHashLen32WithSeeds(byteArray, pos + 32, z + w.getHighValue(), y + fetch64(byteArray, pos + 16));
@ -251,11 +254,11 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
x = z; x = z;
z = swapValue; z = swapValue;
pos += 64; pos += 64;
x = rotate64(x + y + v.getLowValue() + fetch64(byteArray, pos + 8), 37) * k1; x = Long.rotateRight(x + y + v.getLowValue() + fetch64(byteArray, pos + 8), 37) * k1;
y = rotate64(y + v.getHighValue() + fetch64(byteArray, pos + 48), 42) * k1; y = Long.rotateRight(y + v.getHighValue() + fetch64(byteArray, pos + 48), 42) * k1;
x ^= w.getHighValue(); x ^= w.getHighValue();
y += v.getLowValue() + fetch64(byteArray, pos + 40); y += v.getLowValue() + fetch64(byteArray, pos + 40);
z = rotate64(z + w.getLowValue(), 33) * k1; z = Long.rotateRight(z + w.getLowValue(), 33) * k1;
v = weakHashLen32WithSeeds(byteArray, pos, v.getHighValue() * k1, x + w.getLowValue()); v = weakHashLen32WithSeeds(byteArray, pos, v.getHighValue() * k1, x + w.getLowValue());
w = weakHashLen32WithSeeds(byteArray, pos + 32, z + w.getHighValue(), y + fetch64(byteArray, pos + 16)); w = weakHashLen32WithSeeds(byteArray, pos + 32, z + w.getHighValue(), y + fetch64(byteArray, pos + 16));
swapValue = x; swapValue = x;
@ -264,16 +267,16 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
pos += 64; pos += 64;
len -= 128; len -= 128;
} while (len >= 128); } while (len >= 128);
x += rotate64(v.getLowValue() + z, 49) * k0; x += Long.rotateRight(v.getLowValue() + z, 49) * k0;
y = y * k0 + rotate64(w.getHighValue(), 37); y = y * k0 + Long.rotateRight(w.getHighValue(), 37);
z = z * k0 + rotate64(w.getLowValue(), 27); z = z * k0 + Long.rotateRight(w.getLowValue(), 27);
w.setLowValue(w.getLowValue() * 9); w.setLowValue(w.getLowValue() * 9);
v.setLowValue(v.getLowValue() * k0); v.setLowValue(v.getLowValue() * k0);
// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
for (int tail_done = 0; tail_done < len; ) { for (int tail_done = 0; tail_done < len; ) {
tail_done += 32; tail_done += 32;
y = rotate64(x + y, 42) * k0 + v.getHighValue(); y = Long.rotateRight(x + y, 42) * k0 + v.getHighValue();
w.setLowValue(w.getLowValue() + fetch64(byteArray, pos + len - tail_done + 16)); w.setLowValue(w.getLowValue() + fetch64(byteArray, pos + len - tail_done + 16));
x = x * k0 + w.getLowValue(); x = x * k0 + w.getLowValue();
z += w.getHighValue() + fetch64(byteArray, pos + len - tail_done); z += w.getHighValue() + fetch64(byteArray, pos + len - tail_done);
@ -333,8 +336,8 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
final long mul = k2 + len * 2L; final long mul = k2 + len * 2L;
final long a = fetch64(byteArray, 0) + k2; final long a = fetch64(byteArray, 0) + k2;
final long b = fetch64(byteArray, len - 8); final long b = fetch64(byteArray, len - 8);
final long c = rotate64(b, 37) * mul + a; final long c = Long.rotateRight(b, 37) * mul + a;
final long d = (rotate64(a, 25) + b) * mul; final long d = (Long.rotateRight(a, 25) + b) * mul;
return hashLen16(c, d, mul); return hashLen16(c, d, mul);
} }
if (len >= 4) { if (len >= 4) {
@ -361,8 +364,8 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
final long b = fetch64(byteArray, 8); final long b = fetch64(byteArray, 8);
final long c = fetch64(byteArray, len - 8) * mul; final long c = fetch64(byteArray, len - 8) * mul;
final long d = fetch64(byteArray, len - 16) * k2; final long d = fetch64(byteArray, len - 16) * k2;
return hashLen16(rotate64(a + b, 43) + rotate64(c, 30) + d, return hashLen16(Long.rotateRight(a + b, 43) + Long.rotateRight(c, 30) + d,
a + rotate64(b + k2, 18) + c, mul); a + Long.rotateRight(b + k2, 18) + c, mul);
} }
private long hashLen33to64(final byte[] byteArray) { private long hashLen33to64(final byte[] byteArray) {
@ -376,10 +379,10 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
final long f = fetch64(byteArray, 24) * 9; final long f = fetch64(byteArray, 24) * 9;
final long g = fetch64(byteArray, len - 8); final long g = fetch64(byteArray, len - 8);
final long h = fetch64(byteArray, len - 16) * mul; final long h = fetch64(byteArray, len - 16) * mul;
final long u = rotate64(a + g, 43) + (rotate64(b, 30) + c) * 9; final long u = Long.rotateRight(a + g, 43) + (Long.rotateRight(b, 30) + c) * 9;
final long v = ((a + g) ^ d) + f + 1; final long v = ((a + g) ^ d) + f + 1;
final long w = Long.reverseBytes((u + v) * mul) + h; final long w = Long.reverseBytes((u + v) * mul) + h;
final long x = rotate64(e + f, 42) + c; final long x = Long.rotateRight(e + f, 42) + c;
final long y = (Long.reverseBytes((v + w) * mul) + g) * mul; final long y = (Long.reverseBytes((v + w) * mul) + g) * mul;
final long z = e + f + c; final long z = e + f + c;
a = Long.reverseBytes((x + z) * mul + y) + b; a = Long.reverseBytes((x + z) * mul + y) + b;
@ -395,16 +398,6 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
return ByteUtil.bytesToInt(byteArray, start, ByteUtil.CPU_ENDIAN); return ByteUtil.bytesToInt(byteArray, start, ByteUtil.CPU_ENDIAN);
} }
private static long rotate64(final long val, final int shift) {
// Avoid shifting by 64: doing so yields an undefined result.
return shift == 0 ? val : ((val >>> shift) | (val << (64 - shift)));
}
private static int rotate32(final int val, final int shift) {
// Avoid shifting by 32: doing so yields an undefined result.
return shift == 0 ? val : ((val >>> shift) | (val << (32 - shift)));
}
private static long hashLen16(final long u, final long v, final long mul) { private static long hashLen16(final long u, final long v, final long mul) {
// Murmur-inspired hashing. // Murmur-inspired hashing.
long a = (u ^ v) * mul; long a = (u ^ v) * mul;
@ -446,21 +439,21 @@ public class CityHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>
private int mur(int a, int h) { private int mur(int a, int h) {
// Helper from Murmur3 for combining two 32-bit values. // Helper from Murmur3 for combining two 32-bit values.
a *= c1; a *= c1;
a = rotate32(a, 17); a = Integer.rotateRight(a, 17);
a *= c2; a *= c2;
h ^= a; h ^= a;
h = rotate32(h, 19); h = Integer.rotateRight(h, 19);
return h * 5 + 0xe6546b64; return h * 5 + 0xe6546b64;
} }
private static Number128 weakHashLen32WithSeeds( private static Number128 weakHashLen32WithSeeds(
final long w, final long x, final long y, final long z, long a, long b) { final long w, final long x, final long y, final long z, long a, long b) {
a += w; a += w;
b = rotate64(b + a + z, 21); b = Long.rotateRight(b + a + z, 21);
final long c = a; final long c = a;
a += x; a += x;
a += y; a += y;
b += rotate64(a, 44); b += Long.rotateRight(a, 44);
return new Number128(a + z, b + c); return new Number128(a + z, b + c);
} }

View File

@ -21,6 +21,9 @@ import java.nio.charset.Charset;
* @since 4.3.3 * @since 4.3.3
*/ */
public class MurmurHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>{ public class MurmurHash implements Hash32<byte[]>, Hash64<byte[]>, Hash128<byte[]>{
/**
* 单例
*/
public static final MurmurHash INSTANCE = new MurmurHash(); public static final MurmurHash INSTANCE = new MurmurHash();
// Constants for 32 bit variant // Constants for 32 bit variant

View File

@ -30,13 +30,7 @@ public abstract class AbstractMetroHash<R extends AbstractMetroHash<R>> implemen
reset(); reset();
} }
/** @Override
* Applies the instance's Metro hash function to the bytes in the given buffer.
* This updates this instance's hashing state.
*
* @param input 内容
* @return this
*/
public R apply(final ByteBuffer input) { public R apply(final ByteBuffer input) {
reset(); reset();
while (input.remaining() >= 32) { while (input.remaining() >= 32) {
@ -46,18 +40,32 @@ public abstract class AbstractMetroHash<R extends AbstractMetroHash<R>> implemen
} }
/** /**
* Consumes a 32-byte chunk from the byte buffer and updates the hashing state. * 从byteBuffer中计算32-byte块并更新hash状态
* *
* @param partialInput byte buffer with at least 32 bytes remaining. * @param partialInput byte buffer至少有32byte的数据
* @return this * @return this
*/ */
abstract R partialApply32ByteChunk(ByteBuffer partialInput); abstract R partialApply32ByteChunk(ByteBuffer partialInput);
/** /**
* Consumes the remaining bytes from the byte buffer and updates the hashing state. * 从byteBuffer中计算剩余bytes并更新hash状态
* *
* @param partialInput byte buffer with less than 32 bytes remaining. * @param partialInput byte buffer少于32byte的数据
* @return this * @return this
*/ */
abstract R partialApplyRemaining(ByteBuffer partialInput); abstract R partialApplyRemaining(ByteBuffer partialInput);
static long grab(final ByteBuffer bb, final int length) {
long result = bb.get() & 0xFFL;
for (int i = 1; i < length; i++) {
result |= (bb.get() & 0xFFL) << (i << 3);
}
return result;
}
static void writeLittleEndian(final long hash, final ByteBuffer output) {
for (int i = 0; i < 8; i++) {
output.put((byte) (hash >>> (i*8)));
}
}
} }

View File

@ -29,8 +29,8 @@ public interface MetroHash<R extends MetroHash<R>> {
} }
/** /**
* Applies the instance's Metro hash function to the bytes in the given buffer. * 将给定的{@link ByteBuffer}中的数据追加计算hash值<br>
* This updates this instance's hashing state. * 此方法会更新hash值状态
* *
* @param input 内容 * @param input 内容
* @return this * @return this
@ -38,8 +38,7 @@ public interface MetroHash<R extends MetroHash<R>> {
R apply(final ByteBuffer input); R apply(final ByteBuffer input);
/** /**
* Writes the current hash to the given byte buffer in big-endian order. * 将结果hash值写出到{@link ByteBuffer}可选端序
* 将结果hash值写出到{@link ByteBuffer}
* *
* @param output 输出 * @param output 输出
* @param byteOrder 端序 * @param byteOrder 端序

View File

@ -52,24 +52,6 @@ public class MetroHash128 extends AbstractMetroHash<MetroHash128> implements Has
return this; return this;
} }
/**
* 获取高64位结果hash值
*
* @return hash值
*/
public long getHigh() {
return v0;
}
/**
* 获取低64位结果hash值
*
* @return hash值
*/
public long getLow() {
return v1;
}
/** /**
* 获取结果hash值 * 获取结果hash值
* *
@ -87,8 +69,8 @@ public class MetroHash128 extends AbstractMetroHash<MetroHash128> implements Has
@Override @Override
public MetroHash128 write(final ByteBuffer output, final ByteOrder byteOrder) { public MetroHash128 write(final ByteBuffer output, final ByteOrder byteOrder) {
if (ByteOrder.LITTLE_ENDIAN == byteOrder) { if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
MetroHashInternalUtil.writeLittleEndian(v0, output); writeLittleEndian(v0, output);
MetroHashInternalUtil.writeLittleEndian(v1, output); writeLittleEndian(v1, output);
} else { } else {
output.asLongBuffer().put(v1).put(v0); output.asLongBuffer().put(v1).put(v0);
} }
@ -98,14 +80,14 @@ public class MetroHash128 extends AbstractMetroHash<MetroHash128> implements Has
@Override @Override
MetroHash128 partialApply32ByteChunk(final ByteBuffer partialInput) { MetroHash128 partialApply32ByteChunk(final ByteBuffer partialInput) {
assert partialInput.remaining() >= 32; assert partialInput.remaining() >= 32;
v0 += MetroHashInternalUtil.grab8(partialInput) * K0; v0 += grab(partialInput, 8) * K0;
v0 = MetroHashInternalUtil.rotateRight64(v0, 29) + v2; v0 = Long.rotateRight(v0, 29) + v2;
v1 += MetroHashInternalUtil.grab8(partialInput) * K1; v1 += grab(partialInput, 8) * K1;
v1 = MetroHashInternalUtil.rotateRight64(v1, 29) + v3; v1 = Long.rotateRight(v1, 29) + v3;
v2 += MetroHashInternalUtil.grab8(partialInput) * K2; v2 += grab(partialInput, 8) * K2;
v2 = MetroHashInternalUtil.rotateRight64(v2, 29) + v0; v2 = Long.rotateRight(v2, 29) + v0;
v3 += MetroHashInternalUtil.grab8(partialInput) * K3; v3 += grab(partialInput, 8) * K3;
v3 = MetroHashInternalUtil.rotateRight64(v3, 29) + v1; v3 = Long.rotateRight(v3, 29) + v1;
++nChunks; ++nChunks;
return this; return this;
} }
@ -131,52 +113,52 @@ public class MetroHash128 extends AbstractMetroHash<MetroHash128> implements Has
if (partialInput.remaining() >= 1) { if (partialInput.remaining() >= 1) {
metroHash128_1(partialInput); metroHash128_1(partialInput);
} }
v0 += MetroHashInternalUtil.rotateRight64(v0 * K0 + v1, 13); v0 += Long.rotateRight(v0 * K0 + v1, 13);
v1 += MetroHashInternalUtil.rotateRight64(v1 * K1 + v0, 37); v1 += Long.rotateRight(v1 * K1 + v0, 37);
v0 += MetroHashInternalUtil.rotateRight64(v0 * K2 + v1, 13); v0 += Long.rotateRight(v0 * K2 + v1, 13);
v1 += MetroHashInternalUtil.rotateRight64(v1 * K3 + v0, 37); v1 += Long.rotateRight(v1 * K3 + v0, 37);
return this; return this;
} }
// region ----- private methods // region ----- private methods
private void metroHash128_32() { private void metroHash128_32() {
v2 ^= MetroHashInternalUtil.rotateRight64((v0 + v3) * K0 + v1, 21) * K1; v2 ^= Long.rotateRight((v0 + v3) * K0 + v1, 21) * K1;
v3 ^= MetroHashInternalUtil.rotateRight64((v1 + v2) * K1 + v0, 21) * K0; v3 ^= Long.rotateRight((v1 + v2) * K1 + v0, 21) * K0;
v0 ^= MetroHashInternalUtil.rotateRight64((v0 + v2) * K0 + v3, 21) * K1; v0 ^= Long.rotateRight((v0 + v2) * K0 + v3, 21) * K1;
v1 ^= MetroHashInternalUtil.rotateRight64((v1 + v3) * K1 + v2, 21) * K0; v1 ^= Long.rotateRight((v1 + v3) * K1 + v2, 21) * K0;
} }
private void metroHash128_16(final ByteBuffer bb) { private void metroHash128_16(final ByteBuffer bb) {
v0 += MetroHashInternalUtil.grab8(bb) * K2; v0 += grab(bb, 8) * K2;
v0 = MetroHashInternalUtil.rotateRight64(v0, 33) * K3; v0 = Long.rotateRight(v0, 33) * K3;
v1 += MetroHashInternalUtil.grab8(bb) * K2; v1 += grab(bb, 8) * K2;
v1 = MetroHashInternalUtil.rotateRight64(v1, 33) * K3; v1 = Long.rotateRight(v1, 33) * K3;
v0 ^= MetroHashInternalUtil.rotateRight64(v0 * K2 + v1, 45) * K1; v0 ^= Long.rotateRight(v0 * K2 + v1, 45) * K1;
v1 ^= MetroHashInternalUtil.rotateRight64(v1 * K3 + v0, 45) * K0; v1 ^= Long.rotateRight(v1 * K3 + v0, 45) * K0;
} }
private void metroHash128_8(final ByteBuffer bb) { private void metroHash128_8(final ByteBuffer bb) {
v0 += MetroHashInternalUtil.grab8(bb) * K2; v0 += grab(bb, 8) * K2;
v0 = MetroHashInternalUtil.rotateRight64(v0, 33) * K3; v0 = Long.rotateRight(v0, 33) * K3;
v0 ^= MetroHashInternalUtil.rotateRight64(v0 * K2 + v1, 27) * K1; v0 ^= Long.rotateRight(v0 * K2 + v1, 27) * K1;
} }
private void metroHash128_4(final ByteBuffer bb) { private void metroHash128_4(final ByteBuffer bb) {
v1 += MetroHashInternalUtil.grab4(bb) * K2; v1 += grab(bb, 4) * K2;
v1 = MetroHashInternalUtil.rotateRight64(v1, 33) * K3; v1 = Long.rotateRight(v1, 33) * K3;
v1 ^= MetroHashInternalUtil.rotateRight64(v1 * K3 + v0, 46) * K0; v1 ^= Long.rotateRight(v1 * K3 + v0, 46) * K0;
} }
private void metroHash128_2(final ByteBuffer bb) { private void metroHash128_2(final ByteBuffer bb) {
v0 += MetroHashInternalUtil.grab2(bb) * K2; v0 += grab(bb, 2) * K2;
v0 = MetroHashInternalUtil.rotateRight64(v0, 33) * K3; v0 = Long.rotateRight(v0, 33) * K3;
v0 ^= MetroHashInternalUtil.rotateRight64(v0 * K2 + v1, 22) * K1; v0 ^= Long.rotateRight(v0 * K2 + v1, 22) * K1;
} }
private void metroHash128_1(final ByteBuffer bb) { private void metroHash128_1(final ByteBuffer bb) {
v1 += MetroHashInternalUtil.grab1(bb) * K2; v1 += grab(bb, 1) * K2;
v1 = MetroHashInternalUtil.rotateRight64(v1, 33) * K3; v1 = Long.rotateRight(v1, 33) * K3;
v1 ^= MetroHashInternalUtil.rotateRight64(v1 * K3 + v0, 58) * K0; v1 ^= Long.rotateRight(v1 * K3 + v0, 58) * K0;
} }
// endregion // endregion
} }

View File

@ -67,7 +67,7 @@ public class MetroHash64 extends AbstractMetroHash<MetroHash64> implements Hash6
@Override @Override
public MetroHash64 write(final ByteBuffer output, final ByteOrder byteOrder) { public MetroHash64 write(final ByteBuffer output, final ByteOrder byteOrder) {
if(ByteOrder.LITTLE_ENDIAN == byteOrder){ if(ByteOrder.LITTLE_ENDIAN == byteOrder){
MetroHashInternalUtil.writeLittleEndian(hash, output); writeLittleEndian(hash, output);
} else{ } else{
output.asLongBuffer().put(hash); output.asLongBuffer().put(hash);
} }
@ -77,14 +77,14 @@ public class MetroHash64 extends AbstractMetroHash<MetroHash64> implements Hash6
@Override @Override
MetroHash64 partialApply32ByteChunk(final ByteBuffer partialInput) { MetroHash64 partialApply32ByteChunk(final ByteBuffer partialInput) {
assert partialInput.remaining() >= 32; assert partialInput.remaining() >= 32;
v0 += MetroHashInternalUtil.grab8(partialInput) * K0; v0 += grab(partialInput, 8) * K0;
v0 = MetroHashInternalUtil.rotateRight64(v0, 29) + v2; v0 = Long.rotateRight(v0, 29) + v2;
v1 += MetroHashInternalUtil.grab8(partialInput) * K1; v1 += grab(partialInput, 8) * K1;
v1 = MetroHashInternalUtil.rotateRight64(v1, 29) + v3; v1 = Long.rotateRight(v1, 29) + v3;
v2 += MetroHashInternalUtil.grab8(partialInput) * K2; v2 += grab(partialInput, 8) * K2;
v2 = MetroHashInternalUtil.rotateRight64(v2, 29) + v0; v2 = Long.rotateRight(v2, 29) + v0;
v3 += MetroHashInternalUtil.grab8(partialInput) * K3; v3 += grab(partialInput, 8) * K3;
v3 = MetroHashInternalUtil.rotateRight64(v3, 29) + v1; v3 = Long.rotateRight(v3, 29) + v1;
++nChunks; ++nChunks;
return this; return this;
} }
@ -110,49 +110,49 @@ public class MetroHash64 extends AbstractMetroHash<MetroHash64> implements Hash6
if (partialInput.remaining() >= 1) { if (partialInput.remaining() >= 1) {
metroHash64_1(partialInput); metroHash64_1(partialInput);
} }
hash ^= MetroHashInternalUtil.rotateRight64(hash, 28); hash ^= Long.rotateRight(hash, 28);
hash *= K0; hash *= K0;
hash ^= MetroHashInternalUtil.rotateRight64(hash, 29); hash ^= Long.rotateRight(hash, 29);
return this; return this;
} }
// region ----- private methods // region ----- private methods
private void metroHash64_32() { private void metroHash64_32() {
v2 ^= MetroHashInternalUtil.rotateRight64(((v0 + v3) * K0) + v1, 37) * K1; v2 ^= Long.rotateRight(((v0 + v3) * K0) + v1, 37) * K1;
v3 ^= MetroHashInternalUtil.rotateRight64(((v1 + v2) * K1) + v0, 37) * K0; v3 ^= Long.rotateRight(((v1 + v2) * K1) + v0, 37) * K0;
v0 ^= MetroHashInternalUtil.rotateRight64(((v0 + v2) * K0) + v3, 37) * K1; v0 ^= Long.rotateRight(((v0 + v2) * K0) + v3, 37) * K1;
v1 ^= MetroHashInternalUtil.rotateRight64(((v1 + v3) * K1) + v2, 37) * K0; v1 ^= Long.rotateRight(((v1 + v3) * K1) + v2, 37) * K0;
hash += v0 ^ v1; hash += v0 ^ v1;
} }
private void metroHash64_16(final ByteBuffer bb) { private void metroHash64_16(final ByteBuffer bb) {
v0 = hash + MetroHashInternalUtil.grab8(bb) * K2; v0 = hash + grab(bb, 8) * K2;
v0 = MetroHashInternalUtil.rotateRight64(v0, 29) * K3; v0 = Long.rotateRight(v0, 29) * K3;
v1 = hash + MetroHashInternalUtil.grab8(bb) * K2; v1 = hash + grab(bb, 8) * K2;
v1 = MetroHashInternalUtil.rotateRight64(v1, 29) * K3; v1 = Long.rotateRight(v1, 29) * K3;
v0 ^= MetroHashInternalUtil.rotateRight64(v0 * K0, 21) + v1; v0 ^= Long.rotateRight(v0 * K0, 21) + v1;
v1 ^= MetroHashInternalUtil.rotateRight64(v1 * K3, 21) + v0; v1 ^= Long.rotateRight(v1 * K3, 21) + v0;
hash += v1; hash += v1;
} }
private void metroHash64_8(final ByteBuffer bb) { private void metroHash64_8(final ByteBuffer bb) {
hash += MetroHashInternalUtil.grab8(bb) * K3; hash += grab(bb, 8) * K3;
hash ^= MetroHashInternalUtil.rotateRight64(hash, 55) * K1; hash ^= Long.rotateRight(hash, 55) * K1;
} }
private void metroHash64_4(final ByteBuffer bb) { private void metroHash64_4(final ByteBuffer bb) {
hash += MetroHashInternalUtil.grab4(bb) * K3; hash += grab(bb, 4) * K3;
hash ^= MetroHashInternalUtil.rotateRight64(hash, 26) * K1; hash ^= Long.rotateRight(hash, 26) * K1;
} }
private void metroHash64_2(final ByteBuffer bb) { private void metroHash64_2(final ByteBuffer bb) {
hash += MetroHashInternalUtil.grab2(bb) * K3; hash += grab(bb, 2) * K3;
hash ^= MetroHashInternalUtil.rotateRight64(hash, 48) * K1; hash ^= Long.rotateRight(hash, 48) * K1;
} }
private void metroHash64_1(final ByteBuffer bb) { private void metroHash64_1(final ByteBuffer bb) {
hash += MetroHashInternalUtil.grab1(bb) * K3; hash += grab(bb, 1) * K3;
hash ^= MetroHashInternalUtil.rotateRight64(hash, 37) * K1; hash ^= Long.rotateRight(hash, 37) * K1;
} }
// endregion // endregion
} }

View File

@ -1,51 +0,0 @@
package cn.hutool.core.codec.hash.metro;
import java.nio.ByteBuffer;
class MetroHashInternalUtil {
static void writeLittleEndian(final long hash, final ByteBuffer output) {
output.put((byte) hash);
output.put((byte) (hash >>> 8));
output.put((byte) (hash >>> 16));
output.put((byte) (hash >>> 24));
output.put((byte) (hash >>> 32));
output.put((byte) (hash >>> 40));
output.put((byte) (hash >>> 48));
output.put((byte) (hash >>> 56));
}
static long rotateRight64(final long x, final int r) {
return (x >>> r) | (x << (64 - r));
}
static long grab1(final ByteBuffer bb) {
return ((long) bb.get() & 0xFFL);
}
static long grab2(final ByteBuffer bb) {
final long v0 = bb.get();
final long v1 = bb.get();
return (v0 & 0xFFL) | (v1 & 0xFFL) << 8;
}
static long grab4(final ByteBuffer bb) {
final long v0 = bb.get();
final long v1 = bb.get();
final long v2 = bb.get();
final long v3 = bb.get();
return (v0 & 0xFFL) | (v1 & 0xFFL) << 8 | (v2 & 0xFFL) << 16 | (v3 & 0xFFL) << 24;
}
static long grab8(final ByteBuffer bb) {
final long v0 = bb.get();
final long v1 = bb.get();
final long v2 = bb.get();
final long v3 = bb.get();
final long v4 = bb.get();
final long v5 = bb.get();
final long v6 = bb.get();
final long v7 = bb.get();
return (v0 & 0xFFL) | (v1 & 0xFFL) << 8 | (v2 & 0xFFL) << 16 | (v3 & 0xFFL) << 24 | (v4 & 0xFFL) << 32 | (v5 & 0xFFL) << 40 | (v6 & 0xFFL) << 48 | (v7 & 0xFFL) << 56;
}
}

View File

@ -1761,5 +1761,4 @@ public class NumberUtil {
public static boolean isEven(final int num) { public static boolean isEven(final int num) {
return false == isOdd(num); return false == isOdd(num);
} }
} }

View File

@ -30,10 +30,13 @@ import java.util.function.Predicate;
* 也可以将已有的树转换为集合例如 * 也可以将已有的树转换为集合例如
* <pre>{@code final List<JavaBean> javaBeanList = beanTree.flat(originJavaBeanTree);}</pre> * <pre>{@code final List<JavaBean> javaBeanList = beanTree.flat(originJavaBeanTree);}</pre>
* *
* <p>最后引用一句电影经典台词 无处安放的双手以及无处安放的灵魂Hello!树先生</p>
*
* @param <T> Bean类型
* @param <R> 主键外键类型
* @author VampireAchao * @author VampireAchao
* @author emptypoint * @author emptypoint
* @author CreateSequence * @author CreateSequence
* 最后引用一句电影经典台词 无处安放的双手以及无处安放的灵魂Hello!树先生
*/ */
public class BeanTree<T, R extends Comparable<R>> { public class BeanTree<T, R extends Comparable<R>> {
@ -126,10 +129,10 @@ public class BeanTree<T, R extends Comparable<R>> {
if (CollUtil.isEmpty(list)) { if (CollUtil.isEmpty(list)) {
return ListUtil.zero(); return ListUtil.zero();
} }
if (Objects.isNull(parentPredicate)) { if (Objects.isNull(parentPredicate)) {
final Map<R, List<T>> pIdValuesMap = EasyStream.of(list) final Map<R, List<T>> pIdValuesMap = EasyStream.of(list)
.peek(e -> Objects.requireNonNull(idGetter.apply(e), () -> "The id of tree node must not be null " + e)) .peek(e -> Objects.requireNonNull(idGetter.apply(e), () -> "The id of tree node must not be null " + e))
.group(pidGetter); .group(pidGetter);
final List<T> parents = pIdValuesMap.getOrDefault(pidValue, new ArrayList<>()); final List<T> parents = pIdValuesMap.getOrDefault(pidValue, new ArrayList<>());
findChildren(list, pIdValuesMap); findChildren(list, pIdValuesMap);
return parents; return parents;
@ -206,7 +209,7 @@ public class BeanTree<T, R extends Comparable<R>> {
* @param pIdValuesMap 父id与子集的映射 * @param pIdValuesMap 父id与子集的映射
*/ */
private void findChildren(final List<T> list, final Map<R, List<T>> pIdValuesMap) { private void findChildren(final List<T> list, final Map<R, List<T>> pIdValuesMap) {
for (T node : list) { for (final T node : list) {
final List<T> children = pIdValuesMap.get(idGetter.apply(node)); final List<T> children = pIdValuesMap.get(idGetter.apply(node));
if (children != null) { if (children != null) {
childrenSetter.accept(node, children); childrenSetter.accept(node, children);

View File

@ -1,6 +1,7 @@
package cn.hutool.core.codec.hash.metro; package cn.hutool.core.codec.hash.metro;
import cn.hutool.core.codec.HexUtil; import cn.hutool.core.codec.HexUtil;
import cn.hutool.core.codec.Number128;
import cn.hutool.core.text.StrUtil; import cn.hutool.core.text.StrUtil;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -106,7 +107,8 @@ public class MetroHash128Test {
static String h128(final String input) { static String h128(final String input) {
final MetroHash128 mh = MetroHash128.of(0).apply(ByteBuffer.wrap(StrUtil.utf8Bytes(input))); final MetroHash128 mh = MetroHash128.of(0).apply(ByteBuffer.wrap(StrUtil.utf8Bytes(input)));
return hex(mh.getHigh()) + hex(mh.getLow()); final Number128 hash = mh.get();
return hex(hash.getHighValue()) + hex(hash.getLowValue());
} }
private static String hex(final long value){ private static String hex(final long value){

View File

@ -82,13 +82,13 @@ public class NumberUtilTest {
@Test @Test
public void isIntegerTest() { public void isIntegerTest() {
String[] validNumArr = {"0", "-0", "+0", "12", "+12", "1234567890", "2147483647", "-2147483648", final String[] validNumArr = {"0", "-0", "+0", "12", "+12", "1234567890", "2147483647", "-2147483648",
"0x012345678", "0X012345678", "0xabcdef", "-0xabcdef", "0x12abcdef", "0x7FFFFFFF", "-0x80000000", "0x012345678", "0X012345678", "0xabcdef", "-0xabcdef", "0x12abcdef", "0x7FFFFFFF", "-0x80000000",
"01234567", "017777777777", "-020000000000" "01234567", "017777777777", "-020000000000"
}; };
privateIsIntegerTest(validNumArr, true); privateIsIntegerTest(validNumArr, true);
String[] invalidNumArr = {null, "", " ", "+", "+1.", ".1", "99L", "99D", "99F", "12.3", "123e1", "-12.3", "1.2.3", final String[] invalidNumArr = {null, "", " ", "+", "+1.", ".1", "99L", "99D", "99F", "12.3", "123e1", "-12.3", "1.2.3",
"2147483648", "0x80000000", "020000000000", "-2147483649", "-0x80000001", "-020000000001", "-020000000001", "2147483648", "0x80000000", "020000000000", "-2147483649", "-0x80000001", "-020000000001", "-020000000001",
"a", "+a", "123abc", "09" "a", "+a", "123abc", "09"
}; };
@ -96,14 +96,14 @@ public class NumberUtilTest {
} }
private void privateIsIntegerTest(final String[] numStrArr, final boolean expected) { private void privateIsIntegerTest(final String[] numStrArr, final boolean expected) {
for (String numStr : numStrArr) { for (final String numStr : numStrArr) {
Assert.assertEquals("未通过的数字为: " + numStr, expected, NumberUtil.isInteger(numStr)); Assert.assertEquals("未通过的数字为: " + numStr, expected, NumberUtil.isInteger(numStr));
} }
} }
@Test @Test
public void isLongTest() { public void isLongTest() {
String[] validNumArr = { final String[] validNumArr = {
"0", "0L", "-0L", "+0L", "12", "+12", "1234567890123456789", "99L", "0", "0L", "-0L", "+0L", "12", "+12", "1234567890123456789", "99L",
"2147483648", "0x80000000", "020000000000", "-2147483649", "-0x80000001", "-020000000001", "-020000000001", "2147483648", "0x80000000", "020000000000", "-2147483649", "-0x80000001", "-020000000001", "-020000000001",
"9223372036854775807", "-9223372036854775808", "9223372036854775807", "-9223372036854775808",
@ -113,7 +113,7 @@ public class NumberUtilTest {
}; };
privateIsLongTest(validNumArr, true); privateIsLongTest(validNumArr, true);
String[] invalidNumArr = {null, "", " ", "+", "+1.", ".1", "99D", "99F", "12.3", "123e1", "-12.3", "1.2.3", final String[] invalidNumArr = {null, "", " ", "+", "+1.", ".1", "99D", "99F", "12.3", "123e1", "-12.3", "1.2.3",
"a", "+a", "123abc", "09", "a", "+a", "123abc", "09",
"9223372036854775808", "-9223372036854775809", "9223372036854775808", "-9223372036854775809",
"0x8000000000000000", "-0x8000000000000001", "0x8000000000000000", "-0x8000000000000001",
@ -123,7 +123,7 @@ public class NumberUtilTest {
} }
private void privateIsLongTest(final String[] numStrArr, final boolean expected) { private void privateIsLongTest(final String[] numStrArr, final boolean expected) {
for (String numStr : numStrArr) { for (final String numStr : numStrArr) {
Assert.assertEquals("未通过的数字为: " + numStr, expected, NumberUtil.isLong(numStr)); Assert.assertEquals("未通过的数字为: " + numStr, expected, NumberUtil.isLong(numStr));
} }
} }