From 532919dc1bad8797ed2e3f6669c378e26cca79ea Mon Sep 17 00:00:00 2001 From: Kiminuo Date: Tue, 9 Jan 2024 11:23:29 +0100 Subject: [PATCH 1/3] WIP --- NBitcoin.Bench/UInt256Bench.cs | 19 +++++++++++++ NBitcoin.Tests/NBitcoin.Tests.csproj | 4 +-- NBitcoin.Tests/uint256_tests.cs | 22 ++++++++++----- NBitcoin/UInt256.cs | 40 ++++++++++++++++++++++++++-- 4 files changed, 74 insertions(+), 11 deletions(-) diff --git a/NBitcoin.Bench/UInt256Bench.cs b/NBitcoin.Bench/UInt256Bench.cs index 546a435324..6eac93befb 100644 --- a/NBitcoin.Bench/UInt256Bench.cs +++ b/NBitcoin.Bench/UInt256Bench.cs @@ -25,6 +25,25 @@ public void WriteToString() { Value.ToString(); } + +#if HAS_SPAN + [Benchmark] + [Arguments(new char[] { + '0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0' + })] + public void WriteToSpanString(Span destinationSpan) + { + Value.ToSpanString(destinationSpan); + } +#endif + [Benchmark] public void Read() { diff --git a/NBitcoin.Tests/NBitcoin.Tests.csproj b/NBitcoin.Tests/NBitcoin.Tests.csproj index 06a46c31ad..45f99e2aa5 100644 --- a/NBitcoin.Tests/NBitcoin.Tests.csproj +++ b/NBitcoin.Tests/NBitcoin.Tests.csproj @@ -41,8 +41,8 @@ - - + + all runtime; build; native; contentfiles; analyzers diff --git a/NBitcoin.Tests/uint256_tests.cs b/NBitcoin.Tests/uint256_tests.cs index b6c2647fa3..cc0da43fb9 100644 --- a/NBitcoin.Tests/uint256_tests.cs +++ b/NBitcoin.Tests/uint256_tests.cs @@ -1,11 +1,7 @@ -using NBitcoin.Protocol; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; using Xunit; namespace NBitcoin.Tests @@ -115,7 +111,7 @@ public void spanUintSerializationTests() [Fact] [Trait("UnitTest", "UnitTest")] - public void uitnSerializationTests2() + public void uintSerializationTests2() { var v = new uint256("0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"); var vr = new uint256("201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201"); @@ -147,7 +143,7 @@ public void uitnSerializationTests2() [Fact] [Trait("UnitTest", "UnitTest")] - public void uitnSerializationTests() + public void uintSerializationTests() { MemoryStream ms = new MemoryStream(); BitcoinStream stream = new BitcoinStream(ms, true); @@ -193,6 +189,18 @@ public void uitnSerializationTests() Assert.True(vs2.SequenceEqual(vs)); } +#if HAS_SPAN && NET6_0_OR_GREATER // .NET Core 3.1 cannot compile this. + [Fact] + [Trait("UnitTest", "UnitTest")] + public void uintSpanSerializationTests() + { + var v1 = new uint256("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + + Assert.Equal("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff", v1.ToString()); + Assert.Equal("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff".AsSpan(), v1.ToSpanString()); + } +#endif + private void AssertEquals(uint256 a, uint256 b) { Assert.Equal(a, b); diff --git a/NBitcoin/UInt256.cs b/NBitcoin/UInt256.cs index 1ad6eb37de..26d1a4bf88 100644 --- a/NBitcoin/UInt256.cs +++ b/NBitcoin/UInt256.cs @@ -1,9 +1,8 @@ +using NBitcoin.DataEncoders; using System; -using System.Collections; using System.Linq; using System.Runtime.InteropServices; -using NBitcoin.DataEncoders; namespace NBitcoin { @@ -161,6 +160,43 @@ public override string ToString() return Encoder.EncodeData(bytes); } +#if HAS_SPAN + /// The method allocates a new 64 char array. + public Span ToSpanString() + { + Span result = new char[64]; + + ToSpanString(result); + return result; + } + + /// The method does not allocate. + public void ToSpanString(Span destination) + { + Span ulongs = stackalloc ulong[4]; + ulongs[0] = pn0; + ulongs[1] = pn1; + ulongs[2] = pn2; + ulongs[3] = pn3; + Span bytes = MemoryMarshal.Cast(ulongs); + + if (BitConverter.IsLittleEndian) + { + for (int i = 31, j = 0; i >= 0; i--, j += 2) + { + HexEncoder.ToCharsBuffer(bytes[i], destination, startingIndex: j); + } + } + else + { + for (int i = 0; i < 32; i++) + { + HexEncoder.ToCharsBuffer(bytes[i], destination, startingIndex: i); + } + } + } +#endif + public uint256(ulong b) { pn0 = (uint)b; From 5708d91ee1b46204c4c84e1b0376ca827f17fa4d Mon Sep 17 00:00:00 2001 From: Kiminuo Date: Tue, 9 Jan 2024 13:04:00 +0100 Subject: [PATCH 2/3] WIP --- NBitcoin/UInt256.cs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/NBitcoin/UInt256.cs b/NBitcoin/UInt256.cs index 26d1a4bf88..2465e4cc73 100644 --- a/NBitcoin/UInt256.cs +++ b/NBitcoin/UInt256.cs @@ -1,6 +1,7 @@ using NBitcoin.DataEncoders; using System; +using System.Buffers.Binary; using System.Linq; using System.Runtime.InteropServices; @@ -174,25 +175,27 @@ public Span ToSpanString() public void ToSpanString(Span destination) { Span ulongs = stackalloc ulong[4]; - ulongs[0] = pn0; - ulongs[1] = pn1; - ulongs[2] = pn2; - ulongs[3] = pn3; - Span bytes = MemoryMarshal.Cast(ulongs); if (BitConverter.IsLittleEndian) { - for (int i = 31, j = 0; i >= 0; i--, j += 2) - { - HexEncoder.ToCharsBuffer(bytes[i], destination, startingIndex: j); - } + ulongs[0] = pn0; + ulongs[1] = pn1; + ulongs[2] = pn2; + ulongs[3] = pn3; } else { - for (int i = 0; i < 32; i++) - { - HexEncoder.ToCharsBuffer(bytes[i], destination, startingIndex: i); - } + ulongs[0] = BinaryPrimitives.ReverseEndianness(pn0); + ulongs[1] = BinaryPrimitives.ReverseEndianness(pn1); + ulongs[2] = BinaryPrimitives.ReverseEndianness(pn2); + ulongs[3] = BinaryPrimitives.ReverseEndianness(pn3); + } + + Span bytes = MemoryMarshal.Cast(ulongs); + + for (int i = 31, j = 0; i >= 0; i--, j += 2) + { + HexEncoder.ToCharsBuffer(bytes[i], destination, startingIndex: j); } } #endif From ff1637438791967b2ef0ca45fb1e40177eee419d Mon Sep 17 00:00:00 2001 From: Kiminuo Date: Tue, 9 Jan 2024 13:20:02 +0100 Subject: [PATCH 3/3] WIP --- NBitcoin/UInt256.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/NBitcoin/UInt256.cs b/NBitcoin/UInt256.cs index 2465e4cc73..4db276c37f 100644 --- a/NBitcoin/UInt256.cs +++ b/NBitcoin/UInt256.cs @@ -1,7 +1,8 @@ - using NBitcoin.DataEncoders; using System; +#if HAS_SPAN using System.Buffers.Binary; +#endif using System.Linq; using System.Runtime.InteropServices; @@ -162,6 +163,10 @@ public override string ToString() } #if HAS_SPAN + + /// + /// Returns HEX string representation. + /// /// The method allocates a new 64 char array. public Span ToSpanString() { @@ -171,6 +176,9 @@ public Span ToSpanString() return result; } + /// + /// Returns HEX string representation. + /// /// The method does not allocate. public void ToSpanString(Span destination) { @@ -193,6 +201,7 @@ public void ToSpanString(Span destination) Span bytes = MemoryMarshal.Cast(ulongs); + // Reverses order of bytes as pn0, pn1, pn2, and pn3 are set in a little endian manner. for (int i = 31, j = 0; i >= 0; i--, j += 2) { HexEncoder.ToCharsBuffer(bytes[i], destination, startingIndex: j);