Skip to content

Instantly share code, notes, and snippets.

@huobazi
Forked from jessejjohnson/SnowflakeIdGenerator.cs
Created September 2, 2020 08:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save huobazi/350e0481170ef4515d543a73f8947beb to your computer and use it in GitHub Desktop.
Save huobazi/350e0481170ef4515d543a73f8947beb to your computer and use it in GitHub Desktop.
Generate Twitter Snowflake ID's
public class SnowflakeIdGenerator
{
public const byte DefaultMachineIdBits = 6;
public const byte DefaultSequenceBits = 12;
private readonly long _machineId = 0;
private readonly byte _machineIdBits = 0;
private readonly byte _sequenceBits = 0;
private readonly long _maxSequence = 0;
private readonly Stopwatch _stopwatch = Stopwatch.StartNew();
private readonly object _lockObject = new object();
private long _sequence = 0;
private long _lastTimestamp = 0;
private static SnowflakeIdGenerator _instance;
public static void Init(ushort machineId, byte machineIdBits = DefaultMachineIdBits, byte sequenceBits = DefaultSequenceBits)
{
if (_instance != null)
throw new InvalidOperationException($"Instance has already been initialized.");
_instance = new SnowflakeIdGenerator(machineId, machineIdBits, sequenceBits);
}
public static long NewId()
{
if (_instance == null)
throw new Exception("Instance has not been initialized.");
return _instance.NewSequenceId();
}
private static long GetMaxOfBits(byte bits)
{
return (1L << bits) - 1;
}
private static int GetBitsLength(long number)
{
return (int)Math.Log(number, 2) + 1;
}
// 637134336000000000 = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks
private static readonly long _offsetTicks = DateTime.UtcNow.Ticks - 637134336000000000;
public SnowflakeIdGenerator(ushort machineId) : this(machineId, DefaultMachineIdBits, DefaultSequenceBits)
{
}
public SnowflakeIdGenerator(ushort machineId, byte machineIdBits, byte sequenceBits)
{
if (sequenceBits > 20)
throw new ArgumentOutOfRangeException(nameof(sequenceBits), "sequenceBits > 20");
if (machineIdBits > 10)
throw new ArgumentOutOfRangeException(nameof(machineIdBits), "machineIdBits > 10");
_machineIdBits = machineIdBits;
_sequenceBits = sequenceBits;
_maxSequence = GetMaxOfBits(_sequenceBits);
var maxMachineId = GetMaxOfBits(machineIdBits);
if (machineId > maxMachineId)
throw new ArgumentOutOfRangeException(nameof(machineId), $"machineId > {maxMachineId}");
_machineId = machineId;
}
private long GetTimestampNow()
{
// 10000000 = TimeSpan.FromSeconds(1).Ticks
return (_offsetTicks + _stopwatch.Elapsed.Ticks) / 10000000L;
}
private long GetNextTimestamp()
{
long timestamp = GetTimestampNow();
if (timestamp < _lastTimestamp)
throw new Exception("");
while (timestamp == _lastTimestamp)
{
if (_sequence < _maxSequence)
{
_sequence++;
return timestamp;
}
Thread.Sleep(0);
timestamp = GetTimestampNow();
}
_sequence = 0;
return timestamp;
}
public long NewSequenceId()
{
lock (_lockObject)
{
_lastTimestamp = GetNextTimestamp();
//int bitsLength = GetBitsLength(_lastTimestamp);
//Console.WriteLine($"Timestamp bits: {bitsLength}");
int timestampShift = _machineIdBits + _sequenceBits;
int machineIdShift = _sequenceBits;
return (_lastTimestamp << timestampShift) | (_machineId << machineIdShift) | _sequence;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment