SmileParserBootstrapper.java

package tools.jackson.dataformat.smile;

import java.io.*;

import tools.jackson.core.*;
import tools.jackson.core.exc.StreamReadException;
import tools.jackson.core.exc.JacksonIOException;
import tools.jackson.core.io.IOContext;
import tools.jackson.core.sym.ByteQuadsCanonicalizer;

/**
 * Simple bootstrapper version used with Smile format parser.
 */
public class SmileParserBootstrapper
{
    /*
    /**********************************************************************
    /* Configuration
    /**********************************************************************
     */

    protected final IOContext _ioContext;

    protected final InputStream _in;

    /*
    /**********************************************************************
    /* Input buffering
    /**********************************************************************
     */

    protected final byte[] _inputBuffer;

    protected int _inputPtr;

    protected int _inputEnd;

    /**
     * Flag that indicates whether buffer above is to be recycled
     * after being used or not.
     */
    protected final boolean _bufferRecyclable;

    /*
    /**********************************************************************
    /* Input location
    /**********************************************************************
     */

    /**
     * Current number of input units (bytes or chars) that were processed in
     * previous blocks,
     * before contents of current input buffer.
     *<p>
     * Note: includes possible BOMs, if those were part of the input.
     */
    protected int _inputProcessed;

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

    public SmileParserBootstrapper(IOContext ctxt, InputStream in)
    {
        _ioContext = ctxt;
        _in = in;
        _inputBuffer = ctxt.allocReadIOBuffer();
        _inputEnd = _inputPtr = 0;
        _inputProcessed = 0;
        _bufferRecyclable = true;
    }

    public SmileParserBootstrapper(IOContext ctxt, byte[] inputBuffer, int inputStart, int inputLen)
    {
        _ioContext = ctxt;
        _in = null;
        _inputBuffer = inputBuffer;
        _inputPtr = inputStart;
        _inputEnd = (inputStart + inputLen);
        // Need to offset this for correct location info
        _inputProcessed = -inputStart;
        _bufferRecyclable = false;
    }

    public SmileParser constructParser(ObjectReadContext readCtxt,
            int factoryFeatures,
            int generalParserFeatures, int smileFeatures,
            ByteQuadsCanonicalizer rootByteSymbols)
        throws JacksonException
    {
        // 13-Mar-2021, tatu: [dataformats-binary#252] Create canonicalizing OR
        //    placeholder, depending on settings
        ByteQuadsCanonicalizer can = rootByteSymbols.makeChildOrPlaceholder(factoryFeatures);
        // We just need a single byte, really, to know if it starts with header
        int end = _inputEnd;
        if ((_inputPtr < end) && (_in != null)) {
            try {
                int count = _in.read(_inputBuffer, end, _inputBuffer.length - end);
                if (count > 0) {
                    _inputEnd += count;
                }
            } catch (IOException e) {
                throw JacksonIOException.construct(e);
            }
        }

        SmileParser p = new SmileParser(readCtxt, _ioContext, generalParserFeatures, smileFeatures,
                can,
                _in, _inputBuffer, _inputPtr, _inputEnd, _bufferRecyclable);
        boolean hadSig = false;

        if (_inputPtr >= _inputEnd) { // only the case for empty doc
            // 11-Oct-2012, tatu: Actually, let's allow empty documents even if
            //   header signature would otherwise be needed. This is useful for
            //   JAX-RS provider, empty PUT/POST payloads.
            return p;
        }
        final byte firstByte = _inputBuffer[_inputPtr];
        if (firstByte == SmileConstants.HEADER_BYTE_1) {
            // need to ensure it gets properly handled so caller won't see the signature
            hadSig = p.handleSignature(true, true);
        }

        if (!hadSig && SmileReadFeature.REQUIRE_HEADER.enabledIn(smileFeatures)) {
            // Ok, first, let's see if it looks like plain JSON...
            String msg;

            if (firstByte == '{' || firstByte == '[') {
                msg = "Input does not start with Smile format header (first byte = 0x"
                    +Integer.toHexString(firstByte & 0xFF)+") -- rather, it starts with '"+((char) firstByte)
                    +"' (plain JSON input?) -- can not parse";
            } else {
                msg = "Input does not start with Smile format header (first byte = 0x"
                +Integer.toHexString(firstByte & 0xFF)+") and parser has REQUIRE_HEADER enabled: can not parse";
            }
            throw new StreamReadException(p, msg);
        }
        return p;
    }
}