Mega Code Archive

 
Categories / Java / File Input Output
 

Implements an output stream in which the data is written into a byte array The buffer automatically grows as data is wri

/*  * Licensed to the Apache Software Foundation (ASF) under one or more  * contributor license agreements.  See the NOTICE file distributed with  * this work for additional information regarding copyright ownership.  * The ASF licenses this file to You under the Apache License, Version 2.0  * (the "License"); you may not use this file except in compliance with  * the License.  You may obtain a copy of the License at  *   *      http://www.apache.org/licenses/LICENSE-2.0  *   * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  * See the License for the specific language governing permissions and  * limitations under the License.  */ //package jacky.lanlan.song.extension.struts.util; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; /**  * This class implements an output stream in which the data is written into a  * byte array. The buffer automatically grows as data is written to it.  * <p>  * The data can be retrieved using <code>toByteArray()</code> and  * <code>toString()</code>.  * <p>  * 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>.  * <p>  * This is an alternative implementation of the java.io.ByteArrayOutputStream  * class. The original implementation only allocates 32 bytes at the beginning.  * As this class is designed for heavy duty it starts at 1024 bytes. In contrast  * to the original it doesn't reallocate the whole memory block but allocates  * additional buffers. This way no buffers need to be garbage collected and the  * contents don't have to be copied to the new buffer. This class is designed to  * behave exactly like the original. The only exception is the deprecated  * toString(int) method that has been ignored.  *   * @author <a href="mailto:jeremias@apache.org">Jeremias Maerki</a>  * @author Holger Hoffstatte  * @version $Id: ByteArrayOutputStream.java 491007 2006-12-29 13:50:34Z  *          scolebourne $  */ public class ByteArrayOutputStream extends OutputStream {   /** A singleton empty byte array. */   private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];   /** The list of buffers, which grows and never reduces. */   private List<byte[]> buffers = new ArrayList<byte[]>();   /** The index of the current buffer. */   private int currentBufferIndex;   /** The total count of bytes in all the filled buffers. */   private int filledBufferSum;   /** The current buffer. */   private byte[] currentBuffer;   /** The total count of bytes written. */   private int count;   /**    * Creates a new byte array output stream. The buffer capacity is initially    * 1024 bytes, though its size increases if necessary.    */   public ByteArrayOutputStream() {     this(1024);   }   /**    * Creates a new byte array output stream, with a buffer capacity of the    * specified size, in bytes.    *     * @param size    *          the initial size    * @throws IllegalArgumentException    *           if size is negative    */   public ByteArrayOutputStream(int size) {     if (size < 0) { throw new IllegalArgumentException(         "Negative initial size: " + size); }     needNewBuffer(size);   }   /**    * 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);   }   /**    * Makes a new buffer available either by allocating a new one or re-cycling    * an existing one.    *     * @param newcount    *          the size of the buffer if one is created    */   private void needNewBuffer(int newcount) {     if (currentBufferIndex < buffers.size() - 1) {       // Recycling old buffer       filledBufferSum += currentBuffer.length;       currentBufferIndex++;       currentBuffer = getBuffer(currentBufferIndex);     }     else {       // Creating new buffer       int newBufferSize;       if (currentBuffer == null) {         newBufferSize = newcount;         filledBufferSum = 0;       }       else {         newBufferSize = Math.max(currentBuffer.length << 1, newcount             - filledBufferSum);         filledBufferSum += currentBuffer.length;       }       currentBufferIndex++;       currentBuffer = new byte[newBufferSize];       buffers.add(currentBuffer);     }   }   /**    * @see java.io.OutputStream#write(byte[], int, int)    */   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; }     synchronized (this) {       int newcount = count + len;       int remaining = len;       int inBufferPos = count - filledBufferSum;       while (remaining > 0) {         int part = Math.min(remaining, currentBuffer.length - inBufferPos);         System.arraycopy(b, off + len - remaining, currentBuffer, inBufferPos,             part);         remaining -= part;         if (remaining > 0) {           needNewBuffer(newcount);           inBufferPos = 0;         }       }       count = newcount;     }   }   /**    * @see java.io.OutputStream#write(int)    */   public synchronized void write(int b) {     int inBufferPos = count - filledBufferSum;     if (inBufferPos == currentBuffer.length) {       needNewBuffer(count + 1);       inBufferPos = 0;     }     currentBuffer[inBufferPos] = (byte) b;     count++;   }   /**    * @see java.io.ByteArrayOutputStream#size()    */   public synchronized 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 synchronized void reset() {     count = 0;     filledBufferSum = 0;     currentBufferIndex = 0;     currentBuffer = getBuffer(currentBufferIndex);   }   /**    * 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 synchronized 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 synchronized 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.    *     * @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);   } }