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
+ }
+ }
+
}