| File: Expression\TokenCursor.cs | Web Access |
| Project: src\src\Microsoft.ML.Transforms\Microsoft.ML.Transforms.csproj (Microsoft.ML.Transforms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using Microsoft.ML.Runtime; namespace Microsoft.ML.Transforms { using Conditional = System.Diagnostics.ConditionalAttribute; internal sealed class TokenCursor { // This is the token stream. We cache items as we consume them. // This code assumes that the enumerator will produce an Eof token, // When the Eof is produced, _tokens is disposed and set to null. private IEnumerator<Token> _tokens; // The cache buffer. private Token[] _buffer; // The logical token index of _buffer[0]. private int _itokBase; // The limit of the cached range within _buffer (relative to the buffer). private int _itokLim; // The current position within the buffer. private int _itokCur; private Token _tokCur; private TokKind _tidCur; // If this is >= 0, this position is "pinned" - we need to keep it in the buffer. // This is to support rewinding. private int _itokPin; public TokenCursor(IEnumerable<Token> tokens) { Contracts.AssertValue(tokens); _tokens = tokens.GetEnumerator(); _buffer = new Token[0x0400]; _itokPin = -1; // Get the first token. FetchCore(); _tokCur = _buffer[_itokCur]; _tidCur = _tokCur.Kind; AssertValid(); } [Conditional("DEBUG")] private void AssertValid() { Contracts.AssertValue(_buffer); // _itokCur should never reach _itokLim. Contracts.Assert(0 <= _itokCur && _itokCur < _itokLim && _itokLim <= _buffer.Length); // The last token in the buffer is Eof iff _tokens is null. Contracts.Assert((_tokens != null) == (_buffer[_itokLim - 1].Kind != TokKind.Eof)); // _tokCur and _tidCur should match _itokCur. Contracts.Assert(_tokCur == _buffer[_itokCur]); Contracts.Assert(_tidCur == _tokCur.Kind); } public Token TokCur { get { AssertValid(); return _tokCur; } } public TokKind TidCur { get { AssertValid(); return _tidCur; } } public TokKind CtxCur { get { AssertValid(); return _tokCur.KindContext; } } // This fetches an additional token from _tokens and caches it. // If needed, makes room in the cache (_buffer) by either sliding items // or resizing _buffer. private void FetchToken() { Contracts.Assert(_tokens != null); if (_itokLim >= _buffer.Length) { // Need more room. See if we can "slide". if (_itokCur > 0 && _itokPin != 0) { int itokMin = _itokCur; if (0 < _itokPin && _itokPin < itokMin) itokMin = _itokPin; int itokSrc = itokMin; int itokDst = 0; while (itokSrc < _itokLim) _buffer[itokDst++] = _buffer[itokSrc++]; if (0 < _itokPin) _itokPin -= itokMin; _itokLim -= itokMin; _itokCur -= itokMin; _itokBase += itokMin; } else { // Need to resize the buffer. Array.Resize(ref _buffer, 2 * _buffer.Length); } } FetchCore(); } private void FetchCore() { Contracts.Assert(_tokens != null); Contracts.Assert(_itokLim < _buffer.Length); if (!_tokens.MoveNext()) { Contracts.Assert(false, "Token stream should end with an Eof token!"); throw Contracts.Except(); } // Cache the new token. Token tok = _buffer[_itokLim++] = _tokens.Current; Contracts.Assert(tok != null); // See if we're done pulling items from _tokens. if (tok.Kind == TokKind.Eof) { _tokens.Dispose(); _tokens = null; } } // This expects that _itokCur + ditok is either within the buffered token range or // just at the end of it. In other words, it does not support skipping tokens. private void MoveBy(int ditok) { AssertValid(); Contracts.Assert(-_itokCur <= ditok && ditok <= _itokLim - _itokCur); Contracts.Assert(ditok < _itokLim - _itokCur || _tokens != null); while (ditok >= _itokLim - _itokCur) FetchToken(); _itokCur += ditok; _tokCur = _buffer[_itokCur]; _tidCur = _tokCur.Kind; AssertValid(); } public TokKind TidNext() { AssertValid(); if (_tidCur != TokKind.Eof) MoveBy(1); return _tidCur; } /// <summary> /// This expects that ItokCur + ditok is either within the buffered token range or just /// at the end of it. In other words, it does not support skipping tokens. /// </summary> public Token TokPeek(int ditok) { AssertValid(); Contracts.Assert(-_itokCur <= ditok && ditok <= _itokLim - _itokCur); Contracts.Assert(ditok < _itokLim - _itokCur || _tokens != null); while (ditok >= _itokLim - _itokCur) FetchToken(); return _buffer[_itokCur + ditok]; } } }