blob: 6c67e263c88f924c6702a327582729365d06afa9 [file] [log] [blame]
package com.fasterxml.jackson.core.json.async;
import java.io.IOException;
import java.io.OutputStream;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.async.ByteArrayFeeder;
import com.fasterxml.jackson.core.async.NonBlockingInputFeeder;
import com.fasterxml.jackson.core.io.IOContext;
import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
import com.fasterxml.jackson.core.util.VersionUtil;
public class NonBlockingJsonParser
extends NonBlockingJsonParserBase
implements ByteArrayFeeder
{
/*
/**********************************************************************
/* Input source config
/**********************************************************************
*/
/**
* This buffer is actually provided via {@link NonBlockingInputFeeder}
*/
protected byte[] _inputBuffer = NO_BYTES;
/**
* In addition to current buffer pointer, and end pointer,
* we will also need to know number of bytes originally
* contained. This is needed to correctly update location
* information when the block has been completed.
*/
protected int _origBufferLen;
// And from ParserBase:
// protected int _inputPtr;
// protected int _inputEnd;
/*
/**********************************************************************
/* Life-cycle
/**********************************************************************
*/
public NonBlockingJsonParser(IOContext ctxt, int parserFeatures,
ByteQuadsCanonicalizer sym)
{
super(ctxt, parserFeatures, sym);
}
/*
/**********************************************************************
/* AsyncInputFeeder impl
/**********************************************************************
*/
@Override
public ByteArrayFeeder getNonBlockingInputFeeder() {
return this;
}
@Override
public final boolean needMoreInput() {
return (_inputPtr >=_inputEnd) && !_endOfInput;
}
@Override
public void feedInput(byte[] buf, int start, int end) throws IOException
{
// Must not have remaining input
if (_inputPtr < _inputEnd) {
_reportError("Still have %d undecoded bytes, should not call 'feedInput'", _inputEnd - _inputPtr);
}
if (end < start) {
_reportError("Input end (%d) may not be before start (%d)", end, start);
}
// and shouldn't have been marked as end-of-input
if (_endOfInput) {
_reportError("Already closed, can not feed more input");
}
// Time to update pointers first
_currInputProcessed += _origBufferLen;
// And then update buffer settings
_inputBuffer = buf;
_inputPtr = start;
_inputEnd = end;
_origBufferLen = end - start;
}
@Override
public void endOfInput() {
_endOfInput = true;
}
/*
/**********************************************************************
/* Abstract methods/overrides from JsonParser
/**********************************************************************
*/
/* Implementing these methods efficiently for non-blocking cases would
* be complicated; so for now let's just use the default non-optimized
* implementation
*/
// public boolean nextFieldName(SerializableString str) throws IOException
// public String nextTextValue() throws IOException
// public int nextIntValue(int defaultValue) throws IOException
// public long nextLongValue(long defaultValue) throws IOException
// public Boolean nextBooleanValue() throws IOException
@Override
public int releaseBuffered(OutputStream out) throws IOException {
int avail = _inputEnd - _inputPtr;
if (avail > 0) {
out.write(_inputBuffer, _inputPtr, avail);
}
return avail;
}
/*
/**********************************************************************
/* Main-level decoding
/**********************************************************************
*/
@Override
public JsonToken nextToken() throws IOException
{
// First: regardless of where we really are, need at least one more byte;
// can simplify some of the checks by short-circuiting right away
if (_inputPtr >= _inputEnd) {
if (_closed) {
return null;
}
// note: if so, do not even bother changing state
if (_endOfInput) { // except for this special case
return _eofAsNextToken();
}
return JsonToken.NOT_AVAILABLE;
}
// in the middle of tokenization?
if (_currToken == JsonToken.NOT_AVAILABLE) {
return _finishToken();
}
// No: fresh new token; may or may not have existing one
_numTypesValid = NR_UNKNOWN;
// _tokenInputTotal = _currInputProcessed + _inputPtr;
// also: clear any data retained so far
_binaryValue = null;
int ch = _inputBuffer[_inputPtr++];
switch (_majorState) {
case MAJOR_INITIAL:
// TODO: Bootstrapping? BOM?
case MAJOR_ROOT:
return _startValue(ch);
case MAJOR_OBJECT_FIELD: // field or end-object
// expect name
return _startFieldName(ch);
case MAJOR_OBJECT_VALUE:
case MAJOR_ARRAY_ELEMENT: // element or end-array
return _startValue(ch);
default:
}
VersionUtil.throwInternal();
return null;
}
/**
* Method called when a (scalar) value type has been detected, but not all of
* contents have been decoded due to incomplete input available.
*/
protected final JsonToken _finishToken() throws IOException
{
// NOTE: caller ensures availability of at least one byte
switch (_minorState) {
}
return null;
}
/*
/**********************************************************************
/* Second-level decoding, root level
/**********************************************************************
*/
/**
* Helper method called to detect type of a value token (at any level), and possibly
* decode it if contained in input buffer.
* Note that possible header has been ruled out by caller and is not checked here.
*/
private final JsonToken _startValue(int ch) throws IOException
{
return null;
}
/*
/**********************************************************************
/* Second-level decoding, Name decoding
/**********************************************************************
*/
/**
* Method that handles initial token type recognition for token
* that has to be either FIELD_NAME or END_OBJECT.
*/
protected final JsonToken _startFieldName(int ch) throws IOException
{
return null;
}
}