From fc1a64b77c8a901c048e7b973f204d3b7c9870e3 Mon Sep 17 00:00:00 2001 From: John Date: Wed, 15 Jan 2020 14:44:59 +0000 Subject: [PATCH] Fix undefined layout behaviour, and remove pinning: Uint256 and Uint160 (#1387) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix undefined layout behaviour, and remove pinning * Update UInt256.cs * Update UInt256.cs * add StructLayout * set pack=1 * Explicit * Remove UIntBase * Revert constructor * add unsafe * Comment * Comment Uint256 * Removing comments from value 1 * Removing comments from value1 Co-authored-by: Luchuan Co-authored-by: Vitor Nazário Coelho Co-authored-by: Erik Zhang Co-authored-by: Shargon --- src/neo/UInt160.cs | 25 ++++++--- src/neo/UInt256.cs | 27 ++++++--- src/neo/UIntBase.cs | 90 ------------------------------ tests/neo.UnitTests/UT_UIntBase.cs | 49 ---------------- 4 files changed, 35 insertions(+), 156 deletions(-) delete mode 100644 src/neo/UIntBase.cs delete mode 100644 tests/neo.UnitTests/UT_UIntBase.cs diff --git a/src/neo/UInt160.cs b/src/neo/UInt160.cs index 69c2f85ed1..a7cb19fc2a 100644 --- a/src/neo/UInt160.cs +++ b/src/neo/UInt160.cs @@ -1,22 +1,26 @@ +using Neo.IO; using System; using System.Globalization; using System.IO; +using System.Runtime.InteropServices; namespace Neo { /// /// This class stores a 160 bit unsigned int, represented as a 20-byte little-endian byte array + /// It is composed by ulong(64) + ulong(64) + uint(32) = UInt160(160) /// - public class UInt160 : UIntBase, IComparable, IEquatable + [StructLayout(LayoutKind.Explicit, Size = 20)] + public class UInt160 : IComparable, IEquatable, ISerializable { public const int Length = 20; public static readonly UInt160 Zero = new UInt160(); - private ulong value1; - private ulong value2; - private uint value3; + [FieldOffset(0)] private ulong value1; + [FieldOffset(8)] private ulong value2; + [FieldOffset(16)] private uint value3; - public override int Size => Length; + public int Size => Length; public UInt160() { @@ -44,7 +48,7 @@ public int CompareTo(UInt160 other) return value1.CompareTo(other.value1); } - public override void Deserialize(BinaryReader reader) + public void Deserialize(BinaryReader reader) { value1 = reader.ReadUInt64(); value2 = reader.ReadUInt64(); @@ -80,7 +84,7 @@ public override int GetHashCode() /// Method Parse receives a big-endian hex string and stores as a UInt160 little-endian 20-bytes array /// Example: Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01") should create UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 /// - public static new UInt160 Parse(string value) + public static UInt160 Parse(string value) { if (value == null) throw new ArgumentNullException(); @@ -93,13 +97,18 @@ public override int GetHashCode() return new UInt160(data); } - public override void Serialize(BinaryWriter writer) + public void Serialize(BinaryWriter writer) { writer.Write(value1); writer.Write(value2); writer.Write(value3); } + public override string ToString() + { + return "0x" + this.ToArray().ToHexString(reverse: true); + } + /// /// Method TryParse tries to parse a big-endian hex string and store it as a UInt160 little-endian 20-bytes array /// Example: TryParse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01", result) should create result UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 diff --git a/src/neo/UInt256.cs b/src/neo/UInt256.cs index bee5743c3c..d0c01f4f51 100644 --- a/src/neo/UInt256.cs +++ b/src/neo/UInt256.cs @@ -1,23 +1,27 @@ +using Neo.IO; using System; using System.Globalization; using System.IO; +using System.Runtime.InteropServices; namespace Neo { /// /// This class stores a 256 bit unsigned int, represented as a 32-byte little-endian byte array + /// Composed by ulong(64) + ulong(64) + ulong(64) + ulong(64) = UInt256(256) /// - public class UInt256 : UIntBase, IComparable, IEquatable + [StructLayout(LayoutKind.Explicit, Size = 32)] + public class UInt256 : IComparable, IEquatable, ISerializable { public const int Length = 32; public static readonly UInt256 Zero = new UInt256(); - private ulong value1; - private ulong value2; - private ulong value3; - private ulong value4; + [FieldOffset(0)] private ulong value1; + [FieldOffset(8)] private ulong value2; + [FieldOffset(16)] private ulong value3; + [FieldOffset(24)] private ulong value4; - public override int Size => Length; + public int Size => Length; public UInt256() { @@ -47,7 +51,7 @@ public int CompareTo(UInt256 other) return value1.CompareTo(other.value1); } - public override void Deserialize(BinaryReader reader) + public void Deserialize(BinaryReader reader) { value1 = reader.ReadUInt64(); value2 = reader.ReadUInt64(); @@ -85,7 +89,7 @@ public override int GetHashCode() /// Method Parse receives a big-endian hex string and stores as a UInt256 little-endian 32-bytes array /// Example: Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01") should create UInt256 01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 /// - public static new UInt256 Parse(string s) + public static UInt256 Parse(string s) { if (s == null) throw new ArgumentNullException(); @@ -98,7 +102,7 @@ public override int GetHashCode() return new UInt256(data); } - public override void Serialize(BinaryWriter writer) + public void Serialize(BinaryWriter writer) { writer.Write(value1); writer.Write(value2); @@ -106,6 +110,11 @@ public override void Serialize(BinaryWriter writer) writer.Write(value4); } + public override string ToString() + { + return "0x" + this.ToArray().ToHexString(reverse: true); + } + /// /// Method TryParse tries to parse a big-endian hex string and store it as a UInt256 little-endian 32-bytes array /// Example: TryParse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff01", result) should create result UInt256 01ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 diff --git a/src/neo/UIntBase.cs b/src/neo/UIntBase.cs deleted file mode 100644 index b78ccd3023..0000000000 --- a/src/neo/UIntBase.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Neo.IO; -using System; -using System.IO; - -namespace Neo -{ - /// - /// Base class for little-endian unsigned integers. Two classes inherit from this: UInt160 and UInt256. - /// Only basic comparison/serialization are proposed for these classes. For arithmetic purposes, use BigInteger class. - /// - public abstract class UIntBase : ISerializable - { - /// - /// Number of bytes of the unsigned int. - /// Currently, inherited classes use 20-bytes (UInt160) or 32-bytes (UInt256) - /// - public abstract int Size { get; } - - public abstract void Deserialize(BinaryReader reader); - - public abstract override bool Equals(object obj); - - /// - /// Method GetHashCode returns a 32-bit int representing a hash code, composed of the first 4 bytes. - /// - public abstract override int GetHashCode(); - - /// - /// Method Parse receives a big-endian hex string and stores as a UInt160 or UInt256 little-endian byte array - /// Example: Parse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01") should create UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 - /// - public static UIntBase Parse(string s) - { - if (s.Length == 40 || s.Length == 42) - return UInt160.Parse(s); - else if (s.Length == 64 || s.Length == 66) - return UInt256.Parse(s); - else - throw new FormatException(); - } - - public abstract void Serialize(BinaryWriter writer); - - /// - /// Method ToString returns a big-endian string starting by "0x" representing the little-endian unsigned int - /// Example: if this is storing 20-bytes 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4, ToString() should return "0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01" - /// - public override string ToString() - { - return "0x" + this.ToArray().ToHexString(reverse: true); - } - - /// - /// Method TryParse tries to parse a big-endian hex string and stores it as a UInt160 or UInt256 little-endian bytes array - /// Example: TryParse("0xa400ff00ff00ff00ff00ff00ff00ff00ff00ff01", result) should create result UInt160 01ff00ff00ff00ff00ff00ff00ff00ff00ff00a4 - /// - public static bool TryParse(string s, out T result) where T : UIntBase - { - int size; - if (typeof(T) == typeof(UInt160)) - size = 20; - else if (typeof(T) == typeof(UInt256)) - size = 32; - else if (s.Length == 40 || s.Length == 42) - size = 20; - else if (s.Length == 64 || s.Length == 66) - size = 32; - else - size = 0; - if (size == 20) - { - if (UInt160.TryParse(s, out UInt160 r)) - { - result = (T)(UIntBase)r; - return true; - } - } - else if (size == 32) - { - if (UInt256.TryParse(s, out UInt256 r)) - { - result = (T)(UIntBase)r; - return true; - } - } - result = null; - return false; - } - } -} diff --git a/tests/neo.UnitTests/UT_UIntBase.cs b/tests/neo.UnitTests/UT_UIntBase.cs deleted file mode 100644 index 5f02b92c25..0000000000 --- a/tests/neo.UnitTests/UT_UIntBase.cs +++ /dev/null @@ -1,49 +0,0 @@ -using FluentAssertions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; - -namespace Neo.UnitTests.IO -{ - [TestClass] - public class UT_UIntBase - { - [TestMethod] - public void TestParse() - { - UInt160 uInt1601 = (UInt160)UIntBase.Parse("0x0000000000000000000000000000000000000000"); - UInt256 uInt2561 = (UInt256)UIntBase.Parse("0x0000000000000000000000000000000000000000000000000000000000000000"); - UInt160 uInt1602 = (UInt160)UIntBase.Parse("0000000000000000000000000000000000000000"); - UInt256 uInt2562 = (UInt256)UIntBase.Parse("0000000000000000000000000000000000000000000000000000000000000000"); - Assert.AreEqual(UInt160.Zero, uInt1601); - Assert.AreEqual(UInt256.Zero, uInt2561); - Assert.AreEqual(UInt160.Zero, uInt1602); - Assert.AreEqual(UInt256.Zero, uInt2562); - Action action = () => UIntBase.Parse("0000000"); - action.Should().Throw(); - } - - [TestMethod] - public void TestTryParse() - { - UInt160 uInt160 = new UInt160(); - Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000", out uInt160)); - Assert.AreEqual(UInt160.Zero, uInt160); - Assert.AreEqual(false, UIntBase.TryParse("0x00000000000000000000000000000000000000", out uInt160)); - UInt256 uInt256 = new UInt256(); - Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000000000000000000000000000", out uInt256)); - Assert.AreEqual(UInt256.Zero, uInt256); - Assert.AreEqual(false, UIntBase.TryParse("0x00000000000000000000000000000000000000000000000000000000000000", out uInt256)); - UIntBase uIntBase = new UInt160(); - Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000", out uIntBase)); - Assert.AreEqual(UInt160.Zero, uIntBase); - Assert.AreEqual(true, UIntBase.TryParse("0000000000000000000000000000000000000000", out uIntBase)); - Assert.AreEqual(UInt160.Zero, uIntBase); - uIntBase = new UInt256(); - Assert.AreEqual(true, UIntBase.TryParse("0x0000000000000000000000000000000000000000000000000000000000000000", out uIntBase)); - Assert.AreEqual(UInt256.Zero, uIntBase); - Assert.AreEqual(true, UIntBase.TryParse("0000000000000000000000000000000000000000000000000000000000000000", out uIntBase)); - Assert.AreEqual(UInt256.Zero, uIntBase); - Assert.AreEqual(false, UIntBase.TryParse("00000000000000000000000000000000000000000000000000000000000000", out uIntBase)); - } - } -}