From 46b795b82b188facb7045c0e9f708c0b7ccf0f96 Mon Sep 17 00:00:00 2001 From: Gnbrkm41 Date: Thu, 16 Jan 2020 02:32:10 +0900 Subject: [PATCH 1/2] Make System.Guid readonly --- .../System.Private.CoreLib/src/System/Guid.cs | 88 +++++++++++-------- .../System.Runtime/ref/System.Runtime.cs | 4 +- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 65370c0556e628..bd69b6ab7dec8d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -16,21 +16,21 @@ namespace System [Serializable] [NonVersionable] // This only applies to field layout [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] - public partial struct Guid : IFormattable, IComparable, IComparable, IEquatable, ISpanFormattable + public readonly partial struct Guid : IFormattable, IComparable, IComparable, IEquatable, ISpanFormattable { public static readonly Guid Empty = default; - private int _a; // Do not rename (binary serialization) - private short _b; // Do not rename (binary serialization) - private short _c; // Do not rename (binary serialization) - private byte _d; // Do not rename (binary serialization) - private byte _e; // Do not rename (binary serialization) - private byte _f; // Do not rename (binary serialization) - private byte _g; // Do not rename (binary serialization) - private byte _h; // Do not rename (binary serialization) - private byte _i; // Do not rename (binary serialization) - private byte _j; // Do not rename (binary serialization) - private byte _k; // Do not rename (binary serialization) + private readonly int _a; // Do not rename (binary serialization) + private readonly short _b; // Do not rename (binary serialization) + private readonly short _c; // Do not rename (binary serialization) + private readonly byte _d; // Do not rename (binary serialization) + private readonly byte _e; // Do not rename (binary serialization) + private readonly byte _f; // Do not rename (binary serialization) + private readonly byte _g; // Do not rename (binary serialization) + private readonly byte _h; // Do not rename (binary serialization) + private readonly byte _i; // Do not rename (binary serialization) + private readonly byte _j; // Do not rename (binary serialization) + private readonly byte _k; // Do not rename (binary serialization) // Creates a new guid from an array of bytes. public Guid(byte[] b) : @@ -135,7 +135,7 @@ private enum GuidParseThrowStyle : byte private struct GuidResult { private readonly GuidParseThrowStyle _throwStyle; - internal Guid _parsedGuid; + internal MutableGuid _parsedGuid; internal GuidResult(GuidParseThrowStyle canThrow) : this() { @@ -163,6 +163,24 @@ internal void SetFailure(bool overflow, string failureMessageID) } } + // A struct that's identical in its structure to System.Guid. + // This is to work around not being able to mutate the fields once the type is marked readonly by reinterpret-casting. + [StructLayout(LayoutKind.Sequential)] + private struct MutableGuid + { + public int _a; + public short _b; + public short _c; + public byte _d; + public byte _e; + public byte _f; + public byte _g; + public byte _h; + public byte _i; + public byte _j; + public byte _k; + } + // Creates a new guid 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 ("{", "}"). @@ -181,7 +199,7 @@ public Guid(string g) bool success = TryParseGuid(g, ref result); Debug.Assert(success, "GuidParseThrowStyle.All means throw on all failures"); - this = result._parsedGuid; + this = Unsafe.As(ref result._parsedGuid); } public static Guid Parse(string input) => @@ -193,7 +211,7 @@ public static Guid Parse(ReadOnlySpan input) bool success = TryParseGuid(input, ref result); Debug.Assert(success, "GuidParseThrowStyle.AllButOverflow means throw on all failures"); - return result._parsedGuid; + return Unsafe.As(ref result._parsedGuid); } public static bool TryParse(string? input, out Guid result) @@ -212,7 +230,7 @@ public static bool TryParse(ReadOnlySpan input, out Guid result) var parseResult = new GuidResult(GuidParseThrowStyle.None); if (TryParseGuid(input, ref parseResult)) { - result = parseResult._parsedGuid; + result = Unsafe.As(ref parseResult._parsedGuid); return true; } else @@ -248,7 +266,7 @@ public static Guid ParseExact(ReadOnlySpan input, ReadOnlySpan forma _ => throw new FormatException(SR.Format_InvalidGuidFormatSpecification), }; Debug.Assert(success, "GuidParseThrowStyle.AllButOverflow means throw on all failures"); - return result._parsedGuid; + return Unsafe.As(ref result._parsedGuid); } public static bool TryParseExact(string? input, string? format, out Guid result) @@ -299,7 +317,7 @@ public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan fo if (success) { - result = parseResult._parsedGuid; + result = Unsafe.As(ref parseResult._parsedGuid); return true; } else @@ -372,7 +390,7 @@ private static bool TryParseExactD(ReadOnlySpan guidString, ref GuidResult return false; } - ref Guid g = ref result._parsedGuid; + ref MutableGuid g = ref result._parsedGuid; uint uintTmp; if (TryParseHex(guidString.Slice(0, 8), out Unsafe.As(ref g._a)) && // _a @@ -422,7 +440,7 @@ private static bool TryParseExactN(ReadOnlySpan guidString, ref GuidResult return false; } - ref Guid g = ref result._parsedGuid; + ref MutableGuid g = ref result._parsedGuid; uint uintTmp; if (uint.TryParse(guidString.Slice(0, 8), NumberStyles.AllowHexSpecifier, null, out Unsafe.As(ref g._a)) && // _a @@ -732,7 +750,7 @@ public byte[] ToByteArray() var g = new byte[16]; if (BitConverter.IsLittleEndian) { - MemoryMarshal.TryWrite(g, ref this); + MemoryMarshal.TryWrite(g, ref Unsafe.AsRef(this)); } else { @@ -746,7 +764,7 @@ public bool TryWriteBytes(Span destination) { if (BitConverter.IsLittleEndian) { - return MemoryMarshal.TryWrite(destination, ref this); + return MemoryMarshal.TryWrite(destination, ref Unsafe.AsRef(this)); } // slower path for BigEndian @@ -778,7 +796,7 @@ public bool TryWriteBytes(Span destination) public override int GetHashCode() { // Simply XOR all the bits of the GUID 32 bits at a time. - return _a ^ Unsafe.Add(ref _a, 1) ^ Unsafe.Add(ref _a, 2) ^ Unsafe.Add(ref _a, 3); + return _a ^ Unsafe.Add(ref Unsafe.AsRef(_a), 1) ^ Unsafe.Add(ref Unsafe.AsRef(_a), 2) ^ Unsafe.Add(ref Unsafe.AsRef(_a), 3); } // Returns true if and only if the guid represented @@ -793,18 +811,18 @@ public override bool Equals(object? o) // Now compare each of the elements return g._a == _a && - Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && - Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && - Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); + Unsafe.Add(ref Unsafe.AsRef(g._a), 1) == Unsafe.Add(ref Unsafe.AsRef(_a), 1) && + Unsafe.Add(ref Unsafe.AsRef(g._a), 2) == Unsafe.Add(ref Unsafe.AsRef(_a), 2) && + Unsafe.Add(ref Unsafe.AsRef(g._a), 3) == Unsafe.Add(ref Unsafe.AsRef(_a), 3); } public bool Equals(Guid g) { // Now compare each of the elements return g._a == _a && - Unsafe.Add(ref g._a, 1) == Unsafe.Add(ref _a, 1) && - Unsafe.Add(ref g._a, 2) == Unsafe.Add(ref _a, 2) && - Unsafe.Add(ref g._a, 3) == Unsafe.Add(ref _a, 3); + Unsafe.Add(ref Unsafe.AsRef(g._a), 1) == Unsafe.Add(ref Unsafe.AsRef(_a), 1) && + Unsafe.Add(ref Unsafe.AsRef(g._a), 2) == Unsafe.Add(ref Unsafe.AsRef(_a), 2) && + Unsafe.Add(ref Unsafe.AsRef(g._a), 3) == Unsafe.Add(ref Unsafe.AsRef(_a), 3); } private int GetResult(uint me, uint them) => me < them ? -1 : 1; @@ -941,16 +959,16 @@ public int CompareTo(Guid value) public static bool operator ==(Guid a, Guid b) => a._a == b._a && - Unsafe.Add(ref a._a, 1) == Unsafe.Add(ref b._a, 1) && - Unsafe.Add(ref a._a, 2) == Unsafe.Add(ref b._a, 2) && - Unsafe.Add(ref a._a, 3) == Unsafe.Add(ref b._a, 3); + Unsafe.Add(ref Unsafe.AsRef(a._a), 1) == Unsafe.Add(ref Unsafe.AsRef(b._a), 1) && + Unsafe.Add(ref Unsafe.AsRef(a._a), 2) == Unsafe.Add(ref Unsafe.AsRef(b._a), 2) && + Unsafe.Add(ref Unsafe.AsRef(a._a), 3) == Unsafe.Add(ref Unsafe.AsRef(b._a), 3); public static bool operator !=(Guid a, Guid b) => // Now compare each of the elements a._a != b._a || - Unsafe.Add(ref a._a, 1) != Unsafe.Add(ref b._a, 1) || - Unsafe.Add(ref a._a, 2) != Unsafe.Add(ref b._a, 2) || - Unsafe.Add(ref a._a, 3) != Unsafe.Add(ref b._a, 3); + Unsafe.Add(ref Unsafe.AsRef(a._a), 1) != Unsafe.Add(ref Unsafe.AsRef(b._a), 1) || + Unsafe.Add(ref Unsafe.AsRef(a._a), 2) != Unsafe.Add(ref Unsafe.AsRef(b._a), 2) || + Unsafe.Add(ref Unsafe.AsRef(a._a), 3) != Unsafe.Add(ref Unsafe.AsRef(b._a), 3); public string ToString(string? format) { diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 72ea52bb43893c..df45a474e90ba2 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -1341,9 +1341,9 @@ public partial class GopherStyleUriParser : System.UriParser { public GopherStyleUriParser() { } } - public partial struct Guid : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable + public readonly partial struct Guid : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable { - private int _dummyPrimitive; + private readonly int _dummyPrimitive; public static readonly System.Guid Empty; public Guid(byte[] b) { throw null; } public Guid(int a, short b, short c, byte d, byte e, byte f, byte g, byte h, byte i, byte j, byte k) { throw null; } From eab1bca51a80338e67310a2e7f216986a465c83f Mon Sep 17 00:00:00 2001 From: Gnbrkm41 Date: Fri, 17 Jan 2020 00:31:50 +0900 Subject: [PATCH 2/2] Update Unix's NewGuid for the readonly change --- .../System.Private.CoreLib/src/System/Guid.Unix.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.Unix.cs index 5a1364d032d2e2..a4a2b9889bbb22 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.Unix.cs @@ -4,15 +4,16 @@ using System.Diagnostics; using System.Runtime.InteropServices; +using Internal.Runtime.CompilerServices; namespace System { - public partial struct Guid + public readonly partial struct Guid { // This will create a new random guid based on the https://www.ietf.org/rfc/rfc4122.txt public static unsafe Guid NewGuid() { - Guid g; + MutableGuid g; Interop.GetRandomBytes((byte*)&g, sizeof(Guid)); const ushort VersionMask = 0xF000; @@ -31,7 +32,7 @@ public static unsafe Guid NewGuid() g._d = (byte)((g._d & ~ClockSeqHiAndReservedMask) | ClockSeqHiAndReservedValue); } - return g; + return Unsafe.As(ref g); } } }