103 lines
2.9 KiB
C#
103 lines
2.9 KiB
C#
namespace MasstransferCommon.Utils;
|
|
|
|
using System;
|
|
|
|
public class SnowflakeId
|
|
{
|
|
private static readonly DateTime Epoch = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
private const int WorkerIdBits = 5;
|
|
private const int DatacenterIdBits = 5;
|
|
private const int SequenceBits = 12;
|
|
|
|
private const long MaxWorkerId = -1L ^ (-1L << WorkerIdBits);
|
|
private const long MaxDatacenterId = -1L ^ (-1L << DatacenterIdBits);
|
|
|
|
private const long WorkerIdShift = SequenceBits;
|
|
private const long DatacenterIdShift = SequenceBits + WorkerIdBits;
|
|
private const long TimestampLeftShift = SequenceBits + WorkerIdBits + DatacenterIdBits;
|
|
private const long SequenceMask = -1L ^ (-1L << SequenceBits);
|
|
|
|
private readonly object _lock = new object();
|
|
private long _lastTimestamp = -1L;
|
|
private long _sequence = 0L;
|
|
|
|
private static readonly SnowflakeId Instance = new SnowflakeId(0, 0);
|
|
|
|
public long WorkerId { get; }
|
|
public long DatacenterId { get; }
|
|
|
|
public SnowflakeId(long workerId, long datacenterId)
|
|
{
|
|
if (workerId > MaxWorkerId || workerId < 0)
|
|
{
|
|
throw new ArgumentException($"workerId must be between 0 and {MaxWorkerId}");
|
|
}
|
|
|
|
if (datacenterId > MaxDatacenterId || datacenterId < 0)
|
|
{
|
|
throw new ArgumentException($"datacenterId must be between 0 and {MaxDatacenterId}");
|
|
}
|
|
|
|
WorkerId = workerId;
|
|
DatacenterId = datacenterId;
|
|
}
|
|
|
|
public static string GetNextId()
|
|
{
|
|
return Instance.NextId().ToString();
|
|
}
|
|
|
|
private long NextId()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
var timestamp = TimeGen();
|
|
|
|
if (timestamp < _lastTimestamp)
|
|
{
|
|
throw new InvalidOperationException("Clock moved backwards. Refusing to generate id");
|
|
}
|
|
|
|
if (_lastTimestamp == timestamp)
|
|
{
|
|
_sequence = (_sequence + 1) & SequenceMask;
|
|
if (_sequence == 0)
|
|
{
|
|
timestamp = TilNextMillis(_lastTimestamp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_sequence = 0L;
|
|
}
|
|
|
|
_lastTimestamp = timestamp;
|
|
|
|
return ((timestamp - EpochTicks()) << (int)TimestampLeftShift) |
|
|
(DatacenterId << (int)DatacenterIdShift) |
|
|
(WorkerId << (int)WorkerIdShift) |
|
|
_sequence;
|
|
}
|
|
}
|
|
|
|
private long TilNextMillis(long lastTimestamp)
|
|
{
|
|
var timestamp = TimeGen();
|
|
while (timestamp <= lastTimestamp)
|
|
{
|
|
timestamp = TimeGen();
|
|
}
|
|
|
|
return timestamp;
|
|
}
|
|
|
|
private long TimeGen()
|
|
{
|
|
return (long)(DateTime.UtcNow - Epoch).TotalMilliseconds;
|
|
}
|
|
|
|
private static long EpochTicks()
|
|
{
|
|
return (long)(Epoch - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
|
|
}
|
|
} |