From ac499537ed0ae381e12a5b983d1047442ccdf2b3 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Tue, 11 Apr 2023 16:58:16 +0100 Subject: [PATCH 1/7] feat: add RaycastHit.GetComponent and RaycastHit.TryGetComponent --- CHANGELOG.md | 1 + X10D.Unity/src/RaycastHitExtensions.cs | 109 +++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 X10D.Unity/src/RaycastHitExtensions.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cdf854..5ea3856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - X10D: Added `TextWriter.WriteLineNoAlloc(uint[, ReadOnlySpan[, IFormatProvider]])`. - X10D: Added `TextWriter.WriteLineNoAlloc(long[, ReadOnlySpan[, IFormatProvider]])`. - X10D: Added `TextWriter.WriteLineNoAlloc(ulong[, ReadOnlySpan[, IFormatProvider]])`. +- X10D.Unity: Added `RaycastHit.GetComponent` and `RaycastHit.TryGetComponent`. ### Changed diff --git a/X10D.Unity/src/RaycastHitExtensions.cs b/X10D.Unity/src/RaycastHitExtensions.cs new file mode 100644 index 0000000..5e44dca --- /dev/null +++ b/X10D.Unity/src/RaycastHitExtensions.cs @@ -0,0 +1,109 @@ +using System.Diagnostics.CodeAnalysis; +using UnityEngine; + +namespace X10D.Unity; + +/// +/// Extension methods for . +/// +public static class RaycastHitExtensions +{ + /// + /// Gets the component of the specified type from the object that was hit by the raycast. + /// + /// The raycast hit. + /// The type of the component to retrieve. + /// + /// The component of the specified type from the object that was hit by the raycast, or if no + /// component of the specified type was found. + /// + public static T? GetComponent(this RaycastHit hit) + { + if (hit.transform == null) + { + return default; + } + + return hit.transform.GetComponent(); + } + + /// + /// Gets the component of the specified type from the object that was hit by the raycast. + /// + /// The raycast hit. + /// The type of the component to retrieve. + /// + /// The component of the specified type from the object that was hit by the raycast, or if no + /// component of the specified type was found. + /// + /// is . + public static Component? GetComponent(this RaycastHit hit, Type componentType) + { + if (componentType is null) + { + throw new ArgumentNullException(nameof(componentType)); + } + + if (hit.transform == null) + { + return default; + } + + return hit.transform.GetComponent(componentType); + } + + + /// + /// Attempts to get the component of the specified type from the object that was hit by the raycast, and returns a value + /// that indicates whether the operation succeeded. + /// + /// The raycast hit. + /// + /// When this method returns, contains the component of the specified type from the object that was hit by the raycast, or + /// if no component of the specified type was found. + /// + /// The type of the component to retrieve. + /// + /// if the component of the specified type was found; otherwise, . + /// + public static bool TryGetComponent(this RaycastHit hit, [NotNullWhen(true)] out T? component) + { + if (hit.transform == null) + { + component = default; + return false; + } + + return hit.transform.TryGetComponent(out component); + } + + /// + /// Attempts to get the component of the specified type from the object that was hit by the raycast, and returns a value + /// that indicates whether the operation succeeded. + /// + /// The raycast hit. + /// The type of the component to retrieve. + /// + /// When this method returns, contains the component of the specified type from the object that was hit by the raycast, or + /// if no component of the specified type was found. + /// + /// + /// if the component of the specified type was found; otherwise, . + /// + /// is . + public static bool TryGetComponent(this RaycastHit hit, Type componentType, [NotNullWhen(true)] out Component? component) + { + if (componentType is null) + { + throw new ArgumentNullException(nameof(componentType)); + } + + if (hit.transform == null) + { + component = default; + return false; + } + + return hit.transform.TryGetComponent(componentType, out component); + } +} From 5ff521073cdcef13f45f529009fb782b7a7fe027 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Tue, 11 Apr 2023 17:04:09 +0100 Subject: [PATCH 2/7] fix: suppress CA5394 This analysis warning is irrelevant. We're making extension methods here, dammit! This will be the consumer's responsibility to worry about. --- X10D.Unity/src/Drawing/RandomExtensions.cs | 2 ++ X10D.Unity/src/Numerics/RandomExtensions.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/X10D.Unity/src/Drawing/RandomExtensions.cs b/X10D.Unity/src/Drawing/RandomExtensions.cs index 2e90807..c8b4bd7 100644 --- a/X10D.Unity/src/Drawing/RandomExtensions.cs +++ b/X10D.Unity/src/Drawing/RandomExtensions.cs @@ -2,6 +2,8 @@ using X10D.Core; using Random = System.Random; +#pragma warning disable CA5394 + namespace X10D.Unity.Drawing; /// diff --git a/X10D.Unity/src/Numerics/RandomExtensions.cs b/X10D.Unity/src/Numerics/RandomExtensions.cs index 3b10b79..07b54a2 100644 --- a/X10D.Unity/src/Numerics/RandomExtensions.cs +++ b/X10D.Unity/src/Numerics/RandomExtensions.cs @@ -2,6 +2,8 @@ using X10D.Core; using Random = System.Random; +#pragma warning disable CA5394 + namespace X10D.Unity.Numerics; /// From 2e9f27b6b7a40bb4a80644b8b4108ca1369bc541 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Tue, 11 Apr 2023 17:05:58 +0100 Subject: [PATCH 3/7] fix: validate null arguments for Polygon and PolygonF extensions --- X10D.Unity/src/Drawing/PolygonExtensions.cs | 21 ++++++++++ X10D.Unity/src/Drawing/PolygonFExtensions.cs | 42 ++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/X10D.Unity/src/Drawing/PolygonExtensions.cs b/X10D.Unity/src/Drawing/PolygonExtensions.cs index 8a72df8..b1b962f 100644 --- a/X10D.Unity/src/Drawing/PolygonExtensions.cs +++ b/X10D.Unity/src/Drawing/PolygonExtensions.cs @@ -14,8 +14,14 @@ public static class PolygonExtensions /// /// The polygon whose points to update. /// The point to add. + /// is . public static void AddVertex(this Polygon polygon, Vector2Int point) { + if (polygon is null) + { + throw new ArgumentNullException(nameof(polygon)); + } + polygon.AddVertex(point.ToSystemPoint()); } @@ -24,8 +30,23 @@ public static class PolygonExtensions /// /// The polygon whose vertices to update. /// The vertices to add. + /// + /// is . + /// -or- + /// is . + /// public static void AddVertices(this Polygon polygon, IEnumerable vertices) { + if (polygon is null) + { + throw new ArgumentNullException(nameof(polygon)); + } + + if (vertices is null) + { + throw new ArgumentNullException(nameof(vertices)); + } + foreach (Vector2Int vertex in vertices) { polygon.AddVertex(vertex); diff --git a/X10D.Unity/src/Drawing/PolygonFExtensions.cs b/X10D.Unity/src/Drawing/PolygonFExtensions.cs index 0042904..1b91d0c 100644 --- a/X10D.Unity/src/Drawing/PolygonFExtensions.cs +++ b/X10D.Unity/src/Drawing/PolygonFExtensions.cs @@ -14,8 +14,14 @@ public static class PolygonFExtensions /// /// The polygon whose vertices to update. /// The vertex to add. + /// is . public static void AddVertex(this PolygonF polygon, Vector2Int vertex) { + if (polygon is null) + { + throw new ArgumentNullException(nameof(polygon)); + } + polygon.AddVertex(vertex.ToSystemPoint()); } @@ -24,8 +30,14 @@ public static class PolygonFExtensions /// /// The polygon whose vertices to update. /// The vertex to add. + /// is . public static void AddVertex(this PolygonF polygon, Vector2 vertex) { + if (polygon is null) + { + throw new ArgumentNullException(nameof(polygon)); + } + polygon.AddVertex(vertex.ToSystemPointF()); } @@ -34,8 +46,23 @@ public static class PolygonFExtensions /// /// The polygon whose vertices to update. /// The vertices to add. + /// + /// is . + /// -or- + /// is . + /// public static void AddVertices(this PolygonF polygon, IEnumerable vertices) { + if (polygon is null) + { + throw new ArgumentNullException(nameof(polygon)); + } + + if (vertices is null) + { + throw new ArgumentNullException(nameof(vertices)); + } + foreach (Vector2Int vertex in vertices) { polygon.AddVertex(vertex); @@ -47,8 +74,23 @@ public static class PolygonFExtensions /// /// The polygon whose vertices to update. /// The vertices to add. + /// + /// is . + /// -or- + /// is . + /// public static void AddVertices(this PolygonF polygon, IEnumerable vertices) { + if (polygon is null) + { + throw new ArgumentNullException(nameof(polygon)); + } + + if (vertices is null) + { + throw new ArgumentNullException(nameof(vertices)); + } + foreach (Vector2 vertex in vertices) { polygon.AddVertex(vertex); From e6025b1a4cd6a188a358c150ec3e2e43bf835733 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Tue, 11 Apr 2023 17:09:14 +0100 Subject: [PATCH 4/7] fix: validate null arguments in X10D.Unity The CA runs are really testing me today. --- X10D.Unity/src/ComponentExtensions.cs | 6 +++ .../src/Drawing/PolyhedronExtensions.cs | 42 +++++++++++++++++++ X10D.Unity/src/GameObjectExtensions.cs | 6 +++ 3 files changed, 54 insertions(+) diff --git a/X10D.Unity/src/ComponentExtensions.cs b/X10D.Unity/src/ComponentExtensions.cs index 6ad7388..01d0a27 100644 --- a/X10D.Unity/src/ComponentExtensions.cs +++ b/X10D.Unity/src/ComponentExtensions.cs @@ -14,8 +14,14 @@ public static class ComponentExtensions /// The component whose child components to retrieve. /// The type of the components to retrieve. /// An array representing the child components. + /// is . public static T[] GetComponentsInChildrenOnly(this Component component) { + if (component == null) + { + throw new ArgumentNullException(nameof(component)); + } + return component.gameObject.GetComponentsInChildrenOnly(); } } diff --git a/X10D.Unity/src/Drawing/PolyhedronExtensions.cs b/X10D.Unity/src/Drawing/PolyhedronExtensions.cs index 108b9a8..94cd4ed 100644 --- a/X10D.Unity/src/Drawing/PolyhedronExtensions.cs +++ b/X10D.Unity/src/Drawing/PolyhedronExtensions.cs @@ -14,8 +14,14 @@ public static class PolyhedronExtensions /// /// The polyhedron whose vertices to update. /// The vertex to add. + /// is . public static void AddVertex(this Polyhedron polyhedron, Vector3Int vertex) { + if (polyhedron is null) + { + throw new ArgumentNullException(nameof(polyhedron)); + } + polyhedron.AddVertex(vertex.ToSystemVector()); } @@ -24,8 +30,14 @@ public static class PolyhedronExtensions /// /// The polyhedron whose vertices to update. /// The vertex to add. + /// is . public static void AddVertex(this Polyhedron polyhedron, Vector3 vertex) { + if (polyhedron is null) + { + throw new ArgumentNullException(nameof(polyhedron)); + } + polyhedron.AddVertex(vertex.ToSystemVector()); } @@ -34,8 +46,23 @@ public static class PolyhedronExtensions /// /// The polyhedron whose vertices to update. /// The vertices to add. + /// + /// is . + /// -or- + /// is . + /// public static void AddVertices(this Polyhedron polyhedron, IEnumerable vertices) { + if (polyhedron is null) + { + throw new ArgumentNullException(nameof(polyhedron)); + } + + if (vertices is null) + { + throw new ArgumentNullException(nameof(vertices)); + } + foreach (Vector3Int vertex in vertices) { polyhedron.AddVertex(vertex); @@ -47,8 +74,23 @@ public static class PolyhedronExtensions /// /// The polyhedron whose vertices to update. /// The vertices to add. + /// + /// is . + /// -or- + /// is . + /// public static void AddVertices(this Polyhedron polyhedron, IEnumerable vertices) { + if (polyhedron is null) + { + throw new ArgumentNullException(nameof(polyhedron)); + } + + if (vertices is null) + { + throw new ArgumentNullException(nameof(vertices)); + } + foreach (Vector3 vertex in vertices) { polyhedron.AddVertex(vertex); diff --git a/X10D.Unity/src/GameObjectExtensions.cs b/X10D.Unity/src/GameObjectExtensions.cs index 23d7f3c..559ee39 100644 --- a/X10D.Unity/src/GameObjectExtensions.cs +++ b/X10D.Unity/src/GameObjectExtensions.cs @@ -13,8 +13,14 @@ public static class GameObjectExtensions /// The game object whose child components to retrieve. /// The type of the components to retrieve. /// An array representing the child components. + /// is . public static T[] GetComponentsInChildrenOnly(this GameObject gameObject) { + if (gameObject == null) + { + throw new ArgumentNullException(nameof(gameObject)); + } + Transform rootTransform = gameObject.transform; var components = new List(gameObject.GetComponentsInChildren()); From f13907a4d069fb50127ca60d2f0b4d08edb4ee7c Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Tue, 11 Apr 2023 22:23:12 +0100 Subject: [PATCH 5/7] style: move Write/WriteLine overloads to files This change should help to organise this entire class a bit better, due to the number of overloads that exist. --- ...WriterExtensions.WriteLineNoAlloc.Int32.cs | 83 +++ ...WriterExtensions.WriteLineNoAlloc.Int64.cs | 85 +++ ...riterExtensions.WriteLineNoAlloc.UInt32.cs | 88 +++ ...riterExtensions.WriteLineNoAlloc.UInt64.cs | 88 +++ ...TextWriterExtensions.WriteNoAlloc.Int32.cs | 92 +++ ...TextWriterExtensions.WriteNoAlloc.Int64.cs | 95 +++ ...extWriterExtensions.WriteNoAlloc.UInt32.cs | 98 +++ ...extWriterExtensions.WriteNoAlloc.UInt64.cs | 98 +++ X10D/src/IO/TextWriterExtensions.cs | 681 +----------------- 9 files changed, 729 insertions(+), 679 deletions(-) create mode 100644 X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Int32.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Int64.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.UInt32.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.UInt64.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Int32.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Int64.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteNoAlloc.UInt32.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteNoAlloc.UInt64.cs diff --git a/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Int32.cs b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Int32.cs new file mode 100644 index 0000000..8836d63 --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Int32.cs @@ -0,0 +1,83 @@ +using System.Globalization; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// 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 + + WriteLineNoAlloc(writer, (long)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(); + } +} diff --git a/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Int64.cs b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Int64.cs new file mode 100644 index 0000000..83deadd --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Int64.cs @@ -0,0 +1,85 @@ +using System.Globalization; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// Writes the text representation of an 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 an 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 an 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(); + } +} diff --git a/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.UInt32.cs b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.UInt32.cs new file mode 100644 index 0000000..98e9d42 --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.UInt32.cs @@ -0,0 +1,88 @@ +using System.Globalization; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// 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 + + WriteLineNoAlloc(writer, (long)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(); + } +} diff --git a/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.UInt64.cs b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.UInt64.cs new file mode 100644 index 0000000..68f397f --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.UInt64.cs @@ -0,0 +1,88 @@ +using System.Globalization; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// Writes the text representation of an 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 an 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. + /// 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 an 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. + /// 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(); + } +} diff --git a/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Int32.cs b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Int32.cs new file mode 100644 index 0000000..c2366bf --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Int32.cs @@ -0,0 +1,92 @@ +using System.Globalization; +using X10D.Math; + +namespace X10D.IO; + +public static partial 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, 100)]; + 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)); + } + } +} diff --git a/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Int64.cs b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Int64.cs new file mode 100644 index 0000000..049d891 --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Int64.cs @@ -0,0 +1,95 @@ +using System.Globalization; +using X10D.Math; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// Writes the text representation of an 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 an 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 an 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, 100)]; + 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)); + } + } +} diff --git a/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.UInt32.cs b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.UInt32.cs new file mode 100644 index 0000000..f8d32a2 --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.UInt32.cs @@ -0,0 +1,98 @@ +using System.Globalization; +using X10D.Math; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// 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, 100)]; + 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)); + } + } +} diff --git a/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.UInt64.cs b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.UInt64.cs new file mode 100644 index 0000000..5e266e4 --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.UInt64.cs @@ -0,0 +1,98 @@ +using System.Globalization; +using X10D.Math; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// Writes the text representation of an 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 an 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 an 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, 100)]; + 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)); + } + } +} diff --git a/X10D/src/IO/TextWriterExtensions.cs b/X10D/src/IO/TextWriterExtensions.cs index 7d1b3c5..f590205 100644 --- a/X10D/src/IO/TextWriterExtensions.cs +++ b/X10D/src/IO/TextWriterExtensions.cs @@ -1,685 +1,8 @@ -using System.Globalization; -using X10D.Math; - -namespace X10D.IO; +namespace X10D.IO; /// /// IO-related extension methods for . /// -public static class TextWriterExtensions +public static partial 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(); - } } From b8a63c0d5ce7a8fad9e91f3ddc777c58734d9926 Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Tue, 11 Apr 2023 22:27:31 +0100 Subject: [PATCH 6/7] feat: add float/double support for WriteNoAlloc and WriteLineNoAlloc --- X10D.Tests/src/IO/TextWriterTests.Double.cs | 131 ++++++++++++++++++ X10D.Tests/src/IO/TextWriterTests.Single.cs | 131 ++++++++++++++++++ ...riterExtensions.WriteLineNoAlloc.Double.cs | 83 +++++++++++ ...riterExtensions.WriteLineNoAlloc.Single.cs | 83 +++++++++++ ...extWriterExtensions.WriteNoAlloc.Double.cs | 91 ++++++++++++ ...extWriterExtensions.WriteNoAlloc.Single.cs | 91 ++++++++++++ 6 files changed, 610 insertions(+) create mode 100644 X10D.Tests/src/IO/TextWriterTests.Double.cs create mode 100644 X10D.Tests/src/IO/TextWriterTests.Single.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Double.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Single.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Double.cs create mode 100644 X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Single.cs diff --git a/X10D.Tests/src/IO/TextWriterTests.Double.cs b/X10D.Tests/src/IO/TextWriterTests.Double.cs new file mode 100644 index 0000000..6f6ed7f --- /dev/null +++ b/X10D.Tests/src/IO/TextWriterTests.Double.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_GivenDouble_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteNoAlloc(420.0)); + Assert.Throws(() => writer.WriteNoAlloc(420.0, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420.0, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldThrowObjectDisposedException_GivenDouble_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteNoAlloc(420.0)); + Assert.Throws(() => writer.WriteNoAlloc(420.0, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420.0, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowArgumentNullException_GivenDouble_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteLineNoAlloc(420.0)); + Assert.Throws(() => writer.WriteLineNoAlloc(420.0, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420.0, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowObjectDisposedException_GivenDouble_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteLineNoAlloc(420.0)); + Assert.Throws(() => writer.WriteLineNoAlloc(420.0, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420.0, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenDouble() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420.0); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenDouble_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420.0, "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_GivenDouble_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420.0, "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_GivenDouble() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420.0); + _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_GivenDouble_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420.0, "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_GivenDouble_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420.0, "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.Single.cs b/X10D.Tests/src/IO/TextWriterTests.Single.cs new file mode 100644 index 0000000..3ccba4f --- /dev/null +++ b/X10D.Tests/src/IO/TextWriterTests.Single.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_GivenSingle_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteNoAlloc(420.0f)); + Assert.Throws(() => writer.WriteNoAlloc(420.0f, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420.0f, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldThrowObjectDisposedException_GivenSingle_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteNoAlloc(420.0f)); + Assert.Throws(() => writer.WriteNoAlloc(420.0f, "N0")); + Assert.Throws(() => writer.WriteNoAlloc(420.0f, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowArgumentNullException_GivenSingle_AndNullWriter() + { + TextWriter writer = null!; + Assert.Throws(() => writer.WriteLineNoAlloc(420.0f)); + Assert.Throws(() => writer.WriteLineNoAlloc(420.0f, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420.0f, "N0", null)); + } + + [Test] + public void WriteLineNoAlloc_ShouldThrowObjectDisposedException_GivenSingle_AndDisposedStream() + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Dispose(); + stream.Dispose(); + + Assert.Throws(() => writer.WriteLineNoAlloc(420.0f)); + Assert.Throws(() => writer.WriteLineNoAlloc(420.0f, "N0")); + Assert.Throws(() => writer.WriteLineNoAlloc(420.0f, "N0", null)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenSingle() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420.0f); + _writer.Flush(); + + string actual = Encoding.UTF8.GetString(_stream.ToArray()); + const string expected = "420"; + + Assert.That(actual, Is.EqualTo(expected)); + } + + [Test] + public void WriteNoAlloc_ShouldWriteTextValue_GivenSingle_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420.0f, "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_GivenSingle_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteNoAlloc(420.0f, "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_GivenSingle() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420.0f); + _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_GivenSingle_AndFormatString() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420.0f, "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_GivenSingle_AndFormatString_AndCultureInfo() + { + Assert.That(_stream.Length, Is.Zero); + _writer.WriteLineNoAlloc(420.0f, "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/src/IO/TextWriterExtensions.WriteLineNoAlloc.Double.cs b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Double.cs new file mode 100644 index 0000000..1a63f42 --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Double.cs @@ -0,0 +1,83 @@ +using System.Globalization; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// Writes the text representation of an 8-byte floating-point value to the text stream, followed by a line terminator, + /// without allocating a string. + /// + /// The to write to. + /// The 8-byte floating-point value 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, double 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 an 8-byte floating-point value to the text stream, followed by a line terminator, + /// without allocating a string. + /// + /// The to write to. + /// The 8-byte floating-point value 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, double value, ReadOnlySpan format) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + WriteLineNoAlloc(writer, value, format, CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of an 8-byte floating-point value to the text stream, followed by a line terminator, + /// without allocating a string. + /// + /// The to write to. + /// The 8-byte floating-point value 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, double 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(); + } +} diff --git a/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Single.cs b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Single.cs new file mode 100644 index 0000000..bb6d2e0 --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteLineNoAlloc.Single.cs @@ -0,0 +1,83 @@ +using System.Globalization; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// Writes the text representation of a 4-byte floating-point value to the text stream, followed by a line terminator, + /// without allocating a string. + /// + /// The to write to. + /// The 4-byte floating-point value 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, float 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 floating-point value to the text stream, followed by a line terminator, + /// without allocating a string. + /// + /// The to write to. + /// The 4-byte floating-point value 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, float value, ReadOnlySpan format) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + WriteLineNoAlloc(writer, value, format, CultureInfo.CurrentCulture); + } + + /// + /// Writes the text representation of a 4-byte floating-point value to the text stream, followed by a line terminator, + /// without allocating a string. + /// + /// The to write to. + /// The 4-byte floating-point value 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, float 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(); + } +} diff --git a/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Double.cs b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Double.cs new file mode 100644 index 0000000..6b6de18 --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Double.cs @@ -0,0 +1,91 @@ +using System.Globalization; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// Writes the text representation of an 8-byte floating-point value to the text stream, without allocating a string. + /// + /// The to write to. + /// The 8-byte floating-point value 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, double 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 an 8-byte floating-point value to the text stream, without allocating a string. + /// + /// The to write to. + /// The 8-byte floating-point value 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, double 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 an 8-byte floating-point value to the text stream, without allocating a string. + /// + /// The to write to. + /// The 8-byte floating-point value 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, double value, ReadOnlySpan format, + IFormatProvider? formatProvider) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + Span buffer = stackalloc char[100]; + 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)); + } + } +} diff --git a/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Single.cs b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Single.cs new file mode 100644 index 0000000..f595593 --- /dev/null +++ b/X10D/src/IO/TextWriterExtensions.WriteNoAlloc.Single.cs @@ -0,0 +1,91 @@ +using System.Globalization; + +namespace X10D.IO; + +public static partial class TextWriterExtensions +{ + /// + /// Writes the text representation of a 4-byte floating-point value to the text stream, without allocating a string. + /// + /// The to write to. + /// The 4-byte floating-point value 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, float 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 floating-point value to the text stream, without allocating a string. + /// + /// The to write to. + /// The 4-byte floating-point value 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, float 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 floating-point value to the text stream, without allocating a string. + /// + /// The to write to. + /// The 4-byte floating-point value 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, float value, ReadOnlySpan format, + IFormatProvider? formatProvider) + { +#if NET6_0_OR_GREATER + ArgumentNullException.ThrowIfNull(writer); +#else + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } +#endif + + Span buffer = stackalloc char[100]; + 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)); + } + } +} From c5ea6cca5820cdb369c8b32d271cf9fe027129cf Mon Sep 17 00:00:00 2001 From: Oliver Booth Date: Wed, 12 Apr 2023 12:47:32 +0100 Subject: [PATCH 7/7] [ci skip] docs: add conventional commit to contrib guidelines --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c1d7f77..5aae0b4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,10 @@ This project uses C# 11.0 language features where feasible, and adheres to Style There is an `.editorconfig` included in this repository. For quick and painless pull requests, ensure that the analyzer does not throw warnings. +Please ensure that you follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) +specification, as the GitHub release for this project is automatically generated from the commit history, and formatted using the +convetional commits specification. + ### Code style Below are a few pointers to which you may refer, but keep in mind this is not an exhaustive list: