001/*
002 *  Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.mybatisflex.core.keygen.impl;
017
018import com.mybatisflex.core.keygen.IKeyGenerator;
019
020import java.util.concurrent.ThreadLocalRandom;
021
022/**
023 * ULID: 对比UUID的优势在于可排序性和性能。
024 * <p>
025 * 特点:
026 * 1、保证 id 生成的顺序为时间顺序,越往后生成的 ID 值越大;
027 * 2、可以按照生成的时间进行排序,而不需要全局协调;
028 * 3、生成速度快;
029 * <p>
030 * <p>参考:<a href="https://github.com/ulid/spec">Sequence</a>
031 */
032public class ULIDKeyGenerator implements IKeyGenerator {
033
034    private static final char[] ENCODING_CHARS = {
035        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
036        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K',
037        'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X',
038        'Y', 'Z'
039    };
040
041    private static final long TIMESTAMP_OVERFLOW_MASK = 0xFFFF_0000_0000_0000L;
042
043    private static final ThreadLocal<StringBuilder> THREAD_LOCAL_BUILDER =
044        ThreadLocal.withInitial(() -> new StringBuilder(26));
045
046    private long lastTimestamp = 0;
047
048    private long lastRandom = 0;
049
050    @Override
051    public Object generate(Object entity, String keyColumn) {
052        return nextId();
053    }
054
055    /**
056     * 生成一个 ULID
057     *
058     * @return ULID
059     */
060    public String nextId() {
061        return generateULID(System.currentTimeMillis()).toLowerCase();
062    }
063
064    /**
065     * 生成一个严格单调的 ULID
066     *
067     * @return ULID
068     */
069    public synchronized String nextMonotonicId() {
070        long timestamp = System.currentTimeMillis();
071        if (timestamp > lastTimestamp) {
072            lastTimestamp = timestamp;
073            lastRandom = ThreadLocalRandom.current().nextLong();
074        } else {
075            lastRandom++;
076            if (lastRandom == 0) {
077                timestamp = waitNextMillis(lastTimestamp);
078                lastTimestamp = timestamp;
079                lastRandom = ThreadLocalRandom.current().nextLong();
080            }
081        }
082        return generateULID(lastTimestamp, lastRandom).toLowerCase();
083    }
084
085    private String generateULID(long timestamp) {
086        return generateULID(timestamp, ThreadLocalRandom.current().nextLong());
087    }
088
089    private String generateULID(long timestamp, long random) {
090        checkTimestamp(timestamp);
091        StringBuilder builder = THREAD_LOCAL_BUILDER.get();
092        builder.setLength(0);
093
094        appendCrockford(builder, timestamp, 10);
095        appendCrockford(builder, random, 16);
096
097        return builder.toString();
098    }
099
100    private long waitNextMillis(long lastTimestamp) {
101        long timestamp = System.currentTimeMillis();
102        while (timestamp <= lastTimestamp) {
103            timestamp = System.currentTimeMillis();
104        }
105        return timestamp;
106    }
107
108    private static void appendCrockford(StringBuilder builder, long value, int count) {
109        for (int i = (count - 1) * 5; i >= 0; i -= 5) {
110            int index = (int) ((value >>> i) & 0x1F);
111            builder.append(ENCODING_CHARS[index]);
112        }
113    }
114
115    private static void checkTimestamp(long timestamp) {
116        if ((timestamp & TIMESTAMP_OVERFLOW_MASK) != 0) {
117            throw new IllegalArgumentException("ULID does not support timestamps after +10889-08-02T05:31:50.655Z!");
118        }
119    }
120}