From c18bfccfecb3724ed8d274751e2cea38353f6d2f Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Tue, 7 Feb 2023 12:39:56 -0600 Subject: [PATCH 1/4] Add `IList<>` in NullArgumentTest --- MoreLinq.Test/NullArgumentTest.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MoreLinq.Test/NullArgumentTest.cs b/MoreLinq.Test/NullArgumentTest.cs index 9cd3ec8cd..8fbd6dd0c 100644 --- a/MoreLinq.Test/NullArgumentTest.cs +++ b/MoreLinq.Test/NullArgumentTest.cs @@ -223,6 +223,9 @@ public class Enumerable : IEnumerable #pragma warning disable CA1812 // Avoid uninstantiated internal classes + public sealed class List : System.Collections.Generic.List + { } + public sealed class OrderedEnumerable : Enumerable, System.Linq.IOrderedEnumerable { public System.Linq.IOrderedEnumerable CreateOrderedEnumerable(Func keySelector, IComparer? comparer, bool descending) From b9a2a30bcf9a71bdba1a4c18bee29f21c2987db1 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Tue, 7 Feb 2023 12:40:09 -0600 Subject: [PATCH 2/4] Implement `CopyTo` operator --- MoreLinq/CopyTo.cs | 172 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 MoreLinq/CopyTo.cs diff --git a/MoreLinq/CopyTo.cs b/MoreLinq/CopyTo.cs new file mode 100644 index 000000000..5e2fd4f80 --- /dev/null +++ b/MoreLinq/CopyTo.cs @@ -0,0 +1,172 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2023 Turning Code, LLC. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq +{ + using System; + using System.Collections.Generic; + + static partial class MoreEnumerable + { + /// + /// Copies the contents of a sequence into a provided array. + /// + /// + /// The type of elements of + /// The source sequence. + /// The array that is the destination of the elements copied from . + /// The number of elements actually copied. + /// is . + /// is not long enough to hold the data from + /// sequence. + /// + /// + /// All data from will be copied to if possible. If is shorter than , then any remaining elements will be untouched. If + /// is longer than , then an exception will be thrown. + /// + /// + /// This operator executes immediately. + /// + /// + public static int CopyTo(this IEnumerable source, TSource[] array) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (array == null) throw new ArgumentNullException(nameof(array)); + + return CopyTo(source, array, 0); + } + + static int CopyTo(IEnumerable source, TSource[] array, int index) + { + if (source is TSource[] arr) + { + arr.CopyTo(array, index); + return arr.Length; + } + else if (source is ICollection coll) + { + coll.CopyTo(array, index); + return coll.Count; + } + else if (source.TryAsCollectionLike() is CollectionLike c) + { + if (c.Count + index > array.Length) + throw new ArgumentException("Destination is not long enough.", nameof(array)); + + var i = index; + foreach (var el in source) + array[i++] = el; + + return i - index; + } + else + { + var i = index; + foreach (var el in source) + { + if (i >= array.Length) + throw new ArgumentException("Destination is not long enough.", nameof(array)); + + array[i++] = el; + } + + return i - index; + } + } + + /// + /// Copies the contents of a sequence into a provided list. + /// + /// + /// The type of elements of + /// The source sequence. + /// The list that is the destination of the elements copied from . + /// The number of elements actually copied. + /// is . + /// is . + /// + /// + /// All data from will be copied to , starting at position 0. + /// + /// + /// This operator executes immediately. + /// + /// + public static int CopyTo(this IEnumerable source, IList list) + { + return source.CopyTo(list, 0); + } + + /// + /// Copies the contents of a sequence into a provided list. + /// + /// + /// The type of elements of + /// The source sequence. + /// The list that is the destination of the elements copied from . + /// The position in at which to start copying data + /// The number of elements actually copied. + /// is . + /// is . + /// + /// + /// All data from will be copied to , starting at position + /// . + /// + /// + /// This operator executes immediately. + /// + /// + public static int CopyTo(this IEnumerable source, IList list, int index) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (list == null) throw new ArgumentNullException(nameof(list)); + if (index < 0) throw new ArgumentOutOfRangeException(nameof(index)); + + if (list is TSource[] array) + { + return CopyTo(source, array, index); + } + else + { +#if NET6_0_OR_GREATER + if (list is List l + && source.TryAsCollectionLike() is CollectionLike c) + { + l.EnsureCapacity(c.Count + index); + } +#endif + + var i = index; + foreach (var el in source) + { + if (i < list.Count) + list[i] = el; + else + list.Add(el); + i++; + } + + return i - index; + } + } + } +} From df8b0cdde5305e40d16badfed4749dce2167da33 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Tue, 7 Feb 2023 12:40:19 -0600 Subject: [PATCH 3/4] Add tests for `CopyTo` operator --- MoreLinq.Test/CopyToTest.cs | 144 ++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 MoreLinq.Test/CopyToTest.cs diff --git a/MoreLinq.Test/CopyToTest.cs b/MoreLinq.Test/CopyToTest.cs new file mode 100644 index 000000000..1a4ce1a81 --- /dev/null +++ b/MoreLinq.Test/CopyToTest.cs @@ -0,0 +1,144 @@ +#region License and Terms +// MoreLINQ - Extensions to LINQ to Objects +// Copyright (c) 2009 Atif Aziz. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#endregion + +namespace MoreLinq.Test +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using NUnit.Framework; + + [TestFixture] + public class CopyToTest + { + [Test] + public void ThrowsOnNegativeIndex() + { + Assert.That( + () => new BreakingSequence().CopyTo(Array.Empty(), -1), + Throws.TypeOf()); + } + + [Test] + public void ThrowsOnTooMuchDataForArray() + { + Assert.That( + () => MoreEnumerable.From(() => 1).CopyTo(Array.Empty()), + Throws.TypeOf()); + Assert.That( + () => Enumerable.Range(1, 1).CopyTo(Array.Empty()), + Throws.TypeOf()); + Assert.That( + () => new List { 1 }.AsEnumerable().CopyTo(Array.Empty()), + Throws.TypeOf()); + Assert.That( + () => new List { 1 }.AsReadOnly().AsEnumerable().CopyTo(Array.Empty()), + Throws.TypeOf()); + } + + [Test] + public void ThrowsOnTooMuchDataForIListArray() + { + Assert.That( + () => MoreEnumerable.From(() => 1).CopyTo((IList)Array.Empty()), + Throws.TypeOf()); + Assert.That( + () => Enumerable.Range(1, 1).CopyTo((IList)Array.Empty()), + Throws.TypeOf()); + Assert.That( + () => new List { 1 }.AsEnumerable().CopyTo((IList)Array.Empty()), + Throws.TypeOf()); + Assert.That( + () => new List { 1 }.AsReadOnly().AsEnumerable().CopyTo((IList)Array.Empty()), + Throws.TypeOf()); + } + + [Test] + public void CopiesDataToArray() + { + var array = new int[4]; + + using var xs = TestingSequence.Of(1); + var cnt = xs.CopyTo(array); + array.AssertSequenceEqual(1, 0, 0, 0); + Assert.That(cnt, Is.EqualTo(1)); + + cnt = new List { 2 }.AsEnumerable().CopyTo(array, 1); + array.AssertSequenceEqual(1, 2, 0, 0); + Assert.That(cnt, Is.EqualTo(1)); + + cnt = Enumerable.Range(3, 1).CopyTo(array, 2); + array.AssertSequenceEqual(1, 2, 3, 0); + Assert.That(cnt, Is.EqualTo(1)); + + cnt = new[] { 4, }.AsEnumerable().CopyTo(array, 3); + array.AssertSequenceEqual(1, 2, 3, 4); + Assert.That(cnt, Is.EqualTo(1)); + } + + [Test] + public void CopiesDataToList() + { + var list = new List(); + + var cnt = new[] { 1, }.AsEnumerable().CopyTo(list); + list.AssertSequenceEqual(1); + Assert.That(cnt, Is.EqualTo(1)); + + using (var xs = TestingSequence.Of(2)) + { + cnt = xs.CopyTo(list, 1); + list.AssertSequenceEqual(1, 2); + Assert.That(cnt, Is.EqualTo(1)); + } + + using (var xs = TestingSequence.Of(3, 4)) + { + cnt = xs.AsEnumerable().CopyTo(list, 1); + list.AssertSequenceEqual(1, 3, 4); + Assert.That(cnt, Is.EqualTo(2)); + } + } + + [Test] + public void CopiesDataToIList() + { + var list = new Collection(); + + using (var xs = TestingSequence.Of(1)) + { + var cnt = xs.CopyTo(list); + list.AssertSequenceEqual(1); + Assert.That(cnt, Is.EqualTo(1)); + } + + using (var xs = TestingSequence.Of(2)) + { + var cnt = xs.CopyTo(list, 1); + list.AssertSequenceEqual(1, 2); + Assert.That(cnt, Is.EqualTo(1)); + } + + using (var xs = TestingSequence.Of(3, 4)) + { + var cnt = xs.CopyTo(list, 1); + list.AssertSequenceEqual(1, 3, 4); + Assert.That(cnt, Is.EqualTo(2)); + } + } + } +} From efed50ce054e61f37d1c86c3dd0220ed489db543 Mon Sep 17 00:00:00 2001 From: Stuart Turner Date: Sun, 12 Feb 2023 18:06:37 -0600 Subject: [PATCH 4/4] Update `Extensions.g.cs` with `CopyTo` --- MoreLinq/Extensions.g.cs | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/MoreLinq/Extensions.g.cs b/MoreLinq/Extensions.g.cs index a5ca2ac81..8f0ff09bc 100644 --- a/MoreLinq/Extensions.g.cs +++ b/MoreLinq/Extensions.g.cs @@ -1123,6 +1123,84 @@ public static void Consume(this IEnumerable source) } + /// CopyTo extension. + + [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")] + public static partial class CopyToExtension + { + + /// + /// Copies the contents of a sequence into a provided list. + /// + /// + /// The type of elements of + /// The source sequence. + /// The list that is the destination of the elements copied from . + /// The number of elements actually copied. + /// is . + /// is . + /// + /// + /// All data from will be copied to , starting at position 0. + /// + /// + /// This operator executes immediately. + /// + /// + public static int CopyTo(this IEnumerable source, IList list) + => MoreEnumerable.CopyTo(source, list); + /// + /// Copies the contents of a sequence into a provided array. + /// + /// + /// The type of elements of + /// The source sequence. + /// The array that is the destination of the elements copied from . + /// The number of elements actually copied. + /// is . + /// is not long enough to hold the data from + /// sequence. + /// + /// + /// All data from will be copied to if possible. If is shorter than , then any remaining elements will be untouched. If + /// is longer than , then an exception will be thrown. + /// + /// + /// This operator executes immediately. + /// + /// + public static int CopyTo(this IEnumerable source, TSource[] array) + => MoreEnumerable.CopyTo(source, array); + + /// + /// Copies the contents of a sequence into a provided list. + /// + /// + /// The type of elements of + /// The source sequence. + /// The list that is the destination of the elements copied from . + /// The position in at which to start copying data + /// The number of elements actually copied. + /// is . + /// is . + /// + /// + /// All data from will be copied to , starting at position + /// . + /// + /// + /// This operator executes immediately. + /// + /// + public static int CopyTo(this IEnumerable source, IList list, int index) + => MoreEnumerable.CopyTo(source, list, index); + + } + /// CountBetween extension. [GeneratedCode("MoreLinq.ExtensionsGenerator", "1.0.0.0")]