| File: Infrastructure\VirtualChars\VirtualCharSequence.Chunks.cs | Web Access |
| Project: src\aspnetcore\src\Framework\AspNetCoreAnalyzers\src\Analyzers\Microsoft.AspNetCore.App.Analyzers.csproj (Microsoft.AspNetCore.App.Analyzers) |
// 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.Text; using Microsoft.AspNetCore.Analyzers.Infrastructure.VirtualChars; using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.Text; namespace Microsoft.AspNetCore.Analyzers.Infrastructure.VirtualChars; internal partial struct VirtualCharSequence { /// <summary> /// Abstraction over a contiguous chunk of <see cref="VirtualChar"/>s. This /// is used so we can expose <see cref="VirtualChar"/>s over an <see cref="ImmutableArray{VirtualChar}"/> /// or over a <see cref="string"/>. The latter is especially useful for reducing /// memory usage in common cases of string tokens without escapes. /// </summary> private abstract partial class Chunk { protected Chunk() { } public abstract int Length { get; } public abstract VirtualChar this[int index] { get; } public abstract VirtualChar? Find(int position); } /// <summary> /// Thin wrapper over an actual <see cref="ImmutableSegmentedList{T}"/>. /// This will be the common construct we generate when getting the /// <see cref="Chunk"/> for a string token that has escapes in it. /// </summary> private class ImmutableSegmentedListChunk : Chunk { private readonly ImmutableList<VirtualChar> _array; public ImmutableSegmentedListChunk(ImmutableList<VirtualChar> array) => _array = array; public override int Length => _array.Count; public override VirtualChar this[int index] => _array[index]; public override VirtualChar? Find(int position) { if (_array.IsEmpty) { return null; } if (position < _array[0].Span.Start || position >= _array[_array.Count - 1].Span.End) { return null; } var index = BinarySearch(_array, position, static (ch, position) => { if (position < ch.Span.Start) { return 1; } if (position >= ch.Span.End) { return -1; } return 0; }); Debug.Assert(index >= 0); return _array[index]; } } internal static int BinarySearch<TElement, TValue>(ImmutableList<TElement> array, TValue value, Func<TElement, TValue, int> comparer) { int low = 0; int high = array.Count - 1; while (low <= high) { int middle = low + ((high - low) >> 1); int comparison = comparer(array[middle], value); if (comparison == 0) { return middle; } if (comparison > 0) { high = middle - 1; } else { low = middle + 1; } } return ~low; } /// <summary> /// Represents a <see cref="Chunk"/> on top of a normal /// string. This is the common case of the type of the sequence we would /// create for a normal string token without any escapes in it. /// </summary> private class StringChunk : Chunk { private readonly int _firstVirtualCharPosition; /// <summary> /// The underlying string that we're returning virtual chars from. Note: /// this will commonly include things like quote characters. Clients who /// do not want that should then ask for an appropriate <see cref="VirtualCharSequence.GetSubSequence"/> /// back that does not include those characters. /// </summary> private readonly string _underlyingData; public StringChunk(int firstVirtualCharPosition, string data) { _firstVirtualCharPosition = firstVirtualCharPosition; _underlyingData = data; } public override int Length => _underlyingData.Length; public override VirtualChar? Find(int position) { var stringIndex = position - _firstVirtualCharPosition; if (stringIndex < 0 || stringIndex >= _underlyingData.Length) { return null; } return this[stringIndex]; } public override VirtualChar this[int index] { get { #if DEBUG // We should never have a properly paired high/low surrogate in a StringChunk. We are only created // when the string has the same number of chars as there are VirtualChars. if (char.IsHighSurrogate(_underlyingData[index])) { Debug.Assert(index + 1 >= _underlyingData.Length || !char.IsLowSurrogate(_underlyingData[index + 1])); } #endif var span = new TextSpan(_firstVirtualCharPosition + index, length: 1); var ch = _underlyingData[index]; return char.IsSurrogate(ch) ? VirtualChar.Create(ch, span) : VirtualChar.Create(new Rune(ch), span); } } } }