001: // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
002:
003: package jodd.io;
004:
005: import java.io.IOException;
006: import java.io.OutputStream;
007: import java.io.UnsupportedEncodingException;
008: import java.util.List;
009: import java.util.ArrayList;
010:
011: /**
012: * This class implements an output stream in which the data is
013: * written into a byte array. The buffer automatically grows as data
014: * is written to it.
015: * <p>
016: * The data can be retrieved using <code>toByteArray()</code> and
017: * <code>toString()</code>.
018: * <p>
019: * Closing a <code>FastByteArrayOutputStream</code> has no effect. The methods in
020: * this class can be called after the stream has been closed without
021: * generating an <code>IOException</code>.
022: * <p>
023: * This is an alternative implementation of the java.io.FastByteArrayOutputStream
024: * class. The original implementation only allocates 32 bytes at the beginning.
025: * As this class is designed for heavy duty it starts at 1024 bytes. In contrast
026: * to the original it doesn't reallocate the whole memory block but allocates
027: * additional buffers. This way no buffers need to be garbage collected and
028: * the contents don't have to be copied to the new buffer. This class is
029: * designed to behave exactly like the original. The only exception is the
030: * deprecated toString(int) method that has been ignored.
031: *
032: * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
033: */
034: public class FastByteArrayOutputStream extends OutputStream {
035:
036: private List<byte[]> buffers = new ArrayList<byte[]>();
037: private int currentBufferIndex;
038: private int filledBufferSum;
039: private byte[] currentBuffer;
040: private int count;
041:
042: /**
043: * Creates a new byte array output stream. The buffer capacity is
044: * initially 1024 bytes, though its size increases if necessary.
045: */
046: public FastByteArrayOutputStream() {
047: this (1024);
048: }
049:
050: /**
051: * Creates a new byte array output stream, with a buffer capacity of
052: * the specified size, in bytes.
053: *
054: * @param size the initial size.
055: * @throws IllegalArgumentException if size is negative.
056: */
057: public FastByteArrayOutputStream(int size) {
058: if (size < 0) {
059: throw new IllegalArgumentException(
060: "Negative initial size: " + size);
061: }
062: needNewBuffer(size);
063: }
064:
065: private byte[] getBuffer(int index) {
066: return buffers.get(index);
067: }
068:
069: private void needNewBuffer(int newcount) {
070: if (currentBufferIndex < buffers.size() - 1) {
071: //Recycling old buffer
072: filledBufferSum += currentBuffer.length;
073: currentBufferIndex++;
074: currentBuffer = getBuffer(currentBufferIndex);
075: } else {
076: //Creating new buffer
077: int newBufferSize;
078: if (currentBuffer == null) {
079: newBufferSize = newcount;
080: filledBufferSum = 0;
081: } else {
082: newBufferSize = Math.max(currentBuffer.length << 1,
083: newcount - filledBufferSum);
084: filledBufferSum += currentBuffer.length;
085: }
086:
087: currentBufferIndex++;
088: currentBuffer = new byte[newBufferSize];
089: buffers.add(currentBuffer);
090: }
091: }
092:
093: /**
094: * @see java.io.OutputStream#write(byte[], int, int)
095: */
096: @Override
097: public synchronized void write(byte[] b, int off, int len) {
098: if ((off < 0) || (off > b.length) || (len < 0)
099: || ((off + len) > b.length) || ((off + len) < 0)) {
100: throw new IndexOutOfBoundsException();
101: } else if (len == 0) {
102: return;
103: }
104: int newcount = count + len;
105: int remaining = len;
106: int inBufferPos = count - filledBufferSum;
107: while (remaining > 0) {
108: int part = Math.min(remaining, currentBuffer.length
109: - inBufferPos);
110: System.arraycopy(b, off + len - remaining, currentBuffer,
111: inBufferPos, part);
112: remaining -= part;
113: if (remaining > 0) {
114: needNewBuffer(newcount);
115: inBufferPos = 0;
116: }
117: }
118: count = newcount;
119: }
120:
121: /**
122: * Calls the write(byte[]) method.
123: *
124: * @see java.io.OutputStream#write(int)
125: */
126: @Override
127: public synchronized void write(int b) {
128: write(new byte[] { (byte) b }, 0, 1);
129: }
130:
131: /**
132: * @see java.io.ByteArrayOutputStream#size()
133: */
134: public int size() {
135: return count;
136: }
137:
138: /**
139: * Closing a <code>FastByteArrayOutputStream</code> has no effect. The methods in
140: * this class can be called after the stream has been closed without
141: * generating an <code>IOException</code>.
142: */
143: @Override
144: public void close() {
145: //nop
146: }
147:
148: /**
149: * @see java.io.ByteArrayOutputStream#reset()
150: */
151: public synchronized void reset() {
152: count = 0;
153: filledBufferSum = 0;
154: currentBufferIndex = 0;
155: currentBuffer = getBuffer(currentBufferIndex);
156: }
157:
158: /**
159: * @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
160: */
161: public synchronized void writeTo(OutputStream out)
162: throws IOException {
163: int remaining = count;
164: for (int i = 0; i < buffers.size(); i++) {
165: byte[] buf = getBuffer(i);
166: int c = Math.min(buf.length, remaining);
167: out.write(buf, 0, c);
168: remaining -= c;
169: if (remaining == 0) {
170: break;
171: }
172: }
173: }
174:
175: /**
176: * @see java.io.ByteArrayOutputStream#toByteArray()
177: */
178: public synchronized byte toByteArray()[] {
179: int remaining = count;
180: int pos = 0;
181: byte newbuf[] = new byte[count];
182: for (int i = 0; i < buffers.size(); i++) {
183: byte[] buf = getBuffer(i);
184: int c = Math.min(buf.length, remaining);
185: System.arraycopy(buf, 0, newbuf, pos, c);
186: pos += c;
187: remaining -= c;
188: if (remaining == 0) {
189: break;
190: }
191: }
192: return newbuf;
193: }
194:
195: /**
196: * @see java.io.ByteArrayOutputStream#toString()
197: */
198: @Override
199: public String toString() {
200: return new String(toByteArray());
201: }
202:
203: /**
204: * @see java.io.ByteArrayOutputStream#toString(String)
205: */
206: public String toString(String enc)
207: throws UnsupportedEncodingException {
208: return new String(toByteArray(), enc);
209: }
210:
211: }
|