From 858773acf273567bcb7eb5b18c45c9d8e6a97349 Mon Sep 17 00:00:00 2001 From: vnugent Date: Fri, 26 May 2023 21:44:22 -0400 Subject: Minior refactor --- lib/Utils/src/IO/ArrayPoolStreamBuffer.cs | 52 ++++++++++---- lib/Utils/src/IO/FileOperations.cs | 47 ++++++------ lib/Utils/src/IO/IStreamBufferFactory.cs | 41 +++++++++++ lib/Utils/src/IO/SlidingWindowBufferExtensions.cs | 11 +-- lib/Utils/src/IO/VnStreamReader.cs | 84 +++++++++++++++++----- lib/Utils/src/IO/VnStreamWriter.cs | 87 ++++++++++++++++++----- lib/Utils/src/IO/WriteOnlyBufferedStream.cs | 61 ++++++++++++---- lib/Utils/src/Memory/ForwardOnlyMemoryReader.cs | 7 +- lib/Utils/src/Memory/ForwardOnlyMemoryWriter.cs | 9 ++- lib/Utils/src/Memory/ForwardOnlyReader.cs | 6 +- lib/Utils/src/Memory/ForwardOnlyWriter.cs | 9 ++- lib/Utils/src/Memory/IStringSerializeable.cs | 6 +- lib/Utils/src/Memory/MemoryUtil.cs | 47 ++++++------ 13 files changed, 339 insertions(+), 128 deletions(-) create mode 100644 lib/Utils/src/IO/IStreamBufferFactory.cs (limited to 'lib/Utils') diff --git a/lib/Utils/src/IO/ArrayPoolStreamBuffer.cs b/lib/Utils/src/IO/ArrayPoolStreamBuffer.cs index df366e3..b62412f 100644 --- a/lib/Utils/src/IO/ArrayPoolStreamBuffer.cs +++ b/lib/Utils/src/IO/ArrayPoolStreamBuffer.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -27,32 +27,46 @@ using System.Buffers; namespace VNLib.Utils.IO { + internal class ArrayPoolStreamBuffer : ISlindingWindowBuffer { + /// + /// The shared instance to allocate buffers + /// from + /// + public static IStreamBufferFactory Shared { get; } = new DefaultFactory(); + private readonly ArrayPool _pool; private T[] _buffer; - public ArrayPoolStreamBuffer(ArrayPool pool, int bufferSize) + /// + /// Creates a new from the + /// given array instance and it came from. + /// + /// The rented array to use + /// The pool to return the array to when completed + public ArrayPoolStreamBuffer(T[] array, ArrayPool pool) { _pool = pool; - _buffer = _pool.Rent(bufferSize); + _buffer = array; } + /// public int WindowStartPos { get; set; } + + /// public int WindowEndPos { get; set; } - + + /// public Memory Buffer => _buffer.AsMemory(); - public void Advance(int count) - { - WindowEndPos += count; - } + /// + public void Advance(int count) => WindowEndPos += count; - public void AdvanceStart(int count) - { - WindowStartPos += count; - } + /// + public void AdvanceStart(int count) => WindowStartPos += count; + /// public void Close() { //Return buffer to pool @@ -60,11 +74,25 @@ namespace VNLib.Utils.IO _buffer = null; } + /// public void Reset() { //Reset window positions WindowStartPos = 0; WindowEndPos = 0; } + + private sealed class DefaultFactory : IStreamBufferFactory + { + /// + public ISlindingWindowBuffer CreateBuffer(int bufferSize) + { + //rent buffer + T[] array = ArrayPool.Shared.Rent(bufferSize); + + //return wrapper + return new ArrayPoolStreamBuffer(array, ArrayPool.Shared); + } + } } } \ No newline at end of file diff --git a/lib/Utils/src/IO/FileOperations.cs b/lib/Utils/src/IO/FileOperations.cs index e040da4..a8cd258 100644 --- a/lib/Utils/src/IO/FileOperations.cs +++ b/lib/Utils/src/IO/FileOperations.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -27,7 +27,8 @@ using System.IO; using System.Runtime.InteropServices; namespace VNLib.Utils.IO -{ +{ + /// /// Contains cross-platform optimized filesystem operations. /// @@ -38,13 +39,15 @@ namespace VNLib.Utils.IO [DllImport("Shlwapi", SetLastError = true, CharSet = CharSet.Auto)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [return:MarshalAs(UnmanagedType.Bool)] - private static unsafe extern bool PathFileExists(char* path); + private static unsafe extern bool PathFileExists([MarshalAs(UnmanagedType.LPWStr)] string path); + [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [return:MarshalAs(UnmanagedType.I4)] - private static unsafe extern int GetFileAttributes(char* path); + private static unsafe extern int GetFileAttributes([MarshalAs(UnmanagedType.LPWStr)] string path); static readonly bool IsWindows = OperatingSystem.IsWindows(); + /// /// Determines if a file exists. If application is current running in the Windows operating system, Shlwapi.PathFileExists is invoked, /// otherwise is invoked @@ -58,15 +61,9 @@ namespace VNLib.Utils.IO { return File.Exists(filePath); } - unsafe - { - //Get a char pointer to the file path - fixed (char* path = filePath) - { - //Invoke the winap file function - return PathFileExists(path); - } - } + + //Invoke the winapi file function + return PathFileExists(filePath); } /// @@ -84,22 +81,18 @@ namespace VNLib.Utils.IO { return File.GetAttributes(filePath); } - unsafe + + //Invoke the winapi file function and cast the returned int value to file attributes + int attr = GetFileAttributes(filePath); + + //Check for error + if (attr == INVALID_FILE_ATTRIBUTES) { - //Get a char pointer to the file path - fixed (char* path = filePath) - { - //Invoke the winap file function and cast the returned int value to file attributes - int attr = GetFileAttributes(path); - //Check for error - if (attr == INVALID_FILE_ATTRIBUTES) - { - throw new FileNotFoundException("The requested file was not found", filePath); - } - //Cast to file attributes and return - return (FileAttributes)attr; - } + throw new FileNotFoundException("The requested file was not found", filePath); } + + //Cast to file attributes and return + return (FileAttributes)attr; } } } \ No newline at end of file diff --git a/lib/Utils/src/IO/IStreamBufferFactory.cs b/lib/Utils/src/IO/IStreamBufferFactory.cs new file mode 100644 index 0000000..f8cfe2d --- /dev/null +++ b/lib/Utils/src/IO/IStreamBufferFactory.cs @@ -0,0 +1,41 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Utils +* File: VnStreamReader.cs +* +* VnStreamReader.cs is part of VNLib.Utils which is part of the larger +* VNLib collection of libraries and utilities. +* +* VNLib.Utils is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published +* by the Free Software Foundation, either version 2 of the License, +* or (at your option) any later version. +* +* VNLib.Utils is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with VNLib.Utils. If not, see http://www.gnu.org/licenses/. +*/ + +namespace VNLib.Utils.IO +{ + /// + /// An interface that allows creating a of the specified type + /// for stream reading/writing + /// + /// The buffer element type + public interface IStreamBufferFactory + { + /// + /// Creates a new of the specified size + /// + /// The minimum size of the buffer to allocate + /// The buffer instance + ISlindingWindowBuffer CreateBuffer(int bufferSize); + } +} \ No newline at end of file diff --git a/lib/Utils/src/IO/SlidingWindowBufferExtensions.cs b/lib/Utils/src/IO/SlidingWindowBufferExtensions.cs index 0509061..c8c62d3 100644 --- a/lib/Utils/src/IO/SlidingWindowBufferExtensions.cs +++ b/lib/Utils/src/IO/SlidingWindowBufferExtensions.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -26,10 +26,10 @@ using System; using System.IO; using System.Threading; using System.Threading.Tasks; +using System.Runtime.CompilerServices; using VNLib.Utils.Memory; using VNLib.Utils.Extensions; -using System.Runtime.CompilerServices; namespace VNLib.Utils.IO { @@ -49,7 +49,7 @@ namespace VNLib.Utils.IO //Nothing to compact if the starting data pointer is at the beining of the window if (sBuf.WindowStartPos > 0) { - //Get span over engire buffer + //Get span over entire buffer Span buffer = sBuf.Buffer.Span; //Get data within window Span usedData = sBuf.Accumulated; @@ -79,6 +79,7 @@ namespace VNLib.Utils.IO //Advance by 1 sBuf.Advance(1); } + /// /// Appends the specified data to the end of the buffer /// @@ -92,6 +93,7 @@ namespace VNLib.Utils.IO val.CopyTo(sBuf.Remaining); sBuf.Advance(val.Length); } + /// /// Formats and appends a value type to the accumulator with proper endianess /// @@ -165,7 +167,7 @@ namespace VNLib.Utils.IO /// /// The output buffer to write data to /// The number of elements written to the buffer - public static ERRNO Read(this ISlindingWindowBuffer sBuf, in Span buffer) + public static ERRNO Read(this ISlindingWindowBuffer sBuf, Span buffer) { //Calculate the amount of data to copy int dataToCopy = Math.Min(buffer.Length, sBuf.AccumulatedSize); @@ -194,6 +196,7 @@ namespace VNLib.Utils.IO //Update the end of the buffer window to the end of the read data accumulator.Advance(read); } + /// /// Fills the remaining window space of the current accumulator with /// data from the specified stream. diff --git a/lib/Utils/src/IO/VnStreamReader.cs b/lib/Utils/src/IO/VnStreamReader.cs index 70b9734..a03a1de 100644 --- a/lib/Utils/src/IO/VnStreamReader.cs +++ b/lib/Utils/src/IO/VnStreamReader.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -33,6 +33,7 @@ using VNLib.Utils.Extensions; namespace VNLib.Utils.IO { + /// /// Binary based buffered text reader, optimized for reading network streams /// @@ -41,8 +42,10 @@ namespace VNLib.Utils.IO private bool disposedValue; private readonly ISlindingWindowBuffer _buffer; + /// public virtual Stream BaseStream { get; } + /// public Encoding Encoding { get; } @@ -50,33 +53,56 @@ namespace VNLib.Utils.IO /// Number of available bytes of buffered data within the current buffer window /// public int Available => _buffer.AccumulatedSize; + /// /// Gets or sets the line termination used to deliminate a line of data /// public ReadOnlyMemory LineTermination { get; set; } - Span IVnTextReader.BufferedDataWindow => _buffer.Accumulated; + + /// + public Span BufferedDataWindow => _buffer.Accumulated; /// - /// Creates a new that reads encoded data from the base. - /// Internal buffers will be alloced from + /// Creates a new that reads encoded data from the base stream + /// and allocates a new buffer of the specified size from the shared /// /// The underlying stream to read data from - /// The to use when reading from the stream + /// The to use when reading from the stream /// The size of the internal binary buffer - public VnStreamReader(Stream baseStream, Encoding enc, int bufferSize) + /// + public VnStreamReader(Stream baseStream, Encoding encoding, int bufferSize) + : this(baseStream, encoding, bufferSize, ArrayPoolStreamBuffer.Shared) { - BaseStream = baseStream; - Encoding = enc; - //Init a new buffer - _buffer = InitializeBuffer(bufferSize); } /// - /// Invoked by the constuctor method to allocte the internal buffer with the specified buffer size. + /// Creates a new that reads encoded data from the base stream + /// and allocates a new buffer of the specified size from the supplied buffer factory. /// - /// The requested size of the buffer to alloc - /// By default requests the buffer from the instance - protected virtual ISlindingWindowBuffer InitializeBuffer(int bufferSize) => new ArrayPoolStreamBuffer(ArrayPool.Shared, bufferSize); + /// The underlying stream to read data from + /// The to use when reading from the stream + /// The size of the internal binary buffer + /// The buffer factory to create the buffer from + /// + public VnStreamReader(Stream baseStream, Encoding encoding, int bufferSize, IStreamBufferFactory bufferFactory) + :this(baseStream, encoding, bufferFactory?.CreateBuffer(bufferSize)!) + { + } + + /// + /// Creates a new that reads encoded data from the base stream + /// and uses the specified buffer. + /// + /// The underlying stream to read data from + /// The to use when reading from the stream + /// The internal to use + /// + public VnStreamReader(Stream baseStream, Encoding encoding, ISlindingWindowBuffer buffer) + { + BaseStream = baseStream ?? throw new ArgumentNullException(nameof(buffer)); + Encoding = encoding ?? throw new ArgumentNullException(nameof(encoding)); + _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + } /// public override async Task ReadLineAsync() @@ -86,42 +112,53 @@ namespace VNLib.Utils.IO { //Get current buffer window Memory buffered = _buffer.AccumulatedBuffer; + //search for line termination in current buffer int term = buffered.IndexOf(LineTermination); + //Termination found in buffer window if (term > -1) { //Capture the line from the begining of the window to the termination Memory line = buffered[..term]; + //Shift the window to the end of the line (excluding the termination) _buffer.AdvanceStart(term + LineTermination.Length); + //Decode the line to a string return Encoding.GetString(line.Span); } //Termination not found } + //Compact the buffer window and see if space is avialble to buffer more data if (_buffer.CompactBufferWindow()) { //There is room, so buffer more data await _buffer.AccumulateDataAsync(BaseStream, CancellationToken.None); + //Check again to see if more data is buffered if (Available <= 0) { //No string found return null; } + //Get current buffer window Memory buffered = _buffer.AccumulatedBuffer; + //search for line termination in current buffer int term = buffered.IndexOf(LineTermination); + //Termination found in buffer window if (term > -1) { //Capture the line from the begining of the window to the termination Memory line = buffered[..term]; + //Shift the window to the end of the line (excluding the termination) _buffer.AdvanceStart(term + LineTermination.Length); + //Decode the line to a string return Encoding.GetString(line.Span); } @@ -136,6 +173,7 @@ namespace VNLib.Utils.IO /// public override int Read(char[] buffer, int index, int count) => Read(buffer.AsSpan(index, count)); + /// public override int Read(Span buffer) { @@ -143,17 +181,23 @@ namespace VNLib.Utils.IO { return 0; } + //Get current buffer window Span buffered = _buffer.Accumulated; + //Convert all avialable data int encoded = Encoding.GetChars(buffered, buffer); + //Shift buffer window to the end of the converted data _buffer.AdvanceStart(encoded); + //return the number of chars written return Encoding.GetCharCount(buffered); } + /// public override void Close() => _buffer.Close(); + /// protected override void Dispose(bool disposing) { @@ -173,8 +217,14 @@ namespace VNLib.Utils.IO _buffer.Reset(); } - void IVnTextReader.Advance(int count) => _buffer.AdvanceStart(count); - void IVnTextReader.FillBuffer() => _buffer.AccumulateData(BaseStream); - ERRNO IVnTextReader.CompactBufferWindow() => _buffer.CompactBufferWindow(); + + /// + public void Advance(int count) => _buffer.AdvanceStart(count); + + /// + public void FillBuffer() => _buffer.AccumulateData(BaseStream); + + /// + public ERRNO CompactBufferWindow() => _buffer.CompactBufferWindow(); } } \ No newline at end of file diff --git a/lib/Utils/src/IO/VnStreamWriter.cs b/lib/Utils/src/IO/VnStreamWriter.cs index f875932..03b0a6e 100644 --- a/lib/Utils/src/IO/VnStreamWriter.cs +++ b/lib/Utils/src/IO/VnStreamWriter.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -51,6 +51,7 @@ namespace VNLib.Utils.IO /// Gets the underlying stream that interfaces with the backing store /// public virtual Stream BaseStream { get; } + /// public override Encoding Encoding { get; } @@ -58,36 +59,60 @@ namespace VNLib.Utils.IO /// Line termination to use when writing lines to the output /// public ReadOnlyMemory LineTermination { get; set; } + /// public override string NewLine { get => Encoding.GetString(LineTermination.Span); set => LineTermination = Encoding.GetBytes(value); } - + + + /// + /// Creates a new that writes encoded data to the base stream + /// and allocates a new buffer of the specified size from the shared + /// + /// The underlying stream to write data to + /// The to use when writing to the stream + /// The size of the internal binary buffer + /// + public VnStreamWriter(Stream baseStream, Encoding encoding, int bufferSize) + : this(baseStream, encoding, bufferSize, ArrayPoolStreamBuffer.Shared) + { + } + /// - /// Creates a new that writes formatted data - /// to the specified base stream + /// Creates a new that writes encoded data to the base stream + /// and allocates a new buffer of the specified size from the supplied buffer factory. /// - /// The stream to write data to - /// The to use when writing data - /// The size of the internal buffer used to buffer binary data before writing to the base stream - public VnStreamWriter(Stream baseStream, Encoding encoding, int bufferSize = 1024) + /// The underlying stream to write data to + /// The to use when writing to the stream + /// The size of the internal binary buffer + /// The buffer factory to create the buffer from + /// + public VnStreamWriter(Stream baseStream, Encoding encoding, int bufferSize, IStreamBufferFactory bufferFactory) + : this(baseStream, encoding, bufferFactory?.CreateBuffer(bufferSize)!) { - //Store base stream - BaseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream)); + } + + /// + /// Creates a new that writes encoded data to the base stream + /// and uses the specified buffer. + /// + /// The underlying stream to write data to + /// The to use when writing to the stream + /// The internal to use + /// + public VnStreamWriter(Stream baseStream, Encoding encoding, ISlindingWindowBuffer 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(); - _buffer = InitializeBuffer(bufferSize); } - /// - /// Invoked by the constuctor method to allocte the internal buffer with the specified buffer size. - /// - /// The requested size of the buffer to alloc - /// By default requests the buffer from the instance - protected virtual ISlindingWindowBuffer InitializeBuffer(int bufferSize) => new ArrayPoolStreamBuffer(ArrayPool.Shared, bufferSize); /// public void Write(byte value) { @@ -97,19 +122,24 @@ namespace VNLib.Utils.IO //There is not enough room to store the single byte Flush(); } + //Store at the end of the window _buffer.Append(value); } + /// public override void Write(char value) { ReadOnlySpan tbuf = MemoryMarshal.CreateSpan(ref value, 0x01); Write(tbuf); } + /// public override void Write(object? value) => Write(value?.ToString()); + /// public override void Write(string? value) => Write(value.AsSpan()); + /// public override void Write(ReadOnlySpan buffer) { @@ -123,8 +153,10 @@ namespace VNLib.Utils.IO { //Get an available buffer window to store characters in and convert the characters to binary Enc.Convert(reader.Window, _buffer.Remaining, true, out int charsUsed, out int bytesUsed, out completed); + //Update byte position _buffer.Advance(bytesUsed); + //Update char position reader.Advance(charsUsed); @@ -136,31 +168,40 @@ namespace VNLib.Utils.IO } } while (!completed); + //Reset the encoder Enc.Reset(); } + /// public override async Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) { Check(); + + ForwardOnlyMemoryReader reader = new(buffer); + //Create a variable for a character buffer window bool completed; - ForwardOnlyMemoryReader reader = new(buffer); do { //Get an available buffer window to store characters in and convert the characters to binary Enc.Convert(reader.Window.Span, _buffer.Remaining, true, out int charsUsed, out int bytesUsed, out completed); + //Update byte position _buffer.Advance(bytesUsed); + //Update char position reader.Advance(charsUsed); + //Converting did not complete because the buffer was too small if (!completed || reader.WindowSize == 0) { //Flush the buffer and continue await FlushWriterAsync(cancellationToken); } + } while (!completed); + //Reset the encoder Enc.Reset(); } @@ -169,18 +210,23 @@ namespace VNLib.Utils.IO public override void WriteLine() { Check(); + //See if there is room in the binary buffer if (_buffer.RemainingSize < LineTermination.Length) { //There is not enough room to store the termination, so we need to flush the buffer Flush(); } + _buffer.Append(LineTermination.Span); } + /// public override void WriteLine(object? value) => WriteLine(value?.ToString()); + /// public override void WriteLine(string? value) => WriteLine(value.AsSpan()); + /// public override void WriteLine(ReadOnlySpan buffer) { @@ -204,6 +250,7 @@ namespace VNLib.Utils.IO _buffer.Reset(); } } + /// /// Asynchronously flushes the internal buffers to the , and resets the internal buffer state /// @@ -212,6 +259,7 @@ namespace VNLib.Utils.IO public async ValueTask FlushWriterAsync(CancellationToken cancellationToken = default) { Check(); + if (_buffer.AccumulatedSize > 0) { //Flush current window to the stream @@ -232,6 +280,7 @@ namespace VNLib.Utils.IO _buffer.Reset(); Enc.Reset(); } + /// public override void Close() { @@ -252,6 +301,7 @@ namespace VNLib.Utils.IO closed = true; } } + /// protected override void Dispose(bool disposing) { @@ -267,6 +317,7 @@ namespace VNLib.Utils.IO throw new ObjectDisposedException("The stream is closed"); } } + /// public override async ValueTask DisposeAsync() { diff --git a/lib/Utils/src/IO/WriteOnlyBufferedStream.cs b/lib/Utils/src/IO/WriteOnlyBufferedStream.cs index 5e7faa1..e6e138e 100644 --- a/lib/Utils/src/IO/WriteOnlyBufferedStream.cs +++ b/lib/Utils/src/IO/WriteOnlyBufferedStream.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -24,7 +24,6 @@ using System; using System.IO; -using System.Buffers; using System.Threading; using System.Threading.Tasks; @@ -46,28 +45,45 @@ namespace VNLib.Utils.IO public Stream BaseStream { get; init; } /// - /// Initalizes a new using the - /// specified backing stream, using the specified buffer size, and - /// optionally leaves the stream open + /// Initalizes a new using the specified backing + /// stream, allocating a pooled buffer of the specified size, and optionally leaves the stream open. /// /// The backing stream to write buffered data to /// The size of the internal buffer /// A value indicating of the stream should be left open when the buffered stream is closed + /// public WriteOnlyBufferedStream(Stream baseStream, int bufferSize, bool leaveOpen = false) + : this(baseStream, bufferSize, ArrayPoolStreamBuffer.Shared, leaveOpen) + { + } + + /// + /// Creates a new that writes encoded data to the base stream + /// and allocates a new buffer of the specified size from the supplied buffer factory. + /// + /// The underlying stream to write data to + /// The size of the internal binary buffer + /// The buffer factory to create the buffer from + /// A value indicating of the stream should be left open when the buffered stream is closed + /// + public WriteOnlyBufferedStream(Stream baseStream, int bufferSize, IStreamBufferFactory bufferFactory, bool leaveOpen = false) + : this(baseStream, bufferFactory?.CreateBuffer(bufferSize)!, leaveOpen) { - BaseStream = baseStream; - //Create buffer - _buffer = InitializeBuffer(bufferSize); - LeaveOpen = leaveOpen; } + /// - /// Invoked by the constuctor method to allocte the internal buffer with the specified buffer size. + /// Creates a new that writes encoded data to the base stream + /// and uses the specified buffer. /// - /// The requested size of the buffer to alloc - /// By default requests the buffer from the instance - protected virtual ISlindingWindowBuffer InitializeBuffer(int bufferSize) + /// The underlying stream to write data to + /// The internal to use + /// A value indicating of the stream should be left open when the buffered stream is closed + /// + public WriteOnlyBufferedStream(Stream baseStream, ISlindingWindowBuffer buffer, bool leaveOpen = false) { - return new ArrayPoolStreamBuffer(ArrayPool.Shared, bufferSize); + BaseStream = baseStream ?? throw new ArgumentNullException(nameof(buffer)); + _buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + LeaveOpen = leaveOpen; } /// @@ -89,6 +105,7 @@ namespace VNLib.Utils.IO _buffer.Close(); } } + /// public override async ValueTask DisposeAsync() { @@ -115,6 +132,7 @@ namespace VNLib.Utils.IO /// public override void Flush() => WriteBuffer(); + /// public override Task FlushAsync(CancellationToken cancellationToken) => WriteBufferAsync(cancellationToken).AsTask(); @@ -138,6 +156,7 @@ namespace VNLib.Utils.IO _buffer.Reset(); } } + /// public override void Write(byte[] buffer, int offset, int count) => Write(buffer.AsSpan(offset, count)); @@ -182,6 +201,7 @@ namespace VNLib.Utils.IO { //Buffer is full and needs to be flushed await WriteBufferAsync(cancellationToken); + //Advance reader and continue to buffer reader.Advance(buffered); continue; @@ -197,23 +217,28 @@ namespace VNLib.Utils.IO /// Always false /// public override bool CanRead => false; + /// /// Always returns false /// public override bool CanSeek => false; + /// /// Always true /// public override bool CanWrite => true; + /// /// Returns the size of the underlying buffer /// public override long Length => _buffer.AccumulatedSize; + /// /// Always throws /// /// public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } + /// /// Always throws /// @@ -242,11 +267,19 @@ namespace VNLib.Utils.IO throw new NotSupportedException(); } + /// + /// Always throws + /// + /// public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { throw new NotImplementedException(); } + /// + /// Always throws + /// + /// public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) { throw new NotImplementedException(); diff --git a/lib/Utils/src/Memory/ForwardOnlyMemoryReader.cs b/lib/Utils/src/Memory/ForwardOnlyMemoryReader.cs index 39c9594..53d3d77 100644 --- a/lib/Utils/src/Memory/ForwardOnlyMemoryReader.cs +++ b/lib/Utils/src/Memory/ForwardOnlyMemoryReader.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -31,7 +31,7 @@ namespace VNLib.Utils.Memory /// reader for a memory segment /// /// The element type - public struct ForwardOnlyMemoryReader + public record struct ForwardOnlyMemoryReader { private readonly ReadOnlyMemory _segment; private readonly int _size; @@ -43,7 +43,7 @@ namespace VNLib.Utils.Memory /// of the specified type using the specified internal buffer /// /// The buffer to read from - public ForwardOnlyMemoryReader(in ReadOnlyMemory buffer) + public ForwardOnlyMemoryReader(ReadOnlyMemory buffer) { _segment = buffer; _size = buffer.Length; @@ -54,6 +54,7 @@ namespace VNLib.Utils.Memory /// The remaining data window /// public readonly ReadOnlyMemory Window => _segment[_position..]; + /// /// The number of elements remaining in the window /// diff --git a/lib/Utils/src/Memory/ForwardOnlyMemoryWriter.cs b/lib/Utils/src/Memory/ForwardOnlyMemoryWriter.cs index 4f5286d..e0fbe41 100644 --- a/lib/Utils/src/Memory/ForwardOnlyMemoryWriter.cs +++ b/lib/Utils/src/Memory/ForwardOnlyMemoryWriter.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -29,16 +29,18 @@ namespace VNLib.Utils.Memory /// /// Provides a mutable sliding buffer writer /// - public struct ForwardOnlyMemoryWriter + public record struct ForwardOnlyMemoryWriter { /// /// The buffer for writing output data to /// public readonly Memory Buffer { get; } + /// /// The number of characters written to the buffer /// public int Written { readonly get; set; } + /// /// The number of characters remaining in the buffer /// @@ -53,7 +55,7 @@ namespace VNLib.Utils.Memory /// Creates a new assigning the specified buffer /// /// The buffer to write data to - public ForwardOnlyMemoryWriter(in Memory buffer) + public ForwardOnlyMemoryWriter(Memory buffer) { Buffer = buffer; Written = 0; @@ -83,6 +85,7 @@ namespace VNLib.Utils.Memory //update char position Written += data.Length; } + /// /// Appends a single item to the buffer /// diff --git a/lib/Utils/src/Memory/ForwardOnlyReader.cs b/lib/Utils/src/Memory/ForwardOnlyReader.cs index 9829df3..c109e4b 100644 --- a/lib/Utils/src/Memory/ForwardOnlyReader.cs +++ b/lib/Utils/src/Memory/ForwardOnlyReader.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -43,7 +43,7 @@ namespace VNLib.Utils.Memory /// of the specified type using the specified internal buffer /// /// The buffer to read from - public ForwardOnlyReader(in ReadOnlySpan buffer) + public ForwardOnlyReader(ReadOnlySpan buffer) { _segment = buffer; _size = buffer.Length; @@ -57,7 +57,7 @@ namespace VNLib.Utils.Memory /// /// The buffer to read from /// The offset within the supplied buffer to begin the reader at - public ForwardOnlyReader(in ReadOnlySpan buffer, int offset) + public ForwardOnlyReader(ReadOnlySpan buffer, int offset) { _segment = buffer[offset..]; _size = _segment.Length; diff --git a/lib/Utils/src/Memory/ForwardOnlyWriter.cs b/lib/Utils/src/Memory/ForwardOnlyWriter.cs index efd7f2b..aa14a5f 100644 --- a/lib/Utils/src/Memory/ForwardOnlyWriter.cs +++ b/lib/Utils/src/Memory/ForwardOnlyWriter.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -35,10 +35,12 @@ namespace VNLib.Utils.Memory /// The buffer for writing output data to /// public readonly Span Buffer { get; } + /// /// The number of characters written to the buffer /// public int Written { readonly get; set; } + /// /// The number of characters remaining in the buffer /// @@ -53,7 +55,7 @@ namespace VNLib.Utils.Memory /// Creates a new assigning the specified buffer /// /// The buffer to write data to - public ForwardOnlyWriter(in Span buffer) + public ForwardOnlyWriter(Span buffer) { Buffer = buffer; Written = 0; @@ -65,7 +67,7 @@ namespace VNLib.Utils.Memory /// /// The buffer to write data to /// The offset to begin the writer at - public ForwardOnlyWriter(in Span buffer, int offset) + public ForwardOnlyWriter(Span buffer, int offset) { Buffer = buffer[offset..]; Written = 0; @@ -95,6 +97,7 @@ namespace VNLib.Utils.Memory //update char position Written += data.Length; } + /// /// Appends a single item to the buffer /// diff --git a/lib/Utils/src/Memory/IStringSerializeable.cs b/lib/Utils/src/Memory/IStringSerializeable.cs index 12cfe52..13b0fd6 100644 --- a/lib/Utils/src/Memory/IStringSerializeable.cs +++ b/lib/Utils/src/Memory/IStringSerializeable.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -37,6 +37,7 @@ namespace VNLib.Utils.Memory /// /// A string of the desired representation of the current instance string Compile(); + /// /// Compiles the current instance into its safe string representation, and writes its /// contents to the specified buffer writer @@ -44,12 +45,13 @@ namespace VNLib.Utils.Memory /// The ouput writer to write the serialized representation to /// void Compile(ref ForwardOnlyWriter writer); + /// /// Compiles the current instance into its safe string representation, and writes its /// contents to the specified buffer writer /// /// The buffer to write the serialized representation to /// The number of characters written to the buffer - ERRNO Compile(in Span buffer); + ERRNO Compile(Span buffer); } } diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs index 26adf85..7369fc0 100644 --- a/lib/Utils/src/Memory/MemoryUtil.cs +++ b/lib/Utils/src/Memory/MemoryUtil.cs @@ -506,34 +506,37 @@ namespace VNLib.Utils.Memory //Check dest bounts CheckBounds(dest, destOffset, count); + //Check if 64bit + if(sizeof(nuint) == 8) + { + //Get the number of bytes to copy + nuint byteCount = ByteCount(count); -#if TARGET_64BIT - //Get the number of bytes to copy - nuint byteCount = ByteCount(count); + //Get memory handle from source + using MemoryHandle srcHandle = source.Pin(0); - //Get memory handle from source - using MemoryHandle srcHandle = source.Pin(0); + //get source offset + T* src = (T*)srcHandle.Pointer + offset; - //get source offset - T* src = (T*)srcHandle.Pointer + offset; + //pin array + fixed (T* dst = dest) + { + //Offset dest ptr + T* dstOffset = dst + destOffset; - //pin array - fixed (T* dst = dest) + //Copy src to set + Buffer.MemoryCopy(src, dstOffset, byteCount, byteCount); + } + } + else { - //Offset dest ptr - T* dstOffset = dst + destOffset; + //If 32bit its safe to use spans - //Copy src to set - Buffer.MemoryCopy(src, dstOffset, byteCount, byteCount); + Span src = source.AsSpan((int)offset, (int)count); + Span dst = dest.AsSpan((int)destOffset, (int)count); + //Copy + src.CopyTo(dst); } -#else - //If 32bit its safe to use spans - - Span src = source.Span.Slice((int)offset, (int)count); - Span dst = dest.AsSpan((int)destOffset, (int)count); - //Copy - src.CopyTo(dst); -#endif } #endregion @@ -645,7 +648,7 @@ namespace VNLib.Utils.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void CheckBounds(T[] block, nuint offset, nuint count) { - if (((nuint)block.LongLength - offset) <= count) + if (offset + count > (ulong)block.LongLength) { throw new ArgumentOutOfRangeException("The offset or count is outside of the range of the block of memory"); } -- cgit