From 68197ef5c7e6bd6bcbe8496bbf4f1a662a2e7d55 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Wed, 12 Jun 2024 11:57:27 +0100 Subject: [PATCH] fix: fix order of bytes in decimal write bytes methods --- CHANGELOG.md | 1 + X10D.Tests/src/IO/DecimalTests.cs | 54 ++++++++++++++ X10D.Tests/src/IO/StreamTests.WriteDecimal.cs | 3 +- X10D/src/IO/DecimalExtensions.cs | 71 +++++-------------- 4 files changed, 75 insertions(+), 54 deletions(-) create mode 100644 X10D.Tests/src/IO/DecimalTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index c3d26dc..759b016 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ TypeInitializationException. ### Fixed +- X10D: Fixed `decimal.TryWriteBigEndianBytes` and `decimal.TryWriteLittleEndianBytes`. - X10D.Hosting: Fixed `AddHostedSingleton` not accepting an interface as the service type. ## [3.3.0] - 2023-08-21 diff --git a/X10D.Tests/src/IO/DecimalTests.cs b/X10D.Tests/src/IO/DecimalTests.cs new file mode 100644 index 0000000..93dea74 --- /dev/null +++ b/X10D.Tests/src/IO/DecimalTests.cs @@ -0,0 +1,54 @@ +using NUnit.Framework; +using X10D.IO; + +namespace X10D.Tests.IO; + +[TestFixture] +internal class DecimalTests +{ + [Test] + public void GetBigEndianBytes_ShouldReturnBytes_InBigEndian() + { + const decimal value = 1234m; + byte[] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 210]; + + byte[] bytes = value.GetBigEndianBytes(); + + CollectionAssert.AreEqual(expected, bytes); + } + + [Test] + public void GetLittleEndianBytes_ShouldReturnBytes_InLittleEndian() + { + const decimal value = 1234m; + byte[] expected = [210, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + byte[] bytes = value.GetLittleEndianBytes(); + + CollectionAssert.AreEqual(expected, bytes); + } + + [Test] + public void TryWriteBigEndianBytes_ShouldWriteBytes_InBigEndian() + { + const decimal value = 1234m; + byte[] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 210]; + + Span bytes = stackalloc byte[16]; + value.TryWriteBigEndianBytes(bytes); + + CollectionAssert.AreEqual(expected, bytes.ToArray()); + } + + [Test] + public void TryWriteLittleEndianBytes_ShouldWriteBytes_InLittleEndian() + { + const decimal value = 1234m; + byte[] expected = [210, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + Span bytes = stackalloc byte[16]; + value.TryWriteLittleEndianBytes(bytes); + + CollectionAssert.AreEqual(expected, bytes.ToArray()); + } +} diff --git a/X10D.Tests/src/IO/StreamTests.WriteDecimal.cs b/X10D.Tests/src/IO/StreamTests.WriteDecimal.cs index 911dd5d..15e7538 100644 --- a/X10D.Tests/src/IO/StreamTests.WriteDecimal.cs +++ b/X10D.Tests/src/IO/StreamTests.WriteDecimal.cs @@ -48,9 +48,10 @@ internal partial class StreamTests Span actual = stackalloc byte[16]; ReadOnlySpan expected = stackalloc byte[] { - 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x10, 0x00, 0x00 + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x68 }; int read = stream.Read(actual); + Trace.WriteLine(string.Join(' ', actual.ToArray())); Assert.That(read, Is.EqualTo(16)); CollectionAssert.AreEqual(expected.ToArray(), actual.ToArray()); diff --git a/X10D/src/IO/DecimalExtensions.cs b/X10D/src/IO/DecimalExtensions.cs index 68b9a6e..e01f591 100644 --- a/X10D/src/IO/DecimalExtensions.cs +++ b/X10D/src/IO/DecimalExtensions.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Diagnostics.Contracts; using System.Runtime.InteropServices; @@ -13,11 +12,11 @@ public static class DecimalExtensions /// Converts the current decimal number into an array of bytes, as little endian. /// /// The value. - /// An array of bytes with length 4. + /// An array of bytes with length 16. [Pure] public static byte[] GetBigEndianBytes(this decimal value) { - Span buffer = stackalloc byte[4]; + Span buffer = stackalloc byte[16]; value.TryWriteBigEndianBytes(buffer); return buffer.ToArray(); } @@ -26,11 +25,11 @@ public static class DecimalExtensions /// Converts the current decimal number into an array of bytes, as little endian. /// /// The value. - /// An array of bytes with length 4. + /// An array of bytes with length 16. [Pure] public static byte[] GetLittleEndianBytes(this decimal value) { - Span buffer = stackalloc byte[4]; + Span buffer = stackalloc byte[16]; value.TryWriteLittleEndianBytes(buffer); return buffer.ToArray(); } @@ -44,23 +43,17 @@ public static class DecimalExtensions public static bool TryWriteBigEndianBytes(this decimal value, Span destination) { Span buffer = stackalloc int[4]; - GetBits(value, buffer); + decimal.GetBits(value, buffer); - if (buffer[0].TryWriteBigEndianBytes(destination[..4]) && - buffer[1].TryWriteBigEndianBytes(destination[4..8]) && - buffer[2].TryWriteBigEndianBytes(destination[8..12]) && - buffer[3].TryWriteBigEndianBytes(destination[12..])) + Span result = stackalloc byte[16]; + MemoryMarshal.Cast(buffer).CopyTo(result); + + if (BitConverter.IsLittleEndian) { - if (BitConverter.IsLittleEndian) - { - destination.Reverse(); - } - - return true; + result.Reverse(); } - destination.Clear(); - return false; + return result.TryCopyTo(destination); } /// @@ -72,44 +65,16 @@ public static class DecimalExtensions public static bool TryWriteLittleEndianBytes(this decimal value, Span destination) { Span buffer = stackalloc int[4]; - GetBits(value, buffer); + decimal.GetBits(value, buffer); - if (buffer[0].TryWriteLittleEndianBytes(destination[..4]) && - buffer[1].TryWriteLittleEndianBytes(destination[4..8]) && - buffer[2].TryWriteLittleEndianBytes(destination[8..12]) && - buffer[3].TryWriteLittleEndianBytes(destination[12..])) + Span result = stackalloc byte[16]; + MemoryMarshal.Cast(buffer).CopyTo(result); + + if (!BitConverter.IsLittleEndian) { - if (!BitConverter.IsLittleEndian) - { - destination.Reverse(); - } - - return true; + result.Reverse(); } - destination.Clear(); - return false; + return result.TryCopyTo(destination); } - - private static void GetBits(decimal value, Span destination) - { - _ = decimal.GetBits(value, destination); - } - -#if !NET5_0_OR_GREATER - private static void WriteBits(Span destination, Span buffer) - { - var flags = MemoryMarshal.Read(buffer[..4]); - var hi = MemoryMarshal.Read(buffer[4..8]); - var lo = MemoryMarshal.Read(buffer[8..]); - - var low = (uint)lo; - var mid = (uint)(lo >> 32); - - destination[0] = (int)low; - destination[1] = (int)mid; - destination[2] = hi; - destination[3] = flags; - } -#endif }