Backport fix for [Issue#42] (NPE for UTF-32 reader)
diff --git a/release-notes/VERSION b/release-notes/VERSION index cbc5154..a77b7b0 100644 --- a/release-notes/VERSION +++ b/release-notes/VERSION
@@ -14,6 +14,8 @@ * [Issue#38]: Infinite loop in `JsonParser.nextFieldName()` with trailing space after field name (reported by matjazs@github) +* [Issue#42]: NPE in UTF-32 parser + (reported by James R) ------------------------------------------------------------------------ === History: ===
diff --git a/src/java/org/codehaus/jackson/io/UTF32Reader.java b/src/java/org/codehaus/jackson/io/UTF32Reader.java index 35dc712..61ec711 100644 --- a/src/java/org/codehaus/jackson/io/UTF32Reader.java +++ b/src/java/org/codehaus/jackson/io/UTF32Reader.java
@@ -2,52 +2,54 @@ 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 +public class UTF32Reader extends BaseReader { - final boolean mBigEndian; + protected final boolean _bigEndian; /** * 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; + protected char _surrogate = NULL_CHAR; /** * Total read character count; used for error reporting purposes */ - int mCharCount = 0; + protected int _charCount = 0; /** * Total read byte count; used for error reporting purposes */ - int mByteCount = 0; + protected int _byteCount = 0; + protected final boolean _managedBuffers; + /* - //////////////////////////////////////// - // Life-cycle - //////////////////////////////////////// - */ + /********************************************************** + /* Life-cycle + /********************************************************** + */ public UTF32Reader(IOContext ctxt, - InputStream in, byte[] buf, int ptr, int len, - boolean isBigEndian) + InputStream in, byte[] buf, int ptr, int len, + boolean isBigEndian) { super(ctxt, in, buf, ptr, len); - mBigEndian = isBigEndian; + _bigEndian = isBigEndian; + _managedBuffers = (in != null); } /* - //////////////////////////////////////// - // Public API - //////////////////////////////////////// - */ + /********************************************************** + /* Public API + /********************************************************** + */ @Override public int read(char[] cbuf, int start, int len) @@ -69,9 +71,9 @@ int outPtr = start; // Ok, first; do we have a surrogate from last round? - if (mSurrogate != NULL_CHAR) { - cbuf[outPtr++] = mSurrogate; - mSurrogate = NULL_CHAR; + if (_surrogate != NULL_CHAR) { + cbuf[outPtr++] = _surrogate; + _surrogate = 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 @@ -90,7 +92,7 @@ int ptr = _ptr; int ch; - if (mBigEndian) { + if (_bigEndian) { ch = (_buffer[ptr] << 24) | ((_buffer[ptr+1] & 0xFF) << 16) | ((_buffer[ptr+2] & 0xFF) << 8) | (_buffer[ptr+3] & 0xFF); } else { @@ -112,7 +114,7 @@ ch = (0xDC00 | (ch & 0x03FF)); // Room for second part? if (outPtr >= len) { // nope - mSurrogate = (char) ch; + _surrogate = (char) ch; break main_loop; } } @@ -123,36 +125,34 @@ } len = outPtr - start; - mCharCount += len; + _charCount += len; return len; } /* - //////////////////////////////////////// - // Internal methods - //////////////////////////////////////// - */ + /********************************************************** + /* Internal methods + /********************************************************** + */ private void reportUnexpectedEOF(int gotBytes, int needed) throws IOException { - int bytePos = mByteCount + gotBytes; - int charPos = mCharCount; + int bytePos = _byteCount + gotBytes; + int charPos = _charCount; throw new CharConversionException("Unexpected EOF in the middle of a 4-byte UTF-32 char: got " - +gotBytes+", needed "+needed - +", at char #"+charPos+", byte #"+bytePos+")"); + +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; + int bytePos = _byteCount + _ptr - 1; + int charPos = _charCount + offset; throw new CharConversionException("Invalid UTF-32 character 0x" - +Integer.toHexString(value) - +msg+" at char #"+charPos+", byte #"+bytePos+")"); + +Integer.toHexString(value)+msg+" at char #"+charPos+", byte #"+bytePos+")"); } /** @@ -164,7 +164,7 @@ private boolean loadMore(int available) throws IOException { - mByteCount += (_length - available); + _byteCount += (_length - available); // Bytes that need to be moved to the beginning of buffer? if (available > 0) { @@ -180,11 +180,13 @@ * so let's do a separate read right away: */ _ptr = 0; - int count = _in.read(_buffer); + int count = (_in == null) ? -1 : _in.read(_buffer); if (count < 1) { _length = 0; if (count < 0) { // -1 - freeBuffers(); // to help GC? + if (_managedBuffers) { + freeBuffers(); // to help GC? + } return false; } // 0 count is no good; let's err out @@ -197,10 +199,12 @@ * error. */ while (_length < 4) { - int count = _in.read(_buffer, _length, _buffer.length - _length); + int count = (_in == null) ? -1 : _in.read(_buffer, _length, _buffer.length - _length); if (count < 1) { if (count < 0) { // -1, EOF... no good! - freeBuffers(); // to help GC? + if (_managedBuffers) { + freeBuffers(); // to help GC? + } reportUnexpectedEOF(_length, 4); } // 0 count is no good; let's err out @@ -211,4 +215,3 @@ return true; } } -
diff --git a/src/test/org/codehaus/jackson/impl/TestJsonParser.java b/src/test/org/codehaus/jackson/impl/TestJsonParser.java index c73c0b2..6b54584 100644 --- a/src/test/org/codehaus/jackson/impl/TestJsonParser.java +++ b/src/test/org/codehaus/jackson/impl/TestJsonParser.java
@@ -411,6 +411,7 @@ // should still know the field name assertEquals("doc", jp.getCurrentName()); assertToken(JsonToken.END_OBJECT, jp.nextToken()); + assertNull(jp.nextToken()); jp.close(); } }