aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Utils/src')
-rw-r--r--lib/Utils/src/Async/IAsyncEventSink.cs48
-rw-r--r--lib/Utils/src/Extensions/MemoryExtensions.cs140
-rw-r--r--lib/Utils/src/IO/VnMemoryStream.cs87
-rw-r--r--lib/Utils/src/Memory/ArrayPoolBuffer.cs (renamed from lib/Utils/src/Memory/VnTempBuffer.cs)89
-rw-r--r--lib/Utils/src/Memory/Caching/IReusable.cs10
-rw-r--r--lib/Utils/src/Memory/HeapCreation.cs6
-rw-r--r--lib/Utils/src/Memory/IMemoryHandle.cs8
-rw-r--r--lib/Utils/src/Memory/IResizeableMemoryHandle.cs52
-rw-r--r--lib/Utils/src/Memory/MemoryHandle.cs61
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.cs300
-rw-r--r--lib/Utils/src/Memory/MemoryUtilAlloc.cs8
-rw-r--r--lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs6
-rw-r--r--lib/Utils/src/Memory/ProcessHeap.cs2
-rw-r--r--lib/Utils/src/Memory/SubSequence.cs6
-rw-r--r--lib/Utils/src/Memory/SysBufferMemoryManager.cs46
-rw-r--r--lib/Utils/src/Memory/UnmanagedHeapBase.cs29
-rw-r--r--lib/Utils/src/Memory/UnsafeMemoryHandle.cs24
-rw-r--r--lib/Utils/src/Memory/VnString.cs14
-rw-r--r--lib/Utils/src/Memory/Win32PrivateHeap.cs3
-rw-r--r--lib/Utils/src/Resources/ManagedLibrary.cs138
20 files changed, 726 insertions, 351 deletions
diff --git a/lib/Utils/src/Async/IAsyncEventSink.cs b/lib/Utils/src/Async/IAsyncEventSink.cs
new file mode 100644
index 0000000..634b3e6
--- /dev/null
+++ b/lib/Utils/src/Async/IAsyncEventSink.cs
@@ -0,0 +1,48 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Utils
+* File: IAsyncEventSink.cs
+*
+* IAsyncEventSink.cs is part of VNLib.Utils which is part of the larger
+* VNLib collection of libraries and utilities.
+*
+* VNLib.Utils is free software: you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published
+* by the Free Software Foundation, either version 2 of the License,
+* or (at your option) any later version.
+*
+* VNLib.Utils is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with VNLib.Utils. If not, see http://www.gnu.org/licenses/.
+*/
+
+namespace VNLib.Utils.Async
+{
+ /// <summary>
+ /// A type that receives events from asynchronous event sources and publishes
+ /// them to subscribers.
+ /// </summary>
+ /// <typeparam name="T">The event type</typeparam>
+ public interface IAsyncEventSink<T>
+ {
+ /// <summary>
+ /// Publishes a single event to all subscribers
+ /// </summary>
+ /// <param name="evnt">The event to publish</param>
+ /// <returns>A value that indicates if the event was successfully published to subscribers</returns>
+ bool PublishEvent(T evnt);
+
+ /// <summary>
+ /// Publishes an array of events to all subscribers
+ /// </summary>
+ /// <param name="events">The array of events to publish</param>
+ /// <returns>A value that indicates if the events were successfully published to subscribers</returns>
+ bool PublishEvents(T[] events);
+ }
+}
diff --git a/lib/Utils/src/Extensions/MemoryExtensions.cs b/lib/Utils/src/Extensions/MemoryExtensions.cs
index 6525db4..d21ceee 100644
--- a/lib/Utils/src/Extensions/MemoryExtensions.cs
+++ b/lib/Utils/src/Extensions/MemoryExtensions.cs
@@ -48,11 +48,7 @@ namespace VNLib.Utils.Extensions
/// <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);
- }
+ public static UnsafeMemoryHandle<T> Lease<T>(this ArrayPool<T> pool, int size, bool zero = false) where T : unmanaged => new(pool, size, zero);
/// <summary>
/// Retreives a buffer that is at least the reqested length, and clears the array from 0-size.
@@ -81,32 +77,10 @@ namespace VNLib.Utils.Extensions
/// </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);
- }
+ public static string ToString<T>(this T charBuffer) where T : IMemoryHandle<char> => charBuffer.Span.ToString();
/// <summary>
- /// Wraps the <see cref="VnTempBuffer{T}"/> instance in System.Buffers.MemoryManager
+ /// Wraps the <see cref="IMemoryHandle{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>
@@ -117,15 +91,12 @@ namespace VNLib.Utils.Extensions
/// </param>
/// <returns>The <see cref="MemoryManager{T}"/> wrapper</returns>
/// <remarks>NOTE: This wrapper now manages the lifetime of the current handle</remarks>
+ /// <exception cref="ArgumentNullException"></exception>
[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);
- }
+ public static MemoryManager<T> ToMemoryManager<T>(this IMemoryHandle<T> handle, bool ownsHandle) => new SysBufferMemoryManager<T>(handle, ownsHandle);
/// <summary>
- /// Allows direct allocation of a fixed size <see cref="MemoryManager{T}"/> from a <see cref="Win32PrivateHeap"/> instance
+ /// Allows direct allocation of a fixed size <see cref="MemoryManager{T}"/> from a <see cref="IUnmangedHeap"/> instance
/// of the specified number of elements
/// </summary>
/// <typeparam name="T">The unmanaged data type</typeparam>
@@ -133,10 +104,14 @@ namespace VNLib.Utils.Extensions
/// <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="ArgumentException"></exception>
+ /// <exception cref="OutOfMemoryException"></exception>
+ /// <exception cref="ObjectDisposedException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static MemoryManager<T> DirectAlloc<T>(this IUnmangedHeap heap, nuint size, bool zero = false) where T : unmanaged
{
- return new SysBufferMemoryManager<T>(heap, size, zero);
+ MemoryHandle<T> handle = heap.Alloc<T>(size, zero);
+ return new SysBufferMemoryManager<T>(handle, true);
}
/// <summary>
@@ -163,10 +138,10 @@ namespace VNLib.Utils.Extensions
/// </returns>
//Method only exists for consistancy since unsafe handles are always 32bit
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static int GetIntLength<T>(this in UnsafeMemoryHandle<T> handle) where T: unmanaged => handle.IntLength;
+ public static int GetIntLength<T>(this in UnsafeMemoryHandle<T> handle) where T : unmanaged => handle.IntLength;
/// <summary>
- /// Allows direct allocation of a fixed size <see cref="MemoryManager{T}"/> from a <see cref="Win32PrivateHeap"/> instance
+ /// Allows direct allocation of a fixed size <see cref="MemoryManager{T}"/> from a <see cref="IUnmangedHeap"/> instance
/// of the specified number of elements
/// </summary>
/// <typeparam name="T">The unmanaged data type</typeparam>
@@ -181,6 +156,7 @@ namespace VNLib.Utils.Extensions
{
return size >= 0 ? DirectAlloc<T>(heap, (nuint)size, zero) : throw new ArgumentOutOfRangeException(nameof(size), "The size paramter must be a positive integer");
}
+
/// <summary>
/// Gets an offset pointer from the base postion to the number of bytes specified. Performs bounds checks
/// </summary>
@@ -194,6 +170,7 @@ namespace VNLib.Utils.Extensions
{
return elements >= 0 ? memory.GetOffset((nuint)elements) : throw new ArgumentOutOfRangeException(nameof(elements), "The elements paramter must be a positive integer");
}
+
/// <summary>
/// Resizes the current handle on the heap
/// </summary>
@@ -204,7 +181,7 @@ namespace VNLib.Utils.Extensions
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Resize<T>(this MemoryHandle<T> memory, nint elements) where T : unmanaged
+ public static void Resize<T>(this IResizeableMemoryHandle<T> memory, nint elements)
{
if (elements < 0)
{
@@ -224,7 +201,7 @@ namespace VNLib.Utils.Extensions
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ResizeIfSmaller<T>(this MemoryHandle<T> handle, nint count) where T : unmanaged
+ public static void ResizeIfSmaller<T>(this IResizeableMemoryHandle<T> handle, nint count)
{
if(count < 0)
{
@@ -244,7 +221,7 @@ namespace VNLib.Utils.Extensions
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ResizeIfSmaller<T>(this MemoryHandle<T> handle, nuint count) where T : unmanaged
+ public static void ResizeIfSmaller<T>(this IResizeableMemoryHandle<T> handle, nuint count)
{
//Check handle size
if(handle.Length < count)
@@ -254,6 +231,52 @@ namespace VNLib.Utils.Extensions
}
}
+ /// <summary>
+ /// Gets a reference to the element at the specified offset from the base
+ /// address of the <see cref="MemoryHandle{T}"/>
+ /// </summary>
+ /// <param name="block"></param>
+ /// <param name="offset">The element offset from the base address to add to the returned reference</param>
+ /// <returns>The reference to the item at the desired offset</returns>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ref T GetOffsetRef<T>(this IMemoryHandle<T> block, nuint offset)
+ {
+ _ = block ?? throw new ArgumentNullException(nameof(block));
+
+ if (offset >= block.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ }
+
+ return ref Unsafe.Add(ref block.GetReference(), offset);
+ }
+
+ /// <summary>
+ /// Gets a reference to the element at the specified offset from the base
+ /// address of the <see cref="MemoryHandle{T}"/> and casts it to a byte reference
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="block"></param>
+ /// <param name="offset">The number of elements to offset the base reference by</param>
+ /// <returns>The reinterpreted byte reference at the first byte of the element offset</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ref byte GetByteOffsetRef<T>(this IMemoryHandle<T> block, nuint offset)
+ {
+ _ = block ?? throw new ArgumentNullException(nameof(block));
+
+ if (offset >= block.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset));
+ }
+
+ //Get the base reference, then offset by the desired number of elements and cast to a byte reference
+ ref T baseRef = ref block.GetReference();
+ ref T offsetRef = ref Unsafe.Add(ref baseRef, offset);
+ return ref Unsafe.As<T, byte>(ref offsetRef);
+ }
/// <summary>
/// Gets a 64bit friendly span offset for the current <see cref="MemoryHandle{T}"/>
@@ -265,7 +288,7 @@ namespace VNLib.Utils.Extensions
/// <returns>The offset span</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe Span<T> GetOffsetSpan<T>(this MemoryHandle<T> block, nuint offset, int size) where T: unmanaged
+ public static Span<T> GetOffsetSpan<T>(this IMemoryHandle<T> block, nuint offset, int size)
{
_ = block ?? throw new ArgumentNullException(nameof(block));
@@ -282,9 +305,10 @@ namespace VNLib.Utils.Extensions
MemoryUtil.CheckBounds(block, offset, (nuint)size);
//Get long offset from the destination handle
- void* ofPtr = block.GetOffset(offset);
- return new Span<T>(ofPtr, size);
+ ref T ofPtr = ref GetOffsetRef(block, offset);
+ return MemoryMarshal.CreateSpan(ref ofPtr, size);
}
+
/// <summary>
/// Gets a 64bit friendly span offset for the current <see cref="MemoryHandle{T}"/>
/// </summary>
@@ -295,7 +319,7 @@ namespace VNLib.Utils.Extensions
/// <returns>The offset span</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe Span<T> GetOffsetSpan<T>(this MemoryHandle<T> block, nint offset, int size) where T : unmanaged
+ public static unsafe Span<T> GetOffsetSpan<T>(this IMemoryHandle<T> block, nint offset, int size)
{
return offset >= 0 ? block.GetOffsetSpan((nuint)offset, size) : throw new ArgumentOutOfRangeException(nameof(offset));
}
@@ -310,7 +334,7 @@ namespace VNLib.Utils.Extensions
/// <returns>The new <see cref="SubSequence{T}"/> within the block</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static SubSequence<T> GetSubSequence<T>(this MemoryHandle<T> block, nuint offset, int size) where T : unmanaged => new (block, offset, size);
+ public static SubSequence<T> GetSubSequence<T>(this IMemoryHandle<T> block, nuint offset, int size) => new (block, offset, size);
/// <summary>
/// Gets a <see cref="SubSequence{T}"/> window within the current block
@@ -322,7 +346,7 @@ namespace VNLib.Utils.Extensions
/// <returns>The new <see cref="SubSequence{T}"/> within the block</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static SubSequence<T> GetSubSequence<T>(this MemoryHandle<T> block, nint offset, int size) where T : unmanaged
+ public static SubSequence<T> GetSubSequence<T>(this IMemoryHandle<T> block, nint offset, int size)
{
return offset >= 0 ? new (block, (nuint)offset, size) : throw new ArgumentOutOfRangeException(nameof(offset));
}
@@ -334,10 +358,7 @@ namespace VNLib.Utils.Extensions
/// <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, int maxBufferSize = int.MaxValue) where T : unmanaged
- {
- return new PrivateBuffersMemoryPool<T>(heap, maxBufferSize);
- }
+ public static MemoryPool<T> ToPool<T>(this IUnmangedHeap heap, int maxBufferSize = int.MaxValue) where T : unmanaged => new PrivateBuffersMemoryPool<T>(heap, maxBufferSize);
/// <summary>
/// Allocates a structure of the specified type on the current unmanged heap and zero's its memory
@@ -349,14 +370,8 @@ namespace VNLib.Utils.Extensions
/// <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, (nuint)sizeof(T), true);
- //returns the handle
- return (T*)handle;
- }
-
+ public static unsafe T* StructAlloc<T>(this IUnmangedHeap heap) where T : unmanaged => (T*)heap.Alloc(1, (nuint)sizeof(T), true);
+
/// <summary>
/// Frees a structure at the specified address from the this heap.
/// This must be the same heap the structure was allocated from
@@ -468,7 +483,7 @@ namespace VNLib.Utils.Extensions
/// <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
+ public static void WriteAndResize<T>(this IResizeableMemoryHandle<T> handle, ReadOnlySpan<T> input) where T: unmanaged
{
handle.Resize(input.Length);
MemoryUtil.Copy(input, handle, 0);
@@ -618,6 +633,7 @@ namespace VNLib.Utils.Extensions
{
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
@@ -639,6 +655,7 @@ namespace VNLib.Utils.Extensions
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
@@ -657,6 +674,7 @@ namespace VNLib.Utils.Extensions
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
@@ -683,6 +701,7 @@ namespace VNLib.Utils.Extensions
/// <returns>A <see cref="PrivateString"/> instance that owns the underlying string memory</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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>
@@ -711,6 +730,7 @@ namespace VNLib.Utils.Extensions
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
diff --git a/lib/Utils/src/IO/VnMemoryStream.cs b/lib/Utils/src/IO/VnMemoryStream.cs
index 97cef03..7ac56a6 100644
--- a/lib/Utils/src/IO/VnMemoryStream.cs
+++ b/lib/Utils/src/IO/VnMemoryStream.cs
@@ -24,7 +24,9 @@
using System;
using System.IO;
+using System.Buffers;
using System.Threading;
+using System.Diagnostics;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
@@ -33,6 +35,7 @@ using VNLib.Utils.Extensions;
namespace VNLib.Utils.IO
{
+
/// <summary>
/// Provides an unmanaged memory stream. Desigend to help reduce garbage collector load for
/// high frequency memory operations. Similar to <see cref="UnmanagedMemoryStream"/>
@@ -44,35 +47,39 @@ namespace VNLib.Utils.IO
private bool _isReadonly;
//Memory
- private readonly MemoryHandle<byte> _buffer;
+ private readonly IResizeableMemoryHandle<byte> _buffer;
//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="handle"><see cref="IResizeableMemoryHandle{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, nint length, bool readOnly) => FromHandle(handle, true, length, readOnly);
+ public static VnMemoryStream ConsumeHandle(IResizeableMemoryHandle<byte> handle, nint length, bool readOnly) => FromHandle(handle, true, length, readOnly);
/// <summary>
/// Creates a new <see cref="VnMemoryStream"/> from the supplied memory handle
/// of the initial length. This function also accepts a value that indicates if this stream
/// owns the memory handle, which will cause it to be disposed when the stream is disposed.
/// </summary>
- /// <param name="handle"><see cref="MemoryHandle{T}"/> to consume</param>
+ /// <param name="handle"><see cref="IResizeableMemoryHandle{T}"/> to consume</param>
/// <param name="length">The initial length of the stream</param>
/// <param name="readOnly">Should the stream be readonly?</param>
/// <param name="ownsHandle">A value that indicates if the current stream owns the memory handle</param>
/// <exception cref="ArgumentException"></exception>
/// <returns>A <see cref="VnMemoryStream"/> wrapper to access the handle data</returns>
- public static VnMemoryStream FromHandle(MemoryHandle<byte> handle, bool ownsHandle, nint length, bool readOnly)
+ public static VnMemoryStream FromHandle(IResizeableMemoryHandle<byte> handle, bool ownsHandle, nint length, bool readOnly)
{
- handle.ThrowIfClosed();
- return new VnMemoryStream(handle, length, readOnly, ownsHandle);
+ //Check the handle
+ _ = handle ?? throw new ArgumentNullException(nameof(handle));
+
+ return handle.CanRealloc || readOnly
+ ? new VnMemoryStream(handle, length, readOnly, ownsHandle)
+ : throw new ArgumentException("The supplied memory handle must be resizable on a writable stream", nameof(handle));
}
/// <summary>
@@ -155,8 +162,11 @@ namespace VNLib.Utils.IO
/// <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, nint length, bool readOnly, bool ownsHandle)
+ private VnMemoryStream(IResizeableMemoryHandle<byte> buffer, nint length, bool readOnly, bool ownsHandle)
{
+ Debug.Assert(length >= 0, "Length must be positive");
+ Debug.Assert(buffer.CanRealloc || readOnly, "The supplied buffer is not resizable on a writable stream");
+
OwnsHandle = ownsHandle;
_buffer = buffer; //Consume the handle
_length = length; //Store length of the buffer
@@ -200,8 +210,8 @@ namespace VNLib.Utils.IO
{
throw new IOException("The destinaion stream is not writeable");
}
-
- do
+
+ while (LenToPosDiff > 0)
{
//Calc the remaining bytes to read no larger than the buffer size
int bytesToRead = (int)Math.Min(LenToPosDiff, bufferSize);
@@ -213,8 +223,7 @@ namespace VNLib.Utils.IO
//Update position
_position += bytesToRead;
-
- } while (LenToPosDiff > 0);
+ }
}
/// <summary>
@@ -231,37 +240,47 @@ namespace VNLib.Utils.IO
public override async Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken)
{
_ = destination ?? throw new ArgumentNullException(nameof(destination));
-
+
+ if (bufferSize < 1)
+ {
+ throw new ArgumentOutOfRangeException(nameof(bufferSize), "Buffer size must be greater than 0");
+ }
+
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
- */
+ cancellationToken.ThrowIfCancellationRequested();
- using VnTempBuffer<byte> copyBuffer = new(bufferSize);
-
- do
+ if(_length < Int32.MaxValue)
{
- //read from internal stream
- int read = Read(copyBuffer);
+ //Safe to alloc a memory manager to do copy
+ using MemoryManager<byte> asMemManager = _buffer.ToMemoryManager(false);
+
+ /*
+ * CopyTo starts at the current position, as if calling Read()
+ * so the reader must be offset to match and the _length gives us the
+ * actual length of the stream and therefor the segment size
+ */
- if(read <= 0)
+ while(LenToPosDiff > 0)
{
- break;
- }
+ int blockSize = Math.Min((int)LenToPosDiff, bufferSize);
+ Memory<byte> window = asMemManager.Memory.Slice((int)_position, blockSize);
- //write async
- await destination.WriteAsync(copyBuffer.AsMemory(0, read), cancellationToken);
+ //write async
+ await destination.WriteAsync(window, cancellationToken);
- } while (true);
-
+ //Update position
+ _position+= bufferSize;
+ }
+ }
+ else
+ {
+ //TODO support 64bit memory stream copy
+ throw new NotSupportedException("64bit async copies are currently not supported");
+ }
}
/// <summary>
@@ -349,13 +368,13 @@ namespace VNLib.Utils.IO
}
//get the value at the current position
- byte* ptr = _buffer.GetOffset(_position);
+ ref byte ptr = ref _buffer.GetByteOffsetRef((nuint)_position);
//Increment position
_position++;
//Return value
- return *ptr;
+ return ptr;
}
/*
diff --git a/lib/Utils/src/Memory/VnTempBuffer.cs b/lib/Utils/src/Memory/ArrayPoolBuffer.cs
index 5f5f831..92a2022 100644
--- a/lib/Utils/src/Memory/VnTempBuffer.cs
+++ b/lib/Utils/src/Memory/ArrayPoolBuffer.cs
@@ -24,6 +24,8 @@
using System;
using System.Buffers;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using VNLib.Utils.Extensions;
@@ -33,7 +35,7 @@ namespace VNLib.Utils.Memory
/// 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>, IMemoryOwner<T>
+ public sealed class ArrayPoolBuffer<T> : VnDisposeable, IIndexable<int, T>, IMemoryHandle<T>, IMemoryOwner<T>
{
private readonly ArrayPool<T> Pool;
@@ -64,25 +66,29 @@ namespace VNLib.Utils.Memory
}
///<inheritdoc/>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public ref T GetReference() => ref MemoryMarshal.GetArrayDataReference(Buffer);
+
+ ///<inheritdoc/>
Memory<T> IMemoryOwner<T>.Memory => AsMemory();
/// <summary>
- /// Allocates a new <see cref="VnTempBuffer{BufType}"/> with a new buffer from shared array-pool
+ /// Allocates a new <see cref="ArrayPoolBuffer{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)
- {}
+ public ArrayPoolBuffer(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
+ /// Allocates a new <see cref="ArrayPoolBuffer{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)
+ public ArrayPoolBuffer(ArrayPool<T> pool, int minSize, bool zero = false)
{
- Pool = pool;
+ Pool = pool ?? throw new ArgumentNullException(nameof(pool));
Buffer = pool.Rent(minSize, zero);
InitSize = minSize;
}
@@ -126,44 +132,64 @@ namespace VNLib.Utils.Memory
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);
- }
+ public Memory<T> AsMemory(int count) => AsMemory()[..count];
/// <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 count)
+ public Memory<T> AsMemory(int start, int count) => AsMemory().Slice(start, count);
+
+ /// <summary>
+ /// Gets an array segment around the internal buffer
+ /// </summary>
+ /// <returns>The internal array segment</returns>
+ /// <exception cref="ObjectDisposedException"></exception>
+ public ArraySegment<T> AsArraySegment()
{
Check();
- return new Memory<T>(Buffer, 0, count);
+ return new ArraySegment<T>(Buffer, 0, InitSize);
}
-
- /*
- * 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>
+ /// Gets an array segment around the internal buffer
+ /// </summary>
+ /// <returns>The internal array segment</returns>
+ /// <exception cref="ObjectDisposedException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public ArraySegment<T> AsArraySegment(int start, int count)
+ {
+ if(start< 0 || count < 0)
+ {
+ throw new ArgumentOutOfRangeException(start < 0 ? nameof(start) : nameof(count), "Cannot be less than zero");
+ }
+
+ MemoryUtil.CheckBounds(Buffer, (uint)start, (uint)count);
+
+ Check();
+ return new ArraySegment<T>(Buffer, start, count);
+ }
+
+ //Pin, will also check bounds
+ ///<inheritdoc/>
+ public MemoryHandle Pin(int elementIndex) => MemoryUtil.PinArrayAndGetHandle(Buffer, elementIndex);
+
+ void IPinnable.Unpin()
+ {
+ //Gchandle will manage the unpin
+ }
/// <summary>
/// Returns buffer to shared array-pool
@@ -179,16 +205,7 @@ namespace VNLib.Utils.Memory
#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
}
- //Pin, will also check bounds
- ///<inheritdoc/>
- public MemoryHandle Pin(int elementIndex) => MemoryUtil.PinArrayAndGetHandle(Buffer, elementIndex);
-
- void IPinnable.Unpin()
- {
- //Gchandle will manage the unpin
- }
-
///<inheritdoc/>
- ~VnTempBuffer() => Free();
+ ~ArrayPoolBuffer() => Free();
}
} \ No newline at end of file
diff --git a/lib/Utils/src/Memory/Caching/IReusable.cs b/lib/Utils/src/Memory/Caching/IReusable.cs
index 618878f..4472ad3 100644
--- a/lib/Utils/src/Memory/Caching/IReusable.cs
+++ b/lib/Utils/src/Memory/Caching/IReusable.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -25,14 +25,20 @@
namespace VNLib.Utils.Memory.Caching
{
/// <summary>
- /// Allows for use within a <see cref="ReusableStore{T}"/>, this object is intended to be reused heavily
+ /// Allows for use within a <see cref="ObjectRental{T}"/>, this object is intended to be reused heavily
/// </summary>
public interface IReusable
{
/// <summary>
/// The instance should prepare itself for use (or re-use)
+ /// <para>
+ /// This method is guarunteed to be called directly after a constructor
+ /// when a new instance is allocated and before it is ever returned to a
+ /// caller.
+ /// </para>
/// </summary>
void Prepare();
+
/// <summary>
/// The intance is being returned and should determine if it's state is reusabled
/// </summary>
diff --git a/lib/Utils/src/Memory/HeapCreation.cs b/lib/Utils/src/Memory/HeapCreation.cs
index 9ef9fdb..835226c 100644
--- a/lib/Utils/src/Memory/HeapCreation.cs
+++ b/lib/Utils/src/Memory/HeapCreation.cs
@@ -49,6 +49,10 @@ namespace VNLib.Utils.Memory
/// <summary>
/// Specifies that the requested heap will be a shared heap for the process/library
/// </summary>
- Shared = 0x04
+ Shared = 0x04,
+ /// <summary>
+ /// Specifies that the heap will support block reallocation
+ /// </summary>
+ SupportsRealloc = 0x08,
}
} \ No newline at end of file
diff --git a/lib/Utils/src/Memory/IMemoryHandle.cs b/lib/Utils/src/Memory/IMemoryHandle.cs
index cf19ce9..f4e1a36 100644
--- a/lib/Utils/src/Memory/IMemoryHandle.cs
+++ b/lib/Utils/src/Memory/IMemoryHandle.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -42,6 +42,12 @@ namespace VNLib.Utils.Memory
/// Gets the internal block as a span
/// </summary>
Span<T> Span { get; }
+
+ /// <summary>
+ /// Gets a reference to the first element in the block
+ /// </summary>
+ /// <returns>The reference</returns>
+ ref T GetReference();
}
}
diff --git a/lib/Utils/src/Memory/IResizeableMemoryHandle.cs b/lib/Utils/src/Memory/IResizeableMemoryHandle.cs
new file mode 100644
index 0000000..f788b48
--- /dev/null
+++ b/lib/Utils/src/Memory/IResizeableMemoryHandle.cs
@@ -0,0 +1,52 @@
+/*
+* Copyright (c) 2023 Vaughn Nugent
+*
+* Library: VNLib
+* Package: VNLib.Utils
+* File: IResizeableMemoryHandle.cs
+*
+* IResizeableMemoryHandle.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>
+ /// Represents a memory handle that can be resized in place.
+ /// </summary>
+ /// <typeparam name="T">The data type</typeparam>
+ public interface IResizeableMemoryHandle<T> : IMemoryHandle<T>
+ {
+ /// <summary>
+ /// Gets a value indicating whether the handle supports resizing in place
+ /// </summary>
+ bool CanRealloc { get; }
+
+ /// <summary>
+ /// Resizes a memory handle to a new number of elements.
+ /// </summary>
+ /// <remarks>
+ /// Even if a handle is resizable resizing may not be supported for all types of handles.
+ /// </remarks>
+ /// <param name="elements">The new number of elements to resize the handle to</param>
+ /// <exception cref="OutOfMemoryException"></exception>
+ /// <exception cref="NotSupportedException"></exception>
+ void Resize(nuint elements);
+ }
+} \ No newline at end of file
diff --git a/lib/Utils/src/Memory/MemoryHandle.cs b/lib/Utils/src/Memory/MemoryHandle.cs
index 30d2b99..4d2ff0c 100644
--- a/lib/Utils/src/Memory/MemoryHandle.cs
+++ b/lib/Utils/src/Memory/MemoryHandle.cs
@@ -40,8 +40,16 @@ namespace VNLib.Utils.Memory
/// 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
+ public sealed class MemoryHandle<T> :
+ SafeHandleZeroOrMinusOneIsInvalid,
+ IResizeableMemoryHandle<T>,
+ IMemoryHandle<T>,
+ IEquatable<MemoryHandle<T>>
+ where T : unmanaged
{
+ private readonly bool ZeroMemory;
+ private readonly IUnmangedHeap Heap;
+ private nuint _length;
/// <summary>
/// New <typeparamref name="T"/>* pointing to the base of the allocated block
@@ -79,15 +87,11 @@ namespace VNLib.Utils.Memory
}
}
- private readonly bool ZeroMemory;
- private readonly IUnmangedHeap Heap;
- private nuint _length;
-
///<inheritdoc/>
- public nuint Length
+ public nuint Length
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _length;
+ get => _length;
}
/// <summary>
@@ -101,6 +105,13 @@ namespace VNLib.Utils.Memory
get => MemoryUtil.ByteCount<T>(_length);
}
+ ///<inheritdoc/>
+ public bool CanRealloc
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => Heap != null && Heap.CreationFlags.HasFlag(HeapCreation.SupportsRealloc);
+ }
+
/// <summary>
/// Creates a new memory handle, for which is holds ownership, and allocates the number of elements specified on the heap.
/// </summary>
@@ -128,7 +139,7 @@ namespace VNLib.Utils.Memory
/// when accessed, however <see cref="IMemoryHandle{T}"/> operations are
/// considered "safe" meaning they should never raise excpetions
/// </summary>
- public MemoryHandle():base(false)
+ public MemoryHandle() : base(false)
{
_length = 0;
Heap = null!;
@@ -151,7 +162,7 @@ namespace VNLib.Utils.Memory
* If resize raises an exception the current block pointer
* should still be valid, if its not, the pointer should
* be set to 0/-1, which will be considered invalid anyway
- */
+ */
Heap.Resize(ref handle, elements, (nuint)sizeof(T), ZeroMemory);
@@ -161,14 +172,14 @@ namespace VNLib.Utils.Memory
//Catch the disposed exception so we can invalidate the current ptr
catch (ObjectDisposedException)
{
- base.handle = IntPtr.Zero;
+ SetHandle(IntPtr.Zero);
//Set as invalid so release does not get called
- base.SetHandleAsInvalid();
+ SetHandleAsInvalid();
//Propagate the exception
throw;
}
}
-
+
/// <summary>
/// Gets an offset pointer from the base postion to the number of bytes specified. Performs bounds checks
/// </summary>
@@ -190,7 +201,14 @@ namespace VNLib.Utils.Memory
T* bs = ((T*)handle) + elements;
return bs;
}
-
+
+ ///<inheritdoc/>
+ public ref T GetReference()
+ {
+ this.ThrowIfClosed();
+ return ref MemoryUtil.GetRef<T>(handle);
+ }
+
///<inheritdoc/>
///<exception cref="ObjectDisposedException"></exception>
///<exception cref="ArgumentOutOfRangeException"></exception>
@@ -200,14 +218,14 @@ namespace VNLib.Utils.Memory
///</remarks>
public unsafe MemoryHandle Pin(int elementIndex)
{
- if(elementIndex < 0)
+ if (elementIndex < 0)
{
throw new ArgumentOutOfRangeException(nameof(elementIndex));
}
//Get ptr and guard checks before adding the referrence
T* ptr = GetOffset((nuint)elementIndex);
-
+
bool addRef = false;
//use the pinned field as success val
DangerousAddRef(ref addRef);
@@ -216,7 +234,7 @@ namespace VNLib.Utils.Memory
? 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()
@@ -226,11 +244,7 @@ namespace VNLib.Utils.Memory
}
///<inheritdoc/>
- protected override bool ReleaseHandle()
- {
- //Return result of free, only if the handle is valid
- return Heap.Free(ref handle);
- }
+ protected override bool ReleaseHandle() => Heap.Free(ref handle);
/// <summary>
/// Determines if the memory blocks are equal by comparing their base addresses.
@@ -243,14 +257,13 @@ namespace VNLib.Utils.Memory
{
return other != null && (IsClosed | other.IsClosed) == false && _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)
{
diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs
index 8cc9736..0261bdf 100644
--- a/lib/Utils/src/Memory/MemoryUtil.cs
+++ b/lib/Utils/src/Memory/MemoryUtil.cs
@@ -31,7 +31,7 @@ using System.Globalization;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
-using VNLib.Utils.Extensions;
+using VNLib.Utils.Resources;
using VNLib.Utils.Memory.Diagnostics;
namespace VNLib.Utils.Memory
@@ -113,7 +113,7 @@ namespace VNLib.Utils.Memory
Trace.WriteLineIf(globalZero, "Shared heap global zero enabled");
Lazy<IUnmangedHeap> heap = new (() => InitHeapInternal(true, diagEnable, globalZero), LazyThreadSafetyMode.PublicationOnly);
-
+
//Cleanup the heap on process exit
AppDomain.CurrentDomain.DomainUnload += DomainUnloaded;
@@ -165,7 +165,7 @@ namespace VNLib.Utils.Memory
string? rawFlagsEnv = Environment.GetEnvironmentVariable(SHARED_HEAP_RAW_FLAGS);
//Default flags
- HeapCreation cFlags = HeapCreation.UseSynchronization;
+ HeapCreation cFlags = HeapCreation.UseSynchronization | HeapCreation.SupportsRealloc;
/*
* We need to set the shared flag and the synchronziation flag.
@@ -235,7 +235,7 @@ namespace VNLib.Utils.Memory
/// <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
+ public static void UnsafeZeroMemory<T>(ReadOnlySpan<T> block) where T : struct
{
if (block.IsEmpty)
{
@@ -244,11 +244,11 @@ namespace VNLib.Utils.Memory
uint byteSize = ByteCount<T>((uint)block.Length);
- fixed (void* ptr = &MemoryMarshal.GetReference(block))
- {
- //Calls memset
- Unsafe.InitBlock(ptr, 0, byteSize);
- }
+ ref T r0 = ref MemoryMarshal.GetReference(block);
+ ref byte byteRef = ref Unsafe.As<T, byte>(ref r0);
+
+ //Calls memset
+ Unsafe.InitBlock(ref byteRef, 0, byteSize);
}
/// <summary>
@@ -257,7 +257,7 @@ namespace VNLib.Utils.Memory
/// <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
+ public static void UnsafeZeroMemory<T>(ReadOnlyMemory<T> block) where T : struct
{
if (block.IsEmpty)
{
@@ -284,7 +284,7 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The unmanaged</typeparam>
/// <param name="block">The block of memory to initialize</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void InitializeBlock<T>(Span<T> block) where T : unmanaged => UnsafeZeroMemory<T>(block);
+ public static void InitializeBlock<T>(Span<T> block) where T : struct => UnsafeZeroMemory<T>(block);
/// <summary>
/// Initializes a block of memory with zeros
@@ -292,7 +292,7 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The unmanaged</typeparam>
/// <param name="block">The block of memory to initialize</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void InitializeBlock<T>(Memory<T> block) where T : unmanaged => UnsafeZeroMemory<T>(block);
+ public static void InitializeBlock<T>(Memory<T> block) where T : struct => UnsafeZeroMemory<T>(block);
/// <summary>
/// Zeroes a block of memory of the given unmanaged type
@@ -303,16 +303,13 @@ namespace VNLib.Utils.Memory
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
public static void InitializeBlock<T>(T* block, int itemCount) where T : unmanaged
{
- if (itemCount == 0)
+ if (itemCount <= 0 || block == null)
{
return;
}
- //Get the size of the structure
- int size = sizeof(T);
-
//Zero block
- Unsafe.InitBlock(block, 0, (uint)(size * itemCount));
+ Unsafe.InitBlock(block, 0, ByteCount<T>((uint)itemCount));
}
/// <summary>
@@ -321,33 +318,24 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The unmanaged type to zero</typeparam>
/// <param name="block">A pointer to the block of memory to zero</param>
/// <param name="itemCount">The number of elements in the block to zero</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InitializeBlock<T>(IntPtr block, int itemCount) where T : unmanaged => InitializeBlock((T*)block, itemCount);
/// <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 does not have to be primitive type
- int size = Unsafe.SizeOf<T>();
- //Zero block
- Unsafe.InitBlock(block.ToPointer(), 0, (uint)size);
- }
+ /// <param name="structPtr">The pointer to the allocated structure</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ZeroStruct<T>(void* structPtr) => Unsafe.InitBlock(structPtr, 0, (uint)Unsafe.SizeOf<T>());
/// <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)
- {
- //get thes size of the structure
- int size = Unsafe.SizeOf<T>();
- //Zero block
- Unsafe.InitBlock(structPtr, 0, (uint)size);
- }
+ /// <param name="block">The pointer to the allocated structure</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ZeroStruct<T>(IntPtr block) => ZeroStruct<T>(block.ToPointer());
/// <summary>
/// Zeroes a block of memory pointing to the structure
@@ -355,12 +343,20 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The structure type</typeparam>
/// <param name="structPtr">The pointer to the allocated structure</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ZeroStruct<T>(T* structPtr) where T : unmanaged => Unsafe.InitBlock(structPtr, 0, (uint)sizeof(T));
+ public static void ZeroStruct<T>(T* structPtr) where T : unmanaged => ZeroStruct<T>((void*)structPtr);
#endregion
#region Copy
+ /*
+ * Dirty little trick to access internal Buffer.Memmove method for
+ * large references. May not always be supported, so optional safe
+ * guards are in place.
+ */
+ private delegate void BigMemmove(ref byte dest, ref byte src, nuint len);
+ private static readonly BigMemmove? _sysMemmove = ManagedLibrary.TryGetStaticMethod<BigMemmove>(typeof(Buffer), "Memmove", System.Reflection.BindingFlags.NonPublic);
+
/// <summary>
/// Copies data from source memory to destination memory of an umanged data type
/// </summary>
@@ -369,7 +365,7 @@ namespace VNLib.Utils.Memory
/// <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, nuint destOffset) where T : unmanaged
+ public static void Copy<T>(ReadOnlySpan<T> source, IMemoryHandle<T> dest, nuint destOffset) where T: struct
{
if (dest is null)
{
@@ -381,11 +377,17 @@ namespace VNLib.Utils.Memory
return;
}
- //Get long offset from the destination handle (also checks bounds)
- Span<T> dst = dest.GetOffsetSpan(destOffset, source.Length);
+ //Check memhandle bounds
+ CheckBounds(dest, destOffset, (uint)source.Length);
- //Copy data
- source.CopyTo(dst);
+ //Get byte ref and byte count
+ nuint byteCount = ByteCount<T>((uint)source.Length);
+ ref T src = ref MemoryMarshal.GetReference(source);
+ ref T dst = ref dest.GetReference();
+
+ //Use memmove by ref
+ bool success = MemmoveByRef(ref src, 0, ref dst, (uint)destOffset, byteCount);
+ Debug.Assert(success, "Memmove by ref call failed during a 32bit copy");
}
/// <summary>
@@ -396,24 +398,7 @@ namespace VNLib.Utils.Memory
/// <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, nuint destOffset) where T : unmanaged
- {
- if (dest is null)
- {
- throw new ArgumentNullException(nameof(dest));
- }
-
- if (source.IsEmpty)
- {
- return;
- }
-
- //Get long offset from the destination handle (also checks bounds)
- Span<T> dst = dest.GetOffsetSpan(destOffset, source.Length);
-
- //Copy data
- source.Span.CopyTo(dst);
- }
+ public static void Copy<T>(ReadOnlyMemory<T> source, IMemoryHandle<T> dest, nuint destOffset) where T : struct => Copy(source.Span, dest, destOffset);
/// <summary>
/// Copies data from source memory to destination memory of an umanged data type
@@ -425,10 +410,12 @@ namespace VNLib.Utils.Memory
/// <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, nint sourceOffset, Span<T> dest, int destOffset, int count) where T : unmanaged
+ public static void Copy<T>(IMemoryHandle<T> source, nint sourceOffset, Span<T> dest, int destOffset, int count) where T : struct
{
+ _ = source ?? throw new ArgumentNullException(nameof(source));
+
//Validate source/dest/count
- ValidateArgs(sourceOffset, destOffset, count);
+ ValidateCopyArgs(sourceOffset, destOffset, count);
//Check count last for debug reasons
if (count == 0)
@@ -436,14 +423,17 @@ namespace VNLib.Utils.Memory
return;
}
- //Get offset span, also checks bounts
- Span<T> src = source.GetOffsetSpan(sourceOffset, count);
-
- //slice the dest span
- Span<T> dst = dest.Slice(destOffset, count);
+ //Check source bounds
+ CheckBounds(source, (nuint)sourceOffset, (nuint)count);
- //Copy data
- src.CopyTo(dst);
+ //Get byte ref and byte count
+ nuint byteCount = ByteCount<T>((uint)count);
+ ref T src = ref source.GetReference();
+ ref T dst = ref MemoryMarshal.GetReference(dest);
+
+ //Use memmove by ref
+ bool success = MemmoveByRef(ref src, (uint)sourceOffset, ref dst, (uint)destOffset, byteCount);
+ Debug.Assert(success, "Memmove by ref call failed during a 32bit copy");
}
/// <summary>
@@ -458,13 +448,51 @@ namespace VNLib.Utils.Memory
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void Copy<T>(MemoryHandle<T> source, nint sourceOffset, Memory<T> dest, int destOffset, int count) where T : unmanaged
+ public static void Copy<T>(IMemoryHandle<T> source, nint sourceOffset, Memory<T> dest, int destOffset, int count) where T : struct
+ => Copy(source, sourceOffset, dest.Span, destOffset, count);
+
+ /// <summary>
+ /// Copies data from source memory to destination memory of an umanged data type
+ /// using references for blocks smaller than <see cref="UInt32.MaxValue"/> and
+ /// pinning for larger blocks
+ /// </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="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public static void Copy<T>(IMemoryHandle<T> source, nuint sourceOffset, IMemoryHandle<T> dest, nuint destOffset, nuint count) where T : unmanaged
{
- //Call copy method with dest as span
- Copy(source, sourceOffset, dest.Span, destOffset, count);
+ _ = source ?? throw new ArgumentNullException(nameof(source));
+ _ = dest ?? throw new ArgumentNullException(nameof(dest));
+
+ CheckBounds(source, sourceOffset, count);
+ CheckBounds(dest, destOffset, count);
+
+ //Get byte ref and byte count
+ nuint byteCount = ByteCount<T>(count);
+ ref T src = ref source.GetReference();
+ ref T dst = ref dest.GetReference();
+
+ if (!MemmoveByRef(ref src, sourceOffset, ref dst, destOffset, byteCount))
+ {
+ //Copying block larger than 32bit must be done with pointers
+ using MemoryHandle srcH = source.Pin(0);
+ using MemoryHandle dstH = dest.Pin(0);
+
+ //Get pointers and add offsets
+ T* srcOffset = ((T*)srcH.Pointer) + sourceOffset;
+ T* dstOffset = ((T*)dstH.Pointer) + destOffset;
+
+ //Copy memory
+ Buffer.MemoryCopy(srcOffset, dstOffset, byteCount, byteCount);
+ }
}
- private static void ValidateArgs(nint sourceOffset, nint destOffset, nint count)
+ private static void ValidateCopyArgs(nint sourceOffset, nint destOffset, nint count)
{
if(sourceOffset < 0)
{
@@ -483,17 +511,19 @@ namespace VNLib.Utils.Memory
}
/// <summary>
- /// 32/64 bit large block copy
+ /// Preforms a fast referrence based copy on very large blocks of memory
+ /// using pinning and pointers only when the number of bytes to copy is
+ /// larger than <see cref="UInt32.MaxValue"/>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source">The source memory handle to copy data from</param>
- /// <param name="offset">The element offset to begin reading from</param>
+ /// <param name="sourceOffset">The element offset to begin reading from</param>
/// <param name="dest">The destination array to write data to</param>
/// <param name="destOffset"></param>
/// <param name="count">The number of elements to copy</param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public static void Copy<T>(IMemoryHandle<T> source, nuint offset, T[] dest, nuint destOffset, nuint count) where T : unmanaged
+ public static void Copy<T>(IMemoryHandle<T> source, nuint sourceOffset, T[] dest, nuint destOffset, nuint count) where T : unmanaged
{
if (source is null)
{
@@ -511,41 +541,62 @@ namespace VNLib.Utils.Memory
}
//Check source bounds
- CheckBounds(source, offset, count);
+ CheckBounds(source, sourceOffset, count);
- //Check dest bounts
+ //Check dest bounds
CheckBounds(dest, destOffset, count);
- //Check if 64bit
- if(sizeof(void*) == 8)
+ //Get byte refs and byte count
+ nuint byteCount = ByteCount<T>(count);
+ ref T src = ref source.GetReference();
+ ref T dst = ref MemoryMarshal.GetArrayDataReference(dest);
+
+ //Try to memove by ref first, otherwise fallback to pinning
+ if (!MemmoveByRef(ref src, sourceOffset, ref dst, destOffset, byteCount))
{
- //Get the number of bytes to copy
- nuint byteCount = ByteCount<T>(count);
+ //Copying block larger than 32bit must be done with pointers
+ using MemoryHandle srcH = source.Pin(0);
+ using MemoryHandle dstH = PinArrayAndGetHandle(dest, 0);
- //Get memory handle from source
- using MemoryHandle srcHandle = source.Pin(0);
+ //Get pointers and add offsets
+ T* srcOffset = ((T*)srcH.Pointer) + sourceOffset;
+ T* dstOffset = ((T*)dstH.Pointer) + destOffset;
- //get source offset
- T* src = (T*)srcHandle.Pointer + offset;
+ //Copy memory
+ Buffer.MemoryCopy(srcOffset, dstOffset, byteCount, byteCount);
+ }
+ }
+
- //pin array
- fixed (T* dst = &MemoryMarshal.GetArrayDataReference(dest))
- {
- //Offset dest ptr
- T* dstOffset = dst + destOffset;
+ [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
+ private static bool MemmoveByRef<T>(ref T src, nuint srcOffset, ref T dst, nuint dstOffset, nuint byteCount) where T : struct
+ {
+ Debug.Assert(!Unsafe.IsNullRef(ref src), "Null source reference passed to MemmoveByRef");
+ Debug.Assert(!Unsafe.IsNullRef(ref dst), "Null destination reference passed to MemmoveByRef");
- //Copy src to set
- Buffer.MemoryCopy(src, dstOffset, byteCount, byteCount);
- }
+ //Get offset referrences to the source and destination
+ ref T srcOffsetPtr = ref Unsafe.Add(ref src, srcOffset);
+ ref T dstOffsetPtr = ref Unsafe.Add(ref dst, dstOffset);
+
+ //Cast to byte pointers
+ ref byte srcByte = ref Unsafe.As<T, byte>(ref srcOffsetPtr);
+ ref byte dstByte = ref Unsafe.As<T, byte>(ref dstOffsetPtr);
+
+ if (_sysMemmove != null)
+ {
+ //Call sysinternal memmove
+ _sysMemmove(ref dstByte, ref srcByte, byteCount);
+ return true;
+ }
+ else if(byteCount < uint.MaxValue)
+ {
+ //Use safe 32bit block copy
+ Unsafe.CopyBlock(ref dstByte, ref srcByte, (uint)byteCount);
+ return true;
}
else
{
- //If 32bit its safe to use spans
-
- Span<T> src = source.AsSpan((int)offset, (int)count);
- Span<T> dst = dest.AsSpan((int)destOffset, (int)count);
- //Copy
- src.CopyTo(dst);
+ return false;
}
}
@@ -598,6 +649,26 @@ namespace VNLib.Utils.Memory
public static uint ByteCount<T>(uint elementCount) => checked(elementCount * (uint)Unsafe.SizeOf<T>());
/// <summary>
+ /// Gets the byte multiple of the length parameter. NOTE: Does not verify negative values
+ /// </summary>
+ /// <typeparam name="T">The type to get the byte offset of</typeparam>
+ /// <param name="elementCount">The number of elements to get the byte count of</param>
+ /// <returns>The byte multiple of the number of elments</returns>
+ /// <exception cref="OverflowException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static nint ByteCount<T>(nint elementCount) => checked(elementCount * Unsafe.SizeOf<T>());
+
+ /// <summary>
+ /// Gets the byte multiple of the length parameter. NOTE: Does not verify negative values
+ /// </summary>
+ /// <typeparam name="T">The type to get the byte offset of</typeparam>
+ /// <param name="elementCount">The number of elements to get the byte count of</param>
+ /// <returns>The byte multiple of the number of elments</returns>
+ /// <exception cref="OverflowException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static int ByteCount<T>(int elementCount) => checked(elementCount * Unsafe.SizeOf<T>());
+
+ /// <summary>
/// Checks if the offset/count paramters for the given memory handle
/// point outside the block wrapped in the handle
/// </summary>
@@ -682,21 +753,21 @@ namespace VNLib.Utils.Memory
throw new ArgumentOutOfRangeException(nameof(elementOffset));
}
+ _ = array ?? throw new ArgumentNullException(nameof(array));
+
//Quick verify index exists, may be the very last index
CheckBounds(array, (nuint)elementOffset, 1);
//Pin the array
GCHandle arrHandle = GCHandle.Alloc(array, GCHandleType.Pinned);
- //Get array base address
- void* basePtr = (void*)arrHandle.AddrOfPinnedObject();
-
- Debug.Assert(basePtr != null);
+ //safe to get array basee pointer
+ ref T arrBase = ref MemoryMarshal.GetArrayDataReference(array);
//Get element offset
- void* indexOffet = Unsafe.Add<T>(basePtr, elementOffset);
+ ref T indexOffet = ref Unsafe.Add(ref arrBase, elementOffset);
- return new(indexOffet, arrHandle);
+ return new(Unsafe.AsPointer(ref indexOffet), arrHandle);
}
/// <summary>
@@ -741,6 +812,29 @@ namespace VNLib.Utils.Memory
public static Span<T> GetSpan<T>(MemoryHandle handle, int size) => new(handle.Pointer, size);
/// <summary>
+ /// Recovers a reference to the supplied pointer
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="address">The base address to cast to a reference</param>
+ /// <returns>The reference to the supplied address</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ref T GetRef<T>(IntPtr address) => ref Unsafe.AsRef<T>(address.ToPointer());
+
+ /// <summary>
+ /// Recovers a reference to the supplied pointer
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="address">The base address to cast to a reference</param>
+ /// <param name="offset">The offset to add to the base address</param>
+ /// <returns>The reference to the supplied address</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ref T GetRef<T>(IntPtr address, nuint offset)
+ {
+ ref T baseRef = ref GetRef<T>(address);
+ return ref Unsafe.Add(ref baseRef, (nint)offset);
+ }
+
+ /// <summary>
/// Rounds the requested byte size up to the nearest page
/// number of bytes
/// </summary>
diff --git a/lib/Utils/src/Memory/MemoryUtilAlloc.cs b/lib/Utils/src/Memory/MemoryUtilAlloc.cs
index e4210e7..9e305d0 100644
--- a/lib/Utils/src/Memory/MemoryUtilAlloc.cs
+++ b/lib/Utils/src/Memory/MemoryUtilAlloc.cs
@@ -92,7 +92,7 @@ namespace VNLib.Utils.Memory
}
//Round to nearest page (in bytes)
- nint np = NearestPage(elements * sizeof(T));
+ nint np = NearestPage(ByteCount<T>(elements));
//Resize to element size
np /= sizeof(T);
@@ -131,7 +131,7 @@ namespace VNLib.Utils.Memory
}
else
{
- return new VnTempBuffer<T>(ArrayPool<T>.Shared, elements, zero);
+ return new ArrayPoolBuffer<T>(ArrayPool<T>.Shared, elements, zero);
}
}
@@ -154,7 +154,7 @@ namespace VNLib.Utils.Memory
}
//Round to nearest page (in bytes)
- nint np = NearestPage(elements * sizeof(T));
+ nint np = NearestPage(ByteCount<T>(elements));
//Resize to element size
np /= sizeof(T);
@@ -258,7 +258,7 @@ namespace VNLib.Utils.Memory
}
else
{
- return new VnTempBuffer<byte>(ArrayPool<byte>.Shared, elements, zero);
+ return new ArrayPoolBuffer<byte>(ArrayPool<byte>.Shared, elements, zero);
}
}
diff --git a/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs b/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs
index f2bbd51..a17a906 100644
--- a/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs
+++ b/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs
@@ -25,6 +25,8 @@
using System;
using System.Buffers;
+using VNLib.Utils.Extensions;
+
namespace VNLib.Utils.Memory
{
/// <summary>
@@ -48,7 +50,7 @@ namespace VNLib.Utils.Memory
///<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);
+ public override IMemoryOwner<T> Rent(int minBufferSize = 0) => Heap.DirectAlloc<T>(minBufferSize, false);
/// <summary>
/// Allocates a new <see cref="MemoryManager{T}"/> of a different data type from the pool
@@ -56,7 +58,7 @@ namespace VNLib.Utils.Memory
/// <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 => new SysBufferMemoryManager<TDifType>(Heap, (uint)minBufferSize, false);
+ public IMemoryOwner<TDifType> Rent<TDifType>(int minBufferSize = 0) where TDifType : unmanaged => Heap.DirectAlloc<TDifType>(minBufferSize, false);
///<inheritdoc/>
protected override void Dispose(bool disposing)
diff --git a/lib/Utils/src/Memory/ProcessHeap.cs b/lib/Utils/src/Memory/ProcessHeap.cs
index 3d581cd..5d1bee6 100644
--- a/lib/Utils/src/Memory/ProcessHeap.cs
+++ b/lib/Utils/src/Memory/ProcessHeap.cs
@@ -49,7 +49,7 @@ namespace VNLib.Utils.Memory
/// process heap. Meaining memory will be shared across the process
/// </para>
/// </summary>
- public HeapCreation CreationFlags { get; } = HeapCreation.Shared;
+ public HeapCreation CreationFlags { get; } = HeapCreation.Shared | HeapCreation.SupportsRealloc;
/// <summary>
/// Initalizes a new global (cross platform) process heap
diff --git a/lib/Utils/src/Memory/SubSequence.cs b/lib/Utils/src/Memory/SubSequence.cs
index 1db0ba5..86b2347 100644
--- a/lib/Utils/src/Memory/SubSequence.cs
+++ b/lib/Utils/src/Memory/SubSequence.cs
@@ -32,14 +32,14 @@ namespace VNLib.Utils.Memory
/// 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 record struct SubSequence<T> where T: unmanaged
+ public readonly record struct SubSequence<T>
{
readonly nuint _offset;
/// <summary>
/// The handle that owns the memory block
/// </summary>
- public readonly MemoryHandle<T> Handle { get; }
+ public readonly IMemoryHandle<T> Handle { get; }
/// <summary>
/// The number of elements in the current sequence
@@ -54,7 +54,7 @@ namespace VNLib.Utils.Memory
/// <param name="size"></param>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public SubSequence(MemoryHandle<T> block, nuint offset, int size)
+ public SubSequence(IMemoryHandle<T> block, nuint offset, int size)
{
Handle = block ?? throw new ArgumentNullException(nameof(block));
Size = size >= 0 ? size : throw new ArgumentOutOfRangeException(nameof(size));
diff --git a/lib/Utils/src/Memory/SysBufferMemoryManager.cs b/lib/Utils/src/Memory/SysBufferMemoryManager.cs
index aca2543..26c3688 100644
--- a/lib/Utils/src/Memory/SysBufferMemoryManager.cs
+++ b/lib/Utils/src/Memory/SysBufferMemoryManager.cs
@@ -1,5 +1,5 @@
/*
-* Copyright (c) 2022 Vaughn Nugent
+* Copyright (c) 2023 Vaughn Nugent
*
* Library: VNLib
* Package: VNLib.Utils
@@ -25,8 +25,6 @@
using System;
using System.Buffers;
-using VNLib.Utils.Extensions;
-
namespace VNLib.Utils.Memory
{
/// <summary>
@@ -34,7 +32,7 @@ namespace VNLib.Utils.Memory
/// 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
+ public sealed class SysBufferMemoryManager<T> : MemoryManager<T>
{
private readonly IMemoryHandle<T> BackingMemory;
private readonly bool _ownsHandle;
@@ -45,30 +43,19 @@ namespace VNLib.Utils.Memory
/// </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)
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="OverflowException"></exception>
+ public SysBufferMemoryManager(IMemoryHandle<T> existingHandle, bool ownsHandle)
{
- BackingMemory = existingHandle;
- _ownsHandle = ownsHandle;
- }
-
- /// <summary>
- /// Allocates a fized size buffer from the specified unmanaged <see cref="Win32PrivateHeap"/>
- /// </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, nuint elements, bool zero)
- {
- BackingMemory = heap.Alloc<T>(elements, zero);
- _ownsHandle = true;
- }
+ BackingMemory = existingHandle ?? throw new ArgumentNullException(nameof(existingHandle));
+
+ //check for overflow
+ if(existingHandle.Length > Int32.MaxValue)
+ {
+ throw new OverflowException("This memory manager does not accept handles larger than Int32.MaxValue");
+ }
- ///<inheritdoc/>
- protected override bool TryGetArray(out ArraySegment<T> segment)
- {
- //Always false since no array is available
- segment = default;
- return false;
+ _ownsHandle = ownsHandle;
}
///<inheritdoc/>
@@ -81,11 +68,8 @@ namespace VNLib.Utils.Memory
/// </summary>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public unsafe override MemoryHandle Pin(int elementIndex = 0)
- {
- return BackingMemory.Pin(elementIndex);
- }
-
+ public unsafe override MemoryHandle Pin(int elementIndex = 0) => BackingMemory.Pin(elementIndex);
+
///<inheritdoc/>
public override void Unpin()
{}
diff --git a/lib/Utils/src/Memory/UnmanagedHeapBase.cs b/lib/Utils/src/Memory/UnmanagedHeapBase.cs
index bfa6736..0310582 100644
--- a/lib/Utils/src/Memory/UnmanagedHeapBase.cs
+++ b/lib/Utils/src/Memory/UnmanagedHeapBase.cs
@@ -39,21 +39,12 @@ namespace VNLib.Utils.Memory
/// </summary>
public abstract class UnmanagedHeapBase : SafeHandleZeroOrMinusOneIsInvalid, IUnmangedHeap
{
+ private readonly HeapCreation _flags;
+
/// <summary>
/// The heap synchronization handle
/// </summary>
protected readonly object HeapLock;
-
- /// <summary>
- /// The global heap zero flag
- /// </summary>
- protected readonly bool GlobalZero;
-
- /// <summary>
- /// A value that inidicates that locking will
- /// be used when invoking heap operations
- /// </summary>
- protected readonly bool UseSynchronization;
/// <summary>
/// Initalizes the unmanaged heap base class (init synchronization handle)
@@ -63,13 +54,11 @@ namespace VNLib.Utils.Memory
protected UnmanagedHeapBase(HeapCreation flags, bool ownsHandle) : base(ownsHandle)
{
HeapLock = new();
- GlobalZero = flags.HasFlag(HeapCreation.GlobalZero);
- UseSynchronization = flags.HasFlag(HeapCreation.UseSynchronization);
- CreationFlags = flags;
+ _flags = flags;
}
///<inheritdoc/>
- public HeapCreation CreationFlags { get; }
+ public HeapCreation CreationFlags => _flags;
///<inheritdoc/>
///<remarks>Increments the handle count, free must be called to decrement the handle count</remarks>
@@ -82,7 +71,7 @@ namespace VNLib.Utils.Memory
_ = checked(elements * size);
//Force zero if global flag is set
- zero |= GlobalZero;
+ zero |= (_flags & HeapCreation.GlobalZero) > 0;
bool handleCountIncremented = false;
//Increment handle count to prevent premature release
@@ -99,7 +88,7 @@ namespace VNLib.Utils.Memory
LPVOID block;
//Check if lock should be used
- if (UseSynchronization)
+ if ((_flags & HeapCreation.UseSynchronization) > 0)
{
//Enter lock
lock(HeapLock)
@@ -138,7 +127,7 @@ namespace VNLib.Utils.Memory
return true;
}
- if (UseSynchronization)
+ if ((_flags & HeapCreation.UseSynchronization) > 0)
{
//wait for lock
lock (HeapLock)
@@ -173,7 +162,7 @@ namespace VNLib.Utils.Memory
LPVOID newBlock;
//Global zero flag will cause a zero
- zero |= GlobalZero;
+ zero |= (_flags & HeapCreation.GlobalZero) > 0;
/*
* Realloc may return a null pointer if allocation fails
@@ -182,7 +171,7 @@ namespace VNLib.Utils.Memory
* be left untouched
*/
- if (UseSynchronization)
+ if ((_flags & HeapCreation.UseSynchronization) > 0)
{
lock (HeapLock)
{
diff --git a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs
index 6d566f1..e4857d1 100644
--- a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs
+++ b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs
@@ -165,8 +165,7 @@ namespace VNLib.Utils.Memory
if (elementIndex < 0 || elementIndex >= IntLength)
{
throw new ArgumentOutOfRangeException(nameof(elementIndex));
- }
-
+ }
if (_handleType == HandleType.Pool)
{
@@ -174,10 +173,11 @@ namespace VNLib.Utils.Memory
}
else
{
- //Get offset pointer and pass self as pinnable argument, (nothing happens but support it)
- void* basePtr = Unsafe.Add<T>(_memoryPtr.ToPointer(), elementIndex);
+ //Add an offset to the base address of the memory block
+ int byteOffset = MemoryUtil.ByteCount<T>(elementIndex);
+ IntPtr offset = IntPtr.Add(_memoryPtr, byteOffset);
//Unmanaged memory is always pinned, so no need to pass this as IPinnable, since it will cause a box
- return new (basePtr);
+ return MemoryUtil.GetMemoryHandleFromPointer(offset);
}
}
///<inheritdoc/>
@@ -186,6 +186,20 @@ namespace VNLib.Utils.Memory
//Nothing to do since gc handle takes care of array, and unmanaged pointers are not pinned
}
+ ///<inheritdoc/>
+ public readonly ref T GetReference()
+ {
+ switch (_handleType)
+ {
+ case HandleType.Pool:
+ return ref MemoryMarshal.GetArrayDataReference(_poolArr!);
+ case HandleType.PrivateHeap:
+ return ref MemoryUtil.GetRef<T>(_memoryPtr);
+ default:
+ throw new InvalidOperationException("The handle is empty, and cannot capture a reference");
+ }
+ }
+
/// <summary>
/// Determines if the other handle represents the same memory block as the
/// current handle.
diff --git a/lib/Utils/src/Memory/VnString.cs b/lib/Utils/src/Memory/VnString.cs
index 8542688..c937ccc 100644
--- a/lib/Utils/src/Memory/VnString.cs
+++ b/lib/Utils/src/Memory/VnString.cs
@@ -44,7 +44,7 @@ namespace VNLib.Utils.Memory
[ImmutableObject(true)]
public sealed class VnString : VnDisposeable, IEquatable<VnString>, IEquatable<string>, IEquatable<char[]>, IComparable<VnString>, IComparable<string>
{
- private readonly MemoryHandle<char>? Handle;
+ private readonly IMemoryHandle<char>? Handle;
private readonly SubSequence<char> _stringSequence;
@@ -63,7 +63,7 @@ namespace VNLib.Utils.Memory
_stringSequence = sequence;
}
- private VnString(MemoryHandle<char> handle, nuint start, int length)
+ private VnString(IMemoryHandle<char> handle, nuint start, int length)
{
Handle = handle ?? throw new ArgumentNullException(nameof(handle));
//get sequence
@@ -517,14 +517,10 @@ namespace VNLib.Utils.Memory
/// </remarks>
/// <exception cref="ObjectDisposedException"></exception>
public int GetHashCode(StringComparison stringComparison) => string.GetHashCode(AsSpan(), stringComparison);
-
+
///<inheritdoc/>
- protected override void Free()
- {
- //Dispose the handle if we own it (null if we do not have the parent handle)
- Handle?.Dispose();
- }
-
+ protected override void Free() => Handle?.Dispose();
+
public static bool operator ==(VnString left, VnString right) => left is null ? right is not null : left.Equals(right, StringComparison.Ordinal);
public static bool operator !=(VnString left, VnString right) => !(left == right);
diff --git a/lib/Utils/src/Memory/Win32PrivateHeap.cs b/lib/Utils/src/Memory/Win32PrivateHeap.cs
index 41fe33a..42f0328 100644
--- a/lib/Utils/src/Memory/Win32PrivateHeap.cs
+++ b/lib/Utils/src/Memory/Win32PrivateHeap.cs
@@ -177,8 +177,7 @@ namespace VNLib.Utils.Memory
//validate the block on the current heap
result = HeapValidate(handle, HEAP_NO_FLAGS, block);
}
- return result;
-
+ return result;
}
/// <summary>
diff --git a/lib/Utils/src/Resources/ManagedLibrary.cs b/lib/Utils/src/Resources/ManagedLibrary.cs
index f9813a1..56835c7 100644
--- a/lib/Utils/src/Resources/ManagedLibrary.cs
+++ b/lib/Utils/src/Resources/ManagedLibrary.cs
@@ -28,6 +28,7 @@ using System.Linq;
using System.Threading;
using System.Reflection;
using System.Runtime.Loader;
+using System.Collections.Generic;
using System.Runtime.InteropServices;
using VNLib.Utils.IO;
@@ -76,11 +77,8 @@ namespace VNLib.Utils.Resources
_lazyAssembly = new(LoadAssembly, LazyThreadSafetyMode.PublicationOnly);
}
- private Assembly LoadAssembly()
- {
- //Load the assembly into the parent context
- return _loadContext.LoadFromAssemblyPath(AssemblyPath);
- }
+ //Load the assembly into the parent context
+ private Assembly LoadAssembly() => _loadContext.LoadFromAssemblyPath(AssemblyPath);
/// <summary>
/// Raised when the load context that owns this assembly
@@ -136,18 +134,50 @@ namespace VNLib.Utils.Resources
/// <exception cref="EntryPointNotFoundException"></exception>
public T LoadTypeFromAssembly<T>()
{
- Type resourceType = typeof(T);
-
//See if the type is exported
- Type exp = (from type in Assembly.GetExportedTypes()
- where resourceType.IsAssignableFrom(type)
- select type)
- .FirstOrDefault()
- ?? throw new EntryPointNotFoundException($"Imported assembly does not export desired type {resourceType.FullName}");
+ Type exp = TryGetExportedType<T>() ?? throw new EntryPointNotFoundException($"Imported assembly does not export desired type {typeof(T).FullName}");
//Create instance
return (T)Activator.CreateInstance(exp)!;
- }
+ }
+
+ /// <summary>
+ /// Gets the type exported from the current assembly that is
+ /// assignable to the desired type.
+ /// </summary>
+ /// <typeparam name="T">The desired base type to get the exported type of</typeparam>
+ /// <returns>The exported type that matches the desired type from the current assembly</returns>
+ public Type? TryGetExportedType<T>() => TryGetExportedType(typeof(T));
+
+ /// <summary>
+ /// Gets the type exported from the current assembly that is
+ /// assignable to the desired type.
+ /// </summary>
+ /// <param name="resourceType">The desired base type to get the exported type of</param>
+ /// <returns>The exported type that matches the desired type from the current assembly</returns>
+ public Type? TryGetExportedType(Type resourceType) => TryGetAllMatchingTypes(resourceType).FirstOrDefault();
+
+ /// <summary>
+ /// Gets all exported types from the current assembly that are
+ /// assignable to the desired type.
+ /// </summary>
+ /// <typeparam name="T">The desired resource type</typeparam>
+ /// <returns>An enumeration of acceptable types</returns>
+ public IEnumerable<Type> TryGetAllMatchingTypes<T>() => TryGetAllMatchingTypes(typeof(T));
+
+ /// <summary>
+ /// Gets all exported types from the current assembly that are
+ /// assignable to the desired type.
+ /// </summary>
+ /// <param name="resourceType">The desired resource type</param>
+ /// <returns>An enumeration of acceptable types</returns>
+ public IEnumerable<Type> TryGetAllMatchingTypes(Type resourceType)
+ {
+ //try to get all exported types that match the desired type
+ return from type in Assembly.GetExportedTypes()
+ where resourceType.IsAssignableFrom(type)
+ select type;
+ }
/// <summary>
/// Creates a new loader for the desired assembly. The assembly and its dependencies
@@ -171,5 +201,87 @@ namespace VNLib.Utils.Resources
FileInfo fi = new(assemblyName);
return new(fi.FullName, loadContext);
}
+
+ /// <summary>
+ /// A helper method that will attempt to get a named method of the desired
+ /// delegate type from the specified object.
+ /// </summary>
+ /// <typeparam name="TDelegate">The method delegate that matches the signature of the desired method</typeparam>
+ /// <param name="obj">The object to discover and bind the found method to</param>
+ /// <param name="methodName">The name of the method to capture</param>
+ /// <param name="flags">The method binding flags</param>
+ /// <returns>The namaed method delegate for the object type, or null if the method was not found</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static TDelegate? TryGetMethod<TDelegate>(
+ object obj,
+ string methodName,
+ BindingFlags flags = BindingFlags.Public
+ ) where TDelegate : Delegate
+ {
+ _ = obj ?? throw new ArgumentNullException(nameof(obj));
+ return TryGetMethodInternal<TDelegate>(obj.GetType(), methodName, obj, flags | BindingFlags.Instance);
+ }
+
+ /// <summary>
+ /// A helper method that will attempt to get a named method of the desired
+ /// delegate type from the specified object.
+ /// </summary>
+ /// <typeparam name="TDelegate">The method delegate that matches the signature of the desired method</typeparam>
+ /// <param name="obj">The object to discover and bind the found method to</param>
+ /// <param name="methodName">The name of the method to capture</param>
+ /// <param name="flags">The method binding flags</param>
+ /// <returns>The namaed method delegate for the object type or an exception if not found</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="MissingMethodException"></exception>
+ public static TDelegate GetMethod<TDelegate>(
+ object obj,
+ string methodName,
+ BindingFlags flags = BindingFlags.Public
+ ) where TDelegate : Delegate
+ {
+ return TryGetMethod<TDelegate>(obj, methodName, flags)
+ ?? throw new MissingMethodException($"Type {obj.GetType().FullName} is missing desired method {methodName}");
+ }
+
+ /// <summary>
+ /// A helper method that will attempt to get a named static method of the desired
+ /// delegate type from the specified type.
+ /// </summary>
+ /// <typeparam name="TDelegate"></typeparam>
+ /// <param name="type">The type to get the static method for</param>
+ /// <param name="methodName">The name of the static method</param>
+ /// <param name="flags">The optional method binind flags</param>
+ /// <returns>The delegate if found <see langword="null"/> otherwise</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static TDelegate? TryGetStaticMethod<TDelegate>(Type type, string methodName, BindingFlags flags = BindingFlags.Public) where TDelegate : Delegate
+ => TryGetMethodInternal<TDelegate>(type, methodName, null, flags | BindingFlags.Static);
+
+ /// <summary>
+ /// A helper method that will attempt to get a named static method of the desired
+ /// delegate type from the specified type.
+ /// </summary>
+ /// <typeparam name="TDelegate">The delegate method type</typeparam>
+ /// <typeparam name="TType">The type to get the static method for</typeparam>
+ /// <param name="methodName">The name of the static method</param>
+ /// <param name="flags">The optional method binind flags</param>
+ /// <returns>The delegate if found <see langword="null"/> otherwise</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static TDelegate? TryGetStaticMethod<TDelegate, TType>(string methodName,BindingFlags flags = BindingFlags.Public) where TDelegate : Delegate
+ => TryGetMethodInternal<TDelegate>(typeof(TType), methodName, null, flags | BindingFlags.Static);
+
+ private static TDelegate? TryGetMethodInternal<TDelegate>(Type type, string methodName, object? target, BindingFlags flags) where TDelegate : Delegate
+ {
+ _ = type ?? throw new ArgumentNullException(nameof(type));
+
+ //Get delegate argument types incase of a method overload
+ Type[] delegateArgs = typeof(TDelegate).GetMethod("Invoke")!
+ .GetParameters()
+ .Select(static p => p.ParameterType)
+ .ToArray();
+
+ //get the named method and always add the static flag
+ return type.GetMethod(methodName, flags, delegateArgs)
+ ?.CreateDelegate<TDelegate>(target);
+ }
}
}