001/*
002 *    Copyright 2024-2025, Warm-Flow (290631660@qq.com).
003 *
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 *
008 *       https://www.apache.org/licenses/LICENSE-2.0
009 *
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 org.dromara.warm.flow.core.keygen;
017
018/**
019 * warm 生成15位有序id
020 *
021 * @author HUANGJIANSHISHENG
022 */
023public class SnowFlakeId15 implements KenGen {
024    // 开始时间戳 (任意设置,建议为项目的开始时间)
025    private final long epoch = 1609459200000L; // 2021-01-01 00:00:00
026
027    // 各部分的位数
028    private final long sequenceBits = 6L;   // 序列号占用6位
029    private final long machineIdBits = 4L;  // 机器ID占用4位
030
031    // 各部分的最大值
032    private final long maxMachineId = -1L ^ (-1L << machineIdBits);
033    private final long maxSequence = -1L ^ (-1L << sequenceBits);
034
035    // 各部分的偏移量
036    private final long sequenceShift = 0;
037    private final long machineIdShift = sequenceBits;
038    private final long timestampShift = machineIdBits + sequenceBits;
039
040    private long machineId;
041    private long sequence = 0L;
042    private long lastTimestamp = -1L;
043
044    public SnowFlakeId15(long machineId) {
045        if (machineId > maxMachineId || machineId < 0) {
046            throw new IllegalArgumentException("Machine ID is out of bounds.");
047        }
048        this.machineId = machineId;
049    }
050
051    @Override
052    public synchronized long nextId() {
053        long timestamp = timeGen();
054
055        if (timestamp < lastTimestamp) {
056            throw new RuntimeException("Clock moved backwards. Refusing to generate id.");
057        }
058
059        if (timestamp == lastTimestamp) {
060            // 当前毫秒内,则+1
061            sequence = (sequence + 1) & maxSequence;
062            if (sequence == 0) {
063                timestamp = tilNextMillis(lastTimestamp);
064            }
065        } else {
066            sequence = 0L;
067        }
068
069        lastTimestamp = timestamp;
070
071        // 生成ID并保证其为15位
072        long id = ((timestamp - epoch) << timestampShift)
073            | (machineId << machineIdShift)
074            | sequence;
075
076        return id % 10_000_000_000_000_000L; // 保证生成的ID为15位
077    }
078
079    private long tilNextMillis(long lastTimestamp) {
080        long timestamp = timeGen();
081        while (timestamp <= lastTimestamp) {
082            timestamp = timeGen();
083        }
084        return timestamp;
085    }
086
087    private long timeGen() {
088        return System.currentTimeMillis();
089    }
090
091}