Use uuidv7 algorithm from javascript
This commit is contained in:
@@ -1,30 +1,94 @@
|
|||||||
using System.Buffers.Binary;
|
namespace Hutopy.Web.Common;
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
namespace Hutopy.Web.Common;
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adapted from https://raw.githubusercontent.com/uuidjs/uuid/main/src/v7.ts.
|
||||||
|
/// to match the uuidv7 generated on the client
|
||||||
|
/// </summary>
|
||||||
public static class GuidHelper
|
public static class GuidHelper
|
||||||
{
|
{
|
||||||
// TODO: Delete when NET9 is release!
|
private class V7State
|
||||||
|
{
|
||||||
|
public long Msecs { get; set; } = long.MinValue;
|
||||||
|
public int Seq { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly V7State State = new();
|
||||||
|
private static readonly Random Random = new();
|
||||||
|
|
||||||
public static Guid GenerateUuidV7()
|
public static Guid GenerateUuidV7()
|
||||||
{
|
{
|
||||||
Span<byte> uuidv7 = stackalloc byte[16];
|
byte[] randomValues = new byte[16];
|
||||||
ulong unixTimeTicks = (ulong)DateTimeOffset.UtcNow.Subtract(DateTimeOffset.UnixEpoch).Ticks;
|
Random.NextBytes(randomValues);
|
||||||
ulong unixTsMs = (unixTimeTicks & 0x0FFFFFFFFFFFF000) << 4;
|
|
||||||
ulong unixTsMsVer = unixTsMs | 0b0111UL << 12;
|
UpdateV7State(
|
||||||
ulong randA = unixTimeTicks & 0x0000000000000FFF;
|
State,
|
||||||
// merge "unix_ts_ms", "ver" and "rand_a"
|
DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
|
||||||
ulong hi = unixTsMsVer | randA;
|
randomValues);
|
||||||
BinaryPrimitives.WriteUInt64BigEndian(uuidv7, hi);
|
|
||||||
// fill "rand_b" and "var"
|
var values = V7Bytes(randomValues, State.Msecs, State.Seq);
|
||||||
RandomNumberGenerator.Fill(uuidv7[8..]);
|
|
||||||
// set "var"
|
return new(values);
|
||||||
byte varOctet = uuidv7[8];
|
}
|
||||||
varOctet = (byte)(varOctet & 0b00111111);
|
|
||||||
varOctet = (byte)(varOctet | 0b10111111);
|
private static void UpdateV7State(V7State state, long now, byte[] rnds)
|
||||||
uuidv7[8] = varOctet;
|
{
|
||||||
|
if (now > state.Msecs)
|
||||||
|
{
|
||||||
|
state.Seq = (rnds[6] << 23) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9];
|
||||||
|
state.Msecs = now;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state.Seq = (state.Seq + 1) | 0;
|
||||||
|
if (state.Seq == 0)
|
||||||
|
{
|
||||||
|
state.Msecs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] V7Bytes(byte[] rnds, long? msecs = null, int? seq = null, byte[] buf = null, int offset = 0)
|
||||||
|
{
|
||||||
|
if (buf == null)
|
||||||
|
{
|
||||||
|
buf = new byte[16];
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defaults
|
||||||
|
msecs ??= DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||||
|
seq ??= ((rnds[6] & 0x7f) << 24) | (rnds[7] << 16) | (rnds[8] << 8) | rnds[9];
|
||||||
|
|
||||||
|
// byte 0-5: timestamp (48 bits)
|
||||||
|
buf[offset++] = (byte)((msecs.Value / 0x10000000000) & 0xff);
|
||||||
|
buf[offset++] = (byte)((msecs.Value / 0x100000000) & 0xff);
|
||||||
|
buf[offset++] = (byte)((msecs.Value / 0x1000000) & 0xff);
|
||||||
|
buf[offset++] = (byte)((msecs.Value / 0x10000) & 0xff);
|
||||||
|
buf[offset++] = (byte)((msecs.Value / 0x100) & 0xff);
|
||||||
|
buf[offset++] = (byte)(msecs.Value & 0xff);
|
||||||
|
|
||||||
var value = Convert.ToHexString(uuidv7);
|
// byte 6: `version` (4 bits) | sequence bits 28-31 (4 bits)
|
||||||
return Guid.Parse(value);
|
buf[offset++] = (byte)(0x70 | ((seq.Value >> 28) & 0x0f));
|
||||||
|
|
||||||
|
// byte 7: sequence bits 20-27 (8 bits)
|
||||||
|
buf[offset++] = (byte)((seq.Value >> 20) & 0xff);
|
||||||
|
|
||||||
|
// byte 8: `variant` (2 bits) | sequence bits 14-19 (6 bits)
|
||||||
|
buf[offset++] = (byte)(0x80 | ((seq.Value >> 14) & 0x3f));
|
||||||
|
|
||||||
|
// byte 9: sequence bits 6-13 (8 bits)
|
||||||
|
buf[offset++] = (byte)((seq.Value >> 6) & 0xff);
|
||||||
|
|
||||||
|
// byte 10: sequence bits 0-5 (6 bits) | random (2 bits)
|
||||||
|
buf[offset++] = (byte)(((seq.Value << 2) & 0xff) | (rnds[10] & 0x03));
|
||||||
|
|
||||||
|
// bytes 11-15: random (40 bits)
|
||||||
|
buf[offset++] = rnds[11];
|
||||||
|
buf[offset++] = rnds[12];
|
||||||
|
buf[offset++] = rnds[13];
|
||||||
|
buf[offset++] = rnds[14];
|
||||||
|
buf[offset++] = rnds[15];
|
||||||
|
|
||||||
|
return buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user