aboutsummaryrefslogtreecommitdiff
path: root/lib/Utils/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Utils/src')
-rw-r--r--lib/Utils/src/Extensions/MemoryExtensions.cs60
-rw-r--r--lib/Utils/src/IO/VnMemoryStream.cs6
-rw-r--r--lib/Utils/src/Memory/MemoryUtil.cs468
-rw-r--r--lib/Utils/src/Memory/MemoryUtilAlloc.cs81
4 files changed, 567 insertions, 48 deletions
diff --git a/lib/Utils/src/Extensions/MemoryExtensions.cs b/lib/Utils/src/Extensions/MemoryExtensions.cs
index d21ceee..9553578 100644
--- a/lib/Utils/src/Extensions/MemoryExtensions.cs
+++ b/lib/Utils/src/Extensions/MemoryExtensions.cs
@@ -263,7 +263,7 @@ namespace VNLib.Utils.Extensions
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ref byte GetByteOffsetRef<T>(this IMemoryHandle<T> block, nuint offset)
+ public static ref byte GetOffsetByteRef<T>(this IMemoryHandle<T> block, nuint offset)
{
_ = block ?? throw new ArgumentNullException(nameof(block));
@@ -361,16 +361,31 @@ namespace VNLib.Utils.Extensions
public static MemoryPool<T> ToPool<T>(this IUnmangedHeap heap, int maxBufferSize = int.MaxValue) where T : unmanaged => new PrivateBuffersMemoryPool<T>(heap, maxBufferSize);
/// <summary>
- /// Allocates a structure of the specified type on the current unmanged heap and zero's its memory
+ /// Allocates a structure of the specified type on the current unmanged heap and optionally zero's its memory
/// </summary>
/// <typeparam name="T">The structure type</typeparam>
/// <param name="heap"></param>
+ /// <param name="zero">A value that indicates if the structure memory should be zeroed before returning</param>
/// <returns>A pointer to the structure ready for use.</returns>
/// <remarks>Allocations must be freed with <see cref="StructFree{T}(IUnmangedHeap, T*)"/></remarks>
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe T* StructAlloc<T>(this IUnmangedHeap heap) where T : unmanaged => (T*)heap.Alloc(1, (nuint)sizeof(T), true);
+ public static unsafe T* StructAlloc<T>(this IUnmangedHeap heap, bool zero = true) where T : unmanaged => MemoryUtil.StructAlloc<T>(heap, zero);
+
+ /// <summary>
+ /// Allocates a structure of the specified type on the current unmanged heap and optionally zero's its memory
+ /// </summary>
+ /// <typeparam name="T">The structure type</typeparam>
+ /// <param name="heap"></param>
+ /// <param name="zero">A value that indicates if the structure memory should be zeroed before returning</param>
+ /// <returns>A reference/pointer to the structure ready for use.</returns>
+ /// <remarks>Allocations must be freed with <see cref="StructFreeRef{T}(IUnmangedHeap, ref T)"/></remarks>
+ /// <exception cref="OutOfMemoryException"></exception>
+ /// <exception cref="ObjectDisposedException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ref T StructAllocRef<T>(this IUnmangedHeap heap, bool zero = true) where T : unmanaged => ref MemoryUtil.StructAllocRef<T>(heap, zero);
+
/// <summary>
/// Frees a structure at the specified address from the this heap.
@@ -378,17 +393,20 @@ namespace VNLib.Utils.Extensions
/// </summary>
/// <typeparam name="T">The structure type</typeparam>
/// <param name="heap"></param>
- /// <param name="structPtr">A pointer to the structure</param>
+ /// <param name="structPtr">A reference/pointer to the structure</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe void StructFree<T>(this IUnmangedHeap heap, T* structPtr) where T : unmanaged
- {
- IntPtr block = new(structPtr);
- //Free block from heap
- heap.Free(ref block);
- //Clear ref
- *structPtr = default;
- }
-
+ public static unsafe void StructFree<T>(this IUnmangedHeap heap, T* structPtr) where T : unmanaged => MemoryUtil.StructFree(heap, structPtr);
+
+ /// <summary>
+ /// Frees a structure at the specified address from the this heap.
+ /// This must be the same heap the structure was allocated from
+ /// </summary>
+ /// <typeparam name="T">The structure type</typeparam>
+ /// <param name="heap"></param>
+ /// <param name="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);
+
/// <summary>
/// Allocates a block of unmanaged memory of the number of elements to store of an unmanged type
/// </summary>
@@ -406,10 +424,8 @@ namespace VNLib.Utils.Extensions
_ = heap ?? throw new ArgumentNullException(nameof(heap));
//Minimum of one element
elements = Math.Max(elements, 1);
- //Get element size
- nuint elementSize = (nuint)sizeof(T);
//If zero flag is set then specify zeroing memory
- IntPtr block = heap.Alloc(elements, elementSize, zero);
+ IntPtr block = heap.Alloc(elements, (nuint)sizeof(T), zero);
//Return handle wrapper
return new MemoryHandle<T>(heap, block, elements, zero);
}
@@ -430,7 +446,7 @@ namespace VNLib.Utils.Extensions
{
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>
@@ -441,13 +457,13 @@ namespace VNLib.Utils.Extensions
/// <exception cref="OutOfMemoryException"></exception>
/// <exception cref="ObjectDisposedException"></exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static MemoryHandle<T> AllocAndCopy<T>(this IUnmangedHeap heap, ReadOnlySpan<T> initialData) where T:unmanaged
+ 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);
-
+
//Copy initial data
- MemoryUtil.Copy(initialData, handle, 0);
+ MemoryUtil.Copy(initialData, 0, handle, 0, initialData.Length);
return handle;
}
@@ -468,7 +484,7 @@ namespace VNLib.Utils.Extensions
MemoryHandle<T> handle = heap.Alloc<T>(initialData.Length);
//Copy initial data
- MemoryUtil.Copy(initialData, handle, 0);
+ MemoryUtil.Copy(initialData, 0, handle, 0, initialData.Length);
return handle;
}
@@ -486,7 +502,7 @@ namespace VNLib.Utils.Extensions
public static void WriteAndResize<T>(this IResizeableMemoryHandle<T> handle, ReadOnlySpan<T> input) where T: unmanaged
{
handle.Resize(input.Length);
- MemoryUtil.Copy(input, handle, 0);
+ MemoryUtil.Copy(input, 0, handle, 0, input.Length);
}
/// <summary>
diff --git a/lib/Utils/src/IO/VnMemoryStream.cs b/lib/Utils/src/IO/VnMemoryStream.cs
index 7ac56a6..45c4a55 100644
--- a/lib/Utils/src/IO/VnMemoryStream.cs
+++ b/lib/Utils/src/IO/VnMemoryStream.cs
@@ -368,7 +368,7 @@ namespace VNLib.Utils.IO
}
//get the value at the current position
- ref byte ptr = ref _buffer.GetByteOffsetRef((nuint)_position);
+ ref byte ptr = ref _buffer.GetOffsetByteRef((nuint)_position);
//Increment position
_position++;
@@ -488,7 +488,7 @@ namespace VNLib.Utils.IO
_length = newPos;
}
//Copy the input buffer to the internal buffer
- MemoryUtil.Copy(buffer, _buffer, (nuint)_position);
+ MemoryUtil.Copy(buffer, 0, _buffer, (nuint)_position, buffer.Length);
//Update the position
_position = newPos;
}
@@ -527,7 +527,7 @@ namespace VNLib.Utils.IO
byte[] data = new byte[_length];
//Copy the internal buffer to the new array
- MemoryUtil.Copy(_buffer, 0, data, 0, (nuint)_length);
+ MemoryUtil.CopyArray(_buffer, 0, data, 0, (nuint)_length);
return data;
}
diff --git a/lib/Utils/src/Memory/MemoryUtil.cs b/lib/Utils/src/Memory/MemoryUtil.cs
index 0261bdf..a1ad0c1 100644
--- a/lib/Utils/src/Memory/MemoryUtil.cs
+++ b/lib/Utils/src/Memory/MemoryUtil.cs
@@ -294,6 +294,7 @@ namespace VNLib.Utils.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void InitializeBlock<T>(Memory<T> block) where T : struct => UnsafeZeroMemory<T>(block);
+
/// <summary>
/// Zeroes a block of memory of the given unmanaged type
/// </summary>
@@ -301,15 +302,39 @@ namespace VNLib.Utils.Memory
/// <param name="block">A pointer to the block of memory to zero</param>
/// <param name="itemCount">The number of elements in the block to zero</param>
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
- public static void InitializeBlock<T>(T* block, int itemCount) where T : unmanaged
+ public static void InitializeBlock<T>(ref T block, int itemCount) where T : struct
{
- if (itemCount <= 0 || block == null)
+ if (Unsafe.IsNullRef(ref block))
+ {
+ throw new ArgumentNullException(nameof(block));
+ }
+
+ if (itemCount <= 0)
{
return;
}
+ //To bytereference
+ ref byte byteRef = ref Unsafe.As<T, byte>(ref block);
//Zero block
- Unsafe.InitBlock(block, 0, ByteCount<T>((uint)itemCount));
+ Unsafe.InitBlock(ref byteRef, 0, ByteCount<T>((uint)itemCount));
+ }
+
+ /// <summary>
+ /// Zeroes a block of memory of the given unmanaged type
+ /// </summary>
+ /// <typeparam name="T">The unmanaged type to zero</typeparam>
+ /// <param name="block">A pointer to the block of memory to zero</param>
+ /// <param name="itemCount">The number of elements in the block to zero</param>
+ [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
+ public static void InitializeBlock<T>(T* block, int itemCount) where T : unmanaged
+ {
+ if (block == null)
+ {
+ throw new ArgumentNullException(nameof(block));
+ }
+
+ InitializeBlock(ref *block, itemCount);
}
/// <summary>
@@ -327,7 +352,7 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The structure type</typeparam>
/// <param name="structPtr">The pointer to the allocated structure</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ZeroStruct<T>(void* structPtr) => Unsafe.InitBlock(structPtr, 0, (uint)Unsafe.SizeOf<T>());
+ public static void ZeroStruct<T>(void* structPtr) where T: unmanaged => InitializeBlock((T*)structPtr, Unsafe.SizeOf<T>());
/// <summary>
/// Zeroes a block of memory pointing to the structure
@@ -335,7 +360,7 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">The structure type</typeparam>
/// <param name="block">The pointer to the allocated structure</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static void ZeroStruct<T>(IntPtr block) => ZeroStruct<T>(block.ToPointer());
+ public static void ZeroStruct<T>(IntPtr block) where T : unmanaged => ZeroStruct<T>(block.ToPointer());
/// <summary>
/// Zeroes a block of memory pointing to the structure
@@ -345,6 +370,22 @@ namespace VNLib.Utils.Memory
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ZeroStruct<T>(T* structPtr) where T : unmanaged => ZeroStruct<T>((void*)structPtr);
+ /// <summary>
+ /// Zeroes a block of memory pointing to the structure
+ /// </summary>
+ /// <typeparam name="T">The structure type</typeparam>
+ /// <param name="structRef">The reference to the allocated structure</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ZeroStruct<T>(ref T structRef) where T : unmanaged
+ {
+ if(Unsafe.IsNullRef(ref structRef))
+ {
+ throw new ArgumentNullException(nameof(structRef));
+ }
+
+ Unsafe.InitBlock(ref Unsafe.As<T, byte>(ref structRef), 0, (uint)sizeof(T));
+ }
+
#endregion
#region Copy
@@ -355,7 +396,275 @@ namespace VNLib.Utils.Memory
* guards are in place.
*/
private delegate void BigMemmove(ref byte dest, ref byte src, nuint len);
- private static readonly BigMemmove? _sysMemmove = ManagedLibrary.TryGetStaticMethod<BigMemmove>(typeof(Buffer), "Memmove", System.Reflection.BindingFlags.NonPublic);
+ private static readonly BigMemmove? _clrMemmove = ManagedLibrary.TryGetStaticMethod<BigMemmove>(typeof(Buffer), "Memmove", System.Reflection.BindingFlags.NonPublic);
+
+ /// <summary>
+ /// Copies structure data from a source byte reference that points to a sequence of
+ /// of data to the target structure reference.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A referrence to the first byte of source data to copy from</param>
+ /// <param name="target">An initialized target structure to copy data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static void CopyStruct<T>(ref byte source, ref T target) where T : unmanaged
+ {
+ if (Unsafe.IsNullRef(ref target))
+ {
+ throw new ArgumentNullException(nameof(target));
+ }
+ if (Unsafe.IsNullRef(ref source))
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ //Recover byte reference of target struct
+ ref byte dst = ref Unsafe.As<T, byte>(ref target);
+
+ //Memmove
+ bool result = MemmoveByRef(ref source, 0, ref dst, 0, (nuint)sizeof(T));
+ Debug.Assert(result, "Memmove 32bit copy failed");
+ }
+
+ /// <summary>
+ /// Copies the memory of the structure pointed to by the source reference to the target
+ /// reference data sequence
+ /// </summary>
+ /// <remarks>
+ /// Warning: This is a low level api that cannot do bounds checking on the target sequence. It must
+ /// be large enough to hold the structure data.
+ /// </remarks>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A referrence to the first byte of source data to copy from</param>
+ /// <param name="target">A reference to the first byte of the memory location to copy the struct data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static void CopyStruct<T>(ref T source, ref byte target) where T : unmanaged
+ {
+ if (Unsafe.IsNullRef(ref source))
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ if (Unsafe.IsNullRef(ref target))
+ {
+ throw new ArgumentNullException(nameof(target));
+ }
+
+ //Recover byte reference to struct
+ ref byte src = ref Unsafe.As<T, byte>(ref source);
+
+ //Memmove
+ bool result = MemmoveByRef(ref src, 0, ref target, 0, (nuint)sizeof(T));
+ Debug.Assert(result, "Memmove 32bit copy failed");
+ }
+
+
+ /// <summary>
+ /// Copies structure data from a source byte reference that points to a sequence of
+ /// of data to the target structure reference.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A referrence to the first byte of source data to copy from</param>
+ /// <param name="target">A pointer to initialized target structure to copy data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(ref byte source, T* target) where T : unmanaged
+ {
+ if (target == null)
+ {
+ throw new ArgumentNullException(nameof(target));
+ }
+ CopyStruct(ref source, ref *target);
+ }
+
+ /// <summary>
+ /// Copies structure data from a source byte reference that points to a sequence of
+ /// of data to the target structure reference.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A referrence to the first byte of source data to copy from</param>
+ /// <param name="target">A pointer to initialized target structure to copy data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(ref byte source, void* target) where T : unmanaged => CopyStruct(ref source, (T*)target);
+
+ /// <summary>
+ /// Copies structure data from a source sequence of data to the target structure reference.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="sourceData">A referrence to the first byte of source data to copy from</param>
+ /// <param name="target">A pointer to initialized target structure to copy data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(ReadOnlySpan<byte> sourceData, ref T target) where T : unmanaged
+ {
+ if (sourceData.Length < sizeof(T))
+ {
+ throw new ArgumentException("Source data is smaller than the size of the structure");
+ }
+
+ CopyStruct(ref MemoryMarshal.GetReference(sourceData), ref target);
+ }
+
+ /// <summary>
+ /// Copies structure data from a source sequence of data to the target structure reference.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="sourceData">A referrence to the first byte of source data to copy from</param>
+ /// <param name="target">A pointer to initialized target structure to copy data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(ReadOnlySpan<byte> sourceData, T* target) where T : unmanaged => CopyStruct(sourceData, ref *target);
+
+ /// <summary>
+ /// Copies structure data from a source sequence of data to the target structure reference.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="sourceData">A referrence to the first byte of source data to copy from</param>
+ /// <param name="target">A pointer to initialized target structure to copy data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(ReadOnlySpan<byte> sourceData, void* target) where T: unmanaged => CopyStruct(sourceData, (T*)target);
+
+
+
+ /// <summary>
+ /// Copies the memory of the structure pointed to by the source pointer to the target
+ /// reference data sequence
+ /// </summary>
+ /// <remarks>
+ /// Warning: This is a low level api that cannot do bounds checking on the target sequence. It must
+ /// be large enough to hold the structure data.
+ /// </remarks>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A pointer to the first byte of source data to copy from</param>
+ /// <param name="target">A reference to the first byte of the memory location to copy the struct data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(T* source, ref byte target) where T : unmanaged
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ CopyStruct(ref *source, ref target);
+ }
+
+ /// <summary>
+ /// Copies the memory of the structure pointed to by the source pointer to the target
+ /// reference data sequence
+ /// </summary>
+ /// <remarks>
+ /// Warning: This is a low level api that cannot do bounds checking on the target sequence. It must
+ /// be large enough to hold the structure data.
+ /// </remarks>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A pointer to the first byte of source data to copy from</param>
+ /// <param name="target">A reference to the first byte of the memory location to copy the struct data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(void* source, ref byte target) where T : unmanaged => CopyStruct((T*)source, ref target);
+
+ /// <summary>
+ /// Copies the memory of the structure pointed to by the source pointer to the target
+ /// reference data sequence
+ /// </summary>
+ /// <remarks>
+ /// Warning: This is a low level api that cannot do bounds checking on the target sequence. It must
+ /// be large enough to hold the structure data.
+ /// </remarks>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A pointer to the first byte of source data to copy from</param>
+ /// <param name="target">A reference to the first byte of the memory location to copy the struct data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(ref T source, Span<byte> target) where T : unmanaged
+ {
+ //check that the span is large enough to hold the structure
+ if (target.Length < sizeof(T))
+ {
+ throw new ArgumentException("Target span is smaller than the size of the structure");
+ }
+
+ CopyStruct(ref source, ref MemoryMarshal.AsRef<byte>(target));
+ }
+
+ /// <summary>
+ /// Copies the memory of the structure pointed to by the source pointer to the target
+ /// reference data sequence
+ /// </summary>
+ /// <remarks>
+ /// Warning: This is a low level api that cannot do bounds checking on the target sequence. It must
+ /// be large enough to hold the structure data.
+ /// </remarks>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A pointer to the first byte of source data to copy from</param>
+ /// <param name="target">A reference to the first byte of the memory location to copy the struct data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(T* source, Span<byte> target) where T : unmanaged
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+ CopyStruct(ref *source, target);
+ }
+
+ /// <summary>
+ /// Copies the memory of the structure pointed to by the source pointer to the target
+ /// reference data sequence
+ /// </summary>
+ /// <remarks>
+ /// Warning: This is a low level api that cannot do bounds checking on the target sequence. It must
+ /// be large enough to hold the structure data.
+ /// </remarks>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A pointer to the first byte of source data to copy from</param>
+ /// <param name="target">A reference to the first byte of the memory location to copy the struct data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(void* source, Span<byte> target) where T : unmanaged => CopyStruct((T*)source, target);
+
+ /// <summary>
+ /// Copies the memory of the structure pointed to by the source pointer to the target
+ /// reference data sequence
+ /// </summary>
+ /// <remarks>
+ /// Warning: This is a low level api that cannot do bounds checking on the target sequence. It must
+ /// be large enough to hold the structure data.
+ /// </remarks>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A pointer to the first byte of source data to copy from</param>
+ /// <param name="target">A reference to the first byte of the memory location to copy the struct data to</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CopyStruct<T>(IntPtr source, ref byte target) where T : unmanaged => CopyStruct<T>(source.ToPointer(), ref target);
+
+
+ /// <summary>
+ /// Copies the memory of the structure pointed to by the source reference to the target
+ /// structure reference
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A reference to the source structure to copy from</param>
+ /// <param name="target">A reference to the target structure to copy to</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CloneStruct<T>(ref T source, ref T target) where T : struct => Memmove(ref source, 0, ref target, 0, 1);
+
+ /// <summary>
+ /// Copies the memory of the structure pointed to by the source pointer to the target
+ /// structure pointer
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">A pointer to the source structure to copy from</param>
+ /// <param name="target">A pointer to the target structure to copy to</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void CloneStruct<T>(T* source, T* target) where T : unmanaged => Unsafe.CopyBlockUnaligned(target, source, (uint)sizeof(T));
+
/// <summary>
/// Copies data from source memory to destination memory of an umanged data type
@@ -363,30 +672,33 @@ namespace VNLib.Utils.Memory
/// <typeparam name="T">Unmanged type</typeparam>
/// <param name="source">Source data <see cref="ReadOnlySpan{T}"/></param>
/// <param name="dest">Destination <see cref="MemoryHandle{T}"/></param>
+ /// <param name="count">Number of elements to copy</param>
+ /// <param name="sourceOffset">Source offset</param>
/// <param name="destOffset">Dest offset</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public static void Copy<T>(ReadOnlySpan<T> source, IMemoryHandle<T> dest, nuint destOffset) where T: struct
+ public static void Copy<T>(ReadOnlySpan<T> source, int sourceOffset, IMemoryHandle<T> dest, nuint destOffset, int count) where T: struct
{
if (dest is null)
{
throw new ArgumentNullException(nameof(dest));
}
- if (source.IsEmpty)
+ if (count == 0)
{
return;
}
- //Check memhandle bounds
- CheckBounds(dest, destOffset, (uint)source.Length);
+ //Check bounds
+ CheckBounds(source, sourceOffset, count);
+ CheckBounds(dest, destOffset, (uint)count);
//Get byte ref and byte count
- nuint byteCount = ByteCount<T>((uint)source.Length);
+ nuint byteCount = ByteCount<T>((uint)count);
ref T src = ref MemoryMarshal.GetReference(source);
ref T dst = ref dest.GetReference();
//Use memmove by ref
- bool success = MemmoveByRef(ref src, 0, ref dst, (uint)destOffset, byteCount);
+ bool success = MemmoveByRef(ref src, (uint)sourceOffset, ref dst, (uint)destOffset, byteCount);
Debug.Assert(success, "Memmove by ref call failed during a 32bit copy");
}
@@ -395,10 +707,14 @@ namespace VNLib.Utils.Memory
/// </summary>
/// <typeparam name="T">Unmanged type</typeparam>
/// <param name="source">Source data <see cref="ReadOnlyMemory{T}"/></param>
- /// <param name="dest">Destination <see cref="MemoryHandle{T}"/></param>
- /// <param name="destOffset">Dest offset</param>
+ /// <param name="sourceOffset">The element offset in the source memory</param>
+ /// <param name="dest">Destination <see cref="IMemoryHandle{T}"/></param>
+ /// <param name="destOffset">Dest element offset</param>
+ /// <param name="count">The number of elements to copy</param>
/// <exception cref="ArgumentOutOfRangeException"></exception>
- public static void Copy<T>(ReadOnlyMemory<T> source, IMemoryHandle<T> dest, nuint destOffset) where T : struct => Copy(source.Span, dest, destOffset);
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void Copy<T>(ReadOnlyMemory<T> source, int sourceOffset, IMemoryHandle<T> dest, nuint destOffset, int count) where T : struct
+ => Copy(source.Span, sourceOffset, dest, destOffset, count);
/// <summary>
/// Copies data from source memory to destination memory of an umanged data type
@@ -425,6 +741,7 @@ namespace VNLib.Utils.Memory
//Check source bounds
CheckBounds(source, (nuint)sourceOffset, (nuint)count);
+ CheckBounds(dest, destOffset, count);
//Get byte ref and byte count
nuint byteCount = ByteCount<T>((uint)count);
@@ -523,7 +840,7 @@ namespace VNLib.Utils.Memory
/// <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 sourceOffset, T[] dest, nuint destOffset, nuint count) where T : unmanaged
+ public static void CopyArray<T>(IMemoryHandle<T> source, nuint sourceOffset, T[] dest, nuint destOffset, nuint count) where T : unmanaged
{
if (source is null)
{
@@ -566,7 +883,106 @@ namespace VNLib.Utils.Memory
Buffer.MemoryCopy(srcOffset, dstOffset, byteCount, byteCount);
}
}
-
+
+
+ /// <summary>
+ /// Preforms a fast referrence based copy on very large blocks of memory
+ /// using pinning and pointers only when the number of bytes to copy is
+ /// larger than <see cref="UInt32.MaxValue"/>
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="source">The source memory handle to copy data from</param>
+ /// <param name="sourceOffset">The element offset to begin reading from</param>
+ /// <param name="dest">The destination array to write data to</param>
+ /// <param name="destOffset"></param>
+ /// <param name="count">The number of elements to copy</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ /// <exception cref="ArgumentOutOfRangeException"></exception>
+ public static void CopyArray<T>(T[] source, nuint sourceOffset, IMemoryHandle<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;
+ }
+
+ //Check source bounds
+ CheckBounds(source, sourceOffset, count);
+
+ //Check dest bounds
+ CheckBounds(dest, destOffset, count);
+
+ //Get byte refs and byte count
+ nuint byteCount = ByteCount<T>(count);
+ ref T src = ref MemoryMarshal.GetArrayDataReference(source);
+ ref T dst = ref dest.GetReference();
+
+ //Try to memove by ref first, otherwise fallback to pinning
+ if (!MemmoveByRef(ref src, sourceOffset, ref dst, destOffset, byteCount))
+ {
+ //Copying block larger than 32bit must be done with pointers
+ using MemoryHandle srcH = PinArrayAndGetHandle(source, 0);
+ using MemoryHandle dstH = dest.Pin(0);
+
+ //Get pointers and add offsets
+ T* srcOffset = ((T*)srcH.Pointer) + sourceOffset;
+ T* dstOffset = ((T*)dstH.Pointer) + destOffset;
+
+ //Copy memory
+ Buffer.MemoryCopy(srcOffset, dstOffset, byteCount, byteCount);
+ }
+ }
+
+ /// <summary>
+ /// Low level api for copying data from source memory to destination memory of an
+ /// umanged data type.
+ /// </summary>
+ /// <remarks>
+ /// WARNING: It's not possible to do bounds checking when using references. Be sure you
+ /// know what you are doing!
+ /// </remarks>
+ /// <typeparam name="T">The unmanaged or structure type to copy</typeparam>
+ /// <param name="src">A reference to the source data to copy from</param>
+ /// <param name="srcOffset">The offset (in elements) from the reference to begin the copy from</param>
+ /// <param name="dst">The detination</param>
+ /// <param name="dstOffset"></param>
+ /// <param name="elementCount"></param>
+ /// <exception cref="ArgumentException"></exception>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static void Memmove<T>(ref T src, nuint srcOffset, ref T dst, nuint dstOffset, nuint elementCount) where T : struct
+ {
+ if(Unsafe.IsNullRef(ref src))
+ {
+ throw new ArgumentNullException(nameof(src));
+ }
+
+ if(Unsafe.IsNullRef(ref dst))
+ {
+ throw new ArgumentNullException(nameof(dst));
+ }
+
+ if(elementCount == 0)
+ {
+ return;
+ }
+
+ //compute the byte count from the element count
+ nuint byteCount = ByteCount<T>(elementCount);
+
+ if(!MemmoveByRef(ref src, srcOffset, ref dst, dstOffset, byteCount))
+ {
+ throw new ArgumentException("The number of bytes to copy was larger than Uint32.MaxValue and was unsupported on this platform", nameof(elementCount));
+ }
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static bool MemmoveByRef<T>(ref T src, nuint srcOffset, ref T dst, nuint dstOffset, nuint byteCount) where T : struct
@@ -582,10 +998,10 @@ namespace VNLib.Utils.Memory
ref byte srcByte = ref Unsafe.As<T, byte>(ref srcOffsetPtr);
ref byte dstByte = ref Unsafe.As<T, byte>(ref dstOffsetPtr);
- if (_sysMemmove != null)
+ if (_clrMemmove != null)
{
//Call sysinternal memmove
- _sysMemmove(ref dstByte, ref srcByte, byteCount);
+ _clrMemmove(ref dstByte, ref srcByte, byteCount);
return true;
}
else if(byteCount < uint.MaxValue)
@@ -697,8 +1113,11 @@ namespace VNLib.Utils.Memory
[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);
+ //Check span bounds
+ if (offset < 0 || count < 0 || offset + count > block.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset), "Offset or count are beyond the range of the supplied memory handle");
+ }
}
/// <summary>
@@ -713,8 +1132,11 @@ namespace VNLib.Utils.Memory
[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);
+ //Check span bounds
+ if (offset < 0 || count < 0 || offset + count > block.Length)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset), "Offset or count are beyond the range of the supplied memory handle");
+ }
}
/// <summary>
diff --git a/lib/Utils/src/Memory/MemoryUtilAlloc.cs b/lib/Utils/src/Memory/MemoryUtilAlloc.cs
index 9e305d0..bc0f35e 100644
--- a/lib/Utils/src/Memory/MemoryUtilAlloc.cs
+++ b/lib/Utils/src/Memory/MemoryUtilAlloc.cs
@@ -24,6 +24,7 @@
using System;
using System.Buffers;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using VNLib.Utils.Extensions;
@@ -162,6 +163,86 @@ namespace VNLib.Utils.Memory
return SafeAlloc<T>((int)np, zero);
}
+ /// <summary>
+ /// Allocates a structure of the specified type on the specified
+ /// unmanged heap and optionally zero's it's memory
+ /// </summary>
+ /// <typeparam name="T">The structure type</typeparam>
+ /// <param name="heap">The heap to allocate structure memory from</param>
+ /// <param name="zero">A value that indicates if the structure memory should be zeroed before returning</param>
+ /// <returns>A pointer to the structure ready for use.</returns>
+ /// <remarks>Allocations must be freed with <see cref="StructFree{T}(IUnmangedHeap, T*)"/></remarks>
+ /// <exception cref="OutOfMemoryException"></exception>
+ /// <exception cref="ObjectDisposedException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static T* StructAlloc<T>(IUnmangedHeap heap, bool zero) where T : unmanaged
+ {
+ _ = heap ?? throw new ArgumentNullException(nameof(heap));
+ return (T*)heap.Alloc(1, (nuint)sizeof(T), zero);
+ }
+
+ /// <summary>
+ /// Allocates a structure of the specified type on the specified
+ /// unmanged heap and optionally zero's it's memory, then returns
+ /// and reference to the heap allocated structure.
+ /// </summary>
+ /// <typeparam name="T">The structure type</typeparam>
+ /// <param name="heap">The heap to allocate structure memory from</param>
+ /// <param name="zero">A value that indicates if the structure memory should be zeroed before returning</param>
+ /// <returns>A reference to the heap allocated structure</returns>
+ /// <remarks>Allocations must be freed with <see cref="StructFreeRef{T}(IUnmangedHeap, ref T)"/></remarks>
+ /// <exception cref="OutOfMemoryException"></exception>
+ /// <exception cref="ObjectDisposedException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static ref T StructAllocRef<T>(IUnmangedHeap heap, bool zero) where T : unmanaged
+ {
+ //Alloc structure
+ T* ptr = StructAlloc<T>(heap, zero);
+ //Get a reference and assign it
+ return ref Unsafe.AsRef<T>(ptr);
+ }
+
+ /// <summary>
+ /// Frees a structure allocated with <see cref="StructAlloc{T}(IUnmangedHeap, bool)"/>
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="heap">Heap the structure was allocated from to free it back to</param>
+ /// <param name="structPtr">A pointer to the unmanaged structure to free</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void StructFree<T>(IUnmangedHeap heap, T* structPtr) where T : unmanaged => StructFree(heap, (void*)structPtr);
+
+ /// <summary>
+ /// Frees a structure allocated with <see cref="StructAllocRef{T}(IUnmangedHeap, bool)"/>
+ /// by its reference.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <param name="heap">Heap the structure was allocated from to free it back to</param>
+ /// <param name="structRef">A reference to the unmanaged structure to free</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void StructFreeRef<T>(IUnmangedHeap heap, ref T structRef) where T : unmanaged => StructFree(heap, Unsafe.AsPointer(ref structRef));
+
+ /// <summary>
+ /// Frees a structure allocated with <see cref="StructAlloc{T}(IUnmangedHeap, bool)"/>
+ /// </summary>
+ /// <param name="heap">Heap the structure was allocated from to free it back to</param>
+ /// <param name="structPtr">A pointer to the unmanaged structure to free</param>
+ /// <exception cref="ArgumentNullException"></exception>
+ public static void StructFree(IUnmangedHeap heap, void* structPtr)
+ {
+ _ = heap ?? throw new ArgumentNullException(nameof(heap));
+ if(structPtr == null)
+ {
+ throw new ArgumentNullException(nameof(structPtr));
+ }
+ //Get intpointer
+ IntPtr ptr = (IntPtr)structPtr;
+ //Free
+ bool isFree = heap.Free(ref ptr);
+ Debug.Assert(isFree, $"Structure free failed for heap {heap.GetHashCode()}, struct address {ptr:x}");
+ }
+
#endregion
#region ByteOptimimzations