1
0
mirror of https://github.com/oliverbooth/X10D synced 2024-11-09 22:55:42 +00:00

Move Stream extensions to IO namespace (#7)

* Finalizes unit tests, 99% coverage
* Update CHANGELOG
This commit is contained in:
Oliver Booth 2022-04-30 13:52:34 +01:00
parent 842053d571
commit 6505299c00
No known key found for this signature in database
GPG Key ID: 32A00B35503AF634
6 changed files with 1221 additions and 952 deletions

View File

@ -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<T>` and `ReadOnlySpan<T>`, 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<T>()`
- Added `FileInfo.TryWriteHash<T>(Span<byte>, out int)`
- Added `IEnumerable<TSource>.Product()` and `IEnumerable<TSource>.Product<TResult>(Func<TSource>, TResult)` for all built-in numeric types, computing the product of all (transformed) elements
- Added `IEnumerable<T>.Shuffled([Random])`, which wraps `IList<T>.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<T>()`
- 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<T>()`
- Added `FileInfo.TryWriteHash<T>(Span<byte>, out int)`
- Added `IComparable<T>.Clamp(T, T)` - this supersedes `Clamp` defined for hard-coded numeric types (#24)
- Added `IComparable<T1>.GreaterThan(T2)` (#22)
- Added `IComparable<T1>.GreaterThanOrEqualTo(T2)` (#22)
- Added `IComparable<T1>.LessThan(T2)` (#22)
- Added `IComparable<T1>.LessThanOrEqualTo(T2)` (#22)
- Added `IComparable<T1>.Max(T)` (#23)
- Added `IComparable<T1>.Min(T)` (#23)
- Added `IDictionary<TKey, TValue>.AddOrUpdate()`
- Added `IEnumerable<TSource>.Product()` and `IEnumerable<TSource>.Product<TResult>(Func<TSource>, TResult)` for all built-in numeric types, computing the product of all (optionally transformed) elements
- Added `IList<T>.Fill(T)` and `IList<T>.Fill(T, int, int)`
- Added `IPAddress.IsIPv4()` and `IPAddress.IsIPv6()`
- Added `IReadOnlyList<byte>.Pack8Bit()`
- Added `IReadOnlyList<byte>.Pack16Bit()`
- Added `IReadOnlyList<byte>.Pack32Bit()`
- Added `IReadOnlyList<byte>.Pack64Bit()`
- Added `MemberInfo.HasCustomAttribute<T>()` and `MemberInfo.HasCustomAttribute(Type)`
- Added `defaultValue` overload for `MemberInfo.SelectFromCustomAttribute<TAttr, TReturn>()`
- Added `Random.Next<T>()` (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<T>.Clamp(T, T)` which supersedes `short.Clamp(short, short)`, `int.Clamp(int, int)`,
and `long.Clamp(long, long)`
- Added `IComparable<T>.GreaterThan(T)`
- Added `IComparable<T>.GreaterOrEqualTo(T)`
- Added `IComparable<T>.LessThan(T)`
- Added `IComparable<T>.LessThanOrEqualTo(T)`
- Added `IComparable<T>.Max(T)`
- Added `IComparable<T>.Min(T)`
- Added `IReadOnlyCollection<T>.Split(int)`
- Yields the same results as `IEnumerable<T>.Split(int)`, except is able to avoid a hidden allocation with the benefit of knowing the collection size ahead of time
- Added `Type.HasCustomAttribute<T>()` and `Type.HasCustomAttribute(Type)`
- Added `Type.Implements<T>()` and `Type.Implements(Type)`
- Added `Type.Inherits<T>()` and `Type.Inherits(Type)`
- Added `Type.SelectFromCustomAttribute<TAttribute, TReturn>()`
- 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<T>()`
- Added `Stream.TryWriteHash<T>(Span<byte>, 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<T>` and `ReadOnlySpan<T>`, mimicking the corresponding methods in `System.Linq`
- Added `Sign()` for built-in numeric types. For unsigned types, this never returns -1
- Added `Type.Implements<T>()` and `Type.Implements(Type)` (#25)
- Added `Type.Inherits<T>()` 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<byte>`
- `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<char>.Random(int)`, and `IEnumerable<char>.Random(int, int)` have been redefined as `Random.NextString(IReadOnlyList<char>, int)`
- `IComparable<T>.Between(T, T)` has been redefined as `IComparable<T1>.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<T1, T2>.ToGetParameters()`, `IDictionary<T1, T2>.ToGetParameters()`, and `IReadOnlyDictionary<T1, T2>.ToGetParameters()`, has been redefined as `IEnumerable<KeyValuePair<T1, T2>>.ToGetParameters()`
- `Dictionary<T1, T2>.ToConnectionString()`, `IDictionary<T1, T2>.ToConnectionString()`, and `IReadOnlyDictionary<T1, T2>.ToConnectionString()`, has been redefined as `IEnumerable<KeyValuePair<T1, T2>>.ToConnectionString()`
- `IList<T>.OneOf([Random])` and `IEnumerable<T>.OneOf([Random])` have been redefined as `Random.NextFrom<T>(IEnumerable<T>)` to fall in line with the naming convention of `System.Random` (#21)
- `IList<T>.Shuffle([Random])` now uses a Fisher-Yates shuffle implementation
- `IList<T>.Shuffle([Random])` has been repurposed to shuffle the list in place, rather than returning a new enumerable
- `IEnumerable<T>.Shuffle([Random])` has been renamed to `IEnumerable<T>.Shuffled([Random])` to avoid confusion with `IList<T>.Shuffle([Random])`
- `Random.CoinToss()` has been redefined as `Random.NextBoolean()` to fall in line with the naming convention of `System.Random`
- `Random.OneOf<T>(T[])` and `Random.OneOf<T>(IList<T>)` have been redefined as `Random.NextFrom<T>(IEnumerable<T>)` 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<byte>.GetString([Encoding])` has been renamed to `IReadOnlyList<byte>.ToString` and its `Encoding` parameter has
been made non-optional
- Fixed a bug where `IEnumerable<KeyValuePair<K,V>>.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<char>.Random(int)`, and `IEnumerable<char>.Random(int, Random)`, as `Random.NextString(IReadOnlyList<char>, int)`
- Improved performance for:
- `string.IsLower()`
- `string.IsUpper()`
- `string.Reverse()`
- `IEnumerable<T>.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<char>.Random(int)`,
and `IEnumerable<char>.Random(int, Random)`, in favour of `Random.NextString(IReadOnlyList<char>, int)`
- `IList<T>.Shuffle([Random])` now uses a Fisher-Yates shuffle implementation
- `IList<T>.Shuffle([Random])` now returns `void`, as it will reorganize the elements in-place on the current `IList<T>`
- `Dictionary`, `IDictionary`, and `IReadOnlyDictionary` extension methods have been consolidated
to `IEnumerable<KeyValuePair<K,V>>`
- Fixed a bug where `IEnumerable<KeyValuePair<K,V>>.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<byte>.GetString([Encoding])` has been renamed to `IReadOnlyList<byte>.ToString` and its `Encoding` parameter has
been made non-optional
- `WaitHandle.WaitOneAsync` now returns `Task<bool>` 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<T>([IFormatProvider])`
- Removed `IConvertible.ToOrDefault<T>([IFormatProvider])`
- Removed `IConvertible.ToOrDefault<T>(out T, [IFormatProvider])`
- Removed `IConvertible.ToOrNull<T>([IFormatProvider])`
- Removed `IConvertible.ToOrNull<T>(out T, [IFormatProvider])`
- Removed `IConvertible.ToOrOther<T>(T, [IFormatProvider])`
- Removed `IConvertible.ToOrOther<T>(T, out T, [IFormatProvider])`
- Removed `IEnumerable<T>.Split(int)` and `IReadOnlyCollection<T>.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<T>([IFormatProvider])` (#13)
- Removed `IConvertible.ToOrDefault<T>([IFormatProvider])` (#13)
- Removed `IConvertible.ToOrDefault<T>(out T, [IFormatProvider])` (#13)
- Removed `IConvertible.ToOrNull<T>([IFormatProvider])` (#13)
- Removed `IConvertible.ToOrNull<T>(out T, [IFormatProvider])` (#13)
- Removed `IConvertible.ToOrOther<T>(T, [IFormatProvider])` (#13)
- Removed `IConvertible.ToOrOther<T>(out T, T, [IFormatProvider])` (#13)
- Removed `IEnumerable<T>.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<T>`. 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

View File

@ -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<IOException>(() =>
{
using var stream = new DummyStream();
stream.GetHash<SHA1>();
});
Assert.ThrowsException<IOException>(() =>
{
using var stream = new DummyStream();
@ -99,6 +99,341 @@ public class StreamTests
Stream.Null.TryWriteHash<HashAlgorithmTestClassNoCreateMethod>(Span<byte>.Empty, out _));
}
[TestMethod]
public void Write_ShouldThrow_GivenUndefinedEndianness()
{
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0.0f, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0.0, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0.0m, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write((short)0, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0L, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write((ushort)0, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0U, (Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.Write(0UL, (Endianness)(-1));
});
}
[TestMethod]
public void Read_ShouldThrow_GivenNullStream()
{
Stream? stream = null;
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadSingle());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadDouble());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadDecimal());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadInt16());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadInt32());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadInt64());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadUInt16());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadUInt32());
Assert.ThrowsException<ArgumentNullException>(() => stream!.ReadUInt64());
}
[TestMethod]
public void Write_ShouldThrow_GivenNullStream()
{
Stream? stream = null;
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0.0f, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0.0, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0.0m, Endianness.LittleEndian));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write((short)0));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0L));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write((ushort)0));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0U));
Assert.ThrowsException<ArgumentNullException>(() => stream!.Write(0UL));
}
[TestMethod]
public void Read_ShouldThrow_GivenUndefinedEndianness()
{
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadSingle((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadDouble((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadDecimal((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadInt16((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadInt32((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadInt64((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadUInt16((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
using var stream = new MemoryStream();
return stream.ReadUInt32((Endianness)(-1));
});
Assert.ThrowsException<ArgumentOutOfRangeException>(() =>
{
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)

View File

@ -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<ArgumentNullException>(() => TimeSpanParser.TryParse(value!, out _));
}
}

View File

@ -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;
/// </summary>
public static class StreamExtensions
{
private static readonly Endianness DefaultEndianness =
BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian;
/// <summary>
/// Returns the hash of the current stream as an array of bytes using the specified hash algorithm.
/// </summary>
@ -57,6 +61,364 @@ public static class StreamExtensions
return crypt.ComputeHash(stream);
}
/// <summary>
/// Reads a decimal value from the current stream using the system's default endian encoding, and advances the stream
/// position by sixteen bytes.
/// </summary>
/// <param name="stream">The stream to read.</param>
/// <returns>A sixteen-byte decimal value read from the stream.</returns>
public static decimal ReadDecimal(this Stream stream)
{
return stream.ReadDecimal(DefaultEndianness);
}
/// <summary>
/// Reads a decimal value from the current stream using a specified endian encoding, and advances the stream position
/// by sixteen bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>A decimal value read from the stream.</returns>
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);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>A double-precision floating point value read from the stream.</returns>
public static double ReadDouble(this Stream stream)
{
return stream.ReadDouble(DefaultEndianness);
}
/// <summary>
/// Reads a double-precision floating point value from the current stream using a specified endian encoding, and
/// advances the stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>A double-precision floating point value read from the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(double)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadDoubleLittleEndian(buffer)
: BinaryPrimitives.ReadDoubleBigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An two-byte signed integer read from the stream.</returns>
public static short ReadInt16(this Stream stream)
{
return stream.ReadInt16(DefaultEndianness);
}
/// <summary>
/// Reads a two-byte signed integer from the current stream using the specified endian encoding, and advances the
/// stream position by two bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An two-byte unsigned integer read from the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(short)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadInt16LittleEndian(buffer)
: BinaryPrimitives.ReadInt16BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An four-byte signed integer read from the stream.</returns>
public static int ReadInt32(this Stream stream)
{
return stream.ReadInt32(DefaultEndianness);
}
/// <summary>
/// Reads a four-byte signed integer from the current stream using the specified endian encoding, and advances the
/// stream position by four bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An four-byte unsigned integer read from the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(int)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadInt32LittleEndian(buffer)
: BinaryPrimitives.ReadInt32BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An eight-byte signed integer read from the stream.</returns>
public static long ReadInt64(this Stream stream)
{
return stream.ReadInt64(DefaultEndianness);
}
/// <summary>
/// Reads an eight-byte signed integer from the current stream using the specified endian encoding, and advances the
/// stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An eight-byte unsigned integer read from the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(long)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadInt64LittleEndian(buffer)
: BinaryPrimitives.ReadInt64BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>A single-precision floating point value read from the stream.</returns>
public static double ReadSingle(this Stream stream)
{
return stream.ReadSingle(DefaultEndianness);
}
/// <summary>
/// Reads a double-precision floating point value from the current stream using a specified endian encoding, and
/// advances the stream position by four bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>A single-precision floating point value read from the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(float)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadSingleLittleEndian(buffer)
: BinaryPrimitives.ReadSingleBigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An two-byte unsigned integer read from the stream.</returns>
[CLSCompliant(false)]
public static ushort ReadUInt16(this Stream stream)
{
return stream.ReadUInt16(DefaultEndianness);
}
/// <summary>
/// Reads a two-byte unsigned integer from the current stream using the specified endian encoding, and advances the
/// stream position by two bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An two-byte unsigned integer read from the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(ushort)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadUInt16LittleEndian(buffer)
: BinaryPrimitives.ReadUInt16BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An four-byte unsigned integer read from the stream.</returns>
[CLSCompliant(false)]
public static uint ReadUInt32(this Stream stream)
{
return stream.ReadUInt32(DefaultEndianness);
}
/// <summary>
/// Reads a four-byte unsigned integer from the current stream using the specified endian encoding, and advances the
/// stream position by four bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An four-byte unsigned integer read from the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(uint)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadUInt32LittleEndian(buffer)
: BinaryPrimitives.ReadUInt32BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An eight-byte unsigned integer read from the stream.</returns>
[CLSCompliant(false)]
public static ulong ReadUInt64(this Stream stream)
{
return stream.ReadUInt64(DefaultEndianness);
}
/// <summary>
/// Reads an eight-byte unsigned integer from the current stream using the specified endian encoding, and advances the
/// stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An eight-byte unsigned integer read from the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(ulong)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadUInt64LittleEndian(buffer)
: BinaryPrimitives.ReadUInt64BigEndian(buffer);
}
/// <summary>
/// Returns the hash of the current stream as an array of bytes using the specified hash algorithm.
/// </summary>
@ -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);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The two-byte signed integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
public static int Write(this Stream stream, short value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes a two-byte signed integer to the current stream using the specified endian encoding, and advances the
/// stream position by two bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The two-byte signed integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(short)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteInt16LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteInt16BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The four-byte signed integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
public static int Write(this Stream stream, int value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes a four-byte signed integer to the current stream using the specified endian encoding, and advances the
/// stream position by four bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The four-byte signed integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(int)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteInt32BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The eight-byte signed integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
public static int Write(this Stream stream, long value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes an eight-byte signed integer to the current stream using the specified endian encoding, and advances the
/// stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The eight-byte signed integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(long)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteInt64BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The two-byte unsigned integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
[CLSCompliant(false)]
public static int Write(this Stream stream, ushort value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes a two-byte unsigned integer to the current stream using the specified endian encoding, and advances the
/// stream position by two bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The two-byte unsigned integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(ushort)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteUInt16LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteUInt16BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The four-byte unsigned integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
[CLSCompliant(false)]
public static int Write(this Stream stream, uint value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes a four-byte unsigned integer to the current stream using the specified endian encoding, and advances the
/// stream position by four bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The four-byte unsigned integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(uint)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteUInt32BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The eight-byte unsigned integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
[CLSCompliant(false)]
public static int Write(this Stream stream, ulong value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes an eight-byte signed integer to the current stream using the specified endian encoding, and advances the
/// stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The eight-byte signed integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(ulong)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteUInt64BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// Writes a single-precision floating point value to the current stream using the specified endian encoding, and
/// advances the stream position by four bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The single-precision floating point value to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(float)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteSingleLittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteSingleBigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// Writes a double-precision floating point value to the current stream using the specified endian encoding, and
/// advances the stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The double-precision floating point value to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(double)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteDoubleLittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteDoubleBigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// Writes a decimal value to the current stream using the specified endian encoding, and advances the stream position
/// by sixteen bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The decimal value to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> value)
{
long preWritePosition = stream.Position;
stream.Write(value);
return (int)(stream.Position - preWritePosition);
}
}

View File

@ -1,38 +0,0 @@
namespace X10D;
public static partial class StreamExtensions
{
private static unsafe byte[] ReadInternal<T>(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<byte> value)
{
long preWritePosition = stream.Position;
stream.Write(value);
return (int)(stream.Position - preWritePosition);
}
}

View File

@ -1,746 +0,0 @@
using System.Buffers.Binary;
namespace X10D;
/// <summary>
/// Extension methods for <see cref="Stream" />.
/// </summary>
public static partial class StreamExtensions
{
private static readonly Endianness DefaultEndianness =
BitConverter.IsLittleEndian ? Endianness.LittleEndian : Endianness.BigEndian;
/// <summary>
/// Reads a decimal value from the current stream using the system's default endian encoding, and advances the stream
/// position by sixteen bytes.
/// </summary>
/// <param name="stream">The stream to read.</param>
/// <returns>A sixteen-byte decimal value read from the stream.</returns>
public static decimal ReadDecimal(this Stream stream)
{
return stream.ReadDecimal(DefaultEndianness);
}
/// <summary>
/// Reads a decimal value from the current stream using a specified endian encoding, and advances the stream position
/// by sixteen bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>A decimal value read from the stream.</returns>
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);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>A double-precision floating point value read from the stream.</returns>
public static double ReadDouble(this Stream stream)
{
return stream.ReadDouble(DefaultEndianness);
}
/// <summary>
/// Reads a double-precision floating point value from the current stream using a specified endian encoding, and
/// advances the stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>A double-precision floating point value read from the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(int)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadDoubleLittleEndian(buffer)
: BinaryPrimitives.ReadDoubleBigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An two-byte signed integer read from the stream.</returns>
public static short ReadInt16(this Stream stream)
{
return stream.ReadInt16(DefaultEndianness);
}
/// <summary>
/// Reads a two-byte signed integer from the current stream using the specified endian encoding, and advances the
/// stream position by two bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An two-byte unsigned integer read from the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(int)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadInt16LittleEndian(buffer)
: BinaryPrimitives.ReadInt16BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An four-byte signed integer read from the stream.</returns>
public static int ReadInt32(this Stream stream)
{
return stream.ReadInt32(DefaultEndianness);
}
/// <summary>
/// Reads a four-byte signed integer from the current stream using the specified endian encoding, and advances the
/// stream position by four bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An four-byte unsigned integer read from the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(int)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadInt32LittleEndian(buffer)
: BinaryPrimitives.ReadInt32BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An eight-byte signed integer read from the stream.</returns>
public static long ReadInt64(this Stream stream)
{
return stream.ReadInt64(DefaultEndianness);
}
/// <summary>
/// Reads an eight-byte signed integer from the current stream using the specified endian encoding, and advances the
/// stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An eight-byte unsigned integer read from the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(int)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadInt64LittleEndian(buffer)
: BinaryPrimitives.ReadInt64BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>A single-precision floating point value read from the stream.</returns>
public static double ReadSingle(this Stream stream)
{
return stream.ReadSingle(DefaultEndianness);
}
/// <summary>
/// Reads a double-precision floating point value from the current stream using a specified endian encoding, and
/// advances the stream position by four bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>A single-precision floating point value read from the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(int)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadSingleLittleEndian(buffer)
: BinaryPrimitives.ReadSingleBigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An two-byte unsigned integer read from the stream.</returns>
[CLSCompliant(false)]
public static ushort ReadUInt16(this Stream stream)
{
return stream.ReadUInt16(DefaultEndianness);
}
/// <summary>
/// Reads a two-byte unsigned integer from the current stream using the specified endian encoding, and advances the
/// stream position by two bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An two-byte unsigned integer read from the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(int)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadUInt16LittleEndian(buffer)
: BinaryPrimitives.ReadUInt16BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An four-byte unsigned integer read from the stream.</returns>
[CLSCompliant(false)]
public static uint ReadUInt32(this Stream stream)
{
return stream.ReadUInt32(DefaultEndianness);
}
/// <summary>
/// Reads a four-byte unsigned integer from the current stream using the specified endian encoding, and advances the
/// stream position by four bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An four-byte unsigned integer read from the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(int)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadUInt32LittleEndian(buffer)
: BinaryPrimitives.ReadUInt32BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <returns>An eight-byte unsigned integer read from the stream.</returns>
[CLSCompliant(false)]
public static ulong ReadUInt64(this Stream stream)
{
return stream.ReadUInt64(DefaultEndianness);
}
/// <summary>
/// Reads an eight-byte unsigned integer from the current stream using the specified endian encoding, and advances the
/// stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream from which the value should be read.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>An eight-byte unsigned integer read from the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(int)];
stream.Read(buffer);
return endianness == Endianness.LittleEndian
? BinaryPrimitives.ReadUInt64LittleEndian(buffer)
: BinaryPrimitives.ReadUInt64BigEndian(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The two-byte signed integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
public static int Write(this Stream stream, short value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes a two-byte signed integer to the current stream using the specified endian encoding, and advances the
/// stream position by two bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The two-byte signed integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(short)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteInt16LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteInt16BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The four-byte signed integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
public static int Write(this Stream stream, int value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes a four-byte signed integer to the current stream using the specified endian encoding, and advances the
/// stream position by four bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The four-byte signed integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(int)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteInt32BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The eight-byte signed integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
public static int Write(this Stream stream, long value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes an eight-byte signed integer to the current stream using the specified endian encoding, and advances the
/// stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The eight-byte signed integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(long)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteInt64BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The two-byte unsigned integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
[CLSCompliant(false)]
public static int Write(this Stream stream, ushort value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes a two-byte unsigned integer to the current stream using the specified endian encoding, and advances the
/// stream position by two bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The two-byte unsigned integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(ushort)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteUInt16LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteUInt16BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The four-byte unsigned integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
[CLSCompliant(false)]
public static int Write(this Stream stream, uint value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes a four-byte unsigned integer to the current stream using the specified endian encoding, and advances the
/// stream position by four bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The four-byte unsigned integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(uint)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteUInt32BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The eight-byte unsigned integer to write.</param>
/// <returns>The number of bytes written to the stream.</returns>
[CLSCompliant(false)]
public static int Write(this Stream stream, ulong value)
{
return stream.Write(value, DefaultEndianness);
}
/// <summary>
/// Writes an eight-byte signed integer to the current stream using the specified endian encoding, and advances the
/// stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The eight-byte signed integer to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
[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<byte> buffer = stackalloc byte[sizeof(ulong)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteUInt64BigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// Writes a single-precision floating point value to the current stream using the specified endian encoding, and
/// advances the stream position by four bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The single-precision floating point value to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(float)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteSingleLittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteSingleBigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// Writes a double-precision floating point value to the current stream using the specified endian encoding, and
/// advances the stream position by eight bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The double-precision floating point value to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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<byte> buffer = stackalloc byte[sizeof(double)];
if (endianness == Endianness.LittleEndian)
{
BinaryPrimitives.WriteDoubleLittleEndian(buffer, value);
}
else
{
BinaryPrimitives.WriteDoubleBigEndian(buffer, value);
}
return stream.WriteInternal(buffer);
}
/// <summary>
/// Writes a decimal value to the current stream using the specified endian encoding, and advances the stream position
/// by sixteen bytes.
/// </summary>
/// <param name="stream">The stream to which the value should be written.</param>
/// <param name="value">The decimal value to write.</param>
/// <param name="endianness">The endian encoding to use.</param>
/// <returns>The number of bytes written to the stream.</returns>
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);
}
}