Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add List<T>.CopyTo Span<T> APIs #891

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/libraries/System.Collections/ref/System.Collections.cs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,8 @@ public partial class List<T> : System.Collections.Generic.ICollection<T>, System
public void CopyTo(int index, T[] array, int arrayIndex, int count) { }
public void CopyTo(T[] array) { }
public void CopyTo(T[] array, int arrayIndex) { }
public void CopyTo(int sourceIndex, int count, Span<T> destination) { }
public void CopyTo(Span<T> destination) { }
public bool Exists(System.Predicate<T> match) { throw null; }
[return: System.Diagnostics.CodeAnalysis.MaybeNullAttribute]
public T Find(System.Predicate<T> match) { throw null; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace System.Collections.Tests
Expand Down Expand Up @@ -53,6 +54,159 @@ protected void VerifyList(List<T> list, List<T> expectedItems)

#endregion

#region CopyTo_Span

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_NegativeSourceIndex_ThrowsArgumentOutOfRangeException(int count)
{
List<T> list = GenericListFactory(count);

Assert.Throws<ArgumentOutOfRangeException>(() => { Span<T> span = new T[count]; list.CopyTo(-1, count, span); });
Assert.Throws<ArgumentOutOfRangeException>(() => { Span<T> span = new T[count]; list.CopyTo(int.MinValue, count, span); });
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_NegativeCount_ThrowsArgumentOutOfRangeException(int count)
{
List<T> list = GenericListFactory(count);

Assert.Throws<ArgumentOutOfRangeException>(() => { Span<T> span = new T[count]; list.CopyTo(0, -1, span); });
Assert.Throws<ArgumentOutOfRangeException>(() => { Span<T> span = new T[count]; list.CopyTo(0, int.MinValue, span); });
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_SourceIndexLargerThanListCount_ThrowsArgumentOutOfRangeException(int count)
{
List<T> list = GenericListFactory(count);

Assert.Throws<ArgumentOutOfRangeException>(() => { Span<T> span = new T[count]; list.CopyTo(count + 1, 0, span); });
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_CountLargerThanListCount_ThrowsArgumentOutOfRangeException(int count)
{
List<T> list = GenericListFactory(count);

Assert.Throws<ArgumentOutOfRangeException>(() => { Span<T> span = new T[count]; list.CopyTo(0, count + 1, span); });
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_SourceIndexPlusCountLargerThanListCount_ThrowsArgumentOutOfRangeException(int count)
{
List<T> list = GenericListFactory(count);

Assert.Throws<ArgumentOutOfRangeException>(() => { Span<T> span = new T[count]; list.CopyTo(1, count, span); });
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_NotEnoughSpaceInSpan_ThrowsArgumentException(int count)
{
if (count > 0)
{
List<T> list = GenericListFactory(count);

Assert.Throws<ArgumentException>("destination", () => { Span<T> span = new T[count - 1]; list.CopyTo(0, count, span); });
}
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_ExactlyEnoughSpaceInArray(int count)
{
List<T> list = GenericListFactory(count);
Span<T> span = new T[count];
list.CopyTo(0, count, span);
Assert.True(Enumerable.SequenceEqual(list, span.ToArray()));

span = new T[count];
list.CopyTo(span);
Assert.True(Enumerable.SequenceEqual(list, span.ToArray()));
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_SpanIsLargerThanCollection(int count)
{
List<T> list = GenericListFactory(count);
Span<T> span = new T[count * 3 / 2];
list.CopyTo(span);

Assert.True(Enumerable.SequenceEqual(list, span.Slice(0, count).ToArray()));
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_SourceIndexGreaterThanZero(int count)
{
if (count > 0)
{
List<T> list = GenericListFactory(count);
Span<T> span = new T[count];
list.CopyTo(1, count - 1, span);

Assert.True(Enumerable.SequenceEqual(list.Skip(1), span.Slice(0, count - 1).ToArray()));
}
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_CountSmallerListCount(int count)
{
if (count > 0)
{
List<T> list = GenericListFactory(count);
Span<T> span = new T[count];
list.CopyTo(0, count - 1, span);

Assert.True(Enumerable.SequenceEqual(list.Take(count - 1), span.Slice(0, count - 1).ToArray()));
}
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_SourceIndexGreaterThanZeroCountSmallerListCount(int count)
{
if (count > 1)
{
List<T> list = GenericListFactory(count);
Span<T> span = new T[count];
list.CopyTo(1, count - 2, span);

Assert.True(Enumerable.SequenceEqual(list.Skip(1).Take(count - 2), span.Slice(0, count - 2).ToArray()));
}
}

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyToSpan_EnsureDoesntCopyMoreThanListCount(int count)
{
List<T> list = GenericListFactory(count);
list.Capacity = count + 1;

Assert.Throws<ArgumentOutOfRangeException>(() => { Span<T> span = new T[count + 1]; list.CopyTo(0, count + 1, span); });

Span<T> span = new T[count + 1];
T nonDefaultValue;
do
{
nonDefaultValue = CreateT(1);
}
while (EqualityComparer<T>.Default.Equals(nonDefaultValue, default));

span[count] = nonDefaultValue;

list.CopyTo(span);
Assert.Equal(span[count], nonDefaultValue);

}
#endregion

[Theory]
[MemberData(nameof(ValidCollectionSizes))]
public void CopyTo_ArgumentValidity(int count)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,31 @@ public void CopyTo(T[] array, int arrayIndex)
Array.Copy(_items, 0, array, arrayIndex, _size);
}

public void CopyTo(int sourceIndex, int count, Span<T> destination)
felipepessoto marked this conversation as resolved.
Show resolved Hide resolved
{
felipepessoto marked this conversation as resolved.
Show resolved Hide resolved
if (sourceIndex < 0)
felipepessoto marked this conversation as resolved.
Show resolved Hide resolved
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceIndex, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
}

if (count < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
}

if (_size - sourceIndex < count)
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}

_items.AsSpan(sourceIndex, count).CopyTo(destination);
}

public void CopyTo(Span<T> destination)
{
_items.AsSpan(0, _size).CopyTo(destination);
}

// Ensures that the capacity of this list is at least the given minimum
// value. If the current capacity of the list is less than min, the
// capacity is increased to twice the current capacity or to min,
Expand Down