diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/codec/Number128.java b/hutool-core/src/main/java/org/dromara/hutool/core/codec/Number128.java index 91cc45bd6..749b3b91b 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/codec/Number128.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/codec/Number128.java @@ -52,36 +52,36 @@ public class Number128 extends Number implements Comparable{ } /** - * 获取高位值 + * 获取最高有效位(Most Significant Bit),64 bit(8 bytes) * - * @return 高位值 + * @return 最高有效位(Most Significant Bit),64 bit(8 bytes) */ public long getMostSigBits() { return mostSigBits; } /** - * 设置高位值 + * 设置最高有效位(Most Significant Bit),64 bit(8 bytes) * - * @param hiValue 高位值 + * @param hiValue 最高有效位(Most Significant Bit),64 bit(8 bytes) */ public void setMostSigBits(final long hiValue) { this.mostSigBits = hiValue; } /** - * 获取低位值 + * 获取最低有效位(Least Significant Bit),64 bit(8 bytes) * - * @return 地位值 + * @return 最低有效位(Least Significant Bit),64 bit(8 bytes) */ public long getLeastSigBits() { return leastSigBits; } /** - * 设置低位值 + * 设置最低有效位(Least Significant Bit),64 bit(8 bytes) * - * @param leastSigBits 低位值 + * @param leastSigBits 最低有效位(Least Significant Bit),64 bit(8 bytes) */ public void setLeastSigBits(final long leastSigBits) { this.leastSigBits = leastSigBits; diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/data/id/ULID.java b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/ULID.java index 880bea43f..b9d2335b2 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/data/id/ULID.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/ULID.java @@ -21,6 +21,7 @@ import org.dromara.hutool.core.util.RandomUtil; import java.io.Serializable; import java.nio.ByteOrder; import java.util.Objects; +import java.util.Random; /** * 参考:https://github.com/zjcscut/framework-mesh/blob/master/ulid4j/src/main/java/cn/vlts/ulid/ULID.java @@ -51,10 +52,68 @@ public class ULID implements Comparable, Serializable { private static final long OVERFLOW = 0x0000000000000000L; // region ----- Factory methods + + /** + * 创建一个新的ULID,使用当前系统时间戳和随机数 + * + * @return ULID + */ public static ULID of() { - return of(System.currentTimeMillis(), RandomUtil.randomBytes(RANDOMNESS_BYTE_LEN)); + return of(System.currentTimeMillis()); } + /** + * 创建一个新的ULID,使用指定系统时间戳和随机数 + * + * @param timestamp 时间戳 + * @return ULID + */ + public static ULID of(final long timestamp) { + return of(timestamp, RandomUtil.getRandom()); + } + + /** + * 创建一个新的ULID,使用指定系统时间戳和指定随机对象 + * + * @param timestamp 时间戳 + * @param random {@link Random} + * @return ULID + */ + public static ULID of(final long timestamp, final Random random) { + return of(timestamp, RandomUtil.randomBytes(RANDOMNESS_BYTE_LEN, random)); + } + + /** + * 创建一个新的ULID,使用指定系统时间戳和指定填充数 + * + * @param timestamp 时间戳 + * @param randomness 指定填充数 + * @return ULID + */ + public static ULID of(final long timestamp, final byte[] randomness) { + // 时间戳最多为48 bit(6 bytes) + checkTimestamp(timestamp); + Assert.notNull(randomness); + // 随机数部分长度必须为80 bit(10 bytes) + Assert.isTrue(RANDOMNESS_BYTE_LEN == randomness.length, "Invalid randomness"); + + long msb = 0; + // 时间戳左移16位,低位补零准备填入部分随机数位,即16_bit_uint_random + msb |= timestamp << 16; + // randomness[0]左移0位填充到16_bit_uint_random的高8位,randomness[1]填充到16_bit_uint_random的低8位 + msb |= (long) (randomness[0x0] & 0xff) << 8; + // randomness[1]填充到16_bit_uint_random的低8位 + msb |= randomness[0x1] & 0xff; + + return new ULID(new Number128(ByteUtil.toLong(randomness, 2, ByteOrder.BIG_ENDIAN), msb)); + } + + /** + * 解析一个Crockford`s Base32的ULID + * + * @param ulidString Crockford`s Base32的ULID + * @return ULID + */ public static ULID of(final String ulidString) { Objects.requireNonNull(ulidString, "ulidString must not be null!"); if (ulidString.length() != 26) { @@ -75,6 +134,12 @@ public class ULID implements Comparable, Serializable { return new ULID(new Number128(least, most)); } + /** + * 从bytes解析ULID + * + * @param data bytes + * @return ULID + */ public static ULID of(final byte[] data) { Objects.requireNonNull(data, "data must not be null!"); if (data.length != 16) { @@ -91,24 +156,6 @@ public class ULID implements Comparable, Serializable { return new ULID(new Number128(leastSignificantBits, mostSignificantBits)); } - public static ULID of(final long timestamp, final byte[] randomness) { - // 时间戳最多为48 bit(6 bytes) - checkTimestamp(timestamp); - Assert.notNull(randomness); - // 随机数部分长度必须为80 bit(10 bytes) - Assert.isTrue(RANDOMNESS_BYTE_LEN == randomness.length, "Invalid randomness"); - - long msb = 0; - // 时间戳左移16位,低位补零准备填入部分随机数位,即16_bit_uint_random - msb |= timestamp << 16; - // randomness[0]左移0位填充到16_bit_uint_random的高8位,randomness[1]填充到16_bit_uint_random的低8位 - msb |= (long) (randomness[0x0] & 0xff) << 8; - // randomness[1]填充到16_bit_uint_random的低8位 - msb |= randomness[0x1] & 0xff; - - return new ULID(new Number128(ByteUtil.toLong(randomness, 2, ByteOrder.BIG_ENDIAN), msb)); - } - // endregion private final Number128 idValue; @@ -122,27 +169,37 @@ public class ULID implements Comparable, Serializable { this.idValue = number128; } + /** + * 获取最高有效位(Most Significant Bit),64 bit(8 bytes) + * + * @return 最高有效位(Most Significant Bit),64 bit(8 bytes) + */ public long getMostSignificantBits() { return this.idValue.getMostSigBits(); } + /** + * 获取最低有效位(Least Significant Bit),64 bit(8 bytes) + * + * @return 最低有效位(Least Significant Bit),64 bit(8 bytes) + */ public long getLeastSignificantBits() { return this.idValue.getLeastSigBits(); } /** - * Get the timestamp component of ULID + * 获取ULID的时间戳部分 * - * @return the timestamp component + * @return 时间戳 */ public long getTimestamp() { return this.idValue.getMostSigBits() >>> 16; } /** - * Get the randomness component of ULID + * 获取ULID的随机数部分 * - * @return the randomness component + * @return 随机数部分 */ public byte[] getRandomness() { final long msb = this.idValue.getMostSigBits(); @@ -156,6 +213,11 @@ public class ULID implements Comparable, Serializable { return randomness; } + /** + * 自增ULID + * + * @return 返回自增ULID + */ public ULID increment() { final long msb = this.idValue.getMostSigBits(); final long lsb = this.idValue.getLeastSigBits(); @@ -164,9 +226,27 @@ public class ULID implements Comparable, Serializable { if (newLsb == OVERFLOW) { newMsb += 1; } - return new ULID(new Number128(lsb, msb)); + return new ULID(new Number128(newLsb, newMsb)); } + /** + * 获取下一个有序的ULID + * + * @param timestamp 时间戳 + * @return 如果给定时间戳与当前ULID相同,则返回自增ULID,否则返回一个新的ULID + */ + public ULID nextMonotonic(final long timestamp) { + if (getTimestamp() == timestamp) { + return increment(); + } + return of(timestamp); + } + + /** + * 转为bytes值 + * + * @return bytes值 + */ public byte[] toBytes() { final long msb = this.idValue.getMostSigBits(); final long lsb = this.idValue.getLeastSigBits(); @@ -181,12 +261,22 @@ public class ULID implements Comparable, Serializable { return result; } + /** + * 转为UUID + * + * @return UUID + */ public UUID toUUID() { final long msb = this.idValue.getMostSigBits(); final long lsb = this.idValue.getLeastSigBits(); return new UUID(msb, lsb); } + /** + * 转为JDK的UUID + * + * @return UUID + */ public java.util.UUID toJdkUUID() { final long msb = this.idValue.getMostSigBits(); final long lsb = this.idValue.getLeastSigBits(); diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/data/id/ULIDGenerator.java b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/ULIDGenerator.java new file mode 100644 index 000000000..acc2f3008 --- /dev/null +++ b/hutool-core/src/main/java/org/dromara/hutool/core/data/id/ULIDGenerator.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023. looly(loolly@aliyun.com) + * Hutool is licensed under Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * https://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PSL v2 for more details. + */ + +package org.dromara.hutool.core.data.id; + +import org.dromara.hutool.core.lang.generator.Generator; + +/** + * ULID生成器 + * + * @author looly + * @since 6.0.0 + */ +public class ULIDGenerator implements Generator { + @Override + public String next() { + return ULID.of().toString(); + } +} diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java index 979e4ef94..834632c73 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/util/RandomUtil.java @@ -186,8 +186,23 @@ public class RandomUtil { * @return bytes */ public static byte[] randomBytes(final int length) { + return randomBytes(length, getRandom()); + } + + /** + * 随机bytes + * + * @param length 长度 + * @param random {@link Random} + * @return bytes + * @since 6.0.0 + */ + public static byte[] randomBytes(final int length, Random random) { + if (null == random) { + random = getRandom(); + } final byte[] bytes = new byte[length]; - getRandom().nextBytes(bytes); + random.nextBytes(bytes); return bytes; }