diff options
author | vnugent <public@vaughnnugent.com> | 2023-01-08 16:01:54 -0500 |
---|---|---|
committer | vnugent <public@vaughnnugent.com> | 2023-01-08 16:01:54 -0500 |
commit | de94d788e9a47432a7630a8215896b8dd3628599 (patch) | |
tree | 666dec06eef861d101cb6948aff52a3d354c8d73 /Utils/src | |
parent | be6dc557a3b819248b014992eb96c1cb21f8112b (diff) |
Reorder + analyzer cleanup
Diffstat (limited to 'Utils/src')
91 files changed, 0 insertions, 14062 deletions
diff --git a/Utils/src/Async/AccessSerializer.cs b/Utils/src/Async/AccessSerializer.cs deleted file mode 100644 index ce78f6c..0000000 --- a/Utils/src/Async/AccessSerializer.cs +++ /dev/null @@ -1,297 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: AccessSerializer.cs -* -* AccessSerializer.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.Threading; -using System.Threading.Tasks; - -using VNLib.Utils.Resources; - -namespace VNLib.Utils.Async -{ - /// <summary> - /// Provides access arbitration to an exclusive resouce - /// </summary> - /// <typeparam name="TKey">The uinique identifier type for the resource</typeparam> - /// <typeparam name="TResource">The resource type</typeparam> - public sealed class AccessSerializer<TKey, TResource> where TResource : IExclusiveResource - { - private readonly SemaphoreSlim semaphore; - private readonly Func<TKey, TResource> Factory; - private readonly Action CompletedCb; - private int WaitingCount; - /// <summary> - /// Creates a new <see cref="AccessSerializer{K, T}"/> with the specified factory and completed callback - /// </summary> - /// <param name="factory">Factory function to genereate new <typeparamref name="TResource"/> objects from <typeparamref name="TKey"/> keys</param> - /// <param name="completedCb">Function to be invoked when the encapsulated objected is no longer in use</param> - /// <exception cref="ArgumentNullException"></exception> - public AccessSerializer(Func<TKey, TResource> factory, Action completedCb) - { - this.Factory = factory ?? throw new ArgumentNullException(nameof(factory)); - this.CompletedCb = completedCb; - //Setup semaphore for locking - this.semaphore = new SemaphoreSlim(1, 1); - this.WaitingCount = 0; - } - - /// <summary> - /// Attempts to obtain an exclusive lock on the object - /// </summary> - /// <param name="key"></param> - /// <param name="wait">Time to wait for lock</param> - /// <param name="exObj"></param> - /// <returns>true if lock was obtained within the timeout, false if the lock was not obtained</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public bool TryWait(TKey key, TimeSpan wait, out ExclusiveResourceHandle<TResource> exObj) - { - //Increase waiting count while we wait - Interlocked.Increment(ref WaitingCount); - try - { - //Try to obtain the lock - if (semaphore.Wait(wait)) - { - TResource get() => Factory(key); - //Create new exclusive lock handle that will generate a new that calls release when freed - exObj = new(get, Release); - return true; - } - //Lock not taken - exObj = null; - return false; - } - finally - { - //Decrease the waiting count since we are no longer waiting - Interlocked.Decrement(ref WaitingCount); - } - } - /// <summary> - /// Waits for exclusive access to the resource. - /// </summary> - /// <param name="key"></param> - /// <returns>An <see cref="ExclusiveResourceHandle{T}"/> encapsulating the resource</returns> - public ExclusiveResourceHandle<TResource> Wait(TKey key) - { - try - { - //Increase waiting count while we wait - Interlocked.Increment(ref WaitingCount); - //Try to obtain the lock - semaphore.Wait(); - //Local function to generate the output value - TResource get() => Factory(key); - //Create new exclusive lock handle that will generate a new that calls release when freed - return new(get, Release); - } - finally - { - //Decrease the waiting count since we are no longer waiting - Interlocked.Decrement(ref WaitingCount); - } - } - /// <summary> - /// Asynchronously waits for exclusive access to the resource. - /// </summary> - /// <returns>An <see cref="ExclusiveResourceHandle{TResource}"/> encapsulating the resource</returns> - public async Task<ExclusiveResourceHandle<TResource>> WaitAsync(TKey key, CancellationToken cancellationToken = default) - { - try - { - //Increase waiting count while we wait - Interlocked.Increment(ref WaitingCount); - //Try to obtain the lock - await semaphore.WaitAsync(cancellationToken); - //Local function to generate the output value - TResource get() => Factory(key); - //Create new exclusive lock handle that will generate a new that calls release when freed - return new(get, Release); - } - finally - { - //Decrease the waiting count since we are no longer waiting - Interlocked.Decrement(ref WaitingCount); - } - } - /// <summary> - /// Releases an exclusive lock that is held on an object - /// </summary> - private void Release() - { - /* - * If objects are waiting for the current instance, then we will release - * the semaphore and exit, as we no longer have control over the context - */ - if (WaitingCount > 0) - { - this.semaphore.Release(); - } - else - { - //Do not release the sempahore, just dispose of the semaphore - this.semaphore.Dispose(); - //call the completed function - CompletedCb?.Invoke(); - } - } - } - - /// <summary> - /// Provides access arbitration to an <see cref="IExclusiveResource"/> - /// </summary> - /// <typeparam name="TKey">The uinique identifier type for the resource</typeparam> - /// <typeparam name="TArg">The type of the optional argument to be passed to the user-implemented factory function</typeparam> - /// <typeparam name="TResource">The resource type</typeparam> - public sealed class AccessSerializer<TKey, TArg, TResource> where TResource : IExclusiveResource - { - private readonly SemaphoreSlim semaphore; - private readonly Func<TKey, TArg, TResource> Factory; - private readonly Action CompletedCb; - private int WaitingCount; - /// <summary> - /// Creates a new <see cref="AccessSerializer{TKey, TArg, TResource}"/> with the specified factory and completed callback - /// </summary> - /// <param name="factory">Factory function to genereate new <typeparamref name="TResource"/> objects from <typeparamref name="TKey"/> keys</param> - /// <param name="completedCb">Function to be invoked when the encapsulated objected is no longer in use</param> - /// <exception cref="ArgumentNullException"></exception> - public AccessSerializer(Func<TKey, TArg, TResource> factory, Action completedCb) - { - this.Factory = factory ?? throw new ArgumentNullException(nameof(factory)); - this.CompletedCb = completedCb; - //Setup semaphore for locking - this.semaphore = new SemaphoreSlim(1, 1); - this.WaitingCount = 0; - } - - /// <summary> - /// Attempts to obtain an exclusive lock on the object - /// </summary> - /// <param name="key"></param> - /// <param name="arg">The key identifying the resource</param> - /// <param name="wait">Time to wait for lock</param> - /// <param name="exObj"></param> - /// <returns>true if lock was obtained within the timeout, false if the lock was not obtained</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public bool TryWait(TKey key, TArg arg, TimeSpan wait, out ExclusiveResourceHandle<TResource> exObj) - { - //Increase waiting count while we wait - Interlocked.Increment(ref WaitingCount); - try - { - //Try to obtain the lock - if (semaphore.Wait(wait)) - { - TResource get() => Factory(key, arg); - //Create new exclusive lock handle that will generate a new that calls release when freed - exObj = new(get, Release); - return true; - } - //Lock not taken - exObj = null; - return false; - } - finally - { - //Decrease the waiting count since we are no longer waiting - Interlocked.Decrement(ref WaitingCount); - } - } - /// <summary> - /// Waits for exclusive access to the resource. - /// </summary> - /// <param name="key">The unique key that identifies the resource</param> - /// <param name="arg">The state argument to pass to the factory function</param> - /// <returns>An <see cref="ExclusiveResourceHandle{TResource}"/> encapsulating the resource</returns> - public ExclusiveResourceHandle<TResource> Wait(TKey key, TArg arg) - { - try - { - //Increase waiting count while we wait - Interlocked.Increment(ref WaitingCount); - //Try to obtain the lock - semaphore.Wait(); - //Local function to generate the output value - TResource get() => Factory(key, arg); - //Create new exclusive lock handle that will generate a new that calls release when freed - return new(get, Release); - } - finally - { - //Decrease the waiting count since we are no longer waiting - Interlocked.Decrement(ref WaitingCount); - } - } - /// <summary> - /// Asynchronously waits for exclusive access to the resource. - /// </summary> - /// <param name="key"></param> - /// <param name="arg">The state argument to pass to the factory function</param> - /// <param name="cancellationToken"></param> - /// <returns>An <see cref="ExclusiveResourceHandle{TResource}"/> encapsulating the resource</returns> - public async Task<ExclusiveResourceHandle<TResource>> WaitAsync(TKey key, TArg arg, CancellationToken cancellationToken = default) - { - try - { - //Increase waiting count while we wait - Interlocked.Increment(ref WaitingCount); - //Try to obtain the lock - await semaphore.WaitAsync(cancellationToken); - //Local function to generate the output value - TResource get() => Factory(key, arg); - //Create new exclusive lock handle that will generate a new that calls release when freed - return new(get, Release); - } - finally - { - //Decrease the waiting count since we are no longer waiting - Interlocked.Decrement(ref WaitingCount); - } - } - - /// <summary> - /// Releases an exclusive lock that is held on an object - /// </summary> - private void Release() - { - /* - * If objects are waiting for the current instance, then we will release - * the semaphore and exit, as we no longer have control over the context - */ - if (WaitingCount > 0) - { - this.semaphore.Release(); - } - else - { - //Do not release the sempahore, just dispose of the semaphore - this.semaphore.Dispose(); - //call the completed function - CompletedCb?.Invoke(); - } - } - } -}
\ No newline at end of file diff --git a/Utils/src/Async/AsyncExclusiveResource.cs b/Utils/src/Async/AsyncExclusiveResource.cs deleted file mode 100644 index 18e2a42..0000000 --- a/Utils/src/Async/AsyncExclusiveResource.cs +++ /dev/null @@ -1,169 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: AsyncExclusiveResource.cs -* -* AsyncExclusiveResource.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.Threading; -using System.Threading.Tasks; - -namespace VNLib.Utils.Async -{ - /// <summary> - /// Provides a base class for resources that must be obtained exclusivly in a multi-threaded environment - /// but allow state update operations (and their exceptions) to be deferred to the next accessor. - /// </summary> - /// <typeparam name="TState">The state parameter type passed during updates</typeparam> - public abstract class AsyncExclusiveResource<TState> : VnDisposeable, IWaitHandle, IAsyncWaitHandle - { - /// <summary> - /// Main mutli-threading lock used for primary access synchronization - /// </summary> - protected SemaphoreSlim MainLock { get; } = new (1, 1); - - private Task? LastUpdate; - - /// <summary> - /// <inheritdoc/> - /// <br></br> - /// <br></br> - /// If the previous call to <see cref="UpdateAndRelease"/> resulted in an asynchronous update, and exceptions occured, an <see cref="AsyncUpdateException"/> - /// will be thrown enclosing the exception - /// </summary> - /// <param name="millisecondsTimeout">Time in milliseconds to wait for exclusive access to the resource</param> - /// <exception cref="AsyncUpdateException"></exception> - /// <inheritdoc/> - public virtual bool WaitOne(int millisecondsTimeout) - { - //First wait for main lock - if (MainLock.Wait(millisecondsTimeout)) - { - //Main lock has been taken - try - { - //Wait for async update if there is one pending(will throw exceptions if any occurred) - LastUpdate?.Wait(); - return true; - } - catch (AggregateException ae) when (ae.InnerException != null) - { - //Release the main lock and re-throw the inner exception - _ = MainLock.Release(); - //Throw a new async update exception - throw new AsyncUpdateException(ae.InnerException); - } - catch - { - //Release the main lock and re-throw the exception - _ = MainLock.Release(); - throw; - } - } - return false; - } - - ///<inheritdoc/> - ///<exception cref="ObjectDisposedException"></exception> - public virtual async Task WaitOneAsync(CancellationToken token = default) - { - //Wait for main lock - await MainLock.WaitAsync(token).ConfigureAwait(true); - //if the last update completed synchronously, return true - if (LastUpdate == null) - { - return; - } - try - { - //Await the last update task and catch its exceptions - await LastUpdate.ConfigureAwait(false); - } - catch - { - //Release the main lock and re-throw the exception - _ = MainLock.Release(); - throw; - } - } - - /// <summary> - /// Requests a resource update and releases the exclusive lock on this resource. If a deferred update operation has any - /// exceptions during its last operation, they will be thrown here. - /// </summary> - /// <param name="defer">Specifies weather the update should be deferred or awaited on the current call</param> - /// <param name="state">A state parameter to be pased to the update function</param> - /// <exception cref="ObjectDisposedException"></exception> - public async ValueTask UpdateAndRelease(bool defer, TState state) - { - //Otherwise wait and update on the current thread - try - { - //Dispose the update task - LastUpdate?.Dispose(); - //Remove the referrence - LastUpdate = null; - //Run update on the current thread - LastUpdate = await UpdateResource(defer, state).ConfigureAwait(true); - //If the update is not deferred, await the results - if (!defer && LastUpdate != null) - { - await LastUpdate.ConfigureAwait(true); - } - } - finally - { - //Release the main lock - _ = MainLock.Release(); - } - } - - /// <summary> - /// <para> - /// When overrriden in a derived class, is responsible for updating the state of the instance if necessary. - /// </para> - /// <para> - /// If the result of the update retruns a <see cref="Task"/> that represents the deferred update, the next call to <see cref="WaitOne"/> will - /// block until the operation completes and will throw any exceptions that occured - /// </para> - /// </summary> - /// <param name="defer">true if the caller expects a resource update to be deferred, false if the caller expects the result of the update to be awaited</param> - /// <param name="state">State parameter passed when releasing</param> - /// <returns>A <see cref="Task"/> representing the async state update operation, or null if no async state update operation need's to be monitored</returns> - protected abstract ValueTask<Task?> UpdateResource(bool defer, TState state); - - ///<inheritdoc/> - protected override void Free() - { - //Dispose lock - MainLock.Dispose(); - - //Try to cleanup the last update - if (LastUpdate != null && LastUpdate.IsCompletedSuccessfully) - { - LastUpdate.Dispose(); - } - - LastUpdate = null; - } - - } -}
\ No newline at end of file diff --git a/Utils/src/Async/AsyncQueue.cs b/Utils/src/Async/AsyncQueue.cs deleted file mode 100644 index ba45513..0000000 --- a/Utils/src/Async/AsyncQueue.cs +++ /dev/null @@ -1,144 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: AsyncQueue.cs -* -* AsyncQueue.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.Threading; -using System.Threading.Tasks; -using System.Threading.Channels; -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> - { - private readonly Channel<T> _channel; - - /// <summary> - /// Initalizes a new multi-threaded bound channel queue, that accepts - /// the <paramref name="capacity"/> number of items before it will - /// return asynchronously, or fail to enqueue items - /// </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 - /// and a bound/unbound channel capacity - /// </summary> - /// <param name="singleWriter">A value that specifies only a single thread be enqueing items?</param> - /// <param name="singleReader">A value that specifies only a single thread will be dequeing</param> - /// <param name="capacity">The maxium number of items to enque without failing</param> - public AsyncQueue(bool singleWriter, bool singleReader, int capacity = int.MaxValue) - { - if(capacity == int.MaxValue) - { - //Create unbounded - UnboundedChannelOptions opt = new() - { - SingleReader = singleReader, - SingleWriter = singleWriter, - AllowSynchronousContinuations = true, - }; - _channel = Channel.CreateUnbounded<T>(opt); - } - else - { - //Create bounded - BoundedChannelOptions opt = new(capacity) - { - SingleReader = singleReader, - SingleWriter = singleWriter, - AllowSynchronousContinuations = true, - //Default wait for space - FullMode = BoundedChannelFullMode.Wait - }; - _channel = Channel.CreateBounded<T>(opt); - } - } - - /// <summary> - /// Initalizes a new unbound channel based queue - /// </summary> - /// <param name="ubOptions">Channel options</param> - public AsyncQueue(UnboundedChannelOptions ubOptions) - { - _channel = Channel.CreateUnbounded<T>(ubOptions); - } - - /// <summary> - /// Initalizes a new bound channel based queue - /// </summary> - /// <param name="options">Channel options</param> - public AsyncQueue(BoundedChannelOptions options) - { - _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> - 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> - /// <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> - /// <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> - /// <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> - /// <exception cref="ObjectDisposedException"></exception> - public bool TryPeek([MaybeNullWhen(false)] out T result) => _channel.Reader.TryPeek(out result); - } -} diff --git a/Utils/src/Async/AsyncUpdatableResource.cs b/Utils/src/Async/AsyncUpdatableResource.cs deleted file mode 100644 index b4ce519..0000000 --- a/Utils/src/Async/AsyncUpdatableResource.cs +++ /dev/null @@ -1,111 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: AsyncUpdatableResource.cs -* -* AsyncUpdatableResource.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.Json; -using System.Threading.Tasks; - -using VNLib.Utils.IO; -using VNLib.Utils.Resources; - -namespace VNLib.Utils.Async -{ - /// <summary> - /// A callback delegate used for updating a <see cref="AsyncUpdatableResource"/> - /// </summary> - /// <param name="source">The <see cref="AsyncUpdatableResource"/> to be updated</param> - /// <param name="data">The serialized data to be stored/updated</param> - /// <exception cref="ResourceUpdateFailedException"></exception> - public delegate Task AsyncUpdateCallback(object source, Stream data); - /// <summary> - /// A callback delegate invoked when a <see cref="AsyncUpdatableResource"/> delete is requested - /// </summary> - /// <param name="source">The <see cref="AsyncUpdatableResource"/> to be deleted</param> - /// <exception cref="ResourceDeleteFailedException"></exception> - public delegate Task AsyncDeleteCallback(object source); - - /// <summary> - /// Implemented by a resource that is backed by an external data store, that when modified or deleted will - /// be reflected to the backing store. - /// </summary> - public abstract class AsyncUpdatableResource : BackedResourceBase, IAsyncExclusiveResource - { - protected abstract AsyncUpdateCallback UpdateCb { get; } - protected abstract AsyncDeleteCallback DeleteCb { get; } - - /// <summary> - /// Releases the resource and flushes pending changes to its backing store. - /// </summary> - /// <returns>A task that represents the async operation</returns> - /// <exception cref="InvalidOperationException"></exception> - /// <exception cref="ResourceDeleteFailedException"></exception> - /// <exception cref="ResourceUpdateFailedException"></exception> - public virtual async ValueTask ReleaseAsync() - { - //If resource has already been realeased, return - if (IsReleased) - { - return; - } - //If deleted flag is set, invoke the delete callback - if (Deleted) - { - await DeleteCb(this).ConfigureAwait(true); - } - //If the state has been modifed, flush changes to the store - else if (Modified) - { - await FlushPendingChangesAsync().ConfigureAwait(true); - } - //Set the released value - IsReleased = true; - } - - /// <summary> - /// <para> - /// Writes the current state of the the resource to the backing store - /// immediatly by invoking the specified callback. - /// </para> - /// <para> - /// Only call this method if your store supports multiple state updates - /// </para> - /// </summary> - protected virtual async Task FlushPendingChangesAsync() - { - //Get the resource - object resource = GetResource(); - //Open a memory stream to store data in - using VnMemoryStream data = new(); - //Serialize and write to stream - VnEncoding.JSONSerializeToBinary(resource, data, resource.GetType(), base.JSO); - //Reset stream to begining - _ = data.Seek(0, SeekOrigin.Begin); - //Invoke update callback - await UpdateCb(this, data).ConfigureAwait(true); - //Clear modified flag - Modified = false; - } - } -} diff --git a/Utils/src/Async/Exceptions/AsyncUpdateException.cs b/Utils/src/Async/Exceptions/AsyncUpdateException.cs deleted file mode 100644 index de5a491..0000000 --- a/Utils/src/Async/Exceptions/AsyncUpdateException.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: AsyncUpdateException.cs -* -* AsyncUpdateException.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.Resources; - -namespace VNLib.Utils.Async -{ - /// <summary> - /// Represents an exception that was raised during an asyncronous update of a resource. The <see cref="Exception.InnerException"/> stores the - /// details of the actual exception raised - /// </summary> - public sealed class AsyncUpdateException : ResourceUpdateFailedException - { - /// <summary> - /// - /// </summary> - /// <param name="inner"></param> - public AsyncUpdateException(Exception inner) : base("", inner) { } - - public AsyncUpdateException() - {} - - public AsyncUpdateException(string message) : base(message) - {} - - public AsyncUpdateException(string message, Exception innerException) : base(message, innerException) - {} - } -}
\ No newline at end of file diff --git a/Utils/src/Async/IAsyncExclusiveResource.cs b/Utils/src/Async/IAsyncExclusiveResource.cs deleted file mode 100644 index 93157ce..0000000 --- a/Utils/src/Async/IAsyncExclusiveResource.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IAsyncExclusiveResource.cs -* -* IAsyncExclusiveResource.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.Tasks; -using VNLib.Utils.Resources; - -namespace VNLib.Utils.Async -{ - /// <summary> - /// <inheritdoc/> - /// </summary> - public interface IAsyncExclusiveResource : IResource - { - /// <summary> - /// Releases the resource from use. Called when a <see cref="ExclusiveResourceHandle{T}"/> is disposed - /// </summary> - ValueTask ReleaseAsync(); - } -}
\ No newline at end of file diff --git a/Utils/src/Async/IAsyncWaitHandle.cs b/Utils/src/Async/IAsyncWaitHandle.cs deleted file mode 100644 index 1cadc06..0000000 --- a/Utils/src/Async/IAsyncWaitHandle.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IAsyncWaitHandle.cs -* -* IAsyncWaitHandle.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; - -namespace VNLib.Utils.Async -{ - /// <summary> - /// Provides a synchronization handle that can be asynchronously aquired - /// </summary> - public interface IAsyncWaitHandle - { - /// <summary> - /// Waits for exclusive access to the resource until the <see cref="CancellationToken"/> expires - /// </summary> - /// <inheritdoc/> - Task WaitOneAsync(CancellationToken token = default); - } -}
\ No newline at end of file diff --git a/Utils/src/Async/IWaitHandle.cs b/Utils/src/Async/IWaitHandle.cs deleted file mode 100644 index 85e8a2a..0000000 --- a/Utils/src/Async/IWaitHandle.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IWaitHandle.cs -* -* IWaitHandle.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.Threading; - -namespace VNLib.Utils.Async -{ - /// <summary> - /// Provides basic thread synchronization functions similar to <see cref="WaitHandle"/> - /// </summary> - public interface IWaitHandle - { - /// <summary> - /// Waits for exclusive access to the resource indefinitly. If the signal is never received this method never returns - /// </summary> - /// <inheritdoc/> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - /// <returns>true if the current thread received the signal</returns> - public virtual bool WaitOne() => WaitOne(Timeout.Infinite); - /// <summary> - /// Waits for exclusive access to the resource until the specified number of milliseconds - /// </summary> - /// <param name="millisecondsTimeout">Time in milliseconds to wait for exclusive access to the resource</param> - /// <returns>true if the current thread received the signal, false if the timout expired, and access was not granted</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - bool WaitOne(int millisecondsTimeout); - /// <summary> - /// Waits for exclusive access to the resource until the specified <see cref="TimeSpan"/> - /// </summary> - /// <returns>true if the current thread received the signal, false if the timout expired, and access was not granted</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public virtual bool WaitOne(TimeSpan timeout) => WaitOne(Convert.ToInt32(timeout.TotalMilliseconds)); - } -}
\ No newline at end of file diff --git a/Utils/src/BitField.cs b/Utils/src/BitField.cs deleted file mode 100644 index bc001df..0000000 --- a/Utils/src/BitField.cs +++ /dev/null @@ -1,115 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: BitField.cs -* -* BitField.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.Runtime.CompilerServices; - -namespace VNLib.Utils -{ - /// <summary> - /// Represents a field of 64 bits that can be set or cleared using unsigned or signed masks - /// </summary> - public class BitField - { - private ulong Field; - /// <summary> - /// The readonly value of the <see cref="BitField"/> - /// </summary> - public ulong Value => Field; - /// <summary> - /// Creates a new <see cref="BitField"/> initialized to the specified value - /// </summary> - /// <param name="initial">Initial value</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BitField(ulong initial) => Field = initial; - /// <summary> - /// Creates a new <see cref="BitField"/> initialized to the specified value - /// </summary> - /// <param name="initial">Initial value</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BitField(long initial) => Field = unchecked((ulong)initial); - /// <summary> - /// Determines if the specified flag is set - /// </summary> - /// <param name="mask">The mask to compare against the field value</param> - /// <returns>True if the flag(s) is currently set, false if flag is not set</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsSet(ulong mask) => (Field & mask) != 0; - /// <summary> - /// Determines if the specified flag is set - /// </summary> - /// <param name="mask">The mask to compare against the field value</param> - /// <returns>True if the flag(s) is currently set, false if flag is not set</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool IsSet(long mask) => (Field & unchecked((ulong)mask)) != 0; - /// <summary> - /// Determines if the specified flag is set - /// </summary> - /// <param name="mask">The mask to compare against the field value</param> - /// <returns>True if the flag(s) is currently set, false if flag is not set</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set(ulong mask) => Field |= mask; - /// <summary> - /// Determines if the specified flag is set - /// </summary> - /// <param name="mask">The mask to compare against the field value</param> - /// <returns>True if the flag(s) is currently set, false if flag is not set</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set(long mask) => Field |= unchecked((ulong)mask); - /// <summary> - /// Sets or clears a flag(s) indentified by a mask based on the value - /// </summary> - /// <param name="mask">Mask used to identify flags</param> - /// <param name="value">True to set a flag, false to clear a flag</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Set(ulong mask, bool value) - { - if (value) - { - Set(mask); - } - else - { - Clear(mask); - } - } - /// <summary> - /// Clears the flag identified by the specified mask - /// </summary> - /// <param name="mask">The mask used to clear the given flag</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear(ulong mask) => Field &= ~mask; - /// <summary> - /// Clears the flag identified by the specified mask - /// </summary> - /// <param name="mask">The mask used to clear the given flag</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear(long mask) => Field &= ~unchecked((ulong)mask); - /// <summary> - /// Clears all flags by setting the <see cref="Field"/> property value to 0 - /// </summary> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void ClearAll() => Field = 0; - } -}
\ No newline at end of file diff --git a/Utils/src/ERRNO.cs b/Utils/src/ERRNO.cs deleted file mode 100644 index c3c61de..0000000 --- a/Utils/src/ERRNO.cs +++ /dev/null @@ -1,152 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ERRNO.cs -* -* ERRNO.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.Runtime.InteropServices; - -namespace VNLib.Utils -{ - /// <summary> - /// Implements a C style integer error code type. Size is platform dependent - /// </summary> - [StructLayout(LayoutKind.Sequential)] - public readonly struct ERRNO : IEquatable<ERRNO>, ISpanFormattable, IFormattable - { - /// <summary> - /// Represents a successfull error code (true) - /// </summary> - public static readonly ERRNO SUCCESS = true; - - /// <summary> - /// Represents a failure error code (false) - /// </summary> - public static readonly ERRNO E_FAIL = false; - - private readonly nint ErrorCode; - /// <summary> - /// Creates a new <see cref="ERRNO"/> from the specified error value - /// </summary> - /// <param name="errno">The value of the error to represent</param> - public ERRNO(nint errno) => ErrorCode = errno; - /// <summary> - /// Creates a new <see cref="ERRNO"/> from an <see cref="int"/> error code. null = 0 = false - /// </summary> - /// <param name="errorVal">Error code</param> - public static implicit operator ERRNO(int errorVal) => new (errorVal); - /// <summary> - /// Creates a new <see cref="ERRNO"/> from an <see cref="int"/> error code. null = 0 = false - /// </summary> - /// <param name="errorVal">Error code</param> - public static explicit operator ERRNO(int? errorVal) => new(errorVal ?? 0); - /// <summary> - /// Creates a new <see cref="ERRNO"/> from a booleam, 1 if true, 0 if false - /// </summary> - /// <param name="errorVal"></param> - public static implicit operator ERRNO(bool errorVal) => new(errorVal ? 1 : 0); - /// <summary> - /// Creates a new <see cref="ERRNO"/> from a pointer value - /// </summary> - /// <param name="errno">The pointer value representing an error code</param> - public static implicit operator ERRNO(nint errno) => new(errno); - /// <summary> - /// Error value as integer. Value of supplied error code or if cast from boolean 1 if true, 0 if false - /// </summary> - /// <param name="errorVal"><see cref="ERRNO"/> to get error code from</param> - public static implicit operator int(ERRNO errorVal) => (int)errorVal.ErrorCode; - /// <summary> - /// C style boolean conversion. false if 0, true otherwise - /// </summary> - /// <param name="errorVal"></param> - public static implicit operator bool(ERRNO errorVal) => errorVal != 0; - /// <summary> - /// Creates a new <see cref="IntPtr"/> from the value if the stored (nint) error code - /// </summary> - /// <param name="errno">The <see cref="ERRNO"/> contating the pointer value</param> - public static implicit operator IntPtr(ERRNO errno) => new(errno.ErrorCode); - /// <summary> - /// Creates a new <c>nint</c> from the value if the stored error code - /// </summary> - /// <param name="errno">The <see cref="ERRNO"/> contating the pointer value</param> - public static implicit operator nint(ERRNO errno) => errno.ErrorCode; - - public static ERRNO operator +(ERRNO err, int add) => new(err.ErrorCode + add); - public static ERRNO operator +(ERRNO err, nint add) => new(err.ErrorCode + add); - public static ERRNO operator ++(ERRNO err) => new(err.ErrorCode + 1); - public static ERRNO operator --(ERRNO err) => new(err.ErrorCode - 1); - public static ERRNO operator -(ERRNO err, int subtract) => new(err.ErrorCode - subtract); - public static ERRNO operator -(ERRNO err, nint subtract) => new(err.ErrorCode - subtract); - - public static bool operator >(ERRNO err, ERRNO other) => err.ErrorCode > other.ErrorCode; - public static bool operator <(ERRNO err, ERRNO other) => err.ErrorCode < other.ErrorCode; - public static bool operator >=(ERRNO err, ERRNO other) => err.ErrorCode >= other.ErrorCode; - public static bool operator <=(ERRNO err, ERRNO other) => err.ErrorCode <= other.ErrorCode; - - public static bool operator >(ERRNO err, int other) => err.ErrorCode > other; - public static bool operator <(ERRNO err, int other) => err.ErrorCode < other; - public static bool operator >=(ERRNO err, int other) => err.ErrorCode >= other; - public static bool operator <=(ERRNO err, int other) => err.ErrorCode <= other; - - public static bool operator >(ERRNO err, nint other) => err.ErrorCode > other; - public static bool operator <(ERRNO err, nint other) => err.ErrorCode < other; - public static bool operator >=(ERRNO err, nint other) => err.ErrorCode >= other; - public static bool operator <=(ERRNO err, nint other) => err.ErrorCode <= other; - - public static bool operator ==(ERRNO err, ERRNO other) => err.ErrorCode == other.ErrorCode; - public static bool operator !=(ERRNO err, ERRNO other) => err.ErrorCode != other.ErrorCode; - public static bool operator ==(ERRNO err, int other) => err.ErrorCode == other; - public static bool operator !=(ERRNO err, int other) => err.ErrorCode != other; - public static bool operator ==(ERRNO err, nint other) => err.ErrorCode == other; - public static bool operator !=(ERRNO err, nint other) => err.ErrorCode != other; - - public readonly bool Equals(ERRNO other) => ErrorCode == other.ErrorCode; - public readonly override bool Equals(object obj) => obj is ERRNO other && Equals(other); - public readonly override int GetHashCode() => ErrorCode.GetHashCode(); - - /// <summary> - /// The integer error value of the current instance in radix 10 - /// </summary> - /// <returns></returns> - public readonly override string ToString() - { - //Return the string of the error code number - return ErrorCode.ToString(); - } - public readonly string ToString(string format) - { - //Return the string of the error code number - return ErrorCode.ToString(format); - } - - ///<inheritdoc/> - public readonly bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider provider) - { - return ErrorCode.TryFormat(destination, out charsWritten, format, provider); - } - ///<inheritdoc/> - public readonly string ToString(string format, IFormatProvider formatProvider) - { - return ErrorCode.ToString(format, formatProvider); - } - } -} diff --git a/Utils/src/Extensions/CacheExtensions.cs b/Utils/src/Extensions/CacheExtensions.cs deleted file mode 100644 index 5485c2d..0000000 --- a/Utils/src/Extensions/CacheExtensions.cs +++ /dev/null @@ -1,348 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: CacheExtensions.cs -* -* CacheExtensions.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.Linq; -using System.Collections.Generic; - -using VNLib.Utils.Memory.Caching; - -namespace VNLib.Utils.Extensions -{ - /// <summary> - /// Cache collection extensions - /// </summary> - public static class CacheExtensions - { - /// <summary> - /// <para> - /// Stores a new record. If an old record exists, the records are compared, - /// if they are not equal, the old record is evicted and the new record is stored - /// </para> - /// </summary> - /// <typeparam name="TKey"></typeparam> - /// <typeparam name="T">A cachable object</typeparam> - /// <param name="store"></param> - /// <param name="key">The unique key identifying the record</param> - /// <param name="record">The record to store</param> - /// <remarks> - /// Locks on the store parameter to provide mutual exclusion for non thread-safe - /// data structures. - /// </remarks> - public static void StoreRecord<TKey, T>(this IDictionary<TKey, T> store, TKey key, T record) where T : ICacheable - { - T ?oldRecord = default; - lock (store) - { - //See if an old record exists - if (!store.Remove(key, out oldRecord) || oldRecord == null) - { - //Old record doesnt exist, store and return - store[key] = record; - return; - } - //See if the old and new records and the same record - if (oldRecord.Equals(record)) - { - //records are equal, so we can exit - return; - } - //Old record is not equal, so we can store the new record and evict the old on - store[key] = record; - } - //Call evict on the old record - oldRecord.Evicted(); - } - /// <summary> - /// <para> - /// Stores a new record and updates the expiration date. If an old record exists, the records - /// are compared, if they are not equal, the old record is evicted and the new record is stored - /// </para> - /// </summary> - /// <typeparam name="TKey"></typeparam> - /// <typeparam name="T">A cachable object</typeparam> - /// <param name="store"></param> - /// <param name="key">The unique key identifying the record</param> - /// <param name="record">The record to store</param> - /// <param name="validFor">The new expiration time of the record</param> - /// <remarks> - /// Locks on the store parameter to provide mutual exclusion for non thread-safe - /// data structures. - /// </remarks> - public static void StoreRecord<TKey, T>(this IDictionary<TKey, T> store, TKey key, T record, TimeSpan validFor) where T : ICacheable - { - //Update the expiration time - record.Expires = DateTime.UtcNow.Add(validFor); - //Store - StoreRecord(store, key, record); - } - /// <summary> - /// <para> - /// Returns a stored record if it exists and is not expired. If the record exists - /// but has expired, it is evicted. - /// </para> - /// <para> - /// If a record is evicted, the return value evaluates to -1 and the value parameter - /// is set to the old record if the caller wished to inspect the record after the - /// eviction method completes - /// </para> - /// </summary> - /// <typeparam name="TKey"></typeparam> - /// <typeparam name="T">A cachable object</typeparam> - /// <param name="store"></param> - /// <param name="key"></param> - /// <param name="value">The record</param> - /// <returns> - /// Gets a value indicating the reults of the operation. 0 if the record is not found, -1 if expired, 1 if - /// record is valid - /// </returns> - /// <remarks> - /// Locks on the store parameter to provide mutual exclusion for non thread-safe - /// data structures. - /// </remarks> - public static ERRNO TryGetOrEvictRecord<TKey, T>(this IDictionary<TKey, T> store, TKey key, out T? value) where T : ICacheable - { - value = default; - //Cache current date time before entering the lock - DateTime now = DateTime.UtcNow; - //Get value - lock (store) - { - //try to get the value - if (!store.TryGetValue(key, out value)) - { - //not found - return 0; - } - //Not expired - if (value.Expires > now) - { - return true; - } - //Remove from store - _ = store.Remove(key); - } - //Call the evict func - value.Evicted(); - return -1; - } - /// <summary> - /// Updates the expiration date on a record to the specified time if it exists, regardless - /// of its validity - /// </summary> - /// <typeparam name="TKey">Diction key type</typeparam> - /// <typeparam name="T">A cachable object</typeparam> - /// <param name="store"></param> - /// <param name="key">The unique key identifying the record to update</param> - /// <param name="extendedTime">The expiration time (time added to <see cref="DateTime.UtcNow"/>)</param> - /// <remarks> - /// Locks on the store parameter to provide mutual exclusion for non thread-safe - /// data structures. - /// </remarks> - public static void UpdateRecord<TKey, T>(this IDictionary<TKey, T> store, TKey key, TimeSpan extendedTime) where T : ICacheable - { - //Cacl the expiration time - DateTime expiration = DateTime.UtcNow.Add(extendedTime); - lock (store) - { - //Update the expiration time if the record exists - if (store.TryGetValue(key, out T? record) && record != null) - { - record.Expires = expiration; - } - } - } - /// <summary> - /// Evicts a stored record from the store. If the record is found, the eviction - /// method is executed - /// </summary> - /// <typeparam name="TKey"></typeparam> - /// <typeparam name="T"></typeparam> - /// <param name="store"></param> - /// <param name="key">The unique key identifying the record</param> - /// <returns>True if the record was found and evicted</returns> - public static bool EvictRecord<TKey, T>(this IDictionary<TKey, T> store, TKey key) where T : ICacheable - { - T? record = default; - lock (store) - { - //Try to remove the record - if (!store.Remove(key, out record) || record == null) - { - //No record found or null - return false; - } - } - //Call eviction mode - record.Evicted(); - return true; - } - /// <summary> - /// Evicts all expired records from the store - /// </summary> - /// <typeparam name="TKey"></typeparam> - /// <typeparam name="T"></typeparam> - public static void CollectRecords<TKey, T>(this IDictionary<TKey, T> store) where T : ICacheable - { - CollectRecords(store, DateTime.UtcNow); - } - - /// <summary> - /// Evicts all expired records from the store - /// </summary> - /// <typeparam name="TKey"></typeparam> - /// <typeparam name="T"></typeparam> - /// <param name="store"></param> - /// <param name="validAfter">A time that specifies the time which expired records should be evicted</param> - public static void CollectRecords<TKey, T>(this IDictionary<TKey, T> store, DateTime validAfter) where T : ICacheable - { - //Build a query to get the keys that belong to the expired records - IEnumerable<KeyValuePair<TKey, T>> expired = store.Where(s => s.Value.Expires < validAfter); - //temp list for expired records - IEnumerable<T> evicted; - //Take lock on store - lock (store) - { - KeyValuePair<TKey, T>[] kvp = expired.ToArray(); - //enumerate to array so values can be removed while the lock is being held - foreach (KeyValuePair<TKey, T> pair in kvp) - { - //remove the record and call the eviction method - _ = store.Remove(pair); - } - //select values while lock held - evicted = kvp.Select(static v => v.Value); - } - //Iterrate over evicted records and call evicted method - foreach (T ev in evicted) - { - ev.Evicted(); - } - } - - /// <summary> - /// Allows for mutually exclusive use of a <see cref="ICacheable"/> record with a - /// state parameter - /// </summary> - /// <typeparam name="TKey"></typeparam> - /// <typeparam name="T"></typeparam> - /// <typeparam name="State"></typeparam> - /// <param name="store"></param> - /// <param name="key">The unique key identifying the record</param> - /// <param name="state">A user-token type state parameter to pass to the use callback method</param> - /// <param name="useCtx">A callback method that will be passed the record to use within an exclusive context</param> - public static void UseRecord<TKey, T, State>(this IDictionary<TKey, T> store, TKey key, State state, Action<T, State> useCtx) where T: ICacheable - { - lock (store) - { - //If the record exists - if(store.TryGetValue(key, out T record)) - { - //Use it within the lock statement - useCtx(record, state); - } - } - } - /// <summary> - /// Allows for mutually exclusive use of a <see cref="ICacheable"/> - /// </summary> - /// <typeparam name="TKey"></typeparam> - /// <typeparam name="T"></typeparam> - /// <param name="store"></param> - /// <param name="key">The unique key identifying the record</param> - /// <param name="useCtx">A callback method that will be passed the record to use within an exclusive context</param> - public static void UseRecord<TKey, T>(this IDictionary<TKey, T> store, TKey key, Action<T> useCtx) where T : ICacheable - { - lock (store) - { - //If the record exists - if (store.TryGetValue(key, out T record)) - { - //Use it within the lock statement - useCtx(record); - } - } - } - /// <summary> - /// Allows for mutually exclusive use of a <see cref="ICacheable"/> record with a - /// state parameter, only if the found record is valid - /// </summary> - /// <typeparam name="TKey"></typeparam> - /// <typeparam name="T"></typeparam> - /// <typeparam name="State"></typeparam> - /// <param name="store"></param> - /// <param name="key">The unique key identifying the record</param> - /// <param name="state">A user-token type state parameter to pass to the use callback method</param> - /// <param name="useCtx">A callback method that will be passed the record to use within an exclusive context</param> - /// <remarks>If the record is found, but is expired, the record is evicted from the store. The callback is never invoked</remarks> - public static void UseIfValid<TKey, T, State>(this IDictionary<TKey, T> store, TKey key, State state, Action<T, State> useCtx) where T : ICacheable - { - DateTime now = DateTime.UtcNow; - T? record; - lock (store) - { - //If the record exists, check if its valid - if (store.TryGetValue(key, out record) && record.Expires < now) - { - //Use it within the lock statement - useCtx(record, state); - return; - } - //Record is no longer valid - _ = store.Remove(key); - } - //Call evicted method - record?.Evicted(); - } - /// <summary> - /// Allows for mutually exclusive use of a <see cref="ICacheable"/> record with a - /// state parameter, only if the found record is valid - /// </summary> - /// <typeparam name="TKey"></typeparam> - /// <typeparam name="T"></typeparam> - /// <param name="store"></param> - /// <param name="key">The unique key identifying the record</param> - /// <param name="useCtx">A callback method that will be passed the record to use within an exclusive context</param> - /// <remarks>If the record is found, but is expired, the record is evicted from the store. The callback is never invoked</remarks> - public static void UseIfValid<TKey, T>(this IDictionary<TKey, T> store, TKey key, Action<T> useCtx) where T : ICacheable - { - DateTime now = DateTime.UtcNow; - T? record; - lock (store) - { - //If the record exists, check if its valid - if (store.TryGetValue(key, out record) && record.Expires < now) - { - //Use it within the lock statement - useCtx(record); - return; - } - //Record is no longer valid - _ = store.Remove(key); - } - //Call evicted method - record?.Evicted(); - } - } -} diff --git a/Utils/src/Extensions/CollectionExtensions.cs b/Utils/src/Extensions/CollectionExtensions.cs deleted file mode 100644 index e4ec459..0000000 --- a/Utils/src/Extensions/CollectionExtensions.cs +++ /dev/null @@ -1,98 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: CollectionExtensions.cs -* -* CollectionExtensions.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.Collections.Generic; - -using VNLib.Utils.Memory; - -namespace VNLib.Utils.Extensions -{ - /// <summary> - /// Provides collection extension methods - /// </summary> - public static class CollectionExtensions - { - /// <summary> - /// Gets a previously-stored base32 encoded value-type from the lookup and returns its initialized structure from - /// the value stored - /// </summary> - /// <typeparam name="TKey">The key type used to index the lookup</typeparam> - /// <typeparam name="TValue">An unmanaged structure type</typeparam> - /// <param name="lookup"></param> - /// <param name="key">The key used to identify the value</param> - /// <returns>The initialized structure, or default if the lookup returns null/empty string</returns> - public static TValue GetValueType<TKey, TValue>(this IIndexable<TKey, string> lookup, TKey key) where TValue : unmanaged where TKey : notnull - { - //Get value - string value = lookup[key]; - //If the string is set, recover the value and return it - return string.IsNullOrWhiteSpace(value) ? default : VnEncoding.FromBase32String<TValue>(value); - } - - /// <summary> - /// Serializes a value-type in base32 encoding and stores it at the specified key - /// </summary> - /// <typeparam name="TKey">The key type used to index the lookup</typeparam> - /// <typeparam name="TValue">An unmanaged structure type</typeparam> - /// <param name="lookup"></param> - /// <param name="key">The key used to identify the value</param> - /// <param name="value">The value to serialze</param> - public static void SetValueType<TKey, TValue>(this IIndexable<TKey, string> lookup, TKey key, TValue value) where TValue : unmanaged where TKey : notnull - { - //encode string from value type and store in lookup - lookup[key] = VnEncoding.ToBase32String(value); - } - /// <summary> - /// Executes a handler delegate on every element of the list within a try-catch block - /// and rethrows exceptions as an <see cref="AggregateException"/> - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="list"></param> - /// <param name="handler">An <see cref="Action"/> handler delegate to complete some operation on the elements within the list</param> - /// <exception cref="AggregateException"></exception> - public static void TryForeach<T>(this IEnumerable<T> list, Action<T> handler) - { - List<Exception>? exceptionList = null; - foreach(T item in list) - { - try - { - handler(item); - } - catch(Exception ex) - { - //Init new list and add the exception - exceptionList ??= new(); - exceptionList.Add(ex); - } - } - //Raise aggregate exception for all caught exceptions - if(exceptionList?.Count > 0) - { - throw new AggregateException(exceptionList); - } - } - } -} diff --git a/Utils/src/Extensions/IoExtensions.cs b/Utils/src/Extensions/IoExtensions.cs deleted file mode 100644 index f312203..0000000 --- a/Utils/src/Extensions/IoExtensions.cs +++ /dev/null @@ -1,345 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IoExtensions.cs -* -* IoExtensions.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 System.Runtime.Versioning; -using System.Runtime.CompilerServices; - -using VNLib.Utils.IO; -using VNLib.Utils.Memory; - -using static VNLib.Utils.Memory.Memory; - -namespace VNLib.Utils.Extensions -{ - /// <summary> - /// Provieds extension methods for common IO operations - /// </summary> - public static class IoExtensions - { - /// <summary> - /// Unlocks the entire file - /// </summary> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [UnsupportedOSPlatform("ios")] - [UnsupportedOSPlatform("macos")] - [UnsupportedOSPlatform("tvos")] - public static void Unlock(this FileStream fs) - { - _ = fs ?? throw new ArgumentNullException(nameof(fs)); - //Unlock the entire file - fs.Unlock(0, fs.Length); - } - - /// <summary> - /// Locks the entire file - /// </summary> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [UnsupportedOSPlatform("ios")] - [UnsupportedOSPlatform("macos")] - [UnsupportedOSPlatform("tvos")] - public static void Lock(this FileStream fs) - { - _ = fs ?? throw new ArgumentNullException(nameof(fs)); - //Lock the entire length of the file - fs.Lock(0, fs.Length); - } - - /// <summary> - /// Provides an async wrapper for copying data from the current stream to another using an unmanged - /// buffer. - /// </summary> - /// <param name="source"></param> - /// <param name="dest">The destination data stream to write data to</param> - /// <param name="bufferSize">The size of the buffer to use while copying data. (Value will be clamped to the size of the stream if seeking is available)</param> - /// <param name="heap">The <see cref="IUnmangedHeap"/> to allocate the buffer from</param> - /// <param name="token">A token that may cancel asynchronous operations</param> - /// <returns>A <see cref="ValueTask"/> that completes when the copy operation has completed</returns> - /// <exception cref="IOException"></exception> - /// <exception cref="ArgumentException"></exception> - public static async ValueTask CopyToAsync(this Stream source, Stream dest, int bufferSize, IUnmangedHeap heap, CancellationToken token = default) - { - if (source.CanSeek) - { - bufferSize = (int)Math.Min(source.Length, bufferSize); - } - //Alloc a buffer - using IMemoryOwner<byte> buffer = heap.DirectAlloc<byte>(bufferSize); - //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"/> - /// </summary> - /// <param name="source"></param> - /// <param name="dest">The destination data stream to write data to</param> - /// <param name="bufferSize">The size of the buffer to use while copying data. (Value will be clamped to the size of the stream if seeking is available)</param> - /// <param name="count">The number of bytes to copy from the current stream to destination stream</param> - /// <param name="heap">The heap to alloc buffer from</param> - /// <param name="token">A token that may cancel asynchronous operations</param> - /// <returns>A <see cref="ValueTask"/> that completes when the copy operation has completed</returns> - /// <exception cref="IOException"></exception> - /// <exception cref="ArgumentException"></exception> - public static async ValueTask CopyToAsync(this Stream source, Stream dest, long count, int bufferSize, IUnmangedHeap heap, CancellationToken token = default) - { - if (source.CanSeek) - { - bufferSize = (int)Math.Min(source.Length, bufferSize); - } - //Alloc a buffer - using IMemoryOwner<byte> buffer = heap.DirectAlloc<byte>(bufferSize); - //Wait for copy to complete - await CopyToAsync(source, dest, buffer.Memory, count, token); - } - - /// <summary> - /// Copies data from one stream to another, using self managed buffers. May allocate up to 2MB. - /// </summary> - /// <param name="source">Source stream to read from</param> - /// <param name="dest">Destination stream to write data to</param> - /// <param name="heap">The heap to allocate buffers from</param> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="ArgumentNullException"></exception> - public static void CopyTo(this Stream source, Stream dest, IUnmangedHeap? heap = null) - { - if (!source.CanRead) - { - throw new ArgumentException("Source stream is unreadable", nameof(source)); - } - if (!dest.CanWrite) - { - throw new ArgumentException("Destination stream is unwritable", nameof(dest)); - } - heap ??= Shared; - //Get a buffer size, maximum of 2mb buffer size if the stream supports seeking, otherwise, min buf size - int bufSize = source.CanSeek ? (int)Math.Min(source.Length, MAX_BUF_SIZE) : MIN_BUF_SIZE; - //Length must be 0, so return - if (bufSize == 0) - { - return; - } - //Alloc a buffer - using UnsafeMemoryHandle<byte> buffer = heap.UnsafeAlloc<byte>(bufSize); - int read; - do - { - //read - read = source.Read(buffer.Span); - //Guard - if (read == 0) - { - break; - } - //write only the data that was read (slice) - 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> - /// <param name="source">Source stream to read from</param> - /// <param name="dest">Destination stream to write data to</param> - /// <param name="count">Number of bytes to read/write</param> - /// <param name="heap">The heap to allocate buffers from</param> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="ArgumentNullException"></exception> - public static void CopyTo(this Stream source, Stream dest, long count, IUnmangedHeap? heap = null) - { - if (!source.CanRead) - { - throw new ArgumentException("Source stream is unreadable", nameof(source)); - } - if (!dest.CanWrite) - { - throw new ArgumentException("Destination stream is unwritable", nameof(dest)); - } - //Set default heap - heap ??= Shared; - //Get a buffer size, maximum of 2mb buffer size if the stream supports seeking, otherwise, min buf size - int bufSize = source.CanSeek ? (int)Math.Min(source.Length, MAX_BUF_SIZE) : MIN_BUF_SIZE; - //Length must be 0, so return - if (bufSize == 0) - { - return; - } - //Alloc a buffer - using UnsafeMemoryHandle<byte> buffer = heap.UnsafeAlloc<byte>(bufSize); - //wrapper around offset pointer - long total = 0; - int read; - do - { - Span<byte> wrapper = buffer.Span[..(int)Math.Min(bufSize, (count - total))]; - //read - read = source.Read(wrapper); - //Guard - if (read == 0) - { - break; - } - //write only the data that was read (slice) - dest.Write(wrapper[..read]); - //Update total - total += read; - } while (true); - } - - /// <summary> - /// Copies data from the current stream to the destination stream using the supplied memory buffer - /// </summary> - /// <param name="source"></param> - /// <param name="dest">The destination data stream to write data to</param> - /// <param name="buffer">The buffer to use when copying data</param> - /// <param name="token">A token that may cancel asynchronous operations</param> - /// <returns>A <see cref="ValueTask"/> that completes when the copy operation has completed</returns> - /// <exception cref="ArgumentException"></exception> - public static async ValueTask CopyToAsync(this Stream source, Stream dest, Memory<byte> buffer, CancellationToken token = default) - { - //Make sure source can be read from, and dest can be written to - if (!source.CanRead) - { - throw new ArgumentException("Source stream is unreadable", nameof(source)); - } - if (!dest.CanWrite) - { - throw new ArgumentException("Destination stream is unwritable", nameof(dest)); - } - //Read in loop - int read; - while (true) - { - //read - read = await source.ReadAsync(buffer, token); - //Guard - if (read == 0) - { - break; - } - //write only the data that was read (slice) - await dest.WriteAsync(buffer[..read], token); - } - } - - /// <summary> - /// Copies data from the current stream to the destination stream using the supplied memory buffer - /// </summary> - /// <param name="source"></param> - /// <param name="dest">The destination data stream to write data to</param> - /// <param name="buffer">The buffer to use when copying data</param> - /// <param name="count">The number of bytes to copy from the current stream to destination stream</param> - /// <param name="token">A token that may cancel asynchronous operations</param> - /// <returns>A <see cref="ValueTask"/> that completes when the copy operation has completed</returns> - /// <exception cref="ArgumentException"></exception> - public static async ValueTask CopyToAsync(this Stream source, Stream dest, Memory<byte> buffer, long count, CancellationToken token = default) - { - //Make sure source can be read from, and dest can be written to - if (!source.CanRead) - { - throw new ArgumentException("Source stream is unreadable", nameof(source)); - } - if (!dest.CanWrite) - { - throw new ArgumentException("Destination stream is unwritable", nameof(dest)); - } - /* - * Track total count so we copy the exect number of - * bytes from the source - */ - long total = 0; - int bufferSize = buffer.Length; - int read; - while (true) - { - //get offset wrapper of the total buffer or remaining count - Memory<byte> offset = buffer[..(int)Math.Min(bufferSize, count - total)]; - //read - read = await source.ReadAsync(offset, token); - //Guard - if (read == 0) - { - break; - } - //write only the data that was read (slice) - await dest.WriteAsync(offset[..read], token); - //Update total - total += read; - } - } - - /// <summary> - /// Opens a file within the current directory - /// </summary> - /// <param name="dir"></param> - /// <param name="fileName">The name of the file to open</param> - /// <param name="mode">The <see cref="FileMode"/> to open the file with</param> - /// <param name="access">The <see cref="FileAccess"/> to open the file with</param> - /// <param name="share"></param> - /// <param name="bufferSize">The size of the buffer to read/write with</param> - /// <param name="options"></param> - /// <returns>The <see cref="FileStream"/> of the opened file</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static FileStream OpenFile(this DirectoryInfo dir, - string fileName, - FileMode mode, - FileAccess access, - FileShare share = FileShare.None, - int bufferSize = 4096, - FileOptions options = FileOptions.None) - { - _ = dir ?? throw new ArgumentNullException(nameof(dir)); - string fullPath = Path.Combine(dir.FullName, fileName); - return new FileStream(fullPath, mode, access, share, bufferSize, options); - } - /// <summary> - /// Deletes the speicifed file from the current directory - /// </summary> - /// <param name="dir"></param> - /// <param name="fileName">The name of the file to delete</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void DeleteFile(this DirectoryInfo dir, string fileName) - { - _ = dir ?? throw new ArgumentNullException(nameof(dir)); - string fullPath = Path.Combine(dir.FullName, fileName); - File.Delete(fullPath); - } - /// <summary> - /// Determines if a file exists within the current directory - /// </summary> - /// <param name="dir"></param> - /// <param name="fileName">The name of the file to search for</param> - /// <returns>True if the file is found and the user has permission to access the file, false otherwise</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool FileExists(this DirectoryInfo dir, string fileName) - { - _ = dir ?? throw new ArgumentNullException(nameof(dir)); - string fullPath = Path.Combine(dir.FullName, fileName); - return FileOperations.FileExists(fullPath); - } - } -}
\ No newline at end of file diff --git a/Utils/src/Extensions/JsonExtensions.cs b/Utils/src/Extensions/JsonExtensions.cs deleted file mode 100644 index a27dcc0..0000000 --- a/Utils/src/Extensions/JsonExtensions.cs +++ /dev/null @@ -1,215 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: JsonExtensions.cs -* -* JsonExtensions.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.Text.Json; -using System.Collections.Generic; - -using VNLib.Utils.IO; - -namespace VNLib.Utils.Extensions -{ - /// <summary> - /// Specifies how to parse a timespan value from a <see cref="JsonDocument"/> element - /// </summary> - public enum TimeParseType - { - Milliseconds, - Seconds, - Minutes, - Hours, - Days, - Ticks - } - - public static class JsonExtensions - { - /// <summary> - /// Converts a JSON encoded string to an object of the specified type - /// </summary> - /// <typeparam name="T">Output type of the object</typeparam> - /// <param name="value"></param> - /// <param name="options"><see cref="JsonSerializerOptions"/> to use during de-serialization</param> - /// <returns>The new object or default if the string is null or empty</returns> - /// <exception cref="JsonException"></exception> - /// <exception cref="NotSupportedException"></exception> - public static T? AsJsonObject<T>(this string value, JsonSerializerOptions? options = null) - { - return !string.IsNullOrWhiteSpace(value) ? JsonSerializer.Deserialize<T>(value, options) : default; - } - /// <summary> - /// Converts a JSON encoded binary data to an object of the specified type - /// </summary> - /// <typeparam name="T">Output type of the object</typeparam> - /// <param name="utf8bin"></param> - /// <param name="options"><see cref="JsonSerializerOptions"/> to use during de-serialization</param> - /// <returns>The new object or default if the string is null or empty</returns> - /// <exception cref="JsonException"></exception> - /// <exception cref="NotSupportedException"></exception> - public static T? AsJsonObject<T>(this in ReadOnlySpan<byte> utf8bin, JsonSerializerOptions? options = null) - { - return utf8bin.IsEmpty ? default : JsonSerializer.Deserialize<T>(utf8bin, options); - } - /// <summary> - /// Converts a JSON encoded binary data to an object of the specified type - /// </summary> - /// <typeparam name="T">Output type of the object</typeparam> - /// <param name="utf8bin"></param> - /// <param name="options"><see cref="JsonSerializerOptions"/> to use during de-serialization</param> - /// <returns>The new object or default if the string is null or empty</returns> - /// <exception cref="JsonException"></exception> - /// <exception cref="NotSupportedException"></exception> - public static T? AsJsonObject<T>(this in ReadOnlyMemory<byte> utf8bin, JsonSerializerOptions? options = null) - { - return utf8bin.IsEmpty ? default : JsonSerializer.Deserialize<T>(utf8bin.Span, options); - } - /// <summary> - /// Converts a JSON encoded binary data to an object of the specified type - /// </summary> - /// <typeparam name="T">Output type of the object</typeparam> - /// <param name="utf8bin"></param> - /// <param name="options"><see cref="JsonSerializerOptions"/> to use during de-serialization</param> - /// <returns>The new object or default if the string is null or empty</returns> - /// <exception cref="JsonException"></exception> - /// <exception cref="NotSupportedException"></exception> - public static T? AsJsonObject<T>(this byte[] utf8bin, JsonSerializerOptions? options = null) - { - return utf8bin == null ? default : JsonSerializer.Deserialize<T>(utf8bin.AsSpan(), options); - } - /// <summary> - /// Parses a json encoded string to a json documen - /// </summary> - /// <param name="jsonString"></param> - /// <param name="options"></param> - /// <returns>If the json string is null, returns null, otherwise the json document around the data</returns> - /// <exception cref="JsonException"></exception> - public static JsonDocument? AsJsonDocument(this string jsonString, JsonDocumentOptions options = default) - { - return jsonString == null ? null : JsonDocument.Parse(jsonString, options); - } - /// <summary> - /// Shortcut extension to <see cref="JsonElement.GetProperty(string)"/> and returns a string - /// </summary> - /// <param name="element"></param> - /// <param name="propertyName">The name of the property to get the string value of</param> - /// <returns>If the property exists, returns the string stored at that property</returns> - public static string? GetPropString(this in JsonElement element, string propertyName) - { - return element.TryGetProperty(propertyName, out JsonElement el) ? el.GetString() : null; - } - /// <summary> - /// Shortcut extension to <see cref="JsonElement.GetProperty(string)"/> and returns a string - /// </summary> - /// <param name="conf"></param> - /// <param name="propertyName">The name of the property to get the string value of</param> - /// <returns>If the property exists, returns the string stored at that property</returns> - public static string? GetPropString(this IReadOnlyDictionary<string, JsonElement> conf, string propertyName) - { - return conf.TryGetValue(propertyName, out JsonElement el) ? el.GetString() : null; - } - - /// <summary> - /// Shortcut extension to <see cref="JsonElement.GetProperty(string)"/> and returns a string - /// </summary> - /// <param name="conf"></param> - /// <param name="propertyName">The name of the property to get the string value of</param> - /// <returns>If the property exists, returns the string stored at that property</returns> - public static string? GetPropString(this IDictionary<string, JsonElement> conf, string propertyName) - { - return conf.TryGetValue(propertyName, out JsonElement el) ? el.GetString() : null; - } - - /// <summary> - /// Attemts to serialze an object to a JSON encoded string - /// </summary> - /// <param name="obj"></param> - /// <param name="options"><see cref="JsonSerializerOptions"/> to use during serialization</param> - /// <returns>A JSON encoded string of the serialized object, or null if the object is null</returns> - /// <exception cref="NotSupportedException"></exception> - public static string? ToJsonString<T>(this T obj, JsonSerializerOptions? options = null) - { - return obj == null ? null : JsonSerializer.Serialize(obj, options); - } - - /// <summary> - /// Merges the current <see cref="JsonDocument"/> with another <see cref="JsonDocument"/> to - /// create a new document of combined properties - /// </summary> - /// <param name="initial"></param> - /// <param name="other">The <see cref="JsonDocument"/> to combine with the first document</param> - /// <param name="initalName">The name of the new element containing the initial document data</param> - /// <param name="secondName">The name of the new element containing the additional document data</param> - /// <returns>A new document with a parent root containing the combined objects</returns> - public static JsonDocument Merge(this JsonDocument initial, JsonDocument other, string initalName, string secondName) - { - //Open a new memory buffer - using VnMemoryStream ms = new(); - //Encapuslate the memory stream in a writer - using (Utf8JsonWriter writer = new(ms)) - { - //Write the starting - writer.WriteStartObject(); - //Write the first object name - writer.WritePropertyName(initalName); - //Write the inital docuemnt to the stream - initial.WriteTo(writer); - //Write the second object property - writer.WritePropertyName(secondName); - //Write the merging document to the stream - other.WriteTo(writer); - //End the parent element - writer.WriteEndObject(); - } - //rewind the buffer - _ = ms.Seek(0, System.IO.SeekOrigin.Begin); - //Parse the stream into the new document and return it - return JsonDocument.Parse(ms); - } - - /// <summary> - /// Parses a number value into a <see cref="TimeSpan"/> of the specified time - /// </summary> - /// <param name="el"></param> - /// <param name="type">The <see cref="TimeParseType"/> the value represents</param> - /// <returns>The <see cref="TimeSpan"/> of the value</returns> - /// <exception cref="FormatException"></exception> - /// <exception cref="OverflowException"></exception> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="NotSupportedException"></exception> - /// <exception cref="InvalidOperationException"></exception> - public static TimeSpan GetTimeSpan(this in JsonElement el, TimeParseType type) - { - return type switch - { - TimeParseType.Milliseconds => TimeSpan.FromMilliseconds(el.GetDouble()), - TimeParseType.Seconds => TimeSpan.FromSeconds(el.GetDouble()), - TimeParseType.Minutes => TimeSpan.FromMinutes(el.GetDouble()), - TimeParseType.Hours => TimeSpan.FromHours(el.GetDouble()), - TimeParseType.Days => TimeSpan.FromDays(el.GetDouble()), - TimeParseType.Ticks => TimeSpan.FromTicks(el.GetInt64()), - _ => throw new NotSupportedException(), - }; - } - } -}
\ No newline at end of file diff --git a/Utils/src/Extensions/MemoryExtensions.cs b/Utils/src/Extensions/MemoryExtensions.cs deleted file mode 100644 index c8ee5ef..0000000 --- a/Utils/src/Extensions/MemoryExtensions.cs +++ /dev/null @@ -1,769 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: MemoryExtensions.cs -* -* MemoryExtensions.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.Text; -using System.Buffers; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -namespace VNLib.Utils.Extensions -{ - using Utils.Memory; - using VNLib.Utils.Resources; - - /// <summary> - /// Provides memory based extensions to .NET and VNLib memory abstractions - /// </summary> - public static class MemoryExtensions - { - /// <summary> - /// Rents a new array and stores it as a resource within an <see cref="OpenResourceHandle{T}"/> to return the - /// array when work is completed - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="pool"></param> - /// <param name="size">The minimum size array to allocate</param> - /// <param name="zero">Should elements from 0 to size be set to default(T)</param> - /// <returns>A new <see cref="OpenResourceHandle{T}"/> encapsulating the rented array</returns> - public static UnsafeMemoryHandle<T> Lease<T>(this ArrayPool<T> pool, int size, bool zero = false) where T: unmanaged - { - //Pool buffer handles are considered "safe" so im reusing code for now - return new(pool, size, zero); - } - - /// <summary> - /// Retreives a buffer that is at least the reqested length, and clears the array from 0-size. - /// <br></br> - /// The array may be larger than the requested size, and the entire buffer is zeroed - /// </summary> - /// <param name="pool"></param> - /// <param name="size">The minimum length of the array</param> - /// <param name="zero">True if contents should be zeroed</param> - /// <returns>The zeroed array</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T[] Rent<T>(this ArrayPool<T> pool, int size, bool zero) - { - //Rent the array - T[] arr = pool.Rent(size); - //If zero flag is set, zero only the used section - if (zero) - { - Array.Fill(arr, default); - } - return arr; - } - - /// <summary> - /// Copies the characters within the memory handle to a <see cref="string"/> - /// </summary> - /// <returns>The string representation of the buffer</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string ToString<T>(this T charBuffer) where T: IMemoryHandle<char> - { - return charBuffer.Span.ToString(); - } - - /// <summary> - /// Wraps the <see cref="MemoryHandle{T}"/> instance in System.Buffers.MemoryManager - /// wrapper to provide <see cref="Memory{T}"/> buffers from umanaged handles. - /// </summary> - /// <typeparam name="T">The unmanaged data type</typeparam> - /// <param name="handle"></param> - /// <param name="ownsHandle"> - /// A value that indicates if the new <see cref="MemoryManager{T}"/> owns the handle. - /// When <c>true</c>, the new <see cref="MemoryManager{T}"/> maintains the lifetime of the handle. - /// </param> - /// <returns>The <see cref="MemoryManager{T}"/> wrapper</returns> - /// <remarks>NOTE: This wrapper now manages the lifetime of the current handle</remarks> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryManager<T> ToMemoryManager<T>(this MemoryHandle<T> handle, bool ownsHandle = true) where T : unmanaged - { - _ = handle ?? throw new ArgumentNullException(nameof(handle)); - return new SysBufferMemoryManager<T>(handle, ownsHandle); - } - - /// <summary> - /// Wraps the <see cref="VnTempBuffer{T}"/> instance in System.Buffers.MemoryManager - /// wrapper to provide <see cref="Memory{T}"/> buffers from umanaged handles. - /// </summary> - /// <typeparam name="T">The unmanaged data type</typeparam> - /// <param name="handle"></param> - /// <param name="ownsHandle"> - /// A value that indicates if the new <see cref="MemoryManager{T}"/> owns the handle. - /// When <c>true</c>, the new <see cref="MemoryManager{T}"/> maintains the lifetime of the handle. - /// </param> - /// <returns>The <see cref="MemoryManager{T}"/> wrapper</returns> - /// <remarks>NOTE: This wrapper now manages the lifetime of the current handle</remarks> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryManager<T> ToMemoryManager<T>(this VnTempBuffer<T> handle, bool ownsHandle = true) where T : unmanaged - { - _ = handle ?? throw new ArgumentNullException(nameof(handle)); - return new SysBufferMemoryManager<T>(handle, ownsHandle); - } - - /// <summary> - /// Allows direct allocation of a fixed size <see cref="MemoryManager{T}"/> from a <see cref="PrivateHeap"/> instance - /// of the specified number of elements - /// </summary> - /// <typeparam name="T">The unmanaged data type</typeparam> - /// <param name="heap"></param> - /// <param name="size">The number of elements to allocate on the heap</param> - /// <param name="zero">Optionally zeros conents of the block when allocated</param> - /// <returns>The <see cref="MemoryManager{T}"/> wrapper around the block of memory</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryManager<T> DirectAlloc<T>(this IUnmangedHeap heap, ulong size, bool zero = false) where T : unmanaged - { - return new SysBufferMemoryManager<T>(heap, size, zero); - } - - /// <summary> - /// Allows direct allocation of a fixed size <see cref="MemoryManager{T}"/> from a <see cref="PrivateHeap"/> instance - /// of the specified number of elements - /// </summary> - /// <typeparam name="T">The unmanaged data type</typeparam> - /// <param name="heap"></param> - /// <param name="size">The number of elements to allocate on the heap</param> - /// <param name="zero">Optionally zeros conents of the block when allocated</param> - /// <returns>The <see cref="MemoryManager{T}"/> wrapper around the block of memory</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryManager<T> DirectAlloc<T>(this IUnmangedHeap heap, long size, bool zero = false) where T : unmanaged - { - return size < 0 ? throw new ArgumentOutOfRangeException(nameof(size)) : DirectAlloc<T>(heap, (ulong)size, zero); - } - /// <summary> - /// Gets an offset pointer from the base postion to the number of bytes specified. Performs bounds checks - /// </summary> - /// <param name="memory"></param> - /// <param name="elements">Number of elements of type to offset</param> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - /// <returns><typeparamref name="T"/> pointer to the memory offset specified</returns> - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe T* GetOffset<T>(this MemoryHandle<T> memory, long elements) where T : unmanaged - { - return elements < 0 ? throw new ArgumentOutOfRangeException(nameof(elements)) : memory.GetOffset((ulong)elements); - } - /// <summary> - /// Resizes the current handle on the heap - /// </summary> - /// <param name="memory"></param> - /// <param name="elements">Positive number of elemnts the current handle should referrence</param> - /// <exception cref="OverflowException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Resize<T>(this MemoryHandle<T> memory, long elements) where T : unmanaged - { - if (elements < 0) - { - throw new ArgumentOutOfRangeException(nameof(elements)); - } - memory.Resize((ulong)elements); - } - - /// <summary> - /// Resizes the target handle only if the handle is smaller than the requested element count - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="handle"></param> - /// <param name="count">The number of elements to resize to</param> - /// <exception cref="OverflowException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ResizeIfSmaller<T>(this MemoryHandle<T> handle, long count) where T : unmanaged - { - if(count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - ResizeIfSmaller(handle, (ulong)count); - } - - /// <summary> - /// Resizes the target handle only if the handle is smaller than the requested element count - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="handle"></param> - /// <param name="count">The number of elements to resize to</param> - /// <exception cref="OverflowException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ResizeIfSmaller<T>(this MemoryHandle<T> handle, ulong count) where T : unmanaged - { - //Check handle size - if(handle.Length < count) - { - //handle too small, resize - handle.Resize(count); - } - } - -#if TARGET_64_BIT - /// <summary> - /// Gets a 64bit friendly span offset for the current <see cref="MemoryHandle{T}"/> - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="block"></param> - /// <param name="offset">The offset (in elements) from the begining of the block</param> - /// <param name="size">The size of the block (in elements)</param> - /// <returns>The offset span</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Span<T> GetOffsetSpan<T>(this MemoryHandle<T> block, ulong offset, int size) where T: unmanaged - { - _ = block ?? throw new ArgumentNullException(nameof(block)); - if(size < 0) - { - throw new ArgumentOutOfRangeException(nameof(size)); - } - if(size == 0) - { - return Span<T>.Empty; - } - //Make sure the offset size is within the size of the block - if(offset + (ulong)size <= block.Length) - { - //Get long offset from the destination handle - void* ofPtr = block.GetOffset(offset); - return new Span<T>(ofPtr, size); - } - throw new ArgumentOutOfRangeException(nameof(size)); - } - /// <summary> - /// Gets a 64bit friendly span offset for the current <see cref="MemoryHandle{T}"/> - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="block"></param> - /// <param name="offset">The offset (in elements) from the begining of the block</param> - /// <param name="size">The size of the block (in elements)</param> - /// <returns>The offset span</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Span<T> GetOffsetSpan<T>(this MemoryHandle<T> block, long offset, int size) where T : unmanaged - { - return offset < 0 ? throw new ArgumentOutOfRangeException(nameof(offset)) : block.GetOffsetSpan<T>((ulong)offset, size); - } - - - /// <summary> - /// Gets a <see cref="SubSequence{T}"/> window within the current block - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="block"></param> - /// <param name="offset">An offset within the handle</param> - /// <param name="size">The size of the window</param> - /// <returns>The new <see cref="SubSequence{T}"/> within the block</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SubSequence<T> GetSubSequence<T>(this MemoryHandle<T> block, ulong offset, int size) where T : unmanaged - { - return new SubSequence<T>(block, offset, size); - } -#else - - /// <summary> - /// Gets a <see cref="SubSequence{T}"/> window within the current block - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="block"></param> - /// <param name="offset">An offset within the handle</param> - /// <param name="size">The size of the window</param> - /// <returns>The new <see cref="SubSequence{T}"/> within the block</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SubSequence<T> GetSubSequence<T>(this MemoryHandle<T> block, int offset, int size) where T : unmanaged - { - return new SubSequence<T>(block, offset, size); - } - - /// <summary> - /// Gets a 64bit friendly span offset for the current <see cref="MemoryHandle{T}"/> - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="block"></param> - /// <param name="offset">The offset (in elements) from the begining of the block</param> - /// <param name="size">The size of the block (in elements)</param> - /// <returns>The offset span</returns> - /// <exception cref="OverflowException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe Span<T> GetOffsetSpan<T>(this MemoryHandle<T> block, long offset, int size) where T : unmanaged - { - //TODO fix 32bit/64 bit, this is a safe lazy workaround - return block.Span.Slice(checked((int) offset), size); - } -#endif - - /// <summary> - /// Wraps the current instance with a <see cref="MemoryPool{T}"/> wrapper - /// to allow System.Memory buffer rentals. - /// </summary> - /// <typeparam name="T">The unmanged data type to provide allocations from</typeparam> - /// <returns>The new <see cref="MemoryPool{T}"/> heap wrapper.</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryPool<T> ToPool<T>(this IUnmangedHeap heap) where T : unmanaged - { - return new PrivateBuffersMemoryPool<T>(heap); - } - - /// <summary> - /// Allocates a structure of the specified type on the current unmanged heap and zero's its memory - /// </summary> - /// <typeparam name="T">The structure type</typeparam> - /// <param name="heap"></param> - /// <returns>A pointer to the structure ready for use.</returns> - /// <remarks>Allocations must be freed with <see cref="StructFree{T}(IUnmangedHeap, T*)"/></remarks> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe T* StructAlloc<T>(this IUnmangedHeap heap) where T : unmanaged - { - //Allocate the struct on the heap and zero memory it points to - IntPtr handle = heap.Alloc(1, (uint)sizeof(T), true); - //returns the handle - return (T*)handle; - } - /// <summary> - /// Frees a structure at the specified address from the this heap. - /// This must be the same heap the structure was allocated from - /// </summary> - /// <typeparam name="T">The structure type</typeparam> - /// <param name="heap"></param> - /// <param name="structPtr">A pointer to the structure</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void StructFree<T>(this IUnmangedHeap heap, T* structPtr) where T : unmanaged - { - IntPtr block = new(structPtr); - //Free block from heap - heap.Free(ref block); - //Clear ref - *structPtr = default; - } - /// <summary> - /// Allocates a block of unmanaged memory of the number of elements to store of an unmanged type - /// </summary> - /// <typeparam name="T">Unmanaged data type to create a block of</typeparam> - /// <param name="heap"></param> - /// <param name="elements">The size of the block (number of elements)</param> - /// <param name="zero">A flag that zeros the allocated block before returned</param> - /// <returns>The unmanaged <see cref="MemoryHandle{T}"/></returns> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe MemoryHandle<T> Alloc<T>(this IUnmangedHeap heap, ulong elements, bool zero = false) where T : unmanaged - { - //Minimum of one element - elements = Math.Max(elements, 1); - //Get element size - uint elementSize = (uint)sizeof(T); - //If zero flag is set then specify zeroing memory - IntPtr block = heap.Alloc(elements, elementSize, zero); - //Return handle wrapper - return new MemoryHandle<T>(heap, block, elements, zero); - } - /// <summary> - /// Allocates a block of unmanaged memory of the number of elements to store of an unmanged type - /// </summary> - /// <typeparam name="T">Unmanaged data type to create a block of</typeparam> - /// <param name="heap"></param> - /// <param name="elements">The size of the block (number of elements)</param> - /// <param name="zero">A flag that zeros the allocated block before returned</param> - /// <returns>The unmanaged <see cref="MemoryHandle{T}"/></returns> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryHandle<T> Alloc<T>(this IUnmangedHeap heap, long elements, bool zero = false) where T : unmanaged - { - return elements < 0 ? throw new ArgumentOutOfRangeException(nameof(elements)) : Alloc<T>(heap, (ulong)elements, zero); - } - /// <summary> - /// Allocates a buffer from the current heap and initialzies it by copying the initial data buffer - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="heap"></param> - /// <param name="initialData">The initial data to set the buffer to</param> - /// <returns>The initalized <see cref="MemoryHandle{T}"/> block</returns> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryHandle<T> AllocAndCopy<T>(this IUnmangedHeap heap, ReadOnlySpan<T> initialData) where T:unmanaged - { - MemoryHandle<T> handle = heap.Alloc<T>(initialData.Length); - Memory.Copy(initialData, handle, 0); - return handle; - } - - /// <summary> - /// Copies data from the input buffer to the current handle and resizes the handle to the - /// size of the buffer - /// </summary> - /// <typeparam name="T">The unamanged value type</typeparam> - /// <param name="handle"></param> - /// <param name="input">The input buffer to copy data from</param> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void WriteAndResize<T>(this MemoryHandle<T> handle, ReadOnlySpan<T> input) where T: unmanaged - { - handle.Resize(input.Length); - Memory.Copy(input, handle, 0); - } - - /// <summary> - /// Allocates a block of unamanged memory of the number of elements of an unmanaged type, and - /// returns the <see cref="UnsafeMemoryHandle{T}"/> that must be used cautiously - /// </summary> - /// <typeparam name="T">The unamanged value type</typeparam> - /// <param name="heap">The heap to allocate block from</param> - /// <param name="elements">The number of elements to allocate</param> - /// <param name="zero">A flag to zero the initial contents of the buffer</param> - /// <returns>The allocated handle of the specified number of elements</returns> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe UnsafeMemoryHandle<T> UnsafeAlloc<T>(this IUnmangedHeap heap, int elements, bool zero = false) where T : unmanaged - { - if (elements < 1) - { - throw new ArgumentException("Elements must be greater than 0", nameof(elements)); - } - //Minimum of one element - elements = Math.Max(elements, 1); - //Get element size - uint elementSize = (uint)sizeof(T); - //If zero flag is set then specify zeroing memory - IntPtr block = heap.Alloc((uint)elements, elementSize, zero); - //handle wrapper - return new (heap, block, elements); - } - - #region VnBufferWriter - - /// <summary> - /// Formats and appends a value type to the writer with proper endianess - /// </summary> - /// <param name="buffer"></param> - /// <param name="value">The value to format and append to the buffer</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static void Append<T>(this ref ForwardOnlyWriter<byte> buffer, T value) where T: unmanaged - { - //Calc size of structure and fix te size of the buffer - int size = Unsafe.SizeOf<T>(); - Span<byte> output = buffer.Remaining[..size]; - - //Format value and write to buffer - MemoryMarshal.Write(output, ref value); - - //If byte order is reversed, reverse elements - if (!BitConverter.IsLittleEndian) - { - output.Reverse(); - } - - //Update written posiion - buffer.Advance(size); - } - - /// <summary> - /// Formats and appends a value type to the writer with proper endianess - /// </summary> - /// <param name="buffer"></param> - /// <param name="value">The value to format and append to the buffer</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static void Append<T>(this ref ForwardOnlyMemoryWriter<byte> buffer, T value) where T : struct - { - //Format value and write to buffer - int size = Unsafe.SizeOf<T>(); - Span<byte> output = buffer.Remaining.Span[..size]; - - //Format value and write to buffer - MemoryMarshal.Write(output, ref value); - - //If byte order is reversed, reverse elements - if (BitConverter.IsLittleEndian) - { - output.Reverse(); - } - - //Update written posiion - buffer.Advance(size); - } - - /// <summary> - /// Formats and appends the value to end of the buffer - /// </summary> - /// <param name="buffer"></param> - /// <param name="value">The value to format and append to the buffer</param> - /// <param name="format">An optional format argument</param> - /// <param name="formatProvider"></param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Append<T>(this ref ForwardOnlyWriter<char> buffer, T value, ReadOnlySpan<char> format = default, IFormatProvider? formatProvider = default) where T : ISpanFormattable - { - //Format value and write to buffer - if (!value.TryFormat(buffer.Remaining, out int charsWritten, format, formatProvider)) - { - throw new ArgumentOutOfRangeException(nameof(buffer), "The value could not be formatted and appended to the buffer, because there is not enough available space"); - } - //Update written posiion - buffer.Advance(charsWritten); - } - - /// <summary> - /// Formats and appends the value to end of the buffer - /// </summary> - /// <param name="buffer"></param> - /// <param name="value">The value to format and append to the buffer</param> - /// <param name="format">An optional format argument</param> - /// <param name="formatProvider"></param> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Append<T>(this ref ForwardOnlyMemoryWriter<char> buffer, T value, ReadOnlySpan<char> format = default, IFormatProvider? formatProvider = default) where T : ISpanFormattable - { - //Format value and write to buffer - if (!value.TryFormat(buffer.Remaining.Span, out int charsWritten, format, formatProvider)) - { - throw new ArgumentOutOfRangeException(nameof(buffer), "The value could not be formatted and appended to the buffer, because there is not enough available space"); - } - //Update written posiion - buffer.Advance(charsWritten); - } - - - - /// <summary> - /// Encodes a set of characters in the input characters span and any characters - /// in the internal buffer into a sequence of bytes that are stored in the input - /// byte span. A parameter indicates whether to clear the internal state of the - /// encoder after the conversion. - /// </summary> - /// <param name="enc"></param> - /// <param name="chars">Character buffer to encode</param> - /// <param name="offset">The offset in the char buffer to begin encoding chars from</param> - /// <param name="charCount">The number of characers to encode</param> - /// <param name="writer">The buffer writer to use</param> - /// <param name="flush">true to clear the internal state of the encoder after the conversion; otherwise, false.</param> - /// <returns>The actual number of bytes written at the location indicated by the bytes parameter.</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetBytes(this Encoder enc, char[] chars, int offset, int charCount, ref ForwardOnlyWriter<byte> writer, bool flush) - { - return GetBytes(enc, chars.AsSpan(offset, charCount), ref writer, flush); - } - /// <summary> - /// Encodes a set of characters in the input characters span and any characters - /// in the internal buffer into a sequence of bytes that are stored in the input - /// byte span. A parameter indicates whether to clear the internal state of the - /// encoder after the conversion. - /// </summary> - /// <param name="enc"></param> - /// <param name="chars">The character buffer to encode</param> - /// <param name="writer">The buffer writer to use</param> - /// <param name="flush">true to clear the internal state of the encoder after the conversion; otherwise, false.</param> - /// <returns>The actual number of bytes written at the location indicated by the bytes parameter.</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int GetBytes(this Encoder enc, ReadOnlySpan<char> chars, ref ForwardOnlyWriter<byte> writer, bool flush) - { - //Encode the characters - int written = enc.GetBytes(chars, writer.Remaining, flush); - //Update the writer position - writer.Advance(written); - return written; - } - /// <summary> - /// Encodes a set of characters in the input characters span and any characters - /// in the internal buffer into a sequence of bytes that are stored in the input - /// byte span. - /// </summary> - /// <param name="encoding"></param> - /// <param name="chars">The character buffer to encode</param> - /// <param name="writer">The buffer writer to use</param> - /// <returns>The actual number of bytes written at the location indicated by the bytes parameter.</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static int GetBytes(this Encoding encoding, ReadOnlySpan<char> chars, ref ForwardOnlyWriter<byte> writer) - { - //Encode the characters - int written = encoding.GetBytes(chars, writer.Remaining); - //Update the writer position - writer.Advance(written); - return written; - } - /// <summary> - /// Decodes a character buffer in the input characters span and any characters - /// in the internal buffer into a sequence of bytes that are stored in the input - /// byte span. - /// </summary> - /// <param name="encoding"></param> - /// <param name="bytes">The binary buffer to decode</param> - /// <param name="writer">The buffer writer to use</param> - /// <returns>The actual number of *characters* written at the location indicated by the chars parameter.</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static int GetChars(this Encoding encoding, ReadOnlySpan<byte> bytes, ref ForwardOnlyWriter<char> writer) - { - int charCount = encoding.GetCharCount(bytes); - //Encode the characters - _ = encoding.GetChars(bytes, writer.Remaining); - //Update the writer position - writer.Advance(charCount); - return charCount; - } - - /// <summary> - /// Converts the buffer data to a <see cref="PrivateString"/> - /// </summary> - /// <returns>A <see cref="PrivateString"/> instance that owns the underlying string memory</returns> - public static PrivateString ToPrivate(this ref ForwardOnlyWriter<char> buffer) => new(buffer.ToString(), true); - /// <summary> - /// Gets a <see cref="Span{T}"/> over the modified section of the internal buffer - /// </summary> - /// <returns>A <see cref="Span{T}"/> over the modified data</returns> - public static Span<T> AsSpan<T>(this ref ForwardOnlyWriter<T> buffer) => buffer.Buffer[..buffer.Written]; - - - #endregion - - /// <summary> - /// Slices the current array by the specified starting offset to the end - /// of the array - /// </summary> - /// <typeparam name="T">The array type</typeparam> - /// <param name="arr"></param> - /// <param name="start">The start offset of the new array slice</param> - /// <returns>The sliced array</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static T[] Slice<T>(this T[] arr, int start) - { - if(start < 0 || start > arr.Length) - { - throw new ArgumentOutOfRangeException(nameof(start)); - } - Range sliceRange = new(start, arr.Length - start); - return RuntimeHelpers.GetSubArray(arr, sliceRange); - } - /// <summary> - /// Slices the current array by the specified starting offset to including the - /// speciifed number of items - /// </summary> - /// <typeparam name="T">The array type</typeparam> - /// <param name="arr"></param> - /// <param name="start">The start offset of the new array slice</param> - /// <param name="count">The size of the new array</param> - /// <returns>The sliced array</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static T[] Slice<T>(this T[] arr, int start, int count) - { - if(start < 0) - { - throw new ArgumentOutOfRangeException(nameof(start)); - } - if(count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - if(start + count >= arr.Length) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - if(count == 0) - { - return Array.Empty<T>(); - } - //Calc the slice range - Range sliceRange = new(start, start + count); - return RuntimeHelpers.GetSubArray(arr, sliceRange); - } - - /// <summary> - /// Creates a new sub-sequence over the target handle. (allows for convient sub span) - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="handle"></param> - /// <param name="start">Intial offset into the handle</param> - /// <returns>The sub-sequence of the current handle</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span<T> AsSpan<T>(this IMemoryHandle<T> handle, int start) => handle.Span[start..]; - - /// <summary> - /// Creates a new sub-sequence over the target handle. (allows for convient sub span) - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="handle"></param> - /// <param name="start">Intial offset into the handle</param> - /// <param name="count">The number of elements within the new sequence</param> - /// <returns>The sub-sequence of the current handle</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span<T> AsSpan<T>(this IMemoryHandle<T> handle, int start, int count) => handle.Span.Slice(start, count); - - /// <summary> - /// Creates a new sub-sequence over the target handle. (allows for convient sub span) - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="handle"></param> - /// <param name="start">Intial offset into the handle</param> - /// <returns>The sub-sequence of the current handle</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span<T> AsSpan<T>(this in UnsafeMemoryHandle<T> handle, int start) where T: unmanaged => handle.Span[start..]; - - /// <summary> - /// Creates a new sub-sequence over the target handle. (allows for convient sub span) - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="handle"></param> - /// <param name="start">Intial offset into the handle</param> - /// <param name="count">The number of elements within the new sequence</param> - /// <returns>The sub-sequence of the current handle</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span<T> AsSpan<T>(this in UnsafeMemoryHandle<T> handle, int start, int count) where T : unmanaged => handle.Span.Slice(start, count); - - /// <summary> - /// Raises an <see cref="ObjectDisposedException"/> if the current handle - /// has been disposed or set as invalid - /// </summary> - /// <param name="handle"></param> - /// <exception cref="ObjectDisposedException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void ThrowIfClosed(this SafeHandle handle) - { - if (handle.IsClosed || handle.IsInvalid) - { - throw new ObjectDisposedException(handle.GetType().Name); - } - } - } -} diff --git a/Utils/src/Extensions/MutexReleaser.cs b/Utils/src/Extensions/MutexReleaser.cs deleted file mode 100644 index 84dd60f..0000000 --- a/Utils/src/Extensions/MutexReleaser.cs +++ /dev/null @@ -1,62 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: MutexReleaser.cs -* -* MutexReleaser.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.Threading; - -namespace VNLib.Utils.Extensions -{ - /// <summary> - /// Represents a releaser handle for a <see cref="Mutex"/> - /// that has been entered and will be released. Best if used - /// within a using() statment - /// </summary> - public readonly struct MutexReleaser : IDisposable, IEquatable<MutexReleaser> - { - private readonly Mutex _mutext; - internal MutexReleaser(Mutex mutex) => _mutext = mutex; - /// <summary> - /// Releases the held System.Threading.Mutex once. - /// </summary> - public readonly void Dispose() => _mutext.ReleaseMutex(); - /// <summary> - /// Releases the held System.Threading.Mutex once. - /// </summary> - public readonly void ReleaseMutext() => _mutext.ReleaseMutex(); - - ///<inheritdoc/> - public bool Equals(MutexReleaser other) => _mutext.Equals(other._mutext); - - ///<inheritdoc/> - public override bool Equals(object? obj) => obj is MutexReleaser releaser && Equals(releaser); - - ///<inheritdoc/> - public override int GetHashCode() => _mutext.GetHashCode(); - - ///<inheritdoc/> - public static bool operator ==(MutexReleaser left, MutexReleaser right) => left.Equals(right); - ///<inheritdoc/> - public static bool operator !=(MutexReleaser left, MutexReleaser right) => !(left == right); - } -}
\ No newline at end of file diff --git a/Utils/src/Extensions/SafeLibraryExtensions.cs b/Utils/src/Extensions/SafeLibraryExtensions.cs deleted file mode 100644 index 8866059..0000000 --- a/Utils/src/Extensions/SafeLibraryExtensions.cs +++ /dev/null @@ -1,103 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: SafeLibraryExtensions.cs -* -* SafeLibraryExtensions.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.Reflection; - -using VNLib.Utils.Native; - -namespace VNLib.Utils.Extensions -{ - /// <summary> - /// When applied to a delegate, specifies the name of the native method to load - /// </summary> - [AttributeUsage(AttributeTargets.Delegate)] - public sealed class SafeMethodNameAttribute : Attribute - { - /// <summary> - /// Creates a new <see cref="SafeMethodNameAttribute"/> - /// </summary> - /// <param name="MethodName">The name of the native method</param> - public SafeMethodNameAttribute(string MethodName) => this.MethodName = MethodName; - /// <summary> - /// Creates a new <see cref="SafeMethodNameAttribute"/>, that uses the - /// delegate name as the native method name - /// </summary> - public SafeMethodNameAttribute() => MethodName = null; - /// <summary> - /// The name of the native method - /// </summary> - public string? MethodName { get; } - } - - - /// <summary> - /// Contains native library extension methods - /// </summary> - public static class SafeLibraryExtensions - { - const string _missMemberExceptionMessage = $"The delegate type is missing the required {nameof(SafeMethodNameAttribute)} to designate the native method to load"; - - /// <summary> - /// Loads a native method from the current <see cref="SafeLibraryHandle"/> - /// that has a <see cref="SafeMethodNameAttribute"/> - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="library"></param> - /// <returns></returns> - /// <exception cref="MissingMemberException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="EntryPointNotFoundException"></exception> - public static SafeMethodHandle<T> GetMethod<T>(this SafeLibraryHandle library) where T : Delegate - { - Type t = typeof(T); - //Get the method name attribute - SafeMethodNameAttribute? attr = t.GetCustomAttribute<SafeMethodNameAttribute>(); - _ = attr ?? throw new MissingMemberException(_missMemberExceptionMessage); - return library.GetMethod<T>(attr.MethodName ?? t.Name); - } - /// <summary> - /// Loads a native method from the current <see cref="SafeLibraryHandle"/> - /// that has a <see cref="SafeMethodNameAttribute"/> - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="library"></param> - /// <returns></returns> - /// <exception cref="MissingMemberException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="EntryPointNotFoundException"></exception> - /// <remarks> - /// The libraries handle count is left unmodified - /// </remarks> - public static T DangerousGetMethod<T>(this SafeLibraryHandle library) where T: Delegate - { - Type t = typeof(T); - //Get the method name attribute - SafeMethodNameAttribute? attr = t.GetCustomAttribute<SafeMethodNameAttribute>(); - return string.IsNullOrWhiteSpace(attr?.MethodName) - ? throw new MissingMemberException(_missMemberExceptionMessage) - : library.DangerousGetMethod<T>(attr.MethodName); - } - } -} diff --git a/Utils/src/Extensions/SemSlimReleaser.cs b/Utils/src/Extensions/SemSlimReleaser.cs deleted file mode 100644 index c8a22fe..0000000 --- a/Utils/src/Extensions/SemSlimReleaser.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: SemSlimReleaser.cs -* -* SemSlimReleaser.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.Threading; - -namespace VNLib.Utils.Extensions -{ - /// <summary> - /// Represents a releaser handle for a <see cref="SemaphoreSlim"/> - /// that has been entered and will be released. Best if used - /// within a using() statment - /// </summary> - public readonly struct SemSlimReleaser : IDisposable - { - private readonly SemaphoreSlim _semaphore; - internal SemSlimReleaser(SemaphoreSlim semaphore) => _semaphore = semaphore; - /// <summary> - /// Releases the System.Threading.SemaphoreSlim object once. - /// </summary> - public readonly void Dispose() => _semaphore.Release(); - /// <summary> - /// Releases the System.Threading.SemaphoreSlim object once. - /// </summary> - /// <returns>The previous count of the <see cref="SemaphoreSlim"/></returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="SemaphoreFullException"></exception> - public readonly int Release() => _semaphore.Release(); - } -}
\ No newline at end of file diff --git a/Utils/src/Extensions/StringExtensions.cs b/Utils/src/Extensions/StringExtensions.cs deleted file mode 100644 index 09d6517..0000000 --- a/Utils/src/Extensions/StringExtensions.cs +++ /dev/null @@ -1,481 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: StringExtensions.cs -* -* StringExtensions.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; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -using VNLib.Utils.Memory; - -namespace VNLib.Utils.Extensions -{ - public delegate void StatelessSpanAction(ReadOnlySpan<char> line); - - /// <summary> - /// Extention methods for string (character buffer) - /// </summary> - public static class StringExtensions - { - /// <summary> - /// Split a string based on split value and insert into the specified list - /// </summary> - /// <param name="value"></param> - /// <param name="splitter">The value to split the string on</param> - /// <param name="output">The list to output data to</param> - /// <param name="options">String split options</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Split<T>(this string value, string splitter, T output, StringSplitOptions options) where T : ICollection<string> - { - Split(value, splitter.AsSpan(), output, options); - } - /// <summary> - /// Split a string based on split value and insert into the specified list - /// </summary> - /// <param name="value"></param> - /// <param name="splitter">The value to split the string on</param> - /// <param name="output">The list to output data to</param> - /// <param name="options">String split options</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Split<T>(this string value, char splitter, T output, StringSplitOptions options) where T: ICollection<string> - { - //Create span from char pointer - ReadOnlySpan<char> cs = MemoryMarshal.CreateReadOnlySpan(ref splitter, 1); - //Call the split function on the span - Split(value, cs, output, options); - } - /// <summary> - /// Split a string based on split value and insert into the specified list - /// </summary> - /// <param name="value"></param> - /// <param name="splitter">The value to split the string on</param> - /// <param name="output">The list to output data to</param> - /// <param name="options">String split options</param> - /// <exception cref="ArgumentNullException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Split<T>(this string value, ReadOnlySpan<char> splitter, T output, StringSplitOptions options) where T : ICollection<string> - { - Split(value.AsSpan(), splitter, output, options); - } - /// <summary> - /// Split a string based on split value and insert into the specified list - /// </summary> - /// <param name="value"></param> - /// <param name="splitter">The value to split the string on</param> - /// <param name="output">The list to output data to</param> - /// <param name="options">String split options</param> - /// <exception cref="ArgumentNullException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Split<T>(this in ReadOnlySpan<char> value, char splitter, T output, StringSplitOptions options) where T : ICollection<string> - { - //Create span from char pointer - ReadOnlySpan<char> cs = MemoryMarshal.CreateReadOnlySpan(ref splitter, 1); - //Call the split function on the span - Split(in value, cs, output, options); - } - /// <summary> - /// Split a <see cref="ReadOnlySpan{T}"/> based on split value and insert into the specified list - /// </summary> - /// <param name="value"></param> - /// <param name="splitter">The value to split the string on</param> - /// <param name="output">The list to output data to</param> - /// <param name="options">String split options</param> - /// <exception cref="ArgumentNullException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Split<T>(this in ReadOnlySpan<char> value, ReadOnlySpan<char> splitter, T output, StringSplitOptions options) where T : ICollection<string> - { - //Create a local function that adds the split strings to the list - static void SplitFound(ReadOnlySpan<char> split, T output) => output.Add(split.ToString()); - //Invoke the split function with the local callback method - Split(in value, splitter, options, SplitFound, output); - } - /// <summary> - /// Split a <see cref="ReadOnlySpan{T}"/> based on split value and pass it to the split delegate handler - /// </summary> - /// <param name="value"></param> - /// <param name="splitter">The sequence to split the string on</param> - /// <param name="options">String split options</param> - /// <param name="splitCb">The action to invoke when a split segment has been found</param> - /// <param name="state">The state to pass to the callback handler</param> - /// <exception cref="ArgumentNullException"></exception> - public static void Split<T>(this in ReadOnlySpan<char> value, ReadOnlySpan<char> splitter, StringSplitOptions options, ReadOnlySpanAction<char, T> splitCb, T state) - { - _ = splitCb ?? throw new ArgumentNullException(nameof(splitCb)); - //Get span over string - ForwardOnlyReader<char> reader = new(value); - //No string options - if (options == 0) - { - do - { - //Find index of the splitter - int start = reader.Window.IndexOf(splitter); - //guard - if (start == -1) - { - break; - } - //Trim and add it regardless of length - splitCb(reader.Window[..start], state); - //shift window - reader.Advance(start + splitter.Length); - } while (true); - //Trim remaining and add it regardless of length - splitCb(reader.Window, state); - } - //Trim but do not remove empties - else if ((options & StringSplitOptions.RemoveEmptyEntries) == 0) - { - do - { - //Find index of the splitter - int start = reader.Window.IndexOf(splitter); - //guard - if (start == -1) - { - break; - } - //Trim and add it regardless of length - splitCb(reader.Window[..start].Trim(), state); - //shift window - reader.Advance(start + splitter.Length); - } while (true); - //Trim remaining and add it regardless of length - splitCb(reader.Window.Trim(), state); - } - //Remove empty entires but do not trim them - else if ((options & StringSplitOptions.TrimEntries) == 0) - { - //Get data before splitter and trim it - ReadOnlySpan<char> data; - do - { - //Find index of the splitter - int start = reader.Window.IndexOf(splitter); - //guard - if (start == -1) - { - break; - } - //Get data before splitter and trim it - data = reader.Window[..start]; - //If its not empty, then add it to the list - if (!data.IsEmpty) - { - splitCb(data, state); - } - //shift window - reader.Advance(start + splitter.Length); - } while (true); - //Add if not empty - if (reader.WindowSize > 0) - { - splitCb(reader.Window, state); - } - } - //Must mean remove and trim - else - { - //Get data before splitter and trim it - ReadOnlySpan<char> data; - do - { - //Find index of the splitter - int start = reader.Window.IndexOf(splitter); - //guard - if (start == -1) - { - break; - } - //Get data before splitter and trim it - data = reader.Window[..start].Trim(); - //If its not empty, then add it to the list - if (!data.IsEmpty) - { - splitCb(data, state); - } - //shift window - reader.Advance(start + splitter.Length); - } while (true); - //Trim remaining - data = reader.Window.Trim(); - //Add if not empty - if (!data.IsEmpty) - { - splitCb(data, state); - } - } - } - - /// <summary> - /// Split a <see cref="ReadOnlySpan{T}"/> based on split value and pass it to the split delegate handler - /// </summary> - /// <param name="value"></param> - /// <param name="splitter">The character to split the string on</param> - /// <param name="options">String split options</param> - /// <param name="splitCb">The action to invoke when a split segment has been found</param> - /// <param name="state"></param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Split<T>(this in ReadOnlySpan<char> value, char splitter, StringSplitOptions options, ReadOnlySpanAction<char, T> splitCb, T state) - { - //Alloc a span for char - ReadOnlySpan<char> cs = MemoryMarshal.CreateReadOnlySpan(ref splitter, 1); - //Call the split function on the span - Split(in value, cs, options, splitCb, state); - } - /// <summary> - /// Split a <see cref="ReadOnlySpan{T}"/> based on split value and pass it to the split delegate handler - /// </summary> - /// <param name="value"></param> - /// <param name="splitter">The sequence to split the string on</param> - /// <param name="options">String split options</param> - /// <param name="splitCb">The action to invoke when a split segment has been found</param> - /// <exception cref="ArgumentNullException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Split(this in ReadOnlySpan<char> value, ReadOnlySpan<char> splitter, StringSplitOptions options, StatelessSpanAction splitCb) - { - //Create a SpanSplitDelegate with the non-typed delegate as the state argument - static void ssplitcb(ReadOnlySpan<char> param, StatelessSpanAction callback) => callback(param); - //Call split with the new callback delegate - Split(in value, splitter, options, ssplitcb, splitCb); - } - /// <summary> - /// Split a <see cref="ReadOnlySpan{T}"/> based on split value and pass it to the split delegate handler - /// </summary> - /// <param name="value"></param> - /// <param name="splitter">The character to split the string on</param> - /// <param name="options">String split options</param> - /// <param name="splitCb">The action to invoke when a split segment has been found</param> - /// <exception cref="ArgumentNullException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Split(this in ReadOnlySpan<char> value, char splitter, StringSplitOptions options, StatelessSpanAction splitCb) - { - //Create a SpanSplitDelegate with the non-typed delegate as the state argument - static void ssplitcb(ReadOnlySpan<char> param, StatelessSpanAction callback) => callback(param); - //Call split with the new callback delegate - Split(in value, splitter, options, ssplitcb, splitCb); - } - - /// <summary> - /// Gets the index of the end of the found sequence - /// </summary> - /// <param name="data"></param> - /// <param name="search">Sequence to search for within the current sequence</param> - /// <returns>the index of the end of the sequenc</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int EndOf(this in ReadOnlySpan<char> data, ReadOnlySpan<char> search) - { - int index = data.IndexOf(search); - return index > -1 ? index + search.Length : -1; - } - /// <summary> - /// Gets the index of the end of the found character - /// </summary> - /// <param name="data"></param> - /// <param name="search">Character to search for within the current sequence</param> - /// <returns>the index of the end of the sequence</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int EndOf(this in ReadOnlySpan<char> data, char search) - { - int index = data.IndexOf(search); - return index > -1 ? index + 1 : -1; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this in Memory<byte> data, byte search) => data.Span.IndexOf(search); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this in Memory<byte> data, ReadOnlySpan<byte> search) => data.Span.IndexOf(search); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int IndexOf(this in Memory<byte> data, ReadOnlyMemory<byte> search) => IndexOf(data, search.Span); - - /// <summary> - /// Slices the current span from the begining of the segment to the first occurrance of the specified character. - /// If the character is not found, the entire segment is returned - /// </summary> - /// <param name="data"></param> - /// <param name="search">The delimiting character</param> - /// <returns>The segment of data before the search character, or the entire segment if not found</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan<char> SliceBeforeParam(this in ReadOnlySpan<char> data, char search) - { - //Find the index of the specified data - int index = data.IndexOf(search); - //Return the slice of data before the index, or an empty span if it was not found - return index > -1 ? data[..index] : data; - } - /// <summary> - /// Slices the current span from the begining of the segment to the first occurrance of the specified character sequence. - /// If the character sequence is not found, the entire segment is returned - /// </summary> - /// <param name="data"></param> - /// <param name="search">The delimiting character sequence</param> - /// <returns>The segment of data before the search character, or the entire <paramref name="data"/> if the seach sequence is not found</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan<char> SliceBeforeParam(this in ReadOnlySpan<char> data, ReadOnlySpan<char> search) - { - //Find the index of the specified data - int index = data.IndexOf(search); - //Return the slice of data before the index, or an empty span if it was not found - return index > -1 ? data[..index] : data; - } - /// <summary> - /// Gets the remaining segment of data after the specified search character or <see cref="ReadOnlySpan{T}.Empty"/> - /// if the search character is not found within the current segment - /// </summary> - /// <param name="data"></param> - /// <param name="search">The character to search for within the segment</param> - /// <returns>The segment of data after the search character or <see cref="ReadOnlySpan{T}.Empty"/> if not found</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan<char> SliceAfterParam(this in ReadOnlySpan<char> data, char search) - { - //Find the index of the specified data - int index = EndOf(in data, search); - //Return the slice of data after the index, or an empty span if it was not found - return index > -1 ? data[index..] : ReadOnlySpan<char>.Empty; - } - /// <summary> - /// Gets the remaining segment of data after the specified search sequence or <see cref="ReadOnlySpan{T}.Empty"/> - /// if the search sequence is not found within the current segment - /// </summary> - /// <param name="data"></param> - /// <param name="search">The sequence to search for within the segment</param> - /// <returns>The segment of data after the search sequence or <see cref="ReadOnlySpan{T}.Empty"/> if not found</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan<char> SliceAfterParam(this in ReadOnlySpan<char> data, ReadOnlySpan<char> search) - { - //Find the index of the specified data - int index = EndOf(data, search); - //Return the slice of data after the index, or an empty span if it was not found - return index > -1 ? data[index..] : ReadOnlySpan<char>.Empty; - } - /// <summary> - /// Trims any leading or trailing <c>'\r'|'\n'|' '</c>(whitespace) characters from the segment - /// </summary> - /// <returns>The trimmed segment</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan<char> TrimCRLF(this in ReadOnlySpan<char> data) - { - int start = 0, end = data.Length; - //trim leading \r\n chars - while(start < end) - { - char t = data[start]; - //If character \r or \n slice it off - if (t != '\r' && t != '\n' && t != ' ') { - break; - } - //Shift - start++; - } - //remove trailing crlf characters - while (end > start) - { - char t = data[end - 1]; - //If character \r or \n slice it off - if (t != '\r' && t != '\n' && t != ' ') { - break; - } - end--; - } - return data[start..end]; - } - - /// <summary> - /// Replaces a character sequence within the buffer - /// </summary> - /// <param name="buffer">The character buffer to process</param> - /// <param name="search">The sequence to search for</param> - /// <param name="replace">The sequence to write in the place of the search parameter</param> - /// <exception cref="OutOfMemoryException"></exception> - public static int Replace(this ref Span<char> buffer, ReadOnlySpan<char> search, ReadOnlySpan<char> replace) - { - ForwardOnlyWriter<char> writer = new (buffer); - writer.Replace(search, replace); - return writer.Written; - } - - /// <summary> - /// Replaces a character sequence within the writer - /// </summary> - /// <param name="writer"></param> - /// <param name="search">The sequence to search for</param> - /// <param name="replace">The sequence to write in the place of the search parameter</param> - /// <exception cref="OutOfMemoryException"></exception> - public static void Replace(this ref ForwardOnlyWriter<char> writer, ReadOnlySpan<char> search, ReadOnlySpan<char> replace) - { - Span<char> buffer = writer.AsSpan(); - //If the search and replacment parameters are the same length - if (search.Length == replace.Length) - { - buffer.ReplaceInPlace(search, replace); - return; - } - //Search and replace are not the same length - int searchLen = search.Length, start = buffer.IndexOf(search); - if(start == -1) - { - return; - } - //Replacment might be empty - writer.Reset(); - do - { - //Append the data before the split character - writer.Append(buffer[..start]); - //Append the replacment - writer.Append(replace); - //Shift buffer to the end of the - buffer = buffer[(start + searchLen)..]; - //search for next index - start = buffer.IndexOf(search); - } while (start > -1); - //Write remaining data - writer.Append(replace); - } - /// <summary> - /// Replaces very ocurrance of character sequence within a buffer with another sequence of the same length - /// </summary> - /// <param name="buffer"></param> - /// <param name="search">The sequence to search for</param> - /// <param name="replace">The sequence to replace the found sequence with</param> - /// <exception cref="ArgumentException"></exception> - public static void ReplaceInPlace(this Span<char> buffer, ReadOnlySpan<char> search, ReadOnlySpan<char> replace) - { - if(search.Length != replace.Length) - { - throw new ArgumentException("Search parameter and replacment parameter must be the same length"); - } - int start = buffer.IndexOf(search); - while(start > -1) - { - //Shift the buffer to the begining of the search parameter - buffer = buffer[start..]; - //Overwite the search parameter - replace.CopyTo(buffer); - //Search for next index of the search character - start = buffer.IndexOf(search); - } - } - } -}
\ No newline at end of file diff --git a/Utils/src/Extensions/ThreadingExtensions.cs b/Utils/src/Extensions/ThreadingExtensions.cs deleted file mode 100644 index cc9fab9..0000000 --- a/Utils/src/Extensions/ThreadingExtensions.cs +++ /dev/null @@ -1,226 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ThreadingExtensions.cs -* -* ThreadingExtensions.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.Threading; -using System.Threading.Tasks; -using VNLib.Utils.Resources; - -namespace VNLib.Utils.Extensions -{ - - /// <summary> - /// Provides extension methods to common threading and TPL library operations - /// </summary> - public static class ThreadingExtensions - { - /// <summary> - /// Allows an <see cref="OpenResourceHandle{TResource}"/> to execute within a scope limited context - /// </summary> - /// <typeparam name="TResource">The resource type</typeparam> - /// <param name="rh"></param> - /// <param name="safeCallback">The function body that will execute with controlled access to the resource</param> - public static void EnterSafeContext<TResource>(this OpenResourceHandle<TResource> rh, Action<TResource> safeCallback) - { - using (rh) - { - safeCallback(rh.Resource); - } - } - - /// <summary> - /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/> while observing a <see cref="CancellationToken"/> - /// and getting a releaser handle - /// </summary> - /// <param name="semaphore"></param> - /// <param name="cancellationToken">A token to cancel the operation</param> - /// <returns>A releaser handle that may be disposed to release the semaphore</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="OperationCanceledException"></exception> - public static async Task<SemSlimReleaser> GetReleaserAsync(this SemaphoreSlim semaphore, CancellationToken cancellationToken = default) - { - await semaphore.WaitAsync(cancellationToken); - return new SemSlimReleaser(semaphore); - } - /// <summary> - /// Asynchronously waits to enter the <see cref="SemaphoreSlim"/> using a 32-bit signed integer to measure the time intervale - /// and getting a releaser handle - /// </summary> - /// <param name="semaphore"></param> - /// <param name="timeout">A the maximum amount of time in milliseconds to wait to enter the semaphore</param> - /// <returns>A releaser handle that may be disposed to release the semaphore</returns> - /// <exception cref="TimeoutException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - public static async Task<SemSlimReleaser> GetReleaserAsync(this SemaphoreSlim semaphore, int timeout) - { - if (await semaphore.WaitAsync(timeout)) - { - return new SemSlimReleaser(semaphore); - } - throw new TimeoutException("Failed to enter the semaphore before the specified timeout period"); - } - - /// <summary> - /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/> - /// </summary> - /// <param name="semaphore"></param> - /// <returns>A releaser handler that releases the semaphore when disposed</returns> - /// <exception cref="ObjectDisposedException"></exception> - public static SemSlimReleaser GetReleaser(this SemaphoreSlim semaphore) - { - semaphore.Wait(); - return new SemSlimReleaser(semaphore); - } - /// <summary> - /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/> - /// </summary> - /// <param name="semaphore"></param> - /// <param name="timeout">A the maximum amount of time in milliseconds to wait to enter the semaphore</param> - /// <returns>A releaser handler that releases the semaphore when disposed</returns> - /// <exception cref="TimeoutException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - public static SemSlimReleaser GetReleaser(this SemaphoreSlim semaphore, int timeout) - { - if (semaphore.Wait(timeout)) - { - return new SemSlimReleaser(semaphore); - } - throw new TimeoutException("Failed to enter the semaphore before the specified timeout period"); - } - - /// <summary> - /// Blocks the current thread until it can enter the <see cref="Mutex"/> - /// </summary> - /// <param name="mutex"></param> - /// <returns>A releaser handler that releases the semaphore when disposed</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="AbandonedMutexException"></exception> - public static MutexReleaser Enter(this Mutex mutex) - { - mutex.WaitOne(); - return new MutexReleaser(mutex); - } - /// <summary> - /// Blocks the current thread until it can enter the <see cref="SemaphoreSlim"/> - /// </summary> - /// <param name="mutex"></param> - /// <param name="timeout">A the maximum amount of time in milliseconds to wait to enter the semaphore</param> - /// <returns>A releaser handler that releases the semaphore when disposed</returns> - /// <exception cref="TimeoutException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - public static MutexReleaser Enter(this Mutex mutex, int timeout) - { - if (mutex.WaitOne(timeout)) - { - return new MutexReleaser(mutex); - } - throw new TimeoutException("Failed to enter the semaphore before the specified timeout period"); - } - - private static readonly Task<bool> TrueCompleted = Task.FromResult(true); - private static readonly Task<bool> FalseCompleted = Task.FromResult(false); - - /// <summary> - /// Asynchronously waits for a the <see cref="WaitHandle"/> to receive a signal. This method spins until - /// a thread yield will occur, then asynchronously yields. - /// </summary> - /// <param name="handle"></param> - /// <param name="timeoutMs">The timeout interval in milliseconds</param> - /// <returns> - /// A task that compeletes when the wait handle receives a signal or times-out, - /// the result of the awaited task will be <c>true</c> if the signal is received, or - /// <c>false</c> if the timeout interval expires - /// </returns> - /// <exception cref="ArgumentNullException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static Task<bool> WaitAsync(this WaitHandle handle, int timeoutMs = Timeout.Infinite) - { - _ = handle ?? throw new ArgumentNullException(nameof(handle)); - //test non-blocking handle state - if (handle.WaitOne(0)) - { - return TrueCompleted; - } - //When timeout is 0, wh will block, return false - else if(timeoutMs == 0) - { - return FalseCompleted; - } - //Init short lived spinwait - SpinWait sw = new(); - //Spin until yield occurs - while (!sw.NextSpinWillYield) - { - sw.SpinOnce(); - //Check handle state - if (handle.WaitOne(0)) - { - return TrueCompleted; - } - } - //Completion source used to signal the awaiter when the wait handle is signaled - TaskCompletionSource<bool> completion = new(TaskCreationOptions.None); - //Register wait on threadpool to complete the task source - RegisteredWaitHandle registration = ThreadPool.RegisterWaitForSingleObject(handle, TaskCompletionCallback, completion, timeoutMs, true); - //Register continuation to cleanup - _ = completion.Task.ContinueWith(CleanupContinuation, registration, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default) - .ConfigureAwait(false); - return completion.Task; - } - - private static void CleanupContinuation(Task<bool> task, object? taskCompletion) - { - RegisteredWaitHandle registration = (taskCompletion as RegisteredWaitHandle)!; - registration.Unregister(null); - task.Dispose(); - } - private static void TaskCompletionCallback(object? tcsState, bool timedOut) - { - TaskCompletionSource<bool> completion = (tcsState as TaskCompletionSource<bool>)!; - //Set the result of the wait handle timeout - _ = completion.TrySetResult(!timedOut); - } - - - /// <summary> - /// Registers a callback method that will be called when the token has been cancelled. - /// This method waits indefinitely for the token to be cancelled. - /// </summary> - /// <param name="token"></param> - /// <param name="callback">The callback method to invoke when the token has been cancelled</param> - /// <returns>A task that may be unobserved, that completes when the token has been cancelled</returns> - public static Task RegisterUnobserved(this CancellationToken token, Action callback) - { - //Call callback when the wait handle is set - return token.WaitHandle.WaitAsync() - .ContinueWith(static (t, callback) => (callback as Action)!.Invoke(), - callback, - CancellationToken.None, - TaskContinuationOptions.ExecuteSynchronously, - TaskScheduler.Default - ); - } - } -}
\ No newline at end of file diff --git a/Utils/src/Extensions/TimerExtensions.cs b/Utils/src/Extensions/TimerExtensions.cs deleted file mode 100644 index a980d63..0000000 --- a/Utils/src/Extensions/TimerExtensions.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: TimerExtensions.cs -* -* TimerExtensions.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.Threading; -using VNLib.Utils.Resources; - -namespace VNLib.Utils.Extensions -{ - public static class TimerExtensions - { - /// <summary> - /// Attempts to stop the timer - /// </summary> - /// <returns>True if the timer was successfully modified, false otherwise</returns> - public static bool Stop(this Timer timer) => timer.Change(Timeout.Infinite, Timeout.Infinite); - - /// <summary> - /// Attempts to stop an active timer and prepare a <see cref="OpenHandle"/> configured to restore the state of the timer to the specified timespan - /// </summary> - /// <param name="timer"></param> - /// <param name="resumeTime"><see cref="TimeSpan"/> representing the amount of time the timer should wait before invoking the callback function</param> - /// <returns>A new <see cref="OpenHandle"/> if the timer was stopped successfully that will resume the timer when closed, null otherwise</returns> - public static OpenHandle Stop(this Timer timer, TimeSpan resumeTime) - { - return timer.Change(Timeout.Infinite, Timeout.Infinite) ? new CallbackOpenHandle(() => timer.Change(resumeTime, Timeout.InfiniteTimeSpan)) : null; - } - - /// <summary> - /// Attempts to reset and start a timer - /// </summary> - /// <param name="timer"></param> - /// <param name="wait"><see cref="TimeSpan"/> to wait before the timer event is fired</param> - /// <returns>True if the timer was successfully modified</returns> - public static bool Restart(this Timer timer, TimeSpan wait) => timer.Change(wait, Timeout.InfiniteTimeSpan); - - /// <summary> - /// Attempts to reset and start a timer - /// </summary> - /// <param name="timer"></param> - /// <param name="waitMilliseconds">Time in milliseconds to wait before the timer event is fired</param> - /// <returns>True if the timer was successfully modified</returns> - public static bool Restart(this Timer timer, int waitMilliseconds) => timer.Change(waitMilliseconds, Timeout.Infinite); - } -}
\ No newline at end of file diff --git a/Utils/src/Extensions/VnStringExtensions.cs b/Utils/src/Extensions/VnStringExtensions.cs deleted file mode 100644 index 285fc4f..0000000 --- a/Utils/src/Extensions/VnStringExtensions.cs +++ /dev/null @@ -1,418 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: VnStringExtensions.cs -* -* VnStringExtensions.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.Linq; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -using VNLib.Utils.Memory; - -namespace VNLib.Utils.Extensions -{ - [SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "<Pending>")] - public static class VnStringExtensions - { - /// <summary> - /// Derermines if the character exists within the instance - /// </summary> - /// <param name="str"></param> - /// <param name="value">The value to find</param> - /// <returns>True if the character exists within the instance</returns> - /// <exception cref="ObjectDisposedException"></exception> - public static bool Contains(this VnString str, char value) => str.AsSpan().Contains(value); - /// <summary> - /// Derermines if the sequence exists within the instance - /// </summary> - /// <param name="str"></param> - /// <param name="value">The sequence to find</param> - /// <param name="stringComparison"></param> - /// <returns>True if the character exists within the instance</returns> - /// <exception cref="ObjectDisposedException"></exception> - - public static bool Contains(this VnString str, ReadOnlySpan<char> value, StringComparison stringComparison) => str.AsSpan().Contains(value, stringComparison); - - /// <summary> - /// Searches for the first occurrance of the specified character within the current instance - /// </summary> - /// <param name="str"></param> - /// <param name="value">The character to search for within the instance</param> - /// <returns>The 0 based index of the occurance, -1 if the character was not found</returns> - /// <exception cref="ObjectDisposedException"></exception> - public static int IndexOf(this VnString str, char value) => str.IsEmpty ? -1 : str.AsSpan().IndexOf(value); - /// <summary> - /// Searches for the first occurrance of the specified sequence within the current instance - /// </summary> - /// <param name="str"></param> - /// <param name="search">The sequence to search for</param> - /// <returns>The 0 based index of the occurance, -1 if the sequence was not found</returns> - /// <exception cref="ObjectDisposedException"></exception> - public static int IndexOf(this VnString str, ReadOnlySpan<char> search) - { - //Using spans to avoid memory leaks... - ReadOnlySpan<char> self = str.AsSpan(); - return self.IndexOf(search); - } - /// <summary> - /// Searches for the first occurrance of the specified sequence within the current instance - /// </summary> - /// <param name="str"></param> - /// <param name="search">The sequence to search for</param> - /// <param name="comparison">The <see cref="StringComparison"/> type to use in searchr</param> - /// <returns>The 0 based index of the occurance, -1 if the sequence was not found</returns> - /// <exception cref="ObjectDisposedException"></exception> - public static int IndexOf(this VnString str, ReadOnlySpan<char> search, StringComparison comparison) - { - //Using spans to avoid memory leaks... - ReadOnlySpan<char> self = str.AsSpan(); - return self.IndexOf(search, comparison); - } - /// <summary> - /// Searches for the 0 based index of the first occurance of the search parameter after the start index. - /// </summary> - /// <param name="str"></param> - /// <param name="search">The sequence of data to search for</param> - /// <param name="start">The lower boundry of the search area</param> - /// <returns>The absolute index of the first occurrance within the instance, -1 if the sequency was not found in the windowed segment</returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - public static int IndexOf(this VnString str, ReadOnlySpan<char> search, int start) - { - if (start < 0) - { - throw new ArgumentOutOfRangeException(nameof(start), "Start cannot be less than 0"); - } - //Get shifted window - ReadOnlySpan<char> self = str.AsSpan()[start..]; - //Check indexof - int index = self.IndexOf(search); - return index > -1 ? index + start : -1; - } - - /// <summary> - /// Returns the realtive index after the specified sequence within the <see cref="VnString"/> instance - /// </summary> - /// <param name="str"></param> - /// <param name="search">The sequence to search for</param> - /// <returns>The index after the found sequence within the string, -1 if the sequence was not found within the instance</returns> - /// <exception cref="ObjectDisposedException"></exception> - public static int EndOf(this VnString str, ReadOnlySpan<char> search) - { - //Try to get the index of the data - int index = IndexOf(str, search); - //If the data was found, add the length to get the end of the string - return index > -1 ? index + search.Length : -1; - } - - /// <summary> - /// Allows for trimming whitespace characters in a realtive sequence from - /// within a <see cref="VnString"/> buffer defined by the start and end parameters - /// and returning the trimmed entry. - /// </summary> - /// <param name="data"></param> - /// <param name="start">The starting position within the sequence to trim</param> - /// <param name="end">The end of the sequence to trim</param> - /// <returns>The trimmed <see cref="VnString"/> instance as a child of the original entry</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="IndexOutOfRangeException"></exception> - public static VnString AbsoluteTrim(this VnString data, int start, int end) - { - AbsoluteTrim(data, ref start, ref end); - return data[start..end]; - } - /// <summary> - /// Finds whitespace characters within the sequence defined between start and end parameters - /// and adjusts the specified window to "trim" whitespace - /// </summary> - /// <param name="data"></param> - /// <param name="start">The starting position within the sequence to trim</param> - /// <param name="end">The end of the sequence to trim</param> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="IndexOutOfRangeException"></exception> - public static void AbsoluteTrim(this VnString data, ref int start, ref int end) - { - ReadOnlySpan<char> trimmed = data.AsSpan(); - //trim leading whitespace - while (start < end) - { - //If whitespace character shift start up - if (trimmed[start] != ' ') - { - break; - } - //Shift - start++; - } - //remove trailing whitespace characters - while (end > start) - { - //If whiterspace character shift end param down - if (trimmed[end - 1] != ' ') - { - break; - } - end--; - } - } - /// <summary> - /// Allows for trimming whitespace characters in a realtive sequence from - /// within a <see cref="VnString"/> buffer and returning the trimmed entry. - /// </summary> - /// <param name="data"></param> - /// <param name="start">The starting position within the sequence to trim</param> - /// <returns>The trimmed <see cref="VnString"/> instance as a child of the original entry</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="IndexOutOfRangeException"></exception> - public static VnString AbsoluteTrim(this VnString data, int start) => AbsoluteTrim(data, start, data.Length); - /// <summary> - /// Trims leading or trailing whitespace characters and returns a new child instance - /// without leading or trailing whitespace - /// </summary> - /// <returns>A child <see cref="VnString"/> of the current instance without leading or trailing whitespaced</returns> - /// <exception cref="ObjectDisposedException"></exception> - public static VnString RelativeTirm(this VnString data) => AbsoluteTrim(data, 0); - - /// <summary> - /// Allows for enumeration of segments of data within the specified <see cref="VnString"/> instance that are - /// split by the search parameter - /// </summary> - /// <param name="data"></param> - /// <param name="search">The sequence of data to delimit segments</param> - /// <param name="options">The options used to split the string instances</param> - /// <returns>An iterator to enumerate the split segments</returns> - /// <exception cref="ObjectDisposedException"></exception> - public static IEnumerable<VnString> Split(this VnString data, ReadOnlyMemory<char> search, StringSplitOptions options = StringSplitOptions.None) - { - int lowerBound = 0; - //Make sure the length of the search param is not 0 - if(search.IsEmpty) - { - //Return the entire string - yield return data; - } - //No string options - else if (options == 0) - { - do - { - //Capture the first = and store argument + value - int splitIndex = data.IndexOf(search.Span, lowerBound); - //If no split index is found, then return remaining data - if (splitIndex == -1) - { - break; - } - yield return data[lowerBound..splitIndex]; - //Shift the lower window to the end of the last string - lowerBound = splitIndex + search.Length; - } while (true); - //Return remaining data - yield return data[lowerBound..]; - } - //Trim but do not remove empties - else if ((options & StringSplitOptions.RemoveEmptyEntries) == 0) - { - do - { - //Capture the first = and store argument + value - int splitIndex = data.IndexOf(search.Span, lowerBound); - //If no split index is found, then return remaining data - if (splitIndex == -1) - { - break; - } - //trim and return - yield return data.AbsoluteTrim(lowerBound, splitIndex); - //Shift the lower window to the end of the last string - lowerBound = splitIndex + search.Length; - } while (true); - //Return remaining data - yield return data.AbsoluteTrim(lowerBound); - } - //Remove empty entires but do not trim them - else if ((options & StringSplitOptions.TrimEntries) == 0) - { - do - { - //Capture the first = and store argument + value - int splitIndex = data.IndexOf(search.Span, lowerBound); - //If no split index is found, then return remaining data - if (splitIndex == -1) - { - break; - } - //If the split index is the next sequence, then the result is empty, so exclude it - else if(splitIndex > 0) - { - yield return data[lowerBound..splitIndex]; - } - //Shift the lower window to the end of the last string - lowerBound = splitIndex + search.Length; - } while (true); - //Return remaining data if available - if (lowerBound < data.Length) - { - yield return data[lowerBound..]; - } - } - //Must mean remove and trim - else - { - //Get stack varables to pass to trim function - int trimStart, trimEnd; - do - { - //Capture the first = and store argument + value - int splitIndex = data.IndexOf(search.Span, lowerBound); - //If no split index is found, then return remaining data - if (splitIndex == -1) - { - break; - } - //Get stack varables to pass to trim function - trimStart = lowerBound; - trimEnd = splitIndex; //End of the segment is the relative split index + the lower bound of the window - //Trim whitespace chars - data.AbsoluteTrim(ref trimStart, ref trimEnd); - //See if the string has data - if((trimEnd - trimStart) > 0) - { - yield return data[trimStart..trimEnd]; - } - //Shift the lower window to the end of the last string - lowerBound = splitIndex + search.Length; - } while (true); - //Trim remaining - trimStart = lowerBound; - trimEnd = data.Length; - data.AbsoluteTrim(ref trimStart, ref trimEnd); - //If the remaining string is not empty return it - if ((trimEnd - trimStart) > 0) - { - yield return data[trimStart..trimEnd]; - } - } - } - - /// <summary> - /// Trims any leading or trailing <c>'\r'|'\n'|' '</c>(whitespace) characters from the segment - /// </summary> - /// <returns>The trimmed segment</returns> - /// <exception cref="ObjectDisposedException"></exception> - public static VnString TrimCRLF(this VnString data) - { - ReadOnlySpan<char> trimmed = data.AsSpan(); - int start = 0, end = trimmed.Length; - //trim leading \r\n chars - while (start < end) - { - char t = trimmed[start]; - //If character \r or \n slice it off - if (t != '\r' && t != '\n' && t != ' ') { - break; - } - //Shift - start++; - } - //remove trailing crlf characters - while (end > start) - { - char t = trimmed[end - 1]; - //If character \r or \n slice it off - if (t != '\r' && t != '\n' && t != ' ') { - break; - } - end--; - } - return data[start..end]; - } - - /// <summary> - /// Unoptimized character enumerator. You should use <see cref="VnString.AsSpan"/> to enumerate the unerlying data. - /// </summary> - /// <returns>The next character in the sequence</returns> - /// <exception cref="ObjectDisposedException"></exception> - public static IEnumerator<char> GetEnumerator(this VnString data) - { - int index = 0; - while (index < data.Length) - { - yield return data[index++]; - } - } - /// <summary> - /// Converts the current handle to a <see cref="VnString"/>, a zero-alloc immutable wrapper - /// for a memory handle - /// </summary> - /// <param name="handle"></param> - /// <param name="length">The number of characters from the handle to reference (length of the string)</param> - /// <returns>The new <see cref="VnString"/> wrapper</returns> - /// <exception cref="OverflowException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static VnString ToVnString(this MemoryHandle<char> handle, int length) - { - if(handle.Length > int.MaxValue) - { - throw new OverflowException("The handle is larger than 2GB in size"); - } - return VnString.ConsumeHandle(handle, 0, length); - } - /// <summary> - /// Converts the current handle to a <see cref="VnString"/>, a zero-alloc immutable wrapper - /// for a memory handle - /// </summary> - /// <param name="handle"></param> - /// <returns>The new <see cref="VnString"/> wrapper</returns> - /// <exception cref="OverflowException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static VnString ToVnString(this MemoryHandle<char> handle) - { - return VnString.ConsumeHandle(handle, 0, handle.IntLength); - } - /// <summary> - /// Converts the current handle to a <see cref="VnString"/>, a zero-alloc immutable wrapper - /// for a memory handle - /// </summary> - /// <param name="handle"></param> - /// <param name="offset">The offset in characters that represents the begining of the string</param> - /// <param name="length">The number of characters from the handle to reference (length of the string)</param> - /// <returns>The new <see cref="VnString"/> wrapper</returns> - /// <exception cref="OverflowException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static VnString ToVnString(this MemoryHandle<char> handle, -#if TARGET_64_BIT - ulong offset, -#else - int offset, -#endif - int length) - { - if (handle.Length > int.MaxValue) - { - throw new OverflowException("The handle is larger than 2GB in size"); - } - return VnString.ConsumeHandle(handle, offset, length); - } - } -}
\ No newline at end of file diff --git a/Utils/src/IIndexable.cs b/Utils/src/IIndexable.cs deleted file mode 100644 index 129d703..0000000 --- a/Utils/src/IIndexable.cs +++ /dev/null @@ -1,43 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IIndexable.cs -* -* IIndexable.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 -{ - /// <summary> - /// Provides an interface that provides an indexer - /// </summary> - /// <typeparam name="TKey">The lookup Key</typeparam> - /// <typeparam name="TValue">The lookup value</typeparam> - public interface IIndexable<TKey, TValue> - { - /// <summary> - /// Gets or sets the value at the specified index in the collection - /// </summary> - /// <param name="key">The key to lookup the value at</param> - /// <returns>The value at the specified key</returns> - TValue this[TKey key] { get; set;} - } -} 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<T> : ISlindingWindowBuffer<T> - { - private readonly ArrayPool<T> _pool; - private T[] _buffer; - - public ArrayPoolStreamBuffer(ArrayPool<T> pool, int bufferSize) - { - _pool = pool; - _buffer = _pool.Rent(bufferSize); - } - - public int WindowStartPos { get; set; } - public int WindowEndPos { get; set; } - - public Memory<T> 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 -{ - /// <summary> - /// Provides basic stream support sync/async stream operations to a - /// backing stream with virtual event methods. Provides a pass-through - /// as best as possbile. - /// </summary> - public abstract class BackingStream<T> : Stream where T: Stream - { - /// <summary> - /// The backing/underlying stream operations are being performed on - /// </summary> - protected T BaseStream { get; set; } - /// <summary> - /// A value that will cause all calls to write to throw <see cref="NotSupportedException"/> - /// </summary> - protected bool ForceReadOnly { get; set; } - ///<inheritdoc/> - public override bool CanRead => BaseStream.CanRead; - ///<inheritdoc/> - public override bool CanSeek => BaseStream.CanSeek; - ///<inheritdoc/> - public override bool CanWrite => BaseStream.CanWrite && !ForceReadOnly; - ///<inheritdoc/> - public override long Length => BaseStream.Length; - ///<inheritdoc/> - public override int WriteTimeout { get => BaseStream.WriteTimeout; set => BaseStream.WriteTimeout = value; } - ///<inheritdoc/> - public override int ReadTimeout { get => BaseStream.ReadTimeout; set => BaseStream.ReadTimeout = value; } - ///<inheritdoc/> - public override long Position { get => BaseStream.Position; set => BaseStream.Position = value; } - ///<inheritdoc/> - public override void Flush() - { - BaseStream.Flush(); - OnFlush(); - } - ///<inheritdoc/> - public override int Read(byte[] buffer, int offset, int count) => BaseStream.Read(buffer, offset, count); - ///<inheritdoc/> - public override int Read(Span<byte> buffer) => BaseStream.Read(buffer); - ///<inheritdoc/> - public override long Seek(long offset, SeekOrigin origin) => BaseStream.Seek(offset, origin); - ///<inheritdoc/> - public override void SetLength(long value) => BaseStream.SetLength(value); - ///<inheritdoc/> - 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); - } - ///<inheritdoc/> - public override void Write(ReadOnlySpan<byte> buffer) - { - if (ForceReadOnly) - { - throw new NotSupportedException("Stream is set to readonly mode"); - } - BaseStream.Write(buffer); - //Call onwrite function - OnWrite(buffer.Length); - } - ///<inheritdoc/> - public override void Close() - { - BaseStream.Close(); - //Call on close function - OnClose(); - } - - /// <summary> - /// Raised directly after the base stream is closed, when a call to close is made - /// </summary> - protected virtual void OnClose() { } - /// <summary> - /// Raised directly after the base stream is flushed, when a call to flush is made - /// </summary> - protected virtual void OnFlush() { } - /// <summary> - /// Raised directly after a successfull write operation. - /// </summary> - /// <param name="count">The number of bytes written to the stream</param> - protected virtual void OnWrite(int count) { } - - ///<inheritdoc/> - public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - return BaseStream.ReadAsync(buffer, offset, count, cancellationToken); - } - ///<inheritdoc/> - public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default) - { - return BaseStream.ReadAsync(buffer, cancellationToken); - } - ///<inheritdoc/> - public override void CopyTo(Stream destination, int bufferSize) => BaseStream.CopyTo(destination, bufferSize); - ///<inheritdoc/> - public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) - { - return BaseStream.CopyToAsync(destination, bufferSize, cancellationToken); - } - ///<inheritdoc/> - 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); - } - ///<inheritdoc/> - public override async ValueTask WriteAsync(ReadOnlyMemory<byte> 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); - } - ///<inheritdoc/> - public override async Task FlushAsync(CancellationToken cancellationToken) - { - await BaseStream.FlushAsync(cancellationToken); - //Call onflush - OnFlush(); - } - - ///<inheritdoc/> - 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 -{ - /// <summary> - /// Contains cross-platform optimized filesystem operations. - /// </summary> - 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(); - /// <summary> - /// Determines if a file exists. If application is current running in the Windows operating system, Shlwapi.PathFileExists is invoked, - /// otherwise <see cref="File.Exists(string?)"/> is invoked - /// </summary> - /// <param name="filePath">the path to the file</param> - /// <returns>True if the file can be opened, false otherwise</returns> - 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); - } - } - } - - /// <summary> - /// If Windows is detected at load time, gets the attributes for the specified file. - /// </summary> - /// <param name="filePath">The path to the existing file</param> - /// <returns>The attributes of the file </returns> - /// <exception cref="PathTooLongException"></exception> - /// <exception cref="FileNotFoundException"></exception> - /// <exception cref="UnauthorizedAccessException"></exception> - 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 -{ - /// <summary> - /// A data structure that represents a sliding window over a buffer - /// for resetable forward-only reading or writing - /// </summary> - /// <typeparam name="T">The accumuation data type</typeparam> - public interface IDataAccumulator<T> - { - /// <summary> - /// Gets the number of available items within the buffer - /// </summary> - int AccumulatedSize { get; } - /// <summary> - /// The number of elements remaining in the buffer - /// </summary> - int RemainingSize { get; } - /// <summary> - /// The remaining space in the internal buffer as a contiguous segment - /// </summary> - Span<T> Remaining { get; } - /// <summary> - /// The buffer window over the accumulated data - /// </summary> - Span<T> Accumulated { get; } - - /// <summary> - /// Advances the accumulator buffer window by the specified amount - /// </summary> - /// <param name="count">The number of elements accumulated</param> - void Advance(int count); - - /// <summary> - /// Resets the internal state of the accumulator - /// </summary> - 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 -{ - /// <summary> - /// Represents a sliding window buffer for reading/wiriting data - /// </summary> - /// <typeparam name="T"></typeparam> - public interface ISlindingWindowBuffer<T> : IDataAccumulator<T> - { - /// <summary> - /// The number of elements remaining in the buffer - /// </summary> - int IDataAccumulator<T>.RemainingSize => Buffer.Length - WindowEndPos; - /// <summary> - /// The remaining space in the internal buffer as a contiguous segment - /// </summary> - Span<T> IDataAccumulator<T>.Remaining => RemainingBuffer.Span; - /// <summary> - /// The buffer window over the accumulated data - /// </summary> - Span<T> IDataAccumulator<T>.Accumulated => AccumulatedBuffer.Span; - /// <summary> - /// Gets the number of available items within the buffer - /// </summary> - int IDataAccumulator<T>.AccumulatedSize => WindowEndPos - WindowStartPos; - - /// <summary> - /// The starting positon of the available data within the buffer - /// </summary> - int WindowStartPos { get; } - /// <summary> - /// The ending position of the available data within the buffer - /// </summary> - int WindowEndPos { get; } - /// <summary> - /// Buffer memory wrapper - /// </summary> - Memory<T> Buffer { get; } - - /// <summary> - /// Releases resources used by the current instance - /// </summary> - void Close(); - /// <summary> - /// <para> - /// Advances the begining of the accumulated data window. - /// </para> - /// <para> - /// This method is used during reading to singal that data - /// has been read from the internal buffer and the - /// accumulator window can be shifted. - /// </para> - /// </summary> - /// <param name="count">The number of elements to shift by</param> - void AdvanceStart(int count); - - /// <summary> - /// Gets a window within the buffer of available buffered data - /// </summary> - Memory<T> AccumulatedBuffer => Buffer[WindowStartPos..WindowEndPos]; - /// <summary> - /// Gets the available buffer window to write data to - /// </summary> - Memory<T> 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 -{ - /// <summary> - /// Represents a streaming text reader with internal buffers - /// </summary> - public interface IVnTextReader - { - /// <summary> - /// The base stream to read data from - /// </summary> - Stream BaseStream { get; } - /// <summary> - /// The character encoding used by the TextReader - /// </summary> - Encoding Encoding { get; } - /// <summary> - /// Number of available bytes of buffered data within the current buffer window - /// </summary> - int Available { get; } - /// <summary> - /// Gets or sets the line termination used to deliminate a line of data - /// </summary> - ReadOnlyMemory<byte> LineTermination { get; } - /// <summary> - /// The unread/available data within the internal buffer - /// </summary> - Span<byte> BufferedDataWindow { get; } - /// <summary> - /// Shifts the sliding buffer window by the specified number of bytes. - /// </summary> - /// <param name="count">The number of bytes read from the buffer</param> - void Advance(int count); - /// <summary> - /// Reads data from the stream into the remaining buffer space for processing - /// </summary> - void FillBuffer(); - /// <summary> - /// 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 - /// </summary> - /// <returns>The remaining buffer space if any</returns> - 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 -{ - /// <summary> - /// Represents a lazily loaded file stored in memory, with a change mointor - /// that reloads the template if the file was modified in the filesystem - /// </summary> - public abstract class InMemoryTemplate : VnDisposeable - { - protected ManualResetEventSlim TemplateLock; - private readonly FileSystemWatcher? Watcher; - private bool Modified; - private VnMemoryStream templateBuffer; - protected readonly FileInfo TemplateFile; - - /// <summary> - /// Gets the name of the template - /// </summary> - public abstract string TemplateName { get; } - - /// <summary> - /// Creates a new in-memory copy of a file that will detect changes and refresh - /// </summary> - /// <param name="listenForChanges">Should changes to the template file be moniored for changes, and reloaded as necessary</param> - /// <param name="path">The path of the file template</param> - 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(); - } - } - - /// <summary> - /// Gets a cached copy of the template data - /// </summary> - 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(); - } - /// <summary> - /// Updates the internal copy of the file to its memory representation - /// </summary> - 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; - } - /// <summary> - /// Updates the internal copy of the file to its memory representation, asynchronously - /// </summary> - /// <param name="cancellationToken"></param> - /// <returns>A task that completes when the file has been copied into memory</returns> - 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; - } - - /// <summary> - /// Invoked when the template file has been modifed. Note: This event is raised - /// while the <see cref="TemplateLock"/> is held. - /// </summary> - protected abstract void OnModifed(); - - ///<inheritdoc/> - 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 -{ - /// <summary> - /// Represents an open directory within an <see cref="IsolatedStorageFile"/> store for which files can be created, opened, or deleted. - /// </summary> - public sealed class IsolatedStorageDirectory : IsolatedStorage - { - 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> - 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); - } - - /// <summary> - /// Creates a file by its path name within the currnet directory - /// </summary> - /// <param name="fileName">The name of the file</param> - /// <returns>The open file</returns> - /// <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)); - } - /// <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)); - } - /// <summary> - /// Opens a file that exists within the current directory - /// </summary> - /// <param name="fileName">Name with extension of the file</param> - /// <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); - } - /// <summary> - /// Opens a file that exists within the current directory - /// </summary> - /// <param name="fileName">Name with extension of the file</param> - /// <param name="mode">File mode</param> - /// <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); - } - - /// <summary> - /// Determiens if the specified file path refers to an existing file within the directory - /// </summary> - /// <param name="fileName">The name of the file to search for</param> - /// <returns>True if the file exists within the current directory</returns> - /// <exception cref="ArgumentNullException"></exception> - /// <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)); - } - - /// <summary> - /// Removes the directory and its contents from the store - /// </summary> - 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); - - /// <summary> - /// 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 - /// </summary> - /// <param name="directoryName">The name of the child directory</param> - /// <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) - { - 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 -{ - /// <summary> - /// Extention methods for <see cref="ISlindingWindowBuffer{T}"/> - /// </summary> - public static class SlidingWindowBufferExtensions - { - /// <summary> - /// Shifts/resets the current buffered data window down to the - /// begining of the buffer if the buffer window is shifted away - /// from the begining. - /// </summary> - /// <returns>The number of bytes of available space in the buffer</returns> - public static ERRNO CompactBufferWindow<T>(this ISlindingWindowBuffer<T> 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<T> buffer = sBuf.Buffer.Span; - //Get data within window - Span<T> 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; - } - - /// <summary> - /// Appends the specified data to the end of the buffer - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="sBuf"></param> - /// <param name="val">The value to append to the end of the buffer</param> - /// <exception cref="IndexOutOfRangeException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Append<T>(this IDataAccumulator<T> sBuf, T val) - { - //Set the value at first position - sBuf.Remaining[0] = val; - //Advance by 1 - sBuf.Advance(1); - } - /// <summary> - /// Appends the specified data to the end of the buffer - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="sBuf"></param> - /// <param name="val">The value to append to the end of the buffer</param> - /// <exception cref="ArgumentException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Append<T>(this IDataAccumulator<T> sBuf, ReadOnlySpan<T> val) - { - val.CopyTo(sBuf.Remaining); - sBuf.Advance(val.Length); - } - /// <summary> - /// Formats and appends a value type to the accumulator with proper endianess - /// </summary> - /// <typeparam name="T">The value type to appent</typeparam> - /// <param name="accumulator">The binary accumulator to append</param> - /// <param name="value">The value type to append</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Append<T>(this IDataAccumulator<byte> accumulator, T value) where T: unmanaged - { - //Use forward reader for the memory extension to append a value type to a binary accumulator - ForwardOnlyWriter<byte> w = new(accumulator.Remaining); - w.Append(value); - accumulator.Advance(w.Written); - } - - /// <summary> - /// Attempts to write as much data as possible to the remaining space - /// in the buffer and returns the number of bytes accumulated. - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="accumulator"></param> - /// <param name="value">The value to accumulate</param> - /// <returns>The number of bytes accumulated</returns> - public static ERRNO TryAccumulate<T>(this IDataAccumulator<T> accumulator, ReadOnlySpan<T> 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; - } - - /// <summary> - /// Appends a <see cref="ISpanFormattable"/> instance to the end of the accumulator - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="accumulator"></param> - /// <param name="formattable">The formattable instance to write to the accumulator</param> - /// <param name="format">The format arguments</param> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Append<T>(this IDataAccumulator<char> accumulator, in T formattable, ReadOnlySpan<char> format = default) where T : struct, ISpanFormattable - { - ForwardOnlyWriter<char> writer = new(accumulator.Remaining); - writer.Append(formattable, format); - accumulator.Advance(writer.Written); - } - - /// <summary> - /// Uses the remaining data buffer to compile a <see cref="IStringSerializeable"/> - /// instance, then advances the accumulator by the number of characters used. - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="accumulator"></param> - /// <param name="compileable">The <see cref="IStringSerializeable"/> instance to compile</param> - public static void Append<T>(this IDataAccumulator<char> 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); - } - - /// <summary> - /// Reads available data from the current window and writes as much as possible it to the supplied buffer - /// and advances the buffer window - /// </summary> - /// <typeparam name="T">Element type</typeparam> - /// <param name="sBuf"></param> - /// <param name="buffer">The output buffer to write data to</param> - /// <returns>The number of elements written to the buffer</returns> - public static ERRNO Read<T>(this ISlindingWindowBuffer<T> sBuf, in Span<T> 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; - } - - /// <summary> - /// Fills the remaining window space of the current accumulator with - /// data from the specified stream asynchronously. - /// </summary> - /// <param name="accumulator"></param> - /// <param name="input">The stream to read data from</param> - /// <param name="cancellationToken">A token to cancel the operation</param> - /// <returns>A value task representing the operation</returns> - public static async ValueTask AccumulateDataAsync(this ISlindingWindowBuffer<byte> accumulator, Stream input, CancellationToken cancellationToken) - { - //Get a buffer from the end of the current window to the end of the buffer - Memory<byte> 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); - } - /// <summary> - /// Fills the remaining window space of the current accumulator with - /// data from the specified stream. - /// </summary> - /// <param name="accumulator"></param> - /// <param name="input">The stream to read data from</param> - public static void AccumulateData(this IDataAccumulator<byte> accumulator, Stream input) - { - //Get a buffer from the end of the current window to the end of the buffer - Span<byte> 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 -{ - /// <summary> - /// Allows for temporary files to be generated, used, then removed from an <see cref="IsolatedStorageFile"/> - /// </summary> - public sealed class TemporayIsolatedFile : BackingStream<IsolatedStorageFileStream> - { - private readonly IsolatedStorageDirectory Storage; - private readonly string Filename; - /// <summary> - /// Creates a new temporary filestream within the specified <see cref="IsolatedStorageFile"/> - /// </summary> - /// <param name="storage">The file store to genreate temporary files within</param> - 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; - - /// <summary> - /// Provides an unmanaged memory stream. Desigend to help reduce garbage collector load for - /// high frequency memory operations. Similar to <see cref="UnmanagedMemoryStream"/> - /// </summary> - public sealed class VnMemoryStream : Stream, ICloneable - { - private long _position; - private long _length; - //Memory - private readonly MemoryHandle<byte> _buffer; - private bool IsReadonly; - //Default owns handle - private readonly bool OwnsHandle = true; - - /// <summary> - /// Creates a new <see cref="VnMemoryStream"/> pointing to the begining of memory, and consumes the handle. - /// </summary> - /// <param name="handle"><see cref="MemoryHandle{T}"/> to consume</param> - /// <param name="length">Length of the stream</param> - /// <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, Int64 length, bool readOnly) - { - handle.ThrowIfClosed(); - return new VnMemoryStream(handle, length, readOnly, true); - } - - /// <summary> - /// Converts a writable <see cref="VnMemoryStream"/> to readonly to allow shallow copies - /// </summary> - /// <param name="stream">The stream to make readonly</param> - /// <returns>The readonly stream</returns> - public static VnMemoryStream CreateReadonly(VnMemoryStream stream) - { - //Set the readonly flag - stream.IsReadonly = true; - //Return the stream - return stream; - } - - /// <summary> - /// Creates a new memory stream - /// </summary> - public VnMemoryStream() : this(Memory.Shared) { } - /// <summary> - /// Create a new memory stream where buffers will be allocated from the specified heap - /// </summary> - /// <param name="heap"><see cref="PrivateHeap"/> to allocate memory from</param> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ArgumentNullException"></exception> - public VnMemoryStream(IUnmangedHeap heap) : this(heap, 0, false) { } - - /// <summary> - /// Creates a new memory stream and pre-allocates the internal - /// buffer of the specified size on the specified heap to avoid resizing. - /// </summary> - /// <param name="heap"><see cref="PrivateHeap"/> to allocate memory from</param> - /// <param name="bufferSize">Number of bytes (length) of the stream if known</param> - /// <param name="zero">Zero memory allocations during buffer expansions</param> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ArgumentNullException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public VnMemoryStream(IUnmangedHeap heap, long bufferSize, bool zero) - { - _ = heap ?? throw new ArgumentNullException(nameof(heap)); - _buffer = heap.Alloc<byte>(bufferSize, zero); - } - - /// <summary> - /// Creates a new memory stream from the data provided - /// </summary> - /// <param name="heap"><see cref="PrivateHeap"/> to allocate memory from</param> - /// <param name="data">Initial data</param> - public VnMemoryStream(IUnmangedHeap heap, ReadOnlySpan<byte> 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; - } - - /// <summary> - /// WARNING: Dangerous constructor, make sure read-only and owns hanlde are set accordingly - /// </summary> - /// <param name="buffer">The buffer to referrence directly</param> - /// <param name="length">The length property of the stream</param> - /// <param name="readOnly">Is the stream readonly (should mostly be true!)</param> - /// <param name="ownsHandle">Does the new stream own the memory -> <paramref name="buffer"/></param> - private VnMemoryStream(MemoryHandle<byte> buffer, long length, bool readOnly, bool ownsHandle) - { - OwnsHandle = ownsHandle; - _buffer = buffer; //Consume the handle - _length = length; //Store length of the buffer - IsReadonly = readOnly; - } - - /// <summary> - /// UNSAFE Number of bytes between position and length. Never negative - /// </summary> - private long LenToPosDiff => Math.Max(_length - _position, 0); - - /// <summary> - /// If the current stream is a readonly stream, creates an unsafe shallow copy for reading only. - /// </summary> - /// <returns>New stream shallow copy of the internal stream</returns> - /// <exception cref="NotSupportedException"></exception> - 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); - } - - /// <summary> - /// Writes data directly to the destination stream from the internal buffer - /// without allocating or copying any data. - /// </summary> - /// <param name="destination">The stream to write data to</param> - /// <param name="bufferSize">The size of the chunks to write to the destination stream</param> - /// <exception cref="IOException"></exception> - 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<byte> span = _buffer.GetOffsetSpan(_position, bytesToRead); - - destination.Write(span); - - //Update position - _position += bytesToRead; - - } while (LenToPosDiff > 0); - } - - /// <summary> - /// Allocates a temporary buffer of the desired size, copies data from the internal - /// buffer and writes it to the destination buffer asynchronously. - /// </summary> - /// <param name="destination">The stream to write output data to</param> - /// <param name="bufferSize">The size of the buffer to use when copying data</param> - /// <param name="cancellationToken">A token to cancel the opreation</param> - /// <returns>A task that resolves when the remaining data in the stream has been written to the destination</returns> - /// <exception cref="IOException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - 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<byte> 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); - - } - - /// <summary> - /// <inheritdoc/> - /// <para> - /// This property is always true - /// </para> - /// </summary> - public override bool CanRead => true; - /// <summary> - /// <inheritdoc/> - /// <para> - /// This propery is always true - /// </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; - - ///<inheritdoc/> - public override long Position - { - get => _position; - set => Seek(value, SeekOrigin.Begin); - } - /// <summary> - /// Closes the stream and frees the internal allocated memory blocks - /// </summary> - public override void Close() - { - //Only dispose buffer if we own it - if (OwnsHandle) - { - _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(new Span<byte>(buffer, offset, count)); - ///<inheritdoc/> - public override int Read(Span<byte> 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 - */ - ///<inheritdoc/> - public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default) - { - //Read synchronously and return a completed task - int read = Read(buffer.Span); - return ValueTask.FromResult(read); - } - ///<inheritdoc/> - public override Task<int> 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); - } - ///<inheritdoc/> - 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"); - } - } - - - /// <summary> - /// 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) - /// </summary> - /// <param name="value">The size of the stream (and internal buffer)</param> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="NotSupportedException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - 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; - } - ///<inheritdoc/> - public override void Write(byte[] buffer, int offset, int count) => Write(new ReadOnlySpan<byte>(buffer, offset, count)); - ///<inheritdoc/> - public override void Write(ReadOnlySpan<byte> 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; - } - ///<inheritdoc/> - 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; - } - ///<inheritdoc/> - public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default) - { - //Write synchronously and return a completed task - Write(buffer.Span); - return ValueTask.CompletedTask; - } - ///<inheritdoc/> - public override void WriteByte(byte value) - { - Span<byte> buf = MemoryMarshal.CreateSpan(ref value, 1); - Write(buf); - } - - /// <summary> - /// Allocates and copies internal buffer to new managed byte[] - /// </summary> - /// <returns>Copy of internal buffer</returns> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - 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; - - } - /// <summary> - /// Returns a <see cref="ReadOnlySpan{T}"/> window over the data within the entire stream - /// </summary> - /// <returns>A <see cref="ReadOnlySpan{T}"/> of the data within the entire stream</returns> - /// <exception cref="OverflowException"></exception> - public ReadOnlySpan<byte> AsSpan() - { - ReadOnlySpan<byte> output = _buffer.Span; - return output[..(int)_length]; - } - - /// <summary> - /// If the current stream is a readonly stream, creates a shallow copy for reading only. - /// </summary> - /// <returns>New stream shallow copy of the internal stream</returns> - /// <exception cref="InvalidOperationException"></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/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 -{ - /// <summary> - /// Binary based buffered text reader, optimized for reading network streams - /// </summary> - public class VnStreamReader : TextReader, IVnTextReader - { - private bool disposedValue; - - private readonly ISlindingWindowBuffer<byte> _buffer; - ///<inheritdoc/> - public virtual Stream BaseStream { get; } - ///<inheritdoc/> - public Encoding Encoding { get; } - - /// <summary> - /// Number of available bytes of buffered data within the current buffer window - /// </summary> - public int Available => _buffer.AccumulatedSize; - /// <summary> - /// Gets or sets the line termination used to deliminate a line of data - /// </summary> - public ReadOnlyMemory<byte> LineTermination { get; set; } - Span<byte> IVnTextReader.BufferedDataWindow => _buffer.Accumulated; - - /// <summary> - /// Creates a new <see cref="TextReader"/> that reads encoded data from the base. - /// Internal buffers will be alloced from <see cref="ArrayPool{T}.Shared"/> - /// </summary> - /// <param name="baseStream">The underlying stream to read data from</param> - /// <param name="enc">The <see cref="Encoding"/> to use when reading from the stream</param> - /// <param name="bufferSize">The size of the internal binary buffer</param> - public VnStreamReader(Stream baseStream, Encoding enc, int bufferSize) - { - BaseStream = baseStream; - Encoding = enc; - //Init a new buffer - _buffer = InitializeBuffer(bufferSize); - } - - /// <summary> - /// Invoked by the constuctor method to allocte the internal buffer with the specified buffer size. - /// </summary> - /// <param name="bufferSize">The requested size of the buffer to alloc</param> - /// <remarks>By default requests the buffer from the <see cref="ArrayPool{T}.Shared"/> instance</remarks> - protected virtual ISlindingWindowBuffer<byte> InitializeBuffer(int bufferSize) => new ArrayPoolStreamBuffer<byte>(ArrayPool<byte>.Shared, bufferSize); - - ///<inheritdoc/> - public override async Task<string?> ReadLineAsync() - { - //If buffered data is available, check for line termination - if (Available > 0) - { - //Get current buffer window - Memory<byte> 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<byte> 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<byte> 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<byte> 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 - } - - ///<inheritdoc/> - public override int Read(char[] buffer, int index, int count) => Read(buffer.AsSpan(index, count)); - ///<inheritdoc/> - public override int Read(Span<char> buffer) - { - if (Available <= 0) - { - return 0; - } - //Get current buffer window - Span<byte> 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); - } - ///<inheritdoc/> - public override void Close() => _buffer.Close(); - ///<inheritdoc/> - protected override void Dispose(bool disposing) - { - if (!disposedValue) - { - Close(); - disposedValue = true; - } - base.Dispose(disposing); - } - - /// <summary> - /// Resets the internal buffer window - /// </summary> - 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 -{ - /// <summary> - /// Provides a memory optimized <see cref="TextWriter"/> implementation. Optimized for writing - /// to network streams - /// </summary> - public class VnStreamWriter : TextWriter - { - private readonly Encoder Enc; - - private readonly ISlindingWindowBuffer<byte> _buffer; - - private bool closed; - - /// <summary> - /// Gets the underlying stream that interfaces with the backing store - /// </summary> - public virtual Stream BaseStream { get; } - ///<inheritdoc/> - public override Encoding Encoding { get; } - - /// <summary> - /// Line termination to use when writing lines to the output - /// </summary> - public ReadOnlyMemory<byte> LineTermination { get; set; } - ///<inheritdoc/> - public override string NewLine - { - get => Encoding.GetString(LineTermination.Span); - set => LineTermination = Encoding.GetBytes(value); - } - - /// <summary> - /// Creates a new <see cref="VnStreamWriter"/> that writes formatted data - /// to the specified base stream - /// </summary> - /// <param name="baseStream">The stream to write data to</param> - /// <param name="encoding">The <see cref="Encoding"/> to use when writing data</param> - /// <param name="bufferSize">The size of the internal buffer used to buffer binary data before writing to the base stream</param> - 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); - } - - /// <summary> - /// Invoked by the constuctor method to allocte the internal buffer with the specified buffer size. - /// </summary> - /// <param name="bufferSize">The requested size of the buffer to alloc</param> - /// <remarks>By default requests the buffer from the <see cref="MemoryPool{T}.Shared"/> instance</remarks> - protected virtual ISlindingWindowBuffer<byte> InitializeBuffer(int bufferSize) => new ArrayPoolStreamBuffer<byte>(ArrayPool<byte>.Shared, bufferSize); - ///<inheritdoc/> - 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); - } - ///<inheritdoc/> - public override void Write(char value) - { - ReadOnlySpan<char> tbuf = MemoryMarshal.CreateSpan(ref value, 0x01); - Write(tbuf); - } - ///<inheritdoc/> - public override void Write(object value) => Write(value.ToString()); - ///<inheritdoc/> - public override void Write(string value) => Write(value.AsSpan()); - ///<inheritdoc/> - public override void Write(ReadOnlySpan<char> buffer) - { - Check(); - - ForwardOnlyReader<char> 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(); - } - ///<inheritdoc/> - public override async Task WriteAsync(ReadOnlyMemory<char> buffer, CancellationToken cancellationToken = default) - { - Check(); - //Create a variable for a character buffer window - bool completed; - ForwardOnlyMemoryReader<char> 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(); - } - - ///<inheritdoc/> - 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); - } - ///<inheritdoc/> - public override void WriteLine(object value) => WriteLine(value.ToString()); - ///<inheritdoc/> - public override void WriteLine(string value) => WriteLine(value.AsSpan()); - ///<inheritdoc/> - public override void WriteLine(ReadOnlySpan<char> buffer) - { - //Write the value itself - Write(buffer); - //Write the line termination - WriteLine(); - } - - ///<inheritdoc/> - ///<exception cref="ObjectDisposedException"></exception> - 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(); - } - } - /// <summary> - /// Asynchronously flushes the internal buffers to the <see cref="BaseStream"/>, and resets the internal buffer state - /// </summary> - /// <returns>A <see cref="ValueTask"/> that represents the asynchronous flush operation</returns> - /// <exception cref="ObjectDisposedException"></exception> - 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(); - } - } - - ///<inheritdoc/> - public override Task FlushAsync() => FlushWriterAsync().AsTask(); - - /// <summary> - /// Resets internal properies for resuse - /// </summary> - protected void Reset() - { - _buffer.Reset(); - Enc.Reset(); - } - ///<inheritdoc/> - 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; - } - } - ///<inheritdoc/> - 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"); - } - } - ///<inheritdoc/> - 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 -{ - /// <summary> - /// Extension methods to help reuse code for used TextReader implementations - /// </summary> - 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 - */ - - /// <summary> - /// Attempts to read a line from the stream and store it in the specified buffer - /// </summary> - /// <param name="reader"></param> - /// <param name="charBuffer">The character buffer to write data to</param> - /// <returns>Returns the number of bytes read, <see cref="E_BUFFER_TOO_SMALL"/> - /// if the buffer was not large enough, 0 if no data was available</returns> - /// <exception cref="OutOfMemoryException"></exception> - /// <remarks>Allows reading lines of data from the stream without allocations</remarks> - public static ERRNO ReadLine<T>(this ref T reader, Span<char> charBuffer) where T:struct, IVnTextReader - { - return readLine(ref reader, charBuffer); - } - /// <summary> - /// Attempts to read a line from the stream and store it in the specified buffer - /// </summary> - /// <param name="reader"></param> - /// <param name="charBuffer">The character buffer to write data to</param> - /// <returns>Returns the number of bytes read, <see cref="E_BUFFER_TOO_SMALL"/> - /// if the buffer was not large enough, 0 if no data was available</returns> - /// <exception cref="OutOfMemoryException"></exception> - /// <remarks>Allows reading lines of data from the stream without allocations</remarks> - public static ERRNO ReadLine<T>(this T reader, Span<char> charBuffer) where T : class, IVnTextReader - { - return readLine(ref reader, charBuffer); - } - - /// <summary> - /// Fill a buffer with reamining buffered data - /// </summary> - /// <param name="reader"></param> - /// <param name="buffer">Buffer to copy data to</param> - /// <param name="offset">Offset in buffer to begin writing</param> - /// <param name="count">Number of bytes to read</param> - /// <returns>The number of bytes copied to the input buffer</returns> - public static int ReadRemaining<T>(this ref T reader, byte[] buffer, int offset, int count) where T : struct, IVnTextReader - { - return reader.ReadRemaining(buffer.AsSpan(offset, count)); - } - /// <summary> - /// Fill a buffer with reamining buffered data - /// </summary> - /// <param name="reader"></param> - /// <param name="buffer">Buffer to copy data to</param> - /// <param name="offset">Offset in buffer to begin writing</param> - /// <param name="count">Number of bytes to read</param> - /// <returns>The number of bytes copied to the input buffer</returns> - public static int ReadRemaining<T>(this T reader, byte[] buffer, int offset, int count) where T : class, IVnTextReader - { - return reader.ReadRemaining(buffer.AsSpan(offset, count)); - } - - /// <summary> - /// Fill a buffer with reamining buffered data, up to - /// the size of the supplied buffer - /// </summary> - /// <param name="reader"></param> - /// <param name="buffer">Buffer to copy data to</param> - /// <returns>The number of bytes copied to the input buffer</returns> - /// <remarks>You should use the <see cref="IVnTextReader.Available"/> property to know how much remaining data is buffered</remarks> - public static int ReadRemaining<T>(this ref T reader, Span<byte> buffer) where T : struct, IVnTextReader - { - return readRemaining(ref reader, buffer); - } - /// <summary> - /// Fill a buffer with reamining buffered data, up to - /// the size of the supplied buffer - /// </summary> - /// <param name="reader"></param> - /// <param name="buffer">Buffer to copy data to</param> - /// <returns>The number of bytes copied to the input buffer</returns> - /// <remarks>You should use the <see cref="IVnTextReader.Available"/> property to know how much remaining data is buffered</remarks> - public static int ReadRemaining<T>(this T reader, Span<byte> buffer) where T : class, IVnTextReader - { - return readRemaining(ref reader, buffer); - } - - private static ERRNO readLine<T>(ref T reader, Span<char> 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<byte> LineTermination = reader.LineTermination.Span; - //If buffered data is available, check for line termination - if (reader.Available > 0) - { - //Get current buffer window - ReadOnlySpan<byte> 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<byte> 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<byte> 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<byte> 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<T>(ref T reader, Span<byte> 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<byte> 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 -{ - /// <summary> - /// A basic accumulator style write buffered stream - /// </summary> - public class WriteOnlyBufferedStream : Stream - { - private readonly ISlindingWindowBuffer<byte> _buffer; - private readonly bool LeaveOpen; - - /// <summary> - /// Gets the underlying stream that interfaces with the backing store - /// </summary> - public Stream BaseStream { get; init; } - - /// <summary> - /// Initalizes a new <see cref="WriteOnlyBufferedStream"/> using the - /// specified backing stream, using the specified buffer size, and - /// optionally leaves the stream open - /// </summary> - /// <param name="baseStream">The backing stream to write buffered data to</param> - /// <param name="bufferSize">The size of the internal buffer</param> - /// <param name="leaveOpen">A value indicating of the stream should be left open when the buffered stream is closed</param> - public WriteOnlyBufferedStream(Stream baseStream, int bufferSize, bool leaveOpen = false) - { - BaseStream = baseStream; - //Create buffer - _buffer = InitializeBuffer(bufferSize); - LeaveOpen = leaveOpen; - } - /// <summary> - /// Invoked by the constuctor method to allocte the internal buffer with the specified buffer size. - /// </summary> - /// <param name="bufferSize">The requested size of the buffer to alloc</param> - /// <remarks>By default requests the buffer from the <see cref="ArrayPool{T}.Shared"/> instance</remarks> - protected virtual ISlindingWindowBuffer<byte> InitializeBuffer(int bufferSize) - { - return new ArrayPoolStreamBuffer<byte>(ArrayPool<byte>.Shared, bufferSize); - } - - ///<inheritdoc/> - public override void Close() - { - try - { - //Make sure the buffer is empty - WriteBuffer(); - - if (!LeaveOpen) - { - //Dispose stream - BaseStream.Dispose(); - } - } - finally - { - _buffer.Close(); - } - } - ///<inheritdoc/> - 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(); - } - } - - ///<inheritdoc/> - public override void Flush() => WriteBuffer(); - ///<inheritdoc/> - 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(); - } - } - ///<inheritdoc/> - public override void Write(byte[] buffer, int offset, int count) => Write(buffer.AsSpan(offset, count)); - - public override void Write(ReadOnlySpan<byte> buffer) - { - ForwardOnlyReader<byte> 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<byte> buffer, CancellationToken cancellationToken = default) - { - ForwardOnlyMemoryReader<byte> 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); - } - - - /// <summary> - /// Always false - /// </summary> - public override bool CanRead => false; - /// <summary> - /// Always returns false - /// </summary> - public override bool CanSeek => false; - /// <summary> - /// Always true - /// </summary> - public override bool CanWrite => true; - /// <summary> - /// Returns the size of the underlying buffer - /// </summary> - public override long Length => _buffer.AccumulatedSize; - /// <summary> - /// Always throws <see cref="NotSupportedException"/> - /// </summary> - /// <exception cref="NotSupportedException"></exception> - public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } - /// <summary> - /// Always throws <see cref="NotSupportedException"/> - /// </summary> - /// <exception cref="NotSupportedException"></exception> - /// <returns></returns> - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotSupportedException("This stream is not readable"); - } - - /// <summary> - /// Always throws <see cref="NotSupportedException"/> - /// </summary> - /// <exception cref="NotSupportedException"></exception> - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotSupportedException(); - } - - /// <summary> - /// Always throws <see cref="NotSupportedException"/> - /// </summary> - /// <exception cref="NotSupportedException"></exception> - public override void SetLength(long value) - { - throw new NotSupportedException(); - } - - public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - throw new NotImplementedException(); - } - - public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - } -} diff --git a/Utils/src/IObjectStorage.cs b/Utils/src/IObjectStorage.cs deleted file mode 100644 index 5c99cd8..0000000 --- a/Utils/src/IObjectStorage.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IObjectStorage.cs -* -* IObjectStorage.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 -{ - /// <summary> - /// This object will provide methods for storing and retreiving objects by key-value pairing - /// </summary> - public interface IObjectStorage - { - /// <summary> - /// Attempts to retrieve the specified object from storage - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="key">Key for storage</param> - /// <returns>The object in storage, or T.default if object is not found</returns> - public T GetObject<T>(string key); - - /// <summary> - /// Stores the specified object with the specified key - /// </summary> - /// <typeparam name="T"></typeparam> - /// <param name="key">Key paired with object</param> - /// <param name="obj">Object to store</param> - public void SetObject<T>(string key, T obj); - } -}
\ No newline at end of file diff --git a/Utils/src/Logging/ILogProvider.cs b/Utils/src/Logging/ILogProvider.cs deleted file mode 100644 index 55dbd6f..0000000 --- a/Utils/src/Logging/ILogProvider.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ILogProvider.cs -* -* ILogProvider.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.Logging -{ - /// <summary> - /// Self-contained logging interface that allows for applications events to be written to an - /// output source - /// </summary> - public interface ILogProvider - { - /// <summary> - /// Flushes any buffers to the output source - /// </summary> - abstract void Flush(); - - /// <summary> - /// Writes the string to the log with the specified priority log level - /// </summary> - /// <param name="level">The log priority level</param> - /// <param name="value">The message to print</param> - void Write(LogLevel level, string value); - /// <summary> - /// Writes the exception and optional string to the log with the specified priority log level - /// </summary> - /// <param name="level">The log priority level</param> - /// <param name="exception">An exception object to write</param> - /// <param name="value">The message to print</param> - void Write(LogLevel level, Exception exception, string value = ""); - /// <summary> - /// Writes the template string and params arguments to the log with the specified priority log level - /// </summary> - /// <param name="level">The log priority level</param> - /// <param name="value">The log template string</param> - /// <param name="args">Variable length array of objects to log with the specified templatre</param> - void Write(LogLevel level, string value, params object?[] args); - /// <summary> - /// Writes the template string and params arguments to the log with the specified priority log level - /// </summary> - /// <param name="level">The log priority level</param> - /// <param name="value">The log template string</param> - /// <param name="args">Variable length array of objects to log with the specified templatre</param> - void Write(LogLevel level, string value, params ValueType[] args); - - /// <summary> - /// Gets the underlying log source - /// </summary> - /// <returns>The underlying log source</returns> - object GetLogProvider(); - /// <summary> - /// Gets the underlying log source - /// </summary> - /// <returns>The underlying log source</returns> - public virtual T GetLogProvider<T>() => (T)GetLogProvider(); - } -} diff --git a/Utils/src/Logging/LogLevel.cs b/Utils/src/Logging/LogLevel.cs deleted file mode 100644 index 1851c26..0000000 --- a/Utils/src/Logging/LogLevel.cs +++ /dev/null @@ -1,33 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: LogLevel.cs -* -* LogLevel.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.Logging -{ - public enum LogLevel - { - Verbose, Debug, Information, Warning, Error, Fatal - } -} diff --git a/Utils/src/Logging/LoggerExtensions.cs b/Utils/src/Logging/LoggerExtensions.cs deleted file mode 100644 index cd314ed..0000000 --- a/Utils/src/Logging/LoggerExtensions.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: LoggerExtensions.cs -* -* LoggerExtensions.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/. -*/ - -#pragma warning disable CA1062 // Validate arguments of public methods - -using System; - -namespace VNLib.Utils.Logging -{ - /// <summary> - /// Extension helper methods for writing logs to a <see cref="ILogProvider"/> - /// </summary> - public static class LoggerExtensions - { - public static void Debug(this ILogProvider log, Exception exp, string value = "") => log.Write(LogLevel.Debug, exp, value); - public static void Debug(this ILogProvider log, string value) => log.Write(LogLevel.Debug, value); - public static void Debug(this ILogProvider log, string format, params object?[] args) => log.Write(LogLevel.Debug, format, args); - public static void Debug(this ILogProvider log, string format, params ValueType[] args) => log.Write(LogLevel.Debug, format, args); - public static void Error(this ILogProvider log, Exception exp, string value = "") => log.Write(LogLevel.Error, exp, value); - public static void Error(this ILogProvider log, string value) => log.Write(LogLevel.Error, value); - public static void Error(this ILogProvider log, string format, params object?[] args) => log.Write(LogLevel.Error, format, args); - public static void Fatal(this ILogProvider log, Exception exp, string value = "") => log.Write(LogLevel.Fatal, exp, value); - public static void Fatal(this ILogProvider log, string value) => log.Write(LogLevel.Fatal, value); - public static void Fatal(this ILogProvider log, string format, params object?[] args) => log.Write(LogLevel.Fatal, format, args); - public static void Fatal(this ILogProvider log, string format, params ValueType[] args) => log.Write(LogLevel.Fatal, format, args); - public static void Information(this ILogProvider log, Exception exp, string value = "") => log.Write(LogLevel.Information, exp, value); - public static void Information(this ILogProvider log, string value) => log.Write(LogLevel.Information, value); - public static void Information(this ILogProvider log, string format, params object?[] args) => log.Write(LogLevel.Information, format, args); - public static void Information(this ILogProvider log, string format, params ValueType[] args) => log.Write(LogLevel.Information, format, args); - public static void Verbose(this ILogProvider log, Exception exp, string value = "") => log.Write(LogLevel.Verbose, exp, value); - public static void Verbose(this ILogProvider log, string value) => log.Write(LogLevel.Verbose, value); - public static void Verbose(this ILogProvider log, string format, params object?[] args) => log.Write(LogLevel.Verbose, format, args); - public static void Verbose(this ILogProvider log, string format, params ValueType[] args) => log.Write(LogLevel.Verbose, format, args); - public static void Warn(this ILogProvider log, Exception exp, string value = "") => log.Write(LogLevel.Warning, exp, value); - public static void Warn(this ILogProvider log, string value) => log.Write(LogLevel.Warning, value); - public static void Warn(this ILogProvider log, string format, params object?[] args) => log.Write(LogLevel.Warning, format, args); - public static void Warn(this ILogProvider log, string format, params ValueType[] args) => log.Write(LogLevel.Warning, format, args); - } -} diff --git a/Utils/src/Memory/Caching/ICacheHolder.cs b/Utils/src/Memory/Caching/ICacheHolder.cs deleted file mode 100644 index 19eee64..0000000 --- a/Utils/src/Memory/Caching/ICacheHolder.cs +++ /dev/null @@ -1,45 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ICacheHolder.cs -* -* ICacheHolder.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.Memory.Caching -{ - /// <summary> - /// Exposes basic control of classes that manage private caches - /// </summary> - public interface ICacheHolder - { - /// <summary> - /// Clears all held caches without causing application stopping effects. - /// </summary> - /// <remarks>This is a safe "light" cache clear</remarks> - void CacheClear(); - /// <summary> - /// Performs all necessary actions to clear all held caches immediatly. - /// </summary> - /// <remarks>A "hard" cache clear/reset regardless of cost</remarks> - void CacheHardClear(); - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/Caching/ICacheable.cs b/Utils/src/Memory/Caching/ICacheable.cs deleted file mode 100644 index 37575cc..0000000 --- a/Utils/src/Memory/Caching/ICacheable.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ICacheable.cs -* -* ICacheable.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.Memory.Caching -{ - /// <summary> - /// Represents a cacheable entity with an expiration - /// </summary> - public interface ICacheable : IEquatable<ICacheable> - { - /// <summary> - /// A <see cref="DateTime"/> value that the entry is no longer valid - /// </summary> - DateTime Expires { get; set; } - - /// <summary> - /// Invoked when a collection occurs - /// </summary> - void Evicted(); - } -} diff --git a/Utils/src/Memory/Caching/IObjectRental.cs b/Utils/src/Memory/Caching/IObjectRental.cs deleted file mode 100644 index d9489f4..0000000 --- a/Utils/src/Memory/Caching/IObjectRental.cs +++ /dev/null @@ -1,47 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IObjectRental.cs -* -* IObjectRental.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.Memory.Caching -{ - - /// <summary> - /// A thread safe store for reusing CLR managed objects - /// </summary> - /// <typeparam name="T">The reusable object class</typeparam> - public interface IObjectRental<T> where T: class - { - /// <summary> - /// Gets an object from the store, or creates a new one if none are available - /// </summary> - /// <returns>An instance of <typeparamref name="T"/> from the store if available or a new instance if none were available</returns> - T Rent(); - - /// <summary> - /// Returns a rented object back to the rental store for reuse - /// </summary> - /// <param name="item">The previously rented item</param> - void Return(T item); - } - -}
\ No newline at end of file diff --git a/Utils/src/Memory/Caching/IReusable.cs b/Utils/src/Memory/Caching/IReusable.cs deleted file mode 100644 index 618878f..0000000 --- a/Utils/src/Memory/Caching/IReusable.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IReusable.cs -* -* IReusable.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.Memory.Caching -{ - /// <summary> - /// Allows for use within a <see cref="ReusableStore{T}"/>, this object is intended to be reused heavily - /// </summary> - public interface IReusable - { - /// <summary> - /// The instance should prepare itself for use (or re-use) - /// </summary> - void Prepare(); - /// <summary> - /// The intance is being returned and should determine if it's state is reusabled - /// </summary> - /// <returns>true if the instance can/should be reused, false if it should not be reused</returns> - bool Release(); - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/Caching/LRUCache.cs b/Utils/src/Memory/Caching/LRUCache.cs deleted file mode 100644 index 7e96e0a..0000000 --- a/Utils/src/Memory/Caching/LRUCache.cs +++ /dev/null @@ -1,127 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: LRUCache.cs -* -* LRUCache.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.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace VNLib.Utils.Memory.Caching -{ - /// <summary> - /// A base class for a Least Recently Used cache - /// </summary> - /// <typeparam name="TKey">The key for O(1) lookups</typeparam> - /// <typeparam name="TValue">The value to store within cache</typeparam> - public abstract class LRUCache<TKey, TValue> : LRUDataStore<TKey, TValue> where TKey : notnull - { - ///<inheritdoc/> - protected LRUCache() - {} - ///<inheritdoc/> - protected LRUCache(int initialCapacity) : base(initialCapacity) - {} - ///<inheritdoc/> - protected LRUCache(IEqualityComparer<TKey> keyComparer) : base(keyComparer) - {} - ///<inheritdoc/> - protected LRUCache(int initialCapacity, IEqualityComparer<TKey> keyComparer) : base(initialCapacity, keyComparer) - {} - - /// <summary> - /// The maximum number of items to store in LRU cache - /// </summary> - protected abstract int MaxCapacity { get; } - - /// <summary> - /// Adds a new record to the LRU cache - /// </summary> - /// <param name="item">A <see cref="KeyValuePair{TKey, TValue}"/> to add to the cache store</param> - public override void Add(KeyValuePair<TKey, TValue> item) - { - //See if the store is at max capacity and an item needs to be evicted - if(Count == MaxCapacity) - { - //A record needs to be evicted before a new record can be added - - //Get the oldest node from the list to reuse its instance and remove the old value - LinkedListNode<KeyValuePair<TKey, TValue>> oldNode = List.First!; //not null because count is at max capacity so an item must be at the end of the list - //Store old node value field - KeyValuePair<TKey, TValue> oldRecord = oldNode.Value; - //Remove from lookup - LookupTable.Remove(oldRecord.Key); - //Remove the node - List.RemoveFirst(); - //Reuse the old ll node - oldNode.Value = item; - //add lookup with new key - LookupTable.Add(item.Key, oldNode); - //Add to end of list - List.AddLast(oldNode); - //Invoke evicted method - Evicted(oldRecord); - } - else - { - //Add new item to the list - base.Add(item); - } - } - /// <summary> - /// Attempts to get a value by the given key. - /// </summary> - /// <param name="key">The key identifying the value to store</param> - /// <param name="value">The value to store</param> - /// <returns>A value indicating if the value was found in the store</returns> - public override bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value) - { - //See if the cache contains the value - if(base.TryGetValue(key, out value)) - { - //Cache hit - return true; - } - //Cache miss - if(CacheMiss(key, out value)) - { - //Lookup hit - //Add the record to the store (eviction will happen as necessary - Add(key, value); - return true; - } - //Record does not exist - return false; - } - /// <summary> - /// Invoked when a record is evicted from the cache - /// </summary> - /// <param name="evicted">The record that is being evicted</param> - protected abstract void Evicted(KeyValuePair<TKey, TValue> evicted); - /// <summary> - /// Invoked when an entry was requested and was not found in cache. - /// </summary> - /// <param name="key">The key identifying the record to lookup</param> - /// <param name="value">The found value matching the key</param> - /// <returns>A value indicating if the record was found</returns> - protected abstract bool CacheMiss(TKey key, [NotNullWhen(true)] out TValue? value); - } -} diff --git a/Utils/src/Memory/Caching/LRUDataStore.cs b/Utils/src/Memory/Caching/LRUDataStore.cs deleted file mode 100644 index f564fcc..0000000 --- a/Utils/src/Memory/Caching/LRUDataStore.cs +++ /dev/null @@ -1,232 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: LRUDataStore.cs -* -* LRUDataStore.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.Linq; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - -namespace VNLib.Utils.Memory.Caching -{ - /// <summary> - /// A Least Recently Used store base class for E2E O(1) operations - /// </summary> - /// <typeparam name="TKey">A key used for O(1) lookups</typeparam> - /// <typeparam name="TValue">A value to store</typeparam> - public abstract class LRUDataStore<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>, IReadOnlyCollection<TValue>, IEnumerable<KeyValuePair<TKey, TValue>> - where TKey: notnull - { - /// <summary> - /// A lookup table that provides O(1) access times for key-value lookups - /// </summary> - protected Dictionary<TKey, LinkedListNode<KeyValuePair<TKey, TValue>>> LookupTable { get; } - /// <summary> - /// A linked list that tracks the least recently used item. - /// New items (or recently access items) are moved to the end of the list. - /// The head contains the least recently used item - /// </summary> - protected LinkedList<KeyValuePair<TKey, TValue>> List { get; } - - /// <summary> - /// Initializes an empty <see cref="LRUDataStore{TKey, TValue}"/> - /// </summary> - protected LRUDataStore() - { - LookupTable = new(); - List = new(); - } - /// <summary> - /// Initializes an empty <see cref="LRUDataStore{TKey, TValue}"/> and sets - /// the lookup table's inital capacity - /// </summary> - /// <param name="initialCapacity">LookupTable initial capacity</param> - protected LRUDataStore(int initialCapacity) - { - LookupTable = new(initialCapacity); - List = new(); - } - /// <summary> - /// Initializes an empty <see cref="LRUDataStore{TKey, TValue}"/> and uses the - /// specified keycomparison - /// </summary> - /// <param name="keyComparer">A <see cref="IEqualityComparer{T}"/> used by the Lookuptable to compare keys</param> - protected LRUDataStore(IEqualityComparer<TKey> keyComparer) - { - LookupTable = new(keyComparer); - List = new(); - } - /// <summary> - /// Initializes an empty <see cref="LRUDataStore{TKey, TValue}"/> and uses the - /// specified keycomparison, and sets the lookup table's initial capacity - /// </summary> - /// <param name="initialCapacity">LookupTable initial capacity</param> - /// <param name="keyComparer">A <see cref="IEqualityComparer{T}"/> used by the Lookuptable to compare keys</param> - protected LRUDataStore(int initialCapacity, IEqualityComparer<TKey> keyComparer) - { - LookupTable = new(initialCapacity, keyComparer); - List = new(); - } - - /// <summary> - /// Gets or sets a value within the LRU cache. - /// </summary> - /// <param name="key">The key identifying the value</param> - /// <returns>The value stored at the given key</returns> - /// <remarks>Items are promoted in the store when accessed</remarks> - public virtual TValue this[TKey key] - { - get - { - return TryGetValue(key, out TValue? value) - ? value - : throw new KeyNotFoundException("The item or its key were not found in the LRU data store"); - } - set - { - //If a node by the same key in the store exists, just replace its value - if(LookupTable.TryGetValue(key, out LinkedListNode<KeyValuePair<TKey, TValue>>? oldNode)) - { - //Remove the node before re-adding it - List.Remove(oldNode); - oldNode.Value = new KeyValuePair<TKey, TValue>(key, value); - //Move the item to the front of the list - List.AddLast(oldNode); - } - else - { - //Node does not exist yet so create new one - Add(key, value); - } - } - } - ///<inheritdoc/> - public ICollection<TKey> Keys => LookupTable.Keys; - ///<inheritdoc/> - ///<exception cref="NotImplementedException"></exception> - public ICollection<TValue> Values => throw new NotImplementedException(); - IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => LookupTable.Keys; - IEnumerable<TValue> IReadOnlyDictionary<TKey, TValue>.Values => List.Select(static node => node.Value); - IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator() => List.Select(static node => node.Value).GetEnumerator(); - - /// <summary> - /// Gets the number of items within the LRU store - /// </summary> - public int Count => List.Count; - ///<inheritdoc/> - public abstract bool IsReadOnly { get; } - - /// <summary> - /// Adds the specified record to the store and places it at the end of the LRU queue - /// </summary> - /// <param name="key">The key identifying the record</param> - /// <param name="value">The value to store at the key</param> - public void Add(TKey key, TValue value) - { - //Create new kvp lookup ref - KeyValuePair<TKey, TValue> lookupRef = new(key, value); - //Insert the lookup - Add(lookupRef); - } - ///<inheritdoc/> - public bool Remove(KeyValuePair<TKey, TValue> item) => Remove(item.Key); - ///<inheritdoc/> - IEnumerator IEnumerable.GetEnumerator() => List.GetEnumerator(); - ///<inheritdoc/> - public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) => List.CopyTo(array, arrayIndex); - ///<inheritdoc/> - public virtual bool ContainsKey(TKey key) => LookupTable.ContainsKey(key); - ///<inheritdoc/> - public virtual IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => List.GetEnumerator(); - - /// <summary> - /// Adds the specified record to the store and places it at the end of the LRU queue - /// </summary> - /// <param name="item">The item to add</param> - public virtual void Add(KeyValuePair<TKey, TValue> item) - { - //Init new ll node - LinkedListNode<KeyValuePair<TKey, TValue>> newNode = new(item); - //Insert the new node - LookupTable.Add(item.Key, newNode); - //Add to the end of the linked list - List.AddLast(newNode); - } - /// <summary> - /// Removes all elements from the LRU store - /// </summary> - public virtual void Clear() - { - //Clear lists - LookupTable.Clear(); - List.Clear(); - } - /// <summary> - /// Determines if the <see cref="KeyValuePair{TKey, TValue}"/> exists in the store - /// </summary> - /// <param name="item">The record to search for</param> - /// <returns>True if the key was found in the store and the value equals the stored value, false otherwise</returns> - public virtual bool Contains(KeyValuePair<TKey, TValue> item) - { - if (LookupTable.TryGetValue(item.Key, out LinkedListNode<KeyValuePair<TKey, TValue>>? lookup)) - { - return lookup.Value.Value?.Equals(item.Value) ?? false; - } - return false; - } - ///<inheritdoc/> - public virtual bool Remove(TKey key) - { - //Remove the item from the lookup table and if it exists, remove the node from the list - if(LookupTable.Remove(key, out LinkedListNode<KeyValuePair<TKey, TValue>>? node)) - { - //Remove the new from the list - List.Remove(node); - return true; - } - return false; - } - /// <summary> - /// Tries to get a value from the store with its key. Found items are promoted - /// </summary> - /// <param name="key">The key identifying the value</param> - /// <param name="value">The found value</param> - /// <returns>A value indicating if the element was found in the store</returns> - public virtual bool TryGetValue(TKey key, [NotNullWhen(true)] out TValue? value) - { - //Lookup the - if (LookupTable.TryGetValue(key, out LinkedListNode<KeyValuePair<TKey, TValue>>? val)) - { - //Remove the value from the list and add it to the front of the list - List.Remove(val); - List.AddLast(val); - value = val.Value.Value!; - return true; - } - value = default; - return false; - } - - } -} diff --git a/Utils/src/Memory/Caching/ObjectRental.cs b/Utils/src/Memory/Caching/ObjectRental.cs deleted file mode 100644 index 22aca95..0000000 --- a/Utils/src/Memory/Caching/ObjectRental.cs +++ /dev/null @@ -1,236 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ObjectRental.cs -* -* ObjectRental.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.Threading; -using System.Diagnostics; -using System.Collections; -using System.Collections.Generic; - -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.Memory.Caching -{ - //TODO: implement lock-free object tracking - - /// <summary> - /// Provides concurrent storage for reusable objects to be rented and returned. This class - /// and its members is thread-safe - /// </summary> - /// <typeparam name="T">The data type to reuse</typeparam> - public class ObjectRental<T> : ObjectRental, IObjectRental<T>, ICacheHolder, IEnumerable<T> where T: class - { - /// <summary> - /// The initial data-structure capacity if quota is not defined - /// </summary> - public const int INITIAL_STRUCTURE_SIZE = 50; - - protected readonly SemaphoreSlim StorageLock; - protected readonly Stack<T> Storage; - protected readonly HashSet<T> ContainsStore; - - protected readonly Action<T>? ReturnAction; - protected readonly Action<T>? RentAction; - protected readonly Func<T> Constructor; - /// <summary> - /// Is the object type in the current store implement the Idisposable interface? - /// </summary> - protected readonly bool IsDisposableType; - - /// <summary> - /// The maximum number of objects that will be cached. - /// Once this threshold has been reached, objects are - /// no longer stored - /// </summary> - protected readonly int QuotaLimit; - -#pragma warning disable CS8618 //Internal constructor does not set the constructor function - private ObjectRental(int quota) -#pragma warning restore CS8618 - { - //alloc new stack for rentals - Storage = new(Math.Max(quota, INITIAL_STRUCTURE_SIZE)); - //Hashtable for quick lookups - ContainsStore = new(Math.Max(quota, INITIAL_STRUCTURE_SIZE)); - //Semaphore slim to provide exclusive access - StorageLock = new SemaphoreSlim(1, 1); - //Store quota, if quota is -1, set to int-max to "disable quota" - QuotaLimit = quota == 0 ? int.MaxValue : quota; - //Determine if the type is disposeable and store a local value - IsDisposableType = typeof(IDisposable).IsAssignableFrom(typeof(T)); - } - - /// <summary> - /// Creates a new <see cref="ObjectRental{T}"/> store with the rent/return callback methods - /// </summary> - /// <param name="constructor">The type initializer</param> - /// <param name="rentCb">The pre-retnal preperation action</param> - /// <param name="returnCb">The pre-return cleanup action</param> - /// <param name="quota">The maximum number of elements to cache in the store</param> - protected internal ObjectRental(Func<T> constructor, Action<T>? rentCb, Action<T>? returnCb, int quota) : this(quota) - { - this.RentAction = rentCb; - this.ReturnAction = returnCb; - this.Constructor = constructor; - } - - /// <inheritdoc/> - /// <exception cref="ObjectDisposedException"></exception> - public virtual T Rent() - { - Check(); - //See if we have an available object, if not return a new one by invoking the constructor function - T? rental = default; - //Get lock - using (SemSlimReleaser releader = StorageLock.GetReleaser()) - { - //See if the store contains an item ready to use - if(Storage.TryPop(out T? item)) - { - rental = item; - //Remove the item from the hash table - ContainsStore.Remove(item); - } - } - //If no object was removed from the store, create a new one - rental ??= Constructor(); - //If rental cb is defined, invoke it - RentAction?.Invoke(rental); - return rental; - } - - /// <inheritdoc/> - /// <exception cref="ObjectDisposedException"></exception> - public virtual void Return(T item) - { - Check(); - //Invoke return callback if set - ReturnAction?.Invoke(item); - //Keeps track to know if the element was added or need to be cleaned up - bool wasAdded = false; - using (SemSlimReleaser releader = StorageLock.GetReleaser()) - { - //Check quota limit - if (Storage.Count < QuotaLimit) - { - //Store item if it doesnt exist already - if (ContainsStore.Add(item)) - { - //Store the object - Storage.Push(item); - } - //Set the was added flag - wasAdded = true; - } - } - if (!wasAdded && IsDisposableType) - { - //If the element was not added and is disposeable, we can dispose the element - (item as IDisposable)!.Dispose(); - //Write debug message - Debug.WriteLine("Object rental disposed an object over quota"); - } - } - - /// <remarks> - /// NOTE: If <typeparamref name="T"/> implements <see cref="IDisposable"/> - /// interface, this method does nothing - /// </remarks> - /// <inheritdoc/> - /// <exception cref="ObjectDisposedException"></exception> - public virtual void CacheClear() - { - Check(); - //If the type is disposeable, cleaning can be a long process, so defer to hard clear - if (IsDisposableType) - { - return; - } - //take the semaphore - using SemSlimReleaser releader = StorageLock.GetReleaser(); - //Clear stores - ContainsStore.Clear(); - Storage.Clear(); - } - - /// <inheritdoc/> - /// <exception cref="ObjectDisposedException"></exception> - public virtual void CacheHardClear() - { - Check(); - //take the semaphore - using SemSlimReleaser releader = StorageLock.GetReleaser(); - //If the type is disposable, dispose all elements before clearing storage - if (IsDisposableType) - { - //Dispose all elements - foreach (T element in Storage.ToArray()) - { - (element as IDisposable)!.Dispose(); - } - } - //Clear the storeage - Storage.Clear(); - ContainsStore.Clear(); - } - ///<inheritdoc/> - protected override void Free() - { - StorageLock.Dispose(); - //If the element type is disposable, dispose all elements on a hard clear - if (IsDisposableType) - { - //Get all elements - foreach (T element in Storage.ToArray()) - { - (element as IDisposable)!.Dispose(); - } - } - } - - ///<inheritdoc/> - public IEnumerator<T> GetEnumerator() - { - Check(); - //Enter the semaphore - using SemSlimReleaser releader = StorageLock.GetReleaser(); - foreach (T item in Storage) - { - yield return item; - } - } - - IEnumerator IEnumerable.GetEnumerator() - { - Check(); - //Enter the semaphore - using SemSlimReleaser releader = StorageLock.GetReleaser(); - foreach (T item in Storage) - { - yield return item; - } - } - } - -}
\ No newline at end of file diff --git a/Utils/src/Memory/Caching/ObjectRentalBase.cs b/Utils/src/Memory/Caching/ObjectRentalBase.cs deleted file mode 100644 index 305d93f..0000000 --- a/Utils/src/Memory/Caching/ObjectRentalBase.cs +++ /dev/null @@ -1,155 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ObjectRentalBase.cs -* -* ObjectRentalBase.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.Memory.Caching -{ - /// <summary> - /// Provides concurrent storage for reusable objects to be rented and returned. This class - /// and its members is thread-safe - /// </summary> - public abstract class ObjectRental : VnDisposeable - { - /// <summary> - /// Creates a new <see cref="ObjectRental{T}"/> store - /// </summary> - /// <param name="quota">The maximum number of elements that will be cached</param> - public static ObjectRental<TNew> Create<TNew>(int quota = 0) where TNew : class, new() - { - static TNew constructor() => new(); - return new ObjectRental<TNew>(constructor, null, null, quota); - } - /// <summary> - /// Creates a new <see cref="ObjectRental{T}"/> store with generic rental and return callback handlers - /// </summary> - /// <param name="rentCb">Function responsible for preparing an instance to be rented</param> - /// <param name="returnCb">Function responsible for cleaning up an instance before reuse</param> - /// <param name="quota">The maximum number of elements that will be cached</param> - public static ObjectRental<TNew> Create<TNew>(Action<TNew>? rentCb, Action<TNew>? returnCb, int quota = 0) where TNew : class, new() - { - static TNew constructor() => new(); - return new ObjectRental<TNew>(constructor, rentCb, returnCb, quota); - } - /// <summary> - /// Creates a new <see cref="ObjectRental{T}"/> store with a generic constructor function - /// </summary> - /// <param name="constructor">The function invoked to create a new instance when required</param> - /// <param name="quota">The maximum number of elements that will be cached</param> - /// <returns></returns> - public static ObjectRental<TNew> Create<TNew>(Func<TNew> constructor, int quota = 0) where TNew: class - { - return new ObjectRental<TNew>(constructor, null, null, quota); - } - /// <summary> - /// Creates a new <see cref="ObjectRental{T}"/> store with generic rental and return callback handlers - /// </summary> - /// <param name="constructor">The function invoked to create a new instance when required</param> - /// <param name="rentCb">Function responsible for preparing an instance to be rented</param> - /// <param name="returnCb">Function responsible for cleaning up an instance before reuse</param> - /// <param name="quota">The maximum number of elements that will be cached</param> - public static ObjectRental<TNew> Create<TNew>(Func<TNew> constructor, Action<TNew>? rentCb, Action<TNew>? returnCb, int quota = 0) where TNew : class - { - return new ObjectRental<TNew>(constructor, rentCb, returnCb, quota); - } - - /// <summary> - /// Creates a new <see cref="ThreadLocalObjectStorage{TNew}"/> store with generic rental and return callback handlers - /// </summary> - /// <typeparam name="TNew"></typeparam> - /// <param name="constructor">The function invoked to create a new instance when required</param> - /// <param name="rentCb">Function responsible for preparing an instance to be rented</param> - /// <param name="returnCb">Function responsible for cleaning up an instance before reuse</param> - /// <returns>The initialized store</returns> - public static ThreadLocalObjectStorage<TNew> CreateThreadLocal<TNew>(Func<TNew> constructor, Action<TNew>? rentCb, Action<TNew>? returnCb) where TNew : class - { - return new ThreadLocalObjectStorage<TNew>(constructor, rentCb, returnCb); - } - /// <summary> - /// Creates a new <see cref="ThreadLocalObjectStorage{T}"/> store with generic rental and return callback handlers - /// </summary> - /// <param name="rentCb">Function responsible for preparing an instance to be rented</param> - /// <param name="returnCb">Function responsible for cleaning up an instance before reuse</param> - public static ThreadLocalObjectStorage<TNew> CreateThreadLocal<TNew>(Action<TNew>? rentCb, Action<TNew>? returnCb) where TNew : class, new() - { - static TNew constructor() => new(); - return new ThreadLocalObjectStorage<TNew>(constructor, rentCb, returnCb); - } - /// <summary> - /// Creates a new <see cref="ThreadLocalObjectStorage{T}"/> store - /// </summary> - public static ThreadLocalObjectStorage<TNew> CreateThreadLocal<TNew>() where TNew : class, new() - { - static TNew constructor() => new(); - return new ThreadLocalObjectStorage<TNew>(constructor, null, null); - } - /// <summary> - /// Creates a new <see cref="ThreadLocalObjectStorage{T}"/> store with a generic constructor function - /// </summary> - /// <param name="constructor">The function invoked to create a new instance when required</param> - /// <returns></returns> - public static ThreadLocalObjectStorage<TNew> CreateThreadLocal<TNew>(Func<TNew> constructor) where TNew : class - { - return new ThreadLocalObjectStorage<TNew>(constructor, null, null); - } - - /// <summary> - /// Creates a new <see cref="ReusableStore{T}"/> instance with a parameterless constructor - /// </summary> - /// <typeparam name="T">The <see cref="IReusable"/> type</typeparam> - /// <param name="quota">The maximum number of elements that will be cached</param> - /// <returns></returns> - public static ReusableStore<T> CreateReusable<T>(int quota = 0) where T : class, IReusable, new() - { - static T constructor() => new(); - return new(constructor, quota); - } - /// <summary> - /// Creates a new <see cref="ReusableStore{T}"/> instance with the specified constructor - /// </summary> - /// <typeparam name="T">The <see cref="IReusable"/> type</typeparam> - /// <param name="constructor">The constructor function invoked to create new instances of the <see cref="IReusable"/> type</param> - /// <param name="quota">The maximum number of elements that will be cached</param> - /// <returns></returns> - public static ReusableStore<T> CreateReusable<T>(Func<T> constructor, int quota = 0) where T : class, IReusable => new(constructor, quota); - - /// <summary> - /// Creates a new <see cref="ThreadLocalReusableStore{T}"/> instance with a parameterless constructor - /// </summary> - /// <typeparam name="T">The <see cref="IReusable"/> type</typeparam> - /// <returns></returns> - public static ThreadLocalReusableStore<T> CreateThreadLocalReusable<T>() where T : class, IReusable, new() - { - static T constructor() => new(); - return new(constructor); - } - /// <summary> - /// Creates a new <see cref="ThreadLocalReusableStore{T}"/> instance with the specified constructor - /// </summary> - /// <typeparam name="T">The <see cref="IReusable"/> type</typeparam> - /// <param name="constructor">The constructor function invoked to create new instances of the <see cref="IReusable"/> type</param> - /// <returns></returns> - public static ThreadLocalReusableStore<T> CreateThreadLocalReusable<T>(Func<T> constructor) where T : class, IReusable => new(constructor); - } -} diff --git a/Utils/src/Memory/Caching/ReusableStore.cs b/Utils/src/Memory/Caching/ReusableStore.cs deleted file mode 100644 index aacd012..0000000 --- a/Utils/src/Memory/Caching/ReusableStore.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ReusableStore.cs -* -* ReusableStore.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.Memory.Caching -{ - - /// <summary> - /// A reusable object store that extends <see cref="ObjectRental{T}"/>, that allows for objects to be reused heavily - /// </summary> - /// <typeparam name="T">A reusable object</typeparam> - public class ReusableStore<T> : ObjectRental<T> where T : class, IReusable - { - internal ReusableStore(Func<T> constructor, int quota) :base(constructor, null, null, quota) - {} - ///<inheritdoc/> - public override T Rent() - { - //Rent the object (or create it) - T rental = base.Rent(); - //Invoke prepare function - rental.Prepare(); - //return object - return rental; - } - ///<inheritdoc/> - public override void Return(T item) - { - /* - * Clean up the item by invoking the cleanup function, - * and only return the item for reuse if the caller allows - */ - if (item.Release()) - { - base.Return(item); - } - } - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/Caching/ThreadLocalObjectStorage.cs b/Utils/src/Memory/Caching/ThreadLocalObjectStorage.cs deleted file mode 100644 index 511af24..0000000 --- a/Utils/src/Memory/Caching/ThreadLocalObjectStorage.cs +++ /dev/null @@ -1,76 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ThreadLocalObjectStorage.cs -* -* ThreadLocalObjectStorage.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.Threading; - -namespace VNLib.Utils.Memory.Caching -{ - /// <summary> - /// Derrives from <see cref="ObjectRental{T}"/> to provide object rental syntax for <see cref="ThreadLocal{T}"/> - /// storage - /// </summary> - /// <typeparam name="T">The data type to store</typeparam> - public class ThreadLocalObjectStorage<T> : ObjectRental<T> where T: class - { - protected ThreadLocal<T> Store { get; } - - internal ThreadLocalObjectStorage(Func<T> constructor, Action<T>? rentCb, Action<T>? returnCb) - :base(constructor, rentCb, returnCb, 0) - { - Store = new(Constructor); - } - - /// <summary> - /// "Rents" or creates an object for the current thread - /// </summary> - /// <returns>The new or stored instanced</returns> - /// <exception cref="ObjectDisposedException"></exception> - public override T Rent() - { - Check(); - //Get the tlocal value - T value = Store.Value!; - //Invoke the rent action if set - base.RentAction?.Invoke(value); - return value; - } - - /// <inheritdoc/> - /// <exception cref="ObjectDisposedException"></exception> - public override void Return(T item) - { - Check(); - //Invoke the rent action - base.ReturnAction?.Invoke(item); - } - - ///<inheritdoc/> - protected override void Free() - { - Store.Dispose(); - base.Free(); - } - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/Caching/ThreadLocalReusableStore.cs b/Utils/src/Memory/Caching/ThreadLocalReusableStore.cs deleted file mode 100644 index 83cd4d6..0000000 --- a/Utils/src/Memory/Caching/ThreadLocalReusableStore.cs +++ /dev/null @@ -1,64 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ThreadLocalReusableStore.cs -* -* ThreadLocalReusableStore.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.Memory.Caching -{ - /// <summary> - /// A reusable object store that extends <see cref="ThreadLocalObjectStorage{T}"/>, that allows for objects to be reused heavily - /// in a thread-local cache - /// </summary> - /// <typeparam name="T">A reusable object</typeparam> - public class ThreadLocalReusableStore<T> : ThreadLocalObjectStorage<T> where T: class, IReusable - { - /// <summary> - /// Creates a new <see cref="ThreadLocalReusableStore{T}"/> instance - /// </summary> - internal ThreadLocalReusableStore(Func<T> constructor):base(constructor, null, null) - { } - ///<inheritdoc/> - public override T Rent() - { - //Rent the object (or create it) - T rental = base.Rent(); - //Invoke prepare function - rental.Prepare(); - //return object - return rental; - } - ///<inheritdoc/> - public override void Return(T item) - { - /* - * Clean up the item by invoking the cleanup function, - * and only return the item for reuse if the caller allows - */ - if (item.Release()) - { - base.Return(item); - } - } - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/ForwardOnlyBufferWriter.cs b/Utils/src/Memory/ForwardOnlyBufferWriter.cs deleted file mode 100644 index 0ea507e..0000000 --- a/Utils/src/Memory/ForwardOnlyBufferWriter.cs +++ /dev/null @@ -1,122 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ForwardOnlyBufferWriter.cs -* -* ForwardOnlyBufferWriter.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.Memory -{ - /// <summary> - /// Provides a stack based buffer writer - /// </summary> - public ref struct ForwardOnlyWriter<T> - { - /// <summary> - /// The buffer for writing output data to - /// </summary> - public readonly Span<T> Buffer { get; } - /// <summary> - /// The number of characters written to the buffer - /// </summary> - public int Written { readonly get; set; } - /// <summary> - /// The number of characters remaining in the buffer - /// </summary> - public readonly int RemainingSize => Buffer.Length - Written; - - /// <summary> - /// The remaining buffer window - /// </summary> - public readonly Span<T> Remaining => Buffer[Written..]; - - /// <summary> - /// Creates a new <see cref="ForwardOnlyWriter{T}"/> assigning the specified buffer - /// </summary> - /// <param name="buffer">The buffer to write data to</param> - public ForwardOnlyWriter(in Span<T> buffer) - { - Buffer = buffer; - Written = 0; - } - - /// <summary> - /// Returns a compiled string from the characters written to the buffer - /// </summary> - /// <returns>A string of the characters written to the buffer</returns> - public readonly override string ToString() => Buffer[..Written].ToString(); - - /// <summary> - /// Appends a sequence to the buffer - /// </summary> - /// <param name="data">The data to append to the buffer</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public void Append(ReadOnlySpan<T> data) - { - //Make sure the current window is large enough to buffer the new string - if (data.Length > RemainingSize) - { - throw new ArgumentOutOfRangeException(nameof(Remaining) ,"The internal buffer does not have enough buffer space"); - } - Span<T> window = Buffer[Written..]; - //write data to window - data.CopyTo(window); - //update char position - Written += data.Length; - } - /// <summary> - /// Appends a single item to the buffer - /// </summary> - /// <param name="c">The item to append to the buffer</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public void Append(T c) - { - //Make sure the current window is large enough to buffer the new string - if (RemainingSize == 0) - { - throw new ArgumentOutOfRangeException(nameof(Remaining), "The internal buffer does not have enough buffer space"); - } - //Write data to buffer and increment the buffer position - Buffer[Written++] = c; - } - - /// <summary> - /// Advances the writer forward the specifed number of elements - /// </summary> - /// <param name="count">The number of elements to advance the writer by</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public void Advance(int count) - { - if (count > RemainingSize) - { - throw new ArgumentOutOfRangeException(nameof(Remaining), "The internal buffer does not have enough buffer space"); - } - Written += count; - } - - /// <summary> - /// Resets the writer by setting the <see cref="Written"/> - /// property to 0. - /// </summary> - public void Reset() => Written = 0; - } -} diff --git a/Utils/src/Memory/ForwardOnlyMemoryReader.cs b/Utils/src/Memory/ForwardOnlyMemoryReader.cs deleted file mode 100644 index c850b14..0000000 --- a/Utils/src/Memory/ForwardOnlyMemoryReader.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ForwardOnlyMemoryReader.cs -* -* ForwardOnlyMemoryReader.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.Memory -{ - /// <summary> - /// A mutable structure used to implement a simple foward only - /// reader for a memory segment - /// </summary> - /// <typeparam name="T">The element type</typeparam> - public struct ForwardOnlyMemoryReader<T> - { - private readonly ReadOnlyMemory<T> _segment; - private readonly int _size; - - private int _position; - - /// <summary> - /// Initializes a new <see cref="FordwardOnlyMemoryReader{T}"/> - /// of the specified type using the specified internal buffer - /// </summary> - /// <param name="buffer">The buffer to read from</param> - public ForwardOnlyMemoryReader(in ReadOnlyMemory<T> buffer) - { - _segment = buffer; - _size = buffer.Length; - _position = 0; - } - - /// <summary> - /// The remaining data window - /// </summary> - public readonly ReadOnlyMemory<T> Window => _segment[_position..]; - /// <summary> - /// The number of elements remaining in the window - /// </summary> - public readonly int WindowSize => _size - _position; - - - /// <summary> - /// Advances the window position the specified number of elements - /// </summary> - /// <param name="count">The number of elements to advance the widnow position</param> - public void Advance(int count) => _position += count; - - /// <summary> - /// Resets the sliding window to the begining of the buffer - /// </summary> - public void Reset() => _position = 0; - } -} diff --git a/Utils/src/Memory/ForwardOnlyMemoryWriter.cs b/Utils/src/Memory/ForwardOnlyMemoryWriter.cs deleted file mode 100644 index 4f5286d..0000000 --- a/Utils/src/Memory/ForwardOnlyMemoryWriter.cs +++ /dev/null @@ -1,122 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ForwardOnlyMemoryWriter.cs -* -* ForwardOnlyMemoryWriter.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.Memory -{ - /// <summary> - /// Provides a mutable sliding buffer writer - /// </summary> - public struct ForwardOnlyMemoryWriter<T> - { - /// <summary> - /// The buffer for writing output data to - /// </summary> - public readonly Memory<T> Buffer { get; } - /// <summary> - /// The number of characters written to the buffer - /// </summary> - public int Written { readonly get; set; } - /// <summary> - /// The number of characters remaining in the buffer - /// </summary> - public readonly int RemainingSize => Buffer.Length - Written; - - /// <summary> - /// The remaining buffer window - /// </summary> - public readonly Memory<T> Remaining => Buffer[Written..]; - - /// <summary> - /// Creates a new <see cref="ForwardOnlyWriter{T}"/> assigning the specified buffer - /// </summary> - /// <param name="buffer">The buffer to write data to</param> - public ForwardOnlyMemoryWriter(in Memory<T> buffer) - { - Buffer = buffer; - Written = 0; - } - - /// <summary> - /// Returns a compiled string from the characters written to the buffer - /// </summary> - /// <returns>A string of the characters written to the buffer</returns> - public readonly override string ToString() => Buffer[..Written].ToString(); - - /// <summary> - /// Appends a sequence to the buffer - /// </summary> - /// <param name="data">The data to append to the buffer</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public void Append(ReadOnlyMemory<T> data) - { - //Make sure the current window is large enough to buffer the new string - if (data.Length > RemainingSize) - { - throw new ArgumentOutOfRangeException(nameof(Remaining), "The internal buffer does not have enough buffer space"); - } - Memory<T> window = Buffer[Written..]; - //write data to window - data.CopyTo(window); - //update char position - Written += data.Length; - } - /// <summary> - /// Appends a single item to the buffer - /// </summary> - /// <param name="c">The item to append to the buffer</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public void Append(T c) - { - //Make sure the current window is large enough to buffer the new string - if (RemainingSize == 0) - { - throw new ArgumentOutOfRangeException(nameof(Remaining), "The internal buffer does not have enough buffer space"); - } - //Write data to buffer and increment the buffer position - Buffer.Span[Written++] = c; - } - - /// <summary> - /// Advances the writer forward the specifed number of elements - /// </summary> - /// <param name="count">The number of elements to advance the writer by</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public void Advance(int count) - { - if (count > RemainingSize) - { - throw new ArgumentOutOfRangeException(nameof(count), count, "Cannot advance past the end of the buffer"); - } - Written += count; - } - - /// <summary> - /// Resets the writer by setting the <see cref="Written"/> - /// property to 0. - /// </summary> - public void Reset() => Written = 0; - } -} diff --git a/Utils/src/Memory/ForwardOnlyReader.cs b/Utils/src/Memory/ForwardOnlyReader.cs deleted file mode 100644 index aa268c4..0000000 --- a/Utils/src/Memory/ForwardOnlyReader.cs +++ /dev/null @@ -1,74 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ForwardOnlyReader.cs -* -* ForwardOnlyReader.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.Memory -{ - /// <summary> - /// A mutable structure used to implement a simple foward only - /// reader for a memory segment - /// </summary> - /// <typeparam name="T">The element type</typeparam> - public ref struct ForwardOnlyReader<T> - { - private readonly ReadOnlySpan<T> _segment; - private readonly int _size; - - private int _position; - - /// <summary> - /// Initializes a new <see cref="FordwardOnlyReader{T}"/> - /// of the specified type using the specified internal buffer - /// </summary> - /// <param name="buffer">The buffer to read from</param> - public ForwardOnlyReader(in ReadOnlySpan<T> buffer) - { - _segment = buffer; - _size = buffer.Length; - _position = 0; - } - - /// <summary> - /// The remaining data window - /// </summary> - public readonly ReadOnlySpan <T> Window => _segment[_position..]; - - /// <summary> - /// The number of elements remaining in the window - /// </summary> - public readonly int WindowSize => _size - _position; - - /// <summary> - /// Advances the window position the specified number of elements - /// </summary> - /// <param name="count">The number of elements to advance the widnow position</param> - public void Advance(int count) => _position += count; - - /// <summary> - /// Resets the sliding window to the begining of the buffer - /// </summary> - public void Reset() => _position = 0; - } -} diff --git a/Utils/src/Memory/IMemoryHandle.cs b/Utils/src/Memory/IMemoryHandle.cs deleted file mode 100644 index 75d1cce..0000000 --- a/Utils/src/Memory/IMemoryHandle.cs +++ /dev/null @@ -1,53 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IMemoryHandle.cs -* -* IMemoryHandle.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.Memory -{ - /// <summary> - /// Represents a handle for safe access to memory managed/unamanged memory - /// </summary> - /// <typeparam name="T">The type this handle represents</typeparam> - public interface IMemoryHandle<T> : IDisposable, IPinnable - { - /// <summary> - /// The size of the block as an integer - /// </summary> - /// <exception cref="OverflowException"></exception> - int IntLength { get; } - - /// <summary> - /// The number of elements in the block - /// </summary> - ulong Length { get; } - - /// <summary> - /// Gets the internal block as a span - /// </summary> - Span<T> Span { get; } - } - -} diff --git a/Utils/src/Memory/IStringSerializeable.cs b/Utils/src/Memory/IStringSerializeable.cs deleted file mode 100644 index 12cfe52..0000000 --- a/Utils/src/Memory/IStringSerializeable.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IStringSerializeable.cs -* -* IStringSerializeable.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.Memory -{ - /// <summary> - /// A interface that provides indempodent abstractions for compiling an instance - /// to its representitive string. - /// </summary> - public interface IStringSerializeable - { - /// <summary> - /// Compiles the current instance into its safe string representation - /// </summary> - /// <returns>A string of the desired representation of the current instance</returns> - string Compile(); - /// <summary> - /// Compiles the current instance into its safe string representation, and writes its - /// contents to the specified buffer writer - /// </summary> - /// <param name="writer">The ouput writer to write the serialized representation to</param> - /// <exception cref="OutOfMemoryException"></exception> - void Compile(ref ForwardOnlyWriter<char> writer); - /// <summary> - /// Compiles the current instance into its safe string representation, and writes its - /// contents to the specified buffer writer - /// </summary> - /// <param name="buffer">The buffer to write the serialized representation to</param> - /// <returns>The number of characters written to the buffer</returns> - ERRNO Compile(in Span<char> buffer); - } -} diff --git a/Utils/src/Memory/IUnmangedHeap.cs b/Utils/src/Memory/IUnmangedHeap.cs deleted file mode 100644 index 5d8f4bf..0000000 --- a/Utils/src/Memory/IUnmangedHeap.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IUnmangedHeap.cs -* -* IUnmangedHeap.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.Memory -{ - /// <summary> - /// Abstraction for handling (allocating, resizing, and freeing) blocks of unmanaged memory from an unmanged heap - /// </summary> - public interface IUnmangedHeap : IDisposable - { - /// <summary> - /// Allocates a block of memory from the heap and returns a pointer to the new memory block - /// </summary> - /// <param name="size">The size (in bytes) of the element</param> - /// <param name="elements">The number of elements to allocate</param> - /// <param name="zero">An optional parameter to zero the block of memory</param> - /// <returns></returns> - IntPtr Alloc(UInt64 elements, UInt64 size, bool zero); - - /// <summary> - /// Resizes the allocated block of memory to the new size - /// </summary> - /// <param name="block">The block to resize</param> - /// <param name="elements">The new number of elements</param> - /// <param name="size">The size (in bytes) of the type</param> - /// <param name="zero">An optional parameter to zero the block of memory</param> - void Resize(ref IntPtr block, UInt64 elements, UInt64 size, bool zero); - - /// <summary> - /// Free's a previously allocated block of memory - /// </summary> - /// <param name="block">The memory to be freed</param> - /// <returns>A value indicating if the free operation succeeded</returns> - bool Free(ref IntPtr block); - } -} diff --git a/Utils/src/Memory/Memory.cs b/Utils/src/Memory/Memory.cs deleted file mode 100644 index 822d98c..0000000 --- a/Utils/src/Memory/Memory.cs +++ /dev/null @@ -1,456 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: Memory.cs -* -* Memory.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.Security; -using System.Threading; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.Memory -{ - /// <summary> - /// Provides optimized cross-platform maanged/umanaged safe/unsafe memory operations - /// </summary> - [SecurityCritical] - [ComVisible(false)] - public unsafe static class Memory - { - public const string SHARED_HEAP_TYPE_ENV= "VNLIB_SHARED_HEAP_TYPE"; - public const string SHARED_HEAP_INTIAL_SIZE_ENV = "VNLIB_SHARED_HEAP_SIZE"; - - /// <summary> - /// Initial shared heap size (bytes) - /// </summary> - public const ulong SHARED_HEAP_INIT_SIZE = 20971520; - - public const int MAX_BUF_SIZE = 2097152; - public const int MIN_BUF_SIZE = 16000; - - /// <summary> - /// The maximum buffer size requested by <see cref="UnsafeAlloc{T}(int, bool)"/> - /// that will use the array pool before falling back to the <see cref="Shared"/>. - /// heap. - /// </summary> - public const int MAX_UNSAFE_POOL_SIZE = 500 * 1024; - - /// <summary> - /// Provides a shared heap instance for the process to allocate memory from. - /// </summary> - /// <remarks> - /// The backing heap - /// is determined by the OS type and process environment varibles. - /// </remarks> - public static IUnmangedHeap Shared => _sharedHeap.Value; - - private static readonly Lazy<IUnmangedHeap> _sharedHeap; - - static Memory() - { - _sharedHeap = new Lazy<IUnmangedHeap>(() => InitHeapInternal(true), LazyThreadSafetyMode.PublicationOnly); - //Cleanup the heap on process exit - AppDomain.CurrentDomain.DomainUnload += DomainUnloaded; - } - - private static void DomainUnloaded(object sender, EventArgs e) - { - //Dispose the heap if allocated - if (_sharedHeap.IsValueCreated) - { - _sharedHeap.Value.Dispose(); - } - } - - /// <summary> - /// Initializes a new <see cref="IUnmangedHeap"/> determined by compilation/runtime flags - /// and operating system type for the current proccess. - /// </summary> - /// <returns>An <see cref="IUnmangedHeap"/> for the current process</returns> - /// <exception cref="SystemException"></exception> - /// <exception cref="DllNotFoundException"></exception> - public static IUnmangedHeap InitializeNewHeapForProcess() => InitHeapInternal(false); - - private static IUnmangedHeap InitHeapInternal(bool isShared) - { - bool IsWindows = OperatingSystem.IsWindows(); - //Get environment varable - string heapType = Environment.GetEnvironmentVariable(SHARED_HEAP_TYPE_ENV); - //Get inital size - string sharedSize = Environment.GetEnvironmentVariable(SHARED_HEAP_INTIAL_SIZE_ENV); - //Try to parse the shared size from the env - if (!ulong.TryParse(sharedSize, out ulong defaultSize)) - { - defaultSize = SHARED_HEAP_INIT_SIZE; - } - //Gen the private heap from its type or default - switch (heapType) - { - case "win32": - if (!IsWindows) - { - throw new PlatformNotSupportedException("Win32 private heaps are not supported on non-windows platforms"); - } - return PrivateHeap.Create(defaultSize); - case "rpmalloc": - //If the shared heap is being allocated, then return a lock free global heap - return isShared ? RpMallocPrivateHeap.GlobalHeap : new RpMallocPrivateHeap(false); - default: - return IsWindows ? PrivateHeap.Create(defaultSize) : new ProcessHeap(); - } - } - - /// <summary> - /// Gets a value that indicates if the Rpmalloc native library is loaded - /// </summary> - public static bool IsRpMallocLoaded { get; } = Environment.GetEnvironmentVariable(SHARED_HEAP_TYPE_ENV) == "rpmalloc"; - - #region Zero - /// <summary> - /// Zeros a block of memory of umanged type. If Windows is detected at runtime, calls RtlSecureZeroMemory Win32 function - /// </summary> - /// <typeparam name="T">Unmanged datatype</typeparam> - /// <param name="block">Block of memory to be cleared</param> - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] - public static void UnsafeZeroMemory<T>(ReadOnlySpan<T> block) where T : unmanaged - { - if (!block.IsEmpty) - { - checked - { - fixed (void* ptr = &MemoryMarshal.GetReference(block)) - { - //Calls memset - Unsafe.InitBlock(ptr, 0, (uint)(block.Length * sizeof(T))); - } - } - } - } - /// <summary> - /// Zeros a block of memory of umanged type. If Windows is detected at runtime, calls RtlSecureZeroMemory Win32 function - /// </summary> - /// <typeparam name="T">Unmanged datatype</typeparam> - /// <param name="block">Block of memory to be cleared</param> - [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] - public static void UnsafeZeroMemory<T>(ReadOnlyMemory<T> block) where T : unmanaged - { - if (!block.IsEmpty) - { - checked - { - //Pin memory and get pointer - using MemoryHandle handle = block.Pin(); - //Calls memset - Unsafe.InitBlock(handle.Pointer, 0, (uint)(block.Length * sizeof(T))); - } - } - } - - /// <summary> - /// Initializes a block of memory with zeros - /// </summary> - /// <typeparam name="T">The unmanaged</typeparam> - /// <param name="block">The block of memory to initialize</param> - public static void InitializeBlock<T>(Span<T> block) where T : unmanaged => UnsafeZeroMemory<T>(block); - /// <summary> - /// Initializes a block of memory with zeros - /// </summary> - /// <typeparam name="T">The unmanaged</typeparam> - /// <param name="block">The block of memory to initialize</param> - public static void InitializeBlock<T>(Memory<T> block) where T : unmanaged => UnsafeZeroMemory<T>(block); - - /// <summary> - /// Zeroes a block of memory pointing to the structure - /// </summary> - /// <typeparam name="T">The structure type</typeparam> - /// <param name="block">The pointer to the allocated structure</param> - public static void ZeroStruct<T>(IntPtr block) - { - //get thes size of the structure - int size = Unsafe.SizeOf<T>(); - //Zero block - Unsafe.InitBlock(block.ToPointer(), 0, (uint)size); - } - /// <summary> - /// Zeroes a block of memory pointing to the structure - /// </summary> - /// <typeparam name="T">The structure type</typeparam> - /// <param name="structPtr">The pointer to the allocated structure</param> - public static void ZeroStruct<T>(void* structPtr) where T: unmanaged - { - //get thes size of the structure - int size = Unsafe.SizeOf<T>(); - //Zero block - Unsafe.InitBlock(structPtr, 0, (uint)size); - } - /// <summary> - /// Zeroes a block of memory pointing to the structure - /// </summary> - /// <typeparam name="T">The structure type</typeparam> - /// <param name="structPtr">The pointer to the allocated structure</param> - public static void ZeroStruct<T>(T* structPtr) where T : unmanaged - { - //get thes size of the structure - int size = Unsafe.SizeOf<T>(); - //Zero block - Unsafe.InitBlock(structPtr, 0, (uint)size); - } - - #endregion - - #region Copy - /// <summary> - /// Copies data from source memory to destination memory of an umanged data type - /// </summary> - /// <typeparam name="T">Unmanged type</typeparam> - /// <param name="source">Source data <see cref="ReadOnlySpan{T}"/></param> - /// <param name="dest">Destination <see cref="MemoryHandle{T}"/></param> - /// <param name="destOffset">Dest offset</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static void Copy<T>(ReadOnlySpan<T> source, MemoryHandle<T> dest, Int64 destOffset) where T : unmanaged - { - if (source.IsEmpty) - { - return; - } - if (dest.Length < (ulong)(destOffset + source.Length)) - { - throw new ArgumentException("Source data is larger than the dest data block", nameof(source)); - } - //Get long offset from the destination handle - T* offset = dest.GetOffset(destOffset); - fixed(void* src = &MemoryMarshal.GetReference(source)) - { - int byteCount = checked(source.Length * sizeof(T)); - Unsafe.CopyBlock(offset, src, (uint)byteCount); - } - } - /// <summary> - /// Copies data from source memory to destination memory of an umanged data type - /// </summary> - /// <typeparam name="T">Unmanged type</typeparam> - /// <param name="source">Source data <see cref="ReadOnlyMemory{T}"/></param> - /// <param name="dest">Destination <see cref="MemoryHandle{T}"/></param> - /// <param name="destOffset">Dest offset</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static void Copy<T>(ReadOnlyMemory<T> source, MemoryHandle<T> dest, Int64 destOffset) where T : unmanaged - { - if (source.IsEmpty) - { - return; - } - if (dest.Length < (ulong)(destOffset + source.Length)) - { - throw new ArgumentException("Dest constraints are larger than the dest data block", nameof(source)); - } - //Get long offset from the destination handle - T* offset = dest.GetOffset(destOffset); - //Pin the source memory - using MemoryHandle srcHandle = source.Pin(); - int byteCount = checked(source.Length * sizeof(T)); - //Copy block using unsafe class - Unsafe.CopyBlock(offset, srcHandle.Pointer, (uint)byteCount); - } - /// <summary> - /// Copies data from source memory to destination memory of an umanged data type - /// </summary> - /// <typeparam name="T">Unmanged type</typeparam> - /// <param name="source">Source data <see cref="MemoryHandle{T}"/></param> - /// <param name="sourceOffset">Number of elements to offset source data</param> - /// <param name="dest">Destination <see cref="Span{T}"/></param> - /// <param name="destOffset">Dest offset</param> - /// <param name="count">Number of elements to copy</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static void Copy<T>(MemoryHandle<T> source, Int64 sourceOffset, Span<T> dest, int destOffset, int count) where T : unmanaged - { - if (count <= 0) - { - return; - } - if (source.Length < (ulong)(sourceOffset + count)) - { - throw new ArgumentException("Source constraints are larger than the source data block", nameof(count)); - } - if (dest.Length < destOffset + count) - { - throw new ArgumentOutOfRangeException(nameof(destOffset), "Destination offset range cannot exceed the size of the destination buffer"); - } - //Get offset to allow large blocks of memory - T* src = source.GetOffset(sourceOffset); - fixed(T* dst = &MemoryMarshal.GetReference(dest)) - { - //Cacl offset - T* dstoffset = dst + destOffset; - int byteCount = checked(count * sizeof(T)); - //Aligned copy - Unsafe.CopyBlock(dstoffset, src, (uint)byteCount); - } - } - /// <summary> - /// Copies data from source memory to destination memory of an umanged data type - /// </summary> - /// <typeparam name="T">Unmanged type</typeparam> - /// <param name="source">Source data <see cref="MemoryHandle{T}"/></param> - /// <param name="sourceOffset">Number of elements to offset source data</param> - /// <param name="dest">Destination <see cref="Memory{T}"/></param> - /// <param name="destOffset">Dest offset</param> - /// <param name="count">Number of elements to copy</param> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static void Copy<T>(MemoryHandle<T> source, Int64 sourceOffset, Memory<T> dest, int destOffset, int count) where T : unmanaged - { - if (count == 0) - { - return; - } - if (source.Length < (ulong)(sourceOffset + count)) - { - throw new ArgumentException("Source constraints are larger than the source data block", nameof(count)); - } - if(dest.Length < destOffset + count) - { - throw new ArgumentOutOfRangeException(nameof(destOffset), "Destination offset range cannot exceed the size of the destination buffer"); - } - //Get offset to allow large blocks of memory - T* src = source.GetOffset(sourceOffset); - //Pin the memory handle - using MemoryHandle handle = dest.Pin(); - //Byte count - int byteCount = checked(count * sizeof(T)); - //Dest offset - T* dst = ((T*)handle.Pointer) + destOffset; - //Aligned copy - Unsafe.CopyBlock(dst, src, (uint)byteCount); - } - #endregion - - #region Streams - /// <summary> - /// Copies data from one stream to another in specified blocks - /// </summary> - /// <param name="source">Source memory</param> - /// <param name="srcOffset">Source offset</param> - /// <param name="dest">Destination memory</param> - /// <param name="destOffst">Destination offset</param> - /// <param name="count">Number of elements to copy</param> - public static void Copy(Stream source, Int64 srcOffset, Stream dest, Int64 destOffst, Int64 count) - { - if (count == 0) - { - return; - } - if (count < 0) - { - throw new ArgumentException("Count must be a positive integer", nameof(count)); - } - //Seek streams - _ = source.Seek(srcOffset, SeekOrigin.Begin); - _ = dest.Seek(destOffst, SeekOrigin.Begin); - //Create new buffer - using IMemoryHandle<byte> buffer = Shared.Alloc<byte>(count); - Span<byte> buf = buffer.Span; - int total = 0; - do - { - //read from source - int read = source.Read(buf); - //guard - if (read == 0) - { - break; - } - //write read slice to dest - dest.Write(buf[..read]); - //update total read - total += read; - } while (total < count); - } - #endregion - - #region alloc - - /// <summary> - /// Allocates a block of unmanaged, or pooled manaaged memory depending on - /// compilation flags and runtime unamanged allocators. - /// </summary> - /// <typeparam name="T">The unamanged type to allocate</typeparam> - /// <param name="elements">The number of elements of the type within the block</param> - /// <param name="zero">Flag to zero elements during allocation before the method returns</param> - /// <returns>A handle to the block of memory</returns> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - public static UnsafeMemoryHandle<T> UnsafeAlloc<T>(int elements, bool zero = false) where T : unmanaged - { - if (elements < 0) - { - throw new ArgumentException("Number of elements must be a positive integer", nameof(elements)); - } - if(elements > MAX_UNSAFE_POOL_SIZE || IsRpMallocLoaded) - { - // Alloc from heap - IntPtr block = Shared.Alloc((uint)elements, (uint)sizeof(T), zero); - //Init new handle - return new(Shared, block, elements); - } - else - { - return new(ArrayPool<T>.Shared, elements, zero); - } - } - - /// <summary> - /// Allocates a block of unmanaged, or pooled manaaged memory depending on - /// compilation flags and runtime unamanged allocators. - /// </summary> - /// <typeparam name="T">The unamanged type to allocate</typeparam> - /// <param name="elements">The number of elements of the type within the block</param> - /// <param name="zero">Flag to zero elements during allocation before the method returns</param> - /// <returns>A handle to the block of memory</returns> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - public static IMemoryHandle<T> SafeAlloc<T>(int elements, bool zero = false) where T: unmanaged - { - if (elements < 0) - { - throw new ArgumentException("Number of elements must be a positive integer", nameof(elements)); - } - - //If the element count is larger than max pool size, alloc from shared heap - if (elements > MAX_UNSAFE_POOL_SIZE) - { - //Alloc from shared heap - return Shared.Alloc<T>(elements, zero); - } - else - { - //Get temp buffer from shared buffer pool - return new VnTempBuffer<T>(elements, zero); - } - } - - #endregion - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/MemoryHandle.cs b/Utils/src/Memory/MemoryHandle.cs deleted file mode 100644 index a09edea..0000000 --- a/Utils/src/Memory/MemoryHandle.cs +++ /dev/null @@ -1,237 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: MemoryHandle.cs -* -* MemoryHandle.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; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -using Microsoft.Win32.SafeHandles; - -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.Memory -{ - /// <summary> - /// Provides a wrapper for using umanged memory handles from an assigned <see cref="PrivateHeap"/> for <see cref="UnmanagedType"/> types - /// </summary> - /// <remarks> - /// Handles are configured to address blocks larger than 2GB, - /// so some properties may raise exceptions if large blocks are used. - /// </remarks> - public sealed class MemoryHandle<T> : SafeHandleZeroOrMinusOneIsInvalid, IMemoryHandle<T>, IEquatable<MemoryHandle<T>> where T : unmanaged - { - /// <summary> - /// New <typeparamref name="T"/>* pointing to the base of the allocated block - /// </summary> - /// <exception cref="ObjectDisposedException"></exception> - public unsafe T* Base - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => GetOffset(0); - } - /// <summary> - /// New <see cref="IntPtr"/> pointing to the base of the allocated block - /// </summary> - /// <exception cref="ObjectDisposedException"></exception> - public unsafe IntPtr BasePtr - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => (IntPtr)GetOffset(0); - } - /// <summary> - /// Gets a span over the entire allocated block - /// </summary> - /// <returns>A <see cref="Span{T}"/> over the internal data</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="OverflowException"></exception> - public unsafe Span<T> Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - this.ThrowIfClosed(); - return _length == 0 ? Span<T>.Empty : new Span<T>(Base, IntLength); - } - } - - private readonly bool ZeroMemory; - private readonly IUnmangedHeap Heap; - private ulong _length; - - /// <summary> - /// Number of elements allocated to the current instance - /// </summary> - public ulong Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _length; - } - /// <summary> - /// Number of elements in the memory block casted to an integer - /// </summary> - /// <exception cref="OverflowException"></exception> - public int IntLength - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => checked((int)_length); - } - - /// <summary> - /// Number of bytes allocated to the current instance - /// </summary> - /// <exception cref="OverflowException"></exception> - public unsafe ulong ByteLength - { - //Check for overflows when converting to bytes (should run out of memory before this is an issue, but just incase) - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => checked(_length * (UInt64)sizeof(T)); - } - - /// <summary> - /// Creates a new memory handle, for which is holds ownership, and allocates the number of elements specified on the heap. - /// </summary> - /// <param name="heap">The heap to allocate/deallocate memory from</param> - /// <param name="elements">Number of elements to allocate</param> - /// <param name="zero">Zero all memory during allocations from heap</param> - /// <param name="initial">The initial block of allocated memory to wrap</param> - internal MemoryHandle(IUnmangedHeap heap, IntPtr initial, ulong elements, bool zero) : base(true) - { - //Set element size (always allocate at least 1 object) - _length = elements; - ZeroMemory = zero; - //assign heap ref - Heap = heap; - handle = initial; - } - - /// <summary> - /// Resizes the current handle on the heap - /// </summary> - /// <param name="elements">Positive number of elemnts the current handle should referrence</param> - /// <exception cref="OverflowException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - public unsafe void Resize(ulong elements) - { - this.ThrowIfClosed(); - //Update size (should never be less than inital size) - _length = elements; - //Re-alloc (Zero if required) - try - { - Heap.Resize(ref handle, Length, (ulong)sizeof(T), ZeroMemory); - } - //Catch the disposed exception so we can invalidate the current ptr - catch (ObjectDisposedException) - { - base.handle = IntPtr.Zero; - //Set as invalid so release does not get called - base.SetHandleAsInvalid(); - //Propagate the exception - throw; - } - } - /// <summary> - /// Gets an offset pointer from the base postion to the number of bytes specified. Performs bounds checks - /// </summary> - /// <param name="elements">Number of elements of type to offset</param> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - /// <returns><typeparamref name="T"/> pointer to the memory offset specified</returns> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe T* GetOffset(ulong elements) - { - if (elements >= _length) - { - throw new ArgumentOutOfRangeException(nameof(elements), "Element offset cannot be larger than allocated size"); - } - this.ThrowIfClosed(); - //Get ptr and offset it - T* bs = ((T*)handle) + elements; - return bs; - } - - ///<inheritdoc/> - ///<exception cref="ObjectDisposedException"></exception> - ///<exception cref="ArgumentOutOfRangeException"></exception> - public unsafe MemoryHandle Pin(int elementIndex) - { - //Get ptr and guard checks before adding the referrence - T* ptr = GetOffset((ulong)elementIndex); - - bool addRef = false; - //use the pinned field as success val - DangerousAddRef(ref addRef); - //Create a new system.buffers memory handle from the offset ptr address - return !addRef - ? throw new ObjectDisposedException("Failed to increase referrence count on the memory handle because it was released") - : new MemoryHandle(ptr, pinnable: this); - } - - ///<inheritdoc/> - ///<exception cref="ObjectDisposedException"></exception> - public void Unpin() - { - //Dec count on release - DangerousRelease(); - } - - - ///<inheritdoc/> - protected override bool ReleaseHandle() - { - //Return result of free - return Heap.Free(ref handle); - } - - - - /// <summary> - /// Determines if the memory blocks are equal by comparing their base addresses. - /// </summary> - /// <param name="other"><see cref="MemoryHandle{T}"/> to compare</param> - /// <returns>true if the block of memory is the same, false if the handle's size does not - /// match or the base addresses do not match even if they point to an overlapping address space</returns> - /// <exception cref="ObjectDisposedException"></exception> - public bool Equals(MemoryHandle<T> other) - { - this.ThrowIfClosed(); - other.ThrowIfClosed(); - return _length == other._length && handle == other.handle; - } - ///<inheritdoc/> - public override bool Equals(object obj) => obj is MemoryHandle<T> oHandle && Equals(oHandle); - ///<inheritdoc/> - public override int GetHashCode() => base.GetHashCode(); - - - ///<inheritdoc/> - public static implicit operator Span<T>(MemoryHandle<T> handle) - { - //If the handle is invalid or closed return an empty span - return handle.IsClosed || handle.IsInvalid || handle._length == 0 ? Span<T>.Empty : handle.Span; - } - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/PrivateBuffersMemoryPool.cs b/Utils/src/Memory/PrivateBuffersMemoryPool.cs deleted file mode 100644 index 1e85207..0000000 --- a/Utils/src/Memory/PrivateBuffersMemoryPool.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: PrivateBuffersMemoryPool.cs -* -* PrivateBuffersMemoryPool.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.Memory -{ - /// <summary> - /// Provides a <see cref="MemoryPool{T}"/> wrapper for using unmanged <see cref="PrivateHeap"/>s - /// </summary> - /// <typeparam name="T">Unamanged memory type to provide data memory instances from</typeparam> - public sealed class PrivateBuffersMemoryPool<T> : MemoryPool<T> where T : unmanaged - { - private readonly IUnmangedHeap Heap; - - internal PrivateBuffersMemoryPool(IUnmangedHeap heap):base() - { - this.Heap = heap; - } - ///<inheritdoc/> - public override int MaxBufferSize => int.MaxValue; - ///<inheritdoc/> - ///<exception cref="OutOfMemoryException"></exception> - ///<exception cref="ObjectDisposedException"></exception> - ///<exception cref="ArgumentOutOfRangeException"></exception> - public override IMemoryOwner<T> Rent(int minBufferSize = 0) => new SysBufferMemoryManager<T>(Heap, (uint)minBufferSize, false); - - /// <summary> - /// Allocates a new <see cref="MemoryManager{T}"/> of a different data type from the pool - /// </summary> - /// <typeparam name="TDifType">The unmanaged data type to allocate for</typeparam> - /// <param name="minBufferSize">Minumum size of the buffer</param> - /// <returns>The memory owner of a different data type</returns> - public IMemoryOwner<TDifType> Rent<TDifType>(int minBufferSize = 0) where TDifType : unmanaged - { - return new SysBufferMemoryManager<TDifType>(Heap, (uint)minBufferSize, false); - } - ///<inheritdoc/> - protected override void Dispose(bool disposing) - { - //Dispose the heap - Heap.Dispose(); - } - } -} diff --git a/Utils/src/Memory/PrivateHeap.cs b/Utils/src/Memory/PrivateHeap.cs deleted file mode 100644 index 5d97506..0000000 --- a/Utils/src/Memory/PrivateHeap.cs +++ /dev/null @@ -1,184 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: PrivateHeap.cs -* -* PrivateHeap.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.Diagnostics; -using System.Runtime.Versioning; -using System.Runtime.InteropServices; - -using DWORD = System.Int64; -using SIZE_T = System.UInt64; -using LPVOID = System.IntPtr; - -namespace VNLib.Utils.Memory -{ - ///<summary> - ///<para> - /// Provides a win32 private heap managed wrapper class - ///</para> - ///</summary> - ///<remarks> - /// <see cref="PrivateHeap"/> implements <see cref="SafeHandle"/> and tracks allocated blocks by its - /// referrence counter. Allocations increment the count, and free's decrement the count, so the heap may - /// be disposed safely - /// </remarks> - [ComVisible(false)] - [SupportedOSPlatform("Windows")] - public sealed class PrivateHeap : UnmanagedHeapBase - { - private const string KERNEL_DLL = "Kernel32"; - - #region Extern - //Heap flags - public const DWORD HEAP_NO_FLAGS = 0x00; - public const DWORD HEAP_GENERATE_EXCEPTIONS = 0x04; - public const DWORD HEAP_NO_SERIALIZE = 0x01; - public const DWORD HEAP_REALLOC_IN_PLACE_ONLY = 0x10; - public const DWORD HEAP_ZERO_MEMORY = 0x08; - - [DllImport(KERNEL_DLL, SetLastError = true, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - private static extern LPVOID HeapAlloc(IntPtr hHeap, DWORD flags, SIZE_T dwBytes); - [DllImport(KERNEL_DLL, SetLastError = true, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - private static extern LPVOID HeapReAlloc(IntPtr hHeap, DWORD dwFlags, LPVOID lpMem, SIZE_T dwBytes); - [DllImport(KERNEL_DLL, SetLastError = true, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool HeapFree(IntPtr hHeap, DWORD dwFlags, LPVOID lpMem); - - [DllImport(KERNEL_DLL, SetLastError = true, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - private static extern LPVOID HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize); - [DllImport(KERNEL_DLL, SetLastError = true, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - [return: MarshalAs(UnmanagedType.Bool)] - private static extern bool HeapDestroy(IntPtr hHeap); - [DllImport(KERNEL_DLL, SetLastError = true, ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.Bool)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - private static extern bool HeapValidate(IntPtr hHeap, DWORD dwFlags, LPVOID lpMem); - [DllImport(KERNEL_DLL, SetLastError = true, ExactSpelling = true)] - [return: MarshalAs(UnmanagedType.U8)] - [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - private static extern SIZE_T HeapSize(IntPtr hHeap, DWORD flags, LPVOID lpMem); - - #endregion - - /// <summary> - /// Create a new <see cref="PrivateHeap"/> with the specified sizes and flags - /// </summary> - /// <param name="initialSize">Intial size of the heap</param> - /// <param name="maxHeapSize">Maximum size allowed for the heap (disabled = 0, default)</param> - /// <param name="flags">Defalt heap flags to set globally for all blocks allocated by the heap (default = 0)</param> - public static PrivateHeap Create(SIZE_T initialSize, SIZE_T maxHeapSize = 0, DWORD flags = HEAP_NO_FLAGS) - { - //Call create, throw exception if the heap falled to allocate - IntPtr heapHandle = HeapCreate(flags, initialSize, maxHeapSize); - if (heapHandle == IntPtr.Zero) - { - throw new NativeMemoryException("Heap could not be created"); - } -#if TRACE - Trace.WriteLine($"Win32 private heap {heapHandle:x} created"); -#endif - //Heap has been created so we can wrap it - return new(heapHandle); - } - /// <summary> - /// LIFETIME WARNING. Consumes a valid win32 handle and will manage it's lifetime once constructed. - /// Locking and memory blocks will attempt to be allocated from this heap handle. - /// </summary> - /// <param name="win32HeapHandle">An open and valid handle to a win32 private heap</param> - /// <returns>A wrapper around the specified heap</returns> - public static PrivateHeap ConsumeExisting(IntPtr win32HeapHandle) => new (win32HeapHandle); - - private PrivateHeap(IntPtr heapPtr) : base(false, true) => handle = heapPtr; - - /// <summary> - /// Retrieves the size of a memory block allocated from the current heap. - /// </summary> - /// <param name="block">The pointer to a block of memory to get the size of</param> - /// <returns>The size of the block of memory, (SIZE_T)-1 if the operation fails</returns> - public SIZE_T HeapSize(ref LPVOID block) => HeapSize(handle, HEAP_NO_FLAGS, block); - - /// <summary> - /// Validates the specified block of memory within the current heap instance. This function will block hte - /// </summary> - /// <param name="block">Pointer to the block of memory to validate</param> - /// <returns>True if the block is valid, false otherwise</returns> - public bool Validate(ref LPVOID block) - { - bool result; - //Lock the heap before validating - HeapLock.Wait(); - //validate the block on the current heap - result = HeapValidate(handle, HEAP_NO_FLAGS, block); - //Unlock the heap - HeapLock.Release(); - return result; - - } - /// <summary> - /// Validates the current heap instance. The function scans all the memory blocks in the heap and verifies that the heap control structures maintained by - /// the heap manager are in a consistent state. - /// </summary> - /// <returns>If the specified heap or memory block is valid, the return value is nonzero.</returns> - /// <remarks>This can be a consuming operation which will block all allocations</remarks> - public bool Validate() - { - bool result; - //Lock the heap before validating - HeapLock.Wait(); - //validate the entire heap - result = HeapValidate(handle, HEAP_NO_FLAGS, IntPtr.Zero); - //Unlock the heap - HeapLock.Release(); - return result; - } - - ///<inheritdoc/> - protected override bool ReleaseHandle() - { -#if TRACE - Trace.WriteLine($"Win32 private heap {handle:x} destroyed"); -#endif - return HeapDestroy(handle) && base.ReleaseHandle(); - } - ///<inheritdoc/> - protected override sealed LPVOID AllocBlock(ulong elements, ulong size, bool zero) - { - ulong bytes = checked(elements * size); - return HeapAlloc(handle, zero ? HEAP_ZERO_MEMORY : HEAP_NO_FLAGS, bytes); - } - ///<inheritdoc/> - protected override sealed bool FreeBlock(LPVOID block) => HeapFree(handle, HEAP_NO_FLAGS, block); - ///<inheritdoc/> - protected override sealed LPVOID ReAllocBlock(LPVOID block, ulong elements, ulong size, bool zero) - { - ulong bytes = checked(elements * size); - return HeapReAlloc(handle, zero ? HEAP_ZERO_MEMORY : HEAP_NO_FLAGS, block, bytes); - } - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/PrivateString.cs b/Utils/src/Memory/PrivateString.cs deleted file mode 100644 index cd3b1f6..0000000 --- a/Utils/src/Memory/PrivateString.cs +++ /dev/null @@ -1,185 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: PrivateString.cs -* -* PrivateString.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.Diagnostics.CodeAnalysis; - -namespace VNLib.Utils.Memory -{ - /// <summary> - /// Provides a wrapper class that will have unsafe access to the memory of - /// the specified <see cref="string"/> provided during object creation. - /// </summary> - /// <remarks>The value of the memory the protected string points to is undefined when the instance is disposed</remarks> - public class PrivateString : PrivateStringManager, IEquatable<PrivateString>, IEquatable<string>, ICloneable - { - protected string StrRef => base[0]!; - private readonly bool OwnsReferrence; - - /// <summary> - /// Creates a new <see cref="PrivateString"/> over the specified string and the memory it points to. - /// </summary> - /// <param name="data">The <see cref="string"/> instance pointing to the memory to protect</param> - /// <param name="ownsReferrence">Does the current instance "own" the memory the data parameter points to</param> - /// <remarks>You should no longer reference the input string directly</remarks> - public PrivateString(string data, bool ownsReferrence = true) : base(1) - { - //Create a private string manager to store referrence to string - base[0] = data ?? throw new ArgumentNullException(nameof(data)); - OwnsReferrence = ownsReferrence; - } - - //Create private string from a string - public static explicit operator PrivateString?(string? data) - { - //Allow passing null strings during implicit casting - return data == null ? null : new(data); - } - - public static PrivateString? ToPrivateString(string? value) - { - return value == null ? null : new PrivateString(value, true); - } - - //Cast to string - public static explicit operator string (PrivateString str) - { - //Check if disposed, or return the string - str.Check(); - return str.StrRef; - } - - public static implicit operator ReadOnlySpan<char>(PrivateString str) - { - return str.Disposed ? Span<char>.Empty : str.StrRef.AsSpan(); - } - - /// <summary> - /// Gets the value of the internal string as a <see cref="ReadOnlySpan{T}"/> - /// </summary> - /// <returns>The <see cref="ReadOnlySpan{T}"/> referrence to the internal string</returns> - /// <exception cref="ObjectDisposedException"></exception> - public ReadOnlySpan<char> ToReadOnlySpan() - { - Check(); - return StrRef.AsSpan(); - } - - ///<inheritdoc/> - public bool Equals(string? other) - { - Check(); - return StrRef.Equals(other); - } - ///<inheritdoc/> - public bool Equals(PrivateString? other) - { - Check(); - return other != null && StrRef.Equals(other.StrRef); - } - ///<inheritdoc/> - public override bool Equals(object? other) - { - Check(); - return other is PrivateString otherRef && StrRef.Equals(otherRef); - } - ///<inheritdoc/> - public bool Equals(ReadOnlySpan<char> other) - { - Check(); - return StrRef.AsSpan().SequenceEqual(other); - } - /// <summary> - /// Creates a deep copy of the internal string and returns that copy - /// </summary> - /// <returns>A deep copy of the internal string</returns> - public override string ToString() - { - Check(); - return new(StrRef.AsSpan()); - } - /// <summary> - /// String length - /// </summary> - /// <exception cref="ObjectDisposedException"></exception> - public int Length - { - get - { - Check(); - return StrRef.Length; - } - } - /// <summary> - /// Indicates whether the underlying string is null or an empty string ("") - /// </summary> - /// <param name="ps"></param> - /// <returns>True if the parameter is null, or an empty string (""). False otherwise</returns> - public static bool IsNullOrEmpty([NotNullWhen(false)] PrivateString? ps) => ps == null|| ps.Length == 0; - - /// <summary> - /// The hashcode of the underlying string - /// </summary> - /// <returns></returns> - public override int GetHashCode() - { - Check(); - return StrRef.GetHashCode(); - } - - /// <summary> - /// Creates a new deep copy of the current instance that is an independent <see cref="PrivateString"/> - /// </summary> - /// <returns>The new <see cref="PrivateString"/> instance</returns> - /// <exception cref="ObjectDisposedException"></exception> - public override object Clone() - { - Check(); - //Copy all contents of string to another reference - string clone = new (StrRef.AsSpan()); - //return a new private string - return new PrivateString(clone, true); - } - - ///<inheritdoc/> - protected override void Free() - { - Erase(); - } - - /// <summary> - /// Erases the contents of the internal CLR string - /// </summary> - public void Erase() - { - //Only dispose the instance if we own the memory - if (OwnsReferrence && !Disposed) - { - base.Free(); - } - } - - - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/PrivateStringManager.cs b/Utils/src/Memory/PrivateStringManager.cs deleted file mode 100644 index 9ed8f5f..0000000 --- a/Utils/src/Memory/PrivateStringManager.cs +++ /dev/null @@ -1,117 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: PrivateStringManager.cs -* -* PrivateStringManager.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.Memory -{ - /// <summary> - /// When inherited by a class, provides a safe string storage that zeros a CLR string memory on disposal - /// </summary> - public class PrivateStringManager : VnDisposeable, ICloneable - { - /// <summary> - /// Strings to be cleared when exiting - /// </summary> - private readonly string?[] ProtectedElements; - /// <summary> - /// Gets or sets a string referrence into the protected elements store - /// </summary> - /// <param name="index"></param> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="ArgumentNullException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - /// <exception cref="ObjectDisposedException"></exception> - /// <returns>Referrence to string associated with the index</returns> - protected string? this[int index] - { - get - { - Check(); - return ProtectedElements[index]; - } - set - { - Check(); - //Check to see if the string has been interned - if (!string.IsNullOrEmpty(value) && string.IsInterned(value) != null) - { - throw new ArgumentException($"The specified string has been CLR interned and cannot be stored in {nameof(PrivateStringManager)}"); - } - //Clear the old value before setting the new one - if (!string.IsNullOrEmpty(ProtectedElements[index])) - { - Memory.UnsafeZeroMemory<char>(ProtectedElements[index]); - } - //set new value - ProtectedElements[index] = value; - } - } - /// <summary> - /// Create a new instance with fixed array size - /// </summary> - /// <param name="elements">Number of elements to protect</param> - public PrivateStringManager(int elements) - { - //Allocate the string array - ProtectedElements = new string[elements]; - } - ///<inheritdoc/> - protected override void Free() - { - //Zero all strings specified - for (int i = 0; i < ProtectedElements.Length; i++) - { - if (!string.IsNullOrEmpty(ProtectedElements[i])) - { - //Zero the string memory - Memory.UnsafeZeroMemory<char>(ProtectedElements[i]); - //Set to null - ProtectedElements[i] = null; - } - } - } - - /// <summary> - /// Creates a deep copy for a new independent <see cref="PrivateStringManager"/> - /// </summary> - /// <returns>A new independent <see cref="PrivateStringManager"/> instance</returns> - /// <remarks>Be careful duplicating large instances, and make sure clones are properly disposed if necessary</remarks> - /// <exception cref="ObjectDisposedException"></exception> - public virtual object Clone() - { - Check(); - PrivateStringManager other = new (ProtectedElements.Length); - //Copy all strings to the other instance - for(int i = 0; i < ProtectedElements.Length; i++) - { - //Copy all strings and store their copies in the new array - other.ProtectedElements[i] = this.ProtectedElements[i].AsSpan().ToString(); - } - //return the new copy - return other; - } - } -} diff --git a/Utils/src/Memory/ProcessHeap.cs b/Utils/src/Memory/ProcessHeap.cs deleted file mode 100644 index 4f06d52..0000000 --- a/Utils/src/Memory/ProcessHeap.cs +++ /dev/null @@ -1,82 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ProcessHeap.cs -* -* ProcessHeap.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.Diagnostics; -using System.Runtime.InteropServices; - -namespace VNLib.Utils.Memory -{ - /// <summary> - /// Provides a <see cref="IUnmangedHeap"/> wrapper for the <see cref="Marshal"/> virtualalloc - /// global heap methods - /// </summary> - [ComVisible(false)] - public unsafe class ProcessHeap : VnDisposeable, IUnmangedHeap - { - /// <summary> - /// Initalizes a new global (cross platform) process heap - /// </summary> - public ProcessHeap() - { -#if TRACE - Trace.WriteLine($"Default heap instnace created {GetHashCode():x}"); -#endif - } - - ///<inheritdoc/> - ///<exception cref="OverflowException"></exception> - ///<exception cref="OutOfMemoryException"></exception> - public IntPtr Alloc(ulong elements, ulong size, bool zero) - { - return zero - ? (IntPtr)NativeMemory.AllocZeroed((nuint)elements, (nuint)size) - : (IntPtr)NativeMemory.Alloc((nuint)elements, (nuint)size); - } - ///<inheritdoc/> - public bool Free(ref IntPtr block) - { - //Free native mem from ptr - NativeMemory.Free(block.ToPointer()); - block = IntPtr.Zero; - return true; - } - ///<inheritdoc/> - protected override void Free() - { -#if TRACE - Trace.WriteLine($"Default heap instnace disposed {GetHashCode():x}"); -#endif - } - ///<inheritdoc/> - ///<exception cref="OverflowException"></exception> - ///<exception cref="OutOfMemoryException"></exception> - public void Resize(ref IntPtr block, ulong elements, ulong size, bool zero) - { - nuint bytes = checked((nuint)(elements * size)); - IntPtr old = block; - block = (IntPtr)NativeMemory.Realloc(old.ToPointer(), bytes); - } - } -} diff --git a/Utils/src/Memory/RpMallocPrivateHeap.cs b/Utils/src/Memory/RpMallocPrivateHeap.cs deleted file mode 100644 index 70c8a7f..0000000 --- a/Utils/src/Memory/RpMallocPrivateHeap.cs +++ /dev/null @@ -1,279 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: RpMallocPrivateHeap.cs -* -* RpMallocPrivateHeap.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; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -using size_t = System.UInt64; -using LPVOID = System.IntPtr; -using LPHEAPHANDLE = System.IntPtr; - -namespace VNLib.Utils.Memory -{ - /// <summary> - /// A wrapper class for cross platform RpMalloc implementation. - /// </summary> - [ComVisible(false)] - public sealed class RpMallocPrivateHeap : UnmanagedHeapBase - { - const string DLL_NAME = "rpmalloc"; - - #region statics - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern int rpmalloc_initialize(); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern void rpmalloc_finalize(); - - //Heap api - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPHEAPHANDLE rpmalloc_heap_acquire(); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern void rpmalloc_heap_release(LPHEAPHANDLE heap); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc_heap_alloc(LPHEAPHANDLE heap, size_t size); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc_heap_aligned_alloc(LPHEAPHANDLE heap, size_t alignment, size_t size); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc_heap_calloc(LPHEAPHANDLE heap, size_t num, size_t size); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc_heap_aligned_calloc(LPHEAPHANDLE heap, size_t alignment, size_t num, size_t size); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc_heap_realloc(LPHEAPHANDLE heap, LPVOID ptr, size_t size, nuint flags); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc_heap_aligned_realloc(LPHEAPHANDLE heap, LPVOID ptr, size_t alignment, size_t size, nuint flags); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern void rpmalloc_heap_free(LPHEAPHANDLE heap, LPVOID ptr); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern void rpmalloc_heap_free_all(LPHEAPHANDLE heap); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern void rpmalloc_heap_thread_set_current(LPHEAPHANDLE heap); - - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern void rpmalloc_thread_initialize(); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern int rpmalloc_is_thread_initialized(); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern void rpmalloc_thread_finalize(int release_caches); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc(size_t size); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpcalloc(size_t num, size_t size); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rprealloc(LPVOID ptr, size_t size); - [DllImport(DLL_NAME, ExactSpelling = true)] - [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern void rpfree(LPVOID ptr); - - #endregion - - private class RpMallocGlobalHeap : IUnmangedHeap - { - IntPtr IUnmangedHeap.Alloc(ulong elements, ulong size, bool zero) - { - return RpMalloc(elements, (nuint)size, zero); - } - - //Global heap does not need to be disposed - void IDisposable.Dispose() - { } - - bool IUnmangedHeap.Free(ref IntPtr block) - { - //Free the block - RpFree(ref block); - return true; - } - - void IUnmangedHeap.Resize(ref IntPtr block, ulong elements, ulong size, bool zero) - { - //Try to resize the block - IntPtr resize = RpRealloc(block, elements, (nuint)size); - - if (resize == IntPtr.Zero) - { - throw new NativeMemoryOutOfMemoryException("Failed to resize the block"); - } - //assign ptr - block = resize; - } - } - - /// <summary> - /// <para> - /// A <see cref="IUnmangedHeap"/> API for the RPMalloc library if loaded. - /// </para> - /// <para> - /// This heap is thread safe and may be converted to a <see cref="MemoryManager{T}"/> - /// infinitley and disposed safely. - /// </para> - /// <para> - /// If the native library is not loaded, calls to this API will throw a <see cref="DllNotFoundException"/>. - /// </para> - /// </summary> - public static IUnmangedHeap GlobalHeap { get; } = new RpMallocGlobalHeap(); - - /// <summary> - /// <para> - /// Initializes RpMalloc for the current thread and alloctes a block of memory - /// </para> - /// </summary> - /// <param name="elements">The number of elements to allocate</param> - /// <param name="size">The number of bytes per element type (aligment)</param> - /// <param name="zero">Zero the block of memory before returning</param> - /// <returns>A pointer to the block, (zero if failed)</returns> - public static LPVOID RpMalloc(size_t elements, nuint size, bool zero) - { - //See if the current thread has been initialized - if (rpmalloc_is_thread_initialized() == 0) - { - //Initialize the current thread - rpmalloc_thread_initialize(); - } - //Alloc block - LPVOID block; - if (zero) - { - block = rpcalloc(elements, size); - } - else - { - //Calculate the block size - ulong blockSize = checked(elements * size); - block = rpmalloc(blockSize); - } - return block; - } - - /// <summary> - /// Frees a block of memory allocated by RpMalloc - /// </summary> - /// <param name="block">A ref to the pointer of the block to free</param> - public static void RpFree(ref LPVOID block) - { - if (block != IntPtr.Zero) - { - rpfree(block); - block = IntPtr.Zero; - } - } - - /// <summary> - /// Attempts to re-allocate the specified block on the global heap - /// </summary> - /// <param name="block">A pointer to a previously allocated block of memory</param> - /// <param name="elements">The number of elements in the block</param> - /// <param name="size">The number of bytes in the element</param> - /// <returns>A pointer to the new block if the reallocation succeeded, null if the resize failed</returns> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="OverflowException"></exception> - public static LPVOID RpRealloc(LPVOID block, size_t elements, nuint size) - { - if(block == IntPtr.Zero) - { - throw new ArgumentException("The supplied block is not valid", nameof(block)); - } - //Calc new block size - size_t blockSize = checked(elements * size); - return rprealloc(block, blockSize); - } - - #region instance - - /// <summary> - /// Initializes a new RpMalloc first class heap to allocate memory blocks from - /// </summary> - /// <param name="zeroAll">A global flag to zero all blocks of memory allocated</param> - /// <exception cref="NativeMemoryOutOfMemoryException"></exception> - public RpMallocPrivateHeap(bool zeroAll):base(zeroAll, true) - { - //Alloc the heap - handle = rpmalloc_heap_acquire(); - if(IsInvalid) - { - throw new NativeMemoryException("Failed to aquire a new heap"); - } -#if TRACE - Trace.WriteLine($"RPMalloc heap {handle:x} created"); -#endif - } - ///<inheritdoc/> - protected override bool ReleaseHandle() - { -#if TRACE - Trace.WriteLine($"RPMalloc heap {handle:x} destroyed"); -#endif - //Release all heap memory - rpmalloc_heap_free_all(handle); - //Destroy the heap - rpmalloc_heap_release(handle); - //Release base - return base.ReleaseHandle(); - } - ///<inheritdoc/> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected sealed override LPVOID AllocBlock(ulong elements, ulong size, bool zero) - { - //Alloc or calloc and initalize - return zero ? rpmalloc_heap_calloc(handle, elements, size) : rpmalloc_heap_alloc(handle, checked(size * elements)); - } - ///<inheritdoc/> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected sealed override bool FreeBlock(LPVOID block) - { - //Free block - rpmalloc_heap_free(handle, block); - return true; - } - ///<inheritdoc/> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected sealed override LPVOID ReAllocBlock(LPVOID block, ulong elements, ulong size, bool zero) - { - //Realloc - return rpmalloc_heap_realloc(handle, block, checked(elements * size), 0); - } - #endregion - } -} diff --git a/Utils/src/Memory/SubSequence.cs b/Utils/src/Memory/SubSequence.cs deleted file mode 100644 index 3800fb5..0000000 --- a/Utils/src/Memory/SubSequence.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: SubSequence.cs -* -* SubSequence.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.Memory -{ - /// <summary> - /// Represents a subset (or window) of data within a <see cref="MemoryHandle{T}"/> - /// </summary> - /// <typeparam name="T">The unmanaged type to wrap</typeparam> - public readonly struct SubSequence<T> : IEquatable<SubSequence<T>> where T: unmanaged - { - private readonly MemoryHandle<T> _handle; - /// <summary> - /// The number of elements in the current window - /// </summary> - public readonly int Size { get; } - - /// <summary> - /// Creates a new <see cref="SubSequence{T}"/> to the handle to get a window of the block - /// </summary> - /// <param name="block"></param> - /// <param name="offset"></param> - /// <param name="size"></param> -#if TARGET_64_BIT - public SubSequence(MemoryHandle<T> block, ulong offset, int size) -#else - public SubSequence(MemoryHandle<T> block, int offset, int size) -#endif - { - _offset = offset; - Size = size >= 0 ? size : throw new ArgumentOutOfRangeException(nameof(size)); - _handle = block ?? throw new ArgumentNullException(nameof(block)); - } - - -#if TARGET_64_BIT - private readonly ulong _offset; -#else - private readonly int _offset; -#endif - /// <summary> - /// Gets a <see cref="Span{T}"/> that is offset from the base of the handle - /// </summary> - /// <exception cref="ArgumentOutOfRangeException"></exception> - -#if TARGET_64_BIT - public readonly Span<T> Span => Size > 0 ? _handle.GetOffsetSpan(_offset, Size) : Span<T>.Empty; -#else - public readonly Span<T> Span => Size > 0 ? _handle.Span.Slice(_offset, Size) : Span<T>.Empty; -#endif - - /// <summary> - /// Slices the current sequence into a smaller <see cref="SubSequence{T}"/> - /// </summary> - /// <param name="offset">The relative offset from the current window offset</param> - /// <param name="size">The size of the block</param> - /// <returns>A <see cref="SubSequence{T}"/> of the current sequence</returns> - public readonly SubSequence<T> Slice(uint offset, int size) => new (_handle, _offset + checked((int)offset), size); - - /// <summary> - /// Returns the signed 32-bit hashcode - /// </summary> - /// <returns>A signed 32-bit integer that represents the hashcode for the current instance</returns> - /// <exception cref="ObjectDisposedException"></exception> - public readonly override int GetHashCode() => _handle.GetHashCode() + _offset.GetHashCode(); - - ///<inheritdoc/> - public readonly bool Equals(SubSequence<T> other) => Span.SequenceEqual(other.Span); - - ///<inheritdoc/> - public readonly override bool Equals(object? obj) => obj is SubSequence<T> other && Equals(other); - - /// <summary> - /// Determines if two <see cref="SubSequence{T}"/> are equal - /// </summary> - /// <param name="left"></param> - /// <param name="right"></param> - /// <returns>True if the sequences are equal, false otherwise</returns> - public static bool operator ==(SubSequence<T> left, SubSequence<T> right) => left.Equals(right); - /// <summary> - /// Determines if two <see cref="SubSequence{T}"/> are not equal - /// </summary> - /// <param name="left"></param> - /// <param name="right"></param> - /// <returns>True if the sequences are not equal, false otherwise</returns> - public static bool operator !=(SubSequence<T> left, SubSequence<T> right) => !left.Equals(right); - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/SysBufferMemoryManager.cs b/Utils/src/Memory/SysBufferMemoryManager.cs deleted file mode 100644 index 040467f..0000000 --- a/Utils/src/Memory/SysBufferMemoryManager.cs +++ /dev/null @@ -1,102 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: SysBufferMemoryManager.cs -* -* SysBufferMemoryManager.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; - -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.Memory -{ - /// <summary> - /// Provides an unmanaged System.Buffers integration with zero-cost pinning. Uses <see cref="MemoryHandle{T}"/> - /// as a memory provider which implements a <see cref="System.Runtime.InteropServices.SafeHandle"/> - /// </summary> - /// <typeparam name="T">Unmanaged memory type</typeparam> - public sealed class SysBufferMemoryManager<T> : MemoryManager<T> where T :unmanaged - { - private readonly IMemoryHandle<T> BackingMemory; - private readonly bool _ownsHandle; - - /// <summary> - /// Consumes an exisitng <see cref="MemoryHandle{T}"/> to provide <see cref="Memory"/> wrappers. - /// The handle should no longer be referrenced directly - /// </summary> - /// <param name="existingHandle">The existing handle to consume</param> - /// <param name="ownsHandle">A value that indicates if the memory manager owns the handle reference</param> - internal SysBufferMemoryManager(IMemoryHandle<T> existingHandle, bool ownsHandle) - { - BackingMemory = existingHandle; - _ownsHandle = ownsHandle; - } - - /// <summary> - /// Allocates a fized size buffer from the specified unmanaged <see cref="PrivateHeap"/> - /// </summary> - /// <param name="heap">The heap to perform allocations from</param> - /// <param name="elements">The number of elements to allocate</param> - /// <param name="zero">Zero allocations</param> - public SysBufferMemoryManager(IUnmangedHeap heap, ulong elements, bool zero) - { - BackingMemory = heap.Alloc<T>(elements, zero); - _ownsHandle = true; - } - - ///<inheritdoc/> - protected override bool TryGetArray(out ArraySegment<T> segment) - { - //Always false since no array is available - segment = default; - return false; - } - - ///<inheritdoc/> - ///<exception cref="OverflowException"></exception> - ///<exception cref="ObjectDisposedException"></exception> - public override Span<T> GetSpan() => BackingMemory.Span; - - /// <summary> - /// <inheritdoc/> - /// </summary> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public unsafe override MemoryHandle Pin(int elementIndex = 0) - { - return BackingMemory.Pin(elementIndex); - } - - ///<inheritdoc/> - public override void Unpin() - {} - - ///<inheritdoc/> - protected override void Dispose(bool disposing) - { - if (_ownsHandle) - { - BackingMemory.Dispose(); - } - } - } -} diff --git a/Utils/src/Memory/UnmanagedHeapBase.cs b/Utils/src/Memory/UnmanagedHeapBase.cs deleted file mode 100644 index 5c92aff..0000000 --- a/Utils/src/Memory/UnmanagedHeapBase.cs +++ /dev/null @@ -1,185 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: UnmanagedHeapBase.cs -* -* UnmanagedHeapBase.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.Threading; -using System.Runtime.InteropServices; - -using Microsoft.Win32.SafeHandles; - -using size_t = System.UInt64; -using LPVOID = System.IntPtr; - -namespace VNLib.Utils.Memory -{ - /// <summary> - /// Provides a synchronized base methods for accessing unmanaged memory. Implements <see cref="SafeHandle"/> - /// for safe disposal of heaps - /// </summary> - public abstract class UnmanagedHeapBase : SafeHandleZeroOrMinusOneIsInvalid, IUnmangedHeap - { - /// <summary> - /// The heap synchronization handle - /// </summary> - protected readonly SemaphoreSlim HeapLock; - /// <summary> - /// The global heap zero flag - /// </summary> - protected readonly bool GlobalZero; - - /// <summary> - /// Initalizes the unmanaged heap base class (init synchronization handle) - /// </summary> - /// <param name="globalZero">A global flag to zero all blocks of memory during allocation</param> - /// <param name="ownsHandle">A flag that indicates if the handle is owned by the instance</param> - protected UnmanagedHeapBase(bool globalZero, bool ownsHandle) : base(ownsHandle) - { - HeapLock = new(1, 1); - GlobalZero = globalZero; - } - - ///<inheritdoc/> - ///<remarks>Increments the handle count</remarks> - ///<exception cref="OutOfMemoryException"></exception> - ///<exception cref="ObjectDisposedException"></exception> - public LPVOID Alloc(size_t elements, size_t size, bool zero) - { - //Force zero if global flag is set - zero |= GlobalZero; - bool handleCountIncremented = false; - //Increment handle count to prevent premature release - DangerousAddRef(ref handleCountIncremented); - //Failed to increment ref count, class has been disposed - if (!handleCountIncremented) - { - throw new ObjectDisposedException("The handle has been released"); - } - try - { - //wait for lock - HeapLock.Wait(); - //Alloc block - LPVOID block = AllocBlock(elements, size, zero); - //release lock - HeapLock.Release(); - //Check if block was allocated - return block != IntPtr.Zero ? block : throw new NativeMemoryOutOfMemoryException("Failed to allocate the requested block"); - } - catch - { - //Decrement handle count since allocation failed - DangerousRelease(); - throw; - } - } - ///<inheritdoc/> - ///<remarks>Decrements the handle count</remarks> - public bool Free(ref LPVOID block) - { - bool result; - //If disposed, set the block handle to zero and exit to avoid raising exceptions during finalization - if (IsClosed || IsInvalid) - { - block = IntPtr.Zero; - return true; - } - //wait for lock - HeapLock.Wait(); - //Free block - result = FreeBlock(block); - //Release lock before releasing handle - HeapLock.Release(); - //Decrement handle count - DangerousRelease(); - //set block to invalid - block = IntPtr.Zero; - return result; - } - ///<inheritdoc/> - ///<exception cref="OutOfMemoryException"></exception> - ///<exception cref="ObjectDisposedException"></exception> - public void Resize(ref LPVOID block, size_t elements, size_t size, bool zero) - { - //wait for lock - HeapLock.Wait(); - /* - * Realloc may return a null pointer if allocation fails - * so check the results and only assign the block pointer - * if the result is valid. Otherwise pointer block should - * be left untouched - */ - LPVOID newBlock = ReAllocBlock(block, elements, size, zero); - //release lock - HeapLock.Release(); - //Check block - if (newBlock == IntPtr.Zero) - { - throw new NativeMemoryOutOfMemoryException("The memory block could not be resized"); - } - //Set the new block - block = newBlock; - } - - ///<inheritdoc/> - protected override bool ReleaseHandle() - { - HeapLock.Dispose(); - return true; - } - - /// <summary> - /// Allocates a block of memory from the heap - /// </summary> - /// <param name="elements">The number of elements within the block</param> - /// <param name="size">The size of the element type (in bytes)</param> - /// <param name="zero">A flag to zero the allocated block</param> - /// <returns>A pointer to the allocated block</returns> - protected abstract LPVOID AllocBlock(size_t elements, size_t size, bool zero); - /// <summary> - /// Frees a previously allocated block of memory - /// </summary> - /// <param name="block">The block to free</param> - protected abstract bool FreeBlock(LPVOID block); - /// <summary> - /// Resizes the previously allocated block of memory on the current heap - /// </summary> - /// <param name="block">The prevously allocated block</param> - /// <param name="elements">The new number of elements within the block</param> - /// <param name="size">The size of the element type (in bytes)</param> - /// <param name="zero">A flag to indicate if the new region of the block should be zeroed</param> - /// <returns>A pointer to the same block, but resized, null if the allocation falied</returns> - /// <remarks> - /// Heap base relies on the block pointer to remain unchanged if the resize fails so the - /// block is still valid, and the return value is used to determine if the resize was successful - /// </remarks> - protected abstract LPVOID ReAllocBlock(LPVOID block, size_t elements, size_t size, bool zero); - ///<inheritdoc/> - public override int GetHashCode() => handle.GetHashCode(); - ///<inheritdoc/> - public override bool Equals(object? obj) - { - return obj is UnmanagedHeapBase heap && !heap.IsInvalid && !heap.IsClosed && handle == heap.handle; - } - } -} diff --git a/Utils/src/Memory/UnsafeMemoryHandle.cs b/Utils/src/Memory/UnsafeMemoryHandle.cs deleted file mode 100644 index b05ad40..0000000 --- a/Utils/src/Memory/UnsafeMemoryHandle.cs +++ /dev/null @@ -1,231 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: UnsafeMemoryHandle.cs -* -* UnsafeMemoryHandle.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; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; -using System.Diagnostics.CodeAnalysis; - -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.Memory -{ - /// <summary> - /// Represents an unsafe handle to managed/unmanaged memory that should be used cautiously. - /// A referrence counter is not maintained. - /// </summary> - /// <typeparam name="T">Unmanaged memory type</typeparam> - [StructLayout(LayoutKind.Sequential)] - public readonly struct UnsafeMemoryHandle<T> : IMemoryHandle<T>, IEquatable<UnsafeMemoryHandle<T>> where T : unmanaged - { - private enum HandleType - { - None, - Pool, - PrivateHeap - } - - private readonly T[]? _poolArr; - private readonly IntPtr _memoryPtr; - private readonly ArrayPool<T>? _pool; - private readonly IUnmangedHeap? _heap; - private readonly HandleType _handleType; - private readonly int _length; - - ///<inheritdoc/> - public readonly unsafe Span<T> Span - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _handleType == HandleType.Pool ? _poolArr.AsSpan(0, IntLength) : new (_memoryPtr.ToPointer(), IntLength); - } - ///<inheritdoc/> - public readonly int IntLength => _length; - ///<inheritdoc/> - public readonly ulong Length => (ulong)_length; - - /// <summary> - /// Creates an empty <see cref="UnsafeMemoryHandle{T}"/> - /// </summary> - public UnsafeMemoryHandle() - { - _pool = null; - _heap = null; - _poolArr = null; - _memoryPtr = IntPtr.Zero; - _handleType = HandleType.None; - _length = 0; - } - - /// <summary> - /// Inializes a new <see cref="UnsafeMemoryHandle{T}"/> using the specified - /// <see cref="ArrayPool{T}"/> - /// </summary> - /// <param name="elements">The number of elements to store</param> - /// <param name="zero">Zero initial contents?</param> - /// <param name="pool">The explicit pool to alloc buffers from</param> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="ArgumentNullException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public unsafe UnsafeMemoryHandle(ArrayPool<T> pool, int elements, bool zero) - { - if (elements < 0) - { - throw new ArgumentOutOfRangeException(nameof(elements)); - } - //Pool is required - _pool = pool ?? throw new ArgumentNullException(nameof(pool)); - //Rent the array from the pool and hold referrence to it - _poolArr = pool.Rent(elements, zero); - //Cant store ref to array becase GC can move it - _memoryPtr = IntPtr.Zero; - //Set pool handle type - _handleType = HandleType.Pool; - //No heap being loaded - _heap = null; - _length = elements; - } - - /// <summary> - /// Intializes a new <see cref="UnsafeMemoryHandle{T}"/> for block of memory allocated from - /// an <see cref="IUnmangedHeap"/> - /// </summary> - /// <param name="heap">The heap the initial memory block belongs to</param> - /// <param name="initial">A pointer to the unmanaged memory block</param> - /// <param name="elements">The number of elements this block points to</param> - internal UnsafeMemoryHandle(IUnmangedHeap heap, IntPtr initial, int elements) - { - _pool = null; - _poolArr = null; - _heap = heap; - _length = elements; - _memoryPtr = initial; - _handleType = HandleType.PrivateHeap; - } - - /// <summary> - /// Releases memory back to the pool or heap from which is was allocated. - /// </summary> - /// <remarks>After this method is called, this handle points to invalid memory</remarks> - public readonly void Dispose() - { - switch (_handleType) - { - case HandleType.Pool: - { - //Return array to pool - _pool!.Return(_poolArr!); - } - break; - case HandleType.PrivateHeap: - { - IntPtr unalloc = _memoryPtr; - //Free the unmanaged handle - _heap!.Free(ref unalloc); - } - break; - } - } - - ///<inheritdoc/> - public readonly override int GetHashCode() => _handleType == HandleType.Pool ? _poolArr!.GetHashCode() : _memoryPtr.GetHashCode(); - ///<inheritdoc/> - public readonly unsafe MemoryHandle Pin(int elementIndex) - { - //Guard - if (elementIndex < 0 || elementIndex >= IntLength) - { - throw new ArgumentOutOfRangeException(nameof(elementIndex)); - } - - if (_handleType == HandleType.Pool) - { - //Pin the array - GCHandle arrHandle = GCHandle.Alloc(_poolArr, GCHandleType.Pinned); - //Get array base address - void* basePtr = (void*)arrHandle.AddrOfPinnedObject(); - //Get element offset - void* indexOffet = Unsafe.Add<T>(basePtr, elementIndex); - return new (indexOffet, arrHandle); - } - else - { - //Get offset pointer and pass self as pinnable argument, (nothing happens but support it) - void* basePtr = Unsafe.Add<T>(_memoryPtr.ToPointer(), elementIndex); - //Unmanaged memory is always pinned, so no need to pass this as IPinnable, since it will cause a box - return new (basePtr); - } - } - ///<inheritdoc/> - public readonly void Unpin() - { - //Nothing to do since gc handle takes care of array, and unmanaged pointers are not pinned - } - - /// <summary> - /// Determines if the other handle represents the same memory block as the - /// current handle. - /// </summary> - /// <param name="other">The other handle to test</param> - /// <returns>True if the other handle points to the same block of memory as the current handle</returns> - public readonly bool Equals(UnsafeMemoryHandle<T> other) - { - return _handleType == other._handleType && Length == other.Length && GetHashCode() == other.GetHashCode(); - } - - /// <summary> - /// Override for object equality operator, will cause boxing - /// for structures - /// </summary> - /// <param name="obj">The other object to compare</param> - /// <returns> - /// True if the passed object is of type <see cref="UnsafeMemoryHandle{T}"/> - /// and uses the structure equality operator - /// false otherwise. - /// </returns> - public readonly override bool Equals([NotNullWhen(true)] object? obj) => obj is UnsafeMemoryHandle<T> other && Equals(other); - - /// <summary> - /// Casts the handle to it's <see cref="Span{T}"/> representation - /// </summary> - /// <param name="handle">the handle to cast</param> - public static implicit operator Span<T>(in UnsafeMemoryHandle<T> handle) => handle.Span; - - /// <summary> - /// Equality overload - /// </summary> - /// <param name="left"></param> - /// <param name="right"></param> - /// <returns>True if handles are equal, flase otherwise</returns> - public static bool operator ==(in UnsafeMemoryHandle<T> left, in UnsafeMemoryHandle<T> right) => left.Equals(right); - /// <summary> - /// Equality overload - /// </summary> - /// <param name="left"></param> - /// <param name="right"></param> - /// <returns>True if handles are equal, flase otherwise</returns> - public static bool operator !=(in UnsafeMemoryHandle<T> left, in UnsafeMemoryHandle<T> right) => !left.Equals(right); - - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/VnString.cs b/Utils/src/Memory/VnString.cs deleted file mode 100644 index 7fa0c5a..0000000 --- a/Utils/src/Memory/VnString.cs +++ /dev/null @@ -1,497 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: VnString.cs -* -* VnString.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.ComponentModel; -using System.Threading.Tasks; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -using VNLib.Utils.IO; -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.Memory -{ - - /// <summary> - /// Provides an immutable character buffer stored on an unmanged heap. Contains handles to unmanged memory, and should be disposed - /// </summary> - [ComVisible(false)] - [ImmutableObject(true)] - public sealed class VnString : VnDisposeable, IEquatable<VnString>, IEquatable<string>, IEquatable<char[]>, IComparable<VnString>, IComparable<string> - { - private readonly MemoryHandle<char>? Handle; - - private readonly SubSequence<char> _stringSequence; - - /// <summary> - /// The number of unicode characters the current instance can reference - /// </summary> - public int Length => _stringSequence.Size; - /// <summary> - /// Gets a value indicating if the current instance is empty - /// </summary> - public bool IsEmpty => Length == 0; - - private VnString(SubSequence<char> sequence) - { - _stringSequence = sequence; - } - - private VnString( - MemoryHandle<char> handle, -#if TARGET_64_BIT - ulong start, -#else - int start, -#endif - int length) - { - Handle = handle ?? throw new ArgumentNullException(nameof(handle)); - //get sequence - _stringSequence = handle.GetSubSequence(start, length); - } - - /// <summary> - /// Creates and empty <see cref="VnString"/>, not particularly usefull, just and empty instance. - /// </summary> - public VnString() - { - //Default string sequence is empty and does not hold any memory - } - /// <summary> - /// Creates a new <see cref="VnString"/> around a <see cref="ReadOnlySpan{T}"/> or a <see cref="string"/> of data - /// </summary> - /// <param name="data"><see cref="ReadOnlySpan{T}"/> of data to replicate</param> - /// <exception cref="OutOfMemoryException"></exception> - public VnString(ReadOnlySpan<char> data) - { - //Create new handle with enough size (heap) - Handle = Memory.Shared.Alloc<char>(data.Length); - //Copy - Memory.Copy(data, Handle, 0); - //Get subsequence over the whole copy of data - _stringSequence = Handle.GetSubSequence(0, data.Length); - } - /// <summary> - /// Allocates a temporary buffer to read data from the stream until the end of the stream is reached. - /// Decodes data from the user-specified encoding - /// </summary> - /// <param name="stream">Active stream of data to decode to a string</param> - /// <param name="encoding"><see cref="Encoding"/> to use for decoding</param> - /// <param name="bufferSize">The size of the buffer to allocate during copying</param> - /// <returns>The new <see cref="VnString"/> instance</returns> - /// <exception cref="IOException"></exception> - /// <exception cref="OverflowException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - /// <exception cref="InvalidOperationException"></exception> - public static VnString FromStream(Stream stream, Encoding encoding, uint bufferSize) - { - //Make sure the stream is readable - if (!stream.CanRead) - { - throw new InvalidOperationException(); - } - //See if the stream is a vn memory stream - if (stream is VnMemoryStream vnms) - { - //Get the number of characters - int numChars = encoding.GetCharCount(vnms.AsSpan()); - //New handle - MemoryHandle<char> charBuffer = Memory.Shared.Alloc<char>(numChars); - try - { - //Write characters to character buffer - _ = encoding.GetChars(vnms.AsSpan(), charBuffer); - //Consume the new handle - return ConsumeHandle(charBuffer, 0, numChars); - } - catch - { - //If an error occured, dispose the buffer - charBuffer.Dispose(); - throw; - } - } - //Need to read from the stream old school with buffers - else - { - //Create a new char bufer that will expand dyanmically - MemoryHandle<char> charBuffer = Memory.Shared.Alloc<char>(bufferSize); - //Allocate a binary buffer - MemoryHandle<byte> binBuffer = Memory.Shared.Alloc<byte>(bufferSize); - try - { - int length = 0; - //span ref to bin buffer - Span<byte> buffer = binBuffer; - //Run in checked context for overflows - checked - { - do - { - //read - int read = stream.Read(buffer); - //guard - if (read <= 0) - { - break; - } - //Slice into only the read data - ReadOnlySpan<byte> readbytes = buffer[..read]; - //get num chars - int numChars = encoding.GetCharCount(readbytes); - //Guard for overflow - if (((ulong)(numChars + length)) >= int.MaxValue) - { - throw new OverflowException(); - } - //Re-alloc buffer - charBuffer.ResizeIfSmaller(length + numChars); - //Decode and update position - _= encoding.GetChars(readbytes, charBuffer.Span.Slice(length, numChars)); - //Update char count - length += numChars; - } while (true); - } - return ConsumeHandle(charBuffer, 0, length); - } - catch - { - //Free the memory allocated - charBuffer.Dispose(); - //We still want the exception to be propagated! - throw; - } - finally - { - //Dispose the binary buffer - binBuffer.Dispose(); - } - } - } - /// <summary> - /// Creates a new Vnstring from the <see cref="MemoryHandle{T}"/> buffer provided. This function "consumes" - /// a handle, meaning it now takes ownsership of the the memory it points to. - /// </summary> - /// <param name="handle">The <see cref="MemoryHandle{T}"/> to consume</param> - /// <param name="start">The offset from the begining of the buffer marking the begining of the string</param> - /// <param name="length">The number of characters this string points to</param> - /// <returns>The new <see cref="VnString"/></returns> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public static VnString ConsumeHandle( - MemoryHandle<char> handle, - -#if TARGET_64_BIT - ulong start, -#else - int start, -#endif - - int length) - { - if(length < 0) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - if((uint)length > handle.Length) - { - throw new ArgumentOutOfRangeException(nameof(length)); - } - return new VnString(handle, start, length); - } - /// <summary> - /// Asynchronously reads data from the specified stream and uses the specified encoding - /// to decode the binary data to a new <see cref="VnString"/> heap character buffer. - /// </summary> - /// <param name="stream">The stream to read data from</param> - /// <param name="encoding">The encoding to use while decoding data</param> - /// <param name="heap">The <see cref="IUnmangedHeap"/> to allocate buffers from</param> - /// <param name="bufferSize">The size of the buffer to allocate</param> - /// <returns>The new <see cref="VnString"/> containing the data</returns> - /// <exception cref="IOException"></exception> - /// <exception cref="OutOfMemoryException"></exception> - public static async ValueTask<VnString> FromStreamAsync(Stream stream, Encoding encoding, IUnmangedHeap heap, int bufferSize) - { - _ = stream ?? throw new ArgumentNullException(nameof(stream)); - _ = encoding ?? throw new ArgumentNullException(nameof(encoding)); - _ = heap ?? throw new ArgumentNullException(nameof(heap)); - - //Make sure the stream is readable - if (!stream.CanRead) - { - throw new IOException("The input stream is not readable"); - } - //See if the stream is a vn memory stream - if (stream is VnMemoryStream vnms) - { - //Get the number of characters - int numChars = encoding.GetCharCount(vnms.AsSpan()); - //New handle - MemoryHandle<char> charBuffer = heap.Alloc<char>(numChars); - try - { - //Write characters to character buffer - _ = encoding.GetChars(vnms.AsSpan(), charBuffer); - //Consume the new handle - return ConsumeHandle(charBuffer, 0, numChars); - } - catch - { - //If an error occured, dispose the buffer - charBuffer.Dispose(); - throw; - } - } - else - { - //Create a new char bufer starting with the buffer size - MemoryHandle<char> charBuffer = heap.Alloc<char>(bufferSize); - //Rent a temp binary buffer - IMemoryOwner<byte> binBuffer = heap.DirectAlloc<byte>(bufferSize); - try - { - int length = 0; - do - { - //read async - int read = await stream.ReadAsync(binBuffer.Memory); - //guard - if (read <= 0) - { - break; - } - //calculate the number of characters - int numChars = encoding.GetCharCount(binBuffer.Memory.Span[..read]); - //Guard for overflow - if (((ulong)(numChars + length)) >= int.MaxValue) - { - throw new OverflowException(); - } - //Re-alloc buffer - charBuffer.ResizeIfSmaller(length + numChars); - //Decode and update position - _ = encoding.GetChars(binBuffer.Memory.Span[..read], charBuffer.Span.Slice(length, numChars)); - //Update char count - length += numChars; - } while (true); - return ConsumeHandle(charBuffer, 0, length); - } - catch - { - //Free the memory allocated - charBuffer.Dispose(); - //We still want the exception to be propagated! - throw; - } - finally - { - //Dispose the binary buffer - binBuffer.Dispose(); - } - } - } - - /// <summary> - /// Gets the value of the character at the specified index - /// </summary> - /// <param name="index">The index of the character to get</param> - /// <returns>The <see cref="char"/> at the specified index within the buffer</returns> - /// <exception cref="ObjectDisposedException"></exception> - public char CharAt(int index) - { - //Check - Check(); - //Check bounds - return _stringSequence.Span[index]; - } - -#pragma warning disable IDE0057 // Use range operator - /// <summary> - /// Creates a <see cref="VnString"/> that is a window within the current string, - /// the referrence points to the same memory as the first instnace. - /// </summary> - /// <param name="start">The index within the current string to begin the child string</param> - /// <param name="count">The number of characters (or length) of the child string</param> - /// <returns>The child <see cref="VnString"/></returns> - /// <remarks> - /// Making substrings will reference the parents's underlying <see cref="MemoryHandle{T}"/> - /// and all children will be set in a disposed state when the parent instance is disposed - /// </remarks> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public VnString Substring(int start, int count) - { - //Check - Check(); - //Check bounds - if (start < 0 || (start + count) >= Length) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - //get sub-sequence slice for the current string - SubSequence<char> sub = _stringSequence.Slice((uint)start, count); - //Create new string with offsets pointing to same internal referrence - return new VnString(sub); - } - /// <summary> - /// Creates a <see cref="VnString"/> that is a window within the current string, - /// the referrence points to the same memory as the first instnace. - /// </summary> - /// <param name="start">The index within the current string to begin the child string</param> - /// <returns>The child <see cref="VnString"/></returns> - /// <remarks> - /// Making substrings will reference the parents's underlying <see cref="MemoryHandle{T}"/> - /// and all children will be set in a disposed state when the parent instance is disposed - /// </remarks> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public VnString Substring(int start) => Substring(start, (Length - start)); - public VnString this[Range range] - { - get - { - //get start - int start = range.Start.IsFromEnd ? Length - range.Start.Value : range.Start.Value; - //Get end - int end = range.End.IsFromEnd ? Length - range.End.Value : range.End.Value; - //Handle strings with no ending range - return (end >= start) ? Substring(start, (end - start)) : Substring(start); - } - } -#pragma warning restore IDE0057 // Use range operator - - /// <summary> - /// Gets a <see cref="ReadOnlySpan{T}"/> over the internal character buffer - /// </summary> - /// <returns></returns> - /// <exception cref="ObjectDisposedException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan<char> AsSpan() - { - //Check - Check(); - return _stringSequence.Span; - } - - /// <summary> - /// Gets a <see cref="string"/> copy of the internal buffer - /// </summary> - /// <returns><see cref="string"/> representation of internal data</returns> - /// <exception cref="ObjectDisposedException"></exception> - public override unsafe string ToString() - { - //Check - Check(); - //Create a new - return AsSpan().ToString(); - } - /// <summary> - /// Gets the value of the character at the specified index - /// </summary> - /// <param name="index">The index of the character to get</param> - /// <returns>The <see cref="char"/> at the specified index within the buffer</returns> - /// <exception cref="ObjectDisposedException"></exception> - public char this[int index] => CharAt(index); - - //Casting to a vnstring should be explicit so the caller doesnt misuse memory managment - public static explicit operator ReadOnlySpan<char>(VnString? value) => Unsafe.IsNullRef(ref value) || value!.Disposed ? ReadOnlySpan<char>.Empty : value.AsSpan(); - public static explicit operator VnString(string value) => new (value); - public static explicit operator VnString(ReadOnlySpan<char> value) => new (value); - public static explicit operator VnString(char[] value) => new (value); - ///<inheritdoc/> - public override bool Equals(object? obj) - { - if(obj == null) - { - return false; - } - return obj switch - { - VnString => Equals(obj as VnString), //Use operator overload - string => Equals(obj as string), //Use operator overload - char[] => Equals(obj as char[]), //Use operator overload - _ => false, - }; - } - ///<inheritdoc/> - public bool Equals(VnString? other) => !ReferenceEquals(other, null) && Equals(other.AsSpan()); - ///<inheritdoc/> - public bool Equals(VnString? other, StringComparison stringComparison) => !ReferenceEquals(other, null) && Equals(other.AsSpan(), stringComparison); - ///<inheritdoc/> - public bool Equals(string? other) => Equals(other.AsSpan()); - ///<inheritdoc/> - public bool Equals(string? other, StringComparison stringComparison) => Equals(other.AsSpan(), stringComparison); - ///<inheritdoc/> - public bool Equals(char[]? other) => Equals(other.AsSpan()); - ///<inheritdoc/> - public bool Equals(char[]? other, StringComparison stringComparison) => Equals(other.AsSpan(), stringComparison); - ///<inheritdoc/> - public bool Equals(ReadOnlySpan<char> other, StringComparison stringComparison = StringComparison.Ordinal) => Length == other.Length && AsSpan().Equals(other, stringComparison); - ///<inheritdoc/> - public bool Equals(SubSequence<char> other) => Length == other.Size && AsSpan().SequenceEqual(other.Span); - ///<inheritdoc/> - public int CompareTo(string? other) => AsSpan().CompareTo(other, StringComparison.Ordinal); - ///<inheritdoc/> - ///<exception cref="ArgumentNullException"></exception> - public int CompareTo(VnString? other) - { - _ = other ?? throw new ArgumentNullException(nameof(other)); - return AsSpan().CompareTo(other.AsSpan(), StringComparison.Ordinal); - } - - /// <summary> - /// Gets a hashcode for the underyling string by using the .NET <see cref="string.GetHashCode()"/> - /// method on the character representation of the data - /// </summary> - /// <returns></returns> - /// <remarks> - /// It is safe to compare hashcodes of <see cref="VnString"/> to the <see cref="string"/> class or - /// a character span etc - /// </remarks> - /// <exception cref="ObjectDisposedException"></exception> - public override int GetHashCode() => string.GetHashCode(AsSpan()); - ///<inheritdoc/> - protected override void Free() - { - //Dispose the handle if we own it (null if we do not have the parent handle) - Handle?.Dispose(); - } - - public static bool operator ==(VnString left, VnString right) => ReferenceEquals(left, null) ? ReferenceEquals(right, null) : left.Equals(right); - - public static bool operator !=(VnString left, VnString right) => !(left == right); - - public static bool operator <(VnString left, VnString right) => ReferenceEquals(left, null) ? !ReferenceEquals(right, null) : left.CompareTo(right) < 0; - - public static bool operator <=(VnString left, VnString right) => ReferenceEquals(left, null) || left.CompareTo(right) <= 0; - - public static bool operator >(VnString left, VnString right) => !ReferenceEquals(left, null) && left.CompareTo(right) > 0; - - public static bool operator >=(VnString left, VnString right) => ReferenceEquals(left, null) ? ReferenceEquals(right, null) : left.CompareTo(right) >= 0; - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/VnTable.cs b/Utils/src/Memory/VnTable.cs deleted file mode 100644 index 1d5c0a6..0000000 --- a/Utils/src/Memory/VnTable.cs +++ /dev/null @@ -1,213 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: VnTable.cs -* -* VnTable.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.Memory -{ - /// <summary> - /// Provides a Row-Major ordered table for use of storing value-types in umnaged heap memory - /// </summary> - /// <typeparam name="T"></typeparam> - public sealed class VnTable<T> : VnDisposeable, IIndexable<uint, T> where T : unmanaged - { - private readonly MemoryHandle<T>? BufferHandle; - /// <summary> - /// A value that indicates if the table does not contain any values - /// </summary> - public bool Empty { get; } - /// <summary> - /// The number of rows in the table - /// </summary> - public int Rows { get; } - /// <summary> - /// The nuber of columns in the table - /// </summary> - public int Cols { get; } - /// <summary> - /// Creates a new 2 dimensional table in unmanaged heap memory, using the <see cref="Memory.Shared"/> heap. - /// User should dispose of the table when no longer in use - /// </summary> - /// <param name="rows">Number of rows in the table</param> - /// <param name="cols">Number of columns in the table</param> - public VnTable(int rows, int cols) : this(Memory.Shared, rows, cols) { } - /// <summary> - /// Creates a new 2 dimensional table in unmanaged heap memory, using the specified heap. - /// User should dispose of the table when no longer in use - /// </summary> - /// <param name="heap"><see cref="PrivateHeap"/> to allocate table memory from</param> - /// <param name="rows">Number of rows in the table</param> - /// <param name="cols">Number of columns in the table</param> - public VnTable(IUnmangedHeap heap, int rows, int cols) - { - if (rows < 0 || cols < 0) - { - throw new ArgumentOutOfRangeException(nameof(rows), "Row and coulmn number must be 0 or larger"); - } - //empty table - if (rows == 0 && cols == 0) - { - Empty = true; - return; - } - - _ = heap ?? throw new ArgumentNullException(nameof(heap)); - - this.Rows = rows; - this.Cols = cols; - - long tableSize = Math.BigMul(rows, cols); - - //Alloc a buffer with zero memory enabled, with Rows * Cols number of elements - BufferHandle = heap.Alloc<T>(tableSize, true); - } - /// <summary> - /// Gets the value of an item in the table at the given indexes - /// </summary> - /// <param name="row">Row address of the item</param> - /// <param name="col">Column address of item</param> - /// <returns>The value of the item</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="InvalidOperationException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public T Get(int row, int col) - { - Check(); - if (this.Empty) - { - throw new InvalidOperationException("Table is empty"); - } - if (row < 0 || col < 0) - { - throw new ArgumentOutOfRangeException(nameof(row), "Row or column address less than 0"); - } - if (row > this.Rows) - { - throw new ArgumentOutOfRangeException(nameof(row), "Row out of range of current table"); - } - if (col > this.Cols) - { - throw new ArgumentOutOfRangeException(nameof(col), "Column address out of range of current table"); - } - //Calculate the address in memory for the item - //Calc row offset - long address = Cols * row; - //Calc column offset - address += col; - unsafe - { - //Get the value item - return *(BufferHandle!.GetOffset(address)); - } - } - /// <summary> - /// Sets the value of an item in the table at the given address - /// </summary> - /// <param name="item">Value of item to store</param> - /// <param name="row">Row address of the item</param> - /// <param name="col">Column address of item</param> - /// <returns>The value of the item</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="InvalidOperationException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public void Set(int row, int col, T item) - { - Check(); - if (this.Empty) - { - throw new InvalidOperationException("Table is empty"); - } - if (row < 0 || col < 0) - { - throw new ArgumentOutOfRangeException(nameof(row), "Row or column address less than 0"); - } - if (row > this.Rows) - { - throw new ArgumentOutOfRangeException(nameof(row), "Row out of range of current table"); - } - if (col > this.Cols) - { - throw new ArgumentOutOfRangeException(nameof(col), "Column address out of range of current table"); - } - //Calculate the address in memory for the item - //Calc row offset - long address = Cols * row; - //Calc column offset - address += col; - //Set the value item - unsafe - { - *BufferHandle!.GetOffset(address) = item; - } - } - /// <summary> - /// Equivalent to <see cref="VnTable{T}.Get(int, int)"/> and <see cref="VnTable{T}.Set(int, int, T)"/> - /// </summary> - /// <param name="row">Row address of item</param> - /// <param name="col">Column address of item</param> - /// <returns>The value of the item</returns> - public T this[int row, int col] - { - get => Get(row, col); - set => Set(row, col, value); - } - /// <summary> - /// Allows for direct addressing in the table. - /// </summary> - /// <param name="index"></param> - /// <returns></returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="InvalidOperationException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public unsafe T this[uint index] - { - get - { - Check(); - return !Empty ? *(BufferHandle!.GetOffset(index)) : throw new InvalidOperationException("Cannot index an empty table"); - } - - set - { - Check(); - if (Empty) - { - throw new InvalidOperationException("Cannot index an empty table"); - } - *(BufferHandle!.GetOffset(index)) = value; - } - } - ///<inheritdoc/> - protected override void Free() - { - if (!this.Empty) - { - //Dispose the buffer - BufferHandle!.Dispose(); - } - } - } -}
\ No newline at end of file diff --git a/Utils/src/Memory/VnTempBuffer.cs b/Utils/src/Memory/VnTempBuffer.cs deleted file mode 100644 index 7726fe1..0000000 --- a/Utils/src/Memory/VnTempBuffer.cs +++ /dev/null @@ -1,207 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: VnTempBuffer.cs -* -* VnTempBuffer.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; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -using VNLib.Utils.Extensions; -using System.Security.Cryptography; - -namespace VNLib.Utils.Memory -{ - /// <summary> - /// A disposable temporary buffer from shared ArrayPool - /// </summary> - /// <typeparam name="T">Type of buffer to create</typeparam> - public sealed class VnTempBuffer<T> : VnDisposeable, IIndexable<int, T>, IMemoryHandle<T> - { - private readonly ArrayPool<T> Pool; - - /// <summary> - /// Referrence to internal buffer - /// </summary> - public T[] Buffer { get; private set; } - /// <summary> - /// Inital/desired size of internal buffer - /// </summary> - public int InitSize { get; } - - /// <summary> - /// Actual length of internal buffer - /// </summary> - public ulong Length => (ulong)Buffer.LongLength; - - /// <summary> - /// Actual length of internal buffer - /// </summary> - public int IntLength => Buffer.Length; - - ///<inheritdoc/> - ///<exception cref="ObjectDisposedException"></exception> - public Span<T> Span - { - get - { - Check(); - return new Span<T>(Buffer, 0, InitSize); - } - } - - /// <summary> - /// Allocates a new <see cref="VnTempBuffer{BufType}"/> with a new buffer from shared array-pool - /// </summary> - /// <param name="minSize">Minimum size of the buffer</param> - /// <param name="zero">Set the zero memory flag on close</param> - public VnTempBuffer(int minSize, bool zero = false) :this(ArrayPool<T>.Shared, minSize, zero) - {} - /// <summary> - /// Allocates a new <see cref="VnTempBuffer{BufType}"/> with a new buffer from specified array-pool - /// </summary> - /// <param name="pool">The <see cref="ArrayPool{T}"/> to allocate from and return to</param> - /// <param name="minSize">Minimum size of the buffer</param> - /// <param name="zero">Set the zero memory flag on close</param> - public VnTempBuffer(ArrayPool<T> pool, int minSize, bool zero = false) - { - Pool = pool; - Buffer = pool.Rent(minSize, zero); - InitSize = minSize; - } - /// <summary> - /// Gets an offset wrapper around the current buffer - /// </summary> - /// <param name="offset">Offset from begining of current buffer</param> - /// <param name="count">Number of <typeparamref name="T"/> from offset</param> - /// <returns>An <see cref="ArraySegment{BufType}"/> wrapper around the current buffer containing the offset</returns> - public ArraySegment<T> GetOffsetWrapper(int offset, int count) - { - Check(); - //Let arraysegment throw exceptions for checks - return new ArraySegment<T>(Buffer, offset, count); - } - ///<inheritdoc/> - public T this[int index] - { - get - { - Check(); - return Buffer[index]; - } - set - { - Check(); - Buffer[index] = value; - } - } - - /// <summary> - /// Gets a memory structure around the internal buffer - /// </summary> - /// <returns>A memory structure over the buffer</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public Memory<T> AsMemory() - { - Check(); - return new Memory<T>(Buffer, 0, InitSize); - } - /// <summary> - /// Gets a memory structure around the internal buffer - /// </summary> - /// <param name="count">The number of elements included in the result</param> - /// <param name="start">A value specifying the begining index of the buffer to include</param> - /// <returns>A memory structure over the buffer</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public Memory<T> AsMemory(int start, int count) - { - Check(); - return new Memory<T>(Buffer, start, count); - } - /// <summary> - /// Gets a memory structure around the internal buffer - /// </summary> - /// <param name="count">The number of elements included in the result</param> - /// <returns>A memory structure over the buffer</returns> - /// <exception cref="ObjectDisposedException"></exception> - /// <exception cref="ArgumentOutOfRangeException"></exception> - public Memory<T> AsMemory(int count) - { - Check(); - return new Memory<T>(Buffer, 0, count); - } - - /* - * Allow implict casts to span/arrayseg/memory - */ - public static implicit operator Memory<T>(VnTempBuffer<T> buf) => buf == null ? Memory<T>.Empty : buf.ToMemory(); - public static implicit operator Span<T>(VnTempBuffer<T> buf) => buf == null ? Span<T>.Empty : buf.ToSpan(); - public static implicit operator ArraySegment<T>(VnTempBuffer<T> buf) => buf == null ? ArraySegment<T>.Empty : buf.ToArraySegment(); - - public Memory<T> ToMemory() => Disposed ? Memory<T>.Empty : Buffer.AsMemory(0, InitSize); - public Span<T> ToSpan() => Disposed ? Span<T>.Empty : Buffer.AsSpan(0, InitSize); - public ArraySegment<T> ToArraySegment() => Disposed ? ArraySegment<T>.Empty : new(Buffer, 0, InitSize); - - /// <summary> - /// Returns buffer to shared array-pool - /// </summary> - protected override void Free() - { - //Return the buffer to the array pool - Pool.Return(Buffer); - - //Set buffer to null, -#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. - Buffer = null; -#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. - } - - unsafe MemoryHandle IPinnable.Pin(int elementIndex) - { - //Guard - if (elementIndex < 0 || elementIndex >= IntLength) - { - throw new ArgumentOutOfRangeException(nameof(elementIndex)); - } - - //Pin the array - GCHandle arrHandle = GCHandle.Alloc(Buffer, GCHandleType.Pinned); - //Get array base address - void* basePtr = (void*)arrHandle.AddrOfPinnedObject(); - //Get element offset - void* indexOffet = Unsafe.Add<T>(basePtr, elementIndex); - return new(indexOffet, arrHandle, this); - } - - void IPinnable.Unpin() - { - //Gchandle will manage the unpin - } - - ~VnTempBuffer() => Free(); - - - } -}
\ No newline at end of file diff --git a/Utils/src/Native/SafeLibraryHandle.cs b/Utils/src/Native/SafeLibraryHandle.cs deleted file mode 100644 index 4772bd4..0000000 --- a/Utils/src/Native/SafeLibraryHandle.cs +++ /dev/null @@ -1,220 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: SafeLibraryHandle.cs -* -* SafeLibraryHandle.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.Linq; -using System.Reflection; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Diagnostics.CodeAnalysis; - -using VNLib.Utils.Extensions; - -namespace VNLib.Utils.Native -{ - /// <summary> - /// Represents a safe handle to a native library loaded to the current process - /// </summary> - public sealed class SafeLibraryHandle : SafeHandle - { - ///<inheritdoc/> - public override bool IsInvalid => handle == IntPtr.Zero; - - private SafeLibraryHandle(IntPtr libHandle) : base(IntPtr.Zero, true) - { - //Init handle - SetHandle(libHandle); - } - - /// <summary> - /// Finds and loads the specified native libary into the current process by its name at runtime - /// </summary> - /// <param name="libPath">The path (or name of libary) to search for</param> - /// <param name="searchPath"> - /// The <see cref="DllImportSearchPath"/> used to search for libaries - /// within the current filesystem - /// </param> - /// <returns>The loaded <see cref="SafeLibraryHandle"/></returns> - /// <exception cref="ArgumentNullException"></exception> - /// <exception cref="DllNotFoundException"></exception> - public static SafeLibraryHandle LoadLibrary(string libPath, DllImportSearchPath searchPath = DllImportSearchPath.ApplicationDirectory) - { - _ = libPath ?? throw new ArgumentNullException(nameof(libPath)); - //See if the path includes a file extension - return TryLoadLibrary(libPath, searchPath, out SafeLibraryHandle? lib) - ? lib - : throw new DllNotFoundException($"The library {libPath} or one of its dependencies could not be found"); - } - - /// <summary> - /// Attempts to load the specified native libary into the current process by its name at runtime - /// </summary> - ///<param name="libPath">The path (or name of libary) to search for</param> - /// <param name="searchPath"> - /// The <see cref="DllImportSearchPath"/> used to search for libaries - /// within the current filesystem - /// </param> - /// <param name="lib">The handle to the libary if successfully loaded</param> - /// <returns>True if the libary was found and loaded into the current process</returns> - public static bool TryLoadLibrary(string libPath, DllImportSearchPath searchPath, [NotNullWhen(true)] out SafeLibraryHandle? lib) - { - lib = null; - //Allow full rooted paths - if (Path.IsPathRooted(libPath)) - { - //Attempt a native load - if (NativeLibrary.TryLoad(libPath, out IntPtr libHandle)) - { - lib = new(libHandle); - return true; - } - return false; - } - //Check application directory first (including subdirectories) - if ((searchPath & DllImportSearchPath.ApplicationDirectory) > 0) - { - //get the current directory - string libDir = Directory.GetCurrentDirectory(); - if (TryLoadLibraryInternal(libDir, libPath, SearchOption.TopDirectoryOnly, out lib)) - { - return true; - } - } - //See if search in the calling assembly directory - if ((searchPath & DllImportSearchPath.AssemblyDirectory) > 0) - { - //Get the calling assmblies directory - string libDir = Assembly.GetCallingAssembly().Location; - Debug.WriteLine("Native library searching for calling assembly location:{0} ", libDir); - if (TryLoadLibraryInternal(libDir, libPath, SearchOption.TopDirectoryOnly, out lib)) - { - return true; - } - } - //Search system32 dir - if ((searchPath & DllImportSearchPath.System32) > 0) - { - //Get the system directory - string libDir = Environment.GetFolderPath(Environment.SpecialFolder.SystemX86); - if (TryLoadLibraryInternal(libDir, libPath, SearchOption.TopDirectoryOnly, out lib)) - { - return true; - } - } - //Attempt a native load - { - if (NativeLibrary.TryLoad(libPath, out IntPtr libHandle)) - { - lib = new(libHandle); - return true; - } - return false; - } - } - - private static bool TryLoadLibraryInternal(string libDir, string libPath, SearchOption dirSearchOptions, [NotNullWhen(true)] out SafeLibraryHandle? libary) - { - //Try to find the libary file - string? libFile = GetLibraryFile(libDir, libPath, dirSearchOptions); - //Load libary - if (libFile != null && NativeLibrary.TryLoad(libFile, out IntPtr libHandle)) - { - libary = new SafeLibraryHandle(libHandle); - return true; - } - libary = null; - return false; - } - private static string? GetLibraryFile(string dirPath, string libPath, SearchOption search) - { - //slice the lib to its file name - libPath = Path.GetFileName(libPath); - libPath = Path.ChangeExtension(libPath, OperatingSystem.IsWindows() ? ".dll" : ".so"); - //Select the first file that matches the name - return Directory.EnumerateFiles(dirPath, libPath, search).FirstOrDefault(); - } - - /// <summary> - /// Loads a native method from the library of the specified name and managed delegate - /// </summary> - /// <typeparam name="T">The native method delegate type</typeparam> - /// <param name="methodName">The name of the native method</param> - /// <returns>A wapper handle around the native method delegate</returns> - /// <exception cref="ArgumentNullException"></exception> - /// <exception cref="ObjectDisposedException">If the handle is closed or invalid</exception> - /// <exception cref="EntryPointNotFoundException">When the specified entrypoint could not be found</exception> - public SafeMethodHandle<T> GetMethod<T>(string methodName) where T : Delegate - { - //Increment handle count before obtaining a method - bool success = false; - DangerousAddRef(ref success); - if (!success) - { - throw new ObjectDisposedException("The libary has been released!"); - } - try - { - //Get the method pointer - IntPtr nativeMethod = NativeLibrary.GetExport(handle, methodName); - //Get the delegate for the function pointer - T method = Marshal.GetDelegateForFunctionPointer<T>(nativeMethod); - return new(this, method); - } - catch - { - DangerousRelease(); - throw; - } - } - /// <summary> - /// Gets an delegate wrapper for the specified method without tracking its referrence. - /// The caller must manage the <see cref="SafeLibraryHandle"/> referrence count in order - /// to not leak resources or cause process corruption - /// </summary> - /// <typeparam name="T">The native method delegate type</typeparam> - /// <param name="methodName">The name of the native method</param> - /// <returns>A the delegate wrapper on the native method</returns> - /// <exception cref="ArgumentNullException"></exception> - /// <exception cref="ObjectDisposedException">If the handle is closed or invalid</exception> - /// <exception cref="EntryPointNotFoundException">When the specified entrypoint could not be found</exception> - public T DangerousGetMethod<T>(string methodName) where T : Delegate - { - this.ThrowIfClosed(); - //Get the method pointer - IntPtr nativeMethod = NativeLibrary.GetExport(handle, methodName); - //Get the delegate for the function pointer - return Marshal.GetDelegateForFunctionPointer<T>(nativeMethod); - } - - ///<inheritdoc/> - protected override bool ReleaseHandle() - { - //Free the library and set the handle as invalid - NativeLibrary.Free(handle); - SetHandleAsInvalid(); - return true; - } - } -} diff --git a/Utils/src/Native/SafeMethodHandle.cs b/Utils/src/Native/SafeMethodHandle.cs deleted file mode 100644 index 3ba0879..0000000 --- a/Utils/src/Native/SafeMethodHandle.cs +++ /dev/null @@ -1,61 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: SafeMethodHandle.cs -* -* SafeMethodHandle.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.Resources; - -namespace VNLib.Utils.Native -{ - /// <summary> - /// Represents a handle to a <see cref="SafeLibraryHandle"/>'s - /// native method - /// </summary> - /// <typeparam name="T">The native method deelgate type</typeparam> - public class SafeMethodHandle<T> : OpenHandle where T : Delegate - { - private T? _method; - private readonly SafeLibraryHandle Library; - - internal SafeMethodHandle(SafeLibraryHandle lib, T method) - { - Library = lib; - _method = method; - } - - /// <summary> - /// A delegate to the native method - /// </summary> - public T? Method => _method; - - ///<inheritdoc/> - protected override void Free() - { - //Release the method - _method = default; - //Decrement lib handle count - Library.DangerousRelease(); - } - } -} diff --git a/Utils/src/NativeLibraryException.cs b/Utils/src/NativeLibraryException.cs deleted file mode 100644 index 5c66852..0000000 --- a/Utils/src/NativeLibraryException.cs +++ /dev/null @@ -1,89 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: NativeLibraryException.cs -* -* NativeLibraryException.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 -{ - /// <summary> - /// Raised when an internal buffer was not propery sized for the opreation - /// </summary> - public class InternalBufferTooSmallException : OutOfMemoryException - { - public InternalBufferTooSmallException(string message) : base(message) - {} - - public InternalBufferTooSmallException(string message, Exception innerException) : base(message, innerException) - {} - - public InternalBufferTooSmallException() - {} - } - - /// <summary> - /// A base class for all native library related exceptions - /// </summary> - public class NativeLibraryException : SystemException - { - public NativeLibraryException(string message) : base(message) - {} - - public NativeLibraryException(string message, Exception innerException) : base(message, innerException) - {} - - public NativeLibraryException() - {} - } - - /// <summary> - /// Base exception class for native memory related exceptions - /// </summary> - public class NativeMemoryException : NativeLibraryException - { - public NativeMemoryException(string message) : base(message) - {} - - public NativeMemoryException(string message, Exception innerException) : base(message, innerException) - {} - - public NativeMemoryException() - {} - } - - /// <summary> - /// Raised when a memory allocation or resize failed because there is - /// no more memory available - /// </summary> - public class NativeMemoryOutOfMemoryException : OutOfMemoryException - { - public NativeMemoryOutOfMemoryException(string message) : base(message) - {} - - public NativeMemoryOutOfMemoryException(string message, Exception innerException) : base(message, innerException) - {} - - public NativeMemoryOutOfMemoryException() - {} - } -} diff --git a/Utils/src/Resources/BackedResourceBase.cs b/Utils/src/Resources/BackedResourceBase.cs deleted file mode 100644 index 0a2e1e3..0000000 --- a/Utils/src/Resources/BackedResourceBase.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: BackedResourceBase.cs -* -* BackedResourceBase.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.Text.Json; -using System.Runtime.CompilerServices; - -namespace VNLib.Utils.Resources -{ - /// <summary> - /// A base class for a resource that is backed by an external data store. - /// Implements the <see cref="IResource"/> interfaceS - /// </summary> - public abstract class BackedResourceBase : IResource - { - ///<inheritdoc/> - public bool IsReleased { get; protected set; } - - /// <summary> - /// Optional <see cref="JsonSerializerOptions"/> to be used when serializing - /// the resource - /// </summary> - internal protected virtual JsonSerializerOptions? JSO { get; } - - /// <summary> - /// A value indicating whether the instance should be deleted when released - /// </summary> - protected bool Deleted { get; set; } - /// <summary> - /// A value indicating whether the instance should be updated when released - /// </summary> - protected bool Modified { get; set; } - - /// <summary> - /// Checks if the resouce has been disposed and raises an exception if it is - /// </summary> - /// <exception cref="ObjectDisposedException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void Check() - { - if (IsReleased) - { - throw new ObjectDisposedException("The resource has been disposed"); - } - } - - /// <summary> - /// Returns the JSON serializable resource to be updated during an update - /// </summary> - /// <returns>The resource to update</returns> - protected abstract object GetResource(); - - /// <summary> - /// Marks the resource for deletion from backing store during closing events - /// </summary> - public virtual void Delete() => Deleted = true; - } -}
\ No newline at end of file diff --git a/Utils/src/Resources/CallbackOpenHandle.cs b/Utils/src/Resources/CallbackOpenHandle.cs deleted file mode 100644 index 625bd45..0000000 --- a/Utils/src/Resources/CallbackOpenHandle.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: CallbackOpenHandle.cs -* -* CallbackOpenHandle.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.Resources -{ - /// <summary> - /// A concrete <see cref="OpenHandle"/> for a defered operation or a resource that should be released or unwound - /// when the instance lifetime has ended. - /// </summary> - public sealed class CallbackOpenHandle : OpenHandle - { - private readonly Action ReleaseFunc; - /// <summary> - /// Creates a new generic <see cref="OpenHandle"/> with the specified release callback method - /// </summary> - /// <param name="release">The callback function to invoke when the <see cref="OpenHandle"/> is disposed</param> - public CallbackOpenHandle(Action release) => ReleaseFunc = release; - ///<inheritdoc/> - protected override void Free() => ReleaseFunc(); - } -}
\ No newline at end of file diff --git a/Utils/src/Resources/ExclusiveResourceHandle.cs b/Utils/src/Resources/ExclusiveResourceHandle.cs deleted file mode 100644 index 173bdd1..0000000 --- a/Utils/src/Resources/ExclusiveResourceHandle.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ExclusiveResourceHandle.cs -* -* ExclusiveResourceHandle.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.Resources -{ - /// <summary> - /// While in scope, holds an exclusive lock on the specified object that implements the <see cref="IExclusiveResource"/> interface - /// </summary> - /// <typeparam name="T"></typeparam> - public class ExclusiveResourceHandle<T> : OpenResourceHandle<T> where T : IExclusiveResource - { - private readonly Action Release; - private readonly Lazy<T> LazyVal; - - /// <summary> - /// <inheritdoc/> - /// <br></br> - /// <br></br> - /// This value is lazy inialized and will invoke the factory function on first access. - /// Accessing this variable is thread safe while the handle is in scope - /// <br></br> - /// <br></br> - /// Exceptions will be propagated during initialziation - /// </summary> - public override T Resource => LazyVal.Value; - - /// <summary> - /// Creates a new <see cref="ExclusiveResourceHandle{TResource}"/> wrapping the - /// <see cref="IExclusiveResource"/> object to manage its lifecycle and reuse - /// </summary> - /// <param name="factory">Factory function that will generate the value when used</param> - /// <param name="release">Callback function that will be invoked after object gets disposed</param> - internal ExclusiveResourceHandle(Func<T> factory, Action release) - { - //Store the release action - Release = release; - //Store the new lazy val from the factory function (enabled thread safey) - LazyVal = new(factory, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication); - } - - protected override void Free() - { - try - { - //Dispose the value if it was created, otherwise do not create it - if (LazyVal.IsValueCreated) - { - Resource?.Release(); - } - } - finally - { - //Always invoke the release callback - Release(); - } - } - } -}
\ No newline at end of file diff --git a/Utils/src/Resources/IExclusiveResource.cs b/Utils/src/Resources/IExclusiveResource.cs deleted file mode 100644 index 43ec607..0000000 --- a/Utils/src/Resources/IExclusiveResource.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IExclusiveResource.cs -* -* IExclusiveResource.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.Resources -{ - - /// <summary> - /// An object, that when used in a mulithreading context, guaruntees that the caller has exclusive - /// access to the instance and relinquishes exclusive access when Release() is called; - /// </summary> - public interface IExclusiveResource : IResource - { - /// <summary> - /// Releases the resource from use. Called when a <see cref="ExclusiveResourceHandle{T}"/> is disposed - /// </summary> - void Release(); - } -}
\ No newline at end of file diff --git a/Utils/src/Resources/IResource.cs b/Utils/src/Resources/IResource.cs deleted file mode 100644 index 345e284..0000000 --- a/Utils/src/Resources/IResource.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: IResource.cs -* -* IResource.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.Resources -{ - /// <summary> - /// The base interface for any resource that may be backed by an external - /// data store, and has a finite lifetime, usually accessed within a handle - /// </summary> - public interface IResource - { - /// <summary> - /// Gets a value indicating if the resource has been released - /// </summary> - bool IsReleased { get; } - } -}
\ No newline at end of file diff --git a/Utils/src/Resources/OpenHandle.cs b/Utils/src/Resources/OpenHandle.cs deleted file mode 100644 index 6133a65..0000000 --- a/Utils/src/Resources/OpenHandle.cs +++ /dev/null @@ -1,38 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: OpenHandle.cs -* -* OpenHandle.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.Resources -{ - /// <summary> - /// Represents a base class for an open resource or operation that is valid while being held, - /// and is released or unwound when disposed. - /// </summary> - /// <remarks> - /// The <see cref="OpenHandle"/> pattern, may throw exceptions when disposed as deferred - /// release actions are completed - /// </remarks> - public abstract class OpenHandle : VnDisposeable - { - } -}
\ No newline at end of file diff --git a/Utils/src/Resources/OpenResourceHandle.cs b/Utils/src/Resources/OpenResourceHandle.cs deleted file mode 100644 index d9f9fd2..0000000 --- a/Utils/src/Resources/OpenResourceHandle.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: OpenResourceHandle.cs -* -* OpenResourceHandle.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.Resources -{ - /// <summary> - /// <para> - /// An abstract base class for an <see cref="OpenHandle"/> that holds a specific resource and manages its lifetime. - /// </para> - /// </summary> - /// <inheritdoc/> - /// <typeparam name="TResource">The resource type</typeparam> - public abstract class OpenResourceHandle<TResource> : OpenHandle - { - /// <summary> - /// The resource held by the open handle - /// </summary> - /// <exception cref="ObjectDisposedException"></exception> - public abstract TResource Resource { get; } - } -}
\ No newline at end of file diff --git a/Utils/src/Resources/ResourceDeleteFailedException.cs b/Utils/src/Resources/ResourceDeleteFailedException.cs deleted file mode 100644 index 8e796b5..0000000 --- a/Utils/src/Resources/ResourceDeleteFailedException.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ResourceDeleteFailedException.cs -* -* ResourceDeleteFailedException.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.Runtime.Serialization; - -namespace VNLib.Utils.Resources -{ - /// <summary> - /// Raised when a resource delete has failed - /// </summary> - public class ResourceDeleteFailedException : Exception - { - public ResourceDeleteFailedException() { } - public ResourceDeleteFailedException(string message) : base(message) { } - public ResourceDeleteFailedException(string message, Exception innerException) : base(message, innerException) { } - protected ResourceDeleteFailedException(SerializationInfo info, StreamingContext context) : base(info, context) { } - } -}
\ No newline at end of file diff --git a/Utils/src/Resources/ResourceUpdateFailedException.cs b/Utils/src/Resources/ResourceUpdateFailedException.cs deleted file mode 100644 index b4b2b3a..0000000 --- a/Utils/src/Resources/ResourceUpdateFailedException.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: ResourceUpdateFailedException.cs -* -* ResourceUpdateFailedException.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.Runtime.Serialization; - -namespace VNLib.Utils.Resources -{ - /// <summary> - /// Raised when a resource update has failed - /// </summary> - public class ResourceUpdateFailedException : Exception - { - public ResourceUpdateFailedException() { } - public ResourceUpdateFailedException(string message) : base(message) { } - public ResourceUpdateFailedException(string message, Exception innerException) : base(message, innerException) { } - protected ResourceUpdateFailedException(SerializationInfo info, StreamingContext context) : base(info, context) { } - } -}
\ No newline at end of file diff --git a/Utils/src/Resources/UpdatableResource.cs b/Utils/src/Resources/UpdatableResource.cs deleted file mode 100644 index 16f26f2..0000000 --- a/Utils/src/Resources/UpdatableResource.cs +++ /dev/null @@ -1,113 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: UpdatableResource.cs -* -* UpdatableResource.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 VNLib.Utils.IO; - -namespace VNLib.Utils.Resources -{ - /// <summary> - /// A callback delegate used for updating a <see cref="UpdatableResource"/> - /// </summary> - /// <param name="source">The <see cref="UpdatableResource"/> to be updated</param> - /// <param name="data">The serialized data to be stored/updated</param> - /// <exception cref="ResourceUpdateFailedException"></exception> - public delegate void UpdateCallback(object source, Stream data); - /// <summary> - /// A callback delegate invoked when a <see cref="UpdatableResource"/> delete is requested - /// </summary> - /// <param name="source">The <see cref="UpdatableResource"/> to be deleted</param> - /// <exception cref="ResourceDeleteFailedException"></exception> - public delegate void DeleteCallback(object source); - - /// <summary> - /// Implemented by a resource that is backed by an external data store, that when modified or deleted will - /// be reflected to the backing store. - /// </summary> - public abstract class UpdatableResource : BackedResourceBase, IExclusiveResource - { - /// <summary> - /// The update callback method to invoke during a release operation - /// when the resource is updated. - /// </summary> - protected abstract UpdateCallback UpdateCb { get; } - /// <summary> - /// The callback method to invoke during a realease operation - /// when the resource should be deleted - /// </summary> - protected abstract DeleteCallback DeleteCb { get; } - - /// <summary> - /// <inheritdoc/> - /// </summary> - /// <exception cref="InvalidOperationException"></exception> - /// <exception cref="ResourceDeleteFailedException"></exception> - /// <exception cref="ResourceUpdateFailedException"></exception> - public virtual void Release() - { - //If resource has already been realeased, return - if (IsReleased) - { - return; - } - //If deleted flag is set, invoke the delete callback - if (Deleted) - { - DeleteCb(this); - } - //If the state has been modifed, flush changes to the store - else if (Modified) - { - FlushPendingChanges(); - } - //Set the released value - IsReleased = true; - } - - /// <summary> - /// Writes the current state of the the resource to the backing store - /// immediatly by invoking the specified callback. - /// <br></br> - /// <br></br> - /// Only call this method if your store supports multiple state updates - /// </summary> - protected virtual void FlushPendingChanges() - { - //Get the resource - object resource = GetResource(); - //Open a memory stream to store data in - using VnMemoryStream data = new(); - //Serialize and write to stream - VnEncoding.JSONSerializeToBinary(resource, data, resource.GetType(), JSO); - //Reset stream to begining - _ = data.Seek(0, SeekOrigin.Begin); - //Invoke update callback - UpdateCb(this, data); - //Clear modified flag - Modified = false; - } - } -}
\ No newline at end of file diff --git a/Utils/src/VNLib.Utils.csproj b/Utils/src/VNLib.Utils.csproj deleted file mode 100644 index b14ab27..0000000 --- a/Utils/src/VNLib.Utils.csproj +++ /dev/null @@ -1,47 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <TargetFramework>net6.0</TargetFramework> - <RootNamespace>VNLib.Utils</RootNamespace> - <Authors>Vaughn Nugent</Authors> - <Product>VNLib Utilities Library</Product> - <Copyright>Copyright © 2022 Vaughn Nugent</Copyright> - <PackageProjectUrl>https://www.vaughnnugent.com/resources</PackageProjectUrl> - <AssemblyName>VNLib.Utils</AssemblyName> - <Version>1.0.1.10</Version> - <Description>Base utilities library, structs, classes</Description> - <AllowUnsafeBlocks>true</AllowUnsafeBlocks> - <Nullable>enable</Nullable> - <GenerateDocumentationFile>True</GenerateDocumentationFile> - <AnalysisLevel>latest-all</AnalysisLevel> - <SignAssembly>True</SignAssembly> - <AssemblyOriginatorKeyFile>\\vaughnnugent.com\Internal\Folder Redirection\vman\Documents\Programming\Software\StrongNameingKey.snk</AssemblyOriginatorKeyFile> - </PropertyGroup> - - <ItemGroup> - <None Include="..\.editorconfig" Link=".editorconfig" /> - </ItemGroup> - - <PropertyGroup Condition="'$(Configuration)'=='Debug'"> - <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> - </PropertyGroup> - - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> - <Deterministic>False</Deterministic> - </PropertyGroup> - - <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> - <Deterministic>False</Deterministic> - </PropertyGroup> - - <ItemGroup> - <PackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> - <PackageReference Include="ErrorProne.NET.Structs" Version="0.1.2"> - <PrivateAssets>all</PrivateAssets> - <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> - </PackageReference> - </ItemGroup> -</Project> diff --git a/Utils/src/VnDisposeable.cs b/Utils/src/VnDisposeable.cs deleted file mode 100644 index 4230a13..0000000 --- a/Utils/src/VnDisposeable.cs +++ /dev/null @@ -1,85 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: VnDisposeable.cs -* -* VnDisposeable.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.Runtime.CompilerServices; - -namespace VNLib.Utils -{ - /// <summary> - /// Provides a base class with abstract methods for for disposable objects, with disposed check method - /// </summary> - public abstract class VnDisposeable : IDisposable - { - ///<inheritdoc/> - protected bool Disposed { get; private set; } - - /// <summary> - /// When overriden in a child class, is responsible for freeing resources - /// </summary> - protected abstract void Free(); - - /// <summary> - /// Checks if the current object has been disposed. Method will be inlined where possible - /// </summary> - /// <exception cref="ObjectDisposedException"></exception> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected virtual void Check() - { - if (Disposed) - { - throw new ObjectDisposedException("Object has been disposed"); - } - } - - /// <summary> - /// Sets the internal state to diposed without calling <see cref="Free"/> operation. - /// Usefull if another code-path performs the free operation independant of a dispose opreation. - /// </summary> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected void SetDisposedState() => Disposed = true; - ///<inheritdoc/> - protected virtual void Dispose(bool disposing) - { - if (!Disposed) - { - if (disposing) - { - //Call free method - Free(); - } - Disposed = true; - } - } - //Finalizer is not needed here - - ///<inheritdoc/> - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } -}
\ No newline at end of file diff --git a/Utils/src/VnEncoding.cs b/Utils/src/VnEncoding.cs deleted file mode 100644 index 94d8a1a..0000000 --- a/Utils/src/VnEncoding.cs +++ /dev/null @@ -1,914 +0,0 @@ -/* -* Copyright (c) 2022 Vaughn Nugent -* -* Library: VNLib -* Package: VNLib.Utils -* File: VnEncoding.cs -* -* VnEncoding.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.Text.Json; -using System.Threading; -using System.Buffers.Text; -using System.Threading.Tasks; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; - -using VNLib.Utils.IO; -using VNLib.Utils.Memory; -using VNLib.Utils.Extensions; - - -namespace VNLib.Utils -{ - /// <summary> - /// Contains static methods for encoding data - /// </summary> - public static class VnEncoding - { - /// <summary> - /// Encodes a <see cref="ReadOnlySpan{T}"/> with the specified <see cref="Encoding"/> to a <see cref="VnMemoryStream"/> that must be disposed by the user - /// </summary> - /// <param name="data">Data to be encoded</param> - /// <param name="encoding"><see cref="Encoding"/> to encode data with</param> - /// <returns>A <see cref="Stream"/> contating the encoded data</returns> - public static VnMemoryStream GetMemoryStream(in ReadOnlySpan<char> data, Encoding encoding) - { - _ = encoding ?? throw new ArgumentNullException(nameof(encoding)); - //Create new memory handle to copy data to - MemoryHandle<byte>? handle = null; - try - { - //get number of bytes - int byteCount = encoding.GetByteCount(data); - //resize the handle to fit the data - handle = Memory.Memory.Shared.Alloc<byte>(byteCount); - //encode - int size = encoding.GetBytes(data, handle); - //Consume the handle into a new vnmemstream and return it - return VnMemoryStream.ConsumeHandle(handle, size, true); - } - catch - { - //Dispose the handle if there is an excpetion - handle?.Dispose(); - throw; - } - } - - /// <summary> - /// Attempts to deserialze a json object from a stream of UTF8 data - /// </summary> - /// <typeparam name="T">The type of the object to deserialize</typeparam> - /// <param name="data">Binary data to read from</param> - /// <param name="options"><see cref="JsonSerializerOptions"/> object to pass to deserializer</param> - /// <returns>The object decoded from the stream</returns> - /// <exception cref="JsonException"></exception> - /// <exception cref="NotSupportedException"></exception> - public static T? JSONDeserializeFromBinary<T>(Stream? data, JsonSerializerOptions? options = null) - { - //Return default if null - if (data == null) - { - return default; - } - //Create a memory stream as a buffer - using VnMemoryStream ms = new(); - //Copy stream data to memory - data.CopyTo(ms, null); - if (ms.Length > 0) - { - //Rewind - ms.Position = 0; - //Recover data from stream - return ms.AsSpan().AsJsonObject<T>(options); - } - //Stream is empty - return default; - } - /// <summary> - /// Attempts to deserialze a json object from a stream of UTF8 data - /// </summary> - /// <param name="data">Binary data to read from</param> - /// <param name="type"></param> - /// <param name="options"><see cref="JsonSerializerOptions"/> object to pass to deserializer</param> - /// <returns>The object decoded from the stream</returns> - /// <exception cref="JsonException"></exception> - /// <exception cref="NotSupportedException"></exception> - public static object? JSONDeserializeFromBinary(Stream? data, Type type, JsonSerializerOptions? options = null) - { - //Return default if null - if (data == null) - { - return default; - } - //Create a memory stream as a buffer - using VnMemoryStream ms = new(); - //Copy stream data to memory - data.CopyTo(ms, null); - if (ms.Length > 0) - { - //Rewind - ms.Position = 0; - //Recover data from stream - return JsonSerializer.Deserialize(ms.AsSpan(), type, options); - } - //Stream is empty - return default; - } - /// <summary> - /// Attempts to deserialze a json object from a stream of UTF8 data - /// </summary> - /// <typeparam name="T">The type of the object to deserialize</typeparam> - /// <param name="data">Binary data to read from</param> - /// <param name="options"><see cref="JsonSerializerOptions"/> object to pass to deserializer</param> - /// <param name="cancellationToken"></param> - /// <returns>The object decoded from the stream</returns> - /// <exception cref="JsonException"></exception> - /// <exception cref="NotSupportedException"></exception> - public static ValueTask<T?> JSONDeserializeFromBinaryAsync<T>(Stream? data, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) - { - //Return default if null - return data == null || data.Length == 0 ? ValueTask.FromResult<T?>(default) : JsonSerializer.DeserializeAsync<T>(data, options, cancellationToken); - } - /// <summary> - /// Attempts to deserialze a json object from a stream of UTF8 data - /// </summary> - /// <param name="data">Binary data to read from</param> - /// <param name="type"></param> - /// <param name="options"><see cref="JsonSerializerOptions"/> object to pass to deserializer</param> - /// <param name="cancellationToken"></param> - /// <returns>The object decoded from the stream</returns> - /// <exception cref="JsonException"></exception> - /// <exception cref="NotSupportedException"></exception> - public static ValueTask<object?> JSONDeserializeFromBinaryAsync(Stream? data, Type type, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) - { - //Return default if null - return data == null || data.Length == 0 ? ValueTask.FromResult<object?>(default) : JsonSerializer.DeserializeAsync(data, type, options, cancellationToken); - } - /// <summary> - /// Attempts to serialize the object to json and write the encoded data to the stream - /// </summary> - /// <typeparam name="T">The object type to serialize</typeparam> - /// <param name="data">The object to serialize</param> - /// <param name="output">The <see cref="Stream"/> to write output data to</param> - /// <param name="options"><see cref="JsonSerializerOptions"/> object to pass to serializer</param> - /// <exception cref="JsonException"></exception> - public static void JSONSerializeToBinary<T>(T data, Stream output, JsonSerializerOptions? options = null) - { - //return if null - if(data == null) - { - return; - } - //Serialize - JsonSerializer.Serialize(output, data, options); - } - /// <summary> - /// Attempts to serialize the object to json and write the encoded data to the stream - /// </summary> - /// <param name="data">The object to serialize</param> - /// <param name="output">The <see cref="Stream"/> to write output data to</param> - /// <param name="type"></param> - /// <param name="options"><see cref="JsonSerializerOptions"/> object to pass to serializer</param> - /// <exception cref="JsonException"></exception> - public static void JSONSerializeToBinary(object data, Stream output, Type type, JsonSerializerOptions? options = null) - { - //return if null - if (data == null) - { - return; - } - //Serialize - JsonSerializer.Serialize(output, data, type, options); - } - - #region Base32 - - private const string RFC_4648_BASE32_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; - - /// <summary> - /// Attempts to convert the specified byte sequence in Base32 encoding - /// and writing the encoded data to the output buffer. - /// </summary> - /// <param name="input">The input buffer to convert</param> - /// <param name="output">The ouput buffer to write encoded data to</param> - /// <returns>The number of characters written, false if no data was written or output buffer was too small</returns> - public static ERRNO TryToBase32Chars(ReadOnlySpan<byte> input, Span<char> output) - { - ForwardOnlyWriter<char> writer = new(output); - return TryToBase32Chars(input, ref writer); - } - /// <summary> - /// Attempts to convert the specified byte sequence in Base32 encoding - /// and writing the encoded data to the output buffer. - /// </summary> - /// <param name="input">The input buffer to convert</param> - /// <param name="writer">A <see cref="ForwardOnlyWriter{T}"/> to write encoded chars to</param> - /// <returns>The number of characters written, false if no data was written or output buffer was too small</returns> - public static ERRNO TryToBase32Chars(ReadOnlySpan<byte> input, ref ForwardOnlyWriter<char> writer) - { - //calculate char size - int charCount = (int)Math.Ceiling(input.Length / 5d) * 8; - //Make sure there is enough room - if(charCount > writer.RemainingSize) - { - return false; - } - //sliding window over input buffer - ForwardOnlyReader<byte> reader = new(input); - - while (reader.WindowSize > 0) - { - //Convert the current window - WriteChars(reader.Window, ref writer); - //shift the window - reader.Advance(Math.Min(5, reader.WindowSize)); - } - return writer.Written; - } - private unsafe static void WriteChars(ReadOnlySpan<byte> input, ref ForwardOnlyWriter<char> writer) - { - //Get the input buffer as long - ulong inputAsLong = 0; - //Get a byte pointer over the ulong to index it as a byte buffer - byte* buffer = (byte*)&inputAsLong; - //Check proc endianness - if (BitConverter.IsLittleEndian) - { - //store each byte consecutivley and allow for padding - for (int i = 0; (i < 5 && i < input.Length); i++) - { - //Write bytes from upper to lower byte order for little endian systems - buffer[7 - i] = input[i]; - } - } - else - { - //store each byte consecutivley and allow for padding - for (int i = 0; (i < 5 && i < input.Length); i++) - { - //Write bytes from lower to upper byte order for Big Endian systems - buffer[i] = input[i]; - } - } - int rounds = (input.Length) switch - { - 1 => 2, - 2 => 4, - 3 => 5, - 4 => 7, - _ => 8 - }; - //Convert each byte segment up to the number of bytes encoded - for (int i = 0; i < rounds; i++) - { - //store the leading byte - byte val = buffer[7]; - //right shift the value to lower 5 bits - val >>= 3; - //Lookup charcode - char base32Char = RFC_4648_BASE32_CHARS[val]; - //append the character to the writer - writer.Append(base32Char); - //Shift input left by 5 bits so the next 5 bits can be read - inputAsLong <<= 5; - } - //Fill remaining bytes with padding chars - for(; rounds < 8; rounds++) - { - //Append trailing '=' - writer.Append('='); - } - } - - /// <summary> - /// Attempts to decode the Base32 encoded string - /// </summary> - /// <param name="input">The Base32 encoded data to decode</param> - /// <param name="output">The output buffer to write decoded data to</param> - /// <returns>The number of bytes written to the output</returns> - /// <exception cref="FormatException"></exception> - public static ERRNO TryFromBase32Chars(ReadOnlySpan<char> input, Span<byte> output) - { - ForwardOnlyWriter<byte> writer = new(output); - return TryFromBase32Chars(input, ref writer); - } - /// <summary> - /// Attempts to decode the Base32 encoded string - /// </summary> - /// <param name="input">The Base32 encoded data to decode</param> - /// <param name="writer">A <see cref="ForwardOnlyWriter{T}"/> to write decoded bytes to</param> - /// <returns>The number of bytes written to the output</returns> - /// <exception cref="FormatException"></exception> - public unsafe static ERRNO TryFromBase32Chars(ReadOnlySpan<char> input, ref ForwardOnlyWriter<byte> writer) - { - //TODO support Big-Endian byte order - - //trim padding characters - input = input.Trim('='); - //Calc the number of bytes to write - int outputSize = (input.Length * 5) / 8; - //make sure the output buffer is large enough - if(writer.RemainingSize < outputSize) - { - return false; - } - - //buffer used to shift data while decoding - ulong bufferLong = 0; - - //re-cast to byte* to index it as a byte buffer - byte* buffer = (byte*)&bufferLong; - - int count = 0, len = input.Length; - while(count < len) - { - //Convert the character to its char code - byte charCode = GetCharCode(input[count]); - //write byte to buffer - buffer[0] |= charCode; - count++; - //If 8 characters have been decoded, reset the buffer - if((count % 8) == 0) - { - //Write the 5 upper bytes in reverse order to the output buffer - for(int j = 0; j < 5; j++) - { - writer.Append(buffer[4 - j]); - } - //reset - bufferLong = 0; - } - //left shift the buffer up by 5 bits - bufferLong <<= 5; - } - //If remaining data has not be written, but has been buffed, finalize it - if (writer.Written < outputSize) - { - //calculate how many bits the buffer still needs to be shifted by (will be 5 bits off because of the previous loop) - int remainingShift = (7 - (count % 8)) * 5; - //right shift the buffer by the remaining bit count - bufferLong <<= remainingShift; - //calc remaining bytes - int remaining = (outputSize - writer.Written); - //Write remaining bytes to the output - for(int i = 0; i < remaining; i++) - { - writer.Append(buffer[4 - i]); - } - } - return writer.Written; - } - private static byte GetCharCode(char c) - { - //cast to byte to get its base 10 value - return c switch - { - //Upper case - 'A' => 0, - 'B' => 1, - 'C' => 2, - 'D' => 3, - 'E' => 4, - 'F' => 5, - 'G' => 6, - 'H' => 7, - 'I' => 8, - 'J' => 9, - 'K' => 10, - 'L' => 11, - 'M' => 12, - 'N' => 13, - 'O' => 14, - 'P' => 15, - 'Q' => 16, - 'R' => 17, - 'S' => 18, - 'T' => 19, - 'U' => 20, - 'V' => 21, - 'W' => 22, - 'X' => 23, - 'Y' => 24, - 'Z' => 25, - //Lower case - 'a' => 0, - 'b' => 1, - 'c' => 2, - 'd' => 3, - 'e' => 4, - 'f' => 5, - 'g' => 6, - 'h' => 7, - 'i' => 8, - 'j' => 9, - 'k' => 10, - 'l' => 11, - 'm' => 12, - 'n' => 13, - 'o' => 14, - 'p' => 15, - 'q' => 16, - 'r' => 17, - 's' => 18, - 't' => 19, - 'u' => 20, - 'v' => 21, - 'w' => 22, - 'x' => 23, - 'y' => 24, - 'z' => 25, - //Base10 digits - '2' => 26, - '3' => 27, - '4' => 28, - '5' => 29, - '6' => 30, - '7' => 31, - - _=> throw new FormatException("Character found is not a Base32 encoded character") - }; - } - - /// <summary> - /// Calculates the maximum buffer size required to encode a binary block to its Base32 - /// character encoding - /// </summary> - /// <param name="bufferSize">The binary buffer size used to calculate the base32 buffer size</param> - /// <returns>The maximum size (including padding) of the character buffer required to encode the binary data</returns> - public static int Base32CalcMaxBufferSize(int bufferSize) - { - /* - * Base32 encoding consumes 8 bytes for every 5 bytes - * of input data - */ - //Add up to 8 bytes for padding - return (int)(Math.Ceiling(bufferSize / 5d) * 8) + (8 - (bufferSize % 8)); - } - - /// <summary> - /// Converts the binary buffer to a base32 character string with optional padding characters - /// </summary> - /// <param name="binBuffer">The buffer to encode</param> - /// <param name="withPadding">Should padding be included in the result</param> - /// <returns>The base32 encoded string representation of the specified buffer</returns> - /// <exception cref="InternalBufferTooSmallException"></exception> - public static string ToBase32String(ReadOnlySpan<byte> binBuffer, bool withPadding = false) - { - string value; - //Calculate the base32 entropy to alloc an appropriate buffer (minium buffer of 2 chars) - int entropy = Base32CalcMaxBufferSize(binBuffer.Length); - //Alloc buffer for enough size (2*long bytes) is not an issue - using (UnsafeMemoryHandle<char> charBuffer = Memory.Memory.UnsafeAlloc<char>(entropy)) - { - //Encode - ERRNO encoded = TryToBase32Chars(binBuffer, charBuffer.Span); - if (!encoded) - { - throw new InternalBufferTooSmallException("Base32 char buffer was too small"); - } - //Convert with or w/o padding - if (withPadding) - { - value = charBuffer.Span[0..(int)encoded].ToString(); - } - else - { - value = charBuffer.Span[0..(int)encoded].Trim('=').ToString(); - } - } - return value; - } - /// <summary> - /// Converts the base32 character buffer to its structure representation - /// </summary> - /// <typeparam name="T">The structure type</typeparam> - /// <param name="base32">The base32 character buffer</param> - /// <returns>The new structure of the base32 data</returns> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="InternalBufferTooSmallException"></exception> - public static T FromBase32String<T>(ReadOnlySpan<char> base32) where T: unmanaged - { - //calc size of bin buffer - int size = base32.Length; - //Rent a bin buffer - using UnsafeMemoryHandle<byte> binBuffer = Memory.Memory.UnsafeAlloc<byte>(size); - //Try to decode the data - ERRNO decoded = TryFromBase32Chars(base32, binBuffer.Span); - //Marshal back to a struct - return decoded ? MemoryMarshal.Read<T>(binBuffer.Span[..(int)decoded]) : throw new InternalBufferTooSmallException("Binbuffer was too small"); - } - - /// <summary> - /// Gets a byte array of the base32 decoded data - /// </summary> - /// <param name="base32">The character array to decode</param> - /// <returns>The byte[] of the decoded binary data, or null if the supplied character array was empty</returns> - /// <exception cref="InternalBufferTooSmallException"></exception> - public static byte[]? FromBase32String(ReadOnlySpan<char> base32) - { - if (base32.IsEmpty) - { - return null; - } - //Buffer size of the base32 string will always be enough buffer space - using UnsafeMemoryHandle<byte> tempBuffer = Memory.Memory.UnsafeAlloc<byte>(base32.Length); - //Try to decode the data - ERRNO decoded = TryFromBase32Chars(base32, tempBuffer.Span); - - return decoded ? tempBuffer.Span[0..(int)decoded].ToArray() : throw new InternalBufferTooSmallException("Binbuffer was too small"); - } - - /// <summary> - /// Converts a structure to its base32 representation and returns the string of its value - /// </summary> - /// <typeparam name="T">The structure type</typeparam> - /// <param name="value">The structure to encode</param> - /// <param name="withPadding">A value indicating if padding should be used</param> - /// <returns>The base32 string representation of the structure</returns> - /// <exception cref="ArgumentException"></exception> - /// <exception cref="InternalBufferTooSmallException"></exception> - public static string ToBase32String<T>(T value, bool withPadding = false) where T : unmanaged - { - //get the size of the structure - int binSize = Unsafe.SizeOf<T>(); - //Rent a bin buffer - Span<byte> binBuffer = stackalloc byte[binSize]; - //Write memory to buffer - MemoryMarshal.Write(binBuffer, ref value); - //Convert to base32 - return ToBase32String(binBuffer, withPadding); - } - - #endregion - - #region percent encoding - - private static readonly ReadOnlyMemory<byte> HexToUtf8Pos = new byte[16] - { - 0x30, //0 - 0x31, //1 - 0x32, //2 - 0x33, //3 - 0x34, //4 - 0x35, //5 - 0x36, //6 - 0x37, //7 - 0x38, //8 - 0x39, //9 - - 0x41, //A - 0x42, //B - 0x43, //C - 0x44, //D - 0x45, //E - 0x46 //F - }; - - /// <summary> - /// Deterimes the size of the buffer needed to encode a utf8 encoded - /// character buffer into its url-safe percent/hex encoded representation - /// </summary> - /// <param name="utf8Bytes">The buffer to examine</param> - /// <param name="allowedChars">A sequence of characters that are excluded from encoding</param> - /// <returns>The size of the buffer required to encode</returns> - public static unsafe int PercentEncodeCalcBufferSize(ReadOnlySpan<byte> utf8Bytes, in ReadOnlySpan<byte> allowedChars = default) - { - /* - * For every illegal character, the percent encoding adds 3 bytes of - * entropy. So a single byte will be replaced by 3, so adding - * 2 bytes for every illegal character plus the length of the - * intial buffer, we get the size of the buffer needed to - * percent encode. - */ - int count = 0, len = utf8Bytes.Length; - fixed (byte* utfBase = &MemoryMarshal.GetReference(utf8Bytes)) - { - //Find all unsafe characters and add the entropy size - for (int i = 0; i < len; i++) - { - if (!IsUrlSafeChar(utfBase[i], in allowedChars)) - { - count += 2; - } - } - } - //Size is initial buffer size + count bytes - return len + count; - } - - /// <summary> - /// Percent encodes the buffer for utf8 encoded characters to its percent/hex encoded - /// utf8 character representation - /// </summary> - /// <param name="utf8Bytes">The buffer of utf8 encoded characters to encode</param> - /// <param name="utf8Output">The buffer to write the encoded characters to</param> - /// <param name="allowedChars">A sequence of characters that are excluded from encoding</param> - /// <returns>The number of characters encoded and written to the output buffer</returns> - public static ERRNO PercentEncode(ReadOnlySpan<byte> utf8Bytes, Span<byte> utf8Output, in ReadOnlySpan<byte> allowedChars = default) - { - int outPos = 0, len = utf8Bytes.Length; - ReadOnlySpan<byte> lookupTable = HexToUtf8Pos.Span; - for (int i = 0; i < len; i++) - { - byte value = utf8Bytes[i]; - //Check if value is url safe - if(IsUrlSafeChar(value, in allowedChars)) - { - //Skip - utf8Output[outPos++] = value; - } - else - { - //Percent encode - utf8Output[outPos++] = 0x25; // '%' - //Calc and store the encoded by the upper 4 bits - utf8Output[outPos++] = lookupTable[(value & 0xf0) >> 4]; - //Store lower 4 bits in encoded value - utf8Output[outPos++] = lookupTable[value & 0x0f]; - } - } - //Return the size of the output buffer - return outPos; - } - - private static bool IsUrlSafeChar(byte value, in ReadOnlySpan<byte> allowedChars) - { - return - // base10 digits - value > 0x2f && value < 0x3a - // '_' (underscore) - || value == 0x5f - // '-' (hyphen) - || value == 0x2d - // Uppercase letters - || value > 0x40 && value < 0x5b - // lowercase letters - || value > 0x60 && value < 0x7b - // Check allowed characters - || allowedChars.Contains(value); - } - - //TODO: Implement decode with better performance, lookup table or math vs searching the table - - /// <summary> - /// Decodes a percent (url/hex) encoded utf8 encoded character buffer to its utf8 - /// encoded binary value - /// </summary> - /// <param name="utf8Encoded">The buffer containg characters to be decoded</param> - /// <param name="utf8Output">The buffer to write deocded values to</param> - /// <returns>The nuber of bytes written to the output buffer</returns> - /// <exception cref="FormatException"></exception> - public static ERRNO PercentDecode(ReadOnlySpan<byte> utf8Encoded, Span<byte> utf8Output) - { - int outPos = 0, len = utf8Encoded.Length; - ReadOnlySpan<byte> lookupTable = HexToUtf8Pos.Span; - for (int i = 0; i < len; i++) - { - byte value = utf8Encoded[i]; - //Begining of percent encoding character - if(value == 0x25) - { - //Calculate the base16 multiplier from the upper half of the - int multiplier = lookupTable.IndexOf(utf8Encoded[i + 1]); - //get the base16 lower half to add - int lower = lookupTable.IndexOf(utf8Encoded[i + 2]); - //Check format - if(multiplier < 0 || lower < 0) - { - throw new FormatException($"Encoded buffer contains invalid hexadecimal characters following the % character at position {i}"); - } - //Calculate the new value, shift multiplier to the upper 4 bits, then mask + or the lower 4 bits - value = (byte)(((byte)(multiplier << 4)) | ((byte)lower & 0x0f)); - //Advance the encoded index by the two consumed chars - i += 2; - } - utf8Output[outPos++] = value; - } - return outPos; - } - - #endregion - - #region Base64 - - /// <summary> - /// Tries to convert the specified span containing a string representation that is - /// encoded with base-64 digits into a span of 8-bit unsigned integers. - /// </summary> - /// <param name="base64">Base64 character data to recover</param> - /// <param name="buffer">The binary output buffer to write converted characters to</param> - /// <returns>The number of bytes written, or <see cref="ERRNO.E_FAIL"/> of the conversion was unsucessful</returns> - public static ERRNO TryFromBase64Chars(ReadOnlySpan<char> base64, Span<byte> buffer) - { - return Convert.TryFromBase64Chars(base64, buffer, out int bytesWritten) ? bytesWritten : ERRNO.E_FAIL; - } - /// <summary> - /// Tries to convert the 8-bit unsigned integers inside the specified read-only span - /// into their equivalent string representation that is encoded with base-64 digits. - /// You can optionally specify whether to insert line breaks in the return value. - /// </summary> - /// <param name="buffer">The binary buffer to convert characters from</param> - /// <param name="base64">The base64 output buffer</param> - /// <param name="options"> - /// One of the enumeration values that specify whether to insert line breaks in the - /// return value. The default value is System.Base64FormattingOptions.None. - /// </param> - /// <returns>The number of characters encoded, or <see cref="ERRNO.E_FAIL"/> if conversion was unsuccessful</returns> - public static ERRNO TryToBase64Chars(ReadOnlySpan<byte> buffer, Span<char> base64, Base64FormattingOptions options = Base64FormattingOptions.None) - { - return Convert.TryToBase64Chars(buffer, base64, out int charsWritten, options: options) ? charsWritten : ERRNO.E_FAIL; - } - - - /* - * Calc base64 padding chars excluding the length mod 4 = 0 case - * by and-ing 0x03 (011) with the result - */ - - /// <summary> - /// Determines the number of missing padding bytes from the length of the base64 - /// data sequence. - /// <code> - /// Formula (4 - (length mod 4) and 0x03 - /// </code> - /// </summary> - /// <param name="length">The length of the base64 buffer</param> - /// <returns>The number of padding bytes to add to the end of the sequence</returns> - public static int Base64CalcRequiredPadding(int length) => (4 - (length % 4)) & 0x03; - - /// <summary> - /// Converts a base64 utf8 encoded binary buffer to - /// its base64url encoded version - /// </summary> - /// <param name="base64">The binary buffer to convert</param> - public static unsafe void Base64ToUrlSafeInPlace(Span<byte> base64) - { - int len = base64.Length; - - fixed(byte* ptr = &MemoryMarshal.GetReference(base64)) - { - for (int i = 0; i < len; i++) - { - switch (ptr[i]) - { - //Replace + with - (minus) - case 0x2b: - ptr[i] = 0x2d; - break; - //Replace / with _ (underscore) - case 0x2f: - ptr[i] = 0x5f; - break; - } - } - } - } - /// <summary> - /// Converts a base64url encoded utf8 encoded binary buffer to - /// its base64 encoded version - /// </summary> - /// <param name="uft8Base64Url">The base64url utf8 to decode</param> - public static unsafe void Base64FromUrlSafeInPlace(Span<byte> uft8Base64Url) - { - int len = uft8Base64Url.Length; - - fixed (byte* ptr = &MemoryMarshal.GetReference(uft8Base64Url)) - { - for (int i = 0; i < len; i++) - { - switch (ptr[i]) - { - //Replace - with + (plus) - case 0x2d: - ptr[i] = 0x2b; - break; - //Replace _ with / (slash) - case 0x5f: - ptr[i] = 0x2f; - break; - } - } - } - } - - /// <summary> - /// Converts the input buffer to a url safe base64 encoded - /// utf8 buffer from the base64 input buffer. The base64 is copied - /// directly to the output then converted in place. This is - /// just a shortcut method for readonly spans - /// </summary> - /// <param name="base64">The base64 encoded data</param> - /// <param name="base64Url">The base64url encoded output</param> - /// <returns>The size of the <paramref name="base64"/> buffer</returns> - public static ERRNO Base64ToUrlSafe(ReadOnlySpan<byte> base64, Span<byte> base64Url) - { - //Aligned copy to the output buffer - base64.CopyTo(base64Url); - //One time convert the output buffer to url safe - Base64ToUrlSafeInPlace(base64Url); - return base64.Length; - } - - /// <summary> - /// Converts the urlsafe input buffer to a base64 encoded - /// utf8 buffer from the base64 input buffer. The base64 is copied - /// directly to the output then converted in place. This is - /// just a shortcut method for readonly spans - /// </summary> - /// <param name="base64">The base64 encoded data</param> - /// <param name="base64Url">The base64url encoded output</param> - /// <returns>The size of the <paramref name="base64Url"/> buffer</returns> - public static ERRNO Base64FromUrlSafe(ReadOnlySpan<byte> base64Url, Span<byte> base64) - { - //Aligned copy to the output buffer - base64Url.CopyTo(base64); - //One time convert the output buffer to url safe - Base64FromUrlSafeInPlace(base64); - return base64Url.Length; - } - - /// <summary> - /// Decodes a utf8 base64url encoded sequence of data and writes it - /// to the supplied output buffer - /// </summary> - /// <param name="utf8Base64Url">The utf8 base64 url encoded string</param> - /// <param name="output">The output buffer to write the decoded data to</param> - /// <returns>The number of bytes written or <see cref="ERRNO.E_FAIL"/> if the operation failed</returns> - public static ERRNO Base64UrlDecode(ReadOnlySpan<byte> utf8Base64Url, Span<byte> output) - { - if(utf8Base64Url.IsEmpty || output.IsEmpty) - { - return ERRNO.E_FAIL; - } - //url deocde - ERRNO count = Base64FromUrlSafe(utf8Base64Url, output); - - //Writer for adding padding bytes - ForwardOnlyWriter<byte> writer = new (output); - writer.Advance(count); - - //Calc required padding - int paddingToAdd = Base64CalcRequiredPadding(writer.Written); - //Add padding bytes - for (; paddingToAdd > 0; paddingToAdd--) - { - writer.Append(0x3d); // '=' - } - - //Base64 decode in place, we should have a buffer large enough - OperationStatus status = Base64.DecodeFromUtf8InPlace(writer.AsSpan(), out int bytesWritten); - //If status is successful return the number of bytes written - return status == OperationStatus.Done ? bytesWritten : ERRNO.E_FAIL; - } - - /// <summary> - /// Decodes a base64url encoded character sequence - /// of data and writes it to the supplied output buffer - /// </summary> - /// <param name="chars">The character buffer to decode</param> - /// <param name="output">The output buffer to write decoded data to</param> - /// <param name="encoding">The character encoding</param> - /// <returns>The number of bytes written or <see cref="ERRNO.E_FAIL"/> if the operation failed</returns> - /// <exception cref="InternalBufferTooSmallException"></exception> - public static ERRNO Base64UrlDecode(ReadOnlySpan<char> chars, Span<byte> output, Encoding? encoding = null) - { - if (chars.IsEmpty || output.IsEmpty) - { - return ERRNO.E_FAIL; - } - //Set the encoding to utf8 - encoding ??= Encoding.UTF8; - //get the number of bytes to alloc a buffer - int decodedSize = encoding.GetByteCount(chars); - - //alloc buffer - using UnsafeMemoryHandle<byte> decodeHandle = Memory.Memory.UnsafeAlloc<byte>(decodedSize); - //Get the utf8 binary data - int count = encoding.GetBytes(chars, decodeHandle); - return Base64UrlDecode(decodeHandle.Span[..count], output); - } - - #endregion - } -}
\ No newline at end of file |