diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs index 5d8dee3c2b8c85..ea320e00a37bfc 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs @@ -798,8 +798,7 @@ public int IndexOf(T item, int startIndex, int count, IEqualityComparer? equa return -1; } - Requires.Range(startIndex >= 0 && startIndex <= this.Count, nameof(startIndex)); - Requires.Range(count >= 0 && (uint)(startIndex + count) <= (uint)this.Count, nameof(count)); + Requires.ValidateRange(startIndex, count, this.Count, nameof(startIndex)); equalityComparer ??= EqualityComparer.Default; if (equalityComparer == EqualityComparer.Default) @@ -1002,8 +1001,7 @@ public void Sort(int index, int count, IComparer? comparer) { // Don't rely on Array.Sort's argument validation since our internal array may exceed // the bounds of the publicly addressable region. - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0 && index + count <= this.Count, nameof(count)); + Requires.ValidateRange(index, count, this.Count); if (count > 1) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs index 1759263bdf6276..9bdf320978a7c4 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs @@ -173,8 +173,7 @@ public int IndexOf(T item, int startIndex, int count, IEqualityComparer? equa return -1; } - Requires.Range(startIndex >= 0 && startIndex <= self.Length, nameof(startIndex)); - Requires.Range(count >= 0 && (uint)(startIndex + count) <= (uint)self.Length, nameof(count)); + Requires.ValidateRange(startIndex, count, self.Length, nameof(startIndex)); equalityComparer ??= EqualityComparer.Default; if (equalityComparer == EqualityComparer.Default) @@ -826,8 +825,7 @@ public ImmutableArray Sort(int index, int count, IComparer? comparer) { ImmutableArray self = this; self.ThrowNullRefIfNotInitialized(); - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0 && index + count <= self.Length, nameof(count)); + Requires.ValidateRange(index, count, self.Length); // 0 and 1 element arrays don't need to be sorted. if (count > 1) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs index a78409ee75c6a8..0521316838b91f 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.Builder.cs @@ -329,8 +329,7 @@ void IDictionary.Remove(object key) void ICollection.CopyTo(Array array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (KeyValuePair item in this) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.cs index db90561410c6f5..447c5a81768a38 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableDictionary_2.cs @@ -616,8 +616,7 @@ bool ICollection>.Remove(KeyValuePair i void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (KeyValuePair item in this) { @@ -756,8 +755,7 @@ void IDictionary.Clear() void ICollection.CopyTo(Array array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (KeyValuePair item in this) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.Builder.cs index 85c7cc72e20e02..c9d98aeab1ce12 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.Builder.cs @@ -393,8 +393,7 @@ void ICollection.Add(T item) void ICollection.CopyTo(T[] array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (T item in this) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.cs index c6b6f3140e4cc2..3669f74f13b0ec 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet_1.cs @@ -492,8 +492,7 @@ bool ICollection.IsReadOnly void ICollection.CopyTo(T[] array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (T item in this) { @@ -537,8 +536,7 @@ bool ICollection.Remove(T item) void ICollection.CopyTo(Array array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (T item in this) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList.cs index 43c0b23bc954cf..52b5feb60980fb 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList.cs @@ -298,11 +298,6 @@ public static int LastIndexOf(this IImmutableList list, T item, int startI { Requires.NotNull(list, nameof(list)); - if (list.Count == 0 && startIndex == 0) - { - return -1; - } - return list.LastIndexOf(item, startIndex, startIndex + 1, EqualityComparer.Default); } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs index 77021fc61681d7..aedb36eabbad0c 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Builder.cs @@ -54,6 +54,7 @@ public sealed class Builder : IList, IList, IReadOnlyList internal Builder(ImmutableList list) { Requires.NotNull(list, nameof(list)); + _root = list._root; _immutable = list; } @@ -125,10 +126,14 @@ public T this[int index] { get { + Requires.Range(index >= 0 && index < this.Count, nameof(index)); + return this.Root.ItemRef(index); } set { + Requires.Range(index >= 0 && index < this.Count, nameof(index)); + this.Root = this.Root.ReplaceAt(index, value); } } @@ -140,6 +145,8 @@ public T this[int index] /// A read-only reference to the value at the specified index. public ref readonly T ItemRef(int index) { + Requires.Range(index >= 0 && index < this.Count, nameof(index)); + return ref this.Root.ItemRef(index); } @@ -160,6 +167,8 @@ public int IndexOf(T item) /// public void Insert(int index, T item) { + Requires.Range(index >= 0 && index <= this.Count, nameof(index)); + this.Root = this.Root.Insert(index, item); } @@ -168,6 +177,8 @@ public void Insert(int index, T item) /// public void RemoveAt(int index) { + Requires.Range(index >= 0 && index < this.Count, nameof(index)); + this.Root = this.Root.RemoveAt(index); } @@ -270,7 +281,13 @@ public void ForEach(Action action) /// copied from ImmutableList<T>. The System.Array must have /// zero-based indexing. /// - public void CopyTo(T[] array) => _root.CopyTo(array); + public void CopyTo(T[] array) + { + Requires.NotNull(array, nameof(array)); + Requires.Range(array.Length >= this.Count, nameof(array)); + + _root.CopyTo(array); + } /// /// Copies the entire ImmutableList<T> to a compatible one-dimensional @@ -284,7 +301,13 @@ public void ForEach(Action action) /// /// The zero-based index in array at which copying begins. /// - public void CopyTo(T[] array, int arrayIndex) => _root.CopyTo(array, arrayIndex); + public void CopyTo(T[] array, int arrayIndex) + { + Requires.NotNull(array, nameof(array)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); + + _root.CopyTo(array, arrayIndex); + } /// /// Copies a range of elements from the ImmutableList<T> to @@ -302,7 +325,14 @@ public void ForEach(Action action) /// /// The zero-based index in array at which copying begins. /// The number of elements to copy. - public void CopyTo(int index, T[] array, int arrayIndex, int count) => _root.CopyTo(index, array, arrayIndex, count); + public void CopyTo(int index, T[] array, int arrayIndex, int count) + { + Requires.NotNull(array, nameof(array)); + Requires.ValidateRange(index, count, this.Count); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + count) <= (uint)array.Length, nameof(arrayIndex)); + + _root.CopyTo(index, array, arrayIndex, count); + } /// /// Creates a shallow copy of a range of elements in the source ImmutableList<T>. @@ -319,9 +349,8 @@ public void ForEach(Action action) /// public ImmutableList GetRange(int index, int count) { - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0, nameof(count)); - Requires.Range(index + count <= this.Count, nameof(count)); + Requires.ValidateRange(index, count, this.Count); + return ImmutableList.WrapNode(Node.NodeTreeFromList(this, index, count)); } @@ -343,6 +372,7 @@ public ImmutableList GetRange(int index, int count) public ImmutableList ConvertAll(Func converter) { Requires.NotNull(converter, nameof(converter)); + return ImmutableList.WrapNode(_root.ConvertAll(converter)); } @@ -359,7 +389,12 @@ public ImmutableList ConvertAll(Func converter) /// that match the conditions defined by the specified predicate; otherwise, /// false. /// - public bool Exists(Predicate match) => _root.Exists(match); + public bool Exists(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.Exists(match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -373,7 +408,12 @@ public ImmutableList ConvertAll(Func converter) /// The first element that matches the conditions defined by the specified predicate, /// if found; otherwise, the default value for type T. /// - public T? Find(Predicate match) => _root.Find(match); + public T? Find(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.Find(match); + } /// /// Retrieves all the elements that match the conditions defined by the specified @@ -388,7 +428,12 @@ public ImmutableList ConvertAll(Func converter) /// the conditions defined by the specified predicate, if found; otherwise, an /// empty ImmutableList<T>. /// - public ImmutableList FindAll(Predicate match) => _root.FindAll(match); + public ImmutableList FindAll(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.FindAll(match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -403,7 +448,12 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the first occurrence of an element that matches the /// conditions defined by match, if found; otherwise, -1. /// - public int FindIndex(Predicate match) => _root.FindIndex(match); + public int FindIndex(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.FindIndex(match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -417,7 +467,13 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the first occurrence of an element that matches the /// conditions defined by match, if found; otherwise, -1. /// - public int FindIndex(int startIndex, Predicate match) => _root.FindIndex(startIndex, match); + public int FindIndex(int startIndex, Predicate match) + { + Requires.NotNull(match, nameof(match)); + Requires.Range(startIndex >= 0 && startIndex <= this.Count, nameof(startIndex)); + + return _root.FindIndex(startIndex, match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -432,7 +488,13 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the first occurrence of an element that matches the /// conditions defined by match, if found; otherwise, -1. /// - public int FindIndex(int startIndex, int count, Predicate match) => _root.FindIndex(startIndex, count, match); + public int FindIndex(int startIndex, int count, Predicate match) + { + Requires.NotNull(match, nameof(match)); + Requires.ValidateRange(startIndex, count, this.Count, nameof(startIndex)); + + return _root.FindIndex(startIndex, count, match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -446,7 +508,12 @@ public ImmutableList ConvertAll(Func converter) /// The last element that matches the conditions defined by the specified predicate, /// if found; otherwise, the default value for type T. /// - public T? FindLast(Predicate match) => _root.FindLast(match); + public T? FindLast(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.FindLast(match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -461,7 +528,12 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the last occurrence of an element that matches the /// conditions defined by match, if found; otherwise, -1. /// - public int FindLastIndex(Predicate match) => _root.FindLastIndex(match); + public int FindLastIndex(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.FindLastIndex(match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -476,7 +548,13 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the last occurrence of an element that matches the /// conditions defined by match, if found; otherwise, -1. /// - public int FindLastIndex(int startIndex, Predicate match) => _root.FindLastIndex(startIndex, match); + public int FindLastIndex(int startIndex, Predicate match) + { + Requires.NotNull(match, nameof(match)); + Requires.Range(startIndex == 0 || (uint)startIndex < (uint)this.Count, nameof(startIndex)); + + return _root.FindLastIndex(startIndex, match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -494,7 +572,13 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the last occurrence of an element that matches the /// conditions defined by match, if found; otherwise, -1. /// - public int FindLastIndex(int startIndex, int count, Predicate match) => _root.FindLastIndex(startIndex, count, match); + public int FindLastIndex(int startIndex, int count, Predicate match) + { + Requires.NotNull(match, nameof(match)); + Requires.ValidateReverseRange(startIndex, count, this.Count, nameof(startIndex)); + + return _root.FindLastIndex(startIndex, count, match); + } /// /// Searches for the specified object and returns the zero-based index of the @@ -514,8 +598,12 @@ public ImmutableList ConvertAll(Func converter) /// elements in the ImmutableList<T> that extends from index /// to the last element, if found; otherwise, -1. /// - public int IndexOf(T item, int index) => - _root.IndexOf(item, index, this.Count - index, EqualityComparer.Default); + public int IndexOf(T item, int index) + { + Requires.Range((uint)index <= (uint)this.Count, nameof(index)); + + return _root.IndexOf(item, index, this.Count - index, EqualityComparer.Default); + } /// /// Searches for the specified object and returns the zero-based index of the @@ -539,7 +627,7 @@ public int IndexOf(T item, int index) => /// contains count number of elements, if found; otherwise, -1. /// public int IndexOf(T item, int index, int count) => - _root.IndexOf(item, index, count, EqualityComparer.Default); + this.IndexOf(item, index, count, EqualityComparer.Default); /// /// Searches for the specified object and returns the zero-based index of the @@ -566,8 +654,12 @@ public int IndexOf(T item, int index, int count) => /// elements in the ImmutableList<T> that starts at index and /// contains count number of elements, if found; otherwise, -1. /// - public int IndexOf(T item, int index, int count, IEqualityComparer? equalityComparer) => - _root.IndexOf(item, index, count, equalityComparer); + public int IndexOf(T item, int index, int count, IEqualityComparer? equalityComparer) + { + Requires.ValidateRange(index, count, this.Count); + + return _root.IndexOf(item, index, count, equalityComparer); + } /// /// Searches for the specified object and returns the zero-based index of the @@ -612,10 +704,7 @@ public int LastIndexOf(T item) /// public int LastIndexOf(T item, int startIndex) { - if (this.Count == 0 && startIndex == 0) - { - return -1; - } + Requires.Range(startIndex >= 0 && startIndex < this.Count, nameof(startIndex)); return _root.LastIndexOf(item, startIndex, startIndex + 1, EqualityComparer.Default); } @@ -638,7 +727,7 @@ public int LastIndexOf(T item, int startIndex) /// and ends at index, if found; otherwise, -1. /// public int LastIndexOf(T item, int startIndex, int count) => - _root.LastIndexOf(item, startIndex, count, EqualityComparer.Default); + this.LastIndexOf(item, startIndex, count, EqualityComparer.Default); /// /// Searches for the specified object and returns the zero-based index of the @@ -658,8 +747,12 @@ public int LastIndexOf(T item, int startIndex, int count) => /// in the ImmutableList<T> that contains count number of elements /// and ends at index, if found; otherwise, -1. /// - public int LastIndexOf(T item, int startIndex, int count, IEqualityComparer? equalityComparer) => - _root.LastIndexOf(item, startIndex, count, equalityComparer); + public int LastIndexOf(T item, int startIndex, int count, IEqualityComparer? equalityComparer) + { + Requires.ValidateReverseRange(startIndex, count, this.Count, nameof(startIndex)); + + return _root.LastIndexOf(item, startIndex, count, equalityComparer); + } /// /// Determines whether every element in the ImmutableList<T> @@ -674,7 +767,12 @@ public int LastIndexOf(T item, int startIndex, int count, IEqualityComparer? /// conditions defined by the specified predicate; otherwise, false. If the list /// has no elements, the return value is true. /// - public bool TrueForAll(Predicate match) => _root.TrueForAll(match); + public bool TrueForAll(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.TrueForAll(match); + } #endregion @@ -851,9 +949,7 @@ public void Reverse() /// The number of elements in the range to reverse. public void Reverse(int index, int count) { - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0, nameof(count)); - Requires.Range(index + count <= this.Count, nameof(count)); + Requires.ValidateRange(index, count, this.Count); this.Root = this.Root.Reverse(index, count); } @@ -878,6 +974,7 @@ public void Sort() public void Sort(Comparison comparison) { Requires.NotNull(comparison, nameof(comparison)); + this.Root = this.Root.Sort(comparison); } @@ -910,9 +1007,8 @@ public void Sort(IComparer? comparer) /// public void Sort(int index, int count, IComparer? comparer) { - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0, nameof(count)); - Requires.Range(index + count <= this.Count, nameof(count)); + Requires.ValidateRange(index, count, this.Count); + this.Root = this.Root.Sort(index, count, comparer); } @@ -993,6 +1089,8 @@ public int BinarySearch(T item, IComparer? comparer) /// public int BinarySearch(int index, int count, T item, IComparer? comparer) { + Requires.ValidateRange(index, count, this.Count); + return this.Root.BinarySearch(index, count, item, comparer); } @@ -1137,6 +1235,9 @@ void IList.Remove(object? value) /// The zero-based index in at which copying begins. void ICollection.CopyTo(Array array, int arrayIndex) { + Requires.NotNull(array, nameof(array)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); + this.Root.CopyTo(array, arrayIndex); } @@ -1178,6 +1279,7 @@ internal sealed class ImmutableListBuilderDebuggerProxy public ImmutableListBuilderDebuggerProxy(ImmutableList.Builder builder) { Requires.NotNull(builder, nameof(builder)); + _list = builder; } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs index c98d3719e21616..6da2ee0f98e9db 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.Node.cs @@ -81,8 +81,8 @@ private Node() /// Whether this node is prefrozen. private Node(T key, Node left, Node right, bool frozen = false) { - Requires.NotNull(left, nameof(left)); - Requires.NotNull(right, nameof(right)); + Debug.Assert(left is not null); + Debug.Assert(right is not null); Debug.Assert(!frozen || (left._frozen && right._frozen)); _key = key; @@ -149,7 +149,7 @@ internal T this[int index] { get { - Requires.Range(index >= 0 && index < this.Count, nameof(index)); + Debug.Assert(index >= 0 && index < this.Count); Debug.Assert(_left != null && _right != null); if (index < _left._count) @@ -173,7 +173,7 @@ internal T this[int index] /// A read-only reference to the element at the given position. internal ref readonly T ItemRef(int index) { - Requires.Range(index >= 0 && index < this.Count, nameof(index)); + Debug.Assert(index >= 0 && index < this.Count); return ref ItemRefUnchecked(index); } @@ -242,9 +242,9 @@ private ref readonly T ItemRefUnchecked(int index) /// The root of the created node tree. internal static Node NodeTreeFromList(IReadOnlyList items, int start, int length) { - Requires.NotNull(items, nameof(items)); - Requires.Range(start >= 0, nameof(start)); - Requires.Range(length >= 0, nameof(length)); + Debug.Assert(items is not null); + Debug.Assert(start >= 0); + Debug.Assert(length >= 0); if (length == 0) { @@ -302,7 +302,7 @@ internal Node Add(T key) /// The new tree. internal Node Insert(int index, T key) { - Requires.Range(index >= 0 && index <= this.Count, nameof(index)); + Debug.Assert(index >= 0 && index <= this.Count); if (this.IsEmpty) { @@ -330,7 +330,7 @@ internal Node Insert(int index, T key) /// The new tree. internal Node AddRange(IEnumerable keys) { - Requires.NotNull(keys, nameof(keys)); + Debug.Assert(keys is not null); if (this.IsEmpty) { @@ -367,8 +367,8 @@ internal Node AddRange(ReadOnlySpan keys) /// The new tree. internal Node InsertRange(int index, IEnumerable keys) { - Requires.Range(index >= 0 && index <= this.Count, nameof(index)); - Requires.NotNull(keys, nameof(keys)); + Debug.Assert(index >= 0 && index <= this.Count); + Debug.Assert(keys is not null); if (this.IsEmpty) { @@ -397,7 +397,7 @@ internal Node InsertRange(int index, IEnumerable keys) /// The new tree. internal Node RemoveAt(int index) { - Requires.Range(index >= 0 && index < this.Count, nameof(index)); + Debug.Assert(index >= 0 && index < this.Count); Debug.Assert(_left != null && _right != null); Node result; @@ -459,7 +459,7 @@ internal Node RemoveAt(int index) /// internal Node RemoveAll(Predicate match) { - Requires.NotNull(match, nameof(match)); + Debug.Assert(match is not null); ImmutableList.Node result = this; var enumerator = new Enumerator(result); @@ -497,7 +497,7 @@ internal Node RemoveAll(Predicate match) /// The new tree. internal Node ReplaceAt(int index, T value) { - Requires.Range(index >= 0 && index < this.Count, nameof(index)); + Debug.Assert(index >= 0 && index < this.Count); Debug.Assert(!this.IsEmpty); Node result; @@ -534,9 +534,8 @@ internal Node ReplaceAt(int index, T value) /// The reversed list. internal Node Reverse(int index, int count) { - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0, nameof(count)); - Requires.Range(index + count <= this.Count, nameof(index)); + Debug.Assert(index >= 0 && index <= this.Count); + Debug.Assert(count >= 0 && (uint)(index + count) <= (uint)this.Count); Node result = this; int start = index; @@ -571,7 +570,7 @@ internal Node Reverse(int index, int count) /// The sorted list. internal Node Sort(Comparison comparison) { - Requires.NotNull(comparison, nameof(comparison)); + Debug.Assert(comparison != null); // PERF: Eventually this might be reimplemented in a way that does not require allocating an array. var array = new T[this.Count]; @@ -608,9 +607,8 @@ internal Node Sort(Comparison comparison) /// The sorted list. internal Node Sort(int index, int count, IComparer? comparer) { - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0, nameof(count)); - Requires.Argument(index + count <= this.Count); + Debug.Assert(index >= 0 && index <= this.Count); + Debug.Assert(count >= 0 && (uint)(index + count) <= (uint)this.Count); // PERF: Eventually this might be reimplemented in a way that does not require allocating an array. var array = new T[this.Count]; @@ -650,8 +648,8 @@ internal Node Sort(int index, int count, IComparer? comparer) /// internal int BinarySearch(int index, int count, T item, IComparer? comparer) { - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0, nameof(count)); + Debug.Assert(index >= 0); + Debug.Assert(count >= 0); comparer ??= Comparer.Default; if (this.IsEmpty || count <= 0) @@ -759,8 +757,8 @@ internal int BinarySearch(int index, int count, T item, IComparer? comparer) /// internal int IndexOf(T item, int index, int count, IEqualityComparer? equalityComparer) { - Requires.Range(index >= 0 && index <= this.Count, nameof(index)); - Requires.Range(count >= 0 && (uint)(index + count) <= (uint)this.Count, nameof(count)); + Debug.Assert(index >= 0 && index <= this.Count); + Debug.Assert(count >= 0 && (uint)(index + count) <= (uint)this.Count); equalityComparer ??= EqualityComparer.Default; using (var enumerator = new Enumerator(this, startIndex: index, count: count)) @@ -799,14 +797,10 @@ internal int IndexOf(T item, int index, int count, IEqualityComparer? equalit /// internal int LastIndexOf(T item, int startIndex, int count, IEqualityComparer? equalityComparer) { - if (startIndex == 0 && count == 0) - { - return -1; - } - - Requires.Range(startIndex >= 0 && startIndex < this.Count, nameof(startIndex)); - Requires.Range(count >= 0 && count <= this.Count, nameof(count)); - Requires.Range(startIndex - count + 1 >= 0, nameof(count)); + Debug.Assert(startIndex >= 0); + Debug.Assert(startIndex == 0 || startIndex < this.Count); + Debug.Assert(count >= 0 && count <= this.Count); + Debug.Assert(startIndex - count + 1 >= 0); equalityComparer ??= EqualityComparer.Default; using (var enumerator = new Enumerator(this, startIndex: startIndex, count: count, reversed: true)) @@ -836,8 +830,8 @@ internal int LastIndexOf(T item, int startIndex, int count, IEqualityComparer /// internal void CopyTo(T[] array) { - Requires.NotNull(array, nameof(array)); - Requires.Range(array.Length >= this.Count, nameof(array)); + Debug.Assert(array != null); + Debug.Assert(array.Length >= this.Count); int index = 0; foreach (T element in this) @@ -860,9 +854,8 @@ internal void CopyTo(T[] array) /// internal void CopyTo(T[] array, int arrayIndex) { - Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Debug.Assert(array != null); + Debug.Assert(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length); foreach (T element in this) { @@ -888,12 +881,10 @@ internal void CopyTo(T[] array, int arrayIndex) /// The number of elements to copy. internal void CopyTo(int index, T[] array, int arrayIndex, int count) { - Requires.NotNull(array, nameof(array)); - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0, nameof(count)); - Requires.Range(index + count <= this.Count, nameof(count)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(arrayIndex + count <= array.Length, nameof(arrayIndex)); + Debug.Assert(array != null); + Debug.Assert(index >= 0 && index <= this.Count); + Debug.Assert(count >= 0 && (uint)(index + count) <= (uint)this.Count); + Debug.Assert(arrayIndex >= 0 && (uint)(arrayIndex + count) <= (uint)array.Length); using (var enumerator = new Enumerator(this, startIndex: index, count: count)) { @@ -911,9 +902,8 @@ internal void CopyTo(int index, T[] array, int arrayIndex, int count) /// The zero-based index in at which copying begins. internal void CopyTo(Array array, int arrayIndex) { - Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Debug.Assert(array != null); + Debug.Assert(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length); foreach (T element in this) { @@ -962,7 +952,7 @@ internal ImmutableList.Node ConvertAll(Func conver /// internal bool TrueForAll(Predicate match) { - Requires.NotNull(match, nameof(match)); + Debug.Assert(match != null); foreach (T item in this) { @@ -990,7 +980,7 @@ internal bool TrueForAll(Predicate match) /// internal bool Exists(Predicate match) { - Requires.NotNull(match, nameof(match)); + Debug.Assert(match != null); foreach (T item in this) { @@ -1017,7 +1007,7 @@ internal bool Exists(Predicate match) /// internal T? Find(Predicate match) { - Requires.NotNull(match, nameof(match)); + Debug.Assert(match != null); foreach (T item in this) { @@ -1045,7 +1035,7 @@ internal bool Exists(Predicate match) /// internal ImmutableList FindAll(Predicate match) { - Requires.NotNull(match, nameof(match)); + Debug.Assert(match != null); if (this.IsEmpty) { @@ -1083,7 +1073,7 @@ internal ImmutableList FindAll(Predicate match) /// internal int FindIndex(Predicate match) { - Requires.NotNull(match, nameof(match)); + Debug.Assert(match != null); return this.FindIndex(0, _count, match); } @@ -1102,8 +1092,8 @@ internal int FindIndex(Predicate match) /// internal int FindIndex(int startIndex, Predicate match) { - Requires.NotNull(match, nameof(match)); - Requires.Range(startIndex >= 0 && startIndex <= this.Count, nameof(startIndex)); + Debug.Assert(match != null); + Debug.Assert(startIndex >= 0 && startIndex <= this.Count); return this.FindIndex(startIndex, this.Count - startIndex, match); } @@ -1123,10 +1113,9 @@ internal int FindIndex(int startIndex, Predicate match) /// internal int FindIndex(int startIndex, int count, Predicate match) { - Requires.NotNull(match, nameof(match)); - Requires.Range(startIndex >= 0, nameof(startIndex)); - Requires.Range(count >= 0, nameof(count)); - Requires.Range(startIndex <= this.Count - count, nameof(count)); + Debug.Assert(match != null); + Debug.Assert(startIndex >= 0 && startIndex <= this.Count); + Debug.Assert(count >= 0 && (uint)(startIndex + count) <= (uint)this.Count); using (var enumerator = new Enumerator(this, startIndex: startIndex, count: count)) { @@ -1159,7 +1148,7 @@ internal int FindIndex(int startIndex, int count, Predicate match) /// internal T? FindLast(Predicate match) { - Requires.NotNull(match, nameof(match)); + Debug.Assert(match != null); using (var enumerator = new Enumerator(this, reversed: true)) { @@ -1190,7 +1179,7 @@ internal int FindIndex(int startIndex, int count, Predicate match) /// internal int FindLastIndex(Predicate match) { - Requires.NotNull(match, nameof(match)); + Debug.Assert(match != null); return this.IsEmpty ? -1 : this.FindLastIndex(this.Count - 1, this.Count, match); } @@ -1210,9 +1199,9 @@ internal int FindLastIndex(Predicate match) /// internal int FindLastIndex(int startIndex, Predicate match) { - Requires.NotNull(match, nameof(match)); - Requires.Range(startIndex >= 0, nameof(startIndex)); - Requires.Range(startIndex == 0 || startIndex < this.Count, nameof(startIndex)); + Debug.Assert(match != null); + Debug.Assert(startIndex >= 0); + Debug.Assert(startIndex == 0 || startIndex < this.Count); return this.IsEmpty ? -1 : this.FindLastIndex(startIndex, startIndex + 1, match); } @@ -1235,16 +1224,11 @@ internal int FindLastIndex(int startIndex, Predicate match) /// internal int FindLastIndex(int startIndex, int count, Predicate match) { - Requires.NotNull(match, nameof(match)); - - if (startIndex == 0 && count == 0) - { - return -1; - } - - Requires.Range(startIndex >= 0 && startIndex < this.Count, nameof(startIndex)); - Requires.Range(count >= 0 && count <= this.Count, nameof(count)); - Requires.Range(startIndex - count + 1 >= 0, nameof(count)); + Debug.Assert(match != null); + Debug.Assert(startIndex >= 0); + Debug.Assert(startIndex == 0 || startIndex < this.Count); + Debug.Assert(count >= 0 && count <= this.Count); + Debug.Assert(startIndex - count + 1 >= 0); using (var enumerator = new Enumerator(this, startIndex: startIndex, count: count, reversed: true)) { @@ -1450,8 +1434,8 @@ private Node BalanceMany() /// The mutated (or created) node. private Node MutateBoth(Node left, Node right) { - Requires.NotNull(left, nameof(left)); - Requires.NotNull(right, nameof(right)); + Debug.Assert(left is not null); + Debug.Assert(right is not null); Debug.Assert(!this.IsEmpty); if (_frozen) @@ -1476,7 +1460,7 @@ private Node MutateBoth(Node left, Node right) /// The mutated (or created) node. private Node MutateLeft(Node left) { - Requires.NotNull(left, nameof(left)); + Debug.Assert(left is not null); Debug.Assert(!this.IsEmpty); if (_frozen) @@ -1500,7 +1484,7 @@ private Node MutateLeft(Node left) /// The mutated (or created) node. private Node MutateRight(Node right) { - Requires.NotNull(right, nameof(right)); + Debug.Assert(right is not null); Debug.Assert(!this.IsEmpty); if (_frozen) diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.cs index dfd338d69959c3..778e774f5494bb 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList_1.cs @@ -121,7 +121,12 @@ private ImmutableList(Node root) /// cannot find an implementation of the generic interface /// or the interface for type . /// - public int BinarySearch(int index, int count, T item, IComparer? comparer) => _root.BinarySearch(index, count, item, comparer); + public int BinarySearch(int index, int count, T item, IComparer? comparer) + { + Requires.ValidateRange(index, count, this.Count); + + return _root.BinarySearch(index, count, item, comparer); + } #region IImmutableList Properties @@ -170,7 +175,15 @@ private ImmutableList(Node root) /// The 0-based index of the element in the set to return. /// The element at the given position. /// is negative or not less than . - public T this[int index] => _root.ItemRef(index); + public T this[int index] + { + get + { + Requires.Range(index >= 0 && index < this.Count, nameof(index)); + + return _root.ItemRef(index); + } + } /// /// Gets a read-only reference to the element of the set at the given index. @@ -178,7 +191,12 @@ private ImmutableList(Node root) /// The 0-based index of the element in the set to return. /// A read-only reference to the element at the given position. /// is negative or not less than . - public ref readonly T ItemRef(int index) => ref _root.ItemRef(index); + public ref readonly T ItemRef(int index) + { + Requires.Range(index >= 0 && index < this.Count, nameof(index)); + + return ref _root.ItemRef(index); + } #endregion @@ -259,6 +277,7 @@ internal ImmutableList AddRange(ReadOnlySpan items) public ImmutableList Insert(int index, T item) { Requires.Range(index >= 0 && index <= this.Count, nameof(index)); + return this.Wrap(_root.Insert(index, item)); } @@ -361,6 +380,7 @@ public ImmutableList RemoveRange(IEnumerable items, IEqualityComparer? public ImmutableList RemoveAt(int index) { Requires.Range(index >= 0 && index < this.Count, nameof(index)); + ImmutableList.Node result = _root.RemoveAt(index); return this.Wrap(result); } @@ -386,7 +406,12 @@ public ImmutableList RemoveAll(Predicate match) /// /// See the interface. /// - public ImmutableList SetItem(int index, T value) => this.Wrap(_root.ReplaceAt(index, value)); + public ImmutableList SetItem(int index, T value) + { + Requires.Range(index >= 0 && index < this.Count, nameof(index)); + + return this.Wrap(_root.ReplaceAt(index, value)); + } /// /// See the interface. @@ -419,7 +444,12 @@ public ImmutableList Replace(T oldValue, T newValue, IEqualityComparer? eq /// The zero-based starting index of the range to reverse. /// The number of elements in the range to reverse. /// The reversed list. - public ImmutableList Reverse(int index, int count) => this.Wrap(_root.Reverse(index, count)); + public ImmutableList Reverse(int index, int count) + { + Requires.ValidateRange(index, count, this.Count); + + return this.Wrap(_root.Reverse(index, count)); + } /// /// Sorts the elements in the entire using @@ -439,6 +469,7 @@ public ImmutableList Replace(T oldValue, T newValue, IEqualityComparer? eq public ImmutableList Sort(Comparison comparison) { Requires.NotNull(comparison, nameof(comparison)); + return this.Wrap(_root.Sort(comparison)); } @@ -470,9 +501,7 @@ public ImmutableList Sort(Comparison comparison) /// The sorted list. public ImmutableList Sort(int index, int count, IComparer? comparer) { - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0, nameof(count)); - Requires.Range(index + count <= this.Count, nameof(count)); + Requires.ValidateRange(index, count, this.Count); return this.Wrap(_root.Sort(index, count, comparer)); } @@ -504,7 +533,13 @@ public void ForEach(Action action) /// copied from . The must have /// zero-based indexing. /// - public void CopyTo(T[] array) => _root.CopyTo(array); + public void CopyTo(T[] array) + { + Requires.NotNull(array, nameof(array)); + Requires.Range(array.Length >= this.Count, nameof(array)); + + _root.CopyTo(array); + } /// /// Copies the entire to a compatible one-dimensional @@ -518,7 +553,13 @@ public void ForEach(Action action) /// /// The zero-based index in array at which copying begins. /// - public void CopyTo(T[] array, int arrayIndex) => _root.CopyTo(array, arrayIndex); + public void CopyTo(T[] array, int arrayIndex) + { + Requires.NotNull(array, nameof(array)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); + + _root.CopyTo(array, arrayIndex); + } /// /// Copies a range of elements from the to @@ -536,7 +577,14 @@ public void ForEach(Action action) /// /// The zero-based index in array at which copying begins. /// The number of elements to copy. - public void CopyTo(int index, T[] array, int arrayIndex, int count) => _root.CopyTo(index, array, arrayIndex, count); + public void CopyTo(int index, T[] array, int arrayIndex, int count) + { + Requires.NotNull(array, nameof(array)); + Requires.ValidateRange(index, count, this.Count); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + count) <= (uint)array.Length, nameof(arrayIndex)); + + _root.CopyTo(index, array, arrayIndex, count); + } /// /// Creates a shallow copy of a range of elements in the source . @@ -553,9 +601,8 @@ public void ForEach(Action action) /// public ImmutableList GetRange(int index, int count) { - Requires.Range(index >= 0, nameof(index)); - Requires.Range(count >= 0, nameof(count)); - Requires.Range(index + count <= this.Count, nameof(count)); + Requires.ValidateRange(index, count, this.Count); + return this.Wrap(Node.NodeTreeFromList(this, index, count)); } @@ -577,6 +624,7 @@ public ImmutableList GetRange(int index, int count) public ImmutableList ConvertAll(Func converter) { Requires.NotNull(converter, nameof(converter)); + return ImmutableList.WrapNode(_root.ConvertAll(converter)); } @@ -593,7 +641,12 @@ public ImmutableList ConvertAll(Func converter) /// that match the conditions defined by the specified predicate; otherwise, /// false. /// - public bool Exists(Predicate match) => _root.Exists(match); + public bool Exists(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.Exists(match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -607,7 +660,12 @@ public ImmutableList ConvertAll(Func converter) /// The first element that matches the conditions defined by the specified predicate, /// if found; otherwise, the default value for type . /// - public T? Find(Predicate match) => _root.Find(match); + public T? Find(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.Find(match); + } /// /// Retrieves all the elements that match the conditions defined by the specified @@ -622,7 +680,12 @@ public ImmutableList ConvertAll(Func converter) /// the conditions defined by the specified predicate, if found; otherwise, an /// empty . /// - public ImmutableList FindAll(Predicate match) => _root.FindAll(match); + public ImmutableList FindAll(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.FindAll(match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -637,7 +700,12 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the first occurrence of an element that matches the /// conditions defined by , if found; otherwise, -1. /// - public int FindIndex(Predicate match) => _root.FindIndex(match); + public int FindIndex(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.FindIndex(match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -651,7 +719,13 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the first occurrence of an element that matches the /// conditions defined by , if found; otherwise, -1. /// - public int FindIndex(int startIndex, Predicate match) => _root.FindIndex(startIndex, match); + public int FindIndex(int startIndex, Predicate match) + { + Requires.NotNull(match, nameof(match)); + Requires.Range(startIndex >= 0 && startIndex <= this.Count, nameof(startIndex)); + + return _root.FindIndex(startIndex, match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -666,7 +740,13 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the first occurrence of an element that matches the /// conditions defined by , if found; otherwise, -1. /// - public int FindIndex(int startIndex, int count, Predicate match) => _root.FindIndex(startIndex, count, match); + public int FindIndex(int startIndex, int count, Predicate match) + { + Requires.NotNull(match, nameof(match)); + Requires.ValidateRange(startIndex, count, this.Count, nameof(startIndex)); + + return _root.FindIndex(startIndex, count, match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -680,7 +760,12 @@ public ImmutableList ConvertAll(Func converter) /// The last element that matches the conditions defined by the specified predicate, /// if found; otherwise, the default value for type . /// - public T? FindLast(Predicate match) => _root.FindLast(match); + public T? FindLast(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.FindLast(match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -695,7 +780,12 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the last occurrence of an element that matches the /// conditions defined by , if found; otherwise, -1. /// - public int FindLastIndex(Predicate match) => _root.FindLastIndex(match); + public int FindLastIndex(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.FindLastIndex(match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -710,7 +800,13 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the last occurrence of an element that matches the /// conditions defined by , if found; otherwise, -1. /// - public int FindLastIndex(int startIndex, Predicate match) => _root.FindLastIndex(startIndex, match); + public int FindLastIndex(int startIndex, Predicate match) + { + Requires.NotNull(match, nameof(match)); + Requires.Range(startIndex == 0 || (uint)startIndex < (uint)this.Count, nameof(startIndex)); + + return _root.FindLastIndex(startIndex, match); + } /// /// Searches for an element that matches the conditions defined by the specified @@ -728,7 +824,13 @@ public ImmutableList ConvertAll(Func converter) /// The zero-based index of the last occurrence of an element that matches the /// conditions defined by , if found; otherwise, -1. /// - public int FindLastIndex(int startIndex, int count, Predicate match) => _root.FindLastIndex(startIndex, count, match); + public int FindLastIndex(int startIndex, int count, Predicate match) + { + Requires.NotNull(match, nameof(match)); + Requires.ValidateReverseRange(startIndex, count, this.Count, nameof(startIndex)); + + return _root.FindLastIndex(startIndex, count, match); + } /// /// Searches for the specified object and returns the zero-based index of the @@ -754,7 +856,12 @@ public ImmutableList ConvertAll(Func converter) /// elements in the that starts at and /// contains number of elements, if found; otherwise, -1. /// - public int IndexOf(T item, int index, int count, IEqualityComparer? equalityComparer) => _root.IndexOf(item, index, count, equalityComparer); + public int IndexOf(T item, int index, int count, IEqualityComparer? equalityComparer) + { + Requires.ValidateRange(index, count, this.Count); + + return _root.IndexOf(item, index, count, equalityComparer); + } /// /// Searches for the specified object and returns the zero-based index of the @@ -776,7 +883,12 @@ public ImmutableList ConvertAll(Func converter) /// in the that contains number of elements /// and ends at , if found; otherwise, -1. /// - public int LastIndexOf(T item, int index, int count, IEqualityComparer? equalityComparer) => _root.LastIndexOf(item, index, count, equalityComparer); + public int LastIndexOf(T item, int index, int count, IEqualityComparer? equalityComparer) + { + Requires.ValidateReverseRange(index, count, this.Count); + + return _root.LastIndexOf(item, index, count, equalityComparer); + } /// /// Determines whether every element in the @@ -791,7 +903,12 @@ public ImmutableList ConvertAll(Func converter) /// conditions defined by the specified predicate; otherwise, false. If the list /// has no elements, the return value is true. /// - public bool TrueForAll(Predicate match) => _root.TrueForAll(match); + public bool TrueForAll(Predicate match) + { + Requires.NotNull(match, nameof(match)); + + return _root.TrueForAll(match); + } #endregion @@ -982,7 +1099,13 @@ T IList.this[int index] /// /// See the interface. /// - void System.Collections.ICollection.CopyTo(Array array, int arrayIndex) => _root.CopyTo(array, arrayIndex); + void System.Collections.ICollection.CopyTo(Array array, int arrayIndex) + { + Requires.NotNull(array, nameof(array)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); + + _root.CopyTo(array, arrayIndex); + } #endregion diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs index 280682da09e557..aca3489048efa1 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.Node.cs @@ -199,8 +199,7 @@ internal Enumerator GetEnumerator(Builder builder) internal void CopyTo(KeyValuePair[] array, int arrayIndex, int dictionarySize) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + dictionarySize, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + dictionarySize) <= (uint)array.Length, nameof(arrayIndex)); foreach (KeyValuePair item in this) { @@ -214,8 +213,7 @@ internal void CopyTo(KeyValuePair[] array, int arrayIndex, int dic internal void CopyTo(Array array, int arrayIndex, int dictionarySize) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + dictionarySize, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + dictionarySize) <= (uint)array.Length, nameof(arrayIndex)); foreach (KeyValuePair item in this) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs index ab7d775fd5c37c..23c2ac56263015 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedDictionary_2.cs @@ -521,8 +521,7 @@ bool ICollection>.Remove(KeyValuePair i void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (KeyValuePair item in this) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs index f81689196614e5..ac4a76790beccd 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet_1.Node.cs @@ -306,8 +306,7 @@ internal Enumerator GetEnumerator(Builder builder) internal void CopyTo(T[] array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (T item in this) { array[arrayIndex++] = item; @@ -320,8 +319,7 @@ internal void CopyTo(T[] array, int arrayIndex) internal void CopyTo(Array array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (T item in this) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/KeysOrValuesCollectionAccessor.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/KeysOrValuesCollectionAccessor.cs index 5dc7d8205e83af..fb4c4fc6e1842d 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/KeysOrValuesCollectionAccessor.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/KeysOrValuesCollectionAccessor.cs @@ -90,8 +90,7 @@ public void Clear() public void CopyTo(T[] array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (T item in this) { @@ -131,8 +130,7 @@ IEnumerator IEnumerable.GetEnumerator() void ICollection.CopyTo(Array array, int arrayIndex) { Requires.NotNull(array, nameof(array)); - Requires.Range(arrayIndex >= 0, nameof(arrayIndex)); - Requires.Range(array.Length >= arrayIndex + this.Count, nameof(arrayIndex)); + Requires.Range(arrayIndex >= 0 && (uint)(arrayIndex + this.Count) <= (uint)array.Length, nameof(arrayIndex)); foreach (T item in this) { diff --git a/src/libraries/System.Collections.Immutable/src/Validation/Requires.cs b/src/libraries/System.Collections.Immutable/src/Validation/Requires.cs index ac23bd81eed829..7ceb820cd34638 100644 --- a/src/libraries/System.Collections.Immutable/src/Validation/Requires.cs +++ b/src/libraries/System.Collections.Immutable/src/Validation/Requires.cs @@ -106,6 +106,48 @@ public static void FailRange(string? parameterName, string? message = null) } } + /// + /// Validates that and represent a valid range + /// within a collection of size . + /// + /// The zero-based starting index of the range. + /// The number of elements in the range. + /// The total number of elements in the collection. + /// The name of the index parameter for exception messages. + /// + /// is negative or greater than , + /// or is negative or index + count exceeds . + /// + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ValidateRange(int index, int count, int listCount, string indexParameterName = "index") + { + Debug.Assert(listCount >= 0); + Range((uint)index <= (uint)listCount, indexParameterName); + Range(count >= 0 && (uint)(index + count) <= (uint)listCount, nameof(count)); + } + + /// + /// Validates that and represent a valid + /// reverse (backward) range within a collection of size . + /// + /// The zero-based starting index of the backward search. + /// The number of elements in the range. + /// The total number of elements in the collection. + /// The name of the startIndex parameter for exception messages. + /// + /// is negative or not a valid index (unless both it and are zero), + /// or is negative, exceeds , or the backward range extends before index 0. + /// + [DebuggerStepThrough] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void ValidateReverseRange(int startIndex, int count, int listCount, string startIndexParameterName = "startIndex") + { + Debug.Assert(listCount >= 0); + Range(startIndex == 0 || (uint)startIndex < (uint)listCount, startIndexParameterName); + Range((uint)count <= (uint)listCount && startIndex - count + 1 >= 0, nameof(count)); + } + /// /// Throws an if a condition does not evaluate to true. /// diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableArrayBuilderTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableArrayBuilderTest.cs index 6dc60935452e19..44fa1b4266b677 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableArrayBuilderTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableArrayBuilderTest.cs @@ -533,8 +533,10 @@ public void SortRange() var builder = ImmutableArray.CreateBuilder(); builder.AddRange(2, 4, 1, 3); AssertExtensions.Throws("index", () => builder.Sort(-1, 2, Comparer.Default)); + AssertExtensions.Throws("index", () => builder.Sort(5, 0, Comparer.Default)); AssertExtensions.Throws("count", () => builder.Sort(1, 4, Comparer.Default)); AssertExtensions.Throws("count", () => builder.Sort(0, -1, Comparer.Default)); + AssertExtensions.Throws("count", () => builder.Sort(1, int.MaxValue, Comparer.Default)); builder.Sort(builder.Count, 0, Comparer.Default); Assert.Equal(new int[] { 2, 4, 1, 3 }, builder); diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableArrayTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableArrayTest.cs index 6787c977ed55d4..f4966409c9ae98 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableArrayTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableArrayTest.cs @@ -2120,8 +2120,9 @@ public void SortComparerInvalid(IEnumerable source) AssertExtensions.Throws("index", () => array.Sort(-1, 0, Comparer.Default)); AssertExtensions.Throws("count", () => array.Sort(0, -1, Comparer.Default)); - AssertExtensions.Throws("count", () => array.Sort(array.Length + 1, 0, Comparer.Default)); + AssertExtensions.Throws("index", () => array.Sort(array.Length + 1, 0, Comparer.Default)); AssertExtensions.Throws("count", () => array.Sort(0, array.Length + 1, Comparer.Default)); + AssertExtensions.Throws("count", () => array.Sort(0, int.MaxValue, Comparer.Default)); } [Theory] diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTestBase.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTestBase.cs index ce47d4caab77b7..a48aab93a9cbbd 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTestBase.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryBuilderTestBase.cs @@ -106,6 +106,17 @@ public void CopyTo() AssertExtensions.Throws("array", () => builder.CopyTo(null, 0)); } + [Fact] + public void CopyToOverflowValidation() + { + IImmutableDictionary map = this.GetEmptyImmutableDictionary().Add("a", 1); + IDictionary builder = this.GetBuilder(map); + + var collection = (ICollection)builder; + AssertExtensions.Throws("arrayIndex", () => collection.CopyTo(new object[5], int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => collection.CopyTo(new object[5], -1)); + } + [Fact] public void IsReadOnly() { diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTestBase.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTestBase.cs index b4aafc1995604c..52e7739b638488 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTestBase.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableDictionaryTestBase.cs @@ -140,6 +140,19 @@ public void ICollectionMembers() Assert.Equal(new DictionaryEntry("a", 1), (DictionaryEntry)array[1]); } + [Fact] + public void CopyToOverflowValidation() + { + var dictionary = Empty().Add("a", 1); + var array = new KeyValuePair[5]; + + AssertExtensions.Throws("arrayIndex", () => ((ICollection>)dictionary).CopyTo(array, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => ((ICollection>)dictionary).CopyTo(array, -1)); + + AssertExtensions.Throws("arrayIndex", () => ((ICollection)dictionary).CopyTo(new object[5], int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => ((ICollection)dictionary).CopyTo(new object[5], -1)); + } + [Fact] public void IDictionaryOfKVMembers() { diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableHashSetBuilderTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableHashSetBuilderTest.cs index f738552c8b8f4e..be6108366160f3 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableHashSetBuilderTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableHashSetBuilderTest.cs @@ -279,6 +279,16 @@ public void ICollectionOfTMethods() CollectionAssertAreEquivalent(new[] { "a", "b" }, builder.ToArray()); // tests enumerator } + [Fact] + public void CopyToOverflowValidation() + { + ICollection builder = ImmutableHashSet.Create("a").ToBuilder(); + var array = new string[5]; + + AssertExtensions.Throws("arrayIndex", () => builder.CopyTo(array, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => builder.CopyTo(array, -1)); + } + [Fact] public void NullHandling() { diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableListBuilderTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableListBuilderTest.cs index 7d2b9fe483bf4a..fb388193d6d1a9 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableListBuilderTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableListBuilderTest.cs @@ -349,6 +349,42 @@ public void LastIndexOf() (b, v, i, c, eq) => b.LastIndexOf(v, i, c, eq)); } + [Fact] + public void CopyToOverflowValidation() + { + ImmutableList.Builder builder = ImmutableList.Create(1, 2, 3).ToBuilder(); + var array = new int[5]; + + // Overflow scenarios for CopyTo(T[], arrayIndex) + AssertExtensions.Throws("arrayIndex", () => builder.CopyTo(array, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => builder.CopyTo(array, -1)); + + // Overflow scenarios for CopyTo(int, T[], arrayIndex, count) + AssertExtensions.Throws("arrayIndex", () => builder.CopyTo(0, array, int.MaxValue, 3)); + + // Overflow scenarios for ICollection.CopyTo + AssertExtensions.Throws("arrayIndex", () => ((ICollection)builder).CopyTo(array, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => ((ICollection)builder).CopyTo(array, -1)); + } + + [Fact] + public void IndexOfWithIndexOverflowValidation() + { + ImmutableList.Builder builder = ImmutableList.Create(1, 2, 3).ToBuilder(); + + AssertExtensions.Throws("index", () => builder.IndexOf(1, int.MaxValue)); + AssertExtensions.Throws("index", () => builder.IndexOf(1, -1)); + } + + [Fact] + public void LastIndexOfWithStartIndexThrowsOnEmptyBuilder() + { + ImmutableList.Builder builder = ImmutableList.Empty.ToBuilder(); + + // LastIndexOf(item, startIndex) should throw on empty builder, matching List behavior. + AssertExtensions.Throws("startIndex", () => builder.LastIndexOf(5, 0)); + } + [Fact] public void GetEnumeratorExplicit() { @@ -484,6 +520,59 @@ public void ToImmutableList() AssertExtensions.Throws("builder", () => nullBuilder.ToImmutableList()); } + [Fact] + public void SortRangeValidation() + { + ImmutableList.Builder builder = ImmutableList.Create(1, 2, 3).ToBuilder(); + AssertExtensions.Throws("index", () => builder.Sort(-1, 0, null)); + AssertExtensions.Throws("index", () => builder.Sort(4, 0, null)); + AssertExtensions.Throws("count", () => builder.Sort(0, -1, null)); + AssertExtensions.Throws("count", () => builder.Sort(0, 4, null)); + AssertExtensions.Throws("count", () => builder.Sort(2, 2, null)); + AssertExtensions.Throws("count", () => builder.Sort(1, int.MaxValue, null)); + builder.Sort(3, 0, null); + Assert.Equal(new[] { 1, 2, 3 }, builder); + } + + [Fact] + public void GetRangeValidation() + { + ImmutableList.Builder builder = ImmutableList.Create(1, 2, 3).ToBuilder(); + AssertExtensions.Throws("index", () => builder.GetRange(-1, 0)); + AssertExtensions.Throws("index", () => builder.GetRange(4, 0)); + AssertExtensions.Throws("count", () => builder.GetRange(0, -1)); + AssertExtensions.Throws("count", () => builder.GetRange(0, 4)); + AssertExtensions.Throws("count", () => builder.GetRange(2, 2)); + AssertExtensions.Throws("count", () => builder.GetRange(1, int.MaxValue)); + Assert.True(builder.GetRange(3, 0).IsEmpty); + } + + [Fact] + public void ReverseRangeValidation() + { + ImmutableList.Builder builder = ImmutableList.Create(1, 2, 3).ToBuilder(); + AssertExtensions.Throws("index", () => builder.Reverse(-1, 0)); + AssertExtensions.Throws("index", () => builder.Reverse(4, 0)); + AssertExtensions.Throws("count", () => builder.Reverse(0, -1)); + AssertExtensions.Throws("count", () => builder.Reverse(0, 4)); + AssertExtensions.Throws("count", () => builder.Reverse(2, 2)); + AssertExtensions.Throws("count", () => builder.Reverse(1, int.MaxValue)); + builder.Reverse(3, 0); + Assert.Equal(new[] { 1, 2, 3 }, builder); + } + + [Fact] + public void BinarySearchRangeValidation() + { + ImmutableList.Builder builder = ImmutableList.Create(1, 2, 3, 4, 5).ToBuilder(); + AssertExtensions.Throws("index", () => builder.BinarySearch(-1, 0, 1, null)); + AssertExtensions.Throws("index", () => builder.BinarySearch(6, 0, 1, null)); + AssertExtensions.Throws("count", () => builder.BinarySearch(0, -1, 1, null)); + AssertExtensions.Throws("count", () => builder.BinarySearch(0, 6, 1, null)); + AssertExtensions.Throws("count", () => builder.BinarySearch(3, 3, 1, null)); + AssertExtensions.Throws("count", () => builder.BinarySearch(1, int.MaxValue, 1, null)); + } + protected override IEnumerable GetEnumerableOf(params T[] contents) { return ImmutableList.Empty.AddRange(contents).ToBuilder(); diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableListTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableListTest.cs index cc6fe537864641..0878dadbdb1b69 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableListTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableListTest.cs @@ -566,6 +566,15 @@ public void LastIndexOfConsistentWithImmutableArray() } } + [Fact] + public void LastIndexOfWithStartIndexThrowsOnEmptyList() + { + ImmutableList list = ImmutableList.Empty; + + // LastIndexOf(item, startIndex, count, eq) with count=1 on empty list throws on count. + AssertExtensions.Throws("count", () => list.LastIndexOf(5, 0, 1, equalityComparer: null)); + } + [Fact] public void ReplaceTest() { @@ -882,6 +891,108 @@ public void ItemRef_OutOfBounds() Assert.Throws(() => list.ItemRef(5)); } + [Fact] + public void SortRangeValidation() + { + ImmutableList list = ImmutableList.Create(1, 2, 3); + AssertExtensions.Throws("index", () => list.Sort(-1, 0, null)); + AssertExtensions.Throws("index", () => list.Sort(4, 0, null)); + AssertExtensions.Throws("count", () => list.Sort(0, -1, null)); + AssertExtensions.Throws("count", () => list.Sort(0, 4, null)); + AssertExtensions.Throws("count", () => list.Sort(2, 2, null)); + AssertExtensions.Throws("count", () => list.Sort(1, int.MaxValue, null)); + Assert.Equal(list, list.Sort(3, 0, null)); + Assert.Equal(list, list.Sort(0, 0, null)); + } + + [Fact] + public void GetRangeValidation() + { + ImmutableList list = ImmutableList.Create(1, 2, 3); + AssertExtensions.Throws("index", () => list.GetRange(-1, 0)); + AssertExtensions.Throws("index", () => list.GetRange(4, 0)); + AssertExtensions.Throws("count", () => list.GetRange(0, -1)); + AssertExtensions.Throws("count", () => list.GetRange(0, 4)); + AssertExtensions.Throws("count", () => list.GetRange(2, 2)); + AssertExtensions.Throws("count", () => list.GetRange(1, int.MaxValue)); + Assert.True(list.GetRange(3, 0).IsEmpty); + Assert.True(list.GetRange(0, 0).IsEmpty); + } + + [Fact] + public void ReverseRangeValidation() + { + ImmutableList list = ImmutableList.Create(1, 2, 3); + AssertExtensions.Throws("index", () => list.Reverse(-1, 0)); + AssertExtensions.Throws("index", () => list.Reverse(4, 0)); + AssertExtensions.Throws("count", () => list.Reverse(0, -1)); + AssertExtensions.Throws("count", () => list.Reverse(0, 4)); + AssertExtensions.Throws("count", () => list.Reverse(2, 2)); + AssertExtensions.Throws("count", () => list.Reverse(1, int.MaxValue)); + Assert.Equal(list, list.Reverse(3, 0)); + Assert.Equal(list, list.Reverse(0, 0)); + } + + [Fact] + public void CopyToRangeValidation() + { + ImmutableList list = ImmutableList.Create(1, 2, 3); + var array = new int[5]; + AssertExtensions.Throws("index", () => list.CopyTo(-1, array, 0, 0)); + AssertExtensions.Throws("index", () => list.CopyTo(4, array, 0, 0)); + AssertExtensions.Throws("count", () => list.CopyTo(0, array, 0, -1)); + AssertExtensions.Throws("count", () => list.CopyTo(0, array, 0, 4)); + AssertExtensions.Throws("count", () => list.CopyTo(2, array, 0, 2)); + AssertExtensions.Throws("count", () => list.CopyTo(0, array, 0, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => list.CopyTo(0, array, -1, 3)); + AssertExtensions.Throws("arrayIndex", () => list.CopyTo(0, array, 3, 3)); + AssertExtensions.Throws("arrayIndex", () => list.CopyTo(0, array, 4, 2)); + } + + [Fact] + public void CopyToOverflowValidation() + { + ImmutableList list = ImmutableList.Create(1, 2, 3); + var array = new int[5]; + + // Overflow scenarios for CopyTo(T[], arrayIndex) + AssertExtensions.Throws("arrayIndex", () => list.CopyTo(array, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => list.CopyTo(array, -1)); + + // Overflow scenarios for CopyTo(int, T[], arrayIndex, count) + AssertExtensions.Throws("arrayIndex", () => list.CopyTo(0, array, int.MaxValue, 3)); + + // Overflow scenarios for ICollection.CopyTo + AssertExtensions.Throws("arrayIndex", () => ((ICollection)list).CopyTo(array, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => ((ICollection)list).CopyTo(array, -1)); + } + + [Fact] + public void BinarySearchRangeValidation() + { + ImmutableList list = ImmutableList.Create(1, 2, 3, 4, 5); + AssertExtensions.Throws("index", () => list.BinarySearch(-1, 0, 1, null)); + AssertExtensions.Throws("index", () => list.BinarySearch(6, 0, 1, null)); + AssertExtensions.Throws("count", () => list.BinarySearch(0, -1, 1, null)); + AssertExtensions.Throws("count", () => list.BinarySearch(0, 6, 1, null)); + AssertExtensions.Throws("count", () => list.BinarySearch(3, 3, 1, null)); + AssertExtensions.Throws("count", () => list.BinarySearch(1, int.MaxValue, 1, null)); + } + + [Fact] + public void FindIndexRangeValidation() + { + ImmutableList list = ImmutableList.Create(1, 2, 3); + AssertExtensions.Throws("startIndex", () => list.FindIndex(-1, 0, _ => true)); + AssertExtensions.Throws("startIndex", () => list.FindIndex(4, 0, _ => true)); + AssertExtensions.Throws("count", () => list.FindIndex(0, -1, _ => true)); + AssertExtensions.Throws("count", () => list.FindIndex(0, 4, _ => true)); + AssertExtensions.Throws("count", () => list.FindIndex(2, 2, _ => true)); + AssertExtensions.Throws("count", () => list.FindIndex(1, int.MaxValue, _ => true)); + Assert.Equal(-1, list.FindIndex(3, 0, _ => true)); + Assert.Equal(-1, list.FindIndex(0, 0, _ => true)); + } + protected override IEnumerable GetEnumerableOf(params T[] contents) { return ImmutableList.Empty.AddRange((IEnumerable)contents); diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableSetTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableSetTest.cs index d3add493299621..6d12111c9297d0 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableSetTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableSetTest.cs @@ -192,6 +192,19 @@ public void ICollectionMethods() Assert.Same(builder.SyncRoot, builder.SyncRoot); } + [Fact] + public void CopyToOverflowValidation() + { + IImmutableSet set = this.Empty().Add("a"); + var array = new string[5]; + + AssertExtensions.Throws("arrayIndex", () => ((ICollection)set).CopyTo(array, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => ((ICollection)set).CopyTo(array, -1)); + + AssertExtensions.Throws("arrayIndex", () => ((ICollection)set).CopyTo(array, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => ((ICollection)set).CopyTo(array, -1)); + } + [Fact] public void NullHandling() { diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs index 47725fdb2ac991..c4e71498be4012 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedDictionaryTest.cs @@ -467,5 +467,30 @@ private static IImmutableDictionary Empty(IComparer< { return ImmutableSortedDictionary.Empty.WithComparers(keyComparer, valueComparer); } + + [Fact] + public void KeysAndValuesCopyToOverflowValidation() + { + ImmutableSortedDictionary dictionary = ImmutableSortedDictionary.Empty.Add("a", 1); + + // Access Keys/Values through the IDictionary interface to get KeysCollectionAccessor/ValuesCollectionAccessor + IDictionary idict = dictionary; + + ICollection keys = idict.Keys; + var keysArray = new string[5]; + AssertExtensions.Throws("arrayIndex", () => keys.CopyTo(keysArray, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => keys.CopyTo(keysArray, -1)); + + AssertExtensions.Throws("arrayIndex", () => ((ICollection)keys).CopyTo(keysArray, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => ((ICollection)keys).CopyTo(keysArray, -1)); + + ICollection values = idict.Values; + var valuesArray = new int[5]; + AssertExtensions.Throws("arrayIndex", () => values.CopyTo(valuesArray, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => values.CopyTo(valuesArray, -1)); + + AssertExtensions.Throws("arrayIndex", () => ((ICollection)values).CopyTo(valuesArray, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => ((ICollection)values).CopyTo(valuesArray, -1)); + } } } diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetBuilderTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetBuilderTest.cs index e4d7464cea2124..dbb00a77921a44 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetBuilderTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetBuilderTest.cs @@ -346,6 +346,20 @@ public void ICollectionMethods() Assert.Same(builder.SyncRoot, builder.SyncRoot); } + [Fact] + public void CopyToOverflowValidation() + { + ImmutableSortedSet.Builder builder = ImmutableSortedSet.Create(1).ToBuilder(); + var array = new int[5]; + + AssertExtensions.Throws("arrayIndex", () => ((ICollection)builder).CopyTo(array, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => ((ICollection)builder).CopyTo(array, -1)); + + ICollection collection = builder; + AssertExtensions.Throws("arrayIndex", () => collection.CopyTo(array, int.MaxValue)); + AssertExtensions.Throws("arrayIndex", () => collection.CopyTo(array, -1)); + } + [Fact] public void Indexer() { diff --git a/src/libraries/System.Collections.Immutable/tests/IndexOfTests.cs b/src/libraries/System.Collections.Immutable/tests/IndexOfTests.cs index 6fe6fff1cc273d..7873d95b1e204f 100644 --- a/src/libraries/System.Collections.Immutable/tests/IndexOfTests.cs +++ b/src/libraries/System.Collections.Immutable/tests/IndexOfTests.cs @@ -132,7 +132,6 @@ public static void LastIndexOfTest( Assert.Equal(-1, lastIndexOfItem(emptyCollection, 5)); Assert.Equal(-1, lastIndexOfItemEQ(emptyCollection, 5, EqualityComparer.Default)); - Assert.Equal(-1, lastIndexOfItemIndex(emptyCollection, 5, 0)); Assert.Equal(-1, lastIndexOfItemIndexCount(emptyCollection, 5, 0, 0)); Assert.Equal(0, lastIndexOfItem(singleCollection, 10));