Merge pull request #312 from AlexYursha/master

Enable Jackson users to strip security-sensitive data from parser errors.
diff --git a/.travis.yml b/.travis.yml
index 75db9cc..73b3221 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,8 +14,8 @@
 branches:
   only:
     - master
+    - "2.8"
     - "2.7"
-    - "2.6"
 
 env:
   global:
diff --git a/README.md b/README.md
index b93f417..413657c 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@
 
 [![Build Status](https://travis-ci.org/FasterXML/jackson-core.svg?branch=master)](https://travis-ci.org/FasterXML/jackson-core) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-core/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.fasterxml.jackson.core/jackson-core)
 [![Javadoc](https://javadoc-emblem.rhcloud.com/doc/com.fasterxml.jackson.core/jackson-core/badge.svg)](http://www.javadoc.io/doc/com.fasterxml.jackson.core/jackson-core)
+[![Coverage Status](https://coveralls.io/repos/github/FasterXML/jackson-core/badge.svg?branch=master)](https://coveralls.io/github/FasterXML/jackson-core?branch=master)
 
 # Get it!
 
diff --git a/pom.xml b/pom.xml
index 5860f49..53ca4bf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,13 +3,13 @@
   <parent>
     <groupId>com.fasterxml.jackson</groupId>
     <artifactId>jackson-parent</artifactId>
-    <version>2.8</version>
+    <version>2.9-rc1-SNAPSHOT</version>
   </parent>
 
   <groupId>com.fasterxml.jackson.core</groupId>
   <artifactId>jackson-core</artifactId>
   <name>Jackson-core</name>
-  <version>2.8.2-SNAPSHOT</version>
+  <version>2.9.0-SNAPSHOT</version>
   <packaging>bundle</packaging>
   <description>Core Jackson abstractions, basic JSON streaming API implementation</description>
   <inceptionYear>2008</inceptionYear>
@@ -23,7 +23,7 @@
   </scm>
 
   <properties>
-    <!-- 29-Apr-2016, tatu: Retain Java6/JDK1.6 compatibility for streaming for Jackson 2.8 -->
+    <!-- 16-Sep-2016, tatu: Retain Java6/JDK1.6 compatibility for streaming for Jackson 2.x -->
     <javac.src.version>1.6</javac.src.version>
     <javac.target.version>1.6</javac.target.version>
 
diff --git a/release-notes/CREDITS b/release-notes/CREDITS
index c5e54ca..4aaca26 100644
--- a/release-notes/CREDITS
+++ b/release-notes/CREDITS
@@ -95,6 +95,11 @@
    input of a certain size
    (2.7.7)
 
+Allar Haav (haav@github)
+  * Reportef #317: ArrayIndexOutOfBoundsException: 200 on floating point number with exactly
+  200-length decimal part
+   (2.7.8)
+
 Mikael Staldal (mikaelstaldal@github)
   * Contributed fix for #265: `JsonStringEncoder` should allow passing `CharSequence`
    (2.8.0)
diff --git a/release-notes/VERSION b/release-notes/VERSION
index 7db8ee3..170fed2 100644
--- a/release-notes/VERSION
+++ b/release-notes/VERSION
@@ -14,6 +14,15 @@
 === Releases ===
 ------------------------------------------------------------------------
 
+2.9.0 (not yet released)
+
+#304: Optimize `NumberOutput.outputLong()` method
+
+2.8.3 (not yet released)
+
+#318: Add support for writing `byte[]` via `JsonGenerator.writeEmbeddedObject()`
+
+2.8.2 (30-Aug-2016)
 2.8.1 (20-Jul-2016)
 
 No changes since 2.8.0
@@ -49,11 +58,19 @@
   for `getCurrentToken()` and `getCurrentTokenId()`, respectively. Existing methods
   will likely be deprecated in 2.9.
 
-2.7.7 (not yet released)
+2.7.8 (not yet released)
+
+#317: ArrayIndexOutOfBoundsException: 200 on floating point number with exactly
+  200-length decimal part
+ (reported by Allar H)
+
+2.7.7 (27-Aug-2016)
 
 #307: JsonGenerationException: Split surrogate on writeRaw() input thrown for
   input of a certain size
  (reported by Mike N)
+#315: `OutOfMemoryError` when writing BigDecimal
+ (reported by gmethwin@github)
 
 2.7.6 (23-Jul-2016)
 
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
index 92be7cd..da75a61 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
@@ -1326,7 +1326,17 @@
      * @since 2.8
      */
     public void writeEmbeddedObject(Object object) throws IOException {
-        throw new JsonGenerationException("No native support for writing embedded objects",
+        // 01-Sep-2016, tatu: As per [core#318], handle small number of cases
+        if (object == null) {
+            writeNull();
+            return;
+        }
+        if (object instanceof byte[]) {
+            writeBinary((byte[]) object);
+            return;
+        }
+        throw new JsonGenerationException("No native support for writing embedded objects of type "
+                +object.getClass().getName(),
                 this);
     }
     
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java
index 2fd0011..661cad8 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java
@@ -227,7 +227,30 @@
           * 
           * @since 2.8
           */
-         ALLOW_MISSING_VALUES(false)
+         ALLOW_MISSING_VALUES(false),
+
+         /**
+          * Feature that determines whether {@link JsonParser} will allow for a single trailing
+          * comma following the final value (in an Array) or member (in an Object). These commas
+          * will simply be ignored.
+          * <p>
+          * For example, when this feature is enabled, <code>[true,true,]</code> is equivalent to
+          * <code>[true, true]</code> and <code>{"a": true,}</code> is equivalent to
+          * <code>{"a": true}</code>.
+          * <p>
+          * When combined with <code>ALLOW_MISSING_VALUES</code>, this feature takes priority, and
+          * the final trailing comma in an array declaration does not imply a missing
+          * (<code>null</code>) value. For example, when both <code>ALLOW_MISSING_VALUES</code>
+          * and <code>ALLOW_TRAILING_COMMA</code> are enabled, <code>[true,true,]</code> is
+          * equivalent to <code>[true, true]</code>, and <code>[true,true,,]</code> is equivalent to
+          * <code>[true, true, null]</code>.
+          * <p>
+          * Since the JSON specification does not permit trailing commas, this is a non-standard
+          * feature, and as such disabled by default.
+          *
+          * @since 2.9
+          */
+         ALLOW_TRAILING_COMMA(false)
          ;
 
         /**
diff --git a/src/main/java/com/fasterxml/jackson/core/JsonPointer.java b/src/main/java/com/fasterxml/jackson/core/JsonPointer.java
index f927336..6681992 100644
--- a/src/main/java/com/fasterxml/jackson/core/JsonPointer.java
+++ b/src/main/java/com/fasterxml/jackson/core/JsonPointer.java
@@ -20,6 +20,13 @@
 public class JsonPointer
 {
     /**
+     * Character used to separate segments.
+     *
+     * @since 2.9
+     */
+    public final static char SEPARATOR = '/';
+
+    /**
      * Marker instance used to represent segment that matches current
      * node or position (that is, returns true for
      * {@link #matches()}).
diff --git a/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java b/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
index 19bcfd6..d2d05f2 100644
--- a/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
+++ b/src/main/java/com/fasterxml/jackson/core/base/GeneratorBase.java
@@ -1,6 +1,7 @@
 package com.fasterxml.jackson.core.base;
 
 import java.io.*;
+import java.math.BigDecimal;
 
 import com.fasterxml.jackson.core.*;
 import com.fasterxml.jackson.core.json.DupDetector;
@@ -41,6 +42,16 @@
     protected final static String WRITE_RAW = "write a raw (unencoded) value";
     protected final static String WRITE_STRING = "write a string";
 
+    /**
+     * This value is the limit of scale allowed for serializing {@link BigDecimal}
+     * in "plain" (non-engineering) notation; intent is to prevent asymmetric
+     * attack whereupon simple eng-notation with big scale is used to generate
+     * huge "plain" serialization. See [core#315] for details.
+     * 
+     * @since 2.7.7
+     */
+    protected final static int MAX_BIG_DECIMAL_SCALE = 9999;
+    
     /*
     /**********************************************************
     /* Configuration
@@ -426,6 +437,26 @@
         return new DefaultPrettyPrinter();
     }
 
+    /**
+     * Helper method used to serialize a {@link java.math.BigDecimal} as a String,
+     * for serialization, taking into account configuration settings
+     *
+     * @since 2.7.7
+     */
+    protected String _asString(BigDecimal value) throws IOException {
+        if (Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features)) {
+            // 24-Aug-2016, tatu: [core#315] prevent possible DoS vector
+            int scale = value.scale();
+            if ((scale < -MAX_BIG_DECIMAL_SCALE) || (scale > MAX_BIG_DECIMAL_SCALE)) {
+                _reportError(String.format(
+"Attempt to write plain `java.math.BigDecimal` (see JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN) with illegal scale (%d): needs to be between [-%d, %d]",
+scale, MAX_BIG_DECIMAL_SCALE, MAX_BIG_DECIMAL_SCALE));
+            }
+            return value.toPlainString();
+        }
+        return value.toString();
+    }
+
     /*
     /**********************************************************
     /* UTF-8 related helper method(s)
diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
index 884cb97..5b3f931 100644
--- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
+++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
@@ -1028,9 +1028,8 @@
          */
     
         if ((_numTypesValid & NR_DOUBLE) != 0) {
-            /* Let's actually parse from String representation,
-             * to avoid rounding errors that non-decimal floating operations
-             * would incur
+            /* Let's actually parse from String representation, to avoid
+             * rounding errors that non-decimal floating operations could incur
              */
             _numberBigDecimal = NumberInput.parseBigDecimal(getText());
         } else if ((_numTypesValid & NR_BIGINT) != 0) {
diff --git a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java
index 5f0515a..b27de2e 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/ReaderBasedJsonParser.java
@@ -652,26 +652,20 @@
         _binaryValue = null;
 
         // Closing scope?
-        if (i == INT_RBRACKET) {
-            _updateLocation();
-            if (!_parsingContext.inArray()) {
-                _reportMismatchedEndMarker(i, '}');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            return (_currToken = JsonToken.END_ARRAY);
-        }
-        if (i == INT_RCURLY) {
-            _updateLocation();
-            if (!_parsingContext.inObject()) {
-                _reportMismatchedEndMarker(i, ']');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            return (_currToken = JsonToken.END_OBJECT);
+        if (i == INT_RBRACKET || i == INT_RCURLY) {
+            _closeScope(i);
+            return _currToken;
         }
 
         // Nope: do we then expect a comma?
         if (_parsingContext.expectComma()) {
             i = _skipComma(i);
+
+            // Was that a trailing comma?
+            if (isEnabled(Feature.ALLOW_TRAILING_COMMA) && (i == INT_RBRACKET || i == INT_RCURLY)) {
+                _closeScope(i);
+                return _currToken;
+            }
         }
 
         /* And should we now have a name? Always true for Object contexts, since
@@ -811,26 +805,20 @@
         }
         _binaryValue = null;
 
-        if (i == INT_RBRACKET) {
-            _updateLocation();
-            if (!_parsingContext.inArray()) {
-                _reportMismatchedEndMarker(i, '}');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            _currToken = JsonToken.END_ARRAY;
+        // Closing scope?
+        if (i == INT_RBRACKET || i == INT_RCURLY) {
+            _closeScope(i);
             return false;
         }
-        if (i == INT_RCURLY) {
-            _updateLocation();
-            if (!_parsingContext.inObject()) {
-                _reportMismatchedEndMarker(i, ']');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            _currToken = JsonToken.END_OBJECT;
-            return false;
-        }
+
         if (_parsingContext.expectComma()) {
             i = _skipComma(i);
+
+            // Was that a trailing comma?
+            if (isEnabled(Feature.ALLOW_TRAILING_COMMA) && (i == INT_RBRACKET || i == INT_RCURLY)) {
+                _closeScope(i);
+                return false;
+            }
         }
 
         if (!_parsingContext.inObject()) {
@@ -1479,6 +1467,10 @@
         int fractLen = 0;
         // And then see if we get other parts
         if (c == '.') { // yes, fraction
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
             outBuf[outPtr++] = c;
 
             fract_loop:
@@ -2830,4 +2822,29 @@
         }
         _reportError("Unrecognized token '"+sb.toString()+"': was expecting "+msg);
     }
+
+    /*
+    /**********************************************************
+    /* Internal methods, other
+    /**********************************************************
+     */
+
+    private void _closeScope(int i) throws JsonParseException {
+        if (i == INT_RBRACKET) {
+            _updateLocation();
+            if (!_parsingContext.inArray()) {
+                _reportMismatchedEndMarker(i, '}');
+            }
+            _parsingContext = _parsingContext.clearAndGetParent();
+            _currToken = JsonToken.END_ARRAY;
+        }
+        if (i == INT_RCURLY) {
+            _updateLocation();
+            if (!_parsingContext.inObject()) {
+                _reportMismatchedEndMarker(i, ']');
+            }
+            _parsingContext = _parsingContext.clearAndGetParent();
+            _currToken = JsonToken.END_OBJECT;
+        }
+    }
 }
diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java
index 8bc3789..de5d081 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8DataInputJsonParser.java
@@ -575,19 +575,9 @@
         _tokenInputRow = _currInputRow;
 
         // Closing scope?
-        if (i == INT_RBRACKET) {
-            if (!_parsingContext.inArray()) {
-                _reportMismatchedEndMarker(i, '}');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            return (_currToken = JsonToken.END_ARRAY);
-        }
-        if (i == INT_RCURLY) {
-            if (!_parsingContext.inObject()) {
-                _reportMismatchedEndMarker(i, ']');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            return (_currToken = JsonToken.END_OBJECT);
+        if (i == INT_RBRACKET || i == INT_RCURLY) {
+            _closeScope(i);
+            return _currToken;
         }
 
         // Nope: do we then expect a comma?
@@ -596,6 +586,12 @@
                 _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
             }
             i = _skipWS();
+
+            // Was that a trailing comma?
+            if (isEnabled(Feature.ALLOW_TRAILING_COMMA) && (i == INT_RBRACKET || i == INT_RCURLY)) {
+                _closeScope(i);
+                return _currToken;
+            }
         }
 
         /* And should we now have a name? Always true for
@@ -2788,6 +2784,23 @@
     /**********************************************************
      */
 
+    private void _closeScope(int i) throws JsonParseException {
+        if (i == INT_RBRACKET) {
+            if (!_parsingContext.inArray()) {
+                _reportMismatchedEndMarker(i, '}');
+            }
+            _parsingContext = _parsingContext.clearAndGetParent();
+            _currToken = JsonToken.END_ARRAY;
+        }
+        if (i == INT_RCURLY) {
+            if (!_parsingContext.inObject()) {
+                _reportMismatchedEndMarker(i, ']');
+            }
+            _parsingContext = _parsingContext.clearAndGetParent();
+            _currToken = JsonToken.END_OBJECT;
+        }
+    }
+
     /**
      * Helper method needed to fix [Issue#148], masking of 0x00 character
      */
diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
index 15ea15f..80559d4 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8JsonGenerator.java
@@ -938,14 +938,10 @@
         _verifyValueWrite(WRITE_NUMBER);
         if (value == null) {
             _writeNull();
-        } else if (_cfgNumbersAsStrings) {
-            String raw = Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features)
-                    ? value.toPlainString() : value.toString();
-            _writeQuotedRaw(raw);
-        } else if (Feature.WRITE_BIGDECIMAL_AS_PLAIN.enabledIn(_features)) {
-            writeRaw(value.toPlainString());
+        } else  if (_cfgNumbersAsStrings) {
+            _writeQuotedRaw(_asString(value));
         } else {
-            writeRaw(value.toString());
+            writeRaw(_asString(value));
         }
     }
 
diff --git a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java
index 83e6309..23f52bf 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/UTF8StreamJsonParser.java
@@ -738,21 +738,9 @@
         _binaryValue = null;
 
         // Closing scope?
-        if (i == INT_RBRACKET) {
-            _updateLocation();
-            if (!_parsingContext.inArray()) {
-                _reportMismatchedEndMarker(i, '}');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            return (_currToken = JsonToken.END_ARRAY);
-        }
-        if (i == INT_RCURLY) {
-            _updateLocation();
-            if (!_parsingContext.inObject()) {
-                _reportMismatchedEndMarker(i, ']');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            return (_currToken = JsonToken.END_OBJECT);
+        if (i == INT_RBRACKET || i == INT_RCURLY) {
+            _closeScope(i);
+            return _currToken;
         }
 
         // Nope: do we then expect a comma?
@@ -761,6 +749,12 @@
                 _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
             }
             i = _skipWS();
+
+            // Was that a trailing comma?
+            if (isEnabled(Feature.ALLOW_TRAILING_COMMA) && (i == INT_RBRACKET || i == INT_RCURLY)) {
+                _closeScope(i);
+                return _currToken;
+            }
         }
 
         /* And should we now have a name? Always true for
@@ -930,22 +924,8 @@
         _binaryValue = null;
 
         // Closing scope?
-        if (i == INT_RBRACKET) {
-            _updateLocation();
-            if (!_parsingContext.inArray()) {
-                _reportMismatchedEndMarker(i, '}');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            _currToken = JsonToken.END_ARRAY;
-            return false;
-        }
-        if (i == INT_RCURLY) {
-            _updateLocation();
-            if (!_parsingContext.inObject()) {
-                _reportMismatchedEndMarker(i, ']');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            _currToken = JsonToken.END_OBJECT;
+        if (i == INT_RBRACKET || i == INT_RCURLY) {
+            _closeScope(i);
             return false;
         }
 
@@ -955,6 +935,12 @@
                 _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
             }
             i = _skipWS();
+
+            // Was that a trailing comma?
+            if (isEnabled(Feature.ALLOW_TRAILING_COMMA) && (i == INT_RBRACKET || i == INT_RCURLY)) {
+                _closeScope(i);
+                return false;
+            }
         }
 
         if (!_parsingContext.inObject()) {
@@ -1017,22 +1003,8 @@
         }
         _binaryValue = null;
 
-        if (i == INT_RBRACKET) {
-            _updateLocation();
-            if (!_parsingContext.inArray()) {
-                _reportMismatchedEndMarker(i, '}');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            _currToken = JsonToken.END_ARRAY;
-            return null;
-        }
-        if (i == INT_RCURLY) {
-            _updateLocation();
-            if (!_parsingContext.inObject()) {
-                _reportMismatchedEndMarker(i, ']');
-            }
-            _parsingContext = _parsingContext.clearAndGetParent();
-            _currToken = JsonToken.END_OBJECT;
+        if (i == INT_RBRACKET || i == INT_RCURLY) {
+            _closeScope(i);
             return null;
         }
 
@@ -1042,7 +1014,14 @@
                 _reportUnexpectedChar(i, "was expecting comma to separate "+_parsingContext.typeDesc()+" entries");
             }
             i = _skipWS();
+
+            // Was that a trailing comma?
+            if (isEnabled(Feature.ALLOW_TRAILING_COMMA) && (i == INT_RBRACKET || i == INT_RCURLY)) {
+                _closeScope(i);
+                return null;
+            }
         }
+
         if (!_parsingContext.inObject()) {
             _updateLocation();
             _nextTokenNotInObject(i);
@@ -1573,6 +1552,10 @@
 
         // And then see if we get other parts
         if (c == INT_PERIOD) { // yes, fraction
+            if (outPtr >= outBuf.length) {
+                outBuf = _textBuffer.finishCurrentSegment();
+                outPtr = 0;
+            }
             outBuf[outPtr++] = (char) c;
 
             fract_loop:
@@ -1647,7 +1630,7 @@
         // Ok; unless we hit end-of-input, need to push last char read back
         if (!eof) {
             --_inputPtr;
-            // As per #105, need separating space between root values; check here
+            // As per [core#105], need separating space between root values; check here
             if (_parsingContext.inRoot()) {
                 _verifyRootSpace(c);
             }
@@ -3729,6 +3712,25 @@
     /**********************************************************
      */
 
+    private void _closeScope(int i) throws JsonParseException {
+        if (i == INT_RBRACKET) {
+            _updateLocation();
+            if (!_parsingContext.inArray()) {
+                _reportMismatchedEndMarker(i, '}');
+            }
+            _parsingContext = _parsingContext.clearAndGetParent();
+            _currToken = JsonToken.END_ARRAY;
+        }
+        if (i == INT_RCURLY) {
+            _updateLocation();
+            if (!_parsingContext.inObject()) {
+                _reportMismatchedEndMarker(i, ']');
+            }
+            _parsingContext = _parsingContext.clearAndGetParent();
+            _currToken = JsonToken.END_OBJECT;
+        }
+    }
+
     /**
      * Helper method needed to fix [Issue#148], masking of 0x00 character
      */
diff --git a/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
index 74b47c5..180497a 100644
--- a/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
+++ b/src/main/java/com/fasterxml/jackson/core/json/WriterBasedJsonGenerator.java
@@ -719,13 +719,10 @@
         _verifyValueWrite(WRITE_NUMBER);
         if (value == null) {
             _writeNull();
-        } else if (_cfgNumbersAsStrings) {
-            String raw = isEnabled(Feature.WRITE_BIGDECIMAL_AS_PLAIN) ? value.toPlainString() : value.toString();
-            _writeQuotedRaw(raw);
-        } else if (isEnabled(Feature.WRITE_BIGDECIMAL_AS_PLAIN)) {
-            writeRaw(value.toPlainString());
+        } else  if (_cfgNumbersAsStrings) {
+            _writeQuotedRaw(_asString(value));
         } else {
-            writeRaw(value.toString());
+            writeRaw(_asString(value));
         }
     }
 
@@ -734,7 +731,7 @@
     {
         _verifyValueWrite(WRITE_NUMBER);
         if (_cfgNumbersAsStrings) {
-            _writeQuotedRaw(encodedValue);            
+            _writeQuotedRaw(encodedValue);
         } else {
             writeRaw(encodedValue);
         }
diff --git a/src/test/java/com/fasterxml/jackson/core/base64/Base64BinaryParsingTest.java b/src/test/java/com/fasterxml/jackson/core/base64/Base64BinaryParsingTest.java
new file mode 100644
index 0000000..8693bb9
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/base64/Base64BinaryParsingTest.java
@@ -0,0 +1,271 @@
+package com.fasterxml.jackson.core.base64;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+
+public class Base64BinaryParsingTest
+    extends com.fasterxml.jackson.core.BaseTest
+{
+    public void testBase64UsingInputStream() throws Exception
+    {
+        _testBase64Text(MODE_INPUT_STREAM);
+        _testBase64Text(MODE_INPUT_STREAM_THROTTLED);
+        _testBase64Text(MODE_DATA_INPUT);
+    }
+
+    public void testBase64UsingReader() throws Exception
+    {
+        _testBase64Text(MODE_READER);
+    }
+
+    public void testStreaming() throws IOException
+    {
+        _testStreaming(MODE_INPUT_STREAM);
+        _testStreaming(MODE_INPUT_STREAM_THROTTLED);
+        _testStreaming(MODE_DATA_INPUT);
+        _testStreaming(MODE_READER);
+    }
+
+    public void testSimple() throws IOException
+    {
+        for (int mode : ALL_MODES) {
+            _testSimple(mode);
+        }
+    }
+
+    public void testInArray() throws IOException
+    {
+        for (int mode : ALL_MODES) {
+            _testInArray(mode);
+        }
+    }
+
+    public void testWithEscaped() throws IOException {
+        for (int mode : ALL_MODES) {
+            _testEscaped(mode);
+        }
+    }
+
+    /*
+    /**********************************************************
+    /* Test helper methods
+    /**********************************************************
+     */
+
+    @SuppressWarnings("resource")
+    public void _testBase64Text(int mode) throws Exception
+    {
+        // let's actually iterate over sets of encoding modes, lengths
+        
+        final int[] LENS = { 1, 2, 3, 4, 7, 9, 32, 33, 34, 35 };
+        final Base64Variant[] VARIANTS = {
+                Base64Variants.MIME,
+                Base64Variants.MIME_NO_LINEFEEDS,
+                Base64Variants.MODIFIED_FOR_URL,
+                Base64Variants.PEM
+        };
+
+        JsonFactory jsonFactory = new JsonFactory();
+        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        StringWriter chars = null;
+        for (int len : LENS) {
+            byte[] input = new byte[len];
+            for (int i = 0; i < input.length; ++i) {
+                input[i] = (byte) i;
+            }
+            for (Base64Variant variant : VARIANTS) {
+                JsonGenerator g;
+
+                if (mode == MODE_READER) {
+                    chars = new StringWriter();
+                    g = jsonFactory.createGenerator(chars);
+                } else {
+                    bytes.reset();
+                    g = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8);
+                }
+                g.writeBinary(variant, input, 0, input.length);
+                g.close();
+                JsonParser p;
+                if (mode == MODE_READER) {
+                    p = jsonFactory.createParser(chars.toString());
+                } else {
+                    p = createParser(jsonFactory, mode, bytes.toByteArray());
+                }
+                assertToken(JsonToken.VALUE_STRING, p.nextToken());
+                byte[] data = null;
+                try {
+                    data = p.getBinaryValue(variant);
+                } catch (Exception e) {
+                    IOException ioException = new IOException("Failed (variant "+variant+", data length "+len+"): "+e.getMessage());
+                    ioException.initCause(e);
+                    throw ioException;
+                }
+                assertNotNull(data);
+                assertArrayEquals(data, input);
+                if (mode != MODE_DATA_INPUT) { // no look-ahead for DataInput
+                    assertNull(p.nextToken());
+                }
+                p.close();
+            }
+        }
+    }
+
+    private byte[] _generateData(int size)
+    {
+        byte[] result = new byte[size];
+        for (int i = 0; i < size; ++i) {
+            result[i] = (byte) (i % 255);
+        }
+        return result;
+    }
+
+    private void _testStreaming(int mode) throws IOException
+    {
+        final int[] SIZES = new int[] {
+            1, 2, 3, 4, 5, 6,
+            7, 8, 12,
+            100, 350, 1900, 6000, 19000, 65000,
+            139000
+        };
+
+        JsonFactory jsonFactory = new JsonFactory();
+        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        StringWriter chars = null;
+
+        for (int size : SIZES) {
+            byte[] data = _generateData(size);
+            JsonGenerator g;
+            if (mode == MODE_READER) {
+                chars = new StringWriter();
+                g = jsonFactory.createGenerator(chars);
+            } else {
+                bytes.reset();
+                g = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8);
+            }
+
+            g.writeStartObject();
+            g.writeFieldName("b");
+            g.writeBinary(data);
+            g.writeEndObject();
+            g.close();
+
+            // and verify
+            JsonParser p;
+            if (mode == MODE_READER) {
+                p = jsonFactory.createParser(chars.toString());
+            } else {
+                p = createParser(jsonFactory, mode, bytes.toByteArray());
+            }
+            assertToken(JsonToken.START_OBJECT, p.nextToken());
+    
+            assertToken(JsonToken.FIELD_NAME, p.nextToken());
+            assertEquals("b", p.getCurrentName());
+            assertToken(JsonToken.VALUE_STRING, p.nextToken());
+            ByteArrayOutputStream result = new ByteArrayOutputStream(size);
+            int gotten = p.readBinaryValue(result);
+            assertEquals(size, gotten);
+            assertArrayEquals(data, result.toByteArray());
+            assertToken(JsonToken.END_OBJECT, p.nextToken());
+            if (mode != MODE_DATA_INPUT) { // no look-ahead for DataInput
+                assertNull(p.nextToken());
+            }
+            p.close();
+        }
+    }
+
+    private void _testSimple(int mode)
+        throws IOException
+    {
+        /* The usual sample input string, from Thomas Hobbes's "Leviathan"
+         * (via Wikipedia)
+         */
+        final String RESULT = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
+        final byte[] RESULT_BYTES = RESULT.getBytes("US-ASCII");
+
+        // And here's what should produce it...
+        final String INPUT_STR = 
+ "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
++"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
++"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
++"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
++"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="
+            ;
+
+        final String DOC = "\""+INPUT_STR+"\"";
+        JsonParser p = createParser(mode, DOC);
+
+        assertToken(JsonToken.VALUE_STRING, p.nextToken());
+        byte[] data = p.getBinaryValue();
+        assertNotNull(data);
+        assertArrayEquals(RESULT_BYTES, data);
+        p.close();
+    }
+
+    private void _testInArray(int mode) throws IOException
+    {
+        JsonFactory f = new JsonFactory();
+
+        final int entryCount = 7;
+
+        StringWriter sw = new StringWriter();
+        JsonGenerator jg = f.createGenerator(sw);
+        jg.writeStartArray();
+
+        byte[][] entries = new byte[entryCount][];
+        for (int i = 0; i < entryCount; ++i) {
+            byte[] b = new byte[200 + i * 100];
+            for (int x = 0; x < b.length; ++x) {
+                b[x] = (byte) (i + x);
+            }
+            entries[i] = b;
+            jg.writeBinary(b);
+        }
+
+        jg.writeEndArray();
+        jg.close();
+
+        JsonParser p = createParser(f, mode, sw.toString());
+
+        assertToken(JsonToken.START_ARRAY, p.nextToken());
+
+        for (int i = 0; i < entryCount; ++i) {
+            assertToken(JsonToken.VALUE_STRING, p.nextToken());
+            byte[] b = p.getBinaryValue();
+            assertArrayEquals(entries[i], b);
+        }
+        assertToken(JsonToken.END_ARRAY, p.nextToken());
+        p.close();
+    }
+
+    private void _testEscaped(int mode) throws IOException
+    {
+        // Input: "Test!" -> "VGVzdCE="
+
+        // First, try with embedded linefeed half-way through:
+
+        String DOC = quote("VGVz\\ndCE="); // note: must double-quote to get linefeed
+        JsonParser p = createParser(mode, DOC);
+        assertToken(JsonToken.VALUE_STRING, p.nextToken());
+        byte[] b = p.getBinaryValue();
+        assertEquals("Test!", new String(b, "US-ASCII"));
+        if (mode != MODE_DATA_INPUT) {
+            assertNull(p.nextToken());
+        }
+        p.close();
+
+        // and then with escaped chars
+//            DOC = quote("V\\u0047V\\u007AdCE="); // note: must escape backslash...
+        DOC = quote("V\\u0047V\\u007AdCE="); // note: must escape backslash...
+        p = createParser(mode, DOC);
+        assertToken(JsonToken.VALUE_STRING, p.nextToken());
+        b = p.getBinaryValue();
+        assertEquals("Test!", new String(b, "US-ASCII"));
+        if (mode != MODE_DATA_INPUT) {
+            assertNull(p.nextToken());
+        }
+        p.close();
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/base64/Base64GenerationTest.java b/src/test/java/com/fasterxml/jackson/core/base64/Base64GenerationTest.java
new file mode 100644
index 0000000..f3b064f
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/base64/Base64GenerationTest.java
@@ -0,0 +1,207 @@
+package com.fasterxml.jackson.core.base64;
+
+import java.io.*;
+
+import com.fasterxml.jackson.core.*;
+import com.fasterxml.jackson.core.testsupport.ThrottledInputStream;
+
+public class Base64GenerationTest
+    extends com.fasterxml.jackson.core.BaseTest
+{
+    /* The usual sample input string, from Thomas Hobbes's "Leviathan"
+     * (via Wikipedia)
+     */
+    private final static String WIKIPEDIA_BASE64_TEXT = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
+    private final static byte[] WIKIPEDIA_BASE64_AS_BYTES;
+    static {
+        try {
+            WIKIPEDIA_BASE64_AS_BYTES = WIKIPEDIA_BASE64_TEXT.getBytes("US-ASCII");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private final String WIKIPEDIA_BASE64_ENCODED =
+"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
++"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
++"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
++"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
++"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="
+        ;
+
+    
+    private final static Base64Variant[] VARIANTS = {
+            Base64Variants.MIME,
+            Base64Variants.MIME_NO_LINEFEEDS,
+            Base64Variants.MODIFIED_FOR_URL,
+            Base64Variants.PEM
+    };
+
+    /*
+    /**********************************************************
+    /* Test methods
+    /**********************************************************
+     */
+
+    private final JsonFactory JSON_F = new JsonFactory();
+    
+    public void testStreamingBinaryWrites() throws Exception
+    {
+        _testStreamingWrites(JSON_F, true);
+        _testStreamingWrites(JSON_F, false);
+    }
+
+    // For [core#55]
+    public void testIssue55() throws Exception
+    {
+        final JsonFactory f = new JsonFactory();
+
+        // First,  byte-backed:
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+
+        JsonGenerator gen = f.createGenerator(bytes);
+        ByteArrayInputStream data = new ByteArrayInputStream(new byte[2000]);
+        gen.writeBinary(data, 1999);       
+        gen.close();
+
+        final int EXP_LEN = 2670;
+        
+        assertEquals(EXP_LEN, bytes.size());
+
+        // Then char-backed
+        StringWriter sw = new StringWriter();
+        
+        gen = f.createGenerator(sw);
+        data = new ByteArrayInputStream(new byte[2000]);
+        gen.writeBinary(data, 1999);       
+        gen.close();
+        
+        assertEquals(EXP_LEN, sw.toString().length());
+    }
+
+    /**
+     * This is really inadequate test, all in all, but should serve
+     * as some kind of sanity check. Reader-side should more thoroughly
+     * test things, as it does need writers to construct the data first.
+     */
+    public void testSimpleBinaryWrite() throws Exception
+    {
+        _testSimpleBinaryWrite(false);
+        _testSimpleBinaryWrite(true);
+    }
+
+    // for [core#318]
+    public void testBinaryAsEmbeddedObject() throws Exception
+    {
+        JsonGenerator g;
+
+        StringWriter sw = new StringWriter();
+        g = JSON_F.createGenerator(sw);
+        g.writeEmbeddedObject(WIKIPEDIA_BASE64_AS_BYTES);
+        g.close();
+        assertEquals(quote(WIKIPEDIA_BASE64_ENCODED), sw.toString());
+
+        ByteArrayOutputStream bytes =  new ByteArrayOutputStream(100);
+        g = JSON_F.createGenerator(bytes);
+        g.writeEmbeddedObject(WIKIPEDIA_BASE64_AS_BYTES);
+        g.close();
+        assertEquals(quote(WIKIPEDIA_BASE64_ENCODED), bytes.toString("UTF-8"));
+    }
+
+    /*
+    /**********************************************************
+    /* Helper methods
+    /**********************************************************
+     */
+    
+    private void _testSimpleBinaryWrite(boolean useCharBased) throws Exception
+    {
+        /* Let's only test the standard base64 variant; but write
+         * values in root, array and object contexts.
+         */
+        Base64Variant b64v = Base64Variants.getDefaultVariant();
+        JsonFactory jf = new JsonFactory();
+
+        for (int i = 0; i < 3; ++i) {
+            JsonGenerator gen;
+            ByteArrayOutputStream bout = new ByteArrayOutputStream(200);
+            if (useCharBased) {
+                gen = jf.createGenerator(new OutputStreamWriter(bout, "UTF-8"));
+            } else {
+                gen = jf.createGenerator(bout, JsonEncoding.UTF8);
+            }
+
+            switch (i) {
+            case 0: // root
+                gen.writeBinary(b64v, WIKIPEDIA_BASE64_AS_BYTES, 0, WIKIPEDIA_BASE64_AS_BYTES.length);
+                break;
+            case 1: // array
+                gen.writeStartArray();
+                gen.writeBinary(b64v, WIKIPEDIA_BASE64_AS_BYTES, 0, WIKIPEDIA_BASE64_AS_BYTES.length);
+                gen.writeEndArray();
+                break;
+            default: // object
+                gen.writeStartObject();
+                gen.writeFieldName("field");
+                gen.writeBinary(b64v, WIKIPEDIA_BASE64_AS_BYTES, 0, WIKIPEDIA_BASE64_AS_BYTES.length);
+                gen.writeEndObject();
+                break;
+            }
+            gen.close();
+
+            JsonParser jp = jf.createParser(new ByteArrayInputStream(bout.toByteArray()));
+            
+            // Need to skip other events before binary data:
+            switch (i) {
+            case 0:
+                break;
+            case 1:
+                assertEquals(JsonToken.START_ARRAY, jp.nextToken());
+                break;
+            default:
+                assertEquals(JsonToken.START_OBJECT, jp.nextToken());
+                assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
+                break;
+            }
+            assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
+            String actualValue = jp.getText();
+            jp.close();
+            assertEquals(WIKIPEDIA_BASE64_ENCODED, actualValue);
+        }
+    }
+
+    private final static String TEXT = "Some content so that we can test encoding of base64 data; must"
+            +" be long enough include a line wrap or two...";
+    private final static String TEXT4 = TEXT + TEXT + TEXT + TEXT;
+
+    @SuppressWarnings("resource")
+    private void _testStreamingWrites(JsonFactory jf, boolean useBytes) throws Exception
+    {
+        final byte[] INPUT = TEXT4.getBytes("UTF-8");
+        for (Base64Variant variant : VARIANTS) {
+            final String EXP_OUTPUT = "[" + quote(variant.encode(INPUT))+"]";
+            for (boolean passLength : new boolean[] { true, false }) {
+                for (int chunkSize : new int[] { 1, 2, 3, 4, 7, 11, 29, 5000 }) {
+//System.err.println(""+variant+", length "+passLength+", chunk "+chunkSize);
+                    
+                    JsonGenerator jgen;
+                    
+                    final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+                    if (useBytes) {
+                        jgen = jf.createGenerator(bytes);
+                    } else {
+                        jgen = jf.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
+                    }
+                    jgen.writeStartArray();
+                    int length = passLength ? INPUT.length : -1;
+                    InputStream data = new ThrottledInputStream(INPUT, chunkSize);
+                    jgen.writeBinary(variant, data, length);
+                    jgen.writeEndArray();
+                    jgen.close();
+                    String JSON = bytes.toString("UTF-8");
+                    assertEquals(EXP_OUTPUT, JSON);
+                }
+            }
+        }
+    }
+}
diff --git a/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Generation.java b/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Generation.java
deleted file mode 100644
index ef3c673..0000000
--- a/src/test/java/com/fasterxml/jackson/core/base64/TestBase64Generation.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.fasterxml.jackson.core.base64;
-
-import java.io.*;
-
-import com.fasterxml.jackson.core.*;
-import com.fasterxml.jackson.core.testsupport.ThrottledInputStream;
-
-public class TestBase64Generation
-    extends com.fasterxml.jackson.core.BaseTest
-{
-    
-
-    /*
-    /**********************************************************
-    /* Test methods
-    /**********************************************************
-     */
-
-    public void testStreamingWrites() throws Exception
-    {
-        final JsonFactory f = new JsonFactory();
-        _testStreamingWrites(f, true);
-        _testStreamingWrites(f, false);
-    }
-
-    // For [#55]
-    public void testIssue55() throws Exception
-    {
-        final JsonFactory f = new JsonFactory();
-
-        // First,  byte-backed:
-        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-
-        JsonGenerator gen = f.createGenerator(bytes);
-        ByteArrayInputStream data = new ByteArrayInputStream(new byte[2000]);
-        gen.writeBinary(data, 1999);       
-        gen.close();
-
-        final int EXP_LEN = 2670;
-        
-        assertEquals(EXP_LEN, bytes.size());
-
-        // Then char-backed
-        StringWriter sw = new StringWriter();
-        
-        gen = f.createGenerator(sw);
-        data = new ByteArrayInputStream(new byte[2000]);
-        gen.writeBinary(data, 1999);       
-        gen.close();
-        
-        assertEquals(EXP_LEN, sw.toString().length());
-    }
-    
-    /*
-    /**********************************************************
-    /* Helper methods
-    /**********************************************************
-     */
-
-    private final static Base64Variant[] VARIANTS = {
-            Base64Variants.MIME,
-            Base64Variants.MIME_NO_LINEFEEDS,
-            Base64Variants.MODIFIED_FOR_URL,
-            Base64Variants.PEM
-    };
-
-    private final static String TEXT = "Some content so that we can test encoding of base64 data; must"
-            +" be long enough include a line wrap or two...";
-    private final static String TEXT4 = TEXT + TEXT + TEXT + TEXT;
-
-    @SuppressWarnings("resource")
-    private void _testStreamingWrites(JsonFactory jf, boolean useBytes) throws Exception
-    {
-        final byte[] INPUT = TEXT4.getBytes("UTF-8");
-        for (Base64Variant variant : VARIANTS) {
-            final String EXP_OUTPUT = "[" + quote(variant.encode(INPUT))+"]";
-            for (boolean passLength : new boolean[] { true, false }) {
-                for (int chunkSize : new int[] { 1, 2, 3, 4, 7, 11, 29, 5000 }) {
-//System.err.println(""+variant+", length "+passLength+", chunk "+chunkSize);
-                    
-                    JsonGenerator jgen;
-                    
-                    final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-                    if (useBytes) {
-                        jgen = jf.createGenerator(bytes);
-                    } else {
-                        jgen = jf.createGenerator(new OutputStreamWriter(bytes, "UTF-8"));
-                    }
-                    jgen.writeStartArray();
-                    int length = passLength ? INPUT.length : -1;
-                    InputStream data = new ThrottledInputStream(INPUT, chunkSize);
-                    jgen.writeBinary(variant, data, length);
-                    jgen.writeEndArray();
-                    jgen.close();
-                    String JSON = bytes.toString("UTF-8");
-                    assertEquals(EXP_OUTPUT, JSON);
-                }
-            }
-        }
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java b/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java
index a04e5e6..20bdff4 100644
--- a/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/json/GeneratorFeaturesTest.java
@@ -131,7 +131,62 @@
         g.close();
         assertEquals(quote("100"), bos.toString("UTF-8"));
     }
-    
+
+    // [core#315]
+    public void testTooBigBigDecimal() throws Exception
+    {
+        JsonFactory f = new JsonFactory();
+        f.enable(JsonGenerator.Feature.WRITE_BIGDECIMAL_AS_PLAIN);
+
+        // 24-Aug-2016, tatu: Initial check limits scale to [-9999,+9999]
+        BigDecimal BIG = new BigDecimal("1E+9999");
+        BigDecimal TOO_BIG = new BigDecimal("1E+10000");
+        BigDecimal SMALL = new BigDecimal("1E-9999");
+        BigDecimal TOO_SMALL = new BigDecimal("1E-10000");
+
+        for (boolean useBytes : new boolean[] { false, true } ) {
+            for (boolean asString : new boolean[] { false, true } ) {
+                JsonGenerator g;
+                
+                if (useBytes) {
+                    g = f.createGenerator(new ByteArrayOutputStream());
+                } else {
+                    g = f.createGenerator(new StringWriter());
+                }
+                if (asString) {
+                    g.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
+                }
+
+                // first, ok cases:
+                g.writeStartArray();
+                g.writeNumber(BIG);
+                g.writeNumber(SMALL);
+                g.writeEndArray();
+                g.close();
+
+                // then invalid
+                for (BigDecimal input : new BigDecimal[] { TOO_BIG, TOO_SMALL }) {
+                    if (useBytes) {
+                        g = f.createGenerator(new ByteArrayOutputStream());
+                    } else {
+                        g = f.createGenerator(new StringWriter());
+                    }
+                    if (asString) {
+                        g.enable(JsonGenerator.Feature.WRITE_NUMBERS_AS_STRINGS);
+                    }
+                    try {
+                        g.writeNumber(input);
+                        fail("Should not have written without exception: "+input);
+                    } catch (JsonGenerationException e) {
+                        verifyException(e, "Attempt to write plain `java.math.BigDecimal`");
+                        verifyException(e, "illegal scale");
+                    }
+                    g.close();
+                }
+            }
+        }
+    }
+
     private String _writeNumbers(JsonFactory f) throws IOException
     {
         StringWriter sw = new StringWriter();
diff --git a/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorMisc.java b/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorMisc.java
index ded2380..8447524 100644
--- a/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorMisc.java
+++ b/src/test/java/com/fasterxml/jackson/core/main/TestGeneratorMisc.java
@@ -15,6 +15,8 @@
 public class TestGeneratorMisc
     extends com.fasterxml.jackson.core.BaseTest
 {
+    private final JsonFactory JSON_F = new JsonFactory();
+
     /*
     /**********************************************************
     /* Tests for closing, status
@@ -23,12 +25,11 @@
 
     public void testIsClosed() throws IOException
     {
-        JsonFactory jf = new JsonFactory();
         for (int i = 0; i < 2; ++i) {
             boolean stream = ((i & 1) == 0);
             JsonGenerator jg = stream ?
-                jf.createGenerator(new StringWriter())
-                : jf.createGenerator(new ByteArrayOutputStream(), JsonEncoding.UTF8)
+                    JSON_F.createGenerator(new StringWriter())
+                : JSON_F.createGenerator(new ByteArrayOutputStream(), JsonEncoding.UTF8)
                 ;
             assertFalse(jg.isClosed());
             jg.writeStartArray();
@@ -46,9 +47,8 @@
     public void testSimpleWriteObject() throws IOException
     {
         // note: NOT mapping factory, for this test
-        JsonFactory jf = new JsonFactory();
         StringWriter sw = new StringWriter();
-        JsonGenerator gen = jf.createGenerator(sw);
+        JsonGenerator gen = JSON_F.createGenerator(sw);
         gen.writeStartArray();
 
         // simple wrappers first
@@ -65,7 +65,7 @@
         
         // then other basic types
         sw = new StringWriter();
-        gen = jf.createGenerator(sw);
+        gen = JSON_F.createGenerator(sw);
         gen.writeStartArray();
         gen.writeObject(BigInteger.valueOf(1234));
         gen.writeObject(new BigDecimal(0.5));
@@ -76,7 +76,7 @@
 
         // then Atomic types
         sw = new StringWriter();
-        gen = jf.createGenerator(sw);
+        gen = JSON_F.createGenerator(sw);
         gen.writeStartArray();
         gen.writeObject(new AtomicBoolean(false));
         gen.writeObject(new AtomicInteger(13));
@@ -95,9 +95,8 @@
 
     public void testRaw() throws IOException
     {
-        JsonFactory jf = new JsonFactory();
         StringWriter sw = new StringWriter();
-        JsonGenerator gen = jf.createGenerator(sw);
+        JsonGenerator gen = JSON_F.createGenerator(sw);
         gen.writeStartArray();
         gen.writeRaw("-123, true");
         gen.writeRaw(", \"x\"  ");
@@ -118,9 +117,8 @@
 
     public void testRawValue() throws IOException
     {
-        JsonFactory jf = new JsonFactory();
         StringWriter sw = new StringWriter();
-        JsonGenerator gen = jf.createGenerator(sw);
+        JsonGenerator gen = JSON_F.createGenerator(sw);
         gen.writeStartArray();
         gen.writeRawValue("7");
         gen.writeRawValue("[ null ]");
@@ -141,93 +139,6 @@
         assertToken(JsonToken.END_ARRAY, jp.nextToken());
         jp.close();
     }
-    
-    /*
-    /**********************************************************
-    /* Tests for binary data
-    /**********************************************************
-     */
-
-    /**
-     * This is really inadequate test, all in all, but should serve
-     * as some kind of sanity check. Reader-side should more thoroughly
-     * test things, as it does need writers to construct the data first.
-     */
-    public void testBinaryWrite() throws Exception
-    {
-        _testBinaryWrite(false);
-        _testBinaryWrite(true);
-    }
-
-    private void _testBinaryWrite(boolean useCharBased) throws Exception
-    {
-        /* The usual sample input string, from Thomas Hobbes's "Leviathan"
-         * (via Wikipedia)
-         */
-        final String INPUT = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
-        final byte[] INPUT_BYTES = INPUT.getBytes("US-ASCII");
-        // as per MIME variant, result minus lfs =
-        final String OUTPUT =
- "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
-+"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
-+"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
-+"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
-+"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="
-            ;
-
-        /* Let's only test the standard base64 variant; but write
-         * values in root, array and object contexts.
-         */
-        Base64Variant b64v = Base64Variants.getDefaultVariant();
-        JsonFactory jf = new JsonFactory();
-
-        for (int i = 0; i < 3; ++i) {
-            JsonGenerator gen;
-            ByteArrayOutputStream bout = new ByteArrayOutputStream(200);
-            if (useCharBased) {
-                gen = jf.createGenerator(new OutputStreamWriter(bout, "UTF-8"));
-            } else {
-                gen = jf.createGenerator(bout, JsonEncoding.UTF8);
-            }
-
-            switch (i) {
-            case 0: // root
-                gen.writeBinary(b64v, INPUT_BYTES, 0, INPUT_BYTES.length);
-                break;
-            case 1: // array
-                gen.writeStartArray();
-                gen.writeBinary(b64v, INPUT_BYTES, 0, INPUT_BYTES.length);
-                gen.writeEndArray();
-                break;
-            default: // object
-                gen.writeStartObject();
-                gen.writeFieldName("field");
-                gen.writeBinary(b64v, INPUT_BYTES, 0, INPUT_BYTES.length);
-                gen.writeEndObject();
-                break;
-            }
-            gen.close();
-
-            JsonParser jp = jf.createParser(new ByteArrayInputStream(bout.toByteArray()));
-            
-            // Need to skip other events before binary data:
-            switch (i) {
-            case 0:
-                break;
-            case 1:
-                assertEquals(JsonToken.START_ARRAY, jp.nextToken());
-                break;
-            default:
-                assertEquals(JsonToken.START_OBJECT, jp.nextToken());
-                assertEquals(JsonToken.FIELD_NAME, jp.nextToken());
-                break;
-            }
-            assertEquals(JsonToken.VALUE_STRING, jp.nextToken());
-            String actualValue = jp.getText();
-            jp.close();
-            assertEquals(OUTPUT, actualValue);
-        }
-    }
 
     /*
     /**********************************************************
@@ -240,10 +151,9 @@
      */
     public void testLongerObjects() throws Exception
     {
-        final JsonFactory jf = new JsonFactory();
-        _testLongerObjects(jf, 0);
-        _testLongerObjects(jf, 1);
-        _testLongerObjects(jf, 2);
+        _testLongerObjects(JSON_F, 0);
+        _testLongerObjects(JSON_F, 1);
+        _testLongerObjects(JSON_F, 2);
     }
 
     public void _testLongerObjects(JsonFactory jf, int mode) throws Exception
@@ -316,4 +226,40 @@
         assertToken(JsonToken.END_OBJECT, jp.nextToken());
         jp.close();
     }
+
+    /*
+    /**********************************************************
+    /* Tests, other
+    /**********************************************************
+     */
+
+    // NOTE: test for binary data under `base64/` tests
+    public void testAsEmbedded() throws Exception
+    {
+        JsonGenerator g;
+
+        StringWriter sw = new StringWriter();
+        g = JSON_F.createGenerator(sw);
+        g.writeEmbeddedObject(null);
+        g.close();
+        assertEquals("null", sw.toString());
+
+        ByteArrayOutputStream bytes =  new ByteArrayOutputStream(100);
+        g = JSON_F.createGenerator(bytes);
+        g.writeEmbeddedObject(null);
+        g.close();
+        assertEquals("null", bytes.toString("UTF-8"));
+
+        // also, for fun, try illegal unknown thingy
+
+        try {
+            g = JSON_F.createGenerator(bytes);
+            // try writing a Class object
+            g.writeEmbeddedObject(getClass());
+            fail("Expected an exception");
+            g.close(); // never gets here
+        } catch (JsonGenerationException e) {
+            verifyException(e, "No native support for");
+        }
+    }
 }
diff --git a/src/test/java/com/fasterxml/jackson/core/read/Base64BinaryParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/Base64BinaryParsingTest.java
deleted file mode 100644
index fbd7126..0000000
--- a/src/test/java/com/fasterxml/jackson/core/read/Base64BinaryParsingTest.java
+++ /dev/null
@@ -1,158 +0,0 @@
-package com.fasterxml.jackson.core.read;
-
-import static org.junit.Assert.assertArrayEquals;
-
-import java.io.*;
-
-import com.fasterxml.jackson.core.*;
-
-public class Base64BinaryParsingTest
-    extends com.fasterxml.jackson.core.BaseTest
-{
-    public void testBase64UsingInputStream() throws Exception
-    {
-        _testBase64Text(MODE_INPUT_STREAM);
-        _testBase64Text(MODE_INPUT_STREAM_THROTTLED);
-        _testBase64Text(MODE_DATA_INPUT);
-    }
-
-    public void testBase64UsingReader() throws Exception
-    {
-        _testBase64Text(MODE_READER);
-    }
-
-    public void testStreaming() throws IOException
-    {
-        _testStreaming(MODE_INPUT_STREAM);
-        _testStreaming(MODE_INPUT_STREAM_THROTTLED);
-        _testStreaming(MODE_DATA_INPUT);
-        _testStreaming(MODE_READER);
-    }
-
-    /*
-    /**********************************************************
-    /* Test helper methods
-    /**********************************************************
-     */
-
-    @SuppressWarnings("resource")
-    public void _testBase64Text(int mode) throws Exception
-    {
-        // let's actually iterate over sets of encoding modes, lengths
-        
-        final int[] LENS = { 1, 2, 3, 4, 7, 9, 32, 33, 34, 35 };
-        final Base64Variant[] VARIANTS = {
-                Base64Variants.MIME,
-                Base64Variants.MIME_NO_LINEFEEDS,
-                Base64Variants.MODIFIED_FOR_URL,
-                Base64Variants.PEM
-        };
-
-        JsonFactory jsonFactory = new JsonFactory();
-        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-        StringWriter chars = null;
-        for (int len : LENS) {
-            byte[] input = new byte[len];
-            for (int i = 0; i < input.length; ++i) {
-                input[i] = (byte) i;
-            }
-            for (Base64Variant variant : VARIANTS) {
-                JsonGenerator g;
-
-                if (mode == MODE_READER) {
-                    chars = new StringWriter();
-                    g = jsonFactory.createGenerator(chars);
-                } else {
-                    bytes.reset();
-                    g = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8);
-                }
-                g.writeBinary(variant, input, 0, input.length);
-                g.close();
-                JsonParser p;
-                if (mode == MODE_READER) {
-                    p = jsonFactory.createParser(chars.toString());
-                } else {
-                    p = createParser(jsonFactory, mode, bytes.toByteArray());
-                }
-                assertToken(JsonToken.VALUE_STRING, p.nextToken());
-                byte[] data = null;
-                try {
-                    data = p.getBinaryValue(variant);
-                } catch (Exception e) {
-                    IOException ioException = new IOException("Failed (variant "+variant+", data length "+len+"): "+e.getMessage());
-                    ioException.initCause(e);
-                    throw ioException;
-                }
-                assertNotNull(data);
-                assertArrayEquals(data, input);
-                if (mode != MODE_DATA_INPUT) { // no look-ahead for DataInput
-                    assertNull(p.nextToken());
-                }
-                p.close();
-            }
-        }
-    }
-
-    private byte[] _generateData(int size)
-    {
-        byte[] result = new byte[size];
-        for (int i = 0; i < size; ++i) {
-            result[i] = (byte) (i % 255);
-        }
-        return result;
-    }
-
-    private void _testStreaming(int mode) throws IOException
-    {
-        final int[] SIZES = new int[] {
-            1, 2, 3, 4, 5, 6,
-            7, 8, 12,
-            100, 350, 1900, 6000, 19000, 65000,
-            139000
-        };
-
-        JsonFactory jsonFactory = new JsonFactory();
-        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
-        StringWriter chars = null;
-
-        for (int size : SIZES) {
-            byte[] data = _generateData(size);
-            JsonGenerator g;
-            if (mode == MODE_READER) {
-                chars = new StringWriter();
-                g = jsonFactory.createGenerator(chars);
-            } else {
-                bytes.reset();
-                g = jsonFactory.createGenerator(bytes, JsonEncoding.UTF8);
-            }
-
-            g.writeStartObject();
-            g.writeFieldName("b");
-            g.writeBinary(data);
-            g.writeEndObject();
-            g.close();
-
-            // and verify
-            JsonParser p;
-            if (mode == MODE_READER) {
-                p = jsonFactory.createParser(chars.toString());
-            } else {
-                p = createParser(jsonFactory, mode, bytes.toByteArray());
-            }
-            assertToken(JsonToken.START_OBJECT, p.nextToken());
-    
-            assertToken(JsonToken.FIELD_NAME, p.nextToken());
-            assertEquals("b", p.getCurrentName());
-            assertToken(JsonToken.VALUE_STRING, p.nextToken());
-            ByteArrayOutputStream result = new ByteArrayOutputStream(size);
-            int gotten = p.readBinaryValue(result);
-            assertEquals(size, gotten);
-            assertArrayEquals(data, result.toByteArray());
-            assertToken(JsonToken.END_OBJECT, p.nextToken());
-            if (mode != MODE_DATA_INPUT) { // no look-ahead for DataInput
-                assertNull(p.nextToken());
-            }
-            p.close();
-        }
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java
index d91f5e9..6c8da43 100644
--- a/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java
+++ b/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java
@@ -1,5 +1,8 @@
 package com.fasterxml.jackson.core.read;
 
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringReader;
 import java.math.BigDecimal;
 import java.math.BigInteger;
 
@@ -561,7 +564,36 @@
         }
         p.close();
     }
-    
+
+    // [core#317]
+    public void testLongerFloatingPoint() throws Exception
+    {
+        StringBuilder input = new StringBuilder();
+        for (int i = 1; i < 201; i++) {
+            input.append(1);
+        }
+        input.append(".0");
+        final String DOC = input.toString();
+
+        // test out with both Reader and ByteArrayInputStream
+        JsonParser p;
+
+        p = FACTORY.createParser(new StringReader(DOC));
+        _testLongerFloat(p, DOC);
+        p.close();
+        
+        p = FACTORY.createParser(new ByteArrayInputStream(DOC.getBytes("UTF-8")));
+        _testLongerFloat(p, DOC);
+        p.close();
+    }
+
+    private void _testLongerFloat(JsonParser p, String text) throws IOException
+    {
+        assertToken(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken());
+        assertEquals(text, p.getText());
+        assertNull(p.nextToken());
+    }
+
     /*
     /**********************************************************
     /* Helper methods
diff --git a/src/test/java/com/fasterxml/jackson/core/read/TestJsonParserBinary.java b/src/test/java/com/fasterxml/jackson/core/read/TestJsonParserBinary.java
deleted file mode 100644
index 3c5801c..0000000
--- a/src/test/java/com/fasterxml/jackson/core/read/TestJsonParserBinary.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package com.fasterxml.jackson.core.read;
-
-import java.io.*;
-
-import com.fasterxml.jackson.core.*;
-
-import static org.junit.Assert.*;
-
-/**
- * Tests for verifying that accessing base64 encoded content works ok.
- */
-public class TestJsonParserBinary
-    extends com.fasterxml.jackson.core.BaseTest
-{
-    /*
-    /**********************************************************************
-    /* Unit tests
-    /**********************************************************************
-     */
-
-    public void testSimple() throws IOException
-    {
-        for (int mode : ALL_MODES) {
-            _testSimple(mode);
-        }
-    }
-
-    public void testInArray() throws IOException
-    {
-        for (int mode : ALL_MODES) {
-            _testInArray(mode);
-        }
-    }
-
-    public void testWithEscaped() throws IOException {
-        for (int mode : ALL_MODES) {
-            _testEscaped(mode);
-        }
-    }
-
-    /*
-    /**********************************************************************
-    /* Actual test methods
-    /**********************************************************************
-     */
-
-    private void _testSimple(int mode)
-        throws IOException
-    {
-        /* The usual sample input string, from Thomas Hobbes's "Leviathan"
-         * (via Wikipedia)
-         */
-        final String RESULT = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.";
-        final byte[] RESULT_BYTES = RESULT.getBytes("US-ASCII");
-
-        // And here's what should produce it...
-        final String INPUT_STR = 
- "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
-+"IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
-+"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu"
-+"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
-+"ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4="
-            ;
-
-        final String DOC = "\""+INPUT_STR+"\"";
-        JsonParser p = createParser(mode, DOC);
-
-        assertToken(JsonToken.VALUE_STRING, p.nextToken());
-        byte[] data = p.getBinaryValue();
-        assertNotNull(data);
-        assertArrayEquals(RESULT_BYTES, data);
-        p.close();
-    }
-
-    private void _testInArray(int mode) throws IOException
-    {
-        JsonFactory f = new JsonFactory();
-
-        final int entryCount = 7;
-
-        StringWriter sw = new StringWriter();
-        JsonGenerator jg = f.createGenerator(sw);
-        jg.writeStartArray();
-
-        byte[][] entries = new byte[entryCount][];
-        for (int i = 0; i < entryCount; ++i) {
-            byte[] b = new byte[200 + i * 100];
-            for (int x = 0; x < b.length; ++x) {
-                b[x] = (byte) (i + x);
-            }
-            entries[i] = b;
-            jg.writeBinary(b);
-        }
-
-        jg.writeEndArray();
-        jg.close();
-
-        JsonParser p = createParser(f, mode, sw.toString());
-
-        assertToken(JsonToken.START_ARRAY, p.nextToken());
-
-        for (int i = 0; i < entryCount; ++i) {
-            assertToken(JsonToken.VALUE_STRING, p.nextToken());
-            byte[] b = p.getBinaryValue();
-            assertArrayEquals(entries[i], b);
-        }
-        assertToken(JsonToken.END_ARRAY, p.nextToken());
-        p.close();
-    }
-
-    private void _testEscaped(int mode) throws IOException
-    {
-        // Input: "Test!" -> "VGVzdCE="
-
-        // First, try with embedded linefeed half-way through:
-
-        String DOC = quote("VGVz\\ndCE="); // note: must double-quote to get linefeed
-        JsonParser p = createParser(mode, DOC);
-        assertToken(JsonToken.VALUE_STRING, p.nextToken());
-        byte[] b = p.getBinaryValue();
-        assertEquals("Test!", new String(b, "US-ASCII"));
-        if (mode != MODE_DATA_INPUT) {
-            assertNull(p.nextToken());
-        }
-        p.close();
-
-        // and then with escaped chars
-//        DOC = quote("V\\u0047V\\u007AdCE="); // note: must escape backslash...
-        DOC = quote("V\\u0047V\\u007AdCE="); // note: must escape backslash...
-        p = createParser(mode, DOC);
-        assertToken(JsonToken.VALUE_STRING, p.nextToken());
-        b = p.getBinaryValue();
-        assertEquals("Test!", new String(b, "US-ASCII"));
-        if (mode != MODE_DATA_INPUT) {
-            assertNull(p.nextToken());
-        }
-        p.close();
-    }
-}
diff --git a/src/test/java/com/fasterxml/jackson/core/read/TrailingCommasTest.java b/src/test/java/com/fasterxml/jackson/core/read/TrailingCommasTest.java
new file mode 100644
index 0000000..972b650
--- /dev/null
+++ b/src/test/java/com/fasterxml/jackson/core/read/TrailingCommasTest.java
@@ -0,0 +1,316 @@
+package com.fasterxml.jackson.core.read;
+
+import com.fasterxml.jackson.core.BaseTest;
+import com.fasterxml.jackson.core.JsonFactory;
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonParser.Feature;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.core.json.UTF8DataInputJsonParser;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+@RunWith(Parameterized.class)
+public class TrailingCommasTest extends BaseTest {
+
+  private final JsonFactory factory;
+  private final HashSet<JsonParser.Feature> features;
+  private final int mode;
+
+  public TrailingCommasTest(int mode, List<Feature> features) {
+    this.factory = new JsonFactory();
+    this.features = new HashSet<JsonParser.Feature>(features);
+
+    for (JsonParser.Feature feature : features) {
+      factory.enable(feature);
+    }
+
+    this.mode = mode;
+  }
+
+  @Parameterized.Parameters(name = "Mode {0}, Features {1}")
+  public static Collection<Object[]> getTestCases() {
+    ArrayList<Object[]> cases = new ArrayList<Object[]>();
+
+    for (int mode : ALL_MODES) {
+      cases.add(new Object[]{mode, Collections.emptyList()});
+      cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_MISSING_VALUES)});
+      cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_TRAILING_COMMA)});
+      cases.add(new Object[]{mode, Arrays.asList(Feature.ALLOW_MISSING_VALUES, Feature.ALLOW_TRAILING_COMMA)});
+    }
+
+    return cases;
+  }
+
+  @Test
+  public void testArrayBasic() throws Exception {
+    String json = "[\"a\", \"b\"]";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("a", p.getText());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("b", p.getText());
+
+    assertEquals(JsonToken.END_ARRAY, p.nextToken());
+    assertEnd(p);
+  }
+
+  @Test
+  public void testArrayInnerComma() throws Exception {
+    String json = "[\"a\",, \"b\"]";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("a", p.getText());
+
+    if (!features.contains(Feature.ALLOW_MISSING_VALUES)) {
+      assertUnexpected(p, ',');
+      return;
+    }
+
+    assertToken(JsonToken.VALUE_NULL, p.nextToken());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("b", p.getText());
+
+    assertEquals(JsonToken.END_ARRAY, p.nextToken());
+    assertEnd(p);
+  }
+
+  @Test
+  public void testArrayLeadingComma() throws Exception {
+    String json = "[,\"a\", \"b\"]";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+    if (!features.contains(Feature.ALLOW_MISSING_VALUES)) {
+      assertUnexpected(p, ',');
+      return;
+    }
+
+    assertToken(JsonToken.VALUE_NULL, p.nextToken());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("a", p.getText());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("b", p.getText());
+
+    assertEquals(JsonToken.END_ARRAY, p.nextToken());
+    assertEnd(p);
+  }
+
+  @Test
+  public void testArrayTrailingComma() throws Exception {
+    String json = "[\"a\", \"b\",]";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("a", p.getText());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("b", p.getText());
+
+    // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES
+    if (features.contains(Feature.ALLOW_TRAILING_COMMA)) {
+      assertToken(JsonToken.END_ARRAY, p.nextToken());
+      assertEnd(p);
+    } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) {
+      assertToken(JsonToken.VALUE_NULL, p.nextToken());
+      assertToken(JsonToken.END_ARRAY, p.nextToken());
+      assertEnd(p);
+    } else {
+      assertUnexpected(p, ']');
+    }
+  }
+
+  @Test
+  public void testArrayTrailingCommas() throws Exception {
+    String json = "[\"a\", \"b\",,]";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("a", p.getText());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("b", p.getText());
+
+    // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES
+    if (features.contains(Feature.ALLOW_MISSING_VALUES) &&
+        features.contains(Feature.ALLOW_TRAILING_COMMA)) {
+      assertToken(JsonToken.VALUE_NULL, p.nextToken());
+      assertToken(JsonToken.END_ARRAY, p.nextToken());
+      assertEnd(p);
+    } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) {
+      assertToken(JsonToken.VALUE_NULL, p.nextToken());
+      assertToken(JsonToken.VALUE_NULL, p.nextToken());
+      assertToken(JsonToken.END_ARRAY, p.nextToken());
+      assertEnd(p);
+    } else {
+      assertUnexpected(p, ',');
+    }
+  }
+
+  @Test
+  public void testArrayTrailingCommasTriple() throws Exception {
+    String json = "[\"a\", \"b\",,,]";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_ARRAY, p.nextToken());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("a", p.getText());
+
+    assertToken(JsonToken.VALUE_STRING, p.nextToken());
+    assertEquals("b", p.getText());
+
+    // ALLOW_TRAILING_COMMA takes priority over ALLOW_MISSING_VALUES
+    if (features.contains(Feature.ALLOW_MISSING_VALUES) &&
+        features.contains(Feature.ALLOW_TRAILING_COMMA)) {
+      assertToken(JsonToken.VALUE_NULL, p.nextToken());
+      assertToken(JsonToken.VALUE_NULL, p.nextToken());
+      assertToken(JsonToken.END_ARRAY, p.nextToken());
+      assertEnd(p);
+    } else if (features.contains(Feature.ALLOW_MISSING_VALUES)) {
+      assertToken(JsonToken.VALUE_NULL, p.nextToken());
+      assertToken(JsonToken.VALUE_NULL, p.nextToken());
+      assertToken(JsonToken.VALUE_NULL, p.nextToken());
+      assertToken(JsonToken.END_ARRAY, p.nextToken());
+      assertEnd(p);
+    } else {
+      assertUnexpected(p, ',');
+    }
+  }
+
+  @Test
+  public void testObjectBasic() throws Exception {
+    String json = "{\"a\": true, \"b\": false}";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_OBJECT, p.nextToken());
+
+    assertToken(JsonToken.FIELD_NAME, p.nextToken());
+    assertEquals("a", p.getText());
+    assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+
+    assertToken(JsonToken.FIELD_NAME, p.nextToken());
+    assertEquals("b", p.getText());
+    assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+
+    assertEquals(JsonToken.END_OBJECT, p.nextToken());
+    assertEnd(p);
+  }
+
+  @Test
+  public void testObjectInnerComma() throws Exception {
+    String json = "{\"a\": true,, \"b\": false}";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_OBJECT, p.nextToken());
+
+    assertToken(JsonToken.FIELD_NAME, p.nextToken());
+    assertEquals("a", p.getText());
+    assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+
+    assertUnexpected(p, ',');
+  }
+
+  @Test
+  public void testObjectLeadingComma() throws Exception {
+    String json = "{,\"a\": true, \"b\": false}";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_OBJECT, p.nextToken());
+
+    assertUnexpected(p, ',');
+  }
+
+  @Test
+  public void testObjectTrailingComma() throws Exception {
+    String json = "{\"a\": true, \"b\": false,}";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_OBJECT, p.nextToken());
+
+    assertToken(JsonToken.FIELD_NAME, p.nextToken());
+    assertEquals("a", p.getText());
+    assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+
+    assertToken(JsonToken.FIELD_NAME, p.nextToken());
+    assertEquals("b", p.getText());
+    assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+
+    if (features.contains(Feature.ALLOW_TRAILING_COMMA)) {
+      assertToken(JsonToken.END_OBJECT, p.nextToken());
+      assertEnd(p);
+    } else {
+      assertUnexpected(p, '}');
+    }
+  }
+
+  @Test
+  public void testObjectTrailingCommas() throws Exception {
+    String json = "{\"a\": true, \"b\": false,,}";
+
+    JsonParser p = createParser(factory, mode, json);
+
+    assertEquals(JsonToken.START_OBJECT, p.nextToken());
+
+    assertToken(JsonToken.FIELD_NAME, p.nextToken());
+    assertEquals("a", p.getText());
+    assertToken(JsonToken.VALUE_TRUE, p.nextToken());
+
+    assertToken(JsonToken.FIELD_NAME, p.nextToken());
+    assertEquals("b", p.getText());
+    assertToken(JsonToken.VALUE_FALSE, p.nextToken());
+
+    assertUnexpected(p, ',');
+  }
+
+  private void assertEnd(JsonParser p) throws IOException {
+    // Issue #325
+    if (!(p instanceof UTF8DataInputJsonParser)) {
+      JsonToken next = p.nextToken();
+      assertNull("expected end of stream but found " + next, next);
+    }
+  }
+
+  private void assertUnexpected(JsonParser p, char c) throws IOException {
+    try {
+      p.nextToken();
+      fail("No exception thrown");
+    } catch (Exception e) {
+      verifyException(e, String.format("Unexpected character ('%s' (code %d))", c, (int) c));
+    }
+  }
+}