package com.sleepycat.util;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import de.ovgu.cide.jakutil.*;
/** 
 * A replacement for ByteArrayOutputStream that does not synchronize every
 * byte read.
 * <p>This class extends {@link OutputStream} and its <code>write()</code>
 * methods allow it to be used as a standard output stream.  In addition, it
 * provides <code>writeFast()</code> methods that are not declared to throw
 * <code>IOException</code>.  <code>IOException</code> is never thrown by this
 * class.</p>
 * @author Mark Hayes
 */
public class FastOutputStream extends OutputStream {
  /** 
 * The default initial size of the buffer if no initialSize parameter is
 * specified.  This constant is 100 bytes.
 */
  public static final int DEFAULT_INIT_SIZE=100;
  /** 
 * The default amount that the buffer is increased when it is full.  This
 * constant is zero, which means to double the current buffer size.
 */
  public static final int DEFAULT_BUMP_SIZE=0;
  private int len;
  private int bumpLen;
  private byte[] buf;
  private static byte[] ZERO_LENGTH_BYTE_ARRAY=new byte[0];
  /** 
 * Creates an output stream with default sizes.
 */
  public FastOutputStream(){
    initBuffer(DEFAULT_INIT_SIZE,DEFAULT_BUMP_SIZE);
  }
  /** 
 * Creates an output stream with a default bump size and a given initial
 * size.
 * @param initialSize the initial size of the buffer.
 */
  public FastOutputStream(  int initialSize){
    initBuffer(initialSize,DEFAULT_BUMP_SIZE);
  }
  /** 
 * Creates an output stream with a given bump size and initial size.
 * @param initialSize the initial size of the buffer.
 * @param bumpSize the amount to increment the buffer.
 */
  public FastOutputStream(  int initialSize,  int bumpSize){
    initBuffer(initialSize,bumpSize);
  }
  /** 
 * Creates an output stream with a given initial buffer and a default
 * bump size.
 * @param buffer the initial buffer; will be owned by this object.
 */
  public FastOutputStream(  byte[] buffer){
    buf=buffer;
    bumpLen=DEFAULT_BUMP_SIZE;
  }
  /** 
 * Creates an output stream with a given initial buffer and a given
 * bump size.
 * @param buffer the initial buffer; will be owned by this object.
 * @param bumpSize the amount to increment the buffer.  If zero (the
 * default), the current buffer size will be doubled when the buffer is
 * full.
 */
  public FastOutputStream(  byte[] buffer,  int bumpSize){
    buf=buffer;
    bumpLen=bumpSize;
  }
  private void initBuffer(  int bufferSize,  int bumpLen){
    buf=new byte[bufferSize];
    this.bumpLen=bumpLen;
  }
  public int size(){
    return len;
  }
  public void reset(){
    len=0;
  }
  public void write(  int b) throws IOException {
    writeFast(b);
  }
  public void write(  byte[] fromBuf) throws IOException {
    writeFast(fromBuf);
  }
  public void write(  byte[] fromBuf,  int offset,  int length) throws IOException {
    writeFast(fromBuf,offset,length);
  }
  public void writeTo(  OutputStream out) throws IOException {
    out.write(buf,0,len);
  }
  public String toString(){
    return new String(buf,0,len);
  }
  public String toString(  String encoding) throws UnsupportedEncodingException {
    return new String(buf,0,len,encoding);
  }
  public byte[] toByteArray(){
    if (len == 0) {
      return ZERO_LENGTH_BYTE_ARRAY;
    }
 else {
      byte[] toBuf=new byte[len];
      System.arraycopy(buf,0,toBuf,0,len);
      return toBuf;
    }
  }
  /** 
 * Equivalent to <code>write(int)<code> but does not throw
 * <code>IOException</code>.
 * @see #write(int)
 */
  public final void writeFast(  int b){
    if (len + 1 > buf.length)     bump(1);
    buf[len++]=(byte)b;
  }
  /** 
 * Equivalent to <code>write(byte[])<code> but does not throw
 * <code>IOException</code>.
 * @see #write(byte[])
 */
  public final void writeFast(  byte[] fromBuf){
    int needed=len + fromBuf.length - buf.length;
    if (needed > 0)     bump(needed);
    System.arraycopy(fromBuf,0,buf,len,fromBuf.length);
    len+=fromBuf.length;
  }
  /** 
 * Equivalent to <code>write(byte[],int,int)<code> but does not throw
 * <code>IOException</code>.
 * @see #write(byte[],int,int)
 */
  public final void writeFast(  byte[] fromBuf,  int offset,  int length){
    int needed=len + length - buf.length;
    if (needed > 0)     bump(needed);
    System.arraycopy(fromBuf,offset,buf,len,length);
    len+=length;
  }
  /** 
 * Returns the buffer owned by this object.
 * @return the buffer.
 */
  public byte[] getBufferBytes(){
    return buf;
  }
  /** 
 * Returns the offset of the internal buffer.
 * @return always zero currently.
 */
  public int getBufferOffset(){
    return 0;
  }
  /** 
 * Returns the length used in the internal buffer, i.e., the offset at
 * which data will be written next.
 * @return the buffer length.
 */
  public int getBufferLength(){
    return len;
  }
  /** 
 * Ensure that at least the given number of bytes are available in the
 * internal buffer.
 * @param sizeNeeded the number of bytes desired.
 */
  public void makeSpace(  int sizeNeeded){
    int needed=len + sizeNeeded - buf.length;
    if (needed > 0)     bump(needed);
  }
  /** 
 * Skip the given number of bytes in the buffer.
 * @param sizeAdded number of bytes to skip.
 */
  public void addSize(  int sizeAdded){
    len+=sizeAdded;
  }
  private void bump(  int needed){
    int bump=(bumpLen > 0) ? bumpLen : buf.length;
    byte[] toBuf=new byte[buf.length + needed + bump];
    System.arraycopy(buf,0,toBuf,0,len);
    buf=toBuf;
  }
}
