// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable enable using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.Collections.Internal; namespace Microsoft.CodeAnalysis.Collections { internal static class SegmentedArray { #if NET6_0_OR_GREATER /// <seealso cref="Array.Clear(Array)"/> #endif internal static void Clear<T>(SegmentedArray<T> array) => Clear(array, 0, array.Length); /// <seealso cref="Array.Clear(Array, int, int)"/> internal static void Clear<T>(SegmentedArray<T> array, int index, int length) { foreach (var memory in array.GetSegments(index, length)) { memory.Span.Clear(); } } /// <seealso cref="Array.Copy(Array, Array, int)"/> internal static void Copy<T>(SegmentedArray<T> sourceArray, SegmentedArray<T> destinationArray, int length) { if (length == 0) return; if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); if (length > sourceArray.Length) throw new ArgumentException(SR.Arg_LongerThanSrcArray, nameof(sourceArray)); if (length > destinationArray.Length) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); foreach (var (first, second) in GetSegments(sourceArray, destinationArray, length)) { first.CopyTo(second); } } public static void Copy<T>(SegmentedArray<T> sourceArray, Array destinationArray, int length) { if (destinationArray is null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray); if (length == 0) return; if (length < 0) throw new ArgumentOutOfRangeException(nameof(length)); if (length > sourceArray.Length) throw new ArgumentException(SR.Arg_LongerThanSrcArray, nameof(sourceArray)); if (length > destinationArray.Length) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); var copied = 0; foreach (var memory in sourceArray.GetSegments(0, length)) { if (!MemoryMarshal.TryGetArray<T>(memory, out var segment)) { throw new NotSupportedException(); } Array.Copy(segment.Array!, sourceIndex: segment.Offset, destinationArray: destinationArray, destinationIndex: copied, length: segment.Count); copied += segment.Count; } } public static void Copy<T>(SegmentedArray<T> sourceArray, int sourceIndex, SegmentedArray<T> destinationArray, int destinationIndex, int length) { if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); if (sourceIndex < 0) throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_ArrayLB); if (destinationIndex < 0) throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_ArrayLB); if ((uint)(sourceIndex + length) > sourceArray.Length) throw new ArgumentException(SR.Arg_LongerThanSrcArray, nameof(sourceArray)); if ((uint)(destinationIndex + length) > destinationArray.Length) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); if (length == 0) return; if (SegmentedCollectionsMarshal.AsSegments(sourceArray) == SegmentedCollectionsMarshal.AsSegments(destinationArray) && sourceIndex + length > destinationIndex) { // We are copying in the same array with overlap CopyOverlapped(sourceArray, sourceIndex, destinationIndex, length); } else { foreach (var (first, second) in GetSegmentsUnaligned(sourceArray, sourceIndex, destinationArray, destinationIndex, length)) { first.CopyTo(second); } } } // PERF: Avoid inlining this path in Copy<T> [MethodImpl(MethodImplOptions.NoInlining)] private static void CopyOverlapped<T>(SegmentedArray<T> array, int sourceIndex, int destinationIndex, int length) { Debug.Assert(length > 0); Debug.Assert(sourceIndex >= 0); Debug.Assert(destinationIndex >= 0); Debug.Assert((uint)(sourceIndex + length) <= array.Length); Debug.Assert((uint)(destinationIndex + length) <= array.Length); var unalignedEnumerator = GetSegmentsUnaligned(array, sourceIndex, array, destinationIndex, length); if (sourceIndex < destinationIndex) { // We are copying forward in the same array with overlap foreach (var (first, second) in unalignedEnumerator.Reverse()) { first.CopyTo(second); } } else { foreach (var (first, second) in unalignedEnumerator) { first.CopyTo(second); } } } public static void Copy<T>(SegmentedArray<T> sourceArray, int sourceIndex, Array destinationArray, int destinationIndex, int length) { if (destinationArray == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.destinationArray); if (typeof(T[]) != destinationArray.GetType() && destinationArray.Rank != 1) throw new RankException(SR.Rank_MustMatch); if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum); if (sourceIndex < 0) throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_ArrayLB); var dstLB = destinationArray.GetLowerBound(0); if (destinationIndex < dstLB || destinationIndex - dstLB < 0) throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_ArrayLB); destinationIndex -= dstLB; if ((uint)(sourceIndex + length) > sourceArray.Length) throw new ArgumentException(SR.Arg_LongerThanSrcArray, nameof(sourceArray)); if ((uint)(destinationIndex + length) > (nuint)destinationArray.LongLength) throw new ArgumentException(SR.Arg_LongerThanDestArray, nameof(destinationArray)); var copied = 0; foreach (var memory in sourceArray.GetSegments(sourceIndex, length)) { if (!MemoryMarshal.TryGetArray<T>(memory, out var segment)) { throw new NotSupportedException(); } Array.Copy(segment.Array!, sourceIndex: segment.Offset, destinationArray: destinationArray, destinationIndex: destinationIndex + copied, length: segment.Count); copied += segment.Count; } } public static int BinarySearch<T>(SegmentedArray<T> array, T value) { return BinarySearch(array, 0, array.Length, value, comparer: null); } public static int BinarySearch<T>(SegmentedArray<T> array, T value, IComparer<T>? comparer) { return BinarySearch(array, 0, array.Length, value, comparer); } public static int BinarySearch<T>(SegmentedArray<T> array, int index, int length, T value) { return BinarySearch(array, index, length, value, comparer: null); } public static int BinarySearch<T>(SegmentedArray<T> array, int index, int length, T value, IComparer<T>? comparer) { if (index < 0) ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); if (length < 0) ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum(); if (array.Length - index < length) ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); return SegmentedArraySortHelper<T>.BinarySearch(array, index, length, value, comparer); } public static int IndexOf<T>(SegmentedArray<T> array, T value) { return IndexOf(array, value, 0, array.Length, comparer: null); } public static int IndexOf<T>(SegmentedArray<T> array, T value, int startIndex) { return IndexOf(array, value, startIndex, array.Length - startIndex, comparer: null); } public static int IndexOf<T>(SegmentedArray<T> array, T value, int startIndex, int count) { return IndexOf(array, value, startIndex, count, comparer: null); } public static int IndexOf<T>(SegmentedArray<T> array, T value, int startIndex, int count, IEqualityComparer<T>? comparer) { if ((uint)startIndex > (uint)array.Length) { ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_IndexMustBeLessOrEqual(); } if ((uint)count > (uint)(array.Length - startIndex)) { ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count(); } var offset = startIndex; foreach (var memory in array.GetSegments(startIndex, count)) { if (!MemoryMarshal.TryGetArray<T>(memory, out var segment)) { throw new NotSupportedException(); } int index; if (comparer is null || comparer == EqualityComparer<T>.Default) { index = Array.IndexOf(segment.Array!, value, segment.Offset, segment.Count); } else { index = -1; var endIndex = segment.Offset + segment.Count; for (var i = segment.Offset; i < endIndex; i++) { if (comparer.Equals(array[i], value)) { index = i; break; } } } if (index >= 0) { return index + offset - segment.Offset; } offset += segment.Count; } return -1; } public static int LastIndexOf<T>(SegmentedArray<T> array, T value) { return LastIndexOf(array, value, array.Length - 1, array.Length, comparer: null); } public static int LastIndexOf<T>(SegmentedArray<T> array, T value, int startIndex) { return LastIndexOf(array, value, startIndex, array.Length == 0 ? 0 : startIndex + 1, comparer: null); } public static int LastIndexOf<T>(SegmentedArray<T> array, T value, int startIndex, int count) { return LastIndexOf(array, value, startIndex, count, comparer: null); } public static int LastIndexOf<T>(SegmentedArray<T> array, T value, int startIndex, int count, IEqualityComparer<T>? comparer) { if (array.Length == 0) { // Special case for 0 length List // accept -1 and 0 as valid startIndex for compatibility reason. if (startIndex is not (-1) and not 0) { ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_IndexMustBeLess(); } // only 0 is a valid value for count if array is empty if (count != 0) { ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count(); } return -1; } // Make sure we're not out of range if ((uint)startIndex >= (uint)array.Length) { ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_IndexMustBeLess(); } // 2nd half of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0. if (count < 0 || startIndex - count + 1 < 0) { ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count(); } if (comparer is null || comparer == EqualityComparer<T>.Default) { var endIndex = startIndex - count + 1; for (var i = startIndex; i >= endIndex; i--) { if (EqualityComparer<T>.Default.Equals(array[i], value)) return i; } } else { var endIndex = startIndex - count + 1; for (var i = startIndex; i >= endIndex; i--) { if (comparer.Equals(array[i], value)) return i; } } return -1; } public static void Reverse<T>(SegmentedArray<T> array) { Reverse(array, 0, array.Length); } public static void Reverse<T>(SegmentedArray<T> array, int index, int length) { if (index < 0) ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); if (length < 0) ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum(); if (array.Length - index < length) ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); if (length <= 1) return; var firstIndex = index; var lastIndex = index + length - 1; do { var temp = array[firstIndex]; array[firstIndex] = array[lastIndex]; array[lastIndex] = temp; firstIndex++; lastIndex--; } while (firstIndex < lastIndex); } public static void Sort<T>(SegmentedArray<T> array) { if (array.Length > 1) { var segment = new SegmentedArraySegment<T>(array, 0, array.Length); SegmentedArraySortHelper<T>.Sort(segment, (IComparer<T>?)null); } } public static void Sort<T>(SegmentedArray<T> array, int index, int length) { Sort(array, index, length, comparer: null); } public static void Sort<T>(SegmentedArray<T> array, IComparer<T>? comparer) { Sort(array, 0, array.Length, comparer); } public static void Sort<T>(SegmentedArray<T> array, int index, int length, IComparer<T>? comparer) { if (index < 0) ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException(); if (length < 0) ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum(); if (array.Length - index < length) ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen); if (length > 1) { var segment = new SegmentedArraySegment<T>(array, index, length); SegmentedArraySortHelper<T>.Sort(segment, comparer); } } public static void Sort<T>(SegmentedArray<T> array, Comparison<T> comparison) { if (comparison is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison); } if (array.Length > 1) { var segment = new SegmentedArraySegment<T>(array, 0, array.Length); SegmentedArraySortHelper<T>.Sort(segment, comparison); } } private static SegmentEnumerable<T> GetSegments<T>(this SegmentedArray<T> array, int offset, int length) => new(array, offset, length); private static AlignedSegmentEnumerable<T> GetSegments<T>(SegmentedArray<T> first, SegmentedArray<T> second, int length) => new(first, second, length); #pragma warning disable IDE0051 // Remove unused private members (will be used in follow-up) private static AlignedSegmentEnumerable<T> GetSegmentsAligned<T>(SegmentedArray<T> first, int firstOffset, SegmentedArray<T> second, int secondOffset, int length) => new(first, firstOffset, second, secondOffset, length); #pragma warning restore IDE0051 // Remove unused private members private static UnalignedSegmentEnumerable<T> GetSegmentsUnaligned<T>(SegmentedArray<T> first, int firstOffset, SegmentedArray<T> second, int secondOffset, int length) => new(first, firstOffset, second, secondOffset, length); private readonly struct AlignedSegmentEnumerable<T> { private readonly SegmentedArray<T> _first; private readonly int _firstOffset; private readonly SegmentedArray<T> _second; private readonly int _secondOffset; private readonly int _length; public AlignedSegmentEnumerable(SegmentedArray<T> first, SegmentedArray<T> second, int length) : this(first, 0, second, 0, length) { } public AlignedSegmentEnumerable(SegmentedArray<T> first, int firstOffset, SegmentedArray<T> second, int secondOffset, int length) { _first = first; _firstOffset = firstOffset; _second = second; _secondOffset = secondOffset; _length = length; } public AlignedSegmentEnumerator<T> GetEnumerator() => new(SegmentedCollectionsMarshal.AsSegments(_first), _firstOffset, SegmentedCollectionsMarshal.AsSegments(_second), _secondOffset, _length); } private struct AlignedSegmentEnumerator<T> { private readonly T[][] _firstSegments; private readonly int _firstOffset; private readonly T[][] _secondSegments; private readonly int _secondOffset; private readonly int _length; private int _completed; private (Memory<T> first, Memory<T> second) _current; public AlignedSegmentEnumerator(T[][] firstSegments, int firstOffset, T[][] secondSegments, int secondOffset, int length) { _firstSegments = firstSegments; _firstOffset = firstOffset; _secondSegments = secondSegments; _secondOffset = secondOffset; _length = length; _completed = 0; _current = (Memory<T>.Empty, Memory<T>.Empty); } public readonly (Memory<T> first, Memory<T> second) Current => _current; public bool MoveNext() { if (_completed == _length) { _current = (Memory<T>.Empty, Memory<T>.Empty); return false; } if (_completed == 0) { var initialFirstSegment = _firstOffset >> SegmentedArrayHelper.GetSegmentShift<T>(); var initialSecondSegment = _secondOffset >> SegmentedArrayHelper.GetSegmentShift<T>(); var offset = _firstOffset & SegmentedArrayHelper.GetOffsetMask<T>(); Debug.Assert(offset == (_secondOffset & SegmentedArrayHelper.GetOffsetMask<T>()), "Aligned views must start at the same segment offset"); var firstSegment = _firstSegments[initialFirstSegment]; var secondSegment = _secondSegments[initialSecondSegment]; var remainingInSegment = firstSegment.Length - offset; var currentSegmentLength = Math.Min(remainingInSegment, _length); _current = (firstSegment.AsMemory().Slice(offset, currentSegmentLength), secondSegment.AsMemory().Slice(offset, currentSegmentLength)); _completed = currentSegmentLength; return true; } else { var firstSegment = _firstSegments[(_completed + _firstOffset) >> SegmentedArrayHelper.GetSegmentShift<T>()]; var secondSegment = _secondSegments[(_completed + _secondOffset) >> SegmentedArrayHelper.GetSegmentShift<T>()]; var currentSegmentLength = Math.Min(SegmentedArrayHelper.GetSegmentSize<T>(), _length - _completed); _current = (firstSegment.AsMemory().Slice(0, currentSegmentLength), secondSegment.AsMemory().Slice(0, currentSegmentLength)); _completed += currentSegmentLength; return true; } } } private readonly struct UnalignedSegmentEnumerable<T> { private readonly SegmentedArray<T> _first; private readonly int _firstOffset; private readonly SegmentedArray<T> _second; private readonly int _secondOffset; private readonly int _length; public UnalignedSegmentEnumerable(SegmentedArray<T> first, SegmentedArray<T> second, int length) : this(first, 0, second, 0, length) { } public UnalignedSegmentEnumerable(SegmentedArray<T> first, int firstOffset, SegmentedArray<T> second, int secondOffset, int length) { _first = first; _firstOffset = firstOffset; _second = second; _secondOffset = secondOffset; _length = length; } public UnalignedSegmentEnumerator<T> GetEnumerator() => new(SegmentedCollectionsMarshal.AsSegments(_first), _firstOffset, SegmentedCollectionsMarshal.AsSegments(_second), _secondOffset, _length); public ReverseEnumerable Reverse() => new(this); public readonly struct ReverseEnumerable { private readonly UnalignedSegmentEnumerable<T> _enumerable; public ReverseEnumerable(UnalignedSegmentEnumerable<T> enumerable) { _enumerable = enumerable; } public UnalignedSegmentEnumerator<T>.Reverse GetEnumerator() => new(SegmentedCollectionsMarshal.AsSegments(_enumerable._first), _enumerable._firstOffset, SegmentedCollectionsMarshal.AsSegments(_enumerable._second), _enumerable._secondOffset, _enumerable._length); public UnalignedSegmentEnumerable<T> Reverse() => _enumerable; } } private struct UnalignedSegmentEnumerator<T> { private readonly T[][] _firstSegments; private readonly int _firstOffset; private readonly T[][] _secondSegments; private readonly int _secondOffset; private readonly int _length; private int _completed; private (Memory<T> first, Memory<T> second) _current; public UnalignedSegmentEnumerator(T[][] firstSegments, int firstOffset, T[][] secondSegments, int secondOffset, int length) { _firstSegments = firstSegments; _firstOffset = firstOffset; _secondSegments = secondSegments; _secondOffset = secondOffset; _length = length; _completed = 0; _current = (Memory<T>.Empty, Memory<T>.Empty); } public readonly (Memory<T> first, Memory<T> second) Current => _current; public bool MoveNext() { if (_completed == _length) { _current = (Memory<T>.Empty, Memory<T>.Empty); return false; } var initialFirstSegment = (_completed + _firstOffset) >> SegmentedArrayHelper.GetSegmentShift<T>(); var initialSecondSegment = (_completed + _secondOffset) >> SegmentedArrayHelper.GetSegmentShift<T>(); var firstOffset = (_completed + _firstOffset) & SegmentedArrayHelper.GetOffsetMask<T>(); var secondOffset = (_completed + _secondOffset) & SegmentedArrayHelper.GetOffsetMask<T>(); var firstSegment = _firstSegments[initialFirstSegment]; var secondSegment = _secondSegments[initialSecondSegment]; var remainingInFirstSegment = firstSegment.Length - firstOffset; var remainingInSecondSegment = secondSegment.Length - secondOffset; var currentSegmentLength = Math.Min(Math.Min(remainingInFirstSegment, remainingInSecondSegment), _length - _completed); _current = (firstSegment.AsMemory().Slice(firstOffset, currentSegmentLength), secondSegment.AsMemory().Slice(secondOffset, currentSegmentLength)); _completed += currentSegmentLength; return true; } public struct Reverse { private readonly T[][] _firstSegments; private readonly int _firstOffset; private readonly T[][] _secondSegments; private readonly int _secondOffset; private readonly int _length; private int _completed; private (Memory<T> first, Memory<T> second) _current; public Reverse(T[][] firstSegments, int firstOffset, T[][] secondSegments, int secondOffset, int length) { _firstSegments = firstSegments; _firstOffset = firstOffset; _secondSegments = secondSegments; _secondOffset = secondOffset; _length = length; _completed = 0; _current = (Memory<T>.Empty, Memory<T>.Empty); } public readonly (Memory<T> first, Memory<T> second) Current => _current; public bool MoveNext() { if (_completed == _length) { _current = (Memory<T>.Empty, Memory<T>.Empty); return false; } var initialFirstSegment = (_firstOffset + _length - _completed - 1) >> SegmentedArrayHelper.GetSegmentShift<T>(); var initialSecondSegment = (_secondOffset + _length - _completed - 1) >> SegmentedArrayHelper.GetSegmentShift<T>(); var firstOffset = (_firstOffset + _length - _completed - 1) & SegmentedArrayHelper.GetOffsetMask<T>(); var secondOffset = (_secondOffset + _length - _completed - 1) & SegmentedArrayHelper.GetOffsetMask<T>(); var firstSegment = _firstSegments[initialFirstSegment]; var secondSegment = _secondSegments[initialSecondSegment]; var remainingInFirstSegment = firstOffset + 1; var remainingInSecondSegment = secondOffset + 1; var currentSegmentLength = Math.Min(Math.Min(remainingInFirstSegment, remainingInSecondSegment), _length - _completed); _current = (firstSegment.AsMemory().Slice(firstOffset - currentSegmentLength + 1, currentSegmentLength), secondSegment.AsMemory().Slice(secondOffset - currentSegmentLength + 1, currentSegmentLength)); _completed += currentSegmentLength; return true; } } } private readonly struct SegmentEnumerable<T> { private readonly SegmentedArray<T> _array; private readonly int _offset; private readonly int _length; public SegmentEnumerable(SegmentedArray<T> array) { _array = array; _offset = 0; _length = array.Length; } public SegmentEnumerable(SegmentedArray<T> array, int offset, int length) { if (offset < 0 || length < 0 || (uint)(offset + length) > (uint)array.Length) ThrowHelper.ThrowArgumentOutOfRangeException(); _array = array; _offset = offset; _length = length; } public SegmentEnumerator<T> GetEnumerator() => new(SegmentedCollectionsMarshal.AsSegments(_array), _offset, _length); public ReverseEnumerable Reverse() => new(this); public readonly struct ReverseEnumerable { private readonly SegmentEnumerable<T> _enumerable; public ReverseEnumerable(SegmentEnumerable<T> enumerable) { _enumerable = enumerable; } public SegmentEnumerator<T>.Reverse GetEnumerator() => new(SegmentedCollectionsMarshal.AsSegments(_enumerable._array), _enumerable._offset, _enumerable._length); public SegmentEnumerable<T> Reverse() => _enumerable; } } private struct SegmentEnumerator<T> { private readonly T[][] _segments; private readonly int _offset; private readonly int _length; private int _completed; private Memory<T> _current; public SegmentEnumerator(T[][] segments, int offset, int length) { _segments = segments; _offset = offset; _length = length; _completed = 0; _current = Memory<T>.Empty; } public readonly Memory<T> Current => _current; public bool MoveNext() { if (_completed == _length) { _current = Memory<T>.Empty; return false; } if (_completed == 0) { var firstSegment = _offset >> SegmentedArrayHelper.GetSegmentShift<T>(); var offset = _offset & SegmentedArrayHelper.GetOffsetMask<T>(); var segment = _segments[firstSegment]; var remainingInSegment = segment.Length - offset; _current = segment.AsMemory().Slice(offset, Math.Min(remainingInSegment, _length)); _completed = _current.Length; return true; } else { var segment = _segments[(_completed + _offset) >> SegmentedArrayHelper.GetSegmentShift<T>()]; _current = segment.AsMemory().Slice(0, Math.Min(SegmentedArrayHelper.GetSegmentSize<T>(), _length - _completed)); _completed += _current.Length; return true; } } public struct Reverse { private readonly T[][] _segments; private readonly int _offset; private readonly int _length; private int _completed; private Memory<T> _current; public Reverse(T[][] segments, int offset, int length) { _segments = segments; _offset = offset; _length = length; _completed = 0; _current = Memory<T>.Empty; } public readonly Memory<T> Current => _current; public bool MoveNext() { if (_completed == _length) { _current = Memory<T>.Empty; return false; } if (_completed == 0) { var firstSegment = _offset >> SegmentedArrayHelper.GetSegmentShift<T>(); var offset = _offset & SegmentedArrayHelper.GetOffsetMask<T>(); var segment = _segments[firstSegment]; var remainingInSegment = segment.Length - offset; _current = segment.AsMemory().Slice(offset, Math.Min(remainingInSegment, _length)); _completed = _current.Length; return true; } else { var segment = _segments[(_completed + _offset) >> SegmentedArrayHelper.GetSegmentShift<T>()]; _current = segment.AsMemory().Slice(0, Math.Min(SegmentedArrayHelper.GetSegmentSize<T>(), _length - _completed)); _completed += _current.Length; return true; } } } } } } |