// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; namespace System.Collections.Generic { /// <summary> /// Internal helper functions for working with enumerables. /// </summary> internal static partial class EnumerableHelpers { /// <summary>Calls Reset on an enumerator instance.</summary> /// <remarks>Enables Reset to be called without boxing on a struct enumerator that lacks a public Reset.</remarks> internal static void Reset<T>(ref T enumerator) where T : IEnumerator => enumerator.Reset(); /// <summary>Gets an enumerator singleton for an empty collection.</summary> internal static IEnumerator<T> GetEmptyEnumerator<T>() => ((IEnumerable<T>)[]).GetEnumerator(); /// <summary>Converts an enumerable to an array using the same logic as List{T}.</summary> /// <param name="source">The enumerable to convert.</param> /// <param name="length">The number of items stored in the resulting array, 0-indexed.</param> /// <returns> /// The resulting array. The length of the array may be greater than <paramref name="length"/>, /// which is the actual number of elements in the array. /// </returns> internal static T[] ToArray<T>(IEnumerable<T> source, out int length) { // Copied from Array.MaxLength in System.Private.CoreLib/src/libraries/System.Private.CoreLib/src/System/Array.cs const int ArrayMaxLength = 0X7FFFFFC7; #if NET Debug.Assert(Array.MaxLength == ArrayMaxLength); #endif if (source is ICollection<T> ic) { int count = ic.Count; if (count != 0) { // Allocate an array of the desired size, then copy the elements into it. Note that this has the same // issue regarding concurrency as other existing collections like List<T>. If the collection size // concurrently changes between the array allocation and the CopyTo, we could end up either getting an // exception from overrunning the array (if the size went up) or we could end up not filling as many // items as 'count' suggests (if the size went down). This is only an issue for concurrent collections // that implement ICollection<T>, which as of .NET 4.6 is just ConcurrentDictionary<TKey, TValue>. T[] arr = new T[count]; ic.CopyTo(arr, 0); length = count; return arr; } } else { using (var en = source.GetEnumerator()) { if (en.MoveNext()) { const int DefaultCapacity = 4; T[] arr = new T[DefaultCapacity]; arr[0] = en.Current; int count = 1; while (en.MoveNext()) { if (count == arr.Length) { // This is the same growth logic as in List<T>: // If the array is currently empty, we make it a default size. Otherwise, we attempt to // double the size of the array. Doubling will overflow once the size of the array reaches // 2^30, since doubling to 2^31 is 1 larger than Int32.MaxValue. In that case, we instead // constrain the length to be Array.MaxLength (this overflow check works because of the // cast to uint). int newLength = count << 1; if ((uint)newLength > ArrayMaxLength) { newLength = ArrayMaxLength <= count ? count + 1 : ArrayMaxLength; } Array.Resize(ref arr, newLength); } arr[count++] = en.Current; } length = count; return arr; } } } length = 0; return []; } } }