File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\Utilities\FixedSizeArrayBuilder.cs
Web Access
Project: src\src\CodeStyle\Core\Analyzers\Microsoft.CodeAnalysis.CodeStyle.csproj (Microsoft.CodeAnalysis.CodeStyle)
// 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.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
/// <summary>
/// A bare-bones array builder, focused on the case of producing <see cref="ImmutableArray{T}"/>s where the final array
/// size is known at construction time.  In the golden path, where all the expected items are added to the builder, and
/// <see cref="MoveToImmutable"/> is called, this type is entirely garbage free.  In the non-golden path (usually
/// encountered when a cancellation token interrupts getting the final array), this will leak the intermediary array
/// created to store the results.
/// </summary>
/// <remarks>
/// This type should only be used when all of the following are true:
/// <list type="number">
/// <item>
/// The number of elements is known up front, and is fixed.  In other words, it isn't just an initial-capacity, or a
/// rough heuristic.  Rather it will always be the exact number of elements added.
/// </item>
/// <item>
/// Exactly that number of elements is actually added prior to calling <see cref="MoveToImmutable"/>.  This means no
/// patterns like "AddIfNotNull".
/// </item>
/// <item>
/// The builder will be moved to an array (see <see cref="MoveToArray"/>) or <see cref="ImmutableArray{T}"/> (see <see
/// cref="MoveToImmutable"/>).
/// </item>
/// </list>
/// If any of the above are not true (for example, the capacity is a rough hint, or the exact number of elements may not
/// match the capacity specified, or if it's intended as a scratch buffer, and won't realize a final array), then <see
/// cref="ArrayBuilder{T}.GetInstance(int, T)"/> should be used instead.
/// </remarks>
[NonCopyable]
internal struct FixedSizeArrayBuilder<T>(int capacity)
{
    private T[] _values = new T[capacity];
    private int _index;
 
    public void Add(T value)
        => _values[_index++] = value;
 
    #region AddRange overloads.  These allow us to add these collections directly, without allocating an enumerator.
 
    public void AddRange(ImmutableArray<T> values)
    {
        Contract.ThrowIfTrue(_index + values.Length > _values.Length);
        Array.Copy(ImmutableCollectionsMarshal.AsArray(values)!, 0, _values, _index, values.Length);
        _index += values.Length;
    }
 
    public void AddRange(List<T> values)
    {
        Contract.ThrowIfTrue(_index + values.Count > _values.Length);
        foreach (var v in values)
            Add(v);
    }
 
    public void AddRange(HashSet<T> values)
    {
        Contract.ThrowIfTrue(_index + values.Count > _values.Length);
        foreach (var v in values)
            Add(v);
    }
 
    public void AddRange(ArrayBuilder<T> values)
    {
        Contract.ThrowIfTrue(_index + values.Count > _values.Length);
        foreach (var v in values)
            Add(v);
    }
 
    #endregion
 
    public void AddRange(IEnumerable<T> values)
    {
        foreach (var v in values)
            Add(v);
    }
 
    public readonly void Sort()
        => Sort(Comparer<T>.Default);
 
    public readonly void Sort(IComparer<T> comparer)
    {
        if (_index > 1)
            Array.Sort(_values, 0, _index, comparer);
    }
 
    /// <summary>
    /// Moves the underlying buffer out of control of this type, into the returned <see cref="ImmutableArray{T}"/>. It
    /// is an error for a client of this type to specify a capacity and then attempt to call <see
    /// cref="MoveToImmutable"/> without that number of elements actually having been added to the builder.  This will
    /// throw if attempted.  This <see cref="FixedSizeArrayBuilder{T}"/> is effectively unusable once this is called.
    /// The internal buffer will reset to an empty array, meaning no more items could ever be added to it.
    /// </summary>
    public ImmutableArray<T> MoveToImmutable()
        => ImmutableCollectionsMarshal.AsImmutableArray(MoveToArray());
 
    public T[] MoveToArray()
    {
        Contract.ThrowIfTrue(_index != _values.Length);
        var result = _values;
        _values = Array.Empty<T>();
        _index = 0;
        return result;
    }
}