You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mime4j-dev@james.apache.org by ol...@apache.org on 2009/12/21 20:12:09 UTC

svn commit: r892933 - in /james/mime4j/trunk/core/src: main/java/org/apache/james/mime4j/codec/ main/java/org/apache/james/mime4j/util/ test/java/org/apache/james/mime4j/codec/ test/java/org/apache/james/mime4j/util/

Author: olegk
Date: Mon Dec 21 19:12:09 2009
New Revision: 892933

URL: http://svn.apache.org/viewvc?rev=892933&view=rev
Log:
MIME4J-103: QuotedPrintableInputStream refactoring (second round); substituted ByteQueue with ByteArrayBuffer

Removed:
    james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/ByteQueue.java
    james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/UnboundedFifoByteBuffer.java
Modified:
    james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/Base64InputStream.java
    james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableInputStream.java
    james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableOutputStream.java
    james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/ByteArrayBuffer.java
    james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/codec/Base64InputStreamTest.java
    james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/util/TestByteArrayBuffer.java

Modified: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/Base64InputStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/Base64InputStream.java?rev=892933&r1=892932&r2=892933&view=diff
==============================================================================
--- james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/Base64InputStream.java (original)
+++ james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/Base64InputStream.java Mon Dec 21 19:12:09 2009
@@ -24,6 +24,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 /**
  * Performs Base-64 decoding on an underlying stream.
@@ -48,35 +49,38 @@
 
     private final byte[] singleByte = new byte[1];
 
-    private boolean strict;
-
     private final InputStream in;
-    private boolean closed = false;
+    private final boolean strict;
+    private final byte[] encoded;
+    private final ByteArrayBuffer decodedBuf;
 
-    private final byte[] encoded = new byte[ENCODED_BUFFER_SIZE];
     private int position = 0; // current index into encoded buffer
     private int size = 0; // current size of encoded buffer
 
-    private final ByteQueue q = new ByteQueue();
-
+    private boolean closed = false;
     private boolean eof; // end of file or pad character reached
 
-    public Base64InputStream(InputStream in) {
-        this(in, false);
-    }
-
-    public Base64InputStream(InputStream in, boolean strict) {
+    protected Base64InputStream(int bufsize, InputStream in, boolean strict) {
         if (in == null)
             throw new IllegalArgumentException();
-
+        this.encoded = new byte[bufsize];
+        this.decodedBuf = new ByteArrayBuffer(512);
         this.in = in;
         this.strict = strict;
     }
 
+    public Base64InputStream(InputStream in) {
+        this(ENCODED_BUFFER_SIZE, in, false);
+    }
+
+    public Base64InputStream(InputStream in, boolean strict) {
+        this(ENCODED_BUFFER_SIZE, in, strict);
+    }
+
     @Override
     public int read() throws IOException {
         if (closed)
-            throw new IOException("Base64InputStream has been closed");
+            throw new IOException("Stream has been closed");
 
         while (true) {
             int bytes = read0(singleByte, 0, 1);
@@ -91,7 +95,7 @@
     @Override
     public int read(byte[] buffer) throws IOException {
         if (closed)
-            throw new IOException("Base64InputStream has been closed");
+            throw new IOException("Stream has been closed");
 
         if (buffer == null)
             throw new NullPointerException();
@@ -105,7 +109,7 @@
     @Override
     public int read(byte[] buffer, int offset, int length) throws IOException {
         if (closed)
-            throw new IOException("Base64InputStream has been closed");
+            throw new IOException("Stream has been closed");
 
         if (buffer == null)
             throw new NullPointerException();
@@ -131,11 +135,12 @@
             throws IOException {
         int index = from; // index into given buffer
 
-        // check if a previous invocation left decoded bytes in the queue
-
-        int qCount = q.count();
-        while (qCount-- > 0 && index < to) {
-            buffer[index++] = q.dequeue();
+        // check if a previous invocation left decoded content
+        if (decodedBuf.length() > 0) {
+            int len = Math.min(decodedBuf.length(), to - from);
+            System.arraycopy(decodedBuf.buffer(), 0, buffer, index, len);
+            decodedBuf.remove(0, len);
+            index += len;
         }
 
         // eof or pad reached?
@@ -202,15 +207,15 @@
                         if (index < to - 1) {
                             buffer[index++] = b1;
                             buffer[index++] = b2;
-                            q.enqueue(b3);
+                            decodedBuf.append(b3);
                         } else if (index < to) {
                             buffer[index++] = b1;
-                            q.enqueue(b2);
-                            q.enqueue(b3);
+                            decodedBuf.append(b2);
+                            decodedBuf.append(b3);
                         } else {
-                            q.enqueue(b1);
-                            q.enqueue(b2);
-                            q.enqueue(b3);
+                            decodedBuf.append(b1);
+                            decodedBuf.append(b2);
+                            decodedBuf.append(b3);
                         }
 
                         assert index == to;
@@ -236,7 +241,7 @@
             if (index < end) {
                 buffer[index++] = b;
             } else {
-                q.enqueue(b);
+                decodedBuf.append(b);
             }
         } else if (sextets == 3) {
             // two bytes encoded as "XYZ="
@@ -249,10 +254,10 @@
                 buffer[index++] = b2;
             } else if (index < end) {
                 buffer[index++] = b1;
-                q.enqueue(b2);
+                decodedBuf.append(b2);
             } else {
-                q.enqueue(b1);
-                q.enqueue(b2);
+                decodedBuf.append(b1);
+                decodedBuf.append(b2);
             }
         } else {
             // error in encoded data
@@ -264,17 +269,17 @@
 
     private void handleUnexpectedEof(int sextets) throws IOException {
         if (strict)
-            throw new IOException("unexpected end of file");
+            throw new IOException("Unexpected end of BASE64 stream");
         else
-            log.warn("unexpected end of file; dropping " + sextets
+            log.warn("Unexpected end of BASE64 stream; dropping " + sextets
                     + " sextet(s)");
     }
 
     private void handleUnexpecedPad(int sextets) throws IOException {
         if (strict)
-            throw new IOException("unexpected padding character");
+            throw new IOException("Unexpected padding character");
         else
-            log.warn("unexpected padding character; dropping " + sextets
+            log.warn("Unexpected padding character; dropping " + sextets
                     + " sextet(s)");
     }
 }

Modified: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableInputStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableInputStream.java?rev=892933&r1=892932&r2=892933&view=diff
==============================================================================
--- james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableInputStream.java (original)
+++ james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableInputStream.java Mon Dec 21 19:12:09 2009
@@ -24,6 +24,7 @@
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
 
 /**
  * Performs Quoted-Printable decoding on an underlying stream.
@@ -38,10 +39,12 @@
     
     private static Log log = LogFactory.getLog(QuotedPrintableInputStream.class);
     
+    private final byte[] singleByte = new byte[1];
+    
     private final InputStream in;
     private boolean strict;
-    private final ByteQueue data; 
-    private final ByteQueue blanks; 
+    private final ByteArrayBuffer decodedBuf; 
+    private final ByteArrayBuffer blanks; 
     
     private final byte[] encoded;
     private int pos = 0; // current index into encoded buffer
@@ -54,8 +57,8 @@
         this.in = in;
         this.strict = strict;
         this.encoded = new byte[bufsize];
-        this.data = new ByteQueue();
-        this.blanks = new ByteQueue();
+        this.decodedBuf = new ByteArrayBuffer(512);
+        this.blanks = new ByteArrayBuffer(512);
         this.closed = false;
     }
     
@@ -130,24 +133,21 @@
                 break;
             }
             if (Character.isWhitespace(b)) {
-                blanks.enqueue(b);
+                blanks.append(b);
             } else {
-                enqueueBlanks();                
-                data.enqueue(b);
+                if (blanks.length() > 0) {
+                    decodedBuf.append(blanks.buffer(), 0, blanks.length());
+                    blanks.clear();
+                }
+                decodedBuf.append(b);
             }
             pos++;
         }
     }    
     
-    private void enqueueBlanks() {
-        while (blanks.count() > 0) {
-            data.enqueue(blanks.dequeue());
-        }
-    }
-    
     private void decode() throws IOException {
         boolean endOfStream = false;
-        while (data.count() == 0) {
+        while (decodedBuf.length() == 0) {
 
             if (bufferLength() < 3) {
                 int bytesRead = fillBuffer();
@@ -175,33 +175,36 @@
         byte b1 = advance();
         if (b1 == LF) {
             // at end of line
-            if (blanks.count() == 0) {
-                data.enqueue((byte) LF);
+            if (blanks.length() == 0) {
+                decodedBuf.append((byte) LF);
             } else {
-                if (blanks.dequeue() != EQ) {
+                if (blanks.byteAt(0) != EQ) {
                     // hard line break
-                    data.enqueue((byte) CR);
-                    data.enqueue((byte) LF);
+                    decodedBuf.append((byte) CR);
+                    decodedBuf.append((byte) LF);
                 }
             }
             blanks.clear();
         } else if (b1 == EQ) {
             // found special char '='
-            enqueueBlanks();
+            if (blanks.length() > 0) {
+                decodedBuf.append(blanks.buffer(), 0, blanks.length());
+                blanks.clear();
+            }
             byte b2 = advance();
             if (b2 == EQ) {
-                data.enqueue(b2);
+                decodedBuf.append(b2);
                 // deal with '==\r\n' brokenness
                 byte bb1 = peek(0);
                 byte bb2 = peek(1);
                 if (bb1 == LF || (bb1 == CR && bb2 == LF)) {
-                    blanks.enqueue(b2);
+                    blanks.append(b2);
                 }
             } else if (Character.isWhitespace((char) b2)) {
                 // soft line break
                 if (b2 != LF) {
-                    blanks.enqueue(b1);
-                    blanks.enqueue(b2);
+                    blanks.append(b1);
+                    blanks.append(b2);
                 }
             } else {
                 byte b3 = advance();
@@ -212,12 +215,12 @@
                         throw new IOException("Malformed encoded value encountered");
                     } else {
                         log.warn("Malformed encoded value encountered");
-                        data.enqueue((byte) EQ);
-                        if (b2 != -1) data.enqueue((byte) b2);
-                        if (b3 != -1) data.enqueue((byte) b3);
+                        decodedBuf.append((byte) EQ);
+                        if (b2 != -1) decodedBuf.append((byte) b2);
+                        if (b3 != -1) decodedBuf.append((byte) b3);
                     }
                 } else {
-                    data.enqueue((byte)((upper << 4) | lower));
+                    decodedBuf.append((byte)((upper << 4) | lower));
                 }
             }
         } else {
@@ -247,15 +250,32 @@
         if (closed) {
             throw new IOException("Stream has been closed");
         }
+        for (;;) {
+            int bytes = read(singleByte, 0, 1);
+            if (bytes == -1) {
+                return -1;
+            }
+            if (bytes == 1) {
+                return singleByte[0] & 0xff;
+            }
+        }
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+        if (closed) {
+            throw new IOException("Stream has been closed");
+        }
         decode();
-        if (data.count() == 0)
+        if (decodedBuf.length() == 0) {
             return -1;
-        else {
-            byte val = data.dequeue();
-            if (val >= 0)
-                return val;
-            else
-                return val & 0xFF;
+        } else {
+            int chunk = Math.min(decodedBuf.length(), len);
+            if (chunk > 0) {
+                System.arraycopy(decodedBuf.buffer(), 0, b, off, chunk);
+                decodedBuf.remove(0, chunk);
+            }
+            return chunk;
         }
     }
 

Modified: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableOutputStream.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableOutputStream.java?rev=892933&r1=892932&r2=892933&view=diff
==============================================================================
--- james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableOutputStream.java (original)
+++ james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/codec/QuotedPrintableOutputStream.java Mon Dec 21 19:12:09 2009
@@ -52,7 +52,7 @@
 
     private boolean closed = false;
 
-    private byte[] oneByte = new byte[1];
+    private byte[] singleByte = new byte[1];
     
     public QuotedPrintableOutputStream(int bufsize, OutputStream out, boolean binary) {
         super(out);
@@ -218,8 +218,8 @@
 
     @Override
     public void write(int b) throws IOException {
-        oneByte[0] = (byte) b;
-        this.write(oneByte, 0, 1);
+        singleByte[0] = (byte) b;
+        this.write(singleByte, 0, 1);
     }
 
     @Override

Modified: james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/ByteArrayBuffer.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/ByteArrayBuffer.java?rev=892933&r1=892932&r2=892933&view=diff
==============================================================================
--- james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/ByteArrayBuffer.java (original)
+++ james/mime4j/trunk/core/src/main/java/org/apache/james/mime4j/util/ByteArrayBuffer.java Mon Dec 21 19:12:09 2009
@@ -150,6 +150,21 @@
         this.len = len;
     }
     
+    public void remove(int off, int len) {
+        if ((off < 0) || (off > this.len) || (len < 0) ||
+                ((off + len) < 0) || ((off + len) > this.len)) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (len == 0) {
+            return;
+        }
+        int remaining = this.len - off - len;
+        if (remaining > 0) {
+            System.arraycopy(this.buffer, off + len, this.buffer, off, remaining);
+        }
+        this.len -= len;
+    }
+    
     public boolean isEmpty() {
         return this.len == 0; 
     }

Modified: james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/codec/Base64InputStreamTest.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/codec/Base64InputStreamTest.java?rev=892933&r1=892932&r2=892933&view=diff
==============================================================================
--- james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/codec/Base64InputStreamTest.java (original)
+++ james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/codec/Base64InputStreamTest.java Mon Dec 21 19:12:09 2009
@@ -228,7 +228,7 @@
             fail();
         } catch (IOException expected) {
             assertTrue(expected.getMessage().toLowerCase().contains(
-                    "end of file"));
+                    "end of base64 stream"));
         }
     }
 

Modified: james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/util/TestByteArrayBuffer.java
URL: http://svn.apache.org/viewvc/james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/util/TestByteArrayBuffer.java?rev=892933&r1=892932&r2=892933&view=diff
==============================================================================
--- james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/util/TestByteArrayBuffer.java (original)
+++ james/mime4j/trunk/core/src/test/java/org/apache/james/mime4j/util/TestByteArrayBuffer.java Mon Dec 21 19:12:09 2009
@@ -166,4 +166,66 @@
         }
     }
     
+    public void testRemove() throws Exception {
+        ByteArrayBuffer b = new ByteArrayBuffer(16);
+        byte tmp[] = "--+++-".getBytes("US-ASCII");
+        b.append(tmp, 0, tmp.length);
+
+        b.remove(2, 3);
+        assertEquals(3, b.length());
+        assertEquals("---", new String(b.buffer(), 0, b.length(), "US-ASCII"));
+        b.remove(2, 1);
+        b.remove(1, 1);
+        b.remove(0, 1);
+        assertEquals(0, b.length());
+
+        tmp = "+++---".getBytes("US-ASCII");
+        b.append(tmp, 0, tmp.length);
+
+        b.remove(0, 3);
+        assertEquals(3, b.length());
+        assertEquals("---", new String(b.buffer(), 0, b.length(), "US-ASCII"));
+        b.remove(0, 3);
+        assertEquals(0, b.length());
+
+        tmp = "---+++".getBytes("US-ASCII");
+        b.append(tmp, 0, tmp.length);
+
+        b.remove(3, 3);
+        assertEquals(3, b.length());
+        assertEquals("---", new String(b.buffer(), 0, b.length(), "US-ASCII"));
+        b.remove(0, 3);
+        
+        assertEquals(0, b.length());
+    }
+
+    public void testInvalidRemove() throws Exception {
+        ByteArrayBuffer buffer = new ByteArrayBuffer(16);
+        buffer.setLength(8);
+        try {
+            buffer.remove(-1, 0);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.remove(0, -1);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.remove(0, 9);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+        try {
+            buffer.remove(10, 2);
+            fail("IndexOutOfBoundsException should have been thrown");
+        } catch (IndexOutOfBoundsException ex) {
+            // expected
+        }
+    }
+
 }