File: Language\TagHelperCollection.MultiSegmentCollection.cs
Web Access
Project: src\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.

using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.AspNetCore.Razor.Language;

public abstract partial class TagHelperCollection
{
    /// <summary>
    ///  Represents a read-only collection of <see cref="TagHelperDescriptor"/> objects composed
    ///  from multiple contiguous memory segments, providing efficient indexed access across all segments.
    /// </summary>
    /// <remarks>
    ///  This collection is optimized for scenarios where <see cref="TagHelperDescriptor"/> items
    ///  are distributed across several memory segments, allowing for efficient access without merging
    ///  the segments into a single array. The collection is immutable and thread-safe for concurrent
    ///  read operations.
    /// </remarks>
    private sealed class MultiSegmentCollection : SegmentCollectionBase
    {
        private readonly ImmutableArray<ReadOnlyMemory<TagHelperDescriptor>> _segments;
        private readonly int[] _segmentStartIndices;
        private readonly int _count;

        public MultiSegmentCollection(ImmutableArray<ReadOnlyMemory<TagHelperDescriptor>> segments)
        {
            Debug.Assert(segments.Length > 0, "Segments cannot be empty.");

            _segments = segments;

            // Pre-calculate segment boundaries for efficient indexing
            _segmentStartIndices = new int[segments.Length];
            var count = 0;

            for (var i = 0; i < segments.Length; i++)
            {
                Debug.Assert(segments[i].Length > 0, "Segments cannot be empty.");

                _segmentStartIndices[i] = count;
                count += segments[i].Length;
            }

            _count = count;
        }

        protected override int SegmentCount => _segments.Length;

        protected override ReadOnlyMemory<TagHelperDescriptor> GetSegment(int index)
        {
            Debug.Assert(index >= 0 && index < _segments.Length);

            return _segments[index];
        }

        public override int Count => _count;

        public override TagHelperDescriptor this[int index]
        {
            get
            {
                ArgHelper.ThrowIfNegative(index);
                ArgHelper.ThrowIfGreaterThanOrEqual(index, Count);

                // Binary search to find the segment containing this index
                var segmentIndex = FindSegmentIndex(index);
                var localIndex = index - _segmentStartIndices[segmentIndex];

                return _segments[segmentIndex].Span[localIndex];
            }
        }

        private int FindSegmentIndex(int index)
        {
            var searchResult = _segmentStartIndices.BinarySearch(index);

            if (searchResult >= 0)
            {
                return searchResult;
            }

            var insertionPoint = ~searchResult;
            return insertionPoint - 1;
        }
    }
}