35 lines
1.3 KiB
C#
35 lines
1.3 KiB
C#
namespace SharpChat.Snowflake;
|
|
|
|
public class SnowflakeGenerator {
|
|
public const long MASK = 0x7FFFFFFFFFFFFFFF;
|
|
// previous default epoch was 1588377600000, but snowflakes are much larger than SharpIds
|
|
public const long EPOCH = 1356998400000;
|
|
public const byte SHIFT = 16;
|
|
|
|
public readonly long Epoch;
|
|
public readonly byte Shift;
|
|
public readonly long TimestampMask;
|
|
public readonly long SequenceMask;
|
|
|
|
public SnowflakeGenerator(long epoch = EPOCH, byte shift = SHIFT) {
|
|
if(epoch is < 0 or > MASK)
|
|
throw new ArgumentException("Epoch must be a positive int64.", nameof(epoch));
|
|
if(shift is < 1 or > 63)
|
|
throw new ArgumentException("Shift must be between or equal to 1 and 63", nameof(shift));
|
|
|
|
Epoch = epoch;
|
|
Shift = shift;
|
|
|
|
// i think Index only does this as a hack for how integers work in PHP but its gonna run Once per application instance lol
|
|
TimestampMask = ~(~0L << (63 - shift));
|
|
SequenceMask = ~(~0L << shift);
|
|
}
|
|
|
|
public long Now(DateTimeOffset? at = null) {
|
|
return Math.Max(0, (at ?? DateTimeOffset.UtcNow).ToUnixTimeMilliseconds() - Epoch);
|
|
}
|
|
|
|
public long Next(long sequence, DateTimeOffset? at = null) {
|
|
return ((Now(at) & TimestampMask) << Shift) | (sequence & SequenceMask);
|
|
}
|
|
}
|