diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnTypedArray.cs b/Misaki.HighPerformance.LowLevel/Collections/UnTypedArray.cs index 742deb4..7571fe5 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnTypedArray.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnTypedArray.cs @@ -107,6 +107,124 @@ public unsafe struct UnTypedArray : IUnTypedCollection return _buffer; } + /// + /// Copies elements from an untyped source collection to a destination span of a specific type. + /// + /// Specifies the type of elements in the destination span, which must be unmanaged. + /// The typed span where the data will be copied to. + /// Thrown when the source collection size exceeds the destination span capacity. + public readonly void CopyTo(Span destination) + where T : unmanaged + { + var destSize = (uint)destination.Length * (uint)sizeof(T); + if (_size > destSize) + { + throw new ArgumentException("Source collection is larger than the destination span."); + } + + fixed (T* pDest = destination) + { + MemCpy(_buffer, pDest, _size); + } + } + + /// + /// Copies a range of bytes from an untyped source collection to a destination span, interpreting the bytes as elements of type T. + /// + /// Specifies the type of elements in the destination span, which must be unmanaged. + /// The typed span where the elements will be placed. + /// The byte offset in the source collection from which to start copying. + /// The element index in the destination span where copying will begin. + /// The number of elements of type T to copy. + /// Thrown when the specified range exceeds the bounds of the source collection or destination span. + public readonly void CopyTo(Span destination, uint sourceOffset, uint destinationIndex, uint length) + where T : unmanaged + { + var sizeOfElement = (uint)sizeof(T); + if (sourceOffset + (length * sizeOfElement) > _size || destinationIndex + length > destination.Length) + { + throw new ArgumentException("Source collection or destination span is too small for the specified range."); + } + + fixed (T* pDest = destination) + { + MemCpy((byte*)_buffer + sourceOffset, pDest + destinationIndex, length * sizeOfElement); + } + } + + /// + /// Copies elements from a typed source span to an untyped destination collection. + /// + /// Specifies the type of elements in the source span, which must be unmanaged. + /// The typed span containing the elements to be copied. + /// Thrown when the destination collection is smaller than the source span data size. + public readonly void CopyFrom(ReadOnlySpan source) + where T : unmanaged + { + var sourceSize = (uint)(source.Length * sizeof(T)); + + if (_size < sourceSize) + { + throw new ArgumentException("Destination collection is smaller than the source span."); + } + + fixed (T* pSrc = source) + { + MemCpy(pSrc, _buffer, sourceSize); + } + } + + /// + /// Copies a range of elements from a typed source span to an untyped destination collection at a specified byte offset. + /// + /// Specifies the type of elements in the source span, which must be unmanaged. + /// The typed span containing the elements to be copied. + /// The starting element index in the source span from which to begin copying. + /// The byte offset in the destination collection where the data will be placed. + /// The number of elements to copy from the source span. + /// Thrown when the specified range exceeds the bounds of the source span or destination collection. + public readonly void CopyFrom(ReadOnlySpan source, uint sourceIndex, uint destinationOffset, uint length) + where T : unmanaged + { + var sizeOfElement = (uint)sizeof(T); + if (sourceIndex + length > source.Length || destinationOffset + (length * sizeOfElement) > _size) + { + throw new ArgumentException("Source span or destination collection is too small for the specified range."); + } + + fixed (T* pSrc = source) + { + MemCpy(pSrc + sourceIndex, (byte*)_buffer + destinationOffset, length * sizeOfElement); + } + } + + + /// + /// Converts into a Span for efficient memory access. + /// + /// A Span that provides a view over the elements of the UnsafeCollection. + public readonly Span AsSpan() + { + return new(_buffer, (int)_size); + } + + /// + /// Creates a span over a contiguous region of elements in the specified unsafe collection, starting at the given index and covering the specified number of elements. + /// + /// The zero-based index of the first element in the collection to include in the span. Must be greater than or equal to zero and less than the number of elements in the collection. + /// The number of elements to include in the span. Must be greater than or equal to zero and the range defined by + /// and must not exceed the bounds of the collection. + /// A representing the specified region of the collection. + public readonly Span AsSpan(int start, int length) + { + if (start < 0 || length < 0 || (nuint)(start + length) > _size) + { + throw new ArgumentOutOfRangeException(nameof(start), "The specified range is out of bounds of the collection."); + } + + return new Span((byte*)_buffer + start, length); + } + /// public void Dispose() { diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeArray.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeArray.cs index 148409f..dcffb51 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeArray.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeArray.cs @@ -202,6 +202,20 @@ public unsafe struct UnsafeArray : IUnsafeCollection return _buffer; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + ThrowIfNotCreated(); + return new Span(_buffer, _count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan(int start, int length) + { + ThrowIfNotCreated(); + return new Span(_buffer + start, length); + } + /// /// Reinterprets the underlying buffer as an array of a different unmanaged type without copying the data. /// diff --git a/Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs b/Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs index 455f86f..b4abe70 100644 --- a/Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs +++ b/Misaki.HighPerformance.LowLevel/Collections/UnsafeList.cs @@ -2,6 +2,7 @@ using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Contracts; using Misaki.HighPerformance.LowLevel.Utilities; +using System; using System.Collections; using System.Runtime.CompilerServices; @@ -366,6 +367,19 @@ public unsafe struct UnsafeList : IUnsafeCollection return _array.GetUnsafePtr(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan() + { + return _array.AsSpan(0, _count); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly Span AsSpan(int start, int length) + { + CheckIndexCount(start, length); + return _array.AsSpan(start, length); + } + public void Dispose() { _array.Dispose(); diff --git a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj index a64a7e5..a245cab 100644 --- a/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj +++ b/Misaki.HighPerformance.LowLevel/Misaki.HighPerformance.LowLevel.csproj @@ -6,7 +6,7 @@ enable True Misaki - 1.2.1 + 1.2.2 $(AssemblyVersion) True https://git.personalnas.com/Misaki/Misaki.HighPerformance.git diff --git a/Misaki.HighPerformance.LowLevel/Utilities/UnsafeCollectionExtensions.cs b/Misaki.HighPerformance.LowLevel/Utilities/UnsafeCollectionExtensions.cs index a5f3d73..d6fafe5 100644 --- a/Misaki.HighPerformance.LowLevel/Utilities/UnsafeCollectionExtensions.cs +++ b/Misaki.HighPerformance.LowLevel/Utilities/UnsafeCollectionExtensions.cs @@ -14,12 +14,13 @@ public static unsafe class UnsafeCollectionExtensions /// /// Copies elements from a source UnsafeCollection to a destination Span, ensuring both have the same size. /// + /// The type of the destination collection, which must implement . /// Specifies the type of elements being copied, which must be unmanaged. /// Represents the source collection from which elements are copied. /// Represents the target span where elements are copied to. /// Thrown when the sizes of the source collection and destination span do not match. - public static void CopyTo(this IUnsafeCollection source, Span destination) - where T : unmanaged + public static void CopyTo(this C source, Span destination) + where C: IUnsafeCollection where T : unmanaged { if (source.Count > destination.Length) { @@ -35,6 +36,7 @@ public static unsafe class UnsafeCollectionExtensions /// /// Copies a range of elements from a source collection to a destination span, ensuring both are adequately sized. /// + /// The type of the destination collection, which must implement . /// Specifies the type of elements being copied, which must be a value type. /// The collection from which elements are copied. /// The span where the elements will be copied to. @@ -42,8 +44,8 @@ public static unsafe class UnsafeCollectionExtensions /// The starting index in the destination span where the elements will be placed. /// The number of elements to copy from the source to the destination. /// Thrown when the specified range exceeds the bounds of the source collection or destination span. - public static void CopyTo(this IUnsafeCollection source, Span destination, uint sourceIndex, uint destinationIndex, uint length) - where T : unmanaged + public static void CopyTo(this C source, Span destination, uint sourceIndex, uint destinationIndex, uint length) + where C : IUnsafeCollection where T : unmanaged { if (sourceIndex + length > source.Count || destinationIndex + length > destination.Length) { @@ -56,62 +58,16 @@ public static unsafe class UnsafeCollectionExtensions } } - /// - /// Copies elements from an untyped source collection to a destination span of a specific type. - /// - /// Specifies the type of elements in the destination span, which must be unmanaged. - /// The untyped collection from which data is copied. - /// The typed span where the data will be copied to. - /// Thrown when the source collection size exceeds the destination span capacity. - public static void CopyTo(this IUnTypedCollection source, Span destination) - where T : unmanaged - { - var destSize = (uint)destination.Length * (uint)sizeof(T); - if (source.Size > destSize) - { - throw new ArgumentException("Source collection is larger than the destination span."); - } - - fixed (T* pDest = destination) - { - MemCpy(source.GetUnsafePtr(), pDest, source.Size); - } - } - - /// - /// Copies a range of bytes from an untyped source collection to a destination span, interpreting the bytes as elements of type T. - /// - /// Specifies the type of elements in the destination span, which must be unmanaged. - /// The untyped collection from which bytes are copied. - /// The typed span where the elements will be placed. - /// The byte offset in the source collection from which to start copying. - /// The element index in the destination span where copying will begin. - /// The number of elements of type T to copy. - /// Thrown when the specified range exceeds the bounds of the source collection or destination span. - public static void CopyTo(this IUnTypedCollection source, Span destination, uint sourceOffset, uint destinationIndex, uint length) - where T : unmanaged - { - var sizeOfElement = (uint)sizeof(T); - if (sourceOffset + (length * sizeOfElement) > source.Size || destinationIndex + length > destination.Length) - { - throw new ArgumentException("Source collection or destination span is too small for the specified range."); - } - - fixed (T* pDest = destination) - { - MemCpy((byte*)source.GetUnsafePtr() + sourceOffset, pDest + destinationIndex, length * sizeOfElement); - } - } - /// /// Copies elements from a source span to a destination unsafe collection, ensuring both have the same size. /// + /// The type of the destination collection, which must implement . /// Specifies the type of elements being copied, which must be unmanaged. /// Represents the unsafe collection that will receive the copied elements. /// Represents the span containing the elements to be copied to the unsafe collection. /// Thrown when the source span and destination collection have different sizes. - public static void CopyFrom(this IUnsafeCollection destination, ReadOnlySpan source) - where T : unmanaged + public static void CopyFrom(this C destination, ReadOnlySpan source) + where C : IUnsafeCollection where T : unmanaged { if (destination.Count < source.Length) { @@ -127,6 +83,7 @@ public static unsafe class UnsafeCollectionExtensions /// /// Copies a specified range of elements from a source span to a destination collection. /// + /// The type of the destination collection, which must implement . /// Represents the type of elements being copied, which must be unmanaged. /// The collection where elements will be copied to. /// The span containing the elements to be copied. @@ -134,8 +91,8 @@ public static unsafe class UnsafeCollectionExtensions /// The starting index in the destination collection where the elements will be placed. /// The number of elements to copy from the source span to the destination collection. /// Thrown when the specified range exceeds the bounds of the source span or destination collection. - public static void CopyFrom(this IUnsafeCollection destination, ReadOnlySpan source, uint sourceIndex, uint destinationIndex, uint length) - where T : unmanaged + public static void CopyFrom(this C destination, ReadOnlySpan source, uint sourceIndex, uint destinationIndex, uint length) + where C : IUnsafeCollection where T : unmanaged { if (sourceIndex + length > source.Length || destinationIndex + length > destination.Count) { @@ -148,106 +105,6 @@ public static unsafe class UnsafeCollectionExtensions } } - /// - /// Copies elements from a typed source span to an untyped destination collection. - /// - /// Specifies the type of elements in the source span, which must be unmanaged. - /// The untyped collection that will receive the copied data. - /// The typed span containing the elements to be copied. - /// Thrown when the destination collection is smaller than the source span data size. - public static void CopyFrom(this IUnTypedCollection destination, ReadOnlySpan source) - where T : unmanaged - { - var sourceSize = (uint)(source.Length * sizeof(T)); - - if (destination.Size < sourceSize) - { - throw new ArgumentException("Destination collection is smaller than the source span."); - } - - fixed (T* pSrc = source) - { - MemCpy(pSrc, destination.GetUnsafePtr(), sourceSize); - } - } - - /// - /// Copies a range of elements from a typed source span to an untyped destination collection at a specified byte offset. - /// - /// Specifies the type of elements in the source span, which must be unmanaged. - /// The untyped collection where the data will be copied to. - /// The typed span containing the elements to be copied. - /// The starting element index in the source span from which to begin copying. - /// The byte offset in the destination collection where the data will be placed. - /// The number of elements to copy from the source span. - /// Thrown when the specified range exceeds the bounds of the source span or destination collection. - public static void CopyFrom(this IUnTypedCollection destination, ReadOnlySpan source, uint sourceIndex, uint destinationOffset, uint length) - where T : unmanaged - { - var sizeOfElement = (uint)sizeof(T); - if (sourceIndex + length > source.Length || destinationOffset + (length * sizeOfElement) > destination.Size) - { - throw new ArgumentException("Source span or destination collection is too small for the specified range."); - } - - fixed (T* pSrc = source) - { - MemCpy(pSrc + sourceIndex, (byte*)destination.GetUnsafePtr() + destinationOffset, length * sizeOfElement); - } - } - - /// - /// Converts an UnsafeCollection into a Span for efficient memory access. - /// - /// Represents a type that can be stored in unmanaged memory. - /// The UnsafeCollection instance to be converted into a Span. - /// A Span that provides a view over the elements of the UnsafeCollection. - public static Span AsSpan(this IUnsafeCollection source) - where T : unmanaged - { - return new(source.GetUnsafePtr(), source.Count); - } - - /// - /// Creates a span over a contiguous region of elements in the specified unsafe collection, starting at the given - /// index and covering the specified number of elements. - /// - /// The type of elements in the collection. Must be an unmanaged type. - /// The unsafe collection from which to create the span. Must not be null. - /// The zero-based index of the first element in the collection to include in the span. Must be greater than or equal to zero and less than the number of elements in the collection. - /// The number of elements to include in the span. Must be greater than or equal to zero and the range defined by - /// and must not exceed the bounds of the collection. - /// A representing the specified region of the collection. - public static Span AsSpan(this IUnsafeCollection source, int start, int length) - where T : unmanaged - { - return new((T*)source.GetUnsafePtr() + start, length); - } - - /// - /// Converts an UnTypedCollection into a Span for efficient memory access. - /// - /// The UnTypedCollection instance to be converted into a Span. - /// A Span that provides a view over the elements of the UnsafeCollection. - public static Span AsSpan(this IUnTypedCollection source) - { - return new(source.GetUnsafePtr(), (int)source.Size); - } - - /// - /// Creates a span over a contiguous region of elements in the specified unsafe collection, starting at the given - /// index and covering the specified number of elements. - /// - /// The unsafe collection from which to create the span. Must not be null. - /// The zero-based index of the first element in the collection to include in the span. Must be greater than or equal to zero and less than the number of elements in the collection. - /// The number of elements to include in the span. Must be greater than or equal to zero and the range defined by - /// and must not exceed the bounds of the collection. - /// A representing the specified region of the collection. - public static Span AsSpan(this IUnTypedCollection source, int start, int length) - { - return new((byte*)source.GetUnsafePtr() + start, length); - } - /// /// Converts a managed array to an UnsafeArray by copying its elements to unmanaged memory. /// @@ -285,4 +142,33 @@ public static unsafe class UnsafeCollectionExtensions return list; } + + /// + /// Creates a new array containing all elements from the specified unsafe collection. + /// + /// The type of the source collection, which must implement . + /// The type of elements contained in the collection. Must be an unmanaged type. + /// The collection whose elements will be copied to the new array. Must not be null. + /// An array containing all elements from in their current order. + public static T[] ToArray(this C source) + where C : IUnsafeCollection where T : unmanaged + { + return new Span(source.GetUnsafePtr(), source.Count).ToArray(); + } + + /// + /// Creates a new containing the elements of the specified unsafe collection. + /// + /// The type of the source collection, which must implement . + /// The type of elements contained in the collection. Must be an unmanaged type. + /// The unsafe collection whose elements are to be copied to the new list. Must not be null. + /// A containing all elements from in their original order. + public static List ToList(this C source) + where C : IUnsafeCollection where T : unmanaged + { + var list = new List(source.Count); + var span = new Span(source.GetUnsafePtr(), source.Count); + span.CopyTo(CollectionsMarshal.AsSpan(list)); + return list; + } } \ No newline at end of file diff --git a/Misaki.HighPerformance.Test/Program.cs b/Misaki.HighPerformance.Test/Program.cs index 1ed7d89..794fbcf 100644 --- a/Misaki.HighPerformance.Test/Program.cs +++ b/Misaki.HighPerformance.Test/Program.cs @@ -40,9 +40,11 @@ // } //} +using Misaki.HighPerformance.LowLevel.Utilities; + var arr1 = new Misaki.HighPerformance.LowLevel.Collections.UnsafeArray(10, Misaki.HighPerformance.LowLevel.Buffer.Allocator.Persistent); var arr2 = arr1; - +arr2.CopyFrom(arr1.AsSpan()); arr1.Dispose(); try {