diff options
Diffstat (limited to 'lib/Utils/src')
-rw-r--r-- | lib/Utils/src/Extensions/MemoryExtensions.cs | 60 | ||||
-rw-r--r-- | lib/Utils/src/IO/VnMemoryStream.cs | 6 | ||||
-rw-r--r-- | lib/Utils/src/Memory/MemoryUtil.cs | 468 | ||||
-rw-r--r-- | lib/Utils/src/Memory/MemoryUtilAlloc.cs | 81 |
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 |