From b91ed340226d1252d6e2c8b74b6a37084cb3375e Mon Sep 17 00:00:00 2001 From: dcyonce Date: Wed, 23 Jul 2025 08:28:37 -0500 Subject: [PATCH 1/5] Fixed null references in multiple methods Added parameterless constructor Added CompareTo(Guid) Added Equals(Guid) Added == and != overloads --- nanoFramework.CoreLibrary/System/Guid.cs | 87 ++++++++++++++++++------ 1 file changed, 67 insertions(+), 20 deletions(-) diff --git a/nanoFramework.CoreLibrary/System/Guid.cs b/nanoFramework.CoreLibrary/System/Guid.cs index 75e2f06..b2d9eff 100644 --- a/nanoFramework.CoreLibrary/System/Guid.cs +++ b/nanoFramework.CoreLibrary/System/Guid.cs @@ -18,6 +18,11 @@ public struct Guid /// public static readonly Guid Empty = new Guid(new byte[16]); + public Guid() + { + _data = new int[4]; // All zeros + } + /// /// Initializes a new instance of the structure by using the specified integers and bytes. /// @@ -103,7 +108,7 @@ public Guid(byte[] b) #pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one if (b.Length != 16) { - throw new ArgumentException(); + throw new ArgumentException("Byte array must be 16 bytes long.", nameof(b)); } #pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one @@ -160,29 +165,28 @@ public Guid(string g) /// is not a . public int CompareTo(object value) { - if (value == null) - { - return 1; - } - -#pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one - if (value is not Guid) - { - throw new ArgumentException(); - } -#pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one + if (value == null) return 1; + if (value is not Guid other) throw new ArgumentException("Object must be of type Guid."); - int[] other = ((Guid)value)._data; - other ??= new int[4]; + return CompareTo(other); + } + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. + /// + /// An object to compare with this instance. + /// A value that indicates the relative order of the objects being compared. + public int CompareTo(Guid other) + { + _data ??= new int[4]; + other._data ??= new int[4]; for (int i = 0; i < 4; i++) { - if (_data[i] != other[i]) + if (_data[i] != other._data[i]) { - return _data[i] - other[i]; + return _data[i] - other._data[i]; } } - return 0; } @@ -201,6 +205,8 @@ public int CompareTo(object value) /// public byte[] ToByteArray() { + _data ??= new int[4]; // Initialize if null (treat as Empty) + byte[] buffer = new byte[16]; int index = 0; @@ -289,10 +295,19 @@ public override bool Equals(object o) return false; } - int[] other = ((Guid)o)._data; - other ??= new int[4]; + return o is Guid other && Equals(other); + } - return (_data[0] == other[0]) && (_data[1] == other[1]) && (_data[2] == other[2]) && (_data[3] == other[3]); + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// true if the current object is equal to the other parameter; otherwise, false. + public bool Equals(Guid other) + { + _data ??= new int[4]; + other._data ??= new int[4]; + return (_data[0] == other._data[0]) && (_data[1] == other._data[1]) && (_data[2] == other._data[2]) && (_data[3] == other._data[3]); } /// @@ -301,6 +316,7 @@ public override bool Equals(object o) /// The hash code for this instance. public override int GetHashCode() { + _data ??= new int[4]; // Initialize if null (treat as Empty) return _data[0] ^ _data[1] ^ _data[2] ^ _data[3]; } @@ -434,6 +450,8 @@ public static bool TryParseGuidWithDashes( return true; } + + /// /// Converts a hex sub-string to a long, while incrementing the parsePos. /// @@ -447,5 +465,34 @@ private static long HexStringToLong(string str, ref int parsePos, int requiredLe parsePos += requiredLength; return result; } + + /// + /// Determines whether two specified instances of are equal. + /// + /// The first to compare. + /// The second to compare. + /// if equals ; otherwise, . + public static bool operator ==(Guid a, Guid b) + { + // Defensive null handling, though _data should always be initialized + a._data ??= new int[4]; + b._data ??= new int[4]; + + return (a._data[0] == b._data[0]) && + (a._data[1] == b._data[1]) && + (a._data[2] == b._data[2]) && + (a._data[3] == b._data[3]); + } + + /// + /// Determines whether two specified instances of are not equal. + /// + /// The first to compare. + /// The second to compare. + /// if does not equal ; otherwise, . + public static bool operator !=(Guid a, Guid b) + { + return !(a == b); + } } } From af7c79b031c2ad0da01fd853f0e8dfc4b513eba1 Mon Sep 17 00:00:00 2001 From: dcyonce Date: Fri, 15 Aug 2025 05:39:18 -0500 Subject: [PATCH 2/5] Formatting and comment changes requested by Jose --- nanoFramework.CoreLibrary/System/Guid.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/nanoFramework.CoreLibrary/System/Guid.cs b/nanoFramework.CoreLibrary/System/Guid.cs index b2d9eff..3e4dd41 100644 --- a/nanoFramework.CoreLibrary/System/Guid.cs +++ b/nanoFramework.CoreLibrary/System/Guid.cs @@ -108,7 +108,7 @@ public Guid(byte[] b) #pragma warning disable S3928 // Parameter names used into ArgumentException constructors should match an existing one if (b.Length != 16) { - throw new ArgumentException("Byte array must be 16 bytes long.", nameof(b)); + throw new ArgumentException(); } #pragma warning restore S3928 // Parameter names used into ArgumentException constructors should match an existing one @@ -165,8 +165,15 @@ public Guid(string g) /// is not a . public int CompareTo(object value) { - if (value == null) return 1; - if (value is not Guid other) throw new ArgumentException("Object must be of type Guid."); + if (value == null) + { + return 1; + + } + if (value is not Guid other) + { + throw new ArgumentException(); + } return CompareTo(other); } @@ -205,7 +212,8 @@ public int CompareTo(Guid other) /// public byte[] ToByteArray() { - _data ??= new int[4]; // Initialize if null (treat as Empty) + // Initialize if null (treat as Empty) + _data ??= new int[4]; byte[] buffer = new byte[16]; @@ -450,8 +458,6 @@ public static bool TryParseGuidWithDashes( return true; } - - /// /// Converts a hex sub-string to a long, while incrementing the parsePos. /// From 97491549edaa7176d5ada9d28aab4a9cfce574f5 Mon Sep 17 00:00:00 2001 From: dcyonce Date: Fri, 15 Aug 2025 10:44:21 -0500 Subject: [PATCH 3/5] Moved comments per Jose --- nanoFramework.CoreLibrary/System/Guid.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nanoFramework.CoreLibrary/System/Guid.cs b/nanoFramework.CoreLibrary/System/Guid.cs index 3e4dd41..03e2bfe 100644 --- a/nanoFramework.CoreLibrary/System/Guid.cs +++ b/nanoFramework.CoreLibrary/System/Guid.cs @@ -20,7 +20,8 @@ public struct Guid public Guid() { - _data = new int[4]; // All zeros + // All zeros + _data = new int[4]; } /// @@ -324,7 +325,8 @@ public bool Equals(Guid other) /// The hash code for this instance. public override int GetHashCode() { - _data ??= new int[4]; // Initialize if null (treat as Empty) + // Initialize if null (treat as Empty) + _data ??= new int[4]; return _data[0] ^ _data[1] ^ _data[2] ^ _data[3]; } From 8d04c7c1caf582d76d882d9fc59e10d30b91192b Mon Sep 17 00:00:00 2001 From: dcyonce Date: Fri, 15 Aug 2025 11:31:04 -0500 Subject: [PATCH 4/5] Addded UnitTestGuid.cs --- Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj | 3 +- Tests/NFUnitTestTypes/UnitTestGuid.cs | 140 +++++++++++++++++++ 2 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 Tests/NFUnitTestTypes/UnitTestGuid.cs diff --git a/Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj b/Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj index 7ede25b..4b9ae62 100644 --- a/Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj +++ b/Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj @@ -24,6 +24,7 @@ + @@ -47,4 +48,4 @@ - + \ No newline at end of file diff --git a/Tests/NFUnitTestTypes/UnitTestGuid.cs b/Tests/NFUnitTestTypes/UnitTestGuid.cs new file mode 100644 index 0000000..f9ed9c3 --- /dev/null +++ b/Tests/NFUnitTestTypes/UnitTestGuid.cs @@ -0,0 +1,140 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using nanoFramework.TestFramework; + +namespace NFUnitTestTypes +{ + [TestClass] + class UnitTestGuid + { + [TestMethod] + public void Guid_Compare_To_Empty() + { + var empty = Guid.Empty; + var notEmpty = Guid.NewGuid(); + Assert.IsFalse(empty == notEmpty); + } + + [TestMethod] + public void Guid_Empty_IsAllZeros() + { + var empty = Guid.Empty; + var bytes = empty.ToByteArray(); + foreach (var b in bytes) + { + Assert.AreEqual((byte)0, b); + } + } + + [TestMethod] + public void Guid_Constructor_ByteArray_RoundTrip() + { + var original = Guid.NewGuid(); + var bytes = original.ToByteArray(); + var roundTrip = new Guid(bytes); + Assert.AreEqual(original, roundTrip); + } + + [TestMethod] + public void Guid_Equals_And_Operator() + { + var g1 = Guid.NewGuid(); + var g2 = new Guid(g1.ToByteArray()); + Assert.IsTrue(g1.Equals(g2)); + Assert.IsTrue(g1 == g2); + Assert.IsFalse(g1 != g2); + } + + [TestMethod] + public void Guid_NotEquals_And_Operator() + { + var g1 = Guid.NewGuid(); + var g2 = Guid.NewGuid(); + Assert.IsFalse(g1.Equals(g2)); + Assert.IsFalse(g1 == g2); + Assert.IsTrue(g1 != g2); + } + + [TestMethod] + public void Guid_ToString_And_Parse() + { + var g1 = Guid.NewGuid(); + var str = g1.ToString(); + var g2 = new Guid(str); + Assert.AreEqual(g1, g2); + } + + [TestMethod] + public void Guid_GetHashCode_Consistent() + { + var g1 = Guid.NewGuid(); + var g2 = new Guid(g1.ToByteArray()); + Assert.AreEqual(g1.GetHashCode(), g2.GetHashCode()); + } + + [TestMethod] + public void Guid_CompareTo_Object_And_Self() + { + var g1 = Guid.NewGuid(); + var g2 = new Guid(g1.ToByteArray()); + Assert.AreEqual(0, g1.CompareTo(g2)); + Assert.AreEqual(0, g1.CompareTo((object)g2)); + Assert.AreEqual(1, g1.CompareTo(null)); + } + + [TestMethod] + public void Guid_CompareTo_InvalidType_Throws() + { + var g1 = Guid.NewGuid(); + Assert.ThrowsException(typeof(ArgumentException), () => + { + g1.CompareTo("not a guid"); + }); + } + + [TestMethod] + public void Guid_TryParseGuidWithDashes_Valid() + { + var g1 = Guid.NewGuid(); + var str = g1.ToString(); + bool parsed = Guid.TryParseGuidWithDashes(str, out var g2); + Assert.IsTrue(parsed); + Assert.AreEqual(g1, g2); + } + + [TestMethod] + public void Guid_TryParseGuidWithDashes_Invalid() + { + bool parsed = Guid.TryParseGuidWithDashes("invalid-guid", out var g2); + Assert.IsFalse(parsed); + Assert.AreEqual(Guid.Empty, g2); + } + + [TestMethod] + public void Guid_Constructor_String_WithBraces() + { + var g1 = Guid.NewGuid(); + var str = "{" + g1.ToString() + "}"; + var g2 = new Guid(str); + Assert.AreEqual(g1, g2); + } + + [TestMethod] + public void Guid_Constructor_String_Invalid_Throws() + { + Assert.ThrowsException(typeof(ArgumentException), () => + { + var g = new Guid("invalid-guid"); + }); + } + + [TestMethod] + public void Guid_GetHashCode_Empty() + { + var empty = Guid.Empty; + Assert.AreEqual(0, empty.GetHashCode()); + } + } +} From 62858ddb762483d37031aaa7c8a817570cad5d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Mon, 1 Sep 2025 12:50:45 +0100 Subject: [PATCH 5/5] New API requires bumping native version --- nanoFramework.CoreLibrary/System/AssemblyInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nanoFramework.CoreLibrary/System/AssemblyInfo.cs b/nanoFramework.CoreLibrary/System/AssemblyInfo.cs index 1d1984e..49310cc 100644 --- a/nanoFramework.CoreLibrary/System/AssemblyInfo.cs +++ b/nanoFramework.CoreLibrary/System/AssemblyInfo.cs @@ -10,4 +10,4 @@ [assembly: AssemblyProduct(".NET nanoFramework mscorlib")] [assembly: AssemblyCopyright("Copyright (c) .NET Foundation and Contributors")] -[assembly: AssemblyNativeVersion("100.5.0.24")] +[assembly: AssemblyNativeVersion("100.5.0.25")]