Some updates and changes to support some work on https://issues.apache.org/jira/browse/AMQ-3467

Cleanup some code in the DataByteArrayOutputStream and fix an issue in the readUTF 
code that code result in a UTFDataFormatException to be thrown for no reason.

Adds some tests for those classes as well.

git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@1172600 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Timothy A. Bish 2011-09-19 12:46:38 +00:00
parent a36e618f71
commit 199c6838d4
5 changed files with 252 additions and 103 deletions

View File

@ -23,8 +23,8 @@ import java.io.UTFDataFormatException;
/** /**
* Optimized ByteArrayInputStream that can be used more than once * Optimized ByteArrayInputStream that can be used more than once
* *
* *
*/ */
public final class DataByteArrayInputStream extends InputStream implements DataInput { public final class DataByteArrayInputStream extends InputStream implements DataInput {
private byte[] buf; private byte[] buf;
@ -32,9 +32,11 @@ public final class DataByteArrayInputStream extends InputStream implements DataI
private int offset; private int offset;
private int length; private int length;
private byte[] work;
/** /**
* Creates a <code>StoreByteArrayInputStream</code>. * Creates a <code>StoreByteArrayInputStream</code>.
* *
* @param buf the input buffer. * @param buf the input buffer.
*/ */
public DataByteArrayInputStream(byte buf[]) { public DataByteArrayInputStream(byte buf[]) {
@ -42,11 +44,12 @@ public final class DataByteArrayInputStream extends InputStream implements DataI
this.pos = 0; this.pos = 0;
this.offset = 0; this.offset = 0;
this.length = buf.length; this.length = buf.length;
this.work = new byte[8];
} }
/** /**
* Creates a <code>StoreByteArrayInputStream</code>. * Creates a <code>StoreByteArrayInputStream</code>.
* *
* @param sequence the input buffer. * @param sequence the input buffer.
*/ */
public DataByteArrayInputStream(ByteSequence sequence) { public DataByteArrayInputStream(ByteSequence sequence) {
@ -54,6 +57,7 @@ public final class DataByteArrayInputStream extends InputStream implements DataI
this.offset = sequence.getOffset(); this.offset = sequence.getOffset();
this.pos = this.offset; this.pos = this.offset;
this.length = sequence.length; this.length = sequence.length;
this.work = new byte[8];
} }
/** /**
@ -81,7 +85,7 @@ public final class DataByteArrayInputStream extends InputStream implements DataI
/** /**
* reset the <code>StoreByteArrayInputStream</code> to use an new byte * reset the <code>StoreByteArrayInputStream</code> to use an new byte
* array * array
* *
* @param newBuff * @param newBuff
*/ */
public void restart(byte[] newBuff) { public void restart(byte[] newBuff) {
@ -98,7 +102,7 @@ public final class DataByteArrayInputStream extends InputStream implements DataI
/** /**
* reset the <code>StoreByteArrayInputStream</code> to use an new * reset the <code>StoreByteArrayInputStream</code> to use an new
* ByteSequence * ByteSequence
* *
* @param sequence * @param sequence
*/ */
public void restart(ByteSequence sequence) { public void restart(ByteSequence sequence) {
@ -109,7 +113,7 @@ public final class DataByteArrayInputStream extends InputStream implements DataI
/** /**
* re-start the input stream - reusing the current buffer * re-start the input stream - reusing the current buffer
* *
* @param size * @param size
*/ */
public void restart(int size) { public void restart(int size) {
@ -127,7 +131,7 @@ public final class DataByteArrayInputStream extends InputStream implements DataI
* stream has been reached, the value <code>-1</code> is returned. * stream has been reached, the value <code>-1</code> is returned.
* <p> * <p>
* This <code>read</code> method cannot block. * This <code>read</code> method cannot block.
* *
* @return the next byte of data, or <code>-1</code> if the end of the * @return the next byte of data, or <code>-1</code> if the end of the
* stream has been reached. * stream has been reached.
*/ */
@ -138,7 +142,7 @@ public final class DataByteArrayInputStream extends InputStream implements DataI
/** /**
* Reads up to <code>len</code> bytes of data into an array of bytes from * Reads up to <code>len</code> bytes of data into an array of bytes from
* this input stream. * this input stream.
* *
* @param b the buffer into which the data is read. * @param b the buffer into which the data is read.
* @param off the start offset of the data. * @param off the start offset of the data.
* @param len the maximum number of bytes read. * @param len the maximum number of bytes read.
@ -204,34 +208,35 @@ public final class DataByteArrayInputStream extends InputStream implements DataI
} }
public short readShort() { public short readShort() {
int ch1 = read(); this.read(work, 0, 2);
int ch2 = read(); return (short) (((work[0] & 0xff) << 8) | (work[1] & 0xff));
return (short)((ch1 << 8) + (ch2 << 0));
} }
public int readUnsignedShort() { public int readUnsignedShort() {
int ch1 = read(); this.read(work, 0, 2);
int ch2 = read(); return (int) (((work[0] & 0xff) << 8) | (work[1] & 0xff));
return (ch1 << 8) + (ch2 << 0);
} }
public char readChar() { public char readChar() {
int ch1 = read(); this.read(work, 0, 2);
int ch2 = read(); return (char) (((work[0] & 0xff) << 8) | (work[1] & 0xff));
return (char)((ch1 << 8) + (ch2 << 0));
} }
public int readInt() { public int readInt() {
int ch1 = read(); this.read(work, 0, 4);
int ch2 = read(); return ((work[0] & 0xff) << 24) | ((work[1] & 0xff) << 16) |
int ch3 = read(); ((work[2] & 0xff) << 8) | (work[3] & 0xff);
int ch4 = read();
return (ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0);
} }
public long readLong() { public long readLong() {
long rc = ((long)buf[pos++] << 56) + ((long)(buf[pos++] & 255) << 48) + ((long)(buf[pos++] & 255) << 40) + ((long)(buf[pos++] & 255) << 32); this.read(work, 0, 8);
return rc + ((long)(buf[pos++] & 255) << 24) + ((buf[pos++] & 255) << 16) + ((buf[pos++] & 255) << 8) + ((buf[pos++] & 255) << 0);
int i1 = ((work[0] & 0xff) << 24) | ((work[1] & 0xff) << 16) |
((work[2] & 0xff) << 8) | (work[3] & 0xff);
int i2 = ((work[4] & 0xff) << 24) | ((work[5] & 0xff) << 16) |
((work[6] & 0xff) << 8) | (work[7] & 0xff);
return ((i1 & 0xffffffffL) << 32) | (i2 & 0xffffffffL);
} }
public float readFloat() throws IOException { public float readFloat() throws IOException {
@ -262,59 +267,32 @@ public final class DataByteArrayInputStream extends InputStream implements DataI
public String readUTF() throws IOException { public String readUTF() throws IOException {
int length = readUnsignedShort(); int length = readUnsignedShort();
int endPos = pos + length;
int count = 0, a;
char[] characters = new char[length]; char[] characters = new char[length];
int c; while (pos < endPos) {
int c2; if ((characters[count] = (char) buf[pos++]) < '\u0080')
int c3; count++;
int count = 0; else if (((a = characters[count]) & 0xE0) == 0xC0) {
int total = pos + length; if (pos >= endPos) {
while (pos < total) {
c = (int)buf[pos] & 0xff;
if (c > 127) {
break;
}
pos++;
characters[count++] = (char)c;
}
while (pos < total) {
c = (int)buf[pos] & 0xff;
switch (c >> 4) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
pos++;
characters[count++] = (char)c;
break;
case 12:
case 13:
pos += 2;
if (pos > length) {
throw new UTFDataFormatException("bad string"); throw new UTFDataFormatException("bad string");
} }
c2 = (int)buf[pos - 1]; int b = buf[pos++];
if ((c2 & 0xC0) != 0x80) { if ((b & 0xC0) != 0x80) {
throw new UTFDataFormatException("bad string"); throw new UTFDataFormatException("bad string");
} }
characters[count++] = (char)(((c & 0x1F) << 6) | (c2 & 0x3F)); characters[count++] = (char) (((a & 0x1F) << 6) | (b & 0x3F));
break; } else if ((a & 0xf0) == 0xe0) {
case 14: if (pos + 1 >= endPos) {
pos += 3;
if (pos > length) {
throw new UTFDataFormatException("bad string"); throw new UTFDataFormatException("bad string");
} }
c2 = (int)buf[pos - 2]; int b = buf[pos++];
c3 = (int)buf[pos - 1]; int c = buf[pos++];
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) { if (((b & 0xC0) != 0x80) || ((c & 0xC0) != 0x80)) {
throw new UTFDataFormatException("bad string"); throw new UTFDataFormatException("bad string");
} }
characters[count++] = (char)(((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | ((c3 & 0x3F) << 0)); characters[count++] = (char) (((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F));
break; } else {
default:
throw new UTFDataFormatException("bad string"); throw new UTFDataFormatException("bad string");
} }
} }

View File

@ -21,20 +21,22 @@ import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.UTFDataFormatException; import java.io.UTFDataFormatException;
import org.apache.kahadb.page.PageFile;
/** /**
* Optimized ByteArrayOutputStream * Optimized ByteArrayOutputStream
* *
* *
*/ */
public class DataByteArrayOutputStream extends OutputStream implements DataOutput { public class DataByteArrayOutputStream extends OutputStream implements DataOutput {
private static final int DEFAULT_SIZE = 2048; private static final int DEFAULT_SIZE = PageFile.DEFAULT_PAGE_SIZE;
protected byte buf[]; protected byte buf[];
protected int pos; protected int pos;
/** /**
* Creates a new byte array output stream, with a buffer capacity of the * Creates a new byte array output stream, with a buffer capacity of the
* specified size, in bytes. * specified size, in bytes.
* *
* @param size the initial size. * @param size the initial size.
* @exception IllegalArgumentException if size is negative. * @exception IllegalArgumentException if size is negative.
*/ */
@ -54,7 +56,7 @@ public class DataByteArrayOutputStream extends OutputStream implements DataOutpu
/** /**
* start using a fresh byte array * start using a fresh byte array
* *
* @param size * @param size
*/ */
public void restart(int size) { public void restart(int size) {
@ -71,7 +73,7 @@ public class DataByteArrayOutputStream extends OutputStream implements DataOutpu
/** /**
* Get a ByteSequence from the stream * Get a ByteSequence from the stream
* *
* @return the byte sequence * @return the byte sequence
*/ */
public ByteSequence toByteSequence() { public ByteSequence toByteSequence() {
@ -80,9 +82,9 @@ public class DataByteArrayOutputStream extends OutputStream implements DataOutpu
/** /**
* Writes the specified byte to this byte array output stream. * Writes the specified byte to this byte array output stream.
* *
* @param b the byte to be written. * @param b the byte to be written.
* @throws IOException * @throws IOException
*/ */
public void write(int b) throws IOException { public void write(int b) throws IOException {
int newcount = pos + 1; int newcount = pos + 1;
@ -95,11 +97,11 @@ public class DataByteArrayOutputStream extends OutputStream implements DataOutpu
/** /**
* Writes <code>len</code> bytes from the specified byte array starting at * Writes <code>len</code> bytes from the specified byte array starting at
* offset <code>off</code> to this byte array output stream. * offset <code>off</code> to this byte array output stream.
* *
* @param b the data. * @param b the data.
* @param off the start offset in the data. * @param off the start offset in the data.
* @param len the number of bytes to write. * @param len the number of bytes to write.
* @throws IOException * @throws IOException
*/ */
public void write(byte b[], int off, int len) throws IOException { public void write(byte b[], int off, int len) throws IOException {
if (len == 0) { if (len == 0) {
@ -128,9 +130,9 @@ public class DataByteArrayOutputStream extends OutputStream implements DataOutpu
/** /**
* Set the current position for writing * Set the current position for writing
* *
* @param offset * @param offset
* @throws IOException * @throws IOException
*/ */
public void position(int offset) throws IOException { public void position(int offset) throws IOException {
ensureEnoughBuffer(offset); ensureEnoughBuffer(offset);
@ -233,26 +235,18 @@ public class DataByteArrayOutputStream extends OutputStream implements DataOutpu
} }
ensureEnoughBuffer(pos + encodedsize + 2); ensureEnoughBuffer(pos + encodedsize + 2);
writeShort(encodedsize); writeShort(encodedsize);
int i = 0; for (int i = 0; i < strlen; i++) {
for (i = 0; i < strlen; i++) { int charValue = str.charAt(i);
c = str.charAt(i); if (charValue > 0 && charValue <= 127) {
if (!((c >= 0x0001) && (c <= 0x007F))) { buf[pos++] = (byte) charValue;
break; } else if (charValue <= 2047) {
} buf[pos++] = (byte) (0xc0 | (0x1f & (charValue >> 6)));
buf[pos++] = (byte)c; buf[pos++] = (byte) (0x80 | (0x3f & charValue));
}
for (; i < strlen; i++) {
c = str.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
buf[pos++] = (byte)c;
} else if (c > 0x07FF) {
buf[pos++] = (byte)(0xE0 | ((c >> 12) & 0x0F));
buf[pos++] = (byte)(0x80 | ((c >> 6) & 0x3F));
buf[pos++] = (byte)(0x80 | ((c >> 0) & 0x3F));
} else { } else {
buf[pos++] = (byte)(0xC0 | ((c >> 6) & 0x1F)); buf[pos++] = (byte) (0xe0 | (0x0f & (charValue >> 12)));
buf[pos++] = (byte)(0x80 | ((c >> 0) & 0x3F)); buf[pos++] = (byte) (0x80 | (0x3f & (charValue >> 6)));
} buf[pos++] = (byte) (0x80 | (0x3f & charValue));
}
} }
onWrite(); onWrite();
} }
@ -264,10 +258,10 @@ public class DataByteArrayOutputStream extends OutputStream implements DataOutpu
buf = newbuf; buf = newbuf;
} }
} }
/** /**
* This method is called after each write to the buffer. This should allow subclasses * This method is called after each write to the buffer. This should allow subclasses
* to take some action based on the writes, for example flushing data to an external system based on size. * to take some action based on the writes, for example flushing data to an external system based on size.
*/ */
protected void onWrite() throws IOException { protected void onWrite() throws IOException {
} }

View File

@ -28,6 +28,7 @@ import org.apache.kahadb.util.StringMarshaller;
import junit.framework.TestCase; import junit.framework.TestCase;
@SuppressWarnings("rawtypes")
public class PageFileTest extends TestCase { public class PageFileTest extends TestCase {
public void testCRUD() throws IOException { public void testCRUD() throws IOException {

View File

@ -0,0 +1,76 @@
/**
* 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.kahadb.util;
import junit.framework.TestCase;
public class DataByteArrayInputStreamTest extends TestCase {
/**
* https://issues.apache.org/activemq/browse/AMQ-1911
*/
public void testNonAscii() throws Exception {
doMarshallUnMarshallValidation("meißen");
String accumulator = new String();
int test = 0; // int to get Supplementary chars
while(Character.isDefined(test)) {
String toTest = String.valueOf((char)test);
accumulator += toTest;
doMarshallUnMarshallValidation(toTest);
test++;
}
int massiveThreeByteCharValue = 0x0FFF;
String toTest = String.valueOf((char)massiveThreeByteCharValue);
accumulator += toTest;
doMarshallUnMarshallValidation(String.valueOf((char)massiveThreeByteCharValue));
// Altogether
doMarshallUnMarshallValidation(accumulator);
// the three byte values
char t = '\u0800';
final char max = '\uffff';
accumulator = String.valueOf(t);
while (t < max) {
String val = String.valueOf(t);
accumulator += val;
doMarshallUnMarshallValidation(val);
t++;
}
// Altogether so long as it is not too big
while (accumulator.length() > 20000) {
accumulator = accumulator.substring(20000);
}
doMarshallUnMarshallValidation(accumulator);
}
void doMarshallUnMarshallValidation(String value) throws Exception {
DataByteArrayOutputStream out = new DataByteArrayOutputStream();
out.writeBoolean(true);
out.writeUTF(value);
out.close();
DataByteArrayInputStream in = new DataByteArrayInputStream(out.getData());
in.readBoolean();
String readBack = in.readUTF();
assertEquals(value, readBack);
}
}

View File

@ -0,0 +1,100 @@
/**
* 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.kahadb.util;
import java.io.IOException;
import junit.framework.TestCase;
public class DataByteArrayOutputStreamTest extends TestCase {
/**
* This test case assumes that an ArrayIndexOutOfBoundsException will be thrown when the buffer fails to resize
* @throws IOException
*/
public void testResize() throws IOException {
int initSize = 64;
DataByteArrayOutputStream out = new DataByteArrayOutputStream();
fillOut(out, initSize);
// Should resized here
out.writeBoolean(true);
fillOut(out, initSize);
// Should resized here
out.writeByte(1);
fillOut(out, initSize);
// Should resized here
out.writeBytes("test");
fillOut(out, initSize);
// Should resized here
out.writeChar('C');
fillOut(out, initSize);
// Should resized here
out.writeChars("test");
fillOut(out, initSize);
// Should resized here
out.writeDouble(3.1416);
fillOut(out, initSize);
// Should resized here
out.writeFloat((float)3.1416);
fillOut(out, initSize);
// Should resized here
out.writeInt(12345);
fillOut(out, initSize);
// Should resized here
out.writeLong(12345);
fillOut(out, initSize);
// Should resized here
out.writeShort(1234);
fillOut(out, initSize);
// Should resized here
out.writeUTF("test");
fillOut(out, initSize);
// Should resized here
out.write(1234);
fillOut(out, initSize);
// Should resized here
out.write(new byte[10], 5, 5);
fillOut(out, initSize);
// Should resized here
out.write(new byte[10]);
}
/**
* This method restarts the stream to the init size, and fills it up with data
* @param out
* @param size
* @throws IOException
*/
public void fillOut(DataByteArrayOutputStream out, int size) throws IOException {
out.restart(size);
out.write(new byte[size]);
}
}