| 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); } } }