You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by co...@apache.org on 2001/05/21 05:44:38 UTC

cvs commit: jakarta-tomcat/src/share/org/apache/tomcat/util/buf ByteBuffer.java CharBuffer.java

costin      01/05/20 20:44:37

  Added:       src/share/org/apache/tomcat/util/buf ByteBuffer.java
                        CharBuffer.java
  Log:
  The byte and char buffer part of OutputBuffer.
  
  Moved here ( without changing any API in OutputBuffer ) to avoid duplication
  of code, the code will be needed in the encoder.
  
  Revision  Changes    Path
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/buf/ByteBuffer.java
  
  Index: ByteBuffer.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  
  package org.apache.tomcat.util.buf;
  
  import java.io.*;
  
  /**
   *  Wrapper for a resizable byte[].
   *
   *  The buffer can grow to a limit, then will flush to an output channel.
   * 
   *  This is different from ByteChunk - ByteBuffer "owns" the buffer, and can
   *  reallocate it. ByteChunk is just a pointer in an existing buffer, and is
   *  intended for various operations on the buffer ( conversion, search, etc ).
   *
   *  This class is not thred safe ( caller must insure it's used only in a single
   *  thread or synchronize the calls.
   *
   *  You can also use the class in "write through" mode, with everything going
   *  to the output channel ( to avoid double buffering )
   *
   *  Derived from OutputBuffer, JspWriterImpl, BodyContentImpl, StringBuffer with
   *  extra hacks.
   *
   *  @author Costin Manolache
   *  @author Anil K. Vijendran ( JspWriterImpl )
   *  @author Rajiv Mordani ( BodyContentImpl )
   */
  public final class ByteBuffer {
      /**
       *  When we need more space we'll either
       *  grow the buffer ( up to the limit ) or send it to a channel.
       */
      public static interface ByteOutputChannel {
  	/** Send the bytes ( usually the internal conversion buffer ).
  	 *  Expect 8k output if the buffer is full.
  	 */
  	public void realWriteBytes( byte cbuf[], int off, int len) throws IOException;
      }
      
      public static final int DEFAULT_INITIAL=1024;
      private byte buf[];
      // -1: grow undefinitely
      // maximum amount to be cached
      private int limit=-1;
      private boolean directOutput=false;
      
      // next character to be written
      private int pos=0;
      
      private ByteOutputChannel out=null;
      
      public ByteBuffer() {
  	this( DEFAULT_INITIAL );
      }
      
      public ByteBuffer( int initial ) {
  	if( initial > 0 ) {
  	    buf=new byte[ initial ];
  	} else {
  	    // assert out!=null ( will be set later )
  	    buf=new byte[1];
  	    directOutput=true;
  	}
      }
  
      // -------------------- Setup buffer --------------------
      
      /** Maximum amount of data in this buffer.
       *
       *  If -1 or not set, the buffer will grow undefinitely.
       *  Can be smaller than the current buffer size ( which will not shrink ).
       *  When the limit is reached, the buffer will be flushed ( if out is set )
       *  or throw exception.
       */
      public void setLimit(int limit) {
  	this.limit=limit;
      }
      
      public int getLimit() {
  	return limit;
      }
  
      /** When the buffer is full, write the data to the output channel.
       * 	Also used when large amount of data is appended.
       *
       *  If not set, the buffer will grow to the limit.
       */
      public void setByteOutputChannel(ByteOutputChannel out) {
  	this.out=out;
      }
  
      public void recycle() {
  	pos=0;
      }
  
      /** Go directly to the output channel, don't buffer
       */
      public void setDirectOutput(boolean b) {
  	directOutput=b;
      }
      
      // -------------------- Adding data to the buffer --------------------
      
      public void append( char c )
  	throws IOException
      {
  	append( (byte)c);
      }
      
      public void append( byte b )
  	throws IOException
      {
  	// no buffering 
  	if( directOutput ) {
  	    buf[0]=b;
  	    out.realWriteBytes(buf, 0, 1 );
  	    return;
  	}
  
  	makeSpace( 1 );
  
  	// couldn't make space
  	if( pos >= limit ) {
  	    flushBuffer();
  	}
  	buf[pos++]=b;
      }
      
      /** Add data to the buffer
       */
      public void append( byte src[], int off, int len )
  	throws IOException
      {
  	// no buffering 
  	if( directOutput ) {
  	    out.realWriteBytes( src, off, len );
  	    return;
  	}
  
  	// will grow, up to limit
  	makeSpace( len );
  
  	// if we don't have limit: makeSpace can grow as it wants
  	if( limit < 0 ) {
  	    // assert: makeSpace made enough space
  	    System.arraycopy( src, off, buf, pos, len );
  	    pos+=len;
  	    return;
  	}
  	
  	// if we have limit and we're below
  	if( len <= limit - pos ) {
  	    // makeSpace will grow the buffer to the limit,
  	    // so we have space
  	    System.arraycopy( src, off, buf, pos, len );
  	    pos+=len;
  	    return;
  	}
  
  	// need more space than we can afford, need to flush
  	// buffer
  
  	// the buffer is already at ( or bigger than ) limit
  	
  	// Optimization:
  	// If len-avail < length ( i.e. after we fill the buffer with
  	// what we can, the remaining will fit in the buffer ) we'll just
  	// copy the first part, flush, then copy the second part - 1 write
  	// and still have some space for more. We'll still have 2 writes, but
  	// we write more on the first.
  
  	if( len + pos < 2 * limit ) {
  	    /* If the request length exceeds the size of the output buffer,
  	       flush the output buffer and then write the data directly.
  	       We can't avoid 2 writes, but we can write more on the second
  	    */
  	    int avail=limit-pos;
  	    System.arraycopy(src, off, buf, pos, avail);
  	    pos += avail;
  	    
  	    flushBuffer();
  	    
  	    System.arraycopy(src, off+avail, buf, pos, len - avail);
  	    pos+= len - avail;
  	    
  	} else {	// len > buf.length + avail
  	    // long write - flush the buffer and write the rest
  	    // directly from source
  	    flushBuffer();
  	    
  	    out.realWriteBytes( src, off, len );
  	}
      }
  
      public void flushBuffer()
  	throws IOException
      {
  	//assert out!=null
  	if( out==null ) {
  	    throw new IOException( "Buffer overflow, no sink " + limit + " " +
  				   buf.length  );
  	}
  	out.realWriteBytes( buf, 0, pos );
  	pos=0;
      }
  
      /** Make space for len chars. If len is small, allocate
       *	a reserve space too. Never grow bigger than limit.
       */
      private void makeSpace(int count)
      {
  	byte[] tmp = null;
  
  	int newSize;
  	int desiredSize=pos + count;
  
  	// Can't grow above the limit
  	if( limit > 0 &&
  	    desiredSize > limit ) {
  	    desiredSize=limit;
  	}
  
  	// limit < buf.length ( the buffer is already big )
  	// or we already have space
  	if( desiredSize < buf.length ) {
  	    return;
  	}
  	// grow in larger chunks
  	if( desiredSize < 2 * buf.length ) {
  	    newSize= buf.length * 2;
  	    if( limit >0 &&
  		newSize > limit ) newSize=limit;
  	    tmp=new byte[newSize];
  	} else {
  	    newSize= buf.length * 2 + count ;
  	    if( limit > 0 &&
  		newSize > limit ) newSize=limit;
  	    tmp=new byte[newSize];
  	}
  	
  	System.arraycopy(buf, 0, tmp, 0, pos);
  	buf = tmp;
  	tmp = null;
      }
      
      // -------------------- Getting data from the buffer --------------------
  
      /** Return the ( current ) buffer. An append operation may
       *  realocate the buffer ( but this is not a thread-safe class anyway.
       */ 
      public byte[] getBuffer() {
  	return buf;
      }
  
      /** Get the current length of the buffer
       */
      public int getLen() {
  	return pos;
      }
  
      /** The start of the buffer
       */
      public int getOff() {
  	return 0; // no tricks right now - we start from 0
      }
      
      public int getPos() {
  	return pos;
      }
      
      public void setPos( int off ) {
  	this.pos=off;
      }
  }
  
  
  
  1.1                  jakarta-tomcat/src/share/org/apache/tomcat/util/buf/CharBuffer.java
  
  Index: CharBuffer.java
  ===================================================================
  /*
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999 The Apache Software Foundation.  All rights 
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer. 
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:  
   *       "This product includes software developed by the 
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written 
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * [Additional notices, if required by prior licensing conditions]
   *
   */ 
  
  
  package org.apache.tomcat.util.buf;
  
  import java.io.*;
  
  /**
   *  Wrapper for a resizable char[].
   *
   *  The buffer can grow to a limit, then will flush to an output channel.
   * 
   *  This is different from ByteChunk - ByteBuffer "owns" the buffer, and can
   *  reallocate it. ByteChunk is just a pointer in an existing buffer, and is
   *  intended for various operations on the buffer ( conversion, search, etc ).
   *
   *  This class is not thred safe ( caller must insure it's used only in a single
   *  thread or synchronize the calls.
   *
   *  You can also use the class in "write through" mode, with everything going
   *  to the output channel ( to avoid double buffering )
   *
   *  Derived from OutputBuffer, JspWriterImpl, BodyContentImpl, StringBuffer with
   *  extra hacks.
   *
   *  @author Costin Manolache
   *  @author Anil K. Vijendran ( JspWriterImpl )
   *  @author Rajiv Mordani ( BodyContentImpl )
   */
  public final class CharBuffer {
      /**
       *  When we need more space we'll either
       *  grow the buffer ( up to the limit ) or send it to a channel.
       */
      public static interface CharOutputChannel {
  	/** Send the bytes ( usually the internal conversion buffer ).
  	 *  Expect 8k output if the buffer is full.
  	 */
  	public void realWriteChars( char cbuf[], int off, int len) throws IOException;
      }
      
      public static final int DEFAULT_INITIAL=1024;
      private char buf[];
      // next character to be written
      private int pos=0;
  
      // -1: grow undefinitely
      // maximum amount to be cached
      private int limit=-1;
      
      private CharOutputChannel out=null;
      
      public CharBuffer() {
  	this( DEFAULT_INITIAL );
      }
      
      public CharBuffer( int initial ) {
  	buf=new char[ initial ];
      }
  
      // -------------------- Setup buffer --------------------
      
      /** Maximum amount of data in this buffer.
       *
       *  If -1 or not set, the buffer will grow undefinitely.
       *  Can be smaller than the current buffer size ( which will not shrink ).
       *  When the limit is reached, the buffer will be flushed ( if out is set )
       *  or throw exception.
       */
      public void setLimit(int limit) {
  	this.limit=limit;
      }
      
      public int getLimit() {
  	return limit;
      }
  
      /** When the buffer is full, write the data to the output channel.
       * 	Also used when large amount of data is appended.
       *
       *  If not set, the buffer will grow to the limit.
       */
      public void setCharOutputChannel(CharOutputChannel out) {
  	this.out=out;
      }
  
      public void recycle() {
  	pos=0;
      }
  
      // -------------------- Adding data to the buffer --------------------
      
      public void append( char b )
  	throws IOException
      {
  	makeSpace( 1 );
  
  	// couldn't make space
  	if( pos >= limit ) {
  	    flushBuffer();
  	}
  	buf[pos++]=b;
      }
      
      /** Add data to the buffer
       */
      public void append( char src[], int off, int len )
  	throws IOException
      {
  	// will grow, up to limit
  	makeSpace( len );
  
  	// if we don't have limit: makeSpace can grow as it wants
  	if( limit < 0 ) {
  	    // assert: makeSpace made enough space
  	    System.arraycopy( src, off, buf, pos, len );
  	    pos+=len;
  	    return;
  	}
  	
  	// if we have limit and we're below
  	if( len <= limit - pos ) {
  	    // makeSpace will grow the buffer to the limit,
  	    // so we have space
  	    System.arraycopy( src, off, buf, pos, len );
  	    pos+=len;
  	    return;
  	}
  
  	// need more space than we can afford, need to flush
  	// buffer
  
  	// the buffer is already at ( or bigger than ) limit
  	
  	// Optimization:
  	// If len-avail < length ( i.e. after we fill the buffer with
  	// what we can, the remaining will fit in the buffer ) we'll just
  	// copy the first part, flush, then copy the second part - 1 write
  	// and still have some space for more. We'll still have 2 writes, but
  	// we write more on the first.
  
  	if( len + pos < 2 * limit ) {
  	    /* If the request length exceeds the size of the output buffer,
  	       flush the output buffer and then write the data directly.
  	       We can't avoid 2 writes, but we can write more on the second
  	    */
  	    int avail=limit-pos;
  	    System.arraycopy(src, off, buf, pos, avail);
  	    pos += avail;
  	    
  	    flushBuffer();
  	    
  	    System.arraycopy(src, off+avail, buf, pos, len - avail);
  	    pos+= len - avail;
  	    
  	} else {	// len > buf.length + avail
  	    // long write - flush the buffer and write the rest
  	    // directly from source
  	    flushBuffer();
  	    
  	    out.realWriteChars( src, off, len );
  	}
      }
  
  
      /** Add data to the buffer
       */
      public void append( StringBuffer sb )
  	throws IOException
      {
  	int len=sb.length();
  
  	// will grow, up to limit
  	makeSpace( len );
  
  	// if we don't have limit: makeSpace can grow as it wants
  	if( limit < 0 ) {
  	    // assert: makeSpace made enough space
  	    sb.getChars(0, len, buf, pos );
  	    pos+=len;
  	    return;
  	}
  
  	int off=0;
  	int sbOff = off;
  	int sbEnd = off + len;
  	while (sbOff < sbEnd) {
  	    int d = min(limit - pos, sbEnd - sbOff);
  	    sb.getChars( sbOff, sbOff+d, buf, pos);
  	    sbOff += d;
  	    pos += d;
  	    if (pos >= limit)
  		flushBuffer();
  	}
      }
  
      private int min(int a, int b) {
  	if (a < b) return a;
  	return b;
      }
  
      /** Append a string to the buffer
       */
      public void append(String s, int off, int len) throws IOException {
  	if (s==null) return;
  	
  	// will grow, up to limit
  	makeSpace( len );
  
  	// if we don't have limit: makeSpace can grow as it wants
  	if( limit < 0 ) {
  	    // assert: makeSpace made enough space
  	    s.getChars(0, len, buf, pos );
  	    pos+=len;
  	    return;
  	}
  
  	int sOff = off;
  	int sEnd = off + len;
  	while (sOff < sEnd) {
  	    int d = min(limit - pos, sEnd - sOff);
  	    s.getChars( sOff, sOff+d, buf, pos);
  	    sOff += d;
  	    pos += d;
  	    if (pos >= limit)
  		flushBuffer();
  	}
      }
      
      public void flushBuffer()
  	throws IOException
      {
  	//assert out!=null
  	if( out==null ) {
  	    throw new IOException( "Buffer overflow, no sink " + limit + " " +
  				   buf.length  );
  	}
  	out.realWriteChars( buf, 0, pos );
  	pos=0;
      }
  
      /** Make space for len chars. If len is small, allocate
       *	a reserve space too. Never grow bigger than limit.
       */
      private void makeSpace(int count)
      {
  	char[] tmp = null;
  
  	int newSize;
  	int desiredSize=pos + count;
  
  	// Can't grow above the limit
  	if( limit > 0 &&
  	    desiredSize > limit ) {
  	    desiredSize=limit;
  	}
  
  	// limit < buf.length ( the buffer is already big )
  	// or we already have space
  	if( desiredSize < buf.length ) {
  	    return;
  	}
  	// grow in larger chunks
  	if( desiredSize < 2 * buf.length ) {
  	    newSize= buf.length * 2;
  	    if( limit >0 &&
  		newSize > limit ) newSize=limit;
  	    tmp=new char[newSize];
  	} else {
  	    newSize= buf.length * 2 + count ;
  	    if( limit > 0 &&
  		newSize > limit ) newSize=limit;
  	    tmp=new char[newSize];
  	}
  	
  	System.arraycopy(buf, 0, tmp, 0, pos);
  	buf = tmp;
  	tmp = null;
      }
      
      // -------------------- Getting data from the buffer --------------------
  
      /** Return the ( current ) buffer. An append operation may
       *  realocate the buffer ( but this is not a thread-safe class anyway.
       */ 
      public char[] getBuffer() {
  	return buf;
      }
  
      /** Get the current length of the buffer
       */
      public int getLen() {
  	return pos;
      }
  
      /** The start of the buffer
       */
      public int getOff() {
  	return 0; // no tricks right now - we start from 0
      }
      
      public int getPos() {
  	return pos;
      }
      
      public void setPos( int off ) {
  	this.pos=off;
      }
  }