mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
Merge remote-tracking branch 'origin/v5-dev' into v5-dev
This commit is contained in:
commit
a0f172d584
@ -2,7 +2,7 @@
|
|||||||
# 🚀Changelog
|
# 🚀Changelog
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
# 5.7.21 (2022-02-10)
|
# 5.7.21 (2022-02-11)
|
||||||
|
|
||||||
### 🐣新特性
|
### 🐣新特性
|
||||||
* 【extra 】 增加jetbrick模板支持
|
* 【extra 】 增加jetbrick模板支持
|
||||||
@ -18,12 +18,14 @@
|
|||||||
* 【core 】 IntMap和LongMap使用位运算快速求解取余运算(pr#2123@Github)
|
* 【core 】 IntMap和LongMap使用位运算快速求解取余运算(pr#2123@Github)
|
||||||
* 【core 】 新增通用builder类:GenericBuilder(pr#526@Gitee)
|
* 【core 】 新增通用builder类:GenericBuilder(pr#526@Gitee)
|
||||||
* 【core 】 新增copySafely方法与mkdirsSafely方法(pr#527@Gitee)
|
* 【core 】 新增copySafely方法与mkdirsSafely方法(pr#527@Gitee)
|
||||||
|
* 【core 】 新增MetroHash(pr#532@Gitee)
|
||||||
|
|
||||||
### 🐞Bug修复
|
### 🐞Bug修复
|
||||||
* 【core 】 修复ChineseDate农历获取正月出现数组越界BUG(issue#2112@Github)
|
* 【core 】 修复ChineseDate农历获取正月出现数组越界BUG(issue#2112@Github)
|
||||||
* 【extra 】 修复EmojiUtil.toHtmlHex()方法(pr#519@Gitee)
|
* 【extra 】 修复EmojiUtil.toHtmlHex()方法(pr#519@Gitee)
|
||||||
* 【system 】 修复CpuInfo.getUsed()方法(issue#2116@Github)
|
* 【system 】 修复CpuInfo.getUsed()方法(issue#2116@Github)
|
||||||
* 【dfa 】 修复密集匹配和贪婪匹配冲突问题(issue#2126@Github)
|
* 【dfa 】 修复密集匹配和贪婪匹配冲突问题(issue#2126@Github)
|
||||||
|
* 【db 】 修复c3p0丢失信息问题(issue#I4T7XZ@Gitee)
|
||||||
* 【http 】 修复Action中HttpExchange没有关闭问题
|
* 【http 】 修复Action中HttpExchange没有关闭问题
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package cn.hutool.core.lang.hash;
|
package cn.hutool.core.lang.hash;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ByteUtil;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,11 +142,11 @@ public class CityHash {
|
|||||||
len = (len - 1) & ~63;
|
len = (len - 1) & ~63;
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
do {
|
do {
|
||||||
x = rotate(x + y + v.getLowValue() + fetch64(data, pos + 8), 37) * k1;
|
x = rotate64(x + y + v.getLowValue() + fetch64(data, pos + 8), 37) * k1;
|
||||||
y = rotate(y + v.getHighValue() + fetch64(data, pos + 48), 42) * k1;
|
y = rotate64(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 = rotate(z + w.getLowValue(), 33) * k1;
|
z = rotate64(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
|
||||||
@ -221,19 +223,19 @@ public class CityHash {
|
|||||||
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(rotate(y ^ k1, 49) * k1 + fetch64(byteArray, start));
|
v.setLowValue(rotate64(y ^ k1, 49) * k1 + fetch64(byteArray, start));
|
||||||
v.setHighValue(rotate(v.getLowValue(), 42) * k1 + fetch64(byteArray, start + 8));
|
v.setHighValue(rotate64(v.getLowValue(), 42) * k1 + fetch64(byteArray, start + 8));
|
||||||
w.setLowValue(rotate(y + z, 35) * k1 + x);
|
w.setLowValue(rotate64(y + z, 35) * k1 + x);
|
||||||
w.setHighValue(rotate(x + fetch64(byteArray, start + 88), 53) * k1);
|
w.setHighValue(rotate64(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 = rotate(x + y + v.getLowValue() + fetch64(byteArray, pos + 8), 37) * k1;
|
x = rotate64(x + y + v.getLowValue() + fetch64(byteArray, pos + 8), 37) * k1;
|
||||||
y = rotate(y + v.getHighValue() + fetch64(byteArray, pos + 48), 42) * k1;
|
y = rotate64(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 = rotate(z + w.getLowValue(), 33) * k1;
|
z = rotate64(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));
|
||||||
|
|
||||||
@ -241,11 +243,11 @@ public class CityHash {
|
|||||||
x = z;
|
x = z;
|
||||||
z = swapValue;
|
z = swapValue;
|
||||||
pos += 64;
|
pos += 64;
|
||||||
x = rotate(x + y + v.getLowValue() + fetch64(byteArray, pos + 8), 37) * k1;
|
x = rotate64(x + y + v.getLowValue() + fetch64(byteArray, pos + 8), 37) * k1;
|
||||||
y = rotate(y + v.getHighValue() + fetch64(byteArray, pos + 48), 42) * k1;
|
y = rotate64(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 = rotate(z + w.getLowValue(), 33) * k1;
|
z = rotate64(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;
|
||||||
@ -254,16 +256,16 @@ public class CityHash {
|
|||||||
pos += 64;
|
pos += 64;
|
||||||
len -= 128;
|
len -= 128;
|
||||||
} while (len >= 128);
|
} while (len >= 128);
|
||||||
x += rotate(v.getLowValue() + z, 49) * k0;
|
x += rotate64(v.getLowValue() + z, 49) * k0;
|
||||||
y = y * k0 + rotate(w.getHighValue(), 37);
|
y = y * k0 + rotate64(w.getHighValue(), 37);
|
||||||
z = z * k0 + rotate(w.getLowValue(), 27);
|
z = z * k0 + rotate64(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 = rotate(x + y, 42) * k0 + v.getHighValue();
|
y = rotate64(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);
|
||||||
@ -321,8 +323,8 @@ public class CityHash {
|
|||||||
long mul = k2 + len * 2L;
|
long mul = k2 + len * 2L;
|
||||||
long a = fetch64(byteArray, 0) + k2;
|
long a = fetch64(byteArray, 0) + k2;
|
||||||
long b = fetch64(byteArray, len - 8);
|
long b = fetch64(byteArray, len - 8);
|
||||||
long c = rotate(b, 37) * mul + a;
|
long c = rotate64(b, 37) * mul + a;
|
||||||
long d = (rotate(a, 25) + b) * mul;
|
long d = (rotate64(a, 25) + b) * mul;
|
||||||
return hashLen16(c, d, mul);
|
return hashLen16(c, d, mul);
|
||||||
}
|
}
|
||||||
if (len >= 4) {
|
if (len >= 4) {
|
||||||
@ -349,8 +351,8 @@ public class CityHash {
|
|||||||
long b = fetch64(byteArray, 8);
|
long b = fetch64(byteArray, 8);
|
||||||
long c = fetch64(byteArray, len - 8) * mul;
|
long c = fetch64(byteArray, len - 8) * mul;
|
||||||
long d = fetch64(byteArray, len - 16) * k2;
|
long d = fetch64(byteArray, len - 16) * k2;
|
||||||
return hashLen16(rotate(a + b, 43) + rotate(c, 30) + d,
|
return hashLen16(rotate64(a + b, 43) + rotate64(c, 30) + d,
|
||||||
a + rotate(b + k2, 18) + c, mul);
|
a + rotate64(b + k2, 18) + c, mul);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long hashLen33to64(byte[] byteArray) {
|
private static long hashLen33to64(byte[] byteArray) {
|
||||||
@ -364,10 +366,10 @@ public class CityHash {
|
|||||||
long f = fetch64(byteArray, 24) * 9;
|
long f = fetch64(byteArray, 24) * 9;
|
||||||
long g = fetch64(byteArray, len - 8);
|
long g = fetch64(byteArray, len - 8);
|
||||||
long h = fetch64(byteArray, len - 16) * mul;
|
long h = fetch64(byteArray, len - 16) * mul;
|
||||||
long u = rotate(a + g, 43) + (rotate(b, 30) + c) * 9;
|
long u = rotate64(a + g, 43) + (rotate64(b, 30) + c) * 9;
|
||||||
long v = ((a + g) ^ d) + f + 1;
|
long v = ((a + g) ^ d) + f + 1;
|
||||||
long w = Long.reverseBytes((u + v) * mul) + h;
|
long w = Long.reverseBytes((u + v) * mul) + h;
|
||||||
long x = rotate(e + f, 42) + c;
|
long x = rotate64(e + f, 42) + c;
|
||||||
long y = (Long.reverseBytes((v + w) * mul) + g) * mul;
|
long y = (Long.reverseBytes((v + w) * mul) + g) * mul;
|
||||||
long z = e + f + c;
|
long z = e + f + c;
|
||||||
a = Long.reverseBytes((x + z) * mul + y) + b;
|
a = Long.reverseBytes((x + z) * mul + y) + b;
|
||||||
@ -375,37 +377,15 @@ public class CityHash {
|
|||||||
return b + x;
|
return b + x;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long loadUnaligned64(final byte[] byteArray, final int start) {
|
private static long fetch64(byte[] byteArray, int start) {
|
||||||
long result = 0;
|
return ByteUtil.bytesToLong(byteArray, start, ByteUtil.CPU_ENDIAN);
|
||||||
OrderIter orderIter = new OrderIter(8);
|
|
||||||
while (orderIter.hasNext()) {
|
|
||||||
int next = orderIter.next();
|
|
||||||
long value = (byteArray[next + start] & 0xffL) << (next * 8);
|
|
||||||
result |= value;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int loadUnaligned32(final byte[] byteArray, final int start) {
|
|
||||||
int result = 0;
|
|
||||||
OrderIter orderIter = new OrderIter(4);
|
|
||||||
while (orderIter.hasNext()) {
|
|
||||||
int next = orderIter.next();
|
|
||||||
int value = (byteArray[next + start] & 0xff) << (next * 8);
|
|
||||||
result |= value;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long fetch64(byte[] byteArray, final int start) {
|
|
||||||
return loadUnaligned64(byteArray, start);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int fetch32(byte[] byteArray, final int start) {
|
private static int fetch32(byte[] byteArray, final int start) {
|
||||||
return loadUnaligned32(byteArray, start);
|
return ByteUtil.bytesToInt(byteArray, start, ByteUtil.CPU_ENDIAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long rotate(long val, int shift) {
|
private static long rotate64(long val, int shift) {
|
||||||
// Avoid shifting by 64: doing so yields an undefined result.
|
// Avoid shifting by 64: doing so yields an undefined result.
|
||||||
return shift == 0 ? val : ((val >>> shift) | (val << (64 - shift)));
|
return shift == 0 ? val : ((val >>> shift) | (val << (64 - shift)));
|
||||||
}
|
}
|
||||||
@ -465,11 +445,11 @@ public class CityHash {
|
|||||||
private static Number128 weakHashLen32WithSeeds(
|
private static Number128 weakHashLen32WithSeeds(
|
||||||
long w, long x, long y, long z, long a, long b) {
|
long w, long x, long y, long z, long a, long b) {
|
||||||
a += w;
|
a += w;
|
||||||
b = rotate(b + a + z, 21);
|
b = rotate64(b + a + z, 21);
|
||||||
long c = a;
|
long c = a;
|
||||||
a += x;
|
a += x;
|
||||||
a += y;
|
a += y;
|
||||||
b += rotate(a, 44);
|
b += rotate64(a, 44);
|
||||||
return new Number128(a + z, b + c);
|
return new Number128(a + z, b + c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,24 +495,5 @@ public class CityHash {
|
|||||||
b = hashLen16(d, b);
|
b = hashLen16(d, b);
|
||||||
return new Number128(a ^ b, hashLen16(b, a));
|
return new Number128(a ^ b, hashLen16(b, a));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class OrderIter {
|
|
||||||
private static final boolean IS_LITTLE_ENDIAN = "little".equals(System.getProperty("sun.cpu.endian"));
|
|
||||||
|
|
||||||
private final int size;
|
|
||||||
private int index;
|
|
||||||
|
|
||||||
OrderIter(int size) {
|
|
||||||
this.size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasNext() {
|
|
||||||
return index < size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int next() {
|
|
||||||
return IS_LITTLE_ENDIAN ? index++ : (size - 1 - index++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//------------------------------------------------------------------------------------------------------- Private method end
|
//------------------------------------------------------------------------------------------------------- Private method end
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,217 @@
|
|||||||
|
package cn.hutool.core.lang.hash;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ByteUtil;
|
||||||
|
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apache 发布的MetroHash算法,是一组用于非加密用例的最先进的哈希函数。
|
||||||
|
* 除了卓越的性能外,他们还以算法生成而著称。
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* 官方实现:https://github.com/jandrewrogers/MetroHash
|
||||||
|
* 官方文档:http://www.jandrewrogers.com/2015/05/27/metrohash/
|
||||||
|
* Go语言实现:https://github.com/linvon/cuckoo-filter/blob/main/vendor/github.com/dgryski/go-metro/
|
||||||
|
* @author li
|
||||||
|
*/
|
||||||
|
public class MetroHash {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash64 种子加盐
|
||||||
|
*/
|
||||||
|
private final static long k0_64 = 0xD6D018F5;
|
||||||
|
private final static long k1_64 = 0xA2AA033B;
|
||||||
|
private final static long k2_64 = 0x62992FC1;
|
||||||
|
private final static long k3_64 = 0x30BC5B29;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hash128 种子加盐
|
||||||
|
*/
|
||||||
|
private final static long k0_128 = 0xC83A91E1;
|
||||||
|
private final static long k1_128 = 0x8648DBDB;
|
||||||
|
private final static long k2_128 = 0x7BDEC03B;
|
||||||
|
private final static long k3_128 = 0x2F5870A5;
|
||||||
|
|
||||||
|
public static long hash64(byte[] data) {
|
||||||
|
return hash64(data, 1337);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Number128 hash128(byte[] data) {
|
||||||
|
return hash128(data, 1337);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long hash64(byte[] data, long seed) {
|
||||||
|
byte[] buffer = data;
|
||||||
|
long hash = (seed + k2_64) * k0_64;
|
||||||
|
|
||||||
|
long v0, v1, v2, v3;
|
||||||
|
v0 = hash;
|
||||||
|
v1 = hash;
|
||||||
|
v2 = hash;
|
||||||
|
v3 = hash;
|
||||||
|
|
||||||
|
if (buffer.length >= 32) {
|
||||||
|
|
||||||
|
while (buffer.length >= 32) {
|
||||||
|
v0 += littleEndian64(buffer, 0) * k0_64;
|
||||||
|
v0 = rotateLeft64(v0, -29) + v2;
|
||||||
|
v1 += littleEndian64(buffer, 8) * k1_64;
|
||||||
|
v1 = rotateLeft64(v1, -29) + v3;
|
||||||
|
v2 += littleEndian64(buffer, 24) * k2_64;
|
||||||
|
v2 = rotateLeft64(v2, -29) + v0;
|
||||||
|
v3 += littleEndian64(buffer, 32) * k3_64;
|
||||||
|
v3 = rotateLeft64(v3, -29) + v1;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 32, buffer.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
v2 ^= rotateLeft64(((v0 + v3) * k0_64) + v1, -37) * k1_64;
|
||||||
|
v3 ^= rotateLeft64(((v1 + v2) * k1_64) + v0, -37) * k0_64;
|
||||||
|
v0 ^= rotateLeft64(((v0 + v2) * k0_64) + v3, -37) * k1_64;
|
||||||
|
v1 ^= rotateLeft64(((v1 + v3) * k1_64) + v2, -37) * k0_64;
|
||||||
|
hash += v0 ^ v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.length >= 16) {
|
||||||
|
v0 = hash + littleEndian64(buffer, 0) * k2_64;
|
||||||
|
v0 = rotateLeft64(v0, -29) * k3_64;
|
||||||
|
v1 = hash + littleEndian64(buffer, 8) * k2_64;
|
||||||
|
v1 = rotateLeft64(v1, -29) * k3_64;
|
||||||
|
v0 ^= rotateLeft64(v0 * k0_64, -21) + v1;
|
||||||
|
v1 ^= rotateLeft64(v1 * k3_64, -21) + v0;
|
||||||
|
hash += v1;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 16, buffer.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.length >= 8) {
|
||||||
|
hash += littleEndian64(buffer, 0) * k3_64;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
|
||||||
|
hash ^= rotateLeft64(hash, -55) * k1_64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.length >= 4) {
|
||||||
|
hash += (long) littleEndian32(Arrays.copyOfRange(buffer, 0, 4)) * k3_64;
|
||||||
|
hash ^= rotateLeft64(hash, -26) * k1_64;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 4, buffer.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.length >= 2) {
|
||||||
|
hash += (long) littleEndian16(Arrays.copyOfRange(buffer, 0, 2)) * k3_64;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 2, buffer.length);
|
||||||
|
hash ^= rotateLeft64(hash, -48) * k1_64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.length >= 1) {
|
||||||
|
hash += (long) buffer[0] * k3_64;
|
||||||
|
hash ^= rotateLeft64(hash, -38) * k1_64;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash ^= rotateLeft64(hash, -28);
|
||||||
|
hash *= k0_64;
|
||||||
|
hash ^= rotateLeft64(hash, -29);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Number128 hash128(byte[] data, long seed) {
|
||||||
|
byte[] buffer = data;
|
||||||
|
|
||||||
|
long v0, v1, v2, v3;
|
||||||
|
|
||||||
|
v0 = (seed - k0_128) * k3_128;
|
||||||
|
v1 = (seed + k1_128) * k2_128;
|
||||||
|
|
||||||
|
if (buffer.length >= 32) {
|
||||||
|
v2 = (seed + k0_128) * k2_128;
|
||||||
|
v3 = (seed - k1_128) * k3_128;
|
||||||
|
|
||||||
|
while (buffer.length >= 32) {
|
||||||
|
v0 += littleEndian64(buffer, 0) * k0_128;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
|
||||||
|
v0 = rotateRight(v0, 29) + v2;
|
||||||
|
v1 += littleEndian64(buffer, 0) * k1_128;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
|
||||||
|
v1 = rotateRight(v1, 29) + v3;
|
||||||
|
v2 += littleEndian64(buffer, 0) * k2_128;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
|
||||||
|
v2 = rotateRight(v2, 29) + v0;
|
||||||
|
v3 = littleEndian64(buffer, 0) * k3_128;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
|
||||||
|
v3 = rotateRight(v3, 29) + v1;
|
||||||
|
}
|
||||||
|
|
||||||
|
v2 ^= rotateRight(((v0 + v3) * k0_128) + v1, 21) * k1_128;
|
||||||
|
v3 ^= rotateRight(((v1 + v2) * k1_128) + v0, 21) * k0_128;
|
||||||
|
v0 ^= rotateRight(((v0 + v2) * k0_128) + v3, 21) * k1_128;
|
||||||
|
v1 ^= rotateRight(((v1 + v3) * k1_128) + v2, 21) * k0_128;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.length >= 16) {
|
||||||
|
v0 += littleEndian64(buffer, 0) * k2_128;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
|
||||||
|
v0 = rotateRight(v0, 33) * k3_128;
|
||||||
|
v1 += littleEndian64(buffer, 0) * k2_128;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
|
||||||
|
v1 = rotateRight(v1, 33) * k3_128;
|
||||||
|
v0 ^= rotateRight((v0 * k2_128) + v1, 45) + k1_128;
|
||||||
|
v1 ^= rotateRight((v1 * k3_128) + v0, 45) + k0_128;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.length >= 8) {
|
||||||
|
v0 += littleEndian64(buffer, 0) * k2_128;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 8, buffer.length);
|
||||||
|
v0 = rotateRight(v0, 33) * k3_128;
|
||||||
|
v0 ^= rotateRight((v0 * k2_128) + v1, 27) * k1_128;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.length >= 4) {
|
||||||
|
v1 += (long) littleEndian32(buffer) * k2_128;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 4, buffer.length);
|
||||||
|
v1 = rotateRight(v1, 33) * k3_128;
|
||||||
|
v1 ^= rotateRight((v1 * k3_128) + v0, 46) * k0_128;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.length >= 2) {
|
||||||
|
v0 += (long) littleEndian16(buffer) * k2_128;
|
||||||
|
buffer = Arrays.copyOfRange(buffer, 2, buffer.length);
|
||||||
|
v0 = rotateRight(v0, 33) * k3_128;
|
||||||
|
v0 ^= rotateRight((v0 * k2_128) * v1, 22) * k1_128;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.length >= 1) {
|
||||||
|
v1 += (long) buffer[0] * k2_128;
|
||||||
|
v1 = rotateRight(v1, 33) * k3_128;
|
||||||
|
v1 ^= rotateRight((v1 * k3_128) + v0, 58) * k0_128;
|
||||||
|
}
|
||||||
|
|
||||||
|
v0 += rotateRight((v0 * k0_128) + v1, 13);
|
||||||
|
v1 += rotateRight((v1 * k1_128) + v0, 37);
|
||||||
|
v0 += rotateRight((v0 * k2_128) + v1, 13);
|
||||||
|
v1 += rotateRight((v1 * k3_128) + v0, 37);
|
||||||
|
|
||||||
|
return new Number128(v0, v1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static long littleEndian64(byte[] b, int start) {
|
||||||
|
return ByteUtil.bytesToLong(b, start, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int littleEndian32(byte[] b) {
|
||||||
|
return (int) b[0] | (int) b[1] << 8 | (int) b[2] << 16 | (int) b[3] << 24;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int littleEndian16(byte[] b) {
|
||||||
|
return ByteUtil.bytesToShort(b, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long rotateLeft64(long x, int k) {
|
||||||
|
int n = 64;
|
||||||
|
int s = k & (n - 1);
|
||||||
|
return x << s | x >> (n - s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long rotateRight(long val, int shift) {
|
||||||
|
return (val >> shift) | (val << (64 - shift));
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
package cn.hutool.core.lang.hash;
|
package cn.hutool.core.lang.hash;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ByteUtil;
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,6 +43,7 @@ public class MurmurHash implements Serializable{
|
|||||||
|
|
||||||
private static final int DEFAULT_SEED = 0;
|
private static final int DEFAULT_SEED = 0;
|
||||||
private static final Charset DEFAULT_CHARSET = CharsetUtil.CHARSET_UTF_8;
|
private static final Charset DEFAULT_CHARSET = CharsetUtil.CHARSET_UTF_8;
|
||||||
|
private static final ByteOrder DEFAULT_ORDER = ByteOrder.LITTLE_ENDIAN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Murmur3 32-bit Hash值计算
|
* Murmur3 32-bit Hash值计算
|
||||||
@ -76,11 +79,8 @@ public class MurmurHash implements Serializable{
|
|||||||
|
|
||||||
// body
|
// body
|
||||||
for (int i = 0; i < nblocks; i++) {
|
for (int i = 0; i < nblocks; i++) {
|
||||||
int i_4 = i << 2;
|
int i4 = i << 2;
|
||||||
int k = (data[i_4] & 0xff) //
|
int k = ByteUtil.bytesToInt(data, i4, DEFAULT_ORDER);
|
||||||
| ((data[i_4 + 1] & 0xff) << 8) //
|
|
||||||
| ((data[i_4 + 2] & 0xff) << 16) //
|
|
||||||
| ((data[i_4 + 3] & 0xff) << 24);
|
|
||||||
|
|
||||||
// mix functions
|
// mix functions
|
||||||
k *= C1_32;
|
k *= C1_32;
|
||||||
@ -157,14 +157,7 @@ public class MurmurHash implements Serializable{
|
|||||||
// body
|
// body
|
||||||
for (int i = 0; i < nblocks; i++) {
|
for (int i = 0; i < nblocks; i++) {
|
||||||
final int i8 = i << 3;
|
final int i8 = i << 3;
|
||||||
long k = ((long) data[i8] & 0xff) //
|
long k = ByteUtil.bytesToLong(data, i8, DEFAULT_ORDER);
|
||||||
| (((long) data[i8 + 1] & 0xff) << 8) //
|
|
||||||
| (((long) data[i8 + 2] & 0xff) << 16) //
|
|
||||||
| (((long) data[i8 + 3] & 0xff) << 24) //
|
|
||||||
| (((long) data[i8 + 4] & 0xff) << 32)//
|
|
||||||
| (((long) data[i8 + 5] & 0xff) << 40) //
|
|
||||||
| (((long) data[i8 + 6] & 0xff) << 48) //
|
|
||||||
| (((long) data[i8 + 7] & 0xff) << 56);
|
|
||||||
|
|
||||||
// mix functions
|
// mix functions
|
||||||
k *= C1;
|
k *= C1;
|
||||||
@ -241,23 +234,8 @@ public class MurmurHash implements Serializable{
|
|||||||
// body
|
// body
|
||||||
for (int i = 0; i < nblocks; i++) {
|
for (int i = 0; i < nblocks; i++) {
|
||||||
final int i16 = i << 4;
|
final int i16 = i << 4;
|
||||||
long k1 = ((long) data[i16] & 0xff) //
|
long k1 = ByteUtil.bytesToLong(data, i16, DEFAULT_ORDER);
|
||||||
| (((long) data[i16 + 1] & 0xff) << 8) //
|
long k2 = ByteUtil.bytesToLong(data, i16 + 8, DEFAULT_ORDER);
|
||||||
| (((long) data[i16 + 2] & 0xff) << 16) //
|
|
||||||
| (((long) data[i16 + 3] & 0xff) << 24) //
|
|
||||||
| (((long) data[i16 + 4] & 0xff) << 32) //
|
|
||||||
| (((long) data[i16 + 5] & 0xff) << 40) //
|
|
||||||
| (((long) data[i16 + 6] & 0xff) << 48) //
|
|
||||||
| (((long) data[i16 + 7] & 0xff) << 56);
|
|
||||||
|
|
||||||
long k2 = ((long) data[i16 + 8] & 0xff) //
|
|
||||||
| (((long) data[i16 + 9] & 0xff) << 8) //
|
|
||||||
| (((long) data[i16 + 10] & 0xff) << 16) //
|
|
||||||
| (((long) data[i16 + 11] & 0xff) << 24) //
|
|
||||||
| (((long) data[i16 + 12] & 0xff) << 32) //
|
|
||||||
| (((long) data[i16 + 13] & 0xff) << 40) //
|
|
||||||
| (((long) data[i16 + 14] & 0xff) << 48) //
|
|
||||||
| (((long) data[i16 + 15] & 0xff) << 56);
|
|
||||||
|
|
||||||
// mix functions for k1
|
// mix functions for k1
|
||||||
k1 *= C1;
|
k1 *= C1;
|
||||||
|
@ -6,7 +6,7 @@ package cn.hutool.core.lang.hash;
|
|||||||
* @author hexiufeng
|
* @author hexiufeng
|
||||||
* @since 5.2.5
|
* @since 5.2.5
|
||||||
*/
|
*/
|
||||||
public class Number128 extends Number{
|
public class Number128 extends Number {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private long lowValue;
|
private long lowValue;
|
||||||
@ -23,22 +23,47 @@ public class Number128 extends Number{
|
|||||||
this.highValue = highValue;
|
this.highValue = highValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取低位值
|
||||||
|
*
|
||||||
|
* @return 地位值
|
||||||
|
*/
|
||||||
public long getLowValue() {
|
public long getLowValue() {
|
||||||
return lowValue;
|
return lowValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getHighValue() {
|
/**
|
||||||
return highValue;
|
* 设置低位值
|
||||||
}
|
*
|
||||||
|
* @param lowValue 低位值
|
||||||
|
*/
|
||||||
public void setLowValue(long lowValue) {
|
public void setLowValue(long lowValue) {
|
||||||
this.lowValue = lowValue;
|
this.lowValue = lowValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取高位值
|
||||||
|
*
|
||||||
|
* @return 高位值
|
||||||
|
*/
|
||||||
|
public long getHighValue() {
|
||||||
|
return highValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置高位值
|
||||||
|
*
|
||||||
|
* @param hiValue 高位值
|
||||||
|
*/
|
||||||
public void setHighValue(long hiValue) {
|
public void setHighValue(long hiValue) {
|
||||||
this.highValue = hiValue;
|
this.highValue = hiValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取高低位数组,long[0]:低位,long[1]:高位
|
||||||
|
*
|
||||||
|
* @return 高低位数组,long[0]:低位,long[1]:高位
|
||||||
|
*/
|
||||||
public long[] getLongArray() {
|
public long[] getLongArray() {
|
||||||
return new long[]{lowValue, highValue};
|
return new long[]{lowValue, highValue};
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,6 @@ public class TreeBuilder<E> implements Builder<Tree<E>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Map<E, Tree<E>> eTreeMap = MapUtil.sortByValue(this.idTreeMap, false);
|
final Map<E, Tree<E>> eTreeMap = MapUtil.sortByValue(this.idTreeMap, false);
|
||||||
List<Tree<E>> rootTreeList = CollUtil.newArrayList();
|
|
||||||
E parentId;
|
E parentId;
|
||||||
for (Tree<E> node : eTreeMap.values()) {
|
for (Tree<E> node : eTreeMap.values()) {
|
||||||
if (null == node) {
|
if (null == node) {
|
||||||
@ -237,7 +236,6 @@ public class TreeBuilder<E> implements Builder<Tree<E>> {
|
|||||||
parentId = node.getParentId();
|
parentId = node.getParentId();
|
||||||
if (ObjectUtil.equals(this.root.getId(), parentId)) {
|
if (ObjectUtil.equals(this.root.getId(), parentId)) {
|
||||||
this.root.addChildren(node);
|
this.root.addChildren(node);
|
||||||
rootTreeList.add(node);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,11 @@ import java.util.concurrent.atomic.LongAdder;
|
|||||||
*/
|
*/
|
||||||
public class ByteUtil {
|
public class ByteUtil {
|
||||||
|
|
||||||
public static ByteOrder DEFAULT_ORDER = ByteOrder.LITTLE_ENDIAN;
|
public static final ByteOrder DEFAULT_ORDER = ByteOrder.LITTLE_ENDIAN;
|
||||||
|
/**
|
||||||
|
* CPU的字节序
|
||||||
|
*/
|
||||||
|
public static final ByteOrder CPU_ENDIAN = "little".equals(System.getProperty("sun.cpu.endian")) ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* int转byte
|
* int转byte
|
||||||
@ -130,16 +134,29 @@ public class ByteUtil {
|
|||||||
* @return int值
|
* @return int值
|
||||||
*/
|
*/
|
||||||
public static int bytesToInt(byte[] bytes, ByteOrder byteOrder) {
|
public static int bytesToInt(byte[] bytes, ByteOrder byteOrder) {
|
||||||
|
return bytesToInt(bytes, 0, byteOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte[]转int值<br>
|
||||||
|
* 自定义端序
|
||||||
|
*
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return int值
|
||||||
|
* @since 5.7.21
|
||||||
|
*/
|
||||||
|
public static int bytesToInt(byte[] bytes, int start, ByteOrder byteOrder) {
|
||||||
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
|
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
|
||||||
return bytes[0] & 0xFF | //
|
return bytes[start] & 0xFF | //
|
||||||
(bytes[1] & 0xFF) << 8 | //
|
(bytes[1 + start] & 0xFF) << 8 | //
|
||||||
(bytes[2] & 0xFF) << 16 | //
|
(bytes[2 + start] & 0xFF) << 16 | //
|
||||||
(bytes[3] & 0xFF) << 24; //
|
(bytes[3 + start] & 0xFF) << 24; //
|
||||||
} else {
|
} else {
|
||||||
return bytes[3] & 0xFF | //
|
return bytes[3 + start] & 0xFF | //
|
||||||
(bytes[2] & 0xFF) << 8 | //
|
(bytes[2 + start] & 0xFF) << 8 | //
|
||||||
(bytes[1] & 0xFF) << 16 | //
|
(bytes[1 + start] & 0xFF) << 16 | //
|
||||||
(bytes[0] & 0xFF) << 24; //
|
(bytes[start] & 0xFF) << 24; //
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -243,16 +260,31 @@ public class ByteUtil {
|
|||||||
* @return long值
|
* @return long值
|
||||||
*/
|
*/
|
||||||
public static long bytesToLong(byte[] bytes, ByteOrder byteOrder) {
|
public static long bytesToLong(byte[] bytes, ByteOrder byteOrder) {
|
||||||
|
return bytesToLong(bytes, 0, byteOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* byte数组转long<br>
|
||||||
|
* 自定义端序<br>
|
||||||
|
* from: https://stackoverflow.com/questions/4485128/how-do-i-convert-long-to-byte-and-back-in-java
|
||||||
|
*
|
||||||
|
* @param bytes byte数组
|
||||||
|
* @param start 计算数组开始位置
|
||||||
|
* @param byteOrder 端序
|
||||||
|
* @return long值
|
||||||
|
* @since 5.7.21
|
||||||
|
*/
|
||||||
|
public static long bytesToLong(byte[] bytes, int start, ByteOrder byteOrder) {
|
||||||
long values = 0;
|
long values = 0;
|
||||||
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
|
if (ByteOrder.LITTLE_ENDIAN == byteOrder) {
|
||||||
for (int i = (Long.BYTES - 1); i >= 0; i--) {
|
for (int i = (Long.BYTES - 1); i >= 0; i--) {
|
||||||
values <<= Byte.SIZE;
|
values <<= Byte.SIZE;
|
||||||
values |= (bytes[i] & 0xff);
|
values |= (bytes[i + start] & 0xff);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < Long.BYTES; i++) {
|
for (int i = 0; i < Long.BYTES; i++) {
|
||||||
values <<= Byte.SIZE;
|
values <<= Byte.SIZE;
|
||||||
values |= (bytes[i] & 0xff);
|
values |= (bytes[i + start] & 0xff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package cn.hutool.core.util;
|
package cn.hutool.core.util;
|
||||||
|
|
||||||
import cn.hutool.core.lang.hash.CityHash;
|
import cn.hutool.core.lang.hash.CityHash;
|
||||||
|
import cn.hutool.core.lang.hash.MetroHash;
|
||||||
import cn.hutool.core.lang.hash.MurmurHash;
|
import cn.hutool.core.lang.hash.MurmurHash;
|
||||||
import cn.hutool.core.lang.hash.Number128;
|
import cn.hutool.core.lang.hash.Number128;
|
||||||
|
|
||||||
@ -386,7 +387,7 @@ public class HashUtil {
|
|||||||
if (ucChar <= 'Z' && ucChar >= 'A') {
|
if (ucChar <= 'Z' && ucChar >= 'A') {
|
||||||
ucChar = (char) (ucChar + 32);
|
ucChar = (char) (ucChar + 32);
|
||||||
}
|
}
|
||||||
hash += (3 * i * ucChar * ucChar + 5 * i * ucChar + 7 * i + 11 * ucChar) % 16777216;
|
hash += (3L * i * ucChar * ucChar + 5L * i * ucChar + 7L * i + 11 * ucChar) % 16777216;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (i = 1; i <= 96; i++) {
|
for (i = 1; i <= 96; i++) {
|
||||||
@ -394,7 +395,7 @@ public class HashUtil {
|
|||||||
if (ucChar <= 'Z' && ucChar >= 'A') {
|
if (ucChar <= 'Z' && ucChar >= 'A') {
|
||||||
ucChar = (char) (ucChar + 32);
|
ucChar = (char) (ucChar + 32);
|
||||||
}
|
}
|
||||||
hash += (3 * i * ucChar * ucChar + 5 * i * ucChar + 7 * i + 11 * ucChar) % 16777216;
|
hash += (3L * i * ucChar * ucChar + 5L * i * ucChar + 7L * i + 11 * ucChar) % 16777216;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hash < 0) {
|
if (hash < 0) {
|
||||||
@ -545,4 +546,46 @@ public class HashUtil {
|
|||||||
public static long[] cityHash128(byte[] data, Number128 seed) {
|
public static long[] cityHash128(byte[] data, Number128 seed) {
|
||||||
return CityHash.hash128(data, seed).getLongArray();
|
return CityHash.hash128(data, seed).getLongArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetroHash 算法64-bit实现
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @param seed 种子
|
||||||
|
* @return hash值
|
||||||
|
*/
|
||||||
|
public static long metroHash64(byte[] data, long seed) {
|
||||||
|
return MetroHash.hash64(data, seed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetroHash 算法64-bit实现
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @return hash值
|
||||||
|
*/
|
||||||
|
public static long metroHash64(byte[] data) {
|
||||||
|
return MetroHash.hash64(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetroHash 算法128-bit实现
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @param seed 种子
|
||||||
|
* @return hash值,long[0]:低位,long[1]:高位
|
||||||
|
*/
|
||||||
|
public static long[] metroHash128(byte[] data, long seed) {
|
||||||
|
return MetroHash.hash128(data,seed).getLongArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MetroHash 算法128-bit实现
|
||||||
|
*
|
||||||
|
* @param data 数据
|
||||||
|
* @return hash值,long[0]:低位,long[1]:高位
|
||||||
|
*/
|
||||||
|
public static long[] metroHash128(byte[] data) {
|
||||||
|
return MetroHash.hash128(data).getLongArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
36
hutool-core/src/test/java/cn/hutool/core/lang/hash/CityHashTest.java
Executable file
36
hutool-core/src/test/java/cn/hutool/core/lang/hash/CityHashTest.java
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
package cn.hutool.core.lang.hash;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class CityHashTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hash32Test() {
|
||||||
|
int hv = CityHash.hash32(StrUtil.utf8Bytes("你"));
|
||||||
|
Assert.assertEquals(1290029860, hv);
|
||||||
|
|
||||||
|
hv = CityHash.hash32(StrUtil.utf8Bytes("你好"));
|
||||||
|
Assert.assertEquals(1374181357, hv);
|
||||||
|
|
||||||
|
hv = CityHash.hash32(StrUtil.utf8Bytes("见到你很高兴"));
|
||||||
|
Assert.assertEquals(1475516842, hv);
|
||||||
|
hv = CityHash.hash32(StrUtil.utf8Bytes("我们将通过生成一个大的文件的方式来检验各种方法的执行效率因为这种方式在结束的时候需要执行文件"));
|
||||||
|
Assert.assertEquals(0x51020cae, hv);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hash64Test() {
|
||||||
|
long hv = CityHash.hash64(StrUtil.utf8Bytes("你"));
|
||||||
|
Assert.assertEquals(-4296898700418225525L, hv);
|
||||||
|
|
||||||
|
hv = CityHash.hash64(StrUtil.utf8Bytes("你好"));
|
||||||
|
Assert.assertEquals(-4294276205456761303L, hv);
|
||||||
|
|
||||||
|
hv = CityHash.hash64(StrUtil.utf8Bytes("见到你很高兴"));
|
||||||
|
Assert.assertEquals(272351505337503793L, hv);
|
||||||
|
hv = CityHash.hash64(StrUtil.utf8Bytes("我们将通过生成一个大的文件的方式来检验各种方法的执行效率因为这种方式在结束的时候需要执行文件"));
|
||||||
|
Assert.assertEquals(-8234735310919228703L, hv);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
package cn.hutool.core.lang.hash;
|
||||||
|
|
||||||
|
|
||||||
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
|
import cn.hutool.core.util.HexUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://gitee.com/dromara/hutool/pulls/532
|
||||||
|
*/
|
||||||
|
public class MetroHashTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmpty() {
|
||||||
|
Assert.assertEquals("31290877cceaea29", HexUtil.toHex(MetroHash.hash64(StrUtil.utf8Bytes(""), 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void metroHash64Test() {
|
||||||
|
byte[] str = "我是一段测试123".getBytes(CharsetUtil.CHARSET_UTF_8);
|
||||||
|
final long hash64 = MetroHash.hash64(str);
|
||||||
|
Assert.assertEquals(62920234463891865L, hash64);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void metroHash128Test() {
|
||||||
|
byte[] str = "我是一段测试123".getBytes(CharsetUtil.CHARSET_UTF_8);
|
||||||
|
final long[] hash128 = MetroHash.hash128(str).getLongArray();
|
||||||
|
Assert.assertEquals(4956592424592439349L, hash128[0]);
|
||||||
|
Assert.assertEquals(6301214698325086246L, hash128[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据量越大 MetroHash 优势越明显,
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void bulkHashing64Test() {
|
||||||
|
String[] strArray = getRandomStringArray(10000000);
|
||||||
|
long startCity = System.currentTimeMillis();
|
||||||
|
for (String s : strArray) {
|
||||||
|
CityHash.hash64(s.getBytes());
|
||||||
|
}
|
||||||
|
long endCity = System.currentTimeMillis();
|
||||||
|
|
||||||
|
long startMetro = System.currentTimeMillis();
|
||||||
|
for (String s : strArray) {
|
||||||
|
MetroHash.hash64(StrUtil.utf8Bytes(s));
|
||||||
|
}
|
||||||
|
long endMetro = System.currentTimeMillis();
|
||||||
|
|
||||||
|
System.out.println("metroHash =============" + (endMetro - startMetro));
|
||||||
|
System.out.println("cityHash =============" + (endCity - startCity));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据量越大 MetroHash 优势越明显,
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void bulkHashing128Test() {
|
||||||
|
String[] strArray = getRandomStringArray(10000000);
|
||||||
|
long startCity = System.currentTimeMillis();
|
||||||
|
for (String s : strArray) {
|
||||||
|
CityHash.hash128(s.getBytes());
|
||||||
|
}
|
||||||
|
long endCity = System.currentTimeMillis();
|
||||||
|
|
||||||
|
long startMetro = System.currentTimeMillis();
|
||||||
|
for (String s : strArray) {
|
||||||
|
MetroHash.hash128(StrUtil.utf8Bytes(s));
|
||||||
|
}
|
||||||
|
long endMetro = System.currentTimeMillis();
|
||||||
|
|
||||||
|
System.out.println("metroHash =============" + (endMetro - startMetro));
|
||||||
|
System.out.println("cityHash =============" + (endCity - startCity));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static String[] getRandomStringArray(int length) {
|
||||||
|
String[] result = new String[length];
|
||||||
|
Random random = new Random();
|
||||||
|
int index = 0;
|
||||||
|
while (index < length) {
|
||||||
|
result[index++] = getRandomString(random.nextInt(64));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getRandomString(int length) {
|
||||||
|
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
Random random = new Random();
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
int number = random.nextInt(62);
|
||||||
|
sb.append(str.charAt(number));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
36
hutool-core/src/test/java/cn/hutool/core/lang/hash/MurMurHashTest.java
Executable file
36
hutool-core/src/test/java/cn/hutool/core/lang/hash/MurMurHashTest.java
Executable file
@ -0,0 +1,36 @@
|
|||||||
|
package cn.hutool.core.lang.hash;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class MurMurHashTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hash32Test() {
|
||||||
|
int hv = MurmurHash.hash32(StrUtil.utf8Bytes("你"));
|
||||||
|
Assert.assertEquals(222142701, hv);
|
||||||
|
|
||||||
|
hv = MurmurHash.hash32(StrUtil.utf8Bytes("你好"));
|
||||||
|
Assert.assertEquals(1188098267, hv);
|
||||||
|
|
||||||
|
hv = MurmurHash.hash32(StrUtil.utf8Bytes("见到你很高兴"));
|
||||||
|
Assert.assertEquals(-1898490321, hv);
|
||||||
|
hv = MurmurHash.hash32(StrUtil.utf8Bytes("我们将通过生成一个大的文件的方式来检验各种方法的执行效率因为这种方式在结束的时候需要执行文件"));
|
||||||
|
Assert.assertEquals(-1713131054, hv);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hash64Test() {
|
||||||
|
long hv = MurmurHash.hash64(StrUtil.utf8Bytes("你"));
|
||||||
|
Assert.assertEquals(-1349759534971957051L, hv);
|
||||||
|
|
||||||
|
hv = MurmurHash.hash64(StrUtil.utf8Bytes("你好"));
|
||||||
|
Assert.assertEquals(-7563732748897304996L, hv);
|
||||||
|
|
||||||
|
hv = MurmurHash.hash64(StrUtil.utf8Bytes("见到你很高兴"));
|
||||||
|
Assert.assertEquals(-766658210119995316L, hv);
|
||||||
|
hv = MurmurHash.hash64(StrUtil.utf8Bytes("我们将通过生成一个大的文件的方式来检验各种方法的执行效率因为这种方式在结束的时候需要执行文件"));
|
||||||
|
Assert.assertEquals(-7469283059271653317L, hv);
|
||||||
|
}
|
||||||
|
}
|
@ -10,9 +10,16 @@ public class ByteUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void intAndBytesLittleEndianTest() {
|
public void intAndBytesLittleEndianTest() {
|
||||||
// 测试 int 转小端序 byte 数组
|
// 测试 int 转小端序 byte 数组
|
||||||
int int1 = RandomUtil.randomInt();
|
int int1 = RandomUtil.randomInt((Integer.MAX_VALUE));
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
|
||||||
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
buffer.putInt(int1);
|
||||||
|
byte[] bytesIntFromBuffer = buffer.array();
|
||||||
|
|
||||||
byte[] bytesInt = ByteUtil.intToBytes(int1, ByteOrder.LITTLE_ENDIAN);
|
byte[] bytesInt = ByteUtil.intToBytes(int1, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
Assert.assertArrayEquals(bytesIntFromBuffer, bytesInt);
|
||||||
|
|
||||||
int int2 = ByteUtil.bytesToInt(bytesInt, ByteOrder.LITTLE_ENDIAN);
|
int int2 = ByteUtil.bytesToInt(bytesInt, ByteOrder.LITTLE_ENDIAN);
|
||||||
Assert.assertEquals(int1, int2);
|
Assert.assertEquals(int1, int2);
|
||||||
|
|
||||||
@ -28,8 +35,14 @@ public class ByteUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void intAndBytesBigEndianTest() {
|
public void intAndBytesBigEndianTest() {
|
||||||
// 测试 int 转大端序 byte 数组
|
// 测试 int 转大端序 byte 数组
|
||||||
int int2 = RandomUtil.randomInt();
|
int int2 = RandomUtil.randomInt(Integer.MAX_VALUE);
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES);
|
||||||
|
buffer.putInt(int2);
|
||||||
|
byte[] bytesIntFromBuffer = buffer.array();
|
||||||
|
|
||||||
byte[] bytesInt = ByteUtil.intToBytes(int2, ByteOrder.BIG_ENDIAN);
|
byte[] bytesInt = ByteUtil.intToBytes(int2, ByteOrder.BIG_ENDIAN);
|
||||||
|
Assert.assertArrayEquals(bytesIntFromBuffer, bytesInt);
|
||||||
|
|
||||||
// 测试大端序 byte 数组转 int
|
// 测试大端序 byte 数组转 int
|
||||||
int int3 = ByteUtil.bytesToInt(bytesInt, ByteOrder.BIG_ENDIAN);
|
int int3 = ByteUtil.bytesToInt(bytesInt, ByteOrder.BIG_ENDIAN);
|
||||||
@ -39,9 +52,16 @@ public class ByteUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void longAndBytesLittleEndianTest() {
|
public void longAndBytesLittleEndianTest() {
|
||||||
// 测试 long 转 byte 数组
|
// 测试 long 转 byte 数组
|
||||||
long long1 = 2223;
|
long long1 = RandomUtil.randomLong(Long.MAX_VALUE);
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
|
||||||
|
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
buffer.putLong(long1);
|
||||||
|
byte[] bytesLongFromBuffer = buffer.array();
|
||||||
|
|
||||||
byte[] bytesLong = ByteUtil.longToBytes(long1, ByteOrder.LITTLE_ENDIAN);
|
byte[] bytesLong = ByteUtil.longToBytes(long1, ByteOrder.LITTLE_ENDIAN);
|
||||||
|
Assert.assertArrayEquals(bytesLongFromBuffer, bytesLong);
|
||||||
|
|
||||||
long long2 = ByteUtil.bytesToLong(bytesLong, ByteOrder.LITTLE_ENDIAN);
|
long long2 = ByteUtil.bytesToLong(bytesLong, ByteOrder.LITTLE_ENDIAN);
|
||||||
Assert.assertEquals(long1, long2);
|
Assert.assertEquals(long1, long2);
|
||||||
|
|
||||||
@ -57,11 +77,16 @@ public class ByteUtilTest {
|
|||||||
@Test
|
@Test
|
||||||
public void longAndBytesBigEndianTest() {
|
public void longAndBytesBigEndianTest() {
|
||||||
// 测试大端序 long 转 byte 数组
|
// 测试大端序 long 转 byte 数组
|
||||||
long long1 = 2223;
|
long long1 = RandomUtil.randomLong(Long.MAX_VALUE);
|
||||||
|
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
|
||||||
|
buffer.putLong(long1);
|
||||||
|
byte[] bytesLongFromBuffer = buffer.array();
|
||||||
|
|
||||||
byte[] bytesLong = ByteUtil.longToBytes(long1, ByteOrder.BIG_ENDIAN);
|
byte[] bytesLong = ByteUtil.longToBytes(long1, ByteOrder.BIG_ENDIAN);
|
||||||
long long2 = ByteUtil.bytesToLong(bytesLong, ByteOrder.BIG_ENDIAN);
|
Assert.assertArrayEquals(bytesLongFromBuffer, bytesLong);
|
||||||
|
|
||||||
|
long long2 = ByteUtil.bytesToLong(bytesLong, ByteOrder.BIG_ENDIAN);
|
||||||
Assert.assertEquals(long1, long2);
|
Assert.assertEquals(long1, long2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cn.hutool.db.ds.c3p0;
|
package cn.hutool.db.ds.c3p0;
|
||||||
|
|
||||||
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.db.DbRuntimeException;
|
import cn.hutool.db.DbRuntimeException;
|
||||||
import cn.hutool.db.ds.AbstractDSFactory;
|
import cn.hutool.db.ds.AbstractDSFactory;
|
||||||
@ -40,14 +41,6 @@ public class C3p0DSFactory extends AbstractDSFactory {
|
|||||||
@Override
|
@Override
|
||||||
protected DataSource createDataSource(String jdbcUrl, String driver, String user, String pass, Setting poolSetting) {
|
protected DataSource createDataSource(String jdbcUrl, String driver, String user, String pass, Setting poolSetting) {
|
||||||
final ComboPooledDataSource ds = new ComboPooledDataSource();
|
final ComboPooledDataSource ds = new ComboPooledDataSource();
|
||||||
ds.setJdbcUrl(jdbcUrl);
|
|
||||||
try {
|
|
||||||
ds.setDriverClass(driver);
|
|
||||||
} catch (PropertyVetoException e) {
|
|
||||||
throw new DbRuntimeException(e);
|
|
||||||
}
|
|
||||||
ds.setUser(user);
|
|
||||||
ds.setPassword(pass);
|
|
||||||
|
|
||||||
// remarks等特殊配置,since 5.3.8
|
// remarks等特殊配置,since 5.3.8
|
||||||
final Props connProps = new Props();
|
final Props connProps = new Props();
|
||||||
@ -58,7 +51,18 @@ public class C3p0DSFactory extends AbstractDSFactory {
|
|||||||
connProps.setProperty(key, connValue);
|
connProps.setProperty(key, connValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(MapUtil.isNotEmpty(connProps)){
|
||||||
ds.setProperties(connProps);
|
ds.setProperties(connProps);
|
||||||
|
}
|
||||||
|
|
||||||
|
ds.setJdbcUrl(jdbcUrl);
|
||||||
|
try {
|
||||||
|
ds.setDriverClass(driver);
|
||||||
|
} catch (PropertyVetoException e) {
|
||||||
|
throw new DbRuntimeException(e);
|
||||||
|
}
|
||||||
|
ds.setUser(user);
|
||||||
|
ds.setPassword(pass);
|
||||||
|
|
||||||
// 注入属性
|
// 注入属性
|
||||||
poolSetting.toBean(ds);
|
poolSetting.toBean(ds);
|
||||||
|
@ -2,6 +2,7 @@ package cn.hutool.db;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.db.ds.DSFactory;
|
import cn.hutool.db.ds.DSFactory;
|
||||||
|
import cn.hutool.db.ds.DataSourceWrapper;
|
||||||
import cn.hutool.db.ds.bee.BeeDSFactory;
|
import cn.hutool.db.ds.bee.BeeDSFactory;
|
||||||
import cn.hutool.db.ds.c3p0.C3p0DSFactory;
|
import cn.hutool.db.ds.c3p0.C3p0DSFactory;
|
||||||
import cn.hutool.db.ds.dbcp.DbcpDSFactory;
|
import cn.hutool.db.ds.dbcp.DbcpDSFactory;
|
||||||
@ -9,6 +10,7 @@ import cn.hutool.db.ds.druid.DruidDSFactory;
|
|||||||
import cn.hutool.db.ds.hikari.HikariDSFactory;
|
import cn.hutool.db.ds.hikari.HikariDSFactory;
|
||||||
import cn.hutool.db.ds.pooled.PooledDSFactory;
|
import cn.hutool.db.ds.pooled.PooledDSFactory;
|
||||||
import cn.hutool.db.ds.tomcat.TomcatDSFactory;
|
import cn.hutool.db.ds.tomcat.TomcatDSFactory;
|
||||||
|
import com.mchange.v2.c3p0.ComboPooledDataSource;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@ -87,6 +89,15 @@ public class DsTest {
|
|||||||
Assert.assertTrue(CollUtil.isNotEmpty(all));
|
Assert.assertTrue(CollUtil.isNotEmpty(all));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void c3p0DsUserAndPassTest() {
|
||||||
|
// https://gitee.com/dromara/hutool/issues/I4T7XZ
|
||||||
|
DSFactory.setCurrentDSFactory(new C3p0DSFactory());
|
||||||
|
ComboPooledDataSource ds = (ComboPooledDataSource) ((DataSourceWrapper) DSFactory.get("mysql")).getRaw();
|
||||||
|
Assert.assertEquals("root", ds.getUser());
|
||||||
|
Assert.assertEquals("123456", ds.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void hutoolPoolTest() throws SQLException {
|
public void hutoolPoolTest() throws SQLException {
|
||||||
DSFactory.setCurrentDSFactory(new PooledDSFactory());
|
DSFactory.setCurrentDSFactory(new PooledDSFactory());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user