• java测试示例-生成ULID


    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                      |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

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

    参考

    ULID规范解读与实现原理

    java测试

    参考jar库

    <dependency>
        <groupId>com.github.f4b6a3groupId>
        <artifactId>ulid-creatorartifactId>
        <version>5.1.0version>
    dependency>
    <dependency>
        <groupId>de.huxhorn.sulkygroupId>
        <artifactId>de.huxhorn.sulky.ulidartifactId>
        <version>8.3.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    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)];
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97

    测试

    位数: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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    库源码

    注释做了部分清理

    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!");
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373

    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();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    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;
    			};
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    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;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505

    END

  • 相关阅读:
    DRM全解析 —— CRTC详解(4)
    DeepStream系列之yolov8部署测试
    谛听安全 | 内容审核闯关攻略指南
    使用Qt Designer为您的Qt for Python项目创建基于Qt Widgets的图形界面的两种方法
    java基础 --- 关键字 final、this、super、static
    03if基础
    Git学习总结
    在echaerts中渲染50万条数据的优化方案
    【Linux】实验二 Makefile 的编写及应用
    职素丨专业的职素训练 让学员转变为职业人
  • 原文地址:https://blog.csdn.net/privateobject/article/details/128199975