Big-endian fix: GUID handling (#49813)
authorUlrich Weigand <ulrich.weigand@de.ibm.com>
Fri, 19 Mar 2021 12:00:15 +0000 (13:00 +0100)
committerGitHub <noreply@github.com>
Fri, 19 Mar 2021 12:00:15 +0000 (13:00 +0100)
* Fix Utf8Formatter.Guid on big-endian systems
  (first three fields are stored in native byte order, not always
  little-endian)

* Use Guid constructor in BlobContentId.FromHash to remove
  endian-dependent code accessing the structure directly

src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Guid.cs
src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/BlobContentId.cs

index 4b366f7..888f5ab 100644 (file)
@@ -109,7 +109,7 @@ namespace System.Buffers.Text
             DecomposedGuid guidAsBytes = default;
             guidAsBytes.Guid = value;
 
-            // When a GUID is blitted, the first three components are little-endian, and the last component is big-endian.
+            // When a GUID is blitted, the first three components are native-endian, and the last component is big-endian.
 
             // The line below forces the JIT to hoist the bounds check for the following segment.
             // The JIT will optimize away the read, but it cannot optimize away the bounds check
@@ -117,10 +117,20 @@ namespace System.Buffers.Text
             // We use 8 instead of 7 so that we also capture the dash if we're asked to insert one.
 
             { _ = destination[8]; }
-            HexConverter.ToBytesBuffer(guidAsBytes.Byte03, destination, 0, HexConverter.Casing.Lower);
-            HexConverter.ToBytesBuffer(guidAsBytes.Byte02, destination, 2, HexConverter.Casing.Lower);
-            HexConverter.ToBytesBuffer(guidAsBytes.Byte01, destination, 4, HexConverter.Casing.Lower);
-            HexConverter.ToBytesBuffer(guidAsBytes.Byte00, destination, 6, HexConverter.Casing.Lower);
+            if (BitConverter.IsLittleEndian)
+            {
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte03, destination, 0, HexConverter.Casing.Lower);
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte02, destination, 2, HexConverter.Casing.Lower);
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte01, destination, 4, HexConverter.Casing.Lower);
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte00, destination, 6, HexConverter.Casing.Lower);
+            }
+            else
+            {
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte00, destination, 0, HexConverter.Casing.Lower);
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte01, destination, 2, HexConverter.Casing.Lower);
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte02, destination, 4, HexConverter.Casing.Lower);
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte03, destination, 6, HexConverter.Casing.Lower);
+            }
 
             if (flags < 0 /* use dash? */)
             {
@@ -133,8 +143,16 @@ namespace System.Buffers.Text
             }
 
             { _ = destination[4]; }
-            HexConverter.ToBytesBuffer(guidAsBytes.Byte05, destination, 0, HexConverter.Casing.Lower);
-            HexConverter.ToBytesBuffer(guidAsBytes.Byte04, destination, 2, HexConverter.Casing.Lower);
+            if (BitConverter.IsLittleEndian)
+            {
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte05, destination, 0, HexConverter.Casing.Lower);
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte04, destination, 2, HexConverter.Casing.Lower);
+            }
+            else
+            {
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte04, destination, 0, HexConverter.Casing.Lower);
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte05, destination, 2, HexConverter.Casing.Lower);
+            }
 
             if (flags < 0 /* use dash? */)
             {
@@ -147,8 +165,16 @@ namespace System.Buffers.Text
             }
 
             { _ = destination[4]; }
-            HexConverter.ToBytesBuffer(guidAsBytes.Byte07, destination, 0, HexConverter.Casing.Lower);
-            HexConverter.ToBytesBuffer(guidAsBytes.Byte06, destination, 2, HexConverter.Casing.Lower);
+            if (BitConverter.IsLittleEndian)
+            {
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte07, destination, 0, HexConverter.Casing.Lower);
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte06, destination, 2, HexConverter.Casing.Lower);
+            }
+            else
+            {
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte06, destination, 0, HexConverter.Casing.Lower);
+                HexConverter.ToBytesBuffer(guidAsBytes.Byte07, destination, 2, HexConverter.Casing.Lower);
+            }
 
             if (flags < 0 /* use dash? */)
             {
index 270c911..0ca5a41 100644 (file)
@@ -52,7 +52,7 @@ namespace System.Reflection.Metadata
             return FromHash(ImmutableByteArrayInterop.DangerousGetUnderlyingArray(hashCode)!);
         }
 
-        public static unsafe BlobContentId FromHash(byte[] hashCode)
+        public static BlobContentId FromHash(byte[] hashCode)
         {
             const int minHashSize = 20;
 
@@ -66,16 +66,23 @@ namespace System.Reflection.Metadata
                 throw new ArgumentException(SR.Format(SR.HashTooShort, minHashSize), nameof(hashCode));
             }
 
-            Guid guid = default(Guid);
-            byte* guidPtr = (byte*)&guid;
-            for (var i = 0; i < BlobUtilities.SizeOfGuid; i++)
-            {
-                guidPtr[i] = hashCode[i];
-            }
+            // extract guid components from input data
+            uint a = ((uint)hashCode[3] << 24 | (uint)hashCode[2] << 16 | (uint)hashCode[1] << 8 | hashCode[0]);
+            ushort b = (ushort)((ushort)hashCode[5] << 8 | (ushort)hashCode[4]);
+            ushort c = (ushort)((ushort)hashCode[7] << 8 | (ushort)hashCode[6]);
+            byte d = hashCode[8];
+            byte e = hashCode[9];
+            byte f = hashCode[10];
+            byte g = hashCode[11];
+            byte h = hashCode[12];
+            byte i = hashCode[13];
+            byte j = hashCode[14];
+            byte k = hashCode[15];
 
             // modify the guid data so it decodes to the form of a "random" guid ala rfc4122
-            guidPtr[7] = (byte)((guidPtr[7] & 0x0f) | (4 << 4));
-            guidPtr[8] = (byte)((guidPtr[8] & 0x3f) | (2 << 6));
+            c = (ushort)((c & 0x0fff) | (4 << 12));
+            d = (byte)((d & 0x3f) | (2 << 6));
+            Guid guid = new Guid((int)a, (short)b, (short)c, d, e, f, g, h, i, j, k);
 
             // compute a random-looking stamp from the remaining bits, but with the upper bit set
             uint stamp = 0x80000000u | ((uint)hashCode[19] << 24 | (uint)hashCode[18] << 16 | (uint)hashCode[17] << 8 | hashCode[16]);