Check in actual sources, moved to the new package, cleaned up a bit
diff --git a/src/main/java/com/fasterxml/jackson/core/Base64Variant.java b/src/main/java/com/fasterxml/jackson/core/Base64Variant.java
new file mode 100644
index 0000000..34d9bfe
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/Base64Variant.java
@@ -0,0 +1,406 @@
+/* Jackson JSON-processor.
+ *
+ * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
+ *
+ * Licensed under the License specified in file LICENSE, included with
+ * the source code and binary code bundles.
+ * You may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.fasterxml.jackson.core;
+
+import java.util.Arrays;
+
+/**
+ * Abstract base class used to define specific details of which
+ * variant of Base64 encoding/decoding is to be used. Although there is
+ * somewhat standard basic version (so-called "MIME Base64"), other variants
+ * exists, see <a href="http://en.wikipedia.org/wiki/Base64">Base64 Wikipedia entry</a> for details.
+ * 
+ * @author Tatu Saloranta
+ */
+public final class Base64Variant
+{
+    /**
+     * Placeholder used by "no padding" variant, to be used when a character
+     * value is needed.
+     */
+    final static char PADDING_CHAR_NONE = '\0';
+
+    /**
+     * Marker used to denote ascii characters that do not correspond
+     * to a 6-bit value (in this variant), and is not used as a padding
+     * character.
+     */
+    public final static int BASE64_VALUE_INVALID = -1;
+
+    /**
+     * Marker used to denote ascii character (in decoding table) that
+     * is the padding character using this variant (if any).
+     */
+    public final static int BASE64_VALUE_PADDING = -2;
+
+    /*
+    /**********************************************************
+    /* Encoding/decoding tables
+    /**********************************************************
+     */
+
+    /**
+     * Decoding table used for base 64 decoding.
+     */
+    private final int[] _asciiToBase64 = new int[128];
+
+    /**
+     * Encoding table used for base 64 decoding when output is done
+     * as characters.
+     */
+    private final char[] _base64ToAsciiC = new char[64];
+
+    /**
+     * Alternative encoding table used for base 64 decoding when output is done
+     * as ascii bytes.
+     */
+    private final byte[] _base64ToAsciiB = new byte[64];
+
+    /*
+    /**********************************************************
+    /* Other configuration
+    /**********************************************************
+     */
+
+    /**
+     * Symbolic name of variant; used for diagnostics/debugging.
+     */
+    final String _name;
+
+    /**
+     * Whether this variant uses padding or not.
+     */
+    final boolean _usesPadding;
+
+    /**
+     * Characted used for padding, if any ({@link #PADDING_CHAR_NONE} if not).
+     */
+    final char _paddingChar;
+    
+    /**
+     * Maximum number of encoded base64 characters to output during encoding
+     * before adding a linefeed, if line length is to be limited
+     * ({@link java.lang.Integer#MAX_VALUE} if not limited).
+     *<p>
+     * Note: for some output modes (when writing attributes) linefeeds may
+     * need to be avoided, and this value ignored.
+     */
+    final int _maxLineLength;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public Base64Variant(String name, String base64Alphabet, boolean usesPadding, char paddingChar, int maxLineLength)
+    {
+        _name = name;
+        _usesPadding = usesPadding;
+        _paddingChar = paddingChar;
+        _maxLineLength = maxLineLength;
+
+        // Ok and then we need to create codec tables.
+
+        // First the main encoding table:
+        int alphaLen = base64Alphabet.length();
+        if (alphaLen != 64) {
+            throw new IllegalArgumentException("Base64Alphabet length must be exactly 64 (was "+alphaLen+")");
+        }
+
+        // And then secondary encoding table and decoding table:
+        base64Alphabet.getChars(0, alphaLen, _base64ToAsciiC, 0);
+        Arrays.fill(_asciiToBase64, BASE64_VALUE_INVALID);
+        for (int i = 0; i < alphaLen; ++i) {
+            char alpha = _base64ToAsciiC[i];
+            _base64ToAsciiB[i] = (byte) alpha;
+            _asciiToBase64[alpha] = i;
+        }
+
+        // Plus if we use padding, add that in too
+        if (usesPadding) {
+            _asciiToBase64[(int) paddingChar] = BASE64_VALUE_PADDING;
+        }
+    }
+
+    /**
+     * "Copy constructor" that can be used when the base alphabet is identical
+     * to one used by another variant except for the maximum line length
+     * (and obviously, name).
+     */
+    public Base64Variant(Base64Variant base, String name, int maxLineLength)
+    {
+        this(base, name, base._usesPadding, base._paddingChar, maxLineLength);
+    }
+
+    /**
+     * "Copy constructor" that can be used when the base alphabet is identical
+     * to one used by another variant, but other details (padding, maximum
+     * line length) differ
+     */
+    public Base64Variant(Base64Variant base, String name, boolean usesPadding, char paddingChar, int maxLineLength)
+    {
+        _name = name;
+        byte[] srcB = base._base64ToAsciiB;
+        System.arraycopy(srcB, 0, this._base64ToAsciiB, 0, srcB.length);
+        char[] srcC = base._base64ToAsciiC;
+        System.arraycopy(srcC, 0, this._base64ToAsciiC, 0, srcC.length);
+        int[] srcV = base._asciiToBase64;
+        System.arraycopy(srcV, 0, this._asciiToBase64, 0, srcV.length);
+
+        _usesPadding = usesPadding;
+        _paddingChar = paddingChar;
+        _maxLineLength = maxLineLength;
+    }
+
+    /*
+    /**********************************************************
+    /* Public accessors
+    /**********************************************************
+     */
+
+    public String getName() { return _name; }
+
+    public boolean usesPadding() { return _usesPadding; }
+    public boolean usesPaddingChar(char c) { return c == _paddingChar; }
+    public boolean usesPaddingChar(int ch) { return ch == (int) _paddingChar; }
+    public char getPaddingChar() { return _paddingChar; }
+    public byte getPaddingByte() { return (byte)_paddingChar; }
+
+    public int getMaxLineLength() { return _maxLineLength; }
+
+    /*
+    /**********************************************************
+    /* Decoding support
+    /**********************************************************
+     */
+
+    /**
+     * @return 6-bit decoded value, if valid character; 
+     */
+    public int decodeBase64Char(char c)
+    {
+        int ch = (int) c;
+        return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID;
+    }
+
+    public int decodeBase64Char(int ch)
+    {
+        return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID;
+    }
+
+    public int decodeBase64Byte(byte b)
+    {
+        int ch = (int) b;
+        return (ch <= 127) ? _asciiToBase64[ch] : BASE64_VALUE_INVALID;
+    }
+
+    /*
+    /**********************************************************
+    /* Encoding support
+    /**********************************************************
+     */
+
+    public char encodeBase64BitsAsChar(int value)
+    {
+        /* Let's assume caller has done necessary checks; this
+         * method must be fast and inlinable
+         */
+        return _base64ToAsciiC[value];
+    }
+
+    /**
+     * Method that encodes given right-aligned (LSB) 24-bit value
+     * into 4 base64 characters, stored in given result buffer.
+     */
+    public int encodeBase64Chunk(int b24, char[] buffer, int ptr)
+    {
+        buffer[ptr++] = _base64ToAsciiC[(b24 >> 18) & 0x3F];
+        buffer[ptr++] = _base64ToAsciiC[(b24 >> 12) & 0x3F];
+        buffer[ptr++] = _base64ToAsciiC[(b24 >> 6) & 0x3F];
+        buffer[ptr++] = _base64ToAsciiC[b24 & 0x3F];
+        return ptr;
+    }
+
+    public void encodeBase64Chunk(StringBuilder sb, int b24)
+    {
+        sb.append(_base64ToAsciiC[(b24 >> 18) & 0x3F]);
+        sb.append(_base64ToAsciiC[(b24 >> 12) & 0x3F]);
+        sb.append(_base64ToAsciiC[(b24 >> 6) & 0x3F]);
+        sb.append(_base64ToAsciiC[b24 & 0x3F]);
+    }
+
+    /**
+     * Method that outputs partial chunk (which only encodes one
+     * or two bytes of data). Data given is still aligned same as if
+     * it as full data; that is, missing data is at the "right end"
+     * (LSB) of int.
+     *
+     * @param outputBytes Number of encoded bytes included (either 1 or 2)
+     */
+    public int encodeBase64Partial(int bits, int outputBytes, char[] buffer, int outPtr)
+    {
+        buffer[outPtr++] = _base64ToAsciiC[(bits >> 18) & 0x3F];
+        buffer[outPtr++] = _base64ToAsciiC[(bits >> 12) & 0x3F];
+        if (_usesPadding) {
+            buffer[outPtr++] = (outputBytes == 2) ?
+                _base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar;
+            buffer[outPtr++] = _paddingChar;
+        } else {
+            if (outputBytes == 2) {
+                buffer[outPtr++] = _base64ToAsciiC[(bits >> 6) & 0x3F];
+            }
+        }
+        return outPtr;
+    }
+
+    public void encodeBase64Partial(StringBuilder sb, int bits, int outputBytes)
+    {
+        sb.append(_base64ToAsciiC[(bits >> 18) & 0x3F]);
+        sb.append(_base64ToAsciiC[(bits >> 12) & 0x3F]);
+        if (_usesPadding) {
+            sb.append((outputBytes == 2) ?
+                      _base64ToAsciiC[(bits >> 6) & 0x3F] : _paddingChar);
+            sb.append(_paddingChar);
+        } else {
+            if (outputBytes == 2) {
+                sb.append(_base64ToAsciiC[(bits >> 6) & 0x3F]);
+            }
+        }
+    }
+
+    public byte encodeBase64BitsAsByte(int value)
+    {
+        // As with above, assuming it is 6-bit value
+        return _base64ToAsciiB[value];
+    }
+
+    /**
+     * Method that encodes given right-aligned (LSB) 24-bit value
+     * into 4 base64 bytes (ascii), stored in given result buffer.
+     */
+    public int encodeBase64Chunk(int b24, byte[] buffer, int ptr)
+    {
+        buffer[ptr++] = _base64ToAsciiB[(b24 >> 18) & 0x3F];
+        buffer[ptr++] = _base64ToAsciiB[(b24 >> 12) & 0x3F];
+        buffer[ptr++] = _base64ToAsciiB[(b24 >> 6) & 0x3F];
+        buffer[ptr++] = _base64ToAsciiB[b24 & 0x3F];
+        return ptr;
+    }
+
+    /**
+     * Method that outputs partial chunk (which only encodes one
+     * or two bytes of data). Data given is still aligned same as if
+     * it as full data; that is, missing data is at the "right end"
+     * (LSB) of int.
+     *
+     * @param outputBytes Number of encoded bytes included (either 1 or 2)
+     */
+    public int encodeBase64Partial(int bits, int outputBytes, byte[] buffer, int outPtr)
+    {
+        buffer[outPtr++] = _base64ToAsciiB[(bits >> 18) & 0x3F];
+        buffer[outPtr++] = _base64ToAsciiB[(bits >> 12) & 0x3F];
+        if (_usesPadding) {
+            byte pb = (byte) _paddingChar;
+            buffer[outPtr++] = (outputBytes == 2) ?
+                _base64ToAsciiB[(bits >> 6) & 0x3F] : pb;
+            buffer[outPtr++] = pb;
+        } else {
+            if (outputBytes == 2) {
+                buffer[outPtr++] = _base64ToAsciiB[(bits >> 6) & 0x3F];
+            }
+        }
+        return outPtr;
+    }
+
+    /**
+     * Convenience method for converting given byte array as base64 encoded
+     * String using this variant's settings.
+     * Resulting value is "raw", that is, not enclosed in double-quotes.
+     * 
+     * @param input Byte array to encode
+     */
+    public String encode(byte[] input)
+    {
+        return encode(input, false);
+    }
+
+    /**
+     * Convenience method for converting given byte array as base64 encoded
+     * String using this variant's settings, optionally enclosed in
+     * double-quotes.
+     * 
+     * @param input Byte array to encode
+     * @param addQuotes Whether to surround resulting value in double quotes or not
+     */
+    public String encode(byte[] input, boolean addQuotes)
+    {
+        int inputEnd = input.length;
+        StringBuilder sb;
+        {
+            // let's approximate... 33% overhead, ~= 3/8 (0.375)
+            int outputLen = inputEnd + (inputEnd >> 2) + (inputEnd >> 3);
+            sb = new StringBuilder(outputLen);
+        }
+        if (addQuotes) {
+            sb.append('"');
+        }
+
+        int chunksBeforeLF = getMaxLineLength() >> 2;
+
+        // Ok, first we loop through all full triplets of data:
+        int inputPtr = 0;
+        int safeInputEnd = inputEnd-3; // to get only full triplets
+
+        while (inputPtr <= safeInputEnd) {
+            // First, mash 3 bytes into lsb of 32-bit int
+            int b24 = ((int) input[inputPtr++]) << 8;
+            b24 |= ((int) input[inputPtr++]) & 0xFF;
+            b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
+            encodeBase64Chunk(sb, b24);
+            if (--chunksBeforeLF <= 0) {
+                // note: must quote in JSON value, so not really useful...
+                sb.append('\\');
+                sb.append('n');
+                chunksBeforeLF = getMaxLineLength() >> 2;
+            }
+        }
+
+        // And then we may have 1 or 2 leftover bytes to encode
+        int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
+        if (inputLeft > 0) { // yes, but do we have room for output?
+            int b24 = ((int) input[inputPtr++]) << 16;
+            if (inputLeft == 2) {
+                b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
+            }
+            encodeBase64Partial(sb, b24, inputLeft);
+        }
+
+        if (addQuotes) {
+            sb.append('"');
+        }
+        return sb.toString();
+    }
+    
+    /*
+    /**********************************************************
+    /* other methods
+    /**********************************************************
+     */
+
+    @Override
+    public String toString() { return _name; }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/core/Base64Variants.java b/src/main/java/com/fasterxml/jackson/core/Base64Variants.java
new file mode 100644
index 0000000..a97f9dd
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/Base64Variants.java
@@ -0,0 +1,90 @@
+/* Jackson JSON-processor.
+ *
+ * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
+ *
+ * Licensed under the License specified in file LICENSE, included with
+ * the source code and binary code bundles.
+ * You may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.fasterxml.jackson.core;
+
+/**
+ * Container for commonly used Base64 variants.
+ * 
+ * @author Tatu Saloranta
+ */
+public final class Base64Variants
+{
+    final static String STD_BASE64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+    /**
+     * This variant is what most people would think of "the standard"
+     * Base64 encoding.
+     *<p>
+     * See <a href="">wikipedia Base64 entry</a> for details.
+     *<p>
+     * Note that although this can be thought of as the standard variant,
+     * it is <b>not</b> the default for Jackson: no-linefeeds alternative
+     * is because of JSON requirement of escaping all linefeeds.
+     */
+    public final static Base64Variant MIME;
+    static {
+        MIME = new Base64Variant("MIME", STD_BASE64_ALPHABET, true, '=', 76);
+    }
+
+    /**
+     * Slightly non-standard modification of {@link #MIME} which does not
+     * use linefeeds (max line length set to infinite). Useful when linefeeds
+     * wouldn't work well (possibly in attributes), or for minor space savings
+     * (save 1 linefeed per 76 data chars, ie. ~1.4% savings).
+     */
+    public final static Base64Variant MIME_NO_LINEFEEDS;
+    static {
+        MIME_NO_LINEFEEDS = new Base64Variant(MIME, "MIME-NO-LINEFEEDS", Integer.MAX_VALUE);
+    }
+
+    /**
+     * This variant is the one that predates {@link #MIME}: it is otherwise
+     * identical, except that it mandates shorter line length.
+     */
+    public final static Base64Variant PEM = new Base64Variant(MIME, "PEM", true, '=', 64);
+
+    /**
+     * This non-standard variant is usually used when encoded data needs to be
+     * passed via URLs (such as part of GET request). It differs from the
+     * base {@link #MIME} variant in multiple ways.
+     * First, no padding is used: this also means that it generally can not
+     * be written in multiple separate but adjacent chunks (which would not
+     * be the usual use case in any case). Also, no linefeeds are used (max
+     * line length set to infinite). And finally, two characters (plus and
+     * slash) that would need quoting in URLs are replaced with more
+     * optimal alternatives (hyphen and underscore, respectively).
+     */
+    public final static Base64Variant MODIFIED_FOR_URL;
+    static {
+        StringBuffer sb = new StringBuffer(STD_BASE64_ALPHABET);
+        // Replace plus with hyphen, slash with underscore (and no padding)
+        sb.setCharAt(sb.indexOf("+"), '-');
+        sb.setCharAt(sb.indexOf("/"), '_');
+        /* And finally, let's not split lines either, wouldn't work too
+         * well with URLs
+         */
+        MODIFIED_FOR_URL = new Base64Variant("MODIFIED-FOR-URL", sb.toString(), false, Base64Variant.PADDING_CHAR_NONE, Integer.MAX_VALUE);
+    }
+
+    /**
+     * Method used to get the default variant ("MIME_NO_LINEFEEDS") for cases
+     * where caller does not explicitly specify the variant.
+     * We will prefer no-linefeed version because linefeeds in JSON values
+     * must be escaped, making linefeed-containing variants sub-optimal.
+     */
+    public static Base64Variant getDefaultVariant() {
+        return MIME_NO_LINEFEEDS;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/FormatSchema.java b/src/main/java/com/fasterxml/jackson/core/FormatSchema.java
new file mode 100644
index 0000000..cdef7c2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/FormatSchema.java
@@ -0,0 +1,29 @@
+package com.fasterxml.jackson.core;
+
+/**
+ * Simple tag interface used to mark schema objects that are used by some
+ * {@link JsonParser} and {@link JsonGenerator} implementations to further
+ * specify structure of expected format.
+ * Basic JSON-based parsers and generators do not use schemas, but some data
+ * formats (like many binary data formats like Thrift, protobuf) mandate
+ * use of schemas.
+ *<p>
+ * Since there is little commonality between schemas for different data formats,
+ * this interface does not define much meaningful functionality for accessing
+ * schema details; rather, specific parser and generator implementations need
+ * to cast to schema implementations they use. This marker interface is mostly
+ * used for tagging "some kind of schema" -- instead of passing opaque
+ * {@link java.lang.Object} -- for documentation purposes.
+ * 
+ * @since 1.8
+ */
+public interface FormatSchema
+{
+    /**
+     * Method that can be used to get an identifier that can be used for diagnostics
+     * purposes, to indicate what kind of data format this schema is used for: typically
+     * it is a short name of format itself, but it can also contain additional information
+     * in cases where data format supports multiple types of schemas.
+     */
+    public String getSchemaType();
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonEncoding.java b/src/main/java/com/fasterxml/jackson/core/JsonEncoding.java
new file mode 100644
index 0000000..7d78fb2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonEncoding.java
@@ -0,0 +1,47 @@
+package com.fasterxml.jackson.core;
+
+/**
+ * Enumeration that defines legal encodings that can be used
+ * for JSON content, based on list of allowed encodings from
+ * <a href="http://www.ietf.org/rfc/rfc4627.txt">JSON specification</a>.
+ *<p>
+ * Note: if application want to explicitly disregard Encoding
+ * limitations (to read in JSON encoded using an encoding not
+ * listed as allowed), they can use {@link java.io.Reader} /
+ * {@link java.io.Writer} instances as input
+ */
+public enum JsonEncoding {
+    UTF8("UTF-8", false), // N/A for big-endian, really
+        UTF16_BE("UTF-16BE", true),
+        UTF16_LE("UTF-16LE", false),
+        UTF32_BE("UTF-32BE", true),
+        UTF32_LE("UTF-32LE", false)
+        ;
+    
+    protected final String _javaName;
+
+    protected final boolean _bigEndian;
+    
+    JsonEncoding(String javaName, boolean bigEndian)
+    {
+        _javaName = javaName;
+        _bigEndian = bigEndian;
+    }
+
+    /**
+     * Method for accessing encoding name that JDK will support.
+     *
+     * @return Matching encoding name that JDK will support.
+     */
+    public String getJavaName() { return _javaName; }
+
+    /**
+     * Whether encoding is big-endian (if encoding supports such
+     * notion). If no such distinction is made (as is the case for
+     * {@link #UTF8}), return value is undefined.
+     *
+     * @return True for big-endian encodings; false for little-endian
+     *   (or if not applicable)
+     */
+    public boolean isBigEndian() { return _bigEndian; }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java
new file mode 100644
index 0000000..4ad28ca
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java
@@ -0,0 +1,831 @@
+/* Jackson JSON-processor.
+ *
+ * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
+ *
+ * Licensed under the License specified in file LICENSE, included with
+ * the source code and binary code bundles.
+ * You may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.fasterxml.jackson.core;
+
+import java.io.*;
+import java.lang.ref.SoftReference;
+import java.net.URL;
+
+import com.fasterxml.jackson.core.format.InputAccessor;
+import com.fasterxml.jackson.core.format.MatchStrength;
+import com.fasterxml.jackson.core.io.*;
+import com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper;
+import com.fasterxml.jackson.core.json.ReaderBasedJsonParser;
+import com.fasterxml.jackson.core.json.UTF8JsonGenerator;
+import com.fasterxml.jackson.core.json.WriterBasedJsonGenerator;
+import com.fasterxml.jackson.core.sym.BytesToNameCanonicalizer;
+import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer;
+import com.fasterxml.jackson.core.util.BufferRecycler;
+import com.fasterxml.jackson.core.util.VersionUtil;
+
+/**
+ * The main factory class of Jackson package, used to configure and
+ * construct reader (aka parser, {@link JsonParser})
+ * and writer (aka generator, {@link JsonGenerator})
+ * instances.
+ *<p>
+ * Factory instances are thread-safe and reusable after configuration
+ * (if any). Typically applications and services use only a single
+ * globally shared factory instance, unless they need differently
+ * configured factories. Factory reuse is important if efficiency matters;
+ * most recycling of expensive construct is done on per-factory basis.
+ *<p>
+ * Creation of a factory instance is a light-weight operation,
+ * and since there is no need for pluggable alternative implementations
+ * (as there is no "standard" JSON processor API to implement),
+ * the default constructor is used for constructing factory
+ * instances.
+ *
+ * @author Tatu Saloranta
+ */
+public class JsonFactory implements Versioned
+{
+    /**
+     * Name used to identify JSON format
+     * (and returned by {@link #getFormatName()}
+     */
+    public final static String FORMAT_NAME_JSON = "JSON";
+    
+    /**
+     * Bitfield (set of flags) of all parser features that are enabled
+     * by default.
+     */
+    final static int DEFAULT_PARSER_FEATURE_FLAGS = JsonParser.Feature.collectDefaults();
+
+    /**
+     * Bitfield (set of flags) of all generator features that are enabled
+     * by default.
+     */
+    final static int DEFAULT_GENERATOR_FEATURE_FLAGS = JsonGenerator.Feature.collectDefaults();
+
+    /*
+    /**********************************************************
+    /* Buffer, symbol table management
+    /**********************************************************
+     */
+
+    /**
+     * This <code>ThreadLocal</code> contains a {@link java.lang.ref.SoftRerefence}
+     * to a {@link BufferRecycler} used to provide a low-cost
+     * buffer recycling between reader and writer instances.
+     */
+    final protected static ThreadLocal<SoftReference<BufferRecycler>> _recyclerRef
+        = new ThreadLocal<SoftReference<BufferRecycler>>();
+
+    /**
+     * Each factory comes equipped with a shared root symbol table.
+     * It should not be linked back to the original blueprint, to
+     * avoid contents from leaking between factories.
+     */
+    protected CharsToNameCanonicalizer _rootCharSymbols = CharsToNameCanonicalizer.createRoot();
+
+    /**
+     * Alternative to the basic symbol table, some stream-based
+     * parsers use different name canonicalization method.
+     *<p>
+     * TODO: should clean up this; looks messy having 2 alternatives
+     * with not very clear differences.
+     */
+    protected BytesToNameCanonicalizer _rootByteSymbols = BytesToNameCanonicalizer.createRoot();
+
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    /**
+     * Object that implements conversion functionality between
+     * Java objects and JSON content. For base JsonFactory implementation
+     * usually not set by default, but can be explicitly set.
+     * Sub-classes (like @link org.codehaus.jackson.map.MappingJsonFactory}
+     * usually provide an implementation.
+     */
+    protected ObjectCodec _objectCodec;
+
+    /**
+     * Currently enabled parser features.
+     */
+    protected int _parserFeatures = DEFAULT_PARSER_FEATURE_FLAGS;
+
+    /**
+     * Currently enabled generator features.
+     */
+    protected int _generatorFeatures = DEFAULT_GENERATOR_FEATURE_FLAGS;
+
+    /**
+     * Definition of custom character escapes to use for generators created
+     * by this factory, if any. If null, standard data format specific
+     * escapes are used.
+     */
+    protected CharacterEscapes _characterEscapes;
+
+    /**
+     * Optional helper object that may decorate input sources, to do
+     * additional processing on input during parsing.
+     */
+    protected InputDecorator _inputDecorator;
+
+    /**
+     * Optional helper object that may decorate output object, to do
+     * additional processing on output during content generation.
+     */
+    protected OutputDecorator _outputDecorator;
+    
+    /*
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+    
+    /**
+     * Default constructor used to create factory instances.
+     * Creation of a factory instance is a light-weight operation,
+     * but it is still a good idea to reuse limited number of
+     * factory instances (and quite often just a single instance):
+     * factories are used as context for storing some reused
+     * processing objects (such as symbol tables parsers use)
+     * and this reuse only works within context of a single
+     * factory instance.
+     */
+    public JsonFactory() { this(null); }
+
+    public JsonFactory(ObjectCodec oc) { _objectCodec = oc; }
+
+    /*
+    /**********************************************************
+    /* Format detection functionality (since 1.8)
+    /**********************************************************
+     */
+
+    /**
+     * Method that returns short textual id identifying format
+     * this factory supports.
+     *<p>
+     * Note: sub-classes should override this method; default
+     * implementation will return null for all sub-classes
+     */
+    public String getFormatName()
+    {
+        /* Somewhat nasty check: since we can't make this abstract
+         * (due to backwards compatibility concerns), need to prevent
+         * format name "leakage"
+         */
+        if (getClass() == JsonFactory.class) {
+            return FORMAT_NAME_JSON;
+        }
+        return null;
+    }
+
+    public MatchStrength hasFormat(InputAccessor acc) throws IOException
+    {
+        // since we can't keep this abstract, only implement for "vanilla" instance
+        if (getClass() == JsonFactory.class) {
+            return hasJSONFormat(acc);
+        }
+        return null;
+    }
+
+    protected MatchStrength hasJSONFormat(InputAccessor acc) throws IOException
+    {
+        return ByteSourceJsonBootstrapper.hasJSONFormat(acc);
+    }    
+    
+    /*
+    /**********************************************************
+    /* Versioned
+    /**********************************************************
+     */
+
+    @Override
+    public Version version() {
+        // VERSION is included under impl, so can't pass this class:
+        return VersionUtil.versionFor(UTF8JsonGenerator.class);
+    }
+
+    /*
+    /**********************************************************
+    /* Configuration, parser settings
+    /**********************************************************
+     */
+
+    /**
+     * Method for enabling or disabling specified parser feature
+     * (check {@link JsonParser.Feature} for list of features)
+     */
+    public final JsonFactory configure(JsonParser.Feature f, boolean state)
+    {
+        if (state) {
+            enable(f);
+        } else {
+            disable(f);
+        }
+        return this;
+    }
+
+    /**
+     * Method for enabling specified parser feature
+     * (check {@link JsonParser.Feature} for list of features)
+     */
+    public JsonFactory enable(JsonParser.Feature f) {
+        _parserFeatures |= f.getMask();
+        return this;
+    }
+
+    /**
+     * Method for disabling specified parser features
+     * (check {@link JsonParser.Feature} for list of features)
+     */
+    public JsonFactory disable(JsonParser.Feature f) {
+        _parserFeatures &= ~f.getMask();
+        return this;
+    }
+
+    /**
+     * Checked whether specified parser feature is enabled.
+     */
+    public final boolean isEnabled(JsonParser.Feature f) {
+        return (_parserFeatures & f.getMask()) != 0;
+    }
+
+    /**
+     * Method for getting currently configured input decorator (if any;
+     * there is no default decorator).
+     */
+    public InputDecorator getInputDecorator() {
+        return _inputDecorator;
+    }
+
+    /**
+     * Method for overriding currently configured input decorator
+     */
+    public JsonFactory setInputDecorator(InputDecorator d) {
+        _inputDecorator = d;
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration, generator settings
+    /**********************************************************
+     */
+
+    /**
+     * Method for enabling or disabling specified generator feature
+     * (check {@link JsonGenerator.Feature} for list of features)
+     */
+    public final JsonFactory configure(JsonGenerator.Feature f, boolean state) {
+        if (state) {
+            enable(f);
+        } else {
+            disable(f);
+        }
+        return this;
+    }
+
+
+    /**
+     * Method for enabling specified generator features
+     * (check {@link JsonGenerator.Feature} for list of features)
+     */
+    public JsonFactory enable(JsonGenerator.Feature f) {
+        _generatorFeatures |= f.getMask();
+        return this;
+    }
+
+    /**
+     * Method for disabling specified generator feature
+     * (check {@link JsonGenerator.Feature} for list of features)
+     */
+    public JsonFactory disable(JsonGenerator.Feature f) {
+        _generatorFeatures &= ~f.getMask();
+        return this;
+    }
+
+    /**
+     * Check whether specified generator feature is enabled.
+     */
+    public final boolean isEnabled(JsonGenerator.Feature f) {
+        return (_generatorFeatures & f.getMask()) != 0;
+    }
+
+    /**
+     * Method for accessing custom escapes factory uses for {@link JsonGenerator}s
+     * it creates.
+     */
+    public CharacterEscapes getCharacterEscapes() {
+        return _characterEscapes;
+    }
+
+    /**
+     * Method for defining custom escapes factory uses for {@link JsonGenerator}s
+     * it creates.
+     */
+    public JsonFactory setCharacterEscapes(CharacterEscapes esc) {
+        _characterEscapes = esc;
+        return this;
+    }
+
+    /**
+     * Method for getting currently configured output decorator (if any;
+     * there is no default decorator).
+     */
+    public OutputDecorator getOutputDecorator() {
+        return _outputDecorator;
+    }
+
+    /**
+     * Method for overriding currently configured output decorator
+     */
+    public JsonFactory setOutputDecorator(OutputDecorator d) {
+        _outputDecorator = d;
+        return this;
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration, other
+    /**********************************************************
+     */
+
+    /**
+     * Method for associating a {@link ObjectCodec} (typically
+     * a {@link org.codehaus.jackson.map.ObjectMapper}) with
+     * this factory (and more importantly, parsers and generators
+     * it constructs). This is needed to use data-binding methods
+     * of {@link JsonParser} and {@link JsonGenerator} instances.
+     */
+    public JsonFactory setCodec(ObjectCodec oc) {
+        _objectCodec = oc;
+        return this;
+    }
+
+    public ObjectCodec getCodec() { return _objectCodec; }
+
+    /*
+    /**********************************************************
+    /* Reader factories
+    /**********************************************************
+     */
+
+    /**
+     * Method for constructing JSON parser instance to parse
+     * contents of specified file. Encoding is auto-detected
+     * from contents according to JSON specification recommended
+     * mechanism.
+     *<p>
+     * Underlying input stream (needed for reading contents)
+     * will be <b>owned</b> (and managed, i.e. closed as need be) by
+     * the parser, since caller has no access to it.
+     *
+     * @param f File that contains JSON content to parse
+     */
+    public JsonParser createJsonParser(File f)
+        throws IOException, JsonParseException
+    {
+        // true, since we create InputStream from File
+        IOContext ctxt = _createContext(f, true);
+        InputStream in = new FileInputStream(f);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            in = _inputDecorator.decorate(ctxt, in);
+        }
+        return _createJsonParser(in, ctxt);
+    }
+
+    /**
+     * Method for constructing JSON parser instance to parse
+     * contents of resource reference by given URL.
+     * Encoding is auto-detected
+     * from contents according to JSON specification recommended
+     * mechanism.
+     *<p>
+     * Underlying input stream (needed for reading contents)
+     * will be <b>owned</b> (and managed, i.e. closed as need be) by
+     * the parser, since caller has no access to it.
+     *
+     * @param url URL pointing to resource that contains JSON content to parse
+     */
+    public JsonParser createJsonParser(URL url)
+        throws IOException, JsonParseException
+    {
+        // true, since we create InputStream from URL
+        IOContext ctxt = _createContext(url, true);
+        InputStream in = _optimizedStreamFromURL(url);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            in = _inputDecorator.decorate(ctxt, in);
+        }
+        return _createJsonParser(in, ctxt);
+    }
+
+    /**
+     * Method for constructing JSON parser instance to parse
+     * the contents accessed via specified input stream.
+     *<p>
+     * The input stream will <b>not be owned</b> by
+     * the parser, it will still be managed (i.e. closed if
+     * end-of-stream is reacher, or parser close method called)
+     * if (and only if) {@link com.fasterxml.jackson.core.JsonParser.Feature#AUTO_CLOSE_SOURCE}
+     * is enabled.
+     *<p>
+     * Note: no encoding argument is taken since it can always be
+     * auto-detected as suggested by Json RFC.
+     *
+     * @param in InputStream to use for reading JSON content to parse
+     */
+    public JsonParser createJsonParser(InputStream in)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(in, false);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            in = _inputDecorator.decorate(ctxt, in);
+        }
+        return _createJsonParser(in, ctxt);
+    }
+
+    /**
+     * Method for constructing parser for parsing
+     * the contents accessed via specified Reader.
+     <p>
+     * The read stream will <b>not be owned</b> by
+     * the parser, it will still be managed (i.e. closed if
+     * end-of-stream is reacher, or parser close method called)
+     * if (and only if) {@link com.fasterxml.jackson.core.JsonParser.Feature#AUTO_CLOSE_SOURCE}
+     * is enabled.
+     *<p>
+     *
+     * @param r Reader to use for reading JSON content to parse
+     */
+    public JsonParser createJsonParser(Reader r)
+        throws IOException, JsonParseException
+    {
+        // false -> we do NOT own Reader (did not create it)
+        IOContext ctxt = _createContext(r, false);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            r = _inputDecorator.decorate(ctxt, r);
+        }
+	return _createJsonParser(r, ctxt);
+    }
+
+    /**
+     * Method for constructing parser for parsing
+     * the contents of given byte array.
+     */
+    public JsonParser createJsonParser(byte[] data)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(data, true);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            InputStream in = _inputDecorator.decorate(ctxt, data, 0, data.length);
+            if (in != null) {
+                return _createJsonParser(in, ctxt);
+            }
+        }
+        return _createJsonParser(data, 0, data.length, ctxt);
+    }
+
+    /**
+     * Method for constructing parser for parsing
+     * the contents of given byte array.
+     * 
+     * @param data Buffer that contains data to parse
+     * @param offset Offset of the first data byte within buffer
+     * @param len Length of contents to parse within buffer
+     */
+    public JsonParser createJsonParser(byte[] data, int offset, int len)
+        throws IOException, JsonParseException
+    {
+        IOContext ctxt = _createContext(data, true);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            InputStream in = _inputDecorator.decorate(ctxt, data, offset, len);
+            if (in != null) {
+                return _createJsonParser(in, ctxt);
+            }
+        }
+	return _createJsonParser(data, offset, len, ctxt);
+    }
+
+    /**
+     * Method for constructing parser for parsing
+     * contents of given String.
+     */
+    public JsonParser createJsonParser(String content)
+        throws IOException, JsonParseException
+    {
+	Reader r = new StringReader(content);
+        // true -> we own the Reader (and must close); not a big deal
+        IOContext ctxt = _createContext(r, true);
+        // [JACKSON-512]: allow wrapping with InputDecorator
+        if (_inputDecorator != null) {
+            r = _inputDecorator.decorate(ctxt, r);
+        }
+	return _createJsonParser(r, ctxt);
+    }
+
+    /*
+    /**********************************************************
+    /* Generator factories
+    /**********************************************************
+     */
+
+    /**
+     * Method for constructing JSON generator for writing JSON content
+     * using specified output stream.
+     * Encoding to use must be specified, and needs to be one of available
+     * types (as per JSON specification).
+     *<p>
+     * Underlying stream <b>is NOT owned</b> by the generator constructed,
+     * so that generator will NOT close the output stream when
+     * {@link JsonGenerator#close} is called (unless auto-closing
+     * feature,
+     * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET}
+     * is enabled).
+     * Using application needs to close it explicitly if this is the case.
+     *<p>
+     * Note: there are formats that use fixed encoding (like most binary data formats)
+     * and that ignore passed in encoding.
+     *
+     * @param out OutputStream to use for writing JSON content 
+     * @param enc Character encoding to use
+     */
+    public JsonGenerator createJsonGenerator(OutputStream out, JsonEncoding enc)
+        throws IOException
+    {
+	// false -> we won't manage the stream unless explicitly directed to
+        IOContext ctxt = _createContext(out, false);
+        ctxt.setEncoding(enc);
+        if (enc == JsonEncoding.UTF8) {
+            // [JACKSON-512]: allow wrapping with _outputDecorator
+            if (_outputDecorator != null) {
+                out = _outputDecorator.decorate(ctxt, out);
+            }
+            return _createUTF8JsonGenerator(out, ctxt);
+        }
+        Writer w = _createWriter(out, enc, ctxt);
+        // [JACKSON-512]: allow wrapping with _outputDecorator
+        if (_outputDecorator != null) {
+            w = _outputDecorator.decorate(ctxt, w);
+        }
+	return _createJsonGenerator(w, ctxt);
+    }
+
+    /**
+     * Method for constructing JSON generator for writing JSON content
+     * using specified Writer.
+     *<p>
+     * Underlying stream <b>is NOT owned</b> by the generator constructed,
+     * so that generator will NOT close the Reader when
+     * {@link JsonGenerator#close} is called (unless auto-closing
+     * feature,
+     * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#AUTO_CLOSE_TARGET} is enabled).
+     * Using application needs to close it explicitly.
+     *
+     * @param out Writer to use for writing JSON content 
+     */
+    public JsonGenerator createJsonGenerator(Writer out)
+        throws IOException
+    {
+        IOContext ctxt = _createContext(out, false);
+        // [JACKSON-512]: allow wrapping with _outputDecorator
+        if (_outputDecorator != null) {
+            out = _outputDecorator.decorate(ctxt, out);
+        }
+	return _createJsonGenerator(out, ctxt);
+    }
+
+    /**
+     * Convenience method for constructing generator that uses default
+     * encoding of the format (UTF-8 for JSON and most other data formats).
+     *<p>
+     * Note: there are formats that use fixed encoding (like most binary data formats).
+     */
+    public JsonGenerator createJsonGenerator(OutputStream out) throws IOException {
+        return createJsonGenerator(out, JsonEncoding.UTF8);
+    }
+    
+    /**
+     * Method for constructing JSON generator for writing JSON content
+     * to specified file, overwriting contents it might have (or creating
+     * it if such file does not yet exist).
+     * Encoding to use must be specified, and needs to be one of available
+     * types (as per JSON specification).
+     *<p>
+     * Underlying stream <b>is owned</b> by the generator constructed,
+     * i.e. generator will handle closing of file when
+     * {@link JsonGenerator#close} is called.
+     *
+     * @param f File to write contents to
+     * @param enc Character encoding to use
+     */
+    public JsonGenerator createJsonGenerator(File f, JsonEncoding enc)
+        throws IOException
+    {
+	OutputStream out = new FileOutputStream(f);
+	// true -> yes, we have to manage the stream since we created it
+        IOContext ctxt = _createContext(out, true);
+        ctxt.setEncoding(enc);
+        if (enc == JsonEncoding.UTF8) {
+            // [JACKSON-512]: allow wrapping with _outputDecorator
+            if (_outputDecorator != null) {
+                out = _outputDecorator.decorate(ctxt, out);
+            }
+            return _createUTF8JsonGenerator(out, ctxt);
+        }
+        Writer w = _createWriter(out, enc, ctxt);
+        // [JACKSON-512]: allow wrapping with _outputDecorator
+        if (_outputDecorator != null) {
+            w = _outputDecorator.decorate(ctxt, w);
+        }
+	return _createJsonGenerator(w, ctxt);
+    }
+
+    /*
+    /**********************************************************
+    /* Factory methods used by factory for creating parser instances,
+    /* overridable by sub-classes
+    /**********************************************************
+     */
+
+    /**
+     * Overridable factory method that actually instantiates desired parser
+     * given {@link InputStream} and context object.
+     *<p>
+     * This method is specifically designed to remain
+     * compatible between minor versions so that sub-classes can count
+     * on it being called as expected. That is, it is part of official
+     * interface from sub-class perspective, although not a public
+     * method available to users of factory implementations.
+     */
+    protected JsonParser _createJsonParser(InputStream in, IOContext ctxt)
+        throws IOException, JsonParseException
+    {
+        return new ByteSourceJsonBootstrapper(ctxt, in).constructParser(_parserFeatures,
+                _objectCodec, _rootByteSymbols, _rootCharSymbols);
+    }
+
+    /**
+     * Overridable factory method that actually instantiates parser
+     * using given {@link Reader} object for reading content.
+     *<p>
+     * This method is specifically designed to remain
+     * compatible between minor versions so that sub-classes can count
+     * on it being called as expected. That is, it is part of official
+     * interface from sub-class perspective, although not a public
+     * method available to users of factory implementations.
+     */
+    protected JsonParser _createJsonParser(Reader r, IOContext ctxt)
+	throws IOException, JsonParseException
+    {
+        return new ReaderBasedJsonParser(ctxt, _parserFeatures, r, _objectCodec,
+                _rootCharSymbols.makeChild(isEnabled(JsonParser.Feature.CANONICALIZE_FIELD_NAMES),
+                    isEnabled(JsonParser.Feature.INTERN_FIELD_NAMES)));
+    }
+
+    /**
+     * Overridable factory method that actually instantiates parser
+     * using given {@link Reader} object for reading content
+     * passed as raw byte array.
+     *<p>
+     * This method is specifically designed to remain
+     * compatible between minor versions so that sub-classes can count
+     * on it being called as expected. That is, it is part of official
+     * interface from sub-class perspective, although not a public
+     * method available to users of factory implementations.
+     */
+    protected JsonParser _createJsonParser(byte[] data, int offset, int len, IOContext ctxt)
+        throws IOException, JsonParseException
+    {
+        return new ByteSourceJsonBootstrapper(ctxt, data, offset, len).constructParser(_parserFeatures,
+                _objectCodec, _rootByteSymbols, _rootCharSymbols);
+    }
+
+    /*
+    /**********************************************************
+    /* Factory methods used by factory for creating generator instances,
+    /* overridable by sub-classes
+    /**********************************************************
+     */
+    
+    /**
+     * Overridable factory method that actually instantiates generator for
+     * given {@link Writer} and context object.
+     *<p>
+     * This method is specifically designed to remain
+     * compatible between minor versions so that sub-classes can count
+     * on it being called as expected. That is, it is part of official
+     * interface from sub-class perspective, although not a public
+     * method available to users of factory implementations.
+     */
+    protected JsonGenerator _createJsonGenerator(Writer out, IOContext ctxt)
+        throws IOException
+    {
+        WriterBasedJsonGenerator gen = new WriterBasedJsonGenerator(ctxt, _generatorFeatures, _objectCodec, out);
+        if (_characterEscapes != null) {
+            gen.setCharacterEscapes(_characterEscapes);
+        }
+        return gen;
+    }
+
+    /**
+     * Overridable factory method that actually instantiates generator for
+     * given {@link OutputStream} and context object, using UTF-8 encoding.
+     *<p>
+     * This method is specifically designed to remain
+     * compatible between minor versions so that sub-classes can count
+     * on it being called as expected. That is, it is part of official
+     * interface from sub-class perspective, although not a public
+     * method available to users of factory implementations.
+     */
+    protected JsonGenerator _createUTF8JsonGenerator(OutputStream out, IOContext ctxt)
+        throws IOException
+    {
+        UTF8JsonGenerator gen = new UTF8JsonGenerator(ctxt, _generatorFeatures, _objectCodec, out);
+        if (_characterEscapes != null) {
+            gen.setCharacterEscapes(_characterEscapes);
+        }
+        return gen;
+    }
+
+    protected Writer _createWriter(OutputStream out, JsonEncoding enc, IOContext ctxt) throws IOException
+    {
+        // note: this should not get called any more (caller checks, dispatches)
+        if (enc == JsonEncoding.UTF8) { // We have optimized writer for UTF-8
+            return new UTF8Writer(ctxt, out);
+        }
+        // not optimal, but should do unless we really care about UTF-16/32 encoding speed
+        return new OutputStreamWriter(out, enc.getJavaName());
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal factory methods, other
+    /**********************************************************
+     */
+
+    /**
+     * Overridable factory method that actually instantiates desired
+     * context object.
+     */
+    protected IOContext _createContext(Object srcRef, boolean resourceManaged)
+    {
+        return new IOContext(_getBufferRecycler(), srcRef, resourceManaged);
+    }
+
+    /**
+     * Method used by factory to create buffer recycler instances
+     * for parsers and generators.
+     *<p>
+     * Note: only public to give access for <code>ObjectMapper</code>
+     */
+    public BufferRecycler _getBufferRecycler()
+    {
+        SoftReference<BufferRecycler> ref = _recyclerRef.get();
+        BufferRecycler br = (ref == null) ? null : ref.get();
+
+        if (br == null) {
+            br = new BufferRecycler();
+            _recyclerRef.set(new SoftReference<BufferRecycler>(br));
+        }
+        return br;
+    }
+    
+    /**
+     * Helper methods used for constructing an optimal stream for
+     * parsers to use, when input is to be read from an URL.
+     * This helps when reading file content via URL.
+     */
+    protected InputStream _optimizedStreamFromURL(URL url)
+        throws IOException
+    {
+        if ("file".equals(url.getProtocol())) {
+            /* Can not do this if the path refers
+             * to a network drive on windows. This fixes the problem;
+             * might not be needed on all platforms (NFS?), but should not
+             * matter a lot: performance penalty of extra wrapping is more
+             * relevant when accessing local file system.
+             */
+            String host = url.getHost();
+            if (host == null || host.length() == 0) {
+                return new FileInputStream(url.getPath());
+            }
+        }
+        return url.openStream();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonGenerationException.java b/src/main/java/com/fasterxml/jackson/core/JsonGenerationException.java
new file mode 100644
index 0000000..7338d92
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonGenerationException.java
@@ -0,0 +1,27 @@
+package com.fasterxml.jackson.core;
+
+/**
+ * Exception type for exceptions during JSON writing, such as trying
+ * to output  content in wrong context (non-matching end-array or end-object,
+ * for example).
+ */
+public class JsonGenerationException
+    extends JsonProcessingException
+{
+    private final static long serialVersionUID = 123; // Stupid eclipse...
+    
+    public JsonGenerationException(Throwable rootCause)
+    {
+        super(rootCause);
+    }
+
+    public JsonGenerationException(String msg)
+    {
+        super(msg, (JsonLocation)null);
+    }
+
+    public JsonGenerationException(String msg, Throwable rootCause)
+    {
+        super(msg, (JsonLocation)null, rootCause);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
new file mode 100644
index 0000000..f32c244
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
@@ -0,0 +1,1175 @@
+/* Jackson JSON-processor.
+ *
+ * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
+ *
+ * Licensed under the License specified in file LICENSE, included with
+ * the source code and binary code bundles.
+ * You may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.fasterxml.jackson.core;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.io.CharacterEscapes;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+/**
+ * Base class that defines public API for writing JSON content.
+ * Instances are created using factory methods of
+ * a {@link JsonFactory} instance.
+ *
+ * @author Tatu Saloranta
+ */
+public abstract class JsonGenerator
+    implements Closeable, Versioned
+{
+    /**
+     * Enumeration that defines all togglable features for generators.
+     */
+    public enum Feature {
+        /**
+         * Feature that determines whether generator will automatically
+         * close underlying output target that is NOT owned by the
+         * generator.
+         * If disabled, calling application has to separately
+         * close the underlying {@link OutputStream} and {@link Writer}
+         * instances used to create the generator. If enabled, generator
+         * will handle closing, as long as generator itself gets closed:
+         * this happens when end-of-input is encountered, or generator
+         * is closed by a call to {@link JsonGenerator#close}.
+         *<p>
+         * Feature is enabled by default.
+         */
+        AUTO_CLOSE_TARGET(true),
+
+        /**
+         * Feature that determines what happens when the generator is
+         * closed while there are still unmatched
+         * {@link JsonToken#START_ARRAY} or {@link JsonToken#START_OBJECT}
+         * entries in output content. If enabled, such Array(s) and/or
+         * Object(s) are automatically closed; if disabled, nothing
+         * specific is done.
+         *<p>
+         * Feature is enabled by default.
+         */
+        AUTO_CLOSE_JSON_CONTENT(true),
+
+        /**
+         * Feature that determines whether JSON Object field names are
+         * quoted using double-quotes, as specified by JSON specification
+         * or not. Ability to disable quoting was added to support use
+         * cases where they are not usually expected, which most commonly
+         * occurs when used straight from Javascript.
+         */
+        QUOTE_FIELD_NAMES(true),
+
+        /**
+         * Feature that determines whether "exceptional" (not real number)
+         * float/double values are output as quoted strings.
+         * The values checked are Double.Nan,
+         * Double.POSITIVE_INFINITY and Double.NEGATIVE_INIFINTY (and 
+         * associated Float values).
+         * If feature is disabled, these numbers are still output using
+         * associated literal values, resulting in non-conformant
+         * output.
+         *<p>
+         * Feature is enabled by default.
+         */
+        QUOTE_NON_NUMERIC_NUMBERS(true),
+
+        /**
+         * Feature that forces all Java numbers to be written as JSON strings.
+         * Default state is 'false', meaning that Java numbers are to
+         * be serialized using basic numeric serialization (as JSON
+         * numbers, integral or floating point). If enabled, all such
+         * numeric values are instead written out as JSON Strings.
+         *<p>
+         * One use case is to avoid problems with Javascript limitations:
+         * since Javascript standard specifies that all number handling
+         * should be done using 64-bit IEEE 754 floating point values,
+         * result being that some 64-bit integer values can not be
+         * accurately represent (as mantissa is only 51 bit wide).
+         *<p>
+         * Feature is disabled by default.
+         *
+         * @since 1.3
+         */
+        WRITE_NUMBERS_AS_STRINGS(false),
+
+        /**
+         * Feature that specifies that calls to {@link #flush} will cause
+         * matching <code>flush()</code> to underlying {@link OutputStream}
+         * or {@link Writer}; if disabled this will not be done.
+         * Main reason to disable this feature is to prevent flushing at
+         * generator level, if it is not possible to prevent method being
+         * called by other code (like <code>ObjectMapper</code> or third
+         * party libraries).
+         *<p>
+         * Feature is enabled by default.
+         * 
+         * @since 1.7
+         */
+        FLUSH_PASSED_TO_STREAM(true),
+        
+        /**
+         * Feature that specifies that all characters beyond 7-bit ASCII
+         * range (i.e. code points of 128 and above) need to be output
+         * using format-specific escapes (for JSON, backslash escapes),
+         * if format uses escaping mechanisms (which is generally true
+         * for textual formats but not for binary formats).
+         * 
+         * @since 1.8
+         */
+        ESCAPE_NON_ASCII(false)
+        
+            ;
+
+        final boolean _defaultState;
+
+        final int _mask;
+        
+        /**
+         * Method that calculates bit set (flags) of all features that
+         * are enabled by default.
+         */
+        public static int collectDefaults()
+        {
+            int flags = 0;
+            for (Feature f : values()) {
+                if (f.enabledByDefault()) {
+                    flags |= f.getMask();
+                }
+            }
+            return flags;
+        }
+        
+        private Feature(boolean defaultState) {
+            _defaultState = defaultState;
+            _mask = (1 << ordinal());
+        }
+        
+        public boolean enabledByDefault() { return _defaultState; }
+    
+        public int getMask() { return _mask; }
+    }
+
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    /**
+     * Object that handles pretty-printing (usually additional
+     * white space to make results more human-readable) during
+     * output. If null, no pretty-printing is done.
+     */
+    protected PrettyPrinter _cfgPrettyPrinter;
+
+    /*
+    /**********************************************************
+    /* Construction, configuration, initialization
+    /**********************************************************
+     */
+    
+    protected JsonGenerator() { }
+
+    /**
+     * Method to call to make this generator use specified schema.
+     * Method must be called before generating any content, right after instance
+     * has been created.
+     * Note that not all generators support schemas; and those that do usually only
+     * accept specific types of schemas: ones defined for data format this generator
+     * produces.
+     *<p>
+     * If generator does not support specified schema, {@link UnsupportedOperationException}
+     * is thrown.
+     * 
+     * @param schema Schema to use
+     * 
+     * @throws UnsupportedOperationException if generator does not support schema
+     * 
+     * @since 1.8
+     */
+    public void setSchema(FormatSchema schema)
+    {
+        throw new UnsupportedOperationException("Generator of type "+getClass().getName()+" does not support schema of type '"
+                +schema.getSchemaType()+"'");
+    }
+    
+    /**
+     * Method that can be used to verify that given schema can be used with
+     * this generator (using {@link #setSchema}).
+     * 
+     * @param schema Schema to check
+     * 
+     * @return True if this generator can use given schema; false if not
+     * 
+     * @since 1.8
+     */
+    public boolean canUseSchema(FormatSchema schema) {
+        return false;
+    }
+    
+    /**
+     * @since 1.6
+     */
+    @Override
+    public Version version() {
+        return Version.unknownVersion();
+    }
+
+    /**
+     * Method that can be used to get access to object that is used
+     * as target for generated output; this is usually either
+     * {@link OutputStream} or {@link Writer}, depending on what
+     * generator was constructed with.
+     * Note that returned value may be null in some cases; including
+     * case where implementation does not want to exposed raw
+     * source to caller.
+     * In cases where output has been decorated, object returned here
+     * is the decorated version; this allows some level of interaction
+     * between users of generator and decorator object.
+     *<p>
+     * In general use of this accessor should be considered as
+     * "last effort", i.e. only used if no other mechanism is applicable.
+     * 
+     * @since 1.8
+     */
+    public Object getOutputTarget() {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, configuration
+    /**********************************************************
+     */
+
+    /**
+     * Method for enabling specified parser features:
+     * check {@link Feature} for list of available features.
+     *
+     * @return Generator itself (this), to allow chaining
+     *
+     * @since 1.2
+     */
+    public abstract JsonGenerator enable(Feature f);
+
+    /**
+     * Method for disabling specified  features
+     * (check {@link Feature} for list of features)
+     *
+     * @return Generator itself (this), to allow chaining
+     *
+     * @since 1.2
+     */
+    public abstract JsonGenerator disable(Feature f);
+
+    /**
+     * Method for enabling or disabling specified feature:
+     * check {@link Feature} for list of available features.
+     *
+     * @return Generator itself (this), to allow chaining
+     *
+     * @since 1.2
+     */
+    public JsonGenerator configure(Feature f, boolean state)
+    {
+        if (state) {
+            enable(f);
+        } else {
+            disable(f);
+        }
+        return this;
+    }
+
+    /**
+     * Method for checking whether given feature is enabled.
+     * Check {@link Feature} for list of available features.
+     *
+     * @since 1.2
+     */
+    public abstract boolean isEnabled(Feature f);
+
+    /**
+     * Method that can be called to set or reset the object to
+     * use for writing Java objects as JsonContent
+     * (using method {@link #writeObject}).
+     *
+     * @return Generator itself (this), to allow chaining
+     */
+    public abstract JsonGenerator setCodec(ObjectCodec oc);
+
+    /**
+     * Method for accessing the object used for writing Java
+     * object as Json content
+     * (using method {@link #writeObject}).
+     */
+    public abstract ObjectCodec getCodec();
+
+    /*
+    /**********************************************************
+    /* Configuring generator
+    /**********************************************************
+      */
+
+    /**
+     * Method for setting a custom pretty printer, which is usually
+     * used to add indentation for improved human readability.
+     * By default, generator does not do pretty printing.
+     *<p>
+     * To use the default pretty printer that comes with core
+     * Jackson distribution, call {@link #useDefaultPrettyPrinter}
+     * instead.
+     *
+     * @return Generator itself (this), to allow chaining
+     */
+    public JsonGenerator setPrettyPrinter(PrettyPrinter pp) {
+        _cfgPrettyPrinter = pp;
+        return this;
+    }
+
+    /**
+     * Convenience method for enabling pretty-printing using
+     * the default pretty printer
+     * ({@link com.fasterxml.jackson.core.util.DefaultPrettyPrinter}).
+     *
+     * @return Generator itself (this), to allow chaining
+     */
+    public abstract JsonGenerator useDefaultPrettyPrinter();
+
+    /**
+     * Method that can be called to request that generator escapes
+     * all character codes above specified code point (if positive value);
+     * or, to not escape any characters except for ones that must be
+     * escaped for the data format (if -1).
+     * To force escaping of all non-ASCII characters, for example,
+     * this method would be called with value of 127.
+     *<p>
+     * Note that generators are NOT required to support setting of value
+     * higher than 127, because there are other ways to affect quoting
+     * (or lack thereof) of character codes between 0 and 127.
+     * Not all generators support concept of escaping, either; if so,
+     * calling this method will have no effect.
+     *<p>
+     * Default implementation does nothing; sub-classes need to redefine
+     * it according to rules of supported data format.
+     * 
+     * @param charCode Either -1 to indicate that no additional escaping
+     *   is to be done; or highest code point not to escape (meaning higher
+     *   ones will be), if positive value.
+     * 
+     * @since 1.8
+     */
+    public JsonGenerator setHighestNonEscapedChar(int charCode) {
+        return this;
+    }
+
+    /**
+     * Accessor method for testing what is the highest unescaped character
+     * configured for this generator. This may be either positive value
+     * (when escaping configuration has been set and is in effect), or
+     * 0 to indicate that no additional escaping is in effect.
+     * Some generators may not support additional escaping: for example,
+     * generators for binary formats that do not use escaping should
+     * simply return 0.
+     * 
+     * @return Currently active limitation for highest non-escaped character,
+     *   if defined; or -1 to indicate no additional escaping is performed.
+     */
+    public int getHighestEscapedChar() {
+        return 0;
+    }
+    /**
+     * Method for accessing custom escapes factory uses for {@link JsonGenerator}s
+     * it creates.
+     * 
+     * @since 1.8
+     */
+    public CharacterEscapes getCharacterEscapes() {
+        return null;
+    }
+
+    /**
+     * Method for defining custom escapes factory uses for {@link JsonGenerator}s
+     * it creates.
+     * 
+     * @since 1.8
+     */
+    public JsonGenerator setCharacterEscapes(CharacterEscapes esc) {
+        return this;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, write methods, structural
+    /**********************************************************
+     */
+
+    /**
+     * Method for writing starting marker of a JSON Array value
+     * (character '['; plus possible white space decoration
+     * if pretty-printing is enabled).
+     *<p>
+     * Array values can be written in any context where values
+     * are allowed: meaning everywhere except for when
+     * a field name is expected.
+     */
+    public abstract void writeStartArray()
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for writing closing marker of a JSON Array value
+     * (character ']'; plus possible white space decoration
+     * if pretty-printing is enabled).
+     *<p>
+     * Marker can be written if the innermost structured type
+     * is Array.
+     */
+    public abstract void writeEndArray()
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for writing starting marker of a JSON Object value
+     * (character '{'; plus possible white space decoration
+     * if pretty-printing is enabled).
+     *<p>
+     * Object values can be written in any context where values
+     * are allowed: meaning everywhere except for when
+     * a field name is expected.
+     */
+    public abstract void writeStartObject()
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for writing closing marker of a JSON Object value
+     * (character '}'; plus possible white space decoration
+     * if pretty-printing is enabled).
+     *<p>
+     * Marker can be written if the innermost structured type
+     * is Object, and the last written event was either a
+     * complete value, or START-OBJECT marker (see JSON specification
+     * for more details).
+     */
+    public abstract void writeEndObject()
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for writing a field name (JSON String surrounded by
+     * double quotes: syntactically identical to a JSON String value),
+     * possibly decorated by white space if pretty-printing is enabled.
+     *<p>
+     * Field names can only be written in Object context (check out
+     * JSON specification for details), when field name is expected
+     * (field names alternate with values).
+     */
+    public abstract void writeFieldName(String name)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method similar to {@link #writeFieldName(String)}, main difference
+     * being that it may perform better as some of processing (such as
+     * quoting of certain characters, or encoding into external encoding
+     * if supported by generator) can be done just once and reused for
+     * later calls.
+     *<p>
+     * Default implementation simple uses unprocessed name container in
+     * serialized String; implementations are strongly encouraged to make
+     * use of more efficient methods argument object has.
+     * 
+     * @since 1.6
+     */
+    public void writeFieldName(SerializedString name)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(name.getValue());
+    }
+
+    /**
+     * Method similar to {@link #writeFieldName(String)}, main difference
+     * being that it may perform better as some of processing (such as
+     * quoting of certain characters, or encoding into external encoding
+     * if supported by generator) can be done just once and reused for
+     * later calls.
+     *<p>
+     * Default implementation simple uses unprocessed name container in
+     * serialized String; implementations are strongly encouraged to make
+     * use of more efficient methods argument object has.
+     * 
+     * @since 1.7
+     */
+    public void writeFieldName(SerializableString name)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(name.getValue());
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, write methods, text/String values
+    /**********************************************************
+     */
+
+    /**
+     * Method for outputting a String value. Depending on context
+     * this means either array element, (object) field value or
+     * a stand alone String; but in all cases, String will be
+     * surrounded in double quotes, and contents will be properly
+     * escaped as required by JSON specification.
+     */
+    public abstract void writeString(String text)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for outputting a String value. Depending on context
+     * this means either array element, (object) field value or
+     * a stand alone String; but in all cases, String will be
+     * surrounded in double quotes, and contents will be properly
+     * escaped as required by JSON specification.
+     */
+    public abstract void writeString(char[] text, int offset, int len)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method similar to {@link #writeString(String)}, but that takes
+     * {@link SerializableString} which can make this potentially
+     * more efficient to call as generator may be able to reuse
+     * quoted and/or encoded representation.
+     *<p>
+     * Default implementation just calls {@link #writeString(String)};
+     * sub-classes should override it with more efficient implementation
+     * if possible.
+     * 
+     * @since 1.7
+     */
+    public void writeString(SerializableString text)
+        throws IOException, JsonGenerationException
+    {
+        writeString(text.getValue());
+    }
+
+    /**
+     * Method similar to {@link #writeString(String)} but that takes as
+     * its input a UTF-8 encoded String that is to be output as-is, without additional
+     * escaping (type of which depends on data format; backslashes for JSON).
+     * However, quoting that data format requires (like double-quotes for JSON) will be added
+     * around the value if and as necessary.
+     *<p>
+     * Note that some backends may choose not to support this method: for
+     * example, if underlying destination is a {@link java.io.Writer}
+     * using this method would require UTF-8 decoding.
+     * If so, implementation may instead choose to throw a
+     * {@link UnsupportedOperationException} due to ineffectiveness
+     * of having to decode input.
+     * 
+     * @since 1.7
+     */
+    public abstract void writeRawUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method similar to {@link #writeString(String)} but that takes as its input
+     * a UTF-8 encoded String which has <b>not</b> been escaped using whatever
+     * escaping scheme data format requires (for JSON that is backslash-escaping
+     * for control characters and double-quotes; for other formats something else).
+     * This means that textual JSON backends need to check if value needs
+     * JSON escaping, but otherwise can just be copied as is to output.
+     * Also, quoting that data format requires (like double-quotes for JSON) will be added
+     * around the value if and as necessary.
+     *<p>
+     * Note that some backends may choose not to support this method: for
+     * example, if underlying destination is a {@link java.io.Writer}
+     * using this method would require UTF-8 decoding.
+     * In this case
+     * generator implementation may instead choose to throw a
+     * {@link UnsupportedOperationException} due to ineffectiveness
+     * of having to decode input.
+     * 
+     * @since 1.7
+     */
+    public abstract void writeUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException;
+    
+    /*
+    /**********************************************************
+    /* Public API, write methods, binary/raw content
+    /**********************************************************
+     */
+    
+    /**
+     * Method that will force generator to copy
+     * input text verbatim with <b>no</b> modifications (including
+     * that no escaping is done and no separators are added even
+     * if context [array, object] would otherwise require such).
+     * If such separators are desired, use
+     * {@link #writeRawValue(String)} instead.
+     *<p>
+     * Note that not all generator implementations necessarily support
+     * such by-pass methods: those that do not will throw
+     * {@link UnsupportedOperationException}.
+     */
+    public abstract void writeRaw(String text)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method that will force generator to copy
+     * input text verbatim with <b>no</b> modifications (including
+     * that no escaping is done and no separators are added even
+     * if context [array, object] would otherwise require such).
+     * If such separators are desired, use
+     * {@link #writeRawValue(String)} instead.
+     *<p>
+     * Note that not all generator implementations necessarily support
+     * such by-pass methods: those that do not will throw
+     * {@link UnsupportedOperationException}.
+     */
+    public abstract void writeRaw(String text, int offset, int len)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method that will force generator to copy
+     * input text verbatim with <b>no</b> modifications (including
+     * that no escaping is done and no separators are added even
+     * if context [array, object] would otherwise require such).
+     * If such separators are desired, use
+     * {@link #writeRawValue(String)} instead.
+     *<p>
+     * Note that not all generator implementations necessarily support
+     * such by-pass methods: those that do not will throw
+     * {@link UnsupportedOperationException}.
+     */
+    public abstract void writeRaw(char[] text, int offset, int len)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method that will force generator to copy
+     * input text verbatim with <b>no</b> modifications (including
+     * that no escaping is done and no separators are added even
+     * if context [array, object] would otherwise require such).
+     * If such separators are desired, use
+     * {@link #writeRawValue(String)} instead.
+     *<p>
+     * Note that not all generator implementations necessarily support
+     * such by-pass methods: those that do not will throw
+     * {@link UnsupportedOperationException}.
+     */
+    public abstract void writeRaw(char c)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method that will force generator to copy
+     * input text verbatim without any modifications, but assuming
+     * it must constitute a single legal JSON value (number, string,
+     * boolean, null, Array or List). Assuming this, proper separators
+     * are added if and as needed (comma or colon), and generator
+     * state updated to reflect this.
+     */
+    public abstract void writeRawValue(String text)
+        throws IOException, JsonGenerationException;
+
+    public abstract void writeRawValue(String text, int offset, int len)
+        throws IOException, JsonGenerationException;
+
+    public abstract void writeRawValue(char[] text, int offset, int len)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method that will output given chunk of binary data as base64
+     * encoded, as a complete String value (surrounded by double quotes).
+     * This method defaults
+     *<p>
+     * Note: because Json Strings can not contain unescaped linefeeds,
+     * if linefeeds are included (as per last argument), they must be
+     * escaped. This adds overhead for decoding without improving
+     * readability.
+     * Alternatively if linefeeds are not included,
+     * resulting String value may violate the requirement of base64
+     * RFC which mandates line-length of 76 characters and use of
+     * linefeeds. However, all {@link JsonParser} implementations
+     * are required to accept such "long line base64"; as do
+     * typical production-level base64 decoders.
+     *
+     * @param b64variant Base64 variant to use: defines details such as
+     *   whether padding is used (and if so, using which character);
+     *   what is the maximum line length before adding linefeed,
+     *   and also the underlying alphabet to use.
+     */
+    public abstract void writeBinary(Base64Variant b64variant,
+                                     byte[] data, int offset, int len)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
+     * but default to using the Jackson default Base64 variant 
+     * (which is {@link Base64Variants#MIME_NO_LINEFEEDS}).
+     */
+    public void writeBinary(byte[] data, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        writeBinary(Base64Variants.getDefaultVariant(), data, offset, len);
+    }
+
+    /**
+     * Similar to {@link #writeBinary(Base64Variant,byte[],int,int)},
+     * but assumes default to using the Jackson default Base64 variant 
+     * (which is {@link Base64Variants#MIME_NO_LINEFEEDS}). Also
+     * assumes that whole byte array is to be output.
+     */
+    public void writeBinary(byte[] data)
+        throws IOException, JsonGenerationException
+    {
+        writeBinary(Base64Variants.getDefaultVariant(), data, 0, data.length);
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, write methods, other value types
+    /**********************************************************
+     */
+
+    /**
+     * Method for outputting given value as Json number.
+     * Can be called in any context where a value is expected
+     * (Array value, Object field value, root-level value).
+     * Additional white space may be added around the value
+     * if pretty-printing is enabled.
+     */
+    public abstract void writeNumber(int v)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for outputting given value as Json number.
+     * Can be called in any context where a value is expected
+     * (Array value, Object field value, root-level value).
+     * Additional white space may be added around the value
+     * if pretty-printing is enabled.
+     */
+    public abstract void writeNumber(long v)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for outputting given value as Json number.
+     * Can be called in any context where a value is expected
+     * (Array value, Object field value, root-level value).
+     * Additional white space may be added around the value
+     * if pretty-printing is enabled.
+     */
+    public abstract void writeNumber(BigInteger v)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for outputting indicate Json numeric value.
+     * Can be called in any context where a value is expected
+     * (Array value, Object field value, root-level value).
+     * Additional white space may be added around the value
+     * if pretty-printing is enabled.
+     */
+    public abstract void writeNumber(double d)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for outputting indicate Json numeric value.
+     * Can be called in any context where a value is expected
+     * (Array value, Object field value, root-level value).
+     * Additional white space may be added around the value
+     * if pretty-printing is enabled.
+     */
+    public abstract void writeNumber(float f)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for outputting indicate Json numeric value.
+     * Can be called in any context where a value is expected
+     * (Array value, Object field value, root-level value).
+     * Additional white space may be added around the value
+     * if pretty-printing is enabled.
+     */
+    public abstract void writeNumber(BigDecimal dec)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Write method that can be used for custom numeric types that can
+     * not be (easily?) converted to "standard" Java number types.
+     * Because numbers are not surrounded by double quotes, regular
+     * {@link #writeString} method can not be used; nor
+     * {@link #writeRaw} because that does not properly handle
+     * value separators needed in Array or Object contexts.
+     *<p>
+     * Note: because of lack of type safety, some generator
+     * implementations may not be able to implement this
+     * method. For example, if a binary json format is used,
+     * it may require type information for encoding; similarly
+     * for generator-wrappers around Java objects or Json nodes.
+     * If implementation does not implement this method,
+     * it needs to throw {@link UnsupportedOperationException}.
+     */
+    public abstract void writeNumber(String encodedValue)
+        throws IOException, JsonGenerationException,
+               UnsupportedOperationException;
+
+    /**
+     * Method for outputting literal Json boolean value (one of
+     * Strings 'true' and 'false').
+     * Can be called in any context where a value is expected
+     * (Array value, Object field value, root-level value).
+     * Additional white space may be added around the value
+     * if pretty-printing is enabled.
+     */
+    public abstract void writeBoolean(boolean state)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method for outputting literal Json null value.
+     * Can be called in any context where a value is expected
+     * (Array value, Object field value, root-level value).
+     * Additional white space may be added around the value
+     * if pretty-printing is enabled.
+     */
+    public abstract void writeNull()
+        throws IOException, JsonGenerationException;
+
+    /*
+    /**********************************************************
+    /* Public API, write methods, serializing Java objects
+    /**********************************************************
+     */
+
+    /**
+     * Method for writing given Java object (POJO) as Json.
+     * Exactly how the object gets written depends on object
+     * in question (ad on codec, its configuration); for most
+     * beans it will result in Json object, but for others Json
+     * array, or String or numeric value (and for nulls, Json
+     * null literal.
+     * <b>NOTE</b>: generator must have its <b>object codec</b>
+     * set to non-null value; for generators created by a mapping
+     * factory this is the case, for others not.
+     */
+    public abstract void writeObject(Object pojo)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method for writing given JSON tree (expressed as a tree
+     * where given JsonNode is the root) using this generator.
+     * This will generally just call
+     * {@link #writeObject} with given node, but is added
+     * for convenience and to make code more explicit in cases
+     * where it deals specifically with trees.
+     */
+    public abstract void writeTree(JsonNode rootNode)
+        throws IOException, JsonProcessingException;
+
+    /*
+    /**********************************************************
+    /* Public API, convenience field write methods
+    /**********************************************************
+     */
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * that has a String value. Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeString(value);
+     *</pre>
+     *<p>
+     * Note: many performance-sensitive implementations override this method
+     */
+    public void writeStringField(String fieldName, String value)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeString(value);
+    }
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * that has a boolean value. Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeBoolean(value);
+     *</pre>
+     */
+    public final void writeBooleanField(String fieldName, boolean value)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeBoolean(value);
+    }
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * that has JSON literal value null. Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeNull();
+     *</pre>
+     */
+    public final void writeNullField(String fieldName)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeNull();
+    }
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * that has the specified numeric value. Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeNumber(value);
+     *</pre>
+     */
+    public final void writeNumberField(String fieldName, int value)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeNumber(value);
+    }
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * that has the specified numeric value. Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeNumber(value);
+     *</pre>
+     */
+    public final void writeNumberField(String fieldName, long value)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeNumber(value);
+    }
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * that has the specified numeric value. Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeNumber(value);
+     *</pre>
+     */
+    public final void writeNumberField(String fieldName, double value)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeNumber(value);
+    }
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * that has the specified numeric value. Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeNumber(value);
+     *</pre>
+     */
+    public final void writeNumberField(String fieldName, float value)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeNumber(value);
+    }
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * that has the specified numeric value.
+     * Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeNumber(value);
+     *</pre>
+     */
+    public final void writeNumberField(String fieldName, BigDecimal value)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeNumber(value);
+    }
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * that contains specified data in base64-encoded form.
+     * Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeBinary(value);
+     *</pre>
+     */
+    public final void writeBinaryField(String fieldName, byte[] data)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeBinary(data);
+    }
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * (that will contain a JSON Array value), and the START_ARRAY marker.
+     * Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeStartArray();
+     *</pre>
+     *<p>
+     * Note: caller still has to take care to close the array
+     * (by calling {#link #writeEndArray}) after writing all values
+     * of the value Array.
+     */
+    public final void writeArrayFieldStart(String fieldName)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeStartArray();
+    }
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * (that will contain a JSON Object value), and the START_OBJECT marker.
+     * Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeStartObject();
+     *</pre>
+     *<p>
+     * Note: caller still has to take care to close the Object
+     * (by calling {#link #writeEndObject}) after writing all
+     * entries of the value Object.
+     */
+    public final void writeObjectFieldStart(String fieldName)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeStartObject();
+    }
+
+    /**
+     * Convenience method for outputting a field entry ("member")
+     * that has contents of specific Java object as its value.
+     * Equivalent to:
+     *<pre>
+     *  writeFieldName(fieldName);
+     *  writeObject(pojo);
+     *</pre>
+     */
+    public final void writeObjectField(String fieldName, Object pojo)
+        throws IOException, JsonProcessingException
+    {
+        writeFieldName(fieldName);
+        writeObject(pojo);
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, copy-through methods
+    /**********************************************************
+     */
+
+    /**
+     * Method for copying contents of the current event that
+     * the given parser instance points to.
+     * Note that the method <b>will not</b> copy any other events,
+     * such as events contained within Json Array or Object structures.
+     *<p>
+     * Calling this method will not advance the given
+     * parser, although it may cause parser to internally process
+     * more data (if it lazy loads contents of value events, for example)
+     */
+    public abstract void copyCurrentEvent(JsonParser jp)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method for copying contents of the current event
+     * <b>and following events that it encloses</b>
+     * the given parser instance points to.
+     *<p>
+     * So what constitutes enclosing? Here is the list of
+     * events that have associated enclosed events that will
+     * get copied:
+     *<ul>
+     * <li>{@link JsonToken#START_OBJECT}:
+     *   all events up to and including matching (closing)
+     *   {@link JsonToken#END_OBJECT} will be copied
+     *  </li>
+     * <li>{@link JsonToken#START_ARRAY}
+     *   all events up to and including matching (closing)
+     *   {@link JsonToken#END_ARRAY} will be copied
+     *  </li>
+     * <li>{@link JsonToken#FIELD_NAME} the logical value (which
+     *   can consist of a single scalar value; or a sequence of related
+     *   events for structured types (Json Arrays, Objects)) will
+     *   be copied along with the name itself. So essentially the
+     *   whole <b>field entry</b> (name and value) will be copied.
+     *  </li>
+     *</ul>
+     *<p>
+     * After calling this method, parser will point to the
+     * <b>last event</b> that was copied. This will either be
+     * the event parser already pointed to (if there were no
+     * enclosed events), or the last enclosed event copied.
+     */
+    public abstract void copyCurrentStructure(JsonParser jp)
+        throws IOException, JsonProcessingException;
+
+    /*
+    /**********************************************************
+    /* Public API, context access
+    /**********************************************************
+     */
+
+    /**
+     * @return Context object that can give information about logical
+     *   position within generated json content.
+     */
+    public abstract JsonStreamContext getOutputContext();
+
+    /*
+    /**********************************************************
+    /* Public API, buffer handling
+    /**********************************************************
+     */
+
+    /**
+     * Method called to flush any buffered content to the underlying
+     * target (output stream, writer), and to flush the target itself
+     * as well.
+     */
+    public abstract void flush() throws IOException;
+
+    /**
+     * Method that can be called to determine whether this generator
+     * is closed or not. If it is closed, no more output can be done.
+     */
+    public abstract boolean isClosed();
+
+    /*
+    /**********************************************************
+    /* Closeable implementation
+    /**********************************************************
+     */
+
+    /**
+     * Method called to close this generator, so that no more content
+     * can be written.
+     *<p>
+     * Whether the underlying target (stream, writer) gets closed depends
+     * on whether this generator either manages the target (i.e. is the
+     * only one with access to the target -- case if caller passes a
+     * reference to the resource such as File, but not stream); or
+     * has feature {@link Feature#AUTO_CLOSE_TARGET} enabled.
+     * If either of above is true, the target is also closed. Otherwise
+     * (not managing, feature not enabled), target is not closed.
+     */
+    @Override
+    public abstract void close()
+        throws IOException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonLocation.java b/src/main/java/com/fasterxml/jackson/core/JsonLocation.java
new file mode 100644
index 0000000..f72fc0c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonLocation.java
@@ -0,0 +1,139 @@
+package com.fasterxml.jackson.core;
+
+/**
+ * Object that encapsulates Location information used for reporting
+ * parsing (or potentially generation) errors, as well as current location
+ * within input streams.
+ */
+public class JsonLocation
+    implements java.io.Serializable // as per [JACKSON-168]
+{
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Shared immutable "N/A location" that can be returned to indicate
+     * that no location information is available
+     *
+     * @since 1.3
+     */
+    public final static JsonLocation NA = new JsonLocation("N/A", -1L, -1L, -1, -1);
+
+    final long _totalBytes;
+    final long _totalChars;
+
+    final int _lineNr;
+    final int _columnNr;
+
+    /**
+     * Displayable description for input source: file path, url
+     */
+    final Object _sourceRef;
+
+    public JsonLocation(Object srcRef, long totalChars, int lineNr, int colNr)
+    {
+        /* Unfortunately, none of legal encodings are straight single-byte
+         * encodings. Could determine offset for UTF-16/UTF-32, but the
+         * most important one is UTF-8...
+         * so for now, we'll just not report any real byte count
+         */
+        this(srcRef, -1L, totalChars, lineNr, colNr);
+    }
+
+    // 22-Dec-2011, tatu: TODO: add deserializer for this type instead:
+    //@JsonCreator
+    public JsonLocation(/*@JsonProperty("sourceRef")*/ Object sourceRef,
+                        /*@JsonProperty("byteOffset")*/ long totalBytes,
+                        /*@JsonProperty("charOffset")*/ long totalChars,
+                        /*@JsonProperty("lineNr")*/ int lineNr,
+                        /*@JsonProperty("columnNr")*/ int columnNr)
+    {
+        _sourceRef = sourceRef;
+        _totalBytes = totalBytes;
+        _totalChars = totalChars;
+        _lineNr = lineNr;
+        _columnNr = columnNr;
+    }
+
+    /**
+     * Reference to the original resource being read, if one available.
+     * For example, when a parser has been constructed by passing
+     * a {@link java.io.File} instance, this method would return
+     * that File. Will return null if no such reference is available,
+     * for example when {@link java.io.InputStream} was used to
+     * construct the parser instance.
+     */
+    public Object getSourceRef() { return _sourceRef; }
+
+    /**
+     * @return Line number of the location (1-based)
+     */
+    public int getLineNr() { return _lineNr; }
+
+    /**
+     * @return Column number of the location (1-based)
+     */
+    public int getColumnNr() { return _columnNr; }
+
+    /**
+     * @return Character offset within underlying stream, reader or writer,
+     *   if available; -1 if not.
+     */
+    public long getCharOffset() { return _totalChars; }
+
+    /**
+     * @return Byte offset within underlying stream, reader or writer,
+     *   if available; -1 if not.
+     */
+    public long getByteOffset()
+    {
+        return _totalBytes;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder(80);
+        sb.append("[Source: ");
+        if (_sourceRef == null) {
+            sb.append("UNKNOWN");
+        } else {
+            sb.append(_sourceRef.toString());
+        }
+        sb.append("; line: ");
+        sb.append(_lineNr);
+        sb.append(", column: ");
+        sb.append(_columnNr);
+        sb.append(']');
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int hash = (_sourceRef == null) ? 1 : _sourceRef.hashCode();
+        hash ^= _lineNr;
+        hash += _columnNr;
+        hash ^= (int) _totalChars;
+        hash += (int) _totalBytes;
+        return hash;
+    }
+
+    @Override
+    public boolean equals(Object other)
+    {
+        if (other == this) return true;
+        if (other == null) return false;
+        if (!(other instanceof JsonLocation)) return false;
+        JsonLocation otherLoc = (JsonLocation) other;
+
+        if (_sourceRef == null) {
+            if (otherLoc._sourceRef != null) return false;
+        } else if (!_sourceRef.equals(otherLoc._sourceRef)) return false;
+
+        return (_lineNr == otherLoc._lineNr)
+            && (_columnNr == otherLoc._columnNr)
+            && (_totalChars == otherLoc._totalChars)
+            && (getByteOffset() == otherLoc.getByteOffset())
+            ;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonNode.java b/src/main/java/com/fasterxml/jackson/core/JsonNode.java
new file mode 100644
index 0000000..1577d48
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonNode.java
@@ -0,0 +1,668 @@
+package com.fasterxml.jackson.core;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+
+/**
+ * Base class for all JSON nodes, which form the basis of JSON
+ * Tree Model that Jackson implements.
+ * One way to think of these nodes is to consider them
+ * similar to DOM nodes in XML DOM trees.
+ *<p>
+ * As a general design rule, most accessors ("getters") are included
+ * in this base class, to allow for traversing structure without
+ * type casts. Most mutators, however, need to be accessed through
+ * specific sub-classes (such as <code>org.codehaus.jackson.node.ObjectNode</code>
+ * and <code>org.codehaus.jackson.node.ArrayNode</code>).
+ * This seems sensible because proper type
+ * information is generally available when building or modifying
+ * trees, but less often when reading a tree (newly built from
+ * parsed JSON content).
+ *<p>
+ * Actual concrete sub-classes can be found from package
+ * {@link org.codehaus.jackson.node}, which is in 'mapper' jar
+ * (whereas this class is in 'core' jar, since it is declared as
+ * nominal type for operations in {@link ObjectCodec})
+ */
+public abstract class JsonNode
+    implements Iterable<JsonNode>
+{
+    protected final static List<JsonNode> NO_NODES = Collections.emptyList();
+    protected final static List<String> NO_STRINGS = Collections.emptyList();
+
+    protected JsonNode() { }
+
+    /*
+    /**********************************************************
+    /* Public API, type introspection
+    /**********************************************************
+     */
+
+    // // First high-level division between values, containers and "missing"
+
+    /**
+     * Method that returns true for all value nodes: ones that 
+     * are not containers, and that do not represent "missing" nodes
+     * in the path. Such value nodes represent String, Number, Boolean
+     * and null values from JSON.
+     *<p>
+     * Note: one and only one of methods {@link #isValueNode},
+     * {@link #isContainerNode} and {@link #isMissingNode} ever
+     * returns true for any given node.
+     */
+    public boolean isValueNode() { return false; }
+
+    /**
+     * Method that returns true for container nodes: Arrays and Objects.
+     *<p>
+     * Note: one and only one of methods {@link #isValueNode},
+     * {@link #isContainerNode} and {@link #isMissingNode} ever
+     * returns true for any given node.
+     */
+    public boolean isContainerNode() { return false; }
+
+    /**
+     * Method that returns true for "virtual" nodes which represent
+     * missing entries constructed by path accessor methods when
+     * there is no actual node matching given criteria.
+     *<p>
+     * Note: one and only one of methods {@link #isValueNode},
+     * {@link #isContainerNode} and {@link #isMissingNode} ever
+     * returns true for any given node.
+     */
+    public boolean isMissingNode() { return false; }
+
+    // // Then more specific type introspection
+    // // (along with defaults to be overridden)
+
+    /**
+     * @return True if this node represents Json Array
+     */
+    public boolean isArray() { return false; }
+
+    /**
+     * @return True if this node represents Json Object
+     */
+    public boolean isObject() { return false; }
+
+    /**
+     * Method that can be used to check if the node is a wrapper
+     * for a POJO ("Plain Old Java Object" aka "bean".
+     * Returns true only for
+     * instances of {@link org.codehaus.jackson.node.POJONode}.
+     *
+     * @return True if this node wraps a POJO
+     */
+    public boolean isPojo() { return false; }
+
+    /**
+     * @return True if this node represents a numeric Json
+     *   value
+     */
+    public boolean isNumber() { return false; }
+
+    /**
+     * @return True if this node represents an integral (integer)
+     *   numeric Json value
+     */
+    public boolean isIntegralNumber() { return false; }
+
+    /**
+     * @return True if this node represents a non-integral
+     *   numeric Json value
+     */
+    public boolean isFloatingPointNumber() { return false; }
+
+    /**
+     * @return True if this node represents an integral
+     *   numeric Json value that withs in Java int value space
+     */
+    public boolean isInt() { return false; }
+
+    /**
+     * @return True if this node represents an integral
+     *   numeric Json value that fits in Java long value space
+     *   (but not int value space, i.e. {@link #isInt} returns false)
+     */
+    public boolean isLong() { return false; }
+
+    public boolean isDouble() { return false; }
+    public boolean isBigDecimal() { return false; }
+    public boolean isBigInteger() { return false; }
+
+    public boolean isTextual() { return false; }
+
+    /**
+     * Method that can be used to check if this node was created from
+     * Json boolean value (literals "true" and "false").
+     */
+    public boolean isBoolean() { return false; }
+
+    /**
+     * Method that can be used to check if this node was created from
+     * Json liternal null value.
+     */
+    public boolean isNull() { return false; }
+
+    /**
+     * Method that can be used to check if this node represents
+     * binary data (Base64 encoded). Although this will be externally
+     * written as Json String value, {@link #isTextual} will
+     * return false if this method returns true.
+     *
+     * @return True if this node represents base64 encoded binary data
+     */
+    public boolean isBinary() { return false; }
+
+    /**
+     * Method that can be used for efficient type detection
+     * when using stream abstraction for traversing nodes.
+     * Will return the first {@link JsonToken} that equivalent
+     * stream event would produce (for most nodes there is just
+     * one token but for structured/container types multiple)
+     */
+    public abstract JsonToken asToken();
+
+    /**
+     * If this node is a numeric type (as per {@link #isNumber}),
+     * returns native type that node uses to store the numeric
+     * value.
+     */
+    public abstract JsonParser.NumberType getNumberType();
+
+    /*
+    /**********************************************************
+    /* Public API, straight value access
+    /**********************************************************
+     */
+
+    /**
+     * Method to use for accessing String values.
+     * Does <b>NOT</b> do any conversions for non-String value nodes;
+     * for non-String values (ones for which {@link #isTextual} returns
+     * false) null will be returned.
+     * For String values, null is never returned (but empty Strings may be)
+     *
+     * @return Textual value this node contains, iff it is a textual
+     *   json node (comes from Json String value entry)
+     */
+    public String getTextValue() { return null; }
+
+    /**
+     * Method to use for accessing binary content of binary nodes (nodes
+     * for which {@link #isBinary} returns true); or for Text Nodes
+     * (ones for which {@link #getTextValue} returns non-null value),
+     * to read decoded base64 data.
+     * For other types of nodes, returns null.
+     *
+     * @return Binary data this node contains, iff it is a binary
+     *   node; null otherwise
+     */
+    public byte[] getBinaryValue() throws IOException
+    {
+        return null;
+    }
+
+    /**
+     * Method to use for accessing JSON boolean values (value
+     * literals 'true' and 'false').
+     * For other types, always returns false.
+     *
+     * @return Textual value this node contains, iff it is a textual
+     *   json node (comes from Json String value entry)
+     */
+    public boolean getBooleanValue() { return false; }
+
+    /**
+     * Returns numeric value for this node, <b>if and only if</b>
+     * this node is numeric ({@link #isNumber} returns true); otherwise
+     * returns null
+     *
+     * @return Number value this node contains, if any (null for non-number
+     *   nodes).
+     */
+    public Number getNumberValue() { return null; }
+
+    /**
+     * Returns integer value for this node, <b>if and only if</b>
+     * this node is numeric ({@link #isNumber} returns true). For other
+     * types returns 0.
+     * For floating-point numbers, value is truncated using default
+     * Java coercion, similar to how cast from double to int operates.
+     *
+     * @return Integer value this node contains, if any; 0 for non-number
+     *   nodes.
+     */
+    public int getIntValue() { return 0; }
+
+    public long getLongValue() { return 0L; }
+    public double getDoubleValue() { return 0.0; }
+    public BigDecimal getDecimalValue() { return BigDecimal.ZERO; }
+    public BigInteger getBigIntegerValue() { return BigInteger.ZERO; }
+
+    /**
+     * Method for accessing value of the specified element of
+     * an array node. For other nodes, null is always returned.
+     *<p>
+     * For array nodes, index specifies
+     * exact location within array and allows for efficient iteration
+     * over child elements (underlying storage is guaranteed to
+     * be efficiently indexable, i.e. has random-access to elements).
+     * If index is less than 0, or equal-or-greater than
+     * <code>node.size()</code>, null is returned; no exception is
+     * thrown for any index.
+     *
+     * @return Node that represent value of the specified element,
+     *   if this node is an array and has specified element.
+     *   Null otherwise.
+     */
+    public JsonNode get(int index) { return null; }
+
+    /**
+     * Method for accessing value of the specified field of
+     * an object node. If this node is not an object (or it
+     * does not have a value for specified field name), or
+     * if there is no field with such name, null is returned.
+     *
+     * @return Node that represent value of the specified field,
+     *   if this node is an object and has value for the specified
+     *   field. Null otherwise.
+     */
+    public JsonNode get(String fieldName) { return null; }
+    
+    /*
+    /**********************************************************
+    /* Public API, value access with conversion(s)/coercion(s)
+    /**********************************************************
+     */
+
+    /**
+     * Method that will return valid String representation of
+     * the container value, if the node is a value node
+     * (method {@link #isValueNode} returns true), otherwise
+     * empty String.
+     */
+    public abstract String asText();
+
+    /**
+     * Method that will try to convert value of this node to a Java <b>int</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * and 1 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an int (including structured types
+     * like Objects and Arrays),
+     * default value of <b>0</b> will be returned; no exceptions are thrown.
+     */
+    public int asInt() {
+        return asInt(0);
+    }
+    
+    /**
+     * Method that will try to convert value of this node to a Java <b>int</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * and 1 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an int (including structured types
+     * like Objects and Arrays),
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public int asInt(int defaultValue) {
+        return defaultValue;
+    }
+
+    /**
+     * Method that will try to convert value of this node to a Java <b>long</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * and 1 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an long (including structured types
+     * like Objects and Arrays),
+     * default value of <b>0</b> will be returned; no exceptions are thrown.
+     */
+    public long asLong() {
+        return asLong(0L);
+    }
+    
+    /**
+     * Method that will try to convert value of this node to a Java <b>long</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * and 1 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an long (including structured types
+     * like Objects and Arrays),
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public long asLong(long defaultValue) {
+        return defaultValue;
+    }
+    
+    /**
+     * Method that will try to convert value of this node to a Java <b>double</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0.0 (false)
+     * and 1.0 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an int (including structured types
+     * like Objects and Arrays),
+     * default value of <b>0.0</b> will be returned; no exceptions are thrown.
+     */
+    public double asDouble() {
+        return asDouble(0.0);
+    }
+    
+    /**
+     * Method that will try to convert value of this node to a Java <b>double</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0.0 (false)
+     * and 1.0 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an int (including structured types
+     * like Objects and Arrays),
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public double asDouble(double defaultValue) {
+        return defaultValue;
+    }
+
+    /**
+     * Method that will try to convert value of this node to a Java <b>boolean</b>.
+     * JSON booleans map naturally; integer numbers other than 0 map to true, and
+     * 0 maps to false
+     * and Strings 'true' and 'false' map to corresponding values.
+     *<p>
+     * If representation can not be converted to a boolean value (including structured types
+     * like Objects and Arrays),
+     * default value of <b>false</b> will be returned; no exceptions are thrown.
+     */
+    public boolean asBoolean() {
+        return asBoolean(false);
+    }
+    
+    /**
+     * Method that will try to convert value of this node to a Java <b>boolean</b>.
+     * JSON booleans map naturally; integer numbers other than 0 map to true, and
+     * 0 maps to false
+     * and Strings 'true' and 'false' map to corresponding values.
+     *<p>
+     * If representation can not be converted to a boolean value (including structured types
+     * like Objects and Arrays),
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public boolean asBoolean(boolean defaultValue) {
+        return defaultValue;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, value find / existence check methods
+    /**********************************************************
+     */
+    
+    /**
+     * Method that allows checking whether this node is JSON Object node
+     * and contains value for specified property. If this is the case
+     * (including properties with explicit null values), returns true;
+     * otherwise returns false.
+     *<p>
+     * This method is equivalent to:
+     *<pre>
+     *   node.get(fieldName) != null
+     *</pre>
+     * (since return value of get() is node, not value node contains)
+     *
+     * @param fieldName Name of element to check
+     * 
+     * @return True if this node is a JSON Object node, and has a property
+     *   entry with specified name (with any value, including null value)
+     */
+    public boolean has(String fieldName) {
+        return get(fieldName) != null;
+    }
+
+    /**
+     * Method that allows checking whether this node is JSON Array node
+     * and contains a value for specified index
+     * If this is the case
+     * (including case of specified indexing having null as value), returns true;
+     * otherwise returns false.
+     *<p>
+     * Note: array element indexes are 0-based.
+     *<p>
+     * This method is equivalent to:
+     *<pre>
+     *   node.get(index) != null
+     *</pre>
+     *
+     * @param index Index to check
+     * 
+     * @return True if this node is a JSON Object node, and has a property
+     *   entry with specified name (with any value, including null value)
+     */
+    public boolean has(int index) {
+        return get(index) != null;
+    }
+
+    /**
+     * Method for finding a JSON Object field with specified name in this
+     * node or its child nodes, and returning value it has.
+     * If no matching field is found in this node or its descendants, returns null.
+     * 
+     * @param fieldName Name of field to look for
+     * 
+     * @return Value of first matching node found, if any; null if none
+     */
+    public abstract JsonNode findValue(String fieldName);
+
+    /**
+     * Method for finding JSON Object fields with specified name, and returning
+     * found ones as a List. Note that sub-tree search ends if a field is found,
+     * so possible children of result nodes are <b>not</b> included.
+     * If no matching fields are found in this node or its descendants, returns
+     * an empty List.
+     * 
+     * @param fieldName Name of field to look for
+     */
+    public final List<JsonNode> findValues(String fieldName)
+    {
+        List<JsonNode> result = findValues(fieldName, null);
+        if (result == null) {
+            return Collections.emptyList();
+        }
+        return result;
+    }
+
+    /**
+     * Similar to {@link #findValues}, but will additionally convert
+     * values into Strings, calling {@link #getValueAsText}.
+     */
+    public final List<String> findValuesAsText(String fieldName)
+    {
+        List<String> result = findValuesAsText(fieldName, null);
+        if (result == null) {
+            return Collections.emptyList();
+        }
+        return result;
+    }
+    
+    /**
+     * Method similar to {@link #findValue}, but that will return a
+     * "missing node" instead of null if no field is found. Missing node
+     * is a specific kind of node for which {@link #isMissingNode}
+     * returns true; and all value access methods return empty or
+     * missing value.
+     * 
+     * @param fieldName Name of field to look for
+     * 
+     * @return Value of first matching node found; or if not found, a
+     *    "missing node" (non-null instance that has no value)
+     */
+    public abstract JsonNode findPath(String fieldName);
+    
+    /**
+     * Method for finding a JSON Object that contains specified field,
+     * within this node or its descendants.
+     * If no matching field is found in this node or its descendants, returns null.
+     * 
+     * @param fieldName Name of field to look for
+     * 
+     * @return Value of first matching node found, if any; null if none
+     */
+    public abstract JsonNode findParent(String fieldName);
+
+    /**
+     * Method for finding a JSON Object that contains specified field,
+     * within this node or its descendants.
+     * If no matching field is found in this node or its descendants, returns null.
+     * 
+     * @param fieldName Name of field to look for
+     * 
+     * @return Value of first matching node found, if any; null if none
+     */
+    public final List<JsonNode> findParents(String fieldName)
+    {
+        List<JsonNode> result = findParents(fieldName, null);
+        if (result == null) {
+            return Collections.emptyList();
+        }
+        return result;
+    }
+
+    public abstract List<JsonNode> findValues(String fieldName, List<JsonNode> foundSoFar);
+    public abstract List<String> findValuesAsText(String fieldName, List<String> foundSoFar);
+    public abstract List<JsonNode> findParents(String fieldName, List<JsonNode> foundSoFar);
+
+    /*
+    /**********************************************************
+    /* Public API, container access
+    /**********************************************************
+     */
+
+    /**
+     * Method that returns number of child nodes this node contains:
+     * for Array nodes, number of child elements, for Object nodes,
+     * number of fields, and for all other nodes 0.
+     *
+     * @return For non-container nodes returns 0; for arrays number of
+     *   contained elements, and for objects number of fields.
+     */
+    public int size() { return 0; }
+
+    /**
+     * Same as calling {@link #getElements}; implemented so that
+     * convenience "for-each" loop can be used for looping over elements
+     * of JSON Array constructs.
+     */
+    @Override
+    public final Iterator<JsonNode> iterator() { return getElements(); }
+
+    /**
+     * Method for accessing all value nodes of this Node, iff
+     * this node is a JSON Array or Object node. In case of Object node,
+     * field names (keys) are not included, only values.
+     * For other types of nodes, returns empty iterator.
+     */
+    public Iterator<JsonNode> getElements() { return NO_NODES.iterator(); }
+
+    /**
+     * Method for accessing names of all fields for this Node, iff
+     * this node is a JSON Object node.
+     */
+    public Iterator<String> getFieldNames() { return NO_STRINGS.iterator(); }
+
+    /**
+     * @return Iterator that can be used to traverse all key/value pairs for
+     *   object nodes; empty iterator (no contents) for other types
+     */
+    public Iterator<Map.Entry<String, JsonNode>> getFields() {
+        Collection<Map.Entry<String, JsonNode>> coll = Collections.emptyList();
+        return coll.iterator();
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, path handling
+    /**********************************************************
+     */
+
+    /**
+     * This method is similar to {@link #get(String)}, except
+     * that instead of returning null if no such value exists (due
+     * to this node not being an object, or object not having value
+     * for the specified field),
+     * a "missing node" (node that returns true for
+     * {@link #isMissingNode}) will be returned. This allows for
+     * convenient and safe chained access via path calls.
+     */
+    public abstract JsonNode path(String fieldName);
+
+    /**
+     * This method is similar to {@link #get(int)}, except
+     * that instead of returning null if no such element exists (due
+     * to index being out of range, or this node not being an array),
+     * a "missing node" (node that returns true for
+     * {@link #isMissingNode}) will be returned. This allows for
+     * convenient and safe chained access via path calls.
+     */
+    public abstract JsonNode path(int index);
+
+    /**
+     * Method that can be called on object nodes, to access a property
+     * that has object value; or if no such property exists, to create and
+     * return such object node.
+     * If node method is called on is not Object node,
+     * or if property exists and has value that is not object node,
+     * {@link UnsupportedOperationException} is thrown
+     */
+    public JsonNode with(String propertyName) {
+        throw new UnsupportedOperationException("JsonNode not of type ObjectNode (but "
+                +getClass().getName()+"), can not call with() on it");
+    }
+
+    /*
+    /**********************************************************
+    /* Public API: converting to/from Streaming API
+    /**********************************************************
+     */
+
+    /**
+     * Method for constructing a {@link JsonParser} instance for
+     * iterating over contents of the tree that this
+     * node is root of.
+     * Functionally equivalent to first serializing tree using
+     * {@link ObjectCodec} and then re-parsing but
+     * more efficient.
+     */
+    public abstract JsonParser traverse();
+
+    /*
+    /**********************************************************
+    /* Overridden standard methods
+    /**********************************************************
+     */
+    
+    /**
+     *<p>
+     * Note: marked as abstract to ensure all implementation
+     * classes define it properly.
+     */
+    @Override
+    public abstract String toString();
+
+    /**
+     * Equality for node objects is defined as full (deep) value
+     * equality. This means that it is possible to compare complete
+     * JSON trees for equality by comparing equality of root nodes.
+     *<p>
+     * Note: marked as abstract to ensure all implementation
+     * classes define it properly and not rely on definition
+     * from {@link java.lang.Object}.
+     */
+    @Override
+    public abstract boolean equals(Object o);
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParseException.java b/src/main/java/com/fasterxml/jackson/core/JsonParseException.java
new file mode 100644
index 0000000..d1f53c8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonParseException.java
@@ -0,0 +1,22 @@
+package com.fasterxml.jackson.core;
+
+/**
+ * Exception type for parsing problems, used when non-well-formed content
+ * (content that does not conform to JSON syntax as per specification)
+ * is encountered.
+ */
+public class JsonParseException
+    extends JsonProcessingException
+{
+    final static long serialVersionUID = 123; // Stupid eclipse...
+
+    public JsonParseException(String msg, JsonLocation loc)
+    {
+        super(msg, loc);
+    }
+
+    public JsonParseException(String msg, JsonLocation loc, Throwable root)
+    {
+        super(msg, loc, root);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java
new file mode 100644
index 0000000..05fc104
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java
@@ -0,0 +1,1338 @@
+/* Jackson JSON-processor.
+ *
+ * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
+ *
+ * Licensed under the License specified in file LICENSE, included with
+ * the source code and binary code bundles.
+ * You may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fasterxml.jackson.core;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Iterator;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+
+/**
+ * Base class that defines public API for reading JSON content.
+ * Instances are created using factory methods of
+ * a {@link JsonFactory} instance.
+ *
+ * @author Tatu Saloranta
+ */
+public abstract class JsonParser
+    implements Closeable, Versioned
+{
+    private final static int MIN_BYTE_I = (int) Byte.MIN_VALUE;
+    private final static int MAX_BYTE_I = (int) Byte.MAX_VALUE;
+
+    private final static int MIN_SHORT_I = (int) Short.MIN_VALUE;
+    private final static int MAX_SHORT_I = (int) Short.MAX_VALUE;
+
+    /**
+     * Enumeration of possible "native" (optimal) types that can be
+     * used for numbers.
+     */
+    public enum NumberType {
+        INT, LONG, BIG_INTEGER, FLOAT, DOUBLE, BIG_DECIMAL
+    };
+
+    /**
+     * Enumeration that defines all togglable features for parsers.
+     */
+    public enum Feature {
+        
+        // // // Low-level I/O handling features:
+        
+        /**
+         * Feature that determines whether parser will automatically
+         * close underlying input source that is NOT owned by the
+         * parser. If disabled, calling application has to separately
+         * close the underlying {@link InputStream} and {@link Reader}
+         * instances used to create the parser. If enabled, parser
+         * will handle closing, as long as parser itself gets closed:
+         * this happens when end-of-input is encountered, or parser
+         * is closed by a call to {@link JsonParser#close}.
+         *<p>
+         * Feature is enabled by default.
+         */
+        AUTO_CLOSE_SOURCE(true),
+            
+        // // // Support for non-standard data format constructs
+
+        /**
+         * Feature that determines whether parser will allow use
+         * of Java/C++ style comments (both '/'+'*' and
+         * '//' varieties) within parsed content or not.
+         *<p>
+         * Since JSON specification does not mention comments as legal
+         * construct,
+         * this is a non-standard feature; however, in the wild
+         * this is extensively used. As such, feature is
+         * <b>disabled by default</b> for parsers and must be
+         * explicitly enabled (via factory or parser instance).
+         *<p>
+         * This feature can be changed for parser instances.
+         */
+        ALLOW_COMMENTS(false),
+
+        /**
+         * Feature that determines whether parser will allow use
+         * of unquoted field names (which is allowed by Javascript,
+         * but not by JSON specification).
+         *<p>
+         * Since JSON specification requires use of double quotes for
+         * field names,
+         * this is a non-standard feature, and as such disabled by
+         * default.
+         *<p>
+         * This feature can be changed for parser instances.
+         */
+        ALLOW_UNQUOTED_FIELD_NAMES(false),
+
+        /**
+         * Feature that determines whether parser will allow use
+         * of single quotes (apostrophe, character '\'') for
+         * quoting Strings (names and String values). If so,
+         * this is in addition to other acceptabl markers.
+         * but not by JSON specification).
+         *<p>
+         * Since JSON specification requires use of double quotes for
+         * field names,
+         * this is a non-standard feature, and as such disabled by
+         * default.
+         *<p>
+         * This feature can be changed for parser instances.
+         */
+        ALLOW_SINGLE_QUOTES(false),
+
+        /**
+         * Feature that determines whether parser will allow
+         * JSON Strings to contain unquoted control characters
+         * (ASCII characters with value less than 32, including
+         * tab and line feed characters) or not.
+         * If feature is set false, an exception is thrown if such a
+         * character is encountered.
+         *<p>
+         * Since JSON specification requires quoting for all control characters,
+         * this is a non-standard feature, and as such disabled by default.
+         *<p>
+         * This feature can be changed for parser instances.
+         */
+        ALLOW_UNQUOTED_CONTROL_CHARS(false),
+
+        /**
+         * Feature that can be enabled to accept quoting of all character
+         * using backslash qooting mechanism: if not enabled, only characters
+         * that are explicitly listed by JSON specification can be thus
+         * escaped (see JSON spec for small list of these characters)
+         *<p>
+         * Since JSON specification requires quoting for all control characters,
+         * this is a non-standard feature, and as such disabled by default.
+         *<p>
+         * This feature can be changed for parser instances.
+         */
+        ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false),
+
+        /**
+         * Feature that determines whether parser will allow
+         * JSON integral numbers to start with additional (ignorable) 
+         * zeroes (like: 000001). If enabled, no exception is thrown, and extra
+         * nulls are silently ignored (and not included in textual representation
+         * exposed via {@link JsonParser#getText}).
+         *<p>
+         * Since JSON specification does not allow leading zeroes,
+         * this is a non-standard feature, and as such disabled by default.
+         *<p>
+         * This feature can be changed for parser instances.
+         */
+        ALLOW_NUMERIC_LEADING_ZEROS(false),
+        
+        /**
+         * Feature that allows parser to recognize set of
+         * "Not-a-Number" (NaN) tokens as legal floating number
+         * values (similar to how many other data formats and
+         * programming language source code allows it).
+         * Specific subset contains values that
+         * <a href="http://www.w3.org/TR/xmlschema-2/">XML Schema</a>
+         * (see section 3.2.4.1, Lexical Representation)
+         * allows (tokens are quoted contents, not including quotes):
+         *<ul>
+         *  <li>"INF" (for positive infinity), as well as alias of "Infinity"
+         *  <li>"-INF" (for negative infinity), alias "-Infinity"
+         *  <li>"NaN" (for other not-a-numbers, like result of division by zero)
+         *</ul>
+         */
+
+         ALLOW_NON_NUMERIC_NUMBERS(false),
+        
+        // // // Controlling canonicalization (interning etc)
+        
+        /**
+         * Feature that determines whether JSON object field names are
+         * to be canonicalized using {@link String#intern} or not:
+         * if enabled, all field names will be intern()ed (and caller
+         * can count on this being true for all such names); if disabled,
+         * no intern()ing is done. There may still be basic
+         * canonicalization (that is, same String will be used to represent
+         * all identical object property names for a single document).
+         *<p>
+         * Note: this setting only has effect if
+         * {@link #CANONICALIZE_FIELD_NAMES} is true -- otherwise no
+         * canonicalization of any sort is done.
+         */
+        INTERN_FIELD_NAMES(true),
+
+        /**
+         * Feature that determines whether JSON object field names are
+         * to be canonicalized (details of how canonicalization is done
+         * then further specified by
+         * {@link #INTERN_FIELD_NAMES}).
+         */
+        CANONICALIZE_FIELD_NAMES(true),
+
+
+            ;
+
+        final boolean _defaultState;
+
+        /**
+         * Method that calculates bit set (flags) of all features that
+         * are enabled by default.
+         */
+        public static int collectDefaults()
+        {
+            int flags = 0;
+            for (Feature f : values()) {
+                if (f.enabledByDefault()) {
+                    flags |= f.getMask();
+                }
+            }
+            return flags;
+        }
+        
+        private Feature(boolean defaultState) {
+            _defaultState = defaultState;
+        }
+        
+        public boolean enabledByDefault() { return _defaultState; }
+
+        public boolean enabledIn(int flags) { return (flags & getMask()) != 0; }
+        
+        public int getMask() { return (1 << ordinal()); }
+    };
+
+    /*
+    /**********************************************************
+    /* Minimal configuration state
+    /**********************************************************
+     */
+
+    /**
+     * Bit flag composed of bits that indicate which
+     * {@link com.fasterxml.jackson.core.JsonParser.Feature}s
+     * are enabled.
+     */
+    protected int _features;
+
+    /*
+    /**********************************************************
+    /* Minimal generic state
+    /**********************************************************
+     */
+
+    /**
+     * Last token retrieved via {@link #nextToken}, if any.
+     * Null before the first call to <code>nextToken()</code>,
+     * as well as if token has been explicitly cleared
+     * (by call to {@link #clearCurrentToken})
+     */
+    protected JsonToken _currToken;
+
+    /**
+     * Last cleared token, if any: that is, value that was in
+     * effect when {@link #clearCurrentToken} was called.
+     */
+    protected JsonToken _lastClearedToken;
+
+    /*
+    /**********************************************************
+    /* Construction, configuration, initialization
+    /**********************************************************
+     */
+
+    protected JsonParser() { }
+    protected JsonParser(int features) {
+        _features = features;
+    }
+
+    /**
+     * Accessor for {@link ObjectCodec} associated with this
+     * parser, if any. Codec is used by {@link #readValueAs(Class)}
+     * method (and its variants).
+     */
+    public abstract ObjectCodec getCodec();
+
+    /**
+     * Setter that allows defining {@link ObjectCodec} associated with this
+     * parser, if any. Codec is used by {@link #readValueAs(Class)}
+     * method (and its variants).
+     */
+    public abstract void setCodec(ObjectCodec c);
+
+    /**
+     * Method to call to make this parser use specified schema. Method must
+     * be called before trying to parse any content, right after parser instance
+     * has been created.
+     * Note that not all parsers support schemas; and those that do usually only
+     * accept specific types of schemas: ones defined for data format parser can read.
+     *<p>
+     * If parser does not support specified schema, {@link UnsupportedOperationException}
+     * is thrown.
+     * 
+     * @param schema Schema to use
+     * 
+     * @throws UnsupportedOperationException if parser does not support schema
+     */
+    public void setSchema(FormatSchema schema)
+    {
+        throw new UnsupportedOperationException("Parser of type "+getClass().getName()+" does not support schema of type '"
+                +schema.getSchemaType()+"'");
+    }
+    
+    /**
+     * Method that can be used to verify that given schema can be used with
+     * this parser (using {@link #setSchema}).
+     * 
+     * @param schema Schema to check
+     * 
+     * @return True if this parser can use given schema; false if not
+     */
+    public boolean canUseSchema(FormatSchema schema) {
+        return false;
+    }
+    
+    /**
+     * Accessor for getting version of the core package, given a parser instance.
+     */
+    @Override
+    public Version version() {
+        return Version.unknownVersion();
+    }
+
+    /**
+     * Method that can be used to get access to object that is used
+     * to access input being parsed; this is usually either
+     * {@link InputStream} or {@link Reader}, depending on what
+     * parser was constructed with.
+     * Note that returned value may be null in some cases; including
+     * case where parser implementation does not want to exposed raw
+     * source to caller.
+     * In cases where input has been decorated, object returned here
+     * is the decorated version; this allows some level of interaction
+     * between users of parser and decorator object.
+     *<p>
+     * In general use of this accessor should be considered as
+     * "last effort", i.e. only used if no other mechanism is applicable.
+     */
+    public Object getInputSource() {
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Closeable implementation
+    /**********************************************************
+     */
+
+    /**
+     * Closes the parser so that no further iteration or data access
+     * can be made; will also close the underlying input source
+     * if parser either <b>owns</b> the input source, or feature
+     * {@link Feature#AUTO_CLOSE_SOURCE} is enabled.
+     * Whether parser owns the input source depends on factory
+     * method that was used to construct instance (so check
+     * {@link com.fasterxml.jackson.core.JsonFactory} for details,
+     * but the general
+     * idea is that if caller passes in closable resource (such
+     * as {@link InputStream} or {@link Reader}) parser does NOT
+     * own the source; but if it passes a reference (such as
+     * {@link java.io.File} or {@link java.net.URL} and creates
+     * stream or reader it does own them.
+     */
+    @Override
+    public abstract void close() throws IOException;
+
+    /*
+    /**********************************************************
+    /* Buffer handling
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be called to push back any content that
+     * has been read but not consumed by the parser. This is usually
+     * done after reading all content of interest using parser.
+     * Content is released by writing it to given stream if possible;
+     * if underlying input is byte-based it can released, if not (char-based)
+     * it can not.
+     * 
+     * @return -1 if the underlying content source is not byte based
+     *    (that is, input can not be sent to {@link OutputStream};
+     *    otherwise number of bytes released (0 if there was nothing to release)
+     *    
+     * @throws IOException if write to stream threw exception
+     */    
+    public int releaseBuffered(OutputStream out) throws IOException
+    {
+        return -1;
+    }
+
+    /**
+     * Method that can be called to push back any content that
+     * has been read but not consumed by the parser.
+     * This is usually
+     * done after reading all content of interest using parser.
+     * Content is released by writing it to given writer if possible;
+     * if underlying input is char-based it can released, if not (byte-based)
+     * it can not.
+     * 
+     * @return -1 if the underlying content source is not char-based
+     *    (that is, input can not be sent to {@link Writer};
+     *    otherwise number of chars released (0 if there was nothing to release)
+     *    
+     * @throws IOException if write using Writer threw exception
+     */    
+    public int releaseBuffered(Writer w) throws IOException
+    {
+        return -1;
+    }
+    
+    /*
+    /***************************************************
+    /* Public API, configuration
+    /***************************************************
+     */
+
+    /**
+     * Method for enabling specified parser feature
+     * (check {@link Feature} for list of features)
+     */
+    public JsonParser enable(Feature f)
+    {
+        _features |= f.getMask();
+        return this;
+    }
+
+    /**
+     * Method for disabling specified  feature
+     * (check {@link Feature} for list of features)
+     */
+    public JsonParser disable(Feature f)
+    {
+        _features &= ~f.getMask();
+        return this;
+    }
+
+    /**
+     * Method for enabling or disabling specified feature
+     * (check {@link Feature} for list of features)
+     */
+    public JsonParser configure(Feature f, boolean state)
+    {
+        if (state) {
+            enable(f);
+        } else {
+            disable(f);
+        }
+        return this;
+    }
+
+    /**
+     * Method for checking whether specified {@link Feature} is enabled.
+     */
+    public boolean isEnabled(Feature f) {
+        return (_features & f.getMask()) != 0;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, traversal
+    /**********************************************************
+     */
+
+    /**
+     * Main iteration method, which will advance stream enough
+     * to determine type of the next token, if any. If none
+     * remaining (stream has no content other than possible
+     * white space before ending), null will be returned.
+     *
+     * @return Next token from the stream, if any found, or null
+     *   to indicate end-of-input
+     */
+    public abstract JsonToken nextToken()
+        throws IOException, JsonParseException;
+
+    /**
+     * Iteration method that will advance stream enough
+     * to determine type of the next token that is a value type
+     * (including JSON Array and Object start/end markers).
+     * Or put another way, nextToken() will be called once,
+     * and if {@link JsonToken#FIELD_NAME} is returned, another
+     * time to get the value for the field.
+     * Method is most useful for iterating over value entries
+     * of JSON objects; field name will still be available
+     * by calling {@link #getCurrentName} when parser points to
+     * the value.
+     *
+     * @return Next non-field-name token from the stream, if any found,
+     *   or null to indicate end-of-input (or, for non-blocking
+     *   parsers, {@link JsonToken#NOT_AVAILABLE} if no tokens were
+     *   available yet)
+     */
+    public JsonToken nextValue()
+        throws IOException, JsonParseException
+    {
+        /* Implementation should be as trivial as follows; only
+         * needs to change if we are to skip other tokens (for
+         * example, if comments were exposed as tokens)
+         */
+        JsonToken t = nextToken();
+        if (t == JsonToken.FIELD_NAME) {
+            t = nextToken();
+        }
+        return t;
+    }
+
+    /**
+     * Method that fetches next token (as if calling {@link #nextToken}) and
+     * verifies whether it is {@link JsonToken#FIELD_NAME} with specified name
+     * and returns result of that comparison.
+     * It is functionally equivalent to:
+     *<pre>
+     *  return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName());
+     *</pre>
+     * but may be faster for parser to verify, and can therefore be used if caller
+     * expects to get such a property name from input next.
+     * 
+     * @param str Property name to compare next token to (if next token is <code>JsonToken.FIELD_NAME<code>)
+     */
+    public boolean nextFieldName(SerializableString str)
+        throws IOException, JsonParseException
+    {
+        return (nextToken() == JsonToken.FIELD_NAME) && str.getValue().equals(getCurrentName());
+    }
+
+    /**
+     * Method that fetches next token (as if calling {@link #nextToken}) and
+     * if it is {@link JsonToken#VALUE_STRING} returns contained String value;
+     * otherwise returns null.
+     * It is functionally equivalent to:
+     *<pre>
+     *  return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
+     *</pre>
+     * but may be faster for parser to process, and can therefore be used if caller
+     * expects to get a String value next from input.
+     */
+    public String nextTextValue()
+        throws IOException, JsonParseException
+    {
+        return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
+    }
+
+    /**
+     * Method that fetches next token (as if calling {@link #nextToken}) and
+     * if it is {@link JsonToken#VALUE_NUMBER_INT} returns 32-bit int value;
+     * otherwise returns specified default value
+     * It is functionally equivalent to:
+     *<pre>
+     *  return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
+     *</pre>
+     * but may be faster for parser to process, and can therefore be used if caller
+     * expects to get a String value next from input.
+     */
+    public int nextIntValue(int defaultValue)
+        throws IOException, JsonParseException
+    {
+        return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
+    }
+
+    /**
+     * Method that fetches next token (as if calling {@link #nextToken}) and
+     * if it is {@link JsonToken#VALUE_NUMBER_INT} returns 64-bit long value;
+     * otherwise returns specified default value
+     * It is functionally equivalent to:
+     *<pre>
+     *  return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
+     *</pre>
+     * but may be faster for parser to process, and can therefore be used if caller
+     * expects to get a String value next from input.
+     */
+    public long nextLongValue(long defaultValue)
+        throws IOException, JsonParseException
+    {
+        return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
+    }
+
+    /**
+     * Method that fetches next token (as if calling {@link #nextToken}) and
+     * if it is {@link JsonToken#VALUE_TRUE} or {@link JsonToken#VALUE_FALSE}
+     * returns matching Boolean value; otherwise return null.
+     * It is functionally equivalent to:
+     *<pre>
+     *  JsonToken t = nextToken();
+     *  if (t == JsonToken.VALUE_TRUE) return Boolean.TRUE;
+     *  if (t == JsonToken.VALUE_FALSE) return Boolean.FALSE;
+     *  return null;
+     *</pre>
+     * but may be faster for parser to process, and can therefore be used if caller
+     * expects to get a String value next from input.
+     */
+    public Boolean nextBooleanValue()
+        throws IOException, JsonParseException
+    {
+        switch (nextToken()) {
+        case VALUE_TRUE:
+            return Boolean.TRUE;
+        case VALUE_FALSE:
+            return Boolean.FALSE;
+        }
+        return null;
+    }
+    
+    /**
+     * Method that will skip all child tokens of an array or
+     * object token that the parser currently points to,
+     * iff stream points to 
+     * {@link JsonToken#START_OBJECT} or {@link JsonToken#START_ARRAY}.
+     * If not, it will do nothing.
+     * After skipping, stream will point to <b>matching</b>
+     * {@link JsonToken#END_OBJECT} or {@link JsonToken#END_ARRAY}
+     * (possibly skipping nested pairs of START/END OBJECT/ARRAY tokens
+     * as well as value tokens).
+     * The idea is that after calling this method, application
+     * will call {@link #nextToken} to point to the next
+     * available token, if any.
+     */
+    public abstract JsonParser skipChildren()
+        throws IOException, JsonParseException;
+    
+    /**
+     * Method that can be called to determine whether this parser
+     * is closed or not. If it is closed, no new tokens can be
+     * retrieved by calling {@link #nextToken} (and the underlying
+     * stream may be closed). Closing may be due to an explicit
+     * call to {@link #close} or because parser has encountered
+     * end of input.
+     */
+    public abstract boolean isClosed();
+    
+    /*
+    /**********************************************************
+    /* Public API, token accessors
+    /**********************************************************
+     */
+
+    /**
+     * Accessor to find which token parser currently points to, if any;
+     * null will be returned if none.
+     * If return value is non-null, data associated with the token
+     * is available via other accessor methods.
+     *
+     * @return Type of the token this parser currently points to,
+     *   if any: null before any tokens have been read, and
+     *   after end-of-input has been encountered, as well as
+     *   if the current token has been explicitly cleared.
+     */
+    public JsonToken getCurrentToken() {
+        return _currToken;
+    }
+
+    /**
+     * Method for checking whether parser currently points to
+     * a token (and data for that token is available).
+     * Equivalent to check for <code>parser.getCurrentToken() != null</code>.
+     *
+     * @return True if the parser just returned a valid
+     *   token via {@link #nextToken}; false otherwise (parser
+     *   was just constructed, encountered end-of-input
+     *   and returned null from {@link #nextToken}, or the token
+     *   has been consumed)
+     */
+    public boolean hasCurrentToken() {
+        return _currToken != null;
+    }
+
+
+    /**
+     * Method called to "consume" the current token by effectively
+     * removing it so that {@link #hasCurrentToken} returns false, and
+     * {@link #getCurrentToken} null).
+     * Cleared token value can still be accessed by calling
+     * {@link #getLastClearedToken} (if absolutely needed), but
+     * usually isn't.
+     *<p>
+     * Method was added to be used by the optional data binder, since
+     * it has to be able to consume last token used for binding (so that
+     * it will not be used again).
+     */
+    public void clearCurrentToken() {
+        if (_currToken != null) {
+            _lastClearedToken = _currToken;
+            _currToken = null;
+        }
+    }
+
+    /**
+     * Method that can be called to get the name associated with
+     * the current token: for {@link JsonToken#FIELD_NAME}s it will
+     * be the same as what {@link #getText} returns;
+     * for field values it will be preceding field name;
+     * and for others (array values, root-level values) null.
+     */
+    public abstract String getCurrentName()
+        throws IOException, JsonParseException;
+
+    /**
+     * Method that can be used to access current parsing context reader
+     * is in. There are 3 different types: root, array and object contexts,
+     * with slightly different available information. Contexts are
+     * hierarchically nested, and can be used for example for figuring
+     * out part of the input document that correspond to specific
+     * array or object (for highlighting purposes, or error reporting).
+     * Contexts can also be used for simple xpath-like matching of
+     * input, if so desired.
+     */
+    public abstract JsonStreamContext getParsingContext();
+
+    /**
+     * Method that return the <b>starting</b> location of the current
+     * token; that is, position of the first character from input
+     * that starts the current token.
+     */
+    public abstract JsonLocation getTokenLocation();
+
+    /**
+     * Method that returns location of the last processed character;
+     * usually for error reporting purposes.
+     */
+    public abstract JsonLocation getCurrentLocation();
+
+    /**
+     * Method that can be called to get the last token that was
+     * cleared using {@link #clearCurrentToken}. This is not necessarily
+     * the latest token read.
+     * Will return null if no tokens have been cleared,
+     * or if parser has been closed.
+     */
+    public JsonToken getLastClearedToken() {
+        return _lastClearedToken;
+    }
+
+    /**
+     * Specialized accessor that can be used to verify that the current
+     * token indicates start array (usually meaning that current token
+     * is {@link JsonToken#START_ARRAY}) when start array is expected.
+     * For some specialized parsers this can return true for other cases
+     * as well; this is usually done to emulate arrays.
+     *<p>
+     * Default implementation is equivalent to:
+     *<pre>
+     *   getCurrentToken() == JsonToken.START_ARRAY
+     *</pre>
+     * but may be overridden by custom parser implementations.
+     *
+     * @return True if the current token can be considered as a
+     *   start-array marker (such {@link JsonToken#START_ARRAY});
+     *   false if not.
+     */
+    public boolean isExpectedStartArrayToken() {
+        return getCurrentToken() == JsonToken.START_ARRAY;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, access to token information, text
+    /**********************************************************
+     */
+
+    /**
+     * Method for accessing textual representation of the current token;
+     * if no current token (before first call to {@link #nextToken}, or
+     * after encountering end-of-input), returns null.
+     * Method can be called for any token type.
+     */
+    public abstract String getText()
+        throws IOException, JsonParseException;
+
+    /**
+     * Method similar to {@link #getText}, but that will return
+     * underlying (unmodifiable) character array that contains
+     * textual value, instead of constructing a String object
+     * to contain this information.
+     * Note, however, that:
+     *<ul>
+     * <li>Textual contents are not guaranteed to start at
+     *   index 0 (rather, call {@link #getTextOffset}) to
+     *   know the actual offset
+     *  </li>
+     * <li>Length of textual contents may be less than the
+     *  length of returned buffer: call {@link #getTextLength}
+     *  for actual length of returned content.
+     *  </li>
+     * </ul>
+     *<p>
+     * Note that caller <b>MUST NOT</b> modify the returned
+     * character array in any way -- doing so may corrupt
+     * current parser state and render parser instance useless.
+     *<p>
+     * The only reason to call this method (over {@link #getText})
+     * is to avoid construction of a String object (which
+     * will make a copy of contents).
+     */
+    public abstract char[] getTextCharacters()
+        throws IOException, JsonParseException;
+
+    /**
+     * Accessor used with {@link #getTextCharacters}, to know length
+     * of String stored in returned buffer.
+     *
+     * @return Number of characters within buffer returned
+     *   by {@link #getTextCharacters} that are part of
+     *   textual content of the current token.
+     */
+    public abstract int getTextLength()
+        throws IOException, JsonParseException;
+
+    /**
+     * Accessor used with {@link #getTextCharacters}, to know offset
+     * of the first text content character within buffer.
+     *
+     * @return Offset of the first character within buffer returned
+     *   by {@link #getTextCharacters} that is part of
+     *   textual content of the current token.
+     */
+    public abstract int getTextOffset()
+        throws IOException, JsonParseException;
+
+    /**
+     * Method that can be used to determine whether calling of
+     * {@link #getTextCharacters} would be the most efficient
+     * way to access textual content for the event parser currently
+     * points to.
+     *<p> 
+     * Default implementation simply returns false since only actual
+     * implementation class has knowledge of its internal buffering
+     * state.
+     * Implementations are strongly encouraged to properly override
+     * this method, to allow efficient copying of content by other
+     * code.
+     * 
+     * @return True if parser currently has character array that can
+     *   be efficiently returned via {@link #getTextCharacters}; false
+     *   means that it may or may not exist
+     */
+    public boolean hasTextCharacters() {
+        return false;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, access to token information, numeric
+    /**********************************************************
+     */
+
+    /**
+     * Generic number value accessor method that will work for
+     * all kinds of numeric values. It will return the optimal
+     * (simplest/smallest possible) wrapper object that can
+     * express the numeric value just parsed.
+     */
+    public abstract Number getNumberValue()
+        throws IOException, JsonParseException;
+
+    /**
+     * If current token is of type 
+     * {@link JsonToken#VALUE_NUMBER_INT} or
+     * {@link JsonToken#VALUE_NUMBER_FLOAT}, returns
+     * one of {@link NumberType} constants; otherwise returns null.
+     */
+    public abstract NumberType getNumberType()
+        throws IOException, JsonParseException;
+
+    /**
+     * Numeric accessor that can be called when the current
+     * token is of type {@link JsonToken#VALUE_NUMBER_INT} and
+     * it can be expressed as a value of Java byte primitive type.
+     * It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
+     * if so, it is equivalent to calling {@link #getDoubleValue}
+     * and then casting; except for possible overflow/underflow
+     * exception.
+     *<p>
+     * Note: if the resulting integer value falls outside range of
+     * Java byte, a {@link JsonParseException}
+     * will be thrown to indicate numeric overflow/underflow.
+     */
+    public byte getByteValue()
+        throws IOException, JsonParseException
+    {
+        int value = getIntValue();
+        // So far so good: but does it fit?
+        if (value < MIN_BYTE_I || value > MAX_BYTE_I) {
+            throw _constructError("Numeric value ("+getText()+") out of range of Java byte");
+        }
+        return (byte) value;
+    }
+
+    /**
+     * Numeric accessor that can be called when the current
+     * token is of type {@link JsonToken#VALUE_NUMBER_INT} and
+     * it can be expressed as a value of Java short primitive type.
+     * It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
+     * if so, it is equivalent to calling {@link #getDoubleValue}
+     * and then casting; except for possible overflow/underflow
+     * exception.
+     *<p>
+     * Note: if the resulting integer value falls outside range of
+     * Java short, a {@link JsonParseException}
+     * will be thrown to indicate numeric overflow/underflow.
+     */
+    public short getShortValue()
+        throws IOException, JsonParseException
+    {
+        int value = getIntValue();
+        if (value < MIN_SHORT_I || value > MAX_SHORT_I) {
+            throw _constructError("Numeric value ("+getText()+") out of range of Java short");
+        }
+        return (short) value;
+    }
+
+    /**
+     * Numeric accessor that can be called when the current
+     * token is of type {@link JsonToken#VALUE_NUMBER_INT} and
+     * it can be expressed as a value of Java int primitive type.
+     * It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
+     * if so, it is equivalent to calling {@link #getDoubleValue}
+     * and then casting; except for possible overflow/underflow
+     * exception.
+     *<p>
+     * Note: if the resulting integer value falls outside range of
+     * Java int, a {@link JsonParseException}
+     * may be thrown to indicate numeric overflow/underflow.
+     */
+    public abstract int getIntValue()
+        throws IOException, JsonParseException;
+
+    /**
+     * Numeric accessor that can be called when the current
+     * token is of type {@link JsonToken#VALUE_NUMBER_INT} and
+     * it can be expressed as a Java long primitive type.
+     * It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
+     * if so, it is equivalent to calling {@link #getDoubleValue}
+     * and then casting to int; except for possible overflow/underflow
+     * exception.
+     *<p>
+     * Note: if the token is an integer, but its value falls
+     * outside of range of Java long, a {@link JsonParseException}
+     * may be thrown to indicate numeric overflow/underflow.
+     */
+    public abstract long getLongValue()
+        throws IOException, JsonParseException;
+
+    /**
+     * Numeric accessor that can be called when the current
+     * token is of type {@link JsonToken#VALUE_NUMBER_INT} and
+     * it can not be used as a Java long primitive type due to its
+     * magnitude.
+     * It can also be called for {@link JsonToken#VALUE_NUMBER_FLOAT};
+     * if so, it is equivalent to calling {@link #getDecimalValue}
+     * and then constructing a {@link BigInteger} from that value.
+     */
+    public abstract BigInteger getBigIntegerValue()
+        throws IOException, JsonParseException;
+
+    /**
+     * Numeric accessor that can be called when the current
+     * token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} and
+     * it can be expressed as a Java float primitive type.
+     * It can also be called for {@link JsonToken#VALUE_NUMBER_INT};
+     * if so, it is equivalent to calling {@link #getLongValue}
+     * and then casting; except for possible overflow/underflow
+     * exception.
+     *<p>
+     * Note: if the value falls
+     * outside of range of Java float, a {@link JsonParseException}
+     * will be thrown to indicate numeric overflow/underflow.
+     */
+    public abstract float getFloatValue()
+        throws IOException, JsonParseException;
+
+    /**
+     * Numeric accessor that can be called when the current
+     * token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} and
+     * it can be expressed as a Java double primitive type.
+     * It can also be called for {@link JsonToken#VALUE_NUMBER_INT};
+     * if so, it is equivalent to calling {@link #getLongValue}
+     * and then casting; except for possible overflow/underflow
+     * exception.
+     *<p>
+     * Note: if the value falls
+     * outside of range of Java double, a {@link JsonParseException}
+     * will be thrown to indicate numeric overflow/underflow.
+     */
+    public abstract double getDoubleValue()
+        throws IOException, JsonParseException;
+
+    /**
+     * Numeric accessor that can be called when the current
+     * token is of type {@link JsonToken#VALUE_NUMBER_FLOAT} or
+     * {@link JsonToken#VALUE_NUMBER_INT}. No under/overflow exceptions
+     * are ever thrown.
+     */
+    public abstract BigDecimal getDecimalValue()
+        throws IOException, JsonParseException;
+
+    /*
+    /**********************************************************
+    /* Public API, access to token information, other
+    /**********************************************************
+     */
+    
+    /**
+     * Convenience accessor that can be called when the current
+     * token is {@link JsonToken#VALUE_TRUE} or
+     * {@link JsonToken#VALUE_FALSE}.
+     *<p>
+     * Note: if the token is not of above-mentioned boolean types,
+ an integer, but its value falls
+     * outside of range of Java long, a {@link JsonParseException}
+     * may be thrown to indicate numeric overflow/underflow.
+     */
+    public boolean getBooleanValue()
+        throws IOException, JsonParseException
+    {
+        if (_currToken == JsonToken.VALUE_TRUE) return true;
+        if (_currToken == JsonToken.VALUE_FALSE) return false;
+        throw new JsonParseException("Current token ("+_currToken+") not of boolean type", getCurrentLocation());
+    }
+
+    /**
+     * Accessor that can be called if (and only if) the current token
+     * is {@link JsonToken#VALUE_EMBEDDED_OBJECT}. For other token types,
+     * null is returned.
+     *<p>
+     * Note: only some specialized parser implementations support
+     * embedding of objects (usually ones that are facades on top
+     * of non-streaming sources, such as object trees).
+     */
+    public Object getEmbeddedObject()
+        throws IOException, JsonParseException
+    {
+        // By default we will always return null
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, access to token information, binary
+    /**********************************************************
+     */
+
+    /**
+     * Method that can be used to read (and consume -- results
+     * may not be accessible using other methods after the call)
+     * base64-encoded binary data
+     * included in the current textual JSON value.
+     * It works similar to getting String value via {@link #getText}
+     * and decoding result (except for decoding part),
+     * but should be significantly more performant.
+     *<p>
+     * Note that non-decoded textual contents of the current token
+     * are not guaranteed to be accessible after this method
+     * is called. Current implementation, for example, clears up
+     * textual content during decoding.
+     * Decoded binary content, however, will be retained until
+     * parser is advanced to the next event.
+     *
+     * @param b64variant Expected variant of base64 encoded
+     *   content (see {@link Base64Variants} for definitions
+     *   of "standard" variants).
+     *
+     * @return Decoded binary data
+     */
+    public abstract byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException;
+
+    /**
+     * Convenience alternative to {@link #getBinaryValue(Base64Variant)}
+     * that defaults to using
+     * {@link Base64Variants#getDefaultVariant} as the default encoding.
+     */
+    public byte[] getBinaryValue() throws IOException, JsonParseException
+    {
+        return getBinaryValue(Base64Variants.getDefaultVariant());
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, access to token information, coercion/conversion
+    /**********************************************************
+     */
+    
+    /**
+     * Method that will try to convert value of current token to a
+     * <b>int</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * and 1 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an int (including structured type
+     * markers like start/end Object/Array)
+     * default value of <b>0</b> will be returned; no exceptions are thrown.
+     */
+    public int getValueAsInt() throws IOException, JsonParseException {
+        return getValueAsInt(0);
+    }
+    
+    /**
+     * Method that will try to convert value of current token to a
+     * <b>int</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * and 1 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an int (including structured type
+     * markers like start/end Object/Array)
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public int getValueAsInt(int defaultValue) throws IOException, JsonParseException {
+        return defaultValue;
+    }
+
+    /**
+     * Method that will try to convert value of current token to a
+     * <b>long</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * and 1 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an int (including structured type
+     * markers like start/end Object/Array)
+     * default value of <b>0</b> will be returned; no exceptions are thrown.
+     */
+    public long getValueAsLong() throws IOException, JsonParseException {
+        return getValueAsInt(0);
+    }
+    
+    /**
+     * Method that will try to convert value of current token to a
+     * <b>long</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0 (false)
+     * and 1 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an int (including structured type
+     * markers like start/end Object/Array)
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public long getValueAsLong(long defaultValue) throws IOException, JsonParseException {
+        return defaultValue;
+    }
+    
+    /**
+     * Method that will try to convert value of current token to a Java
+     * <b>double</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0.0 (false)
+     * and 1.0 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an int (including structured types
+     * like Objects and Arrays),
+     * default value of <b>0.0</b> will be returned; no exceptions are thrown.
+     */
+    public double getValueAsDouble() throws IOException, JsonParseException {
+        return getValueAsDouble(0.0);
+    }
+    
+    /**
+     * Method that will try to convert value of current token to a
+     * Java <b>double</b>.
+     * Numbers are coerced using default Java rules; booleans convert to 0.0 (false)
+     * and 1.0 (true), and Strings are parsed using default Java language integer
+     * parsing rules.
+     *<p>
+     * If representation can not be converted to an int (including structured types
+     * like Objects and Arrays),
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public double getValueAsDouble(double defaultValue) throws IOException, JsonParseException {
+        return defaultValue;
+    }
+
+    /**
+     * Method that will try to convert value of current token to a
+     * <b>boolean</b>.
+     * JSON booleans map naturally; integer numbers other than 0 map to true, and
+     * 0 maps to false
+     * and Strings 'true' and 'false' map to corresponding values.
+     *<p>
+     * If representation can not be converted to a boolean value (including structured types
+     * like Objects and Arrays),
+     * default value of <b>false</b> will be returned; no exceptions are thrown.
+     */
+    public boolean getValueAsBoolean() throws IOException, JsonParseException {
+        return getValueAsBoolean(false);
+    }
+
+    /**
+     * Method that will try to convert value of current token to a
+     * <b>boolean</b>.
+     * JSON booleans map naturally; integer numbers other than 0 map to true, and
+     * 0 maps to false
+     * and Strings 'true' and 'false' map to corresponding values.
+     *<p>
+     * If representation can not be converted to a boolean value (including structured types
+     * like Objects and Arrays),
+     * specified <b>defaultValue</b> will be returned; no exceptions are thrown.
+     */
+    public boolean getValueAsBoolean(boolean defaultValue) throws IOException, JsonParseException {
+        return defaultValue;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, optional data binding functionality
+    /**********************************************************
+     */
+
+    /**
+     * Method to deserialize JSON content into a non-container
+     * type (it can be an array type, however): typically a bean, array
+     * or a wrapper type (like {@link java.lang.Boolean}).
+     * <b>Note</b>: method can only be called if the parser has
+     * an object codec assigned; this is true for parsers constructed
+     * by {@link org.codehaus.jackson.map.MappingJsonFactory} but
+     * not for {@link JsonFactory} (unless its <code>setCodec</code>
+     * method has been explicitly called).
+     *<p>
+     * This method may advance the event stream, for structured types
+     * the current token will be the closing end marker (END_ARRAY,
+     * END_OBJECT) of the bound structure. For non-structured Json types
+     * (and for {@link JsonToken#VALUE_EMBEDDED_OBJECT})
+     * stream is not advanced.
+     *<p>
+     * Note: this method should NOT be used if the result type is a
+     * container ({@link java.util.Collection} or {@link java.util.Map}.
+     * The reason is that due to type erasure, key and value types
+     * can not be introspected when using this method.
+     */
+    public <T> T readValueAs(Class<T> valueType)
+        throws IOException, JsonProcessingException
+    {
+        ObjectCodec codec = getCodec();
+        if (codec == null) {
+            throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects");
+        }
+        return codec.readValue(this, valueType);
+    }
+
+    /**
+     * Method to deserialize JSON content into a Java type, reference
+     * to which is passed as argument. Type is passed using so-called
+     * "super type token"
+     * and specifically needs to be used if the root type is a 
+     * parameterized (generic) container type.
+     * <b>Note</b>: method can only be called if the parser has
+     * an object codec assigned; this is true for parsers constructed
+     * by {@link org.codehaus.jackson.map.MappingJsonFactory} but
+     * not for {@link JsonFactory} (unless its <code>setCodec</code>
+     * method has been explicitly called).
+     *<p>
+     * This method may advance the event stream, for structured types
+     * the current token will be the closing end marker (END_ARRAY,
+     * END_OBJECT) of the bound structure. For non-structured Json types
+     * (and for {@link JsonToken#VALUE_EMBEDDED_OBJECT})
+     * stream is not advanced.
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T readValueAs(TypeReference<?> valueTypeRef)
+        throws IOException, JsonProcessingException
+    {
+        ObjectCodec codec = getCodec();
+        if (codec == null) {
+            throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects");
+        }
+        /* Ugh. Stupid Java type erasure... can't just chain call,s
+         * must cast here also.
+         */
+        return (T) codec.readValue(this, valueTypeRef);
+    }
+
+    /**
+     * Method for reading sequence of Objects from parser stream,
+     * all with same specified value type.
+     */
+    public <T> Iterator<T> readValuesAs(Class<T> valueType)
+        throws IOException, JsonProcessingException
+    {
+        ObjectCodec codec = getCodec();
+        if (codec == null) {
+            throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects");
+        }
+        return codec.readValues(this, valueType);
+    }
+
+    /**
+     * Method for reading sequence of Objects from parser stream,
+     * all with same specified value type.
+     */
+    public <T> Iterator<T> readValuesAs(TypeReference<?> valueTypeRef)
+        throws IOException, JsonProcessingException
+    {
+        ObjectCodec codec = getCodec();
+        if (codec == null) {
+            throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into Java objects");
+        }
+        return codec.readValues(this, valueTypeRef);
+    }
+    
+    /**
+     * Method to deserialize JSON content into equivalent "tree model",
+     * represented by root {@link JsonNode} of resulting model.
+     * For JSON Arrays it will an array node (with child nodes),
+     * for objects object node (with child nodes), and for other types
+     * matching leaf node type
+     */
+    public JsonNode readValueAsTree()
+        throws IOException, JsonProcessingException
+    {
+        ObjectCodec codec = getCodec();
+        if (codec == null) {
+            throw new IllegalStateException("No ObjectCodec defined for the parser, can not deserialize JSON into JsonNode tree");
+        }
+        return codec.readTree(this);
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    /**
+     * Helper method for constructing {@link JsonParseException}s
+     * based on current state of the parser
+     */
+    protected JsonParseException _constructError(String msg)
+    {
+        return new JsonParseException(msg, getCurrentLocation());
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java b/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java
new file mode 100644
index 0000000..5cf21cb
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonProcessingException.java
@@ -0,0 +1,80 @@
+package com.fasterxml.jackson.core;
+
+/**
+ * Intermediate base class for all problems encountered when
+ * processing (parsing, generating) JSON content
+ * that are not pure I/O problems.
+ * Regular {@link java.io.IOException}s will be passed through as is.
+ * Sub-class of {@link java.io.IOException} for convenience.
+ */
+public class JsonProcessingException
+    extends java.io.IOException
+{
+    final static long serialVersionUID = 123; // Stupid eclipse...
+	
+    protected JsonLocation mLocation;
+
+    protected JsonProcessingException(String msg, JsonLocation loc, Throwable rootCause)
+    {
+        /* Argh. IOException(Throwable,String) is only available starting
+         * with JDK 1.6...
+         */
+        super(msg);
+        if (rootCause != null) {
+            initCause(rootCause);
+        }
+        mLocation = loc;
+    }
+
+    protected JsonProcessingException(String msg)
+    {
+        super(msg);
+    }
+
+    protected JsonProcessingException(String msg, JsonLocation loc)
+    {
+        this(msg, loc, null);
+    }
+
+    protected JsonProcessingException(String msg, Throwable rootCause)
+    {
+        this(msg, null, rootCause);
+    }
+
+    protected JsonProcessingException(Throwable rootCause)
+    {
+        this(null, null, rootCause);
+    }
+
+    public JsonLocation getLocation()
+    {
+        return mLocation;
+    }
+
+    /**
+     * Default method overridden so that we can add location information
+     */
+    @Override
+    public String getMessage()
+    {
+        String msg = super.getMessage();
+        if (msg == null) {
+            msg = "N/A";
+        }
+        JsonLocation loc = getLocation();
+        if (loc != null) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(msg);
+            sb.append('\n');
+            sb.append(" at ");
+            sb.append(loc.toString());
+            return sb.toString();
+        }
+        return msg;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getName()+": "+getMessage();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java b/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java
new file mode 100644
index 0000000..118f899
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonStreamContext.java
@@ -0,0 +1,122 @@
+/* Jackson JSON-processor.
+ *
+ * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
+ *
+ * Licensed under the License specified in file LICENSE, included with
+ * the source code and binary code bundles.
+ * You may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fasterxml.jackson.core;
+
+/**
+ * Shared base class for streaming processing contexts used during
+ * reading and writing of Json content using Streaming API.
+ * This context is also exposed to applications:
+ * context object can be used by applications to get an idea of
+ * relative position of the parser/generator within json content
+ * being processed. This allows for some contextual processing: for
+ * example, output within Array context can differ from that of
+ * Object context.
+ */
+public abstract class JsonStreamContext
+{
+    // // // Type constants used internally
+
+    protected final static int TYPE_ROOT = 0;
+    protected final static int TYPE_ARRAY = 1;
+    protected final static int TYPE_OBJECT = 2;
+
+    protected int _type;
+
+    /**
+     * Index of the currently processed entry. Starts with -1 to signal
+     * that no entries have been started, and gets advanced each
+     * time a new entry is started, either by encountering an expected
+     * separator, or with new values if no separators are expected
+     * (the case for root context).
+     */
+    protected int _index;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected JsonStreamContext() { }
+
+    /*
+    /**********************************************************
+    /* Public API, accessors
+    /**********************************************************
+     */
+
+    /**
+     * Accessor for finding parent context of this context; will
+     * return null for root context.
+     */
+    public abstract JsonStreamContext getParent();
+
+    /**
+     * Method that returns true if this context is an Array context;
+     * that is, content is being read from or written to a Json Array.
+     */
+    public final boolean inArray() { return _type == TYPE_ARRAY; }
+
+    /**
+     * Method that returns true if this context is a Root context;
+     * that is, content is being read from or written to without
+     * enclosing array or object structure.
+     */
+    public final boolean inRoot() { return _type == TYPE_ROOT; }
+
+    /**
+     * Method that returns true if this context is an Object context;
+     * that is, content is being read from or written to a Json Object.
+     */
+    public final boolean inObject() { return _type == TYPE_OBJECT; }
+
+    /**
+     * Method for accessing simple type description of current context;
+     * either ROOT (for root-level values), OBJECT (for field names and
+     * values of JSON Objects) or ARRAY (for values of JSON Arrays)
+     */
+    public final String getTypeDesc() {
+        switch (_type) {
+        case TYPE_ROOT: return "ROOT";
+        case TYPE_ARRAY: return "ARRAY";
+        case TYPE_OBJECT: return "OBJECT";
+        }
+        return "?";
+    }
+
+    /**
+     * @return Number of entries that are complete and started.
+     */
+    public final int getEntryCount()
+    {
+        return _index + 1;
+    }
+
+    /**
+     * @return Index of the currently processed entry, if any
+     */
+    public final int getCurrentIndex()
+    {
+        return (_index < 0) ? 0 : _index;
+    }
+
+    /**
+     * Method for accessing name associated with the current location.
+     * Non-null for <code>FIELD_NAME</code> and value events that directly
+     * follow field names; null for root level and array values.
+     */
+    public abstract String getCurrentName();
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonToken.java b/src/main/java/com/fasterxml/jackson/core/JsonToken.java
new file mode 100644
index 0000000..3859b15
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/JsonToken.java
@@ -0,0 +1,161 @@
+package com.fasterxml.jackson.core;
+
+/**
+ * Enumeration for basic token types used for returning results
+ * of parsing JSON content.
+ */
+public enum JsonToken
+{
+    /* Some notes on implementation:
+     *
+     * - Entries are to be ordered such that start/end array/object
+     *   markers come first, then field name marker (if any), and
+     *   finally scalar value tokens. This is assumed by some
+     *   typing checks.
+     */
+
+    /**
+     * NOT_AVAILABLE can be returned if {@link JsonParser}
+     * implementation can not currently return the requested
+     * token (usually next one), or even if any will be
+     * available, but that may be able to determine this in
+     * future. This is the case with non-blocking parsers --
+     * they can not block to wait for more data to parse and
+     * must return something.
+     *
+     * @since 0.9.7
+     */
+    NOT_AVAILABLE(null),
+
+    /**
+     * START_OBJECT is returned when encountering '{'
+     * which signals starting of an Object value.
+     */
+    START_OBJECT("{"),
+        
+    /**
+     * START_OBJECT is returned when encountering '}'
+     * which signals ending of an Object value
+     */
+    END_OBJECT("}"),
+        
+    /**
+     * START_OBJECT is returned when encountering '['
+     * which signals starting of an Array value
+     */
+    START_ARRAY("["),
+
+    /**
+     * START_OBJECT is returned when encountering ']'
+     * which signals ending of an Array value
+     */
+    END_ARRAY("]"),
+        
+    /**
+     * FIELD_NAME is returned when a String token is encountered
+     * as a field name (same lexical value, different function)
+     */
+    FIELD_NAME(null),
+        
+    /**
+     * Placeholder token returned when the input source has a concept
+     * of embedded Object that are not accessible as usual structure
+     * (of starting with {@link #START_OBJECT}, having values, ending with
+     * {@link #END_OBJECT}), but as "raw" objects.
+     *<p>
+     * Note: this token is never returned by regular JSON readers, but
+     * only by readers that expose other kinds of source (like
+     * {@link JsonNode}-based JSON trees, Maps, Lists and such).
+     *
+     * @since 1.1
+     */
+    VALUE_EMBEDDED_OBJECT(null),
+
+    /**
+     * VALUE_STRING is returned when a String token is encountered
+     * in value context (array element, field value, or root-level
+     * stand-alone value)
+     */
+    VALUE_STRING(null),
+
+    /**
+     * VALUE_NUMBER_INT is returned when an integer numeric token is
+     * encountered in value context: that is, a number that does
+     * not have floating point or exponent marker in it (consists
+     * only of an optional sign, followed by one or more digits)
+     */
+    VALUE_NUMBER_INT(null),
+
+    /**
+     * VALUE_NUMBER_INT is returned when a numeric token other
+     * that is not an integer is encountered: that is, a number that does
+     * have floating point or exponent marker in it, in addition
+     * to one or more digits.
+     */
+    VALUE_NUMBER_FLOAT(null),
+
+    /**
+     * VALUE_TRUE is returned when encountering literal "true" in
+     * value context
+     */
+    VALUE_TRUE("true"),
+
+    /**
+     * VALUE_FALSE is returned when encountering literal "false" in
+     * value context
+     */
+    VALUE_FALSE("false"),
+
+    /**
+     * VALUE_NULL is returned when encountering literal "null" in
+     * value context
+     */
+    VALUE_NULL("null")
+        ;
+
+    final String _serialized;
+
+    final char[] _serializedChars;
+
+    final byte[] _serializedBytes;
+
+    /**
+     * @param Textual representation for this token, if there is a
+     *   single static representation; null otherwise
+     */
+    JsonToken(String token)
+    {
+        if (token == null) {
+            _serialized = null;
+            _serializedChars = null;
+            _serializedBytes = null;
+        } else {
+            _serialized = token;
+            _serializedChars = token.toCharArray();
+            // It's all in ascii, can just case...
+            int len = _serializedChars.length;
+            _serializedBytes = new byte[len];
+            for (int i = 0; i < len; ++i) {
+                _serializedBytes[i] = (byte) _serializedChars[i];
+            }
+        }
+    }
+
+    public String asString() { return _serialized; }
+    public char[] asCharArray() { return _serializedChars; }
+    public byte[] asByteArray() { return _serializedBytes; }
+
+    public boolean isNumeric() {
+        return (this == VALUE_NUMBER_INT) || (this == VALUE_NUMBER_FLOAT);
+    }
+
+    /**
+     * Method that can be used to check whether this token represents
+     * a valid non-structured value. This means all tokens other than
+     * Object/Array start/end markers all field names.
+     */
+    public boolean isScalarValue() {
+        // note: up to 1.5, VALUE_EMBEDDED_OBJECT was incorrectly considered non-scalar!
+        return ordinal() >= VALUE_EMBEDDED_OBJECT.ordinal();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/ObjectCodec.java b/src/main/java/com/fasterxml/jackson/core/ObjectCodec.java
new file mode 100644
index 0000000..1b3507a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/ObjectCodec.java
@@ -0,0 +1,157 @@
+package com.fasterxml.jackson.core;
+
+import java.io.IOException;
+import java.util.Iterator;
+
+import com.fasterxml.jackson.core.type.JavaType;
+import com.fasterxml.jackson.core.type.TypeReference;
+
+/**
+ * Abstract class that defines the interface that {@link JsonParser} and
+ * {@link JsonGenerator} use to serialize and deserialize regular
+ * Java objects (POJOs aka Beans).
+ *<p>
+ * The standard implementation of this class is
+ * {@link org.codehaus.jackson.map.ObjectMapper}.
+ */
+public abstract class ObjectCodec
+{
+    protected ObjectCodec() { }
+
+    /*
+    /**********************************************************
+    /* API for de-serialization (JSON-to-Object)
+    /**********************************************************
+     */
+
+    /**
+     * Method to deserialize JSON content into a non-container
+     * type (it can be an array type, however): typically a bean, array
+     * or a wrapper type (like {@link java.lang.Boolean}).
+     *<p>
+     * Note: this method should NOT be used if the result type is a
+     * container ({@link java.util.Collection} or {@link java.util.Map}.
+     * The reason is that due to type erasure, key and value types
+     * can not be introspected when using this method.
+     */
+    public abstract <T> T readValue(JsonParser jp, Class<T> valueType)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method to deserialize JSON content into a Java type, reference
+     * to which is passed as argument. Type is passed using so-called
+     * "super type token" 
+     * and specifically needs to be used if the root type is a 
+     * parameterized (generic) container type.
+     */
+    public abstract <T> T readValue(JsonParser jp, TypeReference<?> valueTypeRef)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method to deserialize JSON content as tree expressed
+     * using set of {@link JsonNode} instances. Returns
+     * root of the resulting tree (where root can consist
+     * of just a single node if the current event is a
+     * value event, not container).
+     */
+    public abstract <T> T readValue(JsonParser jp, JavaType valueType)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method to deserialize JSON content as tree expressed
+     * using set of {@link JsonNode} instances. Returns
+     * root of the resulting tree (where root can consist
+     * of just a single node if the current event is a
+     * value event, not container).
+     */
+    public abstract JsonNode readTree(JsonParser jp)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method for reading sequence of Objects from parser stream,
+     * all with same specified value type.
+     * 
+     * @since 1.9
+     */
+    public abstract <T> Iterator<T> readValues(JsonParser jp, Class<T> valueType)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method for reading sequence of Objects from parser stream,
+     * all with same specified value type.
+     * 
+     * @since 1.9
+     */
+    public abstract <T> Iterator<T> readValues(JsonParser jp, TypeReference<?> valueTypeRef)
+        throws IOException, JsonProcessingException;
+    
+    /**
+     * Method for reading sequence of Objects from parser stream,
+     * all with same specified value type.
+     * 
+     * @since 1.9
+     */
+    public abstract <T> Iterator<T> readValues(JsonParser jp, JavaType valueType)
+        throws IOException, JsonProcessingException;
+    
+    /*
+    /**********************************************************
+    /* API for serialization (Object-to-JSON)
+    /**********************************************************
+     */
+
+    /**
+     * Method to serialize given Java Object, using generator
+     * provided.
+     */
+    public abstract void writeValue(JsonGenerator jgen, Object value)
+        throws IOException, JsonProcessingException;
+
+    /**
+     * Method to serialize given Json Tree, using generator
+     * provided.
+     */
+    public abstract void writeTree(JsonGenerator jgen, JsonNode rootNode)
+        throws IOException, JsonProcessingException;
+
+    /*
+    /**********************************************************
+    /* API for Tree Model handling
+    /**********************************************************
+     */
+
+    /**
+     * Method for construct root level Object nodes
+     * for Tree Model instances.
+     *
+     * @since 1.2
+     */
+    public abstract JsonNode createObjectNode();
+
+    /**
+     * Method for construct root level Array nodes
+     * for Tree Model instances.
+     *
+     * @since 1.2
+     */
+    public abstract JsonNode createArrayNode();
+
+    /**
+     * Method for constructing a {@link JsonParser} for reading
+     * contents of a JSON tree, as if it was external serialized
+     * JSON content.
+     *
+     * @since 1.3
+     */
+    public abstract JsonParser treeAsTokens(JsonNode n);
+
+    /**
+     * Convenience method for converting given JSON tree into instance of specified
+     * value type. This is equivalent to first constructing a {@link JsonParser} to
+     * iterate over contents of the tree, and using that parser for data binding.
+     * 
+     * @since 1.3
+     */
+    public abstract <T> T treeToValue(JsonNode n, Class<T> valueType)
+        throws IOException, JsonProcessingException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/PrettyPrinter.java b/src/main/java/com/fasterxml/jackson/core/PrettyPrinter.java
new file mode 100644
index 0000000..710b1b9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/PrettyPrinter.java
@@ -0,0 +1,166 @@
+package com.fasterxml.jackson.core;
+
+import java.io.IOException;
+
+/**
+ * Interface for objects that implement pretty printer functionality, such
+ * as indentation.
+ * Pretty printers are used to add white space in output JSON content,
+ * to make results more human readable. Usually this means things like adding
+ * linefeeds and indentation.
+ */
+public interface PrettyPrinter
+{
+    /*
+    /**********************************************************
+    /* First methods that act both as events, and expect
+    /* output for correct functioning (i.e something gets
+    /* output even when not pretty-printing)
+    /**********************************************************
+     */
+
+    // // // Root-level handling:
+
+    /**
+     * Method called after a root-level value has been completely
+     * output, and before another value is to be output.
+     *<p>
+     * Default
+     * handling (without pretty-printing) will output a space, to
+     * allow values to be parsed correctly. Pretty-printer is
+     * to output some other suitable and nice-looking separator
+     * (tab(s), space(s), linefeed(s) or any combination thereof).
+     */
+    public void writeRootValueSeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException;
+
+    // // Object handling
+
+    /**
+     * Method called when an Object value is to be output, before
+     * any fields are output.
+     *<p>
+     * Default handling (without pretty-printing) will output
+     * the opening curly bracket.
+     * Pretty-printer is
+     * to output a curly bracket as well, but can surround that
+     * with other (white-space) decoration.
+     */
+    public void writeStartObject(JsonGenerator jg)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method called after an Object value has been completely output
+     * (minus closing curly bracket).
+     *<p>
+     * Default handling (without pretty-printing) will output
+     * the closing curly bracket.
+     * Pretty-printer is
+     * to output a curly bracket as well, but can surround that
+     * with other (white-space) decoration.
+     *
+     * @param nrOfEntries Number of direct members of the array that
+     *   have been output
+     */
+    public void writeEndObject(JsonGenerator jg, int nrOfEntries)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method called after an object entry (field:value) has been completely
+     * output, and before another value is to be output.
+     *<p>
+     * Default handling (without pretty-printing) will output a single
+     * comma to separate the two. Pretty-printer is
+     * to output a comma as well, but can surround that with other
+     * (white-space) decoration.
+     */
+    public void writeObjectEntrySeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method called after an object field has been output, but
+     * before the value is output.
+     *<p>
+     * Default handling (without pretty-printing) will output a single
+     * colon to separate the two. Pretty-printer is
+     * to output a colon as well, but can surround that with other
+     * (white-space) decoration.
+     */
+    public void writeObjectFieldValueSeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException;
+
+    // // // Array handling
+
+    /**
+     * Method called when an Array value is to be output, before
+     * any member/child values are output.
+     *<p>
+     * Default handling (without pretty-printing) will output
+     * the opening bracket.
+     * Pretty-printer is
+     * to output a bracket as well, but can surround that
+     * with other (white-space) decoration.
+     */
+    public void writeStartArray(JsonGenerator jg)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method called after an Array value has been completely output
+     * (minus closing bracket).
+     *<p>
+     * Default handling (without pretty-printing) will output
+     * the closing bracket.
+     * Pretty-printer is
+     * to output a bracket as well, but can surround that
+     * with other (white-space) decoration.
+     *
+     * @param nrOfValues Number of direct members of the array that
+     *   have been output
+     */
+    public void writeEndArray(JsonGenerator jg, int nrOfValues)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method called after an array value has been completely
+     * output, and before another value is to be output.
+     *<p>
+     * Default handling (without pretty-printing) will output a single
+     * comma to separate the two. Pretty-printer is
+     * to output a comma as well, but can surround that with other
+     * (white-space) decoration.
+     */
+    public void writeArrayValueSeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException;
+
+    /*
+    /**********************************************************
+    /* Then events that by default do not produce any output
+    /* but that are often overridden to add white space
+    /* in pretty-printing mode
+    /**********************************************************
+     */
+
+    /**
+     * Method called after array start marker has been output,
+     * and right before the first value is to be output.
+     * It is <b>not</b> called for arrays with no values.
+     *<p>
+     * Default handling does not output anything, but pretty-printer
+     * is free to add any white space decoration.
+     */
+    public void beforeArrayValues(JsonGenerator jg)
+        throws IOException, JsonGenerationException;
+
+    /**
+     * Method called after object start marker has been output,
+     * and right before the field name of the first entry is
+     * to be output.
+     * It is <b>not</b> called for objects without entries.
+     *<p>
+     * Default handling does not output anything, but pretty-printer
+     * is free to add any white space decoration.
+     */
+    public void beforeObjectEntries(JsonGenerator jg)
+        throws IOException, JsonGenerationException;
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/core/SerializableString.java b/src/main/java/com/fasterxml/jackson/core/SerializableString.java
new file mode 100644
index 0000000..db0c5b3
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/SerializableString.java
@@ -0,0 +1,54 @@
+package com.fasterxml.jackson.core;
+
+/**
+ * Interface that defines how Jackson package can interact with efficient
+ * pre-serialized or lazily-serialized and reused String representations.
+ * Typically implementations store possible serialized version(s) so that
+ * serialization of String can be done more efficiently, especially when
+ * used multiple times.
+ *
+ * @since 1.7 (1.6 introduced implementation, but interface extracted later)
+ * 
+ * @see com.fasterxml.jackson.core.io.SerializedString
+ */
+public interface SerializableString
+{
+    /**
+     * Returns unquoted String that this object represents (and offers
+     * serialized forms for)
+     */
+    public String getValue();
+    
+    /**
+     * Returns length of the (unquoted) String as characters.
+     * Functionally equvalent to:
+     *<pre>
+     *   getValue().length();
+     *</pre>
+     */
+    public int charLength();
+
+    /**
+     * Returns JSON quoted form of the String, as character array. Result
+     * can be embedded as-is in textual JSON as property name or JSON String.
+     */
+    public char[] asQuotedChars();
+
+    /**
+     * Returns UTF-8 encoded version of unquoted String.
+     * Functionally equivalent to (but more efficient than):
+     *<pre>
+     * getValue().getBytes("UTF-8");
+     *</pre>
+     */
+    public byte[] asUnquotedUTF8();
+
+    /**
+     * Returns UTF-8 encoded version of JSON-quoted String.
+     * Functionally equivalent to (but more efficient than):
+     *<pre>
+     * new String(asQuotedChars()).getBytes("UTF-8");
+     *</pre>
+     */
+    public byte[] asQuotedUTF8();
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/Version.java b/src/main/java/com/fasterxml/jackson/core/Version.java
new file mode 100644
index 0000000..eb8b852
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/Version.java
@@ -0,0 +1,90 @@
+package com.fasterxml.jackson.core;
+
+/**
+ * Object that encapsulates version information of a component,
+ * and is return by {@link Versioned#version}.
+ * 
+ * @since 1.6
+ */
+public class Version
+    implements Comparable<Version>
+{
+    private final static Version UNKNOWN_VERSION = new Version(0, 0, 0, null);
+
+    protected final int _majorVersion;
+
+    protected final int _minorVersion;
+
+    protected final int _patchLevel;
+
+    /**
+     * Additional information for snapshot versions; null for non-snapshot
+     * (release) versions.
+     */
+    protected final String _snapshotInfo;
+    
+    public Version(int major, int minor, int patchLevel,
+            String snapshotInfo)
+    {
+        _majorVersion = major;
+        _minorVersion = minor;
+        _patchLevel = patchLevel;
+        _snapshotInfo = snapshotInfo;
+    }
+
+    /**
+     * Method returns canonical "not known" version, which is used as version
+     * in cases where actual version information is not known (instead of null).
+     */
+    public static Version unknownVersion() { return UNKNOWN_VERSION; }
+
+    public boolean isUknownVersion() { return (this == UNKNOWN_VERSION); }
+    public boolean isSnapshot() { return (_snapshotInfo != null && _snapshotInfo.length() > 0); }
+    
+    public int getMajorVersion() { return _majorVersion; }
+    public int getMinorVersion() { return _minorVersion; }
+    public int getPatchLevel() { return _patchLevel; }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append(_majorVersion).append('.');
+        sb.append(_minorVersion).append('.');
+        sb.append(_patchLevel);
+        if (isSnapshot()) {
+            sb.append('-').append(_snapshotInfo);
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return _majorVersion + _minorVersion + _patchLevel;
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null) return false;
+        if (o.getClass() != getClass()) return false;
+        Version other = (Version) o;
+        return (other._majorVersion == _majorVersion)
+            && (other._minorVersion == _minorVersion)
+            && (other._patchLevel == _patchLevel);
+    }
+
+    @Override
+    public int compareTo(Version other)
+    {
+        int diff = _majorVersion - other._majorVersion;
+        if (diff == 0) {
+            diff = _minorVersion - other._minorVersion;
+            if (diff == 0) {
+                diff = _patchLevel - other._patchLevel;
+            }
+        }
+        return diff;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/Versioned.java b/src/main/java/com/fasterxml/jackson/core/Versioned.java
new file mode 100644
index 0000000..46cb0c2
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/Versioned.java
@@ -0,0 +1,20 @@
+package com.fasterxml.jackson.core;
+
+/**
+ * Interface that those Jackson components that are explicitly versioned will implement.
+ * Intention is to allow both plug-in components (custom extensions) and applications and
+ * frameworks that use Jackson to detect exact version of Jackson in use.
+ * This may be useful for example for ensuring that proper Jackson version is deployed
+ * (beyond mechanisms that deployment system may have), as well as for possible
+ * workarounds.
+ * 
+ * @since 1.6
+ */
+public interface Versioned {
+    /**
+     * Method called to detect version of the component that implements this interface;
+     * returned version should never be null, but may return specific "not available"
+     * instance (see {@link Version} for details).
+     */
+    public Version version();
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java b/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
new file mode 100644
index 0000000..b3c5c50
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
@@ -0,0 +1,491 @@
+package com.fasterxml.jackson.core.base;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.json.JsonWriteContext;
+import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
+import com.fasterxml.jackson.core.util.VersionUtil;
+
+/**
+ * This base class implements part of API that a JSON generator exposes
+ * to applications, adds shared internal methods that sub-classes
+ * can use and adds some abstract methods sub-classes must implement.
+ */
+public abstract class GeneratorBase
+    extends JsonGenerator
+{
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    protected ObjectCodec _objectCodec;
+
+    /**
+     * Bit flag composed of bits that indicate which
+     * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s
+     * are enabled.
+     */
+    protected int _features;
+
+    /**
+     * Flag set to indicate that implicit conversion from number
+     * to JSON String is needed (as per
+     * {@link com.fasterxml.jackson.core.JsonGenerator.Feature#WRITE_NUMBERS_AS_STRINGS}).
+     */
+    protected boolean _cfgNumbersAsStrings;
+    
+    /*
+    /**********************************************************
+    /* State
+    /**********************************************************
+     */
+
+    /**
+     * Object that keeps track of the current contextual state
+     * of the generator.
+     */
+    protected JsonWriteContext _writeContext;
+
+    /**
+     * Flag that indicates whether generator is closed or not. Gets
+     * set when it is closed by an explicit call
+     * ({@link #close}).
+     */
+    protected boolean _closed;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected GeneratorBase(int features, ObjectCodec codec)
+    {
+        super();
+        _features = features;
+        _writeContext = JsonWriteContext.createRootContext();
+        _objectCodec = codec;
+        _cfgNumbersAsStrings = isEnabled(Feature.WRITE_NUMBERS_AS_STRINGS);
+    }
+
+    @Override
+    public Version version() {
+        return VersionUtil.versionFor(getClass());
+    }
+    
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    @Override
+    public JsonGenerator enable(Feature f) {
+        _features |= f.getMask();
+        if (f == Feature.WRITE_NUMBERS_AS_STRINGS) {
+            _cfgNumbersAsStrings = true;
+        } else if (f == Feature.ESCAPE_NON_ASCII) {
+            setHighestNonEscapedChar(127);
+        }
+        return this;
+    }
+
+    @Override
+    public JsonGenerator disable(Feature f) {
+        _features &= ~f.getMask();
+        if (f == Feature.WRITE_NUMBERS_AS_STRINGS) {
+            _cfgNumbersAsStrings = false;
+        } else if (f == Feature.ESCAPE_NON_ASCII) {
+            setHighestNonEscapedChar(0);
+        }
+        return this;
+    }
+
+    //public JsonGenerator configure(Feature f, boolean state) { }
+
+    @Override
+    public final boolean isEnabled(Feature f) {
+        return (_features & f.getMask()) != 0;
+    }
+
+    @Override
+    public JsonGenerator useDefaultPrettyPrinter() {
+        return setPrettyPrinter(new DefaultPrettyPrinter());
+    }
+    
+    @Override
+    public JsonGenerator setCodec(ObjectCodec oc) {
+        _objectCodec = oc;
+        return this;
+    }
+
+    @Override
+    public final ObjectCodec getCodec() { return _objectCodec; }
+
+    /*
+    /**********************************************************
+    /* Public API, accessors
+    /**********************************************************
+     */
+
+    /**
+     * Note: co-variant return type.
+     */
+    @Override
+    public final JsonWriteContext getOutputContext() { return _writeContext; }
+
+    /*
+    /**********************************************************
+    /* Public API, write methods, structural
+    /**********************************************************
+     */
+
+    //public void writeStartArray() throws IOException, JsonGenerationException
+    //public void writeEndArray() throws IOException, JsonGenerationException
+    //public void writeStartObject() throws IOException, JsonGenerationException
+    //public void writeEndObject() throws IOException, JsonGenerationException
+
+    /*
+    /**********************************************************
+    /* Public API, write methods, textual
+    /**********************************************************
+     */
+
+    //public abstract void writeString(String text) throws IOException, JsonGenerationException;
+
+    //public abstract void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException;
+
+    //public abstract void writeRaw(String text) throws IOException, JsonGenerationException;
+
+    //public abstract void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException;
+
+    @Override
+    public void writeRawValue(String text)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write raw value");
+        writeRaw(text);
+    }
+
+    @Override
+    public void writeRawValue(String text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write raw value");
+        writeRaw(text, offset, len);
+    }
+
+    @Override
+    public void writeRawValue(char[] text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write raw value");
+        writeRaw(text, offset, len);
+    }
+    
+    //public abstract void writeBinary(byte[] data, int offset, int len, boolean includeLFs) throws IOException, JsonGenerationException;
+
+    
+    /*
+    /**********************************************************
+    /* Public API, write methods, primitive
+    /**********************************************************
+     */
+
+    // Not implemented at this level, added as placeholders
+
+     /*
+    public abstract void writeNumber(int i)
+    public abstract void writeNumber(long l)
+    public abstract void writeNumber(double d)
+    public abstract void writeNumber(float f)
+    public abstract void writeNumber(BigDecimal dec)
+    public abstract void writeBoolean(boolean state)
+    public abstract void writeNull()
+    */
+
+    /*
+    /**********************************************************
+    /* Public API, write methods, POJOs, trees
+    /**********************************************************
+     */
+
+    @Override
+    public void writeObject(Object value)
+        throws IOException, JsonProcessingException
+    {
+        if (value == null) {
+            // important: call method that does check value write:
+            writeNull();
+        } else {
+            /* 02-Mar-2009, tatu: we are NOT to call _verifyValueWrite here,
+             *   because that will be done when codec actually serializes
+             *   contained POJO. If we did call it it would advance state
+             *   causing exception later on
+             */
+            if (_objectCodec != null) {
+                _objectCodec.writeValue(this, value);
+                return;
+            }
+            _writeSimpleObject(value);
+        }
+    }
+
+    @Override
+    public void writeTree(JsonNode rootNode)
+        throws IOException, JsonProcessingException
+    {
+        // As with 'writeObject()', we are not check if write would work
+        if (rootNode == null) {
+            writeNull();
+        } else {
+            if (_objectCodec == null) {
+                throw new IllegalStateException("No ObjectCodec defined for the generator, can not serialize JsonNode-based trees");
+            }
+            _objectCodec.writeTree(this, rootNode);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, low-level output handling
+    /**********************************************************
+     */
+
+    @Override
+    public abstract void flush() throws IOException;
+
+    @Override
+    public void close() throws IOException
+    {
+        _closed = true;
+    }
+
+    @Override
+    public boolean isClosed() { return _closed; }
+
+    /*
+    /**********************************************************
+    /* Public API, copy-through methods
+    /**********************************************************
+     */
+
+    @Override
+    public final void copyCurrentEvent(JsonParser jp)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+        // sanity check; what to do?
+        if (t == null) {
+            _reportError("No current event to copy");
+        }
+        switch(t) {
+        case START_OBJECT:
+            writeStartObject();
+            break;
+        case END_OBJECT:
+            writeEndObject();
+            break;
+        case START_ARRAY:
+            writeStartArray();
+            break;
+        case END_ARRAY:
+            writeEndArray();
+            break;
+        case FIELD_NAME:
+            writeFieldName(jp.getCurrentName());
+            break;
+        case VALUE_STRING:
+            if (jp.hasTextCharacters()) {
+                writeString(jp.getTextCharacters(), jp.getTextOffset(), jp.getTextLength());
+            } else {
+                writeString(jp.getText());
+            }
+            break;
+        case VALUE_NUMBER_INT:
+            switch (jp.getNumberType()) {
+            case INT:
+                writeNumber(jp.getIntValue());
+                break;
+            case BIG_INTEGER:
+                writeNumber(jp.getBigIntegerValue());
+                break;
+            default:
+                writeNumber(jp.getLongValue());
+            }
+            break;
+        case VALUE_NUMBER_FLOAT:
+            switch (jp.getNumberType()) {
+            case BIG_DECIMAL:
+                writeNumber(jp.getDecimalValue());
+                break;
+            case FLOAT:
+                writeNumber(jp.getFloatValue());
+                break;
+            default:
+                writeNumber(jp.getDoubleValue());
+            }
+            break;
+        case VALUE_TRUE:
+            writeBoolean(true);
+            break;
+        case VALUE_FALSE:
+            writeBoolean(false);
+            break;
+        case VALUE_NULL:
+            writeNull();
+            break;
+        case VALUE_EMBEDDED_OBJECT:
+            writeObject(jp.getEmbeddedObject());
+            break;
+        default:
+            _cantHappen();
+        }
+    }
+
+    @Override
+    public final void copyCurrentStructure(JsonParser jp)
+        throws IOException, JsonProcessingException
+    {
+        JsonToken t = jp.getCurrentToken();
+
+        // Let's handle field-name separately first
+        if (t == JsonToken.FIELD_NAME) {
+            writeFieldName(jp.getCurrentName());
+            t = jp.nextToken();
+            // fall-through to copy the associated value
+        }
+
+        switch (t) {
+        case START_ARRAY:
+            writeStartArray();
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                copyCurrentStructure(jp);
+            }
+            writeEndArray();
+            break;
+        case START_OBJECT:
+            writeStartObject();
+            while (jp.nextToken() != JsonToken.END_OBJECT) {
+                copyCurrentStructure(jp);
+            }
+            writeEndObject();
+            break;
+        default: // others are simple:
+            copyCurrentEvent(jp);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Package methods for this, sub-classes
+    /**********************************************************
+     */
+
+    protected abstract void _releaseBuffers();
+
+    protected abstract void _verifyValueWrite(String typeMsg)
+        throws IOException, JsonGenerationException;
+
+    protected void _reportError(String msg)
+        throws JsonGenerationException
+    {
+        throw new JsonGenerationException(msg);
+    }
+
+    protected void _cantHappen()
+    {
+        throw new RuntimeException("Internal error: should never end up through this code path");
+    }
+
+    /**
+     * Helper method to try to call appropriate write method for given
+     * untyped Object. At this point, no structural conversions should be done,
+     * only simple basic types are to be coerced as necessary.
+     *
+     * @param value Non-null value to write
+     */
+    protected void _writeSimpleObject(Object value) 
+        throws IOException, JsonGenerationException
+    {
+        /* 31-Dec-2009, tatu: Actually, we could just handle some basic
+         *    types even without codec. This can improve interoperability,
+         *    and specifically help with TokenBuffer.
+         */
+        if (value == null) {
+            writeNull();
+            return;
+        }
+        if (value instanceof String) {
+            writeString((String) value);
+            return;
+        }
+        if (value instanceof Number) {
+            Number n = (Number) value;
+            if (n instanceof Integer) {
+                writeNumber(n.intValue());
+                return;
+            } else if (n instanceof Long) {
+                writeNumber(n.longValue());
+                return;
+            } else if (n instanceof Double) {
+                writeNumber(n.doubleValue());
+                return;
+            } else if (n instanceof Float) {
+                writeNumber(n.floatValue());
+                return;
+            } else if (n instanceof Short) {
+                writeNumber(n.shortValue());
+                return;
+            } else if (n instanceof Byte) {
+                writeNumber(n.byteValue());
+                return;
+            } else if (n instanceof BigInteger) {
+                writeNumber((BigInteger) n);
+                return;
+            } else if (n instanceof BigDecimal) {
+                writeNumber((BigDecimal) n);
+                return;
+                
+            // then Atomic types
+                
+            } else if (n instanceof AtomicInteger) {
+                writeNumber(((AtomicInteger) n).get());
+                return;
+            } else if (n instanceof AtomicLong) {
+                writeNumber(((AtomicLong) n).get());
+                return;
+            }
+        } else if (value instanceof byte[]) {
+            writeBinary((byte[]) value);
+            return;
+        } else if (value instanceof Boolean) {
+            writeBoolean(((Boolean) value).booleanValue());
+            return;
+        } else if (value instanceof AtomicBoolean) {
+            writeBoolean(((AtomicBoolean) value).get());
+            return;
+        }
+        throw new IllegalStateException("No ObjectCodec defined for the generator, can only serialize simple wrapper types (type passed "
+                +value.getClass().getName()+")");
+    }    
+
+    protected final void _throwInternal() {
+        throw new RuntimeException("Internal error: this code path should never get executed");
+    }
+
+    /**
+     * @since 1.7
+     */
+    protected void _reportUnsupportedOperation() {
+        throw new UnsupportedOperationException("Operation not supported by generator of type "+getClass().getName());
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
new file mode 100644
index 0000000..1288305
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
@@ -0,0 +1,1068 @@
+package com.fasterxml.jackson.core.base;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.IOContext;
+import com.fasterxml.jackson.core.io.NumberInput;
+import com.fasterxml.jackson.core.json.JsonReadContext;
+import com.fasterxml.jackson.core.util.ByteArrayBuilder;
+import com.fasterxml.jackson.core.util.TextBuffer;
+import com.fasterxml.jackson.core.util.VersionUtil;
+
+/**
+ * Intermediate base class used by all Jackson {@link JsonParser}
+ * implementations. Contains most common things that are independent
+ * of actual underlying input source
+ *
+ * @author Tatu Saloranta
+ */
+public abstract class ParserBase
+    extends ParserMinimalBase
+{
+    /*
+    /**********************************************************
+    /* Generic I/O state
+    /**********************************************************
+     */
+
+    /**
+     * I/O context for this reader. It handles buffer allocation
+     * for the reader.
+     */
+    final protected IOContext _ioContext;
+
+    /**
+     * Flag that indicates whether parser is closed or not. Gets
+     * set when parser is either closed by explicit call
+     * ({@link #close}) or when end-of-input is reached.
+     */
+    protected boolean _closed;
+
+    /*
+    /**********************************************************
+    /* Current input data
+    /**********************************************************
+     */
+
+    // Note: type of actual buffer depends on sub-class, can't include
+
+    /**
+     * Pointer to next available character in buffer
+     */
+    protected int _inputPtr = 0;
+
+    /**
+     * Index of character after last available one in the buffer.
+     */
+    protected int _inputEnd = 0;
+
+    /*
+    /**********************************************************
+    /* Current input location information
+    /**********************************************************
+     */
+
+    /**
+     * Number of characters/bytes that were contained in previous blocks
+     * (blocks that were already processed prior to the current buffer).
+     */
+    protected long _currInputProcessed = 0L;
+
+    /**
+     * Current row location of current point in input buffer, starting
+     * from 1, if available.
+     */
+    protected int _currInputRow = 1;
+
+    /**
+     * Current index of the first character of the current row in input
+     * buffer. Needed to calculate column position, if necessary; benefit
+     * of not having column itself is that this only has to be updated
+     * once per line.
+     */
+    protected int _currInputRowStart = 0;
+
+    /*
+    /**********************************************************
+    /* Information about starting location of event
+    /* Reader is pointing to; updated on-demand
+    /**********************************************************
+     */
+
+    // // // Location info at point when current token was started
+
+    /**
+     * Total number of bytes/characters read before start of current token.
+     * For big (gigabyte-sized) sizes are possible, needs to be long,
+     * unlike pointers and sizes related to in-memory buffers.
+     */
+    protected long _tokenInputTotal = 0; 
+
+    /**
+     * Input row on which current token starts, 1-based
+     */
+    protected int _tokenInputRow = 1;
+
+    /**
+     * Column on input row that current token starts; 0-based (although
+     * in the end it'll be converted to 1-based)
+     */
+    protected int _tokenInputCol = 0;
+
+    /*
+    /**********************************************************
+    /* Parsing state
+    /**********************************************************
+     */
+
+    /**
+     * Information about parser context, context in which
+     * the next token is to be parsed (root, array, object).
+     */
+    protected JsonReadContext _parsingContext;
+
+    /**
+     * Secondary token related to the next token after current one;
+     * used if its type is known. This may be value token that
+     * follows FIELD_NAME, for example.
+     */
+    protected JsonToken _nextToken;
+
+    /*
+    /**********************************************************
+    /* Buffer(s) for local name(s) and text content
+    /**********************************************************
+     */
+
+    /**
+     * Buffer that contains contents of String values, including
+     * field names if necessary (name split across boundary,
+     * contains escape sequence, or access needed to char array)
+     */
+    protected final TextBuffer _textBuffer;
+
+    /**
+     * Temporary buffer that is needed if field name is accessed
+     * using {@link #getTextCharacters} method (instead of String
+     * returning alternatives)
+     */
+    protected char[] _nameCopyBuffer = null;
+
+    /**
+     * Flag set to indicate whether the field name is available
+     * from the name copy buffer or not (in addition to its String
+     * representation  being available via read context)
+     */
+    protected boolean _nameCopied = false;
+
+    /**
+     * ByteArrayBuilder is needed if 'getBinaryValue' is called. If so,
+     * we better reuse it for remainder of content.
+     */
+    protected ByteArrayBuilder _byteArrayBuilder = null;
+
+    /**
+     * We will hold on to decoded binary data, for duration of
+     * current event, so that multiple calls to
+     * {@link #getBinaryValue} will not need to decode data more
+     * than once.
+     */
+    protected byte[] _binaryValue;
+
+    /*
+    /**********************************************************
+    /* Constants and fields of former 'JsonNumericParserBase'
+    /**********************************************************
+     */
+
+    final protected static int NR_UNKNOWN = 0;
+
+    // First, integer types
+
+    final protected static int NR_INT = 0x0001;
+    final protected static int NR_LONG = 0x0002;
+    final protected static int NR_BIGINT = 0x0004;
+
+    // And then floating point types
+
+    final protected static int NR_DOUBLE = 0x008;
+    final protected static int NR_BIGDECIMAL = 0x0010;
+
+    // Also, we need some numeric constants
+
+    final static BigDecimal BD_MIN_LONG = new BigDecimal(Long.MIN_VALUE);
+    final static BigDecimal BD_MAX_LONG = new BigDecimal(Long.MAX_VALUE);
+
+    final static BigDecimal BD_MIN_INT = new BigDecimal(Long.MIN_VALUE);
+    final static BigDecimal BD_MAX_INT = new BigDecimal(Long.MAX_VALUE);
+
+    final static long MIN_INT_L = (long) Integer.MIN_VALUE;
+    final static long MAX_INT_L = (long) Integer.MAX_VALUE;
+
+    // These are not very accurate, but have to do... (for bounds checks)
+
+    final static double MIN_LONG_D = (double) Long.MIN_VALUE;
+    final static double MAX_LONG_D = (double) Long.MAX_VALUE;
+
+    final static double MIN_INT_D = (double) Integer.MIN_VALUE;
+    final static double MAX_INT_D = (double) Integer.MAX_VALUE;
+    
+    
+    // Digits, numeric
+    final protected static int INT_0 = '0';
+    final protected static int INT_1 = '1';
+    final protected static int INT_2 = '2';
+    final protected static int INT_3 = '3';
+    final protected static int INT_4 = '4';
+    final protected static int INT_5 = '5';
+    final protected static int INT_6 = '6';
+    final protected static int INT_7 = '7';
+    final protected static int INT_8 = '8';
+    final protected static int INT_9 = '9';
+
+    final protected static int INT_MINUS = '-';
+    final protected static int INT_PLUS = '+';
+    final protected static int INT_DECIMAL_POINT = '.';
+
+    final protected static int INT_e = 'e';
+    final protected static int INT_E = 'E';
+
+    final protected static char CHAR_NULL = '\0';
+    
+    // Numeric value holders: multiple fields used for
+    // for efficiency
+
+    /**
+     * Bitfield that indicates which numeric representations
+     * have been calculated for the current type
+     */
+    protected int _numTypesValid = NR_UNKNOWN;
+
+    // First primitives
+
+    protected int _numberInt;
+
+    protected long _numberLong;
+
+    protected double _numberDouble;
+
+    // And then object types
+
+    protected BigInteger _numberBigInt;
+
+    protected BigDecimal _numberBigDecimal;
+
+    // And then other information about value itself
+
+    /**
+     * Flag that indicates whether numeric value has a negative
+     * value. That is, whether its textual representation starts
+     * with minus character.
+     */
+    protected boolean _numberNegative;
+
+    /**
+     * Length of integer part of the number, in characters
+     */
+    protected int _intLength;
+
+    /**
+     * Length of the fractional part (not including decimal
+     * point or exponent), in characters.
+     * Not used for  pure integer values.
+     */
+    protected int _fractLength;
+
+    /**
+     * Length of the exponent part of the number, if any, not
+     * including 'e' marker or sign, just digits. 
+     * Not used for  pure integer values.
+     */
+    protected int _expLength;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected ParserBase(IOContext ctxt, int features)
+    {
+        super();
+        _features = features;
+        _ioContext = ctxt;
+        _textBuffer = ctxt.constructTextBuffer();
+        _parsingContext = JsonReadContext.createRootContext();
+    }
+    
+    @Override
+    public Version version() {
+        return VersionUtil.versionFor(getClass());
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonParser impl
+    /**********************************************************
+     */
+    
+    /**
+     * Method that can be called to get the name associated with
+     * the current event.
+     */
+    @Override
+    public String getCurrentName()
+        throws IOException, JsonParseException
+    {
+        // [JACKSON-395]: start markers require information from parent
+        if (_currToken == JsonToken.START_OBJECT || _currToken == JsonToken.START_ARRAY) {
+            JsonReadContext parent = _parsingContext.getParent();
+            return parent.getCurrentName();
+        }
+        return _parsingContext.getCurrentName();
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        if (!_closed) {
+            _closed = true;
+            try {
+                _closeInput();
+            } finally {
+                // as per [JACKSON-324], do in finally block
+                // Also, internal buffer(s) can now be released as well
+                _releaseBuffers();
+            }
+        }
+    }
+
+    @Override
+    public boolean isClosed() { return _closed; }
+
+    @Override
+    public JsonReadContext getParsingContext()
+    {
+        return _parsingContext;
+    }
+
+    /**
+     * Method that return the <b>starting</b> location of the current
+     * token; that is, position of the first character from input
+     * that starts the current token.
+     */
+    @Override
+    public JsonLocation getTokenLocation()
+    {
+        return new JsonLocation(_ioContext.getSourceReference(),
+                                getTokenCharacterOffset(),
+                                getTokenLineNr(),
+                                getTokenColumnNr());
+    }
+
+    /**
+     * Method that returns location of the last processed character;
+     * usually for error reporting purposes
+     */
+    @Override
+    public JsonLocation getCurrentLocation()
+    {
+        int col = _inputPtr - _currInputRowStart + 1; // 1-based
+        return new JsonLocation(_ioContext.getSourceReference(),
+                                _currInputProcessed + _inputPtr - 1,
+                                _currInputRow, col);
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, access to token information, text
+    /**********************************************************
+     */
+
+    @Override
+    public boolean hasTextCharacters()
+    {
+        if (_currToken == JsonToken.VALUE_STRING) {
+            return true; // usually true
+        }        
+        if (_currToken == JsonToken.FIELD_NAME) {
+            return _nameCopied;
+        }
+        return false;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public low-level accessors
+    /**********************************************************
+     */
+
+    public final long getTokenCharacterOffset() { return _tokenInputTotal; }
+    public final int getTokenLineNr() { return _tokenInputRow; }
+    public final int getTokenColumnNr() {
+        // note: value of -1 means "not available"; otherwise convert from 0-based to 1-based
+        int col = _tokenInputCol;
+        return (col < 0) ? col : (col + 1);
+    }
+
+    /*
+    /**********************************************************
+    /* Low-level reading, other
+    /**********************************************************
+     */
+
+    protected final void loadMoreGuaranteed()
+        throws IOException
+    {
+        if (!loadMore()) {
+            _reportInvalidEOF();
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Abstract methods needed from sub-classes
+    /**********************************************************
+     */
+
+    protected abstract boolean loadMore() throws IOException;
+    
+    protected abstract void _finishString() throws IOException, JsonParseException;
+
+    protected abstract void _closeInput() throws IOException;
+    
+    /*
+    /**********************************************************
+    /* Low-level reading, other
+    /**********************************************************
+     */
+
+    /**
+     * Method called to release internal buffers owned by the base
+     * reader. This may be called along with {@link #_closeInput} (for
+     * example, when explicitly closing this reader instance), or
+     * separately (if need be).
+     */
+    protected void _releaseBuffers() throws IOException
+    {
+        _textBuffer.releaseBuffers();
+        char[] buf = _nameCopyBuffer;
+        if (buf != null) {
+            _nameCopyBuffer = null;
+            _ioContext.releaseNameCopyBuffer(buf);
+        }
+    }
+    
+    /**
+     * Method called when an EOF is encountered between tokens.
+     * If so, it may be a legitimate EOF, but only iff there
+     * is no open non-root context.
+     */
+    @Override
+    protected void _handleEOF() throws JsonParseException
+    {
+        if (!_parsingContext.inRoot()) {
+            _reportInvalidEOF(": expected close marker for "+_parsingContext.getTypeDesc()+" (from "+_parsingContext.getStartLocation(_ioContext.getSourceReference())+")");
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal/package methods: Error reporting
+    /**********************************************************
+     */
+    
+    protected void _reportMismatchedEndMarker(int actCh, char expCh)
+        throws JsonParseException
+    {
+        String startDesc = ""+_parsingContext.getStartLocation(_ioContext.getSourceReference());
+        _reportError("Unexpected close marker '"+((char) actCh)+"': expected '"+expCh+"' (for "+_parsingContext.getTypeDesc()+" starting at "+startDesc+")");
+    }
+
+    /*
+    /**********************************************************
+    /* Internal/package methods: shared/reusable builders
+    /**********************************************************
+     */
+    
+    public ByteArrayBuilder _getByteArrayBuilder()
+    {
+        if (_byteArrayBuilder == null) {
+            _byteArrayBuilder = new ByteArrayBuilder();
+        } else {
+            _byteArrayBuilder.reset();
+        }
+        return _byteArrayBuilder;
+    }
+
+    /*
+    /**********************************************************
+    /* Methods from former JsonNumericParserBase
+    /**********************************************************
+     */
+
+    // // // Life-cycle of number-parsing
+    
+    protected final JsonToken reset(boolean negative, int intLen, int fractLen, int expLen)
+    {
+        if (fractLen < 1 && expLen < 1) { // integer
+            return resetInt(negative, intLen);
+        }
+        return resetFloat(negative, intLen, fractLen, expLen);
+    }
+        
+    protected final JsonToken resetInt(boolean negative, int intLen)
+    {
+        _numberNegative = negative;
+        _intLength = intLen;
+        _fractLength = 0;
+        _expLength = 0;
+        _numTypesValid = NR_UNKNOWN; // to force parsing
+        return JsonToken.VALUE_NUMBER_INT;
+    }
+    
+    protected final JsonToken resetFloat(boolean negative, int intLen, int fractLen, int expLen)
+    {
+        _numberNegative = negative;
+        _intLength = intLen;
+        _fractLength = fractLen;
+        _expLength = expLen;
+        _numTypesValid = NR_UNKNOWN; // to force parsing
+        return JsonToken.VALUE_NUMBER_FLOAT;
+    }
+    
+    protected final JsonToken resetAsNaN(String valueStr, double value)
+    {
+        _textBuffer.resetWithString(valueStr);
+        _numberDouble = value;
+        _numTypesValid = NR_DOUBLE;
+        return JsonToken.VALUE_NUMBER_FLOAT;
+    }
+    
+    /*
+    /**********************************************************
+    /* Numeric accessors of public API
+    /**********************************************************
+     */
+    
+    @Override
+    public Number getNumberValue() throws IOException, JsonParseException
+    {
+        if (_numTypesValid == NR_UNKNOWN) {
+            _parseNumericValue(NR_UNKNOWN); // will also check event type
+        }
+        // Separate types for int types
+        if (_currToken == JsonToken.VALUE_NUMBER_INT) {
+            if ((_numTypesValid & NR_INT) != 0) {
+                return Integer.valueOf(_numberInt);
+            }
+            if ((_numTypesValid & NR_LONG) != 0) {
+                return Long.valueOf(_numberLong);
+            }
+            if ((_numTypesValid & NR_BIGINT) != 0) {
+                return _numberBigInt;
+            }
+            // Shouldn't get this far but if we do
+            return _numberBigDecimal;
+        }
+    
+        /* And then floating point types. But here optimal type
+         * needs to be big decimal, to avoid losing any data?
+         */
+        if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
+            return _numberBigDecimal;
+        }
+        if ((_numTypesValid & NR_DOUBLE) == 0) { // sanity check
+            _throwInternal();
+        }
+        return Double.valueOf(_numberDouble);
+    }
+    
+    @Override
+    public NumberType getNumberType() throws IOException, JsonParseException
+    {
+        if (_numTypesValid == NR_UNKNOWN) {
+            _parseNumericValue(NR_UNKNOWN); // will also check event type
+        }
+        if (_currToken == JsonToken.VALUE_NUMBER_INT) {
+            if ((_numTypesValid & NR_INT) != 0) {
+                return NumberType.INT;
+            }
+            if ((_numTypesValid & NR_LONG) != 0) {
+                return NumberType.LONG;
+            }
+            return NumberType.BIG_INTEGER;
+        }
+    
+        /* And then floating point types. Here optimal type
+         * needs to be big decimal, to avoid losing any data?
+         * However... using BD is slow, so let's allow returning
+         * double as type if no explicit call has been made to access
+         * data as BD?
+         */
+        if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
+            return NumberType.BIG_DECIMAL;
+        }
+        return NumberType.DOUBLE;
+    }
+    
+    @Override
+    public int getIntValue() throws IOException, JsonParseException
+    {
+        if ((_numTypesValid & NR_INT) == 0) {
+            if (_numTypesValid == NR_UNKNOWN) { // not parsed at all
+                _parseNumericValue(NR_INT); // will also check event type
+            }
+            if ((_numTypesValid & NR_INT) == 0) { // wasn't an int natively?
+                convertNumberToInt(); // let's make it so, if possible
+            }
+        }
+        return _numberInt;
+    }
+    
+    @Override
+    public long getLongValue() throws IOException, JsonParseException
+    {
+        if ((_numTypesValid & NR_LONG) == 0) {
+            if (_numTypesValid == NR_UNKNOWN) {
+                _parseNumericValue(NR_LONG);
+            }
+            if ((_numTypesValid & NR_LONG) == 0) {
+                convertNumberToLong();
+            }
+        }
+        return _numberLong;
+    }
+    
+    @Override
+    public BigInteger getBigIntegerValue() throws IOException, JsonParseException
+    {
+        if ((_numTypesValid & NR_BIGINT) == 0) {
+            if (_numTypesValid == NR_UNKNOWN) {
+                _parseNumericValue(NR_BIGINT);
+            }
+            if ((_numTypesValid & NR_BIGINT) == 0) {
+                convertNumberToBigInteger();
+            }
+        }
+        return _numberBigInt;
+    }
+    
+    @Override
+    public float getFloatValue() throws IOException, JsonParseException
+    {
+        double value = getDoubleValue();
+        /* 22-Jan-2009, tatu: Bounds/range checks would be tricky
+         *   here, so let's not bother even trying...
+         */
+        /*
+        if (value < -Float.MAX_VALUE || value > MAX_FLOAT_D) {
+            _reportError("Numeric value ("+getText()+") out of range of Java float");
+        }
+        */
+        return (float) value;
+    }
+    
+    @Override
+    public double getDoubleValue() throws IOException, JsonParseException
+    {
+        if ((_numTypesValid & NR_DOUBLE) == 0) {
+            if (_numTypesValid == NR_UNKNOWN) {
+                _parseNumericValue(NR_DOUBLE);
+            }
+            if ((_numTypesValid & NR_DOUBLE) == 0) {
+                convertNumberToDouble();
+            }
+        }
+        return _numberDouble;
+    }
+    
+    @Override
+    public BigDecimal getDecimalValue() throws IOException, JsonParseException
+    {
+        if ((_numTypesValid & NR_BIGDECIMAL) == 0) {
+            if (_numTypesValid == NR_UNKNOWN) {
+                _parseNumericValue(NR_BIGDECIMAL);
+            }
+            if ((_numTypesValid & NR_BIGDECIMAL) == 0) {
+                convertNumberToBigDecimal();
+            }
+        }
+        return _numberBigDecimal;
+    }
+
+    /*
+    /**********************************************************
+    /* Conversion from textual to numeric representation
+    /**********************************************************
+     */
+    
+    /**
+     * Method that will parse actual numeric value out of a syntactically
+     * valid number value. Type it will parse into depends on whether
+     * it is a floating point number, as well as its magnitude: smallest
+     * legal type (of ones available) is used for efficiency.
+     *
+     * @param expType Numeric type that we will immediately need, if any;
+     *   mostly necessary to optimize handling of floating point numbers
+     */
+    protected void _parseNumericValue(int expType)
+        throws IOException, JsonParseException
+    {
+        // Int or float?
+        if (_currToken == JsonToken.VALUE_NUMBER_INT) {
+            char[] buf = _textBuffer.getTextBuffer();
+            int offset = _textBuffer.getTextOffset();
+            int len = _intLength;
+            if (_numberNegative) {
+                ++offset;
+            }
+            if (len <= 9) { // definitely fits in int
+                int i = NumberInput.parseInt(buf, offset, len);
+                _numberInt = _numberNegative ? -i : i;
+                _numTypesValid = NR_INT;
+                return;
+            }
+            if (len <= 18) { // definitely fits AND is easy to parse using 2 int parse calls
+                long l = NumberInput.parseLong(buf, offset, len);
+                if (_numberNegative) {
+                    l = -l;
+                }
+                // [JACKSON-230] Could still fit in int, need to check
+                if (len == 10) {
+                    if (_numberNegative) {
+                        if (l >= MIN_INT_L) {
+                            _numberInt = (int) l;
+                            _numTypesValid = NR_INT;
+                            return;
+                        }
+                    } else {
+                        if (l <= MAX_INT_L) {
+                            _numberInt = (int) l;
+                            _numTypesValid = NR_INT;
+                            return;
+                        }
+                    }
+                }
+                _numberLong = l;
+                _numTypesValid = NR_LONG;
+                return;
+            }
+            _parseSlowIntValue(expType, buf, offset, len);
+            return;
+        }
+        if (_currToken == JsonToken.VALUE_NUMBER_FLOAT) {
+            _parseSlowFloatValue(expType);
+            return;
+        }
+        _reportError("Current token ("+_currToken+") not numeric, can not use numeric value accessors");
+    }
+    
+    private final void _parseSlowFloatValue(int expType)
+        throws IOException, JsonParseException
+    {
+        /* Nope: floating point. Here we need to be careful to get
+         * optimal parsing strategy: choice is between accurate but
+         * slow (BigDecimal) and lossy but fast (Double). For now
+         * let's only use BD when explicitly requested -- it can
+         * still be constructed correctly at any point since we do
+         * retain textual representation
+         */
+        try {
+            if (expType == NR_BIGDECIMAL) {
+                _numberBigDecimal = _textBuffer.contentsAsDecimal();
+                _numTypesValid = NR_BIGDECIMAL;
+            } else {
+                // Otherwise double has to do
+                _numberDouble = _textBuffer.contentsAsDouble();
+                _numTypesValid = NR_DOUBLE;
+            }
+        } catch (NumberFormatException nex) {
+            // Can this ever occur? Due to overflow, maybe?
+            _wrapError("Malformed numeric value '"+_textBuffer.contentsAsString()+"'", nex);
+        }
+    }
+    
+    private final void _parseSlowIntValue(int expType, char[] buf, int offset, int len)
+        throws IOException, JsonParseException
+    {
+        String numStr = _textBuffer.contentsAsString();
+        try {
+            // [JACKSON-230] Some long cases still...
+            if (NumberInput.inLongRange(buf, offset, len, _numberNegative)) {
+                // Probably faster to construct a String, call parse, than to use BigInteger
+                _numberLong = Long.parseLong(numStr);
+                _numTypesValid = NR_LONG;
+            } else {
+                // nope, need the heavy guns... (rare case)
+                _numberBigInt = new BigInteger(numStr);
+                _numTypesValid = NR_BIGINT;
+            }
+        } catch (NumberFormatException nex) {
+            // Can this ever occur? Due to overflow, maybe?
+            _wrapError("Malformed numeric value '"+numStr+"'", nex);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Numeric conversions
+    /**********************************************************
+     */    
+    
+    protected void convertNumberToInt()
+        throws IOException, JsonParseException
+    {
+        // First, converting from long ought to be easy
+        if ((_numTypesValid & NR_LONG) != 0) {
+            // Let's verify it's lossless conversion by simple roundtrip
+            int result = (int) _numberLong;
+            if (((long) result) != _numberLong) {
+                _reportError("Numeric value ("+getText()+") out of range of int");
+            }
+            _numberInt = result;
+        } else if ((_numTypesValid & NR_BIGINT) != 0) {
+            // !!! Should check for range...
+            _numberInt = _numberBigInt.intValue();
+        } else if ((_numTypesValid & NR_DOUBLE) != 0) {
+            // Need to check boundaries
+            if (_numberDouble < MIN_INT_D || _numberDouble > MAX_INT_D) {
+                reportOverflowInt();
+            }
+            _numberInt = (int) _numberDouble;
+        } else if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
+            if (BD_MIN_INT.compareTo(_numberBigDecimal) > 0 
+                || BD_MAX_INT.compareTo(_numberBigDecimal) < 0) {
+                reportOverflowInt();
+            }
+            _numberInt = _numberBigDecimal.intValue();
+        } else {
+            _throwInternal(); // should never get here
+        }
+    
+        _numTypesValid |= NR_INT;
+    }
+    
+    protected void convertNumberToLong()
+        throws IOException, JsonParseException
+    {
+        if ((_numTypesValid & NR_INT) != 0) {
+            _numberLong = (long) _numberInt;
+        } else if ((_numTypesValid & NR_BIGINT) != 0) {
+            // !!! Should check for range...
+            _numberLong = _numberBigInt.longValue();
+        } else if ((_numTypesValid & NR_DOUBLE) != 0) {
+            // Need to check boundaries
+            if (_numberDouble < MIN_LONG_D || _numberDouble > MAX_LONG_D) {
+                reportOverflowLong();
+            }
+            _numberLong = (long) _numberDouble;
+        } else if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
+            if (BD_MIN_LONG.compareTo(_numberBigDecimal) > 0 
+                || BD_MAX_LONG.compareTo(_numberBigDecimal) < 0) {
+                reportOverflowLong();
+            }
+            _numberLong = _numberBigDecimal.longValue();
+        } else {
+            _throwInternal(); // should never get here
+        }
+    
+        _numTypesValid |= NR_LONG;
+    }
+    
+    protected void convertNumberToBigInteger()
+        throws IOException, JsonParseException
+    {
+        if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
+            // here it'll just get truncated, no exceptions thrown
+            _numberBigInt = _numberBigDecimal.toBigInteger();
+        } else if ((_numTypesValid & NR_LONG) != 0) {
+            _numberBigInt = BigInteger.valueOf(_numberLong);
+        } else if ((_numTypesValid & NR_INT) != 0) {
+            _numberBigInt = BigInteger.valueOf(_numberInt);
+        } else if ((_numTypesValid & NR_DOUBLE) != 0) {
+            _numberBigInt = BigDecimal.valueOf(_numberDouble).toBigInteger();
+        } else {
+            _throwInternal(); // should never get here
+        }
+        _numTypesValid |= NR_BIGINT;
+    }
+    
+    protected void convertNumberToDouble()
+        throws IOException, JsonParseException
+    {
+        /* 05-Aug-2008, tatus: Important note: this MUST start with
+         *   more accurate representations, since we don't know which
+         *   value is the original one (others get generated when
+         *   requested)
+         */
+    
+        if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
+            _numberDouble = _numberBigDecimal.doubleValue();
+        } else if ((_numTypesValid & NR_BIGINT) != 0) {
+            _numberDouble = _numberBigInt.doubleValue();
+        } else if ((_numTypesValid & NR_LONG) != 0) {
+            _numberDouble = (double) _numberLong;
+        } else if ((_numTypesValid & NR_INT) != 0) {
+            _numberDouble = (double) _numberInt;
+        } else {
+            _throwInternal(); // should never get here
+        }
+    
+        _numTypesValid |= NR_DOUBLE;
+    }
+    
+    protected void convertNumberToBigDecimal()
+        throws IOException, JsonParseException
+    {
+        /* 05-Aug-2008, tatus: Important note: this MUST start with
+         *   more accurate representations, since we don't know which
+         *   value is the original one (others get generated when
+         *   requested)
+         */
+    
+        if ((_numTypesValid & NR_DOUBLE) != 0) {
+            /* Let's actually parse from String representation,
+             * to avoid rounding errors that non-decimal floating operations
+             * would incur
+             */
+            _numberBigDecimal = new BigDecimal(getText());
+        } else if ((_numTypesValid & NR_BIGINT) != 0) {
+            _numberBigDecimal = new BigDecimal(_numberBigInt);
+        } else if ((_numTypesValid & NR_LONG) != 0) {
+            _numberBigDecimal = BigDecimal.valueOf(_numberLong);
+        } else if ((_numTypesValid & NR_INT) != 0) {
+            _numberBigDecimal = BigDecimal.valueOf((long) _numberInt);
+        } else {
+            _throwInternal(); // should never get here
+        }
+        _numTypesValid |= NR_BIGDECIMAL;
+    }
+    
+    /*
+    /**********************************************************
+    /* Number handling exceptions
+    /**********************************************************
+     */    
+    
+    protected void reportUnexpectedNumberChar(int ch, String comment)
+        throws JsonParseException
+    {
+        String msg = "Unexpected character ("+_getCharDesc(ch)+") in numeric value";
+        if (comment != null) {
+            msg += ": "+comment;
+        }
+        _reportError(msg);
+    }
+    
+    protected void reportInvalidNumber(String msg)
+        throws JsonParseException
+    {
+        _reportError("Invalid numeric value: "+msg);
+    }
+    
+    protected void reportOverflowInt()
+        throws IOException, JsonParseException
+    {
+        _reportError("Numeric value ("+getText()+") out of range of int ("+Integer.MIN_VALUE+" - "+Integer.MAX_VALUE+")");
+    }
+    
+    protected void reportOverflowLong()
+        throws IOException, JsonParseException
+    {
+        _reportError("Numeric value ("+getText()+") out of range of long ("+Long.MIN_VALUE+" - "+Long.MAX_VALUE+")");
+    }    
+
+    /*
+    /**********************************************************
+    /* Base64 handling support
+    /**********************************************************
+     */
+
+    /**
+     * Method that sub-classes must implement to support escaped sequences
+     * in base64-encoded sections.
+     * Sub-classes that do not need base64 support can leave this as is
+     */
+    protected char _decodeEscaped()
+        throws IOException, JsonParseException {
+        throw new UnsupportedOperationException();
+    }
+    
+    protected final int _decodeBase64Escape(Base64Variant b64variant, int ch, int index)
+        throws IOException, JsonParseException
+    {
+        // 17-May-2011, tatu: As per [JACKSON-xxx], need to handle escaped chars
+        if (ch != '\\') {
+            throw reportInvalidBase64Char(b64variant, ch, index);
+        }
+        int unescaped = _decodeEscaped();
+        // if white space, skip if first triplet; otherwise errors
+        if (unescaped <= INT_SPACE) {
+            if (index == 0) { // whitespace only allowed to be skipped between triplets
+                return -1;
+            }
+        }
+        // otherwise try to find actual triplet value
+        int bits = b64variant.decodeBase64Char(unescaped);
+        if (bits < 0) {
+            throw reportInvalidBase64Char(b64variant, unescaped, index);
+        }
+        return bits;
+    }
+    
+    protected final int _decodeBase64Escape(Base64Variant b64variant, char ch, int index)
+        throws IOException, JsonParseException
+    {
+        // 17-May-2011, tatu: As per [JACKSON-xxx], need to handle escaped chars
+        if (ch != '\\') {
+            throw reportInvalidBase64Char(b64variant, ch, index);
+        }
+        char unescaped = _decodeEscaped();
+        // if white space, skip if first triplet; otherwise errors
+        if (unescaped <= INT_SPACE) {
+            if (index == 0) { // whitespace only allowed to be skipped between triplets
+                return -1;
+            }
+        }
+        // otherwise try to find actual triplet value
+        int bits = b64variant.decodeBase64Char(unescaped);
+        if (bits < 0) {
+            throw reportInvalidBase64Char(b64variant, unescaped, index);
+        }
+        return bits;
+    }
+    
+    protected IllegalArgumentException reportInvalidBase64Char(Base64Variant b64variant, int ch, int bindex)
+        throws IllegalArgumentException
+    {
+        return reportInvalidBase64Char(b64variant, ch, bindex, null);
+    }
+
+    /**
+     * @param bindex Relative index within base64 character unit; between 0
+     *   and 3 (as unit has exactly 4 characters)
+     */
+    protected IllegalArgumentException reportInvalidBase64Char(Base64Variant b64variant, int ch, int bindex, String msg)
+        throws IllegalArgumentException
+    {
+        String base;
+        if (ch <= INT_SPACE) {
+            base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units";
+        } else if (b64variant.usesPaddingChar(ch)) {
+            base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character";
+        } else if (!Character.isDefined(ch) || Character.isISOControl(ch)) {
+            // Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
+            base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content";
+        } else {
+            base = "Illegal character '"+((char)ch)+"' (code 0x"+Integer.toHexString(ch)+") in base64 content";
+        }
+        if (msg != null) {
+            base = base + ": " + msg;
+        }
+        return new IllegalArgumentException(base);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java
new file mode 100644
index 0000000..f9e39a9
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/base/ParserMinimalBase.java
@@ -0,0 +1,539 @@
+package com.fasterxml.jackson.core.base;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.JsonParser.Feature;
+import com.fasterxml.jackson.core.io.NumberInput;
+import com.fasterxml.jackson.core.util.ByteArrayBuilder;
+
+/**
+ * Intermediate base class used by all Jackson {@link JsonParser}
+ * implementations, but does not add any additional fields that depend
+ * on particular method of obtaining input.
+ *<p>
+ * Note that 'minimal' here mostly refers to minimal number of fields
+ * (size) and functionality that is specific to certain types
+ * of parser implementations; but not necessarily to number of methods.
+ * 
+ * @since 1.6
+ *
+ * @author Tatu Saloranta
+ */
+public abstract class ParserMinimalBase
+    extends JsonParser
+{
+    // Control chars:
+    protected final static int INT_TAB = '\t';
+    protected final static int INT_LF = '\n';
+    protected final static int INT_CR = '\r';
+    protected final static int INT_SPACE = 0x0020;
+
+    // Markup
+    protected final static int INT_LBRACKET = '[';
+    protected final static int INT_RBRACKET = ']';
+    protected final static int INT_LCURLY = '{';
+    protected final static int INT_RCURLY = '}';
+    protected final static int INT_QUOTE = '"';
+    protected final static int INT_BACKSLASH = '\\';
+    protected final static int INT_SLASH = '/';
+    protected final static int INT_COLON = ':';
+    protected final static int INT_COMMA = ',';
+    protected final static int INT_ASTERISK = '*';
+    protected final static int INT_APOSTROPHE = '\'';
+
+    // Letters we need
+    protected final static int INT_b = 'b';
+    protected final static int INT_f = 'f';
+    protected final static int INT_n = 'n';
+    protected final static int INT_r = 'r';
+    protected final static int INT_t = 't';
+    protected final static int INT_u = 'u';
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected ParserMinimalBase() { }
+    protected ParserMinimalBase(int features) {
+        super(features);
+    }
+
+    /*
+    /**********************************************************
+    /* Configuration overrides if any
+    /**********************************************************
+     */
+
+    // from base class:
+
+    //public void enableFeature(Feature f)
+    //public void disableFeature(Feature f)
+    //public void setFeature(Feature f, boolean state)
+    //public boolean isFeatureEnabled(Feature f)
+
+    /*
+    /**********************************************************
+    /* JsonParser impl
+    /**********************************************************
+     */
+
+    @Override
+    public abstract JsonToken nextToken() throws IOException, JsonParseException;
+
+    //public final JsonToken nextValue()
+
+    @Override
+    public JsonParser skipChildren() throws IOException, JsonParseException
+    {
+        if (_currToken != JsonToken.START_OBJECT
+            && _currToken != JsonToken.START_ARRAY) {
+            return this;
+        }
+        int open = 1;
+
+        /* Since proper matching of start/end markers is handled
+         * by nextToken(), we'll just count nesting levels here
+         */
+        while (true) {
+            JsonToken t = nextToken();
+            if (t == null) {
+                _handleEOF();
+                /* given constraints, above should never return;
+                 * however, FindBugs doesn't know about it and
+                 * complains... so let's add dummy break here
+                 */
+                return this;
+            }
+            switch (t) {
+            case START_OBJECT:
+            case START_ARRAY:
+                ++open;
+                break;
+            case END_OBJECT:
+            case END_ARRAY:
+                if (--open == 0) {
+                    return this;
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * Method sub-classes need to implement
+     */
+    protected abstract void _handleEOF() throws JsonParseException;
+
+    //public JsonToken getCurrentToken()
+
+    //public boolean hasCurrentToken()
+
+    @Override
+    public abstract String getCurrentName() throws IOException, JsonParseException;
+    
+    @Override
+    public abstract void close() throws IOException;
+
+    @Override
+    public abstract boolean isClosed();
+
+    @Override
+    public abstract JsonStreamContext getParsingContext();
+
+//    public abstract JsonLocation getTokenLocation();
+
+//   public abstract JsonLocation getCurrentLocation();
+    
+    /*
+    /**********************************************************
+    /* Public API, access to token information, text
+    /**********************************************************
+     */
+
+    @Override
+    public abstract String getText() throws IOException, JsonParseException;
+
+    @Override
+    public abstract char[] getTextCharacters() throws IOException, JsonParseException;
+
+    @Override
+    public abstract boolean hasTextCharacters();
+
+    @Override
+    public abstract int getTextLength() throws IOException, JsonParseException;
+
+    @Override
+    public abstract int getTextOffset() throws IOException, JsonParseException;  
+
+    /*
+    /**********************************************************
+    /* Public API, access to token information, binary
+    /**********************************************************
+     */
+
+    @Override
+    public abstract byte[] getBinaryValue(Base64Variant b64variant)
+        throws IOException, JsonParseException;
+
+    /*
+    /**********************************************************
+    /* Public API, access with conversion/coercion
+    /**********************************************************
+     */
+
+    @Override
+    public boolean getValueAsBoolean(boolean defaultValue) throws IOException, JsonParseException
+    {
+        if (_currToken != null) {
+            switch (_currToken) {
+            case VALUE_NUMBER_INT:
+                return getIntValue() != 0;
+            case VALUE_TRUE:
+                return true;
+            case VALUE_FALSE:
+            case VALUE_NULL:
+                return false;
+            case VALUE_EMBEDDED_OBJECT:
+                {
+                    Object value = this.getEmbeddedObject();
+                    if (value instanceof Boolean) {
+                        return ((Boolean) value).booleanValue();
+                    }
+                }
+            case VALUE_STRING:
+                String str = getText().trim();
+                if ("true".equals(str)) {
+                    return true;
+                }
+                break;
+            }
+        }
+        return defaultValue;
+    }
+    
+    @Override
+    public int getValueAsInt(int defaultValue) throws IOException, JsonParseException
+    {
+        if (_currToken != null) {
+            switch (_currToken) {
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                return getIntValue();
+            case VALUE_TRUE:
+                return 1;
+            case VALUE_FALSE:
+            case VALUE_NULL:
+                return 0;
+            case VALUE_STRING:
+                return NumberInput.parseAsInt(getText(), defaultValue);
+            case VALUE_EMBEDDED_OBJECT:
+                {
+                    Object value = this.getEmbeddedObject();
+                    if (value instanceof Number) {
+                        return ((Number) value).intValue();
+                    }
+                }
+            }
+        }
+        return defaultValue;
+    }
+    
+    @Override
+    public long getValueAsLong(long defaultValue) throws IOException, JsonParseException
+    {
+        if (_currToken != null) {
+            switch (_currToken) {
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                return getLongValue();
+            case VALUE_TRUE:
+                return 1;
+            case VALUE_FALSE:
+            case VALUE_NULL:
+                return 0;
+            case VALUE_STRING:
+                return NumberInput.parseAsLong(getText(), defaultValue);
+            case VALUE_EMBEDDED_OBJECT:
+                {
+                    Object value = this.getEmbeddedObject();
+                    if (value instanceof Number) {
+                        return ((Number) value).longValue();
+                    }
+                }
+            }
+        }
+        return defaultValue;
+    }
+
+    @Override
+    public double getValueAsDouble(double defaultValue) throws IOException, JsonParseException
+    {
+        if (_currToken != null) {
+            switch (_currToken) {
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                return getDoubleValue();
+            case VALUE_TRUE:
+                return 1;
+            case VALUE_FALSE:
+            case VALUE_NULL:
+                return 0;
+            case VALUE_STRING:
+                return NumberInput.parseAsDouble(getText(), defaultValue);
+            case VALUE_EMBEDDED_OBJECT:
+                {
+                    Object value = this.getEmbeddedObject();
+                    if (value instanceof Number) {
+                        return ((Number) value).doubleValue();
+                    }
+                }
+            }
+        }
+        return defaultValue;
+    }
+
+    /*
+    /**********************************************************
+    /* Base64 decoding
+    /**********************************************************
+     */
+
+    /**
+     * Helper method that can be used for base64 decoding in cases where
+     * encoded content has already been read as a String.
+     * 
+     * @since 1.9.3
+     */
+    protected void _decodeBase64(String str, ByteArrayBuilder builder, Base64Variant b64variant)
+        throws IOException, JsonParseException
+    {
+        int ptr = 0;
+        int len = str.length();
+        
+        main_loop:
+        while (ptr < len) {
+            // first, we'll skip preceding white space, if any
+            char ch;
+            do {
+                ch = str.charAt(ptr++);
+                if (ptr >= len) {
+                    break main_loop;
+                }
+            } while (ch <= INT_SPACE);
+            int bits = b64variant.decodeBase64Char(ch);
+            if (bits < 0) {
+                _reportInvalidBase64(b64variant, ch, 0, null);
+            }
+            int decodedData = bits;
+            // then second base64 char; can't get padding yet, nor ws
+            if (ptr >= len) {
+                _reportBase64EOF();
+            }
+            ch = str.charAt(ptr++);
+            bits = b64variant.decodeBase64Char(ch);
+            if (bits < 0) {
+                _reportInvalidBase64(b64variant, ch, 1, null);
+            }
+            decodedData = (decodedData << 6) | bits;
+            // third base64 char; can be padding, but not ws
+            if (ptr >= len) {
+                // but as per [JACKSON-631] can be end-of-input, iff not using padding
+                if (!b64variant.usesPadding()) {
+                    decodedData >>= 4;
+                    builder.append(decodedData);
+                    break;
+                }
+                _reportBase64EOF();
+            }
+            ch = str.charAt(ptr++);
+            bits = b64variant.decodeBase64Char(ch);
+            
+            // First branch: can get padding (-> 1 byte)
+            if (bits < 0) {
+                if (bits != Base64Variant.BASE64_VALUE_PADDING) {
+                    _reportInvalidBase64(b64variant, ch, 2, null);
+                }
+                // Ok, must get padding
+                if (ptr >= len) {
+                    _reportBase64EOF();
+                }
+                ch = str.charAt(ptr++);
+                if (!b64variant.usesPaddingChar(ch)) {
+                    _reportInvalidBase64(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
+                }
+                // Got 12 bits, only need 8, need to shift
+                decodedData >>= 4;
+                builder.append(decodedData);
+                continue;
+            }
+            // Nope, 2 or 3 bytes
+            decodedData = (decodedData << 6) | bits;
+            // fourth and last base64 char; can be padding, but not ws
+            if (ptr >= len) {
+                // but as per [JACKSON-631] can be end-of-input, iff not using padding
+                if (!b64variant.usesPadding()) {
+                    decodedData >>= 2;
+                    builder.appendTwoBytes(decodedData);
+                    break;
+                }
+                _reportBase64EOF();
+            }
+            ch = str.charAt(ptr++);
+            bits = b64variant.decodeBase64Char(ch);
+            if (bits < 0) {
+                if (bits != Base64Variant.BASE64_VALUE_PADDING) {
+                    _reportInvalidBase64(b64variant, ch, 3, null);
+                }
+                decodedData >>= 2;
+                builder.appendTwoBytes(decodedData);
+            } else {
+                // otherwise, our triple is now complete
+                decodedData = (decodedData << 6) | bits;
+                builder.appendThreeBytes(decodedData);
+            }
+        }
+    }
+
+    /**
+     * @param bindex Relative index within base64 character unit; between 0
+     *   and 3 (as unit has exactly 4 characters)
+     */
+    protected void _reportInvalidBase64(Base64Variant b64variant, char ch, int bindex, String msg)
+        throws JsonParseException
+    {
+        String base;
+        if (ch <= INT_SPACE) {
+            base = "Illegal white space character (code 0x"+Integer.toHexString(ch)+") as character #"+(bindex+1)+" of 4-char base64 unit: can only used between units";
+        } else if (b64variant.usesPaddingChar(ch)) {
+            base = "Unexpected padding character ('"+b64variant.getPaddingChar()+"') as character #"+(bindex+1)+" of 4-char base64 unit: padding only legal as 3rd or 4th character";
+        } else if (!Character.isDefined(ch) || Character.isISOControl(ch)) {
+            // Not sure if we can really get here... ? (most illegal xml chars are caught at lower level)
+            base = "Illegal character (code 0x"+Integer.toHexString(ch)+") in base64 content";
+        } else {
+            base = "Illegal character '"+ch+"' (code 0x"+Integer.toHexString(ch)+") in base64 content";
+        }
+        if (msg != null) {
+            base = base + ": " + msg;
+        }
+        throw _constructError(base);
+    }
+
+    protected void _reportBase64EOF() throws JsonParseException {
+        throw _constructError("Unexpected end-of-String in base64 content");
+    }
+    
+    
+    /*
+    /**********************************************************
+    /* Error reporting
+    /**********************************************************
+     */
+    
+    protected void _reportUnexpectedChar(int ch, String comment)
+        throws JsonParseException
+    {
+        String msg = "Unexpected character ("+_getCharDesc(ch)+")";
+        if (comment != null) {
+            msg += ": "+comment;
+        }
+        _reportError(msg);
+    }
+
+    protected void _reportInvalidEOF()
+        throws JsonParseException
+    {
+        _reportInvalidEOF(" in "+_currToken);
+    }
+
+    protected void _reportInvalidEOF(String msg)
+        throws JsonParseException
+    {
+        _reportError("Unexpected end-of-input"+msg);
+    }
+
+    protected void _reportInvalidEOFInValue() throws JsonParseException
+    {
+        _reportInvalidEOF(" in a value");
+    }
+    
+    protected void _throwInvalidSpace(int i)
+        throws JsonParseException
+    {
+        char c = (char) i;
+        String msg = "Illegal character ("+_getCharDesc(c)+"): only regular white space (\\r, \\n, \\t) is allowed between tokens";
+        _reportError(msg);
+    }
+
+    /**
+     * Method called to report a problem with unquoted control character.
+     * Note: starting with version 1.4, it is possible to suppress
+     * exception by enabling {@link Feature#ALLOW_UNQUOTED_CONTROL_CHARS}.
+     */
+    protected void _throwUnquotedSpace(int i, String ctxtDesc)
+        throws JsonParseException
+    {
+        // JACKSON-208; possible to allow unquoted control chars:
+        if (!isEnabled(Feature.ALLOW_UNQUOTED_CONTROL_CHARS) || i >= INT_SPACE) {
+            char c = (char) i;
+            String msg = "Illegal unquoted character ("+_getCharDesc(c)+"): has to be escaped using backslash to be included in "+ctxtDesc;
+            _reportError(msg);
+        }
+    }
+
+    protected char _handleUnrecognizedCharacterEscape(char ch) throws JsonProcessingException
+    {
+        // as per [JACKSON-300]
+        if (isEnabled(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER)) {
+            return ch;
+        }
+        // and [JACKSON-548]
+        if (ch == '\'' && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
+            return ch;
+        }
+        _reportError("Unrecognized character escape "+_getCharDesc(ch));
+        return ch;
+    }
+    
+    /*
+    /**********************************************************
+    /* Error reporting, generic
+    /**********************************************************
+     */
+
+    protected final static String _getCharDesc(int ch)
+    {
+        char c = (char) ch;
+        if (Character.isISOControl(c)) {
+            return "(CTRL-CHAR, code "+ch+")";
+        }
+        if (ch > 255) {
+            return "'"+c+"' (code "+ch+" / 0x"+Integer.toHexString(ch)+")";
+        }
+        return "'"+c+"' (code "+ch+")";
+    }
+
+    protected final void _reportError(String msg)
+        throws JsonParseException
+    {
+        throw _constructError(msg);
+    }
+
+    protected final void _wrapError(String msg, Throwable t)
+        throws JsonParseException
+    {
+        throw _constructError(msg, t);
+    }
+
+    protected final void _throwInternal()
+    {
+        throw new RuntimeException("Internal error: this code path should never get executed");
+    }
+
+    protected final JsonParseException _constructError(String msg, Throwable t)
+    {
+        return new JsonParseException(msg, getCurrentLocation(), t);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/base/package-info.java b/src/main/java/com/fasterxml/jackson/core/base/package-info.java
new file mode 100644
index 0000000..ec4adbe
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/base/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * Base classes used by concrete Parser and Generator implementations;
+ * contain functionality that is not specific to JSON or input
+ * abstraction (byte vs char).
+ * Most formats extend these types, although it is also possible to
+ * directly extend {@link com.fasterxml.jackson.core.JsonParser} or
+ * {@link com.fasterxml.jackson.core.JsonGenerator}.
+ */
+package com.fasterxml.jackson.core.base;
diff --git a/src/main/java/com/fasterxml/jackson/core/format/DataFormatDetector.java b/src/main/java/com/fasterxml/jackson/core/format/DataFormatDetector.java
new file mode 100644
index 0000000..29c5d27
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/format/DataFormatDetector.java
@@ -0,0 +1,176 @@
+package com.fasterxml.jackson.core.format;
+
+import java.io.*;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Simple helper class that allows data format (content type) auto-detection,
+ * given an ordered set of {@link JsonFactory} instances to use for actual low-level
+ * detection.
+ *
+ * @since 1.7
+ */
+public class DataFormatDetector
+{
+    /**
+     * By default we will look ahead at most 64 bytes; in most cases,
+     * much less (4 bytes or so) is needed, but we will allow bit more
+     * leniency to support data formats that need more complex heuristics.
+     */
+    public final static int DEFAULT_MAX_INPUT_LOOKAHEAD = 64;
+    
+    /**
+     * Ordered list of factories which both represent data formats to
+     * detect (in precedence order, starting with highest) and are used
+     * for actual detection.
+     */
+    protected final JsonFactory[] _detectors;
+
+    /**
+     * Strength of match we consider to be good enough to be used
+     * without checking any other formats.
+     * Default value is {@link MatchStrength#SOLID_MATCH}, 
+     */
+    protected final MatchStrength _optimalMatch;
+
+    /**
+     * Strength of minimal match we accept as the answer, unless
+     * better matches are found. 
+     * Default value is {@link MatchStrength#WEAK_MATCH}, 
+     */
+    protected final MatchStrength _minimalMatch;
+
+    /**
+     * Maximum number of leading bytes of the input that we can read
+     * to determine data format.
+     *<p>
+     * Default value is {@link #DEFAULT_MAX_INPUT_LOOKAHEAD}.
+     */
+    protected final int _maxInputLookahead;
+    
+    /*
+    /**********************************************************
+    /* Construction
+    /**********************************************************
+     */
+    
+    public DataFormatDetector(JsonFactory... detectors) {
+        this(detectors, MatchStrength.SOLID_MATCH, MatchStrength.WEAK_MATCH,
+            DEFAULT_MAX_INPUT_LOOKAHEAD);
+    }
+
+    public DataFormatDetector(Collection<JsonFactory> detectors) {
+        this(detectors.toArray(new JsonFactory[detectors.size()]));
+    }
+
+    /**
+     * Method that will return a detector instance that uses given
+     * optimal match level (match that is considered sufficient to return, without
+     * trying to find stronger matches with other formats).
+     */
+    public DataFormatDetector withOptimalMatch(MatchStrength optMatch) {
+        if (optMatch == _optimalMatch) {
+            return this;
+        }
+        return new DataFormatDetector(_detectors, optMatch, _minimalMatch, _maxInputLookahead);
+    }
+    /**
+     * Method that will return a detector instance that uses given
+     * minimal match level; match that may be returned unless a stronger match
+     * is found with other format detectors.
+     */
+    public DataFormatDetector withMinimalMatch(MatchStrength minMatch) {
+        if (minMatch == _minimalMatch) {
+            return this;
+        }
+        return new DataFormatDetector(_detectors, _optimalMatch, minMatch, _maxInputLookahead);
+    }
+
+    /**
+     * Method that will return a detector instance that allows detectors to
+     * read up to specified number of bytes when determining format match strength.
+     */
+    public DataFormatDetector withMaxInputLookahead(int lookaheadBytes)
+    {
+        if (lookaheadBytes == _maxInputLookahead) {
+            return this;
+        }
+        return new DataFormatDetector(_detectors, _optimalMatch, _minimalMatch, lookaheadBytes);
+    }
+    
+    private DataFormatDetector(JsonFactory[] detectors,
+            MatchStrength optMatch, MatchStrength minMatch,
+            int maxInputLookahead)
+    {
+        _detectors = detectors;
+        _optimalMatch = optMatch;
+        _minimalMatch = minMatch;
+        _maxInputLookahead = maxInputLookahead;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    /**
+     * Method to call to find format that content (accessible via given
+     * {@link InputStream}) given has, as per configuration of this detector
+     * instance.
+     * 
+     * @return Matcher object which contains result; never null, even in cases
+     *    where no match (with specified minimal match strength) is found.
+     */
+    public DataFormatMatcher findFormat(InputStream in) throws IOException
+    {
+        return _findFormat(new InputAccessor.Std(in, new byte[_maxInputLookahead]));
+    }
+
+    /**
+     * Method to call to find format that given content (full document)
+     * has, as per configuration of this detector instance.
+     * 
+     * @return Matcher object which contains result; never null, even in cases
+     *    where no match (with specified minimal match strength) is found.
+     */
+    public DataFormatMatcher findFormat(byte[] fullInputData) throws IOException
+    {
+        return _findFormat(new InputAccessor.Std(fullInputData));
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    private DataFormatMatcher _findFormat(InputAccessor.Std acc) throws IOException
+    {
+        JsonFactory bestMatch = null;
+        MatchStrength bestMatchStrength = null;
+        for (JsonFactory f : _detectors) {
+            acc.reset();
+            MatchStrength strength = f.hasFormat(acc);
+            // if not better than what we have so far (including minimal level limit), skip
+            if (strength == null || strength.ordinal() < _minimalMatch.ordinal()) {
+                continue;
+            }
+            // also, needs to better match than before
+            if (bestMatch != null) {
+                if (bestMatchStrength.ordinal() >= strength.ordinal()) {
+                    continue;
+                }
+            }
+            // finally: if it's good enough match, we are done
+            bestMatch = f;
+            bestMatchStrength = strength;
+            if (strength.ordinal() >= _optimalMatch.ordinal()) {
+                break;
+            }
+        }
+        return acc.createMatcher(bestMatch, bestMatchStrength);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/format/DataFormatMatcher.java b/src/main/java/com/fasterxml/jackson/core/format/DataFormatMatcher.java
new file mode 100644
index 0000000..4698377
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/format/DataFormatMatcher.java
@@ -0,0 +1,117 @@
+package com.fasterxml.jackson.core.format;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.io.MergedStream;
+
+/**
+ * Result object constructed by {@link DataFormatDetector} when requested
+ * to detect format of given input data.
+ */
+public class DataFormatMatcher
+{
+    protected final InputStream _originalStream;
+
+    /**
+     * Content read during format matching process
+     */
+    protected final byte[] _bufferedData;
+
+    /**
+     * Number of bytes in {@link #_bufferedData} that were read.
+     */
+    protected final int _bufferedLength;
+
+    /**
+     * Factory that produced sufficient match (if any)
+     */
+    protected final JsonFactory _match;
+
+    /**
+     * Strength of match with {@link #_match}
+     */
+    protected final MatchStrength _matchStrength;
+    
+    protected DataFormatMatcher(InputStream in, byte[] buffered, int bufferedLength,
+            JsonFactory match, MatchStrength strength)
+    {
+        _originalStream = in;
+        _bufferedData = buffered;
+        _bufferedLength = bufferedLength;
+        _match = match;
+        _matchStrength = strength;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, simple accessors
+    /**********************************************************
+     */
+
+    /**
+     * Accessor to use to see if any formats matched well enough with
+     * the input data.
+     */
+    public boolean hasMatch() { return _match != null; }
+
+    /**
+     * Method for accessing strength of the match, if any; if no match,
+     * will return {@link MatchStrength#INCONCLUSIVE}.
+     */
+    public MatchStrength getMatchStrength() {
+        return (_matchStrength == null) ? MatchStrength.INCONCLUSIVE : _matchStrength;
+    }
+
+    /**
+     * Accessor for {@link JsonFactory} that represents format that data matched.
+     */
+    public JsonFactory getMatch() { return _match; }
+
+    /**
+     * Accessor for getting brief textual name of matched format if any (null
+     * if none). Equivalent to:
+     *<pre>
+     *   return hasMatch() ? getMatch().getFormatName() : null;
+     *</pre>
+     */
+    public String getMatchedFormatName() {
+        return _match.getFormatName();
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, factory methods
+    /**********************************************************
+     */
+    
+    /**
+     * Convenience method for trying to construct a {@link JsonParser} for
+     * parsing content which is assumed to be in detected data format.
+     * If no match was found, returns null.
+     */
+    public JsonParser createParserWithMatch() throws IOException {
+        if (_match == null) {
+            return null;
+        }
+        if (_originalStream == null) {
+            return _match.createJsonParser(_bufferedData, 0, _bufferedLength);
+        }
+        return _match.createJsonParser(getDataStream());
+    }
+    
+    /**
+     * Method to use for accessing input for which format detection has been done.
+     * This <b>must</b> be used instead of using stream passed to detector
+     * unless given stream itself can do buffering.
+     * Stream will return all content that was read during matching process, as well
+     * as remaining contents of the underlying stream.
+     */
+    public InputStream getDataStream() {
+        if (_originalStream == null) {
+            return new ByteArrayInputStream(_bufferedData, 0, _bufferedLength);
+        }
+        return new MergedStream(null, _originalStream, _bufferedData, 0, _bufferedLength);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/format/InputAccessor.java b/src/main/java/com/fasterxml/jackson/core/format/InputAccessor.java
new file mode 100644
index 0000000..e49550e
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/format/InputAccessor.java
@@ -0,0 +1,130 @@
+package com.fasterxml.jackson.core.format;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.JsonFactory;
+
+/**
+ * Interface used to expose beginning of a data file to data format
+ * detection code.
+ * 
+ * @since 1.8
+ */
+public interface InputAccessor
+{
+    /**
+     * Method to call to check if more input is available.
+     * Since this may result in more content to be read (at least
+     * one more byte), a {@link IOException} may get thrown.
+     */
+    public boolean hasMoreBytes() throws IOException;
+
+    /**
+     * Returns next byte available, if any; if no more bytes are
+     * available, will throw {@link java.io.EOFException}.
+     */
+    public byte nextByte() throws IOException;
+
+    /**
+     * Method that can be called to reset accessor to read from beginning
+     * of input.
+     */
+    public void reset();
+
+    /*
+    /**********************************************************
+    /* Standard implementation
+    /**********************************************************
+     */
+
+    /**
+     * Basic implementation that reads data from given
+     * {@link InputStream} and buffers it as necessary.
+     */
+    public class Std implements InputAccessor
+    {
+        protected final InputStream _in;
+
+        protected final byte[] _buffer;
+
+        /**
+         * Number of bytes in {@link #_buffer} that are valid
+         * buffered content.
+         */
+        protected int _bufferedAmount;
+        
+        /**
+         * Pointer to next available buffered byte in {@link #_buffer}.
+         */
+        protected int _ptr;
+        
+        /**
+         * Constructor used when content to check is available via
+         * input stream and must be read.
+         */
+        public Std(InputStream in, byte[] buffer)
+        {
+            _in = in;
+            _buffer = buffer;
+            _bufferedAmount = 0;
+        }
+
+        /**
+         * Constructor used when the full input (or at least enough leading bytes
+         * of full input) is available.
+         */
+        public Std(byte[] inputDocument)
+        {
+            _in = null;
+            _buffer = inputDocument;
+            // we have it all:
+            _bufferedAmount = inputDocument.length;
+        }
+        
+        @Override
+        public boolean hasMoreBytes() throws IOException
+        {
+            if (_ptr < _bufferedAmount) { // already got more
+                return true;
+            }
+            int amount = _buffer.length - _ptr;
+            if (amount < 1) { // can not load any more
+                return false;
+            }
+            int count = _in.read(_buffer, _ptr, amount);
+            if (count <= 0) { // EOF
+                return false;
+            }
+            _bufferedAmount += count;
+            return true;
+        }
+
+        @Override
+        public byte nextByte() throws IOException
+        {
+            // should we just try loading more automatically?
+            if (_ptr >- _bufferedAmount) {
+                if (!hasMoreBytes()) {
+                    throw new EOFException("Could not read more than "+_ptr+" bytes (max buffer size: "+_buffer.length+")");
+                }
+            }
+            return _buffer[_ptr++];
+        }
+
+        @Override
+        public void reset() {
+            _ptr = 0;
+        }
+
+        /*
+        /**********************************************************
+        /* Extended API for DataFormatDetector/Matcher
+        /**********************************************************
+         */
+
+        public DataFormatMatcher createMatcher(JsonFactory match, MatchStrength matchStrength)
+        {
+            return new DataFormatMatcher(_in, _buffer, _bufferedAmount, match, matchStrength);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/format/MatchStrength.java b/src/main/java/com/fasterxml/jackson/core/format/MatchStrength.java
new file mode 100644
index 0000000..2ba83d4
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/format/MatchStrength.java
@@ -0,0 +1,64 @@
+package com.fasterxml.jackson.core.format;
+
+/**
+ * Enumeration used to indicate strength of match between data format
+ * and piece of data (typically beginning of a data file).
+ * Values are in increasing match strength; and detectors should return
+ * "strongest" value: that is, it should start with strongest match
+ * criteria, and downgrading if criteria is not fulfilled.
+ * 
+ * @since 1.8
+ */
+public enum MatchStrength
+{
+    /**
+     * Value that indicates that given data can not be in given format.
+     */
+    NO_MATCH,
+    
+    /**
+     * Value that indicates that detector can not find out whether could
+     * be a match or not.
+     * This can occur for example for textual data formats t
+     * when there are so many leading spaces that detector can not
+     * find the first data byte (because detectors typically limit lookahead
+     * to some smallish value).
+     */
+    INCONCLUSIVE,
+
+    /**
+     * Value that indicates that given data could be of specified format (i.e.
+     * it can not be ruled out). This can occur for example when seen data
+     * is both not in canonical formats (for example: JSON data should be a JSON Array or Object
+     * not a scalar value, as per JSON specification) and there are known use case
+     * where a format detected is actually used (plain JSON Strings are actually used, even
+     * though specification does not indicate that as valid usage: as such, seeing a leading
+     * double-quote could indicate a JSON String, which plausibly <b>could</b> indicate
+     * non-standard JSON usage).
+     */
+    WEAK_MATCH,
+    
+    /**
+     * Value that indicates that given data conforms to (one of) canonical form(s) of
+     * the data format.
+     *<p>
+     * For example, when testing for XML data format,
+     * seeing a less-than character ("&lt;") alone (with possible leading spaces)
+     * would be a strong indication that data could
+     * be in xml format (but see below for {@link #FULL_MATCH} description for more)
+     */
+    SOLID_MATCH,
+
+    /**
+     * Value that indicates that given data contains a signature that is deemed
+     * specific enough to uniquely indicate data format used.
+     *<p>
+     * For example, when testing for XML data format,
+     * seing "&lt;xml" as the first data bytes ("XML declaration", as per XML specification)
+     * could give full confidence that data is indeed in XML format.
+     * Not all data formats have unique leading identifiers to allow full matches; for example,
+     * JSON only has heuristic matches and can have at most {@link #SOLID_MATCH}) match.
+     */
+    FULL_MATCH
+    ;
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/format/package-info.java b/src/main/java/com/fasterxml/jackson/core/format/package-info.java
new file mode 100644
index 0000000..bd10394
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/format/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * Package that contains interfaces needed for dynamic, pluggable
+ * format (auto)detection; as well as basic utility classes for
+ * simple format detection functionality.
+ * 
+ * @since 1.8
+ */
+package com.fasterxml.jackson.core.format;
diff --git a/src/main/java/com/fasterxml/jackson/core/io/BaseReader.java b/src/main/java/com/fasterxml/jackson/core/io/BaseReader.java
new file mode 100644
index 0000000..07ab5b0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/BaseReader.java
@@ -0,0 +1,116 @@
+
+package com.fasterxml.jackson.core.io;
+
+import java.io.*;
+
+/**
+ * Simple basic class for optimized readers in this package; implements
+ * "cookie-cutter" methods that are used by all actual implementations.
+ */
+abstract class BaseReader
+    extends Reader
+{
+    /**
+     * JSON actually limits available Unicode range in the high end
+     * to the same as xml (to basically limit UTF-8 max byte sequence
+     * length to 4)
+     */
+    final protected static int LAST_VALID_UNICODE_CHAR = 0x10FFFF;
+
+    final protected static char NULL_CHAR = (char) 0;
+    final protected static char NULL_BYTE = (byte) 0;
+
+    final protected IOContext _context;
+
+    protected InputStream _in;
+
+    protected byte[] _buffer;
+
+    protected int _ptr;
+    protected int _length;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected BaseReader(IOContext context,
+                         InputStream in, byte[] buf, int ptr, int len)
+    {
+        _context = context;
+        _in = in;
+        _buffer = buf;
+        _ptr = ptr;
+        _length = len;
+    }
+
+    /*
+    /**********************************************************
+    /* Reader API
+    /**********************************************************
+     */
+
+    @Override
+    public void close() throws IOException
+    {
+        InputStream in = _in;
+
+        if (in != null) {
+            _in = null;
+            freeBuffers();
+            in.close();
+        }
+    }
+
+    protected char[] _tmpBuf = null;
+
+    /**
+     * Although this method is implemented by the base class, AND it should
+     * never be called by main code, let's still implement it bit more
+     * efficiently just in case
+     */
+    @Override
+    public int read() throws IOException
+    {
+        if (_tmpBuf == null) {
+            _tmpBuf = new char[1];
+        }
+        if (read(_tmpBuf, 0, 1) < 1) {
+            return -1;
+        }
+        return _tmpBuf[0];
+    }
+
+    /*
+    /**********************************************************
+    /* Internal/package methods:
+    /**********************************************************
+     */
+
+    /**
+     * This method should be called along with (or instead of) normal
+     * close. After calling this method, no further reads should be tried.
+     * Method will try to recycle read buffers (if any).
+     */
+    public final void freeBuffers()
+    {
+        byte[] buf = _buffer;
+        if (buf != null) {
+            _buffer = null;
+            _context.releaseReadIOBuffer(buf);
+        }
+    }
+
+    protected void reportBounds(char[] cbuf, int start, int len)
+        throws IOException
+    {
+        throw new ArrayIndexOutOfBoundsException("read(buf,"+start+","+len+"), cbuf["+cbuf.length+"]");
+    }
+
+    protected void reportStrangeStream()
+        throws IOException
+    {
+        throw new IOException("Strange I/O stream, returned 0 bytes on read");
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/CharTypes.java b/src/main/java/com/fasterxml/jackson/core/io/CharTypes.java
new file mode 100644
index 0000000..6a8f1b7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/CharTypes.java
@@ -0,0 +1,236 @@
+package com.fasterxml.jackson.core.io;
+
+import java.util.Arrays;
+
+
+public final class CharTypes
+{
+    private final static char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
+    private final static byte[] HEX_BYTES;
+    static {
+        int len = HEX_CHARS.length;
+        HEX_BYTES = new byte[len];
+        for (int i = 0; i < len; ++i) {
+            HEX_BYTES[i] = (byte) HEX_CHARS[i];
+        }
+    }
+
+
+    /**
+     * Lookup table used for determining which input characters
+     * need special handling when contained in text segment.
+     */
+    final static int[] sInputCodes;
+    static {
+        /* 96 would do for most cases (backslash is ascii 94)
+         * but if we want to do lookups by raw bytes it's better
+         * to have full table
+         */
+        int[] table = new int[256];
+        // Control chars and non-space white space are not allowed unquoted
+        for (int i = 0; i < 32; ++i) {
+            table[i] = -1;
+        }
+        // And then string end and quote markers are special too
+        table['"'] = 1;
+        table['\\'] = 1;
+        sInputCodes = table;
+    }
+
+    /**
+     * Additionally we can combine UTF-8 decoding info into similar
+     * data table.
+     */
+    final static int[] sInputCodesUtf8;
+    static {
+        int[] table = new int[sInputCodes.length];
+        System.arraycopy(sInputCodes, 0, table, 0, sInputCodes.length);
+        for (int c = 128; c < 256; ++c) {
+            int code;
+
+            // We'll add number of bytes needed for decoding
+            if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
+                code = 2;
+            } else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
+                code = 3;
+            } else if ((c & 0xF8) == 0xF0) {
+                // 4 bytes; double-char with surrogates and all...
+                code = 4;
+            } else {
+                // And -1 seems like a good "universal" error marker...
+                code = -1;
+            }
+            table[c] = code;
+        }
+        sInputCodesUtf8 = table;
+    }
+
+    /**
+     * To support non-default (and -standard) unquoted field names mode,
+     * need to have alternate checking.
+     * Basically this is list of 8-bit ASCII characters that are legal
+     * as part of Javascript identifier
+     *
+     * @since 1.2
+     */
+    final static int[] sInputCodesJsNames;
+    static {
+        int[] table = new int[256];
+        // Default is "not a name char", mark ones that are
+        Arrays.fill(table, -1);
+        // Assume rules with JS same as Java (change if/as needed)
+        for (int i = 33; i < 256; ++i) {
+            if (Character.isJavaIdentifierPart((char) i)) {
+                table[i] = 0;
+            }
+        }
+        /* As per [JACKSON-267], '@', '#' and '*' are also to be accepted as well.
+         * And '-' (for hyphenated names); and '+' for sake of symmetricity...
+         */
+        table['@'] = 0;
+        table['#'] = 0;
+        table['*'] = 0;
+        table['-'] = 0;
+        table['+'] = 0;
+        sInputCodesJsNames = table;
+    }
+
+    /**
+     * This table is similar to Latin-1, except that it marks all "high-bit"
+     * code as ok. They will be validated at a later point, when decoding
+     * name
+     */
+    final static int[] sInputCodesUtf8JsNames;
+    static {
+        int[] table = new int[256];
+        // start with 8-bit JS names 
+        System.arraycopy(sInputCodesJsNames, 0, table, 0, sInputCodesJsNames.length);
+        Arrays.fill(table, 128, 128, 0);
+        sInputCodesUtf8JsNames = table;
+    }
+
+    /**
+     * Decoding table used to quickly determine characters that are
+     * relevant within comment content
+     */
+    final static int[] sInputCodesComment = new int[256];
+    static {
+        // but first: let's start with UTF-8 multi-byte markers:
+        System.arraycopy(sInputCodesUtf8, 128, sInputCodesComment, 128, 128);
+    
+        // default (0) means "ok" (skip); -1 invalid, others marked by char itself
+        Arrays.fill(sInputCodesComment, 0, 32, -1); // invalid white space
+        sInputCodesComment['\t'] = 0; // tab is still fine
+        sInputCodesComment['\n'] = '\n'; // lf/cr need to be observed, ends cpp comment
+        sInputCodesComment['\r'] = '\r';
+        sInputCodesComment['*'] = '*'; // end marker for c-style comments
+    }
+
+    /**
+     * Lookup table used for determining which output characters in 
+     * 7-bit ASCII range need to be quoted.
+     */
+    final static int[] sOutputEscapes128;
+    static {
+        int[] table = new int[128];
+        // Control chars need generic escape sequence
+        for (int i = 0; i < 32; ++i) {
+            // 04-Mar-2011, tatu: Used to use "-(i + 1)", replaced with constants
+            table[i] = CharacterEscapes.ESCAPE_STANDARD;
+        }
+        /* Others (and some within that range too) have explicit shorter
+         * sequences
+         */
+        table['"'] = '"';
+        table['\\'] = '\\';
+        // Escaping of slash is optional, so let's not add it
+        table[0x08] = 'b';
+        table[0x09] = 't';
+        table[0x0C] = 'f';
+        table[0x0A] = 'n';
+        table[0x0D] = 'r';
+        sOutputEscapes128 = table;
+    }
+
+    /**
+     * Lookup table for the first 128 Unicode characters (7-bit ASCII)
+     * range. For actual hex digits, contains corresponding value;
+     * for others -1.
+     */
+    final static int[] sHexValues = new int[128];
+    static {
+        Arrays.fill(sHexValues, -1);
+        for (int i = 0; i < 10; ++i) {
+            sHexValues['0' + i] = i;
+        }
+        for (int i = 0; i < 6; ++i) {
+            sHexValues['a' + i] = 10 + i;
+            sHexValues['A' + i] = 10 + i;
+        }
+    }
+
+    public final static int[] getInputCodeLatin1() { return sInputCodes; }
+    public final static int[] getInputCodeUtf8() { return sInputCodesUtf8; }
+
+    public final static int[] getInputCodeLatin1JsNames() { return sInputCodesJsNames; }
+    public final static int[] getInputCodeUtf8JsNames() { return sInputCodesUtf8JsNames; }
+
+    public final static int[] getInputCodeComment() { return sInputCodesComment; }
+    
+    /**
+     * Accessor for getting a read-only encoding table for first 128 Unicode
+     * code points (single-byte UTF-8 characters).
+     * Value of 0 means "no escaping"; other positive values that value is character
+     * to use after backslash; and negative values that generic (backslash - u)
+     * escaping is to be used.
+     */
+    public final static int[] get7BitOutputEscapes() { return sOutputEscapes128; }
+
+    public static int charToHex(int ch)
+    {
+        return (ch > 127) ? -1 : sHexValues[ch];
+    }
+
+    public static void appendQuoted(StringBuilder sb, String content)
+    {
+        final int[] escCodes = sOutputEscapes128;
+        int escLen = escCodes.length;
+        for (int i = 0, len = content.length(); i < len; ++i) {
+            char c = content.charAt(i);
+            if (c >= escLen || escCodes[c] == 0) {
+                sb.append(c);
+                continue;
+            }
+            sb.append('\\');
+            int escCode = escCodes[c];
+            if (escCode < 0) { // generic quoting (hex value)
+                // We know that it has to fit in just 2 hex chars
+                sb.append('u');
+                sb.append('0');
+                sb.append('0');
+                int value = -(escCode + 1);
+                sb.append(HEX_CHARS[value >> 4]);
+                sb.append(HEX_CHARS[value & 0xF]);
+            } else { // "named", i.e. prepend with slash
+                sb.append((char) escCode);
+            }
+        }
+    }
+
+    /**
+     * @since 1.6
+     */
+    public static char[] copyHexChars()
+    {
+        return (char[]) HEX_CHARS.clone();
+    }
+
+    /**
+     * @since 1.6
+     */
+    public static byte[] copyHexBytes()
+    {
+        return (byte[]) HEX_BYTES.clone();
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/core/io/CharacterEscapes.java b/src/main/java/com/fasterxml/jackson/core/io/CharacterEscapes.java
new file mode 100644
index 0000000..12960b1
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/CharacterEscapes.java
@@ -0,0 +1,72 @@
+package com.fasterxml.jackson.core.io;
+
+import com.fasterxml.jackson.core.SerializableString;
+
+/**
+ * Abstract base class that defines interface for customizing character
+ * escaping aspects for String values, for formats that use escaping.
+ * For JSON this applies to both property names and String values.
+ *
+ * @since 1.8
+ */
+public abstract class CharacterEscapes
+{
+    /**
+     * Value used for lookup tables to indicate that matching characters
+     * do not need to be escaped.
+     */
+    public final static int ESCAPE_NONE = 0;
+
+    /**
+     * Value used for lookup tables to indicate that matching characters
+     * are to be escaped using standard escaping; for JSON this means
+     * (for example) using "backslash - u" escape method.
+     */
+    public final static int ESCAPE_STANDARD = -1;
+
+    /**
+     * Value used for lookup tables to indicate that matching characters
+     * will need custom escapes; and that another call
+     * to {@link #getEscapeSequence} is needed to figure out exact escape
+     * sequence to output.
+     */
+    public final static int ESCAPE_CUSTOM = -2;
+    
+    /**
+     * Method generators can call to get lookup table for determining
+     * escape handling for first 128 characters of Unicode (ASCII
+     * characters. Caller is not to modify contents of this array, since
+     * this is expected to be a shared copy.
+     * 
+     * @return Array with size of at least 128, where first 128 entries
+     *    have either one of <code>ESCAPE_xxx</code> constants, or non-zero positive
+     *    integer (meaning of which is data format specific; for JSON it means
+     *    that combination of backslash and character with that value is to be used)
+     *    to indicate that specific escape sequence is to be used.
+     */
+    public abstract int[] getEscapeCodesForAscii();
+
+    /**
+     * Method generators can call to get lookup table for determining
+     * exact escape sequence to use for given character.
+     * It can be called for any character, but typically is called for
+     * either for ASCII characters for which custom escape
+     * sequence is needed; or for any non-ASCII character.
+     */
+    public abstract SerializableString getEscapeSequence(int ch);
+
+    /**
+     * Helper method that can be used to get a copy of standard JSON
+     * escape definitions; this is useful when just wanting to slightly
+     * customize definitions. Caller can modify this array as it sees
+     * fit and usually returns modified instance via {@link #getEscapeCodesForAscii}
+     */
+    public static int[] standardAsciiEscapesForJSON()
+    {
+        int[] esc = CharTypes.get7BitOutputEscapes();
+        int len = esc.length;
+        int[] result = new int[len];
+        System.arraycopy(esc, 0, result, 0, esc.length);
+        return result;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/IOContext.java b/src/main/java/com/fasterxml/jackson/core/io/IOContext.java
new file mode 100644
index 0000000..1ae4fb8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/IOContext.java
@@ -0,0 +1,239 @@
+package com.fasterxml.jackson.core.io;
+
+import com.fasterxml.jackson.core.JsonEncoding;
+import com.fasterxml.jackson.core.util.BufferRecycler;
+import com.fasterxml.jackson.core.util.TextBuffer;
+
+/**
+ * To limit number of configuration and state objects to pass, all
+ * contextual objects that need to be passed by the factory to
+ * readers and writers are combined under this object. One instance
+ * is created for each reader and writer.
+ */
+public final class IOContext
+{
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    /**
+     * Reference to the source object, which can be used for displaying
+     * location information
+     */
+    protected final Object _sourceRef;
+
+    /**
+     * Encoding used by the underlying stream, if known.
+     */
+    protected JsonEncoding _encoding;
+
+    /**
+     * Flag that indicates whether underlying input/output source/target
+     * object is fully managed by the owner of this context (parser or
+     * generator). If true, it is, and is to be closed by parser/generator;
+     * if false, calling application has to do closing (unless auto-closing
+     * feature is enabled for the parser/generator in question; in which
+     * case it acts like the owner).
+     */
+    protected final boolean _managedResource;
+
+    /*
+    /**********************************************************
+    /* Buffer handling, recycling
+    /**********************************************************
+     */
+
+    /**
+     * Recycler used for actual allocation/deallocation/reuse
+     */
+    protected final BufferRecycler _bufferRecycler;
+
+    /**
+     * Reference to the allocated I/O buffer for low-level input reading,
+     * if any allocated.
+     */
+    protected byte[] _readIOBuffer = null;
+
+    /**
+     * Reference to the allocated I/O buffer used for low-level
+     * encoding-related buffering.
+     */
+    protected byte[] _writeEncodingBuffer = null;
+    
+    /**
+     * Reference to the buffer allocated for tokenization purposes,
+     * in which character input is read, and from which it can be
+     * further returned.
+     */
+    protected char[] _tokenCBuffer = null;
+
+    /**
+     * Reference to the buffer allocated for buffering it for
+     * output, before being encoded: generally this means concatenating
+     * output, then encoding when buffer fills up.
+     */
+    protected char[] _concatCBuffer = null;
+
+    /**
+     * Reference temporary buffer Parser instances need if calling
+     * app decides it wants to access name via 'getTextCharacters' method.
+     * Regular text buffer can not be used as it may contain textual
+     * representation of the value token.
+     */
+    protected char[] _nameCopyBuffer = null;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public IOContext(BufferRecycler br, Object sourceRef, boolean managedResource)
+    {
+        _bufferRecycler = br;
+        _sourceRef = sourceRef;
+        _managedResource = managedResource;
+    }
+
+    public void setEncoding(JsonEncoding enc)
+    {
+        _encoding = enc;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, accessors
+    /**********************************************************
+     */
+
+    public final Object getSourceReference() { return _sourceRef; }
+    public final JsonEncoding getEncoding() { return _encoding; }
+    public final boolean isResourceManaged() { return _managedResource; }
+    
+    /*
+    /**********************************************************
+    /* Public API, buffer management
+    /**********************************************************
+     */
+
+    public final TextBuffer constructTextBuffer() {
+        return new TextBuffer(_bufferRecycler);
+    }
+
+    /**
+     *<p>
+     * Note: the method can only be called once during its life cycle.
+     * This is to protect against accidental sharing.
+     */
+    public final byte[] allocReadIOBuffer()
+    {
+        if (_readIOBuffer != null) {
+            throw new IllegalStateException("Trying to call allocReadIOBuffer() second time");
+        }
+        _readIOBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.READ_IO_BUFFER);
+        return _readIOBuffer;
+    }
+
+    public final byte[] allocWriteEncodingBuffer()
+    {
+        if (_writeEncodingBuffer != null) {
+            throw new IllegalStateException("Trying to call allocWriteEncodingBuffer() second time");
+        }
+        _writeEncodingBuffer = _bufferRecycler.allocByteBuffer(BufferRecycler.ByteBufferType.WRITE_ENCODING_BUFFER);
+        return _writeEncodingBuffer;
+    }
+    
+    public final char[] allocTokenBuffer()
+    {
+        if (_tokenCBuffer != null) {
+            throw new IllegalStateException("Trying to call allocTokenBuffer() second time");
+        }
+        _tokenCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.TOKEN_BUFFER);
+        return _tokenCBuffer;
+    }
+
+    public final char[] allocConcatBuffer()
+    {
+        if (_concatCBuffer != null) {
+            throw new IllegalStateException("Trying to call allocConcatBuffer() second time");
+        }
+        _concatCBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.CONCAT_BUFFER);
+        return _concatCBuffer;
+    }
+
+    public final char[] allocNameCopyBuffer(int minSize)
+    {
+        if (_nameCopyBuffer != null) {
+            throw new IllegalStateException("Trying to call allocNameCopyBuffer() second time");
+        }
+        _nameCopyBuffer = _bufferRecycler.allocCharBuffer(BufferRecycler.CharBufferType.NAME_COPY_BUFFER, minSize);
+        return _nameCopyBuffer;
+    }
+
+    /**
+     * Method to call when all the processing buffers can be safely
+     * recycled.
+     */
+    public final void releaseReadIOBuffer(byte[] buf)
+    {
+        if (buf != null) {
+            /* Let's do sanity checks to ensure once-and-only-once release,
+             * as well as avoiding trying to release buffers not owned
+             */
+            if (buf != _readIOBuffer) {
+                throw new IllegalArgumentException("Trying to release buffer not owned by the context");
+            }
+            _readIOBuffer = null;
+            _bufferRecycler.releaseByteBuffer(BufferRecycler.ByteBufferType.READ_IO_BUFFER, buf);
+        }
+    }
+
+    public final void releaseWriteEncodingBuffer(byte[] buf)
+    {
+        if (buf != null) {
+            /* Let's do sanity checks to ensure once-and-only-once release,
+             * as well as avoiding trying to release buffers not owned
+             */
+            if (buf != _writeEncodingBuffer) {
+                throw new IllegalArgumentException("Trying to release buffer not owned by the context");
+            }
+            _writeEncodingBuffer = null;
+            _bufferRecycler.releaseByteBuffer(BufferRecycler.ByteBufferType.WRITE_ENCODING_BUFFER, buf);
+        }
+    }
+    
+    public final void releaseTokenBuffer(char[] buf)
+    {
+        if (buf != null) {
+            if (buf != _tokenCBuffer) {
+                throw new IllegalArgumentException("Trying to release buffer not owned by the context");
+            }
+            _tokenCBuffer = null;
+            _bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.TOKEN_BUFFER, buf);
+        }
+    }
+
+    public final void releaseConcatBuffer(char[] buf)
+    {
+        if (buf != null) {
+            if (buf != _concatCBuffer) {
+                throw new IllegalArgumentException("Trying to release buffer not owned by the context");
+            }
+            _concatCBuffer = null;
+            _bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.CONCAT_BUFFER, buf);
+        }
+    }
+
+    public final void releaseNameCopyBuffer(char[] buf)
+    {
+        if (buf != null) {
+            if (buf != _nameCopyBuffer) {
+                throw new IllegalArgumentException("Trying to release buffer not owned by the context");
+            }
+            _nameCopyBuffer = null;
+            _bufferRecycler.releaseCharBuffer(BufferRecycler.CharBufferType.NAME_COPY_BUFFER, buf);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/InputDecorator.java b/src/main/java/com/fasterxml/jackson/core/io/InputDecorator.java
new file mode 100644
index 0000000..2cf2417
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/InputDecorator.java
@@ -0,0 +1,67 @@
+package com.fasterxml.jackson.core.io;
+
+import java.io.*;
+
+/**
+ * Handler class that can be used to decorate input sources.
+ * Typical use is to use a filter abstraction (filtered stream,
+ * reader) around original input source, and apply additional
+ * processing during read operations.
+ * 
+ * @since 1.8
+ */
+public abstract class InputDecorator
+{
+    /**
+     * Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
+     * creating parser given an {@link InputStream}, when this decorator
+     * has been registered.
+     * 
+     * @param ctxt IO context in use (provides access to declared encoding).
+     *   NOTE: at this point context may not have all information initialized;
+     *   specifically auto-detected encoding is only available once parsing starts,
+     *   which may occur only after this method is called.
+     * @param in Original input source
+     * 
+     * @return InputStream to use; either passed in argument, or something that
+     *   calls it
+     */
+    public abstract InputStream decorate(IOContext ctxt, InputStream in)
+        throws IOException;
+
+    /**
+     * Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
+     * creating parser on given "raw" byte source.
+     * Method can either construct a {@link InputStream} for reading; or return
+     * null to indicate that no wrapping should occur.
+     * 
+     * @param ctxt IO context in use (provides access to declared encoding)
+     *   NOTE: at this point context may not have all information initialized;
+     *   specifically auto-detected encoding is only available once parsing starts,
+     *   which may occur only after this method is called.
+     * @param src Input buffer that contains contents to parse
+     * @param offset Offset of the first available byte in the input buffer
+     * @param length Number of bytes available in the input buffer
+     * 
+     * @return Either {@link InputStream} to use as input source; or null to indicate
+     *   that contents are to be processed as-is by caller
+     */
+    public abstract InputStream decorate(IOContext ctxt, byte[] src, int offset, int length)
+        throws IOException;
+    
+    /**
+     * Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
+     * creating parser given an {@link Reader}, when this decorator
+     * has been registered.
+     * 
+     * @param ctxt IO context in use (provides access to declared encoding)
+     *   NOTE: at this point context may not have all information initialized;
+     *   specifically auto-detected encoding is only available once parsing starts,
+     *   which may occur only after this method is called.
+     * @param src Original input source
+     * 
+     * @return Reader to use; either passed in argument, or something that
+     *   calls it (for example, a {@link FilterReader})
+     */
+    public abstract Reader decorate(IOContext ctxt, Reader src) throws IOException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/JsonStringEncoder.java b/src/main/java/com/fasterxml/jackson/core/io/JsonStringEncoder.java
new file mode 100644
index 0000000..56805af
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/JsonStringEncoder.java
@@ -0,0 +1,407 @@
+package com.fasterxml.jackson.core.io;
+
+import java.lang.ref.SoftReference;
+
+import com.fasterxml.jackson.core.util.BufferRecycler;
+import com.fasterxml.jackson.core.util.ByteArrayBuilder;
+import com.fasterxml.jackson.core.util.TextBuffer;
+
+/**
+ * Helper class used for efficient encoding of JSON String values (including
+ * JSON field names) into Strings or UTF-8 byte arrays.
+ *<p>
+ * Note that methods in here are somewhat optimized, but not ridiculously so.
+ * Reason is that conversion method results are expected to be cached so that
+ * these methods will not be hot spots during normal operation.
+ *
+ * @since 1.6
+ */
+public final class JsonStringEncoder
+{
+    private final static char[] HEX_CHARS = CharTypes.copyHexChars();
+
+    private final static byte[] HEX_BYTES = CharTypes.copyHexBytes();
+
+    private final static int SURR1_FIRST = 0xD800;
+    private final static int SURR1_LAST = 0xDBFF;
+    private final static int SURR2_FIRST = 0xDC00;
+    private final static int SURR2_LAST = 0xDFFF;
+
+    private final static int INT_BACKSLASH = '\\';
+    private final static int INT_U = 'u';
+    private final static int INT_0 = '0';
+    
+    /**
+     * This <code>ThreadLocal</code> contains a {@link java.lang.ref.SoftRerefence}
+     * to a {@link BufferRecycler} used to provide a low-cost
+     * buffer recycling between reader and writer instances.
+     */
+    final protected static ThreadLocal<SoftReference<JsonStringEncoder>> _threadEncoder
+        = new ThreadLocal<SoftReference<JsonStringEncoder>>();
+
+    /**
+     * Lazily constructed text buffer used to produce JSON encoded Strings
+     * as characters (without UTF-8 encoding)
+     */
+    protected TextBuffer _textBuffer;
+
+    /**
+     * Lazily-constructed builder used for UTF-8 encoding of text values
+     * (quoted and unquoted)
+     */
+    protected ByteArrayBuilder _byteBuilder;
+    
+    /**
+     * Temporary buffer used for composing quote/escape sequences
+     */
+    protected final char[] _quoteBuffer;
+    
+    /*
+    /**********************************************************
+    /* Construction, instance access
+    /**********************************************************
+     */
+    
+    public JsonStringEncoder()
+    {
+        _quoteBuffer = new char[6];
+        _quoteBuffer[0] = '\\';
+        _quoteBuffer[2] = '0';
+        _quoteBuffer[3] = '0';
+    }
+    
+    /**
+     * Factory method for getting an instance; this is either recycled per-thread instance,
+     * or a newly constructed one.
+     */
+    public static JsonStringEncoder getInstance()
+    {
+        SoftReference<JsonStringEncoder> ref = _threadEncoder.get();
+        JsonStringEncoder enc = (ref == null) ? null : ref.get();
+
+        if (enc == null) {
+            enc = new JsonStringEncoder();
+            _threadEncoder.set(new SoftReference<JsonStringEncoder>(enc));
+        }
+        return enc;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API
+    /**********************************************************
+     */
+
+    /**
+     * Method that will quote text contents using JSON standard quoting,
+     * and return results as a character array
+     */
+    public char[] quoteAsString(String input)
+    {
+        TextBuffer textBuffer = _textBuffer;
+        if (textBuffer == null) {
+            // no allocator; can add if we must, shouldn't need to
+            _textBuffer = textBuffer = new TextBuffer(null);
+        }
+        char[] outputBuffer = textBuffer.emptyAndGetCurrentSegment();
+        final int[] escCodes = CharTypes.get7BitOutputEscapes();
+        final int escCodeCount = escCodes.length;
+        int inPtr = 0;
+        final int inputLen = input.length();
+        int outPtr = 0;
+ 
+        outer_loop:
+        while (inPtr < inputLen) {
+            tight_loop:
+            while (true) {
+                char c = input.charAt(inPtr);
+                if (c < escCodeCount && escCodes[c] != 0) {
+                    break tight_loop;
+                }
+                if (outPtr >= outputBuffer.length) {
+                    outputBuffer = textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                outputBuffer[outPtr++] = c;
+                if (++inPtr >= inputLen) {
+                    break outer_loop;
+                }
+            }
+            // something to escape; 2 or 6-char variant? 
+            int escCode = escCodes[input.charAt(inPtr++)];
+            int length = _appendSingleEscape(escCode, _quoteBuffer);
+            if ((outPtr + length) > outputBuffer.length) {
+                int first = outputBuffer.length - outPtr;
+                if (first > 0) {
+                    System.arraycopy(_quoteBuffer, 0, outputBuffer, outPtr, first);
+                }
+                outputBuffer = textBuffer.finishCurrentSegment();
+                int second = length - first;
+                System.arraycopy(_quoteBuffer, first, outputBuffer, outPtr, second);
+                outPtr += second;
+            } else {
+                System.arraycopy(_quoteBuffer, 0, outputBuffer, outPtr, length);
+                outPtr += length;
+            }
+            
+        }
+        textBuffer.setCurrentLength(outPtr);
+        return textBuffer.contentsAsArray();
+    }
+
+    /**
+     * Will quote given JSON String value using standard quoting, encode
+     * results as UTF-8, and return result as a byte array.
+     */
+    public byte[] quoteAsUTF8(String text)
+    {
+        ByteArrayBuilder byteBuilder = _byteBuilder;
+        if (byteBuilder == null) {
+            // no allocator; can add if we must, shouldn't need to
+            _byteBuilder = byteBuilder = new ByteArrayBuilder(null);
+        }
+        int inputPtr = 0;
+        int inputEnd = text.length();
+        int outputPtr = 0;
+        byte[] outputBuffer = byteBuilder.resetAndGetFirstSegment();
+        
+        main_loop:
+        while (inputPtr < inputEnd) {
+            final int[] escCodes = CharTypes.get7BitOutputEscapes();
+
+            inner_loop: // ascii and escapes
+            while (true) {
+                int ch = text.charAt(inputPtr);
+                if (ch > 0x7F || escCodes[ch] != 0) {
+                    break inner_loop;
+                }
+                if (outputPtr >= outputBuffer.length) {
+                    outputBuffer = byteBuilder.finishCurrentSegment();
+                    outputPtr = 0;
+                }
+                outputBuffer[outputPtr++] = (byte) ch;
+                if (++inputPtr >= inputEnd) {
+                    break main_loop;
+                }
+            }                
+            if (outputPtr >= outputBuffer.length) {
+                outputBuffer = byteBuilder.finishCurrentSegment();
+                outputPtr = 0;
+            }
+            // Ok, so what did we hit?
+            int ch = (int) text.charAt(inputPtr++);
+            if (ch <= 0x7F) { // needs quoting
+                int escape = escCodes[ch];
+                // ctrl-char, 6-byte escape...
+                outputPtr = _appendByteEscape(ch, escape, byteBuilder, outputPtr);
+                outputBuffer = byteBuilder.getCurrentSegment();
+                continue main_loop;
+            } else if (ch <= 0x7FF) { // fine, just needs 2 byte output
+                outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
+                ch = (0x80 | (ch & 0x3f));
+            } else { // 3 or 4 bytes
+                // Surrogates?
+                if (ch < SURR1_FIRST || ch > SURR2_LAST) { // nope
+                    outputBuffer[outputPtr++] = (byte) (0xe0 | (ch >> 12));
+                    if (outputPtr >= outputBuffer.length) {
+                        outputBuffer = byteBuilder.finishCurrentSegment();
+                        outputPtr = 0;
+                    }
+                    outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
+                    ch = (0x80 | (ch & 0x3f));
+                } else { // yes, surrogate pair
+                    if (ch > SURR1_LAST) { // must be from first range
+                        _throwIllegalSurrogate(ch);
+                    }
+                    // and if so, followed by another from next range
+                    if (inputPtr >= inputEnd) {
+                        _throwIllegalSurrogate(ch);
+                    }
+                    ch = _convertSurrogate(ch, text.charAt(inputPtr++));
+                    if (ch > 0x10FFFF) { // illegal, as per RFC 4627
+                        _throwIllegalSurrogate(ch);
+                    }
+                    outputBuffer[outputPtr++] = (byte) (0xf0 | (ch >> 18));
+                    if (outputPtr >= outputBuffer.length) {
+                        outputBuffer = byteBuilder.finishCurrentSegment();
+                        outputPtr = 0;
+                    }
+                    outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 12) & 0x3f));
+                    if (outputPtr >= outputBuffer.length) {
+                        outputBuffer = byteBuilder.finishCurrentSegment();
+                        outputPtr = 0;
+                    }
+                    outputBuffer[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
+                    ch = (0x80 | (ch & 0x3f));
+                }
+            }
+            if (outputPtr >= outputBuffer.length) {
+                outputBuffer = byteBuilder.finishCurrentSegment();
+                outputPtr = 0;
+            }
+            outputBuffer[outputPtr++] = (byte) ch;
+        }
+        return _byteBuilder.completeAndCoalesce(outputPtr);
+    }
+    
+    /**
+     * Will encode given String as UTF-8 (without any quoting), return
+     * resulting byte array.
+     */
+    public byte[] encodeAsUTF8(String text)
+    {
+        ByteArrayBuilder byteBuilder = _byteBuilder;
+        if (byteBuilder == null) {
+            // no allocator; can add if we must, shouldn't need to
+            _byteBuilder = byteBuilder = new ByteArrayBuilder(null);
+        }
+        int inputPtr = 0;
+        int inputEnd = text.length();
+        int outputPtr = 0;
+        byte[] outputBuffer = byteBuilder.resetAndGetFirstSegment();
+        int outputEnd = outputBuffer.length;
+        
+        main_loop:
+        while (inputPtr < inputEnd) {
+            int c = text.charAt(inputPtr++);
+
+            // first tight loop for ascii
+            while (c <= 0x7F) {
+                if (outputPtr >= outputEnd) {
+                    outputBuffer = byteBuilder.finishCurrentSegment();
+                    outputEnd = outputBuffer.length;
+                    outputPtr = 0;
+                }
+                outputBuffer[outputPtr++] = (byte) c;
+                if (inputPtr >= inputEnd) {
+                    break main_loop;
+                }
+                c = text.charAt(inputPtr++);
+            }
+
+            // then multi-byte...
+            if (outputPtr >= outputEnd) {
+                outputBuffer = byteBuilder.finishCurrentSegment();
+                outputEnd = outputBuffer.length;
+                outputPtr = 0;
+            }
+            if (c < 0x800) { // 2-byte
+                outputBuffer[outputPtr++] = (byte) (0xc0 | (c >> 6));
+            } else { // 3 or 4 bytes
+                // Surrogates?
+                if (c < SURR1_FIRST || c > SURR2_LAST) { // nope
+                    outputBuffer[outputPtr++] = (byte) (0xe0 | (c >> 12));
+                    if (outputPtr >= outputEnd) {
+                        outputBuffer = byteBuilder.finishCurrentSegment();
+                        outputEnd = outputBuffer.length;
+                        outputPtr = 0;
+                    }
+                    outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                } else { // yes, surrogate pair
+                    if (c > SURR1_LAST) { // must be from first range
+                        _throwIllegalSurrogate(c);
+                    }
+                    // and if so, followed by another from next range
+                    if (inputPtr >= inputEnd) {
+                        _throwIllegalSurrogate(c);
+                    }
+                    c = _convertSurrogate(c, text.charAt(inputPtr++));
+                    if (c > 0x10FFFF) { // illegal, as per RFC 4627
+                        _throwIllegalSurrogate(c);
+                    }
+                    outputBuffer[outputPtr++] = (byte) (0xf0 | (c >> 18));
+                    if (outputPtr >= outputEnd) {
+                        outputBuffer = byteBuilder.finishCurrentSegment();
+                        outputEnd = outputBuffer.length;
+                        outputPtr = 0;
+                    }
+                    outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+                    if (outputPtr >= outputEnd) {
+                        outputBuffer = byteBuilder.finishCurrentSegment();
+                        outputEnd = outputBuffer.length;
+                        outputPtr = 0;
+                    }
+                    outputBuffer[outputPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                }
+            }
+            if (outputPtr >= outputEnd) {
+                outputBuffer = byteBuilder.finishCurrentSegment();
+                outputEnd = outputBuffer.length;
+                outputPtr = 0;
+            }
+            outputBuffer[outputPtr++] = (byte) (0x80 | (c & 0x3f));
+        }
+        return _byteBuilder.completeAndCoalesce(outputPtr);
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    private int _appendSingleEscape(int escCode, char[] quoteBuffer)
+    {
+        if (escCode < 0) { // control char, value -(char + 1)
+            int value = -(escCode + 1);
+            quoteBuffer[1] = 'u';
+            // We know it's a control char, so only the last 2 chars are non-0
+            quoteBuffer[4] = HEX_CHARS[value >> 4];
+            quoteBuffer[5] = HEX_CHARS[value & 0xF];
+            return 6;
+        }
+        quoteBuffer[1] = (char) escCode;
+        return 2;
+    }
+
+    private int _appendByteEscape(int ch, int escCode, ByteArrayBuilder byteBuilder, int ptr)
+    {
+        byteBuilder.setCurrentSegmentLength(ptr);
+        byteBuilder.append(INT_BACKSLASH);
+        if (escCode < 0) { // standard escape
+            byteBuilder.append(INT_U);
+            if (ch > 0xFF) {
+                int hi = (ch >> 8);
+                byteBuilder.append(HEX_BYTES[hi >> 4]);
+                byteBuilder.append(HEX_BYTES[hi & 0xF]);
+                ch &= 0xFF;
+            } else {
+                byteBuilder.append(INT_0);
+                byteBuilder.append(INT_0);
+            }
+            byteBuilder.append(HEX_BYTES[ch >> 4]);
+            byteBuilder.append(HEX_BYTES[ch & 0xF]);
+        } else { // 2-char simple escape
+            byteBuilder.append((byte) escCode);
+        }
+        return byteBuilder.getCurrentSegmentLength();
+    }
+    
+    /**
+     * Method called to calculate UTF code point, from a surrogate pair.
+     */
+    private int _convertSurrogate(int firstPart, int secondPart)
+    {
+        // Ok, then, is the second part valid?
+        if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) {
+            throw new IllegalArgumentException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination");
+        }
+        return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST);
+    }
+    
+    private void _throwIllegalSurrogate(int code)
+    {
+        if (code > 0x10FFFF) { // over max?
+            throw new IllegalArgumentException("Illegal character point (0x"+Integer.toHexString(code)+") to output; max is 0x10FFFF as per RFC 4627");
+        }
+        if (code >= SURR1_FIRST) {
+            if (code <= SURR1_LAST) { // Unmatched first part (closing without second part?)
+                throw new IllegalArgumentException("Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")");
+            }
+            throw new IllegalArgumentException("Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")");
+        }
+        // should we ever get this?
+        throw new IllegalArgumentException("Illegal character point (0x"+Integer.toHexString(code)+") to output");
+    }
+
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/MergedStream.java b/src/main/java/com/fasterxml/jackson/core/io/MergedStream.java
new file mode 100644
index 0000000..3645097
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/MergedStream.java
@@ -0,0 +1,145 @@
+package com.fasterxml.jackson.core.io;
+
+import java.io.*;
+
+/**
+ * Simple {@link InputStream} implementation that is used to "unwind" some
+ * data previously read from an input stream; so that as long as some of
+ * that data remains, it's returned; but as long as it's read, we'll
+ * just use data from the underlying original stream. 
+ * This is similar to {@link java.io.PushbackInputStream}, but here there's
+ * only one implicit pushback, when instance is constructed.
+ */
+public final class MergedStream
+    extends InputStream
+{
+    final protected IOContext _context;
+
+    final InputStream _in;
+
+    byte[] _buffer;
+
+    int _ptr;
+
+    final int _end;
+
+    public MergedStream(IOContext context,
+            InputStream in, byte[] buf, int start, int end)
+    {
+        _context = context;
+        _in = in;
+        _buffer = buf;
+        _ptr = start;
+        _end = end;
+    }
+
+    @Override
+    public int available() throws IOException
+    {
+        if (_buffer != null) {
+            return _end - _ptr;
+        }
+        return _in.available();
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        freeMergedBuffer();
+        _in.close();
+    }
+
+    @Override
+    public void mark(int readlimit)
+    {
+        if (_buffer == null) {
+            _in.mark(readlimit);
+        }
+    }
+    
+    @Override
+    public boolean markSupported()
+    {
+        // Only supports marks past the initial rewindable section...
+        return (_buffer == null) && _in.markSupported();
+    }
+    
+    @Override
+    public int read() throws IOException
+    {
+        if (_buffer != null) {
+            int c = _buffer[_ptr++] & 0xFF;
+            if (_ptr >= _end) {
+                freeMergedBuffer();
+            }
+            return c;
+        }
+        return _in.read();
+    }
+    
+    @Override
+    public int read(byte[] b) throws IOException
+    {
+        return read(b, 0, b.length);
+    }
+
+    @Override
+    public int 	read(byte[] b, int off, int len) throws IOException
+    {
+        if (_buffer != null) {
+            int avail = _end - _ptr;
+            if (len > avail) {
+                len = avail;
+            }
+            System.arraycopy(_buffer, _ptr, b, off, len);
+            _ptr += len;
+            if (_ptr >= _end) {
+                freeMergedBuffer();
+            }
+            return len;
+        }
+        return _in.read(b, off, len);
+    }
+
+    @Override
+    public void reset() throws IOException
+    {
+        if (_buffer == null) {
+            _in.reset();
+        }
+    }
+
+    @Override
+    public long skip(long n) throws IOException
+    {
+        long count = 0L;
+
+        if (_buffer != null) {
+            int amount = _end - _ptr;
+
+            if (amount > n) { // all in pushed back segment?
+                _ptr += (int) n;
+                return n;
+            }
+            freeMergedBuffer();
+            count += amount;
+            n -= amount;
+        }
+
+        if (n > 0) {
+            count += _in.skip(n);
+        }
+        return count;
+    }
+
+    private void freeMergedBuffer()
+    {
+        byte[] buf = _buffer;
+        if (buf != null) {
+            _buffer = null;
+            if (_context != null) {
+                _context.releaseReadIOBuffer(buf);
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java
new file mode 100644
index 0000000..42bb7be
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java
@@ -0,0 +1,303 @@
+package com.fasterxml.jackson.core.io;
+
+public final class NumberInput
+{
+    /**
+     * Textual representation of a double constant that can cause nasty problems
+     * with JDK (see http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308).
+     */
+    public final static String NASTY_SMALL_DOUBLE = "2.2250738585072012e-308";
+
+    /**
+     * Constants needed for parsing longs from basic int parsing methods
+     */
+    final static long L_BILLION = 1000000000;
+
+    final static String MIN_LONG_STR_NO_SIGN = String.valueOf(Long.MIN_VALUE).substring(1);
+    final static String MAX_LONG_STR = String.valueOf(Long.MAX_VALUE);
+    
+    /**
+     * Fast method for parsing integers that are known to fit into
+     * regular 32-bit signed int type. This means that length is
+     * between 1 and 9 digits (inclusive)
+     *<p>
+     * Note: public to let unit tests call it
+     */
+    public final static int parseInt(char[] digitChars, int offset, int len)
+    {
+        int num = digitChars[offset] - '0';
+        len += offset;
+        // This looks ugly, but appears the fastest way (as per measurements)
+        if (++offset < len) {
+            num = (num * 10) + (digitChars[offset] - '0');
+            if (++offset < len) {
+                num = (num * 10) + (digitChars[offset] - '0');
+                if (++offset < len) {
+                    num = (num * 10) + (digitChars[offset] - '0');
+                    if (++offset < len) {
+                        num = (num * 10) + (digitChars[offset] - '0');
+                        if (++offset < len) {
+                            num = (num * 10) + (digitChars[offset] - '0');
+                            if (++offset < len) {
+                                num = (num * 10) + (digitChars[offset] - '0');
+                                if (++offset < len) {
+                                    num = (num * 10) + (digitChars[offset] - '0');
+                                    if (++offset < len) {
+                                        num = (num * 10) + (digitChars[offset] - '0');
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return num;
+    }
+
+    /**
+     * Helper method to (more) efficiently parse integer numbers from
+     * String values.
+     * 
+     * @since 1.7
+     */
+    public final static int parseInt(String str)
+    {
+        /* Ok: let's keep strategy simple: ignoring optional minus sign,
+         * we'll accept 1 - 9 digits and parse things efficiently;
+         * otherwise just defer to JDK parse functionality.
+         */
+        char c = str.charAt(0);
+        int length = str.length();
+        boolean negative = (c == '-');
+        int offset = 1;
+        // must have 1 - 9 digits after optional sign:
+        // negative?
+        if (negative) {
+            if (length == 1 || length > 10) {
+                return Integer.parseInt(str);
+            }
+            c = str.charAt(offset++);
+        } else {
+            if (length > 9) {
+                return Integer.parseInt(str);
+            }
+        }
+        if (c > '9' || c < '0') {
+            return Integer.parseInt(str);
+        }
+        int num = c - '0';
+        if (offset < length) {
+            c = str.charAt(offset++);
+            if (c > '9' || c < '0') {
+                return Integer.parseInt(str);
+            }
+            num = (num * 10) + (c - '0');
+            if (offset < length) {
+                c = str.charAt(offset++);
+                if (c > '9' || c < '0') {
+                    return Integer.parseInt(str);
+                }
+                num = (num * 10) + (c - '0');
+                // Let's just loop if we have more than 3 digits:
+                if (offset < length) {
+                    do {
+                        c = str.charAt(offset++);
+                        if (c > '9' || c < '0') {
+                            return Integer.parseInt(str);
+                        }
+                        num = (num * 10) + (c - '0');
+                    } while (offset < length);
+                }
+            }
+        }
+        return negative ? -num : num;
+    }
+    
+    public final static long parseLong(char[] digitChars, int offset, int len)
+    {
+        // Note: caller must ensure length is [10, 18]
+        int len1 = len-9;
+        long val = parseInt(digitChars, offset, len1) * L_BILLION;
+        return val + (long) parseInt(digitChars, offset+len1, 9);
+    }
+
+    public final static long parseLong(String str)
+    {
+        /* Ok, now; as the very first thing, let's just optimize case of "fake longs";
+         * that is, if we know they must be ints, call int parsing
+         */
+        int length = str.length();
+        if (length <= 9) {
+            return (long) parseInt(str);
+        }
+        // !!! TODO: implement efficient 2-int parsing...
+        return Long.parseLong(str);
+    }
+    
+    /**
+     * Helper method for determining if given String representation of
+     * an integral number would fit in 64-bit Java long or not.
+     * Note that input String must NOT contain leading minus sign (even
+     * if 'negative' is set to true).
+     *
+     * @param negative Whether original number had a minus sign (which is
+     *    NOT passed to this method) or not
+     */
+    public final static boolean inLongRange(char[] digitChars, int offset, int len,
+            boolean negative)
+    {
+        String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR;
+        int cmpLen = cmpStr.length();
+        if (len < cmpLen) return true;
+        if (len > cmpLen) return false;
+
+        for (int i = 0; i < cmpLen; ++i) {
+            int diff = digitChars[offset+i] - cmpStr.charAt(i);
+            if (diff != 0) {
+                return (diff < 0);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Similar to {@link #inLongRange(char[],int,int,boolean)}, but
+     * with String argument
+     *
+     * @param negative Whether original number had a minus sign (which is
+     *    NOT passed to this method) or not
+     *
+     * @since 1.5.0
+     */
+    public final static boolean inLongRange(String numberStr, boolean negative)
+    {
+        String cmpStr = negative ? MIN_LONG_STR_NO_SIGN : MAX_LONG_STR;
+        int cmpLen = cmpStr.length();
+        int actualLen = numberStr.length();
+        if (actualLen < cmpLen) return true;
+        if (actualLen > cmpLen) return false;
+
+        // could perhaps just use String.compareTo()?
+        for (int i = 0; i < cmpLen; ++i) {
+            int diff = numberStr.charAt(i) - cmpStr.charAt(i);
+            if (diff != 0) {
+                return (diff < 0);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @since 1.6
+     */
+    public static int parseAsInt(String input, int defaultValue)
+    {
+        if (input == null) {
+            return defaultValue;
+        }
+        input = input.trim();
+        int len = input.length();
+        if (len == 0) {
+            return defaultValue;
+        }
+        // One more thing: use integer parsing for 'simple'
+        int i = 0;
+        if (i < len) { // skip leading sign:
+            char c = input.charAt(0);
+            if (c == '+') { // for plus, actually physically remove
+                input = input.substring(1);
+                len = input.length();
+            } else if (c == '-') { // minus, just skip for checks, must retain
+                ++i;
+            }
+        }
+        for (; i < len; ++i) {
+            char c = input.charAt(i);
+            // if other symbols, parse as Double, coerce
+            if (c > '9' || c < '0') {
+                try {
+                    return (int) parseDouble(input);
+                } catch (NumberFormatException e) {
+                    return defaultValue;
+                }
+            }
+        }
+        try {
+            return Integer.parseInt(input);
+        } catch (NumberFormatException e) { }
+        return defaultValue;
+    }
+
+    /**
+     * @since 1.6
+     */
+    public static long parseAsLong(String input, long defaultValue)
+    {
+        if (input == null) {
+            return defaultValue;
+        }
+        input = input.trim();
+        int len = input.length();
+        if (len == 0) {
+            return defaultValue;
+        }
+        // One more thing: use long parsing for 'simple'
+        int i = 0;
+        if (i < len) { // skip leading sign:
+            char c = input.charAt(0);
+            if (c == '+') { // for plus, actually physically remove
+                input = input.substring(1);
+                len = input.length();
+            } else if (c == '-') { // minus, just skip for checks, must retain
+                ++i;
+            }
+        }
+        for (; i < len; ++i) {
+            char c = input.charAt(i);
+            // if other symbols, parse as Double, coerce
+            if (c > '9' || c < '0') {
+                try {
+                    return (long) parseDouble(input);
+                } catch (NumberFormatException e) {
+                    return defaultValue;
+                }
+            }
+        }
+        try {
+            return Long.parseLong(input);
+        } catch (NumberFormatException e) { }
+        return defaultValue;
+    }
+    
+    /**
+     * @since 1.6
+     */
+    public static double parseAsDouble(String input, double defaultValue)
+    {
+        if (input == null) {
+            return defaultValue;
+        }
+        input = input.trim();
+        int len = input.length();
+        if (len == 0) {
+            return defaultValue;
+        }
+        try {
+            return parseDouble(input);
+        } catch (NumberFormatException e) { }
+        return defaultValue;
+    }
+    
+    /**
+     * @since 1.8
+     */
+    public final static double parseDouble(String numStr) throws NumberFormatException
+    {
+        // [JACKSON-486]: avoid some nasty float representations... but should it be MIN_NORMAL or MIN_VALUE?
+        if (NASTY_SMALL_DOUBLE.equals(numStr)) {
+            return Double.MIN_NORMAL;
+        }
+        return Double.parseDouble(numStr);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/NumberOutput.java b/src/main/java/com/fasterxml/jackson/core/io/NumberOutput.java
new file mode 100644
index 0000000..cb9bb8d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/NumberOutput.java
@@ -0,0 +1,398 @@
+package com.fasterxml.jackson.core.io;
+
+public final class NumberOutput
+{
+    private final static char NULL_CHAR = (char) 0;
+
+    private static int MILLION = 1000000;
+    private static int BILLION = 1000000000;
+    private static long TEN_BILLION_L = 10000000000L;
+    private static long THOUSAND_L = 1000L;
+
+    private static long MIN_INT_AS_LONG = (long) Integer.MIN_VALUE;
+    private static long MAX_INT_AS_LONG = (long) Integer.MAX_VALUE;
+
+    final static String SMALLEST_LONG = String.valueOf(Long.MIN_VALUE);
+
+    final static char[] LEADING_TRIPLETS = new char[4000];
+    final static char[] FULL_TRIPLETS = new char[4000];
+    static {
+        /* Let's fill it with NULLs for ignorable leading digits,
+         * and digit chars for others
+         */
+        int ix = 0;
+        for (int i1 = 0; i1 < 10; ++i1) {
+            char f1 = (char) ('0' + i1);
+            char l1 = (i1 == 0) ? NULL_CHAR : f1;
+            for (int i2 = 0; i2 < 10; ++i2) {
+                char f2 = (char) ('0' + i2);
+                char l2 = (i1 == 0 && i2 == 0) ? NULL_CHAR : f2;
+                for (int i3 = 0; i3 < 10; ++i3) {
+                    // Last is never to be empty
+                    char f3 = (char) ('0' + i3);
+                    LEADING_TRIPLETS[ix] = l1;
+                    LEADING_TRIPLETS[ix+1] = l2;
+                    LEADING_TRIPLETS[ix+2] = f3;
+                    FULL_TRIPLETS[ix] = f1;
+                    FULL_TRIPLETS[ix+1] = f2;
+                    FULL_TRIPLETS[ix+2] = f3;
+                    ix += 4;
+                }
+            }
+        }
+    }
+
+    final static byte[] FULL_TRIPLETS_B = new byte[4000];
+    static {
+        for (int i = 0; i < 4000; ++i) {
+            FULL_TRIPLETS_B[i] = (byte) FULL_TRIPLETS[i];
+        }
+    }
+    
+    final static String[] sSmallIntStrs = new String[] {
+        "0","1","2","3","4","5","6","7","8","9","10"
+    };
+    final static String[] sSmallIntStrs2 = new String[] {
+        "-1","-2","-3","-4","-5","-6","-7","-8","-9","-10"
+    };
+
+    /*
+    /**********************************************************
+    /* Efficient serialization methods using raw buffers
+    /**********************************************************
+     */
+
+    /**
+     * @return Offset within buffer after outputting int
+     */
+    public static int outputInt(int value, char[] buffer, int offset)
+    {
+        if (value < 0) {
+            if (value == Integer.MIN_VALUE) {
+                /* Special case: no matching positive value within range;
+                 * let's then "upgrade" to long and output as such.
+                 */
+                return outputLong((long) value, buffer, offset);
+            }
+            buffer[offset++] = '-';
+            value = -value;
+        }
+
+        if (value < MILLION) { // at most 2 triplets...
+            if (value < 1000) {
+                if (value < 10) {
+                    buffer[offset++] = (char) ('0' + value);
+                } else {
+                    offset = outputLeadingTriplet(value, buffer, offset);
+                }
+            } else {
+                int thousands = value / 1000;
+                value -= (thousands * 1000); // == value % 1000
+                offset = outputLeadingTriplet(thousands, buffer, offset);
+                offset = outputFullTriplet(value, buffer, offset);
+            }
+            return offset;
+        }
+
+        // ok, all 3 triplets included
+        /* Let's first hand possible billions separately before
+         * handling 3 triplets. This is possible since we know we
+         * can have at most '2' as billion count.
+         */
+        boolean hasBillions = (value >= BILLION);
+        if (hasBillions) {
+            value -= BILLION;
+            if (value >= BILLION) {
+                value -= BILLION;
+                buffer[offset++] = '2';
+            } else {
+                buffer[offset++] = '1';
+            }
+        }
+        int newValue = value / 1000;
+        int ones = (value - (newValue * 1000)); // == value % 1000
+        value = newValue;
+        newValue /= 1000;
+        int thousands = (value - (newValue * 1000));
+        
+        // value now has millions, which have 1, 2 or 3 digits
+        if (hasBillions) {
+            offset = outputFullTriplet(newValue, buffer, offset);
+        } else {
+            offset = outputLeadingTriplet(newValue, buffer, offset);
+        }
+        offset = outputFullTriplet(thousands, buffer, offset);
+        offset = outputFullTriplet(ones, buffer, offset);
+        return offset;
+    }
+
+    public static int outputInt(int value, byte[] buffer, int offset)
+    {
+        if (value < 0) {
+            if (value == Integer.MIN_VALUE) {
+                return outputLong((long) value, buffer, offset);
+            }
+            buffer[offset++] = '-';
+            value = -value;
+        }
+
+        if (value < MILLION) { // at most 2 triplets...
+            if (value < 1000) {
+                if (value < 10) {
+                    buffer[offset++] = (byte) ('0' + value);
+                } else {
+                    offset = outputLeadingTriplet(value, buffer, offset);
+                }
+            } else {
+                int thousands = value / 1000;
+                value -= (thousands * 1000); // == value % 1000
+                offset = outputLeadingTriplet(thousands, buffer, offset);
+                offset = outputFullTriplet(value, buffer, offset);
+            }
+            return offset;
+        }
+        boolean hasBillions = (value >= BILLION);
+        if (hasBillions) {
+            value -= BILLION;
+            if (value >= BILLION) {
+                value -= BILLION;
+                buffer[offset++] = '2';
+            } else {
+                buffer[offset++] = '1';
+            }
+        }
+        int newValue = value / 1000;
+        int ones = (value - (newValue * 1000)); // == value % 1000
+        value = newValue;
+        newValue /= 1000;
+        int thousands = (value - (newValue * 1000));
+        
+        if (hasBillions) {
+            offset = outputFullTriplet(newValue, buffer, offset);
+        } else {
+            offset = outputLeadingTriplet(newValue, buffer, offset);
+        }
+        offset = outputFullTriplet(thousands, buffer, offset);
+        offset = outputFullTriplet(ones, buffer, offset);
+        return offset;
+    }
+    
+    /**
+     * @return Offset within buffer after outputting int
+     */
+    public static int outputLong(long value, char[] buffer, int offset)
+    {
+        // First: does it actually fit in an int?
+        if (value < 0L) {
+            /* MIN_INT is actually printed as long, just because its
+             * negation is not an int but long
+             */
+            if (value > MIN_INT_AS_LONG) {
+                return outputInt((int) value, buffer, offset);
+            }
+            if (value == Long.MIN_VALUE) {
+                // Special case: no matching positive value within range
+                int len = SMALLEST_LONG.length();
+                SMALLEST_LONG.getChars(0, len, buffer, offset);
+                return (offset + len);
+            }
+            buffer[offset++] = '-';
+            value = -value;
+        } else {
+            if (value <= MAX_INT_AS_LONG) {
+                return outputInt((int) value, buffer, offset);
+            }
+        }
+
+        /* Ok: real long print. Need to first figure out length
+         * in characters, and then print in from end to beginning
+         */
+        int origOffset = offset;
+        offset += calcLongStrLength(value);
+        int ptr = offset;
+
+        // First, with long arithmetics:
+        while (value > MAX_INT_AS_LONG) { // full triplet
+            ptr -= 3;
+            long newValue = value / THOUSAND_L;
+            int triplet = (int) (value - newValue * THOUSAND_L);
+            outputFullTriplet(triplet, buffer, ptr);
+            value = newValue;
+        }
+        // Then with int arithmetics:
+        int ivalue = (int) value;
+        while (ivalue >= 1000) { // still full triplet
+            ptr -= 3;
+            int newValue = ivalue / 1000;
+            int triplet = ivalue - (newValue * 1000);
+            outputFullTriplet(triplet, buffer, ptr);
+            ivalue = newValue;
+        }
+        // And finally, if anything remains, partial triplet
+        outputLeadingTriplet(ivalue, buffer, origOffset);
+
+        return offset;
+    }
+
+    public static int outputLong(long value, byte[] buffer, int offset)
+    {
+        if (value < 0L) {
+            if (value > MIN_INT_AS_LONG) {
+                return outputInt((int) value, buffer, offset);
+            }
+            if (value == Long.MIN_VALUE) {
+                // Special case: no matching positive value within range
+                int len = SMALLEST_LONG.length();
+                for (int i = 0; i < len; ++i) {
+                    buffer[offset++] = (byte) SMALLEST_LONG.charAt(i);
+                }
+                return offset;
+            }
+            buffer[offset++] = '-';
+            value = -value;
+        } else {
+            if (value <= MAX_INT_AS_LONG) {
+                return outputInt((int) value, buffer, offset);
+            }
+        }
+        int origOffset = offset;
+        offset += calcLongStrLength(value);
+        int ptr = offset;
+
+        // First, with long arithmetics:
+        while (value > MAX_INT_AS_LONG) { // full triplet
+            ptr -= 3;
+            long newValue = value / THOUSAND_L;
+            int triplet = (int) (value - newValue * THOUSAND_L);
+            outputFullTriplet(triplet, buffer, ptr);
+            value = newValue;
+        }
+        // Then with int arithmetics:
+        int ivalue = (int) value;
+        while (ivalue >= 1000) { // still full triplet
+            ptr -= 3;
+            int newValue = ivalue / 1000;
+            int triplet = ivalue - (newValue * 1000);
+            outputFullTriplet(triplet, buffer, ptr);
+            ivalue = newValue;
+        }
+        outputLeadingTriplet(ivalue, buffer, origOffset);
+        return offset;
+    }
+    
+    /*
+    /**********************************************************
+    /* Secondary convenience serialization methods
+    /**********************************************************
+     */
+
+    /* !!! 05-Aug-2008, tatus: Any ways to further optimize
+     *   these? (or need: only called by diagnostics methods?)
+     */
+
+    public static String toString(int value)
+    {
+        // Lookup table for small values
+        if (value < sSmallIntStrs.length) {
+            if (value >= 0) {
+                return sSmallIntStrs[value];
+            }
+            int v2 = -value - 1;
+            if (v2 < sSmallIntStrs2.length) {
+                return sSmallIntStrs2[v2];
+            }
+        }
+        return Integer.toString(value);
+    }
+
+    public static String toString(long value)
+    {
+        if (value <= Integer.MAX_VALUE &&
+            value >= Integer.MIN_VALUE) {
+            return toString((int) value);
+        }
+        return Long.toString(value);
+    }
+
+    public static String toString(double value)
+    {
+        return Double.toString(value);
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    private static int outputLeadingTriplet(int triplet, char[] buffer, int offset)
+    {
+        int digitOffset = (triplet << 2);
+        char c = LEADING_TRIPLETS[digitOffset++];
+        if (c != NULL_CHAR) {
+            buffer[offset++] = c;
+        }
+        c = LEADING_TRIPLETS[digitOffset++];
+        if (c != NULL_CHAR) {
+            buffer[offset++] = c;
+        }
+        // Last is required to be non-empty
+        buffer[offset++] = LEADING_TRIPLETS[digitOffset];
+        return offset;
+    }
+
+    private static int outputLeadingTriplet(int triplet, byte[] buffer, int offset)
+    {
+        int digitOffset = (triplet << 2);
+        char c = LEADING_TRIPLETS[digitOffset++];
+        if (c != NULL_CHAR) {
+            buffer[offset++] = (byte) c;
+        }
+        c = LEADING_TRIPLETS[digitOffset++];
+        if (c != NULL_CHAR) {
+            buffer[offset++] = (byte) c;
+        }
+        // Last is required to be non-empty
+        buffer[offset++] = (byte) LEADING_TRIPLETS[digitOffset];
+        return offset;
+    }
+    
+    private static int outputFullTriplet(int triplet, char[] buffer, int offset)
+    {
+        int digitOffset = (triplet << 2);
+        buffer[offset++] = FULL_TRIPLETS[digitOffset++];
+        buffer[offset++] = FULL_TRIPLETS[digitOffset++];
+        buffer[offset++] = FULL_TRIPLETS[digitOffset];
+        return offset;
+    }
+
+    private static int outputFullTriplet(int triplet, byte[] buffer, int offset)
+    {
+        int digitOffset = (triplet << 2);
+        buffer[offset++] = FULL_TRIPLETS_B[digitOffset++];
+        buffer[offset++] = FULL_TRIPLETS_B[digitOffset++];
+        buffer[offset++] = FULL_TRIPLETS_B[digitOffset];
+        return offset;
+    }
+    
+    /**
+     *<p>
+     * Pre-conditions: posValue is positive, and larger than
+     * Integer.MAX_VALUE (about 2 billions).
+     */
+    private static int calcLongStrLength(long posValue)
+    {
+        int len = 10;
+        long comp = TEN_BILLION_L;
+
+        // 19 is longest, need to worry about overflow
+        while (posValue >= comp) {
+            if (len == 19) {
+                break;
+            }
+            ++len;
+            comp = (comp << 3) + (comp << 1); // 10x
+        }
+        return len;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/OutputDecorator.java b/src/main/java/com/fasterxml/jackson/core/io/OutputDecorator.java
new file mode 100644
index 0000000..291213d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/OutputDecorator.java
@@ -0,0 +1,40 @@
+package com.fasterxml.jackson.core.io;
+
+import java.io.*;
+
+/**
+ * Handler class that can be used to decorate output destinations.
+ * Typical use is to use a filter abstraction (filtered output stream,
+ * writer) around original output destination, and apply additional
+ * processing during write operations.
+ * 
+ * @since 1.8
+ */
+public abstract class OutputDecorator
+{
+    /**
+     * Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
+     * creating generator for given {@link OutputStream}, when this decorator
+     * has been registered.
+     * 
+     * @param ctxt IO context in use (provides access to declared encoding)
+     * @param out Original output destination
+     * 
+     * @return OutputStream to use; either passed in argument, or something that
+     *   calls it
+     */
+    public abstract OutputStream decorate(IOContext ctxt, OutputStream out)
+        throws IOException;
+
+    /**
+     * Method called by {@link com.fasterxml.jackson.core.JsonFactory} instance when
+     * creating generator for given {@link Writer}, when this decorator
+     * has been registered.
+     * 
+     * @param ctxt IO context in use (provides access to declared encoding)
+     * @param w Original output writer
+     * 
+     * @return Writer to use; either passed in argument, or something that calls it
+     */
+    public abstract Writer decorate(IOContext ctxt, Writer w) throws IOException;
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/SegmentedStringWriter.java b/src/main/java/com/fasterxml/jackson/core/io/SegmentedStringWriter.java
new file mode 100644
index 0000000..7cbb16c
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/SegmentedStringWriter.java
@@ -0,0 +1,104 @@
+package com.fasterxml.jackson.core.io;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.util.BufferRecycler;
+import com.fasterxml.jackson.core.util.TextBuffer;
+
+/**
+ * Efficient alternative to {@link StringWriter}, based on using segmented
+ * internal buffer. Initial input buffer is also recyclable.
+ *<p>
+ * This class is most useful when serializing JSON content as a String:
+ * if so, instance of this class can be given as the writer to
+ * <code>JsonGenerator</code>.
+ *
+ * @since 1.3
+ */
+public final class SegmentedStringWriter
+    extends Writer
+{
+    final protected TextBuffer _buffer;
+
+    public SegmentedStringWriter(BufferRecycler br)
+    {
+        super();
+        _buffer = new TextBuffer(br);
+    }
+
+    /*
+    /**********************************************************
+    /* java.io.Writer implementation
+    /**********************************************************
+     */
+
+    @Override
+    public Writer append(char c)
+    {
+        write(c);
+        return this;
+    }
+
+    @Override
+    public Writer append(CharSequence csq)
+    {
+	String str = csq.toString();
+	_buffer.append(str, 0, str.length());
+        return this;
+    }
+
+    @Override
+    public Writer append(CharSequence csq, int start, int end)
+    {
+	String str = csq.subSequence(start, end).toString();
+	_buffer.append(str, 0, str.length());
+        return this;
+    }
+
+    @Override public void close() { } // NOP
+
+    @Override public void flush() { } // NOP
+
+    @Override
+    public void write(char[] cbuf) {
+        _buffer.append(cbuf, 0, cbuf.length);
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len) {
+        _buffer.append(cbuf, off, len);
+    }
+
+    @Override
+    public void write(int c) {
+        _buffer.append((char) c);
+    }
+
+    @Override
+    public void write(String str) { _buffer.append(str, 0, str.length()); }
+
+    @Override
+    public void write(String str, int off, int len) {
+        _buffer.append(str, 0, str.length());
+    }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    /**
+     * Main access method that will construct a String that contains
+     * all the contents, release all internal buffers we may have,
+     * and return result String.
+     * Note that the method is not idempotent -- if called second time,
+     * will just return an empty String.
+     */
+    public String getAndClear()
+    {
+        String result = _buffer.contentsAsString();
+        _buffer.releaseBuffers();
+        return result;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/SerializedString.java b/src/main/java/com/fasterxml/jackson/core/io/SerializedString.java
new file mode 100644
index 0000000..41dc7ec
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/SerializedString.java
@@ -0,0 +1,114 @@
+package com.fasterxml.jackson.core.io;
+
+import com.fasterxml.jackson.core.SerializableString;
+
+/**
+ * String token that can lazily serialize String contained and then reuse that
+ * serialization later on. This is similar to JDBC prepared statements, for example,
+ * in that instances should only be created when they are used more than use;
+ * prime candidates are various serializers.
+ *<p>
+ * Class is final for performance reasons and since this is not designed to
+ * be extensible or customizable (customizations would occur in calling code)
+ *
+ * @since 1.6
+ */
+public class SerializedString implements SerializableString
+{
+    protected final String _value;
+
+    /* 13-Dec-2010, tatu: Whether use volatile or not is actually an important
+     *   decision for multi-core use cases. Cost of volatility can be non-trivial
+     *   for heavy use cases, and serialized-string instances are accessed often.
+     *   Given that all code paths with common Jackson usage patterns go through
+     *   a few memory barriers (mostly with cache/reuse pool access) it seems safe
+     *   enough to omit volatiles here, given how simple lazy initialization is.
+     *   This can be compared to how {@link String#intern} works; lazily and
+     *   without synchronization or use of volatile keyword.
+     */
+    
+    protected /*volatile*/ byte[] _quotedUTF8Ref;
+
+    protected /*volatile*/ byte[] _unquotedUTF8Ref;
+
+    protected /*volatile*/ char[] _quotedChars;
+
+    public SerializedString(String v) { _value = v; }
+
+    /*
+    /**********************************************************
+    /* API
+    /**********************************************************
+     */
+
+    @Override
+    public final String getValue() { return _value; }
+    
+    /**
+     * Returns length of the String as characters
+     */
+    @Override
+    public final int charLength() { return _value.length(); }
+    
+    @Override
+    public final char[] asQuotedChars()
+    {
+        char[] result = _quotedChars;
+        if (result == null) {
+            result = JsonStringEncoder.getInstance().quoteAsString(_value);
+            _quotedChars = result;
+        }
+        return result;
+    }
+
+    /**
+     * Accessor for accessing value that has been quoted using JSON
+     * quoting rules, and encoded using UTF-8 encoding.
+     */
+    @Override
+    public final byte[] asUnquotedUTF8()
+    {
+        byte[] result = _unquotedUTF8Ref;
+        if (result == null) {
+            result = JsonStringEncoder.getInstance().encodeAsUTF8(_value);
+            _unquotedUTF8Ref  = result;
+        }
+        return result;
+    }
+
+    /**
+     * Accessor for accessing value as is (without JSON quoting)
+     * encoded using UTF-8 encoding.
+     */
+    @Override
+    public final byte[] asQuotedUTF8()
+    {
+        byte[] result = _quotedUTF8Ref;
+        if (result == null) {
+            result = JsonStringEncoder.getInstance().quoteAsUTF8(_value);
+            _quotedUTF8Ref = result;
+        }
+        return result;
+    }
+    
+    /*
+    /**********************************************************
+    /* Standard method overrides
+    /**********************************************************
+     */
+
+    @Override
+    public final String toString() { return _value; }
+    
+    @Override
+    public final int hashCode() { return _value.hashCode(); }
+
+    @Override
+    public final boolean equals(Object o)
+    {
+        if (o == this) return true;
+        if (o == null || o.getClass() != getClass()) return false;
+        SerializedString other = (SerializedString) o;
+        return _value.equals(other._value);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/io/UTF32Reader.java b/src/main/java/com/fasterxml/jackson/core/io/UTF32Reader.java
new file mode 100644
index 0000000..45e1783
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/UTF32Reader.java
@@ -0,0 +1,214 @@
+package com.fasterxml.jackson.core.io;
+
+import java.io.*;
+
+
+/**
+ * Since JDK does not come with UTF-32/UCS-4, let's implement a simple
+ * decoder to use.
+ */
+public final class UTF32Reader
+    extends BaseReader
+{
+    final boolean mBigEndian;
+
+    /**
+     * Although input is fine with full Unicode set, Java still uses
+     * 16-bit chars, so we may have to split high-order chars into
+     * surrogate pairs.
+     */
+    char mSurrogate = NULL_CHAR;
+
+    /**
+     * Total read character count; used for error reporting purposes
+     */
+    int mCharCount = 0;
+
+    /**
+     * Total read byte count; used for error reporting purposes
+     */
+    int mByteCount = 0;
+
+    /*
+    ////////////////////////////////////////
+    // Life-cycle
+    ////////////////////////////////////////
+    */
+
+    public UTF32Reader(IOContext ctxt,
+                       InputStream in, byte[] buf, int ptr, int len,
+                       boolean isBigEndian)
+    {
+        super(ctxt, in, buf, ptr, len);
+        mBigEndian = isBigEndian;
+    }
+
+    /*
+    ////////////////////////////////////////
+    // Public API
+    ////////////////////////////////////////
+    */
+
+    @Override
+	public int read(char[] cbuf, int start, int len)
+        throws IOException
+    {
+        // Already EOF?
+        if (_buffer == null) {
+            return -1;
+        }
+        if (len < 1) {
+            return len;
+        }
+        // Let's then ensure there's enough room...
+        if (start < 0 || (start+len) > cbuf.length) {
+            reportBounds(cbuf, start, len);
+        }
+
+        len += start;
+        int outPtr = start;
+
+        // Ok, first; do we have a surrogate from last round?
+        if (mSurrogate != NULL_CHAR) {
+            cbuf[outPtr++] = mSurrogate;
+            mSurrogate = NULL_CHAR;
+            // No need to load more, already got one char
+        } else {
+            /* Note: we'll try to avoid blocking as much as possible. As a
+             * result, we only need to get 4 bytes for a full char.
+             */
+            int left = (_length - _ptr);
+            if (left < 4) {
+                if (!loadMore(left)) { // (legal) EOF?
+                    return -1;
+                }
+            }
+        }
+
+        main_loop:
+        while (outPtr < len) {
+            int ptr = _ptr;
+            int ch;
+
+            if (mBigEndian) {
+                ch = (_buffer[ptr] << 24) | ((_buffer[ptr+1] & 0xFF) << 16)
+                    | ((_buffer[ptr+2] & 0xFF) << 8) | (_buffer[ptr+3] & 0xFF);
+            } else {
+                ch = (_buffer[ptr] & 0xFF) | ((_buffer[ptr+1] & 0xFF) << 8)
+                    | ((_buffer[ptr+2] & 0xFF) << 16) | (_buffer[ptr+3] << 24);
+            }
+            _ptr += 4;
+
+            // Does it need to be split to surrogates?
+            // (also, we can and need to verify illegal chars)
+            if (ch > 0xFFFF) { // need to split into surrogates?
+                if (ch > LAST_VALID_UNICODE_CHAR) {
+                    reportInvalid(ch, outPtr-start,
+                                  "(above "+Integer.toHexString(LAST_VALID_UNICODE_CHAR)+") ");
+                }
+                ch -= 0x10000; // to normalize it starting with 0x0
+                cbuf[outPtr++] = (char) (0xD800 + (ch >> 10));
+                // hmmh. can this ever be 0? (not legal, at least?)
+                ch = (0xDC00 | (ch & 0x03FF));
+                // Room for second part?
+                if (outPtr >= len) { // nope
+                    mSurrogate = (char) ch;
+                    break main_loop;
+                }
+            }
+            cbuf[outPtr++] = (char) ch;
+            if (_ptr >= _length) {
+                break main_loop;
+            }
+        }
+
+        len = outPtr - start;
+        mCharCount += len;
+        return len;
+    }
+
+    /*
+    ////////////////////////////////////////
+    // Internal methods
+    ////////////////////////////////////////
+    */
+
+    private void reportUnexpectedEOF(int gotBytes, int needed)
+        throws IOException
+    {
+        int bytePos = mByteCount + gotBytes;
+        int charPos = mCharCount;
+
+        throw new CharConversionException("Unexpected EOF in the middle of a 4-byte UTF-32 char: got "
+                                          +gotBytes+", needed "+needed
+                                          +", at char #"+charPos+", byte #"+bytePos+")");
+    }
+
+    private void reportInvalid(int value, int offset, String msg)
+        throws IOException
+    {
+        int bytePos = mByteCount + _ptr - 1;
+        int charPos = mCharCount + offset;
+
+        throw new CharConversionException("Invalid UTF-32 character 0x"
+                                          +Integer.toHexString(value)
+                                          +msg+" at char #"+charPos+", byte #"+bytePos+")");
+    }
+
+    /**
+     * @param available Number of "unused" bytes in the input buffer
+     *
+     * @return True, if enough bytes were read to allow decoding of at least
+     *   one full character; false if EOF was encountered instead.
+     */
+    private boolean loadMore(int available)
+        throws IOException
+    {
+        mByteCount += (_length - available);
+
+        // Bytes that need to be moved to the beginning of buffer?
+        if (available > 0) {
+            if (_ptr > 0) {
+                for (int i = 0; i < available; ++i) {
+                    _buffer[i] = _buffer[_ptr+i];
+                }
+                _ptr = 0;
+            }
+            _length = available;
+        } else {
+            /* Ok; here we can actually reasonably expect an EOF,
+             * so let's do a separate read right away:
+             */
+            _ptr = 0;
+            int count = _in.read(_buffer);
+            if (count < 1) {
+                _length = 0;
+                if (count < 0) { // -1
+                    freeBuffers(); // to help GC?
+                    return false;
+                }
+                // 0 count is no good; let's err out
+                reportStrangeStream();
+            }
+            _length = count;
+        }
+
+        /* Need at least 4 bytes; if we don't get that many, it's an
+         * error.
+         */
+        while (_length < 4) {
+            int count = _in.read(_buffer, _length, _buffer.length - _length);
+            if (count < 1) {
+                if (count < 0) { // -1, EOF... no good!
+                    freeBuffers(); // to help GC?
+                    reportUnexpectedEOF(_length, 4);
+                }
+                // 0 count is no good; let's err out
+                reportStrangeStream();
+            }
+            _length += count;
+        }
+        return true;
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/core/io/UTF8Writer.java b/src/main/java/com/fasterxml/jackson/core/io/UTF8Writer.java
new file mode 100644
index 0000000..87c4bd0
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/io/UTF8Writer.java
@@ -0,0 +1,387 @@
+package com.fasterxml.jackson.core.io;
+
+import java.io.*;
+
+
+public final class UTF8Writer
+    extends Writer
+{
+    final static int SURR1_FIRST = 0xD800;
+    final static int SURR1_LAST = 0xDBFF;
+    final static int SURR2_FIRST = 0xDC00;
+    final static int SURR2_LAST = 0xDFFF;
+
+    final protected IOContext _context;
+
+    OutputStream _out;
+
+    byte[] _outBuffer;
+
+    final int _outBufferEnd;
+
+    int _outPtr;
+
+    /**
+     * When outputting chars from BMP, surrogate pairs need to be coalesced.
+     * To do this, both pairs must be known first; and since it is possible
+     * pairs may be split, we need temporary storage for the first half
+     */
+    int _surrogate = 0;
+
+    public UTF8Writer(IOContext ctxt, OutputStream out)
+    {
+        _context = ctxt;
+        _out = out;
+
+        _outBuffer = ctxt.allocWriteEncodingBuffer();
+        /* Max. expansion for a single char (in unmodified UTF-8) is
+         * 4 bytes (or 3 depending on how you view it -- 4 when recombining
+         * surrogate pairs)
+         */
+        _outBufferEnd = _outBuffer.length - 4;
+        _outPtr = 0;
+    }
+
+    @Override
+    public Writer append(char c)
+        throws IOException
+    {
+        write(c);
+        return this;
+    }
+
+    @Override
+    public void close()
+        throws IOException
+    {
+        if (_out != null) {
+            if (_outPtr > 0) {
+                _out.write(_outBuffer, 0, _outPtr);
+                _outPtr = 0;
+            }
+            OutputStream out = _out;
+            _out = null;
+
+            byte[] buf = _outBuffer;
+            if (buf != null) {
+                _outBuffer = null;
+                _context.releaseWriteEncodingBuffer(buf);
+            }
+
+            out.close();
+
+            /* Let's 'flush' orphan surrogate, no matter what; but only
+             * after cleanly closing everything else.
+             */
+            int code = _surrogate;
+            _surrogate = 0;
+            if (code > 0) {
+                throwIllegal(code);
+            }
+        }
+    }
+
+    @Override
+    public void flush()
+        throws IOException
+    {
+        if (_out != null) {
+            if (_outPtr > 0) {
+                _out.write(_outBuffer, 0, _outPtr);
+                _outPtr = 0;
+            }
+            _out.flush();
+        }
+    }
+
+    @Override
+    public void write(char[] cbuf)
+        throws IOException
+    {
+        write(cbuf, 0, cbuf.length);
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len)
+        throws IOException
+    {
+        if (len < 2) {
+            if (len == 1) {
+                write(cbuf[off]);
+            }
+            return;
+        }
+
+        // First: do we have a leftover surrogate to deal with?
+        if (_surrogate > 0) {
+            char second = cbuf[off++];
+            --len;
+            write(convertSurrogate(second));
+            // will have at least one more char
+        }
+
+        int outPtr = _outPtr;
+        byte[] outBuf = _outBuffer;
+        int outBufLast = _outBufferEnd; // has 4 'spare' bytes
+
+        // All right; can just loop it nice and easy now:
+        len += off; // len will now be the end of input buffer
+
+        output_loop:
+        for (; off < len; ) {
+            /* First, let's ensure we can output at least 4 bytes
+             * (longest UTF-8 encoded codepoint):
+             */
+            if (outPtr >= outBufLast) {
+                _out.write(outBuf, 0, outPtr);
+                outPtr = 0;
+            }
+
+            int c = cbuf[off++];
+            // And then see if we have an Ascii char:
+            if (c < 0x80) { // If so, can do a tight inner loop:
+                outBuf[outPtr++] = (byte)c;
+                // Let's calc how many ascii chars we can copy at most:
+                int maxInCount = (len - off);
+                int maxOutCount = (outBufLast - outPtr);
+
+                if (maxInCount > maxOutCount) {
+                    maxInCount = maxOutCount;
+                }
+                maxInCount += off;
+                ascii_loop:
+                while (true) {
+                    if (off >= maxInCount) { // done with max. ascii seq
+                        continue output_loop;
+                    }
+                    c = cbuf[off++];
+                    if (c >= 0x80) {
+                        break ascii_loop;
+                    }
+                    outBuf[outPtr++] = (byte) c;
+                }
+            }
+
+            // Nope, multi-byte:
+            if (c < 0x800) { // 2-byte
+                outBuf[outPtr++] = (byte) (0xc0 | (c >> 6));
+                outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+            } else { // 3 or 4 bytes
+                // Surrogates?
+                if (c < SURR1_FIRST || c > SURR2_LAST) {
+                    outBuf[outPtr++] = (byte) (0xe0 | (c >> 12));
+                    outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                    outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+                    continue;
+                }
+                // Yup, a surrogate:
+                if (c > SURR1_LAST) { // must be from first range
+                    _outPtr = outPtr;
+                    throwIllegal(c);
+                }
+                _surrogate = c;
+                // and if so, followed by another from next range
+                if (off >= len) { // unless we hit the end?
+                    break;
+                }
+                c = convertSurrogate(cbuf[off++]);
+                if (c > 0x10FFFF) { // illegal in JSON as well as in XML
+                    _outPtr = outPtr;
+                    throwIllegal(c);
+                }
+                outBuf[outPtr++] = (byte) (0xf0 | (c >> 18));
+                outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+                outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+            }
+        }
+        _outPtr = outPtr;
+    }
+    
+    @Override
+    public void write(int c) throws IOException
+    {
+        // First; do we have a left over surrogate?
+        if (_surrogate > 0) {
+            c = convertSurrogate(c);
+            // If not, do we start with a surrogate?
+        } else if (c >= SURR1_FIRST && c <= SURR2_LAST) {
+            // Illegal to get second part without first:
+            if (c > SURR1_LAST) {
+                throwIllegal(c);
+            }
+            // First part just needs to be held for now
+            _surrogate = c;
+            return;
+        }
+
+        if (_outPtr >= _outBufferEnd) { // let's require enough room, first
+            _out.write(_outBuffer, 0, _outPtr);
+            _outPtr = 0;
+        }
+
+        if (c < 0x80) { // ascii
+            _outBuffer[_outPtr++] = (byte) c;
+        } else {
+            int ptr = _outPtr;
+            if (c < 0x800) { // 2-byte
+                _outBuffer[ptr++] = (byte) (0xc0 | (c >> 6));
+                _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
+            } else if (c <= 0xFFFF) { // 3 bytes
+                _outBuffer[ptr++] = (byte) (0xe0 | (c >> 12));
+                _outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
+            } else { // 4 bytes
+                if (c > 0x10FFFF) { // illegal
+                    throwIllegal(c);
+                }
+                _outBuffer[ptr++] = (byte) (0xf0 | (c >> 18));
+                _outBuffer[ptr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+                _outBuffer[ptr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                _outBuffer[ptr++] = (byte) (0x80 | (c & 0x3f));
+            }
+            _outPtr = ptr;
+        }
+    }
+
+    @Override
+    public void write(String str) throws IOException
+    {
+        write(str, 0, str.length());
+    }
+
+    @Override
+    public void write(String str, int off, int len)  throws IOException
+    {
+        if (len < 2) {
+            if (len == 1) {
+                write(str.charAt(off));
+            }
+            return;
+        }
+
+        // First: do we have a leftover surrogate to deal with?
+        if (_surrogate > 0) {
+            char second = str.charAt(off++);
+            --len;
+            write(convertSurrogate(second));
+            // will have at least one more char (case of 1 char was checked earlier on)
+        }
+
+        int outPtr = _outPtr;
+        byte[] outBuf = _outBuffer;
+        int outBufLast = _outBufferEnd; // has 4 'spare' bytes
+
+        // All right; can just loop it nice and easy now:
+        len += off; // len will now be the end of input buffer
+
+        output_loop:
+        for (; off < len; ) {
+            /* First, let's ensure we can output at least 4 bytes
+             * (longest UTF-8 encoded codepoint):
+             */
+            if (outPtr >= outBufLast) {
+                _out.write(outBuf, 0, outPtr);
+                outPtr = 0;
+            }
+
+            int c = str.charAt(off++);
+            // And then see if we have an Ascii char:
+            if (c < 0x80) { // If so, can do a tight inner loop:
+                outBuf[outPtr++] = (byte)c;
+                // Let's calc how many ascii chars we can copy at most:
+                int maxInCount = (len - off);
+                int maxOutCount = (outBufLast - outPtr);
+
+                if (maxInCount > maxOutCount) {
+                    maxInCount = maxOutCount;
+                }
+                maxInCount += off;
+                ascii_loop:
+                while (true) {
+                    if (off >= maxInCount) { // done with max. ascii seq
+                        continue output_loop;
+                    }
+                    c = str.charAt(off++);
+                    if (c >= 0x80) {
+                        break ascii_loop;
+                    }
+                    outBuf[outPtr++] = (byte) c;
+                }
+            }
+
+            // Nope, multi-byte:
+            if (c < 0x800) { // 2-byte
+                outBuf[outPtr++] = (byte) (0xc0 | (c >> 6));
+                outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+            } else { // 3 or 4 bytes
+                // Surrogates?
+                if (c < SURR1_FIRST || c > SURR2_LAST) {
+                    outBuf[outPtr++] = (byte) (0xe0 | (c >> 12));
+                    outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                    outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+                    continue;
+                }
+                // Yup, a surrogate:
+                if (c > SURR1_LAST) { // must be from first range
+                    _outPtr = outPtr;
+                    throwIllegal(c);
+                }
+                _surrogate = c;
+                // and if so, followed by another from next range
+                if (off >= len) { // unless we hit the end?
+                    break;
+                }
+                c = convertSurrogate(str.charAt(off++));
+                if (c > 0x10FFFF) { // illegal, as per RFC 4627
+                    _outPtr = outPtr;
+                    throwIllegal(c);
+                }
+                outBuf[outPtr++] = (byte) (0xf0 | (c >> 18));
+                outBuf[outPtr++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+                outBuf[outPtr++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+                outBuf[outPtr++] = (byte) (0x80 | (c & 0x3f));
+            }
+        }
+        _outPtr = outPtr;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    /**
+     * Method called to calculate UTF codepoint, from a surrogate pair.
+     */
+    private int convertSurrogate(int secondPart)
+        throws IOException
+    {
+        int firstPart = _surrogate;
+        _surrogate = 0;
+
+        // Ok, then, is the second part valid?
+        if (secondPart < SURR2_FIRST || secondPart > SURR2_LAST) {
+            throw new IOException("Broken surrogate pair: first char 0x"+Integer.toHexString(firstPart)+", second 0x"+Integer.toHexString(secondPart)+"; illegal combination");
+        }
+        return 0x10000 + ((firstPart - SURR1_FIRST) << 10) + (secondPart - SURR2_FIRST);
+    }
+
+    private void throwIllegal(int code)
+        throws IOException
+    {
+        if (code > 0x10FFFF) { // over max?
+            throw new IOException("Illegal character point (0x"+Integer.toHexString(code)+") to output; max is 0x10FFFF as per RFC 4627");
+        }
+        if (code >= SURR1_FIRST) {
+            if (code <= SURR1_LAST) { // Unmatched first part (closing without second part?)
+                throw new IOException("Unmatched first part of surrogate pair (0x"+Integer.toHexString(code)+")");
+            }
+            throw new IOException("Unmatched second part of surrogate pair (0x"+Integer.toHexString(code)+")");
+        }
+
+        // should we ever get this?
+        throw new IOException("Illegal character point (0x"+Integer.toHexString(code)+") to output");
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper.java b/src/main/java/com/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper.java
new file mode 100644
index 0000000..a1cf9c8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/json/ByteSourceJsonBootstrapper.java
@@ -0,0 +1,518 @@
+package com.fasterxml.jackson.core.json;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.format.InputAccessor;
+import com.fasterxml.jackson.core.format.MatchStrength;
+import com.fasterxml.jackson.core.io.*;
+import com.fasterxml.jackson.core.sym.BytesToNameCanonicalizer;
+import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer;
+
+/**
+ * This class is used to determine the encoding of byte stream
+ * that is to contain JSON content. Rules are fairly simple, and
+ * defined in JSON specification (RFC-4627 or newer), except
+ * for BOM handling, which is a property of underlying
+ * streams.
+ */
+public final class ByteSourceJsonBootstrapper
+{
+    final static byte UTF8_BOM_1 = (byte) 0xEF;
+    final static byte UTF8_BOM_2 = (byte) 0xBB;
+    final static byte UTF8_BOM_3 = (byte) 0xBF;
+    
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    protected final IOContext _context;
+
+    protected final InputStream _in;
+
+    /*
+    /**********************************************************
+    /* Input buffering
+    /**********************************************************
+     */
+
+    protected final byte[] _inputBuffer;
+
+    private int _inputPtr;
+
+    private int _inputEnd;
+
+    /**
+     * Flag that indicates whether buffer above is to be recycled
+     * after being used or not.
+     */
+    private 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;
+
+    /*
+    /**********************************************************
+    /* Data gathered
+    /**********************************************************
+     */
+
+    protected boolean _bigEndian = true;
+
+    protected int _bytesPerChar = 0; // 0 means "dunno yet"
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public ByteSourceJsonBootstrapper(IOContext ctxt, InputStream in)
+    {
+        _context = ctxt;
+        _in = in;
+        _inputBuffer = ctxt.allocReadIOBuffer();
+        _inputEnd = _inputPtr = 0;
+        _inputProcessed = 0;
+        _bufferRecyclable = true;
+    }
+
+    public ByteSourceJsonBootstrapper(IOContext ctxt, byte[] inputBuffer, int inputStart, int inputLen)
+    {
+        _context = ctxt;
+        _in = null;
+        _inputBuffer = inputBuffer;
+        _inputPtr = inputStart;
+        _inputEnd = (inputStart + inputLen);
+        // Need to offset this for correct location info
+        _inputProcessed = -inputStart;
+        _bufferRecyclable = false;
+    }
+
+    /*
+    /**********************************************************
+    /*  Encoding detection during bootstrapping
+    /**********************************************************
+     */
+    
+    /**
+     * Method that should be called after constructing an instace.
+     * It will figure out encoding that content uses, to allow
+     * for instantiating a proper scanner object.
+     */
+    public JsonEncoding detectEncoding()
+        throws IOException, JsonParseException
+    {
+        boolean foundEncoding = false;
+
+        // First things first: BOM handling
+        /* Note: we can require 4 bytes to be read, since no
+         * combination of BOM + valid JSON content can have
+         * shorter length (shortest valid JSON content is single
+         * digit char, but BOMs are chosen such that combination
+         * is always at least 4 chars long)
+         */
+        if (ensureLoaded(4)) {
+            int quad =  (_inputBuffer[_inputPtr] << 24)
+                | ((_inputBuffer[_inputPtr+1] & 0xFF) << 16)
+                | ((_inputBuffer[_inputPtr+2] & 0xFF) << 8)
+                | (_inputBuffer[_inputPtr+3] & 0xFF);
+            
+            if (handleBOM(quad)) {
+                foundEncoding = true;
+            } else {
+                /* If no BOM, need to auto-detect based on first char;
+                 * this works since it must be 7-bit ascii (wrt. unicode
+                 * compatible encodings, only ones JSON can be transferred
+                 * over)
+                 */
+                // UTF-32?
+                if (checkUTF32(quad)) {
+                    foundEncoding = true;
+                } else if (checkUTF16(quad >>> 16)) {
+                    foundEncoding = true;
+                }
+            }
+        } else if (ensureLoaded(2)) {
+            int i16 = ((_inputBuffer[_inputPtr] & 0xFF) << 8)
+                | (_inputBuffer[_inputPtr+1] & 0xFF);
+            if (checkUTF16(i16)) {
+                foundEncoding = true;
+            }
+        }
+
+        JsonEncoding enc;
+
+        /* Not found yet? As per specs, this means it must be UTF-8. */
+        if (!foundEncoding) {
+            enc = JsonEncoding.UTF8;
+        } else {
+            switch (_bytesPerChar) {
+            case 1:
+                enc = JsonEncoding.UTF8;
+                break;
+            case 2:
+                enc = _bigEndian ? JsonEncoding.UTF16_BE : JsonEncoding.UTF16_LE;
+                break;
+            case 4:
+                enc = _bigEndian ? JsonEncoding.UTF32_BE : JsonEncoding.UTF32_LE;
+                break;
+            default:
+                throw new RuntimeException("Internal error"); // should never get here
+            }
+        }
+        _context.setEncoding(enc);
+        return enc;
+    }
+
+    /*
+    /**********************************************************
+    /* Constructing a Reader
+    /**********************************************************
+     */
+    
+    public Reader constructReader()
+        throws IOException
+    {
+        JsonEncoding enc = _context.getEncoding();
+        switch (enc) { 
+        case UTF32_BE:
+        case UTF32_LE:
+            return new UTF32Reader(_context, _in, _inputBuffer, _inputPtr, _inputEnd,
+                                   _context.getEncoding().isBigEndian());
+
+        case UTF16_BE:
+        case UTF16_LE:
+        case UTF8: // only in non-common case where we don't want to do direct mapping
+            {
+                // First: do we have a Stream? If not, need to create one:
+                InputStream in = _in;
+
+                if (in == null) {
+                    in = new ByteArrayInputStream(_inputBuffer, _inputPtr, _inputEnd);
+                } else {
+                    /* Also, if we have any read but unused input (usually true),
+                     * need to merge that input in:
+                     */
+                    if (_inputPtr < _inputEnd) {
+                        in = new MergedStream(_context, in, _inputBuffer, _inputPtr, _inputEnd);
+                    }
+                }
+                return new InputStreamReader(in, enc.getJavaName());
+            }
+        }
+        throw new RuntimeException("Internal error"); // should never get here
+    }
+
+    public JsonParser constructParser(int features, ObjectCodec codec, BytesToNameCanonicalizer rootByteSymbols, CharsToNameCanonicalizer rootCharSymbols)
+        throws IOException, JsonParseException
+    {
+        JsonEncoding enc = detectEncoding();
+
+        // As per [JACKSON-259], may want to fully disable canonicalization:
+        boolean canonicalize = JsonParser.Feature.CANONICALIZE_FIELD_NAMES.enabledIn(features);
+        boolean intern = JsonParser.Feature.INTERN_FIELD_NAMES.enabledIn(features);
+        if (enc == JsonEncoding.UTF8) {
+            /* and without canonicalization, byte-based approach is not performance; just use std UTF-8 reader
+             * (which is ok for larger input; not so hot for smaller; but this is not a common case)
+             */
+            if (canonicalize) {
+                BytesToNameCanonicalizer can = rootByteSymbols.makeChild(canonicalize, intern);
+                return new UTF8StreamJsonParser(_context, features, _in, codec, can, _inputBuffer, _inputPtr, _inputEnd, _bufferRecyclable);
+            }
+        }
+        return new ReaderBasedJsonParser(_context, features, constructReader(), codec, rootCharSymbols.makeChild(canonicalize, intern));
+    }
+
+    /*
+    /**********************************************************
+    /*  Encoding detection for data format auto-detection
+    /**********************************************************
+     */
+
+    /**
+     * Current implementation is not as thorough as other functionality
+     * ({@link com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper}); 
+     * supports UTF-8, for example. But it should work, for now, and can
+     * be improved as necessary.
+     * 
+     * @since 1.8
+     */
+    public static MatchStrength hasJSONFormat(InputAccessor acc) throws IOException
+    {
+        // Ideally we should see "[" or "{"; but if not, we'll accept double-quote (String)
+        // in future could also consider accepting non-standard matches?
+        
+        if (!acc.hasMoreBytes()) {
+            return MatchStrength.INCONCLUSIVE;
+        }
+        byte b = acc.nextByte();
+        // Very first thing, a UTF-8 BOM?
+        if (b == UTF8_BOM_1) { // yes, looks like UTF-8 BOM
+            if (!acc.hasMoreBytes()) {
+                return MatchStrength.INCONCLUSIVE;
+            }
+            if (acc.nextByte() != UTF8_BOM_2) {
+                return MatchStrength.NO_MATCH;
+            }
+            if (!acc.hasMoreBytes()) {
+                return MatchStrength.INCONCLUSIVE;
+            }
+            if (acc.nextByte() != UTF8_BOM_3) {
+                return MatchStrength.NO_MATCH;
+            }
+            if (!acc.hasMoreBytes()) {
+                return MatchStrength.INCONCLUSIVE;
+            }
+            b = acc.nextByte();
+        }
+        // Then possible leading space
+        int ch = skipSpace(acc, b);
+        if (ch < 0) {
+            return MatchStrength.INCONCLUSIVE;
+        }
+        // First, let's see if it looks like a structured type:
+        if (ch == '{') { // JSON object?
+            // Ideally we need to find either double-quote or closing bracket
+            ch = skipSpace(acc);
+            if (ch < 0) {
+                return MatchStrength.INCONCLUSIVE;
+            }
+            if (ch == '"' || ch == '}') {
+                return MatchStrength.SOLID_MATCH;
+            }
+            // ... should we allow non-standard? Let's not yet... can add if need be
+            return MatchStrength.NO_MATCH;
+        }
+        MatchStrength strength;
+        
+        if (ch == '[') {
+            ch = skipSpace(acc);
+            if (ch < 0) {
+                return MatchStrength.INCONCLUSIVE;
+            }
+            // closing brackets is easy; but for now, let's also accept opening...
+            if (ch == ']' || ch == '[') {
+                return MatchStrength.SOLID_MATCH;
+            }
+            return MatchStrength.SOLID_MATCH;
+        } else {
+            // plain old value is not very convincing...
+            strength = MatchStrength.WEAK_MATCH;
+        }
+
+        if (ch == '"') { // string value
+            return strength;
+        }
+        if (ch <= '9' && ch >= '0') { // number
+            return strength;
+        }
+        if (ch == '-') { // negative number
+            ch = skipSpace(acc);
+            if (ch < 0) {
+                return MatchStrength.INCONCLUSIVE;
+            }
+            return (ch <= '9' && ch >= '0') ? strength : MatchStrength.NO_MATCH;
+        }
+        // or one of literals
+        if (ch == 'n') { // null
+            return tryMatch(acc, "ull", strength);
+        }
+        if (ch == 't') { // true
+            return tryMatch(acc, "rue", strength);
+        }
+        if (ch == 'f') { // false
+            return tryMatch(acc, "alse", strength);
+        }
+        return MatchStrength.NO_MATCH;
+    }
+
+    private final static MatchStrength tryMatch(InputAccessor acc, String matchStr, MatchStrength fullMatchStrength)
+        throws IOException
+    {
+        for (int i = 0, len = matchStr.length(); i < len; ++i) {
+            if (!acc.hasMoreBytes()) {
+                return MatchStrength.INCONCLUSIVE;
+            }
+            if (acc.nextByte() != matchStr.charAt(i)) {
+                return MatchStrength.NO_MATCH;
+            }
+        }
+        return fullMatchStrength;
+    }
+    
+    private final static int skipSpace(InputAccessor acc) throws IOException
+    {
+        if (!acc.hasMoreBytes()) {
+            return -1;
+        }
+        return skipSpace(acc, acc.nextByte());
+    }
+    
+    private final static int skipSpace(InputAccessor acc, byte b) throws IOException
+    {
+        while (true) {
+            int ch = (int) b & 0xFF;
+            if (!(ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t')) {
+                return ch;
+            }
+            if (!acc.hasMoreBytes()) {
+                return -1;
+            }
+            b = acc.nextByte();
+            ch = (int) b & 0xFF;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods, parsing
+    /**********************************************************
+     */
+
+    /**
+     * @return True if a BOM was succesfully found, and encoding
+     *   thereby recognized.
+     */
+    private boolean handleBOM(int quad)
+        throws IOException
+    {
+        /* Handling of (usually) optional BOM (required for
+         * multi-byte formats); first 32-bit charsets:
+         */
+        switch (quad) {
+        case 0x0000FEFF:
+            _bigEndian = true;
+            _inputPtr += 4;
+            _bytesPerChar = 4;
+            return true;
+        case 0xFFFE0000: // UCS-4, LE?
+            _inputPtr += 4;
+            _bytesPerChar = 4;
+            _bigEndian = false;
+            return true;
+        case 0x0000FFFE: // UCS-4, in-order...
+            reportWeirdUCS4("2143"); // throws exception
+        case 0xFEFF0000: // UCS-4, in-order...
+            reportWeirdUCS4("3412"); // throws exception
+        }
+        // Ok, if not, how about 16-bit encoding BOMs?
+        int msw = quad >>> 16;
+        if (msw == 0xFEFF) { // UTF-16, BE
+            _inputPtr += 2;
+            _bytesPerChar = 2;
+            _bigEndian = true;
+            return true;
+        }
+        if (msw == 0xFFFE) { // UTF-16, LE
+            _inputPtr += 2;
+            _bytesPerChar = 2;
+            _bigEndian = false;
+            return true;
+        }
+        // And if not, then UTF-8 BOM?
+        if ((quad >>> 8) == 0xEFBBBF) { // UTF-8
+            _inputPtr += 3;
+            _bytesPerChar = 1;
+            _bigEndian = true; // doesn't really matter
+            return true;
+        }
+        return false;
+    }
+
+    private boolean checkUTF32(int quad)
+        throws IOException
+    {
+        /* Handling of (usually) optional BOM (required for
+         * multi-byte formats); first 32-bit charsets:
+         */
+        if ((quad >> 8) == 0) { // 0x000000?? -> UTF32-BE
+            _bigEndian = true;
+        } else if ((quad & 0x00FFFFFF) == 0) { // 0x??000000 -> UTF32-LE
+            _bigEndian = false;
+        } else if ((quad & ~0x00FF0000) == 0) { // 0x00??0000 -> UTF32-in-order
+            reportWeirdUCS4("3412");
+        } else if ((quad & ~0x0000FF00) == 0) { // 0x0000??00 -> UTF32-in-order
+            reportWeirdUCS4("2143");
+        } else {
+            // Can not be valid UTF-32 encoded JSON...
+            return false;
+        }
+        // Not BOM (just regular content), nothing to skip past:
+        //_inputPtr += 4;
+        _bytesPerChar = 4;
+        return true;
+    }
+
+    private boolean checkUTF16(int i16)
+    {
+        if ((i16 & 0xFF00) == 0) { // UTF-16BE
+            _bigEndian = true;
+        } else if ((i16 & 0x00FF) == 0) { // UTF-16LE
+            _bigEndian = false;
+        } else { // nope, not  UTF-16
+            return false;
+        }
+        // Not BOM (just regular content), nothing to skip past:
+        //_inputPtr += 2;
+        _bytesPerChar = 2;
+        return true;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, problem reporting
+    /**********************************************************
+     */
+
+    private void reportWeirdUCS4(String type)
+        throws IOException
+    {
+        throw new CharConversionException("Unsupported UCS-4 endianness ("+type+") detected");
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, raw input access
+    /**********************************************************
+     */
+
+    protected boolean ensureLoaded(int minimum)
+        throws IOException
+    {
+        /* Let's assume here buffer has enough room -- this will always
+         * be true for the limited used this method gets
+         */
+        int gotten = (_inputEnd - _inputPtr);
+        while (gotten < minimum) {
+            int count;
+
+            if (_in == null) { // block source
+                count = -1;
+            } else {
+                count = _in.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd);
+            }
+            if (count < 1) {
+                return false;
+            }
+            _inputEnd += count;
+            gotten += count;
+        }
+        return true;
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java b/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java
new file mode 100644
index 0000000..0edc236
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/json/JsonReadContext.java
@@ -0,0 +1,188 @@
+package com.fasterxml.jackson.core.json;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.CharTypes;
+
+/**
+ * Extension of {@link JsonStreamContext}, which implements
+ * core methods needed, and also exposes
+ * more complete API to parser implementation classes.
+ */
+public final class JsonReadContext
+    extends JsonStreamContext
+{
+    // // // Configuration
+
+    protected final JsonReadContext _parent;
+
+    // // // Location information (minus source reference)
+
+    protected int _lineNr;
+    protected int _columnNr;
+
+    protected String _currentName;
+
+    /*
+    /**********************************************************
+    /* Simple instance reuse slots; speeds up things
+    /* a bit (10-15%) for docs with lots of small
+    /* arrays/objects (for which allocation was
+    /* visible in profile stack frames)
+    /**********************************************************
+     */
+
+    protected JsonReadContext _child = null;
+
+    /*
+    /**********************************************************
+    /* Instance construction, reuse
+    /**********************************************************
+     */
+
+    public JsonReadContext(JsonReadContext parent, int type, int lineNr, int colNr)
+    {
+        super();
+        _type = type;
+        _parent = parent;
+        _lineNr = lineNr;
+        _columnNr = colNr;
+        _index = -1;
+    }
+
+    protected final void reset(int type, int lineNr, int colNr)
+    {
+        _type = type;
+        _index = -1;
+        _lineNr = lineNr;
+        _columnNr = colNr;
+        _currentName = null;
+    }
+
+    // // // Factory methods
+
+    public static JsonReadContext createRootContext(int lineNr, int colNr)
+    {
+        return new JsonReadContext(null, TYPE_ROOT, lineNr, colNr);
+    }
+
+    /**
+     * @since 1.9
+     */
+    public static JsonReadContext createRootContext()
+    {
+        return new JsonReadContext(null, TYPE_ROOT, 1, 0);
+    }
+    
+    public final JsonReadContext createChildArrayContext(int lineNr, int colNr)
+    {
+        JsonReadContext ctxt = _child;
+        if (ctxt == null) {
+            _child = ctxt = new JsonReadContext(this, TYPE_ARRAY, lineNr, colNr);
+            return ctxt;
+        }
+        ctxt.reset(TYPE_ARRAY, lineNr, colNr);
+        return ctxt;
+    }
+
+    public final JsonReadContext createChildObjectContext(int lineNr, int colNr)
+    {
+        JsonReadContext ctxt = _child;
+        if (ctxt == null) {
+            _child = ctxt = new JsonReadContext(this, TYPE_OBJECT, lineNr, colNr);
+            return ctxt;
+        }
+        ctxt.reset(TYPE_OBJECT, lineNr, colNr);
+        return ctxt;
+    }
+
+    /*
+    /**********************************************************
+    /* Abstract method implementation
+    /**********************************************************
+     */
+
+    @Override
+    public final String getCurrentName() { return _currentName; }
+
+    @Override
+    public final JsonReadContext getParent() { return _parent; }
+
+    /*
+    /**********************************************************
+    /* Extended API
+    /**********************************************************
+     */
+
+    /**
+     * @return Location pointing to the point where the context
+     *   start marker was found
+     */
+    public final JsonLocation getStartLocation(Object srcRef)
+    {
+        /* We don't keep track of offsets at this level (only
+         * reader does)
+         */
+        long totalChars = -1L;
+
+        return new JsonLocation(srcRef, totalChars, _lineNr, _columnNr);
+    }
+
+    /*
+    /**********************************************************
+    /* State changes
+    /**********************************************************
+     */
+
+    public final boolean expectComma()
+    {
+        /* Assumption here is that we will be getting a value (at least
+         * before calling this method again), and
+         * so will auto-increment index to avoid having to do another call
+         */
+        int ix = ++_index; // starts from -1
+        return (_type != TYPE_ROOT && ix > 0);
+    }
+
+    public void setCurrentName(String name)
+    {
+        _currentName = name;
+    }
+
+    /*
+    /**********************************************************
+    /* Overridden standard methods
+    /**********************************************************
+     */
+
+    /**
+     * Overridden to provide developer readable "JsonPath" representation
+     * of the context.
+     */
+    @Override
+    public final String toString()
+    {
+        StringBuilder sb = new StringBuilder(64);
+        switch (_type) {
+        case TYPE_ROOT:
+            sb.append("/");
+            break;
+        case TYPE_ARRAY:
+            sb.append('[');
+            sb.append(getCurrentIndex());
+            sb.append(']');
+            break;
+        case TYPE_OBJECT:
+            sb.append('{');
+            if (_currentName != null) {
+                sb.append('"');
+                CharTypes.appendQuoted(sb, _currentName);
+                sb.append('"');
+            } else {
+                sb.append('?');
+            }
+            sb.append('}');
+            break;
+        }
+        return sb.toString();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/JsonWriteContext.java b/src/main/java/com/fasterxml/jackson/core/json/JsonWriteContext.java
new file mode 100644
index 0000000..bee3c62
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/json/JsonWriteContext.java
@@ -0,0 +1,178 @@
+package com.fasterxml.jackson.core.json;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Extension of {@link JsonStreamContext}, which implements
+ * core methods needed, and also exposes
+ * more complete API to generator implementation classes.
+ */
+public class JsonWriteContext
+    extends JsonStreamContext
+{
+    // // // Return values for writeValue()
+
+    public final static int STATUS_OK_AS_IS = 0;
+    public final static int STATUS_OK_AFTER_COMMA = 1;
+    public final static int STATUS_OK_AFTER_COLON = 2;
+    public final static int STATUS_OK_AFTER_SPACE = 3; // in root context
+    public final static int STATUS_EXPECT_VALUE = 4;
+    public final static int STATUS_EXPECT_NAME = 5;
+
+    protected final JsonWriteContext _parent;
+
+    /**
+     * Name of the field of which value is to be parsed; only
+     * used for OBJECT contexts
+     */
+    protected String _currentName;
+    
+    /*
+    /**********************************************************
+    /* Simple instance reuse slots; speed up things
+    /* a bit (10-15%) for docs with lots of small
+    /* arrays/objects
+    /**********************************************************
+     */
+
+    protected JsonWriteContext _child = null;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    protected JsonWriteContext(int type, JsonWriteContext parent)
+    {
+        super();
+        _type = type;
+        _parent = parent;
+        _index = -1;
+    }
+    
+    // // // Factory methods
+
+    public static JsonWriteContext createRootContext()
+    {
+        return new JsonWriteContext(TYPE_ROOT, null);
+    }
+
+    private final JsonWriteContext reset(int type) {
+        _type = type;
+        _index = -1;
+        _currentName = null;
+        return this;
+    }
+    
+    public final JsonWriteContext createChildArrayContext()
+    {
+        JsonWriteContext ctxt = _child;
+        if (ctxt == null) {
+            _child = ctxt = new JsonWriteContext(TYPE_ARRAY, this);
+            return ctxt;
+        }
+        return ctxt.reset(TYPE_ARRAY);
+    }
+
+    public final JsonWriteContext createChildObjectContext()
+    {
+        JsonWriteContext ctxt = _child;
+        if (ctxt == null) {
+            _child = ctxt = new JsonWriteContext(TYPE_OBJECT, this);
+            return ctxt;
+        }
+        return ctxt.reset(TYPE_OBJECT);
+    }
+
+    // // // Shared API
+
+    @Override
+    public final JsonWriteContext getParent() { return _parent; }
+
+    @Override
+    public final String getCurrentName() { return _currentName; }
+    
+    // // // API sub-classes are to implement
+
+    /**
+     * Method that writer is to call before it writes a field name.
+     *
+     * @return Index of the field entry (0-based)
+     */
+    public final int writeFieldName(String name)
+    {
+        if (_type == TYPE_OBJECT) {
+            if (_currentName != null) { // just wrote a name...
+                return STATUS_EXPECT_VALUE;
+            }
+            _currentName = name;
+            return (_index < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA;
+        }
+        return STATUS_EXPECT_VALUE;
+    }
+    
+    public final int writeValue()
+    {
+        // Most likely, object:
+        if (_type == TYPE_OBJECT) {
+            if (_currentName == null) {
+                return STATUS_EXPECT_NAME;
+            }
+            _currentName = null;
+            ++_index;
+            return STATUS_OK_AFTER_COLON;
+        }
+
+        // Ok, array?
+        if (_type == TYPE_ARRAY) {
+            int ix = _index;
+            ++_index;
+            return (ix < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA;
+        }
+        
+        // Nope, root context
+        // No commas within root context, but need space
+        ++_index;
+        return (_index == 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_SPACE;
+    }
+
+    // // // Internally used abstract methods
+
+    protected final void appendDesc(StringBuilder sb)
+    {
+        if (_type == TYPE_OBJECT) {
+            sb.append('{');
+            if (_currentName != null) {
+                sb.append('"');
+                // !!! TODO: Name chars should be escaped?
+                sb.append(_currentName);
+                sb.append('"');
+            } else {
+                sb.append('?');
+            }
+            sb.append('}');
+        } else if (_type == TYPE_ARRAY) {
+            sb.append('[');
+            sb.append(getCurrentIndex());
+            sb.append(']');
+        } else {
+            // nah, ROOT:
+            sb.append("/");
+        }
+    }
+
+    // // // Overridden standard methods
+
+    /**
+     * Overridden to provide developer writeable "JsonPath" representation
+     * of the context.
+     */
+    @Override
+    public final String toString()
+    {
+        StringBuilder sb = new StringBuilder(64);
+        appendDesc(sb);
+        return sb.toString();
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java
new file mode 100644
index 0000000..4ad9357
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java
@@ -0,0 +1,1817 @@
+package com.fasterxml.jackson.core.json;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.base.ParserBase;
+import com.fasterxml.jackson.core.io.CharTypes;
+import com.fasterxml.jackson.core.io.IOContext;
+import com.fasterxml.jackson.core.sym.CharsToNameCanonicalizer;
+import com.fasterxml.jackson.core.util.*;
+
+/**
+ * This is a concrete implementation of {@link JsonParser}, which is
+ * based on a {@link java.io.Reader} to handle low-level character
+ * conversion tasks.
+ */
+public final class ReaderBasedJsonParser
+    extends ParserBase
+{
+    /*
+    /**********************************************************
+    /* Input configuration
+    /**********************************************************
+     */
+
+    /**
+     * Reader that can be used for reading more content, if one
+     * buffer from input source, but in some cases pre-loaded buffer
+     * is handed to the parser.
+     */
+    protected Reader _reader;
+
+    /**
+     * Current buffer from which data is read; generally data is read into
+     * buffer from input source.
+     */
+    protected char[] _inputBuffer;
+
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    protected ObjectCodec _objectCodec;
+
+    final protected CharsToNameCanonicalizer _symbols;
+
+    /*
+    /**********************************************************
+    /* Parsing state
+    /**********************************************************
+     */
+    
+    /**
+     * Flag that indicates that the current token has not yet
+     * been fully processed, and needs to be finished for
+     * some access (or skipped to obtain the next token)
+     */
+    protected boolean _tokenIncomplete = false;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public ReaderBasedJsonParser(IOContext ctxt, int features, Reader r,
+                             ObjectCodec codec, CharsToNameCanonicalizer st)
+    {
+        super(ctxt, features);
+        _reader = r;
+        _inputBuffer = ctxt.allocTokenBuffer();
+        _objectCodec = codec;
+        _symbols = st;
+    }
+
+    /*
+    /**********************************************************
+    /* Base method defs, overrides
+    /**********************************************************
+     */
+    
+    @Override
+    public ObjectCodec getCodec() {
+        return _objectCodec;
+    }
+
+    @Override
+    public void setCodec(ObjectCodec c) {
+        _objectCodec = c;
+    }
+
+    @Override
+    public int releaseBuffered(Writer w) throws IOException
+    {
+        int count = _inputEnd - _inputPtr;
+        if (count < 1) {
+            return 0;
+        }
+        // let's just advance ptr to end
+        int origPtr = _inputPtr;
+        w.write(_inputBuffer, origPtr, count);
+        return count;
+    }
+
+    @Override
+    public Object getInputSource() {
+        return _reader;
+    }
+
+    @Override
+    protected final boolean loadMore() throws IOException
+    {
+        _currInputProcessed += _inputEnd;
+        _currInputRowStart -= _inputEnd;
+
+        if (_reader != null) {
+            int count = _reader.read(_inputBuffer, 0, _inputBuffer.length);
+            if (count > 0) {
+                _inputPtr = 0;
+                _inputEnd = count;
+                return true;
+            }
+            // End of input
+            _closeInput();
+            // Should never return 0, so let's fail
+            if (count == 0) {
+                throw new IOException("Reader returned 0 characters when trying to read "+_inputEnd);
+            }
+        }
+        return false;
+    }
+
+    protected char getNextChar(String eofMsg)
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            if (!loadMore()) {
+                _reportInvalidEOF(eofMsg);
+            }
+        }
+        return _inputBuffer[_inputPtr++];
+    }
+
+    @Override
+    protected void _closeInput() throws IOException
+    {
+        /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
+         *   on the underlying Reader, unless we "own" it, or auto-closing
+         *   feature is enabled.
+         *   One downside is that when using our optimized
+         *   Reader (granted, we only do that for UTF-32...) this
+         *   means that buffer recycling won't work correctly.
+         */
+        if (_reader != null) {
+            if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) {
+                _reader.close();
+            }
+            _reader = null;
+        }
+    }
+
+    /**
+     * Method called to release internal buffers owned by the base
+     * reader. This may be called along with {@link #_closeInput} (for
+     * example, when explicitly closing this reader instance), or
+     * separately (if need be).
+     */
+    @Override
+    protected void _releaseBuffers()
+        throws IOException
+    {
+        super._releaseBuffers();
+        char[] buf = _inputBuffer;
+        if (buf != null) {
+            _inputBuffer = null;
+            _ioContext.releaseTokenBuffer(buf);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, data access
+    /**********************************************************
+     */
+    
+    /**
+     * 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.
+     */
+    @Override
+    public final String getText()
+        throws IOException, JsonParseException
+    {
+        JsonToken t = _currToken;
+        if (t == JsonToken.VALUE_STRING) {
+            if (_tokenIncomplete) {
+                _tokenIncomplete = false;
+                _finishString(); // only strings can be incomplete
+            }
+            return _textBuffer.contentsAsString();
+        }
+        return _getText2(t);
+    }
+    
+    protected final String _getText2(JsonToken t)
+    {
+        if (t == null) {
+            return null;
+        }
+        switch (t) {
+        case FIELD_NAME:
+            return _parsingContext.getCurrentName();
+
+        case VALUE_STRING:
+            // fall through
+        case VALUE_NUMBER_INT:
+        case VALUE_NUMBER_FLOAT:
+            return _textBuffer.contentsAsString();
+        }
+        return t.asString();
+    }
+
+    @Override
+    public char[] getTextCharacters()
+        throws IOException, JsonParseException
+    {
+        if (_currToken != null) { // null only before/after document
+            switch (_currToken) {
+                
+            case FIELD_NAME:
+                if (!_nameCopied) {
+                    String name = _parsingContext.getCurrentName();
+                    int nameLen = name.length();
+                    if (_nameCopyBuffer == null) {
+                        _nameCopyBuffer = _ioContext.allocNameCopyBuffer(nameLen);
+                    } else if (_nameCopyBuffer.length < nameLen) {
+                        _nameCopyBuffer = new char[nameLen];
+                    }
+                    name.getChars(0, nameLen, _nameCopyBuffer, 0);
+                    _nameCopied = true;
+                }
+                return _nameCopyBuffer;
+    
+            case VALUE_STRING:
+                if (_tokenIncomplete) {
+                    _tokenIncomplete = false;
+                    _finishString(); // only strings can be incomplete
+                }
+                // fall through
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                return _textBuffer.getTextBuffer();
+                
+            default:
+                return _currToken.asCharArray();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public int getTextLength()
+        throws IOException, JsonParseException
+    {
+        if (_currToken != null) { // null only before/after document
+            switch (_currToken) {
+                
+            case FIELD_NAME:
+                return _parsingContext.getCurrentName().length();
+            case VALUE_STRING:
+                if (_tokenIncomplete) {
+                    _tokenIncomplete = false;
+                    _finishString(); // only strings can be incomplete
+                }
+                // fall through
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                return _textBuffer.size();
+                
+            default:
+                return _currToken.asCharArray().length;
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public int getTextOffset() throws IOException, JsonParseException
+    {
+        // Most have offset of 0, only some may have other values:
+        if (_currToken != null) {
+            switch (_currToken) {
+            case FIELD_NAME:
+                return 0;
+            case VALUE_STRING:
+                if (_tokenIncomplete) {
+                    _tokenIncomplete = false;
+                    _finishString(); // only strings can be incomplete
+                }
+                // fall through
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                return _textBuffer.getTextOffset();
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public byte[] getBinaryValue(Base64Variant b64variant)
+        throws IOException, JsonParseException
+    {
+        if (_currToken != JsonToken.VALUE_STRING &&
+                (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT || _binaryValue == null)) {
+            _reportError("Current token ("+_currToken+") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary");
+        }
+        /* To ensure that we won't see inconsistent data, better clear up
+         * state...
+         */
+        if (_tokenIncomplete) {
+            try {
+                _binaryValue = _decodeBase64(b64variant);
+            } catch (IllegalArgumentException iae) {
+                throw _constructError("Failed to decode VALUE_STRING as base64 ("+b64variant+"): "+iae.getMessage());
+            }
+            /* let's clear incomplete only now; allows for accessing other
+             * textual content in error cases
+             */
+            _tokenIncomplete = false;
+        } else { // may actually require conversion...
+            if (_binaryValue == null) {
+                ByteArrayBuilder builder = _getByteArrayBuilder();
+                _decodeBase64(getText(), builder, b64variant);
+                _binaryValue = builder.toByteArray();
+            }
+        }
+        return _binaryValue;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, traversal
+    /**********************************************************
+     */
+
+    /**
+     * @return Next token from the stream, if any found, or null
+     *   to indicate end-of-input
+     */
+    @Override
+    public JsonToken nextToken()
+        throws IOException, JsonParseException
+    {
+        _numTypesValid = NR_UNKNOWN;
+
+        /* First: field names are special -- we will always tokenize
+         * (part of) value along with field name to simplify
+         * state handling. If so, can and need to use secondary token:
+         */
+        if (_currToken == JsonToken.FIELD_NAME) {
+            return _nextAfterName();
+        }
+        if (_tokenIncomplete) {
+            _skipString(); // only strings can be partial
+        }
+        int i = _skipWSOrEnd();
+        if (i < 0) { // end-of-input
+            /* 19-Feb-2009, tatu: Should actually close/release things
+             *    like input source, symbol table and recyclable buffers now.
+             */
+            close();
+            return (_currToken = null);
+        }
+
+        /* First, need to ensure we know the starting location of token
+         * after skipping leading white space
+         */
+        _tokenInputTotal = _currInputProcessed + _inputPtr - 1;
+        _tokenInputRow = _currInputRow;
+        _tokenInputCol = _inputPtr - _currInputRowStart - 1;
+
+        // finally: clear any data retained so far
+        _binaryValue = null;
+
+        // Closing scope?
+        if (i == INT_RBRACKET) {
+            if (!_parsingContext.inArray()) {
+                _reportMismatchedEndMarker(i, '}');
+            }
+            _parsingContext = _parsingContext.getParent();
+            return (_currToken = JsonToken.END_ARRAY);
+        }
+        if (i == INT_RCURLY) {
+            if (!_parsingContext.inObject()) {
+                _reportMismatchedEndMarker(i, ']');
+            }
+            _parsingContext = _parsingContext.getParent();
+            return (_currToken = JsonToken.END_OBJECT);
+        }
+
+        // Nope: do we then expect a comma?
+        if (_parsingContext.expectComma()) {
+            if (i != INT_COMMA) {
+                _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
+            }
+            i = _skipWS();
+        }
+
+        /* And should we now have a name? Always true for
+         * Object contexts, since the intermediate 'expect-value'
+         * state is never retained.
+         */
+        boolean inObject = _parsingContext.inObject();
+        if (inObject) {
+           // First, field name itself:
+            String name = _parseFieldName(i);
+            _parsingContext.setCurrentName(name);
+            _currToken = JsonToken.FIELD_NAME;
+            i = _skipWS();
+            if (i != INT_COLON) {
+                _reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
+            }
+            i = _skipWS();
+        }
+
+        // Ok: we must have a value... what is it?
+
+        JsonToken t;
+
+        switch (i) {
+        case INT_QUOTE:
+            _tokenIncomplete = true;
+            t = JsonToken.VALUE_STRING;
+            break;
+        case INT_LBRACKET:
+            if (!inObject) {
+                _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+            }
+            t = JsonToken.START_ARRAY;
+            break;
+        case INT_LCURLY:
+            if (!inObject) {
+                _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+            }
+            t = JsonToken.START_OBJECT;
+            break;
+        case INT_RBRACKET:
+        case INT_RCURLY:
+            // Error: neither is valid at this point; valid closers have
+            // been handled earlier
+            _reportUnexpectedChar(i, "expected a value");
+        case INT_t:
+            _matchToken("true", 1);
+            t = JsonToken.VALUE_TRUE;
+            break;
+        case INT_f:
+            _matchToken("false", 1);
+            t = JsonToken.VALUE_FALSE;
+            break;
+        case INT_n:
+            _matchToken("null", 1);
+            t = JsonToken.VALUE_NULL;
+            break;
+
+        case INT_MINUS:
+            /* Should we have separate handling for plus? Although
+             * it is not allowed per se, it may be erroneously used,
+             * and could be indicate by a more specific error message.
+             */
+        case INT_0:
+        case INT_1:
+        case INT_2:
+        case INT_3:
+        case INT_4:
+        case INT_5:
+        case INT_6:
+        case INT_7:
+        case INT_8:
+        case INT_9:
+            t = parseNumberText(i);
+            break;
+        default:
+            t = _handleUnexpectedValue(i);
+            break;
+        }
+
+        if (inObject) {
+            _nextToken = t;
+            return _currToken;
+        }
+        _currToken = t;
+        return t;
+    }
+
+    private final JsonToken _nextAfterName()
+    {
+        _nameCopied = false; // need to invalidate if it was copied
+        JsonToken t = _nextToken;
+        _nextToken = null;
+        // Also: may need to start new context?
+        if (t == JsonToken.START_ARRAY) {
+            _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+        } else if (t == JsonToken.START_OBJECT) {
+            _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+        }
+        return (_currToken = t);
+    }
+
+    /*
+    @Override
+    public boolean nextFieldName(SerializableString str)
+         throws IOException, JsonParseException
+     */
+
+    // note: identical to one in Utf8StreamParser
+    @Override
+    public String nextTextValue()
+        throws IOException, JsonParseException
+    {
+        if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+            _nameCopied = false;
+            JsonToken t = _nextToken;
+            _nextToken = null;
+            _currToken = t;
+            if (t == JsonToken.VALUE_STRING) {
+                if (_tokenIncomplete) {
+                    _tokenIncomplete = false;
+                    _finishString();
+                }
+                return _textBuffer.contentsAsString();
+            }
+            if (t == JsonToken.START_ARRAY) {
+                _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+            } else if (t == JsonToken.START_OBJECT) {
+                _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+            }
+            return null;
+        }
+        // !!! TODO: optimize this case as well
+        return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
+    }
+
+    // note: identical to one in Utf8StreamParser
+    @Override
+    public int nextIntValue(int defaultValue)
+        throws IOException, JsonParseException
+    {
+        if (_currToken == JsonToken.FIELD_NAME) {
+            _nameCopied = false;
+            JsonToken t = _nextToken;
+            _nextToken = null;
+            _currToken = t;
+            if (t == JsonToken.VALUE_NUMBER_INT) {
+                return getIntValue();
+            }
+            if (t == JsonToken.START_ARRAY) {
+                _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+            } else if (t == JsonToken.START_OBJECT) {
+                _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+            }
+            return defaultValue;
+        }
+        // !!! TODO: optimize this case as well
+        return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
+    }
+
+    // note: identical to one in Utf8StreamParser
+    @Override
+    public long nextLongValue(long defaultValue)
+        throws IOException, JsonParseException
+    {
+        if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+            _nameCopied = false;
+            JsonToken t = _nextToken;
+            _nextToken = null;
+            _currToken = t;
+            if (t == JsonToken.VALUE_NUMBER_INT) {
+                return getLongValue();
+            }
+            if (t == JsonToken.START_ARRAY) {
+                _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+            } else if (t == JsonToken.START_OBJECT) {
+                _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+            }
+            return defaultValue;
+        }
+        // !!! TODO: optimize this case as well
+        return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
+    }
+
+    // note: identical to one in Utf8StreamParser
+    @Override
+    public Boolean nextBooleanValue()
+        throws IOException, JsonParseException
+    {
+        if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+            _nameCopied = false;
+            JsonToken t = _nextToken;
+            _nextToken = null;
+            _currToken = t;
+            if (t == JsonToken.VALUE_TRUE) {
+                return Boolean.TRUE;
+            }
+            if (t == JsonToken.VALUE_FALSE) {
+                return Boolean.FALSE;
+            }
+            if (t == JsonToken.START_ARRAY) {
+                _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+            } else if (t == JsonToken.START_OBJECT) {
+                _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+            }
+            return null;
+        }
+        switch (nextToken()) {
+        case VALUE_TRUE:
+            return Boolean.TRUE;
+        case VALUE_FALSE:
+            return Boolean.FALSE;
+        }
+        return null;
+    }
+    
+    @Override
+    public void close() throws IOException
+    {
+        super.close();
+        _symbols.release();
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, number parsing
+    /* (note: in 1.8 and prior, part of "ReaderBasedNumericParser"
+    /**********************************************************
+     */
+
+    /**
+     * Initial parsing method for number values. It needs to be able
+     * to parse enough input to be able to determine whether the
+     * value is to be considered a simple integer value, or a more
+     * generic decimal value: latter of which needs to be expressed
+     * as a floating point number. The basic rule is that if the number
+     * has no fractional or exponential part, it is an integer; otherwise
+     * a floating point number.
+     *<p>
+     * Because much of input has to be processed in any case, no partial
+     * parsing is done: all input text will be stored for further
+     * processing. However, actual numeric value conversion will be
+     * deferred, since it is usually the most complicated and costliest
+     * part of processing.
+     */
+    protected final JsonToken parseNumberText(int ch)
+        throws IOException, JsonParseException
+    {
+        /* Although we will always be complete with respect to textual
+         * representation (that is, all characters will be parsed),
+         * actual conversion to a number is deferred. Thus, need to
+         * note that no representations are valid yet
+         */
+        boolean negative = (ch == INT_MINUS);
+        int ptr = _inputPtr;
+        int startPtr = ptr-1; // to include sign/digit already read
+        final int inputLen = _inputEnd;
+
+        dummy_loop:
+        do { // dummy loop, to be able to break out
+            if (negative) { // need to read the next digit
+                if (ptr >= _inputEnd) {
+                    break dummy_loop;
+                }
+                ch = _inputBuffer[ptr++];
+                // First check: must have a digit to follow minus sign
+                if (ch > INT_9 || ch < INT_0) {
+                    _inputPtr = ptr;
+                    return _handleInvalidNumberStart(ch, true);
+                }
+                /* (note: has been checked for non-negative already, in
+                 * the dispatching code that determined it should be
+                 * a numeric value)
+                 */
+            }
+            // One special case, leading zero(es):
+            if (ch == INT_0) {
+                break dummy_loop;
+            }
+            
+            /* First, let's see if the whole number is contained within
+             * the input buffer unsplit. This should be the common case;
+             * and to simplify processing, we will just reparse contents
+             * in the alternative case (number split on buffer boundary)
+             */
+            
+            int intLen = 1; // already got one
+            
+            // First let's get the obligatory integer part:
+            
+            int_loop:
+            while (true) {
+                if (ptr >= _inputEnd) {
+                    break dummy_loop;
+                }
+                ch = (int) _inputBuffer[ptr++];
+                if (ch < INT_0 || ch > INT_9) {
+                    break int_loop;
+                }
+                ++intLen;
+            }
+
+            int fractLen = 0;
+            
+            // And then see if we get other parts
+            if (ch == INT_DECIMAL_POINT) { // yes, fraction
+                fract_loop:
+                while (true) {
+                    if (ptr >= inputLen) {
+                        break dummy_loop;
+                    }
+                    ch = (int) _inputBuffer[ptr++];
+                    if (ch < INT_0 || ch > INT_9) {
+                        break fract_loop;
+                    }
+                    ++fractLen;
+                }
+                // must be followed by sequence of ints, one minimum
+                if (fractLen == 0) {
+                    reportUnexpectedNumberChar(ch, "Decimal point not followed by a digit");
+                }
+            }
+
+            int expLen = 0;
+            if (ch == INT_e || ch == INT_E) { // and/or exponent
+                if (ptr >= inputLen) {
+                    break dummy_loop;
+                }
+                // Sign indicator?
+                ch = (int) _inputBuffer[ptr++];
+                if (ch == INT_MINUS || ch == INT_PLUS) { // yup, skip for now
+                    if (ptr >= inputLen) {
+                        break dummy_loop;
+                    }
+                    ch = (int) _inputBuffer[ptr++];
+                }
+                while (ch <= INT_9 && ch >= INT_0) {
+                    ++expLen;
+                    if (ptr >= inputLen) {
+                        break dummy_loop;
+                    }
+                    ch = (int) _inputBuffer[ptr++];
+                }
+                // must be followed by sequence of ints, one minimum
+                if (expLen == 0) {
+                    reportUnexpectedNumberChar(ch, "Exponent indicator not followed by a digit");
+                }
+            }
+
+            // Got it all: let's add to text buffer for parsing, access
+            --ptr; // need to push back following separator
+            _inputPtr = ptr;
+            int len = ptr-startPtr;
+            _textBuffer.resetWithShared(_inputBuffer, startPtr, len);
+            return reset(negative, intLen, fractLen, expLen);
+        } while (false);
+
+        _inputPtr = negative ? (startPtr+1) : startPtr;
+        return parseNumberText2(negative);
+    }
+
+    /**
+     * Method called to parse a number, when the primary parse
+     * method has failed to parse it, due to it being split on
+     * buffer boundary. As a result code is very similar, except
+     * that it has to explicitly copy contents to the text buffer
+     * instead of just sharing the main input buffer.
+     */
+    private final JsonToken parseNumberText2(boolean negative)
+        throws IOException, JsonParseException
+    {
+        char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+        int outPtr = 0;
+
+        // Need to prepend sign?
+        if (negative) {
+            outBuf[outPtr++] = '-';
+        }
+
+        // This is the place to do leading-zero check(s) too:
+        int intLen = 0;
+        char c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++] : getNextChar("No digit following minus sign");
+        if (c == '0') {
+            c = _verifyNoLeadingZeroes();
+        }
+        boolean eof = false;
+
+        // Ok, first the obligatory integer part:
+        int_loop:
+        while (c >= '0' && c <= '9') {
+            ++intLen;
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
+            outBuf[outPtr++] = c;
+            if (_inputPtr >= _inputEnd && !loadMore()) {
+                // EOF is legal for main level int values
+                c = CHAR_NULL;
+                eof = true;
+                break int_loop;
+            }
+            c = _inputBuffer[_inputPtr++];
+        }
+        // Also, integer part is not optional
+        if (intLen == 0) {
+            reportInvalidNumber("Missing integer part (next char "+_getCharDesc(c)+")");
+        }
+
+        int fractLen = 0;
+        // And then see if we get other parts
+        if (c == '.') { // yes, fraction
+            outBuf[outPtr++] = c;
+
+            fract_loop:
+            while (true) {
+                if (_inputPtr >= _inputEnd && !loadMore()) {
+                    eof = true;
+                    break fract_loop;
+                }
+                c = _inputBuffer[_inputPtr++];
+                if (c < INT_0 || c > INT_9) {
+                    break fract_loop;
+                }
+                ++fractLen;
+                if (outPtr >= outBuf.length) {
+                    outBuf = _textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                outBuf[outPtr++] = c;
+            }
+            // must be followed by sequence of ints, one minimum
+            if (fractLen == 0) {
+                reportUnexpectedNumberChar(c, "Decimal point not followed by a digit");
+            }
+        }
+
+        int expLen = 0;
+        if (c == 'e' || c == 'E') { // exponent?
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
+            outBuf[outPtr++] = c;
+            // Not optional, can require that we get one more char
+            c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++]
+                : getNextChar("expected a digit for number exponent");
+            // Sign indicator?
+            if (c == '-' || c == '+') {
+                if (outPtr >= outBuf.length) {
+                    outBuf = _textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                outBuf[outPtr++] = c;
+                // Likewise, non optional:
+                c = (_inputPtr < _inputEnd) ? _inputBuffer[_inputPtr++]
+                    : getNextChar("expected a digit for number exponent");
+            }
+
+            exp_loop:
+            while (c <= INT_9 && c >= INT_0) {
+                ++expLen;
+                if (outPtr >= outBuf.length) {
+                    outBuf = _textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                outBuf[outPtr++] = c;
+                if (_inputPtr >= _inputEnd && !loadMore()) {
+                    eof = true;
+                    break exp_loop;
+                }
+                c = _inputBuffer[_inputPtr++];
+            }
+            // must be followed by sequence of ints, one minimum
+            if (expLen == 0) {
+                reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit");
+            }
+        }
+
+        // Ok; unless we hit end-of-input, need to push last char read back
+        if (!eof) {
+            --_inputPtr;
+        }
+        _textBuffer.setCurrentLength(outPtr);
+        // And there we have it!
+        return reset(negative, intLen, fractLen, expLen);
+    }
+
+    /**
+     * Method called when we have seen one zero, and want to ensure
+     * it is not followed by another
+     */
+    private final char _verifyNoLeadingZeroes()
+        throws IOException, JsonParseException
+    {
+        // Ok to have plain "0"
+        if (_inputPtr >= _inputEnd && !loadMore()) {
+            return '0';
+        }
+        char ch = _inputBuffer[_inputPtr];
+        // if not followed by a number (probably '.'); return zero as is, to be included
+        if (ch < '0' || ch > '9') {
+            return '0';
+        }
+        if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) {
+            reportInvalidNumber("Leading zeroes not allowed");
+        }
+        // if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number)
+        ++_inputPtr; // Leading zero to be skipped
+        if (ch == INT_0) {
+            while (_inputPtr < _inputEnd || loadMore()) {
+                ch = _inputBuffer[_inputPtr];
+                if (ch < '0' || ch > '9') { // followed by non-number; retain one zero
+                    return '0';
+                }
+                ++_inputPtr; // skip previous zero
+                if (ch != '0') { // followed by other number; return 
+                    break;
+                }
+            }
+        }
+        return ch;
+    }
+
+    /**
+     * Method called if expected numeric value (due to leading sign) does not
+     * look like a number
+     */
+    protected JsonToken _handleInvalidNumberStart(int ch, boolean negative)
+        throws IOException, JsonParseException
+    {
+        if (ch == 'I') {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOFInValue();
+                }
+            }
+            ch = _inputBuffer[_inputPtr++];
+            if (ch == 'N') {
+                String match = negative ? "-INF" :"+INF";
+                _matchToken(match, 3);
+                if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
+                    return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
+                }
+                _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
+            } else if (ch == 'n') {
+                String match = negative ? "-Infinity" :"+Infinity";
+                _matchToken(match, 3);
+                if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
+                    return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
+                }
+                _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
+            }
+        }
+        reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, secondary parsing
+    /**********************************************************
+     */
+
+    protected final String _parseFieldName(int i)
+        throws IOException, JsonParseException
+    {
+        if (i != INT_QUOTE) {
+            return _handleUnusualFieldName(i);
+        }
+        /* First: let's try to see if we have a simple name: one that does
+         * not cross input buffer boundary, and does not contain escape
+         * sequences.
+         */
+        int ptr = _inputPtr;
+        int hash = 0;
+        final int inputLen = _inputEnd;
+
+        if (ptr < inputLen) {
+            final int[] codes = CharTypes.getInputCodeLatin1();
+            final int maxCode = codes.length;
+
+            do {
+                int ch = _inputBuffer[ptr];
+                if (ch < maxCode && codes[ch] != 0) {
+                    if (ch == '"') {
+                        int start = _inputPtr;
+                        _inputPtr = ptr+1; // to skip the quote
+                        return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash);
+                    }
+                    break;
+                }
+                hash = (hash * 31) + ch;
+                ++ptr;
+            } while (ptr < inputLen);
+        }
+
+        int start = _inputPtr;
+        _inputPtr = ptr;
+        return _parseFieldName2(start, hash, INT_QUOTE);
+    }
+
+    private String _parseFieldName2(int startPtr, int hash, int endChar)
+        throws IOException, JsonParseException
+    {
+        _textBuffer.resetWithShared(_inputBuffer, startPtr, (_inputPtr - startPtr));
+
+        /* Output pointers; calls will also ensure that the buffer is
+         * not shared and has room for at least one more char.
+         */
+        char[] outBuf = _textBuffer.getCurrentSegment();
+        int outPtr = _textBuffer.getCurrentSegmentSize();
+
+        while (true) {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOF(": was expecting closing '"+((char) endChar)+"' for name");
+                }
+            }
+            char c = _inputBuffer[_inputPtr++];
+            int i = (int) c;
+            if (i <= INT_BACKSLASH) {
+                if (i == INT_BACKSLASH) {
+                    /* Although chars outside of BMP are to be escaped as
+                     * an UTF-16 surrogate pair, does that affect decoding?
+                     * For now let's assume it does not.
+                     */
+                    c = _decodeEscaped();
+                } else if (i <= endChar) {
+                    if (i == endChar) {
+                        break;
+                    }
+                    if (i < INT_SPACE) {
+                        _throwUnquotedSpace(i, "name");
+                    }
+                }
+            }
+            hash = (hash * 31) + i;
+            // Ok, let's add char to output:
+            outBuf[outPtr++] = c;
+
+            // Need more room?
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
+        }
+        _textBuffer.setCurrentLength(outPtr);
+        {
+            TextBuffer tb = _textBuffer;
+            char[] buf = tb.getTextBuffer();
+            int start = tb.getTextOffset();
+            int len = tb.size();
+
+            return _symbols.findSymbol(buf, start, len, hash);
+        }
+    }
+
+    /**
+     * Method called when we see non-white space character other
+     * than double quote, when expecting a field name.
+     * In standard mode will just throw an expection; but
+     * in non-standard modes may be able to parse name.
+     *
+     * @since 1.2
+     */
+    protected final String _handleUnusualFieldName(int i)
+        throws IOException, JsonParseException
+    {
+        // [JACKSON-173]: allow single quotes
+        if (i == INT_APOSTROPHE && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
+            return _parseApostropheFieldName();
+        }
+        // [JACKSON-69]: allow unquoted names if feature enabled:
+        if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) {
+            _reportUnexpectedChar(i, "was expecting double-quote to start field name");
+        }
+        final int[] codes = CharTypes.getInputCodeLatin1JsNames();
+        final int maxCode = codes.length;
+
+        // Also: first char must be a valid name char, but NOT be number
+        boolean firstOk;
+
+        if (i < maxCode) { // identifier, and not a number
+            firstOk = (codes[i] == 0) && (i < INT_0 || i > INT_9);
+        } else {
+            firstOk = Character.isJavaIdentifierPart((char) i);
+        }
+        if (!firstOk) {
+            _reportUnexpectedChar(i, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
+        }
+        int ptr = _inputPtr;
+        int hash = 0;
+        final int inputLen = _inputEnd;
+
+        if (ptr < inputLen) {
+            do {
+                int ch = _inputBuffer[ptr];
+                if (ch < maxCode) {
+                    if (codes[ch] != 0) {
+                        int start = _inputPtr-1; // -1 to bring back first char
+                        _inputPtr = ptr;
+                        return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash);
+                    }
+                } else if (!Character.isJavaIdentifierPart((char) ch)) {
+                    int start = _inputPtr-1; // -1 to bring back first char
+                    _inputPtr = ptr;
+                    return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash);
+                }
+                hash = (hash * 31) + ch;
+                ++ptr;
+            } while (ptr < inputLen);
+        }
+        int start = _inputPtr-1;
+        _inputPtr = ptr;
+        return _parseUnusualFieldName2(start, hash, codes);
+    }
+
+    protected final String _parseApostropheFieldName()
+        throws IOException, JsonParseException
+    {
+        // Note: mostly copy of_parseFieldName
+        int ptr = _inputPtr;
+        int hash = 0;
+        final int inputLen = _inputEnd;
+
+        if (ptr < inputLen) {
+            final int[] codes = CharTypes.getInputCodeLatin1();
+            final int maxCode = codes.length;
+
+            do {
+                int ch = _inputBuffer[ptr];
+                if (ch == '\'') {
+                    int start = _inputPtr;
+                    _inputPtr = ptr+1; // to skip the quote
+                    return _symbols.findSymbol(_inputBuffer, start, ptr - start, hash);
+                }
+                if (ch < maxCode && codes[ch] != 0) {
+                    break;
+                }
+                hash = (hash * 31) + ch;
+                ++ptr;
+            } while (ptr < inputLen);
+        }
+
+        int start = _inputPtr;
+        _inputPtr = ptr;
+
+        return _parseFieldName2(start, hash, INT_APOSTROPHE);
+    }
+
+    /**
+     * Method for handling cases where first non-space character
+     * of an expected value token is not legal for standard JSON content.
+     *
+     * @since 1.3
+     */
+    protected final JsonToken _handleUnexpectedValue(int i)
+        throws IOException, JsonParseException
+    {
+        // Most likely an error, unless we are to allow single-quote-strings
+        switch (i) {
+        case '\'':
+            /* [JACKSON-173]: allow single quotes. Unlike with regular
+             * Strings, we'll eagerly parse contents; this so that there's
+             * no need to store information on quote char used.
+             *
+             * Also, no separation to fast/slow parsing; we'll just do
+             * one regular (~= slowish) parsing, to keep code simple
+             */
+            if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
+                return _handleApostropheValue();
+            }
+            break;
+        case 'N':
+            _matchToken("NaN", 1);
+            if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
+                return resetAsNaN("NaN", Double.NaN);
+            }
+            _reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
+            break;
+        case '+': // note: '-' is taken as number
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOFInValue();
+                }
+            }
+            return _handleInvalidNumberStart(_inputBuffer[_inputPtr++], false);
+        }
+        _reportUnexpectedChar(i, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')");
+        return null;
+    }
+    
+    /**
+     * @since 1.8
+     */
+    protected final JsonToken _handleApostropheValue()
+        throws IOException, JsonParseException
+    {
+        char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+        int outPtr = _textBuffer.getCurrentSegmentSize();
+
+        while (true) {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOF(": was expecting closing quote for a string value");
+                }
+            }
+            char c = _inputBuffer[_inputPtr++];
+            int i = (int) c;
+            if (i <= INT_BACKSLASH) {
+                if (i == INT_BACKSLASH) {
+                    /* Although chars outside of BMP are to be escaped as
+                     * an UTF-16 surrogate pair, does that affect decoding?
+                     * For now let's assume it does not.
+                     */
+                    c = _decodeEscaped();
+                } else if (i <= INT_APOSTROPHE) {
+                    if (i == INT_APOSTROPHE) {
+                        break;
+                    }
+                    if (i < INT_SPACE) {
+                        _throwUnquotedSpace(i, "string value");
+                    }
+                }
+            }
+            // Need more room?
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
+            // Ok, let's add char to output:
+            outBuf[outPtr++] = c;
+        }
+        _textBuffer.setCurrentLength(outPtr);
+        return JsonToken.VALUE_STRING;
+    }
+    
+    /**
+     * @since 1.2
+     */
+    private String _parseUnusualFieldName2(int startPtr, int hash, int[] codes)
+        throws IOException, JsonParseException
+    {
+        _textBuffer.resetWithShared(_inputBuffer, startPtr, (_inputPtr - startPtr));
+        char[] outBuf = _textBuffer.getCurrentSegment();
+        int outPtr = _textBuffer.getCurrentSegmentSize();
+        final int maxCode = codes.length;
+
+        while (true) {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) { // acceptable for now (will error out later)
+                    break;
+                }
+            }
+            char c = _inputBuffer[_inputPtr];
+            int i = (int) c;
+            if (i <= maxCode) {
+                if (codes[i] != 0) {
+                    break;
+                }
+            } else if (!Character.isJavaIdentifierPart(c)) {
+                break;
+            }
+            ++_inputPtr;
+            hash = (hash * 31) + i;
+            // Ok, let's add char to output:
+            outBuf[outPtr++] = c;
+
+            // Need more room?
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
+        }
+        _textBuffer.setCurrentLength(outPtr);
+        {
+            TextBuffer tb = _textBuffer;
+            char[] buf = tb.getTextBuffer();
+            int start = tb.getTextOffset();
+            int len = tb.size();
+
+            return _symbols.findSymbol(buf, start, len, hash);
+        }
+    }
+  
+    @Override
+    protected void _finishString()
+        throws IOException, JsonParseException
+    {
+        /* First: let's try to see if we have simple String value: one
+         * that does not cross input buffer boundary, and does not
+         * contain escape sequences.
+         */
+        int ptr = _inputPtr;
+        final int inputLen = _inputEnd;
+
+        if (ptr < inputLen) {
+            final int[] codes = CharTypes.getInputCodeLatin1();
+            final int maxCode = codes.length;
+
+            do {
+                int ch = _inputBuffer[ptr];
+                if (ch < maxCode && codes[ch] != 0) {
+                    if (ch == '"') {
+                        _textBuffer.resetWithShared(_inputBuffer, _inputPtr, (ptr-_inputPtr));
+                        _inputPtr = ptr+1;
+                        // Yes, we got it all
+                        return;
+                    }
+                    break;
+                }
+                ++ptr;
+            } while (ptr < inputLen);
+        }
+
+        /* Either ran out of input, or bumped into an escape
+         * sequence...
+         */
+        _textBuffer.resetWithCopy(_inputBuffer, _inputPtr, (ptr-_inputPtr));
+        _inputPtr = ptr;
+        _finishString2();
+    }
+
+    protected void _finishString2()
+        throws IOException, JsonParseException
+    {
+        char[] outBuf = _textBuffer.getCurrentSegment();
+        int outPtr = _textBuffer.getCurrentSegmentSize();
+
+        while (true) {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOF(": was expecting closing quote for a string value");
+                }
+            }
+            char c = _inputBuffer[_inputPtr++];
+            int i = (int) c;
+            if (i <= INT_BACKSLASH) {
+                if (i == INT_BACKSLASH) {
+                    /* Although chars outside of BMP are to be escaped as
+                     * an UTF-16 surrogate pair, does that affect decoding?
+                     * For now let's assume it does not.
+                     */
+                    c = _decodeEscaped();
+                } else if (i <= INT_QUOTE) {
+                    if (i == INT_QUOTE) {
+                        break;
+                    }
+                    if (i < INT_SPACE) {
+                        _throwUnquotedSpace(i, "string value");
+                    }
+                }
+            }
+            // Need more room?
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
+            // Ok, let's add char to output:
+            outBuf[outPtr++] = c;
+        }
+        _textBuffer.setCurrentLength(outPtr);
+    }
+
+    /**
+     * Method called to skim through rest of unparsed String value,
+     * if it is not needed. This can be done bit faster if contents
+     * need not be stored for future access.
+     */
+    protected void _skipString()
+        throws IOException, JsonParseException
+    {
+        _tokenIncomplete = false;
+
+        int inputPtr = _inputPtr;
+        int inputLen = _inputEnd;
+        char[] inputBuffer = _inputBuffer;
+
+        while (true) {
+            if (inputPtr >= inputLen) {
+                _inputPtr = inputPtr;
+                if (!loadMore()) {
+                    _reportInvalidEOF(": was expecting closing quote for a string value");
+                }
+                inputPtr = _inputPtr;
+                inputLen = _inputEnd;
+            }
+            char c = inputBuffer[inputPtr++];
+            int i = (int) c;
+            if (i <= INT_BACKSLASH) {
+                if (i == INT_BACKSLASH) {
+                    /* Although chars outside of BMP are to be escaped as
+                     * an UTF-16 surrogate pair, does that affect decoding?
+                     * For now let's assume it does not.
+                     */
+                    _inputPtr = inputPtr;
+                    c = _decodeEscaped();
+                    inputPtr = _inputPtr;
+                    inputLen = _inputEnd;
+                } else if (i <= INT_QUOTE) {
+                    if (i == INT_QUOTE) {
+                        _inputPtr = inputPtr;
+                        break;
+                    }
+                    if (i < INT_SPACE) {
+                        _inputPtr = inputPtr;
+                        _throwUnquotedSpace(i, "string value");
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, other parsing
+    /**********************************************************
+     */
+    
+    /**
+     * We actually need to check the character value here
+     * (to see if we have \n following \r).
+     */
+    protected final void _skipCR() throws IOException
+    {
+        if (_inputPtr < _inputEnd || loadMore()) {
+            if (_inputBuffer[_inputPtr] == '\n') {
+                ++_inputPtr;
+            }
+        }
+        ++_currInputRow;
+        _currInputRowStart = _inputPtr;
+    }
+
+    protected final void _skipLF() throws IOException
+    {
+        ++_currInputRow;
+        _currInputRowStart = _inputPtr;
+    }
+
+    private final int _skipWS()
+        throws IOException, JsonParseException
+    {
+        while (_inputPtr < _inputEnd || loadMore()) {
+            int i = (int) _inputBuffer[_inputPtr++];
+            if (i > INT_SPACE) {
+                if (i != INT_SLASH) {
+                    return i;
+                }
+                _skipComment();
+            } else if (i != INT_SPACE) {
+                if (i == INT_LF) {
+                    _skipLF();
+                } else if (i == INT_CR) {
+                    _skipCR();
+                } else if (i != INT_TAB) {
+                    _throwInvalidSpace(i);
+                }
+            }
+        }
+        throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries");
+    }
+
+    private final int _skipWSOrEnd()
+        throws IOException, JsonParseException
+    {
+        while ((_inputPtr < _inputEnd) || loadMore()) {
+            int i = (int) _inputBuffer[_inputPtr++];
+            if (i > INT_SPACE) {
+                 if (i == INT_SLASH) {
+                     _skipComment();
+                     continue;
+                }
+                 return i;
+            }
+            if (i != INT_SPACE) {
+                if (i == INT_LF) {
+                    _skipLF();
+                } else if (i == INT_CR) {
+                    _skipCR();
+                } else if (i != INT_TAB) {
+                    _throwInvalidSpace(i);
+                }
+            }
+        }
+        // We ran out of input...
+        _handleEOF();
+        return -1;
+    }
+
+    private final void _skipComment()
+        throws IOException, JsonParseException
+    {
+        if (!isEnabled(Feature.ALLOW_COMMENTS)) {
+            _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
+        }
+        // First: check which comment (if either) it is:
+        if (_inputPtr >= _inputEnd && !loadMore()) {
+            _reportInvalidEOF(" in a comment");
+        }
+        char c = _inputBuffer[_inputPtr++];
+        if (c == '/') {
+            _skipCppComment();
+        } else if (c == '*') {
+            _skipCComment();
+        } else {
+            _reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment");
+        }
+    }
+
+    private final void _skipCComment()
+        throws IOException, JsonParseException
+    {
+        // Ok: need the matching '*/'
+        main_loop:
+        while ((_inputPtr < _inputEnd) || loadMore()) {
+            int i = (int) _inputBuffer[_inputPtr++];
+            if (i <= INT_ASTERISK) {
+                if (i == INT_ASTERISK) { // end?
+                    if ((_inputPtr >= _inputEnd) && !loadMore()) {
+                        break main_loop;
+                    }
+                    if (_inputBuffer[_inputPtr] == INT_SLASH) {
+                        ++_inputPtr;
+                        return;
+                    }
+                    continue;
+                }
+                if (i < INT_SPACE) {
+                    if (i == INT_LF) {
+                        _skipLF();
+                    } else if (i == INT_CR) {
+                        _skipCR();
+                    } else if (i != INT_TAB) {
+                        _throwInvalidSpace(i);
+                    }
+                }
+            }
+        }
+        _reportInvalidEOF(" in a comment");
+    }
+
+    private final void _skipCppComment()
+        throws IOException, JsonParseException
+    {
+        // Ok: need to find EOF or linefeed
+        while ((_inputPtr < _inputEnd) || loadMore()) {
+            int i = (int) _inputBuffer[_inputPtr++];
+            if (i < INT_SPACE) {
+                if (i == INT_LF) {
+                    _skipLF();
+                    break;
+                } else if (i == INT_CR) {
+                    _skipCR();
+                    break;
+                } else if (i != INT_TAB) {
+                    _throwInvalidSpace(i);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected final char _decodeEscaped()
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            if (!loadMore()) {
+                _reportInvalidEOF(" in character escape sequence");
+            }
+        }
+        char c = _inputBuffer[_inputPtr++];
+
+        switch ((int) c) {
+            // First, ones that are mapped
+        case INT_b:
+            return '\b';
+        case INT_t:
+            return '\t';
+        case INT_n:
+            return '\n';
+        case INT_f:
+            return '\f';
+        case INT_r:
+            return '\r';
+
+            // And these are to be returned as they are
+        case INT_QUOTE:
+        case INT_SLASH:
+        case INT_BACKSLASH:
+            return c;
+
+        case INT_u: // and finally hex-escaped
+            break;
+
+        default:
+            return _handleUnrecognizedCharacterEscape(c);
+        }
+
+        // Ok, a hex escape. Need 4 characters
+        int value = 0;
+        for (int i = 0; i < 4; ++i) {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOF(" in character escape sequence");
+                }
+            }
+            int ch = (int) _inputBuffer[_inputPtr++];
+            int digit = CharTypes.charToHex(ch);
+            if (digit < 0) {
+                _reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence");
+            }
+            value = (value << 4) | digit;
+        }
+        return (char) value;
+    }
+    
+    /**
+     * Helper method for checking whether input matches expected token
+     * 
+     * @since 1.8
+     */
+    protected final void _matchToken(String matchStr, int i)
+        throws IOException, JsonParseException
+    {
+        final int len = matchStr.length();
+
+        do {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOFInValue();
+                }
+            }
+            if (_inputBuffer[_inputPtr] != matchStr.charAt(i)) {
+                _reportInvalidToken(matchStr.substring(0, i), "'null', 'true', 'false' or NaN");
+            }
+            ++_inputPtr;
+        } while (++i < len);
+
+        // but let's also ensure we either get EOF, or non-alphanum char...
+        if (_inputPtr >= _inputEnd) {
+            if (!loadMore()) {
+                return;
+            }
+        }
+        char c = _inputBuffer[_inputPtr];
+        if (c < '0' || c == ']' || c == '}') { // expected/allowed chars
+            return;
+        }
+        // if Java letter, it's a problem tho
+        if (Character.isJavaIdentifierPart(c)) {
+            ++_inputPtr;
+            _reportInvalidToken(matchStr.substring(0, i), "'null', 'true', 'false' or NaN");
+        }
+        return;
+    }
+
+    /*
+    /**********************************************************
+    /* Binary access
+    /**********************************************************
+     */
+
+    /**
+     * Efficient handling for incremental parsing of base64-encoded
+     * textual content.
+     */
+    protected byte[] _decodeBase64(Base64Variant b64variant)
+        throws IOException, JsonParseException
+    {
+        ByteArrayBuilder builder = _getByteArrayBuilder();
+
+        //main_loop:
+        while (true) {
+            // first, we'll skip preceding white space, if any
+            char ch;
+            do {
+                if (_inputPtr >= _inputEnd) {
+                    loadMoreGuaranteed();
+                }
+                ch = _inputBuffer[_inputPtr++];
+            } while (ch <= INT_SPACE);
+            int bits = b64variant.decodeBase64Char(ch);
+            if (bits < 0) {
+                if (ch == '"') { // reached the end, fair and square?
+                    return builder.toByteArray();
+                }
+                bits = _decodeBase64Escape(b64variant, ch, 0);
+                if (bits < 0) { // white space to skip
+                    continue;
+                }
+            }
+            int decodedData = bits;
+            
+            // then second base64 char; can't get padding yet, nor ws
+            
+            if (_inputPtr >= _inputEnd) {
+                loadMoreGuaranteed();
+            }
+            ch = _inputBuffer[_inputPtr++];
+            bits = b64variant.decodeBase64Char(ch);
+            if (bits < 0) {
+                bits = _decodeBase64Escape(b64variant, ch, 1);
+            }
+            decodedData = (decodedData << 6) | bits;
+            
+            // third base64 char; can be padding, but not ws
+            if (_inputPtr >= _inputEnd) {
+                loadMoreGuaranteed();
+            }
+            ch = _inputBuffer[_inputPtr++];
+            bits = b64variant.decodeBase64Char(ch);
+
+            // First branch: can get padding (-> 1 byte)
+            if (bits < 0) {
+                if (bits != Base64Variant.BASE64_VALUE_PADDING) {
+                    // as per [JACKSON-631], could also just be 'missing'  padding
+                    if (ch == '"' && !b64variant.usesPadding()) {
+                        decodedData >>= 4;
+                        builder.append(decodedData);
+                        return builder.toByteArray();
+                    }
+                    bits = _decodeBase64Escape(b64variant, ch, 2);
+                }
+                if (bits == Base64Variant.BASE64_VALUE_PADDING) {
+                    // Ok, must get more padding chars, then
+                    if (_inputPtr >= _inputEnd) {
+                        loadMoreGuaranteed();
+                    }
+                    ch = _inputBuffer[_inputPtr++];
+                    if (!b64variant.usesPaddingChar(ch)) {
+                        throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
+                    }
+                    // Got 12 bits, only need 8, need to shift
+                    decodedData >>= 4;
+                    builder.append(decodedData);
+                    continue;
+                }
+                // otherwise we got escaped other char, to be processed below
+            }
+            // Nope, 2 or 3 bytes
+            decodedData = (decodedData << 6) | bits;
+            // fourth and last base64 char; can be padding, but not ws
+            if (_inputPtr >= _inputEnd) {
+                loadMoreGuaranteed();
+            }
+            ch = _inputBuffer[_inputPtr++];
+            bits = b64variant.decodeBase64Char(ch);
+            if (bits < 0) {
+                if (bits != Base64Variant.BASE64_VALUE_PADDING) {
+                    // as per [JACKSON-631], could also just be 'missing'  padding
+                    if (ch == '"' && !b64variant.usesPadding()) {
+                        decodedData >>= 2;
+                        builder.appendTwoBytes(decodedData);
+                        return builder.toByteArray();
+                    }
+                    bits = _decodeBase64Escape(b64variant, ch, 3);
+                }
+                if (bits == Base64Variant.BASE64_VALUE_PADDING) {
+                    // With padding we only get 2 bytes; but we have
+                    // to shift it a bit so it is identical to triplet
+                    // case with partial output.
+                    // 3 chars gives 3x6 == 18 bits, of which 2 are
+                    // dummies, need to discard:
+                    decodedData >>= 2;
+                    builder.appendTwoBytes(decodedData);
+                    continue;
+                }
+                // otherwise we got escaped other char, to be processed below
+            }
+            // otherwise, our triplet is now complete
+            decodedData = (decodedData << 6) | bits;
+            builder.appendThreeBytes(decodedData);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Error reporting
+    /**********************************************************
+     */
+
+    protected void _reportInvalidToken(String matchedPart, String msg)
+        throws IOException, JsonParseException
+    {
+        StringBuilder sb = new StringBuilder(matchedPart);
+        /* Let's just try to find what appears to be the token, using
+         * regular Java identifier character rules. It's just a heuristic,
+         * nothing fancy here.
+         */
+        while (true) {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    break;
+                }
+            }
+            char c = _inputBuffer[_inputPtr];
+            if (!Character.isJavaIdentifierPart(c)) {
+                break;
+            }
+            ++_inputPtr;
+            sb.append(c);
+        }
+        _reportError("Unrecognized token '"+sb.toString()+"': was expecting ");
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
new file mode 100644
index 0000000..0d70db7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
@@ -0,0 +1,1758 @@
+package com.fasterxml.jackson.core.json;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.base.GeneratorBase;
+import com.fasterxml.jackson.core.io.CharTypes;
+import com.fasterxml.jackson.core.io.CharacterEscapes;
+import com.fasterxml.jackson.core.io.IOContext;
+import com.fasterxml.jackson.core.io.NumberOutput;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+public class UTF8JsonGenerator
+    extends GeneratorBase
+{
+    private final static byte BYTE_u = (byte) 'u';
+
+    private final static byte BYTE_0 = (byte) '0';
+    
+    private final static byte BYTE_LBRACKET = (byte) '[';
+    private final static byte BYTE_RBRACKET = (byte) ']';
+    private final static byte BYTE_LCURLY = (byte) '{';
+    private final static byte BYTE_RCURLY = (byte) '}';
+ 
+    private final static byte BYTE_BACKSLASH = (byte) '\\';
+    private final static byte BYTE_SPACE = (byte) ' ';
+    private final static byte BYTE_COMMA = (byte) ',';
+    private final static byte BYTE_COLON = (byte) ':';
+    private final static byte BYTE_QUOTE = (byte) '"';
+
+    protected final static int SURR1_FIRST = 0xD800;
+    protected final static int SURR1_LAST = 0xDBFF;
+    protected final static int SURR2_FIRST = 0xDC00;
+    protected final static int SURR2_LAST = 0xDFFF;
+
+    // intermediate copies only made up to certain length...
+    private final static int MAX_BYTES_TO_BUFFER = 512;
+    
+    final static byte[] HEX_CHARS = CharTypes.copyHexBytes();
+
+    private final static byte[] NULL_BYTES = { 'n', 'u', 'l', 'l' };
+    private final static byte[] TRUE_BYTES = { 't', 'r', 'u', 'e' };
+    private final static byte[] FALSE_BYTES = { 'f', 'a', 'l', 's', 'e' };
+
+    /**
+     * This is the default set of escape codes, over 7-bit ASCII range
+     * (first 128 character codes), used for single-byte UTF-8 characters.
+     */
+    protected final static int[] sOutputEscapes = CharTypes.get7BitOutputEscapes();
+    
+    /*
+    /**********************************************************
+    /* Configuration, basic I/O
+    /**********************************************************
+     */
+
+    final protected IOContext _ioContext;
+
+    /**
+     * Underlying output stream used for writing JSON content.
+     */
+    final protected OutputStream _outputStream;
+
+    /*
+    /**********************************************************
+    /* Configuration, output escaping
+    /**********************************************************
+     */
+
+    /**
+     * Currently active set of output escape code definitions (whether
+     * and how to escape or not) for 7-bit ASCII range (first 128
+     * character codes). Defined separately to make potentially
+     * customizable
+     */
+    protected int[] _outputEscapes = sOutputEscapes;
+
+    /**
+     * Value between 128 (0x80) and 65535 (0xFFFF) that indicates highest
+     * Unicode code point that will not need escaping; or 0 to indicate
+     * that all characters can be represented without escaping.
+     * Typically used to force escaping of some portion of character set;
+     * for example to always escape non-ASCII characters (if value was 127).
+     *<p>
+     * NOTE: not all sub-classes make use of this setting.
+     */
+    protected int _maximumNonEscapedChar;
+
+    /**
+     * Definition of custom character escapes to use for generators created
+     * by this factory, if any. If null, standard data format specific
+     * escapes are used.
+     * 
+     * @since 1.8
+     */
+    protected CharacterEscapes _characterEscapes;
+    
+    /*
+    /**********************************************************
+    /* Output buffering
+    /**********************************************************
+     */
+
+    /**
+     * Intermediate buffer in which contents are buffered before
+     * being written using {@link #_outputStream}.
+     */
+    protected byte[] _outputBuffer;
+
+    /**
+     * Pointer to the position right beyond the last character to output
+     * (end marker; may be past the buffer)
+     */
+    protected int _outputTail = 0;
+
+    /**
+     * End marker of the output buffer; one past the last valid position
+     * within the buffer.
+     */
+    protected final int _outputEnd;
+
+    /**
+     * Maximum number of <code>char</code>s that we know will always fit
+     * in the output buffer after escaping
+     */
+    protected final int _outputMaxContiguous;
+    
+    /**
+     * Intermediate buffer in which characters of a String are copied
+     * before being encoded.
+     */
+    protected char[] _charBuffer;
+    
+    /**
+     * Length of <code>_charBuffer</code>
+     */
+    protected final int _charBufferLength;
+    
+    /**
+     * 6 character temporary buffer allocated if needed, for constructing
+     * escape sequences
+     */
+    protected byte[] _entityBuffer;
+
+    /**
+     * Flag that indicates whether the output buffer is recycable (and
+     * needs to be returned to recycler once we are done) or not.
+     */
+    protected boolean _bufferRecyclable;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
+            OutputStream out)
+    {
+        
+        super(features, codec);
+        _ioContext = ctxt;
+        _outputStream = out;
+        _bufferRecyclable = true;
+        _outputBuffer = ctxt.allocWriteEncodingBuffer();
+        _outputEnd = _outputBuffer.length;
+        /* To be exact, each char can take up to 6 bytes when escaped (Unicode
+         * escape with backslash, 'u' and 4 hex digits); but to avoid fluctuation,
+         * we will actually round down to only do up to 1/8 number of chars
+         */
+        _outputMaxContiguous = _outputEnd >> 3;
+        _charBuffer = ctxt.allocConcatBuffer();
+        _charBufferLength = _charBuffer.length;
+
+        // By default we use this feature to determine additional quoting
+        if (isEnabled(Feature.ESCAPE_NON_ASCII)) {
+            setHighestNonEscapedChar(127);
+        }
+    }
+
+    public UTF8JsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
+            OutputStream out, byte[] outputBuffer, int outputOffset, boolean bufferRecyclable)
+    {
+        
+        super(features, codec);
+        _ioContext = ctxt;
+        _outputStream = out;
+        _bufferRecyclable = bufferRecyclable;
+        _outputTail = outputOffset;
+        _outputBuffer = outputBuffer;
+        _outputEnd = _outputBuffer.length;
+        // up to 6 bytes per char (see above), rounded up to 1/8
+        _outputMaxContiguous = _outputEnd >> 3;
+        _charBuffer = ctxt.allocConcatBuffer();
+        _charBufferLength = _charBuffer.length;
+
+        if (isEnabled(Feature.ESCAPE_NON_ASCII)) {
+            setHighestNonEscapedChar(127);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Overridden configuration methods
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonGenerator setHighestNonEscapedChar(int charCode) {
+        _maximumNonEscapedChar = (charCode < 0) ? 0 : charCode;
+        return this;
+    }
+
+    @Override
+    public int getHighestEscapedChar() {
+        return _maximumNonEscapedChar;
+    }
+
+    @Override
+    public JsonGenerator setCharacterEscapes(CharacterEscapes esc)
+    {
+        _characterEscapes = esc;
+        if (esc == null) { // revert to standard escapes
+            _outputEscapes = sOutputEscapes;
+        } else {
+            _outputEscapes = esc.getEscapeCodesForAscii();
+        }
+        return this;
+    }
+
+    /**
+     * Method for accessing custom escapes factory uses for {@link JsonGenerator}s
+     * it creates.
+     * 
+     * @since 1.8
+     */
+    @Override
+    public CharacterEscapes getCharacterEscapes() {
+        return _characterEscapes;
+    }
+
+    @Override
+    public Object getOutputTarget() {
+        return _outputStream;
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridden methods
+    /**********************************************************
+     */
+
+    /* Most overrides in this section are just to make methods final,
+     * to allow better inlining...
+     */
+    @Override
+    public final void writeStringField(String fieldName, String value)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeString(value);
+    }
+
+    @Override
+    public final void writeFieldName(String name)  throws IOException, JsonGenerationException
+    {
+        int status = _writeContext.writeFieldName(name);
+        if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
+            _reportError("Can not write a field name, expecting a value");
+        }
+        if (_cfgPrettyPrinter != null) {
+            _writePPFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
+            return;
+        }
+        if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) { // need comma
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_COMMA;
+        }
+        _writeFieldName(name);
+    }
+    
+    @Override
+    public final void writeFieldName(SerializedString name)
+        throws IOException, JsonGenerationException
+    {
+        // Object is a value, need to verify it's allowed
+        int status = _writeContext.writeFieldName(name.getValue());
+        if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
+            _reportError("Can not write a field name, expecting a value");
+        }
+        if (_cfgPrettyPrinter != null) {
+            _writePPFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
+            return;
+        }
+        if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_COMMA;
+        }
+        _writeFieldName(name);
+    }
+
+    @Override
+    public final void writeFieldName(SerializableString name)
+        throws IOException, JsonGenerationException
+    {
+        // Object is a value, need to verify it's allowed
+        int status = _writeContext.writeFieldName(name.getValue());
+        if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
+            _reportError("Can not write a field name, expecting a value");
+        }
+        if (_cfgPrettyPrinter != null) {
+            _writePPFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
+            return;
+        }
+        if (status == JsonWriteContext.STATUS_OK_AFTER_COMMA) {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_COMMA;
+        }
+        _writeFieldName(name);
+    }
+
+    /*
+    /**********************************************************
+    /* Output method implementations, structural
+    /**********************************************************
+     */
+
+    @Override
+    public final void writeStartArray() throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("start an array");
+        _writeContext = _writeContext.createChildArrayContext();
+        if (_cfgPrettyPrinter != null) {
+            _cfgPrettyPrinter.writeStartArray(this);
+        } else {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_LBRACKET;
+        }
+    }
+
+    @Override
+    public final void writeEndArray() throws IOException, JsonGenerationException
+    {
+        if (!_writeContext.inArray()) {
+            _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc());
+        }
+        if (_cfgPrettyPrinter != null) {
+            _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
+        } else {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_RBRACKET;
+        }
+        _writeContext = _writeContext.getParent();
+    }
+
+    @Override
+    public final void writeStartObject() throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("start an object");
+        _writeContext = _writeContext.createChildObjectContext();
+        if (_cfgPrettyPrinter != null) {
+            _cfgPrettyPrinter.writeStartObject(this);
+        } else {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_LCURLY;
+        }
+    }
+
+    @Override
+    public final void writeEndObject() throws IOException, JsonGenerationException
+    {
+        if (!_writeContext.inObject()) {
+            _reportError("Current context not an object but "+_writeContext.getTypeDesc());
+        }
+        _writeContext = _writeContext.getParent();
+        if (_cfgPrettyPrinter != null) {
+            _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
+        } else {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_RCURLY;
+        }
+    }
+
+    protected final void _writeFieldName(String name)
+        throws IOException, JsonGenerationException
+    {
+        /* To support [JACKSON-46], we'll do this:
+         * (Question: should quoting of spaces (etc) still be enabled?)
+         */
+        if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) {
+            _writeStringSegments(name);
+            return;
+        }
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        // The beef:
+        final int len = name.length();
+        if (len <= _charBufferLength) { // yes, fits right in
+            name.getChars(0, len, _charBuffer, 0);
+            // But as one segment, or multiple?
+            if (len <= _outputMaxContiguous) {
+                if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
+                    _flushBuffer();
+                }
+                _writeStringSegment(_charBuffer, 0, len);
+            } else {
+                _writeStringSegments(_charBuffer, 0, len);
+            }
+        } else {
+            _writeStringSegments(name);
+        }
+
+        // and closing quotes; need room for one more char:
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+
+    protected final void _writeFieldName(SerializableString name)
+        throws IOException, JsonGenerationException
+    {
+        byte[] raw = name.asQuotedUTF8();
+        if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) {
+            _writeBytes(raw);
+            return;
+        }
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+
+        // Can do it all in buffer?
+        final int len = raw.length;
+        if ((_outputTail + len + 1) < _outputEnd) { // yup
+            System.arraycopy(raw, 0, _outputBuffer, _outputTail, len);
+            _outputTail += len;
+            _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        } else {
+            _writeBytes(raw);
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        }
+    }    
+    
+    /**
+     * Specialized version of <code>_writeFieldName</code>, off-lined
+     * to keep the "fast path" as simple (and hopefully fast) as possible.
+     */
+    protected final void _writePPFieldName(String name, boolean commaBefore)
+        throws IOException, JsonGenerationException
+    {
+        if (commaBefore) {
+            _cfgPrettyPrinter.writeObjectEntrySeparator(this);
+        } else {
+            _cfgPrettyPrinter.beforeObjectEntries(this);
+        }
+
+        if (isEnabled(Feature.QUOTE_FIELD_NAMES)) { // standard
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_QUOTE;
+            final int len = name.length();
+            if (len <= _charBufferLength) { // yes, fits right in
+                name.getChars(0, len, _charBuffer, 0);
+                // But as one segment, or multiple?
+                if (len <= _outputMaxContiguous) {
+                    if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
+                        _flushBuffer();
+                    }
+                    _writeStringSegment(_charBuffer, 0, len);
+                } else {
+                    _writeStringSegments(_charBuffer, 0, len);
+                }
+            } else {
+                _writeStringSegments(name);
+            }
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        } else { // non-standard, omit quotes
+            _writeStringSegments(name);
+        }
+    }
+
+    protected final void _writePPFieldName(SerializableString name, boolean commaBefore)
+        throws IOException, JsonGenerationException
+    {
+        if (commaBefore) {
+            _cfgPrettyPrinter.writeObjectEntrySeparator(this);
+        } else {
+            _cfgPrettyPrinter.beforeObjectEntries(this);
+        }
+
+        boolean addQuotes = isEnabled(Feature.QUOTE_FIELD_NAMES); // standard
+        if (addQuotes) {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        }
+        _writeBytes(name.asQuotedUTF8());
+        if (addQuotes) {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Output method implementations, textual
+    /**********************************************************
+     */
+
+    @Override
+    public void writeString(String text)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write text value");
+        if (text == null) {
+            _writeNull();
+            return;
+        }
+        // First: can we make a local copy of chars that make up text?
+        final int len = text.length();
+        if (len > _charBufferLength) { // nope: off-line handling
+            _writeLongString(text);
+            return;
+        }
+        // yes: good.
+        text.getChars(0, len, _charBuffer, 0);
+        // Output: if we can't guarantee it fits in output buffer, off-line as well:
+        if (len > _outputMaxContiguous) {
+            _writeLongString(_charBuffer, 0, len);
+            return;
+        }
+        if ((_outputTail + len) >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        _writeStringSegment(_charBuffer, 0, len); // we checked space already above
+        /* [JACKSON-462] But that method may have had to expand multi-byte Unicode
+         *   chars, so we must check again
+         */
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+
+    private final void _writeLongString(String text)
+        throws IOException, JsonGenerationException
+    {
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        _writeStringSegments(text);
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+
+    private final void _writeLongString(char[] text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        _writeStringSegments(_charBuffer, 0, len);
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+
+    @Override
+    public void writeString(char[] text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write text value");
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        // One or multiple segments?
+        if (len <= _outputMaxContiguous) {
+            if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
+                _flushBuffer();
+            }
+            _writeStringSegment(text, offset, len);
+        } else {
+            _writeStringSegments(text, offset, len);
+        }
+        // And finally, closing quotes
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+
+    @Override
+    public final void writeString(SerializableString text)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write text value");
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        _writeBytes(text.asQuotedUTF8());
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+
+    @Override
+    public void writeRawUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write text value");
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        _writeBytes(text, offset, length);
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+
+    @Override
+    public void writeUTF8String(byte[] text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write text value");
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        // One or multiple segments?
+        if (len <= _outputMaxContiguous) {
+            _writeUTF8Segment(text, offset, len);
+        } else {
+            _writeUTF8Segments(text, offset, len);
+        }
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+    
+    /*
+    /**********************************************************
+    /* Output method implementations, unprocessed ("raw")
+    /**********************************************************
+     */
+
+    @Override
+    public void writeRaw(String text)
+        throws IOException, JsonGenerationException
+    {
+        int start = 0;
+        int len = text.length();
+        while (len > 0) {
+            char[] buf = _charBuffer;
+            final int blen = buf.length;
+            final int len2 = (len < blen) ? len : blen;
+            text.getChars(start, start+len2, buf, 0);
+            writeRaw(buf, 0, len2);
+            start += len2;
+            len -= len2;
+        }
+    }
+
+    @Override
+    public void writeRaw(String text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        while (len > 0) {
+            char[] buf = _charBuffer;
+            final int blen = buf.length;
+            final int len2 = (len < blen) ? len : blen;
+            text.getChars(offset, offset+len2, buf, 0);
+            writeRaw(buf, 0, len2);
+            offset += len2;
+            len -= len2;
+        }
+    }
+
+    // @TODO: rewrite for speed...
+    @Override
+    public final void writeRaw(char[] cbuf, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        // First: if we have 3 x charCount spaces, we know it'll fit just fine
+        {
+            int len3 = len+len+len;
+            if ((_outputTail + len3) > _outputEnd) {
+                // maybe we could flush?
+                if (_outputEnd < len3) { // wouldn't be enough...
+                    _writeSegmentedRaw(cbuf, offset, len);
+                    return;
+                }
+                // yes, flushing brings enough space
+                _flushBuffer();
+            }
+        }
+        len += offset; // now marks the end
+
+        // Note: here we know there is enough room, hence no output boundary checks
+        main_loop:
+        while (offset < len) {
+            inner_loop:
+            while (true) {
+                int ch = (int) cbuf[offset];
+                if (ch > 0x7F) {
+                    break inner_loop;
+                }
+                _outputBuffer[_outputTail++] = (byte) ch;
+                if (++offset >= len) {
+                    break main_loop;
+                }
+            }
+            char ch = cbuf[offset++];
+            if (ch < 0x800) { // 2-byte?
+                _outputBuffer[_outputTail++] = (byte) (0xc0 | (ch >> 6));
+                _outputBuffer[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
+            } else {
+                _outputRawMultiByteChar(ch, cbuf, offset, len);
+            }
+        }
+    }
+
+    @Override
+    public void writeRaw(char ch)
+        throws IOException, JsonGenerationException
+    {
+        if ((_outputTail + 3) >= _outputEnd) {
+            _flushBuffer();
+        }
+        final byte[] bbuf = _outputBuffer;
+        if (ch <= 0x7F) {
+            bbuf[_outputTail++] = (byte) ch;
+        } else  if (ch < 0x800) { // 2-byte?
+            bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6));
+            bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
+        } else {
+            _outputRawMultiByteChar(ch, null, 0, 0);
+        }
+    }
+
+    /**
+     * Helper method called when it is possible that output of raw section
+     * to output may cross buffer boundary
+     */
+    private final void _writeSegmentedRaw(char[] cbuf, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        final int end = _outputEnd;
+        final byte[] bbuf = _outputBuffer;
+        
+        main_loop:
+        while (offset < len) {
+            inner_loop:
+            while (true) {
+                int ch = (int) cbuf[offset];
+                if (ch >= 0x80) {
+                    break inner_loop;
+                }
+                // !!! TODO: fast(er) writes (roll input, output checks in one)
+                if (_outputTail >= end) {
+                    _flushBuffer();
+                }
+                bbuf[_outputTail++] = (byte) ch;
+                if (++offset >= len) {
+                    break main_loop;
+                }
+            }
+            if ((_outputTail + 3) >= _outputEnd) {
+                _flushBuffer();
+            }
+            char ch = cbuf[offset++];
+            if (ch < 0x800) { // 2-byte?
+                bbuf[_outputTail++] = (byte) (0xc0 | (ch >> 6));
+                bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
+            } else {
+                _outputRawMultiByteChar(ch, cbuf, offset, len);
+            }
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Output method implementations, base64-encoded binary
+    /**********************************************************
+     */
+
+    @Override
+    public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write binary value");
+        // Starting quotes
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        _writeBinary(b64variant, data, offset, offset+len);
+        // and closing quotes
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+    
+    /*
+    /**********************************************************
+    /* Output method implementations, primitive
+    /**********************************************************
+     */
+
+    @Override
+    public void writeNumber(int i)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        // up to 10 digits and possible minus sign
+        if ((_outputTail + 11) >= _outputEnd) {
+            _flushBuffer();
+        }
+        if (_cfgNumbersAsStrings) {
+            _writeQuotedInt(i);
+            return;
+        }
+        _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
+    }
+
+    private final void _writeQuotedInt(int i) throws IOException {
+        if ((_outputTail + 13) >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }    
+
+    @Override
+    public void writeNumber(long l)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        if (_cfgNumbersAsStrings) {
+            _writeQuotedLong(l);
+            return;
+        }
+        if ((_outputTail + 21) >= _outputEnd) {
+            // up to 20 digits, minus sign
+            _flushBuffer();
+        }
+        _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
+    }
+
+    private final void _writeQuotedLong(long l) throws IOException {
+        if ((_outputTail + 23) >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+
+    @Override
+    public void writeNumber(BigInteger value)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        if (value == null) {
+            _writeNull();
+        } else if (_cfgNumbersAsStrings) {
+            _writeQuotedRaw(value);
+        } else {
+            writeRaw(value.toString());
+        }
+    }
+
+    
+    @Override
+    public void writeNumber(double d)
+        throws IOException, JsonGenerationException
+    {
+        if (_cfgNumbersAsStrings ||
+            // [JACKSON-139]
+            (((Double.isNaN(d) || Double.isInfinite(d))
+                && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS)))) {
+            writeString(String.valueOf(d));
+            return;
+        }
+        // What is the max length for doubles? 40 chars?
+        _verifyValueWrite("write number");
+        writeRaw(String.valueOf(d));
+    }
+
+    @Override
+    public void writeNumber(float f)
+        throws IOException, JsonGenerationException
+    {
+        if (_cfgNumbersAsStrings ||
+            // [JACKSON-139]
+            (((Float.isNaN(f) || Float.isInfinite(f))
+                && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS)))) {
+            writeString(String.valueOf(f));
+            return;
+        }
+        // What is the max length for floats?
+        _verifyValueWrite("write number");
+        writeRaw(String.valueOf(f));
+    }
+
+    @Override
+    public void writeNumber(BigDecimal value)
+        throws IOException, JsonGenerationException
+    {
+        // Don't really know max length for big decimal, no point checking
+        _verifyValueWrite("write number");
+        if (value == null) {
+            _writeNull();
+        } else if (_cfgNumbersAsStrings) {
+            _writeQuotedRaw(value);
+        } else {
+            writeRaw(value.toString());
+        }
+    }
+
+    @Override
+    public void writeNumber(String encodedValue)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        if (_cfgNumbersAsStrings) {
+            _writeQuotedRaw(encodedValue);            
+        } else {
+            writeRaw(encodedValue);
+        }
+    }
+
+    private final void _writeQuotedRaw(Object value) throws IOException
+    {
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+        writeRaw(value.toString());
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = BYTE_QUOTE;
+    }
+    
+    @Override
+    public void writeBoolean(boolean state)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write boolean value");
+        if ((_outputTail + 5) >= _outputEnd) {
+            _flushBuffer();
+        }
+        byte[] keyword = state ? TRUE_BYTES : FALSE_BYTES;
+        int len = keyword.length;
+        System.arraycopy(keyword, 0, _outputBuffer, _outputTail, len);
+        _outputTail += len;
+    }
+
+    @Override
+    public void writeNull()
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write null value");
+        _writeNull();
+    }
+
+    /*
+    /**********************************************************
+    /* Implementations for other methods
+    /**********************************************************
+     */
+
+    @Override
+    protected final void _verifyValueWrite(String typeMsg)
+        throws IOException, JsonGenerationException
+    {
+        int status = _writeContext.writeValue();
+        if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
+            _reportError("Can not "+typeMsg+", expecting field name");
+        }
+        if (_cfgPrettyPrinter == null) {
+            byte b;
+            switch (status) {
+            case JsonWriteContext.STATUS_OK_AFTER_COMMA:
+                b = BYTE_COMMA;
+                break;
+            case JsonWriteContext.STATUS_OK_AFTER_COLON:
+                b = BYTE_COLON;
+                break;
+            case JsonWriteContext.STATUS_OK_AFTER_SPACE:
+                b = BYTE_SPACE;
+                break;
+            case JsonWriteContext.STATUS_OK_AS_IS:
+            default:
+                return;
+            }
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail] = b;
+            ++_outputTail;
+            return;
+        }
+        // Otherwise, pretty printer knows what to do...
+        _verifyPrettyValueWrite(typeMsg, status);
+    }
+
+    protected final void _verifyPrettyValueWrite(String typeMsg, int status)
+        throws IOException, JsonGenerationException
+    {
+        // If we have a pretty printer, it knows what to do:
+        switch (status) {
+        case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array
+            _cfgPrettyPrinter.writeArrayValueSeparator(this);
+            break;
+        case JsonWriteContext.STATUS_OK_AFTER_COLON:
+            _cfgPrettyPrinter.writeObjectFieldValueSeparator(this);
+            break;
+        case JsonWriteContext.STATUS_OK_AFTER_SPACE:
+            _cfgPrettyPrinter.writeRootValueSeparator(this);
+            break;
+        case JsonWriteContext.STATUS_OK_AS_IS:
+            // First entry, but of which context?
+            if (_writeContext.inArray()) {
+                _cfgPrettyPrinter.beforeArrayValues(this);
+            } else if (_writeContext.inObject()) {
+                _cfgPrettyPrinter.beforeObjectEntries(this);
+            }
+            break;
+        default:
+            _cantHappen();
+            break;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Low-level output handling
+    /**********************************************************
+     */
+
+    @Override
+    public final void flush()
+        throws IOException
+    {
+        _flushBuffer();
+        if (_outputStream != null) {
+            if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
+                _outputStream.flush();
+            }
+        }
+    }
+
+    @Override
+    public void close()
+        throws IOException
+    {
+        super.close();
+
+        /* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
+         *   scopes.
+         */
+        // First: let's see that we still have buffers...
+        if (_outputBuffer != null
+            && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
+            while (true) {
+                JsonStreamContext ctxt = getOutputContext();
+                if (ctxt.inArray()) {
+                    writeEndArray();
+                } else if (ctxt.inObject()) {
+                    writeEndObject();
+                } else {
+                    break;
+                }
+            }
+        }
+        _flushBuffer();
+
+        /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
+         *   on the underlying Reader, unless we "own" it, or auto-closing
+         *   feature is enabled.
+         *   One downside: when using UTF8Writer, underlying buffer(s)
+         *   may not be properly recycled if we don't close the writer.
+         */
+        if (_outputStream != null) {
+            if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
+                _outputStream.close();
+            } else  if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
+                // If we can't close it, we should at least flush
+                _outputStream.flush();
+            }
+        }
+        // Internal buffer(s) generator has can now be released as well
+        _releaseBuffers();
+    }
+
+    @Override
+    protected void _releaseBuffers()
+    {
+        byte[] buf = _outputBuffer;
+        if (buf != null && _bufferRecyclable) {
+            _outputBuffer = null;
+            _ioContext.releaseWriteEncodingBuffer(buf);
+        }
+        char[] cbuf = _charBuffer;
+        if (cbuf != null) {
+            _charBuffer = null;
+            _ioContext.releaseConcatBuffer(cbuf);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing, raw bytes
+    /**********************************************************
+     */
+
+    private final void _writeBytes(byte[] bytes) throws IOException
+    {
+        final int len = bytes.length;
+        if ((_outputTail + len) > _outputEnd) {
+            _flushBuffer();
+            // still not enough?
+            if (len > MAX_BYTES_TO_BUFFER) {
+                _outputStream.write(bytes, 0, len);
+                return;
+            }
+        }
+        System.arraycopy(bytes, 0, _outputBuffer, _outputTail, len);
+        _outputTail += len;
+    }
+
+    private final void _writeBytes(byte[] bytes, int offset, int len) throws IOException
+    {
+        if ((_outputTail + len) > _outputEnd) {
+            _flushBuffer();
+            // still not enough?
+            if (len > MAX_BYTES_TO_BUFFER) {
+                _outputStream.write(bytes, offset, len);
+                return;
+            }
+        }
+        System.arraycopy(bytes, offset, _outputBuffer, _outputTail, len);
+        _outputTail += len;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, mid-level writing, String segments
+    /**********************************************************
+     */
+    
+    /**
+     * Method called when String to write is long enough not to fit
+     * completely in temporary copy buffer. If so, we will actually
+     * copy it in small enough chunks so it can be directly fed
+     * to single-segment writes (instead of maximum slices that
+     * would fit in copy buffer)
+     */
+    private final void _writeStringSegments(String text)
+        throws IOException, JsonGenerationException
+    {
+        int left = text.length();
+        int offset = 0;
+        final char[] cbuf = _charBuffer;
+
+        while (left > 0) {
+            int len = Math.min(_outputMaxContiguous, left);
+            text.getChars(offset, offset+len, cbuf, 0);
+            if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
+                _flushBuffer();
+            }
+            _writeStringSegment(cbuf, 0, len);
+            offset += len;
+            left -= len;
+        }
+    }
+
+    /**
+     * Method called when character sequence to write is long enough that
+     * its maximum encoded and escaped form is not guaranteed to fit in
+     * the output buffer. If so, we will need to choose smaller output
+     * chunks to write at a time.
+     */
+    private final void _writeStringSegments(char[] cbuf, int offset, int totalLen)
+        throws IOException, JsonGenerationException
+    {
+        do {
+            int len = Math.min(_outputMaxContiguous, totalLen);
+            if ((_outputTail + len) > _outputEnd) { // caller must ensure enough space
+                _flushBuffer();
+            }
+            _writeStringSegment(cbuf, offset, len);
+            offset += len;
+            totalLen -= len;
+        } while (totalLen > 0);
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing, text segments
+    /**********************************************************
+     */
+
+    /**
+     * This method called when the string content is already in
+     * a char buffer, and its maximum total encoded and escaped length
+     * can not exceed size of the output buffer.
+     * Caller must ensure that there is enough space in output buffer,
+     * assuming case of all non-escaped ASCII characters, as well as
+     * potentially enough space for other cases (but not necessarily flushed)
+     */
+    private final void _writeStringSegment(char[] cbuf, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        // note: caller MUST ensure (via flushing) there's room for ASCII only
+        
+        // Fast+tight loop for ASCII-only, no-escaping-needed output
+        len += offset; // becomes end marker, then
+
+        int outputPtr = _outputTail;
+        final byte[] outputBuffer = _outputBuffer;
+        final int[] escCodes = _outputEscapes;
+
+        while (offset < len) {
+            int ch = cbuf[offset];
+            // note: here we know that (ch > 0x7F) will cover case of escaping non-ASCII too:
+            if (ch > 0x7F || escCodes[ch] != 0) {
+                break;
+            }
+            outputBuffer[outputPtr++] = (byte) ch;
+            ++offset;
+        }
+        _outputTail = outputPtr;
+        if (offset < len) {
+            // [JACKSON-106]
+            if (_characterEscapes != null) {
+                _writeCustomStringSegment2(cbuf, offset, len);
+            // [JACKSON-102]
+            } else if (_maximumNonEscapedChar == 0) {
+                _writeStringSegment2(cbuf, offset, len);
+            } else {
+                _writeStringSegmentASCII2(cbuf, offset, len);
+            }
+
+        }
+    }
+
+    /**
+     * Secondary method called when content contains characters to escape,
+     * and/or multi-byte UTF-8 characters.
+     */
+    private final void _writeStringSegment2(final char[] cbuf, int offset, final int end)
+        throws IOException, JsonGenerationException
+    {
+        // Ok: caller guarantees buffer can have room; but that may require flushing:
+        if ((_outputTail +  6 * (end - offset)) > _outputEnd) {
+            _flushBuffer();
+        }
+
+        int outputPtr = _outputTail;
+
+        final byte[] outputBuffer = _outputBuffer;
+        final int[] escCodes = _outputEscapes;
+        
+        while (offset < end) {
+            int ch = cbuf[offset++];
+            if (ch <= 0x7F) {
+                 if (escCodes[ch] == 0) {
+                     outputBuffer[outputPtr++] = (byte) ch;
+                     continue;
+                 }
+                 int escape = escCodes[ch];
+                 if (escape > 0) { // 2-char escape, fine
+                     outputBuffer[outputPtr++] = BYTE_BACKSLASH;
+                     outputBuffer[outputPtr++] = (byte) escape;
+                 } else {
+                     // ctrl-char, 6-byte escape...
+                     outputPtr = _writeGenericEscape(ch, outputPtr);
+                }
+                continue;
+            }
+            if (ch <= 0x7FF) { // fine, just needs 2 byte output
+                outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
+                outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
+            } else {
+                outputPtr = _outputMultiByteChar(ch, outputPtr);
+            }
+        }
+        _outputTail = outputPtr;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing, text segment
+    /* with additional escaping (ASCII or such)
+    /* (since 1.8; see [JACKSON-102])
+    /**********************************************************
+     */
+
+    /**
+     * Same as <code>_writeStringSegment2(char[], ...)</code., but with
+     * additional escaping for high-range code points
+     * 
+     * @since 1.8
+     */
+    private final void _writeStringSegmentASCII2(final char[] cbuf, int offset, final int end)
+        throws IOException, JsonGenerationException
+    {
+        // Ok: caller guarantees buffer can have room; but that may require flushing:
+        if ((_outputTail +  6 * (end - offset)) > _outputEnd) {
+            _flushBuffer();
+        }
+    
+        int outputPtr = _outputTail;
+    
+        final byte[] outputBuffer = _outputBuffer;
+        final int[] escCodes = _outputEscapes;
+        final int maxUnescaped = _maximumNonEscapedChar;
+        
+        while (offset < end) {
+            int ch = cbuf[offset++];
+            if (ch <= 0x7F) {
+                 if (escCodes[ch] == 0) {
+                     outputBuffer[outputPtr++] = (byte) ch;
+                     continue;
+                 }
+                 int escape = escCodes[ch];
+                 if (escape > 0) { // 2-char escape, fine
+                     outputBuffer[outputPtr++] = BYTE_BACKSLASH;
+                     outputBuffer[outputPtr++] = (byte) escape;
+                 } else {
+                     // ctrl-char, 6-byte escape...
+                     outputPtr = _writeGenericEscape(ch, outputPtr);
+                 }
+                 continue;
+            }
+            if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
+                outputPtr = _writeGenericEscape(ch, outputPtr);
+                continue;
+            }
+            if (ch <= 0x7FF) { // fine, just needs 2 byte output
+                outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
+                outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
+            } else {
+                outputPtr = _outputMultiByteChar(ch, outputPtr);
+            }
+        }
+        _outputTail = outputPtr;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing, text segment
+    /* with fully custom escaping (and possibly escaping of non-ASCII
+    /**********************************************************
+     */
+
+    /**
+     * Same as <code>_writeStringSegmentASCII2(char[], ...)</code., but with
+     * additional checking for completely custom escapes
+     * 
+     * @since 1.8
+     */
+    private final void _writeCustomStringSegment2(final char[] cbuf, int offset, final int end)
+        throws IOException, JsonGenerationException
+    {
+        // Ok: caller guarantees buffer can have room; but that may require flushing:
+        if ((_outputTail +  6 * (end - offset)) > _outputEnd) {
+            _flushBuffer();
+        }
+        int outputPtr = _outputTail;
+    
+        final byte[] outputBuffer = _outputBuffer;
+        final int[] escCodes = _outputEscapes;
+        // may or may not have this limit
+        final int maxUnescaped = (_maximumNonEscapedChar <= 0) ? 0xFFFF : _maximumNonEscapedChar;
+        final CharacterEscapes customEscapes = _characterEscapes; // non-null
+        
+        while (offset < end) {
+            int ch = cbuf[offset++];
+            if (ch <= 0x7F) {
+                 if (escCodes[ch] == 0) {
+                     outputBuffer[outputPtr++] = (byte) ch;
+                     continue;
+                 }
+                 int escape = escCodes[ch];
+                 if (escape > 0) { // 2-char escape, fine
+                     outputBuffer[outputPtr++] = BYTE_BACKSLASH;
+                     outputBuffer[outputPtr++] = (byte) escape;
+                 } else if (escape == CharacterEscapes.ESCAPE_CUSTOM) {
+                     SerializableString esc = customEscapes.getEscapeSequence(ch);
+                     if (esc == null) {
+                         throw new JsonGenerationException("Invalid custom escape definitions; custom escape not found for character code 0x"
+                                 +Integer.toHexString(ch)+", although was supposed to have one");
+                     }
+                     outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
+                 } else {
+                     // ctrl-char, 6-byte escape...
+                     outputPtr = _writeGenericEscape(ch, outputPtr);
+                 }
+                 continue;
+            }
+            if (ch > maxUnescaped) { // [JACKSON-102] Allow forced escaping if non-ASCII (etc) chars:
+                outputPtr = _writeGenericEscape(ch, outputPtr);
+                continue;
+            }
+            SerializableString esc = customEscapes.getEscapeSequence(ch);
+            if (esc != null) {
+                outputPtr = _writeCustomEscape(outputBuffer, outputPtr, esc, end-offset);
+                continue;
+            }
+            if (ch <= 0x7FF) { // fine, just needs 2 byte output
+                outputBuffer[outputPtr++] = (byte) (0xc0 | (ch >> 6));
+                outputBuffer[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
+            } else {
+                outputPtr = _outputMultiByteChar(ch, outputPtr);
+            }
+        }
+        _outputTail = outputPtr;
+    }
+
+    private int _writeCustomEscape(byte[] outputBuffer, int outputPtr, SerializableString esc, int remainingChars)
+        throws IOException, JsonGenerationException
+    {
+        byte[] raw = esc.asUnquotedUTF8(); // must be escaped at this point, shouldn't double-quote
+        int len = raw.length;
+        if (len > 6) { // may violate constraints we have, do offline
+            return _handleLongCustomEscape(outputBuffer, outputPtr, _outputEnd, raw, remainingChars);
+        }
+        // otherwise will fit without issues, so:
+        System.arraycopy(raw, 0, outputBuffer, outputPtr, len);
+        return (outputPtr + len);
+    }
+    
+    private int _handleLongCustomEscape(byte[] outputBuffer, int outputPtr, int outputEnd, byte[] raw,
+            int remainingChars)
+        throws IOException, JsonGenerationException
+    {
+        int len = raw.length;
+        if ((outputPtr + len) > outputEnd) {
+            _outputTail = outputPtr;
+            _flushBuffer();
+            outputPtr = _outputTail;
+            if (len > outputBuffer.length) { // very unlikely, but possible...
+                _outputStream.write(raw, 0, len);
+                return outputPtr;
+            }
+            System.arraycopy(raw, 0, outputBuffer, outputPtr, len);
+            outputPtr += len;
+        }
+        // but is the invariant still obeyed? If not, flush once more
+        if ((outputPtr +  6 * remainingChars) > outputEnd) {
+            _flushBuffer();
+            return _outputTail;
+        }
+        return outputPtr;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing, "raw UTF-8" segments
+    /**********************************************************
+     */
+    
+    /**
+     * Method called when UTF-8 encoded (but NOT yet escaped!) content is not guaranteed
+     * to fit in the output buffer after escaping; as such, we just need to
+     * chunk writes.
+     */
+    private final void _writeUTF8Segments(byte[] utf8, int offset, int totalLen)
+        throws IOException, JsonGenerationException
+    {
+        do {
+            int len = Math.min(_outputMaxContiguous, totalLen);
+            _writeUTF8Segment(utf8, offset, len);
+            offset += len;
+            totalLen -= len;
+        } while (totalLen > 0);
+    }
+    
+    private final void _writeUTF8Segment(byte[] utf8, final int offset, final int len)
+        throws IOException, JsonGenerationException
+    {
+        // fast loop to see if escaping is needed; don't copy, just look
+        final int[] escCodes = _outputEscapes;
+
+        for (int ptr = offset, end = offset + len; ptr < end; ) {
+            // 28-Feb-2011, tatu: escape codes just cover 7-bit range, so:
+            int ch = utf8[ptr++];
+            if ((ch >= 0) && escCodes[ch] != 0) {
+                _writeUTF8Segment2(utf8, offset, len);
+                return;
+            }
+        }
+        
+        // yes, fine, just copy the sucker
+        if ((_outputTail + len) > _outputEnd) { // enough room or need to flush?
+            _flushBuffer(); // but yes once we flush (caller guarantees length restriction)
+        }
+        System.arraycopy(utf8, offset, _outputBuffer, _outputTail, len);
+        _outputTail += len;
+    }
+
+    private final void _writeUTF8Segment2(final byte[] utf8, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        int outputPtr = _outputTail;
+
+        // Ok: caller guarantees buffer can have room; but that may require flushing:
+        if ((outputPtr + (len * 6)) > _outputEnd) {
+            _flushBuffer();
+            outputPtr = _outputTail;
+        }
+        
+        final byte[] outputBuffer = _outputBuffer;
+        final int[] escCodes = _outputEscapes;
+        len += offset; // so 'len' becomes 'end'
+        
+        while (offset < len) {
+            byte b = utf8[offset++];
+            int ch = b;
+            if (ch < 0 || escCodes[ch] == 0) {
+                outputBuffer[outputPtr++] = b;
+                continue;
+            }
+            int escape = escCodes[ch];
+            if (escape > 0) { // 2-char escape, fine
+                outputBuffer[outputPtr++] = BYTE_BACKSLASH;
+                outputBuffer[outputPtr++] = (byte) escape;
+            } else {
+                // ctrl-char, 6-byte escape...
+                outputPtr = _writeGenericEscape(ch, outputPtr);
+            }
+        }
+        _outputTail = outputPtr;
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing, base64 encoded
+    /**********************************************************
+     */
+    
+    protected void _writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd)
+        throws IOException, JsonGenerationException
+    {
+        // Encoding is by chunks of 3 input, 4 output chars, so:
+        int safeInputEnd = inputEnd - 3;
+        // Let's also reserve room for possible (and quoted) lf char each round
+        int safeOutputEnd = _outputEnd - 6;
+        int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
+
+        // Ok, first we loop through all full triplets of data:
+        while (inputPtr <= safeInputEnd) {
+            if (_outputTail > safeOutputEnd) { // need to flush
+                _flushBuffer();
+            }
+            // First, mash 3 bytes into lsb of 32-bit int
+            int b24 = ((int) input[inputPtr++]) << 8;
+            b24 |= ((int) input[inputPtr++]) & 0xFF;
+            b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
+            _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
+            if (--chunksBeforeLF <= 0) {
+                // note: must quote in JSON value
+                _outputBuffer[_outputTail++] = '\\';
+                _outputBuffer[_outputTail++] = 'n';
+                chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
+            }
+        }
+
+        // And then we may have 1 or 2 leftover bytes to encode
+        int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
+        if (inputLeft > 0) { // yes, but do we have room for output?
+            if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
+                _flushBuffer();
+            }
+            int b24 = ((int) input[inputPtr++]) << 16;
+            if (inputLeft == 2) {
+                b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
+            }
+            _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, character escapes/encoding
+    /**********************************************************
+     */
+    
+    /**
+     * Method called to output a character that is beyond range of
+     * 1- and 2-byte UTF-8 encodings, when outputting "raw" 
+     * text (meaning it is not to be escaped or quoted)
+     */
+    private final int _outputRawMultiByteChar(int ch, char[] cbuf, int inputOffset, int inputLen)
+        throws IOException
+    {
+        // Let's handle surrogates gracefully (as 4 byte output):
+        if (ch >= SURR1_FIRST) {
+            if (ch <= SURR2_LAST) { // yes, outside of BMP
+                // Do we have second part?
+                if (inputOffset >= inputLen) { // nope... have to note down
+                    _reportError("Split surrogate on writeRaw() input (last character)");
+                }
+                _outputSurrogates(ch, cbuf[inputOffset]);
+                return (inputOffset+1);
+            }
+        }
+        final byte[] bbuf = _outputBuffer;
+        bbuf[_outputTail++] = (byte) (0xe0 | (ch >> 12));
+        bbuf[_outputTail++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
+        bbuf[_outputTail++] = (byte) (0x80 | (ch & 0x3f));
+        return inputOffset;
+    }
+
+    protected final void _outputSurrogates(int surr1, int surr2)
+        throws IOException
+    {
+        int c = _decodeSurrogate(surr1, surr2);
+        if ((_outputTail + 4) > _outputEnd) {
+            _flushBuffer();
+        }
+        final byte[] bbuf = _outputBuffer;
+        bbuf[_outputTail++] = (byte) (0xf0 | (c >> 18));
+        bbuf[_outputTail++] = (byte) (0x80 | ((c >> 12) & 0x3f));
+        bbuf[_outputTail++] = (byte) (0x80 | ((c >> 6) & 0x3f));
+        bbuf[_outputTail++] = (byte) (0x80 | (c & 0x3f));
+    }
+    
+    /**
+     * 
+     * @param ch
+     * @param outputPtr Position within output buffer to append multi-byte in
+     * 
+     * @return New output position after appending
+     * 
+     * @throws IOException
+     */
+    private final int _outputMultiByteChar(int ch, int outputPtr)
+        throws IOException
+    {
+        byte[] bbuf = _outputBuffer;
+        if (ch >= SURR1_FIRST && ch <= SURR2_LAST) { // yes, outside of BMP; add an escape
+            bbuf[outputPtr++] = BYTE_BACKSLASH;
+            bbuf[outputPtr++] = BYTE_u;
+            
+            bbuf[outputPtr++] = HEX_CHARS[(ch >> 12) & 0xF];
+            bbuf[outputPtr++] = HEX_CHARS[(ch >> 8) & 0xF];
+            bbuf[outputPtr++] = HEX_CHARS[(ch >> 4) & 0xF];
+            bbuf[outputPtr++] = HEX_CHARS[ch & 0xF];
+        } else {
+            bbuf[outputPtr++] = (byte) (0xe0 | (ch >> 12));
+            bbuf[outputPtr++] = (byte) (0x80 | ((ch >> 6) & 0x3f));
+            bbuf[outputPtr++] = (byte) (0x80 | (ch & 0x3f));
+        }
+        return outputPtr;
+    }
+
+    protected final int _decodeSurrogate(int surr1, int surr2) throws IOException
+    {
+        // First is known to be valid, but how about the other?
+        if (surr2 < SURR2_FIRST || surr2 > SURR2_LAST) {
+            String msg = "Incomplete surrogate pair: first char 0x"+Integer.toHexString(surr1)+", second 0x"+Integer.toHexString(surr2);
+            _reportError(msg);
+        }
+        int c = 0x10000 + ((surr1 - SURR1_FIRST) << 10) + (surr2 - SURR2_FIRST);
+        return c;
+    }
+    
+    private final void _writeNull() throws IOException
+    {
+        if ((_outputTail + 4) >= _outputEnd) {
+            _flushBuffer();
+        }
+        System.arraycopy(NULL_BYTES, 0, _outputBuffer, _outputTail, 4);
+        _outputTail += 4;
+    }
+        
+    /**
+     * Method called to write a generic Unicode escape for given character.
+     * 
+     * @param charToEscape Character to escape using escape sequence (\\uXXXX)
+     */
+    private int _writeGenericEscape(int charToEscape, int outputPtr)
+        throws IOException
+    {
+        final byte[] bbuf = _outputBuffer;
+        bbuf[outputPtr++] = BYTE_BACKSLASH;
+        bbuf[outputPtr++] = BYTE_u;
+        if (charToEscape > 0xFF) {
+            int hi = (charToEscape >> 8) & 0xFF;
+            bbuf[outputPtr++] = HEX_CHARS[hi >> 4];
+            bbuf[outputPtr++] = HEX_CHARS[hi & 0xF];
+            charToEscape &= 0xFF;
+        } else {
+            bbuf[outputPtr++] = BYTE_0;
+            bbuf[outputPtr++] = BYTE_0;
+        }
+        // We know it's a control char, so only the last 2 chars are non-0
+        bbuf[outputPtr++] = HEX_CHARS[charToEscape >> 4];
+        bbuf[outputPtr++] = HEX_CHARS[charToEscape & 0xF];
+        return outputPtr;
+    }
+
+    protected final void _flushBuffer() throws IOException
+    {
+        int len = _outputTail;
+        if (len > 0) {
+            _outputTail = 0;
+            _outputStream.write(_outputBuffer, 0, len);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java
new file mode 100644
index 0000000..e40aa3a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java
@@ -0,0 +1,2950 @@
+package com.fasterxml.jackson.core.json;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.base.ParserBase;
+import com.fasterxml.jackson.core.io.CharTypes;
+import com.fasterxml.jackson.core.io.IOContext;
+import com.fasterxml.jackson.core.sym.*;
+import com.fasterxml.jackson.core.util.*;
+
+/**
+ * This is a concrete implementation of {@link JsonParser}, which is
+ * based on a {@link java.io.InputStream} as the input source.
+ */
+public final class UTF8StreamJsonParser
+    extends ParserBase
+{
+    final static byte BYTE_LF = (byte) '\n';
+
+    private final static int[] sInputCodesUtf8 = CharTypes.getInputCodeUtf8();
+
+    /**
+     * Latin1 encoding is not supported, but we do use 8-bit subset for
+     * pre-processing task, to simplify first pass, keep it fast.
+     */
+    private final static int[] sInputCodesLatin1 = CharTypes.getInputCodeLatin1();
+    
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    /**
+     * Codec used for data binding when (if) requested; typically full
+     * <code>ObjectMapper</code>, but that abstract is not part of core
+     * package.
+     */
+    protected ObjectCodec _objectCodec;
+
+    /**
+     * Symbol table that contains field names encountered so far
+     */
+    final protected BytesToNameCanonicalizer _symbols;
+    
+    /*
+    /**********************************************************
+    /* Parsing state
+    /**********************************************************
+     */
+    
+    /**
+     * Temporary buffer used for name parsing.
+     */
+    protected int[] _quadBuffer = new int[16];
+
+    /**
+     * Flag that indicates that the current token has not yet
+     * been fully processed, and needs to be finished for
+     * some access (or skipped to obtain the next token)
+     */
+    protected boolean _tokenIncomplete = false;
+
+    /**
+     * Temporary storage for partially parsed name bytes.
+     */
+    private int _quad1;
+    
+    /*
+    /**********************************************************
+    /* Input buffering (from former 'StreamBasedParserBase')
+    /**********************************************************
+     */
+    
+    protected InputStream _inputStream;
+
+    /*
+    /**********************************************************
+    /* Current input data
+    /**********************************************************
+     */
+
+    /**
+     * Current buffer from which data is read; generally data is read into
+     * buffer from input source, but in some cases pre-loaded buffer
+     * is handed to the parser.
+     */
+    protected byte[] _inputBuffer;
+
+    /**
+     * Flag that indicates whether the input buffer is recycable (and
+     * needs to be returned to recycler once we are done) or not.
+     *<p>
+     * If it is not, it also means that parser can NOT modify underlying
+     * buffer.
+     */
+    protected boolean _bufferRecyclable;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public UTF8StreamJsonParser(IOContext ctxt, int features, InputStream in,
+            ObjectCodec codec, BytesToNameCanonicalizer sym,
+            byte[] inputBuffer, int start, int end,
+            boolean bufferRecyclable)
+    {
+        super(ctxt, features);
+        _inputStream = in;
+        _objectCodec = codec;
+        _symbols = sym;
+        _inputBuffer = inputBuffer;
+        _inputPtr = start;
+        _inputEnd = end;
+        _bufferRecyclable = bufferRecyclable;
+        // 12-Mar-2010, tatus: Sanity check, related to [JACKSON-259]:
+        if (!JsonParser.Feature.CANONICALIZE_FIELD_NAMES.enabledIn(features)) {
+            // should never construct non-canonical UTF-8/byte parser (instead, use Reader)
+            _throwInternal();
+        }
+    }
+
+    @Override
+    public ObjectCodec getCodec() {
+        return _objectCodec;
+    }
+
+    @Override
+    public void setCodec(ObjectCodec c) {
+        _objectCodec = c;
+    }
+
+    /*
+    /**********************************************************
+    /* Former StreamBasedParserBase methods
+    /**********************************************************
+     */
+
+    @Override
+    public int releaseBuffered(OutputStream out) throws IOException
+    {
+        int count = _inputEnd - _inputPtr;
+        if (count < 1) {
+            return 0;
+        }
+        // let's just advance ptr to end
+        int origPtr = _inputPtr;
+        out.write(_inputBuffer, origPtr, count);
+        return count;
+    }
+
+    @Override
+    public Object getInputSource() {
+        return _inputStream;
+    }
+    
+    /*
+    /**********************************************************
+    /* Low-level reading, other
+    /**********************************************************
+     */
+
+    @Override
+    protected final boolean loadMore()
+        throws IOException
+    {
+        _currInputProcessed += _inputEnd;
+        _currInputRowStart -= _inputEnd;
+        
+        if (_inputStream != null) {
+            int count = _inputStream.read(_inputBuffer, 0, _inputBuffer.length);
+            if (count > 0) {
+                _inputPtr = 0;
+                _inputEnd = count;
+                return true;
+            }
+            // End of input
+            _closeInput();
+            // Should never return 0, so let's fail
+            if (count == 0) {
+                throw new IOException("InputStream.read() returned 0 characters when trying to read "+_inputBuffer.length+" bytes");
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Helper method that will try to load at least specified number bytes in
+     * input buffer, possible moving existing data around if necessary
+     * 
+     * @since 1.6
+     */
+    protected final boolean _loadToHaveAtLeast(int minAvailable)
+        throws IOException
+    {
+        // No input stream, no leading (either we are closed, or have non-stream input source)
+        if (_inputStream == null) {
+            return false;
+        }
+        // Need to move remaining data in front?
+        int amount = _inputEnd - _inputPtr;
+        if (amount > 0 && _inputPtr > 0) {
+            _currInputProcessed += _inputPtr;
+            _currInputRowStart -= _inputPtr;
+            System.arraycopy(_inputBuffer, _inputPtr, _inputBuffer, 0, amount);
+            _inputEnd = amount;
+        } else {
+            _inputEnd = 0;
+        }
+        _inputPtr = 0;
+        while (_inputEnd < minAvailable) {
+            int count = _inputStream.read(_inputBuffer, _inputEnd, _inputBuffer.length - _inputEnd);
+            if (count < 1) {
+                // End of input
+                _closeInput();
+                // Should never return 0, so let's fail
+                if (count == 0) {
+                    throw new IOException("InputStream.read() returned 0 characters when trying to read "+amount+" bytes");
+                }
+                return false;
+            }
+            _inputEnd += count;
+        }
+        return true;
+    }
+    
+    @Override
+    protected void _closeInput() throws IOException
+    {
+        /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
+         *   on the underlying InputStream, unless we "own" it, or auto-closing
+         *   feature is enabled.
+         */
+        if (_inputStream != null) {
+            if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_SOURCE)) {
+                _inputStream.close();
+            }
+            _inputStream = null;
+        }
+    }
+
+    /**
+     * Method called to release internal buffers owned by the base
+     * reader. This may be called along with {@link #_closeInput} (for
+     * example, when explicitly closing this reader instance), or
+     * separately (if need be).
+     */
+    @Override
+    protected void _releaseBuffers() throws IOException
+    {
+        super._releaseBuffers();
+        if (_bufferRecyclable) {
+            byte[] buf = _inputBuffer;
+            if (buf != null) {
+                _inputBuffer = null;
+                _ioContext.releaseReadIOBuffer(buf);
+            }
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, data access
+    /**********************************************************
+     */
+
+    @Override
+    public String getText()
+        throws IOException, JsonParseException
+    {
+        JsonToken t = _currToken;
+        if (t == JsonToken.VALUE_STRING) {
+            if (_tokenIncomplete) {
+                _tokenIncomplete = false;
+                _finishString(); // only strings can be incomplete
+            }
+            return _textBuffer.contentsAsString();
+        }
+        return _getText2(t);
+    }
+
+    protected final String _getText2(JsonToken t)
+    {
+        if (t == null) {
+            return null;
+        }
+        switch (t) {
+        case FIELD_NAME:
+            return _parsingContext.getCurrentName();
+
+        case VALUE_STRING:
+            // fall through
+        case VALUE_NUMBER_INT:
+        case VALUE_NUMBER_FLOAT:
+            return _textBuffer.contentsAsString();
+        }
+        return t.asString();
+    }
+
+    @Override
+    public char[] getTextCharacters()
+        throws IOException, JsonParseException
+    {
+        if (_currToken != null) { // null only before/after document
+            switch (_currToken) {
+                
+            case FIELD_NAME:
+                if (!_nameCopied) {
+                    String name = _parsingContext.getCurrentName();
+                    int nameLen = name.length();
+                    if (_nameCopyBuffer == null) {
+                        _nameCopyBuffer = _ioContext.allocNameCopyBuffer(nameLen);
+                    } else if (_nameCopyBuffer.length < nameLen) {
+                        _nameCopyBuffer = new char[nameLen];
+                    }
+                    name.getChars(0, nameLen, _nameCopyBuffer, 0);
+                    _nameCopied = true;
+                }
+                return _nameCopyBuffer;
+    
+            case VALUE_STRING:
+                if (_tokenIncomplete) {
+                    _tokenIncomplete = false;
+                    _finishString(); // only strings can be incomplete
+                }
+                // fall through
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                return _textBuffer.getTextBuffer();
+                
+            default:
+                return _currToken.asCharArray();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public int getTextLength()
+        throws IOException, JsonParseException
+    {
+        if (_currToken != null) { // null only before/after document
+            switch (_currToken) {
+                
+            case FIELD_NAME:
+                return _parsingContext.getCurrentName().length();
+            case VALUE_STRING:
+                if (_tokenIncomplete) {
+                    _tokenIncomplete = false;
+                    _finishString(); // only strings can be incomplete
+                }
+                // fall through
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                return _textBuffer.size();
+                
+            default:
+                return _currToken.asCharArray().length;
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public int getTextOffset() throws IOException, JsonParseException
+    {
+        // Most have offset of 0, only some may have other values:
+        if (_currToken != null) {
+            switch (_currToken) {
+            case FIELD_NAME:
+                return 0;
+            case VALUE_STRING:
+                if (_tokenIncomplete) {
+                    _tokenIncomplete = false;
+                    _finishString(); // only strings can be incomplete
+                }
+                // fall through
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                return _textBuffer.getTextOffset();
+            }
+        }
+        return 0;
+    }
+
+    @Override
+    public byte[] getBinaryValue(Base64Variant b64variant)
+        throws IOException, JsonParseException
+    {
+        if (_currToken != JsonToken.VALUE_STRING &&
+                (_currToken != JsonToken.VALUE_EMBEDDED_OBJECT || _binaryValue == null)) {
+            _reportError("Current token ("+_currToken+") not VALUE_STRING or VALUE_EMBEDDED_OBJECT, can not access as binary");
+        }
+        /* To ensure that we won't see inconsistent data, better clear up
+         * state...
+         */
+        if (_tokenIncomplete) {
+            try {
+                _binaryValue = _decodeBase64(b64variant);
+            } catch (IllegalArgumentException iae) {
+                throw _constructError("Failed to decode VALUE_STRING as base64 ("+b64variant+"): "+iae.getMessage());
+            }
+            /* let's clear incomplete only now; allows for accessing other
+             * textual content in error cases
+             */
+            _tokenIncomplete = false;
+        } else { // may actually require conversion...
+            if (_binaryValue == null) {
+                ByteArrayBuilder builder = _getByteArrayBuilder();
+                _decodeBase64(getText(), builder, b64variant);
+                _binaryValue = builder.toByteArray();
+            }
+        }
+        return _binaryValue;
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, traversal, basic
+    /**********************************************************
+     */
+
+    /**
+     * @return Next token from the stream, if any found, or null
+     *   to indicate end-of-input
+     */
+    @Override
+    public JsonToken nextToken()
+        throws IOException, JsonParseException
+    {
+        _numTypesValid = NR_UNKNOWN;
+        /* First: field names are special -- we will always tokenize
+         * (part of) value along with field name to simplify
+         * state handling. If so, can and need to use secondary token:
+         */
+        if (_currToken == JsonToken.FIELD_NAME) {
+            return _nextAfterName();
+        }
+        if (_tokenIncomplete) {
+            _skipString(); // only strings can be partial
+        }
+
+        int i = _skipWSOrEnd();
+        if (i < 0) { // end-of-input
+            /* 19-Feb-2009, tatu: Should actually close/release things
+             *    like input source, symbol table and recyclable buffers now.
+             */
+            close();
+            return (_currToken = null);
+        }
+
+        /* First, need to ensure we know the starting location of token
+         * after skipping leading white space
+         */
+        _tokenInputTotal = _currInputProcessed + _inputPtr - 1;
+        _tokenInputRow = _currInputRow;
+        _tokenInputCol = _inputPtr - _currInputRowStart - 1;
+
+        // finally: clear any data retained so far
+        _binaryValue = null;
+
+        // Closing scope?
+        if (i == INT_RBRACKET) {
+            if (!_parsingContext.inArray()) {
+                _reportMismatchedEndMarker(i, '}');
+            }
+            _parsingContext = _parsingContext.getParent();
+            return (_currToken = JsonToken.END_ARRAY);
+        }
+        if (i == INT_RCURLY) {
+            if (!_parsingContext.inObject()) {
+                _reportMismatchedEndMarker(i, ']');
+            }
+            _parsingContext = _parsingContext.getParent();
+            return (_currToken = JsonToken.END_OBJECT);
+        }
+
+        // Nope: do we then expect a comma?
+        if (_parsingContext.expectComma()) {
+            if (i != INT_COMMA) {
+                _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
+            }
+            i = _skipWS();
+        }
+
+        /* And should we now have a name? Always true for
+         * Object contexts, since the intermediate 'expect-value'
+         * state is never retained.
+         */
+        if (!_parsingContext.inObject()) {
+            return _nextTokenNotInObject(i);
+        }
+        // So first parse the field name itself:
+        Name n = _parseFieldName(i);
+        _parsingContext.setCurrentName(n.getName());
+        _currToken = JsonToken.FIELD_NAME;
+        i = _skipWS();
+        if (i != INT_COLON) {
+            _reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
+        }
+        i = _skipWS();
+
+        // Ok: we must have a value... what is it? Strings are very common, check first:
+        if (i == INT_QUOTE) {
+            _tokenIncomplete = true;
+            _nextToken = JsonToken.VALUE_STRING;
+            return _currToken;
+        }        
+        JsonToken t;
+
+        switch (i) {
+        case INT_LBRACKET:
+            t = JsonToken.START_ARRAY;
+            break;
+        case INT_LCURLY:
+            t = JsonToken.START_OBJECT;
+            break;
+        case INT_RBRACKET:
+        case INT_RCURLY:
+            // Error: neither is valid at this point; valid closers have
+            // been handled earlier
+            _reportUnexpectedChar(i, "expected a value");
+        case INT_t:
+            _matchToken("true", 1);
+            t = JsonToken.VALUE_TRUE;
+            break;
+        case INT_f:
+            _matchToken("false", 1);
+             t = JsonToken.VALUE_FALSE;
+            break;
+        case INT_n:
+            _matchToken("null", 1);
+            t = JsonToken.VALUE_NULL;
+            break;
+
+        case INT_MINUS:
+            /* Should we have separate handling for plus? Although
+             * it is not allowed per se, it may be erroneously used,
+             * and could be indicate by a more specific error message.
+             */
+        case INT_0:
+        case INT_1:
+        case INT_2:
+        case INT_3:
+        case INT_4:
+        case INT_5:
+        case INT_6:
+        case INT_7:
+        case INT_8:
+        case INT_9:
+            t = parseNumberText(i);
+            break;
+        default:
+            t = _handleUnexpectedValue(i);
+        }
+        _nextToken = t;
+        return _currToken;
+    }
+
+    private final JsonToken _nextTokenNotInObject(int i)
+        throws IOException, JsonParseException
+    {
+        if (i == INT_QUOTE) {
+            _tokenIncomplete = true;
+            return (_currToken = JsonToken.VALUE_STRING);
+        }
+        switch (i) {
+        case INT_LBRACKET:
+            _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+            return (_currToken = JsonToken.START_ARRAY);
+        case INT_LCURLY:
+            _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+            return (_currToken = JsonToken.START_OBJECT);
+        case INT_RBRACKET:
+        case INT_RCURLY:
+            // Error: neither is valid at this point; valid closers have
+            // been handled earlier
+            _reportUnexpectedChar(i, "expected a value");
+        case INT_t:
+            _matchToken("true", 1);
+            return (_currToken = JsonToken.VALUE_TRUE);
+        case INT_f:
+            _matchToken("false", 1);
+            return (_currToken = JsonToken.VALUE_FALSE);
+        case INT_n:
+            _matchToken("null", 1);
+            return (_currToken = JsonToken.VALUE_NULL);
+        case INT_MINUS:
+            /* Should we have separate handling for plus? Although
+             * it is not allowed per se, it may be erroneously used,
+             * and could be indicate by a more specific error message.
+             */
+        case INT_0:
+        case INT_1:
+        case INT_2:
+        case INT_3:
+        case INT_4:
+        case INT_5:
+        case INT_6:
+        case INT_7:
+        case INT_8:
+        case INT_9:
+            return (_currToken = parseNumberText(i));
+        }
+        return (_currToken = _handleUnexpectedValue(i));
+    }
+    
+    private final JsonToken _nextAfterName()
+    {
+        _nameCopied = false; // need to invalidate if it was copied
+        JsonToken t = _nextToken;
+        _nextToken = null;
+        // Also: may need to start new context?
+        if (t == JsonToken.START_ARRAY) {
+            _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+        } else if (t == JsonToken.START_OBJECT) {
+            _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+        }
+        return (_currToken = t);
+    }
+
+    @Override
+    public void close() throws IOException
+    {
+        super.close();
+        // Merge found symbols, if any:
+        _symbols.release();
+    }
+    
+    /*
+    /**********************************************************
+    /* Public API, traversal, nextXxxValue/nextFieldName
+    /**********************************************************
+     */
+    
+    @Override
+    public boolean nextFieldName(SerializableString str)
+        throws IOException, JsonParseException
+    {
+        // // // Note: most of code below is copied from nextToken()
+        
+        _numTypesValid = NR_UNKNOWN;
+        if (_currToken == JsonToken.FIELD_NAME) { // can't have name right after name
+            _nextAfterName();
+            return false;
+        }
+        if (_tokenIncomplete) {
+            _skipString();
+        }
+        int i = _skipWSOrEnd();
+        if (i < 0) { // end-of-input
+            close();
+            _currToken = null;
+            return false;
+        }
+        _tokenInputTotal = _currInputProcessed + _inputPtr - 1;
+        _tokenInputRow = _currInputRow;
+        _tokenInputCol = _inputPtr - _currInputRowStart - 1;
+
+        // finally: clear any data retained so far
+        _binaryValue = null;
+
+        // Closing scope?
+        if (i == INT_RBRACKET) {
+            if (!_parsingContext.inArray()) {
+                _reportMismatchedEndMarker(i, '}');
+            }
+            _parsingContext = _parsingContext.getParent();
+            _currToken = JsonToken.END_ARRAY;
+            return false;
+        }
+        if (i == INT_RCURLY) {
+            if (!_parsingContext.inObject()) {
+                _reportMismatchedEndMarker(i, ']');
+            }
+            _parsingContext = _parsingContext.getParent();
+            _currToken = JsonToken.END_OBJECT;
+            return false;
+        }
+
+        // Nope: do we then expect a comma?
+        if (_parsingContext.expectComma()) {
+            if (i != INT_COMMA) {
+                _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.getTypeDesc()+" entries");
+            }
+            i = _skipWS();
+        }
+
+        if (!_parsingContext.inObject()) {
+            _nextTokenNotInObject(i);
+            return false;
+        }
+        
+        // // // This part differs, name parsing
+        if (i == INT_QUOTE) {
+            // when doing literal match, must consider escaping:
+            byte[] nameBytes = str.asQuotedUTF8();
+            final int len = nameBytes.length;
+            if ((_inputPtr + len) < _inputEnd) { // maybe...
+                // first check length match by
+                final int end = _inputPtr+len;
+                if (_inputBuffer[end] == INT_QUOTE) {
+                    int offset = 0;
+                    final int ptr = _inputPtr;
+                    while (true) {
+                        if (offset == len) { // yes, match!
+                            _inputPtr = end+1; // skip current value first
+                            // First part is simple; setting of name
+                            _parsingContext.setCurrentName(str.getValue());
+                            _currToken = JsonToken.FIELD_NAME;
+                            // But then we also must handle following value etc
+                            _isNextTokenNameYes();
+                            return true;
+                        }
+                        if (nameBytes[offset] != _inputBuffer[ptr+offset]) {
+                            break;
+                        }
+                        ++offset;
+                    }
+                }
+            }
+        }
+        _isNextTokenNameNo(i);
+        return false;
+    }
+
+    private final void _isNextTokenNameYes()
+        throws IOException, JsonParseException
+    {
+        // very first thing: common case, colon, value, no white space
+        int i;
+        if (_inputPtr < _inputEnd && _inputBuffer[_inputPtr] == INT_COLON) { // fast case first
+            ++_inputPtr;
+            i = _inputBuffer[_inputPtr++];
+            if (i == INT_QUOTE) {
+                _tokenIncomplete = true;
+                _nextToken = JsonToken.VALUE_STRING;
+                return;
+            }
+            if (i == INT_LCURLY) {
+                _nextToken = JsonToken.START_OBJECT;
+                return;
+            }
+            if (i == INT_LBRACKET) {
+                _nextToken = JsonToken.START_ARRAY;
+                return;
+            }
+            i &= 0xFF;
+            if (i <= INT_SPACE || i == INT_SLASH) {
+                --_inputPtr;
+                i = _skipWS();
+            }
+        } else {
+            i = _skipColon();
+        }
+        switch (i) {
+        case INT_QUOTE:
+            _tokenIncomplete = true;
+            _nextToken = JsonToken.VALUE_STRING;
+            return;
+        case INT_LBRACKET:
+            _nextToken = JsonToken.START_ARRAY;
+            return;
+        case INT_LCURLY:
+            _nextToken = JsonToken.START_OBJECT;
+            return;
+        case INT_RBRACKET:
+        case INT_RCURLY:
+            _reportUnexpectedChar(i, "expected a value");
+        case INT_t:
+            _matchToken("true", 1);
+            _nextToken = JsonToken.VALUE_TRUE;
+            return;
+        case INT_f:
+            _matchToken("false", 1);
+            _nextToken = JsonToken.VALUE_FALSE;
+            return;
+        case INT_n:
+            _matchToken("null", 1);
+            _nextToken = JsonToken.VALUE_NULL;
+            return;
+        case INT_MINUS:
+        case INT_0:
+        case INT_1:
+        case INT_2:
+        case INT_3:
+        case INT_4:
+        case INT_5:
+        case INT_6:
+        case INT_7:
+        case INT_8:
+        case INT_9:
+            _nextToken = parseNumberText(i);
+            return;
+        }
+        _nextToken = _handleUnexpectedValue(i);
+    }
+    
+    private final void _isNextTokenNameNo(int i)
+            throws IOException, JsonParseException
+    {
+        // // // and this is back to standard nextToken()
+            
+        Name n = _parseFieldName(i);
+        _parsingContext.setCurrentName(n.getName());
+        _currToken = JsonToken.FIELD_NAME;
+        i = _skipWS();
+        if (i != INT_COLON) {
+            _reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
+        }
+        i = _skipWS();
+
+        // Ok: we must have a value... what is it? Strings are very common, check first:
+        if (i == INT_QUOTE) {
+            _tokenIncomplete = true;
+            _nextToken = JsonToken.VALUE_STRING;
+            return;
+        }        
+        JsonToken t;
+
+        switch (i) {
+        case INT_LBRACKET:
+            t = JsonToken.START_ARRAY;
+            break;
+        case INT_LCURLY:
+            t = JsonToken.START_OBJECT;
+            break;
+        case INT_RBRACKET:
+        case INT_RCURLY:
+            _reportUnexpectedChar(i, "expected a value");
+        case INT_t:
+            _matchToken("true", 1);
+            t = JsonToken.VALUE_TRUE;
+            break;
+        case INT_f:
+            _matchToken("false", 1);
+             t = JsonToken.VALUE_FALSE;
+            break;
+        case INT_n:
+            _matchToken("null", 1);
+            t = JsonToken.VALUE_NULL;
+            break;
+
+        case INT_MINUS:
+        case INT_0:
+        case INT_1:
+        case INT_2:
+        case INT_3:
+        case INT_4:
+        case INT_5:
+        case INT_6:
+        case INT_7:
+        case INT_8:
+        case INT_9:
+            t = parseNumberText(i);
+            break;
+        default:
+            t = _handleUnexpectedValue(i);
+        }
+        _nextToken = t;
+    }
+
+    @Override
+    public String nextTextValue()
+        throws IOException, JsonParseException
+    {
+        // two distinct cases; either got name and we know next type, or 'other'
+        if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+            _nameCopied = false;
+            JsonToken t = _nextToken;
+            _nextToken = null;
+            _currToken = t;
+            if (t == JsonToken.VALUE_STRING) {
+                if (_tokenIncomplete) {
+                    _tokenIncomplete = false;
+                    _finishString();
+                }
+                return _textBuffer.contentsAsString();
+            }
+            if (t == JsonToken.START_ARRAY) {
+                _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+            } else if (t == JsonToken.START_OBJECT) {
+                _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+            }
+            return null;
+        }
+        // !!! TODO: optimize this case as well
+        return (nextToken() == JsonToken.VALUE_STRING) ? getText() : null;
+    }
+
+    @Override
+    public int nextIntValue(int defaultValue)
+        throws IOException, JsonParseException
+    {
+        // two distinct cases; either got name and we know next type, or 'other'
+        if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+            _nameCopied = false;
+            JsonToken t = _nextToken;
+            _nextToken = null;
+            _currToken = t;
+            if (t == JsonToken.VALUE_NUMBER_INT) {
+                return getIntValue();
+            }
+            if (t == JsonToken.START_ARRAY) {
+                _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+            } else if (t == JsonToken.START_OBJECT) {
+                _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+            }
+            return defaultValue;
+        }
+        // !!! TODO: optimize this case as well
+        return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getIntValue() : defaultValue;
+    }
+
+    @Override
+    public long nextLongValue(long defaultValue)
+        throws IOException, JsonParseException
+    {
+        // two distinct cases; either got name and we know next type, or 'other'
+        if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+            _nameCopied = false;
+            JsonToken t = _nextToken;
+            _nextToken = null;
+            _currToken = t;
+            if (t == JsonToken.VALUE_NUMBER_INT) {
+                return getLongValue();
+            }
+            if (t == JsonToken.START_ARRAY) {
+                _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+            } else if (t == JsonToken.START_OBJECT) {
+                _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+            }
+            return defaultValue;
+        }
+        // !!! TODO: optimize this case as well
+        return (nextToken() == JsonToken.VALUE_NUMBER_INT) ? getLongValue() : defaultValue;
+    }
+
+    @Override
+    public Boolean nextBooleanValue()
+        throws IOException, JsonParseException
+    {
+        // two distinct cases; either got name and we know next type, or 'other'
+        if (_currToken == JsonToken.FIELD_NAME) { // mostly copied from '_nextAfterName'
+            _nameCopied = false;
+            JsonToken t = _nextToken;
+            _nextToken = null;
+            _currToken = t;
+            if (t == JsonToken.VALUE_TRUE) {
+                return Boolean.TRUE;
+            }
+            if (t == JsonToken.VALUE_FALSE) {
+                return Boolean.FALSE;
+            }
+            if (t == JsonToken.START_ARRAY) {
+                _parsingContext = _parsingContext.createChildArrayContext(_tokenInputRow, _tokenInputCol);
+            } else if (t == JsonToken.START_OBJECT) {
+                _parsingContext = _parsingContext.createChildObjectContext(_tokenInputRow, _tokenInputCol);
+            }
+            return null;
+        }
+        switch (nextToken()) {
+        case VALUE_TRUE:
+            return Boolean.TRUE;
+        case VALUE_FALSE:
+            return Boolean.FALSE;
+        }
+        return null;
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods, number parsing
+    /* (note: in 1.6 and prior, part of "Utf8NumericParser"
+    /**********************************************************
+     */
+
+    /**
+     * Initial parsing method for number values. It needs to be able
+     * to parse enough input to be able to determine whether the
+     * value is to be considered a simple integer value, or a more
+     * generic decimal value: latter of which needs to be expressed
+     * as a floating point number. The basic rule is that if the number
+     * has no fractional or exponential part, it is an integer; otherwise
+     * a floating point number.
+     *<p>
+     * Because much of input has to be processed in any case, no partial
+     * parsing is done: all input text will be stored for further
+     * processing. However, actual numeric value conversion will be
+     * deferred, since it is usually the most complicated and costliest
+     * part of processing.
+     */
+    protected final JsonToken parseNumberText(int c)
+        throws IOException, JsonParseException
+    {
+        char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+        int outPtr = 0;
+        boolean negative = (c == INT_MINUS);
+
+        // Need to prepend sign?
+        if (negative) {
+            outBuf[outPtr++] = '-';
+            // Must have something after sign too
+            if (_inputPtr >= _inputEnd) {
+                loadMoreGuaranteed();
+            }
+            c = (int) _inputBuffer[_inputPtr++] & 0xFF;
+            // Note: must be followed by a digit
+            if (c < INT_0 || c > INT_9) {
+                return _handleInvalidNumberStart(c, true);
+            }
+        }
+
+        // One special case: if first char is 0, must not be followed by a digit
+        if (c == INT_0) {
+            c = _verifyNoLeadingZeroes();
+        }
+        
+        // Ok: we can first just add digit we saw first:
+        outBuf[outPtr++] = (char) c;
+        int intLen = 1;
+
+        // And then figure out how far we can read without further checks:
+        int end = _inputPtr + outBuf.length;
+        if (end > _inputEnd) {
+            end = _inputEnd;
+        }
+
+        // With this, we have a nice and tight loop:
+        while (true) {
+            if (_inputPtr >= end) {
+                // Long enough to be split across boundary, so:
+                return _parserNumber2(outBuf, outPtr, negative, intLen);
+            }
+            c = (int) _inputBuffer[_inputPtr++] & 0xFF;
+            if (c < INT_0 || c > INT_9) {
+                break;
+            }
+            ++intLen;
+            outBuf[outPtr++] = (char) c;
+        }
+        if (c == '.' || c == 'e' || c == 'E') {
+            return _parseFloatText(outBuf, outPtr, c, negative, intLen);
+        }
+
+        --_inputPtr; // to push back trailing char (comma etc)
+        _textBuffer.setCurrentLength(outPtr);
+
+        // And there we have it!
+        return resetInt(negative, intLen);
+    }
+    
+    /**
+     * Method called to handle parsing when input is split across buffer boundary
+     * (or output is longer than segment used to store it)
+     */
+    private final JsonToken _parserNumber2(char[] outBuf, int outPtr, boolean negative,
+            int intPartLength)
+        throws IOException, JsonParseException
+    {
+        // Ok, parse the rest
+        while (true) {
+            if (_inputPtr >= _inputEnd && !loadMore()) {
+                _textBuffer.setCurrentLength(outPtr);
+                return resetInt(negative, intPartLength);
+            }
+            int c = (int) _inputBuffer[_inputPtr++] & 0xFF;
+            if (c > INT_9 || c < INT_0) {
+                if (c == '.' || c == 'e' || c == 'E') {
+                    return _parseFloatText(outBuf, outPtr, c, negative, intPartLength);
+                }
+                break;
+            }
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
+            outBuf[outPtr++] = (char) c;
+            ++intPartLength;
+        }
+        --_inputPtr; // to push back trailing char (comma etc)
+        _textBuffer.setCurrentLength(outPtr);
+
+        // And there we have it!
+        return resetInt(negative, intPartLength);
+        
+    }
+    
+    /**
+     * Method called when we have seen one zero, and want to ensure
+     * it is not followed by another
+     */
+    private final int _verifyNoLeadingZeroes()
+        throws IOException, JsonParseException
+    {
+        // Ok to have plain "0"
+        if (_inputPtr >= _inputEnd && !loadMore()) {
+            return INT_0;
+        }
+        int ch = _inputBuffer[_inputPtr] & 0xFF;
+        // if not followed by a number (probably '.'); return zero as is, to be included
+        if (ch < INT_0 || ch > INT_9) {
+            return INT_0;
+        }
+        // [JACKSON-358]: we may want to allow them, after all...
+        if (!isEnabled(Feature.ALLOW_NUMERIC_LEADING_ZEROS)) {
+            reportInvalidNumber("Leading zeroes not allowed");
+        }
+        // if so, just need to skip either all zeroes (if followed by number); or all but one (if non-number)
+        ++_inputPtr; // Leading zero to be skipped
+        if (ch == INT_0) {
+            while (_inputPtr < _inputEnd || loadMore()) {
+                ch = _inputBuffer[_inputPtr] & 0xFF;
+                if (ch < INT_0 || ch > INT_9) { // followed by non-number; retain one zero
+                    return INT_0;
+                }
+                ++_inputPtr; // skip previous zeroes
+                if (ch != INT_0) { // followed by other number; return 
+                    break;
+                }
+            }
+        }
+        return ch;
+    }
+    
+    private final JsonToken _parseFloatText(char[] outBuf, int outPtr, int c,
+            boolean negative, int integerPartLength)
+        throws IOException, JsonParseException
+    {
+        int fractLen = 0;
+        boolean eof = false;
+
+        // And then see if we get other parts
+        if (c == '.') { // yes, fraction
+            outBuf[outPtr++] = (char) c;
+
+            fract_loop:
+            while (true) {
+                if (_inputPtr >= _inputEnd && !loadMore()) {
+                    eof = true;
+                    break fract_loop;
+                }
+                c = (int) _inputBuffer[_inputPtr++] & 0xFF;
+                if (c < INT_0 || c > INT_9) {
+                    break fract_loop;
+                }
+                ++fractLen;
+                if (outPtr >= outBuf.length) {
+                    outBuf = _textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                outBuf[outPtr++] = (char) c;
+            }
+            // must be followed by sequence of ints, one minimum
+            if (fractLen == 0) {
+                reportUnexpectedNumberChar(c, "Decimal point not followed by a digit");
+            }
+        }
+
+        int expLen = 0;
+        if (c == 'e' || c == 'E') { // exponent?
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
+            outBuf[outPtr++] = (char) c;
+            // Not optional, can require that we get one more char
+            if (_inputPtr >= _inputEnd) {
+                loadMoreGuaranteed();
+            }
+            c = (int) _inputBuffer[_inputPtr++] & 0xFF;
+            // Sign indicator?
+            if (c == '-' || c == '+') {
+                if (outPtr >= outBuf.length) {
+                    outBuf = _textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                outBuf[outPtr++] = (char) c;
+                // Likewise, non optional:
+                if (_inputPtr >= _inputEnd) {
+                    loadMoreGuaranteed();
+                }
+                c = (int) _inputBuffer[_inputPtr++] & 0xFF;
+            }
+
+            exp_loop:
+            while (c <= INT_9 && c >= INT_0) {
+                ++expLen;
+                if (outPtr >= outBuf.length) {
+                    outBuf = _textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                outBuf[outPtr++] = (char) c;
+                if (_inputPtr >= _inputEnd && !loadMore()) {
+                    eof = true;
+                    break exp_loop;
+                }
+                c = (int) _inputBuffer[_inputPtr++] & 0xFF;
+            }
+            // must be followed by sequence of ints, one minimum
+            if (expLen == 0) {
+                reportUnexpectedNumberChar(c, "Exponent indicator not followed by a digit");
+            }
+        }
+
+        // Ok; unless we hit end-of-input, need to push last char read back
+        if (!eof) {
+            --_inputPtr;
+        }
+        _textBuffer.setCurrentLength(outPtr);
+
+        // And there we have it!
+        return resetFloat(negative, integerPartLength, fractLen, expLen);
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods, secondary parsing
+    /**********************************************************
+     */
+    
+    protected final Name _parseFieldName(int i)
+        throws IOException, JsonParseException
+    {
+        if (i != INT_QUOTE) {
+            return _handleUnusualFieldName(i);
+        }
+        // First: can we optimize out bounds checks?
+        if ((_inputPtr + 9) > _inputEnd) { // Need 8 chars, plus one trailing (quote)
+            return slowParseFieldName();
+        }
+
+        // If so, can also unroll loops nicely
+        /* 25-Nov-2008, tatu: This may seem weird, but here we do
+         *   NOT want to worry about UTF-8 decoding. Rather, we'll
+         *   assume that part is ok (if not it will get caught
+         *   later on), and just handle quotes and backslashes here.
+         */
+        final byte[] input = _inputBuffer;
+        final int[] codes = sInputCodesLatin1;
+
+        int q = input[_inputPtr++] & 0xFF;
+
+        if (codes[q] == 0) {
+            i = input[_inputPtr++] & 0xFF;
+            if (codes[i] == 0) {
+                q = (q << 8) | i;
+                i = input[_inputPtr++] & 0xFF;
+                if (codes[i] == 0) {
+                    q = (q << 8) | i;
+                    i = input[_inputPtr++] & 0xFF;
+                    if (codes[i] == 0) {
+                        q = (q << 8) | i;
+                        i = input[_inputPtr++] & 0xFF;
+                        if (codes[i] == 0) {
+                            _quad1 = q;
+                            return parseMediumFieldName(i, codes);
+                        }
+                        if (i == INT_QUOTE) { // one byte/char case or broken
+                            return findName(q, 4);
+                        }
+                        return parseFieldName(q, i, 4);
+                    }
+                    if (i == INT_QUOTE) { // one byte/char case or broken
+                        return findName(q, 3);
+                    }
+                    return parseFieldName(q, i, 3);
+                }                
+                if (i == INT_QUOTE) { // one byte/char case or broken
+                    return findName(q, 2);
+                }
+                return parseFieldName(q, i, 2);
+            }
+            if (i == INT_QUOTE) { // one byte/char case or broken
+                return findName(q, 1);
+            }
+            return parseFieldName(q, i, 1);
+        }     
+        if (q == INT_QUOTE) { // special case, ""
+            return BytesToNameCanonicalizer.getEmptyName();
+        }
+        return parseFieldName(0, q, 0); // quoting or invalid char
+    }
+
+    protected final Name parseMediumFieldName(int q2, final int[] codes)
+        throws IOException, JsonParseException
+    {
+        // Ok, got 5 name bytes so far
+        int i = _inputBuffer[_inputPtr++] & 0xFF;
+        if (codes[i] != 0) {
+            if (i == INT_QUOTE) { // 5 bytes
+                return findName(_quad1, q2, 1);
+            }
+            return parseFieldName(_quad1, q2, i, 1); // quoting or invalid char
+        }
+        q2 = (q2 << 8) | i;
+        i = _inputBuffer[_inputPtr++] & 0xFF;
+        if (codes[i] != 0) {
+            if (i == INT_QUOTE) { // 6 bytes
+                return findName(_quad1, q2, 2);
+            }
+            return parseFieldName(_quad1, q2, i, 2);
+        }
+        q2 = (q2 << 8) | i;
+        i = _inputBuffer[_inputPtr++] & 0xFF;
+        if (codes[i] != 0) {
+            if (i == INT_QUOTE) { // 7 bytes
+                return findName(_quad1, q2, 3);
+            }
+            return parseFieldName(_quad1, q2, i, 3);
+        }
+        q2 = (q2 << 8) | i;
+        i = _inputBuffer[_inputPtr++] & 0xFF;
+        if (codes[i] != 0) {
+            if (i == INT_QUOTE) { // 8 bytes
+                return findName(_quad1, q2, 4);
+            }
+            return parseFieldName(_quad1, q2, i, 4);
+        }
+        _quadBuffer[0] = _quad1;
+        _quadBuffer[1] = q2;
+        return parseLongFieldName(i);
+    }
+
+    protected Name parseLongFieldName(int q)
+        throws IOException, JsonParseException
+    {
+        // As explained above, will ignore UTF-8 encoding at this point
+        final int[] codes = sInputCodesLatin1;
+        int qlen = 2;
+
+        while (true) {
+            /* Let's offline if we hit buffer boundary (otherwise would
+             * need to [try to] align input, which is bit complicated
+             * and may not always be possible)
+             */
+            if ((_inputEnd - _inputPtr) < 4) {
+                return parseEscapedFieldName(_quadBuffer, qlen, 0, q, 0);
+            }
+            // Otherwise can skip boundary checks for 4 bytes in loop
+
+            int i = _inputBuffer[_inputPtr++] & 0xFF;
+            if (codes[i] != 0) {
+                if (i == INT_QUOTE) {
+                    return findName(_quadBuffer, qlen, q, 1);
+                }
+                return parseEscapedFieldName(_quadBuffer, qlen, q, i, 1);
+            }
+
+            q = (q << 8) | i;
+            i = _inputBuffer[_inputPtr++] & 0xFF;
+            if (codes[i] != 0) {
+                if (i == INT_QUOTE) {
+                    return findName(_quadBuffer, qlen, q, 2);
+                }
+                return parseEscapedFieldName(_quadBuffer, qlen, q, i, 2);
+            }
+
+            q = (q << 8) | i;
+            i = _inputBuffer[_inputPtr++] & 0xFF;
+            if (codes[i] != 0) {
+                if (i == INT_QUOTE) {
+                    return findName(_quadBuffer, qlen, q, 3);
+                }
+                return parseEscapedFieldName(_quadBuffer, qlen, q, i, 3);
+            }
+
+            q = (q << 8) | i;
+            i = _inputBuffer[_inputPtr++] & 0xFF;
+            if (codes[i] != 0) {
+                if (i == INT_QUOTE) {
+                    return findName(_quadBuffer, qlen, q, 4);
+                }
+                return parseEscapedFieldName(_quadBuffer, qlen, q, i, 4);
+            }
+
+            // Nope, no end in sight. Need to grow quad array etc
+            if (qlen >= _quadBuffer.length) {
+                _quadBuffer = growArrayBy(_quadBuffer, qlen);
+            }
+            _quadBuffer[qlen++] = q;
+            q = i;
+        }
+    }
+
+    /**
+     * Method called when not even first 8 bytes are guaranteed
+     * to come consequtively. Happens rarely, so this is offlined;
+     * plus we'll also do full checks for escaping etc.
+     */
+    protected Name slowParseFieldName()
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            if (!loadMore()) {
+                _reportInvalidEOF(": was expecting closing '\"' for name");
+            }
+        }
+        int i = _inputBuffer[_inputPtr++] & 0xFF;
+        if (i == INT_QUOTE) { // special case, ""
+            return BytesToNameCanonicalizer.getEmptyName();
+        }
+        return parseEscapedFieldName(_quadBuffer, 0, 0, i, 0);
+    }
+
+    private final Name parseFieldName(int q1, int ch, int lastQuadBytes)
+        throws IOException, JsonParseException
+    {
+        return parseEscapedFieldName(_quadBuffer, 0, q1, ch, lastQuadBytes);
+    }
+
+    private final Name parseFieldName(int q1, int q2, int ch, int lastQuadBytes)
+        throws IOException, JsonParseException
+    {
+        _quadBuffer[0] = q1;
+        return parseEscapedFieldName(_quadBuffer, 1, q2, ch, lastQuadBytes);
+    }
+
+    /**
+     * Slower parsing method which is generally branched to when
+     * an escape sequence is detected (or alternatively for long
+     * names, or ones crossing input buffer boundary). In any case,
+     * needs to be able to handle more exceptional cases, gets
+     * slower, and hance is offlined to a separate method.
+     */
+    protected Name parseEscapedFieldName(int[] quads, int qlen, int currQuad, int ch,
+                                         int currQuadBytes)
+        throws IOException, JsonParseException
+    {
+        /* 25-Nov-2008, tatu: This may seem weird, but here we do
+         *   NOT want to worry about UTF-8 decoding. Rather, we'll
+         *   assume that part is ok (if not it will get caught
+         *   later on), and just handle quotes and backslashes here.
+         */
+        final int[] codes = sInputCodesLatin1;
+
+        while (true) {
+            if (codes[ch] != 0) {
+                if (ch == INT_QUOTE) { // we are done
+                    break;
+                }
+                // Unquoted white space?
+                if (ch != INT_BACKSLASH) {
+                    // As per [JACKSON-208], call can now return:
+                    _throwUnquotedSpace(ch, "name");
+                } else {
+                    // Nope, escape sequence
+                    ch = _decodeEscaped();
+                }
+                /* Oh crap. May need to UTF-8 (re-)encode it, if it's
+                 * beyond 7-bit ascii. Gets pretty messy.
+                 * If this happens often, may want to use different name
+                 * canonicalization to avoid these hits.
+                 */
+                if (ch > 127) {
+                    // Ok, we'll need room for first byte right away
+                    if (currQuadBytes >= 4) {
+                        if (qlen >= quads.length) {
+                            _quadBuffer = quads = growArrayBy(quads, quads.length);
+                        }
+                        quads[qlen++] = currQuad;
+                        currQuad = 0;
+                        currQuadBytes = 0;
+                    }
+                    if (ch < 0x800) { // 2-byte
+                        currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
+                        ++currQuadBytes;
+                        // Second byte gets output below:
+                    } else { // 3 bytes; no need to worry about surrogates here
+                        currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
+                        ++currQuadBytes;
+                        // need room for middle byte?
+                        if (currQuadBytes >= 4) {
+                            if (qlen >= quads.length) {
+                                _quadBuffer = quads = growArrayBy(quads, quads.length);
+                            }
+                            quads[qlen++] = currQuad;
+                            currQuad = 0;
+                            currQuadBytes = 0;
+                        }
+                        currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
+                        ++currQuadBytes;
+                    }
+                    // And same last byte in both cases, gets output below:
+                    ch = 0x80 | (ch & 0x3f);
+                }
+            }
+            // Ok, we have one more byte to add at any rate:
+            if (currQuadBytes < 4) {
+                ++currQuadBytes;
+                currQuad = (currQuad << 8) | ch;
+            } else {
+                if (qlen >= quads.length) {
+                    _quadBuffer = quads = growArrayBy(quads, quads.length);
+                }
+                quads[qlen++] = currQuad;
+                currQuad = ch;
+                currQuadBytes = 1;
+            }
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOF(" in field name");
+                }
+            }
+            ch = _inputBuffer[_inputPtr++] & 0xFF;
+        }
+
+        if (currQuadBytes > 0) {
+            if (qlen >= quads.length) {
+                _quadBuffer = quads = growArrayBy(quads, quads.length);
+            }
+            quads[qlen++] = currQuad;
+        }
+        Name name = _symbols.findName(quads, qlen);
+        if (name == null) {
+            name = addName(quads, qlen, currQuadBytes);
+        }
+        return name;
+    }
+
+    /**
+     * Method called when we see non-white space character other
+     * than double quote, when expecting a field name.
+     * In standard mode will just throw an expection; but
+     * in non-standard modes may be able to parse name.
+     */
+    protected final Name _handleUnusualFieldName(int ch)
+        throws IOException, JsonParseException
+    {
+        // [JACKSON-173]: allow single quotes
+        if (ch == INT_APOSTROPHE && isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
+            return _parseApostropheFieldName();
+        }
+        // [JACKSON-69]: allow unquoted names if feature enabled:
+        if (!isEnabled(Feature.ALLOW_UNQUOTED_FIELD_NAMES)) {
+            _reportUnexpectedChar(ch, "was expecting double-quote to start field name");
+        }
+        /* Also: note that although we use a different table here,
+         * it does NOT handle UTF-8 decoding. It'll just pass those
+         * high-bit codes as acceptable for later decoding.
+         */
+        final int[] codes = CharTypes.getInputCodeUtf8JsNames();
+        // Also: must start with a valid character...
+        if (codes[ch] != 0) {
+            _reportUnexpectedChar(ch, "was expecting either valid name character (for unquoted name) or double-quote (for quoted) to start field name");
+        }
+
+        /* Ok, now; instead of ultra-optimizing parsing here (as with
+         * regular JSON names), let's just use the generic "slow"
+         * variant. Can measure its impact later on if need be
+         */
+        int[] quads = _quadBuffer;
+        int qlen = 0;
+        int currQuad = 0;
+        int currQuadBytes = 0;
+
+        while (true) {
+            // Ok, we have one more byte to add at any rate:
+            if (currQuadBytes < 4) {
+                ++currQuadBytes;
+                currQuad = (currQuad << 8) | ch;
+            } else {
+                if (qlen >= quads.length) {
+                    _quadBuffer = quads = growArrayBy(quads, quads.length);
+                }
+                quads[qlen++] = currQuad;
+                currQuad = ch;
+                currQuadBytes = 1;
+            }
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOF(" in field name");
+                }
+            }
+            ch = _inputBuffer[_inputPtr] & 0xFF;
+            if (codes[ch] != 0) {
+                break;
+            }
+            ++_inputPtr;
+        }
+
+        if (currQuadBytes > 0) {
+            if (qlen >= quads.length) {
+                _quadBuffer = quads = growArrayBy(quads, quads.length);
+            }
+            quads[qlen++] = currQuad;
+        }
+        Name name = _symbols.findName(quads, qlen);
+        if (name == null) {
+            name = addName(quads, qlen, currQuadBytes);
+        }
+        return name;
+    }
+
+    /* Parsing to support [JACKSON-173]. Plenty of duplicated code;
+     * main reason being to try to avoid slowing down fast path
+     * for valid JSON -- more alternatives, more code, generally
+     * bit slower execution.
+     */
+    protected final Name _parseApostropheFieldName()
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            if (!loadMore()) {
+                _reportInvalidEOF(": was expecting closing '\'' for name");
+            }
+        }
+        int ch = _inputBuffer[_inputPtr++] & 0xFF;
+        if (ch == INT_APOSTROPHE) { // special case, ''
+            return BytesToNameCanonicalizer.getEmptyName();
+        }
+        int[] quads = _quadBuffer;
+        int qlen = 0;
+        int currQuad = 0;
+        int currQuadBytes = 0;
+
+        // Copied from parseEscapedFieldName, with minor mods:
+
+        final int[] codes = sInputCodesLatin1;
+
+        while (true) {
+            if (ch == INT_APOSTROPHE) {
+                break;
+            }
+            // additional check to skip handling of double-quotes
+            if (ch != INT_QUOTE && codes[ch] != 0) {
+                if (ch != INT_BACKSLASH) {
+                    // Unquoted white space?
+                    // As per [JACKSON-208], call can now return:
+                    _throwUnquotedSpace(ch, "name");
+                } else {
+                    // Nope, escape sequence
+                    ch = _decodeEscaped();
+                }
+                /* Oh crap. May need to UTF-8 (re-)encode it, if it's
+                 * beyond 7-bit ascii. Gets pretty messy.
+                 * If this happens often, may want to use different name
+                 * canonicalization to avoid these hits.
+                 */
+                if (ch > 127) {
+                    // Ok, we'll need room for first byte right away
+                    if (currQuadBytes >= 4) {
+                        if (qlen >= quads.length) {
+                            _quadBuffer = quads = growArrayBy(quads, quads.length);
+                        }
+                        quads[qlen++] = currQuad;
+                        currQuad = 0;
+                        currQuadBytes = 0;
+                    }
+                    if (ch < 0x800) { // 2-byte
+                        currQuad = (currQuad << 8) | (0xc0 | (ch >> 6));
+                        ++currQuadBytes;
+                        // Second byte gets output below:
+                    } else { // 3 bytes; no need to worry about surrogates here
+                        currQuad = (currQuad << 8) | (0xe0 | (ch >> 12));
+                        ++currQuadBytes;
+                        // need room for middle byte?
+                        if (currQuadBytes >= 4) {
+                            if (qlen >= quads.length) {
+                                _quadBuffer = quads = growArrayBy(quads, quads.length);
+                            }
+                            quads[qlen++] = currQuad;
+                            currQuad = 0;
+                            currQuadBytes = 0;
+                        }
+                        currQuad = (currQuad << 8) | (0x80 | ((ch >> 6) & 0x3f));
+                        ++currQuadBytes;
+                    }
+                    // And same last byte in both cases, gets output below:
+                    ch = 0x80 | (ch & 0x3f);
+                }
+            }
+            // Ok, we have one more byte to add at any rate:
+            if (currQuadBytes < 4) {
+                ++currQuadBytes;
+                currQuad = (currQuad << 8) | ch;
+            } else {
+                if (qlen >= quads.length) {
+                    _quadBuffer = quads = growArrayBy(quads, quads.length);
+                }
+                quads[qlen++] = currQuad;
+                currQuad = ch;
+                currQuadBytes = 1;
+            }
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOF(" in field name");
+                }
+            }
+            ch = _inputBuffer[_inputPtr++] & 0xFF;
+        }
+
+        if (currQuadBytes > 0) {
+            if (qlen >= quads.length) {
+                _quadBuffer = quads = growArrayBy(quads, quads.length);
+            }
+            quads[qlen++] = currQuad;
+        }
+        Name name = _symbols.findName(quads, qlen);
+        if (name == null) {
+            name = addName(quads, qlen, currQuadBytes);
+        }
+        return name;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, symbol (name) handling
+    /**********************************************************
+     */
+
+    private final Name findName(int q1, int lastQuadBytes)
+        throws JsonParseException
+    {
+        // Usually we'll find it from the canonical symbol table already
+        Name name = _symbols.findName(q1);
+        if (name != null) {
+            return name;
+        }
+        // If not, more work. We'll need add stuff to buffer
+        _quadBuffer[0] = q1;
+        return addName(_quadBuffer, 1, lastQuadBytes);
+    }
+
+    private final Name findName(int q1, int q2, int lastQuadBytes)
+        throws JsonParseException
+    {
+        // Usually we'll find it from the canonical symbol table already
+        Name 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 addName(_quadBuffer, 2, lastQuadBytes);
+    }
+
+    private final Name findName(int[] quads, int qlen, int lastQuad, int lastQuadBytes)
+        throws JsonParseException
+    {
+        if (qlen >= quads.length) {
+            _quadBuffer = quads = growArrayBy(quads, quads.length);
+        }
+        quads[qlen++] = lastQuad;
+        Name name = _symbols.findName(quads, qlen);
+        if (name == null) {
+            return addName(quads, qlen, lastQuadBytes);
+        }
+        return name;
+    }
+
+    /**
+     * This is the main workhorse method used when we take a symbol
+     * table miss. It needs to demultiplex individual bytes, decode
+     * multi-byte chars (if any), and then construct Name instance
+     * and add it to the symbol table.
+     */
+    private final Name addName(int[] quads, int qlen, int lastQuadBytes)
+        throws JsonParseException
+    {
+        /* Ok: must decode UTF-8 chars. No other validation is
+         * needed, since unescaping has been done earlier as necessary
+         * (as well as error reporting for unescaped control chars)
+         */
+        // 4 bytes per quad, except last one maybe less
+        int byteLen = (qlen << 2) - 4 + lastQuadBytes;
+
+        /* And last one is not correctly aligned (leading zero bytes instead
+         * need to shift a bit, instead of trailing). Only need to shift it
+         * for UTF-8 decoding; need revert for storage (since key will not
+         * be aligned, to optimize lookup speed)
+         */
+        int lastQuad;
+
+        if (lastQuadBytes < 4) {
+            lastQuad = quads[qlen-1];
+            // 8/16/24 bit left shift
+            quads[qlen-1] = (lastQuad << ((4 - lastQuadBytes) << 3));
+        } else {
+            lastQuad = 0;
+        }
+
+        // Need some working space, TextBuffer works well:
+        char[] cbuf = _textBuffer.emptyAndGetCurrentSegment();
+        int cix = 0;
+
+        for (int ix = 0; ix < byteLen; ) {
+            int ch = quads[ix >> 2]; // current quad, need to shift+mask
+            int byteIx = (ix & 3);
+            ch = (ch >> ((3 - byteIx) << 3)) & 0xFF;
+            ++ix;
+
+            if (ch > 127) { // multi-byte
+                int needed;
+                if ((ch & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
+                    ch &= 0x1F;
+                    needed = 1;
+                } else if ((ch & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
+                    ch &= 0x0F;
+                    needed = 2;
+                } else if ((ch & 0xF8) == 0xF0) { // 4 bytes; double-char with surrogates and all...
+                    ch &= 0x07;
+                    needed = 3;
+                } else { // 5- and 6-byte chars not valid xml chars
+                    _reportInvalidInitial(ch);
+                    needed = ch = 1; // never really gets this far
+                }
+                if ((ix + needed) > byteLen) {
+                    _reportInvalidEOF(" in field name");
+                }
+                
+                // Ok, always need at least one more:
+                int ch2 = quads[ix >> 2]; // current quad, need to shift+mask
+                byteIx = (ix & 3);
+                ch2 = (ch2 >> ((3 - byteIx) << 3));
+                ++ix;
+                
+                if ((ch2 & 0xC0) != 0x080) {
+                    _reportInvalidOther(ch2);
+                }
+                ch = (ch << 6) | (ch2 & 0x3F);
+                if (needed > 1) {
+                    ch2 = quads[ix >> 2];
+                    byteIx = (ix & 3);
+                    ch2 = (ch2 >> ((3 - byteIx) << 3));
+                    ++ix;
+                    
+                    if ((ch2 & 0xC0) != 0x080) {
+                        _reportInvalidOther(ch2);
+                    }
+                    ch = (ch << 6) | (ch2 & 0x3F);
+                    if (needed > 2) { // 4 bytes? (need surrogates on output)
+                        ch2 = quads[ix >> 2];
+                        byteIx = (ix & 3);
+                        ch2 = (ch2 >> ((3 - byteIx) << 3));
+                        ++ix;
+                        if ((ch2 & 0xC0) != 0x080) {
+                            _reportInvalidOther(ch2 & 0xFF);
+                        }
+                        ch = (ch << 6) | (ch2 & 0x3F);
+                    }
+                }
+                if (needed > 2) { // surrogate pair? once again, let's output one here, one later on
+                    ch -= 0x10000; // to normalize it starting with 0x0
+                    if (cix >= cbuf.length) {
+                        cbuf = _textBuffer.expandCurrentSegment();
+                    }
+                    cbuf[cix++] = (char) (0xD800 + (ch >> 10));
+                    ch = 0xDC00 | (ch & 0x03FF);
+                }
+            }
+            if (cix >= cbuf.length) {
+                cbuf = _textBuffer.expandCurrentSegment();
+            }
+            cbuf[cix++] = (char) ch;
+        }
+
+        // Ok. Now we have the character array, and can construct the String
+        String baseName = new String(cbuf, 0, cix);
+        // And finally, un-align if necessary
+        if (lastQuadBytes < 4) {
+            quads[qlen-1] = lastQuad;
+        }
+        return _symbols.addName(baseName, quads, qlen);
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, String value parsing
+    /**********************************************************
+     */
+
+    @Override
+    protected void _finishString()
+        throws IOException, JsonParseException
+    {
+        // First, single tight loop for ASCII content, not split across input buffer boundary:        
+        int ptr = _inputPtr;
+        if (ptr >= _inputEnd) {
+            loadMoreGuaranteed();
+            ptr = _inputPtr;
+        }
+        int outPtr = 0;
+        char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+        final int[] codes = sInputCodesUtf8;
+
+        final int max = Math.min(_inputEnd, (ptr + outBuf.length));
+        final byte[] inputBuffer = _inputBuffer;
+        while (ptr < max) {
+            int c = (int) inputBuffer[ptr] & 0xFF;
+            if (codes[c] != 0) {
+                if (c == INT_QUOTE) {
+                    _inputPtr = ptr+1;
+                    _textBuffer.setCurrentLength(outPtr);
+                    return;
+                }
+                break;
+            }
+            ++ptr;
+            outBuf[outPtr++] = (char) c;
+        }
+        _inputPtr = ptr;
+        _finishString2(outBuf, outPtr);
+    }
+
+    private final void _finishString2(char[] outBuf, int outPtr)
+        throws IOException, JsonParseException
+    {
+        int c;
+
+        // Here we do want to do full decoding, hence:
+        final int[] codes = sInputCodesUtf8;
+        final byte[] inputBuffer = _inputBuffer;
+
+        main_loop:
+        while (true) {
+            // Then the tight ASCII non-funny-char loop:
+            ascii_loop:
+            while (true) {
+                int ptr = _inputPtr;
+                if (ptr >= _inputEnd) {
+                    loadMoreGuaranteed();
+                    ptr = _inputPtr;
+                }
+                if (outPtr >= outBuf.length) {
+                    outBuf = _textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                final int max = Math.min(_inputEnd, (ptr + (outBuf.length - outPtr)));
+                while (ptr < max) {
+                    c = (int) inputBuffer[ptr++] & 0xFF;
+                    if (codes[c] != 0) {
+                        _inputPtr = ptr;
+                        break ascii_loop;
+                    }
+                    outBuf[outPtr++] = (char) c;
+                }
+                _inputPtr = ptr;
+            }
+            // Ok: end marker, escape or multi-byte?
+            if (c == INT_QUOTE) {
+                break main_loop;
+            }
+
+            switch (codes[c]) {
+            case 1: // backslash
+                c = _decodeEscaped();
+                break;
+            case 2: // 2-byte UTF
+                c = _decodeUtf8_2(c);
+                break;
+            case 3: // 3-byte UTF
+                if ((_inputEnd - _inputPtr) >= 2) {
+                    c = _decodeUtf8_3fast(c);
+                } else {
+                    c = _decodeUtf8_3(c);
+                }
+                break;
+            case 4: // 4-byte UTF
+                c = _decodeUtf8_4(c);
+                // Let's add first part right away:
+                outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
+                if (outPtr >= outBuf.length) {
+                    outBuf = _textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                c = 0xDC00 | (c & 0x3FF);
+                // And let the other char output down below
+                break;
+            default:
+                if (c < INT_SPACE) {
+                    // As per [JACKSON-208], call can now return:
+                    _throwUnquotedSpace(c, "string value");
+                } else {
+                    // Is this good enough error message?
+                    _reportInvalidChar(c);
+                }
+            }
+            // Need more room?
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
+            // Ok, let's add char to output:
+            outBuf[outPtr++] = (char) c;
+        }
+        _textBuffer.setCurrentLength(outPtr);
+    }
+
+    /**
+     * Method called to skim through rest of unparsed String value,
+     * if it is not needed. This can be done bit faster if contents
+     * need not be stored for future access.
+     */
+    protected void _skipString()
+        throws IOException, JsonParseException
+    {
+        _tokenIncomplete = false;
+
+        // Need to be fully UTF-8 aware here:
+        final int[] codes = sInputCodesUtf8;
+        final byte[] inputBuffer = _inputBuffer;
+
+        main_loop:
+        while (true) {
+            int c;
+
+            ascii_loop:
+            while (true) {
+                int ptr = _inputPtr;
+                int max = _inputEnd;
+                if (ptr >= max) {
+                    loadMoreGuaranteed();
+                    ptr = _inputPtr;
+                    max = _inputEnd;
+                }
+                while (ptr < max) {
+                    c = (int) inputBuffer[ptr++] & 0xFF;
+                    if (codes[c] != 0) {
+                        _inputPtr = ptr;
+                        break ascii_loop;
+                    }
+                }
+                _inputPtr = ptr;
+            }
+            // Ok: end marker, escape or multi-byte?
+            if (c == INT_QUOTE) {
+                break main_loop;
+            }
+            
+            switch (codes[c]) {
+            case 1: // backslash
+                _decodeEscaped();
+                break;
+            case 2: // 2-byte UTF
+                _skipUtf8_2(c);
+                break;
+            case 3: // 3-byte UTF
+                _skipUtf8_3(c);
+                break;
+            case 4: // 4-byte UTF
+                _skipUtf8_4(c);
+                break;
+            default:
+                if (c < INT_SPACE) {
+                    // As per [JACKSON-208], call can now return:
+                    _throwUnquotedSpace(c, "string value");
+                } else {
+                    // Is this good enough error message?
+                    _reportInvalidChar(c);
+                }
+            }
+        }
+    }
+
+    /**
+     * Method for handling cases where first non-space character
+     * of an expected value token is not legal for standard JSON content.
+     *
+     * @since 1.3
+     */
+    protected JsonToken _handleUnexpectedValue(int c)
+        throws IOException, JsonParseException
+    {
+        // Most likely an error, unless we are to allow single-quote-strings
+        switch (c) {
+        case '\'':
+            if (isEnabled(Feature.ALLOW_SINGLE_QUOTES)) {
+                return _handleApostropheValue();
+            }
+            break;
+        case 'N':
+            _matchToken("NaN", 1);
+            if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
+                return resetAsNaN("NaN", Double.NaN);
+            }
+            _reportError("Non-standard token 'NaN': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
+            break;
+        case '+': // note: '-' is taken as number
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOFInValue();
+                }
+            }
+            return _handleInvalidNumberStart(_inputBuffer[_inputPtr++] & 0xFF, false);
+        }
+
+        _reportUnexpectedChar(c, "expected a valid value (number, String, array, object, 'true', 'false' or 'null')");
+        return null;
+    }
+    
+    protected JsonToken _handleApostropheValue()
+        throws IOException, JsonParseException
+    {
+        int c = 0;
+        // Otherwise almost verbatim copy of _finishString()
+        int outPtr = 0;
+        char[] outBuf = _textBuffer.emptyAndGetCurrentSegment();
+
+        // Here we do want to do full decoding, hence:
+        final int[] codes = sInputCodesUtf8;
+        final byte[] inputBuffer = _inputBuffer;
+
+        main_loop:
+        while (true) {
+            // Then the tight ascii non-funny-char loop:
+            ascii_loop:
+            while (true) {
+                if (_inputPtr >= _inputEnd) {
+                    loadMoreGuaranteed();
+                }
+                if (outPtr >= outBuf.length) {
+                    outBuf = _textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                int max = _inputEnd;
+                {
+                    int max2 = _inputPtr + (outBuf.length - outPtr);
+                    if (max2 < max) {
+                        max = max2;
+                    }
+                }
+                while (_inputPtr < max) {
+                    c = (int) inputBuffer[_inputPtr++] & 0xFF;
+                    if (c == INT_APOSTROPHE || codes[c] != 0) {
+                        break ascii_loop;
+                    }
+                    outBuf[outPtr++] = (char) c;
+                }
+            }
+
+            // Ok: end marker, escape or multi-byte?
+            if (c == INT_APOSTROPHE) {
+                break main_loop;
+            }
+
+            switch (codes[c]) {
+            case 1: // backslash
+                if (c != INT_QUOTE) { // marked as special, isn't here
+                    c = _decodeEscaped();
+                }
+                break;
+            case 2: // 2-byte UTF
+                c = _decodeUtf8_2(c);
+                break;
+            case 3: // 3-byte UTF
+                if ((_inputEnd - _inputPtr) >= 2) {
+                    c = _decodeUtf8_3fast(c);
+                } else {
+                    c = _decodeUtf8_3(c);
+                }
+                break;
+            case 4: // 4-byte UTF
+                c = _decodeUtf8_4(c);
+                // Let's add first part right away:
+                outBuf[outPtr++] = (char) (0xD800 | (c >> 10));
+                if (outPtr >= outBuf.length) {
+                    outBuf = _textBuffer.finishCurrentSegment();
+                    outPtr = 0;
+                }
+                c = 0xDC00 | (c & 0x3FF);
+                // And let the other char output down below
+                break;
+            default:
+                if (c < INT_SPACE) {
+                    _throwUnquotedSpace(c, "string value");
+                }
+                // Is this good enough error message?
+                _reportInvalidChar(c);
+            }
+            // Need more room?
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
+            // Ok, let's add char to output:
+            outBuf[outPtr++] = (char) c;
+        }
+        _textBuffer.setCurrentLength(outPtr);
+
+        return JsonToken.VALUE_STRING;
+    }
+
+    /**
+     * Method called if expected numeric value (due to leading sign) does not
+     * look like a number
+     */
+    protected JsonToken _handleInvalidNumberStart(int ch, boolean negative)
+        throws IOException, JsonParseException
+    {
+        if (ch == 'I') {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOFInValue();
+                }
+            }
+            ch = _inputBuffer[_inputPtr++];
+            if (ch == 'N') {
+                String match = negative ? "-INF" :"+INF";
+                _matchToken(match, 3);
+                if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
+                    return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
+                }
+                _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
+            } else if (ch == 'n') {
+                String match = negative ? "-Infinity" :"+Infinity";
+                _matchToken(match, 3);
+                if (isEnabled(Feature.ALLOW_NON_NUMERIC_NUMBERS)) {
+                    return resetAsNaN(match, negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY);
+                }
+                _reportError("Non-standard token '"+match+"': enable JsonParser.Feature.ALLOW_NON_NUMERIC_NUMBERS to allow");
+            }
+        }
+        reportUnexpectedNumberChar(ch, "expected digit (0-9) to follow minus sign, for valid numeric value");
+        return null;
+    }
+
+    protected final void _matchToken(String matchStr, int i)
+        throws IOException, JsonParseException
+    {
+        final int len = matchStr.length();
+    
+        do {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOF(" in a value");
+                }
+            }
+            if (_inputBuffer[_inputPtr] != matchStr.charAt(i)) {
+                _reportInvalidToken(matchStr.substring(0, i), "'null', 'true', 'false' or NaN");
+            }
+            ++_inputPtr;
+        } while (++i < len);
+    
+        // but let's also ensure we either get EOF, or non-alphanum char...
+        if (_inputPtr >= _inputEnd) {
+            if (!loadMore()) {
+                return;
+            }
+        }
+        int ch = _inputBuffer[_inputPtr] & 0xFF;
+        if (ch < '0' || ch == ']' || ch == '}') { // expected/allowed chars
+            return;
+        }
+        // but actually only alphanums are problematic
+        char c = (char) _decodeCharForError(ch);
+        if (Character.isJavaIdentifierPart(c)) {
+            ++_inputPtr;
+            _reportInvalidToken(matchStr.substring(0, i), "'null', 'true', 'false' or NaN");
+        }
+    }
+    
+    protected void _reportInvalidToken(String matchedPart, String msg)
+        throws IOException, JsonParseException
+    {
+        StringBuilder sb = new StringBuilder(matchedPart);
+        /* Let's just try to find what appears to be the token, using
+         * regular Java identifier character rules. It's just a heuristic,
+         * nothing fancy here (nor fast).
+         */
+        while (true) {
+            if (_inputPtr >= _inputEnd && !loadMore()) {
+                break;
+            }
+            int i = (int) _inputBuffer[_inputPtr++];
+            char c = (char) _decodeCharForError(i);
+            if (!Character.isJavaIdentifierPart(c)) {
+                break;
+            }
+            ++_inputPtr;
+            sb.append(c);
+        }
+        _reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg);
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods, ws skipping, escape/unescape
+    /**********************************************************
+     */
+
+    private final int _skipWS()
+        throws IOException, JsonParseException
+    {
+        while (_inputPtr < _inputEnd || loadMore()) {
+            int i = _inputBuffer[_inputPtr++] & 0xFF;
+            if (i > INT_SPACE) {
+                if (i != INT_SLASH) {
+                    return i;
+                }
+                _skipComment();
+            } else if (i != INT_SPACE) {
+                if (i == INT_LF) {
+                    _skipLF();
+                } else if (i == INT_CR) {
+                    _skipCR();
+                } else if (i != INT_TAB) {
+                    _throwInvalidSpace(i);
+                }
+            }
+        }
+        throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries");
+    }
+
+    private final int _skipWSOrEnd()
+        throws IOException, JsonParseException
+    {
+        while ((_inputPtr < _inputEnd) || loadMore()) {
+            int i = _inputBuffer[_inputPtr++] & 0xFF;
+            if (i > INT_SPACE) {
+                if (i != INT_SLASH) {
+                    return i;
+                }
+                _skipComment();
+            } else if (i != INT_SPACE) {
+                if (i == INT_LF) {
+                    _skipLF();
+                } else if (i == INT_CR) {
+                    _skipCR();
+                } else if (i != INT_TAB) {
+                    _throwInvalidSpace(i);
+                }
+            }
+        }
+        // We ran out of input...
+        _handleEOF();
+        return -1;
+    }
+
+    /**
+     * Helper method for matching and skipping a colon character,
+     * optionally surrounded by white space
+     * 
+     * @since 1.9
+     */
+    private final int _skipColon()
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        // first fast case: we just got a colon without white space:
+        int i = _inputBuffer[_inputPtr++];
+        if (i == INT_COLON) {
+            if (_inputPtr < _inputEnd) {
+                i = _inputBuffer[_inputPtr] & 0xFF;
+                if (i > INT_SPACE && i != INT_SLASH) {
+                    ++_inputPtr;
+                    return i;
+                }
+            }
+        } else {
+            // need to skip potential leading space
+            i &= 0xFF;
+
+            space_loop:
+            while (true) {
+                switch (i) {
+                case INT_SPACE:
+                case INT_TAB:
+                case INT_CR:
+                    _skipCR();
+                    break;
+                case INT_LF:
+                    _skipLF();
+                    break;
+                case INT_SLASH:
+                    _skipComment();
+                    break;
+                default:
+                    if (i < INT_SPACE) {
+                        _throwInvalidSpace(i);
+                    }
+                    break space_loop;
+                }
+            }
+            if (_inputPtr >= _inputEnd) {
+                loadMoreGuaranteed();
+            }
+            i = _inputBuffer[_inputPtr++] & 0xFF;
+            if (i != INT_COLON) {
+                _reportUnexpectedChar(i, "was expecting a colon to separate field name and value");
+            }
+        }
+
+            // either way, found colon, skip through trailing WS
+        while (_inputPtr < _inputEnd || loadMore()) {
+            i = _inputBuffer[_inputPtr++] & 0xFF;
+            if (i > INT_SPACE) {
+                if (i != INT_SLASH) {
+                    return i;
+                }
+                _skipComment();
+            } else if (i != INT_SPACE) {
+                if (i == INT_LF) {
+                    _skipLF();
+                } else if (i == INT_CR) {
+                    _skipCR();
+                } else if (i != INT_TAB) {
+                    _throwInvalidSpace(i);
+                }
+            }
+        }
+        throw _constructError("Unexpected end-of-input within/between "+_parsingContext.getTypeDesc()+" entries");
+    }
+    
+    private final void _skipComment()
+        throws IOException, JsonParseException
+    {
+        if (!isEnabled(Feature.ALLOW_COMMENTS)) {
+            _reportUnexpectedChar('/', "maybe a (non-standard) comment? (not recognized as one since Feature 'ALLOW_COMMENTS' not enabled for parser)");
+        }
+        // First: check which comment (if either) it is:
+        if (_inputPtr >= _inputEnd && !loadMore()) {
+            _reportInvalidEOF(" in a comment");
+        }
+        int c = _inputBuffer[_inputPtr++] & 0xFF;
+        if (c == INT_SLASH) {
+            _skipCppComment();
+        } else if (c == INT_ASTERISK) {
+            _skipCComment();
+        } else {
+            _reportUnexpectedChar(c, "was expecting either '*' or '/' for a comment");
+        }
+    }
+
+    private final void _skipCComment()
+        throws IOException, JsonParseException
+    {
+        // Need to be UTF-8 aware here to decode content (for skipping)
+        final int[] codes = CharTypes.getInputCodeComment();
+
+        // Ok: need the matching '*/'
+        while ((_inputPtr < _inputEnd) || loadMore()) {
+            int i = (int) _inputBuffer[_inputPtr++] & 0xFF;
+            int code = codes[i];
+            if (code != 0) {
+                switch (code) {
+                case INT_ASTERISK:
+                    if (_inputBuffer[_inputPtr] == INT_SLASH) {
+                        ++_inputPtr;
+                        return;
+                    }
+                    break;
+                case INT_LF:
+                    _skipLF();
+                    break;
+                case INT_CR:
+                    _skipCR();
+                    break;
+                default: // e.g. -1
+                    // Is this good enough error message?
+                    _reportInvalidChar(i);
+                }
+            }
+        }
+        _reportInvalidEOF(" in a comment");
+    }
+
+    private final void _skipCppComment()
+        throws IOException, JsonParseException
+    {
+        // Ok: need to find EOF or linefeed
+        final int[] codes = CharTypes.getInputCodeComment();
+        while ((_inputPtr < _inputEnd) || loadMore()) {
+            int i = (int) _inputBuffer[_inputPtr++] & 0xFF;
+            int code = codes[i];
+            if (code != 0) {
+                switch (code) {
+                case INT_LF:
+                    _skipLF();
+                    return;
+                case INT_CR:
+                    _skipCR();
+                    return;
+                case INT_ASTERISK: // nop for these comments
+                    break;
+                default: // e.g. -1
+                    // Is this good enough error message?
+                    _reportInvalidChar(i);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected final char _decodeEscaped()
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            if (!loadMore()) {
+                _reportInvalidEOF(" in character escape sequence");
+            }
+        }
+        int c = (int) _inputBuffer[_inputPtr++];
+
+        switch ((int) c) {
+            // First, ones that are mapped
+        case INT_b:
+            return '\b';
+        case INT_t:
+            return '\t';
+        case INT_n:
+            return '\n';
+        case INT_f:
+            return '\f';
+        case INT_r:
+            return '\r';
+
+            // And these are to be returned as they are
+        case INT_QUOTE:
+        case INT_SLASH:
+        case INT_BACKSLASH:
+            return (char) c;
+
+        case INT_u: // and finally hex-escaped
+            break;
+
+        default:
+            return _handleUnrecognizedCharacterEscape((char) _decodeCharForError(c));
+        }
+
+        // Ok, a hex escape. Need 4 characters
+        int value = 0;
+        for (int i = 0; i < 4; ++i) {
+            if (_inputPtr >= _inputEnd) {
+                if (!loadMore()) {
+                    _reportInvalidEOF(" in character escape sequence");
+                }
+            }
+            int ch = (int) _inputBuffer[_inputPtr++];
+            int digit = CharTypes.charToHex(ch);
+            if (digit < 0) {
+                _reportUnexpectedChar(ch, "expected a hex-digit for character escape sequence");
+            }
+            value = (value << 4) | digit;
+        }
+        return (char) value;
+    }
+
+    protected int _decodeCharForError(int firstByte)
+        throws IOException, JsonParseException
+    {
+        int c = (int) firstByte;
+        if (c < 0) { // if >= 0, is ascii and fine as is
+            int needed;
+            
+            // Ok; if we end here, we got multi-byte combination
+            if ((c & 0xE0) == 0xC0) { // 2 bytes (0x0080 - 0x07FF)
+                c &= 0x1F;
+                needed = 1;
+            } else if ((c & 0xF0) == 0xE0) { // 3 bytes (0x0800 - 0xFFFF)
+                c &= 0x0F;
+                needed = 2;
+            } else if ((c & 0xF8) == 0xF0) {
+                // 4 bytes; double-char with surrogates and all...
+                c &= 0x07;
+                needed = 3;
+            } else {
+                _reportInvalidInitial(c & 0xFF);
+                needed = 1; // never gets here
+            }
+
+            int d = nextByte();
+            if ((d & 0xC0) != 0x080) {
+                _reportInvalidOther(d & 0xFF);
+            }
+            c = (c << 6) | (d & 0x3F);
+            
+            if (needed > 1) { // needed == 1 means 2 bytes total
+                d = nextByte(); // 3rd byte
+                if ((d & 0xC0) != 0x080) {
+                    _reportInvalidOther(d & 0xFF);
+                }
+                c = (c << 6) | (d & 0x3F);
+                if (needed > 2) { // 4 bytes? (need surrogates)
+                    d = nextByte();
+                    if ((d & 0xC0) != 0x080) {
+                        _reportInvalidOther(d & 0xFF);
+                    }
+                    c = (c << 6) | (d & 0x3F);
+                }
+            }
+        }
+        return c;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods,UTF8 decoding
+    /**********************************************************
+     */
+
+    private final int _decodeUtf8_2(int c)
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        int d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+        return ((c & 0x1F) << 6) | (d & 0x3F);
+    }
+
+    private final int _decodeUtf8_3(int c1)
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        c1 &= 0x0F;
+        int d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+        int c = (c1 << 6) | (d & 0x3F);
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+        c = (c << 6) | (d & 0x3F);
+        return c;
+    }
+
+    private final int _decodeUtf8_3fast(int c1)
+        throws IOException, JsonParseException
+    {
+        c1 &= 0x0F;
+        int d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+        int c = (c1 << 6) | (d & 0x3F);
+        d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+        c = (c << 6) | (d & 0x3F);
+        return c;
+    }
+
+    /**
+     * @return Character value <b>minus 0x10000</c>; this so that caller
+     *    can readily expand it to actual surrogates
+     */
+    private final int _decodeUtf8_4(int c)
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        int d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+        c = ((c & 0x07) << 6) | (d & 0x3F);
+
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+        c = (c << 6) | (d & 0x3F);
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+
+        /* note: won't change it to negative here, since caller
+         * already knows it'll need a surrogate
+         */
+        return ((c << 6) | (d & 0x3F)) - 0x10000;
+    }
+
+    private final void _skipUtf8_2(int c)
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        c = (int) _inputBuffer[_inputPtr++];
+        if ((c & 0xC0) != 0x080) {
+            _reportInvalidOther(c & 0xFF, _inputPtr);
+        }
+    }
+
+    /* Alas, can't heavily optimize skipping, since we still have to
+     * do validity checks...
+     */
+    private final void _skipUtf8_3(int c)
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        //c &= 0x0F;
+        c = (int) _inputBuffer[_inputPtr++];
+        if ((c & 0xC0) != 0x080) {
+            _reportInvalidOther(c & 0xFF, _inputPtr);
+        }
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        c = (int) _inputBuffer[_inputPtr++];
+        if ((c & 0xC0) != 0x080) {
+            _reportInvalidOther(c & 0xFF, _inputPtr);
+        }
+    }
+
+    private final void _skipUtf8_4(int c)
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        int d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        d = (int) _inputBuffer[_inputPtr++];
+        if ((d & 0xC0) != 0x080) {
+            _reportInvalidOther(d & 0xFF, _inputPtr);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, input loading
+    /**********************************************************
+     */
+
+    /**
+     * We actually need to check the character value here
+     * (to see if we have \n following \r).
+     */
+    protected final void _skipCR() throws IOException
+    {
+        if (_inputPtr < _inputEnd || loadMore()) {
+            if (_inputBuffer[_inputPtr] == BYTE_LF) {
+                ++_inputPtr;
+            }
+        }
+        ++_currInputRow;
+        _currInputRowStart = _inputPtr;
+    }
+
+    protected final void _skipLF() throws IOException
+    {
+        ++_currInputRow;
+        _currInputRowStart = _inputPtr;
+    }
+
+    private int nextByte()
+        throws IOException, JsonParseException
+    {
+        if (_inputPtr >= _inputEnd) {
+            loadMoreGuaranteed();
+        }
+        return _inputBuffer[_inputPtr++] & 0xFF;
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, error reporting
+    /**********************************************************
+     */
+
+    protected void _reportInvalidChar(int c)
+        throws JsonParseException
+        {
+            // Either invalid WS or illegal UTF-8 start char
+            if (c < INT_SPACE) {
+                _throwInvalidSpace(c);
+            }
+            _reportInvalidInitial(c);
+        }
+
+    protected void _reportInvalidInitial(int mask)
+        throws JsonParseException
+    {
+        _reportError("Invalid UTF-8 start byte 0x"+Integer.toHexString(mask));
+    }
+
+    protected void _reportInvalidOther(int mask)
+        throws JsonParseException
+    {
+        _reportError("Invalid UTF-8 middle byte 0x"+Integer.toHexString(mask));
+    }
+
+    protected void _reportInvalidOther(int mask, int ptr)
+        throws JsonParseException
+    {
+        _inputPtr = ptr;
+        _reportInvalidOther(mask);
+    }
+
+    public static int[] growArrayBy(int[] arr, int more)
+    {
+        if (arr == null) {
+            return new int[more];
+        }
+        int[] old = arr;
+        int len = arr.length;
+        arr = new int[len + more];
+        System.arraycopy(old, 0, arr, 0, len);
+        return arr;
+    }
+
+    /*
+    /**********************************************************
+    /* Binary access
+    /**********************************************************
+     */
+
+    /**
+     * Efficient handling for incremental parsing of base64-encoded
+     * textual content.
+     */
+    protected byte[] _decodeBase64(Base64Variant b64variant)
+        throws IOException, JsonParseException
+    {
+        ByteArrayBuilder builder = _getByteArrayBuilder();
+
+        //main_loop:
+        while (true) {
+            // first, we'll skip preceding white space, if any
+            int ch;
+            do {
+                if (_inputPtr >= _inputEnd) {
+                    loadMoreGuaranteed();
+                }
+                ch = (int) _inputBuffer[_inputPtr++] & 0xFF;
+            } while (ch <= INT_SPACE);
+            int bits = b64variant.decodeBase64Char(ch);
+            if (bits < 0) { // reached the end, fair and square?
+                if (ch == INT_QUOTE) {
+                    return builder.toByteArray();
+                }
+                bits = _decodeBase64Escape(b64variant, ch, 0);
+                if (bits < 0) { // white space to skip
+                    continue;
+                }
+            }
+            int decodedData = bits;
+            
+            // then second base64 char; can't get padding yet, nor ws
+            
+            if (_inputPtr >= _inputEnd) {
+                loadMoreGuaranteed();
+            }
+            ch = _inputBuffer[_inputPtr++] & 0xFF;
+            bits = b64variant.decodeBase64Char(ch);
+            if (bits < 0) {
+                bits = _decodeBase64Escape(b64variant, ch, 1);
+            }
+            decodedData = (decodedData << 6) | bits;
+            
+            // third base64 char; can be padding, but not ws
+            if (_inputPtr >= _inputEnd) {
+                loadMoreGuaranteed();
+            }
+            ch = _inputBuffer[_inputPtr++] & 0xFF;
+            bits = b64variant.decodeBase64Char(ch);
+
+            // First branch: can get padding (-> 1 byte)
+            if (bits < 0) {
+                if (bits != Base64Variant.BASE64_VALUE_PADDING) {
+                    // as per [JACKSON-631], could also just be 'missing'  padding
+                    if (ch == '"' && !b64variant.usesPadding()) {
+                        decodedData >>= 4;
+                        builder.append(decodedData);
+                        return builder.toByteArray();
+                    }
+                    bits = _decodeBase64Escape(b64variant, ch, 2);
+                }
+                if (bits == Base64Variant.BASE64_VALUE_PADDING) {
+                    // Ok, must get padding
+                    if (_inputPtr >= _inputEnd) {
+                        loadMoreGuaranteed();
+                    }
+                    ch = _inputBuffer[_inputPtr++] & 0xFF;
+                    if (!b64variant.usesPaddingChar(ch)) {
+                        throw reportInvalidBase64Char(b64variant, ch, 3, "expected padding character '"+b64variant.getPaddingChar()+"'");
+                    }
+                    // Got 12 bits, only need 8, need to shift
+                    decodedData >>= 4;
+                    builder.append(decodedData);
+                    continue;
+                }
+            }
+            // Nope, 2 or 3 bytes
+            decodedData = (decodedData << 6) | bits;
+            // fourth and last base64 char; can be padding, but not ws
+            if (_inputPtr >= _inputEnd) {
+                loadMoreGuaranteed();
+            }
+            ch = _inputBuffer[_inputPtr++] & 0xFF;
+            bits = b64variant.decodeBase64Char(ch);
+            if (bits < 0) {
+                if (bits != Base64Variant.BASE64_VALUE_PADDING) {
+                    // as per [JACKSON-631], could also just be 'missing'  padding
+                    if (ch == '"' && !b64variant.usesPadding()) {
+                        decodedData >>= 2;
+                        builder.appendTwoBytes(decodedData);
+                        return builder.toByteArray();
+                    }
+                    bits = _decodeBase64Escape(b64variant, ch, 3);
+                }
+                if (bits == Base64Variant.BASE64_VALUE_PADDING) {
+                    /* With padding we only get 2 bytes; but we have
+                     * to shift it a bit so it is identical to triplet
+                     * case with partial output.
+                     * 3 chars gives 3x6 == 18 bits, of which 2 are
+                     * dummies, need to discard:
+                     */
+                    decodedData >>= 2;
+                    builder.appendTwoBytes(decodedData);
+                    continue;
+                }
+            }
+            // otherwise, our triplet is now complete
+            decodedData = (decodedData << 6) | bits;
+            builder.appendThreeBytes(decodedData);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
new file mode 100644
index 0000000..5dedc60
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
@@ -0,0 +1,1815 @@
+package com.fasterxml.jackson.core.json;
+
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.base.GeneratorBase;
+import com.fasterxml.jackson.core.io.*;
+
+/**
+ * {@link JsonGenerator} that outputs JSON content using a {@link java.io.Writer}
+ * which handles character encoding.
+ */
+public final class WriterBasedJsonGenerator
+    extends GeneratorBase
+{
+    final protected static int SHORT_WRITE = 32;
+
+    final protected static char[] HEX_CHARS = CharTypes.copyHexChars();
+
+    /**
+     * This is the default set of escape codes, over 7-bit ASCII range
+     * (first 128 character codes), used for single-byte UTF-8 characters.
+     */
+    protected final static int[] sOutputEscapes = CharTypes.get7BitOutputEscapes();
+    
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    final protected IOContext _ioContext;
+
+    final protected Writer _writer;
+    
+    /*
+    /**********************************************************
+    /* Configuration, output escaping
+    /**********************************************************
+     */
+
+    /**
+     * Currently active set of output escape code definitions (whether
+     * and how to escape or not) for 7-bit ASCII range (first 128
+     * character codes). Defined separately to make potentially
+     * customizable
+     */
+    protected int[] _outputEscapes = sOutputEscapes;
+
+    /**
+     * Value between 128 (0x80) and 65535 (0xFFFF) that indicates highest
+     * Unicode code point that will not need escaping; or 0 to indicate
+     * that all characters can be represented without escaping.
+     * Typically used to force escaping of some portion of character set;
+     * for example to always escape non-ASCII characters (if value was 127).
+     *<p>
+     * NOTE: not all sub-classes make use of this setting.
+     */
+    protected int _maximumNonEscapedChar;
+
+    /**
+     * Definition of custom character escapes to use for generators created
+     * by this factory, if any. If null, standard data format specific
+     * escapes are used.
+     * 
+     * @since 1.8
+     */
+    protected CharacterEscapes _characterEscapes;
+
+    /**
+     * When custom escapes are used, this member variable can be used to
+     * store escape to use
+     * 
+     * @since 1.8
+     */
+    protected SerializableString _currentEscape;
+
+    /*
+    /**********************************************************
+    /* Output buffering
+    /**********************************************************
+     */
+
+    /**
+     * Intermediate buffer in which contents are buffered before
+     * being written using {@link #_writer}.
+     */
+    protected char[] _outputBuffer;
+
+    /**
+     * Pointer to the first buffered character to output
+     */
+    protected int _outputHead = 0;
+
+    /**
+     * Pointer to the position right beyond the last character to output
+     * (end marker; may point to position right beyond the end of the buffer)
+     */
+    protected int _outputTail = 0;
+
+    /**
+     * End marker of the output buffer; one past the last valid position
+     * within the buffer.
+     */
+    protected int _outputEnd;
+
+    /**
+     * Short (14 char) temporary buffer allocated if needed, for constructing
+     * escape sequences
+     */
+    protected char[] _entityBuffer;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public WriterBasedJsonGenerator(IOContext ctxt, int features, ObjectCodec codec,
+            Writer w)
+    {
+        super(features, codec);
+        _ioContext = ctxt;
+        _writer = w;
+        _outputBuffer = ctxt.allocConcatBuffer();
+        _outputEnd = _outputBuffer.length;
+
+        if (isEnabled(Feature.ESCAPE_NON_ASCII)) {
+            setHighestNonEscapedChar(127);
+        }
+    }
+ 
+    /*
+    /**********************************************************
+    /* Overridden configuration methods
+    /**********************************************************
+     */
+    
+    @Override
+    public JsonGenerator setHighestNonEscapedChar(int charCode) {
+        _maximumNonEscapedChar = (charCode < 0) ? 0 : charCode;
+        return this;
+    }
+
+    @Override
+    public int getHighestEscapedChar() {
+        return _maximumNonEscapedChar;
+    }
+
+    @Override
+    public JsonGenerator setCharacterEscapes(CharacterEscapes esc)
+    {
+        _characterEscapes = esc;
+        if (esc == null) { // revert to standard escapes
+            _outputEscapes = sOutputEscapes;
+        } else {
+            _outputEscapes = esc.getEscapeCodesForAscii();
+        }
+        return this;
+    }
+
+    /**
+     * Method for accessing custom escapes factory uses for {@link JsonGenerator}s
+     * it creates.
+     * 
+     * @since 1.8
+     */
+    @Override
+    public CharacterEscapes getCharacterEscapes() {
+        return _characterEscapes;
+    }
+
+    @Override
+    public Object getOutputTarget() {
+        return _writer;
+    }
+    
+    /*
+    /**********************************************************
+    /* Overridden methods
+    /**********************************************************
+     */
+
+    /* Most overrides in this section are just to make methods final,
+     * to allow better inlining...
+     */
+
+    @Override
+    public final void writeFieldName(String name)  throws IOException, JsonGenerationException
+    {
+        int status = _writeContext.writeFieldName(name);
+        if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
+            _reportError("Can not write a field name, expecting a value");
+        }
+        _writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
+    }
+
+    @Override
+    public final void writeStringField(String fieldName, String value)
+        throws IOException, JsonGenerationException
+    {
+        writeFieldName(fieldName);
+        writeString(value);
+    }
+    
+    @Override
+    public final void writeFieldName(SerializedString name)
+        throws IOException, JsonGenerationException
+    {
+        // Object is a value, need to verify it's allowed
+        int status = _writeContext.writeFieldName(name.getValue());
+        if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
+            _reportError("Can not write a field name, expecting a value");
+        }
+        _writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
+    }
+
+    @Override
+    public final void writeFieldName(SerializableString name)
+        throws IOException, JsonGenerationException
+    {
+        // Object is a value, need to verify it's allowed
+        int status = _writeContext.writeFieldName(name.getValue());
+        if (status == JsonWriteContext.STATUS_EXPECT_VALUE) {
+            _reportError("Can not write a field name, expecting a value");
+        }
+        _writeFieldName(name, (status == JsonWriteContext.STATUS_OK_AFTER_COMMA));
+    }
+    
+    /*
+    /**********************************************************
+    /* Output method implementations, structural
+    /**********************************************************
+     */
+
+    @Override
+    public final void writeStartArray() throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("start an array");
+        _writeContext = _writeContext.createChildArrayContext();
+        if (_cfgPrettyPrinter != null) {
+            _cfgPrettyPrinter.writeStartArray(this);
+        } else {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = '[';
+        }
+    }
+
+    @Override
+    public final void writeEndArray() throws IOException, JsonGenerationException
+    {
+        if (!_writeContext.inArray()) {
+            _reportError("Current context not an ARRAY but "+_writeContext.getTypeDesc());
+        }
+        if (_cfgPrettyPrinter != null) {
+            _cfgPrettyPrinter.writeEndArray(this, _writeContext.getEntryCount());
+        } else {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = ']';
+        }
+        _writeContext = _writeContext.getParent();
+    }
+
+    @Override
+    public final void writeStartObject() throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("start an object");
+        _writeContext = _writeContext.createChildObjectContext();
+        if (_cfgPrettyPrinter != null) {
+            _cfgPrettyPrinter.writeStartObject(this);
+        } else {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = '{';
+        }
+    }
+
+    @Override
+    public final void writeEndObject() throws IOException, JsonGenerationException
+    {
+        if (!_writeContext.inObject()) {
+            _reportError("Current context not an object but "+_writeContext.getTypeDesc());
+        }
+        _writeContext = _writeContext.getParent();
+        if (_cfgPrettyPrinter != null) {
+            _cfgPrettyPrinter.writeEndObject(this, _writeContext.getEntryCount());
+        } else {
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = '}';
+        }
+    }
+
+    protected void _writeFieldName(String name, boolean commaBefore)
+        throws IOException, JsonGenerationException
+    {
+        if (_cfgPrettyPrinter != null) {
+            _writePPFieldName(name, commaBefore);
+            return;
+        }
+        // for fast+std case, need to output up to 2 chars, comma, dquote
+        if ((_outputTail + 1) >= _outputEnd) {
+            _flushBuffer();
+        }
+        if (commaBefore) {
+            _outputBuffer[_outputTail++] = ',';
+        }
+
+        /* To support [JACKSON-46], we'll do this:
+         * (Question: should quoting of spaces (etc) still be enabled?)
+         */
+        if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) {
+            _writeString(name);
+            return;
+        }
+
+        // we know there's room for at least one more char
+        _outputBuffer[_outputTail++] = '"';
+        // The beef:
+        _writeString(name);
+        // and closing quotes; need room for one more char:
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+    }
+
+    public void _writeFieldName(SerializableString name, boolean commaBefore)
+        throws IOException, JsonGenerationException
+    {
+        if (_cfgPrettyPrinter != null) {
+            _writePPFieldName(name, commaBefore);
+            return;
+        }
+        // for fast+std case, need to output up to 2 chars, comma, dquote
+        if ((_outputTail + 1) >= _outputEnd) {
+            _flushBuffer();
+        }
+        if (commaBefore) {
+            _outputBuffer[_outputTail++] = ',';
+        }
+        /* To support [JACKSON-46], we'll do this:
+         * (Question: should quoting of spaces (etc) still be enabled?)
+         */
+        final char[] quoted = name.asQuotedChars();
+        if (!isEnabled(Feature.QUOTE_FIELD_NAMES)) {
+            writeRaw(quoted, 0, quoted.length);
+            return;
+        }
+        // we know there's room for at least one more char
+        _outputBuffer[_outputTail++] = '"';
+        // The beef:
+        final int qlen = quoted.length;
+        if ((_outputTail + qlen + 1) >= _outputEnd) {
+            writeRaw(quoted, 0, qlen);
+            // and closing quotes; need room for one more char:
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = '"';
+        } else {
+            System.arraycopy(quoted, 0, _outputBuffer, _outputTail, qlen);
+            _outputTail += qlen;
+            _outputBuffer[_outputTail++] = '"';
+        }
+    }
+    
+    /**
+     * Specialized version of <code>_writeFieldName</code>, off-lined
+     * to keep the "fast path" as simple (and hopefully fast) as possible.
+     */
+    protected final void _writePPFieldName(String name, boolean commaBefore)
+        throws IOException, JsonGenerationException
+    {
+        if (commaBefore) {
+            _cfgPrettyPrinter.writeObjectEntrySeparator(this);
+        } else {
+            _cfgPrettyPrinter.beforeObjectEntries(this);
+        }
+
+        if (isEnabled(Feature.QUOTE_FIELD_NAMES)) { // standard
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = '"';
+            _writeString(name);
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = '"';
+        } else { // non-standard, omit quotes
+            _writeString(name);
+        }
+    }
+
+    protected final void _writePPFieldName(SerializableString name, boolean commaBefore)
+        throws IOException, JsonGenerationException
+    {
+        if (commaBefore) {
+            _cfgPrettyPrinter.writeObjectEntrySeparator(this);
+        } else {
+            _cfgPrettyPrinter.beforeObjectEntries(this);
+        }
+    
+        final char[] quoted = name.asQuotedChars();
+        if (isEnabled(Feature.QUOTE_FIELD_NAMES)) { // standard
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = '"';
+            writeRaw(quoted, 0, quoted.length);
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = '"';
+        } else { // non-standard, omit quotes
+            writeRaw(quoted, 0, quoted.length);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Output method implementations, textual
+    /**********************************************************
+     */
+
+    @Override
+    public void writeString(String text)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write text value");
+        if (text == null) {
+            _writeNull();
+            return;
+        }
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+        _writeString(text);
+        // And finally, closing quotes
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+    }
+
+    @Override
+    public void writeString(char[] text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write text value");
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+        _writeString(text, offset, len);
+        // And finally, closing quotes
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+    }
+
+    @Override
+    public final void writeString(SerializableString sstr)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write text value");
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+        // Note: copied from writeRaw:
+        char[] text = sstr.asQuotedChars();
+        final int len = text.length;
+        // Only worth buffering if it's a short write?
+        if (len < SHORT_WRITE) {
+            int room = _outputEnd - _outputTail;
+            if (len > room) {
+                _flushBuffer();
+            }
+            System.arraycopy(text, 0, _outputBuffer, _outputTail, len);
+            _outputTail += len;
+        } else {
+            // Otherwise, better just pass through:
+            _flushBuffer();
+            _writer.write(text, 0, len);
+        }
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+    }
+
+    @Override
+    public void writeRawUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException
+    {
+        // could add support for buffering if we really want it...
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException
+    {
+        // could add support for buffering if we really want it...
+        _reportUnsupportedOperation();
+    }
+    
+    /*
+    /**********************************************************
+    /* Output method implementations, unprocessed ("raw")
+    /**********************************************************
+     */
+
+    @Override
+    public void writeRaw(String text)
+        throws IOException, JsonGenerationException
+    {
+        // Nothing to check, can just output as is
+        int len = text.length();
+        int room = _outputEnd - _outputTail;
+
+        if (room == 0) {
+            _flushBuffer();
+            room = _outputEnd - _outputTail;
+        }
+        // But would it nicely fit in? If yes, it's easy
+        if (room >= len) {
+            text.getChars(0, len, _outputBuffer, _outputTail);
+            _outputTail += len;
+        } else {
+            writeRawLong(text);
+        }
+    }
+
+    @Override
+    public void writeRaw(String text, int start, int len)
+        throws IOException, JsonGenerationException
+    {
+        // Nothing to check, can just output as is
+        int room = _outputEnd - _outputTail;
+
+        if (room < len) {
+            _flushBuffer();
+            room = _outputEnd - _outputTail;
+        }
+        // But would it nicely fit in? If yes, it's easy
+        if (room >= len) {
+            text.getChars(start, start+len, _outputBuffer, _outputTail);
+            _outputTail += len;
+        } else {            	
+            writeRawLong(text.substring(start, start+len));
+        }
+    }
+
+    @Override
+    public void writeRaw(char[] text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        // Only worth buffering if it's a short write?
+        if (len < SHORT_WRITE) {
+            int room = _outputEnd - _outputTail;
+            if (len > room) {
+                _flushBuffer();
+            }
+            System.arraycopy(text, offset, _outputBuffer, _outputTail, len);
+            _outputTail += len;
+            return;
+        }
+        // Otherwise, better just pass through:
+        _flushBuffer();
+        _writer.write(text, offset, len);
+    }
+
+    @Override
+    public void writeRaw(char c)
+        throws IOException, JsonGenerationException
+    {
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = c;
+    }
+
+    private void writeRawLong(String text)
+        throws IOException, JsonGenerationException
+    {
+        int room = _outputEnd - _outputTail;
+        // If not, need to do it by looping
+        text.getChars(0, room, _outputBuffer, _outputTail);
+        _outputTail += room;
+        _flushBuffer();
+        int offset = room;
+        int len = text.length() - room;
+
+        while (len > _outputEnd) {
+            int amount = _outputEnd;
+            text.getChars(offset, offset+amount, _outputBuffer, 0);
+            _outputHead = 0;
+            _outputTail = amount;
+            _flushBuffer();
+            offset += amount;
+            len -= amount;
+        }
+        // And last piece (at most length of buffer)
+        text.getChars(offset, offset+len, _outputBuffer, 0);
+        _outputHead = 0;
+        _outputTail = len;
+    }
+
+    /*
+    /**********************************************************
+    /* Output method implementations, base64-encoded binary
+    /**********************************************************
+     */
+
+    @Override
+    public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write binary value");
+        // Starting quotes
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+        _writeBinary(b64variant, data, offset, offset+len);
+        // and closing quotes
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+    }
+
+    /*
+    /**********************************************************
+    /* Output method implementations, primitive
+    /**********************************************************
+     */
+
+    @Override
+    public void writeNumber(int i)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        if (_cfgNumbersAsStrings) {
+            _writeQuotedInt(i);
+            return;
+        }
+        // up to 10 digits and possible minus sign
+        if ((_outputTail + 11) >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
+    }
+
+    private final void _writeQuotedInt(int i) throws IOException {
+        if ((_outputTail + 13) >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+        _outputTail = NumberOutput.outputInt(i, _outputBuffer, _outputTail);
+        _outputBuffer[_outputTail++] = '"';
+    }    
+
+    @Override
+    public void writeNumber(long l)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        if (_cfgNumbersAsStrings) {
+            _writeQuotedLong(l);
+            return;
+        }
+        if ((_outputTail + 21) >= _outputEnd) {
+            // up to 20 digits, minus sign
+            _flushBuffer();
+        }
+        _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
+    }
+
+    private final void _writeQuotedLong(long l) throws IOException {
+        if ((_outputTail + 23) >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+        _outputTail = NumberOutput.outputLong(l, _outputBuffer, _outputTail);
+        _outputBuffer[_outputTail++] = '"';
+    }
+
+    // !!! 05-Aug-2008, tatus: Any ways to optimize these?
+
+    @Override
+    public void writeNumber(BigInteger value)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        if (value == null) {
+            _writeNull();
+        } else if (_cfgNumbersAsStrings) {
+            _writeQuotedRaw(value);
+        } else {
+            writeRaw(value.toString());
+        }
+    }
+
+    
+    @Override
+    public void writeNumber(double d)
+        throws IOException, JsonGenerationException
+    {
+        if (_cfgNumbersAsStrings ||
+            // [JACKSON-139]
+            (((Double.isNaN(d) || Double.isInfinite(d))
+                && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS)))) {
+            writeString(String.valueOf(d));
+            return;
+        }
+        // What is the max length for doubles? 40 chars?
+        _verifyValueWrite("write number");
+        writeRaw(String.valueOf(d));
+    }
+
+    @Override
+    public void writeNumber(float f)
+        throws IOException, JsonGenerationException
+    {
+        if (_cfgNumbersAsStrings ||
+            // [JACKSON-139]
+            (((Float.isNaN(f) || Float.isInfinite(f))
+                && isEnabled(Feature.QUOTE_NON_NUMERIC_NUMBERS)))) {
+            writeString(String.valueOf(f));
+            return;
+        }
+        // What is the max length for floats?
+        _verifyValueWrite("write number");
+        writeRaw(String.valueOf(f));
+    }
+
+    @Override
+    public void writeNumber(BigDecimal value)
+        throws IOException, JsonGenerationException
+    {
+        // Don't really know max length for big decimal, no point checking
+        _verifyValueWrite("write number");
+        if (value == null) {
+            _writeNull();
+        } else if (_cfgNumbersAsStrings) {
+            _writeQuotedRaw(value);
+        } else {
+            writeRaw(value.toString());
+        }
+    }
+
+    @Override
+    public void writeNumber(String encodedValue)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write number");
+        if (_cfgNumbersAsStrings) {
+            _writeQuotedRaw(encodedValue);            
+        } else {
+            writeRaw(encodedValue);
+        }
+    }
+
+    private final void _writeQuotedRaw(Object value) throws IOException
+    {
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+        writeRaw(value.toString());
+        if (_outputTail >= _outputEnd) {
+            _flushBuffer();
+        }
+        _outputBuffer[_outputTail++] = '"';
+    }
+    
+    @Override
+    public void writeBoolean(boolean state)
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write boolean value");
+        if ((_outputTail + 5) >= _outputEnd) {
+            _flushBuffer();
+        }
+        int ptr = _outputTail;
+        char[] buf = _outputBuffer;
+        if (state) {
+            buf[ptr] = 't';
+            buf[++ptr] = 'r';
+            buf[++ptr] = 'u';
+            buf[++ptr] = 'e';
+        } else {
+            buf[ptr] = 'f';
+            buf[++ptr] = 'a';
+            buf[++ptr] = 'l';
+            buf[++ptr] = 's';
+            buf[++ptr] = 'e';
+        }
+        _outputTail = ptr+1;
+    }
+
+    @Override
+    public void writeNull()
+        throws IOException, JsonGenerationException
+    {
+        _verifyValueWrite("write null value");
+        _writeNull();
+    }
+
+    /*
+    /**********************************************************
+    /* Implementations for other methods
+    /**********************************************************
+     */
+
+    @Override
+    protected final void _verifyValueWrite(String typeMsg)
+        throws IOException, JsonGenerationException
+    {
+        int status = _writeContext.writeValue();
+        if (status == JsonWriteContext.STATUS_EXPECT_NAME) {
+            _reportError("Can not "+typeMsg+", expecting field name");
+        }
+        if (_cfgPrettyPrinter == null) {
+            char c;
+            switch (status) {
+            case JsonWriteContext.STATUS_OK_AFTER_COMMA:
+                c = ',';
+                break;
+            case JsonWriteContext.STATUS_OK_AFTER_COLON:
+                c = ':';
+                break;
+            case JsonWriteContext.STATUS_OK_AFTER_SPACE:
+                c = ' ';
+                break;
+            case JsonWriteContext.STATUS_OK_AS_IS:
+            default:
+                return;
+            }
+            if (_outputTail >= _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail] = c;
+            ++_outputTail;
+            return;
+        }
+        // Otherwise, pretty printer knows what to do...
+        _verifyPrettyValueWrite(typeMsg, status);
+    }
+
+    protected final void _verifyPrettyValueWrite(String typeMsg, int status)
+        throws IOException, JsonGenerationException
+    {
+        // If we have a pretty printer, it knows what to do:
+        switch (status) {
+        case JsonWriteContext.STATUS_OK_AFTER_COMMA: // array
+            _cfgPrettyPrinter.writeArrayValueSeparator(this);
+            break;
+        case JsonWriteContext.STATUS_OK_AFTER_COLON:
+            _cfgPrettyPrinter.writeObjectFieldValueSeparator(this);
+            break;
+        case JsonWriteContext.STATUS_OK_AFTER_SPACE:
+            _cfgPrettyPrinter.writeRootValueSeparator(this);
+            break;
+        case JsonWriteContext.STATUS_OK_AS_IS:
+            // First entry, but of which context?
+            if (_writeContext.inArray()) {
+                _cfgPrettyPrinter.beforeArrayValues(this);
+            } else if (_writeContext.inObject()) {
+                _cfgPrettyPrinter.beforeObjectEntries(this);
+            }
+            break;
+        default:
+            _cantHappen();
+            break;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Low-level output handling
+    /**********************************************************
+     */
+
+    @Override
+    public final void flush()
+        throws IOException
+    {
+        _flushBuffer();
+        if (_writer != null) {
+            if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
+                _writer.flush();
+            }
+        }
+    }
+
+    @Override
+    public void close()
+        throws IOException
+    {
+        super.close();
+
+        /* 05-Dec-2008, tatu: To add [JACKSON-27], need to close open
+         *   scopes.
+         */
+        // First: let's see that we still have buffers...
+        if (_outputBuffer != null
+            && isEnabled(Feature.AUTO_CLOSE_JSON_CONTENT)) {
+            while (true) {
+                JsonStreamContext ctxt = getOutputContext();
+                if (ctxt.inArray()) {
+                    writeEndArray();
+                } else if (ctxt.inObject()) {
+                    writeEndObject();
+                } else {
+                    break;
+                }
+            }
+        }
+        _flushBuffer();
+
+        /* 25-Nov-2008, tatus: As per [JACKSON-16] we are not to call close()
+         *   on the underlying Reader, unless we "own" it, or auto-closing
+         *   feature is enabled.
+         *   One downside: when using UTF8Writer, underlying buffer(s)
+         *   may not be properly recycled if we don't close the writer.
+         */
+        if (_writer != null) {
+            if (_ioContext.isResourceManaged() || isEnabled(Feature.AUTO_CLOSE_TARGET)) {
+                _writer.close();
+            } else  if (isEnabled(Feature.FLUSH_PASSED_TO_STREAM)) {
+                // If we can't close it, we should at least flush
+                _writer.flush();
+            }
+        }
+        // Internal buffer(s) generator has can now be released as well
+        _releaseBuffers();
+    }
+
+    @Override
+    protected void _releaseBuffers()
+    {
+        char[] buf = _outputBuffer;
+        if (buf != null) {
+            _outputBuffer = null;
+            _ioContext.releaseConcatBuffer(buf);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing; text, default
+    /**********************************************************
+     */
+
+    private void _writeString(String text)
+        throws IOException, JsonGenerationException
+    {
+        /* One check first: if String won't fit in the buffer, let's
+         * segment writes. No point in extending buffer to huge sizes
+         * (like if someone wants to include multi-megabyte base64
+         * encoded stuff or such)
+         */
+        final int len = text.length();
+        if (len > _outputEnd) { // Let's reserve space for entity at begin/end
+            _writeLongString(text);
+            return;
+        }
+
+        // Ok: we know String will fit in buffer ok
+        // But do we need to flush first?
+        if ((_outputTail + len) > _outputEnd) {
+            _flushBuffer();
+        }
+        text.getChars(0, len, _outputBuffer, _outputTail);
+
+        if (_characterEscapes != null) {
+            _writeStringCustom(len);
+        } else if (_maximumNonEscapedChar != 0) {
+            _writeStringASCII(len, _maximumNonEscapedChar);
+        } else {
+            _writeString2(len);
+        }
+    }
+
+    private void _writeString2(final int len)
+        throws IOException, JsonGenerationException
+    {
+        // And then we'll need to verify need for escaping etc:
+        int end = _outputTail + len;
+        final int[] escCodes = _outputEscapes;
+        final int escLen = escCodes.length;
+
+        output_loop:
+        while (_outputTail < end) {
+            // Fast loop for chars not needing escaping
+            escape_loop:
+            while (true) {
+                char c = _outputBuffer[_outputTail];
+                if (c < escLen && escCodes[c] != 0) {
+                    break escape_loop;
+                }
+                if (++_outputTail >= end) {
+                    break output_loop;
+                }
+            }
+
+            // Ok, bumped into something that needs escaping.
+            /* First things first: need to flush the buffer.
+             * Inlined, as we don't want to lose tail pointer
+             */
+            int flushLen = (_outputTail - _outputHead);
+            if (flushLen > 0) {
+                _writer.write(_outputBuffer, _outputHead, flushLen);
+            }
+            /* In any case, tail will be the new start, so hopefully
+             * we have room now.
+             */
+            char c = _outputBuffer[_outputTail++];
+            _prependOrWriteCharacterEscape(c, escCodes[c]);
+        }
+    }
+
+    /**
+     * Method called to write "long strings", strings whose length exceeds
+     * output buffer length.
+     */
+    private void _writeLongString(String text)
+        throws IOException, JsonGenerationException
+    {
+        // First things first: let's flush the buffer to get some more room
+        _flushBuffer();
+
+        // Then we can write 
+        final int textLen = text.length();
+        int offset = 0;
+        do {
+            int max = _outputEnd;
+            int segmentLen = ((offset + max) > textLen)
+                ? (textLen - offset) : max;
+            text.getChars(offset, offset+segmentLen, _outputBuffer, 0);
+            if (_characterEscapes != null) {
+                _writeSegmentCustom(segmentLen);
+            } else if (_maximumNonEscapedChar != 0) {
+                _writeSegmentASCII(segmentLen, _maximumNonEscapedChar);
+            } else {
+                _writeSegment(segmentLen);
+            }
+            offset += segmentLen;
+        } while (offset < textLen);
+    }
+
+    /**
+     * Method called to output textual context which has been copied
+     * to the output buffer prior to call. If any escaping is needed,
+     * it will also be handled by the method.
+     *<p>
+     * Note: when called, textual content to write is within output
+     * buffer, right after buffered content (if any). That's why only
+     * length of that text is passed, as buffer and offset are implied.
+     */
+    private final void _writeSegment(int end)
+        throws IOException, JsonGenerationException
+    {
+        final int[] escCodes = _outputEscapes;
+        final int escLen = escCodes.length;
+    
+        int ptr = 0;
+        int start = ptr;
+
+        output_loop:
+        while (ptr < end) {
+            // Fast loop for chars not needing escaping
+            char c;
+            while (true) {
+                c = _outputBuffer[ptr];
+                if (c < escLen && escCodes[c] != 0) {
+                    break;
+                }
+                if (++ptr >= end) {
+                    break;
+                }
+            }
+
+            // Ok, bumped into something that needs escaping.
+            /* First things first: need to flush the buffer.
+             * Inlined, as we don't want to lose tail pointer
+             */
+            int flushLen = (ptr - start);
+            if (flushLen > 0) {
+                _writer.write(_outputBuffer, start, flushLen);
+                if (ptr >= end) {
+                    break output_loop;
+                }
+            }
+            ++ptr;
+            // So; either try to prepend (most likely), or write directly:
+            start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCodes[c]);
+        }
+    }
+    
+    /**
+     * This method called when the string content is already in
+     * a char buffer, and need not be copied for processing.
+     */
+    private final void _writeString(char[] text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        if (_characterEscapes != null) {
+            _writeStringCustom(text, offset, len);
+            return;
+        }
+        if (_maximumNonEscapedChar != 0) {
+            _writeStringASCII(text, offset, len, _maximumNonEscapedChar);
+            return;
+        }
+        
+        /* Let's just find longest spans of non-escapable
+         * content, and for each see if it makes sense
+         * to copy them, or write through
+         */
+        len += offset; // -> len marks the end from now on
+        final int[] escCodes = _outputEscapes;
+        final int escLen = escCodes.length;
+        while (offset < len) {
+            int start = offset;
+
+            while (true) {
+                char c = text[offset];
+                if (c < escLen && escCodes[c] != 0) {
+                    break;
+                }
+                if (++offset >= len) {
+                    break;
+                }
+            }
+
+            // Short span? Better just copy it to buffer first:
+            int newAmount = offset - start;
+            if (newAmount < SHORT_WRITE) {
+                // Note: let's reserve room for escaped char (up to 6 chars)
+                if ((_outputTail + newAmount) > _outputEnd) {
+                    _flushBuffer();
+                }
+                if (newAmount > 0) {
+                    System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
+                    _outputTail += newAmount;
+                }
+            } else { // Nope: better just write through
+                _flushBuffer();
+                _writer.write(text, start, newAmount);
+            }
+            // Was this the end?
+            if (offset >= len) { // yup
+                break;
+            }
+            // Nope, need to escape the char.
+            char c = text[offset++];
+            _appendCharacterEscape(c, escCodes[c]);          
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing, text segment
+    /* with additional escaping (ASCII or such)
+    /* (since 1.8; see [JACKSON-102])
+    /**********************************************************
+     */
+
+    /* Same as "_writeString2()", except needs additional escaping
+     * for subset of characters
+     */
+    private void _writeStringASCII(final int len, final int maxNonEscaped)
+        throws IOException, JsonGenerationException
+    {
+        // And then we'll need to verify need for escaping etc:
+        int end = _outputTail + len;
+        final int[] escCodes = _outputEscapes;
+        final int escLimit = Math.min(escCodes.length, _maximumNonEscapedChar+1);
+        int escCode = 0;
+        
+        output_loop:
+        while (_outputTail < end) {
+            char c;
+            // Fast loop for chars not needing escaping
+            escape_loop:
+            while (true) {
+                c = _outputBuffer[_outputTail];
+                if (c < escLimit) {
+                    escCode = escCodes[c];
+                    if (escCode != 0) {
+                        break escape_loop;
+                    }
+                } else if (c > maxNonEscaped) {
+                    escCode = CharacterEscapes.ESCAPE_STANDARD;
+                    break escape_loop;
+                }
+                if (++_outputTail >= end) {
+                    break output_loop;
+                }
+            }
+            int flushLen = (_outputTail - _outputHead);
+            if (flushLen > 0) {
+                _writer.write(_outputBuffer, _outputHead, flushLen);
+            }
+            ++_outputTail;
+            _prependOrWriteCharacterEscape(c, escCode);
+        }
+    }
+
+    private final void _writeSegmentASCII(int end, final int maxNonEscaped)
+        throws IOException, JsonGenerationException
+    {
+        final int[] escCodes = _outputEscapes;
+        final int escLimit = Math.min(escCodes.length, _maximumNonEscapedChar+1);
+    
+        int ptr = 0;
+        int escCode = 0;
+        int start = ptr;
+    
+        output_loop:
+        while (ptr < end) {
+            // Fast loop for chars not needing escaping
+            char c;
+            while (true) {
+                c = _outputBuffer[ptr];
+                if (c < escLimit) {
+                    escCode = escCodes[c];
+                    if (escCode != 0) {
+                        break;
+                    }
+                } else if (c > maxNonEscaped) {
+                    escCode = CharacterEscapes.ESCAPE_STANDARD;
+                    break;
+                }
+                if (++ptr >= end) {
+                    break;
+                }
+            }
+            int flushLen = (ptr - start);
+            if (flushLen > 0) {
+                _writer.write(_outputBuffer, start, flushLen);
+                if (ptr >= end) {
+                    break output_loop;
+                }
+            }
+            ++ptr;
+            start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode);
+        }
+    }
+
+    private final void _writeStringASCII(char[] text, int offset, int len,
+            final int maxNonEscaped)
+        throws IOException, JsonGenerationException
+    {
+        len += offset; // -> len marks the end from now on
+        final int[] escCodes = _outputEscapes;
+        final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
+
+        int escCode = 0;
+        
+        while (offset < len) {
+            int start = offset;
+            char c;
+            
+            while (true) {
+                c = text[offset];
+                if (c < escLimit) {
+                    escCode = escCodes[c];
+                    if (escCode != 0) {
+                        break;
+                    }
+                } else if (c > maxNonEscaped) {
+                    escCode = CharacterEscapes.ESCAPE_STANDARD;
+                    break;
+                }
+                if (++offset >= len) {
+                    break;
+                }
+            }
+
+            // Short span? Better just copy it to buffer first:
+            int newAmount = offset - start;
+            if (newAmount < SHORT_WRITE) {
+                // Note: let's reserve room for escaped char (up to 6 chars)
+                if ((_outputTail + newAmount) > _outputEnd) {
+                    _flushBuffer();
+                }
+                if (newAmount > 0) {
+                    System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
+                    _outputTail += newAmount;
+                }
+            } else { // Nope: better just write through
+                _flushBuffer();
+                _writer.write(text, start, newAmount);
+            }
+            // Was this the end?
+            if (offset >= len) { // yup
+                break;
+            }
+            // Nope, need to escape the char.
+            ++offset;
+            _appendCharacterEscape(c, escCode);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing, text segment
+    /* with custom escaping (possibly coupling with ASCII limits)
+    /* (since 1.8; see [JACKSON-106])
+    /**********************************************************
+     */
+
+    /* Same as "_writeString2()", except needs additional escaping
+     * for subset of characters
+     */
+    private void _writeStringCustom(final int len)
+        throws IOException, JsonGenerationException
+    {
+        // And then we'll need to verify need for escaping etc:
+        int end = _outputTail + len;
+        final int[] escCodes = _outputEscapes;
+        final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
+        final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
+        int escCode = 0;
+        final CharacterEscapes customEscapes = _characterEscapes;
+
+        output_loop:
+        while (_outputTail < end) {
+            char c;
+            // Fast loop for chars not needing escaping
+            escape_loop:
+            while (true) {
+                c = _outputBuffer[_outputTail];
+                if (c < escLimit) {
+                    escCode = escCodes[c];
+                    if (escCode != 0) {
+                        break escape_loop;
+                    }
+                } else if (c > maxNonEscaped) {
+                    escCode = CharacterEscapes.ESCAPE_STANDARD;
+                    break escape_loop;
+                } else {
+                    if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
+                        escCode = CharacterEscapes.ESCAPE_CUSTOM;
+                        break escape_loop;
+                    }
+                }
+                if (++_outputTail >= end) {
+                    break output_loop;
+                }
+            }
+            int flushLen = (_outputTail - _outputHead);
+            if (flushLen > 0) {
+                _writer.write(_outputBuffer, _outputHead, flushLen);
+            }
+            ++_outputTail;
+            _prependOrWriteCharacterEscape(c, escCode);
+        }
+    }
+
+    private final void _writeSegmentCustom(int end)
+        throws IOException, JsonGenerationException
+    {
+        final int[] escCodes = _outputEscapes;
+        final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
+        final int escLimit = Math.min(escCodes.length, _maximumNonEscapedChar+1);
+        final CharacterEscapes customEscapes = _characterEscapes;
+    
+        int ptr = 0;
+        int escCode = 0;
+        int start = ptr;
+    
+        output_loop:
+        while (ptr < end) {
+            // Fast loop for chars not needing escaping
+            char c;
+            while (true) {
+                c = _outputBuffer[ptr];
+                if (c < escLimit) {
+                    escCode = escCodes[c];
+                    if (escCode != 0) {
+                        break;
+                    }
+                } else if (c > maxNonEscaped) {
+                    escCode = CharacterEscapes.ESCAPE_STANDARD;
+                    break;
+                } else {
+                    if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
+                        escCode = CharacterEscapes.ESCAPE_CUSTOM;
+                        break;
+                    }
+                }
+                if (++ptr >= end) {
+                    break;
+                }
+            }
+            int flushLen = (ptr - start);
+            if (flushLen > 0) {
+                _writer.write(_outputBuffer, start, flushLen);
+                if (ptr >= end) {
+                    break output_loop;
+                }
+            }
+            ++ptr;
+            start = _prependOrWriteCharacterEscape(_outputBuffer, ptr, end, c, escCode);
+        }
+    }
+
+    private final void _writeStringCustom(char[] text, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        len += offset; // -> len marks the end from now on
+        final int[] escCodes = _outputEscapes;
+        final int maxNonEscaped = (_maximumNonEscapedChar < 1) ? 0xFFFF : _maximumNonEscapedChar;
+        final int escLimit = Math.min(escCodes.length, maxNonEscaped+1);
+        final CharacterEscapes customEscapes = _characterEscapes;
+
+        int escCode = 0;
+        
+        while (offset < len) {
+            int start = offset;
+            char c;
+            
+            while (true) {
+                c = text[offset];
+                if (c < escLimit) {
+                    escCode = escCodes[c];
+                    if (escCode != 0) {
+                        break;
+                    }
+                } else if (c > maxNonEscaped) {
+                    escCode = CharacterEscapes.ESCAPE_STANDARD;
+                    break;
+                } else {
+                    if ((_currentEscape = customEscapes.getEscapeSequence(c)) != null) {
+                        escCode = CharacterEscapes.ESCAPE_CUSTOM;
+                        break;
+                    }
+                }
+                if (++offset >= len) {
+                    break;
+                }
+            }
+    
+            // Short span? Better just copy it to buffer first:
+            int newAmount = offset - start;
+            if (newAmount < SHORT_WRITE) {
+                // Note: let's reserve room for escaped char (up to 6 chars)
+                if ((_outputTail + newAmount) > _outputEnd) {
+                    _flushBuffer();
+                }
+                if (newAmount > 0) {
+                    System.arraycopy(text, start, _outputBuffer, _outputTail, newAmount);
+                    _outputTail += newAmount;
+                }
+            } else { // Nope: better just write through
+                _flushBuffer();
+                _writer.write(text, start, newAmount);
+            }
+            // Was this the end?
+            if (offset >= len) { // yup
+                break;
+            }
+            // Nope, need to escape the char.
+            ++offset;
+            _appendCharacterEscape(c, escCode);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing; binary
+    /**********************************************************
+     */
+    
+    protected void _writeBinary(Base64Variant b64variant, byte[] input, int inputPtr, final int inputEnd)
+        throws IOException, JsonGenerationException
+    {
+        // Encoding is by chunks of 3 input, 4 output chars, so:
+        int safeInputEnd = inputEnd - 3;
+        // Let's also reserve room for possible (and quoted) lf char each round
+        int safeOutputEnd = _outputEnd - 6;
+        int chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
+
+        // Ok, first we loop through all full triplets of data:
+        while (inputPtr <= safeInputEnd) {
+            if (_outputTail > safeOutputEnd) { // need to flush
+                _flushBuffer();
+            }
+            // First, mash 3 bytes into lsb of 32-bit int
+            int b24 = ((int) input[inputPtr++]) << 8;
+            b24 |= ((int) input[inputPtr++]) & 0xFF;
+            b24 = (b24 << 8) | (((int) input[inputPtr++]) & 0xFF);
+            _outputTail = b64variant.encodeBase64Chunk(b24, _outputBuffer, _outputTail);
+            if (--chunksBeforeLF <= 0) {
+                // note: must quote in JSON value
+                _outputBuffer[_outputTail++] = '\\';
+                _outputBuffer[_outputTail++] = 'n';
+                chunksBeforeLF = b64variant.getMaxLineLength() >> 2;
+            }
+        }
+
+        // And then we may have 1 or 2 leftover bytes to encode
+        int inputLeft = inputEnd - inputPtr; // 0, 1 or 2
+        if (inputLeft > 0) { // yes, but do we have room for output?
+            if (_outputTail > safeOutputEnd) { // don't really need 6 bytes but...
+                _flushBuffer();
+            }
+            int b24 = ((int) input[inputPtr++]) << 16;
+            if (inputLeft == 2) {
+                b24 |= (((int) input[inputPtr++]) & 0xFF) << 8;
+            }
+            _outputTail = b64variant.encodeBase64Partial(b24, inputLeft, _outputBuffer, _outputTail);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing, other
+    /**********************************************************
+     */
+    
+    private final void _writeNull() throws IOException
+    {
+        if ((_outputTail + 4) >= _outputEnd) {
+            _flushBuffer();
+        }
+        int ptr = _outputTail;
+        char[] buf = _outputBuffer;
+        buf[ptr] = 'n';
+        buf[++ptr] = 'u';
+        buf[++ptr] = 'l';
+        buf[++ptr] = 'l';
+        _outputTail = ptr+1;
+    }
+        
+    /*
+    /**********************************************************
+    /* Internal methods, low-level writing, escapes
+    /**********************************************************
+     */
+
+    /**
+     * Method called to try to either prepend character escape at front of
+     * given buffer; or if not possible, to write it out directly.
+     * Uses head and tail pointers (and updates as necessary)
+     */
+    private final void _prependOrWriteCharacterEscape(char ch, int escCode)
+        throws IOException, JsonGenerationException
+    {
+        if (escCode >= 0) { // \\N (2 char)
+            if (_outputTail >= 2) { // fits, just prepend
+                int ptr = _outputTail - 2;
+                _outputHead = ptr;
+                _outputBuffer[ptr++] = '\\';
+                _outputBuffer[ptr] = (char) escCode;
+                return;
+            }
+            // won't fit, write
+            char[] buf = _entityBuffer;
+            if (buf == null) {
+                buf = _allocateEntityBuffer();
+            }
+            _outputHead = _outputTail;
+            buf[1] = (char) escCode;
+            _writer.write(buf, 0, 2);
+            return;
+        }
+        if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
+            if (_outputTail >= 6) { // fits, prepend to buffer
+                char[] buf = _outputBuffer;
+                int ptr = _outputTail - 6;
+                _outputHead = ptr;
+                buf[ptr] = '\\';
+                buf[++ptr] = 'u';
+                // We know it's a control char, so only the last 2 chars are non-0
+                if (ch > 0xFF) { // beyond 8 bytes
+                    int hi = (ch >> 8) & 0xFF;
+                    buf[++ptr] = HEX_CHARS[hi >> 4];
+                    buf[++ptr] = HEX_CHARS[hi & 0xF];
+                    ch &= 0xFF;
+                } else {
+                    buf[++ptr] = '0';
+                    buf[++ptr] = '0';
+                }
+                buf[++ptr] = HEX_CHARS[ch >> 4];
+                buf[++ptr] = HEX_CHARS[ch & 0xF];
+                return;
+            }
+            // won't fit, flush and write
+            char[] buf = _entityBuffer;
+            if (buf == null) {
+                buf = _allocateEntityBuffer();
+            }
+            _outputHead = _outputTail;
+            if (ch > 0xFF) { // beyond 8 bytes
+                int hi = (ch >> 8) & 0xFF;
+                int lo = ch & 0xFF;
+                buf[10] = HEX_CHARS[hi >> 4];
+                buf[11] = HEX_CHARS[hi & 0xF];
+                buf[12] = HEX_CHARS[lo >> 4];
+                buf[13] = HEX_CHARS[lo & 0xF];
+                _writer.write(buf, 8, 6);
+            } else { // We know it's a control char, so only the last 2 chars are non-0
+                buf[6] = HEX_CHARS[ch >> 4];
+                buf[7] = HEX_CHARS[ch & 0xF];
+                _writer.write(buf, 2, 6);
+            }
+            return;
+        }
+        String escape;
+
+        if (_currentEscape == null) {
+            escape = _characterEscapes.getEscapeSequence(ch).getValue();
+        } else {
+            escape = _currentEscape.getValue();
+            _currentEscape = null;
+        }
+        int len = escape.length();
+        if (_outputTail >= len) { // fits in, prepend
+            int ptr = _outputTail - len;
+            _outputHead = ptr;
+            escape.getChars(0, len, _outputBuffer, ptr);
+            return;
+        }
+        // won't fit, write separately
+        _outputHead = _outputTail;
+        _writer.write(escape);
+    }
+
+    /**
+     * Method called to try to either prepend character escape at front of
+     * given buffer; or if not possible, to write it out directly.
+     * 
+     * @return Pointer to start of prepended entity (if prepended); or 'ptr'
+     *   if not.
+     */
+    private final int _prependOrWriteCharacterEscape(char[] buffer, int ptr, int end,
+            char ch, int escCode)
+        throws IOException, JsonGenerationException
+    {
+        if (escCode >= 0) { // \\N (2 char)
+            if (ptr > 1 && ptr < end) { // fits, just prepend
+                ptr -= 2;
+                buffer[ptr] = '\\';
+                buffer[ptr+1] = (char) escCode;
+            } else { // won't fit, write
+                char[] ent = _entityBuffer;
+                if (ent == null) {
+                    ent = _allocateEntityBuffer();
+                }
+                ent[1] = (char) escCode;
+                _writer.write(ent, 0, 2);
+            }
+            return ptr;
+        }
+        if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
+            if (ptr > 5 && ptr < end) { // fits, prepend to buffer
+                ptr -= 6;
+                buffer[ptr++] = '\\';
+                buffer[ptr++] = 'u';
+                // We know it's a control char, so only the last 2 chars are non-0
+                if (ch > 0xFF) { // beyond 8 bytes
+                    int hi = (ch >> 8) & 0xFF;
+                    buffer[ptr++] = HEX_CHARS[hi >> 4];
+                    buffer[ptr++] = HEX_CHARS[hi & 0xF];
+                    ch &= 0xFF;
+                } else {
+                    buffer[ptr++] = '0';
+                    buffer[ptr++] = '0';
+                }
+                buffer[ptr++] = HEX_CHARS[ch >> 4];
+                buffer[ptr] = HEX_CHARS[ch & 0xF];
+                ptr -= 5;
+            } else {
+                // won't fit, flush and write
+                char[] ent = _entityBuffer;
+                if (ent == null) {
+                    ent = _allocateEntityBuffer();
+                }
+                _outputHead = _outputTail;
+                if (ch > 0xFF) { // beyond 8 bytes
+                    int hi = (ch >> 8) & 0xFF;
+                    int lo = ch & 0xFF;
+                    ent[10] = HEX_CHARS[hi >> 4];
+                    ent[11] = HEX_CHARS[hi & 0xF];
+                    ent[12] = HEX_CHARS[lo >> 4];
+                    ent[13] = HEX_CHARS[lo & 0xF];
+                    _writer.write(ent, 8, 6);
+                } else { // We know it's a control char, so only the last 2 chars are non-0
+                    ent[6] = HEX_CHARS[ch >> 4];
+                    ent[7] = HEX_CHARS[ch & 0xF];
+                    _writer.write(ent, 2, 6);
+                }
+            }
+            return ptr;
+        }
+        String escape;
+        if (_currentEscape == null) {
+            escape = _characterEscapes.getEscapeSequence(ch).getValue();
+        } else {
+            escape = _currentEscape.getValue();
+            _currentEscape = null;
+        }
+        int len = escape.length();
+        if (ptr >= len && ptr < end) { // fits in, prepend
+            ptr -= len;
+            escape.getChars(0, len, buffer, ptr);
+        } else { // won't fit, write separately
+            _writer.write(escape);
+        }
+        return ptr;
+    }
+
+    /**
+     * Method called to append escape sequence for given character, at the
+     * end of standard output buffer; or if not possible, write out directly.
+     */
+    private final void _appendCharacterEscape(char ch, int escCode)
+        throws IOException, JsonGenerationException
+    {
+        if (escCode >= 0) { // \\N (2 char)
+            if ((_outputTail + 2) > _outputEnd) {
+                _flushBuffer();
+            }
+            _outputBuffer[_outputTail++] = '\\';
+            _outputBuffer[_outputTail++] = (char) escCode;
+            return;
+        }
+        if (escCode != CharacterEscapes.ESCAPE_CUSTOM) { // std, \\uXXXX
+            if ((_outputTail + 2) > _outputEnd) {
+                _flushBuffer();
+            }
+            int ptr = _outputTail;
+            char[] buf = _outputBuffer;
+            buf[ptr++] = '\\';
+            buf[ptr++] = 'u';
+            // We know it's a control char, so only the last 2 chars are non-0
+            if (ch > 0xFF) { // beyond 8 bytes
+                int hi = (ch >> 8) & 0xFF;
+                buf[ptr++] = HEX_CHARS[hi >> 4];
+                buf[ptr++] = HEX_CHARS[hi & 0xF];
+                ch &= 0xFF;
+            } else {
+                buf[ptr++] = '0';
+                buf[ptr++] = '0';
+            }
+            buf[ptr++] = HEX_CHARS[ch >> 4];
+            buf[ptr] = HEX_CHARS[ch & 0xF];
+            _outputTail = ptr;
+            return;
+        }
+        String escape;
+        if (_currentEscape == null) {
+            escape = _characterEscapes.getEscapeSequence(ch).getValue();
+        } else {
+            escape = _currentEscape.getValue();
+            _currentEscape = null;
+        }
+        int len = escape.length();
+        if ((_outputTail + len) > _outputEnd) {
+            _flushBuffer();
+            if (len > _outputEnd) { // very very long escape; unlikely but theoretically possible
+                _writer.write(escape);
+                return;
+            }
+        }
+        escape.getChars(0, len, _outputBuffer, _outputTail);
+        _outputTail += len;
+    }
+    
+    private char[] _allocateEntityBuffer()
+    {
+        char[] buf = new char[14];
+        // first 2 chars, non-numeric escapes (like \n)
+        buf[0] = '\\';
+        // next 6; 8-bit escapes (control chars mostly)
+        buf[2] = '\\';
+        buf[3] = 'u';
+        buf[4] = '0';
+        buf[5] = '0';
+        // last 6, beyond 8 bits
+        buf[8] = '\\';
+        buf[9] = 'u';
+        _entityBuffer = buf;
+        return buf;
+    }
+    
+    protected final void _flushBuffer() throws IOException
+    {
+        int len = _outputTail - _outputHead;
+        if (len > 0) {
+            int offset = _outputHead;
+            _outputTail = _outputHead = 0;
+            _writer.write(_outputBuffer, offset, len);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/json/package-info.java b/src/main/java/com/fasterxml/jackson/core/json/package-info.java
new file mode 100644
index 0000000..7657a3f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/json/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * JSON-specific parser and generator implementation classes that
+ * Jackson defines and uses.
+ * Application code should not (need to) use contents of this package;
+ * nor are these implementations likely to be of use for sub-classing.
+ */
+package com.fasterxml.jackson.core.json;
diff --git a/src/main/java/com/fasterxml/jackson/core/package-info.java b/src/main/java/com/fasterxml/jackson/core/package-info.java
new file mode 100644
index 0000000..2359269
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/package-info.java
@@ -0,0 +1,30 @@
+/**
+ * Main public API classes of the core streaming JSON
+ * processor: most importantly {@link com.fasterxml.jackson.core.JsonFactory}
+ * used for constructing
+ * JSON parser ({@link com.fasterxml.jackson.core.JsonParser})
+ * and generator
+ * ({@link com.fasterxml.jackson.core.JsonParser})
+ * instances.
+ * <p>
+ * Public API of the higher-level mapping interfaces ("Mapping API")
+ * is found from
+ * under {@link org.codehaus.jackson.map} and not included here,
+ * except for following base interfaces:
+ * <ul>
+ *<li>{@link com.fasterxml.jackson.core.JsonNode} is included
+ *within Streaming API to support integration of the Tree Model
+ *(which is based on <code>JsonNode</code>) with the basic
+ *parsers and generators (iff using mapping-supporting factory: which
+ *is part of Mapping API, not core)
+ *  </li>
+ *<li>{@link com.fasterxml.jackson.core.ObjectCodec} is included so that
+ *  reference to the object capable of serializing/deserializing
+ *  Objects to/from JSON (usually, {@link org.codehaus.jackson.map.ObjectMapper})
+ *  can be exposed, without adding direct dependency to implementation.
+ *  </li>
+ *</ul>
+ * </ul>
+ */
+
+package com.fasterxml.jackson.core;
diff --git a/src/main/java/com/fasterxml/jackson/core/sym/BytesToNameCanonicalizer.java b/src/main/java/com/fasterxml/jackson/core/sym/BytesToNameCanonicalizer.java
new file mode 100644
index 0000000..a8eeaae
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/sym/BytesToNameCanonicalizer.java
@@ -0,0 +1,969 @@
+package com.fasterxml.jackson.core.sym;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.core.util.InternCache;
+
+/**
+ * This class is basically a caching symbol table implementation used for
+ * canonicalizing {@link Name}s, constructed directly from a byte-based
+ * input source.
+ *
+ * @author Tatu Saloranta
+ */
+public final class BytesToNameCanonicalizer
+{
+    protected static final int DEFAULT_TABLE_SIZE = 64;
+
+    /**
+     * Let's not expand symbol tables past some maximum size;
+     * this should protected against OOMEs caused by large documents
+     * with uniquer (~= random) names.
+     * 
+     * @since 1.5
+     */
+    protected static final int MAX_TABLE_SIZE = 0x10000; // 64k entries == 256k mem
+    
+    /**
+     * Let's only share reasonably sized symbol tables. Max size set to 3/4 of 16k;
+     * this corresponds to 64k main hash index. This should allow for enough distinct
+     * names for almost any case.
+     */
+    final static int MAX_ENTRIES_FOR_REUSE = 6000;
+
+    final static int MIN_HASH_SIZE = 16;
+
+    final static int INITIAL_COLLISION_LEN = 32;
+
+    /**
+     * Bucket index is 8 bits, and value 0 is reserved to represent
+     * 'empty' status.
+     */
+    final static int LAST_VALID_BUCKET = 0xFE;
+    
+    /*
+    /**********************************************************
+    /* Linkage, needed for merging symbol tables
+    /**********************************************************
+     */
+
+    final BytesToNameCanonicalizer _parent;
+
+    /*
+    /**********************************************************
+    /* Main table state
+    /**********************************************************
+     */
+
+    /**
+     * Whether canonial symbol Strings are to be intern()ed before added
+     * to the table or not
+     */
+    final boolean _intern;
+    
+    // // // First, global information
+
+    /**
+     * Total number of Names in the symbol table
+     */
+    private int _count;
+
+    // // // Then information regarding primary hash array and its
+    // // // matching Name array
+
+    /**
+     * Mask used to truncate 32-bit hash value to current hash array
+     * size; essentially, hash array size - 1 (since hash array sizes
+     * are 2^N).
+     */
+    private int _mainHashMask;
+
+    /**
+     * Array of 2^N size, which contains combination
+     * of 24-bits of hash (0 to indicate 'empty' slot),
+     * and 8-bit collision bucket index (0 to indicate empty
+     * collision bucket chain; otherwise subtract one from index)
+     */
+    private int[] _mainHash;
+
+    /**
+     * Array that contains <code>Name</code> instances matching
+     * entries in <code>_mainHash</code>. Contains nulls for unused
+     * entries.
+     */
+    private Name[] _mainNames;
+
+    // // // Then the collision/spill-over area info
+
+    /**
+     * Array of heads of collision bucket chains; size dynamically
+     */
+    private Bucket[] _collList;
+
+    /**
+     * Total number of Names in collision buckets (included in
+     * <code>_count</code> along with primary entries)
+     */
+    private int _collCount;
+
+    /**
+     * Index of the first unused collision bucket entry (== size of
+     * the used portion of collision list): less than
+     * or equal to 0xFF (255), since max number of entries is 255
+     * (8-bit, minus 0 used as 'empty' marker)
+     */
+    private int _collEnd;
+
+    // // // Info regarding pending rehashing...
+
+    /**
+     * This flag is set if, after adding a new entry, it is deemed
+     * that a rehash is warranted if any more entries are to be added.
+     */
+    private transient boolean _needRehash;
+
+    /*
+    /**********************************************************
+    /* Sharing, versioning
+    /**********************************************************
+     */
+
+    // // // Which of the buffers may be shared (and are copy-on-write)?
+
+    /**
+     * Flag that indicates whether underlying data structures for
+     * the main hash area are shared or not. If they are, then they
+     * need to be handled in copy-on-write way, i.e. if they need
+     * to be modified, a copy needs to be made first; at this point
+     * it will not be shared any more, and can be modified.
+     *<p>
+     * This flag needs to be checked both when adding new main entries,
+     * and when adding new collision list queues (i.e. creating a new
+     * collision list head entry)
+     */
+    private boolean _mainHashShared;
+
+    private boolean _mainNamesShared;
+
+    /**
+     * Flag that indicates whether underlying data structures for
+     * the collision list are shared or not. If they are, then they
+     * need to be handled in copy-on-write way, i.e. if they need
+     * to be modified, a copy needs to be made first; at this point
+     * it will not be shared any more, and can be modified.
+     *<p>
+     * This flag needs to be checked when adding new collision entries.
+     */
+    private boolean _collListShared;
+
+    /*
+    /**********************************************************
+    /* Construction, merging
+    /**********************************************************
+     */
+
+    public static BytesToNameCanonicalizer createRoot()
+    {
+        return new BytesToNameCanonicalizer(DEFAULT_TABLE_SIZE, true);
+    }
+
+    /**
+     * @param intern Whether canonical symbol Strings should be interned
+     *   or not
+     */
+    public synchronized BytesToNameCanonicalizer makeChild(boolean canonicalize,
+        boolean intern)
+    {
+        return new BytesToNameCanonicalizer(this, intern);
+    }
+
+    /**
+     * Method called by the using code to indicate it is done
+     * with this instance. This lets instance merge accumulated
+     * changes into parent (if need be), safely and efficiently,
+     * and without calling code having to know about parent
+     * information
+     */
+    public void release()
+    {
+        if (maybeDirty() && _parent != null) {
+            _parent.mergeChild(this);
+            /* Let's also mark this instance as dirty, so that just in
+             * case release was too early, there's no corruption
+             * of possibly shared data.
+             */
+            markAsShared();
+        }
+    }
+
+    private BytesToNameCanonicalizer(int hashSize, boolean intern)
+    {
+        _parent = null;
+        _intern = intern;
+        /* Sanity check: let's now allow hash sizes below certain
+         * min. value
+         */
+        if (hashSize < MIN_HASH_SIZE) {
+            hashSize = MIN_HASH_SIZE;
+        } else {
+            /* Also; size must be 2^N; otherwise hash algorithm won't
+             * work... so let's just pad it up, if so
+             */
+            if ((hashSize & (hashSize - 1)) != 0) { // only true if it's 2^N
+                int curr = MIN_HASH_SIZE;
+                while (curr < hashSize) {
+                    curr += curr;
+                }
+                hashSize = curr;
+            }
+        }
+        initTables(hashSize);
+    }
+
+    /**
+     * Constructor used when creating a child instance
+     */
+    private BytesToNameCanonicalizer(BytesToNameCanonicalizer parent, boolean intern)
+    {
+        _parent = parent;
+        _intern = intern;
+
+        // First, let's copy the state as is:
+        _count = parent._count;
+        _mainHashMask = parent._mainHashMask;
+        _mainHash = parent._mainHash;
+        _mainNames = parent._mainNames;
+        _collList = parent._collList;
+        _collCount = parent._collCount;
+        _collEnd = parent._collEnd;
+        _needRehash = false;
+        // And consider all shared, so far:
+        _mainHashShared = true;
+        _mainNamesShared = true;
+        _collListShared = true;
+    }
+
+    private void initTables(int hashSize)
+    {
+        _count = 0;
+        _mainHash = new int[hashSize];
+        _mainNames = new Name[hashSize];
+        _mainHashShared = false;
+        _mainNamesShared = false;
+        _mainHashMask = hashSize - 1;
+
+        _collListShared = true; // just since it'll need to be allocated
+        _collList = null;
+        _collEnd = 0;
+
+        _needRehash = false;
+    }
+
+    private synchronized void mergeChild(BytesToNameCanonicalizer child)
+    {
+        // Only makes sense if child has more entries
+        int childCount = child._count;
+        if (childCount <= _count) {
+            return;
+        }
+
+        /* One caveat: let's try to avoid problems with
+         * degenerate cases of documents with generated "random"
+         * names: for these, symbol tables would bloat indefinitely.
+         * One way to do this is to just purge tables if they grow
+         * too large, and that's what we'll do here.
+         */
+        if (child.size() > MAX_ENTRIES_FOR_REUSE) {
+            /* Should there be a way to get notified about this
+             * event, to log it or such? (as it's somewhat abnormal
+             * thing to happen)
+             */
+            // At any rate, need to clean up the tables, then:
+            initTables(DEFAULT_TABLE_SIZE);
+        } else {
+            _count = child._count;
+            _mainHash = child._mainHash;
+            _mainNames = child._mainNames;
+            _mainHashShared = true; // shouldn't matter for parent
+            _mainNamesShared = true; // - "" -
+            _mainHashMask = child._mainHashMask;
+            _collList = child._collList;
+            _collCount = child._collCount;
+            _collEnd = child._collEnd;
+        }
+    }
+
+    private void markAsShared()
+    {
+        _mainHashShared = true;
+        _mainNamesShared = true;
+        _collListShared = true;
+    }
+
+    /*
+    /**********************************************************
+    /* API, accessors
+    /**********************************************************
+     */
+
+    public int size() { return _count; }
+
+    /**
+     * Method called to check to quickly see if a child symbol table
+     * may have gotten additional entries. Used for checking to see
+     * if a child table should be merged into shared table.
+     */
+    public boolean maybeDirty()
+    {
+        return !_mainHashShared;
+    }
+
+    public static Name getEmptyName()
+    {
+        return Name1.getEmptyName();
+    }
+
+    /**
+     * Finds and returns name matching the specified symbol, if such
+     * name already exists in the table.
+     * If not, will return null.
+     *<p>
+     * Note: separate methods to optimize common case of
+     * short element/attribute names (4 or less ascii characters)
+     *
+     * @param firstQuad int32 containing first 4 bytes of the name;
+     *   if the whole name less than 4 bytes, padded with zero bytes
+     *   in front (zero MSBs, ie. right aligned)
+     *
+     * @return Name matching the symbol passed (or constructed for
+     *   it)
+     */
+    public Name findName(int firstQuad)
+    {
+        int hash = calcHash(firstQuad);
+        int ix = (hash & _mainHashMask);
+        int val = _mainHash[ix];
+        
+        /* High 24 bits of the value are low 24 bits of hash (low 8 bits
+         * are bucket index)... match?
+         */
+        if ((((val >> 8) ^ hash) << 8) == 0) { // match
+            // Ok, but do we have an actual match?
+            Name name = _mainNames[ix];
+            if (name == null) { // main slot empty; can't find
+                return null;
+            }
+            if (name.equals(firstQuad)) {
+                return name;
+            }
+        } else if (val == 0) { // empty slot? no match
+            return null;
+        }
+        // Maybe a spill-over?
+        val &= 0xFF;
+        if (val > 0) { // 0 means 'empty'
+            val -= 1; // to convert from 1-based to 0...
+            Bucket bucket = _collList[val];
+            if (bucket != null) {
+                return bucket.find(hash, firstQuad, 0);
+            }
+        }
+        // Nope, no match whatsoever
+        return null;
+    }
+
+    /**
+     * Finds and returns name matching the specified symbol, if such
+     * name already exists in the table.
+     * If not, will return null.
+     *<p>
+     * Note: separate methods to optimize common case of relatively
+     * short element/attribute names (8 or less ascii characters)
+     *
+     * @param firstQuad int32 containing first 4 bytes of the name.
+     * @param secondQuad int32 containing bytes 5 through 8 of the
+     *   name; if less than 8 bytes, padded with up to 3 zero bytes
+     *   in front (zero MSBs, ie. right aligned)
+     *
+     * @return Name matching the symbol passed (or constructed for
+     *   it)
+     */
+    public Name findName(int firstQuad, int secondQuad)
+    {
+        int hash = calcHash(firstQuad, secondQuad);
+        int ix = (hash & _mainHashMask);
+        int val = _mainHash[ix];
+        
+        /* High 24 bits of the value are low 24 bits of hash (low 8 bits
+         * are bucket index)... match?
+         */
+        if ((((val >> 8) ^ hash) << 8) == 0) { // match
+            // Ok, but do we have an actual match?
+            Name name = _mainNames[ix];
+            if (name == null) { // main slot empty; can't find
+                return null;
+            }
+            if (name.equals(firstQuad, secondQuad)) {
+                return name;
+            }
+        } else if (val == 0) { // empty slot? no match
+            return null;
+        }
+        // Maybe a spill-over?
+        val &= 0xFF;
+        if (val > 0) { // 0 means 'empty'
+            val -= 1; // to convert from 1-based to 0...
+            Bucket bucket = _collList[val];
+            if (bucket != null) {
+                return bucket.find(hash, firstQuad, secondQuad);
+            }
+        }
+        // Nope, no match whatsoever
+        return null;
+    }
+
+    /**
+     * Finds and returns name matching the specified symbol, if such
+     * name already exists in the table; or if not, creates name object,
+     * adds to the table, and returns it.
+     *<p>
+     * Note: this is the general purpose method that can be called for
+     * names of any length. However, if name is less than 9 bytes long,
+     * it is preferable to call the version optimized for short
+     * names.
+     *
+     * @param quads Array of int32s, each of which contain 4 bytes of
+     *   encoded name
+     * @param qlen Number of int32s, starting from index 0, in quads
+     *   parameter
+     *
+     * @return Name matching the symbol passed (or constructed for it)
+     */
+    public Name findName(int[] quads, int qlen)
+    {
+        /* // Not needed, never gets called
+        if (qlen < 3) { // another sanity check
+            return findName(quads[0], (qlen < 2) ? 0 : quads[1]);
+        }
+        */
+        int hash = calcHash(quads, qlen);
+        // (for rest of comments regarding logic, see method above)
+        int ix = (hash & _mainHashMask);
+        int val = _mainHash[ix];
+        if ((((val >> 8) ^ hash) << 8) == 0) {
+            Name name = _mainNames[ix];
+            if (name == null // main slot empty; no collision list then either
+                || name.equals(quads, qlen)) { // should be match, let's verify
+                return name;
+            }
+        } else if (val == 0) { // empty slot? no match
+            return null;
+        }
+        val &= 0xFF;
+        if (val > 0) { // 0 means 'empty'
+            val -= 1; // to convert from 1-based to 0...
+            Bucket bucket = _collList[val];
+            if (bucket != null) {
+                return bucket.find(hash, quads, qlen);
+            }
+        }
+        return null;
+    }
+
+    /*
+    /**********************************************************
+    /* API, mutators
+    /**********************************************************
+     */
+
+    /**
+     * @since 1.6.0
+     */
+    public Name addName(String symbolStr, int q1, int q2)
+    {
+        if (_intern) {
+            symbolStr = InternCache.instance.intern(symbolStr);
+        }
+        int hash = (q2 == 0) ? calcHash(q1) : calcHash(q1, q2);
+        Name symbol = constructName(hash, symbolStr, q1, q2);
+        _addSymbol(hash, symbol);
+        return symbol;
+    }
+    
+    public Name addName(String symbolStr, int[] quads, int qlen)
+    {
+        if (_intern) {
+            symbolStr = InternCache.instance.intern(symbolStr);
+        }
+        int hash = calcHash(quads, qlen);
+        Name symbol = constructName(hash, symbolStr, quads, qlen);
+        _addSymbol(hash, symbol);
+        return symbol;
+    }
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    public final static int calcHash(int firstQuad)
+    {
+        int hash = firstQuad;
+        hash ^= (hash >>> 16); // to xor hi- and low- 16-bits
+        hash ^= (hash >>> 8); // as well as lowest 2 bytes
+        return hash;
+    }
+
+    public final static int calcHash(int firstQuad, int secondQuad)
+    {
+        int hash = (firstQuad * 31) + secondQuad;
+
+        // If this was called for single-quad instance:
+        //int hash = (secondQuad == 0) ? firstQuad : ((firstQuad * 31) + secondQuad);
+
+        hash ^= (hash >>> 16); // to xor hi- and low- 16-bits
+        hash ^= (hash >>> 8); // as well as lowest 2 bytes
+        return hash;
+    }
+
+    public final static int calcHash(int[] quads, int qlen)
+    {
+        // Note: may be called for qlen < 3
+        int hash = quads[0];
+        for (int i = 1; i < qlen; ++i) {
+            hash = (hash * 31) + quads[i];
+        }
+
+        hash ^= (hash >>> 16); // to xor hi- and low- 16-bits
+        hash ^= (hash >>> 8); // as well as lowest 2 bytes
+
+        return hash;
+    }
+
+    /* 26-Nov-2008, tatu: not used currently; if not used in near future,
+     *   let's just delete it.
+     */
+    /*
+    public static int[] calcQuads(byte[] wordBytes)
+    {
+        int blen = wordBytes.length;
+        int[] result = new int[(blen + 3) / 4];
+        for (int i = 0; i < blen; ++i) {
+            int x = wordBytes[i] & 0xFF;
+
+            if (++i < blen) {
+                x = (x << 8) | (wordBytes[i] & 0xFF);
+                if (++i < blen) {
+                    x = (x << 8) | (wordBytes[i] & 0xFF);
+                    if (++i < blen) {
+                        x = (x << 8) | (wordBytes[i] & 0xFF);
+                    }
+                }
+            }
+            result[i >> 2] = x;
+        }
+        return result;
+    }
+    */
+
+    /*
+    /**********************************************************
+    /* Standard methods
+    /**********************************************************
+     */
+
+    /*
+    @Override
+    public String toString()
+    {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[BytesToNameCanonicalizer, size: ");
+        sb.append(_count);
+        sb.append('/');
+        sb.append(_mainHash.length);
+        sb.append(", ");
+        sb.append(_collCount);
+        sb.append(" coll; avg length: ");
+
+        // Average length: minimum of 1 for all (1 == primary hit);
+        // and then 1 per each traversal for collisions/buckets
+        //int maxDist = 1;
+        int pathCount = _count;
+        for (int i = 0; i < _collEnd; ++i) {
+            int spillLen = _collList[i].length();
+            for (int j = 1; j <= spillLen; ++j) {
+                pathCount += j;
+            }
+        }
+        double avgLength;
+
+        if (_count == 0) {
+            avgLength = 0.0;
+        } else {
+            avgLength = (double) pathCount / (double) _count;
+        }
+        // let's round up a bit (two 2 decimal places)
+        //avgLength -= (avgLength % 0.01);
+
+        sb.append(avgLength);
+        sb.append(']');
+        return sb.toString();
+    }
+    */
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+
+    private void _addSymbol(int hash, Name symbol)
+    {
+        if (_mainHashShared) { // always have to modify main entry
+            unshareMain();
+        }
+        // First, do we need to rehash?
+        if (_needRehash) {
+            rehash();
+        }
+
+        ++_count;
+
+        /* Ok, enough about set up: now we need to find the slot to add
+         * symbol in:
+         */
+        int ix = (hash & _mainHashMask);
+        if (_mainNames[ix] == null) { // primary empty?
+            _mainHash[ix] = (hash << 8);
+            if (_mainNamesShared) {
+                unshareNames();
+            }
+            _mainNames[ix] = symbol;
+        } else { // nope, it's a collision, need to spill over
+            /* How about spill-over area... do we already know the bucket
+             * (is the case if it's not the first collision)
+             */
+            if (_collListShared) {
+                unshareCollision(); // also allocates if list was null
+            }
+
+            ++_collCount;
+            int entryValue = _mainHash[ix];
+            int bucket = entryValue & 0xFF;
+            if (bucket == 0) { // first spill over?
+                if (_collEnd <= LAST_VALID_BUCKET) { // yup, still unshared bucket
+                    bucket = _collEnd;
+                    ++_collEnd;
+                    // need to expand?
+                    if (bucket >= _collList.length) {
+                        expandCollision();
+                    }
+                } else { // nope, have to share... let's find shortest?
+                    bucket = findBestBucket();
+                }
+                // Need to mark the entry... and the spill index is 1-based
+                _mainHash[ix] = (entryValue & ~0xFF) | (bucket + 1);
+            } else {
+                --bucket; // 1-based index in value
+            }
+            
+            // And then just need to link the new bucket entry in
+            _collList[bucket] = new Bucket(symbol, _collList[bucket]);
+        }
+
+        /* Ok. Now, do we need a rehash next time? Need to have at least
+         * 50% fill rate no matter what:
+         */
+        {
+            int hashSize = _mainHash.length;
+            if (_count > (hashSize >> 1)) {
+                int hashQuarter = (hashSize >> 2);
+                /* And either strictly above 75% (the usual) or
+                 * just 50%, and collision count >= 25% of total hash size
+                 */
+                if (_count > (hashSize - hashQuarter)) {
+                    _needRehash = true;
+                } else if (_collCount >= hashQuarter) {
+                    _needRehash = true;
+                }
+            }
+        }
+    }
+
+    private void rehash()
+    {
+        _needRehash = false;        
+        // Note: since we'll make copies, no need to unshare, can just mark as such:
+        _mainNamesShared = false;
+
+        /* And then we can first deal with the main hash area. Since we
+         * are expanding linearly (double up), we know there'll be no
+         * collisions during this phase.
+         */
+        int[] oldMainHash = _mainHash;
+        int len = oldMainHash.length;
+        int newLen = len+len;
+
+        /* 13-Mar-2010, tatu: Let's guard against OOME that could be caused by
+         *    large documents with unique (or mostly so) names
+         */
+        if (newLen > MAX_TABLE_SIZE) {
+            nukeSymbols();
+            return;
+        }
+        
+        _mainHash = new int[newLen];
+        _mainHashMask = (newLen - 1);
+        Name[] oldNames = _mainNames;
+        _mainNames = new Name[newLen];
+        int symbolsSeen = 0; // let's do a sanity check
+        for (int i = 0; i < len; ++i) {
+            Name symbol = oldNames[i];
+            if (symbol != null) {
+                ++symbolsSeen;
+                int hash = symbol.hashCode();
+                int ix = (hash & _mainHashMask);
+                _mainNames[ix] = symbol;
+                _mainHash[ix] = hash << 8; // will clear spill index
+            }
+        }
+
+        /* And then the spill area. This may cause collisions, although
+         * not necessarily as many as there were earlier. Let's allocate
+         * same amount of space, however
+         */
+        int oldEnd = _collEnd;
+        if (oldEnd == 0) { // no prior collisions...
+            return;
+        }
+
+        _collCount = 0;
+        _collEnd = 0;
+        _collListShared = false;
+
+        Bucket[] oldBuckets = _collList;
+        _collList = new Bucket[oldBuckets.length];
+        for (int i = 0; i < oldEnd; ++i) {
+            for (Bucket curr = oldBuckets[i]; curr != null; curr = curr._next) {
+                ++symbolsSeen;
+                Name symbol = curr._name;
+                int hash = symbol.hashCode();
+                int ix = (hash & _mainHashMask);
+                int val = _mainHash[ix];
+                if (_mainNames[ix] == null) { // no primary entry?
+                    _mainHash[ix] = (hash << 8);
+                    _mainNames[ix] = symbol;
+                } else { // nope, it's a collision, need to spill over
+                    ++_collCount;
+                    int bucket = val & 0xFF;
+                    if (bucket == 0) { // first spill over?
+                        if (_collEnd <= LAST_VALID_BUCKET) { // yup, still unshared bucket
+                            bucket = _collEnd;
+                            ++_collEnd;
+                            // need to expand?
+                            if (bucket >= _collList.length) {
+                                expandCollision();
+                            }
+                        } else { // nope, have to share... let's find shortest?
+                            bucket = findBestBucket();
+                        }
+                        // Need to mark the entry... and the spill index is 1-based
+                        _mainHash[ix] = (val & ~0xFF) | (bucket + 1);
+                    } else {
+                        --bucket; // 1-based index in value
+                    }
+                    // And then just need to link the new bucket entry in
+                    _collList[bucket] = new Bucket(symbol, _collList[bucket]);
+                }
+            } // for (... buckets in the chain ...)
+        } // for (... list of bucket heads ... )
+
+        if (symbolsSeen != _count) { // sanity check
+            throw new RuntimeException("Internal error: count after rehash "+symbolsSeen+"; should be "+_count);
+        }
+    }
+
+    /**
+     * Helper method called to empty all shared symbols, but to leave
+     * arrays allocated
+     */
+    private void nukeSymbols()
+    {
+        _count = 0;
+        Arrays.fill(_mainHash, 0);
+        Arrays.fill(_mainNames, null);
+        Arrays.fill(_collList, null);
+        _collCount = 0;
+        _collEnd = 0;
+    }
+    
+    /**
+     * Method called to find the best bucket to spill a Name over to:
+     * usually the first bucket that has only one entry, but in general
+     * first one of the buckets with least number of entries
+     */
+    private int findBestBucket()
+    {
+        Bucket[] buckets = _collList;
+        int bestCount = Integer.MAX_VALUE;
+        int bestIx = -1;
+
+        for (int i = 0, len = _collEnd; i < len; ++i) {
+            int count = buckets[i].length();
+            if (count < bestCount) {
+                if (count == 1) { // best possible
+                    return i;
+                }
+                bestCount = count;
+                bestIx = i;
+            }
+        }
+        return bestIx;
+    }
+
+    /**
+     * Method that needs to be called, if the main hash structure
+     * is (may be) shared. This happens every time something is added,
+     * even if addition is to the collision list (since collision list
+     * index comes from lowest 8 bits of the primary hash entry)
+     */
+    private void unshareMain()
+    {
+        int[] old = _mainHash;
+        int len = _mainHash.length;
+
+        _mainHash = new int[len];
+        System.arraycopy(old, 0, _mainHash, 0, len);
+        _mainHashShared = false;
+    }
+
+    private void unshareCollision()
+    {
+        Bucket[] old = _collList;
+        if (old == null) {
+            _collList = new Bucket[INITIAL_COLLISION_LEN];
+        } else {
+            int len = old.length;
+            _collList = new Bucket[len];
+            System.arraycopy(old, 0, _collList, 0, len);
+        }
+        _collListShared = false;
+    }
+
+    private void unshareNames()
+    {
+        Name[] old = _mainNames;
+        int len = old.length;
+        _mainNames = new Name[len];
+        System.arraycopy(old, 0, _mainNames, 0, len);
+        _mainNamesShared = false;
+    }
+
+    private void expandCollision()
+    {
+        Bucket[] old = _collList;
+        int len = old.length;
+        _collList = new Bucket[len+len];
+        System.arraycopy(old, 0, _collList, 0, len);
+    }
+
+
+    /*
+    /**********************************************************
+    /* Constructing name objects
+    /**********************************************************
+     */
+
+    private static Name constructName(int hash, String name, int q1, int q2)
+    {     
+        if (q2 == 0) { // one quad only?
+            return new Name1(name, hash, q1);
+        }
+        return new Name2(name, hash, q1, q2);
+    }
+
+    private static Name constructName(int hash, String name, int[] quads, int qlen)
+    {
+        if (qlen < 4) { // Need to check for 3 quad one, can do others too
+            switch (qlen) {
+            case 1:
+                return new Name1(name, hash, quads[0]);
+            case 2:
+                return new Name2(name, hash, quads[0], quads[1]);
+            case 3:
+                return new Name3(name, hash, quads[0], quads[1], quads[2]);
+            default:
+            }
+        }
+        // Otherwise, need to copy the incoming buffer
+        int[] buf = new int[qlen];
+        for (int i = 0; i < qlen; ++i) {
+            buf[i] = quads[i];
+        }
+        return new NameN(name, hash, buf, qlen);
+    }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    final static class Bucket
+    {
+        protected final Name _name;
+        protected final Bucket _next;
+
+        Bucket(Name name, Bucket next)
+        {
+            _name = name;
+            _next = next;
+        }
+
+        public int length()
+        {
+            int len = 1;
+            for (Bucket curr = _next; curr != null; curr = curr._next) {
+                ++len;
+            }
+            return len;
+        }
+
+        public Name find(int hash, int firstQuad, int secondQuad)
+        {
+            if (_name.hashCode() == hash) {
+                if (_name.equals(firstQuad, secondQuad)) {
+                    return _name;
+                }
+            }
+            for (Bucket curr = _next; curr != null; curr = curr._next) {
+                Name currName = curr._name;
+                if (currName.hashCode() == hash) {
+                    if (currName.equals(firstQuad, secondQuad)) {
+                        return currName;
+                    }
+                }
+            }
+            return null;
+        }
+
+        public Name find(int hash, int[] quads, int qlen)
+        {
+            if (_name.hashCode() == hash) {
+                if (_name.equals(quads, qlen)) {
+                    return _name;
+                }
+            }
+            for (Bucket curr = _next; curr != null; curr = curr._next) {
+                Name currName = curr._name;
+                if (currName.hashCode() == hash) {
+                    if (currName.equals(quads, qlen)) {
+                        return currName;
+                    }
+                }
+            }
+            return null;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/sym/CharsToNameCanonicalizer.java b/src/main/java/com/fasterxml/jackson/core/sym/CharsToNameCanonicalizer.java
new file mode 100644
index 0000000..531e866
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/sym/CharsToNameCanonicalizer.java
@@ -0,0 +1,578 @@
+package com.fasterxml.jackson.core.sym;
+
+import java.util.Arrays;
+
+import com.fasterxml.jackson.core.util.InternCache;
+
+/**
+ * This class is a kind of specialized type-safe Map, from char array to
+ * String value. Specialization means that in addition to type-safety
+ * and specific access patterns (key char array, Value optionally interned
+ * String; values added on access if necessary), and that instances are
+ * meant to be used concurrently, but by using well-defined mechanisms
+ * to obtain such concurrently usable instances. Main use for the class
+ * is to store symbol table information for things like compilers and
+ * parsers; especially when number of symbols (keywords) is limited.
+ *<p>
+ * For optimal performance, usage pattern should be one where matches
+ * should be very common (esp. after "warm-up"), and as with most hash-based
+ * maps/sets, that hash codes are uniformly distributed. Also, collisions
+ * are slightly more expensive than with HashMap or HashSet, since hash codes
+ * are not used in resolving collisions; that is, equals() comparison is
+ * done with all symbols in same bucket index.<br />
+ * Finally, rehashing is also more expensive, as hash codes are not
+ * stored; rehashing requires all entries' hash codes to be recalculated.
+ * Reason for not storing hash codes is reduced memory usage, hoping
+ * for better memory locality.
+ *<p>
+ * Usual usage pattern is to create a single "master" instance, and either
+ * use that instance in sequential fashion, or to create derived "child"
+ * instances, which after use, are asked to return possible symbol additions
+ * to master instance. In either case benefit is that symbol table gets
+ * initialized so that further uses are more efficient, as eventually all
+ * symbols needed will already be in symbol table. At that point no more
+ * Symbol String allocations are needed, nor changes to symbol table itself.
+ *<p>
+ * Note that while individual SymbolTable instances are NOT thread-safe
+ * (much like generic collection classes), concurrently used "child"
+ * instances can be freely used without synchronization. However, using
+ * master table concurrently with child instances can only be done if
+ * access to master instance is read-only (ie. no modifications done).
+ */
+
+public final class CharsToNameCanonicalizer
+{
+    /**
+     * Default initial table size. Shouldn't be miniscule (as there's
+     * cost to both array realloc and rehashing), but let's keep
+     * it reasonably small nonetheless. For systems that properly 
+     * reuse factories it doesn't matter either way; but when
+     * recreating factories often, initial overhead may dominate.
+     */
+    protected static final int DEFAULT_TABLE_SIZE = 64;
+
+    /**
+     * Let's not expand symbol tables past some maximum size;
+     * this should protected against OOMEs caused by large documents
+     * with uniquer (~= random) names.
+     * 
+     * @since 1.5
+     */
+    protected static final int MAX_TABLE_SIZE = 0x10000; // 64k entries == 256k mem
+
+    /**
+     * Let's only share reasonably sized symbol tables. Max size set to 3/4 of 16k;
+     * this corresponds to 64k main hash index. This should allow for enough distinct
+     * names for almost any case.
+     */
+    final static int MAX_ENTRIES_FOR_REUSE = 12000;
+
+    final static CharsToNameCanonicalizer sBootstrapSymbolTable;
+    static {
+        sBootstrapSymbolTable = new CharsToNameCanonicalizer();
+    }
+
+    /*
+    /****************************************
+    /* Configuration:
+    /****************************************
+     */
+
+    /**
+     * Sharing of learnt symbols is done by optional linking of symbol
+     * table instances with their parents. When parent linkage is
+     * defined, and child instance is released (call to <code>release</code>),
+     * parent's shared tables may be updated from the child instance.
+     */
+    protected CharsToNameCanonicalizer _parent;
+
+    /**
+     * Whether canonical symbol Strings are to be intern()ed before added
+     * to the table or not
+     */
+    final protected boolean _intern;
+
+    /**
+     * Whether any canonicalization should be attempted (whether using
+     * intern or not)
+     */
+    final protected boolean _canonicalize;
+    
+    /*
+    /****************************************
+    /* Actual symbol table data:
+    /****************************************
+     */
+
+    /**
+     * Primary matching symbols; it's expected most match occur from
+     * here.
+     */
+    protected String[] _symbols;
+
+    /**
+     * Overflow buckets; if primary doesn't match, lookup is done
+     * from here.
+     *<p>
+     * Note: Number of buckets is half of number of symbol entries, on
+     * assumption there's less need for buckets.
+     */
+    protected Bucket[] _buckets;
+
+    /**
+     * Current size (number of entries); needed to know if and when
+     * rehash.
+     */
+    protected int _size;
+
+    /**
+     * Limit that indicates maximum size this instance can hold before
+     * it needs to be expanded and rehashed. Calculated using fill
+     * factor passed in to constructor.
+     */
+    protected int _sizeThreshold;
+
+    /**
+     * Mask used to get index from hash values; equal to
+     * <code>_buckets.length - 1</code>, when _buckets.length is
+     * a power of two.
+     */
+    protected int _indexMask;
+
+    /*
+    /****************************************
+    /* State regarding shared arrays
+    /****************************************
+     */
+
+    /**
+     * Flag that indicates if any changes have been made to the data;
+     * used to both determine if bucket array needs to be copied when
+     * (first) change is made, and potentially if updated bucket list
+     * is to be resync'ed back to master instance.
+     */
+    protected boolean _dirty;
+
+    /*
+    /****************************************
+    /* Life-cycle
+    /****************************************
+     */
+
+    /**
+     * Method called to create root canonicalizer for a {@link com.fasterxml.jackson.core.JsonFactory}
+     * instance. Root instance is never used directly; its main use is for
+     * storing and sharing underlying symbol arrays as needed.
+     */
+    public static CharsToNameCanonicalizer createRoot()
+    {
+        return sBootstrapSymbolTable.makeOrphan();
+    }
+
+    /**
+     * Main method for constructing a master symbol table instance.
+     *
+     * @param initialSize Minimum initial size for bucket array; internally
+     *   will always use a power of two equal to or bigger than this value.
+     */
+    private CharsToNameCanonicalizer()
+    {
+        // these settings don't really matter for the bootstrap instance
+        _canonicalize = true;
+        _intern = true;
+        // And we'll also set flags so no copying of buckets is needed:
+        _dirty = true;
+        initTables(DEFAULT_TABLE_SIZE);
+    }
+
+    private void initTables(int initialSize)
+    {
+        _symbols = new String[initialSize];
+        _buckets = new Bucket[initialSize >> 1];
+        // Mask is easy to calc for powers of two.
+        _indexMask = initialSize - 1;
+        _size = 0;
+        // Hard-coded fill factor is 75%
+        _sizeThreshold = (initialSize - (initialSize >> 2));
+    }
+
+    /**
+     * Internal constructor used when creating child instances.
+     */
+    private CharsToNameCanonicalizer(CharsToNameCanonicalizer parent,
+            boolean canonicalize, boolean intern,
+            String[] symbols, Bucket[] buckets, int size)
+    {
+        _parent = parent;
+        _canonicalize = canonicalize;
+        _intern = intern;
+
+        _symbols = symbols;
+        _buckets = buckets;
+        _size = size;
+        // Hard-coded fill factor, 75%
+        int arrayLen = (symbols.length);
+        _sizeThreshold = arrayLen - (arrayLen >> 2);
+        _indexMask =  (arrayLen - 1);
+
+        // Need to make copies of arrays, if/when adding new entries
+        _dirty = false;
+    }
+
+    /**
+     * "Factory" method; will create a new child instance of this symbol
+     * table. It will be a copy-on-write instance, ie. it will only use
+     * read-only copy of parent's data, but when changes are needed, a
+     * copy will be created.
+     *<p>
+     * Note: while this method is synchronized, it is generally not
+     * safe to both use makeChild/mergeChild, AND to use instance
+     * actively. Instead, a separate 'root' instance should be used
+     * on which only makeChild/mergeChild are called, but instance itself
+     * is not used as a symbol table.
+     */
+    public synchronized CharsToNameCanonicalizer makeChild(boolean canonicalize, boolean intern)
+    {
+        return new CharsToNameCanonicalizer(this, canonicalize, intern, _symbols, _buckets, _size);
+    }
+
+    private CharsToNameCanonicalizer makeOrphan()
+    {
+        return new CharsToNameCanonicalizer(null, true, true, _symbols, _buckets, _size);
+    }
+
+    /**
+     * Method that allows contents of child table to potentially be
+     * "merged in" with contents of this symbol table.
+     *<p>
+     * Note that caller has to make sure symbol table passed in is
+     * really a child or sibling of this symbol table.
+     */
+    private synchronized void mergeChild(CharsToNameCanonicalizer child)
+    {
+        /* One caveat: let's try to avoid problems with
+         * degenerate cases of documents with generated "random"
+         * names: for these, symbol tables would bloat indefinitely.
+         * One way to do this is to just purge tables if they grow
+         * too large, and that's what we'll do here.
+         */
+        if (child.size() > MAX_ENTRIES_FOR_REUSE) {
+            /* Should there be a way to get notified about this
+             * event, to log it or such? (as it's somewhat abnormal
+             * thing to happen)
+             */
+            // At any rate, need to clean up the tables, then:
+            initTables(DEFAULT_TABLE_SIZE);
+        } else {
+            /* Otherwise, we'll merge changed stuff in, if there are
+             * more entries (which may not be the case if one of siblings
+             * has added symbols first or such)
+             */
+            if (child.size() <= size()) { // nothing to add
+                return;
+            }
+            // Okie dokie, let's get the data in!
+            _symbols = child._symbols;
+            _buckets = child._buckets;
+            _size = child._size;
+            _sizeThreshold = child._sizeThreshold;
+            _indexMask = child._indexMask;
+        }
+        /* Dirty flag... well, let's just clear it, to force copying just
+         * in case. Shouldn't really matter, for master tables.
+         * (which this is, given something is merged to it etc)
+         */
+        _dirty = false;
+    }
+
+    public void release()
+    {
+        // If nothing has been added, nothing to do
+        if (!maybeDirty()) {
+            return;
+        }
+        if (_parent != null) {
+            _parent.mergeChild(this);
+            /* Let's also mark this instance as dirty, so that just in
+             * case release was too early, there's no corruption
+             * of possibly shared data.
+             */
+            _dirty = false;
+        }
+    }
+
+    /*
+    /****************************************
+    /* Public API, generic accessors:
+    /****************************************
+     */
+
+    public int size() { return _size; }
+
+    public boolean maybeDirty() { return _dirty; }
+
+    /*
+    /****************************************
+    /* Public API, accessing symbols:
+    /****************************************
+     */
+
+    public String findSymbol(char[] buffer, int start, int len, int hash)
+    {
+        if (len < 1) { // empty Strings are simplest to handle up front
+            return "";
+        }
+        if (!_canonicalize) { // [JACKSON-259]
+            return new String(buffer, start, len);
+        }
+
+        hash &= _indexMask;
+
+        String sym = _symbols[hash];
+
+        // Optimal case; checking existing primary symbol for hash index:
+        if (sym != null) {
+            // Let's inline primary String equality checking:
+            if (sym.length() == len) {
+                int i = 0;
+                do {
+                    if (sym.charAt(i) != buffer[start+i]) {
+                        break;
+                    }
+                } while (++i < len);
+                // Optimal case; primary match found
+                if (i == len) {
+                    return sym;
+                }
+            }
+            // How about collision bucket?
+            Bucket b = _buckets[hash >> 1];
+            if (b != null) {
+                sym = b.find(buffer, start, len);
+                if (sym != null) {
+                    return sym;
+                }
+            }
+        }
+
+        if (!_dirty) { //need to do copy-on-write?
+            copyArrays();
+            _dirty = true;
+        } else if (_size >= _sizeThreshold) { // Need to expand?
+           rehash();
+            /* Need to recalc hash; rare occurence (index mask has been
+             * recalculated as part of rehash)
+             */
+            hash = calcHash(buffer, start, len) & _indexMask;
+        }
+        ++_size;
+
+        String newSymbol = new String(buffer, start, len);
+        if (_intern) {
+            newSymbol = InternCache.instance.intern(newSymbol);
+        }
+        // Ok; do we need to add primary entry, or a bucket?
+        if (_symbols[hash] == null) {
+            _symbols[hash] = newSymbol;
+        } else {
+            int bix = hash >> 1;
+            _buckets[bix] = new Bucket(newSymbol, _buckets[bix]);
+        }
+
+        return newSymbol;
+    }
+
+    /**
+     * Implementation of a hashing method for variable length
+     * Strings. Most of the time intention is that this calculation
+     * is done by caller during parsing, not here; however, sometimes
+     * it needs to be done for parsed "String" too.
+     *
+     * @param len Length of String; has to be at least 1 (caller guarantees
+     *   this pre-condition)
+     */
+    public static int calcHash(char[] buffer, int start, int len) {
+        int hash = (int) buffer[0];
+        for (int i = 1; i < len; ++i) {
+            hash = (hash * 31) + (int) buffer[i];
+        }
+        return hash;
+    }
+
+    public static int calcHash(String key) {
+        int hash = (int) key.charAt(0);
+        for (int i = 1, len = key.length(); i < len; ++i) {
+            hash = (hash * 31) + (int) key.charAt(i);
+
+        }
+        return hash;
+    }
+
+    /*
+    /****************************************
+    /* Internal methods
+    /****************************************
+     */
+
+    /**
+     * Method called when copy-on-write is needed; generally when first
+     * change is made to a derived symbol table.
+     */
+    private void copyArrays() {
+        String[] oldSyms = _symbols;
+        int size = oldSyms.length;
+        _symbols = new String[size];
+        System.arraycopy(oldSyms, 0, _symbols, 0, size);
+        Bucket[] oldBuckets = _buckets;
+        size = oldBuckets.length;
+        _buckets = new Bucket[size];
+        System.arraycopy(oldBuckets, 0, _buckets, 0, size);
+    }
+
+    /**
+     * Method called when size (number of entries) of symbol table grows
+     * so big that load factor is exceeded. Since size has to remain
+     * power of two, arrays will then always be doubled. Main work
+     * is really redistributing old entries into new String/Bucket
+     * entries.
+     */
+    private void rehash()
+    {
+        int size = _symbols.length;
+        int newSize = size + size;
+
+        /* 12-Mar-2010, tatu: Let's actually limit maximum size we are
+         *    prepared to use, to guard against OOME in case of unbounded
+         *    name sets (unique [non-repeating] names)
+         */
+        if (newSize > MAX_TABLE_SIZE) {
+            /* If this happens, there's no point in either growing or
+             * shrinking hash areas. Rather, it's better to just clean
+             * them up for reuse.
+             */
+            _size = 0;
+            Arrays.fill(_symbols, null);
+            Arrays.fill(_buckets, null);
+            _dirty = true;
+            return;
+        }
+        
+        String[] oldSyms = _symbols;
+        Bucket[] oldBuckets = _buckets;
+        _symbols = new String[newSize];
+        _buckets = new Bucket[newSize >> 1];
+        // Let's update index mask, threshold, now (needed for rehashing)
+        _indexMask = newSize - 1;
+        _sizeThreshold += _sizeThreshold;
+        
+        int count = 0; // let's do sanity check
+
+        /* Need to do two loops, unfortunately, since spill-over area is
+         * only half the size:
+         */
+        for (int i = 0; i < size; ++i) {
+            String symbol = oldSyms[i];
+            if (symbol != null) {
+                ++count;
+                int index = calcHash(symbol) & _indexMask;
+                if (_symbols[index] == null) {
+                    _symbols[index] = symbol;
+                } else {
+                    int bix = index >> 1;
+                    _buckets[bix] = new Bucket(symbol, _buckets[bix]);
+                }
+            }
+        }
+
+        size >>= 1;
+        for (int i = 0; i < size; ++i) {
+            Bucket b = oldBuckets[i];
+            while (b != null) {
+                ++count;
+                String symbol = b.getSymbol();
+                int index = calcHash(symbol) & _indexMask;
+                if (_symbols[index] == null) {
+                    _symbols[index] = symbol;
+                } else {
+                    int bix = index >> 1;
+                    _buckets[bix] = new Bucket(symbol, _buckets[bix]);
+                }
+                b = b.getNext();
+            }
+        }
+
+        if (count != _size) {
+            throw new Error("Internal error on SymbolTable.rehash(): had "+_size+" entries; now have "+count+".");
+        }
+    }
+
+    /*
+    /****************************************
+    /* Bucket class
+    /****************************************
+     */
+
+    /**
+     * This class is a symbol table entry. Each entry acts as a node
+     * in a linked list.
+     */
+    static final class Bucket {
+        private final String _symbol;
+        private final Bucket mNext;
+
+        public Bucket(String symbol, Bucket next) {
+            _symbol = symbol;
+            mNext = next;
+        }
+
+        public String getSymbol() { return _symbol; }
+        public Bucket getNext() { return mNext; }
+
+        public String find(char[] buf, int start, int len) {
+            String sym = _symbol;
+            Bucket b = mNext;
+
+            while (true) { // Inlined equality comparison:
+                if (sym.length() == len) {
+                    int i = 0;
+                    do {
+                        if (sym.charAt(i) != buf[start+i]) {
+                            break;
+                        }
+                    } while (++i < len);
+                    if (i == len) {
+                        return sym;
+                    }
+                }
+                if (b == null) {
+                    break;
+                }
+                sym = b.getSymbol();
+                b = b.getNext();
+            }
+            return null;
+        }
+
+    /* 26-Nov-2008, tatu: not used currently; if not used in near future,
+     *   let's just delete it.
+     */
+        /*
+        public String find(String str) {
+            String sym = _symbol;
+            Bucket b = mNext;
+
+            while (true) {
+                if (sym.equals(str)) {
+                    return sym;
+                }
+                if (b == null) {
+                    break;
+                }
+                sym = b.getSymbol();
+                b = b.getNext();
+            }
+            return null;
+        }
+        */
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/sym/Name.java b/src/main/java/com/fasterxml/jackson/core/sym/Name.java
new file mode 100644
index 0000000..d26f55d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/sym/Name.java
@@ -0,0 +1,50 @@
+package com.fasterxml.jackson.core.sym;
+
+/**
+ * Base class for tokenized names (key strings in objects) that have
+ * been tokenized from byte-based input sources (like
+ * {@link java.io.InputStream}.
+ *
+ * @author Tatu Saloranta
+ */
+public abstract class Name
+{
+    protected final String _name;
+
+    protected final int _hashCode;
+
+    protected Name(String name, int hashCode) {
+        _name = name;
+        _hashCode = hashCode;
+    }
+
+    public String getName() { return _name; }
+
+    /*
+    /**********************************************************
+    /* Methods for package/core parser
+    /**********************************************************
+     */
+
+    public abstract boolean equals(int quad1);
+
+    public abstract boolean equals(int quad1, int quad2);
+
+    public abstract boolean equals(int[] quads, int qlen);
+
+    /*
+    /**********************************************************
+    /* Overridden standard methods
+    /**********************************************************
+     */
+
+    @Override public String toString() { return _name; }
+
+    @Override public final int hashCode() { return _hashCode; }
+
+    @Override public boolean equals(Object o)
+    {
+        // Canonical instances, can usually just do identity comparison
+        return (o == this);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/sym/Name1.java b/src/main/java/com/fasterxml/jackson/core/sym/Name1.java
new file mode 100644
index 0000000..d260a9f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/sym/Name1.java
@@ -0,0 +1,44 @@
+package com.fasterxml.jackson.core.sym;
+
+/**
+ * Specialized implementation of PName: can be used for short Strings
+ * that consists of at most 4 bytes. Usually this means short
+ * ascii-only names.
+ *<p>
+ * The reason for such specialized classes is mostly space efficiency;
+ * and to a lesser degree performance. Both are achieved for short
+ * Strings by avoiding another level of indirection (via quad arrays)
+ */
+public final class Name1
+    extends Name
+{
+    final static Name1 sEmptyName = new Name1("", 0, 0);
+
+    final int mQuad;
+
+    Name1(String name, int hash, int quad)
+    {
+        super(name, hash);
+        mQuad = quad;
+    }
+
+    final static Name1 getEmptyName() { return sEmptyName; }
+
+    @Override
+    public boolean equals(int quad)
+    {
+        return (quad == mQuad);
+    }
+
+    @Override
+    public boolean equals(int quad1, int quad2)
+    {
+        return (quad1 == mQuad) && (quad2 == 0);
+    }
+
+    @Override
+    public boolean equals(int[] quads, int qlen)
+    {
+        return (qlen == 1 && quads[0] == mQuad);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/sym/Name2.java b/src/main/java/com/fasterxml/jackson/core/sym/Name2.java
new file mode 100644
index 0000000..cc425fb
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/sym/Name2.java
@@ -0,0 +1,40 @@
+package com.fasterxml.jackson.core.sym;
+
+/**
+ * Specialized implementation of PName: can be used for short Strings
+ * that consists of 5 to 8 bytes. Usually this means relatively short
+ * ascii-only names.
+ *<p>
+ * The reason for such specialized classes is mostly space efficiency;
+ * and to a lesser degree performance. Both are achieved for short
+ * Strings by avoiding another level of indirection (via quad arrays)
+ */
+public final class Name2
+    extends Name
+{
+    final int mQuad1;
+
+    final int mQuad2;
+
+    Name2(String name, int hash, int quad1, int quad2)
+    {
+        super(name, hash);
+        mQuad1 = quad1;
+        mQuad2 = quad2;
+    }
+
+    @Override
+    public boolean equals(int quad) { return false; }
+
+    @Override
+    public boolean equals(int quad1, int quad2)
+    {
+        return (quad1 == mQuad1) && (quad2 == mQuad2);
+    }
+
+    @Override
+    public boolean equals(int[] quads, int qlen)
+    {
+        return (qlen == 2 && quads[0] == mQuad1 && quads[1] == mQuad2);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/sym/Name3.java b/src/main/java/com/fasterxml/jackson/core/sym/Name3.java
new file mode 100644
index 0000000..fe3a4d7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/sym/Name3.java
@@ -0,0 +1,39 @@
+package com.fasterxml.jackson.core.sym;
+
+/**
+ * Specialized implementation of PName: can be used for short Strings
+ * that consists of 9 to 12 bytes. It's the longest special purpose
+ * implementaion; longer ones are expressed using {@link NameN}.
+ */
+public final class Name3
+    extends Name
+{
+    final int mQuad1;
+    final int mQuad2;
+    final int mQuad3;
+
+    Name3(String name, int hash, int q1, int q2, int q3)
+    {
+        super(name, hash);
+        mQuad1 = q1;
+        mQuad2 = q2;
+        mQuad3 = q3;
+    }
+
+    // Implies quad length == 1, never matches
+    @Override
+    public boolean equals(int quad) { return false; }
+
+    // Implies quad length == 2, never matches
+    @Override
+    public boolean equals(int quad1, int quad2) { return false; }
+
+    @Override
+    public boolean equals(int[] quads, int qlen)
+    {
+        return (qlen == 3)
+            && (quads[0] == mQuad1)
+            && (quads[1] == mQuad2)
+            && (quads[2] == mQuad3);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/sym/NameN.java b/src/main/java/com/fasterxml/jackson/core/sym/NameN.java
new file mode 100644
index 0000000..3040104
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/sym/NameN.java
@@ -0,0 +1,68 @@
+package com.fasterxml.jackson.core.sym;
+
+/**
+ * Generic implementation of PName used for "long" names, where long
+ * means that its byte (UTF-8) representation is 13 bytes or more.
+ */
+public final class NameN
+    extends Name
+{
+    final int[] mQuads;
+    final int mQuadLen;
+
+    NameN(String name, int hash, int[] quads, int quadLen)
+    {
+        super(name, hash);
+        /* We have specialized implementations for shorter
+         * names, so let's not allow runt instances here
+         */
+        if (quadLen < 3) {
+            throw new IllegalArgumentException("Qlen must >= 3");
+        }
+        mQuads = quads;
+        mQuadLen = quadLen;
+    }
+
+    // Implies quad length == 1, never matches
+    @Override
+	public boolean equals(int quad) { return false; }
+
+    // Implies quad length == 2, never matches
+    @Override
+	public boolean equals(int quad1, int quad2) { return false; }
+
+    @Override
+	public boolean equals(int[] quads, int qlen)
+    {
+        if (qlen != mQuadLen) {
+            return false;
+        }
+
+        /* 26-Nov-2008, tatus: Strange, but it does look like
+         *   unrolling here is counter-productive, reducing
+         *   speed. Perhaps it prevents inlining by HotSpot or
+         *   something...
+         */
+        // Will always have >= 3 quads, can unroll
+        /*
+        if (quads[0] == mQuads[0]
+            && quads[1] == mQuads[1]
+            && quads[2] == mQuads[2]) {
+            for (int i = 3; i < qlen; ++i) {
+                if (quads[i] != mQuads[i]) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        */
+
+        // or simpler way without unrolling:
+        for (int i = 0; i < qlen; ++i) {
+            if (quads[i] != mQuads[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/sym/package-info.java b/src/main/java/com/fasterxml/jackson/core/sym/package-info.java
new file mode 100644
index 0000000..b0ae032
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/sym/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Internal implementation classes for efficient handling of
+ * of symbols in JSON (field names in Objects)
+ */
+package com.fasterxml.jackson.core.sym;
diff --git a/src/main/java/com/fasterxml/jackson/core/type/JavaType.java b/src/main/java/com/fasterxml/jackson/core/type/JavaType.java
new file mode 100644
index 0000000..86bc769
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/type/JavaType.java
@@ -0,0 +1,432 @@
+package com.fasterxml.jackson.core.type;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * Base class for type token classes used both to contain information
+ * and as keys for deserializers.
+ *<p>
+ * Instances can (only) be constructed by
+ * {@link org.codehaus.jackson.map.type.TypeFactory}.
+ */
+public abstract class JavaType
+{
+    /**
+     * This is the nominal type-erased Class that would be close to the
+     * type represented (but not exactly type, due to type erasure: type
+     * instance may have more information on this).
+     * May be an interface or abstract class, so instantiation
+     * may not be possible.
+     */
+    protected final Class<?> _class;
+
+    protected final int _hashCode;
+
+    /**
+     * Optional handler (codec) that can be attached to indicate 
+     * what to use for handling (serializing, deserializing) values of
+     * this specific type.
+     *<p>
+     * Note: untyped (i.e. caller has to cast) because it is used for
+     * different kinds of handlers, with unrelated types.
+     *<p>
+     * TODO: make final and possibly promote to sub-classes
+     */
+    protected /*final*/ Object _valueHandler;
+
+    /**
+     * Optional handler that can be attached to indicate how to handle
+     * additional type metadata associated with this type.
+     *<p>
+     * Note: untyped (i.e. caller has to cast) because it is used for
+     * different kinds of handlers, with unrelated types.
+     *<p>
+     * TODO: make final and possibly promote to sub-classes
+     */
+    protected /*final*/ Object _typeHandler;
+    
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    /**
+     * @param raw "Raw" (type-erased) class for this type
+     * @param additionalHash Additional hash code to use, in addition
+     *   to hash code of the class name 
+     */
+    protected JavaType(Class<?> raw, int additionalHash)
+    {
+        _class = raw;
+        _hashCode = raw.getName().hashCode() + additionalHash;
+        _valueHandler = null;
+        _typeHandler = null;
+    }
+    
+    /**
+     * "Copy method" that will construct a new instance that is identical to
+     * this instance, except that it will have specified type handler assigned.
+     * 
+     * @return Newly created type instance
+     */
+    public abstract JavaType withTypeHandler(Object h);
+
+    /**
+     * "Copy method" that will construct a new instance that is identical to
+     * this instance, except that its content type will have specified
+     * type handler assigned.
+     * 
+     * @return Newly created type instance
+     */
+    public abstract JavaType withContentTypeHandler(Object h);
+
+    /**
+     * "Copy method" that will construct a new instance that is identical to
+     * this instance, except that it will have specified value handler assigned.
+     * 
+     * @return Newly created type instance
+     */
+    public abstract JavaType withValueHandler(Object h);
+
+    /**
+     * "Copy method" that will construct a new instance that is identical to
+     * this instance, except that it will have specified content value handler assigned.
+     * 
+     * @return Newly created type instance
+     */
+    public abstract JavaType withContentValueHandler(Object h);
+    
+    /*
+    /**********************************************************
+    /* Type coercion fluent factory methods
+    /**********************************************************
+     */
+    
+    /**
+     * Method that can be called to do a "narrowing" conversions; that is,
+     * to return a type with a raw class that is assignable to the raw
+     * class of this type. If this is not possible, an
+     * {@link IllegalArgumentException} is thrown.
+     * If class is same as the current raw class, instance itself is
+     * returned.
+     */
+    public JavaType narrowBy(Class<?> subclass)
+    {
+        // First: if same raw class, just return this instance
+        if (subclass == _class) {
+            return this;
+        }
+        // Otherwise, ensure compatibility
+        _assertSubclass(subclass, _class);
+        JavaType result = _narrow(subclass);
+
+        // TODO: these checks should NOT actually be needed; above should suffice:
+        if (_valueHandler != result.getValueHandler()) {
+            result = result.withValueHandler(_valueHandler);
+        }
+        if (_typeHandler != result.getTypeHandler()) {
+            result = result.withTypeHandler(_typeHandler);
+        }
+        return result;
+    }
+
+    /**
+     * More efficient version of {@link #narrowBy}, called by
+     * internal framework in cases where compatibility checks
+     * are to be skipped.
+     */
+    public JavaType forcedNarrowBy(Class<?> subclass)
+    {
+        if (subclass == _class) { // can still optimize for simple case
+            return this;
+        }
+        JavaType result = _narrow(subclass);
+        // TODO: these checks should NOT actually be needed; above should suffice:
+        if (_valueHandler != result.getValueHandler()) {
+            result = result.withValueHandler(_valueHandler);
+        }
+        if (_typeHandler != result.getTypeHandler()) {
+            result = result.withTypeHandler(_typeHandler);
+        }
+        return result;
+    }
+
+    /**
+     * Method that can be called to do a "widening" conversions; that is,
+     * to return a type with a raw class that could be assigned from this
+     * type.
+     * If such conversion is not possible, an
+     * {@link IllegalArgumentException} is thrown.
+     * If class is same as the current raw class, instance itself is
+     * returned.
+     */
+    public JavaType widenBy(Class<?> superclass)
+    {
+        // First: if same raw class, just return this instance
+        if (superclass == _class) {
+            return this;
+        }
+        // Otherwise, ensure compatibility
+        _assertSubclass(_class, superclass);
+        return _widen(superclass);
+    }
+
+    protected abstract JavaType _narrow(Class<?> subclass);
+
+    /**
+     *<p>
+     * Default implementation is just to call {@link #_narrow}, since
+     * underlying type construction is usually identical
+     */
+    protected JavaType _widen(Class<?> superclass) {
+        return _narrow(superclass);
+    }
+
+    public abstract JavaType narrowContentsBy(Class<?> contentClass);
+
+    public abstract JavaType widenContentsBy(Class<?> contentClass);
+    
+    /*
+    /**********************************************************
+    /* Public API, simple accessors
+    /**********************************************************
+     */
+
+    public final Class<?> getRawClass() { return _class; }
+
+    /**
+     * Method that can be used to check whether this type has
+     * specified Class as its type erasure. Put another way, returns
+     * true if instantiation of this Type is given (type-erased) Class.
+     */
+    public final boolean hasRawClass(Class<?> clz) {
+        return _class == clz;
+    }
+
+    public boolean isAbstract() {
+        return Modifier.isAbstract(_class.getModifiers());
+    }
+
+    /**
+     * Convenience method for checking whether underlying Java type
+     * is a concrete class or not: abstract classes and interfaces
+     * are not.
+     */
+    public boolean isConcrete() {
+        int mod = _class.getModifiers();
+        if ((mod & (Modifier.INTERFACE | Modifier.ABSTRACT)) == 0) {
+            return true;
+        }
+        /* 19-Feb-2010, tatus: Holy mackarel; primitive types
+         *    have 'abstract' flag set...
+         */
+        if (_class.isPrimitive()) {
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isThrowable() {
+        return Throwable.class.isAssignableFrom(_class);
+    }
+
+    public boolean isArrayType() { return false; }
+
+    public final boolean isEnumType() { return _class.isEnum(); }
+
+    public final boolean isInterface() { return _class.isInterface(); }
+
+    public final boolean isPrimitive() { return _class.isPrimitive(); }
+
+    public final boolean isFinal() { return Modifier.isFinal(_class.getModifiers()); }
+
+    /**
+     * @return True if type represented is a container type; this includes
+     *    array, Map and Collection types.
+     */
+    public abstract boolean isContainerType();
+
+    /**
+     * @return True if type is either true {@link java.util.Collection} type,
+     *    or something similar (meaning it has at least one type parameter,
+     *    which describes type of contents)
+     */
+    public boolean isCollectionLikeType() { return false; }
+
+    /**
+     * @return True if type is either true {@link java.util.Map} type,
+     *    or something similar (meaning it has at least two type parameter;
+     *    first one describing key type, second value type)
+     */
+    public boolean isMapLikeType() { return false; }
+    
+    /*
+    /**********************************************************
+    /* Public API, type parameter access
+    /**********************************************************
+     */
+    
+    /**
+     * Method that can be used to find out if the type directly declares generic
+     * parameters (for its direct super-class and/or super-interfaces).
+     */
+    public boolean hasGenericTypes()
+    {
+        return containedTypeCount() > 0;
+    }
+    
+    /**
+     * Method for accessing key type for this type, assuming type
+     * has such a concept (only Map types do)
+     */
+    public JavaType getKeyType() { return null; }
+
+    /**
+     * Method for accessing content type of this type, if type has
+     * such a thing: simple types do not, structured types do
+     * (like arrays, Collections and Maps)
+     */
+    public JavaType getContentType() { return null; }
+
+    /**
+     * Method for checking how many contained types this type
+     * has. Contained types are usually generic types, so that
+     * generic Maps have 2 contained types.
+     */
+    public int containedTypeCount() { return 0; }
+
+    /**
+     * Method for accessing definitions of contained ("child")
+     * types.
+     * 
+     * @param index Index of contained type to return
+     * 
+     * @return Contained type at index, or null if no such type
+     *    exists (no exception thrown)
+     */
+    public JavaType containedType(int index) { return null; }
+    
+    /**
+     * Method for accessing name of type variable in indicated
+     * position. If no name is bound, will use placeholders (derived
+     * from 0-based index); if no type variable or argument exists
+     * with given index, null is returned.
+     * 
+     * @param index Index of contained type to return
+     * 
+     * @return Contained type at index, or null if no such type
+     *    exists (no exception thrown)
+     */
+    public String containedTypeName(int index) { return null; }
+
+    /*
+    /**********************************************************
+    /* Semi-public API, accessing handlers
+    /**********************************************************
+     */
+    
+    /**
+     * Method for accessing value handler associated with this type, if any
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getValueHandler() { return (T) _valueHandler; }
+
+    /**
+     * Method for accessing type handler associated with this type, if any
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T getTypeHandler() { return (T) _typeHandler; }
+
+    /*
+    /**********************************************************
+    /* Support for producing signatures (1.6+)
+    /**********************************************************
+     */
+    
+    /**
+     * Method that can be used to serialize type into form from which
+     * it can be fully deserialized from at a later point (using
+     * <code>TypeFactory</code> from mapper package).
+     * For simple types this is same as calling
+     * {@link Class#getName}, but for structured types it may additionally
+     * contain type information about contents.
+     */
+    public abstract String toCanonical();
+
+    /**
+     * Method for accessing signature that contains generic
+     * type information, in form compatible with JVM 1.5
+     * as per JLS. It is a superset of {@link #getErasedSignature},
+     * in that generic information can be automatically removed
+     * if necessary (just remove outermost
+     * angle brackets along with content inside)
+     */
+    public String getGenericSignature() {
+        StringBuilder sb = new StringBuilder(40);
+        getGenericSignature(sb);
+        return sb.toString();        
+    }
+
+    /**
+     * 
+     * @param sb StringBuilder to append signature to
+     * 
+     * @return StringBuilder that was passed in; returned to allow
+     * call chaining
+     */
+    public abstract StringBuilder getGenericSignature(StringBuilder sb);
+    
+    /**
+     * Method for accessing signature without generic
+     * type information, in form compatible with all versions
+     * of JVM, and specifically used for type descriptions
+     * when generating byte code.
+     */
+    public String getErasedSignature() {
+        StringBuilder sb = new StringBuilder(40);
+        getErasedSignature(sb);
+        return sb.toString();
+    }
+
+    /**
+     * Method for accessing signature without generic
+     * type information, in form compatible with all versions
+     * of JVM, and specifically used for type descriptions
+     * when generating byte code.
+     * 
+     * @param sb StringBuilder to append signature to
+     * 
+     * @return StringBuilder that was passed in; returned to allow
+     * call chaining
+     */
+    public abstract StringBuilder getErasedSignature(StringBuilder sb);
+    
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+
+    protected void _assertSubclass(Class<?> subclass, Class<?> superClass)
+    {
+        if (!_class.isAssignableFrom(subclass)) {
+            throw new IllegalArgumentException("Class "+subclass.getName()+" is not assignable to "+_class.getName());
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Standard methods; let's make them abstract to force override
+    /**********************************************************
+     */
+
+    @Override
+    public abstract String toString();
+
+    @Override
+    public abstract boolean equals(Object o);
+
+    @Override
+    public final int hashCode() { return _hashCode; }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/type/TypeReference.java b/src/main/java/com/fasterxml/jackson/core/type/TypeReference.java
new file mode 100644
index 0000000..cc8e10a
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/type/TypeReference.java
@@ -0,0 +1,60 @@
+package com.fasterxml.jackson.core.type;
+
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+
+/**
+ * This class is used to pass full generics type information, and
+ * avoid problems with type erasure (that basically removes most
+ * usable type references from runtime Class objects).
+ * It is based on ideas from
+ * <a href="http://gafter.blogspot.com/2006/12/super-type-tokens.html"
+ * >http://gafter.blogspot.com/2006/12/super-type-tokens.html</a>,
+ * Additional idea (from a suggestion made in comments of the article)
+ * is to require bogus implementation of <code>Comparable</code>
+ * (any such generic interface would do, as long as it forces a method
+ * with generic type to be implemented).
+ * to ensure that a Type argument is indeed given.
+ *<p>
+ * Usage is by sub-classing: here is one way to instantiate reference
+ * to generic type <code>List&lt;Integer></code>:
+ *<pre>
+ *  TypeReference ref = new TypeReference&lt;List&lt;Integer>>() { };
+ *</pre>
+ * which can be passed to methods that accept TypeReference.
+ */
+public abstract class TypeReference<T>
+    implements Comparable<TypeReference<T>>
+{
+    final Type _type;
+
+    protected TypeReference()
+    {
+        Type superClass = getClass().getGenericSuperclass();
+        if (superClass instanceof Class<?>) { // sanity check, should never happen
+            throw new IllegalArgumentException("Internal error: TypeReference constructed without actual type information");
+        }
+        /* 22-Dec-2008, tatu: Not sure if this case is safe -- I suspect
+         *   it is possible to make it fail?
+         *   But let's deal with specifc
+         *   case when we know an actual use case, and thereby suitable
+         *   work arounds for valid case(s) and/or error to throw
+         *   on invalid one(s).
+         */
+        _type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
+    }
+
+    public Type getType() { return _type; }
+
+    /**
+     * The only reason we define this method (and require implementation
+     * of <code>Comparable</code>) is to prevent constructing a
+     * reference without type information.
+     */
+    @Override
+    public int compareTo(TypeReference<T> o) {
+        // just need an implementation, not a good one... hence:
+        return 0;
+    }
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/core/type/package-info.java b/src/main/java/com/fasterxml/jackson/core/type/package-info.java
new file mode 100644
index 0000000..4b28a37
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/type/package-info.java
@@ -0,0 +1,8 @@
+/**
+ * Contains classes needed for type introspection, mostly used by data binding
+ * functionality. Most of this functionality is needed to properly handled
+ * generic types, and to simplify and unify processing of things Jackson needs
+ * to determine how contained types (of {@link java.util.Collection} and
+ * {@link java.util.Map} classes) are to be handled.
+ */
+package com.fasterxml.jackson.core.type;
diff --git a/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java b/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java
new file mode 100644
index 0000000..efe2865
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java
@@ -0,0 +1,109 @@
+package com.fasterxml.jackson.core.util;
+
+/**
+ * This is a small utility class, whose main functionality is to allow
+ * simple reuse of raw byte/char buffers. It is usually used through
+ * <code>ThreadLocal</code> member of the owning class pointing to
+ * instance of this class through a <code>SoftReference</code>. The
+ * end result is a low-overhead GC-cleanable recycling: hopefully
+ * ideal for use by stream readers.
+ */
+public class BufferRecycler
+{
+    public final static int DEFAULT_WRITE_CONCAT_BUFFER_LEN = 2000;
+    
+    public enum ByteBufferType {
+        READ_IO_BUFFER(4000)
+        /**
+         * Buffer used for temporarily storing encoded content; used
+         * for example by UTF-8 encoding writer
+         */
+        ,WRITE_ENCODING_BUFFER(4000)
+
+        /**
+         * Buffer used for temporarily concatenating output; used for
+         * example when requesting output as byte array.
+         */
+        ,WRITE_CONCAT_BUFFER(2000)
+        ;
+            
+        private final int size;
+
+        ByteBufferType(int size) { this.size = size; }
+    }
+
+    public enum CharBufferType {
+        TOKEN_BUFFER(2000) // Tokenizable input
+            ,CONCAT_BUFFER(2000) // concatenated output
+            ,TEXT_BUFFER(200) // Text content from input
+            ,NAME_COPY_BUFFER(200) // Temporary buffer for getting name characters
+            ;
+        
+        private final int size;
+
+        CharBufferType(int size) { this.size = size; }
+    }
+
+    final protected byte[][] _byteBuffers = new byte[ByteBufferType.values().length][];
+    final protected char[][] _charBuffers = new char[CharBufferType.values().length][];
+
+    public BufferRecycler() { }
+
+    public final byte[] allocByteBuffer(ByteBufferType type)
+    {
+        int ix = type.ordinal();
+        byte[] buffer = _byteBuffers[ix];
+        if (buffer == null) {
+            buffer = balloc(type.size);
+        } else {
+            _byteBuffers[ix] = null;
+        }
+        return buffer;
+    }
+
+    public final void releaseByteBuffer(ByteBufferType type, byte[] buffer)
+    {
+        _byteBuffers[type.ordinal()] = buffer;
+    }
+
+    public final char[] allocCharBuffer(CharBufferType type)
+    {
+        return allocCharBuffer(type, 0);
+    }
+
+    public final char[] allocCharBuffer(CharBufferType type, int minSize)
+    {
+        if (type.size > minSize) {
+            minSize = type.size;
+        }
+        int ix = type.ordinal();
+        char[] buffer = _charBuffers[ix];
+        if (buffer == null || buffer.length < minSize) {
+            buffer = calloc(minSize);
+        } else {
+            _charBuffers[ix] = null;
+        }
+        return buffer;
+    }
+
+    public final void releaseCharBuffer(CharBufferType type, char[] buffer)
+    {
+        _charBuffers[type.ordinal()] = buffer;
+    }
+
+    /*
+    /**********************************************************
+    /* Actual allocations separated for easier debugging/profiling
+    /**********************************************************
+     */
+
+    private final byte[] balloc(int size)
+    {
+        return new byte[size];
+    }
+
+    private final char[] calloc(int size)
+    {
+        return new char[size];
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/ByteArrayBuilder.java b/src/main/java/com/fasterxml/jackson/core/util/ByteArrayBuilder.java
new file mode 100644
index 0000000..2dd28be
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/ByteArrayBuilder.java
@@ -0,0 +1,294 @@
+/* Jackson JSON-processor.
+ *
+ * Copyright (c) 2007- Tatu Saloranta, tatu.saloranta@iki.fi
+ *
+ * Licensed under the License specified in file LICENSE, included with
+ * the source code and binary code bundles.
+ * You may not use this file except in compliance with the License.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.fasterxml.jackson.core.util;
+
+import java.io.OutputStream;
+import java.util.*;
+
+/**
+ * Helper class that is similar to {@link java.io.ByteArrayOutputStream}
+ * in usage, but more geared to Jackson use cases internally.
+ * Specific changes include segment storage (no need to have linear
+ * backing buffer, can avoid reallocs, copying), as well API
+ * not based on {@link java.io.OutputStream}. In short, a very much
+ * specialized builder object.
+ *<p>
+ * Since version 1.5, also implements {@link OutputStream} to allow
+ * efficient aggregation of output content as a byte array, similar
+ * to how {@link java.io.ByteArrayOutputStream} works, but somewhat more
+ * efficiently for many use cases.
+ */
+public final class ByteArrayBuilder
+    extends OutputStream
+{
+    private final static byte[] NO_BYTES = new byte[0];
+    
+    /**
+     * Size of the first block we will allocate.
+     */
+    private final static int INITIAL_BLOCK_SIZE = 500;
+    
+    /**
+     * Maximum block size we will use for individual non-aggregated
+     * blocks. Let's limit to using 256k chunks.
+     */
+    private final static int MAX_BLOCK_SIZE = (1 << 18);
+    
+    final static int DEFAULT_BLOCK_ARRAY_SIZE = 40;
+
+    /**
+     * Optional buffer recycler instance that we can use for allocating
+     * the first block.
+     * 
+     * @since 1.5
+     */
+    private final BufferRecycler _bufferRecycler;
+    
+    private final LinkedList<byte[]> _pastBlocks = new LinkedList<byte[]>();
+    
+    /**
+     * Number of bytes within byte arrays in {@link _pastBlocks}.
+     */
+    private int _pastLen;
+
+    private byte[] _currBlock;
+
+    private int _currBlockPtr;
+    
+    public ByteArrayBuilder() { this(null); }
+
+    public ByteArrayBuilder(BufferRecycler br) { this(br, INITIAL_BLOCK_SIZE); }
+
+    public ByteArrayBuilder(int firstBlockSize) { this(null, firstBlockSize); }
+
+    public ByteArrayBuilder(BufferRecycler br, int firstBlockSize)
+    {
+        _bufferRecycler = br;
+        if (br == null) {
+            _currBlock = new byte[firstBlockSize];
+        } else {
+            _currBlock = br.allocByteBuffer(BufferRecycler.ByteBufferType.WRITE_CONCAT_BUFFER);
+        }
+    }
+
+    public void reset()
+    {
+        _pastLen = 0;
+        _currBlockPtr = 0;
+
+        if (!_pastBlocks.isEmpty()) {
+            _pastBlocks.clear();
+        }
+    }
+
+    /**
+     * Clean up method to call to release all buffers this object may be
+     * using. After calling the method, no other accessors can be used (and
+     * attempt to do so may result in an exception)
+     */
+    public void release() {
+        reset();
+        if (_bufferRecycler != null && _currBlock != null) {
+            _bufferRecycler.releaseByteBuffer(BufferRecycler.ByteBufferType.WRITE_CONCAT_BUFFER, _currBlock);
+            _currBlock = null;
+        }
+    }
+
+    public void append(int i)
+    {
+        if (_currBlockPtr >= _currBlock.length) {
+            _allocMore();
+        }
+        _currBlock[_currBlockPtr++] = (byte) i;
+    }
+
+    public void appendTwoBytes(int b16)
+    {
+        if ((_currBlockPtr + 1) < _currBlock.length) {
+            _currBlock[_currBlockPtr++] = (byte) (b16 >> 8);
+            _currBlock[_currBlockPtr++] = (byte) b16;
+        } else {
+            append(b16 >> 8);
+            append(b16);
+        }
+    }
+
+    public void appendThreeBytes(int b24)
+    {
+        if ((_currBlockPtr + 2) < _currBlock.length) {
+            _currBlock[_currBlockPtr++] = (byte) (b24 >> 16);
+            _currBlock[_currBlockPtr++] = (byte) (b24 >> 8);
+            _currBlock[_currBlockPtr++] = (byte) b24;
+        } else {
+            append(b24 >> 16);
+            append(b24 >> 8);
+            append(b24);
+        }
+    }
+
+    /**
+     * Method called when results are finalized and we can get the
+     * full aggregated result buffer to return to the caller
+     */
+    public byte[] toByteArray()
+    {
+        int totalLen = _pastLen + _currBlockPtr;
+        
+        if (totalLen == 0) { // quick check: nothing aggregated?
+            return NO_BYTES;
+        }
+        
+        byte[] result = new byte[totalLen];
+        int offset = 0;
+
+        for (byte[] block : _pastBlocks) {
+            int len = block.length;
+            System.arraycopy(block, 0, result, offset, len);
+            offset += len;
+        }
+        System.arraycopy(_currBlock, 0, result, offset, _currBlockPtr);
+        offset += _currBlockPtr;
+        if (offset != totalLen) { // just a sanity check
+            throw new RuntimeException("Internal error: total len assumed to be "+totalLen+", copied "+offset+" bytes");
+        }
+        // Let's only reset if there's sizable use, otherwise will get reset later on
+        if (!_pastBlocks.isEmpty()) {
+            reset();
+        }
+        return result;
+    }
+
+    /*
+    /**********************************************************
+    /* Non-stream API (similar to TextBuffer), since 1.6
+    /**********************************************************
+     */
+
+    /**
+     * Method called when starting "manual" output: will clear out
+     * current state and return the first segment buffer to fill
+     * 
+     * @since 1.6
+     */
+    public byte[] resetAndGetFirstSegment() {
+        reset();
+        return _currBlock;
+    }
+
+    /**
+     * Method called when the current segment buffer is full; will
+     * append to current contents, allocate a new segment buffer
+     * and return it
+     * 
+     * @since 1.6
+     */
+    public byte[] finishCurrentSegment() {
+        _allocMore();
+        return _currBlock;
+    }
+
+    /**
+     * Method that will complete "manual" output process, coalesce
+     * content (if necessary) and return results as a contiguous buffer.
+     * 
+     * @param lastBlockLength Amount of content in the current segment
+     * buffer.
+     * 
+     * @return Coalesced contents
+     */
+    public byte[] completeAndCoalesce(int lastBlockLength)
+    {
+        _currBlockPtr = lastBlockLength;
+        return toByteArray();
+    }
+
+    public byte[] getCurrentSegment() {
+        return _currBlock;
+    }
+
+    public void setCurrentSegmentLength(int len) {
+        _currBlockPtr = len;
+    }
+
+    public int getCurrentSegmentLength() {
+        return _currBlockPtr;
+    }
+    
+    /*
+    /**********************************************************
+    /* OutputStream implementation
+    /**********************************************************
+     */
+    
+    @Override
+    public void write(byte[] b) {
+        write(b, 0, b.length);
+    }
+
+    @Override
+    public void write(byte[] b, int off, int len)
+    {
+        while (true) {
+            int max = _currBlock.length - _currBlockPtr;
+            int toCopy = Math.min(max, len);
+            if (toCopy > 0) {
+                System.arraycopy(b, off, _currBlock, _currBlockPtr, toCopy);
+                off += toCopy;
+                _currBlockPtr += toCopy;
+                len -= toCopy;
+            }
+            if (len <= 0) break;
+            _allocMore();
+        }
+    }
+
+    @Override
+    public void write(int b) {
+        append(b);
+    }
+
+    @Override public void close() { /* NOP */ }
+
+    @Override public void flush() { /* NOP */ }
+
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    
+    private void _allocMore()
+    {
+        _pastLen += _currBlock.length;
+
+        /* Let's allocate block that's half the total size, except
+         * never smaller than twice the initial block size.
+         * The idea is just to grow with reasonable rate, to optimize
+         * between minimal number of chunks and minimal amount of
+         * wasted space.
+         */
+        int newSize = Math.max((_pastLen >> 1), (INITIAL_BLOCK_SIZE + INITIAL_BLOCK_SIZE));
+        // plus not to exceed max we define...
+        if (newSize > MAX_BLOCK_SIZE) {
+            newSize = MAX_BLOCK_SIZE;
+        }
+        _pastBlocks.add(_currBlock);
+        _currBlock = new byte[newSize];
+        _currBlockPtr = 0;
+    }
+
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java b/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java
new file mode 100644
index 0000000..d77bfe8
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/DefaultPrettyPrinter.java
@@ -0,0 +1,299 @@
+package com.fasterxml.jackson.core.util;
+
+import java.io.*;
+import java.util.Arrays;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Default {@link PrettyPrinter} implementation that uses 2-space
+ * indentation with platform-default linefeeds.
+ * Usually this class is not instantiated directly, but instead
+ * method {@link JsonGenerator#useDefaultPrettyPrinter} is
+ * used, which will use an instance of this class for operation.
+ */
+public class DefaultPrettyPrinter
+    implements PrettyPrinter
+{
+    /**
+     * Interface that defines objects that can produce indentation used
+     * to separate object entries and array values. Indentation in this
+     * context just means insertion of white space, independent of whether
+     * linefeeds are output.
+     */
+    public interface Indenter
+    {
+        public void writeIndentation(JsonGenerator jg, int level)
+            throws IOException, JsonGenerationException;
+
+        /**
+         * @return True if indenter is considered inline (does not add linefeeds),
+         *   false otherwise
+         */
+        public boolean isInline();
+    }
+    
+    // // // Config, indentation
+
+    /**
+     * By default, let's use only spaces to separate array values.
+     */
+    protected Indenter _arrayIndenter = new FixedSpaceIndenter();
+
+    /**
+     * By default, let's use linefeed-adding indenter for separate
+     * object entries. We'll further configure indenter to use
+     * system-specific linefeeds, and 2 spaces per level (as opposed to,
+     * say, single tabs)
+     */
+    protected Indenter _objectIndenter = new Lf2SpacesIndenter();
+
+    // // // Config, other white space configuration
+
+    /**
+     * By default we will add spaces around colons used to
+     * separate object fields and values.
+     * If disabled, will not use spaces around colon.
+     */
+    protected boolean _spacesInObjectEntries = true;
+
+    // // // State:
+
+    /**
+     * Number of open levels of nesting. Used to determine amount of
+     * indentation to use.
+     */
+    protected int _nesting = 0;
+
+    /*
+    /**********************************************************
+    /* Life-cycle (construct, configure)
+    /**********************************************************
+    */
+
+    public DefaultPrettyPrinter() { }
+
+    public void indentArraysWith(Indenter i)
+    {
+        _arrayIndenter = (i == null) ? new NopIndenter() : i;
+    }
+
+    public void indentObjectsWith(Indenter i)
+    {
+        _objectIndenter = (i == null) ? new NopIndenter() : i;
+    }
+
+    public void spacesInObjectEntries(boolean b) { _spacesInObjectEntries = b; }
+
+    /*
+    /**********************************************************
+    /* PrettyPrinter impl
+    /**********************************************************
+     */
+
+    @Override
+    public void writeRootValueSeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw(' ');
+    }
+
+    @Override
+    public void writeStartObject(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw('{');
+        if (!_objectIndenter.isInline()) {
+            ++_nesting;
+        }
+    }
+
+    @Override
+    public void beforeObjectEntries(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        _objectIndenter.writeIndentation(jg, _nesting);
+    }
+
+    /**
+     * Method called after an object field has been output, but
+     * before the value is output.
+     *<p>
+     * Default handling (without pretty-printing) will output a single
+     * colon to separate the two. Pretty-printer is
+     * to output a colon as well, but can surround that with other
+     * (white-space) decoration.
+     */
+    @Override
+    public void writeObjectFieldValueSeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        if (_spacesInObjectEntries) {
+            jg.writeRaw(" : ");
+        } else {
+            jg.writeRaw(':');
+        }
+    }
+
+    /**
+     * Method called after an object entry (field:value) has been completely
+     * output, and before another value is to be output.
+     *<p>
+     * Default handling (without pretty-printing) will output a single
+     * comma to separate the two. Pretty-printer is
+     * to output a comma as well, but can surround that with other
+     * (white-space) decoration.
+     */
+    @Override
+    public void writeObjectEntrySeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw(',');
+        _objectIndenter.writeIndentation(jg, _nesting);
+    }
+
+    @Override
+    public void writeEndObject(JsonGenerator jg, int nrOfEntries)
+        throws IOException, JsonGenerationException
+    {
+        if (!_objectIndenter.isInline()) {
+            --_nesting;
+        }
+        if (nrOfEntries > 0) {
+            _objectIndenter.writeIndentation(jg, _nesting);
+        } else {
+            jg.writeRaw(' ');
+        }
+        jg.writeRaw('}');
+    }
+
+    @Override
+    public void writeStartArray(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        if (!_arrayIndenter.isInline()) {
+            ++_nesting;
+        }
+        jg.writeRaw('[');
+    }
+
+    @Override
+    public void beforeArrayValues(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        _arrayIndenter.writeIndentation(jg, _nesting);
+    }
+
+    /**
+     * Method called after an array value has been completely
+     * output, and before another value is to be output.
+     *<p>
+     * Default handling (without pretty-printing) will output a single
+     * comma to separate the two. Pretty-printer is
+     * to output a comma as well, but can surround that with other
+     * (white-space) decoration.
+     */
+    @Override
+    public void writeArrayValueSeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw(',');
+        _arrayIndenter.writeIndentation(jg, _nesting);
+    }
+
+    @Override
+    public void writeEndArray(JsonGenerator jg, int nrOfValues)
+        throws IOException, JsonGenerationException
+    {
+        if (!_arrayIndenter.isInline()) {
+            --_nesting;
+        }
+        if (nrOfValues > 0) {
+            _arrayIndenter.writeIndentation(jg, _nesting);
+        } else {
+            jg.writeRaw(' ');
+        }
+        jg.writeRaw(']');
+    }
+
+    /*
+    /**********************************************************
+    /* Helper classes
+    /**********************************************************
+     */
+
+    /**
+     * Dummy implementation that adds no indentation whatsoever
+     */
+    public static class NopIndenter
+        implements Indenter
+    {
+        public NopIndenter() { }
+        @Override
+        public void writeIndentation(JsonGenerator jg, int level) { }
+        @Override
+        public boolean isInline() { return true; }
+    }
+
+    /**
+     * This is a very simple indenter that only every adds a
+     * single space for indentation. It is used as the default
+     * indenter for array values.
+     */
+    public static class FixedSpaceIndenter
+        implements Indenter
+    {
+        public FixedSpaceIndenter() { }
+
+        @Override
+        public void writeIndentation(JsonGenerator jg, int level)
+            throws IOException, JsonGenerationException
+        {
+            jg.writeRaw(' ');
+        }
+
+        @Override
+        public boolean isInline() { return true; }
+    }
+
+    /**
+     * Default linefeed-based indenter uses system-specific linefeeds and
+     * 2 spaces for indentation per level.
+     */
+    public static class Lf2SpacesIndenter
+        implements Indenter
+    {
+        final static String SYSTEM_LINE_SEPARATOR;
+        static {
+            String lf = null;
+            try {
+                lf = System.getProperty("line.separator");
+            } catch (Throwable t) { } // access exception?
+            SYSTEM_LINE_SEPARATOR = (lf == null) ? "\n" : lf;
+        }
+
+        final static int SPACE_COUNT = 64;
+        final static char[] SPACES = new char[SPACE_COUNT];
+        static {
+            Arrays.fill(SPACES, ' ');
+        }
+
+        public Lf2SpacesIndenter() { }
+
+        @Override
+        public boolean isInline() { return false; }
+
+        @Override
+        public void writeIndentation(JsonGenerator jg, int level)
+            throws IOException, JsonGenerationException
+        {
+            jg.writeRaw(SYSTEM_LINE_SEPARATOR);
+            level += level; // 2 spaces per level
+            while (level > SPACE_COUNT) { // should never happen but...
+                jg.writeRaw(SPACES, 0, SPACE_COUNT); 
+                level -= SPACES.length;
+            }
+            jg.writeRaw(SPACES, 0, level);
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/InternCache.java b/src/main/java/com/fasterxml/jackson/core/util/InternCache.java
new file mode 100644
index 0000000..b1cc7d7
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/InternCache.java
@@ -0,0 +1,49 @@
+package com.fasterxml.jackson.core.util;
+
+import java.util.Map;
+import java.util.LinkedHashMap;
+
+/**
+ * Singleton class that adds a simple first-level cache in front of
+ * regular String.intern() functionality. This is done as a minor
+ * performance optimization, to avoid calling native intern() method
+ * in cases where same String is being interned multiple times.
+ *<p>
+ * Note: that this class extends {@link LinkedHashMap} is an implementation
+ * detail -- no code should ever directly call Map methods.
+ */
+@SuppressWarnings("serial")
+public final class InternCache
+    extends LinkedHashMap<String,String>
+{
+    /**
+     * Size to use is somewhat arbitrary, so let's choose something that's
+     * neither too small (low hit ratio) nor too large (waste of memory)
+     */
+    private final static int MAX_ENTRIES = 192;
+
+    public final static InternCache instance = new InternCache();
+
+    private InternCache() {
+        super(MAX_ENTRIES, 0.8f, true);
+    }
+
+    @Override
+    protected boolean removeEldestEntry(Map.Entry<String,String> eldest)
+    {
+        return size() > MAX_ENTRIES;
+    }
+
+    public synchronized String intern(String input)
+    {
+        String result = get(input);
+        if (result == null) {
+            result = input.intern();
+            put(result, result);
+        }
+        return result;
+    }
+
+
+}
+
diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java b/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java
new file mode 100644
index 0000000..bc22934
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/JsonGeneratorDelegate.java
@@ -0,0 +1,273 @@
+package com.fasterxml.jackson.core.util;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.io.SerializedString;
+
+public class JsonGeneratorDelegate extends JsonGenerator
+{
+    /**
+     * Delegate object that method calls are delegated to.
+     */
+    protected JsonGenerator delegate;
+
+    public JsonGeneratorDelegate(JsonGenerator d) {
+        delegate = d;
+    }   
+
+    @Override
+    public void close() throws IOException {
+        delegate.close();
+    }
+
+    @Override
+    public void copyCurrentEvent(JsonParser jp) throws IOException, JsonProcessingException {
+        delegate.copyCurrentEvent(jp);
+    }
+
+    @Override
+    public void copyCurrentStructure(JsonParser jp) throws IOException, JsonProcessingException {
+        delegate.copyCurrentStructure(jp);
+    }
+
+    @Override
+    public JsonGenerator disable(Feature f) {
+        return delegate.disable(f);
+    }
+
+    @Override
+    public JsonGenerator enable(Feature f) {
+        return delegate.enable(f);
+    }
+
+    @Override
+    public void flush() throws IOException {
+        delegate.flush();
+    }
+
+    @Override
+    public ObjectCodec getCodec() {
+        return delegate.getCodec();
+    }
+
+    @Override
+    public JsonStreamContext getOutputContext() {
+        return delegate.getOutputContext();
+    }
+
+    @Override
+    public void setSchema(FormatSchema schema) {
+        delegate.setSchema(schema);
+    }
+    
+    @Override
+    public boolean canUseSchema(FormatSchema schema) {
+        return delegate.canUseSchema(schema);
+    }
+    
+    @Override
+    public Version version() {
+        return delegate.version();
+    }
+    
+    @Override
+    public Object getOutputTarget() {
+        return delegate.getOutputTarget();
+    }
+    
+    @Override
+    public boolean isClosed() {
+        return delegate.isClosed();
+    }
+
+    @Override
+    public boolean isEnabled(Feature f) {
+        return delegate.isEnabled(f);
+    }
+
+    @Override
+    public JsonGenerator setCodec(ObjectCodec oc) {
+        delegate.setCodec(oc);
+        return this;
+    }
+
+    @Override
+    public JsonGenerator useDefaultPrettyPrinter() {
+        delegate.useDefaultPrettyPrinter();
+        return this;
+    }
+
+    @Override
+    public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        delegate.writeBinary(b64variant, data, offset, len);
+    }
+
+    @Override
+    public void writeBoolean(boolean state) throws IOException, JsonGenerationException {
+        delegate.writeBoolean(state);
+    }
+
+    @Override
+    public void writeEndArray() throws IOException, JsonGenerationException {
+        delegate.writeEndArray();
+    }
+
+    @Override
+    public void writeEndObject() throws IOException, JsonGenerationException {
+        delegate.writeEndObject();
+    }
+
+    @Override
+    public void writeFieldName(String name)
+        throws IOException, JsonGenerationException
+    {
+        delegate.writeFieldName(name);
+    }
+
+    @Override
+    public void writeFieldName(SerializedString name)
+        throws IOException, JsonGenerationException
+    {
+        delegate.writeFieldName(name);
+    }
+
+    @Override
+    public void writeFieldName(SerializableString name)
+        throws IOException, JsonGenerationException
+    {
+        delegate.writeFieldName(name);
+    }
+    
+    @Override
+    public void writeNull() throws IOException, JsonGenerationException {
+        delegate.writeNull();
+    }
+
+    @Override
+    public void writeNumber(int v) throws IOException, JsonGenerationException {
+        delegate.writeNumber(v);
+    }
+
+    @Override
+    public void writeNumber(long v) throws IOException, JsonGenerationException {
+        delegate.writeNumber(v);
+    }
+
+    @Override
+    public void writeNumber(BigInteger v) throws IOException,
+            JsonGenerationException {
+        delegate.writeNumber(v);
+    }
+
+    @Override
+    public void writeNumber(double v) throws IOException,
+            JsonGenerationException {
+        delegate.writeNumber(v);
+    }
+
+    @Override
+    public void writeNumber(float v) throws IOException,
+            JsonGenerationException {
+        delegate.writeNumber(v);
+    }
+
+    @Override
+    public void writeNumber(BigDecimal v) throws IOException,
+            JsonGenerationException {
+        delegate.writeNumber(v);
+    }
+
+    @Override
+    public void writeNumber(String encodedValue) throws IOException, JsonGenerationException, UnsupportedOperationException {
+        delegate.writeNumber(encodedValue);
+    }
+
+    @Override
+    public void writeObject(Object pojo) throws IOException,JsonProcessingException {
+        delegate.writeObject(pojo);
+    }
+
+    @Override
+    public void writeRaw(String text) throws IOException, JsonGenerationException {
+        delegate.writeRaw(text);
+    }
+
+    @Override
+    public void writeRaw(String text, int offset, int len) throws IOException, JsonGenerationException {
+        delegate.writeRaw(text, offset, len);
+    }
+
+    @Override
+    public void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+        delegate.writeRaw(text, offset, len);
+    }
+
+    @Override
+    public void writeRaw(char c) throws IOException, JsonGenerationException {
+        delegate.writeRaw(c);
+    }
+
+    @Override
+    public void writeRawValue(String text) throws IOException, JsonGenerationException {
+        delegate.writeRawValue(text);
+    }
+
+    @Override
+    public void writeRawValue(String text, int offset, int len) throws IOException, JsonGenerationException {
+         delegate.writeRawValue(text, offset, len);
+    }
+
+    @Override
+    public void writeRawValue(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+         delegate.writeRawValue(text, offset, len);
+    }
+
+    @Override
+    public void writeStartArray() throws IOException, JsonGenerationException {
+         delegate.writeStartArray();
+    }
+
+    @Override
+    public void writeStartObject() throws IOException, JsonGenerationException {
+        delegate.writeStartObject();
+    }
+
+    @Override
+    public void writeString(String text) throws IOException,JsonGenerationException {
+        delegate.writeString(text);
+    }
+
+    @Override
+    public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+        delegate.writeString(text, offset, len);
+    }
+
+    @Override
+    public void writeString(SerializableString text) throws IOException, JsonGenerationException {
+        delegate.writeString(text);
+    }
+
+    @Override
+    public void writeRawUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException
+    {
+        delegate.writeRawUTF8String(text, offset, length);
+    }
+
+    @Override
+    public void writeUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException
+    {
+        delegate.writeUTF8String(text, offset, length);
+    }
+    
+    @Override
+    public void writeTree(JsonNode rootNode) throws IOException, JsonProcessingException {
+        delegate.writeTree(rootNode);
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonParserDelegate.java b/src/main/java/com/fasterxml/jackson/core/util/JsonParserDelegate.java
new file mode 100644
index 0000000..a43db72
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/JsonParserDelegate.java
@@ -0,0 +1,243 @@
+package com.fasterxml.jackson.core.util;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Helper class that implements
+ * <a href="http://en.wikipedia.org/wiki/Delegation_pattern">delegation pattern</a> for {@link JsonParser},
+ * to allow for simple overridability of basic parsing functionality.
+ * The idea is that any functionality to be modified can be simply
+ * overridden; and anything else will be delegated by default.
+ * 
+ * @since 1.4
+ */
+public class JsonParserDelegate extends JsonParser
+{
+    /**
+     * Delegate object that method calls are delegated to.
+     */
+    protected JsonParser delegate;
+
+    public JsonParserDelegate(JsonParser d) {
+        delegate = d;
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, configuration
+    /**********************************************************
+     */
+
+    @Override
+    public void setCodec(ObjectCodec c) {
+        delegate.setCodec(c);
+    }
+
+    @Override
+    public ObjectCodec getCodec() {
+        return delegate.getCodec();
+    }
+
+    @Override
+    public JsonParser enable(Feature f) {
+        delegate.enable(f);
+        return this;
+    }
+
+    @Override
+    public JsonParser disable(Feature f) {
+        delegate.disable(f);
+        return this;
+    }
+ 
+    @Override
+    public boolean isEnabled(Feature f) {
+        return delegate.isEnabled(f);
+    }
+
+    @Override
+    public void setSchema(FormatSchema schema) {
+        delegate.setSchema(schema);
+    }
+
+    @Override
+    public boolean canUseSchema(FormatSchema schema) {
+        return delegate.canUseSchema(schema);
+    }
+    
+    @Override
+    public Version version() {
+        return delegate.version();
+    }
+
+    @Override
+    public Object getInputSource() {
+        return delegate.getInputSource();
+    }
+    
+    /*
+    /**********************************************************
+    /* Closeable impl
+    /**********************************************************
+     */
+
+    @Override
+    public void close() throws IOException {
+        delegate.close();
+    }
+
+    @Override
+    public boolean isClosed() {
+        return delegate.isClosed();
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, token accessors
+    /**********************************************************
+     */
+
+    @Override
+    public JsonToken getCurrentToken() {
+        return delegate.getCurrentToken();
+    }
+
+    @Override
+    public boolean hasCurrentToken() {
+        return delegate.hasCurrentToken();
+    }
+
+    @Override
+    public void clearCurrentToken() {
+        delegate.clearCurrentToken();        
+    }
+
+    @Override
+    public String getCurrentName() throws IOException, JsonParseException {
+        return delegate.getCurrentName();
+    }
+
+    @Override
+    public JsonLocation getCurrentLocation() {
+        return delegate.getCurrentLocation();
+    }
+
+    @Override
+    public JsonToken getLastClearedToken() {
+        return delegate.getLastClearedToken();
+    }
+
+    @Override
+    public JsonStreamContext getParsingContext() {
+        return delegate.getParsingContext();
+    }
+
+    /*
+    /**********************************************************
+    /* Public API, access to token information, text
+    /**********************************************************
+     */
+
+    @Override
+    public String getText() throws IOException, JsonParseException {
+        return delegate.getText();
+    }
+
+    @Override
+    public char[] getTextCharacters() throws IOException, JsonParseException {
+        return delegate.getTextCharacters();
+    }
+
+    @Override
+    public int getTextLength() throws IOException, JsonParseException {
+        return delegate.getTextLength();
+    }
+
+    @Override
+    public int getTextOffset() throws IOException, JsonParseException {
+        return delegate.getTextOffset();
+    }
+
+
+    /*
+    /**********************************************************
+    /* Public API, access to token information, numeric
+    /**********************************************************
+     */
+    
+    @Override
+    public BigInteger getBigIntegerValue() throws IOException,JsonParseException {
+        return delegate.getBigIntegerValue();
+    }
+
+    @Override
+    public byte getByteValue() throws IOException, JsonParseException {
+        return delegate.getByteValue();
+    }
+
+    @Override
+    public short getShortValue() throws IOException, JsonParseException {
+        return delegate.getShortValue();
+    }
+
+    @Override
+    public BigDecimal getDecimalValue() throws IOException, JsonParseException {
+        return delegate.getDecimalValue();
+    }
+
+    @Override
+    public double getDoubleValue() throws IOException, JsonParseException {
+        return delegate.getDoubleValue();
+    }
+
+    @Override
+    public float getFloatValue() throws IOException, JsonParseException {
+        return delegate.getFloatValue();
+    }
+
+    @Override
+    public int getIntValue() throws IOException, JsonParseException {
+        return delegate.getIntValue();
+    }
+
+    @Override
+    public long getLongValue() throws IOException, JsonParseException {
+        return delegate.getLongValue();
+    }
+
+    @Override
+    public NumberType getNumberType() throws IOException, JsonParseException {
+        return delegate.getNumberType();
+    }
+
+    @Override
+    public Number getNumberValue() throws IOException, JsonParseException {
+        return delegate.getNumberValue();
+    }
+
+    @Override
+    public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException {
+        return delegate.getBinaryValue(b64variant);
+    }
+
+    @Override
+    public JsonLocation getTokenLocation() {
+        return delegate.getTokenLocation();
+    }
+
+    @Override
+    public JsonToken nextToken() throws IOException, JsonParseException {
+        return delegate.nextToken();
+    }
+    
+    @Override
+    public JsonParser skipChildren() throws IOException, JsonParseException {
+        delegate.skipChildren();
+        // NOTE: must NOT delegate this method to delegate, needs to be self-reference for chaining
+        return this;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java b/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java
new file mode 100644
index 0000000..e796898
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/JsonParserSequence.java
@@ -0,0 +1,150 @@
+package com.fasterxml.jackson.core.util;
+
+import java.io.IOException;
+import java.util.*;
+
+import com.fasterxml.jackson.core.*;
+
+/**
+ * Helper class that can be used to sequence multiple physical
+ * {@link JsonParser}s to create a single logical sequence of
+ * tokens, as a single {@link JsonParser}.
+ *<p>
+ * Fairly simple use of {@link JsonParserDelegate}: only need
+ * to override {@link #nextToken} to handle transition
+ * 
+ * @author tatu
+ * @since 1.5
+ */
+public class JsonParserSequence extends JsonParserDelegate
+{
+    /**
+     * Parsers other than the first one (which is initially assigned
+     * as delegate)
+     */
+    protected final JsonParser[] _parsers;
+    
+    /**
+     * Index of the next parser in {@link #_parsers}.
+     */
+    protected int _nextParser;
+    
+    /*
+     *******************************************************
+     * Construction
+     *******************************************************
+     */
+
+    protected JsonParserSequence(JsonParser[] parsers)
+    {
+        super(parsers[0]);
+        _parsers = parsers;
+        _nextParser = 1;
+    }
+
+    /**
+     * Method that will construct a parser (possibly a sequence) that
+     * contains all given sub-parsers.
+     * All parsers given are checked to see if they are sequences: and
+     * if so, they will be "flattened", that is, contained parsers are
+     * directly added in a new sequence instead of adding sequences
+     * within sequences. This is done to minimize delegation depth,
+     * ideally only having just a single level of delegation.
+     */
+    public static JsonParserSequence createFlattened(JsonParser first, JsonParser second)
+    {
+        if (!(first instanceof JsonParserSequence || second instanceof JsonParserSequence)) {
+            // simple:
+            return new JsonParserSequence(new JsonParser[] { first, second });
+        }
+        ArrayList<JsonParser> p = new ArrayList<JsonParser>();
+        if (first instanceof JsonParserSequence) {
+            ((JsonParserSequence) first).addFlattenedActiveParsers(p);
+        } else {
+            p.add(first);
+        }
+        if (second instanceof JsonParserSequence) {
+            ((JsonParserSequence) second).addFlattenedActiveParsers(p);
+        } else {
+            p.add(second);
+        }
+        return new JsonParserSequence(p.toArray(new JsonParser[p.size()]));
+    }
+
+    protected void addFlattenedActiveParsers(List<JsonParser> result)
+    {
+        for (int i = _nextParser-1, len = _parsers.length; i < len; ++i) {
+            JsonParser p = _parsers[i];
+            if (p instanceof JsonParserSequence) {
+                ((JsonParserSequence) p).addFlattenedActiveParsers(result);
+            } else {
+                result.add(p);
+            }
+        }
+    }
+    
+    /*
+     *******************************************************
+     * Overridden methods, needed: cases where default
+     * delegation does not work
+     *******************************************************
+     */
+    
+    @Override
+    public void close() throws IOException
+    {
+        do {
+            delegate.close();
+        } while (switchToNext());
+    }
+
+    @Override
+    public JsonToken nextToken() throws IOException, JsonParseException
+    {
+        JsonToken t = delegate.nextToken();
+        if (t != null) return t;
+        while (switchToNext()) {
+            t = delegate.nextToken();
+            if (t != null) return t;
+        }
+        return null;
+    }
+
+    /*
+    /*******************************************************
+    /* Additional extended API
+    /*******************************************************
+     */
+
+    /**
+     * Method that is most useful for debugging or testing;
+     * returns actual number of underlying parsers sequence
+     * was constructed with (nor just ones remaining active)
+     */
+    public int containedParsersCount() {
+        return _parsers.length;
+    }
+    
+    /*
+    /*******************************************************
+    /* Helper methods
+    /*******************************************************
+     */
+
+    /**
+     * Method that will switch active parser from the current one
+     * to next parser in sequence, if there is another parser left,
+     * making this the new delegate. Old delegate is returned if
+     * switch succeeds.
+     * 
+     * @return True if switch succeeded; false otherwise
+     */
+    protected boolean switchToNext()
+    {
+        if (_nextParser >= _parsers.length) {
+            return false;
+        }
+        delegate = _parsers[_nextParser++];
+        return true;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/MinimalPrettyPrinter.java b/src/main/java/com/fasterxml/jackson/core/util/MinimalPrettyPrinter.java
new file mode 100644
index 0000000..a077833
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/MinimalPrettyPrinter.java
@@ -0,0 +1,152 @@
+package com.fasterxml.jackson.core.util;
+
+import java.io.IOException;
+
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.core.PrettyPrinter;
+
+/**
+ * {@link PrettyPrinter} implementation that adds no indentation,
+ * just implements everything necessary for value output to work
+ * as expected, and provide simpler extension points to allow
+ * for creating simple custom implementations that add specific
+ * decoration or overrides. Since behavior then is very similar
+ * to using no pretty printer at all, usually sub-classes are used.
+ *<p>
+ * Beyond purely minimal implementation, there is limited amount of
+ * configurability which may be useful for actual use: for example,
+ * it is possible to redefine separator used between root-level
+ * values (default is single space; can be changed to line-feed).
+ * 
+ * @since 1.6
+ */
+public class MinimalPrettyPrinter
+    implements PrettyPrinter
+{
+    /**
+     * Default String used for separating root values is single space.
+     */
+    public final static String DEFAULT_ROOT_VALUE_SEPARATOR = " ";
+    
+    protected String _rootValueSeparator = DEFAULT_ROOT_VALUE_SEPARATOR;
+
+    /*
+    /**********************************************************
+    /* Life-cycle, construction, configuration
+    /**********************************************************
+     */
+    
+    public MinimalPrettyPrinter() {
+        this(DEFAULT_ROOT_VALUE_SEPARATOR);
+    }
+
+    /**
+     * @since 1.9
+     */
+    public MinimalPrettyPrinter(String rootValueSeparator) {
+        _rootValueSeparator = rootValueSeparator;
+    }
+    
+    public void setRootValueSeparator(String sep) {
+        _rootValueSeparator = sep;
+    }
+    
+    /*
+    /**********************************************************
+    /* PrettyPrinter impl
+    /**********************************************************
+     */
+
+    @Override
+    public void writeRootValueSeparator(JsonGenerator jg) throws IOException, JsonGenerationException
+    {
+        if (_rootValueSeparator != null) {
+            jg.writeRaw(_rootValueSeparator);    
+        }
+    }
+    
+    @Override
+    public void writeStartObject(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw('{');
+    }
+    
+    @Override
+    public void beforeObjectEntries(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        // nothing special, since no indentation is added
+    }
+
+    /**
+     * Method called after an object field has been output, but
+     * before the value is output.
+     *<p>
+     * Default handling will just output a single
+     * colon to separate the two, without additional spaces.
+     */
+    @Override
+    public void writeObjectFieldValueSeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw(':');
+    }
+    
+    /**
+     * Method called after an object entry (field:value) has been completely
+     * output, and before another value is to be output.
+     *<p>
+     * Default handling (without pretty-printing) will output a single
+     * comma to separate the two.
+     */
+    @Override
+    public void writeObjectEntrySeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw(',');
+    }
+
+    @Override
+    public void writeEndObject(JsonGenerator jg, int nrOfEntries)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw('}');
+    }
+    
+    @Override
+    public void writeStartArray(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw('[');
+    }
+    
+    @Override
+    public void beforeArrayValues(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        // nothing special, since no indentation is added
+    }
+
+    /**
+     * Method called after an array value has been completely
+     * output, and before another value is to be output.
+     *<p>
+     * Default handling (without pretty-printing) will output a single
+     * comma to separate values.
+     */
+    @Override
+    public void writeArrayValueSeparator(JsonGenerator jg)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw(',');
+    }
+    
+    @Override
+    public void writeEndArray(JsonGenerator jg, int nrOfValues)
+        throws IOException, JsonGenerationException
+    {
+        jg.writeRaw(']');
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java b/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java
new file mode 100644
index 0000000..2188b70
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java
@@ -0,0 +1,707 @@
+package com.fasterxml.jackson.core.util;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+
+import com.fasterxml.jackson.core.io.NumberInput;
+
+/**
+ * TextBuffer is a class similar to {@link StringBuffer}, with
+ * following differences:
+ *<ul>
+ *  <li>TextBuffer uses segments character arrays, to avoid having
+ *     to do additional array copies when array is not big enough.
+ *     This means that only reallocating that is necessary is done only once:
+ *     if and when caller
+ *     wants to access contents in a linear array (char[], String).
+ *    </li>
+*  <li>TextBuffer can also be initialized in "shared mode", in which
+*     it will just act as a wrapper to a single char array managed
+*     by another object (like parser that owns it)
+ *    </li>
+ *  <li>TextBuffer is not synchronized.
+ *    </li>
+ * </ul>
+ */
+public final class TextBuffer
+{
+    final static char[] NO_CHARS = new char[0];
+
+    /**
+     * Let's start with sizable but not huge buffer, will grow as necessary
+     */
+    final static int MIN_SEGMENT_LEN = 1000;
+    
+    /**
+     * Let's limit maximum segment length to something sensible
+     * like 256k
+     */
+    final static int MAX_SEGMENT_LEN = 0x40000;
+    
+    /*
+    /**********************************************************
+    /* Configuration:
+    /**********************************************************
+     */
+
+    private final BufferRecycler _allocator;
+
+    /*
+    /**********************************************************
+    /* Shared input buffers
+    /**********************************************************
+     */
+
+    /**
+     * Shared input buffer; stored here in case some input can be returned
+     * as is, without being copied to collector's own buffers. Note that
+     * this is read-only for this Object.
+     */
+    private char[] _inputBuffer;
+
+    /**
+     * Character offset of first char in input buffer; -1 to indicate
+     * that input buffer currently does not contain any useful char data
+     */
+    private int _inputStart;
+
+    private int _inputLen;
+
+    /*
+    /**********************************************************
+    /* Aggregation segments (when not using input buf)
+    /**********************************************************
+     */
+
+    /**
+     * List of segments prior to currently active segment.
+     */
+    private ArrayList<char[]> _segments;
+
+    /**
+     * Flag that indicates whether _seqments is non-empty
+     */
+    private boolean _hasSegments = false;
+
+    // // // Currently used segment; not (yet) contained in _seqments
+
+    /**
+     * Amount of characters in segments in {@link _segments}
+     */
+    private int _segmentSize;
+
+    private char[] _currentSegment;
+
+    /**
+     * Number of characters in currently active (last) segment
+     */
+    private int _currentSize;
+
+    /*
+    /**********************************************************
+    /* Caching of results
+    /**********************************************************
+     */
+
+    /**
+     * String that will be constructed when the whole contents are
+     * needed; will be temporarily stored in case asked for again.
+     */
+    private String _resultString;
+
+    private char[] _resultArray;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    public TextBuffer(BufferRecycler allocator)
+    {
+        _allocator = allocator;
+    }
+
+    /**
+     * Method called to indicate that the underlying buffers should now
+     * be recycled if they haven't yet been recycled. Although caller
+     * can still use this text buffer, it is not advisable to call this
+     * method if that is likely, since next time a buffer is needed,
+     * buffers need to reallocated.
+     * Note: calling this method automatically also clears contents
+     * of the buffer.
+     */
+    public void releaseBuffers()
+    {
+        if (_allocator == null) {
+            resetWithEmpty();
+        } else {
+            if (_currentSegment != null) {
+                // First, let's get rid of all but the largest char array
+                resetWithEmpty();
+                // And then return that array
+                char[] buf = _currentSegment;
+                _currentSegment = null;
+                _allocator.releaseCharBuffer(BufferRecycler.CharBufferType.TEXT_BUFFER, buf);
+            }
+        }
+    }
+
+    /**
+     * Method called to clear out any content text buffer may have, and
+     * initializes buffer to use non-shared data.
+     */
+    public void resetWithEmpty()
+    {
+        _inputStart = -1; // indicates shared buffer not used
+        _currentSize = 0;
+        _inputLen = 0;
+
+        _inputBuffer = null;
+        _resultString = null;
+        _resultArray = null;
+
+        // And then reset internal input buffers, if necessary:
+        if (_hasSegments) {
+            clearSegments();
+        }
+    }
+
+    /**
+     * Method called to initialize the buffer with a shared copy of data;
+     * this means that buffer will just have pointers to actual data. It
+     * also means that if anything is to be appended to the buffer, it
+     * will first have to unshare it (make a local copy).
+     */
+    public void resetWithShared(char[] buf, int start, int len)
+    {
+        // First, let's clear intermediate values, if any:
+        _resultString = null;
+        _resultArray = null;
+
+        // Then let's mark things we need about input buffer
+        _inputBuffer = buf;
+        _inputStart = start;
+        _inputLen = len;
+
+        // And then reset internal input buffers, if necessary:
+        if (_hasSegments) {
+            clearSegments();
+        }
+    }
+
+    public void resetWithCopy(char[] buf, int start, int len)
+    {
+        _inputBuffer = null;
+        _inputStart = -1; // indicates shared buffer not used
+        _inputLen = 0;
+
+        _resultString = null;
+        _resultArray = null;
+
+        // And then reset internal input buffers, if necessary:
+        if (_hasSegments) {
+            clearSegments();
+        } else if (_currentSegment == null) {
+            _currentSegment = findBuffer(len);
+        }
+        _currentSize = _segmentSize = 0;
+        append(buf, start, len);
+    }
+
+    public void resetWithString(String value)
+    {
+        _inputBuffer = null;
+        _inputStart = -1;
+        _inputLen = 0;
+
+        _resultString = value;
+        _resultArray = null;
+
+        if (_hasSegments) {
+            clearSegments();
+        }
+        _currentSize = 0;
+        
+    }
+    
+    /**
+     * Helper method used to find a buffer to use, ideally one
+     * recycled earlier.
+     */
+    private final char[] findBuffer(int needed)
+    {
+        if (_allocator != null) {
+            return _allocator.allocCharBuffer(BufferRecycler.CharBufferType.TEXT_BUFFER, needed);
+        }
+        return new char[Math.max(needed, MIN_SEGMENT_LEN)];
+    }
+
+    private final void clearSegments()
+    {
+        _hasSegments = false;
+        /* Let's start using _last_ segment from list; for one, it's
+         * the biggest one, and it's also most likely to be cached
+         */
+        /* 28-Aug-2009, tatu: Actually, the current segment should
+         *   be the biggest one, already
+         */
+        //_currentSegment = _segments.get(_segments.size() - 1);
+        _segments.clear();
+        _currentSize = _segmentSize = 0;
+    }
+
+    /*
+    /**********************************************************
+    /* Accessors for implementing public interface
+    /**********************************************************
+     */
+
+    /**
+     * @return Number of characters currently stored by this collector
+     */
+    public int size() {
+        if (_inputStart >= 0) { // shared copy from input buf
+            return _inputLen;
+        }
+        if (_resultArray != null) {
+            return _resultArray.length;
+        }
+        if (_resultString != null) {
+            return _resultString.length();
+        }
+        // local segmented buffers
+        return _segmentSize + _currentSize;
+    }
+
+    public int getTextOffset()
+    {
+        /* Only shared input buffer can have non-zero offset; buffer
+         * segments start at 0, and if we have to create a combo buffer,
+         * that too will start from beginning of the buffer
+         */
+        return (_inputStart >= 0) ? _inputStart : 0;
+    }
+
+    /**
+     * Method that can be used to check whether textual contents can
+     * be efficiently accessed using {@link #getTextBuffer}.
+     * 
+     * @since 1.9
+     */
+    public boolean hasTextAsCharacters()
+    {
+        // if we have array in some form, sure
+        if (_inputStart >= 0 || _resultArray != null) {
+            return true;
+        }
+        // not if we have String as value
+        if (_resultString != null) {
+            return false;
+        }
+        return true;
+    }
+    
+    public char[] getTextBuffer()
+    {
+        // Are we just using shared input buffer?
+        if (_inputStart >= 0) {
+            return _inputBuffer;
+        }
+        if (_resultArray != null) {
+            return _resultArray;
+        }
+        if (_resultString != null) {
+            return (_resultArray = _resultString.toCharArray());
+        }
+        // Nope; but does it fit in just one segment?
+        if (!_hasSegments) {
+            return _currentSegment;
+        }
+        // Nope, need to have/create a non-segmented array and return it
+        return contentsAsArray();
+    }
+
+    /*
+    /**********************************************************
+    /* Other accessors:
+    /**********************************************************
+     */
+
+    public String contentsAsString()
+    {
+        if (_resultString == null) {
+            // Has array been requested? Can make a shortcut, if so:
+            if (_resultArray != null) {
+                _resultString = new String(_resultArray);
+            } else {
+                // Do we use shared array?
+                if (_inputStart >= 0) {
+                    if (_inputLen < 1) {
+                        return (_resultString = "");
+                    }
+                    _resultString = new String(_inputBuffer, _inputStart, _inputLen);
+                } else { // nope... need to copy
+                    // But first, let's see if we have just one buffer
+                    int segLen = _segmentSize;
+                    int currLen = _currentSize;
+                    
+                    if (segLen == 0) { // yup
+                        _resultString = (currLen == 0) ? "" : new String(_currentSegment, 0, currLen);
+                    } else { // no, need to combine
+                        StringBuilder sb = new StringBuilder(segLen + currLen);
+                        // First stored segments
+                        if (_segments != null) {
+                            for (int i = 0, len = _segments.size(); i < len; ++i) {
+                                char[] curr = _segments.get(i);
+                                sb.append(curr, 0, curr.length);
+                            }
+                        }
+                        // And finally, current segment:
+                        sb.append(_currentSegment, 0, _currentSize);
+                        _resultString = sb.toString();
+                    }
+                }
+            }
+        }
+        return _resultString;
+    }
+ 
+    public char[] contentsAsArray()
+    {
+        char[] result = _resultArray;
+        if (result == null) {
+            _resultArray = result = buildResultArray();
+        }
+        return result;
+    }
+
+    /**
+     * Convenience method for converting contents of the buffer
+     * into a {@link BigDecimal}.
+     */
+    public BigDecimal contentsAsDecimal()
+        throws NumberFormatException
+    {
+        // Already got a pre-cut array?
+        if (_resultArray != null) {
+            return new BigDecimal(_resultArray);
+        }
+        // Or a shared buffer?
+        if (_inputStart >= 0) {
+            return new BigDecimal(_inputBuffer, _inputStart, _inputLen);
+        }
+        // Or if not, just a single buffer (the usual case)
+        if (_segmentSize == 0) {
+            return new BigDecimal(_currentSegment, 0, _currentSize);
+        }
+        // If not, let's just get it aggregated...
+        return new BigDecimal(contentsAsArray());
+    }
+
+    /**
+     * Convenience method for converting contents of the buffer
+     * into a Double value.
+     */
+    public double contentsAsDouble()
+        throws NumberFormatException
+    {
+        return NumberInput.parseDouble(contentsAsString());
+    }
+
+    /*
+    /**********************************************************
+    /* Public mutators:
+    /**********************************************************
+     */
+
+    /**
+     * Method called to make sure that buffer is not using shared input
+     * buffer; if it is, it will copy such contents to private buffer.
+     */
+    public void ensureNotShared() {
+        if (_inputStart >= 0) {
+            unshare(16);
+        }
+    }
+
+    public void append(char c) {
+        // Using shared buffer so far?
+        if (_inputStart >= 0) {
+            unshare(16);
+        }
+        _resultString = null;
+        _resultArray = null;
+        // Room in current segment?
+        char[] curr = _currentSegment;
+        if (_currentSize >= curr.length) {
+            expand(1);
+            curr = _currentSegment;
+        }
+        curr[_currentSize++] = c;
+    }
+
+    public void append(char[] c, int start, int len)
+    {
+        // Can't append to shared buf (sanity check)
+        if (_inputStart >= 0) {
+            unshare(len);
+        }
+        _resultString = null;
+        _resultArray = null;
+
+        // Room in current segment?
+        char[] curr = _currentSegment;
+        int max = curr.length - _currentSize;
+            
+        if (max >= len) {
+            System.arraycopy(c, start, curr, _currentSize, len);
+            _currentSize += len;
+        } else {
+            // No room for all, need to copy part(s):
+            if (max > 0) {
+                System.arraycopy(c, start, curr, _currentSize, max);
+                start += max;
+                len -= max;
+            }
+            // And then allocate new segment; we are guaranteed to now
+            // have enough room in segment.
+            expand(len); // note: curr != _currentSegment after this
+            System.arraycopy(c, start, _currentSegment, 0, len);
+            _currentSize = len;
+        }
+    }
+
+    public void append(String str, int offset, int len)
+    {
+        // Can't append to shared buf (sanity check)
+        if (_inputStart >= 0) {
+            unshare(len);
+        }
+        _resultString = null;
+        _resultArray = null;
+
+        // Room in current segment?
+        char[] curr = _currentSegment;
+        int max = curr.length - _currentSize;
+        if (max >= len) {
+            str.getChars(offset, offset+len, curr, _currentSize);
+            _currentSize += len;
+        } else {
+            // No room for all, need to copy part(s):
+            if (max > 0) {
+                str.getChars(offset, offset+max, curr, _currentSize);
+                len -= max;
+                offset += max;
+            }
+            /* And then allocate new segment; we are guaranteed to now
+             * have enough room in segment.
+             */
+            expand(len);
+            str.getChars(offset, offset+len, _currentSegment, 0);
+            _currentSize = len;
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Raw access, for high-performance use:
+    /**********************************************************
+     */
+
+    public char[] getCurrentSegment()
+    {
+        /* Since the intention of the caller is to directly add stuff into
+         * buffers, we should NOT have anything in shared buffer... ie. may
+         * need to unshare contents.
+         */
+        if (_inputStart >= 0) {
+            unshare(1);
+        } else {
+            char[] curr = _currentSegment;
+            if (curr == null) {
+                _currentSegment = findBuffer(0);
+            } else if (_currentSize >= curr.length) {
+                // Plus, we better have room for at least one more char
+                expand(1);
+            }
+        }
+        return _currentSegment;
+    }
+
+    public final char[] emptyAndGetCurrentSegment()
+    {
+        // inlined 'resetWithEmpty()'
+        _inputStart = -1; // indicates shared buffer not used
+        _currentSize = 0;
+        _inputLen = 0;
+
+        _inputBuffer = null;
+        _resultString = null;
+        _resultArray = null;
+
+        // And then reset internal input buffers, if necessary:
+        if (_hasSegments) {
+            clearSegments();
+        }
+        char[] curr = _currentSegment;
+        if (curr == null) {
+            _currentSegment = curr = findBuffer(0);
+        }
+        return curr;
+    }
+
+    public int getCurrentSegmentSize() {
+        return _currentSize;
+    }
+
+    public void setCurrentLength(int len) {
+        _currentSize = len;
+    }
+
+    public char[] finishCurrentSegment()
+    {
+        if (_segments == null) {
+            _segments = new ArrayList<char[]>();
+        }
+        _hasSegments = true;
+        _segments.add(_currentSegment);
+        int oldLen = _currentSegment.length;
+        _segmentSize += oldLen;
+        // Let's grow segments by 50%
+        int newLen = Math.min(oldLen + (oldLen >> 1), MAX_SEGMENT_LEN);
+        char[] curr = _charArray(newLen);
+        _currentSize = 0;
+        _currentSegment = curr;
+        return curr;
+    }
+
+    /**
+     * Method called to expand size of the current segment, to
+     * accomodate for more contiguous content. Usually only
+     * used when parsing tokens like names.
+     */
+    public char[] expandCurrentSegment()
+    {
+        char[] curr = _currentSegment;
+        // Let's grow by 50%
+        int len = curr.length;
+        // Must grow by at least 1 char, no matter what
+        int newLen = (len == MAX_SEGMENT_LEN) ?
+            (MAX_SEGMENT_LEN + 1) : Math.min(MAX_SEGMENT_LEN, len + (len >> 1));
+        _currentSegment = _charArray(newLen);
+        System.arraycopy(curr, 0, _currentSegment, 0, len);
+        return _currentSegment;
+    }
+
+    /*
+    /**********************************************************
+    /* Standard methods:
+    /**********************************************************
+     */
+
+    /**
+     * Note: calling this method may not be as efficient as calling
+     * {@link #contentsAsString}, since it's not guaranteed that resulting
+     * String is cached.
+     */
+    @Override
+    public String toString() {
+         return contentsAsString();
+    }
+
+    /*
+    /**********************************************************
+    /* Internal methods:
+    /**********************************************************
+     */
+
+    /**
+     * Method called if/when we need to append content when we have been
+     * initialized to use shared buffer.
+     */
+    private void unshare(int needExtra)
+    {
+        int sharedLen = _inputLen;
+        _inputLen = 0;
+        char[] inputBuf = _inputBuffer;
+        _inputBuffer = null;
+        int start = _inputStart;
+        _inputStart = -1;
+
+        // Is buffer big enough, or do we need to reallocate?
+        int needed = sharedLen+needExtra;
+        if (_currentSegment == null || needed > _currentSegment.length) {
+            _currentSegment = findBuffer(needed);
+        }
+        if (sharedLen > 0) {
+            System.arraycopy(inputBuf, start, _currentSegment, 0, sharedLen);
+        }
+        _segmentSize = 0;
+        _currentSize = sharedLen;
+    }
+
+    /**
+     * Method called when current segment is full, to allocate new
+     * segment.
+     */
+    private void expand(int minNewSegmentSize)
+    {
+        // First, let's move current segment to segment list:
+        if (_segments == null) {
+            _segments = new ArrayList<char[]>();
+        }
+        char[] curr = _currentSegment;
+        _hasSegments = true;
+        _segments.add(curr);
+        _segmentSize += curr.length;
+        int oldLen = curr.length;
+        // Let's grow segments by 50% minimum
+        int sizeAddition = oldLen >> 1;
+        if (sizeAddition < minNewSegmentSize) {
+            sizeAddition = minNewSegmentSize;
+        }
+        curr = _charArray(Math.min(MAX_SEGMENT_LEN, oldLen + sizeAddition));
+        _currentSize = 0;
+        _currentSegment = curr;
+    }
+
+    private char[] buildResultArray()
+    {
+        if (_resultString != null) { // Can take a shortcut...
+            return _resultString.toCharArray();
+        }
+        char[] result;
+        
+        // Do we use shared array?
+        if (_inputStart >= 0) {
+            if (_inputLen < 1) {
+                return NO_CHARS;
+            }
+            result = _charArray(_inputLen);
+            System.arraycopy(_inputBuffer, _inputStart, result, 0,
+                             _inputLen);
+        } else { // nope 
+            int size = size();
+            if (size < 1) {
+                return NO_CHARS;
+            }
+            int offset = 0;
+            result = _charArray(size);
+            if (_segments != null) {
+                for (int i = 0, len = _segments.size(); i < len; ++i) {
+                    char[] curr = (char[]) _segments.get(i);
+                    int currLen = curr.length;
+                    System.arraycopy(curr, 0, result, offset, currLen);
+                    offset += currLen;
+                }
+            }
+            System.arraycopy(_currentSegment, 0, result, offset, _currentSize);
+        }
+        return result;
+    }
+
+    private final char[] _charArray(int len) {
+        return new char[len];
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/TokenBuffer.java b/src/main/java/com/fasterxml/jackson/core/util/TokenBuffer.java
new file mode 100644
index 0000000..b80ea05
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/TokenBuffer.java
@@ -0,0 +1,1219 @@
+package com.fasterxml.jackson.core.util;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.base.ParserMinimalBase;
+import com.fasterxml.jackson.core.io.SerializedString;
+import com.fasterxml.jackson.core.json.JsonReadContext;
+import com.fasterxml.jackson.core.json.JsonWriteContext;
+
+/**
+ * Utility class used for efficient storage of {@link JsonToken}
+ * sequences, needed for temporary buffering.
+ * Space efficient for different sequence lengths (especially so for smaller
+ * ones; but not significantly less efficient for larger), highly efficient
+ * for linear iteration and appending. Implemented as segmented/chunked
+ * linked list of tokens; only modifications are via appends.
+ * 
+ * @since 1.5
+ */
+public class TokenBuffer
+/* Won't use JsonGeneratorBase, to minimize overhead for validity
+ * checking
+ */
+    extends JsonGenerator
+{
+    protected final static int DEFAULT_PARSER_FEATURES = JsonParser.Feature.collectDefaults();
+
+    /*
+    /**********************************************************
+    /* Configuration
+    /**********************************************************
+     */
+
+    /**
+     * Object codec to use for stream-based object
+     *   conversion through parser/generator interfaces. If null,
+     *   such methods can not be used.
+     */
+    protected ObjectCodec _objectCodec;
+
+    /**
+     * Bit flag composed of bits that indicate which
+     * {@link com.fasterxml.jackson.core.JsonGenerator.Feature}s
+     * are enabled.
+     *<p>
+     * NOTE: most features have no effect on this class
+     */
+    protected int _generatorFeatures;
+
+    protected boolean _closed;
+    
+    /*
+    /**********************************************************
+    /* Token buffering state
+    /**********************************************************
+     */
+
+    /**
+     * First segment, for contents this buffer has
+     */
+    protected Segment _first;
+
+    /**
+     * Last segment of this buffer, one that is used
+     * for appending more tokens
+     */
+    protected Segment _last;
+    
+    /**
+     * Offset within last segment, 
+     */
+    protected int _appendOffset;
+
+    /*
+    /**********************************************************
+    /* Output state
+    /**********************************************************
+     */
+
+    protected JsonWriteContext _writeContext;
+
+    /*
+    /**********************************************************
+    /* Life-cycle
+    /**********************************************************
+     */
+
+    /**
+     * @param codec Object codec to use for stream-based object
+     *   conversion through parser/generator interfaces. If null,
+     *   such methods can not be used.
+     */
+    public TokenBuffer(ObjectCodec codec)
+    {
+        _objectCodec = codec;
+        _generatorFeatures = DEFAULT_PARSER_FEATURES;
+        _writeContext = JsonWriteContext.createRootContext();
+        // at first we have just one segment
+        _first = _last = new Segment();
+        _appendOffset = 0;
+    }
+    
+    /**
+     * Method used to create a {@link JsonParser} that can read contents
+     * stored in this buffer. Will use default <code>_objectCodec</code> for
+     * object conversions.
+     *<p>
+     * Note: instances are not synchronized, that is, they are not thread-safe
+     * if there are concurrent appends to the underlying buffer.
+     * 
+     * @return Parser that can be used for reading contents stored in this buffer
+     */
+    public JsonParser asParser()
+    {
+        return asParser(_objectCodec);
+    }
+
+    /**
+     * Method used to create a {@link JsonParser} that can read contents
+     * stored in this buffer.
+     *<p>
+     * Note: instances are not synchronized, that is, they are not thread-safe
+     * if there are concurrent appends to the underlying buffer.
+     *
+     * @param codec Object codec to use for stream-based object
+     *   conversion through parser/generator interfaces. If null,
+     *   such methods can not be used.
+     * 
+     * @return Parser that can be used for reading contents stored in this buffer
+     */
+    public JsonParser asParser(ObjectCodec codec)
+    {
+        return new Parser(_first, codec);
+    }
+
+    /**
+     * @param src Parser to use for accessing source information
+     *    like location, configured codec
+     */
+    public JsonParser asParser(JsonParser src)
+    {
+        Parser p = new Parser(_first, src.getCodec());
+        p.setLocation(src.getTokenLocation());
+        return p;
+    }
+    
+    /*
+    /**********************************************************
+    /* Other custom methods not needed for implementing interfaces
+    /**********************************************************
+     */
+
+    /**
+     * Helper method that will write all contents of this buffer
+     * using given {@link JsonGenerator}.
+     *<p>
+     * Note: this method would be enough to implement
+     * <code>JsonSerializer</code>  for <code>TokenBuffer</code> type;
+     * but we can not have upwards
+     * references (from core to mapper package); and as such we also
+     * can not take second argument.
+     */
+    public void serialize(JsonGenerator jgen)
+        throws IOException, JsonGenerationException
+    {
+        Segment segment = _first;
+        int ptr = -1;
+
+        while (true) {
+            if (++ptr >= Segment.TOKENS_PER_SEGMENT) {
+                ptr = 0;
+                segment = segment.next();
+                if (segment == null) break;
+            }
+            JsonToken t = segment.type(ptr);
+            if (t == null) break;
+
+            // Note: copied from 'copyCurrentEvent'...
+            switch (t) {
+            case START_OBJECT:
+                jgen.writeStartObject();
+                break;
+            case END_OBJECT:
+                jgen.writeEndObject();
+                break;
+            case START_ARRAY:
+                jgen.writeStartArray();
+                break;
+            case END_ARRAY:
+                jgen.writeEndArray();
+                break;
+            case FIELD_NAME:
+            {
+                // 13-Dec-2010, tatu: Maybe we should start using different type tokens to reduce casting?
+                Object ob = segment.get(ptr);
+                if (ob instanceof SerializableString) {
+                    jgen.writeFieldName((SerializableString) ob);
+                } else {
+                    jgen.writeFieldName((String) ob);
+                }
+            }
+                break;
+            case VALUE_STRING:
+                {
+                    Object ob = segment.get(ptr);
+                    if (ob instanceof SerializableString) {
+                        jgen.writeString((SerializableString) ob);
+                    } else {
+                        jgen.writeString((String) ob);
+                    }
+                }
+                break;
+            case VALUE_NUMBER_INT:
+                {
+                    Number n = (Number) segment.get(ptr);
+                    if (n instanceof BigInteger) {
+                        jgen.writeNumber((BigInteger) n);
+                    } else if (n instanceof Long) {
+                        jgen.writeNumber(n.longValue());
+                    } else {
+                        jgen.writeNumber(n.intValue());
+                    }
+                }
+                break;
+            case VALUE_NUMBER_FLOAT:
+                {
+                    Object n = segment.get(ptr);
+                    if (n instanceof BigDecimal) {
+                        jgen.writeNumber((BigDecimal) n);
+                    } else if (n instanceof Float) {
+                        jgen.writeNumber(((Float) n).floatValue());
+                    } else if (n instanceof Double) {
+                        jgen.writeNumber(((Double) n).doubleValue());
+                    } else if (n == null) {
+                        jgen.writeNull();
+                    } else if (n instanceof String) {
+                        jgen.writeNumber((String) n);
+                    } else {
+                        throw new JsonGenerationException("Unrecognized value type for VALUE_NUMBER_FLOAT: "+n.getClass().getName()+", can not serialize");
+                    }
+                }
+                break;
+            case VALUE_TRUE:
+                jgen.writeBoolean(true);
+                break;
+            case VALUE_FALSE:
+                jgen.writeBoolean(false);
+                break;
+            case VALUE_NULL:
+                jgen.writeNull();
+                break;
+            case VALUE_EMBEDDED_OBJECT:
+                jgen.writeObject(segment.get(ptr));
+                break;
+            default:
+                throw new RuntimeException("Internal error: should never end up through this code path");
+            }
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        // Let's print up to 100 first tokens...
+        final int MAX_COUNT = 100;
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("[TokenBuffer: ");
+        JsonParser jp = asParser();
+        int count = 0;
+
+        while (true) {
+            JsonToken t;
+            try {
+                t = jp.nextToken();
+            } catch (IOException ioe) { // should never occur
+                throw new IllegalStateException(ioe);
+            }
+            if (t == null) break;
+            if (count < MAX_COUNT) {
+                if (count > 0) {
+                    sb.append(", ");
+                }
+                sb.append(t.toString());
+            }
+            ++count;
+        }
+
+        if (count >= MAX_COUNT) {
+            sb.append(" ... (truncated ").append(count-MAX_COUNT).append(" entries)");
+        }
+        sb.append(']');
+        return sb.toString();
+    }
+        
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation: configuration
+    /**********************************************************
+     */
+
+    @Override
+    public JsonGenerator enable(Feature f) {
+        _generatorFeatures |= f.getMask();
+        return this;
+    }
+
+    @Override
+    public JsonGenerator disable(Feature f) {
+        _generatorFeatures &= ~f.getMask();
+        return this;
+    }
+
+    //public JsonGenerator configure(Feature f, boolean state) { }
+
+    @Override
+    public boolean isEnabled(Feature f) {
+        return (_generatorFeatures & f.getMask()) != 0;
+    }
+
+    @Override
+    public JsonGenerator useDefaultPrettyPrinter() {
+        // No-op: we don't indent
+        return this;
+    }
+
+    @Override
+    public JsonGenerator setCodec(ObjectCodec oc) {
+        _objectCodec = oc;
+        return this;
+    }
+
+    @Override
+    public ObjectCodec getCodec() { return _objectCodec; }
+
+    @Override
+    public final JsonWriteContext getOutputContext() { return _writeContext; }
+
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation: low-level output handling
+    /**********************************************************
+     */
+
+    @Override
+    public void flush() throws IOException { /* NOP */ }
+
+    @Override
+    public void close() throws IOException {
+        _closed = true;
+    }
+
+    @Override
+    public boolean isClosed() { return _closed; }
+
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation: write methods, structural
+    /**********************************************************
+     */
+
+    @Override
+    public final void writeStartArray()
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.START_ARRAY);
+        _writeContext = _writeContext.createChildArrayContext();
+    }
+
+    @Override
+    public final void writeEndArray()
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.END_ARRAY);
+        // Let's allow unbalanced tho... i.e. not run out of root level, ever
+        JsonWriteContext c = _writeContext.getParent();
+        if (c != null) {
+            _writeContext = c;
+        }
+    }
+
+    @Override
+    public final void writeStartObject()
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.START_OBJECT);
+        _writeContext = _writeContext.createChildObjectContext();
+    }
+
+    @Override
+    public final void writeEndObject()
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.END_OBJECT);
+        // Let's allow unbalanced tho... i.e. not run out of root level, ever
+        JsonWriteContext c = _writeContext.getParent();
+        if (c != null) {
+            _writeContext = c;
+        }
+    }
+
+    @Override
+    public final void writeFieldName(String name)
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.FIELD_NAME, name);
+        _writeContext.writeFieldName(name);
+    }
+
+    @Override
+    public void writeFieldName(SerializableString name)
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.FIELD_NAME, name);
+        _writeContext.writeFieldName(name.getValue());
+    }
+
+    @Override
+    public void writeFieldName(SerializedString name)
+        throws IOException, JsonGenerationException
+    {
+        _append(JsonToken.FIELD_NAME, name);
+        _writeContext.writeFieldName(name.getValue());
+    }
+    
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation: write methods, textual
+    /**********************************************************
+     */
+
+    @Override
+    public void writeString(String text) throws IOException,JsonGenerationException {
+        if (text == null) {
+            writeNull();
+        } else {
+            _append(JsonToken.VALUE_STRING, text);
+        }
+    }
+
+    @Override
+    public void writeString(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+        writeString(new String(text, offset, len));
+    }
+
+    @Override
+    public void writeString(SerializableString text) throws IOException, JsonGenerationException {
+        if (text == null) {
+            writeNull();
+        } else {
+            _append(JsonToken.VALUE_STRING, text);
+        }
+    }
+    
+    @Override
+    public void writeRawUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException
+    {
+        // could add support for buffering if we really want it...
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeUTF8String(byte[] text, int offset, int length)
+        throws IOException, JsonGenerationException
+    {
+        // could add support for buffering if we really want it...
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(String text) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(String text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRaw(char c) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRawValue(String text) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRawValue(String text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    @Override
+    public void writeRawValue(char[] text, int offset, int len) throws IOException, JsonGenerationException {
+        _reportUnsupportedOperation();
+    }
+
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation: write methods, primitive types
+    /**********************************************************
+     */
+
+    @Override
+    public void writeNumber(int i) throws IOException, JsonGenerationException {
+        _append(JsonToken.VALUE_NUMBER_INT, Integer.valueOf(i));
+    }
+
+    @Override
+    public void writeNumber(long l) throws IOException, JsonGenerationException {
+        _append(JsonToken.VALUE_NUMBER_INT, Long.valueOf(l));
+    }
+
+    @Override
+    public void writeNumber(double d) throws IOException,JsonGenerationException {
+        _append(JsonToken.VALUE_NUMBER_FLOAT, Double.valueOf(d));
+    }
+
+    @Override
+    public void writeNumber(float f) throws IOException, JsonGenerationException {
+        _append(JsonToken.VALUE_NUMBER_FLOAT, Float.valueOf(f));
+    }
+
+    @Override
+    public void writeNumber(BigDecimal dec) throws IOException,JsonGenerationException {
+        if (dec == null) {
+            writeNull();
+        } else {
+            _append(JsonToken.VALUE_NUMBER_FLOAT, dec);
+        }
+    }
+
+    @Override
+    public void writeNumber(BigInteger v) throws IOException, JsonGenerationException {
+        if (v == null) {
+            writeNull();
+        } else {
+            _append(JsonToken.VALUE_NUMBER_INT, v);
+        }
+    }
+
+    @Override
+    public void writeNumber(String encodedValue) throws IOException, JsonGenerationException {
+        /* 03-Dec-2010, tatu: related to [JACKSON-423], should try to keep as numeric
+         *   identity as long as possible
+         */
+        _append(JsonToken.VALUE_NUMBER_FLOAT, encodedValue);
+    }
+
+    @Override
+    public void writeBoolean(boolean state) throws IOException,JsonGenerationException {
+        _append(state ? JsonToken.VALUE_TRUE : JsonToken.VALUE_FALSE);
+    }
+
+    @Override
+    public void writeNull() throws IOException, JsonGenerationException {
+        _append(JsonToken.VALUE_NULL);
+    }
+
+    /*
+    /***********************************************************
+    /* JsonGenerator implementation: write methods for POJOs/trees
+    /***********************************************************
+     */
+
+    @Override
+    public void writeObject(Object value)
+        throws IOException, JsonProcessingException
+    {
+        // embedded means that no conversions should be done...
+        _append(JsonToken.VALUE_EMBEDDED_OBJECT, value);
+    }
+
+    @Override
+    public void writeTree(JsonNode rootNode)
+        throws IOException, JsonProcessingException
+    {
+        /* 31-Dec-2009, tatu: no need to convert trees either is there?
+         *  (note: may need to re-evaluate at some point)
+         */
+        _append(JsonToken.VALUE_EMBEDDED_OBJECT, rootNode);
+    }
+
+    /*
+    /***********************************************************
+    /* JsonGenerator implementation; binary
+    /***********************************************************
+     */
+
+    @Override
+    public void writeBinary(Base64Variant b64variant, byte[] data, int offset, int len)
+        throws IOException, JsonGenerationException
+    {
+        /* 31-Dec-2009, tatu: can do this using multiple alternatives; but for
+         *   now, let's try to limit number of conversions.
+         *   The only (?) tricky thing is that of whether to preserve variant,
+         *   seems pointless, so let's not worry about it unless there's some
+         *   compelling reason to.
+         */
+        byte[] copy = new byte[len];
+        System.arraycopy(data, offset, copy, 0, len);
+        writeObject(copy);
+    }
+
+    /*
+    /**********************************************************
+    /* JsonGenerator implementation; pass-through copy
+    /**********************************************************
+     */
+
+    @Override
+    public void copyCurrentEvent(JsonParser jp) throws IOException, JsonProcessingException
+    {
+        switch (jp.getCurrentToken()) {
+        case START_OBJECT:
+            writeStartObject();
+            break;
+        case END_OBJECT:
+            writeEndObject();
+            break;
+        case START_ARRAY:
+            writeStartArray();
+            break;
+        case END_ARRAY:
+            writeEndArray();
+            break;
+        case FIELD_NAME:
+            writeFieldName(jp.getCurrentName());
+            break;
+        case VALUE_STRING:
+            if (jp.hasTextCharacters()) {
+                writeString(jp.getTextCharacters(), jp.getTextOffset(), jp.getTextLength());
+            } else {
+                writeString(jp.getText());
+            }
+            break;
+        case VALUE_NUMBER_INT:
+            switch (jp.getNumberType()) {
+            case INT:
+                writeNumber(jp.getIntValue());
+                break;
+            case BIG_INTEGER:
+                writeNumber(jp.getBigIntegerValue());
+                break;
+            default:
+                writeNumber(jp.getLongValue());
+            }
+            break;
+        case VALUE_NUMBER_FLOAT:
+            switch (jp.getNumberType()) {
+            case BIG_DECIMAL:
+                writeNumber(jp.getDecimalValue());
+                break;
+            case FLOAT:
+                writeNumber(jp.getFloatValue());
+                break;
+            default:
+                writeNumber(jp.getDoubleValue());
+            }
+            break;
+        case VALUE_TRUE:
+            writeBoolean(true);
+            break;
+        case VALUE_FALSE:
+            writeBoolean(false);
+            break;
+        case VALUE_NULL:
+            writeNull();
+            break;
+        case VALUE_EMBEDDED_OBJECT:
+            writeObject(jp.getEmbeddedObject());
+            break;
+        default:
+            throw new RuntimeException("Internal error: should never end up through this code path");
+        }
+    }
+
+    @Override
+    public void copyCurrentStructure(JsonParser jp) throws IOException, JsonProcessingException {
+        JsonToken t = jp.getCurrentToken();
+
+        // Let's handle field-name separately first
+        if (t == JsonToken.FIELD_NAME) {
+            writeFieldName(jp.getCurrentName());
+            t = jp.nextToken();
+            // fall-through to copy the associated value
+        }
+
+        switch (t) {
+        case START_ARRAY:
+            writeStartArray();
+            while (jp.nextToken() != JsonToken.END_ARRAY) {
+                copyCurrentStructure(jp);
+            }
+            writeEndArray();
+            break;
+        case START_OBJECT:
+            writeStartObject();
+            while (jp.nextToken() != JsonToken.END_OBJECT) {
+                copyCurrentStructure(jp);
+            }
+            writeEndObject();
+            break;
+        default: // others are simple:
+            copyCurrentEvent(jp);
+        }
+    }
+    
+    /*
+    /**********************************************************
+    /* Internal methods
+    /**********************************************************
+     */
+    protected final void _append(JsonToken type) {
+        Segment next = _last.append(_appendOffset, type);
+        if (next == null) {
+            ++_appendOffset;
+        } else {
+            _last = next;
+            _appendOffset = 1; // since we added first at 0
+        }
+    }
+
+    protected final void _append(JsonToken type, Object value) {
+        Segment next = _last.append(_appendOffset, type, value);
+        if (next == null) {
+            ++_appendOffset;
+        } else {
+            _last = next;
+            _appendOffset = 1;
+        }
+    }
+    
+    protected void _reportUnsupportedOperation() {
+        throw new UnsupportedOperationException("Called operation not supported for TokenBuffer");
+    }
+    
+    /*
+    /**********************************************************
+    /* Supporting classes
+    /**********************************************************
+     */
+
+    protected final static class Parser
+        extends ParserMinimalBase
+    {
+        protected ObjectCodec _codec;
+
+        /*
+        /**********************************************************
+        /* Parsing state
+        /**********************************************************
+         */
+
+        /**
+         * Currently active segment
+         */
+        protected Segment _segment;
+
+        /**
+         * Pointer to current token within current segment
+         */
+        protected int _segmentPtr;
+
+        /**
+         * Information about parser context, context in which
+         * the next token is to be parsed (root, array, object).
+         */
+        protected JsonReadContext _parsingContext;
+        
+        protected boolean _closed;
+
+        protected transient ByteArrayBuilder _byteBuilder;
+
+        protected JsonLocation _location = null;
+        
+        /*
+        /**********************************************************
+        /* Construction, init
+        /**********************************************************
+         */
+        
+        public Parser(Segment firstSeg, ObjectCodec codec)
+        {
+            super(0);
+            _segment = firstSeg;
+            _segmentPtr = -1; // not yet read
+            _codec = codec;
+            _parsingContext = JsonReadContext.createRootContext(-1, -1);
+        }
+
+        public void setLocation(JsonLocation l) {
+            _location = l;
+        }
+        
+        @Override
+        public ObjectCodec getCodec() { return _codec; }
+
+        @Override
+        public void setCodec(ObjectCodec c) { _codec = c; }
+
+        /*
+        /**********************************************************
+        /* Extended API beyond JsonParser
+        /**********************************************************
+         */
+        
+        public JsonToken peekNextToken()
+            throws IOException, JsonParseException
+        {
+            // closed? nothing more to peek, either
+            if (_closed) return null;
+            Segment seg = _segment;
+            int ptr = _segmentPtr+1;
+            if (ptr >= Segment.TOKENS_PER_SEGMENT) {
+                ptr = 0;
+                seg = (seg == null) ? null : seg.next();
+            }
+            return (seg == null) ? null : seg.type(ptr);
+        }
+        
+        /*
+        /**********************************************************
+        /* Closeable implementation
+        /**********************************************************
+         */
+
+        @Override
+        public void close() throws IOException {
+            if (!_closed) {
+                _closed = true;
+            }
+        }
+
+        /*
+        /**********************************************************
+        /* Public API, traversal
+        /**********************************************************
+         */
+        
+        @Override
+        public JsonToken nextToken() throws IOException, JsonParseException
+        {
+            // If we are closed, nothing more to do
+            if (_closed || (_segment == null)) return null;
+
+            // Ok, then: any more tokens?
+            if (++_segmentPtr >= Segment.TOKENS_PER_SEGMENT) {
+                _segmentPtr = 0;
+                _segment = _segment.next();
+                if (_segment == null) {
+                    return null;
+                }
+            }
+            _currToken = _segment.type(_segmentPtr);
+            // Field name? Need to update context
+            if (_currToken == JsonToken.FIELD_NAME) {
+                Object ob = _currentObject();
+                String name = (ob instanceof String) ? ((String) ob) : ob.toString();
+                _parsingContext.setCurrentName(name);
+            } else if (_currToken == JsonToken.START_OBJECT) {
+                _parsingContext = _parsingContext.createChildObjectContext(-1, -1);
+            } else if (_currToken == JsonToken.START_ARRAY) {
+                _parsingContext = _parsingContext.createChildArrayContext(-1, -1);
+            } else if (_currToken == JsonToken.END_OBJECT
+                    || _currToken == JsonToken.END_ARRAY) {
+                // Closing JSON Object/Array? Close matching context
+                _parsingContext = _parsingContext.getParent();
+                // but allow unbalanced cases too (more close markers)
+                if (_parsingContext == null) {
+                    _parsingContext = JsonReadContext.createRootContext(-1, -1);
+                }
+            }
+            return _currToken;
+        }
+
+        @Override
+        public boolean isClosed() { return _closed; }
+
+        /*
+        /**********************************************************
+        /* Public API, token accessors
+        /**********************************************************
+         */
+        
+        @Override
+        public JsonStreamContext getParsingContext() { return _parsingContext; }
+
+        @Override
+        public JsonLocation getTokenLocation() { return getCurrentLocation(); }
+
+        @Override
+        public JsonLocation getCurrentLocation() {
+            return (_location == null) ? JsonLocation.NA : _location;
+        }
+
+        @Override
+        public String getCurrentName() { return _parsingContext.getCurrentName(); }
+        
+        /*
+        /**********************************************************
+        /* Public API, access to token information, text
+        /**********************************************************
+         */
+        
+        @Override
+        public String getText()
+        {
+            // common cases first:
+            if (_currToken == JsonToken.VALUE_STRING
+                    || _currToken == JsonToken.FIELD_NAME) {
+                Object ob = _currentObject();
+                if (ob instanceof String) {
+                    return (String) ob;
+                }
+                return (ob == null) ? null : ob.toString();
+            }
+            if (_currToken == null) {
+                return null;
+            }
+            switch (_currToken) {
+            case VALUE_NUMBER_INT:
+            case VALUE_NUMBER_FLOAT:
+                Object ob = _currentObject();
+                return (ob == null) ? null : ob.toString();
+            }
+            return _currToken.asString();
+        }
+
+        @Override
+        public char[] getTextCharacters() {
+            String str = getText();
+            return (str == null) ? null : str.toCharArray();
+        }
+
+        @Override
+        public int getTextLength() {
+            String str = getText();
+            return (str == null) ? 0 : str.length();
+        }
+
+        @Override
+        public int getTextOffset() { return 0; }
+
+        @Override
+        public boolean hasTextCharacters() {
+            // We never have raw buffer available, so:
+            return false;
+        }
+        
+        /*
+        /**********************************************************
+        /* Public API, access to token information, numeric
+        /**********************************************************
+         */
+
+        @Override
+        public BigInteger getBigIntegerValue() throws IOException, JsonParseException
+        {
+            Number n = getNumberValue();
+            if (n instanceof BigInteger) {
+                return (BigInteger) n;
+            }
+            switch (getNumberType()) {
+            case BIG_DECIMAL:
+                return ((BigDecimal) n).toBigInteger();
+            }
+            // int/long is simple, but let's also just truncate float/double:
+            return BigInteger.valueOf(n.longValue());
+        }
+
+        @Override
+        public BigDecimal getDecimalValue() throws IOException, JsonParseException
+        {
+            Number n = getNumberValue();
+            if (n instanceof BigDecimal) {
+                return (BigDecimal) n;
+            }
+            switch (getNumberType()) {
+            case INT:
+            case LONG:
+                return BigDecimal.valueOf(n.longValue());
+            case BIG_INTEGER:
+                return new BigDecimal((BigInteger) n);
+            }
+            // float or double
+            return BigDecimal.valueOf(n.doubleValue());
+        }
+
+        @Override
+        public double getDoubleValue() throws IOException, JsonParseException {
+            return getNumberValue().doubleValue();
+        }
+
+        @Override
+        public float getFloatValue() throws IOException, JsonParseException {
+            return getNumberValue().floatValue();
+        }
+
+        @Override
+        public int getIntValue() throws IOException, JsonParseException
+        {
+            // optimize common case:
+            if (_currToken == JsonToken.VALUE_NUMBER_INT) {
+                return ((Number) _currentObject()).intValue();
+            }
+            return getNumberValue().intValue();
+        }
+
+        @Override
+        public long getLongValue() throws IOException, JsonParseException {
+            return getNumberValue().longValue();
+        }
+
+        @Override
+        public NumberType getNumberType() throws IOException, JsonParseException
+        {
+            Number n = getNumberValue();
+            if (n instanceof Integer) return NumberType.INT;
+            if (n instanceof Long) return NumberType.LONG;
+            if (n instanceof Double) return NumberType.DOUBLE;
+            if (n instanceof BigDecimal) return NumberType.BIG_DECIMAL;
+            if (n instanceof Float) return NumberType.FLOAT;
+            if (n instanceof BigInteger) return NumberType.BIG_INTEGER;
+            return null;
+        }
+
+        @Override
+        public final Number getNumberValue() throws IOException, JsonParseException {
+            _checkIsNumber();
+            return (Number) _currentObject();
+        }
+        
+        /*
+        /**********************************************************
+        /* Public API, access to token information, other
+        /**********************************************************
+         */
+
+        @Override
+        public Object getEmbeddedObject()
+        {
+            if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) {
+                return _currentObject();
+            }
+            return null;
+        }
+
+        @Override
+        public byte[] getBinaryValue(Base64Variant b64variant) throws IOException, JsonParseException
+        {
+            // First: maybe we some special types?
+            if (_currToken == JsonToken.VALUE_EMBEDDED_OBJECT) {
+                // Embedded byte array would work nicely...
+                Object ob = _currentObject();
+                if (ob instanceof byte[]) {
+                    return (byte[]) ob;
+                }
+                // fall through to error case
+            }
+            if (_currToken != JsonToken.VALUE_STRING) {
+                throw _constructError("Current token ("+_currToken+") not VALUE_STRING (or VALUE_EMBEDDED_OBJECT with byte[]), can not access as binary");
+            }
+            final String str = getText();
+            if (str == null) {
+                return null;
+            }
+            ByteArrayBuilder builder = _byteBuilder;
+            if (builder == null) {
+                _byteBuilder = builder = new ByteArrayBuilder(100);
+            }
+            _decodeBase64(str, builder, b64variant);
+            return builder.toByteArray();
+        }
+
+        /*
+        /**********************************************************
+        /* Internal methods
+        /**********************************************************
+         */
+
+        protected final Object _currentObject() {
+            return _segment.get(_segmentPtr);
+        }
+
+        protected final void _checkIsNumber() throws JsonParseException
+        {
+            if (_currToken == null || !_currToken.isNumeric()) {
+                throw _constructError("Current token ("+_currToken+") not numeric, can not use numeric value accessors");
+            }
+        }
+
+        @Override
+        protected void _handleEOF() throws JsonParseException {
+            _throwInternal();
+        }
+    }
+    
+    /**
+     * Individual segment of TokenBuffer that can store up to 16 tokens
+     * (limited by 4 bits per token type marker requirement).
+     * Current implementation uses fixed length array; could alternatively
+     * use 16 distinct fields and switch statement (slightly more efficient
+     * storage, slightly slower access)
+     */
+    protected final static class Segment 
+    {
+        public final static int TOKENS_PER_SEGMENT = 16;
+        
+        /**
+         * Static array used for fast conversion between token markers and
+         * matching {@link JsonToken} instances
+         */
+        private final static JsonToken[] TOKEN_TYPES_BY_INDEX;
+        static {
+            // ... here we know that there are <= 16 values in JsonToken enum
+            TOKEN_TYPES_BY_INDEX = new JsonToken[16];
+            JsonToken[] t = JsonToken.values();
+            System.arraycopy(t, 1, TOKEN_TYPES_BY_INDEX, 1, Math.min(15, t.length - 1));
+        }
+
+        // // // Linking
+        
+        protected Segment _next;
+        
+        // // // State
+
+        /**
+         * Bit field used to store types of buffered tokens; 4 bits per token.
+         * Value 0 is reserved for "not in use"
+         */
+        protected long _tokenTypes;
+
+        
+        // Actual tokens
+
+        protected final Object[] _tokens = new Object[TOKENS_PER_SEGMENT];
+
+        public Segment() { }
+
+        // // // Accessors
+
+        public JsonToken type(int index)
+        {
+            long l = _tokenTypes;
+            if (index > 0) {
+                l >>= (index << 2);
+            }
+            int ix = ((int) l) & 0xF;
+            return TOKEN_TYPES_BY_INDEX[ix];
+        }
+        
+        public Object get(int index) {
+            return _tokens[index];
+        }
+
+        public Segment next() { return _next; }
+        
+        // // // Mutators
+
+        public Segment append(int index, JsonToken tokenType)
+        {
+            if (index < TOKENS_PER_SEGMENT) {
+                set(index, tokenType);
+                return null;
+            }
+            _next = new Segment();
+            _next.set(0, tokenType);
+            return _next;
+        }
+
+        public Segment append(int index, JsonToken tokenType, Object value)
+        {
+            if (index < TOKENS_PER_SEGMENT) {
+                set(index, tokenType, value);
+                return null;
+            }
+            _next = new Segment();
+            _next.set(0, tokenType, value);
+            return _next;
+        }
+        
+        public void set(int index, JsonToken tokenType)
+        {
+            long typeCode = tokenType.ordinal();
+            /* Assumption here is that there are no overwrites, just appends;
+             * and so no masking is needed
+             */
+            if (index > 0) {
+                typeCode <<= (index << 2);
+            }
+            _tokenTypes |= typeCode;
+        }
+
+        public void set(int index, JsonToken tokenType, Object value)
+        {
+            _tokens[index] = value;
+            long typeCode = tokenType.ordinal();
+            /* Assumption here is that there are no overwrites, just appends;
+             * and so no masking is needed
+             */
+            if (index > 0) {
+                typeCode <<= (index << 2);
+            }
+            _tokenTypes |= typeCode;
+        }
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/VersionUtil.java b/src/main/java/com/fasterxml/jackson/core/util/VersionUtil.java
new file mode 100644
index 0000000..fec896d
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/VersionUtil.java
@@ -0,0 +1,79 @@
+package com.fasterxml.jackson.core.util;
+
+import java.io.*;
+import java.util.regex.Pattern;
+
+import com.fasterxml.jackson.core.Version;
+
+/**
+ * Functionality for supporting exposing of component {@link Version}s.
+ * 
+ * @since 1.6
+ */
+public class VersionUtil
+{
+    public final static String VERSION_FILE = "VERSION.txt";
+
+    private final static Pattern VERSION_SEPARATOR = Pattern.compile("[-_./;:]");
+    
+    /**
+     * Helper method that will try to load version information for specified
+     * class. Implementation is simple: class loader that loaded specified
+     * class is asked to load resource with name "VERSION" from same
+     * location (package) as class itself had.
+     * If no version information is found, {@link Version#unknownVersion()} is
+     * returned.
+     */
+    public static Version versionFor(Class<?> cls)
+    {
+        InputStream in;
+        Version version = null;
+        
+        try {
+            in = cls.getResourceAsStream(VERSION_FILE);
+            if (in != null) {
+                try {
+                    BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+                    version = parseVersion(br.readLine());
+                } finally {
+                    try {
+                        in.close();
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+        } catch (IOException e) { }
+        return (version == null) ? Version.unknownVersion() : version;
+    }
+
+    public static Version parseVersion(String versionStr)
+    {
+        if (versionStr == null) return null;
+        versionStr = versionStr.trim();
+        if (versionStr.length() == 0) return null;
+        String[] parts = VERSION_SEPARATOR.split(versionStr);
+        // Let's not bother if there's no separate parts; otherwise use whatever we got
+        if (parts.length < 2) {
+            return null;
+        }
+        int major = parseVersionPart(parts[0]);
+        int minor = parseVersionPart(parts[1]);
+        int patch = (parts.length > 2) ? parseVersionPart(parts[2]) : 0;
+        String snapshot = (parts.length > 3) ? parts[3] : null;
+        return new Version(major, minor, patch, snapshot);
+    }
+
+    protected static int parseVersionPart(String partStr)
+    {
+        partStr = partStr.toString();
+        int len = partStr.length();
+        int number = 0;
+        for (int i = 0; i < len; ++i) {
+            char c = partStr.charAt(i);
+            if (c > '9' || c < '0') break;
+            number = (number * 10) + (c - '0');
+        }
+        return number;
+    }
+}
diff --git a/src/main/java/com/fasterxml/jackson/core/util/package-info.java b/src/main/java/com/fasterxml/jackson/core/util/package-info.java
new file mode 100644
index 0000000..9ad785f
--- /dev/null
+++ b/src/main/java/com/fasterxml/jackson/core/util/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Utility classes used by Jackson Core functionality.
+ */
+package com.fasterxml.jackson.core.util;