Skip to content

Instantly share code, notes, and snippets.

@yaakov-h
Created June 16, 2024 01:42
Show Gist options
  • Save yaakov-h/39a5d93e40c0e01b5f074be75032b545 to your computer and use it in GitHub Desktop.
Save yaakov-h/39a5d93e40c0e01b5f074be75032b545 to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
public readonly struct UUIDv7 {
private readonly InteriorStruct _interior;
public UUIDv7() {
InteriorStruct.Fill(ref _interior);
}
public byte[] GetBytes() => _interior.AsReadOnlySpan().ToArray();
public void CopyTo(Span<byte> destination) => _interior.AsReadOnlySpan().CopyTo(destination);
public override string ToString() => Convert.ToHexString(_interior.AsReadOnlySpan());
public readonly override int GetHashCode() {
HashCode hc = new();
hc.AddBytes(_interior.AsReadOnlySpan());
return hc.ToHashCode();
}
[InlineArray(SIZE)]
private struct InteriorStruct {
private const int SIZE = 16;
private byte _element;
public static void Fill(ref InteriorStruct uuid) {
Span<byte> value = MemoryMarshal.CreateSpan(ref uuid._element, SIZE);
RandomNumberGenerator.Fill(value[6..]);
long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
var timestampBytes = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref timestamp, length: 1));
timestampBytes[..6].CopyTo(value[..6]);
value[..6].Reverse();
// version and variant
value[6] = (byte)((value[6] & 0x0F) | 0x70);
value[8] = (byte)((value[8] & 0x3F) | 0x80);
}
[UnscopedRef]
internal ReadOnlySpan<byte> AsReadOnlySpan() => MemoryMarshal.CreateReadOnlySpan(ref _element, SIZE);
}
}
@vcsjones
Copy link

vcsjones commented Jun 16, 2024

A slightly faster Fill (I think):

        public static void Fill(ref InteriorStruct uuid) {
            Span<byte> value = MemoryMarshal.CreateSpan(ref uuid._element, SIZE);

            long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() << 16;
            BinaryPrimitives.WriteInt64BigEndian(value, timestamp);
            RandomNumberGenerator.Fill(value[6..]);

            // version and variant
            value[6] = (byte)((value[6] & 0x0F) | 0x70);
            value[8] = (byte)((value[8] & 0x3F) | 0x80);
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment