diff --git a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Decoder.java b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Decoder.java
index 7bd83879..aa7576d0 100755
--- a/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Decoder.java
+++ b/common/common-io/src/main/java/com/twelvemonkeys/io/enc/Decoder.java
@@ -57,9 +57,10 @@ public interface Decoder {
* @return the total number of bytes read into the buffer, or {@code 0}
* if there is no more data because the end of the stream has been reached.
*
- * @throws DecodeException if encoded data is corrupt
- * @throws IOException if an I/O error occurs
- * @throws java.io.EOFException if a premature end-of-file is encountered
+ * @throws DecodeException if encoded data is corrupt.
+ * @throws IOException if an I/O error occurs.
+ * @throws java.io.EOFException if a premature end-of-file is encountered.
+ * @throws java.lang.NullPointerException if either argument is {@code null}.
*/
int decode(InputStream stream, ByteBuffer buffer) throws IOException;
}
diff --git a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/LZWDecoder.java b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/LZWDecoder.java
index 51fa49f7..bc47635c 100644
--- a/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/LZWDecoder.java
+++ b/imageio/imageio-tiff/src/main/java/com/twelvemonkeys/imageio/plugins/tiff/LZWDecoder.java
@@ -33,12 +33,11 @@ import com.twelvemonkeys.io.enc.Decoder;
import java.io.IOException;
import java.io.InputStream;
-import java.lang.String;
import java.nio.ByteBuffer;
/**
- * Lempel–Ziv–Welch (LZW) decompression. LZW is a universal loss-less data compression algorithm
- * created by Abraham Lempel, Jacob Ziv, and Terry Welch.
+ * Lempel–Ziv–Welch (LZW) decompression.
+ * LZW is a universal loss-less data compression algorithm created by Abraham Lempel, Jacob Ziv, and Terry Welch.
* Inspired by libTiff's LZW decompression.
*
* @author Harald Kuhr
@@ -57,8 +56,6 @@ abstract class LZWDecoder implements Decoder {
private static final int TABLE_SIZE = 1 << MAX_BITS;
- private final boolean compatibilityMode;
-
private final LZWString[] table;
private int tableLength;
int bitsPerCode;
@@ -70,11 +67,8 @@ abstract class LZWDecoder implements Decoder {
int nextData;
int nextBits;
-
- protected LZWDecoder(final boolean compatibilityMode) {
- this.compatibilityMode = compatibilityMode;
-
- table = new LZWString[compatibilityMode ? TABLE_SIZE + 1024 : TABLE_SIZE]; // libTiff adds 1024 "for compatibility"...
+ protected LZWDecoder(int tableSize) {
+ table = new LZWString[tableSize];
// First 258 entries of table is always fixed
for (int i = 0; i < 256; i++) {
@@ -97,6 +91,10 @@ abstract class LZWDecoder implements Decoder {
}
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
+ if (buffer == null) {
+ throw new NullPointerException("buffer == null"); // As per contract
+ }
+
// Adapted from the pseudo-code example found in the TIFF 6.0 Specification, 1992.
// See Section 13: "LZW Compression"/"LZW Decoding", page 61+
int code;
@@ -114,8 +112,7 @@ abstract class LZWDecoder implements Decoder {
}
else {
if (table[oldCode] == null) {
- System.err.println("tableLength: " + tableLength);
- System.err.println("oldCode: " + oldCode);
+ throw new DecodeException(String.format("Corrupted TIFF LZW: code %d (table size: %d)", oldCode, tableLength));
}
if (isInTable(code)) {
@@ -133,7 +130,7 @@ abstract class LZWDecoder implements Decoder {
oldCode = code;
if (buffer.remaining() < maxString + 1) {
- // Buffer full, stop decoding for now
+ // Buffer (almost) full, stop decoding for now
break;
}
}
@@ -142,18 +139,18 @@ abstract class LZWDecoder implements Decoder {
}
private void addStringToTable(final LZWString string) throws IOException {
+ if (tableLength > table.length) {
+ throw new DecodeException(String.format("TIFF LZW with more than %d bits per code encountered (table overflow)", MAX_BITS));
+ }
+
table[tableLength++] = string;
if (tableLength > maxCode) {
bitsPerCode++;
if (bitsPerCode > MAX_BITS) {
- if (compatibilityMode) {
- bitsPerCode--;
- }
- else {
- throw new DecodeException(String.format("TIFF LZW with more than %d bits per code encountered (table overflow)", MAX_BITS));
- }
+ // Continue reading MAX_BITS (12 bit) length codes
+ bitsPerCode = MAX_BITS;
}
bitMask = bitmaskFor(bitsPerCode);
@@ -173,9 +170,9 @@ abstract class LZWDecoder implements Decoder {
protected abstract int getNextCode(final InputStream stream) throws IOException;
-
static boolean isOldBitReversedStream(final InputStream stream) throws IOException {
stream.mark(2);
+
try {
int one = stream.read();
int two = stream.read();
@@ -191,10 +188,10 @@ abstract class LZWDecoder implements Decoder {
return oldBitReversedStream ? new LZWCompatibilityDecoder() : new LZWSpecDecoder();
}
- private static final class LZWSpecDecoder extends LZWDecoder {
+ static final class LZWSpecDecoder extends LZWDecoder {
protected LZWSpecDecoder() {
- super(false);
+ super(TABLE_SIZE);
}
@Override
@@ -243,7 +240,7 @@ abstract class LZWDecoder implements Decoder {
// compressed data will be identical whether it is an ‘II’ or ‘MM’ file."
protected LZWCompatibilityDecoder() {
- super(true);
+ super(TABLE_SIZE + 1024); // libTiff adds 1024 "for compatibility", this value seems to work fine...
}
@Override
diff --git a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java
index 7122b900..e440a2cd 100644
--- a/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java
+++ b/imageio/imageio-tiff/src/test/java/com/twelvemonkeys/imageio/plugins/tiff/TIFFImageReaderTest.java
@@ -30,7 +30,6 @@ import com.twelvemonkeys.imageio.util.ImageReaderAbstractTestCase;
import org.junit.Test;
import javax.imageio.ImageReadParam;
-import javax.imageio.ImageReader;
import javax.imageio.event.IIOReadWarningListener;
import javax.imageio.spi.ImageReaderSpi;
import javax.imageio.stream.ImageInputStream;
@@ -42,12 +41,9 @@ import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.contains;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.*;
/**
* TIFFImageReaderTest
@@ -72,6 +68,7 @@ public class TIFFImageReaderTest extends ImageReaderAbstractTestCase