Skip to content

Instantly share code, notes, and snippets.

@Xyene
Last active February 8, 2016 13:13
Show Gist options
  • Save Xyene/4971961 to your computer and use it in GitHub Desktop.
Save Xyene/4971961 to your computer and use it in GitHub Desktop.
Heavily optimized buffered byte array I/O stream. Yes, both.
package net.sf.jbl.util;
import java.util.Arrays;
/**
* A one-size-fit-all IO stream
*/
public class ByteStream {
protected byte[] out;
protected byte[] in;
protected int pos = 0;
protected byte flag;
protected final static String err = "stream ended prematurely";
private static final byte READ = 0, WRITE = 1;
private int length;
ByteStream(byte flag) {
this.flag = flag;
}
public static ByteStream writeStream(int buffer) {
ByteStream stream = new ByteStream(WRITE);
stream.out = new byte[buffer];//new DataOutputStream(stream.array = new ByteArrayOutputStream(buffer));
return stream;
}
public static ByteStream writeStream() {
return writeStream(1024);
}
public static ByteStream readStream(byte[] bytes, int pos) {
ByteStream stream = new ByteStream(READ);
stream.pos = pos;
stream.in = bytes;
return stream;
}
public static ByteStream readStream(byte[] bytes) {
return readStream(bytes, 0);
}
public boolean isRead() {
return flag == READ;
}
public boolean isWrite() {
return flag == WRITE;
}
protected void ensureWrite() {
if (!isWrite()) throw new IllegalStateException("WRITE flag is not set");
}
protected void ensureRead() {
if (!isRead()) throw new IllegalStateException("READ flag is not set");
}
/**
* Enlarge this byte vector so that it can receive n more bytes.
*
* @param size number of additional bytes that this byte vector should be
* able to receive.
*/
private void enlarge(int size) {
int length1 = out.length << 1;
int length2 = length + size;
byte[] newData = new byte[length1 > length2 ? length1 : length2];
System.arraycopy(out, 0, newData, 0, length);
out = newData;
}
public ByteStream writeInt(final int i) {
int length = this.length;
if (length + 4 > out.length) {
enlarge(4);
}
byte[] data = this.out;
data[length++] = (byte) (i >>> 24);
data[length++] = (byte) (i >>> 16);
data[length++] = (byte) (i >>> 8);
data[length++] = (byte) i;
this.length = length;
return this;
}
public ByteStream writeLong(final long l) {
writeInt((int) (l >>> 32));
writeInt((int) l);
return this;
}
public ByteStream writeDouble(double d) {
return writeLong(Double.doubleToLongBits(d));
}
public ByteStream writeUTF(final String s) {
writeShort(s.length());
int charLength = s.length();
int len = length;
if (len + 2 + charLength > out.length) {
enlarge(2 + charLength);
}
byte[] data = this.out;
// optimistic algorithm: instead of computing the byte length and then
// serializing the string (which requires two loops), we assume the byte
// length is equal to char length (which is the most frequent case), and
// we start serializing the string right away. During the serialization,
// if we find that this assumption is wrong, we continue with the
// general method.
data[len++] = (byte) (charLength >>> 8);
data[len++] = (byte) charLength;
for (int i = 0; i < charLength; ++i) {
char c = s.charAt(i);
if (c >= '\001' && c <= '\177') {
data[len++] = (byte) c;
} else {
int byteLength = i;
for (int j = i; j < charLength; ++j) {
c = s.charAt(j);
if (c >= '\001' && c <= '\177') {
byteLength++;
} else if (c > '\u07FF') {
byteLength += 3;
} else {
byteLength += 2;
}
}
data[length] = (byte) (byteLength >>> 8);
data[length + 1] = (byte) byteLength;
if (length + 2 + byteLength > data.length) {
length = len;
enlarge(2 + byteLength);
data = this.out;
}
for (int j = i; j < charLength; ++j) {
c = s.charAt(j);
if (c >= '\001' && c <= '\177') {
data[len++] = (byte) c;
} else if (c > '\u07FF') {
data[len++] = (byte) (0xE0 | c >> 12 & 0xF);
data[len++] = (byte) (0x80 | c >> 6 & 0x3F);
data[len++] = (byte) (0x80 | c & 0x3F);
} else {
data[len++] = (byte) (0xC0 | c >> 6 & 0x1F);
data[len++] = (byte) (0x80 | c & 0x3F);
}
}
break;
}
}
length = len;
return this;
}
public ByteStream writeShort(int s) {
int length = this.length;
if (length + 2 > out.length) {
enlarge(2);
}
byte[] data = this.out;
data[length++] = (byte) (s >>> 8);
data[length++] = (byte) s;
this.length = length;
return this;
}
public ByteStream writeByte(int b) {
int length = this.length;
if (length + 2 > out.length) {
enlarge(2);
}
out[length++] = (byte) b;
this.length = length;
return this;
}
public ByteStream writeBytes(final byte[] b) {
if (length + b.length > out.length) {
enlarge(b.length);
}
System.arraycopy(b, 0, out, length, b.length);
length += b.length;
return this;
}
public ByteStream forkWrite(int from, int to) {
int size = to - from;
ByteStream out = writeStream(size);
System.arraycopy(in, from, out.out, 0, size);
return out;
}
public boolean readBoolean() {
return readByte() == 1 ? true : false;
}
public int readUnsignedByte() {
ensureRead();
return in[pos++];
}
public short readShort() {
ensureRead();
byte[] b = this.in;
return (short) (((b[pos++] & 0xFF) << 8) | (b[pos++] & 0xFF));
}
public int readUnsignedShort() {
ensureRead();
byte[] b = this.in;
return ((b[pos++] & 0xFF) << 8) | (b[pos++] & 0xFF);
}
public int readInt() {
ensureRead();
byte[] b = this.in;
return ((b[pos++] & 0xFF) << 24) | ((b[pos++] & 0xFF) << 16)
| ((b[pos++] & 0xFF) << 8) | (b[pos++] & 0xFF);
}
public long readLong() {
return (readInt() << 32) | readInt() & 0xFFFFFFFFL;
}
public float readFloat() {
return Float.intBitsToFloat(readInt());
}
public double readDouble() {
return Double.longBitsToDouble(readLong());
}
public byte readByte() {
ensureRead();
return (byte) (in[pos++] & 0xFF);
}
public byte[] read(int n) {
ensureRead();
byte[] arr = new byte[n];
System.arraycopy(in, pos, arr, 0, n);
pos += n;
return arr;
}
public byte[] toByteArray() {
return out;
}
public int position() {
return pos;
}
public String toString() {
return "{ByteStream" + Arrays.toString(out) + "}";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment