Files
2026-08-27 10:56:38 -06:00

291 lines
5.7 KiB
C#

using System;
using System.Text;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax;
internal sealed class SlidingTextWindow : IDisposable
{
public const char InvalidCharacter = '\uffff';
private const int DefaultWindowLength = 2048;
private readonly SourceText _text;
private int _basis;
private int _offset;
private readonly int _textEnd;
private char[] _characterWindow;
private int _characterWindowCount;
private int _lexemeStart;
private readonly StringTable _strings;
private static readonly ObjectPool<char[]> s_windowPool = new ObjectPool<char[]>((Factory<char[]>)(() => new char[2048]), true);
public SourceText Text => _text;
public int Position => _basis + _offset;
public int Offset => _offset;
public char[] CharacterWindow => _characterWindow;
public int LexemeRelativeStart => _lexemeStart;
public int CharacterWindowCount => _characterWindowCount;
public int LexemeStartPosition => _basis + _lexemeStart;
public int Width => _offset - _lexemeStart;
public SlidingTextWindow(SourceText text)
{
_text = text;
_basis = 0;
_offset = 0;
_textEnd = text.Length;
_strings = StringTable.GetInstance();
_characterWindow = s_windowPool.Allocate();
_lexemeStart = 0;
}
public void Dispose()
{
if (_characterWindow != null)
{
s_windowPool.Free(_characterWindow);
_characterWindow = null;
_strings.Free();
}
}
public void Start()
{
_lexemeStart = _offset;
}
public void Reset(int position)
{
int num = position - _basis;
if (num >= 0 && num <= _characterWindowCount)
{
_offset = num;
return;
}
int val = Math.Min(_text.Length, position + _characterWindow.Length) - position;
val = Math.Max(val, 0);
if (val > 0)
{
_text.CopyTo(position, _characterWindow, 0, val);
}
_lexemeStart = 0;
_offset = 0;
_basis = position;
_characterWindowCount = val;
}
private bool MoreChars()
{
if (_offset >= _characterWindowCount)
{
if (Position >= _textEnd)
{
return false;
}
if (_lexemeStart > _characterWindowCount / 4)
{
Array.Copy(_characterWindow, _lexemeStart, _characterWindow, 0, _characterWindowCount - _lexemeStart);
_characterWindowCount -= _lexemeStart;
_offset -= _lexemeStart;
_basis += _lexemeStart;
_lexemeStart = 0;
}
if (_characterWindowCount >= _characterWindow.Length)
{
char[] characterWindow = _characterWindow;
char[] array = new char[_characterWindow.Length * 2];
Array.Copy(characterWindow, 0, array, 0, _characterWindowCount);
_characterWindow = array;
}
int num = Math.Min(_textEnd - (_basis + _characterWindowCount), _characterWindow.Length - _characterWindowCount);
_text.CopyTo(_basis + _characterWindowCount, _characterWindow, _characterWindowCount, num);
_characterWindowCount += num;
return num > 0;
}
return true;
}
internal bool IsReallyAtEnd()
{
if (_offset >= _characterWindowCount)
{
return Position >= _textEnd;
}
return false;
}
public void AdvanceChar()
{
_offset++;
}
public bool TryAdvance(char c)
{
if (PeekChar() != c)
{
return false;
}
AdvanceChar();
return true;
}
public void AdvanceChar(int n)
{
_offset += n;
}
public void AdvancePastNewLine()
{
AdvanceChar(GetNewLineWidth());
}
public int GetNewLineWidth()
{
return GetNewLineWidth(PeekChar(), PeekChar(1));
}
public static int GetNewLineWidth(char currentChar, char nextChar)
{
if (currentChar != '\r' || nextChar != '\n')
{
return 1;
}
return 2;
}
public char NextChar()
{
char num = PeekChar();
if (num != '\uffff')
{
AdvanceChar();
}
return num;
}
public char PeekChar()
{
if (_offset >= _characterWindowCount && !MoreChars())
{
return '\uffff';
}
return _characterWindow[_offset];
}
public char PeekChar(int delta)
{
int position = Position;
AdvanceChar(delta);
char result = ((_offset < _characterWindowCount || MoreChars()) ? _characterWindow[_offset] : '\uffff');
Reset(position);
return result;
}
internal bool AdvanceIfMatches(string desired)
{
int length = desired.Length;
for (int i = 0; i < length; i++)
{
if (PeekChar(i) != desired[i])
{
return false;
}
}
AdvanceChar(length);
return true;
}
public string Intern(StringBuilder text)
{
return _strings.Add(text);
}
public string Intern(char[] array, int start, int length)
{
return _strings.Add(array, start, length);
}
public string GetInternedText()
{
return Intern(_characterWindow, _lexemeStart, Width);
}
public string GetText(bool intern)
{
return GetText(LexemeStartPosition, Width, intern);
}
public string GetText(int position, int length, bool intern)
{
int num = position - _basis;
switch (length)
{
case 0:
return string.Empty;
case 1:
if (_characterWindow[num] == ' ')
{
return " ";
}
if (_characterWindow[num] == '\n')
{
return "\n";
}
break;
case 2:
{
char c = _characterWindow[num];
if (c == '\r' && _characterWindow[num + 1] == '\n')
{
return "\r\n";
}
if (c == '/' && _characterWindow[num + 1] == '/')
{
return "//";
}
break;
}
case 3:
if (_characterWindow[num] == '/' && _characterWindow[num + 1] == '/' && _characterWindow[num + 2] == ' ')
{
return "// ";
}
break;
}
if (intern)
{
return Intern(_characterWindow, num, length);
}
return new string(_characterWindow, num, length);
}
internal static char GetCharsFromUtf32(uint codepoint, out char lowSurrogate)
{
if (codepoint < 65536)
{
lowSurrogate = '\uffff';
return (char)codepoint;
}
lowSurrogate = (char)((codepoint - 65536) % 1024 + 56320);
return (char)((codepoint - 65536) / 1024 + 55296);
}
}