using System; namespace Avalonia.Utilities { // TODO12: This should not be public #if !BUILDTASK public #endif ref struct CharacterReader { private ReadOnlySpan _s; public CharacterReader(ReadOnlySpan s) :this() { _s = s; } public bool End => _s.IsEmpty; public char Peek => _s[0]; public int Position { get; private set; } public char Take() { Position++; char taken = _s[0]; _s = _s.Slice(1); return taken; } public void SkipWhitespace() { var trimmed = _s.TrimStart(); Position += _s.Length - trimmed.Length; _s = trimmed; } public bool TakeIf(char c) { if (Peek == c) { Take(); return true; } else { return false; } } internal bool TakeIf(string s) { var p = TryPeek(s.Length); if (p.SequenceEqual(s.AsSpan())) { _s = _s.Slice(s.Length); Position += s.Length; return true; } return false; } public bool TakeIf(Func condition) { if (condition(Peek)) { Take(); return true; } return false; } public ReadOnlySpan TakeUntil(char c) { int len; for (len = 0; len < _s.Length && _s[len] != c; len++) { } var span = _s.Slice(0, len); _s = _s.Slice(len); Position += len; return span; } public ReadOnlySpan TakeWhile(Func condition) { int len; for (len = 0; len < _s.Length && condition(_s[len]); len++) { } var span = _s.Slice(0, len); _s = _s.Slice(len); Position += len; return span; } public ReadOnlySpan TryPeek(int count) { if (_s.Length < count) return ReadOnlySpan.Empty; return _s.Slice(0, count); } public ReadOnlySpan PeekWhitespace() { var trimmed = _s.TrimStart(); return _s.Slice(0, _s.Length - trimmed.Length); } public void Skip(int count) { if (_s.Length < count) throw new IndexOutOfRangeException(); _s = _s.Slice(count); } } }