Last active
February 8, 2016 13:13
-
-
Save Xyene/4971961 to your computer and use it in GitHub Desktop.
Heavily optimized buffered byte array I/O stream. Yes, both.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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