From 6505299c006b0489efd3277350e5f3a1fcaa4ca3 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Sat, 30 Apr 2022 13:52:34 +0100 Subject: [PATCH] Move Stream extensions to IO namespace (#7) * Finalizes unit tests, 99% coverage * Update CHANGELOG --- CHANGELOG.md | 276 +++---- X10D.Tests/src/IO/StreamTests.cs | 341 +++++++- X10D.Tests/src/Time/TimeSpanParserTests.cs | 15 + X10D/src/IO/StreamExtensions.cs | 757 +++++++++++++++++- .../StreamExtensions.Internal.cs | 38 - X10D/src/StreamExtensions/StreamExtensions.cs | 746 ----------------- 6 files changed, 1221 insertions(+), 952 deletions(-) create mode 100644 X10D.Tests/src/Time/TimeSpanParserTests.cs delete mode 100644 X10D/src/StreamExtensions/StreamExtensions.Internal.cs delete mode 100644 X10D/src/StreamExtensions/StreamExtensions.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 21383d9..e72091a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,132 +3,67 @@ ## [3.0.0 (Unreleased)] ### Added - -- Added `StringBuilderReader` (inheriting `TextReader`) which allows reading from a `StringBuilder` without consuming the underlying string -- Added extension methods for `System.Decimal` -- Added `All` and `Any` for `Span` and `ReadOnlySpan`, mimicking the corresponding methods in `System.Linq` -- Added time-related extension methods (`Ticks`, `Milliseconds`, `Seconds`, `Minutes`, `Hours`, `Days`, and `Weeks`) to all built-in numeric types -- Added `TimeSpan.Ago()` and `TimeSpan.FromNow()` -- Added `Factorial` extension method for built-in numeric types -- Added `FileInfo.GetHash()` -- Added `FileInfo.TryWriteHash(Span, out int)` -- Added `IEnumerable.Product()` and `IEnumerable.Product(Func, TResult)` for all built-in numeric types, computing the product of all (transformed) elements -- Added `IEnumerable.Shuffled([Random])`, which wraps `IList.Shuffle([Random])` and returns the result -- Added `T.AsArray()` -- Added `T.AsEnumerable()` +- Added `T.AsArrayValue()` +- Added `T.AsEnumerableValue()` +- Added `T.RepeatValue(int)` - Added `T.ToJson([JsonSerializerOptions])` +- Added `string.FromJson([JsonSerializerOptions])` - Added `T[].AsReadOnly()` - Added `T[].Clear([Range])` and `T[].Clear(int, int)` -- Added `T[].Fill(T)` and `T[].Fill(T, int, int)` -- Added `Random.Next()` -- Added `Random.NextDouble(double)` -- Added `Random.NextDouble(double, double)` -- Added `Random.NextSingle()` -- Added `Random.NextSingle(float)` -- Added `Random.NextSingle(float, float)` -- Added `Random.NextInt64()` -- Added `Random.NextInt64(long)` -- Added `Random.NextInt64(long, long)` -- Added `Random.NextColor()` +- Added `DateTime.IsLeapYear()` +- Added `DateTimeOffset.IsLeapYear()` +- Added `Endianness` enum +- Added `FileInfo.GetHash()` +- Added `FileInfo.TryWriteHash(Span, out int)` +- Added `IComparable.Clamp(T, T)` - this supersedes `Clamp` defined for hard-coded numeric types (#24) +- Added `IComparable.GreaterThan(T2)` (#22) +- Added `IComparable.GreaterThanOrEqualTo(T2)` (#22) +- Added `IComparable.LessThan(T2)` (#22) +- Added `IComparable.LessThanOrEqualTo(T2)` (#22) +- Added `IComparable.Max(T)` (#23) +- Added `IComparable.Min(T)` (#23) +- Added `IDictionary.AddOrUpdate()` +- Added `IEnumerable.Product()` and `IEnumerable.Product(Func, TResult)` for all built-in numeric types, computing the product of all (optionally transformed) elements +- Added `IList.Fill(T)` and `IList.Fill(T, int, int)` +- Added `IPAddress.IsIPv4()` and `IPAddress.IsIPv6()` +- Added `IReadOnlyList.Pack8Bit()` +- Added `IReadOnlyList.Pack16Bit()` +- Added `IReadOnlyList.Pack32Bit()` +- Added `IReadOnlyList.Pack64Bit()` +- Added `MemberInfo.HasCustomAttribute()` and `MemberInfo.HasCustomAttribute(Type)` +- Added `defaultValue` overload for `MemberInfo.SelectFromCustomAttribute()` +- Added `Random.Next()` (returns a random field from a specified enum type) +- Added `Random.NextByte([byte[, byte]])` +- Added `Random.NextColorArgb()`, returning a random color in RGB space, including random alpha +- Added `Random.NextColorRgb()`, returning a random color in RGB space with alpha 255 +- Added `Random.NextDouble(double[, double])` +- Added `Random.NextInt16([short[, short]])` +- Added `Random.NextSingle(float[, float])` (#34) - Added `Random.NextUnitVector2()` - Added `Random.NextUnitVector3()` - Added `Random.NextRotation()` - Added `Rune.Repeat(int)` -- Added `bool.GetBytes()` -- Added `byte.GetBytes()` - Added `byte.IsEven()` - Added `byte.IsOdd()` - Added `byte.IsPrime()` +- Added `byte.UnpackBits()` +- Added `byte.RangeTo(byte)`, `byte.RangeTo(short)`, `byte.RangeTo(int)`, and `byte.RangeTo(long)` +- Added `int.Mod(int)` +- Added `int.RangeTo(int)`, and `int.RangeTo(long)` +- Added `int.UnpackBits()` +- Added `long.Mod(long)` +- Added `long.RangeTo(long)` +- Added `long.UnpackBits()` - Added `sbyte.IsEven()` - Added `sbyte.IsOdd()` - Added `sbyte.IsPrime()` -- Added `sbyte.Sign()` -- Added `decimal.Sign()` -- Added `double.Acos()` -- Added `double.Acosh()` -- Added `double.Asin()` -- Added `double.Asinh()` -- Added `double.Atan()` -- Added `double.Atanh()` -- Added `double.ComplexSqrt()` -- Added `double.Cos()` -- Added `double.Cosh()` -- Added `double.LerpFrom(double, double)` -- Added `double.LerpTo(double, double)` -- Added `double.LerpWith(double, double)` -- Added `double.Sign()` -- Added `double.Sin()` -- Added `double.Sinh()` -- Added `double.Sqrt()` -- Added `double.Tan()` -- Added `double.Tanh()` -- Added `float.Acos()` -- Added `float.Acosh()` -- Added `float.Asin()` -- Added `float.Asinh()` -- Added `float.Atan()` -- Added `float.Atanh()` -- Added `float.ComplexSqrt()` -- Added `float.Cos()` -- Added `float.Cosh()` -- Added `float.LerpFrom(float, float)` -- Added `float.LerpTo(float, float)` -- Added `float.LerpWith(float, float)` -- Added `float.Sign()` -- Added `float.Sin()` -- Added `float.Sinh()` -- Added `float.Sqrt()` -- Added `float.Tan()` -- Added `float.Tanh()` -- Added `short.ToHostOrder()` -- Added `short.LerpFrom(double, double)` -- Added `short.LerpTo(double, double)` -- Added `short.LerpWith(double, double)` -- Added `short.LerpFrom(float, float)` -- Added `short.LerpTo(float, float)` -- Added `short.LerpWith(float, float)` -- Added `short.Sign()` -- Added `short.ToNetworkOrder()` -- Added `short.To(short)` and `short.To(short, short)` -- Added `int.LerpFrom(double, double)` -- Added `int.LerpTo(double, double)` -- Added `int.LerpWith(double, double)` -- Added `int.LerpFrom(float, float)` -- Added `int.LerpTo(float, float)` -- Added `int.LerpWith(float, float)` -- Added `int.Mod(int)` -- Added `int.RotateLeft(int)` -- Added `int.RotateRight(int)` -- Added `int.Sign()` -- Added `int.ToHostOrder()` -- Added `int.ToNetworkOrder()` -- Added `int.To(int)` and `int.To(int, int)` -- Added `long.ToHostOrder()` -- Added `long.LerpFrom(double, double)` -- Added `long.LerpTo(double, double)` -- Added `long.LerpWith(double, double)` -- Added `long.LerpFrom(float, float)` -- Added `long.LerpTo(float, float)` -- Added `long.LerpWith(float, float)` -- Added `long.Sign()` -- Added `long.ToNetworkOrder()` -- Added `long.To(long)` and `long.To(long, long)` -- Added `IComparable.Clamp(T, T)` which supersedes `short.Clamp(short, short)`, `int.Clamp(int, int)`, - and `long.Clamp(long, long)` -- Added `IComparable.GreaterThan(T)` -- Added `IComparable.GreaterOrEqualTo(T)` -- Added `IComparable.LessThan(T)` -- Added `IComparable.LessThanOrEqualTo(T)` -- Added `IComparable.Max(T)` -- Added `IComparable.Min(T)` -- Added `IReadOnlyCollection.Split(int)` - - Yields the same results as `IEnumerable.Split(int)`, except is able to avoid a hidden allocation with the benefit of knowing the collection size ahead of time -- Added `Type.HasCustomAttribute()` and `Type.HasCustomAttribute(Type)` -- Added `Type.Implements()` and `Type.Implements(Type)` -- Added `Type.Inherits()` and `Type.Inherits(Type)` -- Added `Type.SelectFromCustomAttribute()` -- Added `DateTimeOffset` extensions which supersede `DateTime` extensions -- Added `Endianness` enum +- Added `sbyte.Mod(sbyte)` +- Added `short.IsEven()` +- Added `short.IsOdd()` +- Added `short.Mod(short)` +- Added `short.RangeTo(short)`, `short.RangeTo(int)`, and `short.RangeTo(long)` +- Added `short.UnpackBits()` +- Added `string.IsPalindrome()` - Added `Stream.GetHash()` - Added `Stream.TryWriteHash(Span, out int)` - Added `Stream.ReadInt16([Endian])` @@ -143,72 +78,85 @@ - Added `Stream.Write(ushort, [Endian])` - Added `Stream.Write(uint, [Endian])` - Added `Stream.Write(ulong, [Endian])` -- Added `string.IsPalindrome()` -- Added `string.FromJson([JsonSerializerOptions])` +- Added `TimeSpan.Ago()` +- Added `TimeSpan.FromNow()` - Added `TimeSpanParser.TryParse` which supersedes `TimeSpanParser.Parse` +- Added `Sqrt()` and `ComplexSqrt()` for all built-in decimal types +- Added `All()` and `Any()` for `Span` and `ReadOnlySpan`, mimicking the corresponding methods in `System.Linq` +- Added `Sign()` for built-in numeric types. For unsigned types, this never returns -1 +- Added `Type.Implements()` and `Type.Implements(Type)` (#25) +- Added `Type.Inherits()` and `Type.Inherits(Type)` (#25) +- Added `DigitalRoot` function for built-in integer types +- Added `Factorial` function for built-in integer types +- Added `HostToNetworkOrder` and `NetworkToHostOrder` for `short`, `int`, and `long` +- Added `IsLeapYear` function for `DateTime` and `DateTimeOffset`, as well as built-in numeric types +- Added `MultiplicativePersistence` function for built-in integer types +- Added `RotateLeft` and `RotateRight` for built-in integer types +- Added trigonometric functions for built-in numeric types, including `Acos()`, `Asin()`, `Atan()`, `Atan2()`, `Cos()`, `Sin()`, `Tan()` (#49) +- Added time-related extension methods for built-in numeric types, including `Milliseconds()`, `Seconds()`, `Minutes()`, `Hours()`, `Days()`, and `Weeks()`. `Ticks()` is also available, but only for integers; not floating point +- Added `StringBuilderReader` (inheriting `TextReader`) which allows reading from a `StringBuilder` without consuming the underlying string +- Added extension methods for `System.Decimal` ### Changed - -- Update to .NET 6 +- Updated to .NET 6 (#45) - Methods defined to accept `byte[]` have been changed accept `IReadOnlyList` -- `Enum.Next([bool])` and `Enum.Previous([bool])` have been separated. Non-wrapped overloads now use the `Unchecked` prefix (`NextUnchecked` and `PreviousUnchecked`) -- Improve performance for: +- Extension methods are now defined in appropriate child namespaces to reduce the risk of name collisions (#7) +- `char[].Random(int)`, `char[].Random(int, int)`, `IEnumerable.Random(int)`, and `IEnumerable.Random(int, int)` have been redefined as `Random.NextString(IReadOnlyList, int)` +- `IComparable.Between(T, T)` has been redefined as `IComparable.Between(T2, T3, [InclusiveOptions])` to allow comparison of disparate yet comparable types, and also offers inclusivity options +- `DateTime` extensions now wrap `DateTimeOffset` extensions +- `DateTime.ToUnixTimestamp([bool])` has been redefined as `DateTime.ToUnixTimeMilliseconds()` and `DateTime.ToUnixTimeSeconds()` +- `Dictionary.ToGetParameters()`, `IDictionary.ToGetParameters()`, and `IReadOnlyDictionary.ToGetParameters()`, has been redefined as `IEnumerable>.ToGetParameters()` +- `Dictionary.ToConnectionString()`, `IDictionary.ToConnectionString()`, and `IReadOnlyDictionary.ToConnectionString()`, has been redefined as `IEnumerable>.ToConnectionString()` +- `IList.OneOf([Random])` and `IEnumerable.OneOf([Random])` have been redefined as `Random.NextFrom(IEnumerable)` to fall in line with the naming convention of `System.Random` (#21) +- `IList.Shuffle([Random])` now uses a Fisher-Yates shuffle implementation +- `IList.Shuffle([Random])` has been repurposed to shuffle the list in place, rather than returning a new enumerable + - `IEnumerable.Shuffle([Random])` has been renamed to `IEnumerable.Shuffled([Random])` to avoid confusion with `IList.Shuffle([Random])` +- `Random.CoinToss()` has been redefined as `Random.NextBoolean()` to fall in line with the naming convention of `System.Random` +- `Random.OneOf(T[])` and `Random.OneOf(IList)` have been redefined as `Random.NextFrom(IEnumerable)` to fall in line with the naming convention of `System.Random` +- `Enum.Next([bool])` and `Enum.Previous([bool])` have been redefined as `Enum.Next()`, `Enum.Previous()`, `Enum.NextUnchecked()`, `Enum.PreviousUnchecked()`. The `Unchecked` variants of these methods do not perform index validation, and will throw `IndexOutOfRangeException` when attempting to access an invalid index. The checked variants will perform a modulo to wrap the index +- Seperated `string.WithAlternative(string, [bool])` to `string.WithEmptyAlternative(string)` and `string.WithWhiteSpaceAlternative(string)` +- `string.AsNullIfEmpty()` and `string.AsNullIfWhiteSpace()` now accept a nullable `string` +- `IEnumerable.GetString([Encoding])` has been renamed to `IReadOnlyList.ToString` and its `Encoding` parameter has + been made non-optional +- Fixed a bug where `IEnumerable>.ToConnectionString()` would not sanitize types with custom `ToString()` + implementation (#20) + Renamed `string.Random(int[, Random])` to `string.Randomize(int[, Random])` +- Redefined `char[].Random(int)`, `char[].Random(int, Random)`, `IEnumerable.Random(int)`, and `IEnumerable.Random(int, Random)`, as `Random.NextString(IReadOnlyList, int)` +- Improved performance for: - `string.IsLower()` - `string.IsUpper()` - `string.Reverse()` -- `IEnumerable.Split(int)` now lazily enumerates, rather than consuming in full before yielding -- Separated `short.FromUnixTimestamp(bool)` into `short.FromUnixTimeMilliseconds` and `short.FromUnixTimeSeconds` -- Separated `int.FromUnixTimestamp(bool)` into `int.FromUnixTimeMilliseconds` and `int.FromUnixTimeSeconds` -- Separated `long.FromUnixTimestamp(bool)` into `long.FromUnixTimeMilliseconds` and `long.FromUnixTimeSeconds` -- Seperated `string.WithAlternative(string, [bool])` to `string.WithEmptyAlternative(string)` and `string.WithWhiteSpaceAlternative(string)` -- `IComparable.Between` now accepts an `InclusiveOptions` parameter to specify whether checks shall be inclusive or exclusive -- Renamed `Random.CoinToss()` to `Random.NextBoolean()` to fall into consistency with `Random.Next()`, `Random.NextBytes()` - and `Random.NextDouble()` -- Renamed `Random.OneOf()` to `Random.NextFrom()` (same reasoning) -- Renamed `List.OneOf()` to `List.Random()` -- Renamed `string.Random(int[, Random])` to `string.Randomize(int[, Random])` -- Renamed `char[].Random(int)`, `char[].Random(int, Random)`, `IEnumerable.Random(int)`, - and `IEnumerable.Random(int, Random)`, in favour of `Random.NextString(IReadOnlyList, int)` -- `IList.Shuffle([Random])` now uses a Fisher-Yates shuffle implementation -- `IList.Shuffle([Random])` now returns `void`, as it will reorganize the elements in-place on the current `IList` -- `Dictionary`, `IDictionary`, and `IReadOnlyDictionary` extension methods have been consolidated - to `IEnumerable>` -- Fixed a bug where `IEnumerable>.ToConnectionString()` would not sanitize types with custom `ToString()` - implementation (#20) -- `DateTime` extensions now wrap `DateTimeOffset` extensions -- `DateTime.ToUnixTimestamp([bool])` has been split into `DateTime.ToUnixTimeMilliseconds()` and `DateTime.ToUnixTimeSeconds()` -- `IEnumerable.GetString([Encoding])` has been renamed to `IReadOnlyList.ToString` and its `Encoding` parameter has - been made non-optional -- `WaitHandle.WaitOneAsync` now returns `Task` as a result of calling `Task.Run`, rather than returning a - typeless `new Task()` -- `string.AsNullIfEmpty()` and `string.AsNullIfWhiteSpace()` now accept a nullable `string` -- `string.WithAlternative` is now `string.WithEmptyAlternative` and `string.WithWhiteSpaceAlternative` + - `TimeSpanParser` ### Removed - +- Indefinitely suspended Unity support, until such a time that Unity can be updated to a newer version of .NET. See: https://forum.unity.com/threads/unity-future-net-development-status.1092205/ - Removed `bool.And(bool)` - Removed `bool.NAnd(bool)` - Removed `bool.NOr(bool)` -- Removed `bool.Not()` +- Removed `bool.Not(bool)` - Removed `bool.Or(bool)` -- Removed `bool.XNOr(bool)` -- Removed `bool.XOr(bool)` -- Removed `float.ToBoolean()` -- Removed `double.ToBoolean()` -- Removed `int.ToBoolean()` -- Removed `long.ToBoolean()` -- Removed `IConvertible.To([IFormatProvider])` -- Removed `IConvertible.ToOrDefault([IFormatProvider])` -- Removed `IConvertible.ToOrDefault(out T, [IFormatProvider])` -- Removed `IConvertible.ToOrNull([IFormatProvider])` -- Removed `IConvertible.ToOrNull(out T, [IFormatProvider])` -- Removed `IConvertible.ToOrOther(T, [IFormatProvider])` -- Removed `IConvertible.ToOrOther(T, out T, [IFormatProvider])` -- Removed `IEnumerable.Split(int)` and `IReadOnlyCollection.Split` (this functionality is now provided by LINQ in the form of the [`Chunk`](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.chunk?view=net-6.0) method) +- Removed `bool.ToByte()` +- Removed `bool.ToInt16()` +- Removed `bool.ToInt64()` +- Removed `bool.XNOr()` +- Removed `bool.XOr()` +- Removed `IConvertible.To([IFormatProvider])` (#13) +- Removed `IConvertible.ToOrDefault([IFormatProvider])` (#13) +- Removed `IConvertible.ToOrDefault(out T, [IFormatProvider])` (#13) +- Removed `IConvertible.ToOrNull([IFormatProvider])` (#13) +- Removed `IConvertible.ToOrNull(out T, [IFormatProvider])` (#13) +- Removed `IConvertible.ToOrOther(T, [IFormatProvider])` (#13) +- Removed `IConvertible.ToOrOther(out T, T, [IFormatProvider])` (#13) +- Removed `IEnumerable.Split(int)` - this functionality is now provided by .NET in the form of the `Chunk` method. See: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.chunk?view=net-6.0 - Removed `MemberInfo.GetDefaultValue()` (use `SelectFromCustomAttribute()` instead) - Removed `MemberInfo.GetDescription()` (use `SelectFromCustomAttribute()` instead) -- Removed `Random.NextInt64()` and its overloads. This functionality is built in starting with .NET 6 -- Removed `TimeSpanParser.Parse` +- Removed `int.ToBoolean()` +- Removed `long.ToBoolean()` +- Removed `short.ToBoolean()` +- Removed `uint.ToBoolean()` +- Removed `ushort.ToBoolean()` +- Removed `ulong.ToBoolean()` +- Removed `WaitHandle.WaitOneAsync()`. For suspensions of execution during asynchronous operations, favour `TaskCompletionSource` or `TaskCompletionSource`. See: https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource?view=net-6.0 and https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.taskcompletionsource-1?view=net-6.0 ## [2.6.0] - 2020-10-20 diff --git a/X10D.Tests/src/IO/StreamTests.cs b/X10D.Tests/src/IO/StreamTests.cs index c8c9430..77809fd 100644 --- a/X10D.Tests/src/IO/StreamTests.cs +++ b/X10D.Tests/src/IO/StreamTests.cs @@ -38,7 +38,7 @@ public class StreamTests } [TestMethod] - public void TryWriteHashSha1ShouldBeCorrect() + public void TryWriteHashSha1_ShouldBeCorrect() { // SHA-1 byte[] expectedHash = @@ -58,14 +58,14 @@ public class StreamTests } [TestMethod] - public void NonReadableStreamShouldThrow() + public void GetHash_TryWriteHash_ShouldThrow_GivenNonReadableStream() { Assert.ThrowsException(() => { using var stream = new DummyStream(); stream.GetHash(); }); - + Assert.ThrowsException(() => { using var stream = new DummyStream(); @@ -99,6 +99,341 @@ public class StreamTests Stream.Null.TryWriteHash(Span.Empty, out _)); } + [TestMethod] + public void Write_ShouldThrow_GivenUndefinedEndianness() + { + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.Write(0.0f, (Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.Write(0.0, (Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.Write(0.0m, (Endianness)(-1)); + }); + + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.Write((short)0, (Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.Write(0, (Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.Write(0L, (Endianness)(-1)); + }); + + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.Write((ushort)0, (Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.Write(0U, (Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.Write(0UL, (Endianness)(-1)); + }); + } + + [TestMethod] + public void Read_ShouldThrow_GivenNullStream() + { + Stream? stream = null; + Assert.ThrowsException(() => stream!.ReadSingle()); + Assert.ThrowsException(() => stream!.ReadDouble()); + Assert.ThrowsException(() => stream!.ReadDecimal()); + Assert.ThrowsException(() => stream!.ReadInt16()); + Assert.ThrowsException(() => stream!.ReadInt32()); + Assert.ThrowsException(() => stream!.ReadInt64()); + Assert.ThrowsException(() => stream!.ReadUInt16()); + Assert.ThrowsException(() => stream!.ReadUInt32()); + Assert.ThrowsException(() => stream!.ReadUInt64()); + } + + [TestMethod] + public void Write_ShouldThrow_GivenNullStream() + { + Stream? stream = null; + Assert.ThrowsException(() => stream!.Write(0.0f, Endianness.LittleEndian)); + Assert.ThrowsException(() => stream!.Write(0.0, Endianness.LittleEndian)); + Assert.ThrowsException(() => stream!.Write(0.0m, Endianness.LittleEndian)); + Assert.ThrowsException(() => stream!.Write((short)0)); + Assert.ThrowsException(() => stream!.Write(0)); + Assert.ThrowsException(() => stream!.Write(0L)); + Assert.ThrowsException(() => stream!.Write((ushort)0)); + Assert.ThrowsException(() => stream!.Write(0U)); + Assert.ThrowsException(() => stream!.Write(0UL)); + } + + [TestMethod] + public void Read_ShouldThrow_GivenUndefinedEndianness() + { + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.ReadSingle((Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.ReadDouble((Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.ReadDecimal((Endianness)(-1)); + }); + + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.ReadInt16((Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.ReadInt32((Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.ReadInt64((Endianness)(-1)); + }); + + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.ReadUInt16((Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.ReadUInt32((Endianness)(-1)); + }); + Assert.ThrowsException(() => + { + using var stream = new MemoryStream(); + return stream.ReadUInt64((Endianness)(-1)); + }); + } + + [TestMethod] + public void ReadDouble_WriteDouble_ShouldBeSymmetric() + { + using var stream = new MemoryStream(); + stream.Write(420.0, BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420.0, stream.ReadDouble(), 1e-6); + + stream.Position = 0; + stream.Write(420.0, Endianness.LittleEndian); + + stream.Position = 0; + Assert.AreEqual(420.0, stream.ReadDouble(Endianness.LittleEndian), 1e-6); + + stream.Position = 0; + stream.Write(420.0, Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420.0, stream.ReadDouble(Endianness.BigEndian), 1e-6); + } + + [TestMethod] + public void ReadDecimal_WriteSingle_ShouldBeSymmetric() + { + using var stream = new MemoryStream(); + stream.Write(420.0m, BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420.0m, stream.ReadDecimal()); + + stream.Position = 0; + stream.Write(420.0m, Endianness.LittleEndian); + + stream.Position = 0; + Assert.AreEqual(420.0m, stream.ReadDecimal(Endianness.LittleEndian)); + + stream.Position = 0; + stream.Write(420.0m, Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420.0m, stream.ReadDecimal(Endianness.BigEndian)); + } + + [TestMethod] + public void ReadSingle_WriteSingle_ShouldBeSymmetric() + { + using var stream = new MemoryStream(); + stream.Write(420.0f, BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420.0f, stream.ReadSingle(), 1e-6f); + + stream.Position = 0; + stream.Write(420.0f, Endianness.LittleEndian); + + stream.Position = 0; + Assert.AreEqual(420.0f, stream.ReadSingle(Endianness.LittleEndian), 1e-6f); + + stream.Position = 0; + stream.Write(420.0f, Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420.0f, stream.ReadSingle(Endianness.BigEndian), 1e-6f); + } + + [TestMethod] + public void ReadInt16_WriteInt16_ShouldBeSymmetric() + { + using var stream = new MemoryStream(); + stream.Write((short)420); + + stream.Position = 0; + Assert.AreEqual(420, stream.ReadInt16()); + + stream.Position = 0; + stream.Write((short)420, Endianness.LittleEndian); + + stream.Position = 0; + Assert.AreEqual(420, stream.ReadInt16(Endianness.LittleEndian)); + + stream.Position = 0; + stream.Write((short)420, Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420, stream.ReadInt16(Endianness.BigEndian)); + } + + [TestMethod] + public void ReadInt32_WriteInt32_ShouldBeSymmetric() + { + using var stream = new MemoryStream(); + stream.Write(420); + + stream.Position = 0; + Assert.AreEqual(420, stream.ReadInt32()); + + stream.Position = 0; + stream.Write(420, Endianness.LittleEndian); + + stream.Position = 0; + Assert.AreEqual(420, stream.ReadInt32(Endianness.LittleEndian)); + + stream.Position = 0; + stream.Write(420, Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420, stream.ReadInt32(Endianness.BigEndian)); + } + + [TestMethod] + public void ReadInt64_WriteInt64_ShouldBeSymmetric() + { + using var stream = new MemoryStream(); + stream.Write(420L); + + stream.Position = 0; + Assert.AreEqual(420L, stream.ReadInt64()); + + stream.Position = 0; + stream.Write(420L, Endianness.LittleEndian); + + stream.Position = 0; + Assert.AreEqual(420L, stream.ReadInt64(Endianness.LittleEndian)); + + stream.Position = 0; + stream.Write(420L, Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420L, stream.ReadInt64(Endianness.BigEndian)); + } + + [TestMethod] + [CLSCompliant(false)] + public void ReadUInt16_WriteUInt16_ShouldBeSymmetric() + { + using var stream = new MemoryStream(); + stream.Write((ushort)420); + + stream.Position = 0; + Assert.AreEqual((ushort)420, stream.ReadUInt16()); + + stream.Position = 0; + stream.Write((ushort)420, Endianness.LittleEndian); + + stream.Position = 0; + Assert.AreEqual((ushort)420, stream.ReadUInt16(Endianness.LittleEndian)); + + stream.Position = 0; + stream.Write((ushort)420, Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual((ushort)420, stream.ReadUInt16(Endianness.BigEndian)); + } + + [TestMethod] + [CLSCompliant(false)] + public void ReadUInt32_WriteUInt32_ShouldBeSymmetric() + { + using var stream = new MemoryStream(); + stream.Write(420U); + + stream.Position = 0; + Assert.AreEqual(420U, stream.ReadUInt32()); + + stream.Position = 0; + stream.Write(420U, Endianness.LittleEndian); + + stream.Position = 0; + Assert.AreEqual(420U, stream.ReadUInt32(Endianness.LittleEndian)); + + stream.Position = 0; + stream.Write(420U, Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420U, stream.ReadUInt32(Endianness.BigEndian)); + } + + [TestMethod] + [CLSCompliant(false)] + public void ReadUInt64_WriteUInt64_ShouldBeSymmetric() + { + using var stream = new MemoryStream(); + stream.Write(420UL); + + stream.Position = 0; + Assert.AreEqual(420UL, stream.ReadUInt64()); + + stream.Position = 0; + stream.Write(420UL, Endianness.LittleEndian); + + stream.Position = 0; + Assert.AreEqual(420UL, stream.ReadUInt64(Endianness.LittleEndian)); + + stream.Position = 0; + stream.Write(420UL, Endianness.BigEndian); + + stream.Position = 0; + Assert.AreEqual(420UL, stream.ReadUInt64(Endianness.BigEndian)); + } + private class DummyStream : Stream { public DummyStream(bool readable = false) diff --git a/X10D.Tests/src/Time/TimeSpanParserTests.cs b/X10D.Tests/src/Time/TimeSpanParserTests.cs new file mode 100644 index 0000000..b671070 --- /dev/null +++ b/X10D.Tests/src/Time/TimeSpanParserTests.cs @@ -0,0 +1,15 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using X10D.Time; + +namespace X10D.Tests.Time; + +[TestClass] +public class TimeSpanParserTests +{ + [TestMethod] + public void TryParse_ShouldThrow_GivenNullString() + { + string? value = null; + Assert.ThrowsException(() => TimeSpanParser.TryParse(value!, out _)); + } +} diff --git a/X10D/src/IO/StreamExtensions.cs b/X10D/src/IO/StreamExtensions.cs index ce8a054..ab353de 100644 --- a/X10D/src/IO/StreamExtensions.cs +++ b/X10D/src/IO/StreamExtensions.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Buffers.Binary; +using System.Reflection; using System.Security.Cryptography; namespace X10D.IO; @@ -8,6 +9,9 @@ namespace X10D.IO; /// public static class StreamExtensions { + private static readonly Endianness DefaultEndianness = + BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian; + /// /// Returns the hash of the current stream as an array of bytes using the specified hash algorithm. /// @@ -57,6 +61,364 @@ public static class StreamExtensions return crypt.ComputeHash(stream); } + /// + /// Reads a decimal value from the current stream using the system's default endian encoding, and advances the stream + /// position by sixteen bytes. + /// + /// The stream to read. + /// A sixteen-byte decimal value read from the stream. + public static decimal ReadDecimal(this Stream stream) + { + return stream.ReadDecimal(DefaultEndianness); + } + + /// + /// Reads a decimal value from the current stream using a specified endian encoding, and advances the stream position + /// by sixteen bytes. + /// + /// The stream from which the value should be read. + /// The endian encoding to use. + /// A decimal value read from the stream. + public static decimal ReadDecimal(this Stream stream, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + const int decimalSize = sizeof(decimal); + const int int32Size = sizeof(int); + const int partitionSize = decimalSize / int32Size; + + var bits = new int[partitionSize]; + for (var index = 0; index < partitionSize; index++) + { + bits[index] = stream.ReadInt32(endianness); + } + + if (endianness != DefaultEndianness) + { + Array.Reverse(bits); + } + + return new decimal(bits); + } + + /// + /// Reads a double-precision floating point value from the current stream using the system's default endian encoding, + /// and advances the stream position by eight bytes. + /// + /// The stream from which the value should be read. + /// A double-precision floating point value read from the stream. + public static double ReadDouble(this Stream stream) + { + return stream.ReadDouble(DefaultEndianness); + } + + /// + /// Reads a double-precision floating point value from the current stream using a specified endian encoding, and + /// advances the stream position by eight bytes. + /// + /// The stream from which the value should be read. + /// The endian encoding to use. + /// A double-precision floating point value read from the stream. + public static double ReadDouble(this Stream stream, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(double)]; + stream.Read(buffer); + + return endianness == Endianness.LittleEndian + ? BinaryPrimitives.ReadDoubleLittleEndian(buffer) + : BinaryPrimitives.ReadDoubleBigEndian(buffer); + } + + /// + /// Reads a two-byte signed integer from the current stream using the system's default endian encoding, and advances + /// the stream position by two bytes. + /// + /// The stream from which the value should be read. + /// An two-byte signed integer read from the stream. + public static short ReadInt16(this Stream stream) + { + return stream.ReadInt16(DefaultEndianness); + } + + /// + /// Reads a two-byte signed integer from the current stream using the specified endian encoding, and advances the + /// stream position by two bytes. + /// + /// The stream from which the value should be read. + /// The endian encoding to use. + /// An two-byte unsigned integer read from the stream. + public static short ReadInt16(this Stream stream, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(short)]; + stream.Read(buffer); + + return endianness == Endianness.LittleEndian + ? BinaryPrimitives.ReadInt16LittleEndian(buffer) + : BinaryPrimitives.ReadInt16BigEndian(buffer); + } + + /// + /// Reads a four-byte signed integer from the current stream using the system's default endian encoding, and advances + /// the stream position by four bytes. + /// + /// The stream from which the value should be read. + /// An four-byte signed integer read from the stream. + public static int ReadInt32(this Stream stream) + { + return stream.ReadInt32(DefaultEndianness); + } + + /// + /// Reads a four-byte signed integer from the current stream using the specified endian encoding, and advances the + /// stream position by four bytes. + /// + /// The stream from which the value should be read. + /// The endian encoding to use. + /// An four-byte unsigned integer read from the stream. + public static int ReadInt32(this Stream stream, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(int)]; + stream.Read(buffer); + + return endianness == Endianness.LittleEndian + ? BinaryPrimitives.ReadInt32LittleEndian(buffer) + : BinaryPrimitives.ReadInt32BigEndian(buffer); + } + + /// + /// Reads an eight-byte signed integer from the current stream using the system's default endian encoding, and + /// advances the stream position by eight bytes. + /// + /// The stream from which the value should be read. + /// An eight-byte signed integer read from the stream. + public static long ReadInt64(this Stream stream) + { + return stream.ReadInt64(DefaultEndianness); + } + + /// + /// Reads an eight-byte signed integer from the current stream using the specified endian encoding, and advances the + /// stream position by eight bytes. + /// + /// The stream from which the value should be read. + /// The endian encoding to use. + /// An eight-byte unsigned integer read from the stream. + public static long ReadInt64(this Stream stream, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(long)]; + stream.Read(buffer); + + return endianness == Endianness.LittleEndian + ? BinaryPrimitives.ReadInt64LittleEndian(buffer) + : BinaryPrimitives.ReadInt64BigEndian(buffer); + } + + /// + /// Reads a single-precision floating point value from the current stream using the system's default endian encoding, + /// and advances the stream position by four bytes. + /// + /// The stream from which the value should be read. + /// A single-precision floating point value read from the stream. + public static double ReadSingle(this Stream stream) + { + return stream.ReadSingle(DefaultEndianness); + } + + /// + /// Reads a double-precision floating point value from the current stream using a specified endian encoding, and + /// advances the stream position by four bytes. + /// + /// The stream from which the value should be read. + /// The endian encoding to use. + /// A single-precision floating point value read from the stream. + public static double ReadSingle(this Stream stream, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(float)]; + stream.Read(buffer); + + return endianness == Endianness.LittleEndian + ? BinaryPrimitives.ReadSingleLittleEndian(buffer) + : BinaryPrimitives.ReadSingleBigEndian(buffer); + } + + /// + /// Reads a two-byte unsigned integer from the current stream using the system's default endian encoding, and advances + /// the stream position by two bytes. + /// + /// The stream from which the value should be read. + /// An two-byte unsigned integer read from the stream. + [CLSCompliant(false)] + public static ushort ReadUInt16(this Stream stream) + { + return stream.ReadUInt16(DefaultEndianness); + } + + /// + /// Reads a two-byte unsigned integer from the current stream using the specified endian encoding, and advances the + /// stream position by two bytes. + /// + /// The stream from which the value should be read. + /// The endian encoding to use. + /// An two-byte unsigned integer read from the stream. + [CLSCompliant(false)] + public static ushort ReadUInt16(this Stream stream, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(ushort)]; + stream.Read(buffer); + + return endianness == Endianness.LittleEndian + ? BinaryPrimitives.ReadUInt16LittleEndian(buffer) + : BinaryPrimitives.ReadUInt16BigEndian(buffer); + } + + /// + /// Reads a four-byte unsigned integer from the current stream using the system's default endian encoding, and + /// advances the stream position by four bytes. + /// + /// The stream from which the value should be read. + /// An four-byte unsigned integer read from the stream. + [CLSCompliant(false)] + public static uint ReadUInt32(this Stream stream) + { + return stream.ReadUInt32(DefaultEndianness); + } + + /// + /// Reads a four-byte unsigned integer from the current stream using the specified endian encoding, and advances the + /// stream position by four bytes. + /// + /// The stream from which the value should be read. + /// The endian encoding to use. + /// An four-byte unsigned integer read from the stream. + [CLSCompliant(false)] + public static uint ReadUInt32(this Stream stream, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(uint)]; + stream.Read(buffer); + + return endianness == Endianness.LittleEndian + ? BinaryPrimitives.ReadUInt32LittleEndian(buffer) + : BinaryPrimitives.ReadUInt32BigEndian(buffer); + } + + /// + /// Reads an eight-byte unsigned integer from the current stream using the system's default endian encoding, and + /// advances the stream position by eight bytes. + /// + /// The stream from which the value should be read. + /// An eight-byte unsigned integer read from the stream. + [CLSCompliant(false)] + public static ulong ReadUInt64(this Stream stream) + { + return stream.ReadUInt64(DefaultEndianness); + } + + /// + /// Reads an eight-byte unsigned integer from the current stream using the specified endian encoding, and advances the + /// stream position by eight bytes. + /// + /// The stream from which the value should be read. + /// The endian encoding to use. + /// An eight-byte unsigned integer read from the stream. + [CLSCompliant(false)] + public static ulong ReadUInt64(this Stream stream, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(ulong)]; + stream.Read(buffer); + + return endianness == Endianness.LittleEndian + ? BinaryPrimitives.ReadUInt64LittleEndian(buffer) + : BinaryPrimitives.ReadUInt64BigEndian(buffer); + } + /// /// Returns the hash of the current stream as an array of bytes using the specified hash algorithm. /// @@ -119,4 +481,397 @@ public static class StreamExtensions _ = stream.Read(buffer); // we don't care about the number of bytes read. we can ignore MustUseReturnValue return crypt.TryComputeHash(buffer, destination, out bytesWritten); } + + /// + /// Writes a two-byte signed integer to the current stream using the system's default endian encoding, and advances + /// the stream position by two bytes. + /// + /// The stream to which the value should be written. + /// The two-byte signed integer to write. + /// The number of bytes written to the stream. + public static int Write(this Stream stream, short value) + { + return stream.Write(value, DefaultEndianness); + } + + /// + /// Writes a two-byte signed integer to the current stream using the specified endian encoding, and advances the + /// stream position by two bytes. + /// + /// The stream to which the value should be written. + /// The two-byte signed integer to write. + /// The endian encoding to use. + /// The number of bytes written to the stream. + public static int Write(this Stream stream, short value, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(short)]; + + if (endianness == Endianness.LittleEndian) + { + BinaryPrimitives.WriteInt16LittleEndian(buffer, value); + } + else + { + BinaryPrimitives.WriteInt16BigEndian(buffer, value); + } + + return stream.WriteInternal(buffer); + } + + /// + /// Writes a four-byte signed integer to the current stream using the system's default endian encoding, and advances + /// the stream position by four bytes. + /// + /// The stream to which the value should be written. + /// The four-byte signed integer to write. + /// The number of bytes written to the stream. + public static int Write(this Stream stream, int value) + { + return stream.Write(value, DefaultEndianness); + } + + /// + /// Writes a four-byte signed integer to the current stream using the specified endian encoding, and advances the + /// stream position by four bytes. + /// + /// The stream to which the value should be written. + /// The four-byte signed integer to write. + /// The endian encoding to use. + /// The number of bytes written to the stream. + public static int Write(this Stream stream, int value, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(int)]; + + if (endianness == Endianness.LittleEndian) + { + BinaryPrimitives.WriteInt32LittleEndian(buffer, value); + } + else + { + BinaryPrimitives.WriteInt32BigEndian(buffer, value); + } + + return stream.WriteInternal(buffer); + } + + /// + /// Writes an eight-byte signed integer to the current stream using the system's default endian encoding, and advances + /// the stream position by eight bytes. + /// + /// The stream to which the value should be written. + /// The eight-byte signed integer to write. + /// The number of bytes written to the stream. + public static int Write(this Stream stream, long value) + { + return stream.Write(value, DefaultEndianness); + } + + /// + /// Writes an eight-byte signed integer to the current stream using the specified endian encoding, and advances the + /// stream position by eight bytes. + /// + /// The stream to which the value should be written. + /// The eight-byte signed integer to write. + /// The endian encoding to use. + /// The number of bytes written to the stream. + public static int Write(this Stream stream, long value, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(long)]; + + if (endianness == Endianness.LittleEndian) + { + BinaryPrimitives.WriteInt64LittleEndian(buffer, value); + } + else + { + BinaryPrimitives.WriteInt64BigEndian(buffer, value); + } + + return stream.WriteInternal(buffer); + } + + /// + /// Writes a two-byte unsigned integer to the current stream using the system's default endian encoding, and advances + /// the stream position by two bytes. + /// + /// The stream to which the value should be written. + /// The two-byte unsigned integer to write. + /// The number of bytes written to the stream. + [CLSCompliant(false)] + public static int Write(this Stream stream, ushort value) + { + return stream.Write(value, DefaultEndianness); + } + + /// + /// Writes a two-byte unsigned integer to the current stream using the specified endian encoding, and advances the + /// stream position by two bytes. + /// + /// The stream to which the value should be written. + /// The two-byte unsigned integer to write. + /// The endian encoding to use. + /// The number of bytes written to the stream. + [CLSCompliant(false)] + public static int Write(this Stream stream, ushort value, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(ushort)]; + + if (endianness == Endianness.LittleEndian) + { + BinaryPrimitives.WriteUInt16LittleEndian(buffer, value); + } + else + { + BinaryPrimitives.WriteUInt16BigEndian(buffer, value); + } + + return stream.WriteInternal(buffer); + } + + /// + /// Writes a four-byte unsigned integer to the current stream using the system's default endian encoding, and advances + /// the stream position by four bytes. + /// + /// The stream to which the value should be written. + /// The four-byte unsigned integer to write. + /// The number of bytes written to the stream. + [CLSCompliant(false)] + public static int Write(this Stream stream, uint value) + { + return stream.Write(value, DefaultEndianness); + } + + /// + /// Writes a four-byte unsigned integer to the current stream using the specified endian encoding, and advances the + /// stream position by four bytes. + /// + /// The stream to which the value should be written. + /// The four-byte unsigned integer to write. + /// The endian encoding to use. + /// The number of bytes written to the stream. + [CLSCompliant(false)] + public static int Write(this Stream stream, uint value, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(uint)]; + + if (endianness == Endianness.LittleEndian) + { + BinaryPrimitives.WriteUInt32LittleEndian(buffer, value); + } + else + { + BinaryPrimitives.WriteUInt32BigEndian(buffer, value); + } + + return stream.WriteInternal(buffer); + } + + /// + /// Writes an eight-byte unsigned integer to the current stream using the system's default endian encoding, and + /// advances the stream position by eight bytes. + /// + /// The stream to which the value should be written. + /// The eight-byte unsigned integer to write. + /// The number of bytes written to the stream. + [CLSCompliant(false)] + public static int Write(this Stream stream, ulong value) + { + return stream.Write(value, DefaultEndianness); + } + + /// + /// Writes an eight-byte signed integer to the current stream using the specified endian encoding, and advances the + /// stream position by eight bytes. + /// + /// The stream to which the value should be written. + /// The eight-byte signed integer to write. + /// The endian encoding to use. + /// The number of bytes written to the stream. + [CLSCompliant(false)] + public static int Write(this Stream stream, ulong value, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(ulong)]; + + if (endianness == Endianness.LittleEndian) + { + BinaryPrimitives.WriteUInt64LittleEndian(buffer, value); + } + else + { + BinaryPrimitives.WriteUInt64BigEndian(buffer, value); + } + + return stream.WriteInternal(buffer); + } + + /// + /// Writes a single-precision floating point value to the current stream using the specified endian encoding, and + /// advances the stream position by four bytes. + /// + /// The stream to which the value should be written. + /// The single-precision floating point value to write. + /// The endian encoding to use. + /// The number of bytes written to the stream. + public static int Write(this Stream stream, float value, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(float)]; + + if (endianness == Endianness.LittleEndian) + { + BinaryPrimitives.WriteSingleLittleEndian(buffer, value); + } + else + { + BinaryPrimitives.WriteSingleBigEndian(buffer, value); + } + + return stream.WriteInternal(buffer); + } + + /// + /// Writes a double-precision floating point value to the current stream using the specified endian encoding, and + /// advances the stream position by eight bytes. + /// + /// The stream to which the value should be written. + /// The double-precision floating point value to write. + /// The endian encoding to use. + /// The number of bytes written to the stream. + public static int Write(this Stream stream, double value, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + Span buffer = stackalloc byte[sizeof(double)]; + + if (endianness == Endianness.LittleEndian) + { + BinaryPrimitives.WriteDoubleLittleEndian(buffer, value); + } + else + { + BinaryPrimitives.WriteDoubleBigEndian(buffer, value); + } + + return stream.WriteInternal(buffer); + } + + /// + /// Writes a decimal value to the current stream using the specified endian encoding, and advances the stream position + /// by sixteen bytes. + /// + /// The stream to which the value should be written. + /// The decimal value to write. + /// The endian encoding to use. + /// The number of bytes written to the stream. + public static int Write(this Stream stream, decimal value, Endianness endianness) + { + if (stream is null) + { + throw new ArgumentNullException(nameof(stream)); + } + + if (!Enum.IsDefined(endianness)) + { + throw new ArgumentOutOfRangeException(nameof(endianness)); + } + + int[] bits = decimal.GetBits(value); + long preWritePosition = stream.Position; + + if (endianness != DefaultEndianness) + { + Array.Reverse(bits); + } + + foreach (int section in bits) + { + stream.Write(section, endianness); + } + + return (int)(stream.Position - preWritePosition); + } + + private static int WriteInternal(this Stream stream, Span value) + { + long preWritePosition = stream.Position; + stream.Write(value); + return (int)(stream.Position - preWritePosition); + } } diff --git a/X10D/src/StreamExtensions/StreamExtensions.Internal.cs b/X10D/src/StreamExtensions/StreamExtensions.Internal.cs deleted file mode 100644 index d87cbde..0000000 --- a/X10D/src/StreamExtensions/StreamExtensions.Internal.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace X10D; - -public static partial class StreamExtensions -{ - private static unsafe byte[] ReadInternal(this Stream stream, Endianness endianness) - where T : unmanaged - { - var buffer = new byte[sizeof(T)]; - stream.Read(buffer, 0, buffer.Length); - SwapIfNeeded(ref buffer, endianness); - return buffer; - } - - private static void SwapIfNeeded(ref byte[] buffer, Endianness endianness) - { - bool swapNeeded = BitConverter.IsLittleEndian == (endianness == Endianness.BigEndian); - if (swapNeeded) - { - Array.Reverse(buffer); - } - } - - private static int WriteInternal(this Stream stream, byte[] value, Endianness endianness) - { - var clone = (byte[])value.Clone(); - SwapIfNeeded(ref clone, endianness); - long preWritePosition = stream.Position; - stream.Write(clone, 0, clone.Length); - return (int)(stream.Position - preWritePosition); - } - - private static int WriteInternal(this Stream stream, Span value) - { - long preWritePosition = stream.Position; - stream.Write(value); - return (int)(stream.Position - preWritePosition); - } -} diff --git a/X10D/src/StreamExtensions/StreamExtensions.cs b/X10D/src/StreamExtensions/StreamExtensions.cs deleted file mode 100644 index c3f6cc2..0000000 --- a/X10D/src/StreamExtensions/StreamExtensions.cs +++ /dev/null @@ -1,746 +0,0 @@ -using System.Buffers.Binary; - -namespace X10D; - -/// -/// Extension methods for . -/// -public static partial class StreamExtensions -{ - private static readonly Endianness DefaultEndianness = - BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian; - - /// - /// Reads a decimal value from the current stream using the system's default endian encoding, and advances the stream - /// position by sixteen bytes. - /// - /// The stream to read. - /// A sixteen-byte decimal value read from the stream. - public static decimal ReadDecimal(this Stream stream) - { - return stream.ReadDecimal(DefaultEndianness); - } - - /// - /// Reads a decimal value from the current stream using a specified endian encoding, and advances the stream position - /// by sixteen bytes. - /// - /// The stream from which the value should be read. - /// The endian encoding to use. - /// A decimal value read from the stream. - public static decimal ReadDecimal(this Stream stream, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - const int decimalSize = sizeof(decimal); - const int int32Size = sizeof(int); - const int partitionSize = decimalSize / int32Size; - - var bits = new int[partitionSize]; - for (var index = 0; index < partitionSize; index++) - { - bits[index] = stream.ReadInt32(endianness); - } - - return new decimal(bits); - } - - /// - /// Reads a double-precision floating point value from the current stream using the system's default endian encoding, - /// and advances the stream position by eight bytes. - /// - /// The stream from which the value should be read. - /// A double-precision floating point value read from the stream. - public static double ReadDouble(this Stream stream) - { - return stream.ReadDouble(DefaultEndianness); - } - - /// - /// Reads a double-precision floating point value from the current stream using a specified endian encoding, and - /// advances the stream position by eight bytes. - /// - /// The stream from which the value should be read. - /// The endian encoding to use. - /// A double-precision floating point value read from the stream. - public static double ReadDouble(this Stream stream, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(int)]; - stream.Read(buffer); - - return endianness == Endianness.LittleEndian - ? BinaryPrimitives.ReadDoubleLittleEndian(buffer) - : BinaryPrimitives.ReadDoubleBigEndian(buffer); - } - - /// - /// Reads a two-byte signed integer from the current stream using the system's default endian encoding, and advances - /// the stream position by two bytes. - /// - /// The stream from which the value should be read. - /// An two-byte signed integer read from the stream. - public static short ReadInt16(this Stream stream) - { - return stream.ReadInt16(DefaultEndianness); - } - - /// - /// Reads a two-byte signed integer from the current stream using the specified endian encoding, and advances the - /// stream position by two bytes. - /// - /// The stream from which the value should be read. - /// The endian encoding to use. - /// An two-byte unsigned integer read from the stream. - public static short ReadInt16(this Stream stream, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(int)]; - stream.Read(buffer); - - return endianness == Endianness.LittleEndian - ? BinaryPrimitives.ReadInt16LittleEndian(buffer) - : BinaryPrimitives.ReadInt16BigEndian(buffer); - } - - /// - /// Reads a four-byte signed integer from the current stream using the system's default endian encoding, and advances - /// the stream position by four bytes. - /// - /// The stream from which the value should be read. - /// An four-byte signed integer read from the stream. - public static int ReadInt32(this Stream stream) - { - return stream.ReadInt32(DefaultEndianness); - } - - /// - /// Reads a four-byte signed integer from the current stream using the specified endian encoding, and advances the - /// stream position by four bytes. - /// - /// The stream from which the value should be read. - /// The endian encoding to use. - /// An four-byte unsigned integer read from the stream. - public static int ReadInt32(this Stream stream, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(int)]; - stream.Read(buffer); - - return endianness == Endianness.LittleEndian - ? BinaryPrimitives.ReadInt32LittleEndian(buffer) - : BinaryPrimitives.ReadInt32BigEndian(buffer); - } - - /// - /// Reads an eight-byte signed integer from the current stream using the system's default endian encoding, and - /// advances the stream position by eight bytes. - /// - /// The stream from which the value should be read. - /// An eight-byte signed integer read from the stream. - public static long ReadInt64(this Stream stream) - { - return stream.ReadInt64(DefaultEndianness); - } - - /// - /// Reads an eight-byte signed integer from the current stream using the specified endian encoding, and advances the - /// stream position by eight bytes. - /// - /// The stream from which the value should be read. - /// The endian encoding to use. - /// An eight-byte unsigned integer read from the stream. - public static long ReadInt64(this Stream stream, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(int)]; - stream.Read(buffer); - - return endianness == Endianness.LittleEndian - ? BinaryPrimitives.ReadInt64LittleEndian(buffer) - : BinaryPrimitives.ReadInt64BigEndian(buffer); - } - - /// - /// Reads a single-precision floating point value from the current stream using the system's default endian encoding, - /// and advances the stream position by four bytes. - /// - /// The stream from which the value should be read. - /// A single-precision floating point value read from the stream. - public static double ReadSingle(this Stream stream) - { - return stream.ReadSingle(DefaultEndianness); - } - - /// - /// Reads a double-precision floating point value from the current stream using a specified endian encoding, and - /// advances the stream position by four bytes. - /// - /// The stream from which the value should be read. - /// The endian encoding to use. - /// A single-precision floating point value read from the stream. - public static double ReadSingle(this Stream stream, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(int)]; - stream.Read(buffer); - - return endianness == Endianness.LittleEndian - ? BinaryPrimitives.ReadSingleLittleEndian(buffer) - : BinaryPrimitives.ReadSingleBigEndian(buffer); - } - - /// - /// Reads a two-byte unsigned integer from the current stream using the system's default endian encoding, and advances - /// the stream position by two bytes. - /// - /// The stream from which the value should be read. - /// An two-byte unsigned integer read from the stream. - [CLSCompliant(false)] - public static ushort ReadUInt16(this Stream stream) - { - return stream.ReadUInt16(DefaultEndianness); - } - - /// - /// Reads a two-byte unsigned integer from the current stream using the specified endian encoding, and advances the - /// stream position by two bytes. - /// - /// The stream from which the value should be read. - /// The endian encoding to use. - /// An two-byte unsigned integer read from the stream. - [CLSCompliant(false)] - public static ushort ReadUInt16(this Stream stream, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(int)]; - stream.Read(buffer); - - return endianness == Endianness.LittleEndian - ? BinaryPrimitives.ReadUInt16LittleEndian(buffer) - : BinaryPrimitives.ReadUInt16BigEndian(buffer); - } - - /// - /// Reads a four-byte unsigned integer from the current stream using the system's default endian encoding, and - /// advances the stream position by four bytes. - /// - /// The stream from which the value should be read. - /// An four-byte unsigned integer read from the stream. - [CLSCompliant(false)] - public static uint ReadUInt32(this Stream stream) - { - return stream.ReadUInt32(DefaultEndianness); - } - - /// - /// Reads a four-byte unsigned integer from the current stream using the specified endian encoding, and advances the - /// stream position by four bytes. - /// - /// The stream from which the value should be read. - /// The endian encoding to use. - /// An four-byte unsigned integer read from the stream. - [CLSCompliant(false)] - public static uint ReadUInt32(this Stream stream, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(int)]; - stream.Read(buffer); - - return endianness == Endianness.LittleEndian - ? BinaryPrimitives.ReadUInt32LittleEndian(buffer) - : BinaryPrimitives.ReadUInt32BigEndian(buffer); - } - - /// - /// Reads an eight-byte unsigned integer from the current stream using the system's default endian encoding, and - /// advances the stream position by eight bytes. - /// - /// The stream from which the value should be read. - /// An eight-byte unsigned integer read from the stream. - [CLSCompliant(false)] - public static ulong ReadUInt64(this Stream stream) - { - return stream.ReadUInt64(DefaultEndianness); - } - - /// - /// Reads an eight-byte unsigned integer from the current stream using the specified endian encoding, and advances the - /// stream position by eight bytes. - /// - /// The stream from which the value should be read. - /// The endian encoding to use. - /// An eight-byte unsigned integer read from the stream. - [CLSCompliant(false)] - public static ulong ReadUInt64(this Stream stream, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(int)]; - stream.Read(buffer); - - return endianness == Endianness.LittleEndian - ? BinaryPrimitives.ReadUInt64LittleEndian(buffer) - : BinaryPrimitives.ReadUInt64BigEndian(buffer); - } - - /// - /// Writes a two-byte signed integer to the current stream using the system's default endian encoding, and advances - /// the stream position by two bytes. - /// - /// The stream to which the value should be written. - /// The two-byte signed integer to write. - /// The number of bytes written to the stream. - public static int Write(this Stream stream, short value) - { - return stream.Write(value, DefaultEndianness); - } - - /// - /// Writes a two-byte signed integer to the current stream using the specified endian encoding, and advances the - /// stream position by two bytes. - /// - /// The stream to which the value should be written. - /// The two-byte signed integer to write. - /// The endian encoding to use. - /// The number of bytes written to the stream. - public static int Write(this Stream stream, short value, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(short)]; - - if (endianness == Endianness.LittleEndian) - { - BinaryPrimitives.WriteInt16LittleEndian(buffer, value); - } - else - { - BinaryPrimitives.WriteInt16BigEndian(buffer, value); - } - - return stream.WriteInternal(buffer); - } - - /// - /// Writes a four-byte signed integer to the current stream using the system's default endian encoding, and advances - /// the stream position by four bytes. - /// - /// The stream to which the value should be written. - /// The four-byte signed integer to write. - /// The number of bytes written to the stream. - public static int Write(this Stream stream, int value) - { - return stream.Write(value, DefaultEndianness); - } - - /// - /// Writes a four-byte signed integer to the current stream using the specified endian encoding, and advances the - /// stream position by four bytes. - /// - /// The stream to which the value should be written. - /// The four-byte signed integer to write. - /// The endian encoding to use. - /// The number of bytes written to the stream. - public static int Write(this Stream stream, int value, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(int)]; - - if (endianness == Endianness.LittleEndian) - { - BinaryPrimitives.WriteInt32LittleEndian(buffer, value); - } - else - { - BinaryPrimitives.WriteInt32BigEndian(buffer, value); - } - - return stream.WriteInternal(buffer); - } - - /// - /// Writes an eight-byte signed integer to the current stream using the system's default endian encoding, and advances - /// the stream position by eight bytes. - /// - /// The stream to which the value should be written. - /// The eight-byte signed integer to write. - /// The number of bytes written to the stream. - public static int Write(this Stream stream, long value) - { - return stream.Write(value, DefaultEndianness); - } - - /// - /// Writes an eight-byte signed integer to the current stream using the specified endian encoding, and advances the - /// stream position by eight bytes. - /// - /// The stream to which the value should be written. - /// The eight-byte signed integer to write. - /// The endian encoding to use. - /// The number of bytes written to the stream. - public static int Write(this Stream stream, long value, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(long)]; - - if (endianness == Endianness.LittleEndian) - { - BinaryPrimitives.WriteInt64LittleEndian(buffer, value); - } - else - { - BinaryPrimitives.WriteInt64BigEndian(buffer, value); - } - - return stream.WriteInternal(buffer); - } - - /// - /// Writes a two-byte unsigned integer to the current stream using the system's default endian encoding, and advances - /// the stream position by two bytes. - /// - /// The stream to which the value should be written. - /// The two-byte unsigned integer to write. - /// The number of bytes written to the stream. - [CLSCompliant(false)] - public static int Write(this Stream stream, ushort value) - { - return stream.Write(value, DefaultEndianness); - } - - /// - /// Writes a two-byte unsigned integer to the current stream using the specified endian encoding, and advances the - /// stream position by two bytes. - /// - /// The stream to which the value should be written. - /// The two-byte unsigned integer to write. - /// The endian encoding to use. - /// The number of bytes written to the stream. - [CLSCompliant(false)] - public static int Write(this Stream stream, ushort value, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(ushort)]; - - if (endianness == Endianness.LittleEndian) - { - BinaryPrimitives.WriteUInt16LittleEndian(buffer, value); - } - else - { - BinaryPrimitives.WriteUInt16BigEndian(buffer, value); - } - - return stream.WriteInternal(buffer); - } - - /// - /// Writes a four-byte unsigned integer to the current stream using the system's default endian encoding, and advances - /// the stream position by four bytes. - /// - /// The stream to which the value should be written. - /// The four-byte unsigned integer to write. - /// The number of bytes written to the stream. - [CLSCompliant(false)] - public static int Write(this Stream stream, uint value) - { - return stream.Write(value, DefaultEndianness); - } - - /// - /// Writes a four-byte unsigned integer to the current stream using the specified endian encoding, and advances the - /// stream position by four bytes. - /// - /// The stream to which the value should be written. - /// The four-byte unsigned integer to write. - /// The endian encoding to use. - /// The number of bytes written to the stream. - [CLSCompliant(false)] - public static int Write(this Stream stream, uint value, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(uint)]; - - if (endianness == Endianness.LittleEndian) - { - BinaryPrimitives.WriteUInt32LittleEndian(buffer, value); - } - else - { - BinaryPrimitives.WriteUInt32BigEndian(buffer, value); - } - - return stream.WriteInternal(buffer); - } - - /// - /// Writes an eight-byte unsigned integer to the current stream using the system's default endian encoding, and - /// advances the stream position by eight bytes. - /// - /// The stream to which the value should be written. - /// The eight-byte unsigned integer to write. - /// The number of bytes written to the stream. - [CLSCompliant(false)] - public static int Write(this Stream stream, ulong value) - { - return stream.Write(value, DefaultEndianness); - } - - /// - /// Writes an eight-byte signed integer to the current stream using the specified endian encoding, and advances the - /// stream position by eight bytes. - /// - /// The stream to which the value should be written. - /// The eight-byte signed integer to write. - /// The endian encoding to use. - /// The number of bytes written to the stream. - [CLSCompliant(false)] - public static int Write(this Stream stream, ulong value, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(ulong)]; - - if (endianness == Endianness.LittleEndian) - { - BinaryPrimitives.WriteUInt64LittleEndian(buffer, value); - } - else - { - BinaryPrimitives.WriteUInt64BigEndian(buffer, value); - } - - return stream.WriteInternal(buffer); - } - - /// - /// Writes a single-precision floating point value to the current stream using the specified endian encoding, and - /// advances the stream position by four bytes. - /// - /// The stream to which the value should be written. - /// The single-precision floating point value to write. - /// The endian encoding to use. - /// The number of bytes written to the stream. - public static int Write(this Stream stream, float value, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(float)]; - - if (endianness == Endianness.LittleEndian) - { - BinaryPrimitives.WriteSingleLittleEndian(buffer, value); - } - else - { - BinaryPrimitives.WriteSingleBigEndian(buffer, value); - } - - return stream.WriteInternal(buffer); - } - - /// - /// Writes a double-precision floating point value to the current stream using the specified endian encoding, and - /// advances the stream position by eight bytes. - /// - /// The stream to which the value should be written. - /// The double-precision floating point value to write. - /// The endian encoding to use. - /// The number of bytes written to the stream. - public static int Write(this Stream stream, double value, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - Span buffer = stackalloc byte[sizeof(double)]; - - if (endianness == Endianness.LittleEndian) - { - BinaryPrimitives.WriteDoubleLittleEndian(buffer, value); - } - else - { - BinaryPrimitives.WriteDoubleBigEndian(buffer, value); - } - - return stream.WriteInternal(buffer); - } - - /// - /// Writes a decimal value to the current stream using the specified endian encoding, and advances the stream position - /// by sixteen bytes. - /// - /// The stream to which the value should be written. - /// The decimal value to write. - /// The endian encoding to use. - /// The number of bytes written to the stream. - public static int Write(this Stream stream, decimal value, Endianness endianness) - { - if (stream is null) - { - throw new ArgumentNullException(nameof(stream)); - } - - if (!Enum.IsDefined(typeof(Endianness), endianness)) - { - throw new ArgumentOutOfRangeException(nameof(endianness)); - } - - int[]? bits = decimal.GetBits(value); - long preWritePosition = stream.Position; - - foreach (int section in bits) - { - stream.Write(section, endianness); - } - - return (int)(stream.Position - preWritePosition); - } -}