From 9d26f3da60dab8d07d07ecc29e0ca3c4cc12f160 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Thu, 6 Apr 2023 19:11:54 +0100 Subject: [PATCH] feat: add TextWriter.WriteNoAlloc/WriteLineNoAlloc Allows writing of integer types without allocating a string. --- CHANGELOG.md | 8 + X10D.Tests/src/IO/TextWriterTests.Int32.cs | 131 ++++ X10D.Tests/src/IO/TextWriterTests.Int64.cs | 131 ++++ X10D.Tests/src/IO/TextWriterTests.UInt32.cs | 131 ++++ X10D.Tests/src/IO/TextWriterTests.UInt64.cs | 131 ++++ X10D.Tests/src/IO/TextWriterTests.cs | 46 ++ X10D/src/IO/TextWriterExtensions.cs | 685 ++++++++++++++++++++ 7 files changed, 1263 insertions(+) create mode 100644 X10D.Tests/src/IO/TextWriterTests.Int32.cs create mode 100644 X10D.Tests/src/IO/TextWriterTests.Int64.cs create mode 100644 X10D.Tests/src/IO/TextWriterTests.UInt32.cs create mode 100644 X10D.Tests/src/IO/TextWriterTests.UInt64.cs create mode 100644 X10D.Tests/src/IO/TextWriterTests.cs create mode 100644 X10D/src/IO/TextWriterExtensions.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index e7daff2..4de5308 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - X10D: Added math-related extension methods for `BigInteger`. - X10D: Added `Span.Replace(T, T)`. - X10D: Added `CountDigits` for integer types. +- X10D: Added `TextWriter.WriteNoAlloc(int[, ReadOnlySpan[, IFormatProvider]])`. +- X10D: Added `TextWriter.WriteNoAlloc(uint[, ReadOnlySpan[, IFormatProvider]])`. +- X10D: Added `TextWriter.WriteNoAlloc(long[, ReadOnlySpan[, IFormatProvider]])`. +- X10D: Added `TextWriter.WriteNoAlloc(ulong[, ReadOnlySpan[, IFormatProvider]])`. +- X10D: Added `TextWriter.WriteLineNoAlloc(int[, ReadOnlySpan[, IFormatProvider]])`. +- X10D: Added `TextWriter.WriteLineNoAlloc(uint[, ReadOnlySpan[, IFormatProvider]])`. +- X10D: Added `TextWriter.WriteLineNoAlloc(long[, ReadOnlySpan[, IFormatProvider]])`. +- X10D: Added `TextWriter.WriteLineNoAlloc(ulong[, ReadOnlySpan[, IFormatProvider]])`. ### Changed - X10D: `DateTime.Age(DateTime)` and `DateTimeOffset.Age(DateTimeOffset)` parameter renamed from `asOf` to `referenceDate`. diff --git a/X10D.Tests/src/IO/TextWriterTests.Int32.cs b/X10D.Tests/src/IO/TextWriterTests.Int32.cs new file mode 100644 index 0000000..31bed11 --- /dev/null +++ b/X10D.Tests/src/IO/TextWriterTests.Int32.cs @@ -0,0 +1,131 @@ +using System.Globalization; +using System.Text; +using NUnit.Framework; +using X10D.IO; + +namespace X10D.Tests.IO; + +public partial class TextWriterTests +{ + [Test] + public void WriteNoAlloc_ShouldThrowArgumentNullException_GivenInt32_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteNoAlloc(420)); + Assert.Throws(() => writer.WriteNoAlloc(420, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldThrowObjectDisposedException_GivenInt32_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteNoAlloc(420)); + Assert.Throws(() => writer.WriteNoAlloc(420, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowArgumentNullException_GivenInt32_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteLineNoAlloc(420)); + Assert.Throws(() => writer.WriteLineNoAlloc(420, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowObjectDisposedException_GivenInt32_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteLineNoAlloc(420)); + Assert.Throws(() => writer.WriteLineNoAlloc(420, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenInt32() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenInt32_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420, "N0"); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenInt32_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420, "N0", CultureInfo.CurrentCulture); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenInt32() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenInt32_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420, "N0"); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenInt32_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420, "N0", CultureInfo.CurrentCulture); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } +} diff --git a/X10D.Tests/src/IO/TextWriterTests.Int64.cs b/X10D.Tests/src/IO/TextWriterTests.Int64.cs new file mode 100644 index 0000000..affd830 --- /dev/null +++ b/X10D.Tests/src/IO/TextWriterTests.Int64.cs @@ -0,0 +1,131 @@ +using System.Globalization; +using System.Text; +using NUnit.Framework; +using X10D.IO; + +namespace X10D.Tests.IO; + +public partial class TextWriterTests +{ + [Test] + public void WriteNoAlloc_ShouldThrowArgumentNullException_GivenInt64_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteNoAlloc(420L)); + Assert.Throws(() => writer.WriteNoAlloc(420L, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420L, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldThrowObjectDisposedException_GivenInt64_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteNoAlloc(420L)); + Assert.Throws(() => writer.WriteNoAlloc(420L, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420L, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowArgumentNullException_GivenInt64_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteLineNoAlloc(420L)); + Assert.Throws(() => writer.WriteLineNoAlloc(420L, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420L, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowObjectDisposedException_GivenInt64_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteLineNoAlloc(420L)); + Assert.Throws(() => writer.WriteLineNoAlloc(420L, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420L, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenInt64() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420L); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenInt64_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420L, "N0"); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenInt64_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420L, "N0", CultureInfo.CurrentCulture); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenInt64() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420L); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenInt64_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420L, "N0"); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenInt64_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420L, "N0", CultureInfo.CurrentCulture); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } +} diff --git a/X10D.Tests/src/IO/TextWriterTests.UInt32.cs b/X10D.Tests/src/IO/TextWriterTests.UInt32.cs new file mode 100644 index 0000000..8aab080 --- /dev/null +++ b/X10D.Tests/src/IO/TextWriterTests.UInt32.cs @@ -0,0 +1,131 @@ +using System.Globalization; +using System.Text; +using NUnit.Framework; +using X10D.IO; + +namespace X10D.Tests.IO; + +public partial class TextWriterTests +{ + [Test] + public void WriteNoAlloc_ShouldThrowArgumentNullException_GivenUInt32_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteNoAlloc(420U)); + Assert.Throws(() => writer.WriteNoAlloc(420U, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420U, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldThrowObjectDisposedException_GivenUInt32_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteNoAlloc(420U)); + Assert.Throws(() => writer.WriteNoAlloc(420U, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420U, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowArgumentNullException_GivenUInt32_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteLineNoAlloc(420U)); + Assert.Throws(() => writer.WriteLineNoAlloc(420U, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420U, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowObjectDisposedException_GivenUInt32_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteLineNoAlloc(420U)); + Assert.Throws(() => writer.WriteLineNoAlloc(420U, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420U, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenUInt32() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420U); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenUInt32_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420U, "N0"); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenUInt32_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420U, "N0", CultureInfo.CurrentCulture); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenUInt32() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420U); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenUInt32_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420U, "N0"); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenUInt32_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420U, "N0", CultureInfo.CurrentCulture); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } +} diff --git a/X10D.Tests/src/IO/TextWriterTests.UInt64.cs b/X10D.Tests/src/IO/TextWriterTests.UInt64.cs new file mode 100644 index 0000000..57f5425 --- /dev/null +++ b/X10D.Tests/src/IO/TextWriterTests.UInt64.cs @@ -0,0 +1,131 @@ +using System.Globalization; +using System.Text; +using NUnit.Framework; +using X10D.IO; + +namespace X10D.Tests.IO; + +public partial class TextWriterTests +{ + [Test] + public void WriteNoAlloc_ShouldThrowArgumentNullException_GivenUInt64_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteNoAlloc(420UL)); + Assert.Throws(() => writer.WriteNoAlloc(420UL, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420UL, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldThrowObjectDisposedException_GivenUInt64_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteNoAlloc(420UL)); + Assert.Throws(() => writer.WriteNoAlloc(420UL, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420UL, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowArgumentNullException_GivenUInt64_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteLineNoAlloc(420UL)); + Assert.Throws(() => writer.WriteLineNoAlloc(420UL, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420UL, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowObjectDisposedException_GivenUInt64_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteLineNoAlloc(420UL)); + Assert.Throws(() => writer.WriteLineNoAlloc(420UL, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420UL, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenUInt64() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420UL); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenUInt64_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420UL, "N0"); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenUInt64_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420UL, "N0", CultureInfo.CurrentCulture); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenUInt64() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420UL); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenUInt64_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420UL, "N0"); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteLineNoAlloc_ShouldWriteTextValue_GivenUInt64_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420UL, "N0", CultureInfo.CurrentCulture); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + var expected = $"420{Environment.NewLine}"; + + Assert.That(actual, Is.EqualTo(expected)); + } +} diff --git a/X10D.Tests/src/IO/TextWriterTests.cs b/X10D.Tests/src/IO/TextWriterTests.cs new file mode 100644 index 0000000..b98268a --- /dev/null +++ b/X10D.Tests/src/IO/TextWriterTests.cs @@ -0,0 +1,46 @@ +using System.Diagnostics; +using System.Text; +using NUnit.Framework; + +namespace X10D.Tests.IO; + +[TestFixture] +public partial class TextWriterTests +{ + private MemoryStream _stream = null!; + private StreamWriter _writer = null!; + + [OneTimeSetUp] + public void OneTimeSetup() + { + _stream = new MemoryStream(); + _writer = new StreamWriter(_stream, Encoding.UTF8); + _writer.Write(' '); + _writer.Flush(); + _stream.SetLength(0); + _stream.Position = 0; + + Trace.Listeners.Add(new ConsoleTraceListener()); + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + _writer.Dispose(); + _stream.Dispose(); + + Trace.Flush(); + } + + [SetUp] + public void Setup() + { + _stream.SetLength(0); + } + + [TearDown] + public void TearDown() + { + _stream.SetLength(0); + } +} diff --git a/X10D/src/IO/TextWriterExtensions.cs b/X10D/src/IO/TextWriterExtensions.cs new file mode 100644 index 0000000..7d1b3c5 --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.cs @@ -0,0 +1,685 @@ +using System.Globalization; +using X10D.Math; + +namespace X10D.IO; + +/// +/// IO-related extension methods for . +/// +public static class TextWriterExtensions +{ + /// + /// Writes the text representation of a 4-byte signed integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 4-byte signed integer to write. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteNoAlloc(this TextWriter writer, int value) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + WriteNoAlloc(writer, value, "N0".AsSpan(), CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 4-byte signed integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 4-byte signed integer to write. + /// A standard or custom numeric format string. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteNoAlloc(this TextWriter writer, int value, ReadOnlySpan format) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + WriteNoAlloc(writer, value, format, CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 4-byte signed integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 4-byte signed integer to write. + /// A standard or custom numeric format string. + /// An object that supplies culture-specific formatting information. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteNoAlloc(this TextWriter writer, int value, ReadOnlySpan format, IFormatProvider? formatProvider) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + int digitCount = value.CountDigits(); + Span buffer = stackalloc char[System.Math.Max(value < 0 ? digitCount + 1 : digitCount, 1000)]; + if (value.TryFormat(buffer, out int charsWritten, format, formatProvider)) + { + Span truncated = buffer[..charsWritten]; + for (var index = 0; index < truncated.Length; index++) + { + writer.Write(truncated[index]); + } + } + else + { + writer.Write(value.ToString(format.ToString(), formatProvider)); + } + } + + /// + /// Writes the text representation of a 4-byte unsigned integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 4-byte unsigned integer to write. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteNoAlloc(this TextWriter writer, uint value) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + WriteNoAlloc(writer, value, "N0".AsSpan(), CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 4-byte unsigned integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 4-byte unsigned integer to write. + /// A standard or custom numeric format string. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteNoAlloc(this TextWriter writer, uint value, ReadOnlySpan format) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + WriteNoAlloc(writer, value, format, CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 4-byte unsigned integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 4-byte unsigned integer to write. + /// A standard or custom numeric format string. + /// An object that supplies culture-specific formatting information. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteNoAlloc(this TextWriter writer, + uint value, + ReadOnlySpan format, + IFormatProvider? formatProvider) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + int digitCount = value.CountDigits(); + Span buffer = stackalloc char[System.Math.Max(digitCount, 1000)]; + if (value.TryFormat(buffer, out int charsWritten, format, formatProvider)) + { + Span truncated = buffer[..charsWritten]; + for (var index = 0; index < truncated.Length; index++) + { + writer.Write(truncated[index]); + } + } + else + { + writer.Write(value.ToString(format.ToString(), formatProvider)); + } + } + + /// + /// Writes the text representation of a 8-byte signed integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 8-byte signed integer to write. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteNoAlloc(this TextWriter writer, long value) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + WriteNoAlloc(writer, value, "N0".AsSpan(), CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 8-byte signed integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 8-byte signed integer to write. + /// A standard or custom numeric format string. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteNoAlloc(this TextWriter writer, long value, ReadOnlySpan format) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + WriteNoAlloc(writer, value, format, CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 8-byte signed integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 8-byte signed integer to write. + /// A standard or custom numeric format string. + /// An object that supplies culture-specific formatting information. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteNoAlloc(this TextWriter writer, + long value, + ReadOnlySpan format, + IFormatProvider? formatProvider) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + int digitCount = value.CountDigits(); + Span buffer = stackalloc char[System.Math.Max(value < 0 ? digitCount + 1 : digitCount, 1000)]; + if (value.TryFormat(buffer, out int charsWritten, format, formatProvider)) + { + Span truncated = buffer[..charsWritten]; + for (var index = 0; index < truncated.Length; index++) + { + writer.Write(truncated[index]); + } + } + else + { + writer.Write(value.ToString(format.ToString(), formatProvider)); + } + } + + /// + /// Writes the text representation of a 8-byte unsigned integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 8-byte unsigned integer to write. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteNoAlloc(this TextWriter writer, ulong value) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + WriteNoAlloc(writer, value, "N0".AsSpan(), CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 8-byte signed integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 8-byte unsigned integer to write. + /// A standard or custom numeric format string. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteNoAlloc(this TextWriter writer, ulong value, ReadOnlySpan format) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + WriteNoAlloc(writer, value, format, CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 8-byte signed integer to the text stream, without allocating a string. + /// + /// The to write to. + /// The 8-byte unsigned integer to write. + /// A standard or custom numeric format string. + /// An object that supplies culture-specific formatting information. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteNoAlloc(this TextWriter writer, + ulong value, + ReadOnlySpan format, + IFormatProvider? formatProvider) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + int digitCount = value.CountDigits(); + Span buffer = stackalloc char[System.Math.Max(digitCount, 1000)]; + if (value.TryFormat(buffer, out int charsWritten, format, formatProvider)) + { + Span truncated = buffer[..charsWritten]; + for (var index = 0; index < truncated.Length; index++) + { + writer.Write(truncated[index]); + } + } + else + { + writer.Write(value.ToString(format.ToString(), formatProvider)); + } + } + + /// + /// Writes the text representation of a 4-byte signed integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 4-byte signed integer to write. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteLineNoAlloc(this TextWriter writer, int value) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteLineNoAlloc(value, "N0".AsSpan(), CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 4-byte signed integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 4-byte signed integer to write. + /// A standard or custom numeric format string. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteLineNoAlloc(this TextWriter writer, int value, ReadOnlySpan format) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteLineNoAlloc(value, format, CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 4-byte signed integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 4-byte signed integer to write. + /// A standard or custom numeric format string. + /// An object that supplies culture-specific formatting information. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteLineNoAlloc(this TextWriter writer, int value, ReadOnlySpan format, + IFormatProvider? formatProvider) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteNoAlloc(value, format, formatProvider); + writer.WriteLine(); + } + + /// + /// Writes the text representation of a 4-byte unsigned integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 4-byte unsigned integer to write. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteLineNoAlloc(this TextWriter writer, uint value) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteLineNoAlloc(value, "N0".AsSpan(), CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 4-byte unsigned integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 4-byte unsigned integer to write. + /// A standard or custom numeric format string. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteLineNoAlloc(this TextWriter writer, uint value, ReadOnlySpan format) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteLineNoAlloc(value, format, CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 4-byte unsigned integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 4-byte unsigned integer to write. + /// A standard or custom numeric format string. + /// An object that supplies culture-specific formatting information. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteLineNoAlloc(this TextWriter writer, + uint value, + ReadOnlySpan format, + IFormatProvider? formatProvider) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteNoAlloc(value, format, formatProvider); + writer.WriteLine(); + } + + /// + /// Writes the text representation of a 8-byte signed integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 8-byte signed integer to write. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteLineNoAlloc(this TextWriter writer, long value) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteLineNoAlloc(value, "N0".AsSpan(), CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 8-byte signed integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 8-byte signed integer to write. + /// A standard or custom numeric format string. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteLineNoAlloc(this TextWriter writer, long value, ReadOnlySpan format) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteLineNoAlloc(value, format, CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 8-byte signed integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 8-byte signed integer to write. + /// A standard or custom numeric format string. + /// An object that supplies culture-specific formatting information. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + public static void WriteLineNoAlloc(this TextWriter writer, + long value, + ReadOnlySpan format, + IFormatProvider? formatProvider) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteNoAlloc(value, format, formatProvider); + writer.WriteLine(); + } + + /// + /// Writes the text representation of a 8-byte unsigned integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 8-byte unsigned integer to write. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteLineNoAlloc(this TextWriter writer, ulong value) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteLineNoAlloc(value, "N0".AsSpan(), CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 8-byte signed integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 8-byte unsigned integer to write. + /// A standard or custom numeric format string. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteLineNoAlloc(this TextWriter writer, ulong value, ReadOnlySpan format) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteLineNoAlloc(value, format, CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 8-byte signed integer to the text stream, followed by a line terminator, without + /// allocating a string. + /// + /// The to write to. + /// The 8-byte unsigned integer to write. + /// A standard or custom numeric format string. + /// An object that supplies culture-specific formatting information. + /// This method may still allocate if the integer is too large to fit in a stack-allocated buffer. + /// is . + /// The is closed. + /// An I/O error occurs. + [CLSCompliant(false)] + public static void WriteLineNoAlloc(this TextWriter writer, + ulong value, + ReadOnlySpan format, + IFormatProvider? formatProvider) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + writer.WriteNoAlloc(value, format, formatProvider); + writer.WriteLine(); + } +}