/**********************************************************\ | | | hprose | | | | Official WebSite: http://www.hprose.com/ | | http://www.hprose.org/ | | | \**********************************************************/ /**********************************************************\ * * * ByteBufferStream.java * * * * ByteBuffer Stream for Java. * * * * LastModified: Jul 15, 2015 * * Author: Ma Bingyao <andot@hprose.com> * * * \**********************************************************/ package hprose.io; import hprose.common.HproseException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; public final class ByteBufferStream { final static class ByteBufferPool { private final int POOLNUM = 12; private final int POOLSIZE = 32; private final ByteBuffer[][] pool = new ByteBuffer[POOLNUM][]; private final int[] position = new int[POOLNUM]; private final static int[] debruijn = new int[] { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 }; private static int log2(int x) { return debruijn[(x & -x) * 0x077CB531 >>> 27]; } private ByteBufferPool() { for (int i = 0; i < POOLNUM; ++i) { pool[i] = new ByteBuffer[POOLSIZE]; position[i] = -1; } } private ByteBuffer allocate(int capacity) { capacity = pow2roundup(capacity); if (capacity < 1024) { return ByteBuffer.allocate(capacity); } int index = log2(capacity) - 10; if (index < POOLNUM && position[index] >= 0) { int pos = position[index]; ByteBuffer byteBuffer = pool[index][pos]; pool[index][pos] = null; position[index] = pos - 1; if (byteBuffer != null) return byteBuffer; } return ByteBuffer.allocateDirect(capacity); } private void free(ByteBuffer buffer) { if (buffer.isDirect()) { int capacity = buffer.capacity(); if (capacity == pow2roundup(capacity)) { buffer.clear(); int index = log2(capacity) - 10; if (index >= 0 && index < POOLNUM) { int pos = position[index]; if (pos < POOLSIZE - 1) { pool[index][++pos] = buffer; position[index] = pos; } } } } } } private final static ThreadLocal<ByteBufferPool> byteBufferPool = new ThreadLocal<ByteBufferPool>() { @Override protected ByteBufferPool initialValue() { return new ByteBufferPool(); } }; public ByteBuffer buffer; InputStream istream; OutputStream ostream; private static int pow2roundup(int x) { --x; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; return x + 1; } public final static ByteBuffer allocate(int capacity) { return byteBufferPool.get().allocate(capacity); } public final static void free(ByteBuffer buffer) { byteBufferPool.get().free(buffer); } public ByteBufferStream() { this(1024); } public ByteBufferStream(int capacity) { buffer = allocate(capacity); } public ByteBufferStream(ByteBuffer buffer) { this.buffer = buffer; } public final static ByteBufferStream wrap(byte[] array, int offset, int length) { return new ByteBufferStream(ByteBuffer.wrap(array, offset, length)); } public final static ByteBufferStream wrap(byte[] array) { return new ByteBufferStream(ByteBuffer.wrap(array)); } public final void close() { if (buffer != null) { free(buffer); buffer = null; } } public final InputStream getInputStream() { if (istream == null) { istream = new ByteBufferInputStream(this); } return istream; } public final OutputStream getOutputStream() { if (ostream == null) { ostream = new ByteBufferOutputStream(this); } return ostream; } public final int read() { if (buffer.hasRemaining()) { return buffer.get() & 0xff; } else { return -1; } } public final int read(byte b[]) { return read(b, 0, b.length); } public final int read(byte b[], int off, int len) { if (len <= 0) { return 0; } int remain = buffer.remaining(); if (remain <= 0) { return -1; } if (len >= remain) { buffer.get(b, off, remain); return remain; } buffer.get(b, off, len); return len; } public final int read(ByteBuffer b) { int len = b.remaining(); if (len <= 0) { return 0; } int remain = buffer.remaining(); if (remain <= 0) { return -1; } if (len >= remain) { b.put(buffer); return remain; } int oldlimit = buffer.limit(); buffer.limit(buffer.position() + len); b.put(buffer); buffer.limit(oldlimit); return len; } public final long skip(long n) { if (n <= 0) { return 0; } int remain = buffer.remaining(); if (remain <= 0) { return 0; } if (n > remain) { buffer.position(buffer.limit()); return remain; } buffer.position(buffer.position() + (int) n); return n; } public final int available() { return buffer.remaining(); } public final boolean markSupported() { return true; } public final void mark(int readlimit) { buffer.mark(); } public final void reset() { buffer.reset(); } private void grow(int n) { if (buffer.remaining() < n) { int required = buffer.position() + n; if (required > buffer.capacity()) { int size = pow2roundup(required); ByteBuffer buf = allocate(size); buffer.flip(); buf.put(buffer); free(buffer); buffer = buf; } else { buffer.limit(required); } } } public final void write(int b) { grow(1); buffer.put((byte) b); } public final void write(byte b[]) { write(b, 0, b.length); } public final void write(byte b[], int off, int len) { grow(len); buffer.put(b, off, len); } public final void write(ByteBuffer b) { grow(b.remaining()); buffer.put(b); } public final void flip() { if (buffer.position() != 0) { buffer.flip(); } } public final void rewind() { buffer.rewind(); } public final byte[] toArray() { flip(); byte[] data = new byte[buffer.limit()]; buffer.get(data); return data; } public final void readFrom(InputStream istream) throws IOException { byte[] b = new byte[8192]; for (;;) { int n = istream.read(b); if (n == -1) { break; } write(b, 0, n); } } public final void writeTo(OutputStream ostream) throws IOException { if (buffer.hasArray()) { ostream.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining()); } else { byte[] b = new byte[8192]; for (;;) { int n = read(b); if (n == -1) { break; } ostream.write(b, 0, n); } } } public final void readFrom(ByteChannel channel, int length) throws IOException { int n = 0; grow(length); buffer.limit(buffer.position() + length); while (n < length) { int nn = channel.read(buffer); if (nn == -1) { break; } n += nn; } if (n < length) { throw new HproseException("Unexpected EOF"); } } public final void writeTo(ByteChannel channel) throws IOException { while (buffer.hasRemaining()) { channel.write(buffer); } } }