diff --git a/X10D/src/ExceptionMessages.Designer.cs b/X10D/src/ExceptionMessages.Designer.cs
index d575442..a22c995 100644
--- a/X10D/src/ExceptionMessages.Designer.cs
+++ b/X10D/src/ExceptionMessages.Designer.cs
@@ -60,6 +60,15 @@ namespace X10D {
}
}
+ ///
+ /// Looks up a localized string similar to The buffer is too small to contain the data..
+ ///
+ internal static string BufferTooSmall {
+ get {
+ return ResourceManager.GetString("BufferTooSmall", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to count must be greater than or equal to 0..
///
diff --git a/X10D/src/ExceptionMessages.resx b/X10D/src/ExceptionMessages.resx
index a86e79e..867da37 100644
--- a/X10D/src/ExceptionMessages.resx
+++ b/X10D/src/ExceptionMessages.resx
@@ -23,6 +23,9 @@
PublicKeyToken=b77a5c561934e089
+
+ The buffer is too small to contain the data.
+
{{0}} is not a class.
diff --git a/X10D/src/StringBuilderExtensions/StringBuilderReader.cs b/X10D/src/StringBuilderExtensions/StringBuilderReader.cs
new file mode 100644
index 0000000..c07fb12
--- /dev/null
+++ b/X10D/src/StringBuilderExtensions/StringBuilderReader.cs
@@ -0,0 +1,214 @@
+using System.Text;
+
+namespace X10D;
+
+// NOTE: the overriden async overloads simply wrap the result of their sync counterparts because StringBuilder isn't inherently
+// async. calling Task.FromResult (or creating a new ValueTask) is sufficient enough in this case, because there is simply no
+// other way to have native async reading of a StringBuilder
+
+///
+/// Represents a reads from a .
+///
+public class StringBuilderReader : TextReader
+{
+ private readonly StringBuilder _stringBuilder;
+ private int _index;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The to wrap.
+ /// is .
+ public StringBuilderReader(StringBuilder stringBuilder)
+ {
+ _stringBuilder = stringBuilder ?? throw new ArgumentNullException(nameof(stringBuilder));
+ }
+
+ ///
+ public override int Read()
+ {
+ if (_index >= _stringBuilder.Length)
+ {
+ return -1;
+ }
+
+ return _stringBuilder[_index++];
+ }
+
+ ///
+ public override int Read(char[] buffer, int index, int count)
+ {
+ if (buffer is null)
+ {
+ throw new ArgumentNullException(nameof(buffer));
+ }
+
+ if (index < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(index));
+ }
+
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count));
+ }
+
+ if (buffer.Length - index < count)
+ {
+ throw new ArgumentException(ExceptionMessages.BufferTooSmall, nameof(buffer));
+ }
+
+ if (_index >= _stringBuilder.Length)
+ {
+ return -1;
+ }
+
+ int length = Math.Min(_stringBuilder.Length - _index, count);
+ _stringBuilder.CopyTo(_index, buffer, index, length);
+ _index += length;
+ return length;
+ }
+
+ ///
+ public override int Read(Span buffer)
+ {
+ int count = Math.Min(buffer.Length, _stringBuilder.Length - _index);
+ for (var index = 0; index < count; index++)
+ {
+ buffer[index] = _stringBuilder[index + _index];
+ }
+
+ _index += count;
+ return count;
+ }
+
+ ///
+ public override Task ReadAsync(char[] buffer, int index, int count)
+ {
+ return Task.FromResult(Read(buffer, index, count));
+ }
+
+ // except not really 🔽
+ ///
+ /// Asynchronously reads the characters from the current stream into a memory block.
+ ///
+ ///
+ /// When this method returns, contains the specified memory block of characters replaced by the characters read from the
+ /// current source.
+ ///
+ /// Ignored.
+ ///
+ /// A value task that represents the asynchronous read operation. The value of the type parameter contains the number of
+ /// characters that have been read, or 0 if at the end of the stream and no data was read. The number will be less than or
+ /// equal to the buffer length, depending on whether the data is available within the stream.
+ ///
+ public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default)
+ {
+ return new ValueTask(Read(buffer.Span));
+ }
+
+ ///
+ public override int ReadBlock(Span buffer)
+ {
+ return Read(buffer);
+ }
+
+ ///
+ public override Task ReadBlockAsync(char[] buffer, int index, int count)
+ {
+ return Task.FromResult(ReadBlock(buffer, index, count));
+ }
+
+ // except not really 🔽
+ ///
+ /// Asynchronously reads the characters from the current stream and writes the data to a buffer.
+ ///
+ ///
+ /// When this method returns, contains the specified memory block of characters replaced by the characters read from the
+ /// current source.
+ ///
+ /// Ignored.
+ ///
+ /// A value task that represents the asynchronous read operation. The value of the type parameter contains the total
+ /// number of characters read into the buffer. The result value can be less than the number of characters requested if the
+ /// number of characters currently available is less than the requested number, or it can be 0 (zero) if the end of the
+ /// stream has been reached.
+ ///
+ public override ValueTask ReadBlockAsync(Memory buffer, CancellationToken cancellationToken = default)
+ {
+ return new ValueTask(ReadBlock(buffer.Span));
+ }
+
+ ///
+ public override Task ReadLineAsync()
+ {
+ return Task.FromResult(ReadLine());
+ }
+
+ ///
+ public override Task ReadToEndAsync()
+ {
+ return Task.FromResult(ReadToEnd());
+ }
+
+ ///
+ public override int Peek()
+ {
+ if (_index >= _stringBuilder.Length)
+ {
+ return -1;
+ }
+
+ return _stringBuilder[_index];
+ }
+
+ ///
+ public override int ReadBlock(char[] buffer, int index, int count)
+ {
+ if (_index >= _stringBuilder.Length)
+ {
+ return -1;
+ }
+
+ int length = Math.Min(count, _stringBuilder.Length - _index);
+ _stringBuilder.CopyTo(_index, buffer, index, length);
+ _index += length;
+ return length;
+ }
+
+ ///
+ public override string? ReadLine()
+ {
+ if (_index >= _stringBuilder.Length)
+ {
+ return null;
+ }
+
+ int start = _index;
+ while (_index < _stringBuilder.Length && _stringBuilder[_index] != '\n')
+ {
+ _index++;
+ }
+
+ if (_index < _stringBuilder.Length)
+ {
+ _index++;
+ }
+
+ return _stringBuilder.ToString(start, _index - start - 1);
+ }
+
+ ///
+ public override string ReadToEnd()
+ {
+ var value = _stringBuilder.ToString(_index, _stringBuilder.Length - _index);
+ _index = _stringBuilder.Length;
+ return value;
+ }
+
+ ///
+ public override void Close()
+ {
+ _index = _stringBuilder.Length;
+ }
+}