aboutsummaryrefslogtreecommitdiff
path: root/Utils/src
diff options
context:
space:
mode:
Diffstat (limited to 'Utils/src')
-rw-r--r--Utils/src/Async/AccessSerializer.cs297
-rw-r--r--Utils/src/Async/AsyncExclusiveResource.cs169
-rw-r--r--Utils/src/Async/AsyncQueue.cs144
-rw-r--r--Utils/src/Async/AsyncUpdatableResource.cs111
-rw-r--r--Utils/src/Async/Exceptions/AsyncUpdateException.cs52
-rw-r--r--Utils/src/Async/IAsyncExclusiveResource.cs40
-rw-r--r--Utils/src/Async/IAsyncWaitHandle.cs41
-rw-r--r--Utils/src/Async/IWaitHandle.cs59
-rw-r--r--Utils/src/BitField.cs115
-rw-r--r--Utils/src/ERRNO.cs152
-rw-r--r--Utils/src/Extensions/CacheExtensions.cs348
-rw-r--r--Utils/src/Extensions/CollectionExtensions.cs98
-rw-r--r--Utils/src/Extensions/IoExtensions.cs345
-rw-r--r--Utils/src/Extensions/JsonExtensions.cs215
-rw-r--r--Utils/src/Extensions/MemoryExtensions.cs769
-rw-r--r--Utils/src/Extensions/MutexReleaser.cs62
-rw-r--r--Utils/src/Extensions/SafeLibraryExtensions.cs103
-rw-r--r--Utils/src/Extensions/SemSlimReleaser.cs51
-rw-r--r--Utils/src/Extensions/StringExtensions.cs481
-rw-r--r--Utils/src/Extensions/ThreadingExtensions.cs226
-rw-r--r--Utils/src/Extensions/TimerExtensions.cs66
-rw-r--r--Utils/src/Extensions/VnStringExtensions.cs418
-rw-r--r--Utils/src/IIndexable.cs43
-rw-r--r--Utils/src/IO/ArrayPoolStreamBuffer.cs70
-rw-r--r--Utils/src/IO/BackingStream.cs181
-rw-r--r--Utils/src/IO/FileOperations.cs105
-rw-r--r--Utils/src/IO/IDataAccumulator.cs64
-rw-r--r--Utils/src/IO/ISlindingWindowBuffer.cs91
-rw-r--r--Utils/src/IO/IVnTextReader.cs72
-rw-r--r--Utils/src/IO/InMemoryTemplate.cs196
-rw-r--r--Utils/src/IO/IsolatedStorageDirectory.cs154
-rw-r--r--Utils/src/IO/SlidingWindowBufferExtensions.cs213
-rw-r--r--Utils/src/IO/TemporayIsolatedFile.cs57
-rw-r--r--Utils/src/IO/VnMemoryStream.cs469
-rw-r--r--Utils/src/IO/VnStreamReader.cs180
-rw-r--r--Utils/src/IO/VnStreamWriter.cs292
-rw-r--r--Utils/src/IO/VnTextReaderExtensions.cs223
-rw-r--r--Utils/src/IO/WriteOnlyBufferedStream.cs255
-rw-r--r--Utils/src/IObjectStorage.cs48
-rw-r--r--Utils/src/Logging/ILogProvider.cs79
-rw-r--r--Utils/src/Logging/LogLevel.cs33
-rw-r--r--Utils/src/Logging/LoggerExtensions.cs60
-rw-r--r--Utils/src/Memory/Caching/ICacheHolder.cs45
-rw-r--r--Utils/src/Memory/Caching/ICacheable.cs44
-rw-r--r--Utils/src/Memory/Caching/IObjectRental.cs47
-rw-r--r--Utils/src/Memory/Caching/IReusable.cs42
-rw-r--r--Utils/src/Memory/Caching/LRUCache.cs127
-rw-r--r--Utils/src/Memory/Caching/LRUDataStore.cs232
-rw-r--r--Utils/src/Memory/Caching/ObjectRental.cs236
-rw-r--r--Utils/src/Memory/Caching/ObjectRentalBase.cs155
-rw-r--r--Utils/src/Memory/Caching/ReusableStore.cs61
-rw-r--r--Utils/src/Memory/Caching/ThreadLocalObjectStorage.cs76
-rw-r--r--Utils/src/Memory/Caching/ThreadLocalReusableStore.cs64
-rw-r--r--Utils/src/Memory/ForwardOnlyBufferWriter.cs122
-rw-r--r--Utils/src/Memory/ForwardOnlyMemoryReader.cs74
-rw-r--r--Utils/src/Memory/ForwardOnlyMemoryWriter.cs122
-rw-r--r--Utils/src/Memory/ForwardOnlyReader.cs74
-rw-r--r--Utils/src/Memory/IMemoryHandle.cs53
-rw-r--r--Utils/src/Memory/IStringSerializeable.cs55
-rw-r--r--Utils/src/Memory/IUnmangedHeap.cs59
-rw-r--r--Utils/src/Memory/Memory.cs456
-rw-r--r--Utils/src/Memory/MemoryHandle.cs237
-rw-r--r--Utils/src/Memory/PrivateBuffersMemoryPool.cs67
-rw-r--r--Utils/src/Memory/PrivateHeap.cs184
-rw-r--r--Utils/src/Memory/PrivateString.cs185
-rw-r--r--Utils/src/Memory/PrivateStringManager.cs117
-rw-r--r--Utils/src/Memory/ProcessHeap.cs82
-rw-r--r--Utils/src/Memory/RpMallocPrivateHeap.cs279
-rw-r--r--Utils/src/Memory/SubSequence.cs113
-rw-r--r--Utils/src/Memory/SysBufferMemoryManager.cs102
-rw-r--r--Utils/src/Memory/UnmanagedHeapBase.cs185
-rw-r--r--Utils/src/Memory/UnsafeMemoryHandle.cs231
-rw-r--r--Utils/src/Memory/VnString.cs497
-rw-r--r--Utils/src/Memory/VnTable.cs213
-rw-r--r--Utils/src/Memory/VnTempBuffer.cs207
-rw-r--r--Utils/src/Native/SafeLibraryHandle.cs220
-rw-r--r--Utils/src/Native/SafeMethodHandle.cs61
-rw-r--r--Utils/src/NativeLibraryException.cs89
-rw-r--r--Utils/src/Resources/BackedResourceBase.cs79
-rw-r--r--Utils/src/Resources/CallbackOpenHandle.cs44
-rw-r--r--Utils/src/Resources/ExclusiveResourceHandle.cs81
-rw-r--r--Utils/src/Resources/IExclusiveResource.cs39
-rw-r--r--Utils/src/Resources/IResource.cs38
-rw-r--r--Utils/src/Resources/OpenHandle.cs38
-rw-r--r--Utils/src/Resources/OpenResourceHandle.cs44
-rw-r--r--Utils/src/Resources/ResourceDeleteFailedException.cs40
-rw-r--r--Utils/src/Resources/ResourceUpdateFailedException.cs40
-rw-r--r--Utils/src/Resources/UpdatableResource.cs113
-rw-r--r--Utils/src/VNLib.Utils.csproj47
-rw-r--r--Utils/src/VnDisposeable.cs85
-rw-r--r--Utils/src/VnEncoding.cs914
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