Skip to content

ToGuid conversion incorrect #6

Closed as not planned
Closed as not planned
@mdsitton

Description

@mdsitton

So i discovered that the default conversion the C# Guid type is not correct when trying to round-trip the type through a string:

using Medo;
Uuid7 id = Uuid7.NewUuid7();

Console.WriteLine($"uuid7:\t{id.ToString()}");
Console.WriteLine($"MsSql:\t{id.ToGuidMsSql().ToString()}");
Console.WriteLine($"ToGuid:\t{id.ToGuid().ToString()}");

output:

PS D:\development\uuidv7test> dotnet run
uuid7:  01899148-b29e-703a-848b-af5e76f5e9c2
MsSql:  01899148-b29e-703a-848b-af5e76f5e9c2
ToGuid: 48918901-9eb2-3a70-848b-af5e76f5e9c2

The internal Guid type is stored in a little endian format, however there is a new constructor to convert big endian bytes to little endian when this gets released in a .net version:
https://github.com/dotnet/runtime/blob/d389ab955dbb65547643fa1aed52669b02c04294/src/libraries/System.Private.CoreLib/src/System/Guid.cs#L69
dotnet/runtime#87993

For now a manual conversion is required though.

The current code manually converts to little endian for the ToGuidMsSql function but this should be done all the time IMO.

Additionally if you try to round trip through bytes to guid and back it works from the uuid side but it's still not correct. Since it's just interpreting the big endian bytes as little endian and then reading those back exactly as they were input. But if you round-trip through ToGuidMsSql it's also wrong because they don't get flipped on the way back into uuid7

Uuid7 idRt = new Uuid7(id.ToGuid());
Uuid7 idRtMs = new Uuid7(id.ToGuidMsSql());

Console.WriteLine($"rt:\t{idRt.ToString()}");
Console.WriteLine($"rt2:\t{idRtMs.ToString()}");
rt:       01899148-b29e-703a-848b-af5e76f5e9c2
rtMsSql:  48918901-9eb2-3a70-848b-af5e76f5e9c2

So this is what the proper conversion should look like:

// Read and rewrite values to flip to what Guid expects
var flip = (Span<byte> bytes) =>
{
    var aPos = bytes.Slice(0, 4);
    var bPos = bytes.Slice(4, 2);
    var cPos = bytes.Slice(6, 2);

    int a = BinaryPrimitives.ReadInt32BigEndian(aPos);
    short b = BinaryPrimitives.ReadInt16BigEndian(bPos);
    short c = BinaryPrimitives.ReadInt16BigEndian(cPos);

    BinaryPrimitives.WriteInt32LittleEndian(aPos, a);
    BinaryPrimitives.WriteInt16LittleEndian(bPos, b);
    BinaryPrimitives.WriteInt16LittleEndian(cPos, c);
};

// Correct manual conversion:
Span<byte> bytes = stackalloc byte[16];
id.TryWriteBytes(bytes);

// flip to little endian for Guid
flip(bytes);
var fixedGuid = new Guid(bytes);

Span<byte> guidBytes = stackalloc byte[16];
fixedGuid.TryWriteBytes(guidBytes);

// flip to big endian for Uuid7
flip(guidBytes);

var uuid7Rt = new Uuid7(guidBytes);
Console.WriteLine($"guid--rt:\t{fixedGuid.ToString()}");
Console.WriteLine($"uuid7-rt:\t{uuid7Rt.ToString()}");
guid--rt:       01899148-b29e-703a-848b-af5e76f5e9c2
uuid7-rt:       01899148-b29e-703a-848b-af5e76f5e9c2

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions