diff options
Diffstat (limited to 'lib/Utils/src')
-rw-r--r-- | lib/Utils/src/Async/AsyncQueue.cs | 45 | ||||
-rw-r--r-- | lib/Utils/src/Async/IAsyncEventSource.cs | 45 | ||||
-rw-r--r-- | lib/Utils/src/Async/IAsyncQueue.cs | 76 | ||||
-rw-r--r-- | lib/Utils/src/Extensions/IoExtensions.cs | 78 | ||||
-rw-r--r-- | lib/Utils/src/IO/IsolatedStorageDirectory.cs | 77 | ||||
-rw-r--r-- | lib/Utils/src/IO/VnMemoryStream.cs | 53 | ||||
-rw-r--r-- | lib/Utils/src/IO/VnStreamWriter.cs | 2 | ||||
-rw-r--r-- | lib/Utils/src/Memory/HeapCreation.cs | 2 | ||||
-rw-r--r-- | lib/Utils/src/Memory/MemoryUtil.cs | 6 | ||||
-rw-r--r-- | lib/Utils/src/Memory/NativeHeap.cs | 4 | ||||
-rw-r--r-- | lib/Utils/src/Memory/ProcessHeap.cs | 4 | ||||
-rw-r--r-- | lib/Utils/src/Memory/Win32PrivateHeap.cs | 2 | ||||
-rw-r--r-- | lib/Utils/src/VnEncoding.cs | 9 |
13 files changed, 307 insertions, 96 deletions
diff --git a/lib/Utils/src/Async/AsyncQueue.cs b/lib/Utils/src/Async/AsyncQueue.cs index ba45513..45f1219 100644 --- a/lib/Utils/src/Async/AsyncQueue.cs +++ b/lib/Utils/src/Async/AsyncQueue.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -30,11 +30,12 @@ using System.Diagnostics.CodeAnalysis; namespace VNLib.Utils.Async { + /// <summary> /// Provides a <see cref="Channel{T}"/> based asynchronous queue /// </summary> /// <typeparam name="T">The event object type</typeparam> - public class AsyncQueue<T> + public class AsyncQueue<T> : IAsyncQueue<T> { private readonly Channel<T> _channel; @@ -45,12 +46,13 @@ namespace VNLib.Utils.Async /// </summary> /// <param name="capacity">The maxium number of items to allow in the queue</param> public AsyncQueue(int capacity):this(false, false, capacity) - {} + { } + /// <summary> /// Initalizes a new multi-threaded unbound channel queue /// </summary> public AsyncQueue():this(false, false) - {} + { } /// <summary> /// Initalizes a new queue that allows specifying concurrency requirements @@ -105,39 +107,22 @@ namespace VNLib.Utils.Async _channel = Channel.CreateBounded<T>(options); } - /// <summary> - /// Attemts to enqeue an item if the queue has the capacity - /// </summary> - /// <param name="item">The item to eqneue</param> - /// <returns>True if the queue can accept another item, false otherwise</returns> + /// <inheritdoc/> public bool TryEnque(T item) => _channel.Writer.TryWrite(item); - /// <summary> - /// Enqueues an item to the end of the queue and notifies a waiter that an item was enqueued - /// </summary> - /// <param name="item">The item to enqueue</param> - /// <param name="cancellationToken"></param> + + /// <inheritdoc/> /// <exception cref="ObjectDisposedException"></exception> public ValueTask EnqueueAsync(T item, CancellationToken cancellationToken = default) => _channel.Writer.WriteAsync(item, cancellationToken); - /// <summary> - /// Asynchronously waits for an item to be Enqueued to the end of the queue. - /// </summary> - /// <returns>The item at the begining of the queue</returns> + + /// <inheritdoc/> /// <exception cref="ObjectDisposedException"></exception> public ValueTask<T> DequeueAsync(CancellationToken cancellationToken = default) => _channel.Reader.ReadAsync(cancellationToken); - /// <summary> - /// Removes the object at the beginning of the queue and stores it to the result parameter. Without waiting for a change - /// event. - /// </summary> - /// <param name="result">The item that was at the begining of the queue</param> - /// <returns>True if the queue could be read synchronously, false if the lock could not be entered, or the queue contains no items</returns> + + /// <inheritdoc/> /// <exception cref="ObjectDisposedException"></exception> public bool TryDequeue([MaybeNullWhen(false)] out T result) => _channel.Reader.TryRead(out result); - /// <summary> - /// Peeks the object at the beginning of the queue and stores it to the result parameter. Without waiting for a change - /// event. - /// </summary> - /// <param name="result">The item that was at the begining of the queue</param> - /// <returns>True if the queue could be read synchronously, false if the lock could not be entered, or the queue contains no items</returns> + + /// <inheritdoc/> /// <exception cref="ObjectDisposedException"></exception> public bool TryPeek([MaybeNullWhen(false)] out T result) => _channel.Reader.TryPeek(out result); } diff --git a/lib/Utils/src/Async/IAsyncEventSource.cs b/lib/Utils/src/Async/IAsyncEventSource.cs new file mode 100644 index 0000000..968f94c --- /dev/null +++ b/lib/Utils/src/Async/IAsyncEventSource.cs @@ -0,0 +1,45 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Utils +* File: IAsyncEventSource.cs +* +* IAsyncEventSource.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.Async +{ + /// <summary> + /// A type that publishes events to asynchronous event queues + /// </summary> + /// <typeparam name="T">The event item type to publish</typeparam> + public interface IAsyncEventSource<T> + { + /// <summary> + /// Subscribes a new queue to publish events to + /// </summary> + /// <param name="queue">The queue instance to publish new events to</param> + void Subscribe(IAsyncQueue<T> queue); + + /// <summary> + /// Unsubscribes a previously subscribed queue from receiving events + /// </summary> + /// <param name="queue">The queue instance to unregister from events</param> + void Unsubscribe(IAsyncQueue<T> queue); + } +} diff --git a/lib/Utils/src/Async/IAsyncQueue.cs b/lib/Utils/src/Async/IAsyncQueue.cs new file mode 100644 index 0000000..ab786f1 --- /dev/null +++ b/lib/Utils/src/Async/IAsyncQueue.cs @@ -0,0 +1,76 @@ +/* +* Copyright (c) 2023 Vaughn Nugent +* +* Library: VNLib +* Package: VNLib.Utils +* File: IAsyncQueue.cs +* +* IAsyncQueue.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.Threading; +using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; + +namespace VNLib.Utils.Async +{ + /// <summary> + /// Provides a generic asynchronous queue + /// </summary> + /// <typeparam name="T">The item message type</typeparam> +#pragma warning disable CA1711 // Identifiers should not have incorrect suffix + public interface IAsyncQueue<T> +#pragma warning restore CA1711 // Identifiers should not have incorrect suffix + { + /// <summary> + /// Attemts to enqueue an item if the queue has the capacity + /// </summary> + /// <param name="item">The item to eqneue</param> + /// <returns>True if the queue can accept another item, false otherwise</returns> + bool TryEnque(T item); + + /// <summary> + /// Enqueues an item to the end of the queue and notifies a waiter that an item was enqueued + /// </summary> + /// <param name="item">The item to enqueue</param> + /// <param name="cancellationToken">A token to cancel the operation</param> + ValueTask EnqueueAsync(T item, CancellationToken cancellationToken = default); + + /// <summary> + /// Asynchronously waits for an item to be Enqueued to the end of the queue. + /// </summary> + /// <param name="cancellationToken">A token to cancel the operation</param> + /// <returns>The item at the begining of the queue</returns> + ValueTask<T> DequeueAsync(CancellationToken cancellationToken = default); + + /// <summary> + /// Removes the object at the beginning of the queue and stores it to the result parameter. Without waiting for a change + /// event. + /// </summary> + /// <param name="result">The item that was at the begining of the queue</param> + /// <returns>True if the queue could be read synchronously, false if the lock could not be entered, or the queue contains no items</returns> + bool TryDequeue([MaybeNullWhen(false)] out T result); + + /// <summary> + /// Peeks the object at the beginning of the queue and stores it to the result parameter. Without waiting for a change + /// event. + /// </summary> + /// <param name="result">The item that was at the begining of the queue</param> + /// <returns>True if the queue could be read synchronously, false if the lock could not be entered, or the queue contains no items</returns> + bool TryPeek([MaybeNullWhen(false)] out T result); + } +} diff --git a/lib/Utils/src/Extensions/IoExtensions.cs b/lib/Utils/src/Extensions/IoExtensions.cs index d727fed..ffa374b 100644 --- a/lib/Utils/src/Extensions/IoExtensions.cs +++ b/lib/Utils/src/Extensions/IoExtensions.cs @@ -26,15 +26,19 @@ using System; using System.IO; using System.Buffers; using System.Threading; +using System.Diagnostics; using System.Threading.Tasks; using System.Runtime.Versioning; +using System.IO.IsolatedStorage; using System.Runtime.CompilerServices; using VNLib.Utils.IO; using VNLib.Utils.Memory; - using static VNLib.Utils.Memory.MemoryUtil; +//Disable configure await warning +#pragma warning disable CA2007 + namespace VNLib.Utils.Extensions { /// <summary> @@ -103,6 +107,7 @@ namespace VNLib.Utils.Extensions //Wait for copy to complete await CopyToAsync(source, dest, buffer.Memory, token); } + /// <summary> /// Provides an async wrapper for copying data from the current stream to another with a /// buffer from the <paramref name="heap"/> @@ -186,6 +191,7 @@ namespace VNLib.Utils.Extensions dest.Write(buffer.Span[..read]); } while (true); } + /// <summary> /// Copies data from one stream to another, using self managed buffers. May allocate up to 2MB. /// </summary> @@ -440,5 +446,75 @@ namespace VNLib.Utils.Extensions return Parent.WriteFileAsync(path, data, contentType, cancellation); } } + + /// <summary> + /// The maximum buffer size to use when copying files + /// </summary> + public const long MaxCopyBufferSize = 0x10000; //64k + + /// <summary> + /// The minimum buffer size to use when copying files + /// </summary> + public const long MinCopyBufferSize = 0x1000; //4k + + /// <summary> + /// Creates a new <see cref="ISimpleFilesystem"/> wrapper for the given <see cref="IsolatedStorageDirectory"/> + /// <para> + /// Buffers are clamped to <see cref="MaxCopyBufferSize"/> and <see cref="MinCopyBufferSize"/> + /// </para> + /// </summary> + /// <param name="dir"></param> + /// <returns>A <see cref="ISimpleFilesystem"/> wrapper around the <see cref="IsolatedStorageDirectory"/></returns> + public static ISimpleFilesystem CreateSimpleFs(this IsolatedStorageDirectory dir) => new IsolatedStorageSimpleFs(dir); + + private sealed record class IsolatedStorageSimpleFs(IsolatedStorageDirectory Directory) : ISimpleFilesystem + { + ///<inheritdoc/> + public string GetExternalFilePath(string filePath) => Directory.GetFullFilePath(filePath); + + ///<inheritdoc/> + public Task DeleteFileAsync(string filePath, CancellationToken cancellation) + { + Directory.DeleteFile(filePath); + return Task.CompletedTask; + } + + ///<inheritdoc/> + public async Task WriteFileAsync(string filePath, Stream data, string contentType, CancellationToken cancellation) + { + //For when I forget and increase the buffer size + Debug.Assert(MaxCopyBufferSize < int.MaxValue, "MaxCopyBufferSize is too large to be cast to an int"); + + //Try to calculate a buffer size + long bufferSize = data.CanSeek ? Math.Clamp(data.Length, MinCopyBufferSize, MaxCopyBufferSize) : MinCopyBufferSize; + + //Open the local file + await using IsolatedStorageFileStream output = Directory.OpenFile(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite); + + //Copy file + await CopyToAsync(data, output, (int)bufferSize, Shared, cancellation); + + //All done + } + + ///<inheritdoc/> + public async Task<long> ReadFileAsync(string filePath, Stream output, CancellationToken cancellation) + { + //For when I forget and increase the buffer size + Debug.Assert(MaxCopyBufferSize < int.MaxValue, "MaxCopyBufferSize is too large to be cast to an int"); + + //Open the local file + await using IsolatedStorageFileStream local = Directory.OpenFile(filePath, FileMode.Open, FileAccess.Read); + + //Try to calculate a buffer size + long bufferSize = Math.Clamp(local.Length, MinCopyBufferSize, MaxCopyBufferSize); + + //Copy file + await CopyToAsync(local, output, (int)bufferSize, Shared, cancellation); + + return local.Length; + } + + } } }
\ No newline at end of file diff --git a/lib/Utils/src/IO/IsolatedStorageDirectory.cs b/lib/Utils/src/IO/IsolatedStorageDirectory.cs index c06018e..6ed9339 100644 --- a/lib/Utils/src/IO/IsolatedStorageDirectory.cs +++ b/lib/Utils/src/IO/IsolatedStorageDirectory.cs @@ -1,5 +1,5 @@ /* -* Copyright (c) 2022 Vaughn Nugent +* Copyright (c) 2023 Vaughn Nugent * * Library: VNLib * Package: VNLib.Utils @@ -35,18 +35,23 @@ namespace VNLib.Utils.IO { private readonly string DirectoryPath; private readonly IsolatedStorageFile Storage; + /// <summary> /// Creates a new <see cref="IsolatedStorageDirectory"/> within the specified file using the directory name. /// </summary> /// <param name="storage">A configured and open <see cref="IsolatedStorageFile"/></param> /// <param name="dir">The directory name to open or create within the store</param> + /// <exception cref="ArgumentException"></exception> public IsolatedStorageDirectory(IsolatedStorageFile storage, string dir) { - this.Storage = storage; - this.DirectoryPath = dir; + Storage = storage ?? throw new ArgumentNullException(nameof(storage)); + DirectoryPath = dir ?? throw new ArgumentNullException(nameof(dir)); + //If the directory doesnt exist, create it - if (!this.Storage.DirectoryExists(dir)) - this.Storage.CreateDirectory(dir); + if (!Storage.DirectoryExists(dir)) + { + Storage.CreateDirectory(dir); + } } private IsolatedStorageDirectory(IsolatedStorageDirectory parent, string dirName) @@ -54,9 +59,9 @@ namespace VNLib.Utils.IO //Store ref to parent dir Parent = parent; //Referrence store - this.Storage = parent.Storage; + Storage = parent.Storage; //Add the name of this dir to the end of the specified dir path - this.DirectoryPath = Path.Combine(parent.DirectoryPath, dirName); + DirectoryPath = Path.Combine(parent.DirectoryPath, dirName); } /// <summary> @@ -67,19 +72,15 @@ namespace VNLib.Utils.IO /// <exception cref="IsolatedStorageException"></exception> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="DirectoryNotFoundException"></exception> - public IsolatedStorageFileStream CreateFile(string fileName) - { - return this.Storage.CreateFile(Path.Combine(DirectoryPath, fileName)); - } + public IsolatedStorageFileStream CreateFile(string fileName) => Storage.CreateFile(GetFullFilePath(fileName)); + /// <summary> /// Removes a file from the current directory /// </summary> /// <param name="fileName">The path of the file to remove</param> /// <exception cref="IsolatedStorageException"></exception> - public void DeleteFile(string fileName) - { - this.Storage.DeleteFile(Path.Combine(this.DirectoryPath, fileName)); - } + public void DeleteFile(string fileName) => Storage.DeleteFile(GetFullFilePath(fileName)); + /// <summary> /// Opens a file that exists within the current directory /// </summary> @@ -87,10 +88,9 @@ namespace VNLib.Utils.IO /// <param name="mode">File mode</param> /// <param name="access">File access</param> /// <returns>The open <see cref="IsolatedStorageFileStream"/> from the current directory</returns> - public IsolatedStorageFileStream OpenFile(string fileName, FileMode mode, FileAccess access) - { - return this.Storage.OpenFile(Path.Combine(DirectoryPath, fileName), mode, access); - } + public IsolatedStorageFileStream OpenFile(string fileName, FileMode mode, FileAccess access) + => Storage.OpenFile(GetFullFilePath(fileName), mode, access); + /// <summary> /// Opens a file that exists within the current directory /// </summary> @@ -99,10 +99,8 @@ namespace VNLib.Utils.IO /// <param name="access">File access</param> /// <param name="share">The file shareing mode</param> /// <returns>The open <see cref="IsolatedStorageFileStream"/> from the current directory</returns> - public IsolatedStorageFileStream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share) - { - return this.Storage.OpenFile(Path.Combine(DirectoryPath, fileName), mode, access, share); - } + public IsolatedStorageFileStream OpenFile(string fileName, FileMode mode, FileAccess access, FileShare share) + => Storage.OpenFile(GetFullFilePath(fileName), mode, access, share); /// <summary> /// Determiens if the specified file path refers to an existing file within the directory @@ -113,25 +111,22 @@ namespace VNLib.Utils.IO /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="IsolatedStorageException"></exception> /// <exception cref="InvalidOperationException"></exception> - public bool FileExists(string fileName) - { - return this.Storage.FileExists(Path.Combine(this.DirectoryPath, fileName)); - } + public bool FileExists(string fileName) => Storage.FileExists(GetFullFilePath(fileName)); /// <summary> /// Removes the directory and its contents from the store /// </summary> - public override void Remove() - { - Storage.DeleteDirectory(this.DirectoryPath); - } + public override void Remove() => Storage.DeleteDirectory(DirectoryPath); ///<inheritdoc/> public override long AvailableFreeSpace => Storage.AvailableFreeSpace; + ///<inheritdoc/> public override long Quota => Storage.Quota; + ///<inheritdoc/> public override long UsedSize => Storage.UsedSize; + ///<inheritdoc/> public override bool IncreaseQuotaTo(long newQuotaSize) => Storage.IncreaseQuotaTo(newQuotaSize); @@ -139,9 +134,7 @@ namespace VNLib.Utils.IO /// The parent <see cref="IsolatedStorageDirectory"/> this directory is a child within. null if there are no parent directories /// above this dir /// </summary> - public IsolatedStorageDirectory? Parent { get; } -#nullable disable /// <summary> /// Creates a child directory within the current directory @@ -150,9 +143,23 @@ namespace VNLib.Utils.IO /// <returns>A new <see cref="IsolatedStorageDirectory"/> for which <see cref="IsolatedStorageFileStream"/>s can be opened/created</returns> /// <exception cref="ArgumentException"></exception> /// <exception cref="ArgumentNullException"></exception> - public IsolatedStorageDirectory CreateChildDirectory(string directoryName) + public IsolatedStorageDirectory CreateChildDirectory(string directoryName) => new (this, directoryName); + + /// <summary> + /// Gets the IsolatedStorage file path localized to the current directory, including th path + /// of any parent directories. NOTE: it's not possible to get the full filesystem path due to + /// isolated storage security restrictions. + /// </summary> + /// <param name="filePath"> + /// The relative path to the file within the directory to recover the file path from + /// </param> + /// <returns>The localized relative file path within the current directory</returns> + /// <exception cref="ArgumentException"></exception> + public string GetFullFilePath(string filePath) { - return new IsolatedStorageDirectory(this, directoryName); + return Path.IsPathRooted(filePath) + ? throw new ArgumentException("The file path may not be fully rooted, it must be a relative", nameof(filePath)) + : Path.Combine(DirectoryPath, filePath); } } }
\ No newline at end of file diff --git a/lib/Utils/src/IO/VnMemoryStream.cs b/lib/Utils/src/IO/VnMemoryStream.cs index d269134..97cef03 100644 --- a/lib/Utils/src/IO/VnMemoryStream.cs +++ b/lib/Utils/src/IO/VnMemoryStream.cs @@ -56,12 +56,25 @@ namespace VNLib.Utils.IO /// <param name="readOnly">Should the stream be readonly?</param> /// <exception cref="ArgumentException"></exception> /// <returns>A <see cref="VnMemoryStream"/> wrapper to access the handle data</returns> - public static VnMemoryStream ConsumeHandle(MemoryHandle<byte> handle, nint length, bool readOnly) + public static VnMemoryStream ConsumeHandle(MemoryHandle<byte> handle, nint length, bool readOnly) => FromHandle(handle, true, length, readOnly); + + /// <summary> + /// Creates a new <see cref="VnMemoryStream"/> from the supplied memory handle + /// of the initial length. This function also accepts a value that indicates if this stream + /// owns the memory handle, which will cause it to be disposed when the stream is disposed. + /// </summary> + /// <param name="handle"><see cref="MemoryHandle{T}"/> to consume</param> + /// <param name="length">The initial length of the stream</param> + /// <param name="readOnly">Should the stream be readonly?</param> + /// <param name="ownsHandle">A value that indicates if the current stream owns the memory handle</param> + /// <exception cref="ArgumentException"></exception> + /// <returns>A <see cref="VnMemoryStream"/> wrapper to access the handle data</returns> + public static VnMemoryStream FromHandle(MemoryHandle<byte> handle, bool ownsHandle, nint length, bool readOnly) { handle.ThrowIfClosed(); - return new VnMemoryStream(handle, length, readOnly, true); + return new VnMemoryStream(handle, length, readOnly, ownsHandle); } - + /// <summary> /// Converts a writable <see cref="VnMemoryStream"/> to readonly to allow shallow copies /// </summary> @@ -258,6 +271,7 @@ namespace VNLib.Utils.IO /// </para> /// </summary> public override bool CanRead => true; + /// <summary> /// <inheritdoc/> /// <para> @@ -265,13 +279,16 @@ namespace VNLib.Utils.IO /// </para> /// </summary> public override bool CanSeek => true; + /// <summary> /// True unless the stream is (or has been converted to) a readonly /// stream. /// </summary> public override bool CanWrite => !_isReadonly; + ///<inheritdoc/> public override long Length => _length; + ///<inheritdoc/> public override bool CanTimeout => false; @@ -281,6 +298,7 @@ namespace VNLib.Utils.IO get => _position; set => Seek(value, SeekOrigin.Begin); } + /// <summary> /// Closes the stream and frees the internal allocated memory blocks /// </summary> @@ -292,13 +310,17 @@ namespace VNLib.Utils.IO _buffer.Dispose(); } } + ///<inheritdoc/> public override void Flush() { } + // Override to reduce base class overhead ///<inheritdoc/> public override Task FlushAsync(CancellationToken cancellationToken) => Task.CompletedTask; + ///<inheritdoc/> public override int Read(byte[] buffer, int offset, int count) => Read(buffer.AsSpan(offset, count)); + ///<inheritdoc/> public override int Read(Span<byte> buffer) { @@ -317,6 +339,7 @@ namespace VNLib.Utils.IO return bytesToRead; } + ///<inheritdoc/> public override unsafe int ReadByte() { @@ -346,6 +369,7 @@ namespace VNLib.Utils.IO int read = Read(buffer.Span); return ValueTask.FromResult(read); } + ///<inheritdoc/> public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { @@ -353,6 +377,7 @@ namespace VNLib.Utils.IO int read = Read(buffer.AsSpan(offset, count)); return Task.FromResult(read); } + ///<inheritdoc/> public override long Seek(long offset, SeekOrigin origin) { @@ -384,8 +409,7 @@ namespace VNLib.Utils.IO default: throw new ArgumentException("Stream operation is not supported on current stream"); } - } - + } /// <summary> /// Resizes the internal buffer to the exact size (in bytes) of the @@ -423,8 +447,10 @@ namespace VNLib.Utils.IO //Make sure the position is not pointing outside of the buffer after resize _position = Math.Min(_position, _length); } + ///<inheritdoc/> public override void Write(byte[] buffer, int offset, int count) => Write(buffer.AsSpan(offset, count)); + ///<inheritdoc/> public override void Write(ReadOnlySpan<byte> buffer) { @@ -447,6 +473,7 @@ namespace VNLib.Utils.IO //Update the position _position = newPos; } + ///<inheritdoc/> public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { @@ -454,6 +481,7 @@ namespace VNLib.Utils.IO Write(buffer, offset, count); return Task.CompletedTask; } + ///<inheritdoc/> public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default) { @@ -461,6 +489,7 @@ namespace VNLib.Utils.IO Write(buffer.Span); return ValueTask.CompletedTask; } + ///<inheritdoc/> public override void WriteByte(byte value) { @@ -504,19 +533,5 @@ namespace VNLib.Utils.IO /// <exception cref="NotSupportedException"></exception> 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) - */ - - ///<inheritdoc/> - 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/lib/Utils/src/IO/VnStreamWriter.cs b/lib/Utils/src/IO/VnStreamWriter.cs index 03b0a6e..5ec65fc 100644 --- a/lib/Utils/src/IO/VnStreamWriter.cs +++ b/lib/Utils/src/IO/VnStreamWriter.cs @@ -33,6 +33,8 @@ using System.Runtime.CompilerServices; using VNLib.Utils.Memory; +#pragma warning disable CA2215, CA2007, CS8765 // Dispose methods should call base class dispose + namespace VNLib.Utils.IO { /// <summary> diff --git a/lib/Utils/src/Memory/HeapCreation.cs b/lib/Utils/src/Memory/HeapCreation.cs index 2d30c29..9ef9fdb 100644 --- a/lib/Utils/src/Memory/HeapCreation.cs +++ b/lib/Utils/src/Memory/HeapCreation.cs @@ -49,6 +49,6 @@ namespace VNLib.Utils.Memory /// <summary> /// Specifies that the requested heap will be a shared heap for the process/library /// </summary> - IsSharedHeap = 0x04 + Shared = 0x04 } }
\ No newline at end of file diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs index 6af2392..8cc9736 100644 --- a/lib/Utils/src/Memory/MemoryUtil.cs +++ b/lib/Utils/src/Memory/MemoryUtil.cs @@ -109,8 +109,8 @@ namespace VNLib.Utils.Memory _ = ERRNO.TryParse(Environment.GetEnvironmentVariable(SHARED_HEAP_ENABLE_DIAGNOISTICS_ENV), out ERRNO diagEnable); _ = ERRNO.TryParse(Environment.GetEnvironmentVariable(SHARED_HEAP_GLOBAL_ZERO), out ERRNO globalZero); - Trace.WriteIf(diagEnable, "Shared heap diagnostics enabled"); - Trace.WriteIf(globalZero, "Shared heap global zero enabled"); + Trace.WriteLineIf(diagEnable, "Shared heap diagnostics enabled"); + Trace.WriteLineIf(globalZero, "Shared heap global zero enabled"); Lazy<IUnmangedHeap> heap = new (() => InitHeapInternal(true, diagEnable, globalZero), LazyThreadSafetyMode.PublicationOnly); @@ -173,7 +173,7 @@ namespace VNLib.Utils.Memory * The heap impl may reset the synchronziation flag if it does not * need serialziation */ - cFlags |= isShared ? HeapCreation.IsSharedHeap : HeapCreation.None; + cFlags |= isShared ? HeapCreation.Shared : HeapCreation.None; //Set global zero flag if requested cFlags |= globalZero ? HeapCreation.GlobalZero : HeapCreation.None; diff --git a/lib/Utils/src/Memory/NativeHeap.cs b/lib/Utils/src/Memory/NativeHeap.cs index 4b76866..7a3d4dd 100644 --- a/lib/Utils/src/Memory/NativeHeap.cs +++ b/lib/Utils/src/Memory/NativeHeap.cs @@ -177,9 +177,9 @@ namespace VNLib.Utils.Memory { public IntPtr HeapPointer; - public HeapCreation CreationFlags; - public ERRNO Flags; + + public HeapCreation CreationFlags; } readonly record struct HeapMethods diff --git a/lib/Utils/src/Memory/ProcessHeap.cs b/lib/Utils/src/Memory/ProcessHeap.cs index 84c9e0e..3d581cd 100644 --- a/lib/Utils/src/Memory/ProcessHeap.cs +++ b/lib/Utils/src/Memory/ProcessHeap.cs @@ -45,11 +45,11 @@ namespace VNLib.Utils.Memory /// <summary> /// <inheritdoc/> /// <para> - /// Is always <see cref="HeapCreation.IsSharedHeap"/> as this heap is the default + /// Is always <see cref="HeapCreation.Shared"/> as this heap is the default /// process heap. Meaining memory will be shared across the process /// </para> /// </summary> - public HeapCreation CreationFlags { get; } = HeapCreation.IsSharedHeap; + public HeapCreation CreationFlags { get; } = HeapCreation.Shared; /// <summary> /// Initalizes a new global (cross platform) process heap diff --git a/lib/Utils/src/Memory/Win32PrivateHeap.cs b/lib/Utils/src/Memory/Win32PrivateHeap.cs index 0a00a77..41fe33a 100644 --- a/lib/Utils/src/Memory/Win32PrivateHeap.cs +++ b/lib/Utils/src/Memory/Win32PrivateHeap.cs @@ -110,7 +110,7 @@ namespace VNLib.Utils.Memory /// </remarks> public static Win32PrivateHeap Create(nuint initialSize, HeapCreation cFlags, nuint maxHeapSize = 0, DWORD flags = HEAP_NO_FLAGS) { - if (cFlags.HasFlag(HeapCreation.IsSharedHeap)) + if (cFlags.HasFlag(HeapCreation.Shared)) { //Clear the synchronization flag because we don't need it for a process heap cFlags &= ~HeapCreation.UseSynchronization; diff --git a/lib/Utils/src/VnEncoding.cs b/lib/Utils/src/VnEncoding.cs index e945135..e24f7df 100644 --- a/lib/Utils/src/VnEncoding.cs +++ b/lib/Utils/src/VnEncoding.cs @@ -51,10 +51,15 @@ namespace VNLib.Utils /// </summary> /// <param name="data">Data to be encoded</param> /// <param name="encoding"><see cref="Encoding"/> to encode data with</param> + /// <param name="heap">Heap to allocate memory from</param> /// <returns>A <see cref="Stream"/> contating the encoded data</returns> - public static VnMemoryStream GetMemoryStream(ReadOnlySpan<char> data, Encoding encoding) + public static VnMemoryStream GetMemoryStream(ReadOnlySpan<char> data, Encoding encoding, IUnmangedHeap? heap = null) { _ = encoding ?? throw new ArgumentNullException(nameof(encoding)); + + //Assign default heap if not specified + heap ??= MemoryUtil.Shared; + //Create new memory handle to copy data to MemoryHandle<byte>? handle = null; try @@ -62,7 +67,7 @@ namespace VNLib.Utils //get number of bytes int byteCount = encoding.GetByteCount(data); //resize the handle to fit the data - handle = MemoryUtil.Shared.Alloc<byte>(byteCount); + handle = heap.Alloc<byte>(byteCount); //encode int size = encoding.GetBytes(data, handle); //Consume the handle into a new vnmemstream and return it |