diff --git a/Tests/NFUnitTestSystemLib/UnitTestGuid.cs b/Tests/NFUnitTestSystemLib/UnitTestGuid.cs index e2b3ea3..a85d00f 100644 --- a/Tests/NFUnitTestSystemLib/UnitTestGuid.cs +++ b/Tests/NFUnitTestSystemLib/UnitTestGuid.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; @@ -60,7 +60,7 @@ public void ByteArrayConstructor_Test2() /// /// - Byte[] guid16 = GetRandomBytes(16); + byte[] guid16 = GetRandomBytes(16); Guid myGuid1 = new Guid(guid16); } @@ -82,7 +82,7 @@ public void ArgumentException_Test3() { size = random.Next(100); } - Byte[] guidNot16 = GetRandomBytes(size); + byte[] guidNot16 = GetRandomBytes(size); Assert.ThrowsException(typeof(ArgumentException), () => { Guid myGuid1 = new Guid(guidNot16); }); } @@ -95,7 +95,7 @@ public void ArgumentNullException_Test4() /// /// - Byte[] nullByte = null; + byte[] nullByte = null; Assert.ThrowsException(typeof(ArgumentNullException), () => { Guid myGuid1 = new Guid(nullByte); }); } @@ -157,11 +157,11 @@ public void Int_Short_Constructor_Test6() /// Random random = new Random(); - int _int = random.Next(Int32.MaxValue); + int _int = random.Next(int.MaxValue); short _short1 = (short)random.Next(32768); short _short2 = (short)random.Next(32768); - Byte[] _bArr = GetRandomBytes(8); - Guid _guid = new Guid(_int, _short1, _short2, _bArr[0], _bArr[1], _bArr[2], _bArr[3], _bArr[4], _bArr[5], _bArr[6], _bArr[7]); + byte[] _bArr = GetRandomBytes(8); + _ = new Guid(_int, _short1, _short2, _bArr[0], _bArr[1], _bArr[2], _bArr[3], _bArr[4], _bArr[5], _bArr[6], _bArr[7]); } [TestMethod] @@ -174,11 +174,11 @@ public void UInt_Ushort_Constructor_Test7() /// Random random = new Random(); - int randoInt = random.Next(Int32.MaxValue); + int randoInt = random.Next(int.MaxValue); uint _uInt = (uint)(randoInt * 2); ushort _uShort1 = (ushort)random.Next(65536); ushort _uShort2 = (ushort)random.Next(65536); - Byte[] _bArr = GetRandomBytes(8); + byte[] _bArr = GetRandomBytes(8); Guid _guid = new Guid(_uInt, _uShort1, _uShort1, _bArr[0], _bArr[1], _bArr[2], _bArr[3], _bArr[4], _bArr[5], _bArr[6], _bArr[7]); } @@ -192,7 +192,7 @@ public void Guid_Empty_Test8() /// /// Guid guid = Guid.Empty; - Byte[] _bArr = guid.ToByteArray(); + byte[] _bArr = guid.ToByteArray(); for (int i = 0; i < 16; i++) { Assert.AreEqual(_bArr[i], (byte)0); @@ -211,26 +211,21 @@ public void Guid_CompareTo_Test9() Guid guid1 = Guid.Empty; // Verifing any instance of Guid, regardless of its value, is greater than null Assert.AreEqual(guid1.CompareTo(null), 1); - Byte[] _bArr = new Byte[16]; + byte[] _bArr = new byte[16]; + // Creating a Guid with all bytes zero Guid guid2 = new Guid(_bArr); Assert.AreEqual(guid1.CompareTo(guid2), 0); + Guid guid3 = new Guid(0x4dff36b5, 0x9dde, 0x4f76, 0x9a, 0x2a, 0x96, 0x43, 0x50, 0x47, 0x06, 0x3d); - if (guid3.CompareTo(guid1) <= 0) - { - throw new Exception("Expected : " + guid3.ToString() + " is greater than " + guid1.ToString()); - } + Assert.IsTrue(guid3.CompareTo(guid1) > 0, "Expected : " + guid3.ToString() + " is greater than " + guid1.ToString()); + Guid guid4 = new Guid(0x4dff36b5, 0x9dde, 0x4f76, 0x9a, 0x2a, 0x96, 0x43, 0x50, 0x47, 0x06, 0x3d); Assert.AreEqual(guid4.CompareTo(guid3), 0); + Guid guid5 = new Guid(0x4dff36b5, 0x9dde, 0x4f76, 0x9a, 0x2a, 0x96, 0x43, 0x50, 0x47, 0x06, 0x3e); - if (guid5.CompareTo(guid4) <= 0) - { - throw new Exception("Expected : " + guid5.ToString() + " is greater than " + guid4.ToString()); - } - if (guid4.CompareTo(guid5) >= 0) - { - throw new Exception("Expected : " + guid4.ToString() + " is less than " + guid5.ToString()); - } + Assert.IsTrue(guid5.CompareTo(guid4) > 0, "Expected : " + guid5.ToString() + " is greater than " + guid4.ToString()); + Assert.IsTrue(guid4.CompareTo(guid5) < 0, "Expected : " + guid4.ToString() + " is less than " + guid5.ToString()); } [TestMethod] @@ -242,25 +237,34 @@ public void Guid_ToString_Test10() /// /// - String[] strArr1 = new String[] { "00000000-0000-0000-0000-000000000000", + string[] strArr1 = new string[] + { + "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000", "4dff36b5-9dde-4f76-9a2a-96435047063d", - "ffffffff-ffff-ffff-ffff-ffffffffffff"}; + "ffffffff-ffff-ffff-ffff-ffffffffffff" + }; + Guid guid1 = Guid.Empty; - Byte[] _byteArr1 = new Byte[16]; + byte[] _byteArr1 = new byte[16]; Guid guid2 = new Guid(_byteArr1); Guid guid3 = new Guid(0x4dff36b5, 0x9dde, 0x4f76, 0x9a, 0x2a, 0x96, 0x43, 0x50, 0x47, 0x06, 0x3d); - Byte[] _byteArr2 = new Byte[16]; + + byte[] _byteArr2 = new byte[16]; + for (int i = 0; i < _byteArr2.Length; i++) { - _byteArr2[i] = Byte.MaxValue; + _byteArr2[i] = byte.MaxValue; } + Guid guid4 = new Guid(_byteArr2); - String[] strArr2 = new String[] { guid1.ToString(), guid2.ToString(), guid3.ToString(), guid4.ToString() }; + + string[] strArr2 = new string[] { guid1.ToString(), guid2.ToString(), guid3.ToString(), guid4.ToString() }; + for (int i = 0; i < strArr1.Length; i++) { OutputHelper.WriteLine(strArr1[i]); - Assert.AreEqual(String.Compare(strArr1[i], strArr2[i]), 0); + Assert.AreEqual(string.Compare(strArr1[i], strArr2[i]), 0); } } @@ -280,11 +284,14 @@ public void Guid_Equals_Test11() Guid[] gArr1 = new Guid[] { guid11, guid12, guid13 }; // Creating Guids with 16 bytes constructor - Byte[] _bArr1 = new Byte[16]; + byte[] _bArr1 = new byte[16]; + Guid guid21 = new Guid(_bArr1); - Byte[] _bArr2 = new Byte[] { 181, 54, 255, 77, 222, 157, 118, 79, 154, 42, 150, 67, 80, 71, 6, 61 }; + byte[] _bArr2 = new byte[] { 181, 54, 255, 77, 222, 157, 118, 79, 154, 42, 150, 67, 80, 71, 6, 61 }; + Guid guid22 = new Guid(_bArr2); - Byte[] _bArr3 = new Byte[] { 255, 255, 255, 127, 255, 127, 255, 127, 255, 255, 255, 255, 255, 255, 255, 255 }; + byte[] _bArr3 = new byte[] { 255, 255, 255, 127, 255, 127, 255, 127, 255, 255, 255, 255, 255, 255, 255, 255 }; + Guid guid23 = new Guid(_bArr3); Guid[] gArr2 = new Guid[] { guid21, guid22, guid23 }; @@ -315,5 +322,120 @@ public void Guid_Equals_Test11() } } + [DataRow("00000000-0000-0000-0000-000000000000")] + [DataRow("4dff36b5-9dde-4f76-9a2a-96435047063d")] + [DataRow("ffffffff-ffff-ffff-ffff-ffffffffffff")] + [DataRow("a8a110d5-fc49-43c5-bf46-802db8f843ff")] + [DataRow("44332211-6655-8877-9900-aabbccddeeff")] + [DataRow("11223344-5566-7788-9900-aabbccddeeff")] + [TestMethod] + public void Ctor_FromString_Test00(string guidString) + { + /// + /// 1. Creates a Guid from a string + /// 2. Verifies the Guid is created correctly + /// + + Guid guid = new Guid(guidString); + + Assert.AreEqual(guidString, guid.ToString()); + } + [DataRow("{00000000-0000-0000-0000-000000000000}")] + [DataRow("{4dff36b5-9dde-4f76-9a2a-96435047063d}")] + [DataRow("{ffffffff-ffff-ffff-ffff-ffffffffffff}")] + [DataRow("{a8a110d5-fc49-43c5-bf46-802db8f843ff}")] + [DataRow("{44332211-6655-8877-9900-aabbccddeeff}")] + [DataRow("{11223344-5566-7788-9900-aabbccddeeff}")] + [TestMethod] + public void Ctor_FromString_Test01(string guidString) + { + /// + /// 1. Creates a Guid from a string + /// 2. Verifies the Guid is created correctly + /// + + Guid guid = new Guid(guidString); + + Assert.AreEqual(guidString, $"{{{guid.ToString()}}}"); + } + + [TestMethod] + public void Ctor_FromString_Test02() + { + Guid testGuid = new Guid("a8a110d5-fc49-43c5-bf46-802db8f843ff"); + Guid fullGuid = new Guid(uint.MaxValue, ushort.MaxValue, ushort.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); + + Assert.AreEqual(new Guid("ffffffff-ffff-ffff-ffff-ffffffffffff"), fullGuid); + Assert.AreEqual((new Guid("a8a110d5-fc49-43c5-bf46-802db8f843ff")).ToString(), testGuid.ToString()); + } + + [TestMethod] + public void Guid_ByteArray_Test() + { + object[][] testData = new object[][] + { + new object[] { Guid.Empty, new byte[16] }, + new object[] { new Guid("44332211-6655-8877-9900-aabbccddeeff"), new byte[] { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF } }, + new object[] { new Guid("11223344-5566-7788-9900-aabbccddeeff"), new byte[] { 0x44, 0x33, 0x22, 0x11, 0x66, 0x55, 0x88, 0x77, 0x99, 0x00, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF } }, + new object[] { new Guid("a8a110d5-fc49-43c5-bf46-802db8f843ff"), new byte[] { 0xd5, 0x10, 0xa1, 0xa8, 0x49, 0xfc, 0xc5, 0x43, 0xbf, 0x46, 0x80, 0x2d, 0xb8, 0xf8, 0x43, 0xff } } + }; + + foreach (object[] item in testData) + { + Guid guid = new Guid((byte[])item[1]); + + OutputHelper.WriteLine($"Actual: {guid}"); + OutputHelper.WriteLine($"Expected: {item[0]}"); + + Assert.AreEqual(item[0], guid); + } + } + + [TestMethod] + public void Ctor_NullByteArray_ThrowsArgumentNullException() + { + Assert.ThrowsException( + typeof(ArgumentNullException), + () => new Guid((byte[])null)); + } + + [DataRow(15)] + [DataRow(17)] + [TestMethod] + public void Ctor_InvalidLengthByteArray_ThrowsArgumentException(int length) + { + Assert.ThrowsException( + typeof(ArgumentException), + () => new Guid(new byte[length])); + } + + [TestMethod] + public void Ctor_UInt_UShort_UShort_Byte_Byte_Byte_Byte_Byte_Byte_Byte_Byte() + { + Guid guid = new Guid(0xa8a110d5, 0xfc49, 0x43c5, 0xbf, 0x46, 0x80, 0x2d, 0xb8, 0xf8, 0x43, 0xff); + Assert.AreEqual(new Guid("a8a110d5-fc49-43c5-bf46-802db8f843ff"), guid); + } + + [TestMethod] + public void NewGuid() + { + Guid guid1 = Guid.NewGuid(); + + Assert.AreNotEqual(Guid.Empty, guid1); + Assert.IsTrue((guid1.ToByteArray()[7] & 0xF0) == 0x40); + + Guid guid2 = Guid.NewGuid(); + + Assert.AreNotEqual(guid1, guid2); + Assert.IsTrue((guid2.ToByteArray()[7] & 0xF0) == 0x40); + } + + [TestMethod] + public void ToByteArray() + { + byte[] myGuidAsArray = new Guid("a8a110d5-fc49-43c5-bf46-802db8f843ff").ToByteArray(); + + CollectionAssert.AreEqual(new byte[] { 0xd5, 0x10, 0xa1, 0xa8, 0x49, 0xfc, 0xc5, 0x43, 0xbf, 0x46, 0x80, 0x2d, 0xb8, 0xf8, 0x43, 0xff }, myGuidAsArray); + } } } diff --git a/nanoFramework.CoreLibrary/System/Guid.cs b/nanoFramework.CoreLibrary/System/Guid.cs index 77794b6..6ccee86 100644 --- a/nanoFramework.CoreLibrary/System/Guid.cs +++ b/nanoFramework.CoreLibrary/System/Guid.cs @@ -135,12 +135,6 @@ public Guid(byte[] b) /// /// A string that contains a GUID in one of the following formats ("d" represents a hexadecimal digit whose case is ignored): /// - /// 32 contiguous hexadecimal digits: - /// - /// dddddddddddddddddddddddddddddddd - /// - /// -or- - /// /// Groups of 8, 4, 4, 4, and 12 hexadecimal digits with hyphens between the groups. The entire GUID can optionally be enclosed in matching braces or parentheses: /// /// dddddddd-dddd-dddd-dddd-dddddddddddd @@ -149,18 +143,6 @@ public Guid(byte[] b) /// /// {dddddddd-dddd-dddd-dddd-dddddddddddd} /// - /// -or- - /// - /// (dddddddd-dddd-dddd-dddd-dddddddddddd) - /// - /// -or- - /// - /// Groups of 8, 4, and 4 hexadecimal digits, and a subset of eight groups of 2 hexadecimal digits, with each group prefixed by "0x" or "0X", and separated by commas. The entire GUID, as well as the subset, is enclosed in matching braces: - /// - /// {0xdddddddd, 0xdddd, 0xdddd,{0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd,0xdd}} - /// - /// All braces, commas, and "0x" prefixes are required. All embedded spaces are ignored. All leading zeros in a group are ignored. - /// /// The hexadecimal digits shown in a group are the maximum number of meaningful hexadecimal digits that can appear in that group. You can specify from 1 to the number of hexadecimal digits shown for a group. The specified digits are assumed to be the low-order digits of the group. /// /// is ." @@ -172,11 +154,13 @@ public Guid(string g) #pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one if (!TryParseGuidWithDashes( g, - out this)) + out Guid result)) { throw new ArgumentException(); } #pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one + + this = result; } /// @@ -342,78 +326,114 @@ private static char HexToChar(int a) } /// - /// Creates a new based on the value in the string. The value is made up - /// of hex digits speared by the dash ("-"). The string may begin and end with - /// brackets ("{", "}"). - /// - /// The string must be of the form dddddddd-dddd-dddd-dddd-dddddddddddd. where - /// d is a hex digit. (That is 8 hex digits, followed by 4, then 4, then 4, - /// then 12) such as: "CA761232-ED42-11CE-BACD-00AA0057B223" + /// Converts the string representation of a GUID to the equivalent structure. /// - /// A string containing the GUID to convert. + /// A string containing the GUID to convert. /// When this method returns, contains the parsed value. If the method returns , result contains a valid . If the method returns , result equals . /// if the parse operation was successful; otherwise, . + /// + /// The .NET nanoFramework implementation of this method only supports 'D' and 'N' format specifiers. + /// + [Obsolete("This will be renamed to TryParse in a future version.")] public static bool TryParseGuidWithDashes( - string guidString, + string input, out Guid result) { int startPos = 0; - int temp; - long templ; - result = Empty; - // check to see that it's the proper length - if (guidString[0] == '{') + // because this is a struct we can't assign the Empty directly to result, + // otherwise it will overwrite the _data field of the struct, as this is a shallow copy + result = new Guid(Empty.ToByteArray()); + + // Check for optional surrounding braces + if (input[0] == '{') { - if (guidString.Length != 38 || guidString[37] != '}') + if (input.Length != 38 || input[37] != '}') { return false; } + startPos = 1; } - else if (guidString.Length != 36) + else if (input.Length != 36) { return false; } - if (guidString[8 + startPos] != '-' || - guidString[13 + startPos] != '-' || - guidString[18 + startPos] != '-' || - guidString[23 + startPos] != '-') + // Verify hyphen positions + if (input[8 + startPos] != '-' || + input[13 + startPos] != '-' || + input[18 + startPos] != '-' || + input[23 + startPos] != '-') { return false; } int currentPos = startPos; + try { - result._data[0] = (int)HexStringToLong(guidString, ref currentPos, 8); + // Data1: 8 hex digits + result._data[0] = (int)HexStringToLong(input, ref currentPos, 8); - // Increment past the '-' + // Skip dash ++currentPos; - result._data[1] = (ushort)HexStringToLong(guidString, ref currentPos, 4) - | ((ushort)HexStringToLong(guidString, ref currentPos, 4) << 16); + // Data2: 4 hex digits + ushort data2 = (ushort)HexStringToLong(input, ref currentPos, 4); - // Increment past the '-' + // Skip dash ++currentPos; - temp = (int)HexStringToLong(guidString, ref currentPos, 4); + // Data3: 4 hex digits + ushort data3 = (ushort)HexStringToLong(input, ref currentPos, 4); - // Increment past the '-' + // These values are already in big‑endian order as in the string + // They must be stored directly (without swapping) in the internal little‑endian representation + result._data[1] = data2 | (data3 << 16); + + // Skip dash ++currentPos; - templ = HexStringToLong(guidString, ref currentPos, 12); + // Data4 – first part: 4 hex digits (2 bytes) + ushort group4 = (ushort)HexStringToLong(input, ref currentPos, 4); + + // Skip dash + ++currentPos; + + // Data4 – second part: 12 hex digits (6 bytes) + long group5 = HexStringToLong(input, ref currentPos, 12); + + // For Data4, we need to convert from big‑endian to little‑endian + // Swap the 2-byte group + ushort group4_le = (ushort)(((group4 & 0xFF) << 8) | (group4 >> 8)); + + // Split the 6-byte group into its high 2 bytes and low 4 bytes + // high 16 bits (big‑endian) + ushort group5High = (ushort)(group5 >> 32); + + // low 32 bits (big‑endian) + uint group5Low = (uint)(group5 & 0xFFFFFFFF); + + // Swap bytes for each + ushort group5High_le = (ushort)(((group5High & 0xFF) << 8) | (group5High >> 8)); + uint group5Low_le = ((group5Low & 0xFF) << 24) | + (((group5Low >> 8) & 0xFF) << 16) | + (((group5Low >> 16) & 0xFF) << 8) | + ((group5Low >> 24) & 0xFF); + + // Combine the converted Data4 parts into _data[2] and _data[3] + // _data[2]: lower 16 bits from swapped group4, upper 16 bits from swapped group5High + result._data[2] = group4_le | (group5High_le << 16); + + // _data[3]: swapped group5Low + result._data[3] = (int)group5Low_le; } catch { - result = Empty; return false; } - result._data[2] = temp | (int)(templ >> 32) << 16; - result._data[3] = (int)templ; - return true; }