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();
}
}