java测试示例-生成ULID

news2024/12/24 0:08:22

ULID全称Universally Unique Lexicographically Sortable Identifier,直译就是通用唯一按字典排序的标识符,原始仓库是https://github.com/ulid/javascript,由前端开发者alizain发起,基于JavaScript语言。从项目中的commit历史来看已超5年,得到充分的实践验证。ULID出现的原因是认为主流的UUID方案在许多场景下可能不是最优的,存在问题:

  • UUID不是128 bit随机编码(由128 bit随机数通过编码生成字符串)的最高效实现方式
  • UUID的v1/v2实现在许多环境中是不切实际的,因为这两个版本的的实现需要访问唯一的、稳定的MAC地址
  • UUID的v3/v5实现需要唯一的种子,并且产生随机分布的ID,这可能会导致在许多数据结构中出现碎片
  • UUID的v4除了随机性之外不需要提供其他信息,随机性可能会在许多数据结构中导致碎片

概括一下是:UUID的v1/v2实现依赖唯一稳定MAC地址不现实,v3/v4/v5实现因为随机性产生的ID会"碎片化"。

ULID的特点如下:

  • 设计为128 bit大小,与UUID兼容
  • 每毫秒生成1.21e+24个唯一的ULID(高性能)
  • 按字典顺序(字母顺序)排序
  • 标准编码为26个字符的字符串,而不是像UUID那样需要36个字符
  • 使用Crockford的base32算法来提高效率和可读性(每个字符5 bit)
  • 不区分大小写
  • 没有特殊字符串(URL安全,不需要进行二次URL编码)
  • 可单调排序(正确地检测并处理相同的毫秒,所谓单调性,就是毫秒数相同的情况下,能够确保新的ULID随机部分的在最低有效位上加1位)

ULID规范在ULID/javascript类库中实现

# 格式
 01GKJNSEJZ      A44X2DPXGSK303WP
|----------|    |----------------|
 Timestamp          Randomness
   48bits             80bits

# 所有字符必须使用默认的ASCII字符集
ttttttttttrrrrrrrrrrrrrrrr
# 共占据26个字符
where
# 时间戳占据高(左边)10个(编码后的)字符
t is Timestamp (10 characters)
# 随机数占据低(右边)16个(编码后的)字符
r is Randomness (16 characters)

# 使用Crockford Base32编码算法,排除了I、 L、O、U字母,避免混淆和滥用,字母表
0123456789ABCDEFGHJKMNPQRSTVWXYZ
# 二进制布局的多个部分被编码为16 byte,每个部分都以最高字节优先
0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                      32_bit_uint_time_high                    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     16_bit_uint_time_low      |       16_bit_uint_random      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       32_bit_uint_random                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                       32_bit_uint_random                      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

基于Base32编码能生成的最大合法ULID是7ZZZZZZZZZZZZZZZZZZZZZZZZZ,使用的时间戳为epoch time的281474976710655或者说2 ^ 48 - 1。对于任何大于此值的ULID进行解码或编码的尝试都应被拒绝,以防止溢出错误

参考

ULID规范解读与实现原理

java测试

参考jar库

<dependency>
    <groupId>com.github.f4b6a3</groupId>
    <artifactId>ulid-creator</artifactId>
    <version>5.1.0</version>
</dependency>
<dependency>
    <groupId>de.huxhorn.sulky</groupId>
    <artifactId>de.huxhorn.sulky.ulid</artifactId>
    <version>8.3.0</version>
</dependency>

demo


import java.security.SecureRandom;

/**
 * Time32Main 类说明:
 *
 * @author z.y.l
 * @version v1.0
 * @date 2022/12/5
 */
public class Time32Main {
    public static void main(String[] args) {
        final long time = System.currentTimeMillis();
        //32进制编码
        final char[] alphabet = {
                '0','1','2','3','4','5','6','7','8','9',
                'A','B','C','D','E','F','G','H','J','K',
                'M','N','P','Q','R','S','T','V','W','X',
                'Y','Z',};
        // 时间戳 转 32编码
        t1(time,alphabet);
        t2(time,alphabet);
        //ulId 生成
        toUlIdStr(time,alphabet);
    }
    public static void t1(long time,char[] char32s){
        int count = 10, maskBits = 5, mask = 0x1F, offset = 0 , i1;
        // 31 对应二进制 00011111
        char[] buffer = new char[count];
        long l1;
        System.out.print("位数:" + count);
        System.out.print(",32进制 每位对应 二进制个数:" + maskBits);
        System.out.print(",求 32进制编码 运算数:" + mask + "(二进制" + Integer.toBinaryString(mask) + ")");
        System.out.println(",偏移:" + offset);
        System.out.println("时间戳:" + time + "(二进制" + Long.toBinaryString(time) + ")");
        for(int i = 0; i < count; i++) {
            i1 = (count - i - 1) * maskBits;
            l1 = time >>> ((count - i - 1) * maskBits);
            int index = (int)( l1 & mask);
            buffer[offset+i] = char32s[index];
            System.out.print("时间戳 >>> " + i1 + " = " + l1 + " & " + mask + " = " + index + " ~ " + char32s[index]);
            System.out.print( ",时间戳 >>> " + Integer.toBinaryString(i1) + " = " + Long.toBinaryString(l1) );
            System.out.println( " & 00011111 = " + Integer.toBinaryString(index) );
        }
        System.out.println(time + " = [转32编码] = " + new String(buffer));
    }
    public static void t2(long time,char[] alphabet){
        char[] chars = new char[10];
        genMsd(alphabet,chars,time);
        System.out.println(">>> "+new String(chars));
    }

    private static void genMsd(char[] alphabet,char[] chars,long time){
        // 31 的 二进制 0b11111
        chars[0x00] = alphabet[(int) (time >>> 45 & 0b11111)];
        chars[0x01] = alphabet[(int) (time >>> 40 & 0b11111)];
        chars[0x02] = alphabet[(int) (time >>> 35 & 0b11111)];
        chars[0x03] = alphabet[(int) (time >>> 30 & 0b11111)];
        chars[0x04] = alphabet[(int) (time >>> 25 & 0b11111)];
        chars[0x05] = alphabet[(int) (time >>> 20 & 0b11111)];
        chars[0x06] = alphabet[(int) (time >>> 15 & 0b11111)];
        chars[0x07] = alphabet[(int) (time >>> 10 & 0b11111)];
        chars[0x08] = alphabet[(int) (time >>> 5 & 0b11111)];
        chars[0x09] = alphabet[(int) (time & 0b11111)];
    }
    public static void toUlIdStr(long time,char[] alphabet){
        SecureRandom random = new SecureRandom();
        long msb = (time << 16) | (random.nextLong() & 0xffffL);
        long lsb = random.nextLong();
        System.out.println("ulId = "+toUlIdStr(msb,lsb,alphabet));
    }
    public static String toUlIdStr(long msb,long lsb,char[] alphabet) {
        final char[] chars = new char[26];
        // msb 初始化的是 左移16位 << 16,需要恢复 就要 右移16位
        long time = msb >>> 16;
        long random0 = ((msb & 0xffffL) << 24) | (lsb >>> 40);
        long random1 = (lsb & 0xffffffffffL);
        // 第一段 10位,时间戳 转 32编码
        genMsd(alphabet,chars,time);
        // 第二段 16位,分两次 生成
        genLsd(alphabet,chars,random0,0x0a);
        genLsd(alphabet,chars,random1,0x12);

        return new String(chars);
    }
    private static void genLsd(char[] alphabet,char[] chars,long random,int offset){
        // 31 的 二进制 0b11111
        chars[offset] = alphabet[(int) (random >>> 35 & 0b11111)];
        chars[offset+0x01] = alphabet[(int) (random >>> 30 & 0b11111)];
        chars[offset+0x02] = alphabet[(int) (random >>> 25 & 0b11111)];
        chars[offset+0x03] = alphabet[(int) (random >>> 20 & 0b11111)];
        chars[offset+0x04] = alphabet[(int) (random >>> 15 & 0b11111)];
        chars[offset+0x05] = alphabet[(int) (random >>> 10 & 0b11111)];
        chars[offset+0x06] = alphabet[(int) (random >>> 5 & 0b11111)];
        chars[offset+0x07] = alphabet[(int) (random & 0b11111)];
    }
}

测试

位数:10,32进制 每位对应 二进制个数:5,求 32进制编码 运算数:31(二进制11111),偏移:0
时间戳:1670295370335(二进制11000010011100101010111001011101001011111)
时间戳 >>> 45 = 0 & 31 = 0 ~ 0,时间戳 >>> 101101 = 0 & 00011111 = 0
时间戳 >>> 40 = 1 & 31 = 1 ~ 1,时间戳 >>> 101000 = 1 & 00011111 = 1
时间戳 >>> 35 = 48 & 31 = 16 ~ G,时间戳 >>> 100011 = 110000 & 00011111 = 10000
时间戳 >>> 30 = 1555 & 31 = 19 ~ K,时间戳 >>> 11110 = 11000010011 & 00011111 = 10011
时间戳 >>> 25 = 49778 & 31 = 18 ~ J,时间戳 >>> 11001 = 1100001001110010 & 00011111 = 10010
时间戳 >>> 20 = 1592917 & 31 = 21 ~ N,时间戳 >>> 10100 = 110000100111001010101 & 00011111 = 10101
时间戳 >>> 15 = 50973369 & 31 = 25 ~ S,时间戳 >>> 1111 = 11000010011100101010111001 & 00011111 = 11001
时间戳 >>> 10 = 1631147822 & 31 = 14 ~ E,时间戳 >>> 1010 = 1100001001110010101011100101110 & 00011111 = 1110
时间戳 >>> 5 = 52196730322 & 31 = 18 ~ J,时间戳 >>> 101 = 110000100111001010101110010111010010 & 00011111 = 10010
时间戳 >>> 0 = 1670295370335 & 31 = 31 ~ Z,时间戳 >>> 0 = 11000010011100101010111001011101001011111 & 00011111 = 11111
1670295370335 = [转32编码] = 01GKJNSEJZ
01GKJNSEJZ
01GKJNSEJZA44X2DPXGSK303WP

在这里插入图片描述

库源码

注释做了部分清理

ulid-creator


package de.huxhorn.sulky.ulid;

import java.io.Serializable;
import java.security.SecureRandom;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;

@SuppressWarnings("PMD.ShortClassName")
public class ULID
{
	private static final char[] ENCODING_CHARS = {
			'0','1','2','3','4','5','6','7','8','9',
			'A','B','C','D','E','F','G','H','J','K',
			'M','N','P','Q','R','S','T','V','W','X',
			'Y','Z',
	};

	private static final byte[] DECODING_CHARS = {
			// 0
			-1, -1, -1, -1, -1, -1, -1, -1,
			// 8
			-1, -1, -1, -1, -1, -1, -1, -1,
			// 16
			-1, -1, -1, -1, -1, -1, -1, -1,
			// 24
			-1, -1, -1, -1, -1, -1, -1, -1,
			// 32
			-1, -1, -1, -1, -1, -1, -1, -1,
			// 40
			-1, -1, -1, -1, -1, -1, -1, -1,
			// 48
			0, 1, 2, 3, 4, 5, 6, 7,
			// 56
			8, 9, -1, -1, -1, -1, -1, -1,
			// 64
			-1, 10, 11, 12, 13, 14, 15, 16,
			// 72
			17, 1, 18, 19, 1, 20, 21, 0,
			// 80
			22, 23, 24, 25, 26, -1, 27, 28,
			// 88
			29, 30, 31, -1, -1, -1, -1, -1,
			// 96
			-1, 10, 11, 12, 13, 14, 15, 16,
			// 104
			17, 1, 18, 19, 1, 20, 21, 0,
			// 112
			22, 23, 24, 25, 26, -1, 27, 28,
			// 120
			29, 30, 31,
	};

	private static final int MASK = 0x1F;
	private static final int MASK_BITS = 5;
	private static final long TIMESTAMP_OVERFLOW_MASK = 0xFFFF_0000_0000_0000L;
	private static final long TIMESTAMP_MSB_MASK = 0xFFFF_FFFF_FFFF_0000L;
	private static final long RANDOM_MSB_MASK = 0xFFFFL;

	private final Random random;

	public ULID()
	{
		this(new SecureRandom());
	}

	public ULID(Random random)
	{
		Objects.requireNonNull(random, "random must not be null!");
		this.random = random;
	}

	public void appendULID(StringBuilder stringBuilder)
	{
		Objects.requireNonNull(stringBuilder, "stringBuilder must not be null!");
		internalAppendULID(stringBuilder, System.currentTimeMillis(), random);
	}

	public String nextULID()
	{
		return nextULID(System.currentTimeMillis());
	}

	public String nextULID(long timestamp)
	{
		return internalUIDString(timestamp, random);
	}

	public Value nextValue()
	{
		return nextValue(System.currentTimeMillis());
	}

	public Value nextValue(long timestamp)
	{
		return internalNextValue(timestamp, random);
	}

	public Value nextMonotonicValue(Value previousUlid)
	{
		return nextMonotonicValue(previousUlid, System.currentTimeMillis());
	}

	public Value nextMonotonicValue(Value previousUlid, long timestamp)
	{
		Objects.requireNonNull(previousUlid, "previousUlid must not be null!");
		if(previousUlid.timestamp() == timestamp)
		{
			return previousUlid.increment();
		}
		return nextValue(timestamp);
	}

	public Optional<Value> nextStrictlyMonotonicValue(Value previousUlid)
	{
		return nextStrictlyMonotonicValue(previousUlid, System.currentTimeMillis());
	}

	public Optional<Value> nextStrictlyMonotonicValue(Value previousUlid, long timestamp)
	{
		Value result = nextMonotonicValue(previousUlid, timestamp);
		if(result.compareTo(previousUlid) < 1)
		{
			return Optional.empty();
		}
		return Optional.of(result);
	}

	public static Value parseULID(String ulidString)
	{
		Objects.requireNonNull(ulidString, "ulidString must not be null!");
		if(ulidString.length() != 26)
		{
			throw new IllegalArgumentException("ulidString must be exactly 26 chars long.");
		}

		String timeString = ulidString.substring(0, 10);
		long time = internalParseCrockford(timeString);
		if ((time & TIMESTAMP_OVERFLOW_MASK) != 0)
		{
			throw new IllegalArgumentException("ulidString must not exceed '7ZZZZZZZZZZZZZZZZZZZZZZZZZ'!");
		}
		String part1String = ulidString.substring(10, 18);
		String part2String = ulidString.substring(18);
		long part1 = internalParseCrockford(part1String);
		long part2 = internalParseCrockford(part2String);

		long most = (time << 16) | (part1 >>> 24);
		long least = part2 | (part1 << 40);
		return new Value(most, least);
	}

	public static Value fromBytes(byte[] data)
	{
		Objects.requireNonNull(data, "data must not be null!");
		if(data.length != 16)
		{
			throw new IllegalArgumentException("data must be 16 bytes in length!");
		}
		long mostSignificantBits = 0;
		long leastSignificantBits = 0;
		for (int i=0; i<8; i++)
		{
			mostSignificantBits = (mostSignificantBits << 8) | (data[i] & 0xff);
		}
		for (int i=8; i<16; i++)
		{
			leastSignificantBits = (leastSignificantBits << 8) | (data[i] & 0xff);
		}
		return new Value(mostSignificantBits, leastSignificantBits);
	}

	public static class Value
		implements Comparable<Value>, Serializable
	{
		private static final long serialVersionUID = -3563159514112487717L;

		/*
		 * The most significant 64 bits of this ULID.
		 */
		private final long mostSignificantBits;

		/*
		 * The least significant 64 bits of this ULID.
		 */
		private final long leastSignificantBits;

		public Value(long mostSignificantBits, long leastSignificantBits)
		{
			this.mostSignificantBits = mostSignificantBits;
			this.leastSignificantBits = leastSignificantBits;
		}

		public long getMostSignificantBits() {
			return mostSignificantBits;
		}

		public long getLeastSignificantBits() {
			return leastSignificantBits;
		}


		public long timestamp()
		{
			return mostSignificantBits >>> 16;
		}

		public byte[] toBytes()
		{
			byte[] result=new byte[16];
			for (int i=0; i<8; i++)
			{
				result[i] = (byte)((mostSignificantBits >> ((7-i)*8)) & 0xFF);
			}
			for (int i=8; i<16; i++)
			{
				result[i] = (byte)((leastSignificantBits >> ((15-i)*8)) & 0xFF);
			}

			return result;
		}

		public Value increment()
		{
			long lsb = leastSignificantBits;
			if(lsb != 0xFFFF_FFFF_FFFF_FFFFL)
			{
				return new Value(mostSignificantBits, lsb+1);
			}
			long msb = mostSignificantBits;
			if((msb & RANDOM_MSB_MASK) != RANDOM_MSB_MASK)
			{
				return new Value(msb + 1, 0);
			}
			return new Value(msb & TIMESTAMP_MSB_MASK, 0);
		}

		@Override
		public int hashCode() {
			long hilo = mostSignificantBits ^ leastSignificantBits;
			return ((int)(hilo >> 32)) ^ (int) hilo;
		}

		@Override
		public boolean equals(Object o)
		{
			if (this == o) return true;
			if (o == null || getClass() != o.getClass()) return false;

			Value value = (Value) o;

			return mostSignificantBits == value.mostSignificantBits
					&& leastSignificantBits == value.leastSignificantBits;
		}

		@Override
		public int compareTo(Value val)
		{
			return (this.mostSignificantBits < val.mostSignificantBits ? -1 :
					(this.mostSignificantBits > val.mostSignificantBits ? 1 :
							(this.leastSignificantBits < val.leastSignificantBits ? -1 :
									(this.leastSignificantBits > val.leastSignificantBits ? 1 :
											0))));
		}

		@Override
		public String toString()
		{
			char[] buffer = new char[26];

			internalWriteCrockford(buffer, timestamp(), 10, 0);
			long value = ((mostSignificantBits & 0xFFFFL) << 24);
			long interim = (leastSignificantBits >>> 40);
			value = value | interim;
			internalWriteCrockford(buffer, value, 8, 10);
			internalWriteCrockford(buffer, leastSignificantBits, 8, 18);

			return new String(buffer);
		}
	}

	/*
	 * http://crockford.com/wrmg/base32.html
	 */
	static void internalAppendCrockford(StringBuilder builder, long value, int count)
	{
		for(int i = count-1; i >= 0; i--)
		{
			int index = (int)((value >>> (i * MASK_BITS)) & MASK);
			builder.append(ENCODING_CHARS[index]);
		}
	}

	static long internalParseCrockford(String input)
	{
		Objects.requireNonNull(input, "input must not be null!");
		int length = input.length();
		if(length > 12)
		{
			throw new IllegalArgumentException("input length must not exceed 12 but was "+length+"!");
		}

		long result = 0;
		for(int i=0;i<length;i++)
		{
			char current = input.charAt(i);
			byte value = -1;
			if(current < DECODING_CHARS.length)
			{
				value = DECODING_CHARS[current];
			}
			if(value < 0)
			{
				throw new IllegalArgumentException("Illegal character '"+current+"'!");
			}
			result |= ((long)value) << ((length - 1 - i)*MASK_BITS);
		}
		return result;
	}

	/*
	 * http://crockford.com/wrmg/base32.html
	 */
	static void internalWriteCrockford(char[] buffer, long value, int count, int offset)
	{
		for(int i = 0; i < count; i++)
		{
			int index = (int)((value >>> ((count - i - 1) * MASK_BITS)) & MASK);
			buffer[offset+i] = ENCODING_CHARS[index];
		}
	}

	static String internalUIDString(long timestamp, Random random)
	{
		checkTimestamp(timestamp);

		char[] buffer = new char[26];

		internalWriteCrockford(buffer, timestamp, 10, 0);
		internalWriteCrockford(buffer, random.nextLong(), 8, 10);
		internalWriteCrockford(buffer, random.nextLong(), 8, 18);

		return new String(buffer);
	}

	static void internalAppendULID(StringBuilder builder, long timestamp, Random random)
	{
		checkTimestamp(timestamp);

		internalAppendCrockford(builder, timestamp, 10);
		internalAppendCrockford(builder, random.nextLong(), 8);
		internalAppendCrockford(builder, random.nextLong(), 8);
	}

	static Value internalNextValue(long timestamp, Random random)
	{
		checkTimestamp(timestamp);
		long mostSignificantBits = random.nextLong();
		long leastSignificantBits = random.nextLong();
		mostSignificantBits &= 0xFFFF;
		mostSignificantBits |= (timestamp << 16);
		return new Value(mostSignificantBits, leastSignificantBits);
	}

	private static void checkTimestamp(long timestamp)
	{
		if((timestamp & TIMESTAMP_OVERFLOW_MASK) != 0)
		{
			throw new IllegalArgumentException("ULID does not support timestamps after +10889-08-02T05:31:50.655Z!");
		}
	}
}

de.huxhorn.sulky.ulid

package com.github.f4b6a3.ulid;

public final class UlidCreator {
	private UlidCreator() {
	}
	public static Ulid getUlid() {
		return UlidFactoryHolder.INSTANCE.create();
	}
	public static Ulid getUlid(final long time) {
		return UlidFactoryHolder.INSTANCE.create(time);
	}
	public static Ulid getMonotonicUlid() {
		return MonotonicFactoryHolder.INSTANCE.create();
	}
	public static Ulid getMonotonicUlid(final long time) {
		return MonotonicFactoryHolder.INSTANCE.create(time);
	}
	private static class UlidFactoryHolder {
		static final UlidFactory INSTANCE = UlidFactory.newInstance();
	}
	private static class MonotonicFactoryHolder {
		static final UlidFactory INSTANCE = UlidFactory.newMonotonicInstance();
	}
}
package com.github.f4b6a3.ulid;

import java.security.SecureRandom;
import java.time.Clock;
import java.util.Random;
import java.util.function.IntFunction;
import java.util.function.LongFunction;
import java.util.function.LongSupplier;

public final class UlidFactory {
	private final Clock clock;
	private final LongFunction<Ulid> ulidFunction;

	public UlidFactory() {
		this(new UlidFunction(IRandom.newInstance()));
	}
	private UlidFactory(LongFunction<Ulid> ulidFunction) {
		this(ulidFunction, null);
	}
	private UlidFactory(LongFunction<Ulid> ulidFunction, Clock clock) {
		this.ulidFunction = ulidFunction;
		this.clock = clock != null ? clock : Clock.systemUTC();
	}
	public static UlidFactory newInstance() {
		return new UlidFactory(new UlidFunction(IRandom.newInstance()));
	}
	public static UlidFactory newInstance(Random random) {
		return new UlidFactory(new UlidFunction(IRandom.newInstance(random)));
	}
	public static UlidFactory newInstance(LongSupplier randomFunction) {
		return new UlidFactory(new UlidFunction(IRandom.newInstance(randomFunction)));
	}
	public static UlidFactory newInstance(IntFunction<byte[]> randomFunction) {
		return new UlidFactory(new UlidFunction(IRandom.newInstance(randomFunction)));
	}
	public static UlidFactory newMonotonicInstance() {
		return new UlidFactory(new MonotonicFunction(IRandom.newInstance()));
	}
	public static UlidFactory newMonotonicInstance(Random random) {
		return new UlidFactory(new MonotonicFunction(IRandom.newInstance(random)));
	}
	public static UlidFactory newMonotonicInstance(LongSupplier randomFunction) {
		return new UlidFactory(new MonotonicFunction(IRandom.newInstance(randomFunction)));
	}
	public static UlidFactory newMonotonicInstance(IntFunction<byte[]> randomFunction) {
		return new UlidFactory(new MonotonicFunction(IRandom.newInstance(randomFunction)));
	}
	static UlidFactory newMonotonicInstance(LongSupplier randomFunction, Clock clock) {
		return new UlidFactory(new MonotonicFunction(IRandom.newInstance(randomFunction)), clock);
	}
	static UlidFactory newMonotonicInstance(IntFunction<byte[]> randomFunction, Clock clock) {
		return new UlidFactory(new MonotonicFunction(IRandom.newInstance(randomFunction)), clock);
	}
	public synchronized Ulid create() {
		return this.ulidFunction.apply(clock.millis());
	}
	public synchronized Ulid create(final long time) {
		return this.ulidFunction.apply(time);
	}

	static final class UlidFunction implements LongFunction<Ulid> {
		private final IRandom random;
		public UlidFunction(IRandom random) {
			this.random = random;
		}
		@Override
		public Ulid apply(final long time) {
			if (this.random instanceof ByteRandom) {
				return new Ulid(time, this.random.nextBytes(Ulid.RANDOM_BYTES));
			} else {
				final long msb = (time << 16) | (this.random.nextLong() & 0xffffL);
				final long lsb = this.random.nextLong();
				return new Ulid(msb, lsb);
			}
		}
	}

	static final class MonotonicFunction implements LongFunction<Ulid> {
		private Ulid lastUlid;
		private final IRandom random;
		protected static final int CLOCK_DRIFT_TOLERANCE = 10_000;
		public MonotonicFunction(IRandom random) {
			this.random = random;
			this.lastUlid = new Ulid(0L, this.random.nextBytes(Ulid.RANDOM_BYTES));
		}
		@Override
		public synchronized Ulid apply(final long time) {
			final long lastTime = lastUlid.getTime();
			if ((time > lastTime - CLOCK_DRIFT_TOLERANCE) && (time <= lastTime)) {
				this.lastUlid = this.lastUlid.increment();
			} else {
				if (this.random instanceof ByteRandom) {
					this.lastUlid = new Ulid(time, this.random.nextBytes(Ulid.RANDOM_BYTES));
				} else {
					final long msb = (time << 16) | (this.random.nextLong() & 0xffffL);
					final long lsb = this.random.nextLong();
					this.lastUlid = new Ulid(msb, lsb);
				}
			}
			return new Ulid(this.lastUlid);
		}
	}

	static interface IRandom {
		public long nextLong();
		public byte[] nextBytes(int length);
		static IRandom newInstance() {
			return new ByteRandom();
		}
		static IRandom newInstance(Random random) {
			if (random == null) {
				return new ByteRandom();
			} else {
				if (random instanceof SecureRandom) {
					return new ByteRandom(random);
				} else {
					return new LongRandom(random);
				}
			}
		}
		static IRandom newInstance(LongSupplier randomFunction) {
			return new LongRandom(randomFunction);
		}
		static IRandom newInstance(IntFunction<byte[]> randomFunction) {
			return new ByteRandom(randomFunction);
		}
	}

	static class LongRandom implements IRandom {
		private final LongSupplier randomFunction;
		public LongRandom() {
			this(newRandomFunction(null));
		}
		public LongRandom(Random random) {
			this(newRandomFunction(random));
		}
		public LongRandom(LongSupplier randomFunction) {
			this.randomFunction = randomFunction != null ? randomFunction : newRandomFunction(null);
		}
		@Override
		public long nextLong() {
			return randomFunction.getAsLong();
		}
		@Override
		public byte[] nextBytes(int length) {
			int shift = 0;
			long random = 0;
			final byte[] bytes = new byte[length];
			for (int i = 0; i < length; i++) {
				if (shift < Byte.SIZE) {
					shift = Long.SIZE;
					random = randomFunction.getAsLong();
				}
				shift -= Byte.SIZE; // 56, 48, 40...
				bytes[i] = (byte) (random >>> shift);
			}
			return bytes;
		}
		static LongSupplier newRandomFunction(Random random) {
			final Random entropy = random != null ? random : new SecureRandom();
			return entropy::nextLong;
		}
	}
	static class ByteRandom implements IRandom {

		private final IntFunction<byte[]> randomFunction;
		public ByteRandom() {
			this(newRandomFunction(null));
		}
		public ByteRandom(Random random) {
			this(newRandomFunction(random));
		}
		public ByteRandom(IntFunction<byte[]> randomFunction) {
			this.randomFunction = randomFunction != null ? randomFunction : newRandomFunction(null);
		}
		@Override
		public long nextLong() {
			long number = 0;
			byte[] bytes = this.randomFunction.apply(Long.BYTES);
			for (int i = 0; i < Long.BYTES; i++) {
				number = (number << 8) | (bytes[i] & 0xff);
			}
			return number;
		}
		@Override
		public byte[] nextBytes(int length) {
			return this.randomFunction.apply(length);
		}
		static IntFunction<byte[]> newRandomFunction(Random random) {
			final Random entropy = random != null ? random : new SecureRandom();
			return (final int length) -> {
				final byte[] bytes = new byte[length];
				entropy.nextBytes(bytes);
				return bytes;
			};
		}
	}
}
package com.github.f4b6a3.ulid;

import java.io.Serializable;
import java.time.Instant;
import java.util.SplittableRandom;
import java.util.UUID;

public final class Ulid implements Serializable, Comparable<Ulid> {

    private static final long serialVersionUID = 2625269413446854731L;

    private final long msb; // most significant bits
    private final long lsb; // least significant bits

    public static final int ULID_CHARS = 26;
    public static final int TIME_CHARS = 10;
    public static final int RANDOM_CHARS = 16;
    public static final int ULID_BYTES = 16;
    public static final int TIME_BYTES = 6;
    public static final int RANDOM_BYTES = 10;

    private static final char[] ALPHABET_UPPERCASE = //
            { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
                    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', //
                    'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z' };

    private static final char[] ALPHABET_LOWERCASE = //
            { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
                    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', //
                    'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z' };

    private static final long[] ALPHABET_VALUES = new long[128];
    static {
        for (int i = 0; i < ALPHABET_VALUES.length; i++) {
            ALPHABET_VALUES[i] = -1;
        }
        // Numbers
        ALPHABET_VALUES['0'] = 0x00;
        ALPHABET_VALUES['1'] = 0x01;
        ALPHABET_VALUES['2'] = 0x02;
        ALPHABET_VALUES['3'] = 0x03;
        ALPHABET_VALUES['4'] = 0x04;
        ALPHABET_VALUES['5'] = 0x05;
        ALPHABET_VALUES['6'] = 0x06;
        ALPHABET_VALUES['7'] = 0x07;
        ALPHABET_VALUES['8'] = 0x08;
        ALPHABET_VALUES['9'] = 0x09;
        // Lower case
        ALPHABET_VALUES['a'] = 0x0a;
        ALPHABET_VALUES['b'] = 0x0b;
        ALPHABET_VALUES['c'] = 0x0c;
        ALPHABET_VALUES['d'] = 0x0d;
        ALPHABET_VALUES['e'] = 0x0e;
        ALPHABET_VALUES['f'] = 0x0f;
        ALPHABET_VALUES['g'] = 0x10;
        ALPHABET_VALUES['h'] = 0x11;
        ALPHABET_VALUES['j'] = 0x12;
        ALPHABET_VALUES['k'] = 0x13;
        ALPHABET_VALUES['m'] = 0x14;
        ALPHABET_VALUES['n'] = 0x15;
        ALPHABET_VALUES['p'] = 0x16;
        ALPHABET_VALUES['q'] = 0x17;
        ALPHABET_VALUES['r'] = 0x18;
        ALPHABET_VALUES['s'] = 0x19;
        ALPHABET_VALUES['t'] = 0x1a;
        ALPHABET_VALUES['v'] = 0x1b;
        ALPHABET_VALUES['w'] = 0x1c;
        ALPHABET_VALUES['x'] = 0x1d;
        ALPHABET_VALUES['y'] = 0x1e;
        ALPHABET_VALUES['z'] = 0x1f;
        // Lower case OIL
        ALPHABET_VALUES['o'] = 0x00;
        ALPHABET_VALUES['i'] = 0x01;
        ALPHABET_VALUES['l'] = 0x01;
        // Upper case
        ALPHABET_VALUES['A'] = 0x0a;
        ALPHABET_VALUES['B'] = 0x0b;
        ALPHABET_VALUES['C'] = 0x0c;
        ALPHABET_VALUES['D'] = 0x0d;
        ALPHABET_VALUES['E'] = 0x0e;
        ALPHABET_VALUES['F'] = 0x0f;
        ALPHABET_VALUES['G'] = 0x10;
        ALPHABET_VALUES['H'] = 0x11;
        ALPHABET_VALUES['J'] = 0x12;
        ALPHABET_VALUES['K'] = 0x13;
        ALPHABET_VALUES['M'] = 0x14;
        ALPHABET_VALUES['N'] = 0x15;
        ALPHABET_VALUES['P'] = 0x16;
        ALPHABET_VALUES['Q'] = 0x17;
        ALPHABET_VALUES['R'] = 0x18;
        ALPHABET_VALUES['S'] = 0x19;
        ALPHABET_VALUES['T'] = 0x1a;
        ALPHABET_VALUES['V'] = 0x1b;
        ALPHABET_VALUES['W'] = 0x1c;
        ALPHABET_VALUES['X'] = 0x1d;
        ALPHABET_VALUES['Y'] = 0x1e;
        ALPHABET_VALUES['Z'] = 0x1f;
        // Upper case OIL
        ALPHABET_VALUES['O'] = 0x00;
        ALPHABET_VALUES['I'] = 0x01;
        ALPHABET_VALUES['L'] = 0x01;
    }

    private static final long INCREMENT_OVERFLOW = 0x0000000000000000L;

    public Ulid(Ulid ulid) {
        this.msb = ulid.msb;
        this.lsb = ulid.lsb;
    }


    public Ulid(long mostSignificantBits, long leastSignificantBits) {
        this.msb = mostSignificantBits;
        this.lsb = leastSignificantBits;
    }

    public Ulid(long time, byte[] random) {

        // The time component has 48 bits.
        if ((time & 0xffff000000000000L) != 0) {
            // ULID specification:
            // "Any attempt to decode or encode a ULID larger than this (time > 2^48-1)
            // should be rejected by all implementations, to prevent overflow bugs."
            throw new IllegalArgumentException("Invalid time value"); // overflow or negative time!
        }
        // The random component has 80 bits (10 bytes).
        if (random == null || random.length != RANDOM_BYTES) {
            throw new IllegalArgumentException("Invalid random bytes"); // null or wrong length!
        }

        long long0 = 0;
        long long1 = 0;

        long0 |= time << 16;
        long0 |= (long) (random[0x0] & 0xff) << 8;
        long0 |= (long) (random[0x1] & 0xff);

        long1 |= (long) (random[0x2] & 0xff) << 56;
        long1 |= (long) (random[0x3] & 0xff) << 48;
        long1 |= (long) (random[0x4] & 0xff) << 40;
        long1 |= (long) (random[0x5] & 0xff) << 32;
        long1 |= (long) (random[0x6] & 0xff) << 24;
        long1 |= (long) (random[0x7] & 0xff) << 16;
        long1 |= (long) (random[0x8] & 0xff) << 8;
        long1 |= (long) (random[0x9] & 0xff);

        this.msb = long0;
        this.lsb = long1;
    }

    public static Ulid fast() {
        final long time = System.currentTimeMillis();
        final SplittableRandom random = new SplittableRandom();
        return new Ulid((time << 16) | (random.nextLong() & 0xffffL), random.nextLong());
    }

    public static Ulid from(UUID uuid) {
        return new Ulid(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits());
    }

    public static Ulid from(byte[] bytes) {

        if (bytes == null || bytes.length != ULID_BYTES) {
            throw new IllegalArgumentException("Invalid ULID bytes"); // null or wrong length!
        }

        long msb = 0;
        long lsb = 0;

        msb |= (bytes[0x0] & 0xffL) << 56;
        msb |= (bytes[0x1] & 0xffL) << 48;
        msb |= (bytes[0x2] & 0xffL) << 40;
        msb |= (bytes[0x3] & 0xffL) << 32;
        msb |= (bytes[0x4] & 0xffL) << 24;
        msb |= (bytes[0x5] & 0xffL) << 16;
        msb |= (bytes[0x6] & 0xffL) << 8;
        msb |= (bytes[0x7] & 0xffL);

        lsb |= (bytes[0x8] & 0xffL) << 56;
        lsb |= (bytes[0x9] & 0xffL) << 48;
        lsb |= (bytes[0xa] & 0xffL) << 40;
        lsb |= (bytes[0xb] & 0xffL) << 32;
        lsb |= (bytes[0xc] & 0xffL) << 24;
        lsb |= (bytes[0xd] & 0xffL) << 16;
        lsb |= (bytes[0xe] & 0xffL) << 8;
        lsb |= (bytes[0xf] & 0xffL);

        return new Ulid(msb, lsb);
    }

    public static Ulid from(String string) {

        final char[] chars = toCharArray(string);

        long time = 0;
        long random0 = 0;
        long random1 = 0;

        time |= ALPHABET_VALUES[chars[0x00]] << 45;
        time |= ALPHABET_VALUES[chars[0x01]] << 40;
        time |= ALPHABET_VALUES[chars[0x02]] << 35;
        time |= ALPHABET_VALUES[chars[0x03]] << 30;
        time |= ALPHABET_VALUES[chars[0x04]] << 25;
        time |= ALPHABET_VALUES[chars[0x05]] << 20;
        time |= ALPHABET_VALUES[chars[0x06]] << 15;
        time |= ALPHABET_VALUES[chars[0x07]] << 10;
        time |= ALPHABET_VALUES[chars[0x08]] << 5;
        time |= ALPHABET_VALUES[chars[0x09]];

        random0 |= ALPHABET_VALUES[chars[0x0a]] << 35;
        random0 |= ALPHABET_VALUES[chars[0x0b]] << 30;
        random0 |= ALPHABET_VALUES[chars[0x0c]] << 25;
        random0 |= ALPHABET_VALUES[chars[0x0d]] << 20;
        random0 |= ALPHABET_VALUES[chars[0x0e]] << 15;
        random0 |= ALPHABET_VALUES[chars[0x0f]] << 10;
        random0 |= ALPHABET_VALUES[chars[0x10]] << 5;
        random0 |= ALPHABET_VALUES[chars[0x11]];

        random1 |= ALPHABET_VALUES[chars[0x12]] << 35;
        random1 |= ALPHABET_VALUES[chars[0x13]] << 30;
        random1 |= ALPHABET_VALUES[chars[0x14]] << 25;
        random1 |= ALPHABET_VALUES[chars[0x15]] << 20;
        random1 |= ALPHABET_VALUES[chars[0x16]] << 15;
        random1 |= ALPHABET_VALUES[chars[0x17]] << 10;
        random1 |= ALPHABET_VALUES[chars[0x18]] << 5;
        random1 |= ALPHABET_VALUES[chars[0x19]];

        final long msb = (time << 16) | (random0 >>> 24);
        final long lsb = (random0 << 40) | (random1 & 0xffffffffffL);

        return new Ulid(msb, lsb);
    }

    public UUID toUuid() {
        return new UUID(this.msb, this.lsb);
    }

    public byte[] toBytes() {

        final byte[] bytes = new byte[ULID_BYTES];

        bytes[0x0] = (byte) (msb >>> 56);
        bytes[0x1] = (byte) (msb >>> 48);
        bytes[0x2] = (byte) (msb >>> 40);
        bytes[0x3] = (byte) (msb >>> 32);
        bytes[0x4] = (byte) (msb >>> 24);
        bytes[0x5] = (byte) (msb >>> 16);
        bytes[0x6] = (byte) (msb >>> 8);
        bytes[0x7] = (byte) (msb);

        bytes[0x8] = (byte) (lsb >>> 56);
        bytes[0x9] = (byte) (lsb >>> 48);
        bytes[0xa] = (byte) (lsb >>> 40);
        bytes[0xb] = (byte) (lsb >>> 32);
        bytes[0xc] = (byte) (lsb >>> 24);
        bytes[0xd] = (byte) (lsb >>> 16);
        bytes[0xe] = (byte) (lsb >>> 8);
        bytes[0xf] = (byte) (lsb);

        return bytes;
    }

    @Override
    public String toString() {
        return toString(ALPHABET_UPPERCASE);
    }

    public String toLowerCase() {
        return toString(ALPHABET_LOWERCASE);
    }

    public Ulid toRfc4122() {

        // set the 4 most significant bits of the 7th byte to 0, 1, 0 and 0
        final long msb4 = (this.msb & 0xffffffffffff0fffL) | 0x0000000000004000L; // RFC-4122 version 4
        // set the 2 most significant bits of the 9th byte to 1 and 0
        final long lsb4 = (this.lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // RFC-4122 variant 2

        return new Ulid(msb4, lsb4);
    }

    public Instant getInstant() {
        return Instant.ofEpochMilli(this.getTime());
    }

    public static Instant getInstant(String string) {
        return Instant.ofEpochMilli(getTime(string));
    }

    public long getTime() {
        return this.msb >>> 16;
    }

    public static long getTime(String string) {

        final char[] chars = toCharArray(string);

        long time = 0;

        time |= ALPHABET_VALUES[chars[0x00]] << 45;
        time |= ALPHABET_VALUES[chars[0x01]] << 40;
        time |= ALPHABET_VALUES[chars[0x02]] << 35;
        time |= ALPHABET_VALUES[chars[0x03]] << 30;
        time |= ALPHABET_VALUES[chars[0x04]] << 25;
        time |= ALPHABET_VALUES[chars[0x05]] << 20;
        time |= ALPHABET_VALUES[chars[0x06]] << 15;
        time |= ALPHABET_VALUES[chars[0x07]] << 10;
        time |= ALPHABET_VALUES[chars[0x08]] << 5;
        time |= ALPHABET_VALUES[chars[0x09]];

        return time;
    }

    public byte[] getRandom() {

        final byte[] bytes = new byte[RANDOM_BYTES];

        bytes[0x0] = (byte) (msb >>> 8);
        bytes[0x1] = (byte) (msb);

        bytes[0x2] = (byte) (lsb >>> 56);
        bytes[0x3] = (byte) (lsb >>> 48);
        bytes[0x4] = (byte) (lsb >>> 40);
        bytes[0x5] = (byte) (lsb >>> 32);
        bytes[0x6] = (byte) (lsb >>> 24);
        bytes[0x7] = (byte) (lsb >>> 16);
        bytes[0x8] = (byte) (lsb >>> 8);
        bytes[0x9] = (byte) (lsb);

        return bytes;
    }

    public static byte[] getRandom(String string) {

        final char[] chars = toCharArray(string);

        long random0 = 0;
        long random1 = 0;

        random0 |= ALPHABET_VALUES[chars[0x0a]] << 35;
        random0 |= ALPHABET_VALUES[chars[0x0b]] << 30;
        random0 |= ALPHABET_VALUES[chars[0x0c]] << 25;
        random0 |= ALPHABET_VALUES[chars[0x0d]] << 20;
        random0 |= ALPHABET_VALUES[chars[0x0e]] << 15;
        random0 |= ALPHABET_VALUES[chars[0x0f]] << 10;
        random0 |= ALPHABET_VALUES[chars[0x10]] << 5;
        random0 |= ALPHABET_VALUES[chars[0x11]];

        random1 |= ALPHABET_VALUES[chars[0x12]] << 35;
        random1 |= ALPHABET_VALUES[chars[0x13]] << 30;
        random1 |= ALPHABET_VALUES[chars[0x14]] << 25;
        random1 |= ALPHABET_VALUES[chars[0x15]] << 20;
        random1 |= ALPHABET_VALUES[chars[0x16]] << 15;
        random1 |= ALPHABET_VALUES[chars[0x17]] << 10;
        random1 |= ALPHABET_VALUES[chars[0x18]] << 5;
        random1 |= ALPHABET_VALUES[chars[0x19]];

        final byte[] bytes = new byte[RANDOM_BYTES];

        bytes[0x0] = (byte) (random0 >>> 32);
        bytes[0x1] = (byte) (random0 >>> 24);
        bytes[0x2] = (byte) (random0 >>> 16);
        bytes[0x3] = (byte) (random0 >>> 8);
        bytes[0x4] = (byte) (random0);

        bytes[0x5] = (byte) (random1 >>> 32);
        bytes[0x6] = (byte) (random1 >>> 24);
        bytes[0x7] = (byte) (random1 >>> 16);
        bytes[0x8] = (byte) (random1 >>> 8);
        bytes[0x9] = (byte) (random1);

        return bytes;
    }

    public long getMostSignificantBits() {
        return this.msb;
    }

    public long getLeastSignificantBits() {
        return this.lsb;
    }

    public Ulid increment() {

        long newMsb = this.msb;
        long newLsb = this.lsb + 1; // increment the LEAST significant bits

        if (newLsb == INCREMENT_OVERFLOW) {
            newMsb += 1; // increment the MOST significant bits
        }

        return new Ulid(newMsb, newLsb);
    }

    public static boolean isValid(String string) {
        return string != null && isValidCharArray(string.toCharArray());
    }

    @Override
    public int hashCode() {
        final long bits = msb ^ lsb;
        return (int) (bits ^ (bits >>> 32));
    }

    @Override
    public boolean equals(Object other) {
        if (other == null)
            return false;
        if (other.getClass() != Ulid.class)
            return false;
        Ulid that = (Ulid) other;
        if (lsb != that.lsb)
            return false;
        else if (msb != that.msb)
            return false;
        return true;
    }

    @Override
    public int compareTo(Ulid that) {

        final long min = 0x8000000000000000L;

        final long a = this.msb + min;
        final long b = that.msb + min;

        if (a > b)
            return 1;
        else if (a < b)
            return -1;

        final long c = this.lsb + min;
        final long d = that.lsb + min;

        if (c > d)
            return 1;
        else if (c < d)
            return -1;

        return 0;
    }

    String toString(char[] alphabet) {

        final char[] chars = new char[ULID_CHARS];

        long time = this.msb >>> 16;
        long random0 = ((this.msb & 0xffffL) << 24) | (this.lsb >>> 40);
        long random1 = (this.lsb & 0xffffffffffL);

        chars[0x00] = alphabet[(int) (time >>> 45 & 0b11111)];
        chars[0x01] = alphabet[(int) (time >>> 40 & 0b11111)];
        chars[0x02] = alphabet[(int) (time >>> 35 & 0b11111)];
        chars[0x03] = alphabet[(int) (time >>> 30 & 0b11111)];
        chars[0x04] = alphabet[(int) (time >>> 25 & 0b11111)];
        chars[0x05] = alphabet[(int) (time >>> 20 & 0b11111)];
        chars[0x06] = alphabet[(int) (time >>> 15 & 0b11111)];
        chars[0x07] = alphabet[(int) (time >>> 10 & 0b11111)];
        chars[0x08] = alphabet[(int) (time >>> 5 & 0b11111)];
        chars[0x09] = alphabet[(int) (time & 0b11111)];

        chars[0x0a] = alphabet[(int) (random0 >>> 35 & 0b11111)];
        chars[0x0b] = alphabet[(int) (random0 >>> 30 & 0b11111)];
        chars[0x0c] = alphabet[(int) (random0 >>> 25 & 0b11111)];
        chars[0x0d] = alphabet[(int) (random0 >>> 20 & 0b11111)];
        chars[0x0e] = alphabet[(int) (random0 >>> 15 & 0b11111)];
        chars[0x0f] = alphabet[(int) (random0 >>> 10 & 0b11111)];
        chars[0x10] = alphabet[(int) (random0 >>> 5 & 0b11111)];
        chars[0x11] = alphabet[(int) (random0 & 0b11111)];

        chars[0x12] = alphabet[(int) (random1 >>> 35 & 0b11111)];
        chars[0x13] = alphabet[(int) (random1 >>> 30 & 0b11111)];
        chars[0x14] = alphabet[(int) (random1 >>> 25 & 0b11111)];
        chars[0x15] = alphabet[(int) (random1 >>> 20 & 0b11111)];
        chars[0x16] = alphabet[(int) (random1 >>> 15 & 0b11111)];
        chars[0x17] = alphabet[(int) (random1 >>> 10 & 0b11111)];
        chars[0x18] = alphabet[(int) (random1 >>> 5 & 0b11111)];
        chars[0x19] = alphabet[(int) (random1 & 0b11111)];

        return new String(chars);
    }

    static char[] toCharArray(String string) {
        char[] chars = string == null ? null : string.toCharArray();
        if (!isValidCharArray(chars)) {
            throw new IllegalArgumentException(String.format("Invalid ULID: \"%s\"", string));
        }
        return chars;
    }

    static boolean isValidCharArray(final char[] chars) {
        if (chars == null || chars.length != ULID_CHARS) {
            return false;
        }
        if ((ALPHABET_VALUES[chars[0]] & 0b11000) != 0) {
            return false;
        }
        for (int i = 0; i < chars.length; i++) {
            if (ALPHABET_VALUES[chars[i]] == -1) {
                return false;
            }
        }
        return true;
    }
}

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/67061.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于java(ssm)留学生交流互动论坛系统源码(java毕业设计)

基于java&#xff08;ssm&#xff09;留学生交流互动论坛系统 留学生交流互动论坛系统&#xff0c;是基于java编程语言&#xff0c;mysql数据库&#xff0c;ssm框架和idea工具开发&#xff0c;本系统主要分为留学生&#xff0c;管理员两个角色&#xff0c;其中留学生可以注册登…

Vue中的过滤器(管道)

过滤器&#xff1a;将指定的数据&#xff0c;按照一套流程过滤加工&#xff0c;最后返回一个过滤之后的值 注册局部过滤器 将过滤器写在filters配置项中的是局部过滤器&#xff0c;只供该vue匹配的容器使用 new Vue({el: #root,data: function(){return {time: 1670297916166}}…

JVM之内存区域划分、类加载和垃圾回收

文章目录前言一、JVM内存区域划分二、类加载1.类加载是什么&#xff1f;2.类加载的过程3.何时触发类加载&#xff1f;4.双亲委派模型三、垃圾回收&#xff08;GC&#xff09;1.GC是什么&#xff1f;2.GC回收哪部分内容&#xff1f;3.怎么回收&#xff1f;&#xff08;1&#xf…

Rust 跑简单的例子

Rust 一门赋予每个人构建可靠且高效软件能力的语言 安装 curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh 提示失败 curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh info: downloading installer curl: (60) SSL certificate problem: certifi…

FastDFS搭建及整合Nginx实现文件上传

一、准备环境 FastDFS需要两个服务&#xff0c;一个tracker跟踪器&#xff0c;一个storage存储节点&#xff0c;tracker做调度配置&#xff0c;storage完成文件存储上传等功能。 这里我们使用两台虚拟机服务器&#xff08;centos 7)来部署&#xff0c;有条件的同学建议直接上云…

Vue中多条件图片路径通过Map存储获取避免嵌套if-else

场景 若依前后端分离版手把手教你本地搭建环境并运行项目&#xff1a; 若依前后端分离版手把手教你本地搭建环境并运行项目_霸道流氓气质的博客-CSDN博客_前后端分离项目本地运行 前端接收到后台数据之后需进行多个条件判断进而显示对应的图片路径。 比如先判断车辆的类型、…

第十三章:AQS

AQS 基础概念为什么 AQS 是 JUC 最重要的基石&#xff1f;AQS 能干什么AQS内部结构AQS内部类NodeAQS 源码分析以 lock方法为入口讲解nonfairTryAcquire 方法addWaiter方法线程B线程CacquireQueued 方法B节点C节点unlockcancelAcquire 方法总结AQS 基础概念 AQS 全称&#xff1…

【树莓派】了解wiringPi库、控制继电器

目录一、wiringPi库二、继电器1、继电器介绍及接线说明2、树莓派控制继电器一、wiringPi库 wiringPi是一个很棒的树莓派IO控制库&#xff0c;使用C语言开发&#xff0c;提供了丰富的接口&#xff1a;GPIO控制&#xff0c;中断&#xff0c;多线程等。 在树莓派命令行输入gpio -…

供应商管理软件有哪些特点和优势?

在这个快节奏的商业环境中&#xff0c;企业常常需要同时处理多个供应商。手动处理所有这些流程会有不少困难&#xff0c;为了克服这个问题&#xff0c;供应商管理软件是市场上可用的最佳解决方案。 好用的供应商管理软件&#xff0c;比如广受客户好评的8Manage SRM&#xff0c…

Spring 长事务导致connection closed,又熬了一个大夜!

大家好&#xff0c;我是不才陈某~ 是的&#xff0c;今早一到公司就收到了机器人的告警&#xff0c;从异常日志来看是数据库连接已关闭&#xff0c;然后我在解决这个问题的过程中发现了几个问题&#xff0c;不急&#xff0c;听我一一道来 异常被try后没有继续抛出&#xff0c;导…

CN_广域网WAN@PPP协议

文章目录WAN和LANPPP协议PPP协议有三个组成部分&#xff1a;LCPNCP成帧方法PPP帧的格式信息部分范围工作过程PPP协议特点透明传输WAN&InternetWAN和LAN WAN:广域网&#xff08;全写为 wide area network) 广 域 网局 域 网覆盖范围很广,通常跨区域较小,通常在一个区域内连…

Ubuntu内核OverlayFS权限逃逸漏洞(CVE-2021-3493)

文章目录前言关于linux kernel一、漏洞介绍二、漏洞原理三、漏洞影响版本四、漏洞复现五、修复方法前言 关于linux kernel Linux Kernel 一般指Linux内核。Linux是一种开源电脑操作系统内核。它是一个用C语言写成&#xff0c;符合POSIX标准的类Unix操作系统。 一、漏洞介绍 …

如何掌握HEC-RAS建模方法与涉河建设项目防洪评价报告编制

随着社会经济的快速发展&#xff0c;我国河道周边土地开发利用率不断增大&#xff0c;临河建筑物与日俱增&#xff0c;部分河道侵占严重&#xff0c;导致防洪压力增大。迫切需要对全国从事防洪评价咨询类的技术人员开展防洪评价技术方面的学习&#xff0c;为了让相关工程技术人…

深度学习-支持向量机(SVM)

1. 简介 在机器学习领域&#xff0c;支持向量机SVM(Support Vector Machine)是一个有监督的学习模型&#xff0c;通常用来进行模式识别、分类(异常值检测)以及回归分析。SVM算法中&#xff0c;我们将数据绘制在n维空间中&#xff08;n代表数据的特征数&#xff09;&#xff0c;…

C++ 函数指针探幽

首先看下面两个声明代表什么意思&#xff1f; double* (*(*pf)[2])(double*,int); double* (*pa[2])(double*,int);要搞清楚这两个式子&#xff0c;则先要清楚 指向指针的指针指针数组与指向数组的指针函数指针 指向指针的指针 指针的指针特殊点在于指向的是一个指针而已&am…

栈与队列2:用队列实现栈

主要是我自己刷题的一些记录过程。如果有错可以指出哦&#xff0c;大家一起进步。 转载代码随想录 原文链接&#xff1a; 代码随想录 leetcode链接&#xff1a;344. 反转字符串 题目&#xff1a; 请你仅使用两个队列实现一个后入先出&#xff08;LIFO&#xff09;的栈&#x…

计量经济学复习

计量经济学 习题&#xff08;史浩江版&#xff09; 习题一 一. 单项选择题 1、横截面数据是指&#xff08;A&#xff09;。 A 同一时点上不同统计单位相同统计指标组成的数据 B 同一时点上相同统计单位相同统计指标组成的数据 C 同一时点上相同统计单位不同统计指标组成的…

GPT-Chinese 复现

github 环境准备 conda -create gpt_cn python3.7 conda activate gpt_cnconda install pytorch1.10.0 torchvision0.11.0 torchaudio0.10.0 -c pytorch pip install -r requirements.txt错误 module distutils has no attribute version解决方案&#xff1a; pip uninstal…

[附源码]计算机毕业设计基于Springboot游戏交易平台

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

MinIO实战

1.简介 MinIO 是一款基于Go语言发开的高性能、分布式的对象存储系统。客户端支持Java,Net,Python,Javacript, Golang语言。 2.部署 2.1单机器单节点&#xff08;docker&#xff09; 官网教程&#xff1a;https://min.io/docs/minio/container/index.html mkdir -p ~/minio/dat…