1. /*
  2. * Copyright 2003,2004 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.io.output;
  17. import java.io.IOException;
  18. import java.io.OutputStream;
  19. import java.io.UnsupportedEncodingException;
  20. import java.util.List;
  21. /**
  22. * This class implements an output stream in which the data is
  23. * written into a byte array. The buffer automatically grows as data
  24. * is written to it.
  25. * <p>
  26. * The data can be retrieved using <code>toByteArray()</code> and
  27. * <code>toString()</code>.
  28. * <p>
  29. * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
  30. * this class can be called after the stream has been closed without
  31. * generating an <tt>IOException</tt>.
  32. * <p>
  33. * This is an alternative implementation of the java.io.ByteArrayOutputStream
  34. * class. The original implementation only allocates 32 bytes at the beginning.
  35. * As this class is designed for heavy duty it starts at 1024 bytes. In contrast
  36. * to the original it doesn't reallocate the whole memory block but allocates
  37. * additional buffers. This way no buffers need to be garbage collected and
  38. * the contents don't have to be copied to the new buffer. This class is
  39. * designed to behave exactly like the original. The only exception is the
  40. * deprecated toString(int) method that has been ignored.
  41. * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>
  42. * @version $Id: ByteArrayOutputStream.java,v 1.8 2004/04/24 19:24:09 jeremias Exp $
  43. */
  44. public class ByteArrayOutputStream extends OutputStream {
  45. private List buffers = new java.util.ArrayList();
  46. private int currentBufferIndex;
  47. private int filledBufferSum;
  48. private byte[] currentBuffer;
  49. private int count;
  50. /**
  51. * Creates a new byte array output stream. The buffer capacity is
  52. * initially 1024 bytes, though its size increases if necessary.
  53. */
  54. public ByteArrayOutputStream() {
  55. this(1024);
  56. }
  57. /**
  58. * Creates a new byte array output stream, with a buffer capacity of
  59. * the specified size, in bytes.
  60. *
  61. * @param size the initial size.
  62. * @exception IllegalArgumentException if size is negative.
  63. */
  64. public ByteArrayOutputStream(int size) {
  65. if (size < 0) {
  66. throw new IllegalArgumentException(
  67. "Negative initial size: " + size);
  68. }
  69. needNewBuffer(size);
  70. }
  71. private byte[] getBuffer(int index) {
  72. return (byte[])buffers.get(index);
  73. }
  74. private void needNewBuffer(int newcount) {
  75. if (currentBufferIndex < buffers.size() - 1) {
  76. //Recycling old buffer
  77. filledBufferSum += currentBuffer.length;
  78. currentBufferIndex++;
  79. currentBuffer = getBuffer(currentBufferIndex);
  80. } else {
  81. //Creating new buffer
  82. int newBufferSize;
  83. if (currentBuffer == null) {
  84. newBufferSize = newcount;
  85. filledBufferSum = 0;
  86. } else {
  87. newBufferSize = Math.max(
  88. currentBuffer.length << 1,
  89. newcount - filledBufferSum);
  90. filledBufferSum += currentBuffer.length;
  91. }
  92. currentBufferIndex++;
  93. currentBuffer = new byte[newBufferSize];
  94. buffers.add(currentBuffer);
  95. }
  96. }
  97. /**
  98. * @see java.io.OutputStream#write(byte[], int, int)
  99. */
  100. public synchronized void write(byte[] b, int off, int len) {
  101. if ((off < 0)
  102. || (off > b.length)
  103. || (len < 0)
  104. || ((off + len) > b.length)
  105. || ((off + len) < 0)) {
  106. throw new IndexOutOfBoundsException();
  107. } else if (len == 0) {
  108. return;
  109. }
  110. int newcount = count + len;
  111. int remaining = len;
  112. int inBufferPos = count - filledBufferSum;
  113. while (remaining > 0) {
  114. int part = Math.min(remaining, currentBuffer.length - inBufferPos);
  115. System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos, part);
  116. remaining -= part;
  117. if (remaining > 0) {
  118. needNewBuffer(newcount);
  119. inBufferPos = 0;
  120. }
  121. }
  122. count = newcount;
  123. }
  124. /**
  125. * Calls the write(byte[]) method.
  126. *
  127. * @see java.io.OutputStream#write(int)
  128. */
  129. public synchronized void write(int b) {
  130. write(new byte[] {(byte)b}, 0, 1);
  131. }
  132. /**
  133. * @see java.io.ByteArrayOutputStream#size()
  134. */
  135. public int size() {
  136. return count;
  137. }
  138. /**
  139. * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
  140. * this class can be called after the stream has been closed without
  141. * generating an <tt>IOException</tt>.
  142. * @throws IOException in case an I/O error occurs
  143. */
  144. public void close() throws IOException {
  145. //nop
  146. }
  147. /**
  148. * @see java.io.ByteArrayOutputStream#reset()
  149. */
  150. public synchronized void reset() {
  151. count = 0;
  152. filledBufferSum = 0;
  153. currentBufferIndex = 0;
  154. currentBuffer = getBuffer(currentBufferIndex);
  155. }
  156. /**
  157. * @see java.io.ByteArrayOutputStream#writeTo(OutputStream)
  158. */
  159. public synchronized void writeTo(OutputStream out) throws IOException {
  160. int remaining = count;
  161. for (int i = 0; i < buffers.size(); i++) {
  162. byte[] buf = getBuffer(i);
  163. int c = Math.min(buf.length, remaining);
  164. out.write(buf, 0, c);
  165. remaining -= c;
  166. if (remaining == 0) {
  167. break;
  168. }
  169. }
  170. }
  171. /**
  172. * @see java.io.ByteArrayOutputStream#toByteArray()
  173. */
  174. public synchronized byte toByteArray()[] {
  175. int remaining = count;
  176. int pos = 0;
  177. byte newbuf[] = new byte[count];
  178. for (int i = 0; i < buffers.size(); i++) {
  179. byte[] buf = getBuffer(i);
  180. int c = Math.min(buf.length, remaining);
  181. System.arraycopy(buf, 0, newbuf, pos, c);
  182. pos += c;
  183. remaining -= c;
  184. if (remaining == 0) {
  185. break;
  186. }
  187. }
  188. return newbuf;
  189. }
  190. /**
  191. * @see java.io.ByteArrayOutputStream#toString()
  192. */
  193. public String toString() {
  194. return new String(toByteArray());
  195. }
  196. /**
  197. * @see java.io.ByteArrayOutputStream#toString(String)
  198. */
  199. public String toString(String enc) throws UnsupportedEncodingException {
  200. return new String(toByteArray(), enc);
  201. }
  202. }