Add one more puzzle-piece for async parsing api
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java
index 97cd004..eb93746 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java
@@ -12,6 +12,7 @@
 import com.fasterxml.jackson.core.format.MatchStrength;
 import com.fasterxml.jackson.core.io.*;
 import com.fasterxml.jackson.core.json.*;
+import com.fasterxml.jackson.core.json.async.NonBlockingJsonParser;
 import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
 import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer;
 import com.fasterxml.jackson.core.util.BufferRecycler;
@@ -738,7 +739,7 @@
 
     /*
     /**********************************************************
-    /* Parser factories (new ones, as per [Issue-25])
+    /* Parser factories, traditional (blocking) I/O sources
     /**********************************************************
      */
 
@@ -926,6 +927,12 @@
     }
 
     /**
+     * Optional method for constructing parser for reading contents from specified {@link DataInput}
+     * instance.
+     *<p>
+     * If this factory does not support {@link DataInput} as source,
+     * will throw {@link UnsupportedOperationException}
+     *
      * @since 2.8
      */
     public JsonParser createParser(DataInput in) throws IOException {
@@ -935,6 +942,34 @@
 
     /*
     /**********************************************************
+    /* Parser factories, non-blocking (async) sources
+    /**********************************************************
+     */
+
+    /**
+     * Optional method for constructing parser for non-blocking parsing
+     * via {@link com.fasterxml.jackson.core.async.ByteArrayFeeder}
+     * interface (accessed using {@link JsonParser#getNonBlockingInputFeeder()}
+     * from constructed instance).
+     *<p>
+     * If this factory does not support non-blocking parsing (either at all,
+     * or from byte array),
+     * will throw {@link UnsupportedOperationException}
+     *
+     * @since 2.9
+     */
+    public JsonParser createNonBlockingByteArrayParser() throws IOException
+    {
+        // 17-May-2017, tatu: Need to take care not to accidentally create JSON parser
+        //   for non-JSON input:
+        _requireJSONFactory("Non-blocking source not (yet?) support for this format (%s)");
+        IOContext ctxt = _createContext(null, false);
+        ByteQuadsCanonicalizer can = _byteSymbolCanonicalizer.makeChild(_factoryFeatures);
+        return new NonBlockingJsonParser(ctxt, _parserFeatures, can);
+    }
+
+    /*
+    /**********************************************************
     /* Parser factories (old ones, pre-2.2)
     /**********************************************************
      */
@@ -1332,17 +1367,15 @@
     }
 
     /**
+     * Optional factory method, expected to be overridden
+     *
      * @since 2.8
      */
     protected JsonParser _createParser(DataInput input, IOContext ctxt) throws IOException
     {
         // 13-May-2016, tatu: Need to take care not to accidentally create JSON parser for
-        //   non-JSON input. So, bit unclean but...
-        String format = getFormatName();
-        if (format != FORMAT_NAME_JSON) { // NOTE: only ensure override; full equality NOT needed
-            throw new UnsupportedOperationException(String.format(
-                    "InputData source not (yet?) support for this format (%s)", format));
-        }
+        //   non-JSON input.
+        _requireJSONFactory("InputData source not (yet?) support for this format (%s)");
         // Also: while we can't do full bootstrapping (due to read-ahead limitations), should
         // at least handle possible UTF-8 BOM
         int firstByte = ByteSourceJsonBootstrapper.skipUTF8BOM(input);
@@ -1558,4 +1591,31 @@
         }
         return url.openStream();
     }
+
+    /*
+    /**********************************************************
+    /* Internal helper methods
+    /**********************************************************
+     */
+
+    /**
+     * Helper method called to work around the problem of this class both defining
+     * general API for constructing parsers+generators AND implementing the API
+     * for JSON handling. Problem here is that when adding new functionality
+     * via factory methods, it is not possible to leave these methods abstract
+     * (because we are implementing them for JSON); but there is risk that
+     * sub-classes do not override them all (plus older version can not implement).
+     * So a work-around is to add a check to ensure that factory is still one
+     * used for JSON; and if not, make base implementation of a factory method fail.
+     *
+     * @since 2.9
+     */
+    private final void _requireJSONFactory(String msg) {
+        // NOTE: since we only really care about whether this is standard JSON-backed factory,
+        // or its sub-class / delegated to one, no need to check for equality, identity is enough
+        String format = getFormatName();
+        if (format != FORMAT_NAME_JSON) {
+            throw new UnsupportedOperationException(String.format(msg, format));
+        }
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParser.java
new file mode 100644
index 0000000..6c67e26
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParser.java
@@ -0,0 +1,220 @@
+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;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingParserBase.java b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java
similarity index 93%
rename from src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingParserBase.java
rename to src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java
index d6d613a..4955417 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingParserBase.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/async/NonBlockingJsonParserBase.java
@@ -4,7 +4,6 @@
 import java.util.Arrays;
 
 import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.core.async.NonBlockingInputFeeder;
 import com.fasterxml.jackson.core.base.ParserBase;
 import com.fasterxml.jackson.core.io.IOContext;
 import com.fasterxml.jackson.core.sym.ByteQuadsCanonicalizer;
@@ -12,7 +11,7 @@
 /**
  * Intermediate base class for non-blocking JSON parsers.
  */
-public abstract class NonBlockingParserBase<F extends NonBlockingInputFeeder>
+public abstract class NonBlockingJsonParserBase
     extends ParserBase
 {
     /*
@@ -139,7 +138,7 @@
     /**********************************************************************
      */
 
-    public NonBlockingParserBase(IOContext ctxt, int parserFeatures,
+    public NonBlockingJsonParserBase(IOContext ctxt, int parserFeatures,
             ByteQuadsCanonicalizer sym)
     {
         super(ctxt, parserFeatures);
@@ -169,6 +168,16 @@
 
     /*
     /**********************************************************
+    /* Test support
+    /**********************************************************
+     */
+
+    protected ByteQuadsCanonicalizer symbolTableForTests() {
+        return _symbols;
+    }
+
+    /*
+    /**********************************************************
     /* Abstract methods from JsonParser
     /**********************************************************
      */
@@ -290,13 +299,23 @@
     }
 
     @Override
-    public int getTextOffset() throws IOException
-    {
+    public int getTextOffset() throws IOException {
         return 0;
     }
 
-//    public abstract int getText(Writer w) throws IOException;
-
+    @Override
+    public int getText(Writer w) throws IOException
+    {
+        if (_currToken == JsonToken.VALUE_STRING) {
+            return _textBuffer.contentsToWriter(w);
+        }
+        if (_currToken == JsonToken.NOT_AVAILABLE) {
+            _reportError("Current token not available: can not call this method");
+        }
+        // otherwise default handling works fine
+        return super.getText(w);
+    }
+    
     /*
     /**********************************************************************
     /* Public API, access to token information, binary
@@ -420,7 +439,7 @@
     protected final String _addDecodedToSymbols(int len, String name)
     {
         if (len < 5) {
-            return _symbols.addName(name, _quad1, 0);
+            return _symbols.addName(name, _quad1);
         }
         if (len < 9) {
             return _symbols.addName(name, _quad1, _quad2);