From de94d788e9a47432a7630a8215896b8dd3628599 Mon Sep 17 00:00:00 2001 From: vnugent Date: Sun, 8 Jan 2023 16:01:54 -0500 Subject: Reorder + analyzer cleanup --- Utils/src/IO/ArrayPoolStreamBuffer.cs | 70 ---- Utils/src/IO/BackingStream.cs | 181 ---------- Utils/src/IO/FileOperations.cs | 105 ------ Utils/src/IO/IDataAccumulator.cs | 64 ---- Utils/src/IO/ISlindingWindowBuffer.cs | 91 ----- Utils/src/IO/IVnTextReader.cs | 72 ---- Utils/src/IO/InMemoryTemplate.cs | 196 ----------- Utils/src/IO/IsolatedStorageDirectory.cs | 154 --------- Utils/src/IO/SlidingWindowBufferExtensions.cs | 213 ------------ Utils/src/IO/TemporayIsolatedFile.cs | 57 ---- Utils/src/IO/VnMemoryStream.cs | 469 -------------------------- Utils/src/IO/VnStreamReader.cs | 180 ---------- Utils/src/IO/VnStreamWriter.cs | 292 ---------------- Utils/src/IO/VnTextReaderExtensions.cs | 223 ------------ Utils/src/IO/WriteOnlyBufferedStream.cs | 255 -------------- 15 files changed, 2622 deletions(-) delete mode 100644 Utils/src/IO/ArrayPoolStreamBuffer.cs delete mode 100644 Utils/src/IO/BackingStream.cs delete mode 100644 Utils/src/IO/FileOperations.cs delete mode 100644 Utils/src/IO/IDataAccumulator.cs delete mode 100644 Utils/src/IO/ISlindingWindowBuffer.cs delete mode 100644 Utils/src/IO/IVnTextReader.cs delete mode 100644 Utils/src/IO/InMemoryTemplate.cs delete mode 100644 Utils/src/IO/IsolatedStorageDirectory.cs delete mode 100644 Utils/src/IO/SlidingWindowBufferExtensions.cs delete mode 100644 Utils/src/IO/TemporayIsolatedFile.cs delete mode 100644 Utils/src/IO/VnMemoryStream.cs delete mode 100644 Utils/src/IO/VnStreamReader.cs delete mode 100644 Utils/src/IO/VnStreamWriter.cs delete mode 100644 Utils/src/IO/VnTextReaderExtensions.cs delete mode 100644 Utils/src/IO/WriteOnlyBufferedStream.cs (limited to 'Utils/src/IO') diff --git a/Utils/src/IO/ArrayPoolStreamBuffer.cs b/Utils/src/IO/ArrayPoolStreamBuffer.cs deleted file mode 100644 index df366e3..0000000 --- a/Utils/src/IO/ArrayPoolStreamBuffer.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ArrayPoolStreamBuffer.cs -* -* ArrayPoolStreamBuffer.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/. -*/ - -using System; -using System.Buffers; - -namespace VNLib.Utils.IO -{ - internal class ArrayPoolStreamBuffer : ISlindingWindowBuffer - { - private readonly ArrayPool _pool; - private T[] _buffer; - - public ArrayPoolStreamBuffer(ArrayPool pool, int bufferSize) - { - _pool = pool; - _buffer = _pool.Rent(bufferSize); - } - - public int WindowStartPos { get; set; } - public int WindowEndPos { get; set; } - - public Memory Buffer => _buffer.AsMemory(); - - public void Advance(int count) - { - WindowEndPos += count; - } - - public void AdvanceStart(int count) - { - WindowStartPos += count; - } - - public void Close() - { - //Return buffer to pool - _pool.Return(_buffer); - _buffer = null; - } - - public void Reset() - { - //Reset window positions - WindowStartPos = 0; - WindowEndPos = 0; - } - } -} \ No newline at end of file diff --git a/Utils/src/IO/BackingStream.cs b/Utils/src/IO/BackingStream.cs deleted file mode 100644 index cb56b09..0000000 --- a/Utils/src/IO/BackingStream.cs +++ /dev/null @@ -1,181 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: BackingStream.cs -* -* BackingStream.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/. -*/ - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace VNLib.Utils.IO -{ - /// - /// Provides basic stream support sync/async stream operations to a - /// backing stream with virtual event methods. Provides a pass-through - /// as best as possbile. - /// - public abstract class BackingStream : Stream where T: Stream - { - /// - /// The backing/underlying stream operations are being performed on - /// - protected T BaseStream { get; set; } - /// - /// A value that will cause all calls to write to throw - /// - protected bool ForceReadOnly { get; set; } - /// - public override bool CanRead => BaseStream.CanRead; - /// - public override bool CanSeek => BaseStream.CanSeek; - /// - public override bool CanWrite => BaseStream.CanWrite && !ForceReadOnly; - /// - public override long Length => BaseStream.Length; - /// - public override int WriteTimeout { get => BaseStream.WriteTimeout; set => BaseStream.WriteTimeout = value; } - /// - public override int ReadTimeout { get => BaseStream.ReadTimeout; set => BaseStream.ReadTimeout = value; } - /// - public override long Position { get => BaseStream.Position; set => BaseStream.Position = value; } - /// - public override void Flush() - { - BaseStream.Flush(); - OnFlush(); - } - /// - public override int Read(byte[] buffer, int offset, int count) => BaseStream.Read(buffer, offset, count); - /// - public override int Read(Span buffer) => BaseStream.Read(buffer); - /// - public override long Seek(long offset, SeekOrigin origin) => BaseStream.Seek(offset, origin); - /// - public override void SetLength(long value) => BaseStream.SetLength(value); - /// - public override void Write(byte[] buffer, int offset, int count) - { - if (ForceReadOnly) - { - throw new NotSupportedException("Stream is set to readonly mode"); - } - BaseStream.Write(buffer, offset, count); - //Call onwrite function - OnWrite(count); - } - /// - public override void Write(ReadOnlySpan buffer) - { - if (ForceReadOnly) - { - throw new NotSupportedException("Stream is set to readonly mode"); - } - BaseStream.Write(buffer); - //Call onwrite function - OnWrite(buffer.Length); - } - /// - public override void Close() - { - BaseStream.Close(); - //Call on close function - OnClose(); - } - - /// - /// Raised directly after the base stream is closed, when a call to close is made - /// - protected virtual void OnClose() { } - /// - /// Raised directly after the base stream is flushed, when a call to flush is made - /// - protected virtual void OnFlush() { } - /// - /// Raised directly after a successfull write operation. - /// - /// The number of bytes written to the stream - protected virtual void OnWrite(int count) { } - - /// - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return BaseStream.ReadAsync(buffer, offset, count, cancellationToken); - } - /// - public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) - { - return BaseStream.ReadAsync(buffer, cancellationToken); - } - /// - public override void CopyTo(Stream destination, int bufferSize) => BaseStream.CopyTo(destination, bufferSize); - /// - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - return BaseStream.CopyToAsync(destination, bufferSize, cancellationToken); - } - /// - public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (ForceReadOnly) - { - throw new NotSupportedException("Stream is set to readonly mode"); - } - //We want to maintain pass through as much as possible, so supress warning -#pragma warning disable CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' - await BaseStream.WriteAsync(buffer, offset, count, cancellationToken); -#pragma warning restore CA1835 // Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' - - //Call on-write and pass the number of bytes written - OnWrite(count); - } - /// - public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - if (ForceReadOnly) - { - throw new NotSupportedException("Stream is set to readonly mode"); - } - await BaseStream.WriteAsync(buffer, cancellationToken); - //Call on-write and pass the length - OnWrite(buffer.Length); - } - /// - public override async Task FlushAsync(CancellationToken cancellationToken) - { - await BaseStream.FlushAsync(cancellationToken); - //Call onflush - OnFlush(); - } - - /// - public override async ValueTask DisposeAsync() - { - //Dispose the base stream and await it - await BaseStream.DisposeAsync(); - //Call onclose - OnClose(); - //Suppress finalize - GC.SuppressFinalize(this); - } - } -} \ No newline at end of file diff --git a/Utils/src/IO/FileOperations.cs b/Utils/src/IO/FileOperations.cs deleted file mode 100644 index e040da4..0000000 --- a/Utils/src/IO/FileOperations.cs +++ /dev/null @@ -1,105 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: FileOperations.cs -* -* FileOperations.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/. -*/ - -using System; -using System.IO; -using System.Runtime.InteropServices; - -namespace VNLib.Utils.IO -{ - /// - /// Contains cross-platform optimized filesystem operations. - /// - public static class FileOperations - { - public const int INVALID_FILE_ATTRIBUTES = -1; - - [DllImport("Shlwapi", SetLastError = true, CharSet = CharSet.Auto)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - [return:MarshalAs(UnmanagedType.Bool)] - private static unsafe extern bool PathFileExists(char* path); - [DllImport("kernel32", SetLastError = true, CharSet = CharSet.Auto)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - [return:MarshalAs(UnmanagedType.I4)] - private static unsafe extern int GetFileAttributes(char* 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 - /// - /// the path to the file - /// True if the file can be opened, false otherwise - public static bool FileExists(string filePath) - { - //If windows is detected, use the unmanged function - if (!IsWindows) - { - 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); - } - } - } - - /// - /// If Windows is detected at load time, gets the attributes for the specified file. - /// - /// The path to the existing file - /// The attributes of the file - /// - /// - /// - public static FileAttributes GetAttributes(string filePath) - { - //If windows is detected, use the unmanged function - if (!IsWindows) - { - return File.GetAttributes(filePath); - } - unsafe - { - //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; - } - } - } - } -} \ No newline at end of file diff --git a/Utils/src/IO/IDataAccumulator.cs b/Utils/src/IO/IDataAccumulator.cs deleted file mode 100644 index 5129a55..0000000 --- a/Utils/src/IO/IDataAccumulator.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IDataAccumulator.cs -* -* IDataAccumulator.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/. -*/ - -using System; - -namespace VNLib.Utils.IO -{ - /// - /// A data structure that represents a sliding window over a buffer - /// for resetable forward-only reading or writing - /// - /// The accumuation data type - public interface IDataAccumulator - { - /// - /// Gets the number of available items within the buffer - /// - int AccumulatedSize { get; } - /// - /// The number of elements remaining in the buffer - /// - int RemainingSize { get; } - /// - /// The remaining space in the internal buffer as a contiguous segment - /// - Span Remaining { get; } - /// - /// The buffer window over the accumulated data - /// - Span Accumulated { get; } - - /// - /// Advances the accumulator buffer window by the specified amount - /// - /// The number of elements accumulated - void Advance(int count); - - /// - /// Resets the internal state of the accumulator - /// - void Reset(); - } -} \ No newline at end of file diff --git a/Utils/src/IO/ISlindingWindowBuffer.cs b/Utils/src/IO/ISlindingWindowBuffer.cs deleted file mode 100644 index ff4e142..0000000 --- a/Utils/src/IO/ISlindingWindowBuffer.cs +++ /dev/null @@ -1,91 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ISlindingWindowBuffer.cs -* -* ISlindingWindowBuffer.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/. -*/ - -using System; - -namespace VNLib.Utils.IO -{ - /// - /// Represents a sliding window buffer for reading/wiriting data - /// - /// - public interface ISlindingWindowBuffer : IDataAccumulator - { - /// - /// The number of elements remaining in the buffer - /// - int IDataAccumulator.RemainingSize => Buffer.Length - WindowEndPos; - /// - /// The remaining space in the internal buffer as a contiguous segment - /// - Span IDataAccumulator.Remaining => RemainingBuffer.Span; - /// - /// The buffer window over the accumulated data - /// - Span IDataAccumulator.Accumulated => AccumulatedBuffer.Span; - /// - /// Gets the number of available items within the buffer - /// - int IDataAccumulator.AccumulatedSize => WindowEndPos - WindowStartPos; - - /// - /// The starting positon of the available data within the buffer - /// - int WindowStartPos { get; } - /// - /// The ending position of the available data within the buffer - /// - int WindowEndPos { get; } - /// - /// Buffer memory wrapper - /// - Memory Buffer { get; } - - /// - /// Releases resources used by the current instance - /// - void Close(); - /// - /// - /// Advances the begining of the accumulated data window. - /// - /// - /// This method is used during reading to singal that data - /// has been read from the internal buffer and the - /// accumulator window can be shifted. - /// - /// - /// The number of elements to shift by - void AdvanceStart(int count); - - /// - /// Gets a window within the buffer of available buffered data - /// - Memory AccumulatedBuffer => Buffer[WindowStartPos..WindowEndPos]; - /// - /// Gets the available buffer window to write data to - /// - Memory RemainingBuffer => Buffer[WindowEndPos..]; - } -} \ No newline at end of file diff --git a/Utils/src/IO/IVnTextReader.cs b/Utils/src/IO/IVnTextReader.cs deleted file mode 100644 index 625ba78..0000000 --- a/Utils/src/IO/IVnTextReader.cs +++ /dev/null @@ -1,72 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IVnTextReader.cs -* -* IVnTextReader.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/. -*/ - -using System; -using System.IO; -using System.Text; - -namespace VNLib.Utils.IO -{ - /// - /// Represents a streaming text reader with internal buffers - /// - public interface IVnTextReader - { - /// - /// The base stream to read data from - /// - Stream BaseStream { get; } - /// - /// The character encoding used by the TextReader - /// - Encoding Encoding { get; } - /// - /// Number of available bytes of buffered data within the current buffer window - /// - int Available { get; } - /// - /// Gets or sets the line termination used to deliminate a line of data - /// - ReadOnlyMemory LineTermination { get; } - /// - /// The unread/available data within the internal buffer - /// - Span BufferedDataWindow { get; } - /// - /// Shifts the sliding buffer window by the specified number of bytes. - /// - /// The number of bytes read from the buffer - void Advance(int count); - /// - /// Reads data from the stream into the remaining buffer space for processing - /// - void FillBuffer(); - /// - /// Compacts the available buffer space back to the begining of the buffer region - /// and determines if there is room for more data to be buffered - /// - /// The remaining buffer space if any - ERRNO CompactBufferWindow(); - } -} \ No newline at end of file diff --git a/Utils/src/IO/InMemoryTemplate.cs b/Utils/src/IO/InMemoryTemplate.cs deleted file mode 100644 index 12f9092..0000000 --- a/Utils/src/IO/InMemoryTemplate.cs +++ /dev/null @@ -1,196 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: InMemoryTemplate.cs -* -* InMemoryTemplate.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/. -*/ - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.IO -{ - /// - /// Represents a lazily loaded file stored in memory, with a change mointor - /// that reloads the template if the file was modified in the filesystem - /// - public abstract class InMemoryTemplate : VnDisposeable - { - protected ManualResetEventSlim TemplateLock; - private readonly FileSystemWatcher? Watcher; - private bool Modified; - private VnMemoryStream templateBuffer; - protected readonly FileInfo TemplateFile; - - /// - /// Gets the name of the template - /// - public abstract string TemplateName { get; } - - /// - /// Creates a new in-memory copy of a file that will detect changes and refresh - /// - /// Should changes to the template file be moniored for changes, and reloaded as necessary - /// The path of the file template - protected InMemoryTemplate(string path, bool listenForChanges = true) - { - TemplateFile = new FileInfo(path); - TemplateLock = new(true); - //Make sure the file exists - if (!TemplateFile.Exists) - { - throw new FileNotFoundException("Template file does not exist"); - } - if (listenForChanges) - { - //Setup a watcher to reload the template when modified - Watcher = new FileSystemWatcher(TemplateFile.DirectoryName) - { - EnableRaisingEvents = true, - IncludeSubdirectories = false, - NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size - }; - Watcher.Changed += Watcher_Changed; - } - //Set modified flag to make sure the template is read on first use - this.Modified = true; - } - - private void Watcher_Changed(object sender, FileSystemEventArgs e) - { - //Make sure the event was raied for this template - if (!e.FullPath.Equals(TemplateFile.FullName, StringComparison.OrdinalIgnoreCase)) - { - return; - } - TemplateLock.Reset(); - try - { - //Set modified flag - Modified = true; - //Refresh the fileinfo object - TemplateFile.Refresh(); - //Invoke onmodifed function - OnModifed(); - } - finally - { - TemplateLock.Set(); - } - } - - /// - /// Gets a cached copy of the template data - /// - protected VnMemoryStream GetTemplateData() - { - //Make sure access is synchronized incase the file gets updated during access on another thread - TemplateLock.Wait(); - //Determine if the file has been modified and needs to be reloaded - if (Modified) - { - TemplateLock.Reset(); - try - { - //Read a new copy of the templte into mem - ReadFile(); - } - finally - { - TemplateLock.Set(); - } - } - //Return a copy of the memory stream - return templateBuffer.GetReadonlyShallowCopy(); - } - /// - /// Updates the internal copy of the file to its memory representation - /// - protected void ReadFile() - { - //Open the file stream - using FileStream fs = TemplateFile.OpenRead(); - //Dispose the old template buffer - templateBuffer?.Dispose(); - //Create a new stream for storing the cached copy - VnMemoryStream newBuf = new(); - try - { - fs.CopyTo(newBuf, null); - } - catch - { - newBuf.Dispose(); - throw; - } - //Create the readonly copy - templateBuffer = VnMemoryStream.CreateReadonly(newBuf); - //Clear the modified flag - Modified = false; - } - /// - /// Updates the internal copy of the file to its memory representation, asynchronously - /// - /// - /// A task that completes when the file has been copied into memory - protected async Task ReadFileAsync(CancellationToken cancellationToken = default) - { - //Open the file stream - await using FileStream fs = TemplateFile.OpenRead(); - //Dispose the old template buffer - templateBuffer?.Dispose(); - //Create a new stream for storing the cached copy - VnMemoryStream newBuf = new(); - try - { - //Copy async - await fs.CopyToAsync(newBuf, 8192, Memory.Memory.Shared, cancellationToken); - } - catch - { - newBuf.Dispose(); - throw; - } - //Create the readonly copy - templateBuffer = VnMemoryStream.CreateReadonly(newBuf); - //Clear the modified flag - Modified = false; - } - - /// - /// Invoked when the template file has been modifed. Note: This event is raised - /// while the is held. - /// - protected abstract void OnModifed(); - - /// - protected override void Free() - { - //Dispose the watcher - Watcher?.Dispose(); - //free the stream - templateBuffer?.Dispose(); - } - } -} \ No newline at end of file diff --git a/Utils/src/IO/IsolatedStorageDirectory.cs b/Utils/src/IO/IsolatedStorageDirectory.cs deleted file mode 100644 index 65460ff..0000000 --- a/Utils/src/IO/IsolatedStorageDirectory.cs +++ /dev/null @@ -1,154 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IsolatedStorageDirectory.cs -* -* IsolatedStorageDirectory.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/. -*/ - -using System; -using System.IO; -using System.IO.IsolatedStorage; - -namespace VNLib.Utils.IO -{ - /// - /// Represents an open directory within an store for which files can be created, opened, or deleted. - /// - public sealed class IsolatedStorageDirectory : IsolatedStorage - { - private readonly string DirectoryPath; - private readonly IsolatedStorageFile Storage; - /// - /// Creates a new within the specified file using the directory name. - /// - /// A configured and open - /// The directory name to open or create within the store - public IsolatedStorageDirectory(IsolatedStorageFile storage, string dir) - { - this.Storage = storage; - this.DirectoryPath = dir; - //If the directory doesnt exist, create it - if (!this.Storage.DirectoryExists(dir)) - this.Storage.CreateDirectory(dir); - } - - private IsolatedStorageDirectory(IsolatedStorageDirectory parent, string dirName) - { - //Store ref to parent dir - Parent = parent; - //Referrence store - this.Storage = parent.Storage; - //Add the name of this dir to the end of the specified dir path - this.DirectoryPath = Path.Combine(parent.DirectoryPath, dirName); - } - - /// - /// Creates a file by its path name within the currnet directory - /// - /// The name of the file - /// The open file - /// - /// - /// - public IsolatedStorageFileStream CreateFile(string fileName) - { - return this.Storage.CreateFile(Path.Combine(DirectoryPath, fileName)); - } - /// - /// Removes a file from the current directory - /// - /// The path of the file to remove - /// - public void DeleteFile(string fileName) - { - this.Storage.DeleteFile(Path.Combine(this.DirectoryPath, fileName)); - } - /// - /// Opens a file that exists within the current directory - /// - /// Name with extension of the file - /// File mode - /// File access - /// The open from the current directory - public IsolatedStorageFileStream OpenFile(string fileName, FileMode mode, FileAccess access) - { - return this.Storage.OpenFile(Path.Combine(DirectoryPath, fileName), mode, access); - } - /// - /// Opens a file that exists within the current directory - /// - /// Name with extension of the file - /// File mode - /// File access - /// The file shareing mode - /// The open from the current directory - public IsolatedStorageFileStream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share) - { - return this.Storage.OpenFile(Path.Combine(DirectoryPath, fileName), mode, access, share); - } - - /// - /// Determiens if the specified file path refers to an existing file within the directory - /// - /// The name of the file to search for - /// True if the file exists within the current directory - /// - /// - /// - /// - public bool FileExists(string fileName) - { - return this.Storage.FileExists(Path.Combine(this.DirectoryPath, fileName)); - } - - /// - /// Removes the directory and its contents from the store - /// - public override void Remove() - { - Storage.DeleteDirectory(this.DirectoryPath); - } - - public override long AvailableFreeSpace => Storage.AvailableFreeSpace; - public override long Quota => Storage.Quota; - public override long UsedSize => Storage.UsedSize; - public override bool IncreaseQuotaTo(long newQuotaSize) => Storage.IncreaseQuotaTo(newQuotaSize); - - /// - /// The parent this directory is a child within. null if there are no parent directories - /// above this dir - /// - - public IsolatedStorageDirectory? Parent { get; } -#nullable disable - - /// - /// Creates a child directory within the current directory - /// - /// The name of the child directory - /// A new for which s can be opened/created - /// - /// - public IsolatedStorageDirectory CreateChildDirectory(string directoryName) - { - return new IsolatedStorageDirectory(this, directoryName); - } - } -} \ No newline at end of file diff --git a/Utils/src/IO/SlidingWindowBufferExtensions.cs b/Utils/src/IO/SlidingWindowBufferExtensions.cs deleted file mode 100644 index 0509061..0000000 --- a/Utils/src/IO/SlidingWindowBufferExtensions.cs +++ /dev/null @@ -1,213 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: SlidingWindowBufferExtensions.cs -* -* SlidingWindowBufferExtensions.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/. -*/ - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -using VNLib.Utils.Memory; -using VNLib.Utils.Extensions; -using System.Runtime.CompilerServices; - -namespace VNLib.Utils.IO -{ - /// - /// Extention methods for - /// - public static class SlidingWindowBufferExtensions - { - /// - /// Shifts/resets the current buffered data window down to the - /// begining of the buffer if the buffer window is shifted away - /// from the begining. - /// - /// The number of bytes of available space in the buffer - public static ERRNO CompactBufferWindow(this ISlindingWindowBuffer sBuf) - { - //Nothing to compact if the starting data pointer is at the beining of the window - if (sBuf.WindowStartPos > 0) - { - //Get span over engire buffer - Span buffer = sBuf.Buffer.Span; - //Get data within window - Span usedData = sBuf.Accumulated; - //Copy remaining to the begining of the buffer - usedData.CopyTo(buffer); - - //Reset positions, then advance to the specified size - sBuf.Reset(); - sBuf.Advance(usedData.Length); - } - //Return the number of bytes of available space - return sBuf.RemainingSize; - } - - /// - /// Appends the specified data to the end of the buffer - /// - /// - /// - /// The value to append to the end of the buffer - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Append(this IDataAccumulator sBuf, T val) - { - //Set the value at first position - sBuf.Remaining[0] = val; - //Advance by 1 - sBuf.Advance(1); - } - /// - /// Appends the specified data to the end of the buffer - /// - /// - /// - /// The value to append to the end of the buffer - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Append(this IDataAccumulator sBuf, ReadOnlySpan val) - { - val.CopyTo(sBuf.Remaining); - sBuf.Advance(val.Length); - } - /// - /// Formats and appends a value type to the accumulator with proper endianess - /// - /// The value type to appent - /// The binary accumulator to append - /// The value type to append - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Append(this IDataAccumulator accumulator, T value) where T: unmanaged - { - //Use forward reader for the memory extension to append a value type to a binary accumulator - ForwardOnlyWriter w = new(accumulator.Remaining); - w.Append(value); - accumulator.Advance(w.Written); - } - - /// - /// Attempts to write as much data as possible to the remaining space - /// in the buffer and returns the number of bytes accumulated. - /// - /// - /// - /// The value to accumulate - /// The number of bytes accumulated - public static ERRNO TryAccumulate(this IDataAccumulator accumulator, ReadOnlySpan value) - { - //Calc data size and reserve space for final crlf - int dataToCopy = Math.Min(value.Length, accumulator.RemainingSize); - - //Write as much data as possible - accumulator.Append(value[..dataToCopy]); - - //Return number of bytes not written - return dataToCopy; - } - - /// - /// Appends a instance to the end of the accumulator - /// - /// - /// - /// The formattable instance to write to the accumulator - /// The format arguments - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Append(this IDataAccumulator accumulator, in T formattable, ReadOnlySpan format = default) where T : struct, ISpanFormattable - { - ForwardOnlyWriter writer = new(accumulator.Remaining); - writer.Append(formattable, format); - accumulator.Advance(writer.Written); - } - - /// - /// Uses the remaining data buffer to compile a - /// instance, then advances the accumulator by the number of characters used. - /// - /// - /// - /// The instance to compile - public static void Append(this IDataAccumulator accumulator, in T compileable) where T : IStringSerializeable - { - //Write directly to the remaining space - int written = compileable.Compile(accumulator.Remaining); - //Advance the writer - accumulator.Advance(written); - } - - /// - /// Reads available data from the current window and writes as much as possible it to the supplied buffer - /// and advances the buffer window - /// - /// Element type - /// - /// 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) - { - //Calculate the amount of data to copy - int dataToCopy = Math.Min(buffer.Length, sBuf.AccumulatedSize); - //Copy the data to the buffer - sBuf.Accumulated[..dataToCopy].CopyTo(buffer); - //Advance the window - sBuf.AdvanceStart(dataToCopy); - //Return the number of bytes copied - return dataToCopy; - } - - /// - /// Fills the remaining window space of the current accumulator with - /// data from the specified stream asynchronously. - /// - /// - /// The stream to read data from - /// A token to cancel the operation - /// A value task representing the operation - public static async ValueTask AccumulateDataAsync(this ISlindingWindowBuffer accumulator, Stream input, CancellationToken cancellationToken) - { - //Get a buffer from the end of the current window to the end of the buffer - Memory bufWindow = accumulator.RemainingBuffer; - //Read from stream async - int read = await input.ReadAsync(bufWindow, cancellationToken); - //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. - /// - /// - /// The stream to read data from - public static void AccumulateData(this IDataAccumulator accumulator, Stream input) - { - //Get a buffer from the end of the current window to the end of the buffer - Span bufWindow = accumulator.Remaining; - //Read from stream async - int read = input.Read(bufWindow); - //Update the end of the buffer window to the end of the read data - accumulator.Advance(read); - } - } -} \ No newline at end of file diff --git a/Utils/src/IO/TemporayIsolatedFile.cs b/Utils/src/IO/TemporayIsolatedFile.cs deleted file mode 100644 index 3bee92b..0000000 --- a/Utils/src/IO/TemporayIsolatedFile.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: TemporayIsolatedFile.cs -* -* TemporayIsolatedFile.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/. -*/ - -using System; -using System.IO; -using System.IO.IsolatedStorage; - -namespace VNLib.Utils.IO -{ - /// - /// Allows for temporary files to be generated, used, then removed from an - /// - public sealed class TemporayIsolatedFile : BackingStream - { - private readonly IsolatedStorageDirectory Storage; - private readonly string Filename; - /// - /// Creates a new temporary filestream within the specified - /// - /// The file store to genreate temporary files within - public TemporayIsolatedFile(IsolatedStorageDirectory storage) - { - //Store ref - this.Storage = storage; - //Creaet a new random filename - this.Filename = Path.GetRandomFileName(); - //try to created a new file within the isolaged storage - this.BaseStream = storage.CreateFile(this.Filename); - } - protected override void OnClose() - { - //Remove the file from the storage - Storage.DeleteFile(this.Filename); - } - } -} \ No newline at end of file diff --git a/Utils/src/IO/VnMemoryStream.cs b/Utils/src/IO/VnMemoryStream.cs deleted file mode 100644 index 389a7da..0000000 --- a/Utils/src/IO/VnMemoryStream.cs +++ /dev/null @@ -1,469 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: VnMemoryStream.cs -* -* VnMemoryStream.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/. -*/ - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Runtime.InteropServices; - -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.IO -{ - - using Utils.Memory; - - /// - /// Provides an unmanaged memory stream. Desigend to help reduce garbage collector load for - /// high frequency memory operations. Similar to - /// - public sealed class VnMemoryStream : Stream, ICloneable - { - private long _position; - private long _length; - //Memory - private readonly MemoryHandle _buffer; - private bool IsReadonly; - //Default owns handle - private readonly bool OwnsHandle = true; - - /// - /// Creates a new pointing to the begining of memory, and consumes the handle. - /// - /// to consume - /// Length of the stream - /// Should the stream be readonly? - /// - /// A wrapper to access the handle data - public static VnMemoryStream ConsumeHandle(MemoryHandle handle, Int64 length, bool readOnly) - { - handle.ThrowIfClosed(); - return new VnMemoryStream(handle, length, readOnly, true); - } - - /// - /// Converts a writable to readonly to allow shallow copies - /// - /// The stream to make readonly - /// The readonly stream - public static VnMemoryStream CreateReadonly(VnMemoryStream stream) - { - //Set the readonly flag - stream.IsReadonly = true; - //Return the stream - return stream; - } - - /// - /// Creates a new memory stream - /// - public VnMemoryStream() : this(Memory.Shared) { } - /// - /// Create a new memory stream where buffers will be allocated from the specified heap - /// - /// to allocate memory from - /// - /// - public VnMemoryStream(IUnmangedHeap heap) : this(heap, 0, false) { } - - /// - /// Creates a new memory stream and pre-allocates the internal - /// buffer of the specified size on the specified heap to avoid resizing. - /// - /// to allocate memory from - /// Number of bytes (length) of the stream if known - /// Zero memory allocations during buffer expansions - /// - /// - /// - public VnMemoryStream(IUnmangedHeap heap, long bufferSize, bool zero) - { - _ = heap ?? throw new ArgumentNullException(nameof(heap)); - _buffer = heap.Alloc(bufferSize, zero); - } - - /// - /// Creates a new memory stream from the data provided - /// - /// to allocate memory from - /// Initial data - public VnMemoryStream(IUnmangedHeap heap, ReadOnlySpan data) - { - _ = heap ?? throw new ArgumentNullException(nameof(heap)); - //Alloc the internal buffer to match the data stream - _buffer = heap.AllocAndCopy(data); - //Set length - _length = data.Length; - //Position will default to 0 cuz its dotnet :P - return; - } - - /// - /// WARNING: Dangerous constructor, make sure read-only and owns hanlde are set accordingly - /// - /// The buffer to referrence directly - /// The length property of the stream - /// Is the stream readonly (should mostly be true!) - /// Does the new stream own the memory -> - private VnMemoryStream(MemoryHandle buffer, long length, bool readOnly, bool ownsHandle) - { - OwnsHandle = ownsHandle; - _buffer = buffer; //Consume the handle - _length = length; //Store length of the buffer - IsReadonly = readOnly; - } - - /// - /// UNSAFE Number of bytes between position and length. Never negative - /// - private long LenToPosDiff => Math.Max(_length - _position, 0); - - /// - /// If the current stream is a readonly stream, creates an unsafe shallow copy for reading only. - /// - /// New stream shallow copy of the internal stream - /// - public VnMemoryStream GetReadonlyShallowCopy() - { - //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); - } - - /// - /// Writes data directly to the destination stream from the internal buffer - /// without allocating or copying any data. - /// - /// The stream to write data to - /// The size of the chunks to write to the destination stream - /// - public override void CopyTo(Stream destination, int bufferSize) - { - _ = destination ?? throw new ArgumentNullException(nameof(destination)); - - if (!destination.CanWrite) - { - throw new IOException("The destinaion stream is not writeable"); - } - - do - { - //Calc the remaining bytes to read no larger than the buffer size - int bytesToRead = (int)Math.Min(LenToPosDiff, bufferSize); - - //Create a span wrapper by using the offet function to support memory handles larger than 2gb - ReadOnlySpan span = _buffer.GetOffsetSpan(_position, bytesToRead); - - destination.Write(span); - - //Update position - _position += bytesToRead; - - } while (LenToPosDiff > 0); - } - - /// - /// Allocates a temporary buffer of the desired size, copies data from the internal - /// buffer and writes it to the destination buffer asynchronously. - /// - /// The stream to write output data to - /// The size of the buffer to use when copying data - /// A token to cancel the opreation - /// A task that resolves when the remaining data in the stream has been written to the destination - /// - /// - /// - public override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - _ = destination ?? throw new ArgumentNullException(nameof(destination)); - - if (!destination.CanWrite) - { - throw new IOException("The destinaion stream is not writeable"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - /* - * Alloc temp copy buffer. This is a requirement because - * the stream may be larger than an int32 so it must be - * copied by segment - */ - - using VnTempBuffer copyBuffer = new(bufferSize); - - do - { - //read from internal stream - int read = Read(copyBuffer); - - if(read <= 0) - { - break; - } - - //write async - await destination.WriteAsync(copyBuffer.AsMemory(0, read), cancellationToken); - - } while (true); - - } - - /// - /// - /// - /// This property is always true - /// - /// - public override bool CanRead => true; - /// - /// - /// - /// This propery is always true - /// - /// - public override bool CanSeek => true; - /// - /// True unless the stream is (or has been converted to) a readonly - /// stream. - /// - public override bool CanWrite => !IsReadonly; - /// - public override long Length => _length; - /// - public override bool CanTimeout => false; - - /// - public override long Position - { - get => _position; - set => Seek(value, SeekOrigin.Begin); - } - /// - /// Closes the stream and frees the internal allocated memory blocks - /// - public override void Close() - { - //Only dispose buffer if we own it - if (OwnsHandle) - { - _buffer.Dispose(); - } - } - /// - public override void Flush() { } - // Override to reduce base class overhead - /// - public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; - /// - public override int Read(byte[] buffer, int offset, int count) => Read(new Span(buffer, offset, count)); - /// - public override int Read(Span destination) - { - if (destination.Length == 0) - { - return 0; - } - //Number of bytes to read from memory buffer - int bytesToRead = checked((int)Math.Min(LenToPosDiff, destination.Length)); - //Copy bytes to buffer - Memory.Copy(_buffer, _position, destination, 0, bytesToRead); - //Increment buffer position - _position += bytesToRead; - //Bytestoread should never be larger than int.max because span length is an integer - return bytesToRead; - } - - /* - * Async reading will always run synchronously in a memory stream, - * so overrides are just so avoid base class overhead - */ - /// - public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) - { - //Read synchronously and return a completed task - int read = Read(buffer.Span); - return ValueTask.FromResult(read); - } - /// - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - //Read synchronously and return a completed task - int read = Read(buffer.AsSpan(offset, count)); - return Task.FromResult(read); - } - /// - public override long Seek(long offset, SeekOrigin origin) - { - if (offset < 0) - { - throw new ArgumentOutOfRangeException(nameof(offset), "Offset cannot be less than 0"); - } - switch (origin) - { - case SeekOrigin.Begin: - //Length will never be greater than int.Max so output will never exceed int.max - _position = Math.Min(_length, offset); - return _position; - case SeekOrigin.Current: - long newPos = _position + offset; - //Length will never be greater than int.Max so output will never exceed length - _position = Math.Min(_length, newPos); - return newPos; - case SeekOrigin.End: - long real_index = _length - offset; - //If offset moves the position negative, just set the position to 0 and continue - _position = Math.Min(real_index, 0); - return real_index; - default: - throw new ArgumentException("Stream operation is not supported on current stream"); - } - } - - - /// - /// Resizes the internal buffer to the exact size (in bytes) of the - /// value argument. A value of 0 will free the entire buffer. A value - /// greater than zero will resize the buffer (and/or alloc) - /// - /// The size of the stream (and internal buffer) - /// - /// - /// - /// - public override void SetLength(long value) - { - if (IsReadonly) - { - throw new NotSupportedException("This stream is readonly"); - } - if (value < 0) - { - throw new ArgumentOutOfRangeException(nameof(value), "Value cannot be less than 0"); - } - //Resize the buffer to the specified length - _buffer.Resize(value); - //Set length - _length = value; - //Make sure the position is not pointing outside of the buffer - _position = Math.Min(_position, _length); - return; - } - /// - public override void Write(byte[] buffer, int offset, int count) => Write(new ReadOnlySpan(buffer, offset, count)); - /// - public override void Write(ReadOnlySpan buffer) - { - if (IsReadonly) - { - throw new NotSupportedException("Write operation is not allowed on readonly stream!"); - } - //Calculate the new final position - long newPos = (_position + buffer.Length); - //Determine if the buffer needs to be expanded - if (buffer.Length > LenToPosDiff) - { - //Expand buffer if required - _buffer.ResizeIfSmaller(newPos); - //Update length - _length = newPos; - } - //Copy the input buffer to the internal buffer - Memory.Copy(buffer, _buffer, _position); - //Update the position - _position = newPos; - return; - } - /// - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - //Write synchronously and return a completed task - Write(buffer, offset, count); - return Task.CompletedTask; - } - /// - public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - //Write synchronously and return a completed task - Write(buffer.Span); - return ValueTask.CompletedTask; - } - /// - public override void WriteByte(byte value) - { - Span buf = MemoryMarshal.CreateSpan(ref value, 1); - Write(buf); - } - - /// - /// Allocates and copies internal buffer to new managed byte[] - /// - /// Copy of internal buffer - /// - /// - public byte[] ToArray() - { - //Alloc a new array of the size of the internal buffer - byte[] data = new byte[_length]; - //Copy data from the internal buffer to the output buffer - _buffer.Span.CopyTo(data); - return data; - - } - /// - /// Returns a window over the data within the entire stream - /// - /// A of the data within the entire stream - /// - public ReadOnlySpan AsSpan() - { - ReadOnlySpan output = _buffer.Span; - return output[..(int)_length]; - } - - /// - /// If the current stream is a readonly stream, creates a shallow copy for reading only. - /// - /// New stream shallow copy of the internal stream - /// - public object Clone() => GetReadonlyShallowCopy(); - - /* - * Override the Dispose async method to avoid the base class overhead - * and task allocation since this will always be a syncrhonous - * operation (freeing memory) - */ - - /// - public override ValueTask DisposeAsync() - { - //Dispose and return completed task - base.Dispose(true); - GC.SuppressFinalize(this); - return ValueTask.CompletedTask; - } - } -} \ No newline at end of file diff --git a/Utils/src/IO/VnStreamReader.cs b/Utils/src/IO/VnStreamReader.cs deleted file mode 100644 index 70b9734..0000000 --- a/Utils/src/IO/VnStreamReader.cs +++ /dev/null @@ -1,180 +0,0 @@ -/* -* Copyright (c) 2022 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/. -*/ - -using System; -using System.IO; -using System.Text; -using System.Buffers; -using System.Threading; -using System.Threading.Tasks; - -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.IO -{ - /// - /// Binary based buffered text reader, optimized for reading network streams - /// - public class VnStreamReader : TextReader, IVnTextReader - { - private bool disposedValue; - - private readonly ISlindingWindowBuffer _buffer; - /// - public virtual Stream BaseStream { get; } - /// - public Encoding Encoding { get; } - - /// - /// 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; - - /// - /// Creates a new that reads encoded data from the base. - /// Internal buffers will be alloced from - /// - /// The underlying stream to read data from - /// The to use when reading from the stream - /// The size of the internal binary buffer - public VnStreamReader(Stream baseStream, Encoding enc, int bufferSize) - { - 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. - /// - /// 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 override async Task ReadLineAsync() - { - //If buffered data is available, check for line termination - if (Available > 0) - { - //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); - } - } - //Termination not found within the entire buffer, so buffer space has been exhausted - - //OOM is raised in the TextReader base class, the standard is preserved -#pragma warning disable CA2201 // Do not raise reserved exception types - throw new OutOfMemoryException("A line termination was not found within the buffer"); -#pragma warning restore CA2201 // Do not raise reserved exception types - } - - /// - public override int Read(char[] buffer, int index, int count) => Read(buffer.AsSpan(index, count)); - /// - public override int Read(Span buffer) - { - if (Available <= 0) - { - 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) - { - if (!disposedValue) - { - Close(); - disposedValue = true; - } - base.Dispose(disposing); - } - - /// - /// Resets the internal buffer window - /// - protected void ClearBuffer() - { - _buffer.Reset(); - } - - void IVnTextReader.Advance(int count) => _buffer.AdvanceStart(count); - void IVnTextReader.FillBuffer() => _buffer.AccumulateData(BaseStream); - ERRNO IVnTextReader.CompactBufferWindow() => _buffer.CompactBufferWindow(); - } -} \ No newline at end of file diff --git a/Utils/src/IO/VnStreamWriter.cs b/Utils/src/IO/VnStreamWriter.cs deleted file mode 100644 index 37d700c..0000000 --- a/Utils/src/IO/VnStreamWriter.cs +++ /dev/null @@ -1,292 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: VnStreamWriter.cs -* -* VnStreamWriter.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/. -*/ - -using System; -using System.IO; -using System.Text; -using System.Buffers; -using System.Threading; -using System.Threading.Tasks; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -using VNLib.Utils.Memory; - -namespace VNLib.Utils.IO -{ - /// - /// Provides a memory optimized implementation. Optimized for writing - /// to network streams - /// - public class VnStreamWriter : TextWriter - { - private readonly Encoder Enc; - - private readonly ISlindingWindowBuffer _buffer; - - private bool closed; - - /// - /// Gets the underlying stream that interfaces with the backing store - /// - public virtual Stream BaseStream { get; } - /// - public override Encoding Encoding { get; } - - /// - /// 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 formatted data - /// to the specified base stream - /// - /// 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) - { - //Store base stream - BaseStream = baseStream; - Encoding = encoding; - //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) - { - //See if there is room in the binary buffer - if (_buffer.AccumulatedSize == 0) - { - //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) - { - Check(); - - ForwardOnlyReader reader = new(buffer); - - //Create a variable for a character buffer window - bool completed; - do - { - //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); - - //Converting did not complete because the buffer was too small - if (!completed || reader.WindowSize == 0) - { - //Flush the buffer and continue - Flush(); - } - - } while (!completed); - //Reset the encoder - Enc.Reset(); - } - /// - public override async Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - Check(); - //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(); - } - - /// - 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) - { - //Write the value itself - Write(buffer); - //Write the line termination - WriteLine(); - } - - /// - /// - public override void Flush() - { - Check(); - //If data is available to be written, write it to the base stream - if (_buffer.AccumulatedSize > 0) - { - //Write all buffered data to stream - BaseStream.Write(_buffer.Accumulated); - //Reset the buffer - _buffer.Reset(); - } - } - /// - /// Asynchronously flushes the internal buffers to the , and resets the internal buffer state - /// - /// A that represents the asynchronous flush operation - /// - public async ValueTask FlushWriterAsync(CancellationToken cancellationToken = default) - { - Check(); - if (_buffer.AccumulatedSize > 0) - { - //Flush current window to the stream - await BaseStream.WriteAsync(_buffer.AccumulatedBuffer, cancellationToken); - //Reset the buffer - _buffer.Reset(); - } - } - - /// - public override Task FlushAsync() => FlushWriterAsync().AsTask(); - - /// - /// Resets internal properies for resuse - /// - protected void Reset() - { - _buffer.Reset(); - Enc.Reset(); - } - /// - public override void Close() - { - //Only invoke close once - if (closed) - { - return; - } - try - { - Flush(); - } - finally - { - //Release the memory handle if its set - _buffer.Close(); - //Set closed flag - closed = true; - } - } - /// - protected override void Dispose(bool disposing) - { - Close(); - base.Dispose(disposing); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Check() - { - if (closed) - { - throw new ObjectDisposedException("The stream is closed"); - } - } - /// - public override async ValueTask DisposeAsync() - { - //Only invoke close once - if (closed) - { - return; - } - try - { - await FlushWriterAsync(); - } - finally - { - //Set closed flag - closed = true; - //Release the memory handle if its set - _buffer.Close(); - } - GC.SuppressFinalize(this); - } - } -} \ No newline at end of file diff --git a/Utils/src/IO/VnTextReaderExtensions.cs b/Utils/src/IO/VnTextReaderExtensions.cs deleted file mode 100644 index 119461b..0000000 --- a/Utils/src/IO/VnTextReaderExtensions.cs +++ /dev/null @@ -1,223 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: VnTextReaderExtensions.cs -* -* VnTextReaderExtensions.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/. -*/ - -using System; - -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.IO -{ - /// - /// Extension methods to help reuse code for used TextReader implementations - /// - public static class VnTextReaderExtensions - { - public const int E_BUFFER_TOO_SMALL = -1; - - - /* - * Generic extensions provide constained compiler method invocation - * for structs the implement the IVNtextReader - */ - - /// - /// Attempts to read a line from the stream and store it in the specified buffer - /// - /// - /// The character buffer to write data to - /// Returns the number of bytes read, - /// if the buffer was not large enough, 0 if no data was available - /// - /// Allows reading lines of data from the stream without allocations - public static ERRNO ReadLine(this ref T reader, Span charBuffer) where T:struct, IVnTextReader - { - return readLine(ref reader, charBuffer); - } - /// - /// Attempts to read a line from the stream and store it in the specified buffer - /// - /// - /// The character buffer to write data to - /// Returns the number of bytes read, - /// if the buffer was not large enough, 0 if no data was available - /// - /// Allows reading lines of data from the stream without allocations - public static ERRNO ReadLine(this T reader, Span charBuffer) where T : class, IVnTextReader - { - return readLine(ref reader, charBuffer); - } - - /// - /// Fill a buffer with reamining buffered data - /// - /// - /// Buffer to copy data to - /// Offset in buffer to begin writing - /// Number of bytes to read - /// The number of bytes copied to the input buffer - public static int ReadRemaining(this ref T reader, byte[] buffer, int offset, int count) where T : struct, IVnTextReader - { - return reader.ReadRemaining(buffer.AsSpan(offset, count)); - } - /// - /// Fill a buffer with reamining buffered data - /// - /// - /// Buffer to copy data to - /// Offset in buffer to begin writing - /// Number of bytes to read - /// The number of bytes copied to the input buffer - public static int ReadRemaining(this T reader, byte[] buffer, int offset, int count) where T : class, IVnTextReader - { - return reader.ReadRemaining(buffer.AsSpan(offset, count)); - } - - /// - /// Fill a buffer with reamining buffered data, up to - /// the size of the supplied buffer - /// - /// - /// Buffer to copy data to - /// The number of bytes copied to the input buffer - /// You should use the property to know how much remaining data is buffered - public static int ReadRemaining(this ref T reader, Span buffer) where T : struct, IVnTextReader - { - return readRemaining(ref reader, buffer); - } - /// - /// Fill a buffer with reamining buffered data, up to - /// the size of the supplied buffer - /// - /// - /// Buffer to copy data to - /// The number of bytes copied to the input buffer - /// You should use the property to know how much remaining data is buffered - public static int ReadRemaining(this T reader, Span buffer) where T : class, IVnTextReader - { - return readRemaining(ref reader, buffer); - } - - private static ERRNO readLine(ref T reader, Span chars) where T: IVnTextReader - { - /* - * I am aware of a potential bug, the line decoding process - * shifts the interal buffer by the exact number of bytes to - * the end of the line, without considering if the decoder failed - * to properly decode the entire line. - * - * I dont expect this to be an issue unless there is a bug within the specified - * encoder implementation - */ - ReadOnlySpan LineTermination = reader.LineTermination.Span; - //If buffered data is available, check for line termination - if (reader.Available > 0) - { - //Get current buffer window - ReadOnlySpan bytes = reader.BufferedDataWindow; - //search for line termination in current buffer - int term = bytes.IndexOf(LineTermination); - //Termination found in buffer window - if (term > -1) - { - //Capture the line from the begining of the window to the termination - ReadOnlySpan line = bytes[..term]; - //Get the number ot chars - int charCount = reader.Encoding.GetCharCount(line); - //See if the buffer is large enough - if (bytes.Length < charCount) - { - return E_BUFFER_TOO_SMALL; - } - //Use the decoder to convert the data - _ = reader.Encoding.GetChars(line, chars); - //Shift the window to the end of the line (excluding the termination, regardless of the conversion result) - reader.Advance(term + LineTermination.Length); - //Return the number of characters - return charCount; - } - //Termination not found but there may be more data waiting - } - //Compact the buffer window and make sure it was compacted so there is room to fill the buffer - if (reader.CompactBufferWindow()) - { - //There is room, so buffer more data - reader.FillBuffer(); - //Check again to see if more data is buffered - if (reader.Available <= 0) - { - //No data avialable - return 0; - } - //Get current buffer window - ReadOnlySpan bytes = reader.BufferedDataWindow; - //search for line termination in current buffer - int term = bytes.IndexOf(LineTermination); - //Termination found in buffer window - if (term > -1) - { - //Capture the line from the begining of the window to the termination - ReadOnlySpan line = bytes[..term]; - //Get the number ot chars - int charCount = reader.Encoding.GetCharCount(line); - //See if the buffer is large enough - if (bytes.Length < charCount) - { - return E_BUFFER_TOO_SMALL; - } - //Use the decoder to convert the data - _ = reader.Encoding.GetChars(line, chars); - //Shift the window to the end of the line (excluding the termination, regardless of the conversion result) - reader.Advance(term + LineTermination.Length); - //Return the number of characters - return charCount; - } - } - - //Termination not found within the entire buffer, so buffer space has been exhausted - - //Supress as this response is expected when the buffer is exhausted, -#pragma warning disable CA2201 // Do not raise reserved exception types - throw new OutOfMemoryException("The line was not found within the current buffer, cannot continue"); -#pragma warning restore CA2201 // Do not raise reserved exception types - } - - private static int readRemaining(ref T reader, Span buffer) where T: IVnTextReader - { - //guard for empty buffer - if (buffer.Length == 0 || reader.Available == 0) - { - return 0; - } - //get the remaining bytes in the reader - Span remaining = reader.BufferedDataWindow; - //Calculate the number of bytes to copy - int canCopy = Math.Min(remaining.Length, buffer.Length); - //Copy remaining bytes to buffer - remaining[..canCopy].CopyTo(buffer); - //Shift the window by the number of bytes copied - reader.Advance(canCopy); - return canCopy; - } - } -} \ No newline at end of file diff --git a/Utils/src/IO/WriteOnlyBufferedStream.cs b/Utils/src/IO/WriteOnlyBufferedStream.cs deleted file mode 100644 index 5e7faa1..0000000 --- a/Utils/src/IO/WriteOnlyBufferedStream.cs +++ /dev/null @@ -1,255 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: WriteOnlyBufferedStream.cs -* -* WriteOnlyBufferedStream.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/. -*/ - -using System; -using System.IO; -using System.Buffers; -using System.Threading; -using System.Threading.Tasks; - -using VNLib.Utils.Memory; - -namespace VNLib.Utils.IO -{ - /// - /// A basic accumulator style write buffered stream - /// - public class WriteOnlyBufferedStream : Stream - { - private readonly ISlindingWindowBuffer _buffer; - private readonly bool LeaveOpen; - - /// - /// Gets the underlying stream that interfaces with the backing store - /// - public Stream BaseStream { get; init; } - - /// - /// Initalizes a new using the - /// specified backing stream, using the specified buffer 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) - { - BaseStream = baseStream; - //Create buffer - _buffer = InitializeBuffer(bufferSize); - LeaveOpen = leaveOpen; - } - /// - /// 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) - { - return new ArrayPoolStreamBuffer(ArrayPool.Shared, bufferSize); - } - - /// - public override void Close() - { - try - { - //Make sure the buffer is empty - WriteBuffer(); - - if (!LeaveOpen) - { - //Dispose stream - BaseStream.Dispose(); - } - } - finally - { - _buffer.Close(); - } - } - /// - public override async ValueTask DisposeAsync() - { - try - { - if (_buffer.AccumulatedSize > 0) - { - await WriteBufferAsync(CancellationToken.None); - } - - if (!LeaveOpen) - { - //Dispose stream - await BaseStream.DisposeAsync(); - } - - GC.SuppressFinalize(this); - } - finally - { - _buffer.Close(); - } - } - - /// - public override void Flush() => WriteBuffer(); - /// - public override Task FlushAsync(CancellationToken cancellationToken) => WriteBufferAsync(cancellationToken).AsTask(); - - private void WriteBuffer() - { - //Only if data is available to write - if (_buffer.AccumulatedSize > 0) - { - //Write data to stream - BaseStream.Write(_buffer.Accumulated); - //Reset position - _buffer.Reset(); - } - } - - private async ValueTask WriteBufferAsync(CancellationToken token = default) - { - if(_buffer.AccumulatedSize > 0) - { - await BaseStream.WriteAsync(_buffer.AccumulatedBuffer, token); - _buffer.Reset(); - } - } - /// - public override void Write(byte[] buffer, int offset, int count) => Write(buffer.AsSpan(offset, count)); - - public override void Write(ReadOnlySpan buffer) - { - ForwardOnlyReader reader = new(buffer); - //Attempt to buffer/flush data until all data is sent - do - { - //Try to buffer as much as possible - ERRNO buffered = _buffer.TryAccumulate(reader.Window); - - if(buffered < reader.WindowSize) - { - //Buffer is full and needs to be flushed - WriteBuffer(); - //Advance reader and continue to buffer - reader.Advance(buffered); - continue; - } - - break; - } - while (true); - } - - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return WriteAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask(); - } - - public async override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) - { - ForwardOnlyMemoryReader reader = new(buffer); - //Attempt to buffer/flush data until all data is sent - do - { - //Try to buffer as much as possible - ERRNO buffered = _buffer.TryAccumulate(reader.Window.Span); - - if (buffered < reader.WindowSize) - { - //Buffer is full and needs to be flushed - await WriteBufferAsync(cancellationToken); - //Advance reader and continue to buffer - reader.Advance(buffered); - continue; - } - - break; - } - while (true); - } - - - /// - /// 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 - /// - /// - /// - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotSupportedException("This stream is not readable"); - } - - /// - /// Always throws - /// - /// - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - /// - /// Always throws - /// - /// - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - } -} -- cgit