You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2011/01/24 01:46:10 UTC

svn commit: r1062592 - in /commons/proper/codec/trunk/src: java/org/apache/commons/codec/binary/ test/org/apache/commons/codec/binary/

Author: ggregory
Date: Mon Jan 24 00:46:09 2011
New Revision: 1062592

URL: http://svn.apache.org/viewvc?rev=1062592&view=rev
Log:
[CODEC-105] ArrayIndexOutOfBoundsException when doing multiple reads() on encoding b64InputStream. https://issues.apache.org/jira/browse/CODEC-105

Added:
    commons/proper/codec/trunk/src/test/org/apache/commons/codec/binary/Codec105ErrorInputStream.java
Modified:
    commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base64.java
    commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base64InputStream.java
    commons/proper/codec/trunk/src/test/org/apache/commons/codec/binary/Base64InputStreamTest.java

Modified: commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base64.java
URL: http://svn.apache.org/viewvc/commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base64.java?rev=1062592&r1=1062591&r2=1062592&view=diff
==============================================================================
--- commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base64.java (original)
+++ commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base64.java Mon Jan 24 00:46:09 2011
@@ -406,15 +406,9 @@ public class Base64 implements BinaryEnc
     int readResults(byte[] b, int bPos, int bAvail) {
         if (buffer != null) {
             int len = Math.min(avail(), bAvail);
-            if (buffer != b) {
-                System.arraycopy(buffer, readPos, b, bPos, len);
-                readPos += len;
-                if (readPos >= pos) {
-                    buffer = null;
-                }
-            } else {
-                // Re-using the original consumer's output array is only
-                // allowed for one round.
+            System.arraycopy(buffer, readPos, b, bPos, len);
+            readPos += len;
+            if (readPos >= pos) {
                 buffer = null;
             }
             return len;
@@ -423,27 +417,6 @@ public class Base64 implements BinaryEnc
     }
 
     /**
-     * Sets the streaming buffer. This is a small optimization where we try to buffer directly to the consumer's output
-     * array for one round (if the consumer calls this method first) instead of starting our own buffer.
-     * 
-     * @param out
-     *            byte[] array to buffer directly to.
-     * @param outPos
-     *            Position to start buffering into.
-     * @param outAvail
-     *            Amount of bytes available for direct buffering.
-     */
-    void setInitialBuffer(byte[] out, int outPos, int outAvail) {
-        // We can re-use consumer's original output array under
-        // special circumstances, saving on some System.arraycopy().
-        if (out != null && out.length == outAvail) {
-            buffer = out;
-            pos = outPos;
-            readPos = outPos;
-        }
-    }
-
-    /**
      * <p>
      * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with
      * the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush last
@@ -493,7 +466,10 @@ public class Base64 implements BinaryEnc
                     }
                     break;
             }
-            if (lineLength > 0 && pos > 0) {
+            // Don't want to append the CRLF two times in a row, so make sure previous
+            // character is not from CRLF!
+            byte b = lineSeparator[lineSeparator.length - 1];
+            if (lineLength > 0 && pos > 0 && buffer[pos-1] != b) {
                 System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length);
                 pos += lineSeparator.length;
             }
@@ -751,18 +727,8 @@ public class Base64 implements BinaryEnc
         if (pArray == null || pArray.length == 0) {
             return pArray;
         }
-        long len = (pArray.length * 3) / 4;
-        byte[] buf = new byte[(int) len];
-        setInitialBuffer(buf, 0, buf.length);
         decode(pArray, 0, pArray.length);
         decode(pArray, 0, -1); // Notify decoder of EOF.
-
-        // Would be nice to just return buf (like we sometimes do in the encode
-        // logic), but we have no idea what the line-length was (could even be
-        // variable).  So we cannot determine ahead of time exactly how big an
-        // array is necessary.  Hence the need to construct a 2nd byte array to
-        // hold the final result:
-
         byte[] result = new byte[pos];
         readResults(result, 0, result.length);
         return result;
@@ -946,23 +912,11 @@ public class Base64 implements BinaryEnc
         if (pArray == null || pArray.length == 0) {
             return pArray;
         }
-        long len = getEncodeLength(pArray, lineLength, lineSeparator);
-        byte[] buf = new byte[(int) len];
-        setInitialBuffer(buf, 0, buf.length);
         encode(pArray, 0, pArray.length);
         encode(pArray, 0, -1); // Notify encoder of EOF.
-        // Encoder might have resized, even though it was unnecessary.
-        if (buffer != buf) {
-            readResults(buf, 0, buf.length);
-        }
-        // In URL-SAFE mode we skip the padding characters, so sometimes our
-        // final length is a bit smaller.
-        if (isUrlSafe() && pos < buf.length) {
-            byte[] smallerBuf = new byte[pos];
-            System.arraycopy(buf, 0, smallerBuf, 0, pos);
-            buf = smallerBuf;
-        }
-        return buf;        
+        byte[] buf = new byte[pos - readPos];
+        readResults(buf, 0, buf.length);
+        return buf;
     }
 
     /**

Modified: commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base64InputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base64InputStream.java?rev=1062592&r1=1062591&r2=1062592&view=diff
==============================================================================
--- commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base64InputStream.java (original)
+++ commons/proper/codec/trunk/src/java/org/apache/commons/codec/binary/Base64InputStream.java Mon Jan 24 00:46:09 2011
@@ -166,11 +166,6 @@ public class Base64InputStream extends F
                 if (!base64.hasData()) {
                     byte[] buf = new byte[doEncode ? 4096 : 8192];
                     int c = in.read(buf);
-                    // A little optimization to avoid System.arraycopy()
-                    // when possible.
-                    if (c > 0 && b.length == len) {
-                        base64.setInitialBuffer(b, offset, len);
-                    }
                     if (doEncode) {
                         base64.encode(buf, 0, c);
                     } else {

Modified: commons/proper/codec/trunk/src/test/org/apache/commons/codec/binary/Base64InputStreamTest.java
URL: http://svn.apache.org/viewvc/commons/proper/codec/trunk/src/test/org/apache/commons/codec/binary/Base64InputStreamTest.java?rev=1062592&r1=1062591&r2=1062592&view=diff
==============================================================================
--- commons/proper/codec/trunk/src/test/org/apache/commons/codec/binary/Base64InputStreamTest.java (original)
+++ commons/proper/codec/trunk/src/test/org/apache/commons/codec/binary/Base64InputStreamTest.java Mon Jan 24 00:46:09 2011
@@ -19,6 +19,7 @@ package org.apache.commons.codec.binary;
 
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.Arrays;
@@ -49,6 +50,16 @@ public class Base64InputStreamTest exten
     }
 
     /**
+     * Tests the bug reported in CODEC-105. Bad interactions with InputStream when reading one byte at a time.
+     */
+    public void testCodec105() throws IOException {
+        Base64InputStream in = new Base64InputStream(new Codec105ErrorInputStream(), true, 0, null);
+        for (int i = 0; i < 5; i++) {
+            in.read();
+        }
+    }
+
+    /**
      * Test for the CODEC-101 bug:  InputStream.read(byte[]) should never return 0
      * because Java's builtin InputStreamReader hates that.
      *

Added: commons/proper/codec/trunk/src/test/org/apache/commons/codec/binary/Codec105ErrorInputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/codec/trunk/src/test/org/apache/commons/codec/binary/Codec105ErrorInputStream.java?rev=1062592&view=auto
==============================================================================
--- commons/proper/codec/trunk/src/test/org/apache/commons/codec/binary/Codec105ErrorInputStream.java (added)
+++ commons/proper/codec/trunk/src/test/org/apache/commons/codec/binary/Codec105ErrorInputStream.java Mon Jan 24 00:46:09 2011
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.codec.binary;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Emits three line-feeds '\n' in a row, one at a time, and then EOF.
+ * 
+ * Recreates the bug described in CODEC-105.
+ * 
+ * @author Apache Software Foundation
+ * @version $Id $
+ * @since 1.5
+ */
+public class Codec105ErrorInputStream extends InputStream {
+    private static final int EOF = -1;
+
+    int countdown = 3;
+
+    public int read() throws IOException {
+        if (this.countdown-- > 0) {
+            return '\n';
+        } else {
+            return EOF;
+        }
+    }
+
+    public int read(byte b[], int pos, int len) throws IOException {
+        if (this.countdown-- > 0) {
+            b[pos] = '\n';
+            return 1;
+        } else {
+            return EOF;
+        }
+    }
+}