From 96f9698aa42660860d66fb4a8f28cc5ad2ecd530 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sat, 14 Mar 2026 23:40:34 -0400 Subject: [PATCH] Add proper U/Int128 support --- .../Extensions/BinaryReaderExtensionsTests.cs | 62 +++++ .../ByteArrayReaderExtensionsTests.cs | 56 +++++ .../Extensions/StreamReaderExtensionsTests.cs | 56 +++++ .../Extensions/BinaryReaderExtensions.cs | 130 ++++++++--- .../Extensions/BinaryWriterExtensions.cs | 35 +-- .../Extensions/ByteArrayReaderExtensions.cs | 125 +++++++--- .../Extensions/ByteArrayWriterExtensions.cs | 35 +-- SabreTools.IO/Extensions/NumericExtensions.cs | 219 +++++++++++++++++- .../Extensions/StreamReaderExtensions.cs | 127 +++++++--- .../Extensions/StreamWriterExtensions.cs | 35 +-- 10 files changed, 715 insertions(+), 165 deletions(-) diff --git a/SabreTools.IO.Test/Extensions/BinaryReaderExtensionsTests.cs b/SabreTools.IO.Test/Extensions/BinaryReaderExtensionsTests.cs index f919c52..650b7e4 100644 --- a/SabreTools.IO.Test/Extensions/BinaryReaderExtensionsTests.cs +++ b/SabreTools.IO.Test/Extensions/BinaryReaderExtensionsTests.cs @@ -684,6 +684,16 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(expected, read); } + [Fact] + public void ReadInt128LittleEndianTest() + { + var stream = new MemoryStream(_bytes); + var br = new BinaryReader(stream); + var expected = (Int128)new BigInteger(_bytes); + Int128 read = br.ReadInt128LittleEndian(); + Assert.Equal(expected, read); + } + [Fact] public void ReadUInt128Test() { @@ -705,6 +715,16 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(expected, read); } + [Fact] + public void ReadUInt128LittleEndianTest() + { + var stream = new MemoryStream(_bytes); + var br = new BinaryReader(stream); + var expected = (UInt128)new BigInteger(_bytes); + UInt128 read = br.ReadUInt128LittleEndian(); + Assert.Equal(expected, read); + } + [Fact] public void ReadNullTerminatedStringTest() { @@ -1680,6 +1700,17 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(0, br.BaseStream.Position); } + [Fact] + public void PeekInt128LittleEndianTest() + { + var stream = new MemoryStream(_bytes); + var br = new BinaryReader(stream); + var expected = (Int128)new BigInteger(_bytes); + Int128 read = br.PeekInt128LittleEndian(); + Assert.Equal(expected, read); + Assert.Equal(0, br.BaseStream.Position); + } + [Fact] public void PeekUInt128Test() { @@ -1703,6 +1734,17 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(0, br.BaseStream.Position); } + [Fact] + public void PeekUInt128LittleEndianTest() + { + var stream = new MemoryStream(_bytes); + var br = new BinaryReader(stream); + var expected = (UInt128)new BigInteger(_bytes); + UInt128 read = br.PeekUInt128LittleEndian(); + Assert.Equal(expected, read); + Assert.Equal(0, br.BaseStream.Position); + } + #endregion #region Try Read @@ -2389,6 +2431,16 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(default, read); } + [Fact] + public void TryReadInt128LittleEndianTest() + { + var stream = new MemoryStream([]); + var br = new BinaryReader(stream); + bool actual = br.TryReadInt128LittleEndian(out Int128 read); + Assert.False(actual); + Assert.Equal(default, read); + } + [Fact] public void TryReadUInt128Test() { @@ -2409,6 +2461,16 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(default, read); } + [Fact] + public void TryReadUInt128LittleEndianTest() + { + var stream = new MemoryStream([]); + var br = new BinaryReader(stream); + bool actual = br.TryReadUInt128LittleEndian(out UInt128 read); + Assert.False(actual); + Assert.Equal(default, read); + } + #endregion } } diff --git a/SabreTools.IO.Test/Extensions/ByteArrayReaderExtensionsTests.cs b/SabreTools.IO.Test/Extensions/ByteArrayReaderExtensionsTests.cs index 06e5478..25611a9 100644 --- a/SabreTools.IO.Test/Extensions/ByteArrayReaderExtensionsTests.cs +++ b/SabreTools.IO.Test/Extensions/ByteArrayReaderExtensionsTests.cs @@ -591,6 +591,15 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(expected, read); } + [Fact] + public void ReadInt128LittleEndianTest() + { + int offset = 0; + var expected = (Int128)new BigInteger(_bytes); + Int128 read = _bytes.ReadInt128LittleEndian(ref offset); + Assert.Equal(expected, read); + } + [Fact] public void ReadUInt128Test() { @@ -610,6 +619,15 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(expected, read); } + [Fact] + public void ReadUInt128LittleEndianTest() + { + int offset = 0; + var expected = (UInt128)new BigInteger(_bytes); + UInt128 read = _bytes.ReadUInt128LittleEndian(ref offset); + Assert.Equal(expected, read); + } + [Fact] public void ReadNullTerminatedStringTest() { @@ -1519,6 +1537,16 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(0, offset); } + [Fact] + public void PeekInt128LittleEndianTest() + { + int offset = 0; + var expected = (Int128)new BigInteger(_bytes); + Int128 read = _bytes.PeekInt128LittleEndian(ref offset); + Assert.Equal(expected, read); + Assert.Equal(0, offset); + } + [Fact] public void PeekUInt128Test() { @@ -1540,6 +1568,16 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(0, offset); } + [Fact] + public void PeekUInt128LittleEndianTest() + { + int offset = 0; + var expected = (UInt128)new BigInteger(_bytes); + UInt128 read = _bytes.PeekUInt128LittleEndian(ref offset); + Assert.Equal(expected, read); + Assert.Equal(0, offset); + } + #endregion #region Try Read @@ -2158,6 +2196,15 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(default, read); } + [Fact] + public void TryReadInt128LittleEndianTest() + { + int offset = 0; + bool actual = Array.Empty().TryReadInt128LittleEndian(ref offset, out Int128 read); + Assert.False(actual); + Assert.Equal(default, read); + } + [Fact] public void TryReadUInt128Test() { @@ -2176,6 +2223,15 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(default, read); } + [Fact] + public void TryReadUInt128LittleEndianTest() + { + int offset = 0; + bool actual = Array.Empty().TryReadUInt128LittleEndian(ref offset, out UInt128 read); + Assert.False(actual); + Assert.Equal(default, read); + } + #endregion } } diff --git a/SabreTools.IO.Test/Extensions/StreamReaderExtensionsTests.cs b/SabreTools.IO.Test/Extensions/StreamReaderExtensionsTests.cs index 7d0cf9f..3d981e4 100644 --- a/SabreTools.IO.Test/Extensions/StreamReaderExtensionsTests.cs +++ b/SabreTools.IO.Test/Extensions/StreamReaderExtensionsTests.cs @@ -595,6 +595,15 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(expected, read); } + [Fact] + public void ReadInt128LittleEndianTest() + { + var stream = new MemoryStream(_bytes); + var expected = (Int128)new BigInteger(_bytes); + Int128 read = stream.ReadInt128LittleEndian(); + Assert.Equal(expected, read); + } + [Fact] public void ReadUInt128Test() { @@ -614,6 +623,15 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(expected, read); } + [Fact] + public void ReadUInt128LittleEndianTest() + { + var stream = new MemoryStream(_bytes); + var expected = (UInt128)new BigInteger(_bytes); + UInt128 read = stream.ReadUInt128LittleEndian(); + Assert.Equal(expected, read); + } + [Fact] public void ReadNullTerminatedStringTest() { @@ -1515,6 +1533,16 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(0, stream.Position); } + [Fact] + public void PeekInt128LittleEndianTest() + { + var stream = new MemoryStream(_bytes); + var expected = (Int128)new BigInteger(_bytes); + Int128 read = stream.PeekInt128LittleEndian(); + Assert.Equal(expected, read); + Assert.Equal(0, stream.Position); + } + [Fact] public void PeekUInt128Test() { @@ -1536,6 +1564,16 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(0, stream.Position); } + [Fact] + public void PeekUInt128LittleEndianTest() + { + var stream = new MemoryStream(_bytes); + var expected = (UInt128)new BigInteger(_bytes); + UInt128 read = stream.PeekUInt128LittleEndian(); + Assert.Equal(expected, read); + Assert.Equal(0, stream.Position); + } + #endregion #region Try Read @@ -2146,6 +2184,15 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(default, read); } + [Fact] + public void TryReadInt128LittleEndianTest() + { + var stream = new MemoryStream([]); + bool actual = stream.TryReadInt128LittleEndian(out Int128 read); + Assert.False(actual); + Assert.Equal(default, read); + } + [Fact] public void TryReadUInt128Test() { @@ -2164,6 +2211,15 @@ namespace SabreTools.IO.Test.Extensions Assert.Equal(default, read); } + [Fact] + public void TryReadUInt128LittleEndianTest() + { + var stream = new MemoryStream([]); + bool actual = stream.TryReadUInt128LittleEndian(out UInt128 read); + Assert.False(actual); + Assert.Equal(default, read); + } + #endregion } } diff --git a/SabreTools.IO/Extensions/BinaryReaderExtensions.cs b/SabreTools.IO/Extensions/BinaryReaderExtensions.cs index 56aecf7..2ced8a1 100644 --- a/SabreTools.IO/Extensions/BinaryReaderExtensions.cs +++ b/SabreTools.IO/Extensions/BinaryReaderExtensions.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -#if NET7_0_OR_GREATER -using System.Numerics; -#endif using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -461,15 +458,17 @@ namespace SabreTools.IO.Extensions return new Guid(buffer); } - // TODO: Determine if the reverse reads are doing what are expected #if NET7_0_OR_GREATER /// /// Read an Int128 from the underlying stream /// + /// Reads in machine native format public static Int128 ReadInt128(this BinaryReader reader) { - byte[] buffer = reader.ReadBytes(16); - return (Int128)new BigInteger(buffer); + if (BitConverter.IsLittleEndian) + return reader.ReadInt128LittleEndian(); + else + return reader.ReadInt128BigEndian(); } /// @@ -479,17 +478,29 @@ namespace SabreTools.IO.Extensions public static Int128 ReadInt128BigEndian(this BinaryReader reader) { byte[] buffer = reader.ReadBytes(16); - Array.Reverse(buffer); - return (Int128)new BigInteger(buffer); + return buffer.ToInt128BigEndian(0); + } + + /// + /// Read an Int128 from the underlying stream + /// + /// Reads in little-endian format + public static Int128 ReadInt128LittleEndian(this BinaryReader reader) + { + byte[] buffer = reader.ReadBytes(16); + return buffer.ToInt128LittleEndian(0); } /// /// Read a UInt128 from the underlying stream /// + /// Reads in machine native format public static UInt128 ReadUInt128(this BinaryReader reader) { - byte[] buffer = reader.ReadBytes(16); - return (UInt128)new BigInteger(buffer); + if (BitConverter.IsLittleEndian) + return reader.ReadUInt128LittleEndian(); + else + return reader.ReadUInt128BigEndian(); } /// @@ -499,8 +510,17 @@ namespace SabreTools.IO.Extensions public static UInt128 ReadUInt128BigEndian(this BinaryReader reader) { byte[] buffer = reader.ReadBytes(16); - Array.Reverse(buffer); - return (UInt128)new BigInteger(buffer); + return buffer.ToUInt128BigEndian(0); + } + + /// + /// Read a UInt128 from the underlying stream + /// + /// Reads in little-endian format + public static UInt128 ReadUInt128LittleEndian(this BinaryReader reader) + { + byte[] buffer = reader.ReadBytes(16); + return buffer.ToUInt128LittleEndian(0); } #endif @@ -1677,9 +1697,10 @@ namespace SabreTools.IO.Extensions /// Only works properly on seekable streams public static Int128 PeekInt128(this BinaryReader reader) { - Int128 value = reader.ReadInt128(); - reader.BaseStream.SeekIfPossible(-16, SeekOrigin.Current); - return value; + if (BitConverter.IsLittleEndian) + return reader.PeekInt128LittleEndian(); + else + return reader.PeekInt128BigEndian(); } /// @@ -1694,6 +1715,18 @@ namespace SabreTools.IO.Extensions return value; } + /// + /// Peek an Int128 from the base stream + /// + /// Reads in little-endian format + /// Only works properly on seekable streams + public static Int128 PeekInt128LittleEndian(this BinaryReader reader) + { + Int128 value = reader.ReadInt128LittleEndian(); + reader.BaseStream.SeekIfPossible(-16, SeekOrigin.Current); + return value; + } + /// /// Peek a UInt128 from the base stream /// @@ -1701,9 +1734,10 @@ namespace SabreTools.IO.Extensions /// Only works properly on seekable streams public static UInt128 PeekUInt128(this BinaryReader reader) { - UInt128 value = reader.ReadUInt128(); - reader.BaseStream.SeekIfPossible(-16, SeekOrigin.Current); - return value; + if (BitConverter.IsLittleEndian) + return reader.PeekUInt128LittleEndian(); + else + return reader.PeekUInt128BigEndian(); } /// @@ -1717,6 +1751,18 @@ namespace SabreTools.IO.Extensions reader.BaseStream.SeekIfPossible(-16, SeekOrigin.Current); return value; } + + /// + /// Peek a UInt128 from the base stream + /// + /// Reads in little-endian format + /// Only works properly on seekable streams + public static UInt128 PeekUInt128LittleEndian(this BinaryReader reader) + { + UInt128 value = reader.ReadUInt128LittleEndian(); + reader.BaseStream.SeekIfPossible(-16, SeekOrigin.Current); + return value; + } #endif #endregion @@ -2605,14 +2651,10 @@ namespace SabreTools.IO.Extensions /// Reads in machine native format public static bool TryReadInt128(this BinaryReader reader, out Int128 value) { - if (reader.BaseStream.Position > reader.BaseStream.Length - 16) - { - value = default; - return false; - } - - value = reader.ReadInt128(); - return true; + if (BitConverter.IsLittleEndian) + return reader.TryReadInt128LittleEndian(out value); + else + return reader.TryReadInt128BigEndian(out value); } /// @@ -2632,10 +2674,10 @@ namespace SabreTools.IO.Extensions } /// - /// Read a UInt128 from the base stream + /// Read an Int128 from the base stream /// - /// Reads in machine native format - public static bool TryReadUInt128(this BinaryReader reader, out UInt128 value) + /// Reads in little-endian format + public static bool TryReadInt128LittleEndian(this BinaryReader reader, out Int128 value) { if (reader.BaseStream.Position > reader.BaseStream.Length - 16) { @@ -2643,10 +2685,22 @@ namespace SabreTools.IO.Extensions return false; } - value = reader.ReadUInt128(); + value = reader.ReadInt128LittleEndian(); return true; } + /// + /// Read a UInt128 from the base stream + /// + /// Reads in machine native format + public static bool TryReadUInt128(this BinaryReader reader, out UInt128 value) + { + if (BitConverter.IsLittleEndian) + return reader.TryReadUInt128LittleEndian(out value); + else + return reader.TryReadUInt128BigEndian(out value); + } + /// /// Read a UInt128 from the base stream /// @@ -2662,6 +2716,22 @@ namespace SabreTools.IO.Extensions value = reader.ReadUInt128BigEndian(); return true; } + + /// + /// Read a UInt128 from the base stream + /// + /// Reads in little-endian format + public static bool TryReadUInt128LittleEndian(this BinaryReader reader, out UInt128 value) + { + if (reader.BaseStream.Position > reader.BaseStream.Length - 16) + { + value = default; + return false; + } + + value = reader.ReadUInt128LittleEndian(); + return true; + } #endif #endregion diff --git a/SabreTools.IO/Extensions/BinaryWriterExtensions.cs b/SabreTools.IO/Extensions/BinaryWriterExtensions.cs index bd3034c..2c2f69f 100644 --- a/SabreTools.IO/Extensions/BinaryWriterExtensions.cs +++ b/SabreTools.IO/Extensions/BinaryWriterExtensions.cs @@ -1,8 +1,5 @@ using System; using System.IO; -#if NET7_0_OR_GREATER -using System.Numerics; -#endif using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -311,13 +308,11 @@ namespace SabreTools.IO.Extensions /// /// Write an Int128 /// + /// Writes in little-endian format public static bool Write(this BinaryWriter writer, Int128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(writer, padded); + byte[] buffer = value.ToByteArrayLittleEndian(); + return WriteFromBuffer(writer, buffer); } /// @@ -326,24 +321,18 @@ namespace SabreTools.IO.Extensions /// Writes in big-endian format public static bool WriteBigEndian(this BinaryWriter writer, Int128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - Array.Reverse(buffer); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(writer, padded); + byte[] buffer = value.ToByteArrayBigEndian(); + return WriteFromBuffer(writer, buffer); } /// /// Write a UInt128 /// + /// Writes in little-endian format public static bool Write(this BinaryWriter writer, UInt128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(writer, padded); + byte[] buffer = value.ToByteArrayLittleEndian(); + return WriteFromBuffer(writer, buffer); } /// @@ -352,12 +341,8 @@ namespace SabreTools.IO.Extensions /// Writes in big-endian format public static bool WriteBigEndian(this BinaryWriter writer, UInt128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - Array.Reverse(buffer); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(writer, padded); + byte[] buffer = value.ToByteArrayBigEndian(); + return WriteFromBuffer(writer, buffer); } #endif diff --git a/SabreTools.IO/Extensions/ByteArrayReaderExtensions.cs b/SabreTools.IO/Extensions/ByteArrayReaderExtensions.cs index 8bae5ca..0128421 100644 --- a/SabreTools.IO/Extensions/ByteArrayReaderExtensions.cs +++ b/SabreTools.IO/Extensions/ByteArrayReaderExtensions.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -#if NET7_0_OR_GREATER -using System.Numerics; -#endif using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -675,8 +672,10 @@ namespace SabreTools.IO.Extensions /// Reads in machine native format public static Int128 ReadInt128(this byte[] content, ref int offset) { - byte[] buffer = ReadExactlyToBuffer(content, ref offset, 16); - return (Int128)new BigInteger(buffer); + if (BitConverter.IsLittleEndian) + return content.ReadInt128LittleEndian(ref offset); + else + return content.ReadInt128BigEndian(ref offset); } /// @@ -686,8 +685,17 @@ namespace SabreTools.IO.Extensions public static Int128 ReadInt128BigEndian(this byte[] content, ref int offset) { byte[] buffer = ReadExactlyToBuffer(content, ref offset, 16); - Array.Reverse(buffer); - return (Int128)new BigInteger(buffer); + return buffer.ToInt128BigEndian(0); + } + + /// + /// Read an Int128 and increment the pointer to an array + /// + /// Reads in little-endian format + public static Int128 ReadInt128LittleEndian(this byte[] content, ref int offset) + { + byte[] buffer = ReadExactlyToBuffer(content, ref offset, 16); + return buffer.ToInt128LittleEndian(0); } /// @@ -696,8 +704,10 @@ namespace SabreTools.IO.Extensions /// Reads in machine native format public static UInt128 ReadUInt128(this byte[] content, ref int offset) { - byte[] buffer = ReadExactlyToBuffer(content, ref offset, 16); - return (UInt128)new BigInteger(buffer); + if (BitConverter.IsLittleEndian) + return content.ReadUInt128LittleEndian(ref offset); + else + return content.ReadUInt128BigEndian(ref offset); } /// @@ -707,8 +717,17 @@ namespace SabreTools.IO.Extensions public static UInt128 ReadUInt128BigEndian(this byte[] content, ref int offset) { byte[] buffer = ReadExactlyToBuffer(content, ref offset, 16); - Array.Reverse(buffer); - return (UInt128)new BigInteger(buffer); + return buffer.ToUInt128BigEndian(0); + } + + /// + /// Read a UInt128 and increment the pointer to an array + /// + /// Reads in little-endian format + public static UInt128 ReadUInt128LittleEndian(this byte[] content, ref int offset) + { + byte[] buffer = ReadExactlyToBuffer(content, ref offset, 16); + return buffer.ToUInt128LittleEndian(0); } #endif @@ -1877,9 +1896,10 @@ namespace SabreTools.IO.Extensions /// Reads in machine native format public static Int128 PeekInt128(this byte[] content, ref int offset) { - Int128 value = content.ReadInt128(ref offset); - offset -= 16; - return value; + if (BitConverter.IsLittleEndian) + return content.PeekInt128LittleEndian(ref offset); + else + return content.PeekInt128BigEndian(ref offset); } /// @@ -1893,15 +1913,27 @@ namespace SabreTools.IO.Extensions return value; } + /// + /// Peek an Int128 without incrementing the pointer to an array + /// + /// Reads in little-endian format + public static Int128 PeekInt128LittleEndian(this byte[] content, ref int offset) + { + Int128 value = content.ReadInt128LittleEndian(ref offset); + offset -= 16; + return value; + } + /// /// Peek a UInt128 without incrementing the pointer to an array /// /// Reads in machine native format public static UInt128 PeekUInt128(this byte[] content, ref int offset) { - UInt128 value = content.ReadUInt128(ref offset); - offset -= 16; - return value; + if (BitConverter.IsLittleEndian) + return content.PeekUInt128LittleEndian(ref offset); + else + return content.PeekUInt128BigEndian(ref offset); } /// @@ -1914,6 +1946,17 @@ namespace SabreTools.IO.Extensions offset -= 16; return value; } + + /// + /// Peek a UInt128 without incrementing the pointer to an array + /// + /// Reads in little-endian format + public static UInt128 PeekUInt128LittleEndian(this byte[] content, ref int offset) + { + UInt128 value = content.ReadUInt128LittleEndian(ref offset); + offset -= 16; + return value; + } #endif #endregion @@ -2817,14 +2860,10 @@ namespace SabreTools.IO.Extensions /// Reads in machine native format public static bool TryReadInt128(this byte[] content, ref int offset, out Int128 value) { - if (offset > content.Length - 16) - { - value = default; - return false; - } - - value = content.ReadInt128(ref offset); - return true; + if (BitConverter.IsLittleEndian) + return content.TryReadInt128LittleEndian(ref offset, out value); + else + return content.TryReadInt128BigEndian(ref offset, out value); } /// @@ -2844,10 +2883,10 @@ namespace SabreTools.IO.Extensions } /// - /// Read a UInt128 and increment the pointer to an array + /// Read an Int128 and increment the pointer to an array /// - /// Reads in machine native format - public static bool TryReadUInt128(this byte[] content, ref int offset, out UInt128 value) + /// Reads in little-endian format + public static bool TryReadInt128LittleEndian(this byte[] content, ref int offset, out Int128 value) { if (offset > content.Length - 16) { @@ -2855,10 +2894,22 @@ namespace SabreTools.IO.Extensions return false; } - value = content.ReadUInt128(ref offset); + value = content.ReadInt128LittleEndian(ref offset); return true; } + /// + /// Read a UInt128 and increment the pointer to an array + /// + /// Reads in machine native format + public static bool TryReadUInt128(this byte[] content, ref int offset, out UInt128 value) + { + if (BitConverter.IsLittleEndian) + return content.TryReadUInt128LittleEndian(ref offset, out value); + else + return content.TryReadUInt128BigEndian(ref offset, out value); + } + /// /// Read a UInt128 and increment the pointer to an array /// @@ -2874,6 +2925,22 @@ namespace SabreTools.IO.Extensions value = content.ReadUInt128BigEndian(ref offset); return true; } + + /// + /// Read a UInt128 and increment the pointer to an array + /// + /// Reads in little-endian format + public static bool TryReadUInt128LittleEndian(this byte[] content, ref int offset, out UInt128 value) + { + if (offset > content.Length - 16) + { + value = default; + return false; + } + + value = content.ReadUInt128LittleEndian(ref offset); + return true; + } #endif #endregion diff --git a/SabreTools.IO/Extensions/ByteArrayWriterExtensions.cs b/SabreTools.IO/Extensions/ByteArrayWriterExtensions.cs index c2ab1b0..58e1083 100644 --- a/SabreTools.IO/Extensions/ByteArrayWriterExtensions.cs +++ b/SabreTools.IO/Extensions/ByteArrayWriterExtensions.cs @@ -1,7 +1,4 @@ using System; -#if NET7_0_OR_GREATER -using System.Numerics; -#endif using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -490,13 +487,11 @@ namespace SabreTools.IO.Extensions /// /// Write an Int128 and increment the pointer to an array /// + /// Writes in little-endian format public static bool Write(this byte[] content, ref int offset, Int128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(content, ref offset, padded); + byte[] buffer = value.ToByteArrayLittleEndian(); + return WriteFromBuffer(content, ref offset, buffer); } /// @@ -505,24 +500,18 @@ namespace SabreTools.IO.Extensions /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, Int128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - Array.Reverse(buffer); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(content, ref offset, padded); + byte[] buffer = value.ToByteArrayBigEndian(); + return WriteFromBuffer(content, ref offset, buffer); } /// /// Write a UInt128 and increment the pointer to an array /// + /// Writes in little-endian format public static bool Write(this byte[] content, ref int offset, UInt128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(content, ref offset, padded); + byte[] buffer = value.ToByteArrayLittleEndian(); + return WriteFromBuffer(content, ref offset, buffer); } /// @@ -531,12 +520,8 @@ namespace SabreTools.IO.Extensions /// Writes in big-endian format public static bool WriteBigEndian(this byte[] content, ref int offset, UInt128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - Array.Reverse(buffer); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(content, ref offset, padded); + byte[] buffer = value.ToByteArrayBigEndian(); + return WriteFromBuffer(content, ref offset, buffer); } #endif diff --git a/SabreTools.IO/Extensions/NumericExtensions.cs b/SabreTools.IO/Extensions/NumericExtensions.cs index f2be54e..8a8d6e7 100644 --- a/SabreTools.IO/Extensions/NumericExtensions.cs +++ b/SabreTools.IO/Extensions/NumericExtensions.cs @@ -4,7 +4,6 @@ namespace SabreTools.IO.Extensions /// Extensions for numeric conversion /// /// TODO: Add fractional types (Half, Single, Double, Decimal) - /// TODO: Add U/Int128 /// TODO: Add GUID public static class NumericExtensions { @@ -262,6 +261,104 @@ namespace SabreTools.IO.Extensions | ((ulong)value[offset + 7] << 56); } +#if NET7_0_OR_GREATER + /// + /// Convert a byte array at an offset to an Int128 + /// + /// Reads in big-endian format + public static System.Int128 ToInt128BigEndian(this byte[] value, int offset) + { + return value[offset + 15] + | ((System.Int128)value[offset + 14] << 8) + | ((System.Int128)value[offset + 13] << 16) + | ((System.Int128)value[offset + 12] << 24) + | ((System.Int128)value[offset + 11] << 32) + | ((System.Int128)value[offset + 10] << 40) + | ((System.Int128)value[offset + 9] << 48) + | ((System.Int128)value[offset + 8] << 56) + | ((System.Int128)value[offset + 7] << 64) + | ((System.Int128)value[offset + 6] << 72) + | ((System.Int128)value[offset + 5] << 80) + | ((System.Int128)value[offset + 4] << 88) + | ((System.Int128)value[offset + 3] << 96) + | ((System.Int128)value[offset + 2] << 104) + | ((System.Int128)value[offset + 1] << 112) + | ((System.Int128)value[offset + 0] << 120); + } + + /// + /// Convert a byte array at an offset to an Int128 + /// + /// Reads in little-endian format + public static System.Int128 ToInt128LittleEndian(this byte[] value, int offset) + { + return value[offset + 0] + | ((System.Int128)value[offset + 1] << 8) + | ((System.Int128)value[offset + 2] << 16) + | ((System.Int128)value[offset + 3] << 24) + | ((System.Int128)value[offset + 4] << 32) + | ((System.Int128)value[offset + 5] << 40) + | ((System.Int128)value[offset + 6] << 48) + | ((System.Int128)value[offset + 7] << 56) + | ((System.Int128)value[offset + 8] << 64) + | ((System.Int128)value[offset + 9] << 72) + | ((System.Int128)value[offset + 10] << 80) + | ((System.Int128)value[offset + 11] << 88) + | ((System.Int128)value[offset + 12] << 96) + | ((System.Int128)value[offset + 13] << 104) + | ((System.Int128)value[offset + 14] << 112) + | ((System.Int128)value[offset + 15] << 120); + } + + /// + /// Convert a byte array at an offset to a UInt128 + /// + /// Reads in big-endian format + public static System.UInt128 ToUInt128BigEndian(this byte[] value, int offset) + { + return value[offset + 15] + | ((System.UInt128)value[offset + 14] << 8) + | ((System.UInt128)value[offset + 13] << 16) + | ((System.UInt128)value[offset + 12] << 24) + | ((System.UInt128)value[offset + 11] << 32) + | ((System.UInt128)value[offset + 10] << 40) + | ((System.UInt128)value[offset + 9] << 48) + | ((System.UInt128)value[offset + 8] << 56) + | ((System.UInt128)value[offset + 7] << 64) + | ((System.UInt128)value[offset + 6] << 72) + | ((System.UInt128)value[offset + 5] << 80) + | ((System.UInt128)value[offset + 4] << 88) + | ((System.UInt128)value[offset + 3] << 96) + | ((System.UInt128)value[offset + 2] << 104) + | ((System.UInt128)value[offset + 1] << 112) + | ((System.UInt128)value[offset + 0] << 120); + } + + /// + /// Convert a byte array at an offset to a UInt128 + /// + /// Reads in little-endian format + public static System.UInt128 ToUInt128LittleEndian(this byte[] value, int offset) + { + return value[offset + 0] + | ((System.UInt128)value[offset + 1] << 8) + | ((System.UInt128)value[offset + 2] << 16) + | ((System.UInt128)value[offset + 3] << 24) + | ((System.UInt128)value[offset + 4] << 32) + | ((System.UInt128)value[offset + 5] << 40) + | ((System.UInt128)value[offset + 6] << 48) + | ((System.UInt128)value[offset + 7] << 56) + | ((System.UInt128)value[offset + 8] << 64) + | ((System.UInt128)value[offset + 9] << 72) + | ((System.UInt128)value[offset + 10] << 80) + | ((System.UInt128)value[offset + 11] << 88) + | ((System.UInt128)value[offset + 12] << 96) + | ((System.UInt128)value[offset + 13] << 104) + | ((System.UInt128)value[offset + 14] << 112) + | ((System.UInt128)value[offset + 15] << 120); + } +#endif + #endregion #region To Byte Array @@ -618,6 +715,124 @@ namespace SabreTools.IO.Extensions return output; } - #endregion +#if NET7_0_OR_GREATER + /// + /// Convert an Int64 to a byte array + /// + /// Reads in big-endian format + public static byte[] ToByteArrayBigEndian(this System.Int128 value) + { + byte[] output = + [ + (byte)((value >> 120) & 0xFF), + (byte)((value >> 112) & 0xFF), + (byte)((value >> 104) & 0xFF), + (byte)((value >> 96) & 0xFF), + (byte)((value >> 88) & 0xFF), + (byte)((value >> 80) & 0xFF), + (byte)((value >> 72) & 0xFF), + (byte)((value >> 64) & 0xFF), + (byte)((value >> 56) & 0xFF), + (byte)((value >> 48) & 0xFF), + (byte)((value >> 40) & 0xFF), + (byte)((value >> 32) & 0xFF), + (byte)((value >> 24) & 0xFF), + (byte)((value >> 16) & 0xFF), + (byte)((value >> 8) & 0xFF), + (byte)(value & 0xFF), + ]; + + return output; + } + + /// + /// Convert an Int64 to a byte array + /// + /// Reads in little-endian format + public static byte[] ToByteArrayLittleEndian(this System.Int128 value) + { + byte[] output = + [ + (byte)(value & 0xFF), + (byte)((value >> 8) & 0xFF), + (byte)((value >> 16) & 0xFF), + (byte)((value >> 24) & 0xFF), + (byte)((value >> 32) & 0xFF), + (byte)((value >> 40) & 0xFF), + (byte)((value >> 48) & 0xFF), + (byte)((value >> 56) & 0xFF), + (byte)((value >> 64) & 0xFF), + (byte)((value >> 72) & 0xFF), + (byte)((value >> 80) & 0xFF), + (byte)((value >> 88) & 0xFF), + (byte)((value >> 96) & 0xFF), + (byte)((value >> 104) & 0xFF), + (byte)((value >> 112) & 0xFF), + (byte)((value >> 120) & 0xFF), + ]; + + return output; + } + + /// + /// Convert a UInt64 to a byte array + /// + /// Reads in big-endian format + public static byte[] ToByteArrayBigEndian(this System.UInt128 value) + { + byte[] output = + [ + (byte)((value >> 120) & 0xFF), + (byte)((value >> 112) & 0xFF), + (byte)((value >> 104) & 0xFF), + (byte)((value >> 96) & 0xFF), + (byte)((value >> 88) & 0xFF), + (byte)((value >> 80) & 0xFF), + (byte)((value >> 72) & 0xFF), + (byte)((value >> 64) & 0xFF), + (byte)((value >> 56) & 0xFF), + (byte)((value >> 48) & 0xFF), + (byte)((value >> 40) & 0xFF), + (byte)((value >> 32) & 0xFF), + (byte)((value >> 24) & 0xFF), + (byte)((value >> 16) & 0xFF), + (byte)((value >> 8) & 0xFF), + (byte)(value & 0xFF), + ]; + + return output; + } + + /// + /// Convert a UInt64 to a byte array + /// + /// Reads in little-endian format + public static byte[] ToByteArrayLittleEndian(this System.UInt128 value) + { + byte[] output = + [ + (byte)(value & 0xFF), + (byte)((value >> 8) & 0xFF), + (byte)((value >> 16) & 0xFF), + (byte)((value >> 24) & 0xFF), + (byte)((value >> 32) & 0xFF), + (byte)((value >> 40) & 0xFF), + (byte)((value >> 48) & 0xFF), + (byte)((value >> 56) & 0xFF), + (byte)((value >> 64) & 0xFF), + (byte)((value >> 72) & 0xFF), + (byte)((value >> 80) & 0xFF), + (byte)((value >> 88) & 0xFF), + (byte)((value >> 96) & 0xFF), + (byte)((value >> 104) & 0xFF), + (byte)((value >> 112) & 0xFF), + (byte)((value >> 120) & 0xFF), + ]; + + return output; + } +#endif + +#endregion } } diff --git a/SabreTools.IO/Extensions/StreamReaderExtensions.cs b/SabreTools.IO/Extensions/StreamReaderExtensions.cs index ed8af02..8ebb8bf 100644 --- a/SabreTools.IO/Extensions/StreamReaderExtensions.cs +++ b/SabreTools.IO/Extensions/StreamReaderExtensions.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -#if NET7_0_OR_GREATER -using System.Numerics; -#endif using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -670,8 +667,10 @@ namespace SabreTools.IO.Extensions /// Reads in machine native format public static Int128 ReadInt128(this Stream stream) { - byte[] buffer = ReadExactlyToBuffer(stream, 16); - return (Int128)new BigInteger(buffer); + if (BitConverter.IsLittleEndian) + return stream.ReadInt128LittleEndian(); + else + return stream.ReadInt128BigEndian(); } /// @@ -681,8 +680,17 @@ namespace SabreTools.IO.Extensions public static Int128 ReadInt128BigEndian(this Stream stream) { byte[] buffer = ReadExactlyToBuffer(stream, 16); - Array.Reverse(buffer); - return (Int128)new BigInteger(buffer); + return buffer.ToInt128BigEndian(0); + } + + /// + /// Read an Int128 from the stream + /// + /// Reads in little-endian format + public static Int128 ReadInt128LittleEndian(this Stream stream) + { + byte[] buffer = ReadExactlyToBuffer(stream, 16); + return buffer.ToInt128LittleEndian(0); } /// @@ -691,8 +699,10 @@ namespace SabreTools.IO.Extensions /// Reads in machine native format public static UInt128 ReadUInt128(this Stream stream) { - byte[] buffer = ReadExactlyToBuffer(stream, 16); - return (UInt128)new BigInteger(buffer); + if (BitConverter.IsLittleEndian) + return stream.ReadUInt128LittleEndian(); + else + return stream.ReadUInt128BigEndian(); } /// @@ -702,8 +712,17 @@ namespace SabreTools.IO.Extensions public static UInt128 ReadUInt128BigEndian(this Stream stream) { byte[] buffer = ReadExactlyToBuffer(stream, 16); - Array.Reverse(buffer); - return (UInt128)new BigInteger(buffer); + return buffer.ToUInt128BigEndian(0); + } + + /// + /// Read a UInt128 from the stream + /// + /// Reads in little-endian format + public static UInt128 ReadUInt128LittleEndian(this Stream stream) + { + byte[] buffer = ReadExactlyToBuffer(stream, 16); + return buffer.ToUInt128LittleEndian(0); } #endif @@ -1930,9 +1949,10 @@ namespace SabreTools.IO.Extensions /// Only works properly on seekable streams public static Int128 PeekInt128(this Stream stream) { - Int128 value = stream.ReadInt128(); - stream.SeekIfPossible(-16, SeekOrigin.Current); - return value; + if (BitConverter.IsLittleEndian) + return stream.PeekInt128LittleEndian(); + else + return stream.PeekInt128BigEndian(); } /// @@ -1947,6 +1967,18 @@ namespace SabreTools.IO.Extensions return value; } + /// + /// Peek an Int128 from the stream + /// + /// Reads in little-endian format + /// Only works properly on seekable streams + public static Int128 PeekInt128LittleEndian(this Stream stream) + { + Int128 value = stream.ReadInt128LittleEndian(); + stream.SeekIfPossible(-16, SeekOrigin.Current); + return value; + } + /// /// Peek a UInt128 from the stream /// @@ -1954,9 +1986,10 @@ namespace SabreTools.IO.Extensions /// Only works properly on seekable streams public static UInt128 PeekUInt128(this Stream stream) { - UInt128 value = stream.ReadUInt128(); - stream.SeekIfPossible(-16, SeekOrigin.Current); - return value; + if (BitConverter.IsLittleEndian) + return stream.PeekUInt128LittleEndian(); + else + return stream.PeekUInt128BigEndian(); } /// @@ -1970,6 +2003,18 @@ namespace SabreTools.IO.Extensions stream.SeekIfPossible(-16, SeekOrigin.Current); return value; } + + /// + /// Peek a UInt128 from the stream + /// + /// Reads in little-endian format + /// Only works properly on seekable streams + public static UInt128 PeekUInt128LittleEndian(this Stream stream) + { + UInt128 value = stream.ReadUInt128LittleEndian(); + stream.SeekIfPossible(-16, SeekOrigin.Current); + return value; + } #endif #endregion @@ -2858,14 +2903,10 @@ namespace SabreTools.IO.Extensions /// Reads in machine native format public static bool TryReadInt128(this Stream stream, out Int128 value) { - if (stream.Position > stream.Length - 16) - { - value = default; - return false; - } - - value = stream.ReadInt128(); - return true; + if (BitConverter.IsLittleEndian) + return stream.TryReadInt128LittleEndian(out value); + else + return stream.TryReadInt128BigEndian(out value); } /// @@ -2885,10 +2926,10 @@ namespace SabreTools.IO.Extensions } /// - /// Read a UInt128 from the stream + /// Read an Int128 from the stream /// - /// Reads in machine native format - public static bool TryReadUInt128(this Stream stream, out UInt128 value) + /// Reads in little-endian format + public static bool TryReadInt128LittleEndian(this Stream stream, out Int128 value) { if (stream.Position > stream.Length - 16) { @@ -2896,10 +2937,22 @@ namespace SabreTools.IO.Extensions return false; } - value = stream.ReadUInt128(); + value = stream.ReadInt128LittleEndian(); return true; } + /// + /// Read a UInt128 from the stream + /// + /// Reads in machine native format + public static bool TryReadUInt128(this Stream stream, out UInt128 value) + { + if (BitConverter.IsLittleEndian) + return stream.TryReadUInt128LittleEndian(out value); + else + return stream.TryReadUInt128BigEndian(out value); + } + /// /// Read a UInt128 from the stream /// @@ -2915,6 +2968,22 @@ namespace SabreTools.IO.Extensions value = stream.ReadUInt128BigEndian(); return true; } + + /// + /// Read a UInt128 from the stream + /// + /// Reads in little-endian format + public static bool TryReadUInt128LittleEndian(this Stream stream, out UInt128 value) + { + if (stream.Position > stream.Length - 16) + { + value = default; + return false; + } + + value = stream.ReadUInt128LittleEndian(); + return true; + } #endif #endregion diff --git a/SabreTools.IO/Extensions/StreamWriterExtensions.cs b/SabreTools.IO/Extensions/StreamWriterExtensions.cs index e12620a..919d58c 100644 --- a/SabreTools.IO/Extensions/StreamWriterExtensions.cs +++ b/SabreTools.IO/Extensions/StreamWriterExtensions.cs @@ -1,8 +1,5 @@ using System; using System.IO; -#if NET7_0_OR_GREATER -using System.Numerics; -#endif using System.Reflection; using System.Runtime.InteropServices; using System.Text; @@ -490,13 +487,11 @@ namespace SabreTools.IO.Extensions /// /// Write an Int128 /// + /// Writes in little-endian format public static bool Write(this Stream stream, Int128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(stream, padded); + byte[] buffer = value.ToByteArrayLittleEndian(); + return WriteFromBuffer(stream, buffer); } /// @@ -505,24 +500,18 @@ namespace SabreTools.IO.Extensions /// Writes in big-endian format public static bool WriteBigEndian(this Stream stream, Int128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - Array.Reverse(buffer); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(stream, padded); + byte[] buffer = value.ToByteArrayBigEndian(); + return WriteFromBuffer(stream, buffer); } /// /// Write a UInt128 /// + /// Writes in little-endian format public static bool Write(this Stream stream, UInt128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(stream, padded); + byte[] buffer = value.ToByteArrayLittleEndian(); + return WriteFromBuffer(stream, buffer); } /// @@ -531,12 +520,8 @@ namespace SabreTools.IO.Extensions /// Writes in big-endian format public static bool WriteBigEndian(this Stream stream, UInt128 value) { - byte[] buffer = ((BigInteger)value).ToByteArray(); - Array.Reverse(buffer); - - byte[] padded = new byte[16]; - Array.Copy(buffer, 0, padded, 16 - buffer.Length, buffer.Length); - return WriteFromBuffer(stream, padded); + byte[] buffer = value.ToByteArrayBigEndian(); + return WriteFromBuffer(stream, buffer); } #endif