NonBlockingJsonParserBase.java

package tools.jackson.core.json.async;

import java.io.*;

import tools.jackson.core.*;
import tools.jackson.core.io.IOContext;
import tools.jackson.core.json.JsonParserBase;
import tools.jackson.core.json.JsonReadContext;
import tools.jackson.core.json.JsonReadFeature;
import tools.jackson.core.sym.ByteQuadsCanonicalizer;
import tools.jackson.core.util.ByteArrayBuilder;

import static tools.jackson.core.JsonTokenId.*;

/**
 * Intermediate base class for non-blocking JSON parsers.
 */
public abstract class NonBlockingJsonParserBase
    extends JsonParserBase
{
    /*
    /**********************************************************************
    /* Major state constants
    /**********************************************************************
     */

    /**
     * State right after parser has been constructed, before seeing the first byte
     * to handle possible (but optional) BOM.
     */
    protected final static int MAJOR_INITIAL = 0;

    /**
     * State right after parser a root value has been
     * finished, but next token has not yet been recognized.
     */
    protected final static int MAJOR_ROOT = 1;

    protected final static int MAJOR_OBJECT_PROPERTY_FIRST = 2;
    protected final static int MAJOR_OBJECT_PROPERTY_NEXT = 3;

    protected final static int MAJOR_OBJECT_VALUE = 4;

    protected final static int MAJOR_ARRAY_ELEMENT_FIRST = 5;
    protected final static int MAJOR_ARRAY_ELEMENT_NEXT = 6;

    /**
     * State after non-blocking input source has indicated that no more input
     * is forthcoming AND we have exhausted all the input
     */
    protected final static int MAJOR_CLOSED = 7;

    /*
    /**********************************************************************
    /* Minor state constants
    /**********************************************************************
     */

    /**
     * State in which part of (UTF-8) BOM has been detected, but not yet completely.
     */
    protected final static int MINOR_ROOT_BOM = 1;

    /**
     * State between root-level value, waiting for at least one white-space
     * character as separator
     *
     * @deprecated Since 3.1.8 not used
     */
    @Deprecated // since 3.1.8
    protected final static int MINOR_ROOT_NEED_SEPARATOR = 2;

    /**
     * State between root-level value, having processed at least one white-space
     * character, and expecting either more, start of a value, or end of input
     * stream.
     *
     * @deprecated Since 3.1.8 not used
     */
    @Deprecated // since 3.1.8
    protected final static int MINOR_ROOT_GOT_SEPARATOR = 3;

    // state before property name itself, waiting for quote (or unquoted name)
    protected final static int MINOR_PROPERTY_LEADING_WS = 4;
    // state before property name, expecting comma (or closing curly), then property name
    protected final static int MINOR_PROPERTY_LEADING_COMMA = 5;

    // State within regular (double-quoted) property name
    protected final static int MINOR_PROPERTY_NAME = 7;
    // State within regular (double-quoted) property name, within escape (having
    // encountered either just backslash, or backslash and 'u' and 0 - 3 hex digits,
    protected final static int MINOR_PROPERTY_NAME_ESCAPE = 8;

    protected final static int MINOR_PROPERTY_APOS_NAME = 9;
    protected final static int MINOR_PROPERTY_UNQUOTED_NAME = 10;

    protected final static int MINOR_VALUE_LEADING_WS = 12;
    protected final static int MINOR_VALUE_EXPECTING_COMMA = 13;
    protected final static int MINOR_VALUE_EXPECTING_COLON = 14;
    protected final static int MINOR_VALUE_WS_AFTER_COMMA = 15;

    protected final static int MINOR_VALUE_TOKEN_NULL = 16;
    protected final static int MINOR_VALUE_TOKEN_TRUE = 17;
    protected final static int MINOR_VALUE_TOKEN_FALSE = 18;

    protected final static int MINOR_VALUE_TOKEN_NON_STD = 19;

    protected final static int MINOR_NUMBER_PLUS = 22;

    protected final static int MINOR_NUMBER_MINUS = 23;
    protected final static int MINOR_NUMBER_ZERO = 24; // zero as first, possibly trimming multiple
    protected final static int MINOR_NUMBER_MINUSZERO = 25; // "-0" (and possibly more zeroes) receive
    protected final static int MINOR_NUMBER_INTEGER_DIGITS = 26;

    protected final static int MINOR_NUMBER_FRACTION_DIGITS = 30;
    protected final static int MINOR_NUMBER_EXPONENT_MARKER = 31;
    protected final static int MINOR_NUMBER_EXPONENT_DIGITS = 32;

    // Resumption state for an explicit '+' followed by '0' (and possibly more
    // zeros). Companion to {@link #MINOR_NUMBER_ZERO} and
    // {@link #MINOR_NUMBER_MINUSZERO}; needed so that the buffered text on
    // resume correctly retains the leading '+' character.
    protected final static int MINOR_NUMBER_PLUSZERO = 33;

    // [core#707] JSON5 hexadecimal literal states:
    //  - HEX_PREFIX: textBuffer holds optional sign + '0' + 'x'/'X'; awaiting first hex digit
    //  - HEX_DIGITS: textBuffer holds sign + '0x'/'0X' + at least one hex digit; awaiting more
    protected final static int MINOR_NUMBER_HEX_PREFIX = 34;
    protected final static int MINOR_NUMBER_HEX_DIGITS = 35;

    protected final static int MINOR_VALUE_STRING = 40;
    protected final static int MINOR_VALUE_STRING_ESCAPE = 41;
    protected final static int MINOR_VALUE_STRING_UTF8_2 = 42;
    protected final static int MINOR_VALUE_STRING_UTF8_3 = 43;
    protected final static int MINOR_VALUE_STRING_UTF8_4 = 44;
    protected final static int MINOR_VALUE_APOS_STRING = 45;

    /**
     * Special state at which point decoding of a non-quoted token has encountered
     * a problem; that is, either not matching fully (like "truf" instead of "true",
     * at "tru"), or not having trailing separator (or end of input), like "trueful".
     * Attempt is made, then, to decode likely full input token to report suitable
     * error.
     */
    protected final static int MINOR_VALUE_TOKEN_ERROR = 50;

    protected final static int MINOR_COMMENT_LEADING_SLASH = 51;
    protected final static int MINOR_COMMENT_CLOSING_ASTERISK = 52;
    protected final static int MINOR_COMMENT_C = 53;
    protected final static int MINOR_COMMENT_CPP = 54;
    protected final static int MINOR_COMMENT_YAML = 55;

    /*
    /**********************************************************************
    /* Helper objects, symbols (property names)
    /**********************************************************************
     */

    /**
     * Symbol table that contains property names encountered so far
     */
    final protected ByteQuadsCanonicalizer _symbols;

    /**
     * Temporary buffer used for name parsing.
     */
    protected int[] _quadBuffer = new int[8];

    protected int _quadLength;

    protected int _quad1;

    /**
     * State of token suspended at end of input, meaning depends on minor state:
     *<ul>
     * <li>Property names: current (partial) quad</li>
     * <li>UTF-8 sequences in String values: bits decoded so far</li>
     * <li>Keywords, BOM: number of bytes matched so far</li>
     * <li>Comments: minor state to return to after comment</li>
     *</ul>
     */
    protected int _pending32;

    /**
     * Companion to {@link #_pending32}, meaning depends on minor state:
     *<ul>
     * <li>Property names: number of bytes in current quad</li>
     * <li>UTF-8 sequences in String values: number of bytes decoded so far
     *   (for 4-byte sequences, range already verified if 2 or more)</li>
     * <li>Comments: 4-byte UTF-8 lead byte whose range is yet to be verified
     *   against the following byte (0 if none)</li>
     *</ul>
     */
    protected int _pendingBytes;

    /**
     * Partially decoded value of escape sequence split across input buffers.
     */
    protected int _quoted32;

    /**
     * State of escape sequence split across input buffers: non-negative value
     * is the number of hex digits of unicode escape read so far; negative values
     * are special states (see {@code QUOTED_xxx} constants of
     * {@link NonBlockingUtf8JsonParserBase}).
     */
    protected int _quotedDigits;

    /**
     * High surrogate code point awaiting matching low surrogate during
     * property name parsing, or 0 if none pending.
     *
     * @since 3.1
     */
    protected int _pendingSurrogateInName;

    /*
    /**********************************************************************
    /* Additional parsing state
    /**********************************************************************
     */

    /**
     * Current main decoding state within logical tree
     */
    protected int _majorState;

    /**
     * Value of {@link #_majorState} after completing a scalar value
     */
    protected int _majorStateAfterValue;

    /**
     * Additional indicator within state; contextually relevant for just that state
     */
    protected int _minorState;

    /**
     * Secondary minor state indicator used during decoding of escapes and/or
     * multi-byte Unicode characters
     */
    protected int _minorStateAfterSplit;

    /**
     * Flag that is sent when calling application indicates that there will
     * be no more input to parse.
     */
    protected boolean _endOfInput = false;

    /*
    /**********************************************************************
    /* Additional parsing state: non-standard tokens
    /**********************************************************************
     */

    protected final static int NON_STD_TOKEN_NAN = 0;

    protected final static int NON_STD_TOKEN_INFINITY = 1;
    protected final static int NON_STD_TOKEN_PLUS_INFINITY = 2;
    protected final static int NON_STD_TOKEN_MINUS_INFINITY = 3;

    protected final static String[] NON_STD_TOKENS = new String[] {
            "NaN",
            "Infinity", "+Infinity", "-Infinity",
    };

    protected final static double[] NON_STD_TOKEN_VALUES = new double[] {
            Double.NaN,
            Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY,
    };

    /**
     * When tokenizing non-standard ("odd") tokens, this is the type to consider;
     * also works as index to actual textual representation.
     */
    protected int _nonStdTokenType;

    /*
    /**********************************************************************
    /* Location tracking, additional
    /**********************************************************************
     */

    /**
     * Since we are fed content that may or may not start at zero offset, we need
     * to keep track of the first byte within that buffer, to be able to calculate
     * logical offset within input "stream"
     */
    protected int _currBufferStart = 0;

    /**
     * Alternate row tracker, used to keep track of position by `\r` marker
     * (whereas <code>_currInputRow</code> tracks `\n`). Used to simplify
     * tracking of linefeeds, assuming that input typically uses various
     * linefeed combinations (`\r`, `\n` or `\r\n`) consistently, in which
     * case we can simply choose max of two row candidates.
     */
    protected int _currInputRowAlt = 1;

    /*
    /**********************************************************************
    /* Life-cycle
    /**********************************************************************
     */

    public NonBlockingJsonParserBase(ObjectReadContext readCtxt, IOContext ctxt,
            int stdFeatures, int formatFeatures, ByteQuadsCanonicalizer sym)
    {
        super(readCtxt, ctxt, stdFeatures, formatFeatures);
        _symbols = sym;
        _currToken = null;
        _majorState = MAJOR_INITIAL;
        _majorStateAfterValue = MAJOR_ROOT;
    }

    @Override
    public boolean canParseAsync() { return true; }

    @Override
    public boolean willInternPropertyNames() {
        return _symbols.willInternStrings();
    }

    /*
    /**********************************************************
    /* Test support
    /**********************************************************
     */

    // Alas, needs to be public for testing
    public ByteQuadsCanonicalizer symbolTableForTests() {
        return _symbols;
    }

    /*
    /**********************************************************
    /* Abstract methods from JsonParser
    /**********************************************************
     */

    @Override
    public abstract int releaseBuffered(OutputStream out) throws JacksonException;

    @Override
    protected void _releaseBuffers() throws JacksonException
    {
        super._releaseBuffers();
        // Merge found symbols, if any:
        _symbols.release();
        // any other temp buffers?
    }

    @Override
    public Object streamReadInputSource() {
        // since input is "pushed", to traditional source...
        return null;
    }

    @Override
    protected void _closeInput() {
        // 30-May-2017, tatu: Seems like this is the most certain way to prevent
        //    further decoding... not the optimal place, but due to inheritance
        //    hierarchy most convenient.
        _currBufferStart = 0;
        _inputEnd = 0;
    }

    /*
    /**********************************************************************
    /* Overridden methods
    /**********************************************************************
     */

    @Override
    public boolean hasStringCharacters()
    {
        if (_currToken == JsonToken.VALUE_STRING) {
            // yes; is or can be made available efficiently as char[]
            return _textBuffer.hasTextAsCharacters();
        }
        if (_currToken == JsonToken.PROPERTY_NAME) {
            // not necessarily; possible but:
            return _nameCopied;
        }
        // other types, no benefit from accessing as char[]
        return false;
    }

    @Override
    public TokenStreamLocation currentLocation()
    {
        int col = _inputPtr - _currInputRowStart + 1; // 1-based
        // Since we track CR and LF separately, max should gives us right answer
        int row = Math.max(_currInputRow, _currInputRowAlt);
        return new TokenStreamLocation(_contentReference(),
                _currInputProcessed + (_inputPtr - _currBufferStart), -1L, // bytes, chars
                row, col);
    }

    @Override // @since 2.17
    protected TokenStreamLocation _currentLocationMinusOne() {
        final int prevInputPtr = _inputPtr - 1;
        int row = Math.max(_currInputRow, _currInputRowAlt);
        final int col = prevInputPtr - _currInputRowStart + 1; // 1-based
        return new TokenStreamLocation(_contentReference(),
                _currInputProcessed + (prevInputPtr - _currBufferStart), -1L, // bytes, chars
                row, col);
    }

    @Override
    public TokenStreamLocation currentTokenLocation()
    {
        return new TokenStreamLocation(_contentReference(),
                _tokenInputTotal, -1L, _tokenInputRow, _tokenInputCol);
    }

    /*
    /**********************************************************************
    /* Public API, access to token information, text
    /**********************************************************************
     */

    /**
     * Method for accessing textual representation of the current event;
     * if no current event (before first call to {@link #nextToken}, or
     * after encountering end-of-input), returns null.
     * Method can be called for any event.
     *
     * @throws JacksonException if there are general I/O or parse issues, including if the text is too large,
     * see {@link tools.jackson.core.StreamReadConstraints.Builder#maxStringLength(int)}
     */
    @Override
    public String getString() throws JacksonException
    {
        if (_currToken == JsonToken.VALUE_STRING) {
            return _textBuffer.contentsAsString();
        }
        return _getText2(_currToken);
    }

    protected final String _getText2(JsonToken t) throws JacksonException
    {
        if (t == null) {
            return null;
        }
        switch (t.id()) {
        case ID_NOT_AVAILABLE:
            return null;
        case ID_PROPERTY_NAME:
            return _streamReadContext.currentName();
        case ID_STRING:
            // fall through
        case ID_NUMBER_INT:
        case ID_NUMBER_FLOAT:
            return _textBuffer.contentsAsString();
        default:
          return t.asString();
        }
    }

    @Override // since 2.8
    public int getString(Writer writer) throws JacksonException
    {
        JsonToken t = _currToken;
        try {
            if (t == JsonToken.VALUE_STRING) {
                return _textBuffer.contentsToWriter(writer);
            }
            if (t == JsonToken.PROPERTY_NAME) {
                String n = _streamReadContext.currentName();
                writer.write(n);
                return n.length();
            }
            if (t != null) {
                if (t.isNumeric()) {
                    return _textBuffer.contentsToWriter(writer);
                }
                if (t == JsonToken.NOT_AVAILABLE) {
                    _reportError("Current token not available: cannot call this method");
                }
                char[] ch = t.asCharArray();
                writer.write(ch);
                return ch.length;
            }
        } catch (IOException e) {
            throw _wrapIOFailure(e);
        }
        return 0;
    }

    /**
     * Non-blocking parsers cannot reliably support streaming string reads
     * since content availability is unpredictable. This method throws
     * {@link UnsupportedOperationException} to indicate that it should not
     * be used with non-blocking parsers.
     *
     * @throws UnsupportedOperationException always, as this operation is not
     *         supported for non-blocking parsers
     */
    @Override
    public long readString(Writer writer) throws JacksonException
    {
        throw new UnsupportedOperationException(
"`readString(Writer)` not supported for non-blocking parsers: content availability is unpredictable and `java.io.Writer` itself blocking");
    }

    // // // Let's override default impls for improved performance

    @Override
    public String getValueAsString() throws JacksonException
    {
        if (_currToken == JsonToken.VALUE_STRING) {
            return _textBuffer.contentsAsString();
        }
        if (_currToken == JsonToken.PROPERTY_NAME) {
            return currentName();
        }
        return super.getValueAsString(null);
    }

    @Override
    public String getValueAsString(String defValue) throws JacksonException
    {
        if (_currToken == JsonToken.VALUE_STRING) {
            return _textBuffer.contentsAsString();
        }
        if (_currToken == JsonToken.PROPERTY_NAME) {
            return currentName();
        }
        return super.getValueAsString(defValue);
    }

    @Override
    public char[] getStringCharacters() throws JacksonException
    {
        if (_currToken != null) { // null only before/after document
            switch (_currToken.id()) {

            case ID_PROPERTY_NAME:
                return currentNameInBuffer();
            case ID_STRING:
                // fall through
            case ID_NUMBER_INT:
            case ID_NUMBER_FLOAT:
                return _textBuffer.getTextBuffer();

            default:
                return _currToken.asCharArray();
            }
        }
        return null;
    }

    @Override
    public int getStringLength() throws JacksonException
    {
        if (_currToken != null) { // null only before/after document
            switch (_currToken.id()) {

            case ID_PROPERTY_NAME:
                return _streamReadContext.currentName().length();
            case ID_STRING:
                // fall through
            case ID_NUMBER_INT:
            case ID_NUMBER_FLOAT:
                return _textBuffer.size();

            default:
                return _currToken.asCharArray().length;
            }
        }
        return 0;
    }

    @Override
    public int getStringOffset() throws JacksonException
    {
        // Most have offset of 0, only some may have other values:
        if (_currToken != null) {
            switch (_currToken.id()) {
            case ID_PROPERTY_NAME:
                return 0;
            case ID_STRING:
                // fall through
            case ID_NUMBER_INT:
            case ID_NUMBER_FLOAT:
                return _textBuffer.getTextOffset();
            default:
            }
        }
        return 0;
    }

    /*
    /**********************************************************************
    /* Public API, access to token information, binary
    /**********************************************************************
     */

    @Override
    public byte[] getBinaryValue(Base64Variant b64variant) throws JacksonException
    {
        if (_currToken != JsonToken.VALUE_STRING) {
            _reportError("Current token (%s) not VALUE_STRING or VALUE_EMBEDDED_OBJECT, cannot access as binary",
                    _currToken);
        }
        if (_binaryValue == null) {
            @SuppressWarnings("resource")
            ByteArrayBuilder builder = _getByteArrayBuilder();
            _decodeBase64(getString(), builder, b64variant);
            _binaryValue = builder.toByteArray();
        }
        return _binaryValue;
    }

    @Override
    public int readBinaryValue(Base64Variant b64variant, OutputStream out) throws JacksonException
    {
        byte[] b = getBinaryValue(b64variant);
        try {
            out.write(b);
        } catch (IOException e) {
            throw _wrapIOFailure(e);
        }
        return b.length;
    }

    @Override
    public Object getEmbeddedObject() throws JacksonException
    {
        if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT ) {
            return _binaryValue;
        }
        return null;
    }

    /*
    /**********************************************************************
    /* Handling of nested scope, state
    /**********************************************************************
     */

    protected final JsonToken _startArrayScope() throws JacksonException
    {
        createChildArrayContext(-1, -1);
        _majorState = MAJOR_ARRAY_ELEMENT_FIRST;
        _majorStateAfterValue = MAJOR_ARRAY_ELEMENT_NEXT;
        return _updateToken(JsonToken.START_ARRAY);
    }

    protected final JsonToken _startObjectScope() throws JacksonException
    {
        createChildObjectContext(-1, -1);
        _majorState = MAJOR_OBJECT_PROPERTY_FIRST;
        _majorStateAfterValue = MAJOR_OBJECT_PROPERTY_NEXT;
        return _updateToken(JsonToken.START_OBJECT);
    }

    protected final JsonToken _closeArrayScope() throws JacksonException
    {
        if (!_streamReadContext.inArray()) {
            _reportMismatchedEndMarker(']', '}');
        }
        JsonReadContext ctxt = _streamReadContext.getParent();
        _streamReadContext = ctxt;
        int st;
        if (ctxt.inObject()) {
            st = MAJOR_OBJECT_PROPERTY_NEXT;
        } else if (ctxt.inArray()) {
            st = MAJOR_ARRAY_ELEMENT_NEXT;
        } else {
            st = MAJOR_ROOT;
        }
        _majorState = st;
        _majorStateAfterValue = st;
        return _updateToken(JsonToken.END_ARRAY);
    }

    protected final JsonToken _closeObjectScope() throws JacksonException
    {
        if (!_streamReadContext.inObject()) {
            _reportMismatchedEndMarker('}', ']');
        }
        JsonReadContext ctxt = _streamReadContext.getParent();
        _streamReadContext = ctxt;
        int st;
        if (ctxt.inObject()) {
            st = MAJOR_OBJECT_PROPERTY_NEXT;
        } else if (ctxt.inArray()) {
            st = MAJOR_ARRAY_ELEMENT_NEXT;
        } else {
            st = MAJOR_ROOT;
        }
        _majorState = st;
        _majorStateAfterValue = st;
        return _updateToken(JsonToken.END_OBJECT);
    }

    /*
    /**********************************************************
    /* Internal methods, symbol (name) handling
    /**********************************************************
     */

    protected final String _findName(int q1, int lastQuadBytes) throws JacksonException
    {
        q1 = _padLastQuad(q1, lastQuadBytes);
        // Usually we'll find it from the canonical symbol table already
        String name = _symbols.findName(q1);
        if (name != null) {
            return name;
        }
        // If not, more work. We'll need add stuff to buffer
        _quadBuffer[0] = q1;
        return _decodeAndAddUTF8Name(_symbols, _quadBuffer, 1, lastQuadBytes);
    }

    protected final String _findName(int q1, int q2, int lastQuadBytes) throws JacksonException
    {
        q2 = _padLastQuad(q2, lastQuadBytes);
        // Usually we'll find it from the canonical symbol table already
        String name = _symbols.findName(q1, q2);
        if (name != null) {
            return name;
        }
        // If not, more work. We'll need add stuff to buffer
        _quadBuffer[0] = q1;
        _quadBuffer[1] = q2;
        return _decodeAndAddUTF8Name(_symbols, _quadBuffer, 2, lastQuadBytes);
    }

    protected final String _findName(int q1, int q2, int q3, int lastQuadBytes) throws JacksonException
    {
        q3 = _padLastQuad(q3, lastQuadBytes);
        String name = _symbols.findName(q1, q2, q3);
        if (name != null) {
            return name;
        }
        int[] quads = _quadBuffer;
        quads[0] = q1;
        quads[1] = q2;
        quads[2] = q3; // already padded
        return _decodeAndAddUTF8Name(_symbols, quads, 3, lastQuadBytes);
    }

    /**
     * @deprecated Since 3.1.8 use {@link #_decodeAndAddUTF8Name} instead
     */
    @Deprecated // @since 3.1.8
    protected final String _addName(int[] quads, int qlen, int lastQuadBytes)
        throws JacksonException
    {
        return _decodeAndAddUTF8Name(_symbols, quads, qlen, lastQuadBytes);
    }

    /*
    /**********************************************************************
    /* Internal methods, state changes
    /**********************************************************************
     */

    // Helper method called at point when all input has been exhausted and
    // input feeder has indicated no more input will be forthcoming.
    protected final JsonToken _eofAsNextToken() throws JacksonException {
        _majorState = MAJOR_CLOSED;
        if (!_streamReadContext.inRoot()) {
            _handleEOF();
        }
        close();
        return _updateTokenToNull();
    }

    protected final JsonToken _fieldComplete(String name) throws JacksonException
    {
        _majorState = MAJOR_OBJECT_VALUE;
        _streamReadContext.setCurrentName(name);
        return _updateToken(JsonToken.PROPERTY_NAME);
    }

    protected final JsonToken _valueComplete(JsonToken t) throws JacksonException
    {
        _majorState = _majorStateAfterValue;
        return _updateToken(t);
    }

    protected final JsonToken _valueCompleteInt(int value, String asText) throws JacksonException
    {
        _textBuffer.resetWithString(asText);
        _intLength = asText.length();
        _numTypesValid = NR_INT; // to force parsing
        _numberInt = value;
        _majorState = _majorStateAfterValue;
        return _updateToken(JsonToken.VALUE_NUMBER_INT);
    }

    protected final String _nonStdToken(int type) {
        return NON_STD_TOKENS[type];
    }

    /*
    /**********************************************************************
    /* Internal methods, error reporting, related
    /**********************************************************************
     */

    protected final JsonToken _valueNonStdNumberComplete(int type) throws JacksonException
    {
        String tokenStr = NON_STD_TOKENS[type];
        _textBuffer.resetWithString(tokenStr);
        if (!isEnabled(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS)) {
            _reportError("Non-standard token '%s': enable `JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS` to allow",
                    tokenStr);
        }
        _intLength = 0;
        _numTypesValid = NR_DOUBLE;
        _numberDouble = NON_STD_TOKEN_VALUES[type];
        _majorState = _majorStateAfterValue;
        return _updateToken(JsonToken.VALUE_NUMBER_FLOAT);
    }

    protected final void _updateTokenLocation()
    {
        _tokenInputRow = Math.max(_currInputRow, _currInputRowAlt);
        final int ptr = _inputPtr;
        _tokenInputCol = ptr - _currInputRowStart;
        _tokenInputTotal = _currInputProcessed + (ptr - _currBufferStart);
    }
}