aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs4
-rw-r--r--lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs2
-rw-r--r--lib/Hashing.Portable/src/ManagedHash.cs8
-rw-r--r--lib/Net.Messaging.FBM/src/Client/FBMRequest.cs2
-rw-r--r--lib/Plugins.Essentials/src/Accounts/PasswordHashing.cs2
-rw-r--r--lib/Utils/src/Extensions/MemoryExtensions.cs44
-rw-r--r--lib/Utils/src/Memory/ArrayPoolBuffer.cs33
-rw-r--r--lib/Utils/src/Memory/IResizeableMemoryHandle.cs6
-rw-r--r--lib/Utils/src/Memory/IUnmangedHeap.cs6
-rw-r--r--lib/Utils/src/Memory/MemoryHandle.cs17
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.cs82
-rw-r--r--lib/Utils/src/Memory/MemoryUtilAlloc.cs31
-rw-r--r--lib/Utils/src/Memory/UnmanagedHeapBase.cs6
-rw-r--r--lib/Utils/src/Memory/UnsafeMemoryHandle.cs118
-rw-r--r--lib/Utils/src/Memory/VnString.cs198
-rw-r--r--lib/Utils/src/VnEncoding.cs109
-rw-r--r--lib/Utils/tests/Memory/MemoryUtilTests.cs6
17 files changed, 427 insertions, 247 deletions
diff --git a/lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs b/lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs
index 278359c..65704df 100644
--- a/lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs
+++ b/lib/Hashing.Portable/src/IdentityUtility/HashingExtensions.cs
@@ -163,7 +163,7 @@ namespace VNLib.Hashing.IdentityUtility
using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc(hashBufSize);
//compute hash
- if (!hmac.TryComputeHash(raw, buffer, out int hashBytesWritten))
+ if (!hmac.TryComputeHash(raw, buffer.Span, out int hashBytesWritten))
{
throw new InternalBufferTooSmallException("Hash buffer size was too small");
}
@@ -199,7 +199,7 @@ namespace VNLib.Hashing.IdentityUtility
using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc(buffSize, true);
//Encode data
- int converted = enc.GetBytes(data, buffer);
+ int converted = enc.GetBytes(data, buffer.Span);
//Try encrypt
return !alg.TryEncrypt(buffer.Span, output, padding, out int bytesWritten) ? ERRNO.E_FAIL : (ERRNO)bytesWritten;
diff --git a/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs b/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs
index c5409a8..158a1b8 100644
--- a/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs
+++ b/lib/Hashing.Portable/src/IdentityUtility/JsonWebToken.cs
@@ -69,7 +69,7 @@ namespace VNLib.Hashing.IdentityUtility
using MemoryHandle<byte> binBuffer = heap.Alloc<byte>(utf8Size, true);
//Decode to utf8
- utf8Size = textEncoding.GetBytes(urlEncJwtString, binBuffer);
+ utf8Size = textEncoding.GetBytes(urlEncJwtString, binBuffer.Span);
//Parse and return the jwt
return ParseRaw(binBuffer.Span[..utf8Size], heap);
diff --git a/lib/Hashing.Portable/src/ManagedHash.cs b/lib/Hashing.Portable/src/ManagedHash.cs
index 5a6aaba..43c2a7c 100644
--- a/lib/Hashing.Portable/src/ManagedHash.cs
+++ b/lib/Hashing.Portable/src/ManagedHash.cs
@@ -136,7 +136,7 @@ namespace VNLib.Hashing
using UnsafeMemoryHandle<byte> binbuf = MemoryUtil.UnsafeAlloc(byteCount, true);
//Encode data
- byteCount = CharEncoding.GetBytes(data, binbuf);
+ byteCount = CharEncoding.GetBytes(data, binbuf.Span);
//hash the buffer
return ComputeHash(binbuf.Span[..byteCount], buffer, type);
@@ -156,7 +156,7 @@ namespace VNLib.Hashing
//Alloc buffer
using UnsafeMemoryHandle<byte> binbuf = MemoryUtil.UnsafeAlloc(byteCount, true);
//Encode data
- byteCount = CharEncoding.GetBytes(data, binbuf);
+ byteCount = CharEncoding.GetBytes(data, binbuf.Span);
//hash the buffer
return ComputeHash(binbuf.Span[..byteCount], type);
}
@@ -289,7 +289,7 @@ namespace VNLib.Hashing
using UnsafeMemoryHandle<byte> binbuf = MemoryUtil.UnsafeAlloc(byteCount, true);
//Encode data
- byteCount = CharEncoding.GetBytes(data, binbuf);
+ byteCount = CharEncoding.GetBytes(data, binbuf.Span);
//hash the buffer
return ComputeHmac(key, binbuf.Span[..byteCount], output, type);
@@ -312,7 +312,7 @@ namespace VNLib.Hashing
using UnsafeMemoryHandle<byte> binbuf = MemoryUtil.UnsafeAlloc(byteCount, true);
//Encode data
- byteCount = CharEncoding.GetBytes(data, binbuf);
+ byteCount = CharEncoding.GetBytes(data, binbuf.Span);
//hash the buffer
return ComputeHmac(key, binbuf.Span[..byteCount], type);
diff --git a/lib/Net.Messaging.FBM/src/Client/FBMRequest.cs b/lib/Net.Messaging.FBM/src/Client/FBMRequest.cs
index 418a9ec..8432d27 100644
--- a/lib/Net.Messaging.FBM/src/Client/FBMRequest.cs
+++ b/lib/Net.Messaging.FBM/src/Client/FBMRequest.cs
@@ -244,7 +244,7 @@ namespace VNLib.Net.Messaging.FBM.Client
using UnsafeMemoryHandle<char> buffer = MemoryUtil.UnsafeAlloc<char>(charSize + 128);
- ERRNO count = Compile(buffer);
+ ERRNO count = Compile(buffer.Span);
return buffer.AsSpan(0, count).ToString();
}
diff --git a/lib/Plugins.Essentials/src/Accounts/PasswordHashing.cs b/lib/Plugins.Essentials/src/Accounts/PasswordHashing.cs
index b9fde20..509a8d0 100644
--- a/lib/Plugins.Essentials/src/Accounts/PasswordHashing.cs
+++ b/lib/Plugins.Essentials/src/Accounts/PasswordHashing.cs
@@ -103,7 +103,7 @@ namespace VNLib.Plugins.Essentials.Accounts
//Alloc heap buffer
using UnsafeMemoryHandle<byte> secretBuffer = MemoryUtil.UnsafeAlloc(_secret.BufferSize, true);
- return VerifyInternal(passHash, password, secretBuffer);
+ return VerifyInternal(passHash, password, secretBuffer.Span);
}
}
diff --git a/lib/Utils/src/Extensions/MemoryExtensions.cs b/lib/Utils/src/Extensions/MemoryExtensions.cs
index 9553578..7ff9936 100644
--- a/lib/Utils/src/Extensions/MemoryExtensions.cs
+++ b/lib/Utils/src/Extensions/MemoryExtensions.cs
@@ -48,7 +48,39 @@ namespace VNLib.Utils.Extensions
/// <param name="size">The minimum size array to allocate</param>
/// <param name="zero">Should elements from 0 to size be set to default(T)</param>
/// <returns>A new <see cref="OpenResourceHandle{T}"/> encapsulating the rented array</returns>
- public static UnsafeMemoryHandle<T> Lease<T>(this ArrayPool<T> pool, int size, bool zero = false) where T : unmanaged => new(pool, size, zero);
+ public static UnsafeMemoryHandle<T> UnsafeAlloc<T>(this ArrayPool<T> pool, int size, bool zero = false) where T : unmanaged
+ {
+ T[] array = pool.Rent(size);
+
+ if (zero)
+ {
+ MemoryUtil.InitializeBlock(array, (uint)size);
+ }
+
+ return new(pool, array, size);
+ }
+
+ /// <summary>
+ /// Rents a new array and stores it as a resource within an <see cref="OpenResourceHandle{T}"/> to return the
+ /// array when work is completed
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="pool"></param>
+ /// <param name="size">The minimum size array to allocate</param>
+ /// <param name="zero">Should elements from 0 to size be set to default(T)</param>
+ /// <returns>A new <see cref="OpenResourceHandle{T}"/> encapsulating the rented array</returns>
+ public static IMemoryHandle<T> SafeAlloc<T>(this ArrayPool<T> pool, int size, bool zero = false) where T : struct
+ {
+ T[] array = pool.Rent(size);
+
+ if (zero)
+ {
+ MemoryUtil.InitializeBlock(array, (uint)size);
+ }
+
+ //Use the array pool buffer wrapper to return the array to the pool when the handle is disposed
+ return new ArrayPoolBuffer<T>(pool, array, size);
+ }
/// <summary>
/// Retreives a buffer that is at least the reqested length, and clears the array from 0-size.
@@ -60,14 +92,14 @@ namespace VNLib.Utils.Extensions
/// <param name="zero">True if contents should be zeroed</param>
/// <returns>The zeroed array</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static T[] Rent<T>(this ArrayPool<T> pool, int size, bool zero)
+ public static T[] Rent<T>(this ArrayPool<T> pool, int size, bool zero)
{
//Rent the array
T[] arr = pool.Rent(size);
//If zero flag is set, zero only the used section
if (zero)
{
- arr.AsSpan().Clear();
+ Array.Clear(arr, 0, size);
}
return arr;
}
@@ -405,7 +437,7 @@ namespace VNLib.Utils.Extensions
/// <param name="heap"></param>
/// <param name="structRef">A reference to the structure</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe void StructFreeRef<T>(this IUnmangedHeap heap, ref T structRef) where T : unmanaged => MemoryUtil.StructFreeRef(heap, ref structRef);
+ public static void StructFreeRef<T>(this IUnmangedHeap heap, ref T structRef) where T : unmanaged => MemoryUtil.StructFreeRef(heap, ref structRef);
/// <summary>
/// Allocates a block of unmanaged memory of the number of elements to store of an unmanged type
@@ -519,7 +551,7 @@ namespace VNLib.Utils.Extensions
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe UnsafeMemoryHandle<T> UnsafeAlloc<T>(this IUnmangedHeap heap, int elements, bool zero = false) where T : unmanaged
+ public static UnsafeMemoryHandle<T> UnsafeAlloc<T>(this IUnmangedHeap heap, int elements, bool zero = false) where T : unmanaged
{
if (elements < 1)
{
@@ -528,7 +560,7 @@ namespace VNLib.Utils.Extensions
}
//Get element size
- nuint elementSize = (nuint)sizeof(T);
+ nuint elementSize = (nuint)Unsafe.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);
diff --git a/lib/Utils/src/Memory/ArrayPoolBuffer.cs b/lib/Utils/src/Memory/ArrayPoolBuffer.cs
index 92a2022..2f00e66 100644
--- a/lib/Utils/src/Memory/ArrayPoolBuffer.cs
+++ b/lib/Utils/src/Memory/ArrayPoolBuffer.cs
@@ -3,9 +3,9 @@
*
* Library: VNLib
* Package: VNLib.Utils
-* File: VnTempBuffer.cs
+* File: ArrayPoolBuffer.cs
*
-* VnTempBuffer.cs is part of VNLib.Utils which is part of the larger
+* ArrayPoolBuffer.cs is part of VNLib.Utils which is part of the larger
* VNLib collection of libraries and utilities.
*
* VNLib.Utils is free software: you can redistribute it and/or modify
@@ -35,7 +35,11 @@ namespace VNLib.Utils.Memory
/// A disposable temporary buffer from shared ArrayPool
/// </summary>
/// <typeparam name="T">Type of buffer to create</typeparam>
- public sealed class ArrayPoolBuffer<T> : VnDisposeable, IIndexable<int, T>, IMemoryHandle<T>, IMemoryOwner<T>
+ public sealed class ArrayPoolBuffer<T> :
+ VnDisposeable,
+ IIndexable<int, T>,
+ IMemoryHandle<T>,
+ IMemoryOwner<T>
{
private readonly ArrayPool<T> Pool;
@@ -92,7 +96,28 @@ namespace VNLib.Utils.Memory
Buffer = pool.Rent(minSize, zero);
InitSize = minSize;
}
-
+
+ /// <summary>
+ /// Initialzies a new <see cref="ArrayPoolBuffer{T}"/> from the specified rented array
+ /// that belongs to the supplied pool
+ /// </summary>
+ /// <param name="pool">The pool the array was rented from</param>
+ /// <param name="array">The rented array</param>
+ /// <param name="size">The size of the buffer around the array. May be smaller or exact size of the array</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public ArrayPoolBuffer(ArrayPool<T> pool, T[] array, int size)
+ {
+ Pool = pool ?? throw new ArgumentNullException(nameof(pool));
+ Buffer = array ?? throw new ArgumentNullException(nameof(array));
+
+ if (size < 0 || size > array.Length)
+ throw new ArgumentOutOfRangeException(nameof(size));
+
+ InitSize = size;
+ }
+
+
/// <summary>
/// Gets an offset wrapper around the current buffer
/// </summary>
diff --git a/lib/Utils/src/Memory/IResizeableMemoryHandle.cs b/lib/Utils/src/Memory/IResizeableMemoryHandle.cs
index f788b48..1d83196 100644
--- a/lib/Utils/src/Memory/IResizeableMemoryHandle.cs
+++ b/lib/Utils/src/Memory/IResizeableMemoryHandle.cs
@@ -42,11 +42,15 @@ namespace VNLib.Utils.Memory
/// Resizes a memory handle to a new number of elements.
/// </summary>
/// <remarks>
- /// Even if a handle is resizable resizing may not be supported for all types of handles.
+ /// Even if a handle is resizable resizing may not be supported for all types of handles. <br/>
+ /// Be careful not to resize handles that are pinned or have raw pointers/references floating.
/// </remarks>
/// <param name="elements">The new number of elements to resize the handle to</param>
/// <exception cref="OutOfMemoryException"></exception>
+ /// <exception cref="OverflowException"></exception>
/// <exception cref="NotSupportedException"></exception>
+ /// <exception cref="ObjectDisposedException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
void Resize(nuint elements);
}
} \ No newline at end of file
diff --git a/lib/Utils/src/Memory/IUnmangedHeap.cs b/lib/Utils/src/Memory/IUnmangedHeap.cs
index cb4b6ba..fdc0f9b 100644
--- a/lib/Utils/src/Memory/IUnmangedHeap.cs
+++ b/lib/Utils/src/Memory/IUnmangedHeap.cs
@@ -43,15 +43,19 @@ 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>
+ /// <exception cref="OutOfMemoryException"></exception>
IntPtr Alloc(nuint elements, nuint size, bool zero);
/// <summary>
- /// Resizes the allocated block of memory to the new size
+ /// Resizes the allocated block of memory to the new size.
+ /// May not be supported by all heaps.
/// </summary>
/// <param name="block">The block to resize</param>
/// <param name="elements">The new number of elements</param>
/// <param name="size">The size (in bytes) of the type</param>
/// <param name="zero">An optional parameter to zero the block of memory</param>
+ /// <exception cref="NotSupportedException"></exception>
+ /// <exception cref="OutOfMemoryException"></exception>
void Resize(ref IntPtr block, nuint elements, nuint size, bool zero);
/// <summary>
diff --git a/lib/Utils/src/Memory/MemoryHandle.cs b/lib/Utils/src/Memory/MemoryHandle.cs
index 4d2ff0c..f474abd 100644
--- a/lib/Utils/src/Memory/MemoryHandle.cs
+++ b/lib/Utils/src/Memory/MemoryHandle.cs
@@ -71,10 +71,7 @@ namespace VNLib.Utils.Memory
get => (IntPtr)GetOffset(0);
}
- /// <summary>
- /// Gets a span over the entire allocated block
- /// </summary>
- /// <returns>A <see cref="Span{T}"/> over the internal data</returns>
+ /// <inheritdoc/>
/// <exception cref="ObjectDisposedException"></exception>
/// <exception cref="OverflowException"></exception>
public unsafe Span<T> Span
@@ -145,10 +142,7 @@ namespace VNLib.Utils.Memory
Heap = null!;
}
- /// <summary>
- /// Resizes the current handle on the heap
- /// </summary>
- /// <param name="elements">Positive number of elemnts the current handle should referrence</param>
+ /// <inheritdoc/>
/// <exception cref="OverflowException"></exception>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
@@ -263,12 +257,5 @@ namespace VNLib.Utils.Memory
///<inheritdoc/>
public override int GetHashCode() => base.GetHashCode();
-
- ///<inheritdoc/>
- public static implicit operator Span<T>(MemoryHandle<T> handle)
- {
- //If the handle is invalid or closed return an empty span
- return handle.IsClosed || handle.IsInvalid || handle._length == 0 ? Span<T>.Empty : handle.Span;
- }
}
} \ No newline at end of file
diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs
index 52a9528..c149c85 100644
--- a/lib/Utils/src/Memory/MemoryUtil.cs
+++ b/lib/Utils/src/Memory/MemoryUtil.cs
@@ -243,12 +243,9 @@ namespace VNLib.Utils.Memory
}
uint byteSize = ByteCount<T>((uint)block.Length);
-
ref T r0 = ref MemoryMarshal.GetReference(block);
- ref byte byteRef = ref Unsafe.As<T, byte>(ref r0);
-
//Calls memset
- Unsafe.InitBlock(ref byteRef, 0, byteSize);
+ ZeroByRef(ref r0, byteSize);
}
/// <summary>
@@ -272,6 +269,19 @@ namespace VNLib.Utils.Memory
Unsafe.InitBlock(handle.Pointer, 0, byteSize);
}
+ [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
+ private static void ZeroByRef<T>(ref T src, uint elements)
+ {
+ Debug.Assert(Unsafe.IsNullRef(ref src) == false, "Null reference passed to ZeroByRef");
+
+ //Convert to bytes
+ uint byteSize = ByteCount<T>(elements);
+ ref byte byteRef = ref Unsafe.As<T, byte>(ref src);
+
+ //Call init block
+ Unsafe.InitBlock(ref byteRef, 0, byteSize);
+ }
+
/*
* Initializing a non-readonly span/memory as of .NET 6.0 is a reference
* reintpretation, essentially a pointer cast, so there is little/no cost
@@ -294,6 +304,39 @@ namespace VNLib.Utils.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InitializeBlock<T>(Memory<T> block) where T : struct => UnsafeZeroMemory<T>(block);
+ /// <summary>
+ /// Initializes the entire array with zeros
+ /// </summary>
+ /// <typeparam name="T">A structure type to initialize</typeparam>
+ /// <param name="array">The array to zero</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void InitializeBlock<T>(T[] array) where T : struct => InitializeBlock(array, (uint)array.Length);
+
+ /// <summary>
+ /// Initializes the array with zeros up to the specified count
+ /// </summary>
+ /// <typeparam name="T">A structure type to initialize</typeparam>
+ /// <param name="array">The array to zero</param>
+ /// <param name="count">The number of elements in the array to zero</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void InitializeBlock<T>(T[] array, uint count) where T: struct
+ {
+ if(array == null)
+ {
+ throw new ArgumentNullException(nameof(array));
+ }
+
+ //Check bounds
+ CheckBounds(array, 0, count);
+
+ //Get array data reference
+ ref T arrRef = ref MemoryMarshal.GetArrayDataReference(array);
+ ZeroByRef(ref arrRef, count);
+ }
/// <summary>
/// Zeroes a block of memory of the given unmanaged type
@@ -314,10 +357,7 @@ namespace VNLib.Utils.Memory
return;
}
- //To bytereference
- ref byte byteRef = ref Unsafe.As<T, byte>(ref block);
- //Zero block
- Unsafe.InitBlock(ref byteRef, 0, ByteCount<T>((uint)itemCount));
+ ZeroByRef(ref block, (uint)itemCount);
}
/// <summary>
@@ -1287,5 +1327,31 @@ namespace VNLib.Utils.Memory
//Multiply back to page sizes
return pages * SystemPageSize;
}
+
+ /// <summary>
+ /// Rounds the requested number of elements up to the nearest page
+ /// </summary>
+ /// <typeparam name="T">The unmanaged type</typeparam>
+ /// <param name="elements">The number of elements of size T to round</param>
+ /// <returns>The number of elements rounded to the nearest page in elements</returns>
+ public static nuint NearestPage<T>(nuint elements) where T : unmanaged
+ {
+ nuint elSize = (nuint)sizeof(T);
+ //Round to nearest page (in bytes)
+ return NearestPage(elements * elSize) / elSize;
+ }
+
+ /// <summary>
+ /// Rounds the requested number of elements up to the nearest page
+ /// </summary>
+ /// <typeparam name="T">The unmanaged type</typeparam>
+ /// <param name="elements">The number of elements of size T to round</param>
+ /// <returns>The number of elements rounded to the nearest page in elements</returns>
+ public static nint NearestPage<T>(nint elements) where T : unmanaged
+ {
+ nint elSize = sizeof(T);
+ //Round to nearest page (in bytes)
+ return NearestPage(elements * elSize) / elSize;
+ }
}
} \ No newline at end of file
diff --git a/lib/Utils/src/Memory/MemoryUtilAlloc.cs b/lib/Utils/src/Memory/MemoryUtilAlloc.cs
index bc0f35e..0f25682 100644
--- a/lib/Utils/src/Memory/MemoryUtilAlloc.cs
+++ b/lib/Utils/src/Memory/MemoryUtilAlloc.cs
@@ -52,6 +52,11 @@ namespace VNLib.Utils.Memory
throw new ArgumentException("Number of elements must be a positive integer", nameof(elements));
}
+ if (elements == 0)
+ {
+ return default;
+ }
+
/*
* We may allocate from the share heap only if the heap is not using locks
* or if the element size could cause performance issues because its too large
@@ -69,7 +74,8 @@ namespace VNLib.Utils.Memory
}
else
{
- return new(ArrayPool<T>.Shared, elements, zero);
+ //Rent the array from the pool
+ return ArrayPool<T>.Shared.UnsafeAlloc(elements, zero);
}
}
@@ -84,7 +90,6 @@ namespace VNLib.Utils.Memory
/// <returns>A handle to the block of memory</returns>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="OutOfMemoryException"></exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static UnsafeMemoryHandle<T> UnsafeAllocNearestPage<T>(int elements, bool zero = false) where T : unmanaged
{
if (elements < 0)
@@ -93,11 +98,7 @@ namespace VNLib.Utils.Memory
}
//Round to nearest page (in bytes)
- nint np = NearestPage(ByteCount<T>(elements));
-
- //Resize to element size
- np /= sizeof(T);
-
+ nint np = NearestPage<T>(elements);
return UnsafeAlloc<T>((int)np, zero);
}
@@ -155,11 +156,7 @@ namespace VNLib.Utils.Memory
}
//Round to nearest page (in bytes)
- nint np = NearestPage(ByteCount<T>(elements));
-
- //Resize to element size
- np /= sizeof(T);
-
+ nint np = NearestPage<T>(elements);
return SafeAlloc<T>((int)np, zero);
}
@@ -247,7 +244,6 @@ namespace VNLib.Utils.Memory
#region ByteOptimimzations
-
/// <summary>
/// Allocates a block of unmanaged, or pooled manaaged memory depending on
/// compilation flags and runtime unamanged allocators.
@@ -264,6 +260,11 @@ namespace VNLib.Utils.Memory
throw new ArgumentException("Number of elements must be a positive integer", nameof(elements));
}
+ if(elements == 0)
+ {
+ return default;
+ }
+
/*
* We may allocate from the share heap only if the heap is not using locks
* or if the element size could cause performance issues because its too large
@@ -281,7 +282,7 @@ namespace VNLib.Utils.Memory
}
else
{
- return new(ArrayPool<byte>.Shared, elements, zero);
+ return ArrayPool<byte>.Shared.UnsafeAlloc(elements, zero);
}
}
@@ -305,7 +306,6 @@ namespace VNLib.Utils.Memory
//Round to nearest page (in bytes)
nint np = NearestPage(elements);
-
return UnsafeAlloc((int)np, zero);
}
@@ -362,7 +362,6 @@ namespace VNLib.Utils.Memory
//Round to nearest page (in bytes)
nint np = NearestPage(elements);
-
return SafeAlloc((int)np, zero);
}
diff --git a/lib/Utils/src/Memory/UnmanagedHeapBase.cs b/lib/Utils/src/Memory/UnmanagedHeapBase.cs
index 0310582..daf360c 100644
--- a/lib/Utils/src/Memory/UnmanagedHeapBase.cs
+++ b/lib/Utils/src/Memory/UnmanagedHeapBase.cs
@@ -152,10 +152,14 @@ namespace VNLib.Utils.Memory
///<inheritdoc/>
///<exception cref="OverflowException"></exception>
- ///<exception cref="OutOfMemoryException"></exception>
///<exception cref="ObjectDisposedException"></exception>
public void Resize(ref LPVOID block, nuint elements, nuint size, bool zero)
{
+ if ((_flags & HeapCreation.SupportsRealloc) == 0)
+ {
+ throw new NotSupportedException("The underlying heap does not support block reallocation");
+ }
+
//Check for overflow for size
_ = checked(elements * size);
diff --git a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs
index 6a1fcc8..f79a094 100644
--- a/lib/Utils/src/Memory/UnsafeMemoryHandle.cs
+++ b/lib/Utils/src/Memory/UnsafeMemoryHandle.cs
@@ -24,6 +24,7 @@
using System;
using System.Buffers;
+using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Diagnostics.CodeAnalysis;
@@ -32,6 +33,7 @@ using VNLib.Utils.Extensions;
namespace VNLib.Utils.Memory
{
+
/// <summary>
/// Represents an unsafe handle to managed/unmanaged memory that should be used cautiously.
/// A referrence counter is not maintained.
@@ -59,55 +61,51 @@ namespace VNLib.Utils.Memory
public readonly Span<T> Span
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => _handleType == HandleType.Pool ? _poolArr.AsSpan(0, _length) : MemoryUtil.GetSpan<T>(_memoryPtr, _length);
+ get
+ {
+ return _handleType switch
+ {
+ HandleType.None => Span<T>.Empty,
+ HandleType.Pool => _poolArr!.AsSpan(0, _length),
+ HandleType.PrivateHeap => MemoryUtil.GetSpan<T>(_memoryPtr, _length),
+ _ => throw new InvalidOperationException("Invalid handle type"),
+ };
+ }
}
+
/// <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 nuint Length => (nuint)_length;
/// <summary>
- /// Creates an empty <see cref="UnsafeMemoryHandle{T}"/>
- /// </summary>
- public UnsafeMemoryHandle()
- {
- _pool = null;
- _heap = null;
- _poolArr = null;
- _memoryPtr = IntPtr.Zero;
- _handleType = HandleType.None;
- _length = 0;
- }
-
- /// <summary>
/// Inializes a new <see cref="UnsafeMemoryHandle{T}"/> using the specified
/// <see cref="ArrayPool{T}"/>
/// </summary>
/// <param name="elements">The number of elements to store</param>
- /// <param name="zero">Zero initial contents?</param>
+ /// <param name="array">The array reference to store/param>
/// <param name="pool">The explicit pool to alloc buffers from</param>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public UnsafeMemoryHandle(ArrayPool<T> pool, int elements, bool zero)
+ internal UnsafeMemoryHandle(ArrayPool<T> pool, T[] array, int elements)
{
if (elements < 0)
{
throw new ArgumentOutOfRangeException(nameof(elements));
}
- //Pool is required
+ //Pool and array is required
_pool = pool ?? throw new ArgumentNullException(nameof(pool));
- //Rent the array from the pool and hold referrence to it
- _poolArr = pool.Rent(elements, zero);
- //Cant store ref to array becase GC can move it
- _memoryPtr = IntPtr.Zero;
+ _poolArr = array ?? throw new ArgumentNullException(nameof(array));
//Set pool handle type
_handleType = HandleType.Pool;
//No heap being loaded
_heap = null;
_length = elements;
+ _memoryPtr = IntPtr.Zero;
}
/// <summary>
@@ -129,8 +127,13 @@ namespace VNLib.Utils.Memory
/// <summary>
/// Releases memory back to the pool or heap from which is was allocated.
+ /// <para>
+ /// After this method is called, this handle points to invalid memory
+ /// </para>
+ /// <para>
+ /// Warning: Double Free -> Do not call more than once. Using statment is encouraged
+ /// </para>
/// </summary>
- /// <remarks>After this method is called, this handle points to invalid memory</remarks>
public readonly void Dispose()
{
switch (_handleType)
@@ -145,42 +148,37 @@ namespace VNLib.Utils.Memory
{
IntPtr unalloc = _memoryPtr;
//Free the unmanaged handle
- _heap!.Free(ref unalloc);
+ bool unsafeFreed = _heap!.Free(ref unalloc);
+ Debug.Assert(unsafeFreed, "A previously allocated unsafe memhandle failed to free");
}
break;
}
- }
-
- ///<inheritdoc/>
- public readonly override int GetHashCode() => _handleType == HandleType.Pool ? _poolArr!.GetHashCode() : _memoryPtr.GetHashCode();
+ }
+
///<inheritdoc/>
public readonly MemoryHandle Pin(int elementIndex)
{
- //guard empty handle
- if (_handleType == HandleType.None)
- {
- throw new InvalidOperationException("The handle is empty, and cannot be pinned");
- }
-
//Guard size
if (elementIndex < 0 || elementIndex >= _length)
{
throw new ArgumentOutOfRangeException(nameof(elementIndex));
- }
-
- if (_handleType == HandleType.Pool)
- {
- return MemoryUtil.PinArrayAndGetHandle(_poolArr!, elementIndex);
}
- else
+
+ switch (_handleType)
{
- //Add an offset to the base address of the memory block
- int byteOffset = MemoryUtil.ByteCount<T>(elementIndex);
- IntPtr offset = IntPtr.Add(_memoryPtr, byteOffset);
- //Unmanaged memory is always pinned, so no need to pass this as IPinnable, since it will cause a box
- return MemoryUtil.GetMemoryHandleFromPointer(offset);
+ case HandleType.Pool:
+ return MemoryUtil.PinArrayAndGetHandle(_poolArr!, elementIndex);
+ case HandleType.PrivateHeap:
+ //Add an offset to the base address of the memory block
+ int byteOffset = MemoryUtil.ByteCount<T>(elementIndex);
+ IntPtr offset = IntPtr.Add(_memoryPtr, byteOffset);
+ //Unmanaged memory is always pinned, so no need to pass this as IPinnable, since it will cause a box
+ return MemoryUtil.GetMemoryHandleFromPointer(offset);
+ default:
+ throw new InvalidOperationException("The handle is empty, and cannot be pinned");
}
}
+
///<inheritdoc/>
public readonly void Unpin()
{
@@ -188,6 +186,7 @@ namespace VNLib.Utils.Memory
}
///<inheritdoc/>
+ ///<exception cref="InvalidOperationException"></exception>
public readonly ref T GetReference()
{
switch (_handleType)
@@ -201,18 +200,38 @@ namespace VNLib.Utils.Memory
}
}
+ ///<inheritdoc/>
+ public readonly override int GetHashCode()
+ {
+ //Get hashcode for the proper memory type
+ return _handleType switch
+ {
+ HandleType.Pool => _poolArr!.GetHashCode(),
+ HandleType.PrivateHeap => _memoryPtr.GetHashCode(),
+ _ => base.GetHashCode(),
+ };
+ }
+
/// <summary>
/// Determines if the other handle represents the same memory block as the
/// current handle.
/// </summary>
/// <param name="other">The other handle to test</param>
/// <returns>True if the other handle points to the same block of memory as the current handle</returns>
- public readonly bool Equals(UnsafeMemoryHandle<T> other)
+ public readonly bool Equals(in UnsafeMemoryHandle<T> other)
{
return _handleType == other._handleType && Length == other.Length && GetHashCode() == other.GetHashCode();
}
/// <summary>
+ /// Determines if the other handle represents the same memory block as the
+ /// current handle.
+ /// </summary>
+ /// <param name="other">The other handle to test</param>
+ /// <returns>True if the other handle points to the same block of memory as the current handle</returns>
+ public readonly bool Equals(UnsafeMemoryHandle<T> other) => Equals(in other);
+
+ /// <summary>
/// Override for object equality operator, will cause boxing
/// for structures
/// </summary>
@@ -222,13 +241,7 @@ namespace VNLib.Utils.Memory
/// and uses the structure equality operator
/// false otherwise.
/// </returns>
- public readonly override bool Equals([NotNullWhen(true)] object? obj) => obj is UnsafeMemoryHandle<T> other && Equals(other);
-
- /// <summary>
- /// Casts the handle to it's <see cref="Span{T}"/> representation
- /// </summary>
- /// <param name="handle">the handle to cast</param>
- public static implicit operator Span<T>(in UnsafeMemoryHandle<T> handle) => handle.Span;
+ public readonly override bool Equals([NotNullWhen(true)] object? obj) => obj is UnsafeMemoryHandle<T> other && Equals(in other);
/// <summary>
/// Equality overload
@@ -237,6 +250,7 @@ namespace VNLib.Utils.Memory
/// <param name="right"></param>
/// <returns>True if handles are equal, flase otherwise</returns>
public static bool operator ==(in UnsafeMemoryHandle<T> left, in UnsafeMemoryHandle<T> right) => left.Equals(right);
+
/// <summary>
/// Equality overload
/// </summary>
diff --git a/lib/Utils/src/Memory/VnString.cs b/lib/Utils/src/Memory/VnString.cs
index c937ccc..d5b34ac 100644
--- a/lib/Utils/src/Memory/VnString.cs
+++ b/lib/Utils/src/Memory/VnString.cs
@@ -42,7 +42,13 @@ namespace VNLib.Utils.Memory
/// </summary>
[ComVisible(false)]
[ImmutableObject(true)]
- public sealed class VnString : VnDisposeable, IEquatable<VnString>, IEquatable<string>, IEquatable<char[]>, IComparable<VnString>, IComparable<string>
+ public sealed class VnString :
+ VnDisposeable,
+ IEquatable<VnString>,
+ IEquatable<string>,
+ IEquatable<char[]>,
+ IComparable<VnString>,
+ IComparable<string>
{
private readonly IMemoryHandle<char>? Handle;
@@ -58,10 +64,7 @@ namespace VNLib.Utils.Memory
/// </summary>
public bool IsEmpty => Length == 0;
- private VnString(SubSequence<char> sequence)
- {
- _stringSequence = sequence;
- }
+ private VnString(SubSequence<char> sequence) => _stringSequence = sequence;
private VnString(IMemoryHandle<char> handle, nuint start, int length)
{
@@ -97,7 +100,77 @@ namespace VNLib.Utils.Memory
//Get subsequence over the whole copy of data
_stringSequence = Handle.GetSubSequence(0, data.Length);
}
-
+
+ /// <summary>
+ /// Creates a new <see cref="VnString"/> from the binary data, using the specified encoding
+ /// and allocating the internal buffer from the desired heap, or <see cref="MemoryUtil.Shared"/>
+ /// heap instance if null. If the <paramref name="data"/> is empty, an empty <see cref="VnString"/>
+ /// is returned.
+ /// </summary>
+ /// <param name="data">The data to decode</param>
+ /// <param name="encoding"></param>
+ /// <param name="heap"></param>
+ /// <returns>The decoded string from the binary data, or an empty string if no data was provided</returns>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static VnString FromBinary(ReadOnlySpan<byte> data, Encoding encoding, IUnmangedHeap? heap = null)
+ {
+ _ = encoding ?? throw new ArgumentNullException(nameof(encoding));
+
+ if (data.IsEmpty)
+ {
+ return new VnString();
+ }
+
+ //Fall back to shared heap
+ heap ??= MemoryUtil.Shared;
+
+ //Get the number of characters
+ int numChars = encoding.GetCharCount(data);
+
+ //New handle for decoded data
+ MemoryHandle<char> charBuffer = heap.Alloc<char>(numChars);
+ try
+ {
+ //Write characters to character buffer
+ _ = encoding.GetChars(data, charBuffer.Span);
+ //Consume the new handle
+ return ConsumeHandle(charBuffer, 0, numChars);
+ }
+ catch
+ {
+ //If an error occured, dispose the buffer
+ charBuffer.Dispose();
+ throw;
+ }
+ }
+
+ /// <summary>
+ /// Creates a new Vnstring from the <see cref="MemoryHandle{T}"/> buffer provided. This function "consumes"
+ /// a handle, meaning it now takes ownsership of the the memory it points to.
+ /// </summary>
+ /// <param name="handle">The <see cref="MemoryHandle{T}"/> to consume</param>
+ /// <param name="start">The offset from the begining of the buffer marking the begining of the string</param>
+ /// <param name="length">The number of characters this string points to</param>
+ /// <returns>The new <see cref="VnString"/></returns>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public static VnString ConsumeHandle(IMemoryHandle<char> handle, nuint start, int length)
+ {
+ if (handle is null)
+ {
+ throw new ArgumentNullException(nameof(handle));
+ }
+
+ if (length < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(length));
+ }
+
+ //Check handle bounts
+ MemoryUtil.CheckBounds(handle, start, (nuint)length);
+
+ return new VnString(handle, start, 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
@@ -111,7 +184,7 @@ namespace VNLib.Utils.Memory
/// <exception cref="OverflowException"></exception>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="InvalidOperationException"></exception>
- public static VnString FromStream(Stream stream, Encoding encoding, IUnmangedHeap heap, uint bufferSize)
+ public static VnString FromStream(Stream stream, Encoding encoding, IUnmangedHeap heap, int bufferSize)
{
_ = stream ?? throw new ArgumentNullException(nameof(stream));
_ = encoding ?? throw new ArgumentNullException(nameof(encoding));
@@ -125,36 +198,27 @@ namespace VNLib.Utils.Memory
//See if the stream is a vn memory stream
if (stream is VnMemoryStream vnms)
{
- //Get the number of characters
- int numChars = encoding.GetCharCount(vnms.AsSpan());
- //New handle
- MemoryHandle<char> charBuffer = heap.Alloc<char>(numChars);
- try
- {
- //Write characters to character buffer
- _ = encoding.GetChars(vnms.AsSpan(), charBuffer);
- //Consume the new handle
- return ConsumeHandle(charBuffer, 0, numChars);
- }
- catch
- {
- //If an error occured, dispose the buffer
- charBuffer.Dispose();
- throw;
- }
+ return FromBinary(vnms.AsSpan(), encoding, heap);
+ }
+ //Try to get the internal buffer from am memory span
+ else if (stream is MemoryStream ms && ms.TryGetBuffer(out ArraySegment<byte> arrSeg))
+ {
+ return FromBinary(arrSeg.AsSpan(), encoding, heap);
}
//Need to read from the stream old school with buffers
else
{
+ //Allocate a binary buffer
+ using UnsafeMemoryHandle<byte> binBuffer = heap.UnsafeAlloc<byte>(bufferSize);
+
//Create a new char bufer that will expand dyanmically
MemoryHandle<char> charBuffer = heap.Alloc<char>(bufferSize);
- //Allocate a binary buffer
- MemoryHandle<byte> binBuffer = heap.Alloc<byte>(bufferSize);
+
try
{
int length = 0;
//span ref to bin buffer
- Span<byte> buffer = binBuffer;
+ Span<byte> buffer = binBuffer.Span;
//Run in checked context for overflows
checked
{
@@ -193,40 +257,8 @@ namespace VNLib.Utils.Memory
//We still want the exception to be propagated!
throw;
}
- finally
- {
- //Dispose the binary buffer
- binBuffer.Dispose();
- }
- }
- }
-
- /// <summary>
- /// Creates a new Vnstring from the <see cref="MemoryHandle{T}"/> buffer provided. This function "consumes"
- /// a handle, meaning it now takes ownsership of the the memory it points to.
- /// </summary>
- /// <param name="handle">The <see cref="MemoryHandle{T}"/> to consume</param>
- /// <param name="start">The offset from the begining of the buffer marking the begining of the string</param>
- /// <param name="length">The number of characters this string points to</param>
- /// <returns>The new <see cref="VnString"/></returns>
- /// <exception cref="ArgumentOutOfRangeException"></exception>
- public static VnString ConsumeHandle(MemoryHandle<char> handle, nuint start, int length)
- {
- if (handle is null)
- {
- throw new ArgumentNullException(nameof(handle));
- }
-
- 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
@@ -251,36 +283,25 @@ namespace VNLib.Utils.Memory
throw new IOException("The input stream is not readable");
}
- //See if the stream is a vn memory stream
+ /*
+ * If the stream is some type of memory stream, we can just use
+ * the underlying buffer if possible
+ */
if (stream is VnMemoryStream vnms)
{
- //Get the number of characters
- int numChars = encoding.GetCharCount(vnms.AsSpan());
-
- //New handle
- MemoryHandle<char> charBuffer = heap.Alloc<char>(numChars);
-
- try
- {
- //Write characters to character buffer
- _ = encoding.GetChars(vnms.AsSpan(), charBuffer);
- //Consume the new handle
- return ConsumeHandle(charBuffer, 0, numChars);
- }
- catch
- {
- //If an error occured, dispose the buffer
- charBuffer.Dispose();
- throw;
- }
+ return FromBinary(vnms.AsSpan(), encoding, heap);
+ }
+ else if(stream is MemoryStream ms && ms.TryGetBuffer(out ArraySegment<byte> arrSeg))
+ {
+ return FromBinary(arrSeg.AsSpan(), encoding, heap);
}
else
{
- //Create a new char bufer starting with the buffer size
- MemoryHandle<char> charBuffer = heap.Alloc<char>(bufferSize);
-
//Rent a temp binary buffer
- IMemoryOwner<byte> binBuffer = heap.DirectAlloc<byte>(bufferSize);
+ using MemoryManager<byte> binBuffer = heap.DirectAlloc<byte>(bufferSize);
+
+ //Create a new char buffer starting with the buffer size
+ MemoryHandle<char> charBuffer = heap.Alloc<char>(bufferSize);
try
{
@@ -302,14 +323,14 @@ namespace VNLib.Utils.Memory
//Guard for overflow
if (((ulong)(numChars + length)) >= int.MaxValue)
{
- throw new OverflowException();
+ throw new OverflowException("The provided stream is larger than 2gb and is not supported");
}
//Re-alloc buffer
charBuffer.ResizeIfSmaller(length + numChars);
//Decode and update position
- _ = encoding.GetChars(binBuffer.Memory.Span[..read], charBuffer.Span.Slice(length, numChars));
+ _ = encoding.GetChars(binBuffer.GetSpan()[..read], charBuffer.Span.Slice(length, numChars));
//Update char count
length += numChars;
@@ -323,11 +344,6 @@ namespace VNLib.Utils.Memory
//We still want the exception to be propagated!
throw;
}
- finally
- {
- //Dispose the binary buffer
- binBuffer.Dispose();
- }
}
}
diff --git a/lib/Utils/src/VnEncoding.cs b/lib/Utils/src/VnEncoding.cs
index e24f7df..9eb60df 100644
--- a/lib/Utils/src/VnEncoding.cs
+++ b/lib/Utils/src/VnEncoding.cs
@@ -69,7 +69,7 @@ namespace VNLib.Utils
//resize the handle to fit the data
handle = heap.Alloc<byte>(byteCount);
//encode
- int size = encoding.GetBytes(data, handle);
+ int size = encoding.GetBytes(data, handle.Span);
//Consume the handle into a new vnmemstream and return it
return VnMemoryStream.ConsumeHandle(handle, size, true);
}
@@ -459,27 +459,28 @@ namespace VNLib.Utils
string value;
//Calculate the base32 entropy to alloc an appropriate buffer (minium buffer of 2 chars)
int entropy = Base32CalcMaxBufferSize(binBuffer.Length);
+
//Alloc buffer for enough size (2*long bytes) is not an issue
- using (UnsafeMemoryHandle<char> charBuffer = MemoryUtil.UnsafeAlloc<char>(entropy))
+ using UnsafeMemoryHandle<char> charBuffer = MemoryUtil.UnsafeAlloc<char>(entropy);
+
+ //Encode
+ ERRNO encoded = TryToBase32Chars(binBuffer, charBuffer.Span);
+ if (!encoded)
{
- //Encode
- ERRNO encoded = TryToBase32Chars(binBuffer, charBuffer.Span);
- if (!encoded)
- {
- throw new InternalBufferTooSmallException("Base32 char buffer was too small");
- }
- //Convert with or w/o padding
- if (withPadding)
- {
- value = charBuffer.Span[0..(int)encoded].ToString();
- }
- else
- {
- value = charBuffer.Span[0..(int)encoded].Trim('=').ToString();
- }
+ throw new InternalBufferTooSmallException("Base32 char buffer was too small");
+ }
+ //Convert with or w/o padding
+ if (withPadding)
+ {
+ value = charBuffer.Span[0..(int)encoded].ToString();
}
+ else
+ {
+ value = charBuffer.Span[0..(int)encoded].Trim('=').ToString();
+ }
+
return value;
- }
+ }
/// <summary>
/// Converts the base32 character buffer to its structure representation
@@ -506,7 +507,6 @@ namespace VNLib.Utils
/// </summary>
/// <param name="base32">The character array to decode</param>
/// <returns>The byte[] of the decoded binary data, or null if the supplied character array was empty</returns>
- /// <exception cref="InternalBufferTooSmallException"></exception>
public static byte[]? FromBase32String(ReadOnlySpan<char> base32)
{
if (base32.IsEmpty)
@@ -515,10 +515,12 @@ namespace VNLib.Utils
}
//Buffer size of the base32 string will always be enough buffer space
using UnsafeMemoryHandle<byte> tempBuffer = MemoryUtil.UnsafeAlloc(base32.Length);
+
//Try to decode the data
ERRNO decoded = TryFromBase32Chars(base32, tempBuffer.Span);
-
- return decoded ? tempBuffer.Span[0..(int)decoded].ToArray() : throw new InternalBufferTooSmallException("Binbuffer was too small");
+ Debug.Assert(decoded > 0, "The supplied base32 buffer was too small to decode data into, but should not have been");
+
+ return tempBuffer.Span[0..(int)decoded].ToArray();
}
/// <summary>
@@ -942,16 +944,28 @@ namespace VNLib.Utils
{
return ERRNO.E_FAIL;
}
+
//Set the encoding to utf8
encoding ??= Encoding.UTF8;
//get the number of bytes to alloc a buffer
int decodedSize = encoding.GetByteCount(chars);
- //alloc buffer
- using UnsafeMemoryHandle<byte> decodeHandle = MemoryUtil.UnsafeAlloc(decodedSize);
- //Get the utf8 binary data
- int count = encoding.GetBytes(chars, decodeHandle);
- return Base64UrlDecode(decodeHandle.Span[..count], output);
+ if(decodedSize > MAX_STACKALLOC)
+ {
+ //Alloc heap buffer
+ using UnsafeMemoryHandle<byte> decodeHandle = MemoryUtil.UnsafeAlloc(decodedSize);
+ //Get the utf8 binary data
+ int count = encoding.GetBytes(chars, decodeHandle.Span);
+ return Base64UrlDecode(decodeHandle.Span[..count], output);
+ }
+ else
+ {
+ //Alloc stack buffer
+ Span<byte> decodeBuffer = stackalloc byte[decodedSize];
+ //Get the utf8 binary data
+ int count = encoding.GetBytes(chars, decodeBuffer);
+ return Base64UrlDecode(decodeBuffer[..count], output);
+ }
}
@@ -1036,16 +1050,14 @@ namespace VNLib.Utils
if(maxBufSize > MAX_STACKALLOC)
{
- //alloc buffer
+ //Alloc heap buffer
using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAllocNearestPage(maxBufSize);
-
return ConvertToBase64UrlStringInternal(rawData, buffer.Span, includePadding);
}
else
{
//Stack alloc buffer
Span<byte> buffer = stackalloc byte[maxBufSize];
-
return ConvertToBase64UrlStringInternal(rawData, buffer, includePadding);
}
}
@@ -1116,24 +1128,37 @@ namespace VNLib.Utils
//We need to alloc an intermediate buffer, get the base64 max size
int maxSize = Base64.GetMaxEncodedToUtf8Length(input.Length);
- //Alloc buffer
- using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc(maxSize);
-
- //Encode to url safe binary
- ERRNO count = Base64UrlEncode(input, buffer.Span, includePadding);
-
- if (count <= 0)
+ if (maxSize > MAX_STACKALLOC)
{
- return count;
+ //Alloc heap buffer
+ using UnsafeMemoryHandle<byte> buffer = MemoryUtil.UnsafeAlloc(maxSize);
+ return Base64UrlEncodeCore(input, buffer.Span, output, encoding, includePadding);
}
+ else
+ {
+ //Alloc stack buffer
+ Span<byte> bufer = stackalloc byte[maxSize];
+ return Base64UrlEncodeCore(input, bufer, output, encoding, includePadding);
+ }
+
+ static ERRNO Base64UrlEncodeCore(ReadOnlySpan<byte> input, Span<byte> buffer, Span<char> output, Encoding encoding, bool includePadding)
+ {
+ //Encode to url safe binary
+ ERRNO count = Base64UrlEncode(input, buffer, includePadding);
- //Get char count to return to caller
- int charCount = encoding.GetCharCount(buffer.Span[..(int)count]);
+ if (count <= 0)
+ {
+ return count;
+ }
- //Encode to characters
- encoding.GetChars(buffer.AsSpan(0, count), output);
+ //Get char count to return to caller
+ int charCount = encoding.GetCharCount(buffer[..(int)count]);
- return charCount;
+ //Encode to characters
+ encoding.GetChars(buffer[0..(int)count], output);
+
+ return charCount;
+ }
}
#endregion
diff --git a/lib/Utils/tests/Memory/MemoryUtilTests.cs b/lib/Utils/tests/Memory/MemoryUtilTests.cs
index e63dc03..0774f47 100644
--- a/lib/Utils/tests/Memory/MemoryUtilTests.cs
+++ b/lib/Utils/tests/Memory/MemoryUtilTests.cs
@@ -165,6 +165,10 @@ namespace VNLib.Utils.Memory.Tests
Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = handle.Pin(1024));
}
+ //Use the byte only overload
+ using (UnsafeMemoryHandle<byte> handle = MemoryUtil.UnsafeAlloc(1024))
+ { }
+
//test against negative number
Assert.ThrowsException<ArgumentException>(() => MemoryUtil.UnsafeAlloc<byte>(-1));
@@ -217,7 +221,7 @@ namespace VNLib.Utils.Memory.Tests
Assert.IsTrue(0 == empty.IntLength);
//Test pinning while empty
- Assert.ThrowsException<InvalidOperationException>(() => _ = empty.Pin(0));
+ Assert.ThrowsException<ArgumentOutOfRangeException>(() => _ = empty.Pin(0));
}
//Negative value