diff options
Diffstat (limited to 'lib/Utils/src')
22 files changed, 674 insertions, 470 deletions
diff --git a/lib/Utils/src/Extensions/IoExtensions.cs b/lib/Utils/src/Extensions/IoExtensions.cs index baba7dc..637cfab 100644 --- a/lib/Utils/src/Extensions/IoExtensions.cs +++ b/lib/Utils/src/Extensions/IoExtensions.cs @@ -33,7 +33,7 @@ using System.Runtime.CompilerServices; using VNLib.Utils.IO; using VNLib.Utils.Memory; -using static VNLib.Utils.Memory.Memory; +using static VNLib.Utils.Memory.MemoryUtil; namespace VNLib.Utils.Extensions { diff --git a/lib/Utils/src/Extensions/MemoryExtensions.cs b/lib/Utils/src/Extensions/MemoryExtensions.cs index c8ee5ef..17ad79d 100644 --- a/lib/Utils/src/Extensions/MemoryExtensions.cs +++ b/lib/Utils/src/Extensions/MemoryExtensions.cs @@ -124,7 +124,7 @@ namespace VNLib.Utils.Extensions } /// <summary> - /// Allows direct allocation of a fixed size <see cref="MemoryManager{T}"/> from a <see cref="PrivateHeap"/> instance + /// Allows direct allocation of a fixed size <see cref="MemoryManager{T}"/> from a <see cref="Win32PrivateHeap"/> instance /// of the specified number of elements /// </summary> /// <typeparam name="T">The unmanaged data type</typeparam> @@ -133,13 +133,39 @@ namespace VNLib.Utils.Extensions /// <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 + public static MemoryManager<T> DirectAlloc<T>(this IUnmangedHeap heap, nuint 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 + /// Gets the integer length (number of elements) of the <see cref="IMemoryHandle{T}"/> + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="handle"></param> + /// <returns> + /// The integer length of the handle, or throws <see cref="OverflowException"/> if + /// the platform is 64bit and the handle is larger than <see cref="int.MaxValue"/> + /// </returns> + /// <exception cref="OverflowException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int GetIntLength<T>(this IMemoryHandle<T> handle) => Convert.ToInt32(handle.Length); + + /// <summary> + /// Gets the integer length (number of elements) of the <see cref="UnsafeMemoryHandle{T}"/> + /// </summary> + /// <typeparam name="T">The unmanaged type</typeparam> + /// <param name="handle"></param> + /// <returns> + /// The integer length of the handle, or throws <see cref="OverflowException"/> if + /// the platform is 64bit and the handle is larger than <see cref="int.MaxValue"/> + /// </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; + + /// <summary> + /// Allows direct allocation of a fixed size <see cref="MemoryManager{T}"/> from a <see cref="Win32PrivateHeap"/> instance /// of the specified number of elements /// </summary> /// <typeparam name="T">The unmanaged data type</typeparam> @@ -147,11 +173,12 @@ 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="OverflowException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MemoryManager<T> DirectAlloc<T>(this IUnmangedHeap heap, long size, bool zero = false) where T : unmanaged + public static MemoryManager<T> DirectAlloc<T>(this IUnmangedHeap heap, nint size, bool zero = false) where T : unmanaged { - return size < 0 ? throw new ArgumentOutOfRangeException(nameof(size)) : DirectAlloc<T>(heap, (ulong)size, zero); + 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 @@ -161,11 +188,10 @@ namespace VNLib.Utils.Extensions /// <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 + public static unsafe T* GetOffset<T>(this MemoryHandle<T> memory, nint elements) where T : unmanaged { - return elements < 0 ? throw new ArgumentOutOfRangeException(nameof(elements)) : memory.GetOffset((ulong)elements); + 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 @@ -177,13 +203,13 @@ namespace VNLib.Utils.Extensions /// <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 + public static void Resize<T>(this MemoryHandle<T> memory, nint elements) where T : unmanaged { if (elements < 0) { throw new ArgumentOutOfRangeException(nameof(elements)); } - memory.Resize((ulong)elements); + memory.Resize((nuint)elements); } /// <summary> @@ -197,13 +223,13 @@ namespace VNLib.Utils.Extensions /// <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 + public static void ResizeIfSmaller<T>(this MemoryHandle<T> handle, nint count) where T : unmanaged { if(count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } - ResizeIfSmaller(handle, (ulong)count); + ResizeIfSmaller(handle, (nuint)count); } /// <summary> @@ -217,7 +243,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, ulong count) where T : unmanaged + public static void ResizeIfSmaller<T>(this MemoryHandle<T> handle, nuint count) where T : unmanaged { //Check handle size if(handle.Length < count) @@ -227,7 +253,7 @@ namespace VNLib.Utils.Extensions } } -#if TARGET_64_BIT + /// <summary> /// Gets a 64bit friendly span offset for the current <see cref="MemoryHandle{T}"/> /// </summary> @@ -238,9 +264,10 @@ 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, ulong offset, int size) where T: unmanaged + public static unsafe Span<T> GetOffsetSpan<T>(this MemoryHandle<T> block, nuint offset, int size) where T: unmanaged { _ = block ?? throw new ArgumentNullException(nameof(block)); + if(size < 0) { throw new ArgumentOutOfRangeException(nameof(size)); @@ -249,14 +276,13 @@ namespace VNLib.Utils.Extensions { 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)); + + //Check bounds + MemoryUtil.CheckBounds(block, offset, (nuint)size); + + //Get long offset from the destination handle + void* ofPtr = block.GetOffset(offset); + return new Span<T>(ofPtr, size); } /// <summary> /// Gets a 64bit friendly span offset for the current <see cref="MemoryHandle{T}"/> @@ -268,12 +294,11 @@ 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, long offset, int size) where T : unmanaged + public static unsafe Span<T> GetOffsetSpan<T>(this MemoryHandle<T> block, nint offset, int size) where T : unmanaged { - return offset < 0 ? throw new ArgumentOutOfRangeException(nameof(offset)) : block.GetOffsetSpan<T>((ulong)offset, size); + return offset >= 0 ? block.GetOffsetSpan((nuint)offset, size) : throw new ArgumentOutOfRangeException(nameof(offset)); } - /// <summary> /// Gets a <see cref="SubSequence{T}"/> window within the current block /// </summary> @@ -283,12 +308,8 @@ namespace VNLib.Utils.Extensions /// <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 - + public static SubSequence<T> GetSubSequence<T>(this MemoryHandle<T> block, nuint offset, int size) where T : unmanaged => new (block, offset, size); + /// <summary> /// Gets a <see cref="SubSequence{T}"/> window within the current block /// </summary> @@ -298,30 +319,12 @@ namespace VNLib.Utils.Extensions /// <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 + public static SubSequence<T> GetSubSequence<T>(this MemoryHandle<T> block, nint offset, int size) where T : unmanaged { - return new SubSequence<T>(block, offset, size); + return offset >= 0 ? new (block, (nuint)offset, size) : throw new ArgumentOutOfRangeException(nameof(offset)); } /// <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> @@ -346,10 +349,11 @@ namespace VNLib.Utils.Extensions 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); + IntPtr handle = heap.Alloc(1, (nuint)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 @@ -366,6 +370,7 @@ namespace VNLib.Utils.Extensions //Clear ref *structPtr = default; } + /// <summary> /// Allocates a block of unmanaged memory of the number of elements to store of an unmanged type /// </summary> @@ -378,17 +383,18 @@ namespace VNLib.Utils.Extensions /// <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 + public static unsafe MemoryHandle<T> Alloc<T>(this IUnmangedHeap heap, nuint elements, bool zero = false) where T : unmanaged { //Minimum of one element elements = Math.Max(elements, 1); //Get element size - uint elementSize = (uint)sizeof(T); + nuint elementSize = (nuint)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> @@ -401,10 +407,11 @@ namespace VNLib.Utils.Extensions /// <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 + public static MemoryHandle<T> Alloc<T>(this IUnmangedHeap heap, nint elements, bool zero = false) where T : unmanaged { - return elements < 0 ? throw new ArgumentOutOfRangeException(nameof(elements)) : Alloc<T>(heap, (ulong)elements, zero); + return elements >= 0 ? Alloc<T>(heap, (nuint)elements, zero) : throw new ArgumentOutOfRangeException(nameof(elements)); } + /// <summary> /// Allocates a buffer from the current heap and initialzies it by copying the initial data buffer /// </summary> @@ -417,8 +424,12 @@ namespace VNLib.Utils.Extensions [MethodImpl(MethodImplOptions.AggressiveInlining)] public static MemoryHandle<T> AllocAndCopy<T>(this IUnmangedHeap heap, ReadOnlySpan<T> initialData) where T:unmanaged { + //Aloc block MemoryHandle<T> handle = heap.Alloc<T>(initialData.Length); - Memory.Copy(initialData, handle, 0); + + //Copy initial data + MemoryUtil.Copy(initialData, handle, 0); + return handle; } @@ -435,12 +446,13 @@ namespace VNLib.Utils.Extensions public static void WriteAndResize<T>(this MemoryHandle<T> handle, ReadOnlySpan<T> input) where T: unmanaged { handle.Resize(input.Length); - Memory.Copy(input, handle, 0); + MemoryUtil.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 + /// returns the <see cref="UnsafeMemoryHandle{T}"/> that must be used cautiously. + /// If elements is less than 1 an empty handle is returned /// </summary> /// <typeparam name="T">The unamanged value type</typeparam> /// <param name="heap">The heap to allocate block from</param> @@ -455,14 +467,16 @@ namespace VNLib.Utils.Extensions { if (elements < 1) { - throw new ArgumentException("Elements must be greater than 0", nameof(elements)); + //Return an empty handle + return new UnsafeMemoryHandle<T>(); } - //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); + nuint elementSize = (nuint)sizeof(T); + + //If zero flag is set then specify zeroing memory (safe case because of the above check) + IntPtr block = heap.Alloc((nuint)elements, elementSize, zero); + //handle wrapper return new (heap, block, elements); } @@ -560,8 +574,6 @@ namespace VNLib.Utils.Extensions 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 @@ -644,11 +656,13 @@ namespace VNLib.Utils.Extensions /// Converts the buffer data to a <see cref="PrivateString"/> /// </summary> /// <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> /// <returns>A <see cref="Span{T}"/> over the modified data</returns> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span<T> AsSpan<T>(this ref ForwardOnlyWriter<T> buffer) => buffer.Buffer[..buffer.Written]; diff --git a/lib/Utils/src/Extensions/VnStringExtensions.cs b/lib/Utils/src/Extensions/VnStringExtensions.cs index 285fc4f..329c7a6 100644 --- a/lib/Utils/src/Extensions/VnStringExtensions.cs +++ b/lib/Utils/src/Extensions/VnStringExtensions.cs @@ -25,13 +25,17 @@ using System; using System.Linq; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using VNLib.Utils.Memory; +using System.Runtime.CompilerServices; + +#pragma warning disable CA1062 // Validate arguments of public methods namespace VNLib.Utils.Extensions { - [SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "<Pending>")] + /// <summary> + /// A collection of extensions for <see cref="VnString"/> + /// </summary> public static class VnStringExtensions { /// <summary> @@ -41,7 +45,9 @@ namespace VNLib.Utils.Extensions /// <param name="value">The value to find</param> /// <returns>True if the character exists within the instance</returns> /// <exception cref="ObjectDisposedException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Contains(this VnString str, char value) => str.AsSpan().Contains(value); + /// <summary> /// Derermines if the sequence exists within the instance /// </summary> @@ -50,9 +56,10 @@ namespace VNLib.Utils.Extensions /// <param name="stringComparison"></param> /// <returns>True if the character exists within the instance</returns> /// <exception cref="ObjectDisposedException"></exception> - + [MethodImpl(MethodImplOptions.AggressiveInlining)] 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> @@ -60,7 +67,9 @@ namespace VNLib.Utils.Extensions /// <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> + [MethodImpl(MethodImplOptions.AggressiveInlining)] 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> @@ -68,12 +77,9 @@ namespace VNLib.Utils.Extensions /// <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); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(this VnString str, ReadOnlySpan<char> search) => str.AsSpan().IndexOf(search); + /// <summary> /// Searches for the first occurrance of the specified sequence within the current instance /// </summary> @@ -82,12 +88,9 @@ namespace VNLib.Utils.Extensions /// <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); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int IndexOf(this VnString str, ReadOnlySpan<char> search, StringComparison comparison) => str.AsSpan().IndexOf(search, comparison); + /// <summary> /// Searches for the 0 based index of the first occurance of the search parameter after the start index. /// </summary> @@ -136,11 +139,13 @@ namespace VNLib.Utils.Extensions /// <returns>The trimmed <see cref="VnString"/> instance as a child of the original entry</returns> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="IndexOutOfRangeException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] 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 @@ -175,6 +180,7 @@ namespace VNLib.Utils.Extensions end--; } } + /// <summary> /// Allows for trimming whitespace characters in a realtive sequence from /// within a <see cref="VnString"/> buffer and returning the trimmed entry. @@ -184,13 +190,16 @@ namespace VNLib.Utils.Extensions /// <returns>The trimmed <see cref="VnString"/> instance as a child of the original entry</returns> /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="IndexOutOfRangeException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] 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> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static VnString RelativeTirm(this VnString data) => AbsoluteTrim(data, 0); /// <summary> @@ -349,19 +358,6 @@ namespace VNLib.Utils.Extensions } /// <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> @@ -370,14 +366,9 @@ namespace VNLib.Utils.Extensions /// <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); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VnString ToVnString(this MemoryHandle<char> handle, int length) => VnString.ConsumeHandle(handle, 0, length); + /// <summary> /// Converts the current handle to a <see cref="VnString"/>, a zero-alloc immutable wrapper /// for a memory handle @@ -386,10 +377,9 @@ namespace VNLib.Utils.Extensions /// <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); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VnString ToVnString(this MemoryHandle<char> handle) => VnString.ConsumeHandle(handle, 0, handle.GetIntLength()); + /// <summary> /// Converts the current handle to a <see cref="VnString"/>, a zero-alloc immutable wrapper /// for a memory handle @@ -398,21 +388,8 @@ namespace VNLib.Utils.Extensions /// <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); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static VnString ToVnString(this MemoryHandle<char> handle, nuint offset, int length) => VnString.ConsumeHandle(handle, offset, length); } }
\ No newline at end of file diff --git a/lib/Utils/src/IO/InMemoryTemplate.cs b/lib/Utils/src/IO/InMemoryTemplate.cs index ae8bf79..5a4a799 100644 --- a/lib/Utils/src/IO/InMemoryTemplate.cs +++ b/lib/Utils/src/IO/InMemoryTemplate.cs @@ -165,7 +165,7 @@ namespace VNLib.Utils.IO try { //Copy async - await fs.CopyToAsync(newBuf, 8192, Memory.Memory.Shared, cancellationToken); + await fs.CopyToAsync(newBuf, 8192, Memory.MemoryUtil.Shared, cancellationToken); } catch { diff --git a/lib/Utils/src/IO/VnMemoryStream.cs b/lib/Utils/src/IO/VnMemoryStream.cs index 4e8a2b3..e984cc1 100644 --- a/lib/Utils/src/IO/VnMemoryStream.cs +++ b/lib/Utils/src/IO/VnMemoryStream.cs @@ -28,24 +28,23 @@ using System.Threading; using System.Threading.Tasks; using System.Runtime.InteropServices; +using VNLib.Utils.Memory; 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; + private nint _position; + private nint _length; + private bool _isReadonly; + //Memory private readonly MemoryHandle<byte> _buffer; - private bool IsReadonly; //Default owns handle private readonly bool OwnsHandle = true; @@ -57,7 +56,7 @@ namespace VNLib.Utils.IO /// <param name="readOnly">Should the stream be readonly?</param> /// <exception cref="ArgumentException"></exception> /// <returns>A <see cref="VnMemoryStream"/> wrapper to access the handle data</returns> - public static VnMemoryStream ConsumeHandle(MemoryHandle<byte> handle, Int64 length, bool readOnly) + public static VnMemoryStream ConsumeHandle(MemoryHandle<byte> handle, nint length, bool readOnly) { handle.ThrowIfClosed(); return new VnMemoryStream(handle, length, readOnly, true); @@ -71,7 +70,7 @@ namespace VNLib.Utils.IO public static VnMemoryStream CreateReadonly(VnMemoryStream stream) { //Set the readonly flag - stream.IsReadonly = true; + stream._isReadonly = true; //Return the stream return stream; } @@ -79,11 +78,12 @@ namespace VNLib.Utils.IO /// <summary> /// Creates a new memory stream /// </summary> - public VnMemoryStream() : this(Memory.Shared) { } + public VnMemoryStream() : this(MemoryUtil.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> + /// <param name="heap"><see cref="Win32PrivateHeap"/> to allocate memory from</param> /// <exception cref="OutOfMemoryException"></exception> /// <exception cref="ArgumentNullException"></exception> public VnMemoryStream(IUnmangedHeap heap) : this(heap, 0, false) { } @@ -92,13 +92,13 @@ namespace VNLib.Utils.IO /// 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="heap"><see cref="Win32PrivateHeap"/> 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) + public VnMemoryStream(IUnmangedHeap heap, nuint bufferSize, bool zero) { _ = heap ?? throw new ArgumentNullException(nameof(heap)); _buffer = heap.Alloc<byte>(bufferSize, zero); @@ -107,7 +107,7 @@ namespace VNLib.Utils.IO /// <summary> /// Creates a new memory stream from the data provided /// </summary> - /// <param name="heap"><see cref="PrivateHeap"/> to allocate memory from</param> + /// <param name="heap"><see cref="Win32PrivateHeap"/> to allocate memory from</param> /// <param name="data">Initial data</param> public VnMemoryStream(IUnmangedHeap heap, ReadOnlySpan<byte> data) { @@ -116,8 +116,7 @@ namespace VNLib.Utils.IO _buffer = heap.AllocAndCopy(data); //Set length _length = data.Length; - //Position will default to 0 cuz its dotnet :P - return; + _position = 0; } /// <summary> @@ -127,18 +126,18 @@ 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, long length, bool readOnly, bool ownsHandle) + private VnMemoryStream(MemoryHandle<byte> buffer, nint length, bool readOnly, bool ownsHandle) { OwnsHandle = ownsHandle; _buffer = buffer; //Consume the handle _length = length; //Store length of the buffer - IsReadonly = readOnly; + _isReadonly = readOnly; } /// <summary> /// UNSAFE Number of bytes between position and length. Never negative /// </summary> - private long LenToPosDiff => Math.Max(_length - _position, 0); + private nint LenToPosDiff => Math.Max(_length - _position, 0); /// <summary> /// If the current stream is a readonly stream, creates an unsafe shallow copy for reading only. @@ -148,7 +147,7 @@ namespace VNLib.Utils.IO public VnMemoryStream GetReadonlyShallowCopy() { //Create a new readonly copy (stream does not own the handle) - return !IsReadonly + return !_isReadonly ? throw new NotSupportedException("This stream is not readonly. Cannot create shallow copy on a mutable stream") : new VnMemoryStream(_buffer, _length, true, false); } @@ -163,6 +162,10 @@ namespace VNLib.Utils.IO public override void CopyTo(Stream destination, int bufferSize) { _ = destination ?? throw new ArgumentNullException(nameof(destination)); + if(bufferSize < 1) + { + throw new ArgumentOutOfRangeException(nameof(bufferSize), "Buffer size must be greater than 0"); + } if (!destination.CanWrite) { @@ -250,7 +253,7 @@ namespace VNLib.Utils.IO /// True unless the stream is (or has been converted to) a readonly /// stream. /// </summary> - public override bool CanWrite => !IsReadonly; + public override bool CanWrite => !_isReadonly; ///<inheritdoc/> public override long Length => _length; ///<inheritdoc/> @@ -279,7 +282,7 @@ namespace VNLib.Utils.IO ///<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)); + public override int Read(byte[] buffer, int offset, int count) => Read(buffer.AsSpan(offset, count)); ///<inheritdoc/> public override int Read(Span<byte> buffer) { @@ -288,12 +291,14 @@ namespace VNLib.Utils.IO return 0; } //Number of bytes to read from memory buffer - int bytesToRead = checked((int)Math.Min(LenToPosDiff, buffer.Length)); + int bytesToRead = (int)Math.Min(LenToPosDiff, buffer.Length); + //Copy bytes to buffer - Memory.Copy(_buffer, _position, buffer, 0, bytesToRead); + MemoryUtil.Copy(_buffer, _position, buffer, 0, bytesToRead); + //Increment buffer position _position += bytesToRead; - //Bytestoread should never be larger than int.max because span length is an integer + return bytesToRead; } @@ -322,22 +327,27 @@ namespace VNLib.Utils.IO { throw new ArgumentOutOfRangeException(nameof(offset), "Offset cannot be less than 0"); } + if(offset > nint.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(offset), "Offset cannot be less than nint.MaxValue"); + } + + //safe cast to nint + nint _offset = (nint)offset; + 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; + //Length will never be greater than nint.Max so output will never exceed nint.max + return _position = Math.Min(_length, _offset); 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; + //Calc new seek position from current position + nint newPos = _position + _offset; + return _position = Math.Min(_length, 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; + //Calc new seek position from end of stream, should be len -1 so 0 can be specified from the end + nint realIndex = _length - (_offset - 1); + return _position = Math.Min(realIndex, 0); default: throw new ArgumentException("Stream operation is not supported on current stream"); } @@ -356,7 +366,7 @@ namespace VNLib.Utils.IO /// <exception cref="ArgumentOutOfRangeException"></exception> public override void SetLength(long value) { - if (IsReadonly) + if (_isReadonly) { throw new NotSupportedException("This stream is readonly"); } @@ -364,25 +374,33 @@ namespace VNLib.Utils.IO { throw new ArgumentOutOfRangeException(nameof(value), "Value cannot be less than 0"); } + if(value > nint.MaxValue) + { + throw new ArgumentOutOfRangeException(nameof(value), "Value cannot be greater than nint.MaxValue"); + } + + nint _value = (nint)value; + //Resize the buffer to the specified length - _buffer.Resize(value); + _buffer.Resize(_value); + //Set length - _length = value; - //Make sure the position is not pointing outside of the buffer + _length = _value; + + //Make sure the position is not pointing outside of the buffer after resize _position = Math.Min(_position, _length); - return; } ///<inheritdoc/> - public override void Write(byte[] buffer, int offset, int count) => Write(new ReadOnlySpan<byte>(buffer, offset, count)); + public override void Write(byte[] buffer, int offset, int count) => Write(buffer.AsSpan(offset, count)); ///<inheritdoc/> public override void Write(ReadOnlySpan<byte> buffer) { - if (IsReadonly) + if (_isReadonly) { throw new NotSupportedException("Write operation is not allowed on readonly stream!"); } //Calculate the new final position - long newPos = (_position + buffer.Length); + nint newPos = (_position + buffer.Length); //Determine if the buffer needs to be expanded if (buffer.Length > LenToPosDiff) { @@ -392,10 +410,9 @@ namespace VNLib.Utils.IO _length = newPos; } //Copy the input buffer to the internal buffer - Memory.Copy(buffer, _buffer, _position); + MemoryUtil.Copy(buffer, _buffer, (nuint)_position); //Update the position _position = newPos; - return; } ///<inheritdoc/> public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) @@ -423,16 +440,17 @@ namespace VNLib.Utils.IO /// </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 + //Alloc a new array of the size of the internal buffer, may be 64 bit large block byte[] data = new byte[_length]; - //Copy data from the internal buffer to the output buffer - _buffer.Span.CopyTo(data); + + //Copy the internal buffer to the new array + MemoryUtil.Copy(_buffer, 0, data, 0, (nuint)_length); + return data; - } + /// <summary> /// Returns a <see cref="ReadOnlySpan{T}"/> window over the data within the entire stream /// </summary> @@ -440,8 +458,10 @@ namespace VNLib.Utils.IO /// <exception cref="OverflowException"></exception> public ReadOnlySpan<byte> AsSpan() { - ReadOnlySpan<byte> output = _buffer.Span; - return output[..(int)_length]; + //Get 32bit length or throw + int len = Convert.ToInt32(_length); + //Get span with no offset + return _buffer.AsSpan(0, len); } /// <summary> diff --git a/lib/Utils/src/Memory/IMemoryHandle.cs b/lib/Utils/src/Memory/IMemoryHandle.cs index 75d1cce..cf19ce9 100644 --- a/lib/Utils/src/Memory/IMemoryHandle.cs +++ b/lib/Utils/src/Memory/IMemoryHandle.cs @@ -34,15 +34,9 @@ namespace VNLib.Utils.Memory 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; } + nuint Length { get; } /// <summary> /// Gets the internal block as a span diff --git a/lib/Utils/src/Memory/IUnmangedHeap.cs b/lib/Utils/src/Memory/IUnmangedHeap.cs index 5d8f4bf..94f34c8 100644 --- a/lib/Utils/src/Memory/IUnmangedHeap.cs +++ b/lib/Utils/src/Memory/IUnmangedHeap.cs @@ -38,7 +38,7 @@ namespace VNLib.Utils.Memory /// <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); + IntPtr Alloc(nuint elements, nuint size, bool zero); /// <summary> /// Resizes the allocated block of memory to the new size @@ -47,7 +47,7 @@ namespace VNLib.Utils.Memory /// <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); + void Resize(ref IntPtr block, nuint elements, nuint size, bool zero); /// <summary> /// Free's a previously allocated block of memory diff --git a/lib/Utils/src/Memory/MemoryHandle.cs b/lib/Utils/src/Memory/MemoryHandle.cs index a09edea..df2792b 100644 --- a/lib/Utils/src/Memory/MemoryHandle.cs +++ b/lib/Utils/src/Memory/MemoryHandle.cs @@ -34,7 +34,7 @@ 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 + /// Provides a wrapper for using umanged memory handles from an assigned <see cref="Win32PrivateHeap"/> for <see cref="UnmanagedType"/> types /// </summary> /// <remarks> /// Handles are configured to address blocks larger than 2GB, @@ -72,31 +72,21 @@ namespace VNLib.Utils.Memory get { this.ThrowIfClosed(); - return _length == 0 ? Span<T>.Empty : new Span<T>(Base, IntLength); + int len = Convert.ToInt32(_length); + return _length == 0 ? Span<T>.Empty : new Span<T>(Base, len); } } private readonly bool ZeroMemory; private readonly IUnmangedHeap Heap; - private ulong _length; + private nuint _length; - /// <summary> - /// Number of elements allocated to the current instance - /// </summary> - public ulong Length + ///<inheritdoc/> + public nuint 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 @@ -106,7 +96,7 @@ namespace VNLib.Utils.Memory { //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)); + get => MemoryUtil.ByteCount<T>(_length); } /// <summary> @@ -116,7 +106,7 @@ namespace VNLib.Utils.Memory /// <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) + internal MemoryHandle(IUnmangedHeap heap, IntPtr initial, nuint elements, bool zero) : base(true) { //Set element size (always allocate at least 1 object) _length = elements; @@ -133,7 +123,7 @@ namespace VNLib.Utils.Memory /// <exception cref="OverflowException"></exception> /// <exception cref="OutOfMemoryException"></exception> /// <exception cref="ObjectDisposedException"></exception> - public unsafe void Resize(ulong elements) + public unsafe void Resize(nuint elements) { this.ThrowIfClosed(); //Update size (should never be less than inital size) @@ -141,7 +131,7 @@ namespace VNLib.Utils.Memory //Re-alloc (Zero if required) try { - Heap.Resize(ref handle, Length, (ulong)sizeof(T), ZeroMemory); + Heap.Resize(ref handle, Length, (nuint)sizeof(T), ZeroMemory); } //Catch the disposed exception so we can invalidate the current ptr catch (ObjectDisposedException) @@ -161,7 +151,7 @@ namespace VNLib.Utils.Memory /// <exception cref="ArgumentOutOfRangeException"></exception> /// <returns><typeparamref name="T"/> pointer to the memory offset specified</returns> [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe T* GetOffset(ulong elements) + public unsafe T* GetOffset(nuint elements) { if (elements >= _length) { @@ -176,10 +166,14 @@ namespace VNLib.Utils.Memory ///<inheritdoc/> ///<exception cref="ObjectDisposedException"></exception> ///<exception cref="ArgumentOutOfRangeException"></exception> + ///<remarks> + ///Calling this method increments the handle's referrence count. + ///Disposing the returned handle decrements the handle count. + ///</remarks> public unsafe MemoryHandle Pin(int elementIndex) { //Get ptr and guard checks before adding the referrence - T* ptr = GetOffset((ulong)elementIndex); + T* ptr = GetOffset((nuint)elementIndex); bool addRef = false; //use the pinned field as success val @@ -198,15 +192,12 @@ namespace VNLib.Utils.Memory 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. diff --git a/lib/Utils/src/Memory/Memory.cs b/lib/Utils/src/Memory/MemoryUtil.cs index e04c386..410db6b 100644 --- a/lib/Utils/src/Memory/Memory.cs +++ b/lib/Utils/src/Memory/MemoryUtil.cs @@ -23,7 +23,6 @@ */ using System; -using System.IO; using System.Buffers; using System.Security; using System.Threading; @@ -39,7 +38,7 @@ namespace VNLib.Utils.Memory /// </summary> [SecurityCritical] [ComVisible(false)] - public unsafe static class Memory + public unsafe static class MemoryUtil { public const string SHARED_HEAP_TYPE_ENV= "VNLIB_SHARED_HEAP_TYPE"; public const string SHARED_HEAP_INTIAL_SIZE_ENV = "VNLIB_SHARED_HEAP_SIZE"; @@ -47,7 +46,7 @@ namespace VNLib.Utils.Memory /// <summary> /// Initial shared heap size (bytes) /// </summary> - public const ulong SHARED_HEAP_INIT_SIZE = 20971520; + public const nuint SHARED_HEAP_INIT_SIZE = 20971520; public const int MAX_BUF_SIZE = 2097152; public const int MIN_BUF_SIZE = 16000; @@ -68,14 +67,18 @@ namespace VNLib.Utils.Memory /// </remarks> public static IUnmangedHeap Shared => _sharedHeap.Value; - private static readonly Lazy<IUnmangedHeap> _sharedHeap; + + private static readonly Lazy<IUnmangedHeap> _sharedHeap = InitHeapInternal(); - static Memory() + //Avoiding statit initializer + private static Lazy<IUnmangedHeap> InitHeapInternal() { - _sharedHeap = new Lazy<IUnmangedHeap>(() => InitHeapInternal(true), LazyThreadSafetyMode.PublicationOnly); + Lazy<IUnmangedHeap> heap = new (() => InitHeapInternal(true), LazyThreadSafetyMode.PublicationOnly); //Cleanup the heap on process exit AppDomain.CurrentDomain.DomainUnload += DomainUnloaded; + return heap; } + private static void DomainUnloaded(object? sender, EventArgs e) { @@ -99,11 +102,11 @@ namespace VNLib.Utils.Memory { bool IsWindows = OperatingSystem.IsWindows(); //Get environment varable - string heapType = Environment.GetEnvironmentVariable(SHARED_HEAP_TYPE_ENV); + string? heapType = Environment.GetEnvironmentVariable(SHARED_HEAP_TYPE_ENV); //Get inital size - string sharedSize = Environment.GetEnvironmentVariable(SHARED_HEAP_INTIAL_SIZE_ENV); + 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)) + if (!nuint.TryParse(sharedSize, out nuint defaultSize)) { defaultSize = SHARED_HEAP_INIT_SIZE; } @@ -115,12 +118,12 @@ namespace VNLib.Utils.Memory { throw new PlatformNotSupportedException("Win32 private heaps are not supported on non-windows platforms"); } - return PrivateHeap.Create(defaultSize); + return Win32PrivateHeap.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(); + return IsWindows ? Win32PrivateHeap.Create(defaultSize) : new ProcessHeap(); } } @@ -130,6 +133,7 @@ namespace VNLib.Utils.Memory 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> @@ -150,6 +154,7 @@ namespace VNLib.Utils.Memory } } } + /// <summary> /// Zeros a block of memory of umanged type. If Windows is detected at runtime, calls RtlSecureZeroMemory Win32 function /// </summary> @@ -175,12 +180,15 @@ namespace VNLib.Utils.Memory /// </summary> /// <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); + /// <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> + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void InitializeBlock<T>(Memory<T> block) where T : unmanaged => UnsafeZeroMemory<T>(block); /// <summary> @@ -195,6 +203,7 @@ namespace VNLib.Utils.Memory //Zero block Unsafe.InitBlock(block.ToPointer(), 0, (uint)size); } + /// <summary> /// Zeroes a block of memory pointing to the structure /// </summary> @@ -207,6 +216,7 @@ namespace VNLib.Utils.Memory //Zero block Unsafe.InitBlock(structPtr, 0, (uint)size); } + /// <summary> /// Zeroes a block of memory pointing to the structure /// </summary> @@ -223,6 +233,7 @@ namespace VNLib.Utils.Memory #endregion #region Copy + /// <summary> /// Copies data from source memory to destination memory of an umanged data type /// </summary> @@ -231,24 +242,25 @@ 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, Int64 destOffset) where T : unmanaged + public static void Copy<T>(ReadOnlySpan<T> source, MemoryHandle<T> dest, nuint destOffset) where T : unmanaged { - if (source.IsEmpty) - { - return; - } - if (dest.Length < (ulong)(destOffset + source.Length)) + if (dest is null) { - throw new ArgumentException("Source data is larger than the dest data block", nameof(source)); + throw new ArgumentNullException(nameof(dest)); } - //Get long offset from the destination handle - T* offset = dest.GetOffset(destOffset); - fixed(void* src = &MemoryMarshal.GetReference(source)) + + if (source.IsEmpty) { - int byteCount = checked(source.Length * sizeof(T)); - Unsafe.CopyBlock(offset, src, (uint)byteCount); + return; } + + //Get long offset from the destination handle (also checks bounds) + Span<T> dst = dest.GetOffsetSpan(destOffset, source.Length); + + //Copy data + source.CopyTo(dst); } + /// <summary> /// Copies data from source memory to destination memory of an umanged data type /// </summary> @@ -257,24 +269,25 @@ 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, Int64 destOffset) where T : unmanaged + public static void Copy<T>(ReadOnlyMemory<T> source, MemoryHandle<T> dest, nuint destOffset) where T : unmanaged { - if (source.IsEmpty) + if (dest is null) { - return; + throw new ArgumentNullException(nameof(dest)); } - if (dest.Length < (ulong)(destOffset + source.Length)) + + if (source.IsEmpty) { - throw new ArgumentException("Dest constraints are larger than the dest data block", nameof(source)); + return; } - //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); + + //Get long offset from the destination handle (also checks bounds) + Span<T> dst = dest.GetOffsetSpan(destOffset, source.Length); + + //Copy data + source.Span.CopyTo(dst); } + /// <summary> /// Copies data from source memory to destination memory of an umanged data type /// </summary> @@ -285,31 +298,27 @@ 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, Int64 sourceOffset, Span<T> dest, int destOffset, int count) where T : unmanaged + public static void Copy<T>(MemoryHandle<T> source, nint sourceOffset, Span<T> dest, int destOffset, int count) where T : unmanaged { - if (count <= 0) + //Validate source/dest/count + ValidateArgs(sourceOffset, destOffset, count); + + //Check count last for debug reasons + 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); - } + + //Get offset span, also checks bounts + Span<T> src = source.GetOffsetSpan(sourceOffset, count); + + //slice the dest span + Span<T> dst = dest.Slice(destOffset, count); + + //Copy data + src.CopyTo(dst); } + /// <summary> /// Copies data from source memory to destination memory of an umanged data type /// </summary> @@ -319,77 +328,214 @@ namespace VNLib.Utils.Memory /// <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>(MemoryHandle<T> source, Int64 sourceOffset, Memory<T> dest, int destOffset, int count) where T : unmanaged + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Copy<T>(MemoryHandle<T> source, nint sourceOffset, Memory<T> dest, int destOffset, int count) where T : unmanaged { - if (count == 0) + //Call copy method with dest as span + Copy(source, sourceOffset, dest.Span, destOffset, count); + } + + private static void ValidateArgs(nint sourceOffset, nint destOffset, nint count) + { + if(sourceOffset < 0) { - return; + throw new ArgumentOutOfRangeException(nameof(sourceOffset), "Source offset must be a postive integer"); } - if (source.Length < (ulong)(sourceOffset + count)) + + if(destOffset < 0) { - throw new ArgumentException("Source constraints are larger than the source data block", nameof(count)); + throw new ArgumentOutOfRangeException(nameof(destOffset), "Destination offset must be a positive integer"); } - if(dest.Length < destOffset + count) + + if(count < 0) { - throw new ArgumentOutOfRangeException(nameof(destOffset), "Destination offset range cannot exceed the size of the destination buffer"); + throw new ArgumentOutOfRangeException(nameof(count), "Count parameter must be a postitive integer"); } - //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 + /// 32/64 bit large block copy /// </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) + /// <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="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 { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (dest is null) + { + throw new ArgumentNullException(nameof(dest)); + } + if (count == 0) { return; } - if (count < 0) + + //Check source bounds + CheckBounds(source, offset, count); + + //Check dest bounts + CheckBounds(dest, destOffset, count); + + +#if TARGET_64BIT + //Get the number of bytes to copy + nuint byteCount = ByteCount<T>(count); + + //Get memory handle from source + using MemoryHandle srcHandle = source.Pin(0); + + //get source offset + T* src = (T*)srcHandle.Pointer + offset; + + //pin array + fixed (T* dst = dest) + { + //Offset dest ptr + T* dstOffset = dst + destOffset; + + //Copy src to set + Buffer.MemoryCopy(src, dstOffset, byteCount, byteCount); + } +#else + //If 32bit its safe to use spans + + Span<T> src = source.Span.Slice((int)offset, (int)count); + Span<T> dst = dest.AsSpan((int)destOffset, (int)count); + //Copy + src.CopyTo(dst); +#endif + } + + #endregion + + #region Validation + + /// <summary> + /// Gets the size in bytes of the handle + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="handle">The handle to get the byte size of</param> + /// <returns>The number of bytes pointed to by the handle</returns> + /// <exception cref="ArgumentNullException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nuint ByteSize<T>(IMemoryHandle<T> handle) + { + _ = handle ?? throw new ArgumentNullException(nameof(handle)); + return checked(handle.Length * (nuint)Unsafe.SizeOf<T>()); + } + + /// <summary> + /// Gets the size in bytes of the handle + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="handle">The handle to get the byte size of</param> + /// <returns>The number of bytes pointed to by the handle</returns> + /// <exception cref="ArgumentNullException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static nuint ByteSize<T>(in UnsafeMemoryHandle<T> handle) where T : unmanaged => checked(handle.Length * (nuint)sizeof(T)); + + /// <summary> + /// Gets the byte multiple of the length parameter + /// </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 nuint ByteCount<T>(nuint elementCount) => checked(elementCount * (nuint)Unsafe.SizeOf<T>()); + /// <summary> + /// Gets the byte multiple of the length parameter + /// </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 uint ByteCount<T>(uint elementCount) => checked(elementCount * (uint)Unsafe.SizeOf<T>()); + + /// <summary> + /// Checks if the offset/count paramters for the given memory handle + /// point outside the block wrapped in the handle + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="handle">The handle to check bounds of</param> + /// <param name="offset">The base offset to add</param> + /// <param name="count">The number of bytes expected to be assigned or dereferrenced</param> + /// <exception cref="ArgumentOutOfRangeException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CheckBounds<T>(IMemoryHandle<T> handle, nuint offset, nuint count) + { + if (offset + count > handle.Length) { - throw new ArgumentException("Count must be a positive integer", nameof(count)); + throw new ArgumentException("The offset or count is outside of the range of the block of memory"); } - //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 + } + + /// <summary> + /// Checks if the offset/count paramters for the given block + /// point outside the block wrapped in the handle + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="block">The handle to check bounds of</param> + /// <param name="offset">The base offset to add</param> + /// <param name="count">The number of bytes expected to be assigned or dereferrenced</param> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CheckBounds<T>(ReadOnlySpan<T> block, int offset, int count) + { + //Call slice and discard to raise exception + _ = block.Slice(offset, count); + } + + /// <summary> + /// Checks if the offset/count paramters for the given block + /// point outside the block wrapped in the handle + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="block">The handle to check bounds of</param> + /// <param name="offset">The base offset to add</param> + /// <param name="count">The number of bytes expected to be assigned or dereferrenced</param> + /// <exception cref="ArgumentOutOfRangeException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CheckBounds<T>(Span<T> block, int offset, int count) + { + //Call slice and discard to raise exception + _ = block.Slice(offset, count); + } + + /// <summary> + /// Checks if the offset/count paramters for the given block + /// point outside the block bounds + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="block">The handle to check bounds of</param> + /// <param name="offset">The base offset to add</param> + /// <param name="count">The number of bytes expected to be assigned or dereferrenced</param> + /// <exception cref="ArgumentOutOfRangeException"></exception> + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CheckBounds<T>(T[] block, nuint offset, nuint count) + { + if (((nuint)block.LongLength - offset) <= count) { - //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); + throw new ArgumentException("The offset or count is outside of the range of the block of memory"); + } } + #endregion + #region alloc /// <summary> @@ -408,6 +554,7 @@ namespace VNLib.Utils.Memory { throw new ArgumentException("Number of elements must be a positive integer", nameof(elements)); } + if(elements > MAX_UNSAFE_POOL_SIZE || IsRpMallocLoaded) { // Alloc from heap diff --git a/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs b/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs index 1e85207..e73a26f 100644 --- a/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs +++ b/lib/Utils/src/Memory/PrivateBuffersMemoryPool.cs @@ -28,7 +28,7 @@ using System.Buffers; namespace VNLib.Utils.Memory { /// <summary> - /// Provides a <see cref="MemoryPool{T}"/> wrapper for using unmanged <see cref="PrivateHeap"/>s + /// Provides a <see cref="MemoryPool{T}"/> wrapper for using unmanged <see cref="Win32PrivateHeap"/>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 diff --git a/lib/Utils/src/Memory/PrivateStringManager.cs b/lib/Utils/src/Memory/PrivateStringManager.cs index 9ed8f5f..8f01e98 100644 --- a/lib/Utils/src/Memory/PrivateStringManager.cs +++ b/lib/Utils/src/Memory/PrivateStringManager.cs @@ -63,7 +63,7 @@ namespace VNLib.Utils.Memory //Clear the old value before setting the new one if (!string.IsNullOrEmpty(ProtectedElements[index])) { - Memory.UnsafeZeroMemory<char>(ProtectedElements[index]); + MemoryUtil.UnsafeZeroMemory<char>(ProtectedElements[index]); } //set new value ProtectedElements[index] = value; @@ -87,7 +87,7 @@ namespace VNLib.Utils.Memory if (!string.IsNullOrEmpty(ProtectedElements[i])) { //Zero the string memory - Memory.UnsafeZeroMemory<char>(ProtectedElements[i]); + MemoryUtil.UnsafeZeroMemory<char>(ProtectedElements[i]); //Set to null ProtectedElements[i] = null; } diff --git a/lib/Utils/src/Memory/ProcessHeap.cs b/lib/Utils/src/Memory/ProcessHeap.cs index 4f06d52..7afe4b1 100644 --- a/lib/Utils/src/Memory/ProcessHeap.cs +++ b/lib/Utils/src/Memory/ProcessHeap.cs @@ -48,20 +48,23 @@ namespace VNLib.Utils.Memory ///<inheritdoc/> ///<exception cref="OverflowException"></exception> ///<exception cref="OutOfMemoryException"></exception> - public IntPtr Alloc(ulong elements, ulong size, bool zero) + public IntPtr Alloc(nuint elements, nuint size, bool zero) { return zero - ? (IntPtr)NativeMemory.AllocZeroed((nuint)elements, (nuint)size) - : (IntPtr)NativeMemory.Alloc((nuint)elements, (nuint)size); + ? (IntPtr)NativeMemory.AllocZeroed(elements, size) + : (IntPtr)NativeMemory.Alloc(elements, 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() { @@ -69,14 +72,25 @@ namespace VNLib.Utils.Memory 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) + public void Resize(ref IntPtr block, nuint elements, nuint size, bool zero) { - nuint bytes = checked((nuint)(elements * size)); - IntPtr old = block; - block = (IntPtr)NativeMemory.Realloc(old.ToPointer(), bytes); + nuint bytes = checked(elements * size); + + //Alloc + void* newBlock = NativeMemory.Realloc(block.ToPointer(), bytes); + + //Check + if (newBlock == null) + { + throw new NativeMemoryOutOfMemoryException("Failed to resize the allocated block"); + } + + //Assign block ptr + block = (IntPtr)newBlock; } } } diff --git a/lib/Utils/src/Memory/RpMallocPrivateHeap.cs b/lib/Utils/src/Memory/RpMallocPrivateHeap.cs index 8ed79b6..f9b7db6 100644 --- a/lib/Utils/src/Memory/RpMallocPrivateHeap.cs +++ b/lib/Utils/src/Memory/RpMallocPrivateHeap.cs @@ -28,7 +28,6 @@ using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; -using size_t = System.UInt64; using LPVOID = System.IntPtr; using LPHEAPHANDLE = System.IntPtr; @@ -59,22 +58,22 @@ namespace VNLib.Utils.Memory 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); + static extern LPVOID rpmalloc_heap_alloc(LPHEAPHANDLE heap, nuint size); [DllImport(DLL_NAME, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc_heap_aligned_alloc(LPHEAPHANDLE heap, size_t alignment, size_t size); + static extern LPVOID rpmalloc_heap_aligned_alloc(LPHEAPHANDLE heap, nuint alignment, nuint size); [DllImport(DLL_NAME, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc_heap_calloc(LPHEAPHANDLE heap, size_t num, size_t size); + static extern LPVOID rpmalloc_heap_calloc(LPHEAPHANDLE heap, nuint num, nuint 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); + static extern LPVOID rpmalloc_heap_aligned_calloc(LPHEAPHANDLE heap, nuint alignment, nuint num, nuint size); [DllImport(DLL_NAME, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc_heap_realloc(LPHEAPHANDLE heap, LPVOID ptr, size_t size, nuint flags); + static extern LPVOID rpmalloc_heap_realloc(LPHEAPHANDLE heap, LPVOID ptr, nuint 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); + static extern LPVOID rpmalloc_heap_aligned_realloc(LPHEAPHANDLE heap, LPVOID ptr, nuint alignment, nuint size, nuint flags); [DllImport(DLL_NAME, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] static extern void rpmalloc_heap_free(LPHEAPHANDLE heap, LPVOID ptr); @@ -96,13 +95,13 @@ namespace VNLib.Utils.Memory static extern void rpmalloc_thread_finalize(int release_caches); [DllImport(DLL_NAME, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpmalloc(size_t size); + static extern LPVOID rpmalloc(nuint size); [DllImport(DLL_NAME, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rpcalloc(size_t num, size_t size); + static extern LPVOID rpcalloc(nuint num, nuint size); [DllImport(DLL_NAME, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] - static extern LPVOID rprealloc(LPVOID ptr, size_t size); + static extern LPVOID rprealloc(LPVOID ptr, nuint size); [DllImport(DLL_NAME, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] static extern void rpfree(LPVOID ptr); @@ -111,9 +110,9 @@ namespace VNLib.Utils.Memory private sealed class RpMallocGlobalHeap : IUnmangedHeap { - IntPtr IUnmangedHeap.Alloc(ulong elements, ulong size, bool zero) + IntPtr IUnmangedHeap.Alloc(nuint elements, nuint size, bool zero) { - return RpMalloc(elements, (nuint)size, zero); + return RpMalloc(elements, size, zero); } //Global heap does not need to be disposed @@ -127,17 +126,13 @@ namespace VNLib.Utils.Memory return true; } - void IUnmangedHeap.Resize(ref IntPtr block, ulong elements, ulong size, bool zero) + void IUnmangedHeap.Resize(ref IntPtr block, nuint elements, nuint 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"); - } + IntPtr resize = RpRealloc(block, elements, size); + //assign ptr - block = resize; + block = resize != IntPtr.Zero ? resize : throw new NativeMemoryOutOfMemoryException("Failed to resize the block"); } } @@ -164,7 +159,7 @@ namespace VNLib.Utils.Memory /// <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) + public static LPVOID RpMalloc(nuint elements, nuint size, bool zero) { //See if the current thread has been initialized if (rpmalloc_is_thread_initialized() == 0) @@ -172,8 +167,10 @@ namespace VNLib.Utils.Memory //Initialize the current thread rpmalloc_thread_initialize(); } + //Alloc block LPVOID block; + if (zero) { block = rpcalloc(elements, size); @@ -181,7 +178,8 @@ namespace VNLib.Utils.Memory else { //Calculate the block size - ulong blockSize = checked(elements * size); + nuint blockSize = checked(elements * size); + block = rpmalloc(blockSize); } return block; @@ -209,14 +207,16 @@ namespace VNLib.Utils.Memory /// <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) + public static LPVOID RpRealloc(LPVOID block, nuint 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); + nuint blockSize = checked(elements * size); + return rprealloc(block, blockSize); } @@ -239,6 +239,7 @@ namespace VNLib.Utils.Memory Trace.WriteLine($"RPMalloc heap {handle:x} created"); #endif } + ///<inheritdoc/> protected override bool ReleaseHandle() { @@ -252,13 +253,15 @@ namespace VNLib.Utils.Memory //Release base return base.ReleaseHandle(); } + ///<inheritdoc/> [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected sealed override LPVOID AllocBlock(ulong elements, ulong size, bool zero) + protected sealed override LPVOID AllocBlock(nuint elements, nuint 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) @@ -267,13 +270,15 @@ namespace VNLib.Utils.Memory rpmalloc_heap_free(handle, block); return true; } + ///<inheritdoc/> [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected sealed override LPVOID ReAllocBlock(LPVOID block, ulong elements, ulong size, bool zero) + protected sealed override LPVOID ReAllocBlock(LPVOID block, nuint elements, nuint size, bool zero) { //Realloc return rpmalloc_heap_realloc(handle, block, checked(elements * size), 0); } + #endregion } } diff --git a/lib/Utils/src/Memory/SubSequence.cs b/lib/Utils/src/Memory/SubSequence.cs index 3800fb5..87e369b 100644 --- a/lib/Utils/src/Memory/SubSequence.cs +++ b/lib/Utils/src/Memory/SubSequence.cs @@ -35,10 +35,7 @@ namespace VNLib.Utils.Memory 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; } + private readonly nuint _offset; /// <summary> /// Creates a new <see cref="SubSequence{T}"/> to the handle to get a window of the block @@ -46,33 +43,23 @@ namespace VNLib.Utils.Memory /// <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 + public SubSequence(MemoryHandle<T> block, nuint offset, int size) { _offset = offset; Size = size >= 0 ? size : throw new ArgumentOutOfRangeException(nameof(size)); _handle = block ?? throw new ArgumentNullException(nameof(block)); } + /// <summary> + /// The number of elements in the current window + /// </summary> + public readonly int Size { get; } -#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 + public readonly Span<T> Span => Size > 0 ? _handle.GetOffsetSpan(_offset, Size) : Span<T>.Empty; /// <summary> /// Slices the current sequence into a smaller <see cref="SubSequence{T}"/> @@ -80,7 +67,7 @@ namespace VNLib.Utils.Memory /// <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); + public readonly SubSequence<T> Slice(nuint offset, int size) => new (_handle, checked(_offset + offset), size); /// <summary> /// Returns the signed 32-bit hashcode diff --git a/lib/Utils/src/Memory/SysBufferMemoryManager.cs b/lib/Utils/src/Memory/SysBufferMemoryManager.cs index 040467f..aca2543 100644 --- a/lib/Utils/src/Memory/SysBufferMemoryManager.cs +++ b/lib/Utils/src/Memory/SysBufferMemoryManager.cs @@ -40,7 +40,7 @@ namespace VNLib.Utils.Memory private readonly bool _ownsHandle; /// <summary> - /// Consumes an exisitng <see cref="MemoryHandle{T}"/> to provide <see cref="Memory"/> wrappers. + /// Consumes an exisitng <see cref="MemoryHandle{T}"/> to provide <see cref="MemoryUtil"/> wrappers. /// The handle should no longer be referrenced directly /// </summary> /// <param name="existingHandle">The existing handle to consume</param> @@ -52,12 +52,12 @@ namespace VNLib.Utils.Memory } /// <summary> - /// Allocates a fized size buffer from the specified unmanaged <see cref="PrivateHeap"/> + /// 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, ulong elements, bool zero) + public SysBufferMemoryManager(IUnmangedHeap heap, nuint elements, bool zero) { BackingMemory = heap.Alloc<T>(elements, zero); _ownsHandle = true; diff --git a/lib/Utils/src/Memory/UnmanagedHeapBase.cs b/lib/Utils/src/Memory/UnmanagedHeapBase.cs index 5c92aff..1f7dc7f 100644 --- a/lib/Utils/src/Memory/UnmanagedHeapBase.cs +++ b/lib/Utils/src/Memory/UnmanagedHeapBase.cs @@ -28,7 +28,6 @@ using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -using size_t = System.UInt64; using LPVOID = System.IntPtr; namespace VNLib.Utils.Memory @@ -43,6 +42,7 @@ namespace VNLib.Utils.Memory /// The heap synchronization handle /// </summary> protected readonly SemaphoreSlim HeapLock; + /// <summary> /// The global heap zero flag /// </summary> @@ -63,7 +63,7 @@ namespace VNLib.Utils.Memory ///<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) + public LPVOID Alloc(nuint elements, nuint size, bool zero) { //Force zero if global flag is set zero |= GlobalZero; @@ -93,6 +93,7 @@ namespace VNLib.Utils.Memory throw; } } + ///<inheritdoc/> ///<remarks>Decrements the handle count</remarks> public bool Free(ref LPVOID block) @@ -116,10 +117,11 @@ namespace VNLib.Utils.Memory 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) + public void Resize(ref LPVOID block, nuint elements, nuint size, bool zero) { //wait for lock HeapLock.Wait(); @@ -155,12 +157,14 @@ namespace VNLib.Utils.Memory /// <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); + protected abstract LPVOID AllocBlock(nuint elements, nuint 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> @@ -173,9 +177,11 @@ namespace VNLib.Utils.Memory /// 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); + protected abstract LPVOID ReAllocBlock(LPVOID block, nuint elements, nuint size, bool zero); + ///<inheritdoc/> public override int GetHashCode() => handle.GetHashCode(); + ///<inheritdoc/> public override bool Equals(object? obj) { diff --git a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs index b05ad40..72edb26 100644 --- a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs +++ b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs @@ -40,7 +40,7 @@ namespace VNLib.Utils.Memory [StructLayout(LayoutKind.Sequential)] public readonly struct UnsafeMemoryHandle<T> : IMemoryHandle<T>, IEquatable<UnsafeMemoryHandle<T>> where T : unmanaged { - private enum HandleType + private enum HandleType : byte { None, Pool, @@ -60,10 +60,12 @@ namespace VNLib.Utils.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _handleType == HandleType.Pool ? _poolArr.AsSpan(0, IntLength) : new (_memoryPtr.ToPointer(), IntLength); } - ///<inheritdoc/> + /// <summary> + /// Gets the integer number of elements of the block of memory pointed to by this handle + /// </summary> public readonly int IntLength => _length; ///<inheritdoc/> - public readonly ulong Length => (ulong)_length; + public readonly nuint Length => (nuint)_length; /// <summary> /// Creates an empty <see cref="UnsafeMemoryHandle{T}"/> @@ -153,11 +155,18 @@ namespace VNLib.Utils.Memory ///<inheritdoc/> public readonly unsafe MemoryHandle Pin(int elementIndex) { - //Guard + //guard empty handle + if (_handleType == HandleType.None) + { + throw new InvalidOperationException("The handle is empty, and cannot be pinned"); + } + + //Guard size if (elementIndex < 0 || elementIndex >= IntLength) { throw new ArgumentOutOfRangeException(nameof(elementIndex)); } + if (_handleType == HandleType.Pool) { diff --git a/lib/Utils/src/Memory/VnString.cs b/lib/Utils/src/Memory/VnString.cs index 7fa0c5a..8bb0bb6 100644 --- a/lib/Utils/src/Memory/VnString.cs +++ b/lib/Utils/src/Memory/VnString.cs @@ -52,6 +52,7 @@ namespace VNLib.Utils.Memory /// 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> @@ -62,14 +63,7 @@ namespace VNLib.Utils.Memory _stringSequence = sequence; } - private VnString( - MemoryHandle<char> handle, -#if TARGET_64_BIT - ulong start, -#else - int start, -#endif - int length) + private VnString(MemoryHandle<char> handle, nuint start, int length) { Handle = handle ?? throw new ArgumentNullException(nameof(handle)); //get sequence @@ -83,6 +77,7 @@ namespace VNLib.Utils.Memory { //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> @@ -90,13 +85,13 @@ namespace VNLib.Utils.Memory /// <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); + //Create new handle and copy incoming data to it + Handle = MemoryUtil.Shared.AllocAndCopy(data); + //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 @@ -122,7 +117,7 @@ namespace VNLib.Utils.Memory //Get the number of characters int numChars = encoding.GetCharCount(vnms.AsSpan()); //New handle - MemoryHandle<char> charBuffer = Memory.Shared.Alloc<char>(numChars); + MemoryHandle<char> charBuffer = MemoryUtil.Shared.Alloc<char>(numChars); try { //Write characters to character buffer @@ -141,9 +136,9 @@ namespace VNLib.Utils.Memory else { //Create a new char bufer that will expand dyanmically - MemoryHandle<char> charBuffer = Memory.Shared.Alloc<char>(bufferSize); + MemoryHandle<char> charBuffer = MemoryUtil.Shared.Alloc<char>(bufferSize); //Allocate a binary buffer - MemoryHandle<byte> binBuffer = Memory.Shared.Alloc<byte>(bufferSize); + MemoryHandle<byte> binBuffer = MemoryUtil.Shared.Alloc<byte>(bufferSize); try { int length = 0; @@ -194,6 +189,7 @@ namespace VNLib.Utils.Memory } } } + /// <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. @@ -203,27 +199,24 @@ namespace VNLib.Utils.Memory /// <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) + public static VnString ConsumeHandle(MemoryHandle<char> handle, nuint start, int length) { - if(length < 0) + if (handle is null) { - throw new ArgumentOutOfRangeException(nameof(length)); + throw new ArgumentNullException(nameof(handle)); } - if((uint)length > handle.Length) + + if (length < 0) { throw new ArgumentOutOfRangeException(nameof(length)); } + + //Check handle bounts + MemoryUtil.CheckBounds(handle, start, (nuint)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. @@ -354,10 +347,11 @@ namespace VNLib.Utils.Memory throw new ArgumentOutOfRangeException(nameof(count)); } //get sub-sequence slice for the current string - SubSequence<char> sub = _stringSequence.Slice((uint)start, count); + SubSequence<char> sub = _stringSequence.Slice((nuint)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. @@ -403,13 +397,12 @@ namespace VNLib.Utils.Memory /// </summary> /// <returns><see cref="string"/> representation of internal data</returns> /// <exception cref="ObjectDisposedException"></exception> - public override unsafe string ToString() + public override string ToString() { - //Check - Check(); //Create a new return AsSpan().ToString(); } + /// <summary> /// Gets the value of the character at the specified index /// </summary> @@ -474,7 +467,21 @@ namespace VNLib.Utils.Memory /// a character span etc /// </remarks> /// <exception cref="ObjectDisposedException"></exception> - public override int GetHashCode() => string.GetHashCode(AsSpan()); + public override int GetHashCode() => GetHashCode(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> + /// <param name="stringComparison">The string comperison mode</param> + /// <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 int GetHashCode(StringComparison stringComparison) => string.GetHashCode(AsSpan(), stringComparison); + ///<inheritdoc/> protected override void Free() { diff --git a/lib/Utils/src/Memory/VnTable.cs b/lib/Utils/src/Memory/VnTable.cs index 1d5c0a6..2c6ce74 100644 --- a/lib/Utils/src/Memory/VnTable.cs +++ b/lib/Utils/src/Memory/VnTable.cs @@ -35,33 +35,38 @@ namespace VNLib.Utils.Memory 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; } + public uint Rows { get; } + /// <summary> /// The nuber of columns in the table /// </summary> - public int Cols { get; } + public uint Cols { get; } + /// <summary> - /// Creates a new 2 dimensional table in unmanaged heap memory, using the <see cref="Memory.Shared"/> heap. + /// Creates a new 2 dimensional table in unmanaged heap memory, using the <see cref="MemoryUtil.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) { } + public VnTable(uint rows, uint cols) : this(MemoryUtil.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="heap"><see cref="Win32PrivateHeap"/> 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) + public VnTable(IUnmangedHeap heap, uint rows, uint cols) { if (rows < 0 || cols < 0) { @@ -71,19 +76,28 @@ namespace VNLib.Utils.Memory if (rows == 0 && cols == 0) { Empty = true; - return; } + else + { + _ = heap ?? throw new ArgumentNullException(nameof(heap)); - _ = heap ?? throw new ArgumentNullException(nameof(heap)); + this.Rows = rows; + this.Cols = cols; - this.Rows = rows; - this.Cols = cols; + ulong tableSize = checked((ulong) rows * (ulong) cols); - long tableSize = Math.BigMul(rows, cols); + if (tableSize > nuint.MaxValue) + { +#pragma warning disable CA2201 // Do not raise reserved exception types + throw new OutOfMemoryException("Table size is too large"); +#pragma warning restore CA2201 // Do not raise reserved exception types + } - //Alloc a buffer with zero memory enabled, with Rows * Cols number of elements - BufferHandle = heap.Alloc<T>(tableSize, true); + //Alloc a buffer with zero memory enabled, with Rows * Cols number of elements + BufferHandle = heap.Alloc<T>((nuint)tableSize, true); + } } + /// <summary> /// Gets the value of an item in the table at the given indexes /// </summary> @@ -93,7 +107,7 @@ namespace VNLib.Utils.Memory /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="InvalidOperationException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> - public T Get(int row, int col) + public T Get(uint row, uint col) { Check(); if (this.Empty) @@ -114,15 +128,18 @@ namespace VNLib.Utils.Memory } //Calculate the address in memory for the item //Calc row offset - long address = Cols * row; + ulong address = checked(row * this.Cols); + //Calc column offset address += col; + unsafe { //Get the value item - return *(BufferHandle!.GetOffset(address)); + return *(BufferHandle!.GetOffset((nuint)address)); } } + /// <summary> /// Sets the value of an item in the table at the given address /// </summary> @@ -133,7 +150,7 @@ namespace VNLib.Utils.Memory /// <exception cref="ObjectDisposedException"></exception> /// <exception cref="InvalidOperationException"></exception> /// <exception cref="ArgumentOutOfRangeException"></exception> - public void Set(int row, int col, T item) + public void Set(uint row, uint col, T item) { Check(); if (this.Empty) @@ -152,28 +169,34 @@ namespace VNLib.Utils.Memory { 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; + ulong address = checked(Cols * row); + //Calc column offset address += col; + //Set the value item unsafe { - *BufferHandle!.GetOffset(address) = item; + *BufferHandle!.GetOffset((nuint)address) = item; } } + /// <summary> - /// Equivalent to <see cref="VnTable{T}.Get(int, int)"/> and <see cref="VnTable{T}.Set(int, int, T)"/> + /// Equivalent to <see cref="VnTable{T}.Get(uint, uint)"/> and <see cref="VnTable{T}.Set(uint, uint, 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] + public T this[uint row, uint col] { get => Get(row, col); set => Set(row, col, value); } + /// <summary> /// Allows for direct addressing in the table. /// </summary> @@ -200,10 +223,11 @@ namespace VNLib.Utils.Memory *(BufferHandle!.GetOffset(index)) = value; } } + ///<inheritdoc/> protected override void Free() { - if (!this.Empty) + if (!Empty) { //Dispose the buffer BufferHandle!.Dispose(); diff --git a/lib/Utils/src/Memory/VnTempBuffer.cs b/lib/Utils/src/Memory/VnTempBuffer.cs index 7726fe1..1d8e42f 100644 --- a/lib/Utils/src/Memory/VnTempBuffer.cs +++ b/lib/Utils/src/Memory/VnTempBuffer.cs @@ -28,7 +28,6 @@ using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using VNLib.Utils.Extensions; -using System.Security.Cryptography; namespace VNLib.Utils.Memory { @@ -52,12 +51,7 @@ namespace VNLib.Utils.Memory /// <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; + public nuint Length => (nuint)Buffer.LongLength; ///<inheritdoc/> ///<exception cref="ObjectDisposedException"></exception> @@ -77,6 +71,7 @@ namespace VNLib.Utils.Memory /// <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> @@ -89,6 +84,7 @@ namespace VNLib.Utils.Memory Buffer = pool.Rent(minSize, zero); InitSize = minSize; } + /// <summary> /// Gets an offset wrapper around the current buffer /// </summary> @@ -101,6 +97,7 @@ namespace VNLib.Utils.Memory //Let arraysegment throw exceptions for checks return new ArraySegment<T>(Buffer, offset, count); } + ///<inheritdoc/> public T this[int index] { @@ -127,6 +124,7 @@ namespace VNLib.Utils.Memory Check(); return new Memory<T>(Buffer, 0, InitSize); } + /// <summary> /// Gets a memory structure around the internal buffer /// </summary> @@ -140,6 +138,7 @@ namespace VNLib.Utils.Memory Check(); return new Memory<T>(Buffer, start, count); } + /// <summary> /// Gets a memory structure around the internal buffer /// </summary> @@ -181,17 +180,20 @@ namespace VNLib.Utils.Memory unsafe MemoryHandle IPinnable.Pin(int elementIndex) { //Guard - if (elementIndex < 0 || elementIndex >= IntLength) + if (elementIndex < 0 || elementIndex >= Buffer.Length) { 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); } diff --git a/lib/Utils/src/Memory/PrivateHeap.cs b/lib/Utils/src/Memory/Win32PrivateHeap.cs index 5d97506..fe214f4 100644 --- a/lib/Utils/src/Memory/PrivateHeap.cs +++ b/lib/Utils/src/Memory/Win32PrivateHeap.cs @@ -28,7 +28,6 @@ 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 @@ -39,13 +38,13 @@ namespace VNLib.Utils.Memory ///</para> ///</summary> ///<remarks> - /// <see cref="PrivateHeap"/> implements <see cref="SafeHandle"/> and tracks allocated blocks by its + /// <see cref="Win32PrivateHeap"/> 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 + public sealed class Win32PrivateHeap : UnmanagedHeapBase { private const string KERNEL_DLL = "Kernel32"; @@ -59,10 +58,12 @@ namespace VNLib.Utils.Memory [DllImport(KERNEL_DLL, SetLastError = true, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - private static extern LPVOID HeapAlloc(IntPtr hHeap, DWORD flags, SIZE_T dwBytes); + private static extern LPVOID HeapAlloc(IntPtr hHeap, DWORD flags, nuint 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); + private static extern LPVOID HeapReAlloc(IntPtr hHeap, DWORD dwFlags, LPVOID lpMem, nuint dwBytes); + [DllImport(KERNEL_DLL, SetLastError = true, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [return: MarshalAs(UnmanagedType.Bool)] @@ -70,32 +71,35 @@ namespace VNLib.Utils.Memory [DllImport(KERNEL_DLL, SetLastError = true, ExactSpelling = true)] [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] - private static extern LPVOID HeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize); + private static extern LPVOID HeapCreate(DWORD flOptions, nuint dwInitialSize, nuint 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)] + [return: MarshalAs(UnmanagedType.Bool)] 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); + private static extern nuint HeapSize(IntPtr hHeap, DWORD flags, LPVOID lpMem); #endregion /// <summary> - /// Create a new <see cref="PrivateHeap"/> with the specified sizes and flags + /// Create a new <see cref="Win32PrivateHeap"/> 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) + public static Win32PrivateHeap Create(nuint initialSize, nuint 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"); @@ -112,16 +116,16 @@ namespace VNLib.Utils.Memory /// </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); + public static Win32PrivateHeap ConsumeExisting(IntPtr win32HeapHandle) => new (win32HeapHandle); - private PrivateHeap(IntPtr heapPtr) : base(false, true) => handle = heapPtr; + private Win32PrivateHeap(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); + public nuint 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 @@ -167,17 +171,20 @@ namespace VNLib.Utils.Memory return HeapDestroy(handle) && base.ReleaseHandle(); } ///<inheritdoc/> - protected override sealed LPVOID AllocBlock(ulong elements, ulong size, bool zero) + protected override sealed LPVOID AllocBlock(nuint elements, nuint size, bool zero) { - ulong bytes = checked(elements * size); + nuint 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) + protected override sealed LPVOID ReAllocBlock(LPVOID block, nuint elements, nuint size, bool zero) { - ulong bytes = checked(elements * size); + nuint bytes = checked(elements * size); + return HeapReAlloc(handle, zero ? HEAP_ZERO_MEMORY : HEAP_NO_FLAGS, block, bytes); } } diff --git a/lib/Utils/src/VnEncoding.cs b/lib/Utils/src/VnEncoding.cs index 94d8a1a..8359f8f 100644 --- a/lib/Utils/src/VnEncoding.cs +++ b/lib/Utils/src/VnEncoding.cs @@ -61,7 +61,7 @@ namespace VNLib.Utils //get number of bytes int byteCount = encoding.GetByteCount(data); //resize the handle to fit the data - handle = Memory.Memory.Shared.Alloc<byte>(byteCount); + handle = Memory.MemoryUtil.Shared.Alloc<byte>(byteCount); //encode int size = encoding.GetBytes(data, handle); //Consume the handle into a new vnmemstream and return it @@ -479,7 +479,7 @@ namespace VNLib.Utils //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)) + using (UnsafeMemoryHandle<char> charBuffer = Memory.MemoryUtil.UnsafeAlloc<char>(entropy)) { //Encode ERRNO encoded = TryToBase32Chars(binBuffer, charBuffer.Span); @@ -512,7 +512,7 @@ namespace VNLib.Utils //calc size of bin buffer int size = base32.Length; //Rent a bin buffer - using UnsafeMemoryHandle<byte> binBuffer = Memory.Memory.UnsafeAlloc<byte>(size); + using UnsafeMemoryHandle<byte> binBuffer = Memory.MemoryUtil.UnsafeAlloc<byte>(size); //Try to decode the data ERRNO decoded = TryFromBase32Chars(base32, binBuffer.Span); //Marshal back to a struct @@ -532,7 +532,7 @@ namespace VNLib.Utils return null; } //Buffer size of the base32 string will always be enough buffer space - using UnsafeMemoryHandle<byte> tempBuffer = Memory.Memory.UnsafeAlloc<byte>(base32.Length); + using UnsafeMemoryHandle<byte> tempBuffer = Memory.MemoryUtil.UnsafeAlloc<byte>(base32.Length); //Try to decode the data ERRNO decoded = TryFromBase32Chars(base32, tempBuffer.Span); @@ -903,7 +903,7 @@ namespace VNLib.Utils int decodedSize = encoding.GetByteCount(chars); //alloc buffer - using UnsafeMemoryHandle<byte> decodeHandle = Memory.Memory.UnsafeAlloc<byte>(decodedSize); + using UnsafeMemoryHandle<byte> decodeHandle = Memory.MemoryUtil.UnsafeAlloc<byte>(decodedSize); //Get the utf8 binary data int count = encoding.GetBytes(chars, decodeHandle); return Base64UrlDecode(decodeHandle.Span[..count], output); |