aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils/src/IO
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Utils/src/IO')
-rw-r--r--lib/Utils/src/IO/ArrayPoolStreamBuffer.cs26
-rw-r--r--lib/Utils/src/IO/IVnTextReader.cs7
-rw-r--r--lib/Utils/src/IO/VnMemoryStream.cs109
-rw-r--r--lib/Utils/src/IO/VnStreamWriter.cs36
-rw-r--r--lib/Utils/src/IO/VnTextReaderExtensions.cs4
5 files changed, 118 insertions, 64 deletions
diff --git a/lib/Utils/src/IO/ArrayPoolStreamBuffer.cs b/lib/Utils/src/IO/ArrayPoolStreamBuffer.cs
index b62412f..a943a5b 100644
--- a/lib/Utils/src/IO/ArrayPoolStreamBuffer.cs
+++ b/lib/Utils/src/IO/ArrayPoolStreamBuffer.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -28,7 +28,13 @@ using System.Buffers;
namespace VNLib.Utils.IO
{
- internal class ArrayPoolStreamBuffer<T> : ISlindingWindowBuffer<T>
+ /// <summary>
+ /// Creates a new <see cref="ArrayPoolStreamBuffer{T}"/> from the
+ /// given array instance and <see cref="ArrayPool{T}"/> it came from.
+ /// </summary>
+ /// <param name="array">The rented array to use</param>
+ /// <param name="pool">The pool to return the array to when completed</param>
+ internal class ArrayPoolStreamBuffer<T>(T[] array, ArrayPool<T> pool) : ISlindingWindowBuffer<T>
{
/// <summary>
/// The shared <see cref="IStreamBufferFactory{T}"/> instance to allocate buffers
@@ -36,20 +42,8 @@ namespace VNLib.Utils.IO
/// </summary>
public static IStreamBufferFactory<T> Shared { get; } = new DefaultFactory();
- private readonly ArrayPool<T> _pool;
- private T[] _buffer;
-
- /// <summary>
- /// Creates a new <see cref="ArrayPoolStreamBuffer{T}"/> from the
- /// given array instance and <see cref="ArrayPool{T}"/> it came from.
- /// </summary>
- /// <param name="array">The rented array to use</param>
- /// <param name="pool">The pool to return the array to when completed</param>
- public ArrayPoolStreamBuffer(T[] array, ArrayPool<T> pool)
- {
- _pool = pool;
- _buffer = array;
- }
+ private readonly ArrayPool<T> _pool = pool;
+ private T[] _buffer = array;
///<inheritdoc/>
public int WindowStartPos { get; set; }
diff --git a/lib/Utils/src/IO/IVnTextReader.cs b/lib/Utils/src/IO/IVnTextReader.cs
index 625ba78..93de2d1 100644
--- a/lib/Utils/src/IO/IVnTextReader.cs
+++ b/lib/Utils/src/IO/IVnTextReader.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -23,7 +23,6 @@
*/
using System;
-using System.IO;
using System.Text;
namespace VNLib.Utils.IO
@@ -34,10 +33,6 @@ namespace VNLib.Utils.IO
public interface IVnTextReader
{
/// <summary>
- /// The base stream to read data from
- /// </summary>
- Stream BaseStream { get; }
- /// <summary>
/// The character encoding used by the TextReader
/// </summary>
Encoding Encoding { get; }
diff --git a/lib/Utils/src/IO/VnMemoryStream.cs b/lib/Utils/src/IO/VnMemoryStream.cs
index f4bf970..3f14061 100644
--- a/lib/Utils/src/IO/VnMemoryStream.cs
+++ b/lib/Utils/src/IO/VnMemoryStream.cs
@@ -41,15 +41,21 @@ namespace VNLib.Utils.IO
/// </summary>
public sealed class VnMemoryStream : Stream, ICloneable
{
+ public const int DefaultBufferSize = 4096;
+
private nint _position;
private nint _length;
private bool _isReadonly;
//Memory
private readonly IResizeableMemoryHandle<byte> _buffer;
+
//Default owns handle
private readonly bool OwnsHandle = true;
+ //Lazy loaded memory wrapper
+ private MemoryManager<byte>? _memoryWrapper;
+
/// <summary>
/// Creates a new <see cref="VnMemoryStream"/> pointing to the begining of memory, and consumes the handle.
/// </summary>
@@ -78,15 +84,19 @@ namespace VNLib.Utils.IO
ArgumentNullException.ThrowIfNull(handle);
return handle.CanRealloc || readOnly
- ? new VnMemoryStream(handle, length, readOnly, ownsHandle)
+ ? new VnMemoryStream(handle, existingManager: null, length, readOnly, ownsHandle)
: throw new ArgumentException("The supplied memory handle must be resizable on a writable stream", nameof(handle));
}
/// <summary>
/// Converts a writable <see cref="VnMemoryStream"/> to readonly to allow shallow copies
/// </summary>
+ /// <remarks>
+ /// This funciton will convert the stream passed into it to a readonly stream.
+ /// The function passes through the input stream as the return value
+ /// </remarks>
/// <param name="stream">The stream to make readonly</param>
- /// <returns>The readonly stream</returns>
+ /// <returns>A reference to the modified input stream</returns>
public static VnMemoryStream CreateReadonly(VnMemoryStream stream)
{
ArgumentNullException.ThrowIfNull(stream);
@@ -101,15 +111,21 @@ namespace VNLib.Utils.IO
/// global heap instance.
/// </summary>
public VnMemoryStream() : this(MemoryUtil.Shared) { }
-
+
/// <summary>
/// Create a new memory stream where buffers will be allocated from the specified heap
/// </summary>
/// <param name="heap"><see cref="Win32PrivateHeap"/> to allocate memory from</param>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="ArgumentNullException"></exception>
- public VnMemoryStream(IUnmangedHeap heap) : this(heap, 0, false) { }
-
+ public VnMemoryStream(IUnmangedHeap heap) : this(heap, DefaultBufferSize, false) { }
+
+ /// <summary>
+ /// Creates a new memory stream using the <see cref="MemoryUtil.Shared"/>
+ /// global heap instance.
+ /// </summary>
+ public VnMemoryStream(nuint bufferSize, bool zero) : this(MemoryUtil.Shared, bufferSize, zero) { }
+
/// <summary>
/// Creates a new memory stream and pre-allocates the internal
/// buffer of the specified size on the specified heap to avoid resizing.
@@ -163,7 +179,14 @@ namespace VNLib.Utils.IO
/// <param name="length">The length property of the stream</param>
/// <param name="readOnly">Is the stream readonly (should mostly be true!)</param>
/// <param name="ownsHandle">Does the new stream own the memory -> <paramref name="buffer"/></param>
- private VnMemoryStream(IResizeableMemoryHandle<byte> buffer, nint length, bool readOnly, bool ownsHandle)
+ /// <param name="existingManager">A reference to an existing memory manager class</param>
+ private VnMemoryStream(
+ IResizeableMemoryHandle<byte> buffer,
+ MemoryManager<byte>? existingManager,
+ nint length,
+ bool readOnly,
+ bool ownsHandle
+ )
{
Debug.Assert(length >= 0, "Length must be positive");
Debug.Assert(buffer.CanRealloc || readOnly, "The supplied buffer is not resizable on a writable stream");
@@ -172,6 +195,7 @@ namespace VNLib.Utils.IO
_buffer = buffer; //Consume the handle
_length = length; //Store length of the buffer
_isReadonly = readOnly;
+ _memoryWrapper = existingManager;
}
/// <summary>
@@ -189,7 +213,7 @@ namespace VNLib.Utils.IO
//Create a new readonly copy (stream does not own the handle)
return !_isReadonly
? throw new NotSupportedException("This stream is not readonly. Cannot create shallow copy on a mutable stream")
- : new VnMemoryStream(_buffer, _length, true, false);
+ : new VnMemoryStream(_buffer, _memoryWrapper, _length, readOnly: true, ownsHandle: false);
}
/// <summary>
@@ -247,27 +271,31 @@ namespace VNLib.Utils.IO
cancellationToken.ThrowIfCancellationRequested();
+ //Memory manager requires 32bit or less in length
if(_length < Int32.MaxValue)
{
- //Safe to alloc a memory manager to do copy
- using MemoryManager<byte> asMemManager = _buffer.ToMemoryManager(false);
+ //Get/alloc the internal memory manager and get the block
+ ReadOnlyMemory<byte> asMemory = AsMemory();
+
+ Debug.Assert(asMemory.Length >= LenToPosDiff, "Internal memory block smaller than desired for stream copy");
/*
* CopyTo starts at the current position, as if calling Read()
* so the reader must be offset to match and the _length gives us the
* actual length of the stream and therefor the segment size
- */
+ */
- while(LenToPosDiff > 0)
+ while (LenToPosDiff > 0)
{
int blockSize = Math.Min((int)LenToPosDiff, bufferSize);
- Memory<byte> window = asMemManager.Memory.Slice((int)_position, blockSize);
+
+ ReadOnlyMemory<byte> window = asMemory.Slice((int)_position, blockSize);
//write async
await destination.WriteAsync(window, cancellationToken);
//Update position
- _position+= bufferSize;
+ _position += bufferSize;
}
}
else
@@ -354,7 +382,7 @@ namespace VNLib.Utils.IO
}
///<inheritdoc/>
- public override unsafe int ReadByte()
+ public override int ReadByte()
{
if (LenToPosDiff == 0)
{
@@ -362,7 +390,7 @@ namespace VNLib.Utils.IO
}
//get the value at the current position
- ref byte ptr = ref _buffer.GetOffsetByteRef((nuint)_position);
+ ref byte ptr = ref _buffer.GetOffsetByteRef(_position);
//Increment position
_position++;
@@ -486,7 +514,7 @@ namespace VNLib.Utils.IO
throw new NotSupportedException("Write operation is not allowed on readonly stream!");
}
//Calculate the new final position
- nint newPos = (_position + buffer.Length);
+ nint newPos = checked(_position + buffer.Length);
//Determine if the buffer needs to be expanded
if (buffer.Length > LenToPosDiff)
{
@@ -495,8 +523,16 @@ namespace VNLib.Utils.IO
//Update length
_length = newPos;
}
+
//Copy the input buffer to the internal buffer
- MemoryUtil.Copy(buffer, 0, _buffer, (nuint)_position, buffer.Length);
+ MemoryUtil.Copy(
+ source: buffer,
+ sourceOffset: 0,
+ dest: _buffer,
+ destOffset: (nuint)_position,
+ count: buffer.Length
+ );
+
//Update the position
_position = newPos;
}
@@ -550,6 +586,7 @@ namespace VNLib.Utils.IO
/// <summary>
/// Returns a <see cref="ReadOnlySpan{T}"/> window over the data within the entire stream
+ /// that is equal in length to the stream length.
/// </summary>
/// <returns>A <see cref="ReadOnlySpan{T}"/> of the data within the entire stream</returns>
/// <exception cref="OverflowException"></exception>
@@ -560,6 +597,40 @@ namespace VNLib.Utils.IO
//Get span with no offset
return _buffer.AsSpan(0, len);
}
+
+ /// <summary>
+ /// Returns a <see cref="ReadOnlyMemory{T}"/> structure which is a window of the buffered
+ /// data as it currently sits. For writeable straems, you must call this function
+ /// every time the size of the stream changes. The memory structure is just a "pointer" to
+ /// the internal buffer.
+ /// </summary>
+ /// <returns>
+ /// A memory snapshot of the stream.
+ /// </returns>
+ /// <remarks>
+ /// This function causes an internal allocation on the first call. After the first call
+ /// to this function, all calls are thread-safe.
+ /// </remarks>
+ public ReadOnlyMemory<byte> AsMemory()
+ {
+ /*
+ * Safe cast stram length to int, because memory window requires a 32bit
+ * integer. Also will throw before allocating the mmemory manager
+ */
+
+ int len = Convert.ToInt32(_length);
+
+ //Defer/lazy init the memory manager
+ MemoryManager<byte> asMemory = LazyInitializer.EnsureInitialized(ref _memoryWrapper, AllocMemManager);
+
+ Debug.Assert(asMemory != null);
+
+ /*
+ * Buffer window may be larger than the actual stream legnth, so
+ * slice the memory to the actual length of the stream
+ */
+ return asMemory.Memory[..len];
+ }
/// <summary>
/// If the current stream is a readonly stream, creates a shallow copy for reading only.
@@ -568,5 +639,7 @@ namespace VNLib.Utils.IO
/// <exception cref="NotSupportedException"></exception>
public object Clone() => GetReadonlyShallowCopy();
+ private MemoryManager<byte> AllocMemManager() => _buffer.ToMemoryManager(false);
+
}
-} \ No newline at end of file
+}
diff --git a/lib/Utils/src/IO/VnStreamWriter.cs b/lib/Utils/src/IO/VnStreamWriter.cs
index ddebc07..7d43f48 100644
--- a/lib/Utils/src/IO/VnStreamWriter.cs
+++ b/lib/Utils/src/IO/VnStreamWriter.cs
@@ -41,21 +41,29 @@ namespace VNLib.Utils.IO
/// Provides a memory optimized <see cref="TextWriter"/> implementation. Optimized for writing
/// to network streams
/// </summary>
- public class VnStreamWriter : TextWriter
+ /// <remarks>
+ /// Creates a new <see cref="VnStreamWriter"/> that writes encoded data to the base stream
+ /// and uses the specified buffer.
+ /// </remarks>
+ /// <param name="baseStream">The underlying stream to write data to</param>
+ /// <param name="encoding">The <see cref="Encoding"/> to use when writing to the stream</param>
+ /// <param name="buffer">The internal <see cref="ISlindingWindowBuffer{T}"/> to use</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ public class VnStreamWriter(Stream baseStream, Encoding encoding, ISlindingWindowBuffer<byte> buffer) : TextWriter
{
- private readonly Encoder Enc;
+ private readonly Encoder Enc = encoding.GetEncoder();
- private readonly ISlindingWindowBuffer<byte> _buffer;
+ private readonly ISlindingWindowBuffer<byte> _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer));
private bool closed;
/// <summary>
/// Gets the underlying stream that interfaces with the backing store
/// </summary>
- public virtual Stream BaseStream { get; }
+ public virtual Stream BaseStream { get; } = baseStream ?? throw new ArgumentNullException(nameof(buffer));
///<inheritdoc/>
- public override Encoding Encoding { get; }
+ public override Encoding Encoding { get; } = encoding ?? throw new ArgumentNullException(nameof(encoding));
/// <summary>
/// Line termination to use when writing lines to the output
@@ -97,24 +105,6 @@ namespace VNLib.Utils.IO
{
}
- /// <summary>
- /// Creates a new <see cref="VnStreamWriter"/> that writes encoded data to the base stream
- /// and uses the specified buffer.
- /// </summary>
- /// <param name="baseStream">The underlying stream to write data to</param>
- /// <param name="encoding">The <see cref="Encoding"/> to use when writing to the stream</param>
- /// <param name="buffer">The internal <see cref="ISlindingWindowBuffer{T}"/> to use</param>
- /// <exception cref="ArgumentNullException"></exception>
- public VnStreamWriter(Stream baseStream, Encoding encoding, ISlindingWindowBuffer<byte> buffer)
- {
- BaseStream = baseStream ?? throw new ArgumentNullException(nameof(buffer));
- Encoding = encoding ?? throw new ArgumentNullException(nameof(encoding));
- _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer));
-
- //Get an encoder
- Enc = encoding.GetEncoder();
- }
-
///<inheritdoc/>
public void Write(byte value)
{
diff --git a/lib/Utils/src/IO/VnTextReaderExtensions.cs b/lib/Utils/src/IO/VnTextReaderExtensions.cs
index 9ca5ae5..5dc6117 100644
--- a/lib/Utils/src/IO/VnTextReaderExtensions.cs
+++ b/lib/Utils/src/IO/VnTextReaderExtensions.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2023 Vaughn Nugent
+* Copyright (c) 2024 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -66,6 +66,7 @@ namespace VNLib.Utils.IO
/// <remarks>Allows reading lines of data from the stream without allocations</remarks>
public static ERRNO ReadLine<T>(this T reader, Span<char> charBuffer) where T : class, IVnTextReader
{
+ ArgumentNullException.ThrowIfNull(reader);
return ReadLineInternal(ref reader, charBuffer);
}
@@ -118,6 +119,7 @@ namespace VNLib.Utils.IO
/// <remarks>You should use the <see cref="IVnTextReader.Available"/> property to know how much remaining data is buffered</remarks>
public static int ReadRemaining<T>(this T reader, Span<byte> buffer) where T : class, IVnTextReader
{
+ ArgumentNullException.ThrowIfNull(reader);
return ReadRemainingInternal(ref reader, buffer);
}