File: Language\Intermediate\IntermediateNodeCollection.InlineList.cs
Web Access
Project: src\roslyn\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using System;
using System.Buffers;
using System.Collections.Generic;

namespace Microsoft.AspNetCore.Razor.Language.Intermediate;

public sealed partial class IntermediateNodeCollection
{
    /// <summary>
    /// A list-like struct that stores a single item inline (no array allocation)
    /// and uses <see cref="ArrayPool{T}"/> for backing storage when more than one
    /// item is needed. On resize, the old array is returned to the pool.
    /// </summary>
    /// <remarks>
    /// This struct is private to <see cref="IntermediateNodeCollection"/> and its methods
    /// make assumptions about their inputs (e.g., valid indices, non-null items) that are
    /// guaranteed by the collection wrapper. They do not perform defensive validation.
    /// </remarks>
    private struct InlineList
    {
        private IntermediateNode _single;
        private IntermediateNode[] _items;
        private int _count;

        public readonly int Count => _count;

        public IntermediateNode this[int index]
        {
            readonly get
            {
                if (_items != null)
                {
                    return _items[index];
                }

                if (index == 0 && _count == 1)
                {
                    return _single;
                }

                throw new ArgumentOutOfRangeException(nameof(index));
            }

            set
            {
                if (_items != null)
                {
                    _items[index] = value;
                    return;
                }

                if (index == 0 && _count == 1)
                {
                    _single = value;
                    return;
                }

                throw new ArgumentOutOfRangeException(nameof(index));
            }
        }

        public void Add(IntermediateNode item)
        {
            if (_count == 0)
            {
                _single = item;
                _count = 1;
                return;
            }

            if (_items == null)
            {
                // Transition from inline to array
                _items = ArrayPool<IntermediateNode>.Shared.Rent(4);
                _items[0] = _single;
                _items[1] = item;
                _single = null;
                _count = 2;
                return;
            }

            if (_count == _items.Length)
            {
                Grow(_count + 1);
            }

            _items[_count++] = item;
        }

        public void Insert(int index, IntermediateNode item)
        {
            if (_count == 0 && index == 0)
            {
                _single = item;
                _count = 1;
                return;
            }

            if (_items == null)
            {
                // Currently have 1 item inline, need to transition to array
                _items = ArrayPool<IntermediateNode>.Shared.Rent(4);
                if (index == 0)
                {
                    _items[0] = item;
                    _items[1] = _single;
                }
                else
                {
                    _items[0] = _single;
                    _items[1] = item;
                }

                _single = null;
                _count = 2;
                return;
            }

            if (_count == _items.Length)
            {
                Grow(_count + 1);
            }

            if (index < _count)
            {
                Array.Copy(_items, index, _items, index + 1, _count - index);
            }

            _items[index] = item;
            _count++;
        }

        public void RemoveAt(int index)
        {
            if (_count == 1)
            {
                // Removing the single item
                _single = null;
                _count = 0;
            }
            else if (_count == 2)
            {
                // Transition back to single item mode when possible.
                _single = index == 0 ? _items[1] : _items[0];
                ArrayPool<IntermediateNode>.Shared.Return(_items, clearArray: true);
                _count = 1;
                _items = null;
            }
            else
            {
                _count--;
                if (index < _count)
                {
                    Array.Copy(_items, index + 1, _items, index, _count - index);
                }

                _items[_count] = null;
            }
        }

        public void Clear()
        {
            if (_items != null)
            {
                Array.Clear(_items, 0, _count);
                ArrayPool<IntermediateNode>.Shared.Return(_items, clearArray: false);
                _items = null;
            }
            else
            {
                _single = null;
            }

            _count = 0;
        }

        public readonly int IndexOf(IntermediateNode item)
        {
            if (_items != null)
            {
                return Array.IndexOf(_items, item, 0, _count);
            }

            if (_count == 1 && EqualityComparer<IntermediateNode>.Default.Equals(_single, item))
            {
                return 0;
            }

            return -1;
        }

        public readonly void CopyTo(IntermediateNode[] array, int arrayIndex)
        {
            if (_items != null)
            {
                Array.Copy(_items, 0, array, arrayIndex, _count);
            }
            else if (_count == 1)
            {
                array[arrayIndex] = _single;
            }
        }

        private void Grow(int minimumRequired)
        {
            var newCapacity = _items.Length * 2;
            if (newCapacity < minimumRequired)
            {
                newCapacity = minimumRequired;
            }

            var oldArray = _items;
            var newArray = ArrayPool<IntermediateNode>.Shared.Rent(newCapacity);
            Array.Copy(oldArray, newArray, _count);
            _items = newArray;
            ArrayPool<IntermediateNode>.Shared.Return(oldArray, clearArray: true);
        }
    }
}