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