From df15fe079b3c11eb112fda147bed402405629e84 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 18 Jul 2024 07:28:00 -0400 Subject: [PATCH 01/11] Fixed `UInt160` and expanded class --- src/Neo/UInt160.cs | 84 ++++++++++++++++++------------- tests/Neo.UnitTests/UT_UInt160.cs | 24 +++++++-- 2 files changed, 68 insertions(+), 40 deletions(-) diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index 8dfd6bf70c..f3a88a9340 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -13,6 +13,7 @@ using System; using System.Globalization; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Neo @@ -31,68 +32,72 @@ public class UInt160 : IComparable, IEquatable, ISerializable /// /// Represents 0. /// - public static readonly UInt160 Zero = new(); + public static UInt160 Zero => new(); - [FieldOffset(0)] private ulong value1; - [FieldOffset(8)] private ulong value2; - [FieldOffset(16)] private uint value3; + [FieldOffset(0)] private ulong _value1; + [FieldOffset(8)] private ulong _value2; + [FieldOffset(16)] private uint _value3; public int Size => Length; /// /// Initializes a new instance of the class. /// - public UInt160() - { - } + public UInt160() { } /// /// Initializes a new instance of the class. /// /// The value of the . - public unsafe UInt160(ReadOnlySpan value) + public UInt160(ReadOnlySpan value) { - if (value.Length != Length) throw new FormatException(); - fixed (ulong* p = &value1) - { - Span dst = new(p, Length); - value[..Length].CopyTo(dst); - } + if (value.Length != Length) + throw new FormatException(); + + var bytes = value.ToArray(); + _value1 = Unsafe.As(ref bytes[0]); + _value2 = Unsafe.As(ref bytes[8]); + _value3 = Unsafe.As(ref bytes[16]); } public int CompareTo(UInt160 other) { - int result = value3.CompareTo(other.value3); + var result = _value3.CompareTo(other._value3); if (result != 0) return result; - result = value2.CompareTo(other.value2); + result = _value2.CompareTo(other._value2); if (result != 0) return result; - return value1.CompareTo(other.value1); + return _value1.CompareTo(other._value1); } public void Deserialize(ref MemoryReader reader) { - value1 = reader.ReadUInt64(); - value2 = reader.ReadUInt64(); - value3 = reader.ReadUInt32(); + _value1 = reader.ReadUInt64(); + _value2 = reader.ReadUInt64(); + _value3 = reader.ReadUInt32(); } public override bool Equals(object obj) { - if (ReferenceEquals(obj, this)) return true; - return Equals(obj as UInt160); + if (obj == null) + return false; + + var other = obj as UInt160; + if (other == null) + return false; + return Equals(other); } public bool Equals(UInt160 other) { - if (other is null) return false; - return value1 == other.value1 - && value2 == other.value2 - && value3 == other.value3; + if (other == null) return false; + return _value1 == other._value1 && + _value2 == other._value2 && + _value3 == other._value3; } public override int GetHashCode() { - return (int)value1; + return HashCode.Combine(_value1, _value2, _value3); } /// @@ -109,9 +114,9 @@ public static UInt160 Parse(string value) public void Serialize(BinaryWriter writer) { - writer.Write(value1); - writer.Write(value2); - writer.Write(value3); + writer.Write(_value1); + writer.Write(_value2); + writer.Write(_value3); } public override string ToString() @@ -139,9 +144,9 @@ public static bool TryParse(string s, out UInt160 result) result = null; return false; } - byte[] data = new byte[Length]; - for (int i = 0; i < Length; i++) - if (!byte.TryParse(s.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier, null, out data[Length - i - 1])) + var data = new byte[Length]; + for (var i = 0; i < Length; i++) + if (!byte.TryParse(s.AsSpan(i * 2, 2), NumberStyles.AllowHexSpecifier, null, out data[Length - i - 1])) { result = null; return false; @@ -150,16 +155,25 @@ public static bool TryParse(string s, out UInt160 result) return true; } + public static implicit operator UInt160(string s) + { + return Parse(s); + } + public static bool operator ==(UInt160 left, UInt160 right) { if (ReferenceEquals(left, right)) return true; - if (left is null || right is null) return false; + if (left is null || right is null) + return Equals(left, right); return left.Equals(right); } public static bool operator !=(UInt160 left, UInt160 right) { - return !(left == right); + if (ReferenceEquals(left, right)) return false; + if (left is null || right is null) + return !Equals(left, right); + return !left.Equals(right); } public static bool operator >(UInt160 left, UInt160 right) diff --git a/tests/Neo.UnitTests/UT_UInt160.cs b/tests/Neo.UnitTests/UT_UInt160.cs index 4799a80d19..173129acdd 100644 --- a/tests/Neo.UnitTests/UT_UInt160.cs +++ b/tests/Neo.UnitTests/UT_UInt160.cs @@ -9,11 +9,10 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. -#pragma warning disable CS1718 - using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; +using System.Security.Cryptography; namespace Neo.UnitTests.IO { @@ -40,6 +39,13 @@ public void TestGernerator2() Assert.IsNotNull(uInt160); } + [TestMethod] + public void TestGernerator3() + { + UInt160 uInt160 = "0x0000000000000000000000000000000000000000"; + Assert.IsNotNull(uInt160); + } + [TestMethod] public void TestCompareTo() { @@ -57,9 +63,13 @@ public void TestEquals() byte[] temp = new byte[20]; temp[19] = 0x01; UInt160 result = new UInt160(temp); - Assert.AreEqual(true, UInt160.Zero.Equals(UInt160.Zero)); - Assert.AreEqual(false, UInt160.Zero.Equals(result)); - Assert.AreEqual(false, result.Equals(null)); + Assert.IsTrue(UInt160.Zero.Equals(UInt160.Zero)); + Assert.IsFalse(UInt160.Zero.Equals(result)); + Assert.IsFalse(result.Equals(null)); + Assert.IsTrue(UInt160.Zero == UInt160.Zero); + Assert.IsFalse(UInt160.Zero != UInt160.Zero); + Assert.IsTrue(UInt160.Zero == "0x0000000000000000000000000000000000000000"); + Assert.IsFalse(UInt160.Zero == "0x0000000000000000000000000000000000000001"); } [TestMethod] @@ -92,24 +102,28 @@ public void TestTryParse() public void TestOperatorLarger() { Assert.AreEqual(false, UInt160.Zero > UInt160.Zero); + Assert.IsFalse(UInt160.Zero > "0x0000000000000000000000000000000000000000"); } [TestMethod] public void TestOperatorLargerAndEqual() { Assert.AreEqual(true, UInt160.Zero >= UInt160.Zero); + Assert.IsTrue(UInt160.Zero >= "0x0000000000000000000000000000000000000000"); } [TestMethod] public void TestOperatorSmaller() { Assert.AreEqual(false, UInt160.Zero < UInt160.Zero); + Assert.IsFalse(UInt160.Zero < "0x0000000000000000000000000000000000000000"); } [TestMethod] public void TestOperatorSmallerAndEqual() { Assert.AreEqual(true, UInt160.Zero <= UInt160.Zero); + Assert.IsTrue(UInt160.Zero >= "0x0000000000000000000000000000000000000000"); } } } From acd1ef4302f2fe6ca00efd3470141db30e9ca5a2 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 18 Jul 2024 08:29:36 -0400 Subject: [PATCH 02/11] Cleaned up code for `TryParse` --- src/Neo/UInt160.cs | 48 ++++++++++++++++--------------- tests/Neo.UnitTests/UT_UInt160.cs | 5 ++-- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index f3a88a9340..b74a552e2c 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -11,8 +11,8 @@ using Neo.IO; using System; -using System.Globalization; using System.IO; +using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -121,38 +121,35 @@ public void Serialize(BinaryWriter writer) public override string ToString() { - return "0x" + this.ToArray().ToHexString(reverse: true); + return "0x" + this.ToArray().ToHexString(); } /// /// Parses an from the specified . /// - /// An represented by a . + /// An represented by a . /// The parsed . /// if an is successfully parsed; otherwise, . - public static bool TryParse(string s, out UInt160 result) + public static bool TryParse(string str, out UInt160 result) { - if (s == null) - { - result = null; - return false; - } - if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) - s = s[2..]; - if (s.Length != Length * 2) + result = null; + + if (string.IsNullOrWhiteSpace(str)) return false; + + if (str.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) { - result = null; - return false; + str = str[2..]; + + if (str.Length != Length * 2) return false; + + var data = Enumerable.Range(0, Length) + .Select(s => Convert.ToByte(str.Substring(s * 2, 2), 16)) + .ToArray(); + + result = new(data); + return true; } - var data = new byte[Length]; - for (var i = 0; i < Length; i++) - if (!byte.TryParse(s.AsSpan(i * 2, 2), NumberStyles.AllowHexSpecifier, null, out data[Length - i - 1])) - { - result = null; - return false; - } - result = new UInt160(data); - return true; + return false; } public static implicit operator UInt160(string s) @@ -160,6 +157,11 @@ public static implicit operator UInt160(string s) return Parse(s); } + public static implicit operator UInt160(byte[] b) + { + return new UInt160(b); + } + public static bool operator ==(UInt160 left, UInt160 right) { if (ReferenceEquals(left, right)) return true; diff --git a/tests/Neo.UnitTests/UT_UInt160.cs b/tests/Neo.UnitTests/UT_UInt160.cs index 173129acdd..3502c7cf90 100644 --- a/tests/Neo.UnitTests/UT_UInt160.cs +++ b/tests/Neo.UnitTests/UT_UInt160.cs @@ -35,15 +35,16 @@ public void TestGernerator1() [TestMethod] public void TestGernerator2() { - UInt160 uInt160 = new UInt160(new byte[20]); + UInt160 uInt160 = new byte[20]; Assert.IsNotNull(uInt160); } [TestMethod] public void TestGernerator3() { - UInt160 uInt160 = "0x0000000000000000000000000000000000000000"; + UInt160 uInt160 = "0xff00000000000000000000000000000000000001"; Assert.IsNotNull(uInt160); + Assert.IsTrue(uInt160.ToString() == "0xff00000000000000000000000000000000000001"); } [TestMethod] From 47ac12e47db140b987eec12354c519e5ffddeda0 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 18 Jul 2024 08:33:28 -0400 Subject: [PATCH 03/11] Fixed `TryParse` --- src/Neo/UInt160.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index b74a552e2c..3765a38e10 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -137,11 +137,12 @@ public static bool TryParse(string str, out UInt160 result) if (string.IsNullOrWhiteSpace(str)) return false; if (str.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) - { str = str[2..]; - if (str.Length != Length * 2) return false; + if (str.Length != Length * 2) return false; + try + { var data = Enumerable.Range(0, Length) .Select(s => Convert.ToByte(str.Substring(s * 2, 2), 16)) .ToArray(); @@ -149,7 +150,10 @@ public static bool TryParse(string str, out UInt160 result) result = new(data); return true; } - return false; + catch + { + return false; + } } public static implicit operator UInt160(string s) From 28c9af02cb347083ff01143c8eeb95a913d6a2a3 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 18 Jul 2024 08:55:43 -0400 Subject: [PATCH 04/11] Fixed small bug with `TryParse` --- src/Neo/UInt160.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index 3765a38e10..fb0c132f6e 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -121,7 +121,7 @@ public void Serialize(BinaryWriter writer) public override string ToString() { - return "0x" + this.ToArray().ToHexString(); + return "0x" + this.ToArray().ToHexString(reverse: true); } /// @@ -145,6 +145,7 @@ public static bool TryParse(string str, out UInt160 result) { var data = Enumerable.Range(0, Length) .Select(s => Convert.ToByte(str.Substring(s * 2, 2), 16)) + .Reverse() .ToArray(); result = new(data); From 950555b9091917ab073fc5c6810207119e86d893 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Tue, 23 Jul 2024 04:46:08 -0400 Subject: [PATCH 05/11] Change `UInt160.Zero` to `static readonly` --- src/Neo/UInt160.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index fb0c132f6e..d058bbe5d0 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -32,7 +32,7 @@ public class UInt160 : IComparable, IEquatable, ISerializable /// /// Represents 0. /// - public static UInt160 Zero => new(); + public readonly static UInt160 Zero = new(); [FieldOffset(0)] private ulong _value1; [FieldOffset(8)] private ulong _value2; From 8ccafb4e6ea5c2938cb4be3f3104c5324e0b9db3 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Thu, 1 Aug 2024 10:51:17 +0800 Subject: [PATCH 06/11] benchmark UInt160 --- .../Neo.Benchmarks/Benchmarks.UInt160.cs | 167 ++++++++++++++++ .../Neo.Benchmarks/Neo.Benchmarks.csproj | 1 + benchmarks/Neo.Benchmarks/OldUInt160.cs | 184 ++++++++++++++++++ benchmarks/Neo.Benchmarks/Program.cs | 3 +- 4 files changed, 354 insertions(+), 1 deletion(-) create mode 100644 benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs create mode 100644 benchmarks/Neo.Benchmarks/OldUInt160.cs diff --git a/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs b/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs new file mode 100644 index 0000000000..ea746733aa --- /dev/null +++ b/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs @@ -0,0 +1,167 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// Benchmarks.UInt160.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using BenchmarkDotNet.Attributes; + +namespace Neo.Benchmark; + +public class Benchmarks_UInt160 +{ + [Benchmark] + public void TestOldUInt160Gernerator1() + { + OldUInt160 uInt160 = new OldUInt160(); + } + + [Benchmark] + public void TestOldUInt160Gernerator2() + { + OldUInt160 uInt160 = new OldUInt160(new byte[20]); + } + + [Benchmark] + public void TestOldUInt160CompareTo() + { + byte[] temp = new byte[20]; + temp[19] = 0x01; + OldUInt160 result = new OldUInt160(temp); + OldUInt160.Zero.CompareTo(OldUInt160.Zero); + OldUInt160.Zero.CompareTo(result); + result.CompareTo(OldUInt160.Zero); + } + + [Benchmark] + public void TestOldUInt160Equals() + { + byte[] temp = new byte[20]; + temp[19] = 0x01; + OldUInt160 result = new OldUInt160(temp); + OldUInt160.Zero.Equals(OldUInt160.Zero); + OldUInt160.Zero.Equals(result); + result.Equals(null); + } + + [Benchmark] + public void TestOldUInt160Parse() + { + OldUInt160 result = OldUInt160.Parse("0x0000000000000000000000000000000000000000"); + OldUInt160 result1 = OldUInt160.Parse("0000000000000000000000000000000000000000"); + } + + [Benchmark] + public void TestOldUInt160TryParse() + { + OldUInt160.TryParse(null, out _); + OldUInt160.TryParse("0x0000000000000000000000000000000000000000", out var temp); + OldUInt160.TryParse("0x1230000000000000000000000000000000000000", out temp); + OldUInt160.TryParse("000000000000000000000000000000000000000", out _); + OldUInt160.TryParse("0xKK00000000000000000000000000000000000000", out _); + } + + [Benchmark] + public void TestOldUInt160OperatorLarger() + { + _ = OldUInt160.Zero > OldUInt160.Zero; + } + + [Benchmark] + public void TestOldUInt160OperatorLargerAndEqual() + { + _ = OldUInt160.Zero >= OldUInt160.Zero; + } + + [Benchmark] + public void TestOldUInt160OperatorSmaller() + { + _ = OldUInt160.Zero < OldUInt160.Zero; + } + + [Benchmark] + public void TestOldUInt160OperatorSmallerAndEqual() + { + _ = OldUInt160.Zero <= OldUInt160.Zero; + } + + [Benchmark] + public void TestGernerator1() + { + UInt160 uInt160 = new UInt160(); + } + + [Benchmark] + public void TestGernerator2() + { + UInt160 uInt160 = new UInt160(new byte[20]); + } + + [Benchmark] + public void TestCompareTo() + { + byte[] temp = new byte[20]; + temp[19] = 0x01; + UInt160 result = new UInt160(temp); + UInt160.Zero.CompareTo(UInt160.Zero); + UInt160.Zero.CompareTo(result); + result.CompareTo(UInt160.Zero); + } + + [Benchmark] + public void TestEquals() + { + byte[] temp = new byte[20]; + temp[19] = 0x01; + UInt160 result = new UInt160(temp); + UInt160.Zero.Equals(UInt160.Zero); + UInt160.Zero.Equals(result); + result.Equals(null); + } + + [Benchmark] + public void TestParse() + { + UInt160 result = UInt160.Parse("0x0000000000000000000000000000000000000000"); + UInt160 result1 = UInt160.Parse("0000000000000000000000000000000000000000"); + } + + [Benchmark] + public void TestTryParse() + { + UInt160.TryParse(null, out _); + UInt160.TryParse("0x0000000000000000000000000000000000000000", out var temp); + UInt160.TryParse("0x1230000000000000000000000000000000000000", out temp); + UInt160.TryParse("000000000000000000000000000000000000000", out _); + UInt160.TryParse("0xKK00000000000000000000000000000000000000", out _); + } + + [Benchmark] + public void TestOperatorLarger() + { + _ = UInt160.Zero > UInt160.Zero; + } + + [Benchmark] + public void TestOperatorLargerAndEqual() + { + _ = UInt160.Zero >= UInt160.Zero; + } + + [Benchmark] + public void TestOperatorSmaller() + { + _ = UInt160.Zero < UInt160.Zero; + } + + [Benchmark] + public void TestOperatorSmallerAndEqual() + { + _ = UInt160.Zero <= UInt160.Zero; + } +} diff --git a/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj b/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj index c4b1a35da9..a59fc6e728 100644 --- a/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj +++ b/benchmarks/Neo.Benchmarks/Neo.Benchmarks.csproj @@ -5,6 +5,7 @@ net8.0 Neo enable + true diff --git a/benchmarks/Neo.Benchmarks/OldUInt160.cs b/benchmarks/Neo.Benchmarks/OldUInt160.cs new file mode 100644 index 0000000000..965ac985ea --- /dev/null +++ b/benchmarks/Neo.Benchmarks/OldUInt160.cs @@ -0,0 +1,184 @@ +// Copyright (C) 2015-2024 The Neo Project. +// +// OldUInt160.cs file belongs to the neo project and is free +// software distributed under the MIT software license, see the +// accompanying file LICENSE in the main directory of the +// repository or http://www.opensource.org/licenses/mit-license.php +// for more details. +// +// Redistribution and use in source and binary forms with or without +// modifications are permitted. + +using Neo.Extensions; +using Neo.IO; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace Neo +{ + /// + /// Represents a 160-bit unsigned integer. + /// + [StructLayout(LayoutKind.Explicit, Size = 20)] + public class OldUInt160 : IComparable, IEquatable, ISerializable + { + /// + /// The length of values. + /// + public const int Length = 20; + + /// + /// Represents 0. + /// + public static readonly OldUInt160 Zero = new(); + + [FieldOffset(0)] private ulong value1; + [FieldOffset(8)] private ulong value2; + [FieldOffset(16)] private uint value3; + + public int Size => Length; + + /// + /// Initializes a new instance of the class. + /// + public OldUInt160() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The value of the . + public unsafe OldUInt160(ReadOnlySpan value) + { + if (value.Length != Length) throw new FormatException(); + fixed (ulong* p = &value1) + { + Span dst = new(p, Length); + value[..Length].CopyTo(dst); + } + } + + public int CompareTo(OldUInt160 other) + { + int result = value3.CompareTo(other.value3); + if (result != 0) return result; + result = value2.CompareTo(other.value2); + if (result != 0) return result; + return value1.CompareTo(other.value1); + } + + public void Deserialize(ref MemoryReader reader) + { + value1 = reader.ReadUInt64(); + value2 = reader.ReadUInt64(); + value3 = reader.ReadUInt32(); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(obj, this)) return true; + return Equals(obj as OldUInt160); + } + + public bool Equals(OldUInt160 other) + { + if (other is null) return false; + return value1 == other.value1 + && value2 == other.value2 + && value3 == other.value3; + } + + public override int GetHashCode() + { + return (int)value1; + } + + /// + /// Parses an from the specified . + /// + /// An represented by a . + /// The parsed . + /// is not in the correct format. + public static OldUInt160 Parse(string value) + { + if (!TryParse(value, out var result)) throw new FormatException(); + return result; + } + + 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); + } + + /// + /// Parses an from the specified . + /// + /// An represented by a . + /// The parsed . + /// if an is successfully parsed; otherwise, . + public static bool TryParse(string s, out OldUInt160 result) + { + if (s == null) + { + result = null; + return false; + } + if (s.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) + s = s[2..]; + if (s.Length != Length * 2) + { + result = null; + return false; + } + byte[] data = new byte[Length]; + for (int i = 0; i < Length; i++) + if (!byte.TryParse(s.Substring(i * 2, 2), NumberStyles.AllowHexSpecifier, null, out data[Length - i - 1])) + { + result = null; + return false; + } + result = new OldUInt160(data); + return true; + } + + public static bool operator ==(OldUInt160 left, OldUInt160 right) + { + if (ReferenceEquals(left, right)) return true; + if (left is null || right is null) return false; + return left.Equals(right); + } + + public static bool operator !=(OldUInt160 left, OldUInt160 right) + { + return !(left == right); + } + + public static bool operator >(OldUInt160 left, OldUInt160 right) + { + return left.CompareTo(right) > 0; + } + + public static bool operator >=(OldUInt160 left, OldUInt160 right) + { + return left.CompareTo(right) >= 0; + } + + public static bool operator <(OldUInt160 left, OldUInt160 right) + { + return left.CompareTo(right) < 0; + } + + public static bool operator <=(OldUInt160 left, OldUInt160 right) + { + return left.CompareTo(right) <= 0; + } + } +} diff --git a/benchmarks/Neo.Benchmarks/Program.cs b/benchmarks/Neo.Benchmarks/Program.cs index e39ef23ff7..c44b76f839 100644 --- a/benchmarks/Neo.Benchmarks/Program.cs +++ b/benchmarks/Neo.Benchmarks/Program.cs @@ -12,4 +12,5 @@ using BenchmarkDotNet.Running; using Neo.Benchmark; -BenchmarkRunner.Run(); +// BenchmarkRunner.Run(); +BenchmarkRunner.Run(); From c1b0fc708b7bffbd480983e71056f5b72d65ee74 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 1 Aug 2024 16:08:52 -0400 Subject: [PATCH 07/11] Fix benchmark --- .../Neo.Benchmarks/Benchmarks.UInt160.cs | 47 ++++++++----------- src/Neo/UInt160.cs | 3 +- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs b/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs index ea746733aa..48f9fd779b 100644 --- a/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs +++ b/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs @@ -15,6 +15,9 @@ namespace Neo.Benchmark; public class Benchmarks_UInt160 { + static readonly OldUInt160 s_oldUInt160 = new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + static readonly UInt160 s_newUInt160 = new([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]); + [Benchmark] public void TestOldUInt160Gernerator1() { @@ -30,23 +33,17 @@ public void TestOldUInt160Gernerator2() [Benchmark] public void TestOldUInt160CompareTo() { - byte[] temp = new byte[20]; - temp[19] = 0x01; - OldUInt160 result = new OldUInt160(temp); OldUInt160.Zero.CompareTo(OldUInt160.Zero); - OldUInt160.Zero.CompareTo(result); - result.CompareTo(OldUInt160.Zero); + OldUInt160.Zero.CompareTo(s_oldUInt160); + s_oldUInt160.CompareTo(OldUInt160.Zero); } [Benchmark] public void TestOldUInt160Equals() { - byte[] temp = new byte[20]; - temp[19] = 0x01; - OldUInt160 result = new OldUInt160(temp); OldUInt160.Zero.Equals(OldUInt160.Zero); - OldUInt160.Zero.Equals(result); - result.Equals(null); + OldUInt160.Zero.Equals(s_oldUInt160); + s_oldUInt160.Equals(null); } [Benchmark] @@ -69,25 +66,25 @@ public void TestOldUInt160TryParse() [Benchmark] public void TestOldUInt160OperatorLarger() { - _ = OldUInt160.Zero > OldUInt160.Zero; + _ = s_oldUInt160 > OldUInt160.Zero; } [Benchmark] public void TestOldUInt160OperatorLargerAndEqual() { - _ = OldUInt160.Zero >= OldUInt160.Zero; + _ = s_oldUInt160 >= OldUInt160.Zero; } [Benchmark] public void TestOldUInt160OperatorSmaller() { - _ = OldUInt160.Zero < OldUInt160.Zero; + _ = s_oldUInt160 < OldUInt160.Zero; } [Benchmark] public void TestOldUInt160OperatorSmallerAndEqual() { - _ = OldUInt160.Zero <= OldUInt160.Zero; + _ = s_oldUInt160 <= OldUInt160.Zero; } [Benchmark] @@ -105,23 +102,17 @@ public void TestGernerator2() [Benchmark] public void TestCompareTo() { - byte[] temp = new byte[20]; - temp[19] = 0x01; - UInt160 result = new UInt160(temp); UInt160.Zero.CompareTo(UInt160.Zero); - UInt160.Zero.CompareTo(result); - result.CompareTo(UInt160.Zero); + UInt160.Zero.CompareTo(s_newUInt160); + s_newUInt160.CompareTo(UInt160.Zero); } [Benchmark] public void TestEquals() { - byte[] temp = new byte[20]; - temp[19] = 0x01; - UInt160 result = new UInt160(temp); UInt160.Zero.Equals(UInt160.Zero); - UInt160.Zero.Equals(result); - result.Equals(null); + UInt160.Zero.Equals(s_newUInt160); + s_newUInt160.Equals(null); } [Benchmark] @@ -144,24 +135,24 @@ public void TestTryParse() [Benchmark] public void TestOperatorLarger() { - _ = UInt160.Zero > UInt160.Zero; + _ = s_newUInt160 > UInt160.Zero; } [Benchmark] public void TestOperatorLargerAndEqual() { - _ = UInt160.Zero >= UInt160.Zero; + _ = s_newUInt160 >= UInt160.Zero; } [Benchmark] public void TestOperatorSmaller() { - _ = UInt160.Zero < UInt160.Zero; + _ = s_newUInt160 < UInt160.Zero; } [Benchmark] public void TestOperatorSmallerAndEqual() { - _ = UInt160.Zero <= UInt160.Zero; + _ = s_newUInt160 <= UInt160.Zero; } } diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index 4705d10b25..80e7fb9672 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -12,6 +12,7 @@ using Neo.Extensions; using Neo.IO; using System; +using System.Globalization; using System.IO; using System.Linq; using System.Runtime.CompilerServices; @@ -145,7 +146,7 @@ public static bool TryParse(string str, out UInt160 result) try { var data = Enumerable.Range(0, Length) - .Select(s => Convert.ToByte(str.Substring(s * 2, 2), 16)) + .Select(s => byte.Parse(str.Substring(s * 2, 2), NumberStyles.HexNumber)) .Reverse() .ToArray(); From b7209e25ad6d99984a8397bfb675330fc900eca8 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 1 Aug 2024 16:43:06 -0400 Subject: [PATCH 08/11] Fixed bugs and added features for `UInt160` class --- .../Neo.Benchmarks/Benchmarks.UInt160.cs | 18 +++++------- src/Neo/UInt160.cs | 29 ++++++++++--------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs b/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs index 48f9fd779b..37fa701ff9 100644 --- a/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs +++ b/benchmarks/Neo.Benchmarks/Benchmarks.UInt160.cs @@ -21,13 +21,13 @@ public class Benchmarks_UInt160 [Benchmark] public void TestOldUInt160Gernerator1() { - OldUInt160 uInt160 = new OldUInt160(); + _ = new OldUInt160(); } [Benchmark] public void TestOldUInt160Gernerator2() { - OldUInt160 uInt160 = new OldUInt160(new byte[20]); + _ = new OldUInt160(new byte[20]); } [Benchmark] @@ -49,8 +49,8 @@ public void TestOldUInt160Equals() [Benchmark] public void TestOldUInt160Parse() { - OldUInt160 result = OldUInt160.Parse("0x0000000000000000000000000000000000000000"); - OldUInt160 result1 = OldUInt160.Parse("0000000000000000000000000000000000000000"); + _ = OldUInt160.Parse("0x0000000000000000000000000000000000000000"); + _ = OldUInt160.Parse("0000000000000000000000000000000000000000"); } [Benchmark] @@ -60,7 +60,6 @@ public void TestOldUInt160TryParse() OldUInt160.TryParse("0x0000000000000000000000000000000000000000", out var temp); OldUInt160.TryParse("0x1230000000000000000000000000000000000000", out temp); OldUInt160.TryParse("000000000000000000000000000000000000000", out _); - OldUInt160.TryParse("0xKK00000000000000000000000000000000000000", out _); } [Benchmark] @@ -90,13 +89,13 @@ public void TestOldUInt160OperatorSmallerAndEqual() [Benchmark] public void TestGernerator1() { - UInt160 uInt160 = new UInt160(); + _ = new UInt160(); } [Benchmark] public void TestGernerator2() { - UInt160 uInt160 = new UInt160(new byte[20]); + _ = new UInt160(new byte[20]); } [Benchmark] @@ -118,8 +117,8 @@ public void TestEquals() [Benchmark] public void TestParse() { - UInt160 result = UInt160.Parse("0x0000000000000000000000000000000000000000"); - UInt160 result1 = UInt160.Parse("0000000000000000000000000000000000000000"); + _ = UInt160.Parse("0x0000000000000000000000000000000000000000"); + _ = UInt160.Parse("0000000000000000000000000000000000000000"); } [Benchmark] @@ -129,7 +128,6 @@ public void TestTryParse() UInt160.TryParse("0x0000000000000000000000000000000000000000", out var temp); UInt160.TryParse("0x1230000000000000000000000000000000000000", out temp); UInt160.TryParse("000000000000000000000000000000000000000", out _); - UInt160.TryParse("0xKK00000000000000000000000000000000000000", out _); } [Benchmark] diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index 80e7fb9672..895bc16b33 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -15,7 +15,6 @@ using System.Globalization; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Neo @@ -51,15 +50,18 @@ public UInt160() { } /// Initializes a new instance of the class. /// /// The value of the . - public UInt160(ReadOnlySpan value) + public unsafe UInt160(ReadOnlySpan value) { if (value.Length != Length) throw new FormatException(); - var bytes = value.ToArray(); - _value1 = Unsafe.As(ref bytes[0]); - _value2 = Unsafe.As(ref bytes[8]); - _value3 = Unsafe.As(ref bytes[16]); + fixed (void* dstPointer = &_value1) + { + fixed (void* srcPointer = value) + { + Buffer.MemoryCopy(srcPointer, dstPointer, Length, Length); + } + } } public int CompareTo(UInt160 other) @@ -80,6 +82,9 @@ public void Deserialize(ref MemoryReader reader) public override bool Equals(object obj) { + if (ReferenceEquals(this, obj)) + return true; + if (obj == null) return false; @@ -138,18 +143,16 @@ public static bool TryParse(string str, out UInt160 result) if (string.IsNullOrWhiteSpace(str)) return false; - if (str.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) + if (str.StartsWith("0x", StringComparison.InvariantCulture)) str = str[2..]; if (str.Length != Length * 2) return false; try { - var data = Enumerable.Range(0, Length) - .Select(s => byte.Parse(str.Substring(s * 2, 2), NumberStyles.HexNumber)) - .Reverse() - .ToArray(); - + var data = new byte[Length]; + for (var i = 0; i < Length; i++) + data[Length - i - 1] = byte.Parse(str.Substring(i * 2, 2), NumberStyles.HexNumber); result = new(data); return true; } @@ -171,7 +174,6 @@ public static implicit operator UInt160(byte[] b) public static bool operator ==(UInt160 left, UInt160 right) { - if (ReferenceEquals(left, right)) return true; if (left is null || right is null) return Equals(left, right); return left.Equals(right); @@ -179,7 +181,6 @@ public static implicit operator UInt160(byte[] b) public static bool operator !=(UInt160 left, UInt160 right) { - if (ReferenceEquals(left, right)) return false; if (left is null || right is null) return !Equals(left, right); return !left.Equals(right); From 9bc543658a070076ef5c2f2e9bc3e3bfc6543aac Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Thu, 1 Aug 2024 17:17:17 -0400 Subject: [PATCH 09/11] Revert and just keep bug fixes --- src/Neo/UInt160.cs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index 895bc16b33..363c244cbd 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -55,12 +55,10 @@ public unsafe UInt160(ReadOnlySpan value) if (value.Length != Length) throw new FormatException(); - fixed (void* dstPointer = &_value1) + fixed (void* p = &_value1) { - fixed (void* srcPointer = value) - { - Buffer.MemoryCopy(srcPointer, dstPointer, Length, Length); - } + Span dst = new(p, Length); + value[..Length].CopyTo(dst); } } @@ -82,16 +80,8 @@ public void Deserialize(ref MemoryReader reader) public override bool Equals(object obj) { - if (ReferenceEquals(this, obj)) - return true; - - if (obj == null) - return false; - - var other = obj as UInt160; - if (other == null) - return false; - return Equals(other); + if (ReferenceEquals(obj, this)) return true; + return Equals(obj as UInt160); } public bool Equals(UInt160 other) @@ -152,7 +142,10 @@ public static bool TryParse(string str, out UInt160 result) { var data = new byte[Length]; for (var i = 0; i < Length; i++) - data[Length - i - 1] = byte.Parse(str.Substring(i * 2, 2), NumberStyles.HexNumber); + { + if (!byte.TryParse(str.AsSpan(i * 2, 2), NumberStyles.HexNumber, null, out data[Length - i - 1])) + return false; + } result = new(data); return true; } From bc65668356fa5854457cf8069dd9bcdc0fdae06d Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Fri, 2 Aug 2024 13:08:42 -0400 Subject: [PATCH 10/11] Made @shargon changes --- src/Neo/UInt160.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index 363c244cbd..6757642690 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -15,6 +15,7 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Neo @@ -62,6 +63,7 @@ public unsafe UInt160(ReadOnlySpan value) } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int CompareTo(UInt160 other) { var result = _value3.CompareTo(other._value3); @@ -78,12 +80,14 @@ public void Deserialize(ref MemoryReader reader) _value3 = reader.ReadUInt32(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { if (ReferenceEquals(obj, this)) return true; return Equals(obj as UInt160); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(UInt160 other) { if (other == null) return false; @@ -129,21 +133,23 @@ public override string ToString() /// if an is successfully parsed; otherwise, . public static bool TryParse(string str, out UInt160 result) { + var startIndex = 0; + result = null; if (string.IsNullOrWhiteSpace(str)) return false; if (str.StartsWith("0x", StringComparison.InvariantCulture)) - str = str[2..]; + startIndex = 2; - if (str.Length != Length * 2) return false; + if ((str.Length - startIndex) != Length * 2) return false; try { var data = new byte[Length]; for (var i = 0; i < Length; i++) { - if (!byte.TryParse(str.AsSpan(i * 2, 2), NumberStyles.HexNumber, null, out data[Length - i - 1])) + if (!byte.TryParse(str.AsSpan(i * 2 + startIndex, 2), NumberStyles.HexNumber, null, out data[Length - i - 1])) return false; } result = new(data); From 6ac2adb2391eef8ce2ea52ebec18f2bdf3f6dbf9 Mon Sep 17 00:00:00 2001 From: Christopher Schuchardt Date: Fri, 2 Aug 2024 13:48:08 -0400 Subject: [PATCH 11/11] Set `InvariantCultureIgnoreCase` back for `0x` and `0X` --- src/Neo/UInt160.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Neo/UInt160.cs b/src/Neo/UInt160.cs index 6757642690..da4c920cbe 100644 --- a/src/Neo/UInt160.cs +++ b/src/Neo/UInt160.cs @@ -139,7 +139,7 @@ public static bool TryParse(string str, out UInt160 result) if (string.IsNullOrWhiteSpace(str)) return false; - if (str.StartsWith("0x", StringComparison.InvariantCulture)) + if (str.StartsWith("0x", StringComparison.InvariantCultureIgnoreCase)) startIndex = 2; if ((str.Length - startIndex) != Length * 2) return false;