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 2005/10/28 06:11:48 UTC

svn commit: r329081 [1/2] - /tomcat/sandbox/java/org/apache/tomcat/util/buf/

Author: costin
Date: Thu Oct 27 21:11:42 2005
New Revision: 329081

URL: http://svn.apache.org/viewcvs?rev=329081&view=rev
Log:
Import the current version of o.a.t.util.buf, for refactoring. I'm
trying to use nio.

Added:
    tomcat/sandbox/java/org/apache/tomcat/util/buf/
    tomcat/sandbox/java/org/apache/tomcat/util/buf/Ascii.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/B2CConverter.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/Base64.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/ByteChunk.java   (with props)
    tomcat/sandbox/java/org/apache/tomcat/util/buf/C2BConverter.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/CharChunk.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/DateTool.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/HexUtils.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/MessageBytes.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/StringCache.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/TimeStamp.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/UDecoder.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/UEncoder.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/UTF8Decoder.java
    tomcat/sandbox/java/org/apache/tomcat/util/buf/package.html

Added: tomcat/sandbox/java/org/apache/tomcat/util/buf/Ascii.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/tomcat/util/buf/Ascii.java?rev=329081&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/buf/Ascii.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/buf/Ascii.java Thu Oct 27 21:11:42 2005
@@ -0,0 +1,246 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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 org.apache.tomcat.util.buf;
+
+/**
+ * This class implements some basic ASCII character handling functions.
+ *
+ * @author dac@eng.sun.com
+ * @author James Todd [gonzo@eng.sun.com]
+ */
+public final class Ascii {
+    /*
+     * Character translation tables.
+     */
+
+    private static final byte[] toUpper = new byte[256];
+    private static final byte[] toLower = new byte[256];
+
+    /*
+     * Character type tables.
+     */
+
+    private static final boolean[] isAlpha = new boolean[256];
+    private static final boolean[] isUpper = new boolean[256];
+    private static final boolean[] isLower = new boolean[256];
+    private static final boolean[] isWhite = new boolean[256];
+    private static final boolean[] isDigit = new boolean[256];
+
+    /*
+     * Initialize character translation and type tables.
+     */
+
+    static {
+        for (int i = 0; i < 256; i++) {
+            toUpper[i] = (byte)i;
+            toLower[i] = (byte)i;
+        }
+
+        for (int lc = 'a'; lc <= 'z'; lc++) {
+            int uc = lc + 'A' - 'a';
+
+            toUpper[lc] = (byte)uc;
+            toLower[uc] = (byte)lc;
+            isAlpha[lc] = true;
+            isAlpha[uc] = true;
+            isLower[lc] = true;
+            isUpper[uc] = true;
+        }
+
+        isWhite[ ' '] = true;
+        isWhite['\t'] = true;
+        isWhite['\r'] = true;
+        isWhite['\n'] = true;
+        isWhite['\f'] = true;
+        isWhite['\b'] = true;
+
+        for (int d = '0'; d <= '9'; d++) {
+            isDigit[d] = true;
+        }
+    }
+
+    /**
+     * Returns the upper case equivalent of the specified ASCII character.
+     */
+
+    public static int toUpper(int c) {
+        return toUpper[c & 0xff] & 0xff;
+    }
+
+    /**
+     * Returns the lower case equivalent of the specified ASCII character.
+     */
+
+    public static int toLower(int c) {
+        return toLower[c & 0xff] & 0xff;
+    }
+
+    /**
+     * Returns true if the specified ASCII character is upper or lower case.
+     */
+
+    public static boolean isAlpha(int c) {
+        return isAlpha[c & 0xff];
+    }
+
+    /**
+     * Returns true if the specified ASCII character is upper case.
+     */
+
+    public static boolean isUpper(int c) {
+        return isUpper[c & 0xff];
+    }
+
+    /**
+     * Returns true if the specified ASCII character is lower case.
+     */
+
+    public static boolean isLower(int c) {
+        return isLower[c & 0xff];
+    }
+
+    /**
+     * Returns true if the specified ASCII character is white space.
+     */
+
+    public static boolean isWhite(int c) {
+        return isWhite[c & 0xff];
+    }
+
+    /**
+     * Returns true if the specified ASCII character is a digit.
+     */
+
+    public static boolean isDigit(int c) {
+        return isDigit[c & 0xff];
+    }
+
+    /**
+     * Parses an unsigned integer from the specified subarray of bytes.
+     * @param b the bytes to parse
+     * @param off the start offset of the bytes
+     * @param len the length of the bytes
+     * @exception NumberFormatException if the integer format was invalid
+     */
+    public static int parseInt(byte[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        int n = c - '0';
+
+        while (--len > 0) {
+            if (!isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            n = n * 10 + c - '0';
+        }
+
+        return n;
+    }
+
+    public static int parseInt(char[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        int n = c - '0';
+
+        while (--len > 0) {
+            if (!isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            n = n * 10 + c - '0';
+        }
+
+        return n;
+    }
+
+    /**
+     * Parses an unsigned long from the specified subarray of bytes.
+     * @param b the bytes to parse
+     * @param off the start offset of the bytes
+     * @param len the length of the bytes
+     * @exception NumberFormatException if the long format was invalid
+     */
+    public static long parseLong(byte[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        long n = c - '0';
+        long m;
+        
+        while (--len > 0) {
+            if (!isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            m = n * 10 + c - '0';
+
+            if (m < n) {
+                // Overflow
+                throw new NumberFormatException();
+            } else {
+                n = m;
+            }
+        }
+
+        return n;
+    }
+
+    public static long parseLong(char[] b, int off, int len)
+        throws NumberFormatException
+    {
+        int c;
+
+        if (b == null || len <= 0 || !isDigit(c = b[off++])) {
+            throw new NumberFormatException();
+        }
+
+        long n = c - '0';
+        long m;
+
+        while (--len > 0) {
+            if (!isDigit(c = b[off++])) {
+                throw new NumberFormatException();
+            }
+            m = n * 10 + c - '0';
+
+            if (m < n) {
+                // Overflow
+                throw new NumberFormatException();
+            } else {
+                n = m;
+            }
+        }
+
+        return n;
+    }
+
+}

Added: tomcat/sandbox/java/org/apache/tomcat/util/buf/B2CConverter.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/tomcat/util/buf/B2CConverter.java?rev=329081&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/buf/B2CConverter.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/buf/B2CConverter.java Thu Oct 27 21:11:42 2005
@@ -0,0 +1,273 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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 org.apache.tomcat.util.buf;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+
+/** Efficient conversion of bytes  to character .
+ *  
+ *  This uses the standard JDK mechansim - a reader - but provides mechanisms
+ *  to recycle all the objects that are used. It is compatible with JDK1.1
+ *  and up,
+ *  ( nio is better, but it's not available even in 1.2 or 1.3 )
+ *
+ *  Not used in the current code, the performance gain is not very big
+ *  in the current case ( since String is created anyway ), but it will
+ *  be used in a later version or after the remaining optimizations.
+ */
+public class B2CConverter {
+    
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( B2CConverter.class );
+    
+    private IntermediateInputStream iis;
+    private ReadConvertor conv;
+    private String encoding;
+
+    protected B2CConverter() {
+    }
+    
+    /** Create a converter, with bytes going to a byte buffer
+     */
+    public B2CConverter(String encoding)
+	throws IOException
+    {
+	this.encoding=encoding;
+	reset();
+    }
+
+    
+    /** Reset the internal state, empty the buffers.
+     *  The encoding remain in effect, the internal buffers remain allocated.
+     */
+    public  void recycle() {
+	conv.recycle();
+    }
+
+    static final int BUFFER_SIZE=8192;
+    char result[]=new char[BUFFER_SIZE];
+
+    /** Convert a buffer of bytes into a chars
+     */
+    public  void convert( ByteChunk bb, CharChunk cb )
+	throws IOException
+    {
+	// Set the ByteChunk as input to the Intermediate reader
+	iis.setByteChunk( bb );
+	convert(cb);
+    }
+
+    private void convert(CharChunk cb)
+	throws IOException
+    {
+	try {
+	    // read from the reader
+	    while( true ) { // conv.ready() ) {
+		int cnt=conv.read( result, 0, BUFFER_SIZE );
+		if( cnt <= 0 ) {
+		    // End of stream ! - we may be in a bad state
+		    if( debug>0)
+			log( "EOF" );
+		    //		    reset();
+		    return;
+		}
+		if( debug > 1 )
+		    log("Converted: " + new String( result, 0, cnt ));
+
+		// XXX go directly
+		cb.append( result, 0, cnt );
+	    }
+	} catch( IOException ex) {
+	    if( debug>0)
+		log( "Reseting the converter " + ex.toString() );
+	    reset();
+	    throw ex;
+	}
+    }
+
+    public void reset()
+	throws IOException
+    {
+	// destroy the reader/iis
+	iis=new IntermediateInputStream();
+	conv=new ReadConvertor( iis, encoding );
+    }
+
+    private final int debug=0;
+    void log( String s ) {
+        if (log.isDebugEnabled())
+            log.debug("B2CConverter: " + s );
+    }
+
+    // -------------------- Not used - the speed improvemnt is quite small
+
+    /*
+    private Hashtable decoders;
+    public static final boolean useNewString=false;
+    public static final boolean useSpecialDecoders=true;
+    private UTF8Decoder utfD;
+    // private char[] conversionBuff;
+    CharChunk conversionBuf;
+
+
+    private  static String decodeString(ByteChunk mb, String enc)
+	throws IOException
+    {
+	byte buff=mb.getBuffer();
+	int start=mb.getStart();
+	int end=mb.getEnd();
+	if( useNewString ) {
+	    if( enc==null) enc="UTF8";
+	    return new String( buff, start, end-start, enc );
+	}
+	B2CConverter b2c=null;
+	if( useSpecialDecoders &&
+	    (enc==null || "UTF8".equalsIgnoreCase(enc))) {
+	    if( utfD==null ) utfD=new UTF8Decoder();
+	    b2c=utfD;
+	}
+	if(decoders == null ) decoders=new Hashtable();
+	if( enc==null ) enc="UTF8";
+	b2c=(B2CConverter)decoders.get( enc );
+	if( b2c==null ) {
+	    if( useSpecialDecoders ) {
+		if( "UTF8".equalsIgnoreCase( enc ) ) {
+		    b2c=new UTF8Decoder();
+		}
+	    }
+	    if( b2c==null )
+		b2c=new B2CConverter( enc );
+	    decoders.put( enc, b2c );
+	}
+	if( conversionBuf==null ) conversionBuf=new CharChunk(1024);
+
+	try {
+	    conversionBuf.recycle();
+	    b2c.convert( this, conversionBuf );
+	    //System.out.println("XXX 1 " + conversionBuf );
+	    return conversionBuf.toString();
+	} catch( IOException ex ) {
+	    ex.printStackTrace();
+	    return null;
+	}
+    }
+
+    */
+}
+
+// -------------------- Private implementation --------------------
+
+
+
+/**
+ * 
+ */
+final class  ReadConvertor extends InputStreamReader {
+    // stream with flush() and close(). overriden.
+    private IntermediateInputStream iis;
+    
+    // Has a private, internal byte[8192]
+    
+    /** Create a converter.
+     */
+    public ReadConvertor( IntermediateInputStream in, String enc )
+	throws UnsupportedEncodingException
+    {
+	super( in, enc );
+	iis=in;
+    }
+    
+    /** Overriden - will do nothing but reset internal state.
+     */
+    public  final void close() throws IOException {
+	// NOTHING
+	// Calling super.close() would reset out and cb.
+    }
+    
+    public  final int read(char cbuf[], int off, int len)
+	throws IOException
+    {
+	// will do the conversion and call write on the output stream
+	return super.read( cbuf, off, len );
+    }
+    
+    /** Reset the buffer
+     */
+    public  final void recycle() {
+    }
+}
+
+
+/** Special output stream where close() is overriden, so super.close()
+    is never called.
+    
+    This allows recycling. It can also be disabled, so callbacks will
+    not be called if recycling the converter and if data was not flushed.
+*/
+final class IntermediateInputStream extends InputStream {
+    byte buf[];
+    int pos;
+    int len;
+    int end;
+    
+    public IntermediateInputStream() {
+    }
+    
+    public  final void close() throws IOException {
+	// shouldn't be called - we filter it out in writer
+	throw new IOException("close() called - shouldn't happen ");
+    }
+    
+    public  final  int read(byte cbuf[], int off, int len) throws IOException {
+	if( pos >= end ) return -1;
+	if (pos + len > end) {
+	    len = end - pos;
+	}
+	if (len <= 0) {
+	    return 0;
+	}
+	System.arraycopy(buf, pos, cbuf, off, len);
+	pos += len;
+	return len;
+    }
+    
+    public  final int read() throws IOException {
+	return (pos < end ) ? (buf[pos++] & 0xff) : -1;
+    }
+
+    // -------------------- Internal methods --------------------
+
+    void setBuffer( byte b[], int p, int l ) {
+	buf=b;
+	pos=p;
+	len=l;
+	end=pos+len;
+    }
+
+    void setByteChunk( ByteChunk mb ) {
+	buf=mb.getBytes();
+	pos=mb.getStart();
+	len=mb.getLength();
+	end=pos+len;
+    }
+
+}

Added: tomcat/sandbox/java/org/apache/tomcat/util/buf/Base64.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/tomcat/util/buf/Base64.java?rev=329081&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/buf/Base64.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/buf/Base64.java Thu Oct 27 21:11:42 2005
@@ -0,0 +1,268 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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 org.apache.tomcat.util.buf;
+
+
+/**
+ * This class provides encode/decode for RFC 2045 Base64 as
+ * defined by RFC 2045, N. Freed and N. Borenstein.
+ * RFC 2045: Multipurpose Internet Mail Extensions (MIME)
+ * Part One: Format of Internet Message Bodies. Reference
+ * 1996 Available at: http://www.ietf.org/rfc/rfc2045.txt
+ * This class is used by XML Schema binary format validation
+ *
+ * @author Jeffrey Rodriguez
+ * @version $Revision: 299789 $ $Date: 2004-09-17 11:34:19 -0700 (Fri, 17 Sep 2004) $
+ */
+
+public final class Base64 {
+
+
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog( Base64.class );
+    
+    static private final int  BASELENGTH         = 255;
+    static private final int  LOOKUPLENGTH       = 63;
+    static private final int  TWENTYFOURBITGROUP = 24;
+    static private final int  EIGHTBIT           = 8;
+    static private final int  SIXTEENBIT         = 16;
+    static private final int  SIXBIT             = 6;
+    static private final int  FOURBYTE           = 4;
+
+
+    static private final byte PAD               = ( byte ) '=';
+    static private byte [] base64Alphabet       = new byte[BASELENGTH];
+    static private byte [] lookUpBase64Alphabet = new byte[LOOKUPLENGTH];
+
+    static {
+
+        for (int i = 0; i<BASELENGTH; i++ ) {
+            base64Alphabet[i] = -1;
+        }
+        for ( int i = 'Z'; i >= 'A'; i-- ) {
+            base64Alphabet[i] = (byte) (i-'A');
+        }
+        for ( int i = 'z'; i>= 'a'; i--) {
+            base64Alphabet[i] = (byte) ( i-'a' + 26);
+        }
+
+        for ( int i = '9'; i >= '0'; i--) {
+            base64Alphabet[i] = (byte) (i-'0' + 52);
+        }
+
+        base64Alphabet['+']  = 62;
+        base64Alphabet['/']  = 63;
+
+       for (int i = 0; i<=25; i++ )
+            lookUpBase64Alphabet[i] = (byte) ('A'+i );
+
+        for (int i = 26,  j = 0; i<=51; i++, j++ )
+            lookUpBase64Alphabet[i] = (byte) ('a'+ j );
+
+        for (int i = 52,  j = 0; i<=61; i++, j++ )
+            lookUpBase64Alphabet[i] = (byte) ('0' + j );
+
+    }
+
+
+    static boolean isBase64( byte octect ) {
+        //shall we ignore white space? JEFF??
+        return(octect == PAD || base64Alphabet[octect] != -1 );
+    }
+
+
+    static boolean isArrayByteBase64( byte[] arrayOctect ) {
+        int length = arrayOctect.length;
+        if ( length == 0 )
+            return false;
+        for ( int i=0; i < length; i++ ) {
+            if ( Base64.isBase64( arrayOctect[i] ) == false)
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Encodes hex octects into Base64
+     *
+     * @param binaryData Array containing binaryData
+     * @return Encoded Base64 array
+     */
+    public static byte[] encode( byte[] binaryData ) {
+        int      lengthDataBits    = binaryData.length*EIGHTBIT;
+        int      fewerThan24bits   = lengthDataBits%TWENTYFOURBITGROUP;
+        int      numberTriplets    = lengthDataBits/TWENTYFOURBITGROUP;
+        byte     encodedData[]     = null;
+
+
+        if ( fewerThan24bits != 0 ) //data not divisible by 24 bit
+            encodedData = new byte[ (numberTriplets + 1 )*4  ];
+        else // 16 or 8 bit
+            encodedData = new byte[ numberTriplets*4 ];
+
+        byte k=0, l=0, b1=0,b2=0,b3=0;
+
+        int encodedIndex = 0;
+        int dataIndex   = 0;
+        int i           = 0;
+        for ( i = 0; i<numberTriplets; i++ ) {
+
+            dataIndex = i*3;
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex + 1];
+            b3 = binaryData[dataIndex + 2];
+
+            l  = (byte)(b2 & 0x0f);
+            k  = (byte)(b1 & 0x03);
+
+            encodedIndex = i*4;
+            encodedData[encodedIndex]   = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex+1] = lookUpBase64Alphabet[(b2 >>4 ) |
+( k<<4 )];
+            encodedData[encodedIndex+2] = lookUpBase64Alphabet[ (l <<2 ) |
+( b3>>6)];
+            encodedData[encodedIndex+3] = lookUpBase64Alphabet[ b3 & 0x3f ];
+        }
+
+        // form integral number of 6-bit groups
+        dataIndex    = i*3;
+        encodedIndex = i*4;
+        if (fewerThan24bits == EIGHTBIT ) {
+            b1 = binaryData[dataIndex];
+            k = (byte) ( b1 &0x03 );
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ k<<4 ];
+            encodedData[encodedIndex + 2] = PAD;
+            encodedData[encodedIndex + 3] = PAD;
+        } else if ( fewerThan24bits == SIXTEENBIT ) {
+
+            b1 = binaryData[dataIndex];
+            b2 = binaryData[dataIndex +1 ];
+            l = ( byte ) ( b2 &0x0f );
+            k = ( byte ) ( b1 &0x03 );
+            encodedData[encodedIndex]     = lookUpBase64Alphabet[ b1 >>2 ];
+            encodedData[encodedIndex + 1] = lookUpBase64Alphabet[ (b2 >>4 )
+| ( k<<4 )];
+            encodedData[encodedIndex + 2] = lookUpBase64Alphabet[ l<<2 ];
+            encodedData[encodedIndex + 3] = PAD;
+        }
+        return encodedData;
+    }
+
+
+    /**
+     * Decodes Base64 data into octects
+     *
+     * @param base64Data Byte array containing Base64 data
+     * @return Array containind decoded data.
+     */
+    public byte[] decode( byte[] base64Data ) {
+        int      numberQuadruple    = base64Data.length/FOURBYTE;
+        byte     decodedData[]      = null;
+        byte     b1=0,b2=0,b3=0, b4=0, marker0=0, marker1=0;
+
+        // Throw away anything not in base64Data
+        // Adjust size
+
+        int encodedIndex = 0;
+        int dataIndex    = 0;
+        decodedData      = new byte[ numberQuadruple*3 + 1 ];
+
+        for (int i = 0; i<numberQuadruple; i++ ) {
+            dataIndex = i*4;
+            marker0   = base64Data[dataIndex +2];
+            marker1   = base64Data[dataIndex +3];
+
+            b1 = base64Alphabet[base64Data[dataIndex]];
+            b2 = base64Alphabet[base64Data[dataIndex +1]];
+
+            if ( marker0 != PAD && marker1 != PAD ) {     //No PAD e.g 3cQl
+                b3 = base64Alphabet[ marker0 ];
+                b4 = base64Alphabet[ marker1 ];
+
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
+                decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
+(b3>>2) & 0xf) );
+                decodedData[encodedIndex+2] = (byte)( b3<<6 | b4 );
+            } else if ( marker0 == PAD ) {               //Two PAD e.g. 3c[Pad][Pad]
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 ) ;
+                decodedData[encodedIndex+1] = (byte)((b2 & 0xf)<<4 );
+                decodedData[encodedIndex+2] = (byte) 0;
+            } else if ( marker1 == PAD ) {              //One PAD e.g. 3cQ[Pad]
+                b3 = base64Alphabet[ marker0 ];
+
+                decodedData[encodedIndex]   = (byte)(  b1 <<2 | b2>>4 );
+                decodedData[encodedIndex+1] = (byte)(((b2 & 0xf)<<4 ) |(
+(b3>>2) & 0xf) );
+                decodedData[encodedIndex+2] = (byte)( b3<<6);
+            }
+            encodedIndex += 3;
+        }
+        return decodedData;
+
+    }
+
+    static final int base64[]= {
+	64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
+	    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
+	    64,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+	    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64,
+	    64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+	    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+	    64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64
+    };
+
+    public static String base64Decode( String orig ) {
+	char chars[]=orig.toCharArray();
+	StringBuffer sb=new StringBuffer();
+	int i=0;
+
+	int shift = 0;   // # of excess bits stored in accum
+	int acc = 0;
+	
+	for (i=0; i<chars.length; i++) {
+	    int v = base64[ chars[i] & 0xFF ];
+	    
+	    if ( v >= 64 ) {
+		if( chars[i] != '=' )
+                    if (log.isDebugEnabled())
+                        log.debug("Wrong char in base64: " + chars[i]);
+	    } else {
+		acc= ( acc << 6 ) | v;
+		shift += 6;
+		if ( shift >= 8 ) {
+		    shift -= 8;
+		    sb.append( (char) ((acc >> shift) & 0xff));
+		}
+	    }
+	}
+	return sb.toString();
+    }
+
+
+}
+

Added: tomcat/sandbox/java/org/apache/tomcat/util/buf/ByteChunk.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/tomcat/util/buf/ByteChunk.java?rev=329081&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/buf/ByteChunk.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/buf/ByteChunk.java Thu Oct 27 21:11:42 2005
@@ -0,0 +1,824 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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 org.apache.tomcat.util.buf;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/*
+ * In a server it is very important to be able to operate on
+ * the original byte[] without converting everything to chars.
+ * Some protocols are ASCII only, and some allow different
+ * non-UNICODE encodings. The encoding is not known beforehand,
+ * and can even change during the execution of the protocol.
+ * ( for example a multipart message may have parts with different
+ *  encoding )
+ *
+ * For HTTP it is not very clear how the encoding of RequestURI
+ * and mime values can be determined, but it is a great advantage
+ * to be able to parse the request without converting to string.
+ */
+
+// TODO: This class could either extend ByteBuffer, or better a ByteBuffer inside
+// this way it could provide the search/etc on ByteBuffer, as a helper.
+
+/**
+ * This class is used to represent a chunk of bytes, and
+ * utilities to manipulate byte[].
+ *
+ * The buffer can be modified and used for both input and output.
+ *
+ * There are 2 modes: The chunk can be associated with a sink - ByteInputChannel or ByteOutputChannel,
+ * which will be used when the buffer is empty ( on input ) or filled ( on output ).
+ * For output, it can also grow. This operating mode is selected by calling setLimit() or
+ * allocate(initial, limit) with limit != -1.
+ *
+ * Various search and append method are defined - similar with String and StringBuffer, but
+ * operating on bytes.
+ *
+ * This is important because it allows processing the http headers directly on the received bytes,
+ * without converting to chars and Strings until the strings are needed. In addition, the charset
+ * is determined later, from headers or user code.
+ *
+ *
+ * @author dac@sun.com
+ * @author James Todd [gonzo@sun.com]
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public final class ByteChunk implements Cloneable, Serializable {
+
+    /** Input interface, used when the buffer is emptiy
+     *
+     * Same as java.nio.channel.ReadableByteChannel
+     */
+    public static interface ByteInputChannel {
+        /** 
+         * Read new bytes ( usually the internal conversion buffer ).
+         * The implementation is allowed to ignore the parameters, 
+         * and mutate the chunk if it wishes to implement its own buffering.
+         */
+        public int realReadBytes(byte cbuf[], int off, int len)
+            throws IOException;
+    }
+
+    /** Same as java.nio.channel.WrittableByteChannel.
+     */
+    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;
+    }
+
+    // --------------------
+
+    /** Default encoding used to convert to strings. It should be UTF8,
+	as most standards seem to converge, but the servlet API requires
+	8859_1, and this object is used mostly for servlets. 
+    */
+    public static final String DEFAULT_CHARACTER_ENCODING="ISO-8859-1";
+        
+    // byte[]
+    private byte[] buff;
+
+    private int start=0;
+    private int end;
+
+    private String enc;
+
+    private boolean isSet=false; // XXX
+
+    // How much can it grow, when data is added
+    private int limit=-1;
+
+    private ByteInputChannel in = null;
+    private ByteOutputChannel out = null;
+
+    private boolean isOutput=false;
+    private boolean optimizedWrite=true;
+    
+    /**
+     * Creates a new, uninitialized ByteChunk object.
+     */
+    public ByteChunk() {
+    }
+
+    public ByteChunk( int initial ) {
+	allocate( initial, -1 );
+    }
+
+    //--------------------
+    public ByteChunk getClone() {
+	try {
+	    return (ByteChunk)this.clone();
+	} catch( Exception ex) {
+	    return null;
+	}
+    }
+
+    public boolean isNull() {
+	return ! isSet; // buff==null;
+    }
+    
+    /**
+     * Resets the message buff to an uninitialized state.
+     */
+    public void recycle() {
+	//	buff = null;
+	enc=null;
+	start=0;
+	end=0;
+	isSet=false;
+    }
+
+    public void reset() {
+	buff=null;
+    }
+
+    // -------------------- Setup --------------------
+
+    public void allocate( int initial, int limit  ) {
+	isOutput=true;
+	if( buff==null || buff.length < initial ) {
+	    buff=new byte[initial];
+	}    
+	this.limit=limit;
+	start=0;
+	end=0;
+	isSet=true;
+    }
+
+    /**
+     * Sets the message bytes to the specified subarray of bytes.
+     * 
+     * @param b the ascii bytes
+     * @param off the start offset of the bytes
+     * @param len the length of the bytes
+     */
+    public void setBytes(byte[] b, int off, int len) {
+        buff = b;
+        start = off;
+        end = start+ len;
+        isSet=true;
+    }
+
+    public void setOptimizedWrite(boolean optimizedWrite) {
+        this.optimizedWrite = optimizedWrite;
+    }
+
+    public void setEncoding( String enc ) {
+	this.enc=enc;
+    }
+    public String getEncoding() {
+        if (enc == null)
+            enc=DEFAULT_CHARACTER_ENCODING;
+        return enc;
+    }
+
+    /**
+     * Returns the message bytes.
+     */
+    public byte[] getBytes() {
+	return getBuffer();
+    }
+
+    /**
+     * Returns the message bytes.
+     */
+    public byte[] getBuffer() {
+	return buff;
+    }
+
+    /**
+     * Returns the start offset of the bytes.
+     * For output this is the end of the buffer.
+     */
+    public int getStart() {
+	return start;
+    }
+
+    public int getOffset() {
+	return start;
+    }
+
+    public void setOffset(int off) {
+        if (end < off ) end=off;
+	start=off;
+    }
+
+    /**
+     * Returns the length of the bytes.
+     * XXX need to clean this up
+     */
+    public int getLength() {
+	return end-start;
+    }
+
+    /** 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 empty, read the data from the input channel.
+     */
+    public void setByteInputChannel(ByteInputChannel in) {
+        this.in = in;
+    }
+
+    /** 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 int getEnd() {
+	return end;
+    }
+
+    public void setEnd( int i ) {
+	end=i;
+    }
+
+    // -------------------- Adding data to the buffer --------------------
+    /** Append a char, by casting it to byte. This IS NOT intended for unicode.
+     *
+     * @param c
+     * @throws IOException
+     */
+    public void append( char c )
+	throws IOException
+    {
+	append( (byte)c);
+    }
+
+    public void append( byte b )
+	throws IOException
+    {
+	makeSpace( 1 );
+
+	// couldn't make space
+	if( limit >0 && end >= limit ) {
+	    flushBuffer();
+	}
+	buff[end++]=b;
+    }
+
+    public void append( ByteChunk src )
+	throws IOException
+    {
+	append( src.getBytes(), src.getStart(), src.getLength());
+    }
+
+    /** Add data to the buffer
+     */
+    public void append( byte 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, buff, end, len );
+	    end+=len;
+	    return;
+	}
+
+        // Optimize on a common case.
+        // If the buffer is empty and the source is going to fill up all the
+        // space in buffer, may as well write it directly to the output,
+        // and avoid an extra copy
+        if ( optimizedWrite && len == limit && end == start) {
+            out.realWriteBytes( src, off, len );
+            return;
+        }
+	// if we have limit and we're below
+	if( len <= limit - end ) {
+	    // makeSpace will grow the buffer to the limit,
+	    // so we have space
+	    System.arraycopy( src, off, buff, end, len );
+	    end+=len;
+	    return;
+	}
+
+	// need more space than we can afford, need to flush
+	// buffer
+
+	// the buffer is already at ( or bigger than ) limit
+
+        // We chunk the data into slices fitting in the buffer limit, although
+        // if the data is written directly if it doesn't fit
+
+        int avail=limit-end;
+        System.arraycopy(src, off, buff, end, avail);
+        end += avail;
+
+        flushBuffer();
+
+        int remain = len - avail;
+
+        while (remain > (limit - end)) {
+            out.realWriteBytes( src, (off + len) - remain, limit - end );
+            remain = remain - (limit - end);
+        }
+
+        System.arraycopy(src, (off + len) - remain, buff, end, remain);
+        end += remain;
+
+    }
+
+
+    // -------------------- Removing data from the buffer --------------------
+
+    public int substract()
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadBytes( buff, 0, buff.length );
+            if (n < 0)
+                return -1;
+        }
+
+        return (buff[start++] & 0xFF);
+
+    }
+
+    public int substract(ByteChunk src)
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadBytes( buff, 0, buff.length );
+            if (n < 0)
+                return -1;
+        }
+
+        int len = getLength();
+        src.append(buff, start, len);
+        start = end;
+        return len;
+
+    }
+
+    public int substract( byte src[], int off, int len )
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadBytes( buff, 0, buff.length );
+            if (n < 0)
+                return -1;
+        }
+
+        int n = len;
+        if (len > getLength()) {
+            n = getLength();
+        }
+        System.arraycopy(buff, start, src, off, n);
+        start += n;
+        return n;
+
+    }
+
+
+    /** Send the buffer to the sink. Called by append() when the limit is reached.
+     *  You can also call it explicitely to force the data to be written.
+     *
+     * @throws IOException
+     */
+    public void flushBuffer()
+	throws IOException
+    {
+	//assert out!=null
+	if( out==null ) {
+	    throw new IOException( "Buffer overflow, no sink " + limit + " " +
+				   buff.length  );
+	}
+	out.realWriteBytes( buff, start, end-start );
+	end=start;
+    }
+
+    /** 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=end + count;
+
+	// Can't grow above the limit
+	if( limit > 0 &&
+	    desiredSize > limit) {
+	    desiredSize=limit;
+	}
+
+	if( buff==null ) {
+	    if( desiredSize < 256 ) desiredSize=256; // take a minimum
+	    buff=new byte[desiredSize];
+	}
+	
+	// limit < buf.length ( the buffer is already big )
+	// or we already have space XXX
+	if( desiredSize <= buff.length ) {
+	    return;
+	}
+	// grow in larger chunks
+	if( desiredSize < 2 * buff.length ) {
+	    newSize= buff.length * 2;
+	    if( limit >0 &&
+		newSize > limit ) newSize=limit;
+	    tmp=new byte[newSize];
+	} else {
+	    newSize= buff.length * 2 + count ;
+	    if( limit > 0 &&
+		newSize > limit ) newSize=limit;
+	    tmp=new byte[newSize];
+	}
+	
+	System.arraycopy(buff, start, tmp, 0, end-start);
+	buff = tmp;
+	tmp = null;
+	end=end-start;
+	start=0;
+    }
+    
+    // -------------------- Conversion and getters --------------------
+
+    public String toString() {
+        if (null == buff) {
+            return null;
+        } else if (end-start == 0) {
+            return "";
+        }
+        return StringCache.toString(this);
+    }
+    
+    public String toStringInternal() {
+        String strValue=null;
+        try {
+            if( enc==null ) enc=DEFAULT_CHARACTER_ENCODING;
+            strValue = new String( buff, start, end-start, enc );
+            /*
+             Does not improve the speed too much on most systems,
+             it's safer to use the "clasical" new String().
+             
+             Most overhead is in creating char[] and copying,
+             the internal implementation of new String() is very close to
+             what we do. The decoder is nice for large buffers and if
+             we don't go to String ( so we can take advantage of reduced GC)
+             
+             // Method is commented out, in:
+              return B2CConverter.decodeString( enc );
+              */
+        } catch (java.io.UnsupportedEncodingException e) {
+            // Use the platform encoding in that case; the usage of a bad
+            // encoding will have been logged elsewhere already
+            strValue = new String(buff, start, end-start);
+        }
+        return strValue;
+    }
+
+    public int getInt()
+    {
+	return Ascii.parseInt(buff, start,end-start);
+    }
+
+    public long getLong() {
+        return Ascii.parseLong(buff, start,end-start);
+    }
+
+
+    // -------------------- equals --------------------
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * @param s the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equals(String s) {
+	// XXX ENCODING - this only works if encoding is UTF8-compat
+	// ( ok for tomcat, where we compare ascii - header names, etc )!!!
+	
+	byte[] b = buff;
+	int blen = end-start;
+	if (b == null || blen != s.length()) {
+	    return false;
+	}
+	int boff = start;
+	for (int i = 0; i < blen; i++) {
+	    if (b[boff++] != s.charAt(i)) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * @param s the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equalsIgnoreCase(String s) {
+	byte[] b = buff;
+	int blen = end-start;
+	if (b == null || blen != s.length()) {
+	    return false;
+	}
+	int boff = start;
+	for (int i = 0; i < blen; i++) {
+	    if (Ascii.toLower(b[boff++]) != Ascii.toLower(s.charAt(i))) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    public boolean equals( ByteChunk bb ) {
+	return equals( bb.getBytes(), bb.getStart(), bb.getLength());
+    }
+    
+    public boolean equals( byte b2[], int off2, int len2) {
+	byte b1[]=buff;
+	if( b1==null && b2==null ) return true;
+
+	int len=end-start;
+	if ( len2 != len || b1==null || b2==null ) 
+	    return false;
+		
+	int off1 = start;
+
+	while ( len-- > 0) {
+	    if (b1[off1++] != b2[off2++]) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    public boolean equals( CharChunk cc ) {
+	return equals( cc.getChars(), cc.getStart(), cc.getLength());
+    }
+    
+    public boolean equals( char c2[], int off2, int len2) {
+	// XXX works only for enc compatible with ASCII/UTF !!!
+	byte b1[]=buff;
+	if( c2==null && b1==null ) return true;
+	
+	if (b1== null || c2==null || end-start != len2 ) {
+	    return false;
+	}
+	int off1 = start;
+	int len=end-start;
+	
+	while ( len-- > 0) {
+	    if ( (char)b1[off1++] != c2[off2++]) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param s the string
+     */
+    public boolean startsWith(String s) {
+	// Works only if enc==UTF
+	byte[] b = buff;
+	int blen = s.length();
+	if (b == null || blen > end-start) {
+	    return false;
+	}
+	int boff = start;
+	for (int i = 0; i < blen; i++) {
+	    if (b[boff++] != s.charAt(i)) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    /* Returns true if the message bytes start with the specified byte array */
+    public boolean startsWith(byte[] b2) {
+        byte[] b1 = buff;
+        if (b1 == null && b2 == null) {
+            return true;
+        }
+
+        int len = end - start;
+        if (b1 == null || b2 == null || b2.length > len) {
+            return false;
+        }
+        for (int i = start, j = 0; i < end && j < b2.length; ) {
+            if (b1[i++] != b2[j++]) 
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param s the string
+     * @param pos The position
+     */
+    public boolean startsWithIgnoreCase(String s, int pos) {
+	byte[] b = buff;
+	int len = s.length();
+	if (b == null || len+pos > end-start) {
+	    return false;
+	}
+	int off = start+pos;
+	for (int i = 0; i < len; i++) {
+	    if (Ascii.toLower( b[off++] ) != Ascii.toLower( s.charAt(i))) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    public int indexOf( String src, int srcOff, int srcLen, int myOff ) {
+	char first=src.charAt( srcOff );
+
+	// Look for first char 
+	int srcEnd = srcOff + srcLen;
+        
+	for( int i=myOff+start; i <= (end - srcLen); i++ ) {
+	    if( buff[i] != first ) continue;
+	    // found first char, now look for a match
+            int myPos=i+1;
+	    for( int srcPos=srcOff + 1; srcPos< srcEnd; ) {
+                if( buff[myPos++] != src.charAt( srcPos++ ))
+		    break;
+                if( srcPos==srcEnd ) return i-start; // found it
+	    }
+	}
+	return -1;
+    }
+
+    // -------------------- Hash code  --------------------
+
+    // normal hash. 
+    public int hash() {
+	return hashBytes( buff, start, end-start);
+    }
+
+    // hash ignoring case
+    public int hashIgnoreCase() {
+	return hashBytesIC( buff, start, end-start );
+    }
+
+    private static int hashBytes( byte buff[], int start, int bytesLen ) {
+	int max=start+bytesLen;
+	byte bb[]=buff;
+	int code=0;
+	for (int i = start; i < max ; i++) {
+	    code = code * 37 + bb[i];
+	}
+	return code;
+    }
+
+    private static int hashBytesIC( byte bytes[], int start,
+				    int bytesLen )
+    {
+	int max=start+bytesLen;
+	byte bb[]=bytes;
+	int code=0;
+	for (int i = start; i < max ; i++) {
+	    code = code * 37 + Ascii.toLower(bb[i]);
+	}
+	return code;
+    }
+
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param c the character
+     * @param starting The start position
+     */
+    public int indexOf(char c, int starting) {
+	int ret = indexOf( buff, start+starting, end, c);
+	return (ret >= start) ? ret - start : -1;
+    }
+
+    public static int  indexOf( byte bytes[], int off, int end, char qq )
+    {
+	// Works only for UTF 
+	while( off < end ) {
+	    byte b=bytes[off];
+	    if( b==qq )
+		return off;
+	    off++;
+	}
+	return -1;
+    }
+
+    /** Find a character, no side effects.
+     *  @return index of char if found, -1 if not
+     */
+    public static int findChar( byte buf[], int start, int end, char c ) {
+	byte b=(byte)c;
+	int offset = start;
+	while (offset < end) {
+	    if (buf[offset] == b) {
+		return offset;
+	    }
+	    offset++;
+	}
+	return -1;
+    }
+
+    /** Find a character, no side effects.
+     *  @return index of char if found, -1 if not
+     */
+    public static int findChars( byte buf[], int start, int end, byte c[] ) {
+	int clen=c.length;
+	int offset = start;
+	while (offset < end) {
+	    for( int i=0; i<clen; i++ ) 
+		if (buf[offset] == c[i]) {
+		    return offset;
+		}
+	    offset++;
+	}
+	return -1;
+    }
+
+    /** Find the first character != c 
+     *  @return index of char if found, -1 if not
+     */
+    public static int findNotChars( byte buf[], int start, int end, byte c[] )
+    {
+	int clen=c.length;
+	int offset = start;
+	boolean found;
+		
+	while (offset < end) {
+	    found=true;
+	    for( int i=0; i<clen; i++ ) {
+		if (buf[offset] == c[i]) {
+		    found=false;
+		    break;
+		}
+	    }
+	    if( found ) { // buf[offset] != c[0..len]
+		return offset;
+	    }
+	    offset++;
+	}
+	return -1;
+    }
+
+
+    /**
+     * Convert specified String to a byte array. This ONLY WORKS for ascii, UTF chars will be truncated.
+     * 
+     * @param value to convert to byte array
+     * @return the byte array value
+     */
+    public static final byte[] convertToBytes(String value) {
+        byte[] result = new byte[value.length()];
+        for (int i = 0; i < value.length(); i++) {
+            result[i] = (byte) value.charAt(i);
+        }
+        return result;
+    }
+    
+    
+}

Propchange: tomcat/sandbox/java/org/apache/tomcat/util/buf/ByteChunk.java
------------------------------------------------------------------------------
    svn:executable = *

Added: tomcat/sandbox/java/org/apache/tomcat/util/buf/C2BConverter.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/tomcat/util/buf/C2BConverter.java?rev=329081&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/buf/C2BConverter.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/buf/C2BConverter.java Thu Oct 27 21:11:42 2005
@@ -0,0 +1,261 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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 org.apache.tomcat.util.buf;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+
+/** Efficient conversion of character to bytes.
+ *  
+ *  This uses the standard JDK mechansim - a writer - but provides mechanisms
+ *  to recycle all the objects that are used. It is compatible with JDK1.1 and up,
+ *  ( nio is better, but it's not available even in 1.2 or 1.3 )
+ * 
+ */
+public final class C2BConverter {
+    
+    private static org.apache.commons.logging.Log log=
+        org.apache.commons.logging.LogFactory.getLog(C2BConverter.class );
+    
+    private IntermediateOutputStream ios;
+    private WriteConvertor conv;
+    private ByteChunk bb;
+    private String enc;
+    
+    /** Create a converter, with bytes going to a byte buffer
+     */
+    public C2BConverter(ByteChunk output, String encoding) throws IOException {
+	this.bb=output;
+	ios=new IntermediateOutputStream( output );
+	conv=new WriteConvertor( ios, encoding );
+        this.enc=encoding;
+    }
+
+    /** Create a converter
+     */
+    public C2BConverter(String encoding) throws IOException {
+	this( new ByteChunk(1024), encoding );
+    }
+
+    public ByteChunk getByteChunk() {
+	return bb;
+    }
+
+    public String getEncoding() {
+        return enc;
+    }
+
+    public void setByteChunk(ByteChunk bb) {
+	this.bb=bb;
+	ios.setByteChunk( bb );
+    }
+
+    /** Reset the internal state, empty the buffers.
+     *  The encoding remain in effect, the internal buffers remain allocated.
+     */
+    public  final void recycle() {
+	conv.recycle();
+	bb.recycle();
+    }
+
+    /** Generate the bytes using the specified encoding
+     */
+    public  final void convert(char c[], int off, int len ) throws IOException {
+	conv.write( c, off, len );
+    }
+
+    /** Generate the bytes using the specified encoding
+     */
+    public  final void convert(String s ) throws IOException {
+	conv.write( s );
+    }
+
+    /** Generate the bytes using the specified encoding
+     */
+    public  final void convert(char c ) throws IOException {
+	conv.write( c );
+    }
+
+    /** Convert a message bytes chars to bytes
+     */
+    public final void convert(MessageBytes mb ) throws IOException {
+        int type=mb.getType();
+        if( type==MessageBytes.T_BYTES )
+            return;
+        ByteChunk orig=bb;
+        setByteChunk( mb.getByteChunk());
+        bb.recycle();
+        bb.allocate( 32, -1 );
+        
+        if( type==MessageBytes.T_STR ) {
+            convert( mb.getString() );
+            // System.out.println("XXX Converting " + mb.getString() );
+        } else if( type==MessageBytes.T_CHARS ) {
+            CharChunk charC=mb.getCharChunk();
+            convert( charC.getBuffer(),
+                                charC.getOffset(), charC.getLength());
+            //System.out.println("XXX Converting " + mb.getCharChunk() );
+        } else {
+            if (log.isDebugEnabled())
+                log.debug("XXX unknowon type " + type );
+        }
+        flushBuffer();
+        //System.out.println("C2B: XXX " + bb.getBuffer() + bb.getLength()); 
+        setByteChunk(orig);
+    }
+
+    /** Flush any internal buffers into the ByteOutput or the internal
+     *  byte[]
+     */
+    public  final void flushBuffer() throws IOException {
+	conv.flush();
+    }
+
+}
+
+// -------------------- Private implementation --------------------
+
+
+
+/**
+ *  Special writer class, where close() is overritten. The default implementation
+ *  would set byteOutputter to null, and the writter can't be recycled. 
+ *
+ *  Note that the flush method will empty the internal buffers _and_ call
+ *  flush on the output stream - that's why we use an intermediary output stream
+ *  that overrides flush(). The idea is to  have full control: flushing the
+ *  char->byte converter should be independent of flushing the OutputStream.
+ * 
+ *  When a WriteConverter is created, it'll allocate one or 2 byte buffers,
+ *  with a 8k size that can't be changed ( at least in JDK1.1 -> 1.4 ). It would
+ *  also allocate a ByteOutputter or equivalent - again some internal buffers.
+ *
+ *  It is essential to keep  this object around and reuse it. You can use either
+ *  pools or per thread data - but given that in most cases a converter will be
+ *  needed for every thread and most of the time only 1 ( or 2 ) encodings will
+ *  be used, it is far better to keep it per thread and eliminate the pool 
+ *  overhead too.
+ * 
+ */
+ final class	WriteConvertor extends OutputStreamWriter {
+    // stream with flush() and close(). overriden.
+    private IntermediateOutputStream ios;
+    
+    // Has a private, internal byte[8192]
+    
+    /** Create a converter.
+     */
+    public WriteConvertor( IntermediateOutputStream out, String enc )
+	throws UnsupportedEncodingException
+    {
+	super( out, enc );
+	ios=out;
+    }
+    
+    /** Overriden - will do nothing but reset internal state.
+     */
+    public  final void close() throws IOException {
+	// NOTHING
+	// Calling super.close() would reset out and cb.
+    }
+    
+    /**
+     *  Flush the characters only
+     */ 
+    public  final void flush() throws IOException {
+	// Will flushBuffer and out()
+	// flushBuffer put any remaining chars in the byte[] 
+	super.flush();
+    }
+    
+    public  final void write(char cbuf[], int off, int len) throws IOException {
+	// will do the conversion and call write on the output stream
+	super.write( cbuf, off, len );
+    }
+    
+    /** Reset the buffer
+     */
+    public  final void recycle() {
+	ios.disable();
+	try {
+	    //	    System.out.println("Reseting writer");
+	    flush();
+	} catch( Exception ex ) {
+	    ex.printStackTrace();
+	}
+	ios.enable();
+    }
+    
+}
+
+
+/** Special output stream where close() is overriden, so super.close()
+    is never called.
+    
+    This allows recycling. It can also be disabled, so callbacks will
+    not be called if recycling the converter and if data was not flushed.
+*/
+final class IntermediateOutputStream extends OutputStream {
+    private ByteChunk tbuff;
+    private boolean enabled=true;
+    
+    public IntermediateOutputStream(ByteChunk tbuff) {
+	    this.tbuff=tbuff;
+    }
+    
+    public  final void close() throws IOException {
+	// shouldn't be called - we filter it out in writer
+	throw new IOException("close() called - shouldn't happen ");
+    }
+    
+    public  final void flush() throws IOException {
+	// nothing - write will go directly to the buffer,
+	// we don't keep any state
+    }
+    
+    public  final  void write(byte cbuf[], int off, int len) throws IOException {
+	// will do the conversion and call write on the output stream
+	if( enabled ) {
+	    tbuff.append( cbuf, off, len );
+	}
+    }
+    
+    public  final void write( int i ) throws IOException {
+	throw new IOException("write( int ) called - shouldn't happen ");
+    }
+
+    // -------------------- Internal methods --------------------
+
+    void setByteChunk( ByteChunk bb ) {
+	tbuff=bb;
+    }
+    
+    /** Temporary disable - this is used to recycle the converter without
+     *  generating an output if the buffers were not flushed
+     */
+    final void disable() {
+	enabled=false;
+    }
+
+    /** Reenable - used to recycle the converter
+     */
+    final void enable() {
+	enabled=true;
+    }
+}

Added: tomcat/sandbox/java/org/apache/tomcat/util/buf/CharChunk.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/tomcat/util/buf/CharChunk.java?rev=329081&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/buf/CharChunk.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/buf/CharChunk.java Thu Oct 27 21:11:42 2005
@@ -0,0 +1,698 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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 org.apache.tomcat.util.buf;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * Utilities to manipluate char chunks. While String is
+ * the easiest way to manipulate chars ( search, substrings, etc),
+ * it is known to not be the most efficient solution - Strings are
+ * designed as imutable and secure objects.
+ * 
+ * @author dac@sun.com
+ * @author James Todd [gonzo@sun.com]
+ * @author Costin Manolache
+ * @author Remy Maucherat
+ */
+public final class CharChunk implements Cloneable, Serializable {
+
+    // Input interface, used when the buffer is emptied.
+    public static interface CharInputChannel {
+        /** 
+         * Read new bytes ( usually the internal conversion buffer ).
+         * The implementation is allowed to ignore the parameters, 
+         * and mutate the chunk if it wishes to implement its own buffering.
+         */
+        public int realReadChars(char cbuf[], int off, int len)
+            throws IOException;
+    }
+    /**
+     *  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;
+    }
+    
+    // -------------------- 
+    // char[]
+    private char buff[];
+
+    private int start;
+    private int end;
+
+    private boolean isSet=false;  // XXX 
+
+    private boolean isOutput=false;
+
+    // -1: grow undefinitely
+    // maximum amount to be cached
+    private int limit=-1;
+
+    private CharInputChannel in = null;
+    private CharOutputChannel out = null;
+    
+    private boolean optimizedWrite=true;
+
+    /**
+     * Creates a new, uninitialized CharChunk object.
+     */
+    public CharChunk() {
+    }
+
+    public CharChunk(int size) {
+	allocate( size, -1 );
+    }
+
+    // --------------------
+    
+    public CharChunk getClone() {
+	try {
+	    return (CharChunk)this.clone();
+	} catch( Exception ex) {
+	    return null;
+	}
+    }
+
+    public boolean isNull() {
+	if( end > 0 ) return false;
+	return !isSet; //XXX 
+    }
+    
+    /**
+     * Resets the message bytes to an uninitialized state.
+     */
+    public void recycle() {
+	//	buff=null;
+	isSet=false; // XXX
+	start=0;
+	end=0;
+    }
+
+    public void reset() {
+	buff=null;
+    }
+
+    // -------------------- Setup --------------------
+
+    public void allocate( int initial, int limit  ) {
+	isOutput=true;
+	if( buff==null || buff.length < initial ) {
+	    buff=new char[initial];
+	}
+	this.limit=limit;
+	start=0;
+	end=0;
+	isOutput=true;
+	isSet=true;
+    }
+
+
+    public void setOptimizedWrite(boolean optimizedWrite) {
+        this.optimizedWrite = optimizedWrite;
+    }
+
+    public void setChars( char[] c, int off, int len ) {
+        buff=c;
+        start=off;
+        end=start + len;
+        isSet=true;
+    }
+
+    /** 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 empty, read the data from the input channel.
+     */
+    public void setCharInputChannel(CharInputChannel in) {
+        this.in = in;
+    }
+
+    /** 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;
+    }
+
+    // compat 
+    public char[] getChars()
+    {
+	return getBuffer();
+    }
+    
+    public char[] getBuffer()
+    {
+	return buff;
+    }
+    
+    /**
+     * Returns the start offset of the bytes.
+     * For output this is the end of the buffer.
+     */
+    public int getStart() {
+	return start;
+    }
+    
+    public int getOffset() {
+	return start;
+    }
+
+    /**
+     * Returns the start offset of the bytes.
+     */
+    public void setOffset(int off) {
+	start=off;
+    }
+
+    /**
+     * Returns the length of the bytes.
+     */
+    public int getLength() {
+	return end-start;
+    }
+
+
+    public int getEnd() {
+	return end;
+    }
+
+    public void setEnd( int i ) {
+	end=i;
+    }
+
+    // -------------------- Adding data --------------------
+    
+    public void append( char b )
+	throws IOException
+    {
+	makeSpace( 1 );
+
+	// couldn't make space
+	if( limit >0 && end >= limit ) {
+	    flushBuffer();
+	}
+	buff[end++]=b;
+    }
+    
+    public void append( CharChunk src )
+	throws IOException
+    {
+	append( src.getBuffer(), src.getOffset(), src.getLength());
+    }
+
+    /** 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, buff, end, len );
+	    end+=len;
+	    return;
+	}
+
+        // Optimize on a common case.
+        // If the source is going to fill up all the space in buffer, may
+        // as well write it directly to the output, and avoid an extra copy
+        if ( optimizedWrite && len == limit && end == start) {
+            out.realWriteChars( src, off, len );
+            return;
+        }
+	
+	// if we have limit and we're below
+	if( len <= limit - end ) {
+	    // makeSpace will grow the buffer to the limit,
+	    // so we have space
+	    System.arraycopy( src, off, buff, end, len );
+            
+	    end+=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 + end < 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-end;
+	    System.arraycopy(src, off, buff, end, avail);
+	    end += avail;
+	    
+	    flushBuffer();
+	    
+	    System.arraycopy(src, off+avail, buff, end, len - avail);
+	    end+= 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, buff, end );
+	    end+=len;
+	    return;
+	}
+
+	int off=0;
+	int sbOff = off;
+	int sbEnd = off + len;
+	while (sbOff < sbEnd) {
+	    int d = min(limit - end, sbEnd - sbOff);
+	    sb.getChars( sbOff, sbOff+d, buff, end);
+	    sbOff += d;
+	    end += d;
+	    if (end >= limit)
+		flushBuffer();
+	}
+    }
+
+    /** Append a string to the buffer
+     */
+    public void append(String s) throws IOException {
+        append(s, 0, s.length());
+    }
+    
+    /** 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(off, off+len, buff, end );
+	    end+=len;
+	    return;
+	}
+
+	int sOff = off;
+	int sEnd = off + len;
+	while (sOff < sEnd) {
+	    int d = min(limit - end, sEnd - sOff);
+	    s.getChars( sOff, sOff+d, buff, end);
+	    sOff += d;
+	    end += d;
+	    if (end >= limit)
+		flushBuffer();
+	}
+    }
+    
+    // -------------------- Removing data from the buffer --------------------
+
+    public int substract()
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadChars(buff, end, buff.length - end);
+            if (n < 0)
+                return -1;
+        }
+
+        return (buff[start++]);
+
+    }
+
+    public int substract(CharChunk src)
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadChars( buff, end, buff.length - end);
+            if (n < 0)
+                return -1;
+        }
+
+        int len = getLength();
+        src.append(buff, start, len);
+        start = end;
+        return len;
+
+    }
+
+    public int substract( char src[], int off, int len )
+        throws IOException {
+
+        if ((end - start) == 0) {
+            if (in == null)
+                return -1;
+            int n = in.realReadChars( buff, end, buff.length - end);
+            if (n < 0)
+                return -1;
+        }
+
+        int n = len;
+        if (len > getLength()) {
+            n = getLength();
+        }
+        System.arraycopy(buff, start, src, off, n);
+        start += n;
+        return n;
+
+    }
+
+
+    public void flushBuffer()
+	throws IOException
+    {
+	//assert out!=null
+	if( out==null ) {
+	    throw new IOException( "Buffer overflow, no sink " + limit + " " +
+				   buff.length  );
+	}
+	out.realWriteChars( buff, start, end - start );
+	end=start;
+    }
+
+    /** 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=end + count;
+
+	// Can't grow above the limit
+	if( limit > 0 &&
+	    desiredSize > limit) {
+	    desiredSize=limit;
+	}
+
+	if( buff==null ) {
+	    if( desiredSize < 256 ) desiredSize=256; // take a minimum
+	    buff=new char[desiredSize];
+	}
+
+	// limit < buf.length ( the buffer is already big )
+	// or we already have space XXX
+	if( desiredSize <= buff.length) {
+	    return;
+	}
+	// grow in larger chunks
+	if( desiredSize < 2 * buff.length ) {
+	    newSize= buff.length * 2;
+	    if( limit >0 &&
+		newSize > limit ) newSize=limit;
+	    tmp=new char[newSize];
+	} else {
+	    newSize= buff.length * 2 + count ;
+	    if( limit > 0 &&
+		newSize > limit ) newSize=limit;
+	    tmp=new char[newSize];
+	}
+	
+	System.arraycopy(buff, start, tmp, start, end-start);
+	buff = tmp;
+	tmp = null;
+    }
+    
+    // -------------------- Conversion and getters --------------------
+
+    public String toString() {
+        if (null == buff) {
+            return null;
+        } else if (end-start == 0) {
+            return "";
+        }
+        return StringCache.toString(this);
+    }
+    
+    public String toStringInternal() {
+        return new String(buff, start, end-start);
+    }
+
+    public int getInt()
+    {
+	return Ascii.parseInt(buff, start,
+				end-start);
+    }
+    
+    // -------------------- equals --------------------
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * @param s the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equals(String s) {
+	char[] c = buff;
+	int len = end-start;
+	if (c == null || len != s.length()) {
+	    return false;
+	}
+	int off = start;
+	for (int i = 0; i < len; i++) {
+	    if (c[off++] != s.charAt(i)) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    /**
+     * Compares the message bytes to the specified String object.
+     * @param s the String to compare
+     * @return true if the comparison succeeded, false otherwise
+     */
+    public boolean equalsIgnoreCase(String s) {
+	char[] c = buff;
+	int len = end-start;
+	if (c == null || len != s.length()) {
+	    return false;
+	}
+	int off = start;
+	for (int i = 0; i < len; i++) {
+	    if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    public boolean equals(CharChunk cc) {
+	return equals( cc.getChars(), cc.getOffset(), cc.getLength());
+    }
+
+    public boolean equals(char b2[], int off2, int len2) {
+	char b1[]=buff;
+	if( b1==null && b2==null ) return true;
+	
+	if (b1== null || b2==null || end-start != len2) {
+	    return false;
+	}
+	int off1 = start;
+	int len=end-start;
+	while ( len-- > 0) {
+	    if (b1[off1++] != b2[off2++]) {
+		return false;
+	    }
+	}
+	return true;
+    }
+
+    public boolean equals(byte b2[], int off2, int len2) {
+	char b1[]=buff;
+	if( b2==null && b1==null ) return true;
+
+	if (b1== null || b2==null || end-start != len2) {
+	    return false;
+	}
+	int off1 = start;
+	int len=end-start;
+	
+	while ( len-- > 0) {
+	    if ( b1[off1++] != (char)b2[off2++]) {
+		return false;
+	    }
+	}
+	return true;
+    }
+    
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param s the string
+     */
+    public boolean startsWith(String s) {
+	char[] c = buff;
+	int len = s.length();
+	if (c == null || len > end-start) {
+	    return false;
+	}
+	int off = start;
+	for (int i = 0; i < len; i++) {
+	    if (c[off++] != s.charAt(i)) {
+		return false;
+	    }
+	}
+	return true;
+    }
+    
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param s the string
+     */
+    public boolean startsWithIgnoreCase(String s, int pos) {
+	char[] c = buff;
+	int len = s.length();
+	if (c == null || len+pos > end-start) {
+	    return false;
+	}
+	int off = start+pos;
+	for (int i = 0; i < len; i++) {
+	    if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) {
+		return false;
+	    }
+	}
+	return true;
+    }
+    
+
+    // -------------------- Hash code  --------------------
+
+    // normal hash. 
+    public int hash() {
+	int code=0;
+	for (int i = start; i < start + end-start; i++) {
+	    code = code * 37 + buff[i];
+	}
+	return code;
+    }
+
+    // hash ignoring case
+    public int hashIgnoreCase() {
+	int code=0;
+	for (int i = start; i < end; i++) {
+	    code = code * 37 + Ascii.toLower(buff[i]);
+	}
+	return code;
+    }
+
+    public int indexOf(char c) {
+	return indexOf( c, start);
+    }
+    
+    /**
+     * Returns true if the message bytes starts with the specified string.
+     * @param c the character
+     */
+    public int indexOf(char c, int starting) {
+	int ret = indexOf( buff, start+starting, end, c );
+	return (ret >= start) ? ret - start : -1;
+    }
+
+    public static int indexOf( char chars[], int off, int cend, char qq )
+    {
+	while( off < cend ) {
+	    char b=chars[off];
+	    if( b==qq )
+		return off;
+	    off++;
+	}
+	return -1;
+    }
+    
+
+    public int indexOf( String src, int srcOff, int srcLen, int myOff ) {
+	char first=src.charAt( srcOff );
+
+	// Look for first char 
+	int srcEnd = srcOff + srcLen;
+        
+	for( int i=myOff+start; i <= (end - srcLen); i++ ) {
+	    if( buff[i] != first ) continue;
+	    // found first char, now look for a match
+            int myPos=i+1;
+	    for( int srcPos=srcOff + 1; srcPos< srcEnd; ) {
+                if( buff[myPos++] != src.charAt( srcPos++ ))
+		    break;
+                if( srcPos==srcEnd ) return i-start; // found it
+	    }
+	}
+	return -1;
+    }
+
+    // -------------------- utils
+    private int min(int a, int b) {
+	if (a < b) return a;
+	return b;
+    }
+
+}

Added: tomcat/sandbox/java/org/apache/tomcat/util/buf/DateTool.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/tomcat/util/buf/DateTool.java?rev=329081&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/buf/DateTool.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/buf/DateTool.java Thu Oct 27 21:11:42 2005
@@ -0,0 +1,164 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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 org.apache.tomcat.util.buf;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ *  Common place for date utils.
+ *
+ * @deprecated Will be replaced with a more efficient impl, based on
+ * FastDateFormat, with an API using less objects.
+ * @author dac@eng.sun.com
+ * @author Jason Hunter [jch@eng.sun.com]
+ * @author James Todd [gonzo@eng.sun.com]
+ * @author Costin Manolache
+ */
+public class DateTool {
+
+    /** US locale - all HTTP dates are in english
+     */
+    private final static Locale LOCALE_US = Locale.US;
+
+    /** GMT timezone - all HTTP dates are on GMT
+     */
+    public final static TimeZone GMT_ZONE = TimeZone.getTimeZone("GMT");
+
+    /** format for RFC 1123 date string -- "Sun, 06 Nov 1994 08:49:37 GMT"
+     */
+    public final static String RFC1123_PATTERN =
+        "EEE, dd MMM yyyy HH:mm:ss z";
+
+    // format for RFC 1036 date string -- "Sunday, 06-Nov-94 08:49:37 GMT"
+    public final static String rfc1036Pattern =
+        "EEEEEEEEE, dd-MMM-yy HH:mm:ss z";
+
+    // format for C asctime() date string -- "Sun Nov  6 08:49:37 1994"
+    public final static String asctimePattern =
+        "EEE MMM d HH:mm:ss yyyy";
+
+    /** Pattern used for old cookies
+     */
+    private final static String OLD_COOKIE_PATTERN = "EEE, dd-MMM-yyyy HH:mm:ss z";
+
+    /** DateFormat to be used to format dates. Called from MessageBytes
+     */
+    private final static DateFormat rfc1123Format =
+	new SimpleDateFormat(RFC1123_PATTERN, LOCALE_US);
+    
+    /** DateFormat to be used to format old netscape cookies
+	Called from ServerCookie
+     */
+    private final static DateFormat oldCookieFormat =
+	new SimpleDateFormat(OLD_COOKIE_PATTERN, LOCALE_US);
+    
+    private final static DateFormat rfc1036Format =
+	new SimpleDateFormat(rfc1036Pattern, LOCALE_US);
+    
+    private final static DateFormat asctimeFormat =
+	new SimpleDateFormat(asctimePattern, LOCALE_US);
+    
+    static {
+	rfc1123Format.setTimeZone(GMT_ZONE);
+	oldCookieFormat.setTimeZone(GMT_ZONE);
+	rfc1036Format.setTimeZone(GMT_ZONE);
+	asctimeFormat.setTimeZone(GMT_ZONE);
+    }
+ 
+    private static String rfc1123DS;
+    private static long   rfc1123Sec;
+
+    private static StringManager sm =
+        StringManager.getManager("org.apache.tomcat.util.buf.res");
+
+    // Called from MessageBytes.getTime()
+    static long parseDate( MessageBytes value ) {
+     	return parseDate( value.toString());
+    }
+
+    // Called from MessageBytes.setTime
+    /** 
+     */
+    public static String format1123( Date d ) {
+	String dstr=null;
+	synchronized(rfc1123Format) {
+	    dstr = format1123(d, rfc1123Format);
+	}
+	return dstr;
+    } 
+
+    public static String format1123( Date d,DateFormat df ) {
+        long dt = d.getTime() / 1000;
+        if ((rfc1123DS != null) && (dt == rfc1123Sec))
+            return rfc1123DS;
+        rfc1123DS  = df.format( d );
+        rfc1123Sec = dt;
+        return rfc1123DS;
+    } 
+
+
+    // Called from ServerCookie
+    /** 
+     */
+    public static void formatOldCookie( Date d, StringBuffer sb,
+					  FieldPosition fp )
+    {
+	synchronized(oldCookieFormat) {
+	    oldCookieFormat.format( d, sb, fp );
+	}
+    }
+
+    // Called from ServerCookie
+    public static String formatOldCookie( Date d )
+    {
+	String ocf=null;
+	synchronized(oldCookieFormat) {
+	    ocf= oldCookieFormat.format( d );
+	}
+	return ocf;
+    }
+
+    
+    /** Called from HttpServletRequest.getDateHeader().
+	Not efficient - but not very used.
+     */
+    public static long parseDate( String dateString ) {
+	DateFormat [] format = {rfc1123Format,rfc1036Format,asctimeFormat};
+	return parseDate(dateString,format);
+    }
+    public static long parseDate( String dateString, DateFormat []format ) {
+	Date date=null;
+	for(int i=0; i < format.length; i++) {
+	    try {
+		date = format[i].parse(dateString);
+		return date.getTime();
+	    } catch (ParseException e) { }
+	    catch (StringIndexOutOfBoundsException e) { }
+	}
+	String msg = sm.getString("httpDate.pe", dateString);
+	throw new IllegalArgumentException(msg);
+    }
+
+}

Added: tomcat/sandbox/java/org/apache/tomcat/util/buf/HexUtils.java
URL: http://svn.apache.org/viewcvs/tomcat/sandbox/java/org/apache/tomcat/util/buf/HexUtils.java?rev=329081&view=auto
==============================================================================
--- tomcat/sandbox/java/org/apache/tomcat/util/buf/HexUtils.java (added)
+++ tomcat/sandbox/java/org/apache/tomcat/util/buf/HexUtils.java Thu Oct 27 21:11:42 2005
@@ -0,0 +1,193 @@
+/*
+ *  Copyright 1999-2004 The Apache Software Foundation
+ *
+ *  Licensed 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 org.apache.tomcat.util.buf;
+
+import java.io.ByteArrayOutputStream;
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Library of utility methods useful in dealing with converting byte arrays
+ * to and from strings of hexadecimal digits.
+ * Code from Ajp11, from Apache's JServ.
+ *
+ * @author Craig R. McClanahan
+ */
+
+public final class HexUtils {
+
+
+    // -------------------------------------------------------------- Constants
+
+
+    /**
+     *  Table for HEX to DEC byte translation.
+     */
+    public static final int[] DEC = {
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        00, 01, 02, 03, 04, 05, 06, 07,  8,  9, -1, -1, -1, -1, -1, -1,
+        -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+    };
+
+
+    /**
+     * Table for DEC to HEX byte translation.
+     */
+    public static final byte[] HEX = 
+    { (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', 
+      (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'a', (byte) 'b', 
+      (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f' };
+
+
+    /**
+     * The string manager for this package.
+     */
+    private static StringManager sm =
+	StringManager.getManager("org.apache.tomcat.util.buf.res");
+
+
+    // --------------------------------------------------------- Static Methods
+
+
+    /**
+     * Convert a String of hexadecimal digits into the corresponding
+     * byte array by encoding each two hexadecimal digits as a byte.
+     *
+     * @param digits Hexadecimal digits representation
+     *
+     * @exception IllegalArgumentException if an invalid hexadecimal digit
+     *  is found, or the input string contains an odd number of hexadecimal
+     *  digits
+     */
+    public static byte[] convert(String digits) {
+
+	ByteArrayOutputStream baos = new ByteArrayOutputStream();
+	for (int i = 0; i < digits.length(); i += 2) {
+	    char c1 = digits.charAt(i);
+	    if ((i+1) >= digits.length())
+		throw new IllegalArgumentException
+		    (sm.getString("hexUtil.odd"));
+	    char c2 = digits.charAt(i + 1);
+	    byte b = 0;
+	    if ((c1 >= '0') && (c1 <= '9'))
+		b += ((c1 - '0') * 16);
+	    else if ((c1 >= 'a') && (c1 <= 'f'))
+		b += ((c1 - 'a' + 10) * 16);
+	    else if ((c1 >= 'A') && (c1 <= 'F'))
+		b += ((c1 - 'A' + 10) * 16);
+	    else
+		throw new IllegalArgumentException
+		    (sm.getString("hexUtil.bad"));
+	    if ((c2 >= '0') && (c2 <= '9'))
+		b += (c2 - '0');
+	    else if ((c2 >= 'a') && (c2 <= 'f'))
+		b += (c2 - 'a' + 10);
+	    else if ((c2 >= 'A') && (c2 <= 'F'))
+		b += (c2 - 'A' + 10);
+	    else
+		throw new IllegalArgumentException
+		    (sm.getString("hexUtil.bad"));
+	    baos.write(b);
+	}
+	return (baos.toByteArray());
+
+    }
+
+
+    /**
+     * Convert a byte array into a printable format containing a
+     * String of hexadecimal digit characters (two per byte).
+     *
+     * @param bytes Byte array representation
+     */
+    public static String convert(byte bytes[]) {
+
+	StringBuffer sb = new StringBuffer(bytes.length * 2);
+	for (int i = 0; i < bytes.length; i++) {
+	    sb.append(convertDigit((int) (bytes[i] >> 4)));
+	    sb.append(convertDigit((int) (bytes[i] & 0x0f)));
+	}
+	return (sb.toString());
+
+    }
+
+    /**
+     * Convert 4 hex digits to an int, and return the number of converted
+     * bytes.
+     *
+     * @param hex Byte array containing exactly four hexadecimal digits
+     *
+     * @exception IllegalArgumentException if an invalid hexadecimal digit
+     *  is included
+     */
+    public static int convert2Int( byte[] hex ) {
+	// Code from Ajp11, from Apache's JServ
+    
+	// assert b.length==4
+	// assert valid data
+	int len;
+	if(hex.length < 4 ) return 0;
+	if( DEC[hex[0]]<0 )
+	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+	len = DEC[hex[0]];
+	len = len << 4;
+	if( DEC[hex[1]]<0 )
+	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+	len += DEC[hex[1]];
+	len = len << 4;
+	if( DEC[hex[2]]<0 )
+	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+	len += DEC[hex[2]];
+	len = len << 4;
+	if( DEC[hex[3]]<0 )
+	    throw new IllegalArgumentException(sm.getString("hexUtil.bad"));
+	len += DEC[hex[3]];
+	return len;
+    }
+
+
+
+    /**
+     * [Private] Convert the specified value (0 .. 15) to the corresponding
+     * hexadecimal digit.
+     *
+     * @param value Value to be converted
+     */
+    private static char convertDigit(int value) {
+
+	value &= 0x0f;
+	if (value >= 10)
+	    return ((char) (value - 10 + 'a'));
+	else
+	    return ((char) (value + '0'));
+
+    }
+
+
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: svn commit: r329081 [1/2] - /tomcat/sandbox/java/org/apache/tomcat/util/buf/

Posted by Costin Manolache <co...@gmail.com>.
I'll try.

My biggest problem is to do this without breaking too much backward compat.
After we add BB - the rest might be easier.

Costin

On 10/28/05, Remy Maucherat <re...@apache.org> wrote:
> costin@apache.org wrote:
> > Author: costin
> > Date: Thu Oct 27 21:11:42 2005
> > New Revision: 329081
> >
> > URL: http://svn.apache.org/viewcvs?rev=329081&view=rev
> > Log:
> > Import the current version of o.a.t.util.buf, for refactoring. I'm
> > trying to use nio.
> >
> > Added:
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/Ascii.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/B2CConverter.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/Base64.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/ByteChunk.java   (with props)
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/C2BConverter.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/CharChunk.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/DateTool.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/HexUtils.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/MessageBytes.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/StringCache.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/TimeStamp.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/UDecoder.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/UEncoder.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/UTF8Decoder.java
> >     tomcat/sandbox/java/org/apache/tomcat/util/buf/package.html
>
> Along with the CharChunk funcionality update, can CharSequence support
> be added to it as well ? (the JDK regexp engine uses that interface)
>
> Rémy
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
> For additional commands, e-mail: dev-help@tomcat.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org


Re: svn commit: r329081 [1/2] - /tomcat/sandbox/java/org/apache/tomcat/util/buf/

Posted by Remy Maucherat <re...@apache.org>.
costin@apache.org wrote:
> Author: costin
> Date: Thu Oct 27 21:11:42 2005
> New Revision: 329081
> 
> URL: http://svn.apache.org/viewcvs?rev=329081&view=rev
> Log:
> Import the current version of o.a.t.util.buf, for refactoring. I'm
> trying to use nio.
> 
> Added:
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/Ascii.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/B2CConverter.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/Base64.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/ByteChunk.java   (with props)
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/C2BConverter.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/CharChunk.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/DateTool.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/HexUtils.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/MessageBytes.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/StringCache.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/TimeStamp.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/UDecoder.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/UEncoder.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/UTF8Decoder.java
>     tomcat/sandbox/java/org/apache/tomcat/util/buf/package.html

Along with the CharChunk funcionality update, can CharSequence support 
be added to it as well ? (the JDK regexp engine uses that interface)

Rémy

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org