public override int GetCount(bool onlyIfCheap) { if (onlyIfCheap && !_hasOnlyCollections) { return(-1); } int count = 0; ConcatNIterator <TSource>?node, previousN = this; do { node = previousN; IEnumerable <TSource> source = node._head; // Enumerable.Count() handles ICollections in O(1) time, but check for them here anyway // to avoid a method call because 1) they're common and 2) this code is run in a loop. var collection = source as ICollection <TSource>; Debug.Assert(!_hasOnlyCollections || collection != null); int sourceCount = collection?.Count ?? source.Count(); checked { count += sourceCount; } }while ((previousN = node.PreviousN) != null); Debug.Assert(node._tail is Concat2Iterator <TSource>); return(checked (count + node._tail.GetCount(onlyIfCheap))); }
internal override IEnumerable <TSource> GetEnumerable(int index) { if (index > _nextIndex) { return(null); } // Walk back through the chain of ConcatNIterators looking for the one // that has its _nextIndex equal to index. If we don't find one, then it // must be prior to any of them, so call GetEnumerable on the previous // Concat2Iterator. This avoids a deep recursive call chain. ConcatNIterator <TSource> current = this; while (true) { if (index == current._nextIndex) { return(current._next); } ConcatNIterator <TSource> prevN = current._previousConcat as ConcatNIterator <TSource>; if (prevN != null) { current = prevN; continue; } Debug.Assert(current._previousConcat is Concat2Iterator <TSource>); Debug.Assert(index == 0 || index == 1); return(current._previousConcat.GetEnumerable(index)); } }
private TSource[] PreallocatingToArray() { // If there are only ICollections in this iterator, then we can just get the count, preallocate the // array, and copy them as we go. This has better time complexity than continuously re-walking the // linked list via GetEnumerable, and better memory usage than buffering the collections. Debug.Assert(_hasOnlyCollections); int count = GetCount(onlyIfCheap: true); Debug.Assert(count >= 0); if (count == 0) { return(Array.Empty <TSource>()); } var array = new TSource[count]; int arrayIndex = array.Length; // We start copying in collection-sized chunks from the end of the array. ConcatNIterator <TSource>?node, previousN = this; do { node = previousN; ICollection <TSource> source = (ICollection <TSource>)node._head; int sourceCount = source.Count; if (sourceCount > 0) { checked { arrayIndex -= sourceCount; } source.CopyTo(array, arrayIndex); } }while ((previousN = node.PreviousN) != null); var previous2 = (Concat2Iterator <TSource>)node._tail; var second = (ICollection <TSource>)previous2._second; int secondCount = second.Count; if (secondCount > 0) { second.CopyTo(array, checked (arrayIndex - secondCount)); } if (arrayIndex > secondCount) { var first = (ICollection <TSource>)previous2._first; first.CopyTo(array, 0); } return(array); }
internal override IEnumerable <TSource> GetEnumerable(int index) { Debug.Assert(index >= 0); if (index > _headIndex) { return(null); } ConcatNIterator <TSource> node, previousN = this; do { node = previousN; if (index == node._headIndex) { return(node._head); } }while ((previousN = node.PreviousN) != null); Debug.Assert(index == 0 || index == 1); Debug.Assert(node._tail is Concat2Iterator <TSource>); return(node._tail.GetEnumerable(index)); }