feat(unsafe-collections)!: move span/array copy methods to core

CopyTo, CopyFrom, ToArray, and ToList are now implemented as instance methods on UnsafeArray<T> and UnsafeList<T>, replacing the previous extension methods. This change enables more direct and efficient copying between unsafe collections and managed spans, arrays, and lists, with overloads for ranges and resizing as needed. Extension methods for these operations have been removed. Minor cleanup and usage updates are included.

BREAKING CHANGE: Extension methods for copying and converting between unsafe collections and spans/arrays/lists have been removed. Use the new instance methods instead.
This commit is contained in:
2026-03-28 11:28:50 +09:00
parent e1c9a3781b
commit 04dd7222d9
6 changed files with 186 additions and 135 deletions

View File

@@ -286,6 +286,9 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
/// <summary>
/// Reinterprets the underlying buffer as an array of a different unmanaged type without copying the data.
/// </summary>
/// <remarks>
/// The returned UnsafeArray<U> shares the same memory as the original array, and does not own the memory.
/// </remarks>
/// <typeparam name="U">The unmanaged type to reinterpret the buffer as.</typeparam>
/// <returns>An UnsafeArray<U> that views the same memory as the original array, but as elements of type U.</returns>
/// <exception cref="InvalidOperationException">Thrown if the total size of the buffer in bytes is not a multiple of the size of type U.</exception>
@@ -304,6 +307,92 @@ public unsafe struct UnsafeArray<T> : IUnsafeCollection<T>
return new UnsafeArray<U>((U*)_buffer, newCount);
}
/// <summary>
/// Copies elements from a source UnsafeCollection to a destination Span, ensuring both have the same size.
/// </summary>
/// <param name="destination">Represents the target span where elements are copied to.</param>
public readonly void CopyTo(Span<T> destination)
{
var size = Math.Min(destination.Length, Count);
fixed (T* pDest = destination)
{
MemCpy(pDest, _buffer, (uint)(size * sizeof(T)));
}
}
/// <summary>
/// Copies a range of elements from a source collection to a destination span, ensuring both are adequately sized.
/// </summary>
/// <param name="destination">The span where the elements will be copied to.</param>
/// <param name="sourceIndex">The starting index in the source collection for the copy operation.</param>
/// <param name="destinationIndex">The starting index in the destination span where the elements will be placed.</param>
/// <param name="length">The number of elements to copy from the source to the destination.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified range exceeds the bounds of the source collection or destination span.</exception>
public readonly void CopyTo(Span<T> destination, int sourceIndex, int destinationIndex, int length)
{
if (sourceIndex + length > _count || destinationIndex + length > destination.Length)
{
throw new ArgumentOutOfRangeException(nameof(length), "Source collection or destination span is too small for the specified range.");
}
fixed (T* pDest = destination)
{
MemCpy(pDest + destinationIndex, _buffer + sourceIndex, (nuint)(length * sizeof(T)));
}
}
/// <summary>
/// Copies elements from a source span to a destination unsafe collection, ensuring both have the same size.
/// </summary>
/// <param name="source">Represents the span containing the elements to be copied to the unsafe collection.</param>
public void CopyFrom(ReadOnlySpan<T> source)
{
if (_count < source.Length)
{
Resize(source.Length);
}
fixed (T* pSrc = source)
{
MemCpy(_buffer, pSrc, (nuint)(source.Length * sizeof(T)));
}
}
/// <summary>
/// Copies a specified range of elements from a source span to a destination collection.
/// </summary>
/// <param name="source">The span containing the elements to be copied.</param>
/// <param name="sourceIndex">The starting index in the source span from which to begin copying.</param>
/// <param name="destinationIndex">The starting index in the destination collection where the elements will be placed.</param>
/// <param name="length">The number of elements to copy from the source span to the destination collection.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified range exceeds the bounds of the source span or destination collection.</exception>
public void CopyFrom(ReadOnlySpan<T> source, int sourceIndex, int destinationIndex, int length)
{
if (sourceIndex + length > source.Length)
{
throw new ArgumentOutOfRangeException(nameof(length), "Source span or destination collection is too small for the specified range.");
}
if (destinationIndex + length > _count)
{
Resize(destinationIndex + length);
}
fixed (T* pSrc = source)
{
MemCpy(_buffer + destinationIndex, pSrc + sourceIndex, (nuint)(length * sizeof(T)));
}
}
/// <summary>
/// Creates a new array containing all elements.
/// </summary>
/// <returns>An array containing all elements.</returns>
public readonly T[] ToArray()
{
return new Span<T>(_buffer, _count).ToArray();
}
/// <inheritdoc/>
public void Dispose()
{

View File

@@ -118,7 +118,7 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
/// Use <see cref="AsParallelWriter"/> to create a parallel writer for a list.
/// The list must live at least as long as the parallel writer, and the parallel writer must not be used after the list is disposed.
/// </remarks>
public readonly unsafe struct ParallelWriter
public readonly struct ParallelWriter
{
public readonly UnsafeList<T>* listData;
@@ -515,17 +515,102 @@ public unsafe struct UnsafeList<T> : IUnsafeCollection<T>
return _array.AsSpan(start, length);
}
/// <summary>
/// Copies elements from a source UnsafeCollection to a destination Span, ensuring both have the same size.
/// </summary>
/// <param name="destination">Represents the target span where elements are copied to.</param>
public readonly void CopyTo(Span<T> destination)
{
var size = Math.Min(destination.Length, Count);
fixed (T* pDest = destination)
{
MemCpy(pDest, _array.GetUnsafePtr(), (uint)(size * sizeof(T)));
}
}
/// <summary>
/// Copies a range of elements from a source collection to a destination span, ensuring both are adequately sized.
/// </summary>
/// <param name="destination">The span where the elements will be copied to.</param>
/// <param name="sourceIndex">The starting index in the source collection for the copy operation.</param>
/// <param name="destinationIndex">The starting index in the destination span where the elements will be placed.</param>
/// <param name="length">The number of elements to copy from the source to the destination.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified range exceeds the bounds of the source collection or destination span.</exception>
public readonly void CopyTo(Span<T> destination, int sourceIndex, int destinationIndex, int length)
{
if (sourceIndex + length > _count || destinationIndex + length > destination.Length)
{
throw new ArgumentOutOfRangeException(nameof(length), "Source collection or destination span is too small for the specified range.");
}
fixed (T* pDest = destination)
{
MemCpy(pDest + destinationIndex, (byte*)_array.GetUnsafePtr() + sourceIndex * sizeof(T), (nuint)(length * sizeof(T)));
}
}
/// <summary>
/// Copies elements from a source span to a destination unsafe collection, ensuring both have the same size.
/// </summary>
/// <param name="source">Represents the span containing the elements to be copied to the unsafe collection.</param>
public void CopyFrom(ReadOnlySpan<T> source)
{
if (_count < source.Length)
{
Resize(source.Length);
}
fixed (T* pSrc = source)
{
MemCpy(_array.GetUnsafePtr(), pSrc, (nuint)(source.Length * sizeof(T)));
}
}
/// <summary>
/// Copies a specified range of elements from a source span to a destination collection.
/// </summary>
/// <param name="source">The span containing the elements to be copied.</param>
/// <param name="sourceIndex">The starting index in the source span from which to begin copying.</param>
/// <param name="destinationIndex">The starting index in the destination collection where the elements will be placed.</param>
/// <param name="length">The number of elements to copy from the source span to the destination collection.</param>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the specified range exceeds the bounds of the source span or destination collection.</exception>
public void CopyFrom(ReadOnlySpan<T> source, int sourceIndex, int destinationIndex, int length)
{
if (sourceIndex + length > source.Length)
{
throw new ArgumentOutOfRangeException(nameof(length), "Source span or destination collection is too small for the specified range.");
}
if (destinationIndex + length > _count)
{
Resize(destinationIndex + length);
}
fixed (T* pSrc = source)
{
MemCpy((byte*)_array.GetUnsafePtr() + destinationIndex * sizeof(T), pSrc + sourceIndex, (nuint)(length * sizeof(T)));
}
}
/// <summary>
/// Creates a new <see cref="List{T}"/> containing the elements.
/// </summary>
/// <returns>A <see cref="List{T}"/> containing all elements.</returns>
public readonly List<T> ToList()
{
var list = new List<T>(_count);
var span = new Span<T>(_array.GetUnsafePtr(), _count);
list.AddRange(span);
return list;
}
public void Dispose()
{
_array.Dispose();
_count = 0;
}
public static implicit operator UnsafeArray<T>(UnsafeList<T> list)
{
return list.AsUnsafeArray();
}
public static implicit operator ReadOnlyUnsafeCollection<T>(UnsafeList<T> list)
{
return list.AsReadOnly();