001: /*
002: * <copyright>
003: *
004: * Copyright 2002-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.util;
028:
029: import java.io.IOException;
030: import java.io.OutputStream;
031: import java.io.UnsupportedEncodingException;
032:
033: /**
034: * Replacement for ByteArrayOutputStream which grows a linked list
035: * of multiple byte[]s instead of a single byte[].
036: * <p>
037: * The advantage of this implementation is that it grows in size
038: * more efficiently than a single byte[]. This class also allows
039: * greater control over the growth memory usage by overriding
040: * the "allocLink(int)" method.
041: *
042: * @see java.io.ByteArrayOutputStream
043: */
044: public class LinkedByteOutputStream extends OutputStream {
045:
046: protected static final int DEFAULT_INITIAL_SIZE = 1024;
047:
048: protected final int initialSize;
049:
050: protected int count;
051: protected Link head;
052: protected Link tail;
053:
054: /**
055: * Creates a new linked byte output stream with a minimal buffer
056: * growth increment of the default size (1024 bytes).
057: */
058: public LinkedByteOutputStream() {
059: this (DEFAULT_INITIAL_SIZE);
060: }
061:
062: /**
063: * Creates a new linked byte output stream with the specified
064: * initial size.
065: *
066: * @param size the initial size.
067: */
068: public LinkedByteOutputStream(int size) {
069: this .initialSize = size;
070: if (size < 0) {
071: throw new IllegalArgumentException("Negative size: " + size);
072: }
073: }
074:
075: /**
076: * Writes the specified byte to this linked byte output stream.
077: *
078: * @param b the byte to be written.
079: */
080: public synchronized void write(int b) {
081: byte[] buf;
082: if (tail == null) {
083: Link x = allocLink(1);
084: buf = x.buf;
085: head = x;
086: tail = x;
087: } else {
088: buf = tail.buf;
089: if (tail.offset + 1 > buf.length) {
090: Link x = allocLink(1);
091: buf = x.buf;
092: tail.next = x;
093: tail = x;
094: }
095: }
096: buf[tail.offset++] = (byte) b;
097: ++count;
098: }
099:
100: /**
101: * Writes <code>len</code> bytes from the specified byte array
102: * starting at offset <code>off</code> to this linked byte output
103: * stream.
104: *
105: * @param b the data.
106: * @param off the start offset in the data.
107: * @param len the number of bytes to write.
108: */
109: public void write(byte[] b, int off, int len) {
110: if ((off < 0) || (off > b.length) || (len < 0)
111: || ((off + len) > b.length) || ((off + len) < 0)) {
112: throw new IndexOutOfBoundsException();
113: } else if (len == 0) {
114: return;
115: }
116: synchronized (this ) {
117: if (tail == null) {
118: Link x = allocLink(len);
119: byte[] buf = x.buf;
120: head = x;
121: tail = x;
122: System.arraycopy(b, off, buf, 0, len);
123: tail.offset = len;
124: count = len;
125: } else {
126: byte[] buf = tail.buf;
127: int offset = tail.offset;
128: int length = buf.length;
129: int avail = length - offset;
130: int spill = len - avail;
131: if (spill > 0) {
132: if (avail > 0) {
133: System.arraycopy(b, off, buf, offset, avail);
134: tail.offset = length;
135: }
136: Link x = allocLink(spill);
137: buf = x.buf;
138: tail.next = x;
139: tail = x;
140: System.arraycopy(b, off + avail, buf, 0, spill);
141: tail.offset = spill;
142: } else {
143: System.arraycopy(b, off, buf, offset, len);
144: tail.offset += len;
145: }
146: count += len;
147: }
148: }
149: }
150:
151: /**
152: * Writes the complete contents of this linked byte output stream to
153: * the specified output stream argument, as if by calling the output
154: * stream's write method using <code>out.write(buf, 0, count)</code>.
155: *
156: * @param out the output stream to which to write the data.
157: * @exception IOException if an I/O error occurs.
158: */
159: public synchronized void writeTo(OutputStream out)
160: throws IOException {
161: for (Link curr = head; curr != null; curr = curr.next) {
162: out.write(curr.buf, 0, curr.offset);
163: }
164: }
165:
166: /**
167: * Resets the <code>count</code> field of this linked byte output
168: * stream to zero, so that all currently accumulated output in the
169: * ouput stream is discarded.
170: * <p>
171: * Note that this implementation does not reuse the reclaimed
172: * links. This would require checking the tail for a non-null
173: * next link.
174: */
175: public synchronized void reset() {
176: count = 0;
177: Link x = head;
178: head = null;
179: tail = null;
180: freeLinks(x);
181: }
182:
183: /**
184: * Creates a newly allocated byte array. Its size is the current
185: * size of this output stream and the valid contents of the buffer
186: * have been copied into it.
187: *
188: * @return the current contents of this output stream, as a byte array.
189: * @see #size()
190: */
191: public synchronized byte[] toByteArray() {
192: byte[] newbuf = new byte[count];
193: int i = 0;
194: for (Link curr = head; curr != null; curr = curr.next) {
195: int j = curr.offset;
196: System.arraycopy(curr.buf, 0, newbuf, i, j);
197: i += j;
198: }
199: return newbuf;
200: }
201:
202: /**
203: * @return the number of valid bytes in this output stream.
204: */
205: public int size() {
206: return count;
207: }
208:
209: /**
210: * Converts the buffer's contents into a string, translating bytes into
211: * characters according to the platform's default character encoding.
212: *
213: * @return String translated from the buffer's contents.
214: */
215: public String toString() {
216: return new String(toByteArray());
217: }
218:
219: /**
220: * Converts the buffer's contents into a string, translating bytes into
221: * characters according to the specified character encoding.
222: *
223: * @param enc a character-encoding name.
224: * @return String translated from the buffer's contents.
225: * @throws UnsupportedEncodingException
226: * If the named encoding is not supported.
227: */
228: public String toString(String enc)
229: throws UnsupportedEncodingException {
230: return new String(toByteArray(), enc);
231: }
232:
233: /**
234: * Closing a <tt>LinkedByteOutputStream</tt> has no effect. The methods in
235: * this class can be called after the stream has been closed without
236: * generating an <tt>IOException</tt>.
237: */
238: public void close() throws IOException {
239: }
240:
241: /**
242: * Construct a new link with at least the specified
243: * capacity.
244: * <p>
245: * The default implementation works like array list,
246: * growing from the initial size by (3/2).
247: * <p>
248: * This is here to support subclassing:<ol>
249: * <li>Support more complex growth algorithms.</li>
250: * <li>Support future link pooling<li>
251: * </ol>
252: */
253: protected Link allocLink(int minIncrement) {
254: int oldCapacity = count;
255: int newIncrement;
256: if (oldCapacity < initialSize) {
257: newIncrement = initialSize;
258: } else {
259: int newCapacity = (oldCapacity * 3) / 2 + 1;
260: newIncrement = newCapacity - oldCapacity;
261: }
262: if (newIncrement < minIncrement)
263: newIncrement = minIncrement;
264: return new Link(newIncrement);
265: }
266:
267: /**
268: * Free all links in the linked list, starting with the
269: * specified link.
270: * <p>
271: * This is here to support future link pooling. If you
272: * implement this method, consider implementing "finalize()".
273: */
274: protected void freeLinks(Link first) {
275: }
276:
277: /**
278: * Inner class for each link in the linked list.
279: */
280: protected static class Link {
281: // non-null byte buffer:
282: public final byte[] buf;
283: // index between 0 and buf.length:
284: public int offset;
285: // next link, null if (offset < buf.length):
286: public Link next;
287:
288: // constructor:
289: public Link(int len) {
290: this .buf = new byte[len];
291: }
292:
293: // developer debug:
294: public String toString() {
295: return "{" + offset + "/" + buf.length + "}["
296: + new String(buf) + "]"
297: + (next != null ? next.toString() : "");
298: }
299: }
300:
301: }
|