11using System ;
2- using System . Buffers ;
3- using System . Runtime . CompilerServices ;
42using DuckDB . NET . Native ;
53
64namespace DuckDB . NET . Data . Extensions ;
75
86internal static class GuidConverter
97{
10- #if ! NET6_0_OR_GREATER
11- private const string GuidFormat = "D" ;
12- private static readonly char [ ] HexDigits = "0123456789abcdef" . ToCharArray ( ) ;
8+ private const int GuidSize = 16 ;
139
14- //Ported from duckdb source code UUID::ToString
15- //https://github.com/duckdb/duckdb/blob/9c91b3a329073ea1767b0aaff94b51da98dd03e2/src/common/types/uuid.cpp#L56
16- public static Guid ConvertToGuid ( this DuckDBHugeInt input )
10+ public static unsafe Guid ConvertToGuid ( this DuckDBHugeInt input )
1711 {
18- Span < char > buffer = stackalloc char [ 36 ] ;
19- var num = input . Upper ^ long . MinValue ;
20- var position = 0 ;
21-
22- ByteToHex ( buffer , ref position , ( ulong ) ( ( num >> 56 ) & 0xFF ) ) ;
23- ByteToHex ( buffer , ref position , ( ulong ) ( ( num >> 48 ) & 0xFF ) ) ;
24- ByteToHex ( buffer , ref position , ( ulong ) ( ( num >> 40 ) & 0xFF ) ) ;
25- ByteToHex ( buffer , ref position , ( ulong ) ( ( num >> 32 ) & 0xFF ) ) ;
26-
27- buffer [ position ++ ] = '-' ;
28-
29- ByteToHex ( buffer , ref position , ( ulong ) ( ( num >> 24 ) & 0xFF ) ) ;
30- ByteToHex ( buffer , ref position , ( ulong ) ( ( num >> 16 ) & 0xFF ) ) ;
31-
32- buffer [ position ++ ] = '-' ;
33-
34- ByteToHex ( buffer , ref position , ( ulong ) ( ( num >> 8 ) & 0xFF ) ) ;
35- ByteToHex ( buffer , ref position , ( ulong ) ( num & 0xFF ) ) ;
36-
37- buffer [ position ++ ] = '-' ;
38-
39- ByteToHex ( buffer , ref position , ( input . Lower >> 56 ) & 0xFF ) ;
40- ByteToHex ( buffer , ref position , ( input . Lower >> 48 ) & 0xFF ) ;
41-
42- buffer [ position ++ ] = '-' ;
43-
44- ByteToHex ( buffer , ref position , ( input . Lower >> 40 ) & 0xFF ) ;
45- ByteToHex ( buffer , ref position , ( input . Lower >> 32 ) & 0xFF ) ;
46- ByteToHex ( buffer , ref position , ( input . Lower >> 24 ) & 0xFF ) ;
47- ByteToHex ( buffer , ref position , ( input . Lower >> 16 ) & 0xFF ) ;
48- ByteToHex ( buffer , ref position , ( input . Lower >> 8 ) & 0xFF ) ;
49- ByteToHex ( buffer , ref position , input . Lower & 0xFF ) ;
50-
51- return Guid . ParseExact ( new string ( buffer . ToArray ( ) ) , GuidFormat ) ;
52-
53- static void ByteToHex ( Span < char > buffer , ref int position , ulong value )
54- {
55- buffer [ position ++ ] = HexDigits [ ( value >> 4 ) & 0xF ] ;
56- buffer [ position ++ ] = HexDigits [ value & 0xF ] ;
57- }
58- }
12+ Span < byte > bytes = stackalloc byte [ 32 ] ;
13+
14+ // Reverse the bit flip on the upper 64 bits
15+ var upper = input . Upper ^ ( ( long ) 1 << 63 ) ;
16+
17+ #if NET6_0_OR_GREATER
18+ // Write upper 64 bits (bytes 0-7)
19+ BitConverter . TryWriteBytes ( bytes [ GuidSize ..] , upper ) ;
20+
21+ // Write lower 64 bits (bytes 8-15)
22+ BitConverter . TryWriteBytes ( bytes [ ( GuidSize + 8 ) ..] , input . Lower ) ;
5923#else
60- public static Guid ConvertToGuid ( this DuckDBHugeInt input )
61- {
62- var bytes = ArrayPool < byte > . Shared . Rent ( 32 ) ;
63- try
64- {
65- // Reverse the bit flip on the upper 64 bits
66- long upper = input . Upper ^ ( ( long ) 1 << 63 ) ;
67-
68- // Write upper 64 bits (bytes 0-7)
69- BitConverter . TryWriteBytes ( bytes . AsSpan ( 16 ) , upper ) ;
70-
71- // Write lower 64 bits (bytes 8-15)
72- BitConverter . TryWriteBytes ( bytes . AsSpan ( 16 + 8 ) , input . Lower ) ;
73-
74- // Reconstruct the Guid bytes (reverse the original byte reordering)
75- ReorderBytesForGuid ( ) ;
76-
77- // Create Guid from the first 16 bytes
78- return new Guid ( bytes . AsSpan ( 0 , 16 ) ) ;
79- }
80- finally
81- {
82- ArrayPool < byte > . Shared . Return ( bytes ) ;
83- }
84-
85- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
86- void ReorderBytesForGuid ( )
87- {
88- // First 4 bytes (little-endian)
89- bytes [ 6 ] = bytes [ 16 + 0 ] ;
90- bytes [ 7 ] = bytes [ 16 + 1 ] ;
91- bytes [ 4 ] = bytes [ 16 + 2 ] ;
92- bytes [ 5 ] = bytes [ 16 + 3 ] ;
93-
94- // Next 4 bytes (little-endian)
95- bytes [ 0 ] = bytes [ 16 + 4 ] ;
96- bytes [ 1 ] = bytes [ 16 + 5 ] ;
97- bytes [ 2 ] = bytes [ 16 + 6 ] ;
98- bytes [ 3 ] = bytes [ 16 + 7 ] ;
99-
100- // Last 8 bytes (big-endian)
101- bytes [ 15 ] = bytes [ 16 + 8 ] ;
102- bytes [ 14 ] = bytes [ 16 + 9 ] ;
103- bytes [ 13 ] = bytes [ 16 + 10 ] ;
104- bytes [ 12 ] = bytes [ 16 + 11 ] ;
105- bytes [ 11 ] = bytes [ 16 + 12 ] ;
106- bytes [ 10 ] = bytes [ 16 + 13 ] ;
107- bytes [ 9 ] = bytes [ 16 + 14 ] ;
108- bytes [ 8 ] = bytes [ 16 + 15 ] ;
109- }
110- }
24+ var data = BitConverter . GetBytes ( upper ) ;
25+ data . CopyTo ( bytes ) ;
26+
27+ data = BitConverter . GetBytes ( input . Lower ) ;
28+ data . CopyTo ( bytes . Slice ( GuidSize + 8 ) ) ;
29+ #endif
30+
31+ // Reconstruct the Guid bytes (reverse the original byte reordering)
32+
33+ // First 4 bytes (little-endian)
34+ bytes [ 6 ] = bytes [ GuidSize + 0 ] ;
35+ bytes [ 7 ] = bytes [ GuidSize + 1 ] ;
36+ bytes [ 4 ] = bytes [ GuidSize + 2 ] ;
37+ bytes [ 5 ] = bytes [ GuidSize + 3 ] ;
38+
39+ // Next 4 bytes (little-endian)
40+ bytes [ 0 ] = bytes [ GuidSize + 4 ] ;
41+ bytes [ 1 ] = bytes [ GuidSize + 5 ] ;
42+ bytes [ 2 ] = bytes [ GuidSize + 6 ] ;
43+ bytes [ 3 ] = bytes [ GuidSize + 7 ] ;
44+
45+ // Last 8 bytes (big-endian)
46+ bytes [ 15 ] = bytes [ GuidSize + 8 ] ;
47+ bytes [ 14 ] = bytes [ GuidSize + 9 ] ;
48+ bytes [ 13 ] = bytes [ GuidSize + 10 ] ;
49+ bytes [ 12 ] = bytes [ GuidSize + 11 ] ;
50+ bytes [ 11 ] = bytes [ GuidSize + 12 ] ;
51+ bytes [ 10 ] = bytes [ GuidSize + 13 ] ;
52+ bytes [ 9 ] = bytes [ GuidSize + 14 ] ;
53+ bytes [ 8 ] = bytes [ GuidSize + 15 ] ;
54+
55+ // Create Guid from the first 16 bytes
56+ #if NET6_0_OR_GREATER
57+ return new Guid ( bytes [ ..GuidSize ] ) ;
58+ #else
59+ return new Guid ( bytes . Slice ( 0 , GuidSize ) . ToArray ( ) ) ;
11160#endif
61+ }
62+
11263
11364 //https://github.com/duckdb/duckdb/blob/9c91b3a329073ea1767b0aaff94b51da98dd03e2/src/common/types/uuid.cpp#L6
11465 public static DuckDBHugeInt ToHugeInt ( this Guid guid )
11566 {
116- var bytes = ArrayPool < byte > . Shared . Rent ( 32 ) ;
67+ Span < byte > bytes = stackalloc byte [ 32 ] ;
11768
118- try
119- {
12069#if NET6_0_OR_GREATER
121- guid . TryWriteBytes ( bytes ) ;
70+ guid . TryWriteBytes ( bytes ) ;
12271#else
123- Buffer . BlockCopy ( guid . ToByteArray ( ) , 0 , bytes , 0 , 16 ) ;
72+ var byteArray = guid . ToByteArray ( ) ;
73+ byteArray . AsSpan ( ) . CopyTo ( bytes ) ;
12474#endif
125- bytes [ 16 + 0 ] = bytes [ 6 ] ; // First 4 bytes (little-endian)
126- bytes [ 16 + 1 ] = bytes [ 7 ] ;
127- bytes [ 16 + 2 ] = bytes [ 4 ] ;
128- bytes [ 16 + 3 ] = bytes [ 5 ] ;
129- bytes [ 16 + 4 ] = bytes [ 0 ] ; // Next 4 bytes (little-endian)
130- bytes [ 16 + 5 ] = bytes [ 1 ] ;
131- bytes [ 16 + 6 ] = bytes [ 2 ] ;
132- bytes [ 16 + 7 ] = bytes [ 3 ] ;
133-
134- bytes [ 16 + 8 ] = bytes [ 15 ] ; // Big endian
135- bytes [ 16 + 9 ] = bytes [ 14 ] ;
136- bytes [ 16 + 10 ] = bytes [ 13 ] ;
137- bytes [ 16 + 11 ] = bytes [ 12 ] ;
138- bytes [ 16 + 12 ] = bytes [ 11 ] ;
139- bytes [ 16 + 13 ] = bytes [ 10 ] ;
140- bytes [ 16 + 14 ] = bytes [ 9 ] ;
141- bytes [ 16 + 15 ] = bytes [ 8 ] ;
142-
143- // Upper 64 bits (bytes 0-7)
144- long upper = BitConverter . ToInt64 ( bytes , 16 + 0 ) ;
145-
146- // Lower 64 bits (bytes 8-15)
147- ulong lower = BitConverter . ToUInt64 ( bytes , 16 + 8 ) ;
148-
149- // Flip the first bit to make `order by uuid` same as `order by uuid::varchar`
150- upper ^= ( ( long ) 1 << 63 ) ;
151-
152- return new DuckDBHugeInt ( lower , upper ) ;
153- }
154- finally
155- {
156- ArrayPool < byte > . Shared . Return ( bytes ) ;
157- }
75+ bytes [ GuidSize + 0 ] = bytes [ 6 ] ; // First 4 bytes (little-endian)
76+ bytes [ GuidSize + 1 ] = bytes [ 7 ] ;
77+ bytes [ GuidSize + 2 ] = bytes [ 4 ] ;
78+ bytes [ GuidSize + 3 ] = bytes [ 5 ] ;
79+
80+ bytes [ GuidSize + 4 ] = bytes [ 0 ] ; // Next 4 bytes (little-endian)
81+ bytes [ GuidSize + 5 ] = bytes [ 1 ] ;
82+ bytes [ GuidSize + 6 ] = bytes [ 2 ] ;
83+ bytes [ GuidSize + 7 ] = bytes [ 3 ] ;
84+
85+ bytes [ GuidSize + 8 ] = bytes [ 15 ] ; // Big endian
86+ bytes [ GuidSize + 9 ] = bytes [ 14 ] ;
87+ bytes [ GuidSize + 10 ] = bytes [ 13 ] ;
88+ bytes [ GuidSize + 11 ] = bytes [ 12 ] ;
89+ bytes [ GuidSize + 12 ] = bytes [ 11 ] ;
90+ bytes [ GuidSize + 13 ] = bytes [ 10 ] ;
91+ bytes [ GuidSize + 14 ] = bytes [ 9 ] ;
92+ bytes [ GuidSize + 15 ] = bytes [ 8 ] ;
93+
94+ #if NET6_0_OR_GREATER
95+ // Upper 64 bits (bytes 0-7)
96+ long upper = BitConverter . ToInt64 ( bytes [ GuidSize ..] ) ;
97+
98+ // Lower 64 bits (bytes 8-15)
99+ ulong lower = BitConverter . ToUInt64 ( bytes [ ( GuidSize + 8 ) ..] ) ;
100+ #else
101+ var array = bytes . ToArray ( ) ;
102+
103+ long upper = BitConverter . ToInt64 ( array , GuidSize ) ;
104+ ulong lower = BitConverter . ToUInt64 ( array , GuidSize + 8 ) ;
105+ #endif
106+
107+ // Flip the first bit to make `order by uuid` same as `order by uuid::varchar`
108+ upper ^= ( long ) 1 << 63 ;
109+
110+ return new DuckDBHugeInt ( lower , upper ) ;
158111 }
159112}
0 commit comments