| File: ArrayBuilder.cs | Web Access |
| Project: src\aspnetcore\src\Http\Routing\src\Microsoft.AspNetCore.Routing.csproj (Microsoft.AspNetCore.Routing) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // LICENSING NOTE: This file is from the dotnet corefx repository. // // See https://github.com/dotnet/corefx/blob/143df51926f2ad397fef9c9ca7ede88e2721e801/src/Common/src/System/Collections/Generic/ArrayBuilder.cs #nullable disable using System.Diagnostics; namespace Microsoft.AspNetCore.Routing; /// <summary> /// Helper type for avoiding allocations while building arrays. /// </summary> /// <typeparam name="T">The element type.</typeparam> internal struct ArrayBuilder<T> { private const int DefaultCapacity = 4; private const int MaxCoreClrArrayLength = 0x7fefffff; // For byte arrays the limit is slightly larger private T[] _array; // Starts out null, initialized on first Add. private int _count; // Number of items into _array we're using. /// <summary> /// Initializes the <see cref="ArrayBuilder{T}"/> with a specified capacity. /// </summary> /// <param name="capacity">The capacity of the array to allocate.</param> public ArrayBuilder(int capacity) : this() { Debug.Assert(capacity >= 0); if (capacity > 0) { _array = new T[capacity]; } } /// <summary> /// Gets the number of items this instance can store without re-allocating, /// or 0 if the backing array is <c>null</c>. /// </summary> public int Capacity => _array?.Length ?? 0; /// <summary>Gets the current underlying array.</summary> public T[] Buffer => _array; /// <summary> /// Gets the number of items in the array currently in use. /// </summary> public int Count => _count; /// <summary> /// Gets or sets the item at a certain index in the array. /// </summary> /// <param name="index">The index into the array.</param> public T this[int index] { get { Debug.Assert(index >= 0 && index < _count); return _array[index]; } } /// <summary> /// Adds an item to the backing array, resizing it if necessary. /// </summary> /// <param name="item">The item to add.</param> public void Add(T item) { if (_count == Capacity) { EnsureCapacity(_count + 1); } UncheckedAdd(item); } /// <summary> /// Gets the first item in this builder. /// </summary> public T First() { Debug.Assert(_count > 0); return _array[0]; } /// <summary> /// Gets the last item in this builder. /// </summary> public T Last() { Debug.Assert(_count > 0); return _array[_count - 1]; } /// <summary> /// Creates an array from the contents of this builder. /// </summary> /// <remarks> /// Do not call this method twice on the same builder. /// </remarks> public T[] ToArray() { if (_count == 0) { return Array.Empty<T>(); } Debug.Assert(_array != null); // Nonzero _count should imply this T[] result = _array; if (_count < result.Length) { // Avoid a bit of overhead (method call, some branches, extra codegen) // which would be incurred by using Array.Resize result = new T[_count]; Array.Copy(_array, 0, result, 0, _count); } #if DEBUG // Try to prevent callers from using the ArrayBuilder after ToArray, if _count != 0. _count = -1; _array = null; #endif return result; } /// <summary> /// Adds an item to the backing array, without checking if there is room. /// </summary> /// <param name="item">The item to add.</param> /// <remarks> /// Use this method if you know there is enough space in the <see cref="ArrayBuilder{T}"/> /// for another item, and you are writing performance-sensitive code. /// </remarks> public void UncheckedAdd(T item) { Debug.Assert(_count < Capacity); _array[_count++] = item; } private void EnsureCapacity(int minimum) { Debug.Assert(minimum > Capacity); int capacity = Capacity; int nextCapacity = capacity == 0 ? DefaultCapacity : 2 * capacity; if ((uint)nextCapacity > (uint)MaxCoreClrArrayLength) { nextCapacity = Math.Max(capacity + 1, MaxCoreClrArrayLength); } nextCapacity = Math.Max(nextCapacity, minimum); T[] next = new T[nextCapacity]; if (_count > 0) { Array.Copy(_array, 0, next, 0, _count); } _array = next; } }