package com.hundsun.jresplus.common.util.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; /** * 线程不安全,智能收缩的动态byteArray实现outputStrem,使用结束后不是用 close方法关闭流,而应该是reset重置 * * @author LeoHu copy by sagahl copy by fish * */ public final class ByteArrayOutputStream extends OutputStream { private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; /** 每个buffer的长度,缺省为5K */ private int bufferEntrySize = 1024 * 5; private List<byte[]> buffers = new ArrayList<byte[]>(); /** 当前使用的buffer下标. */ private int currentBufferIndex; /** 当前buffer */ private byte[] currentBuffer; /** 已写入的数据长度 */ private int count; /** 每次写的统计 ,统计10次吧 */ private Turn turn = new Turn(10); public ByteArrayOutputStream() { needNewBuffer(); } public ByteArrayOutputStream(int bufferEntrySize) { this.bufferEntrySize = bufferEntrySize; } public int getBufferEntrySize() { return this.bufferEntrySize; } /** * Return the appropriate <code>byte[]</code> buffer specified by index. * * @param index * the index of the buffer required * @return the buffer */ private byte[] getBuffer(int index) { return buffers.get(index); } /** * 得到当前buffer中第一个可写的下标 * * @return */ private int getCurrentBufferPos() { if (count == 0) { return 0; } int pos = count % bufferEntrySize; if (pos == 0) { // 一个buffer用光了 needNewBuffer(); return 0; } return pos; } /** * 需要一个新的buffer entry */ private void needNewBuffer() { if (currentBufferIndex < buffers.size() - 1) { // 还没有未使用的buffer,继续使用老的buffer currentBufferIndex++; currentBuffer = getBuffer(currentBufferIndex); } else { // 创建新buffer byte[] createOne = new byte[bufferEntrySize]; buffers.add(createOne); currentBuffer = createOne; currentBufferIndex++; } } /** * Write the bytes to byte array. * * @param b * the bytes to write * @param off * The start offset * @param len * The number of bytes to write */ public void write(byte[] b, int off, int len) { if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length) || ((off + len) < 0)) { throw new IndexOutOfBoundsException(); } else if (len == 0) { return; } int remaining = len; int inBufferPos = getCurrentBufferPos(); while (remaining > 0) { int part = Math.min(remaining, bufferEntrySize - inBufferPos); System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part); remaining -= part; count += part; if (remaining > 0) { inBufferPos = getCurrentBufferPos(); } } } /** * Write a byte to byte array. * * @param b * the byte to write */ public void write(int b) { int inBufferPos = getCurrentBufferPos(); currentBuffer[inBufferPos] = (byte) b; count++; } /** * Writes the entire contents of the specified input stream to this byte * stream. Bytes from the input stream are read directly into the internal * buffers of this streams. * * @param in * the input stream to read from * @return total number of bytes read from the input stream (and written to * this stream) * @throws IOException * if an I/O error occurs while reading the input stream * @since Commons IO 1.4 */ public int read(InputStream in) throws IOException { int readCount = 0; int inBufferPos = getCurrentBufferPos(); int n = in.read(currentBuffer, inBufferPos, bufferEntrySize - inBufferPos); while (n != -1) { readCount += n; inBufferPos += n; count += n; if (inBufferPos == bufferEntrySize) { needNewBuffer(); inBufferPos = 0; } n = in.read(currentBuffer, inBufferPos, bufferEntrySize - inBufferPos); } return readCount; } /** * Return the current size of the byte array. * * @return the current size of the byte array */ public int size() { return count; } /** * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in * this class can be called after the stream has been closed without * generating an <tt>IOException</tt>. * * @throws IOException * never (this method should not declare this exception but it * has to now due to backwards compatability) */ public void close() throws IOException { // nop } /** * @see java.io.ByteArrayOutputStream#reset() */ public void reset() { int leftBufferCount = turn.addCountGetAvg(); count = 0; currentBufferIndex = 0; currentBuffer = getBuffer(0); curtailBuffer(leftBufferCount); } /** * clear和reset最大的不同是当前大小不参与容量计算 */ public void clear() { count = 0; currentBufferIndex = 0; currentBuffer = getBuffer(0); } /** * 缩减buffers.size到n * * @param n */ private void curtailBuffer(int n) { if (n < 1) { return; } if (this.buffers.size() <= n) { return; } for (int i = this.buffers.size() - 1; i >= n; i--) { this.buffers.remove(i); } } /** * Writes the entire contents of this byte stream to the specified output * stream. * * @param out * the output stream to write to * @throws IOException * if an I/O error occurs, such as if the stream is closed * @see java.io.ByteArrayOutputStream#writeTo(OutputStream) */ public void writeTo(OutputStream out) throws IOException { int remaining = count; for (int i = 0; i < buffers.size(); i++) { byte[] buf = getBuffer(i); int c = Math.min(buf.length, remaining); out.write(buf, 0, c); remaining -= c; if (remaining == 0) { break; } } } /** * Gets the curent contents of this byte stream as a byte array. The result * is independent of this stream. * * @return the current contents of this output stream, as a byte array * @see java.io.ByteArrayOutputStream#toByteArray() */ public byte[] toByteArray() { int remaining = count; if (remaining == 0) { return EMPTY_BYTE_ARRAY; } byte newbuf[] = new byte[remaining]; int pos = 0; for (int i = 0; i < buffers.size(); i++) { byte[] buf = getBuffer(i); int c = Math.min(buf.length, remaining); System.arraycopy(buf, 0, newbuf, pos, c); pos += c; remaining -= c; if (remaining == 0) { break; } } return newbuf; } /** * Gets the curent contents of this byte stream as a string. * * @return the contents of the byte array as a String * @see java.io.ByteArrayOutputStream#toString() */ public String toString() { return new String(toByteArray()); } /** * Gets the curent contents of this byte stream as a string using the * specified encoding. * * @param enc * the name of the character encoding * @return the string converted from the byte array * @throws UnsupportedEncodingException * if the encoding is not supported * @see java.io.ByteArrayOutputStream#toString(String) */ public String toString(String enc) throws UnsupportedEncodingException { return new String(toByteArray(), enc); } /** * 把当前的byte内容转换为InputStream * * @return 线程不安全的Input流 */ public InputStream getInputStream() { return new InputStream() { private int currentPos = 0; private int mark = 0; @Override public int read() throws IOException { if (currentPos == count) { return -1; } int currentBufferIndex = currentPos / bufferEntrySize; int currenPosInBuffer = currentPos % bufferEntrySize; byte cur = buffers.get(currentBufferIndex)[currenPosInBuffer]; currentPos++; return cur & 0xff; } @Override public int available() throws IOException { return count - currentPos; } @Override public synchronized void mark(int readlimit) { mark = currentPos; } @Override public synchronized void reset() throws IOException { currentPos = mark; } @Override public boolean markSupported() { return true; } @Override public long skip(long n) throws IOException { if (currentPos + n > count) { n = count - currentPos; } if (n < 0) { return 0; } currentPos += n; return n; } }; } public interface BytesVisitor { public void visit(byte[] buffer, int length); } public void visitBytes(BytesVisitor visitor) { int lastIndex = count / bufferEntrySize; for (int i = 0; i < lastIndex; i++) { byte[] buffer = this.buffers.get(i); visitor.visit(buffer, buffer.length); } int left = count % bufferEntrySize; if (left > 0) { byte[] buffer = this.buffers.get(lastIndex); visitor.visit(buffer, left); } } /** * 循环队列 * */ private final class Turn { private Fragment currentFg; /** * 创建一个长度为size的循环队列 */ private Turn(int size) { Fragment head = new Fragment(); currentFg = head; for (int i = 0; i < size - 1; i++) { currentFg.next = new Fragment(); currentFg = currentFg.next; } currentFg.next = head; } /** * 增加一个数据并且统计平均值 */ private int addCountGetAvg() { if (count != 0) { currentFg.writeCount = count; currentFg = currentFg.next; } long total = 0L; int usedFragment = 0; Fragment f = currentFg.next; while (f != currentFg) { total += f.writeCount; if (f.writeCount != 0) { usedFragment++; } f = f.next; } if (usedFragment == 0 || total == 0) { return count; } return (int) (total / usedFragment / bufferEntrySize) + 1; } } private static final class Fragment { private int writeCount = 0; private Fragment next; } }