costin 00/12/07 10:34:54 Modified: src/share/org/apache/tomcat/util Ascii.java DateTool.java MessageBytes.java src/share/org/apache/tomcat/util/http Cookies.java Headers.java Parameters.java Added: src/share/org/apache/tomcat/util ByteChunk.java CharChunk.java src/share/org/apache/tomcat/util/collections MultiMapNamesEnumeration.java MultiMapValuesEnumeration.java Log: First refactoring of MessageBytes. The byte[] manipulation moved to ByteChunk, char[] to CharChunk. Both act as "cursors" in an existing byte/char[] ( they can't resize and don't own the buffer ). The int/date conversion is cached, but it's just a cached value, it's no longer "first class". Probably it's better to do the caching and conversion at a higher level anyway. In Headers, removed the Enumerations - as it was a bad choice and confusing. It is still possible to enumerate a MultiMap, for use by the upper layer ( facade ). Revision Changes Path 1.6 +0 -7 jakarta-tomcat/src/share/org/apache/tomcat/util/Ascii.java Index: Ascii.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/Ascii.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- Ascii.java 2000/08/11 06:14:18 1.5 +++ Ascii.java 2000/12/07 18:34:51 1.6 @@ -221,11 +221,4 @@ return n; } - public static boolean equalsIgnoreCase(String str, MessageBytes mB ) { - return mB.equalsIgnoreCase( str ); - } - - - - } 1.7 +7 -4 jakarta-tomcat/src/share/org/apache/tomcat/util/DateTool.java Index: DateTool.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/DateTool.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- DateTool.java 2000/08/28 06:08:18 1.6 +++ DateTool.java 2000/12/07 18:34:51 1.7 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/DateTool.java,v 1.6 2000/08/28 06:08:18 costin Exp $ - * $Revision: 1.6 $ - * $Date: 2000/08/28 06:08:18 $ + * $Header: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/DateTool.java,v 1.7 2000/12/07 18:34:51 costin Exp $ + * $Revision: 1.7 $ + * $Date: 2000/12/07 18:34:51 $ * * ==================================================================== * @@ -131,7 +131,10 @@ StringManager.getManager("org.apache.tomcat.resources"); public static long parseDate( MessageBytes value ) { - String dateString=value.toString(); + return parseDate( value.toString()); + } + + public static long parseDate( String dateString ) { Date date=null; try { date = DateTool.rfc1123Format.parse(dateString); 1.14 +87 -313 jakarta-tomcat/src/share/org/apache/tomcat/util/MessageBytes.java Index: MessageBytes.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/MessageBytes.java,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- MessageBytes.java 2000/11/30 04:58:57 1.13 +++ MessageBytes.java 2000/12/07 18:34:51 1.14 @@ -84,35 +84,22 @@ public static final int T_BYTES = 2; public static final int T_CHARS = 3; - // support for efficient int and date parsing/formating - public static final int T_INT = 4; - public static final int T_DATE = 5; - private int hashCode=0; private boolean hasHashCode=false; private boolean caseSensitive=true; - // byte[] - private byte[] bytes; - private int bytesOff; - private int bytesLen; - private String enc; - private boolean hasByteValue=false; - - // Caching the result of a conversion - - // char[] - private char chars[]; - private int charsOff; - private int charsLen; - private boolean hasCharValue=false; + ByteChunk byteC=new ByteChunk(); + + CharChunk charC=new CharChunk(); // String private String strValue; private boolean hasStrValue=false; // efficient int and date + // XXX used only for headers - shouldn't be + // stored here. private int intValue; private boolean hasIntValue=false; private Date dateValue; @@ -137,26 +124,22 @@ } public boolean isNull() { - return bytes==null && strValue==null; + return byteC.isNull() && charC.isNull() && ! hasStrValue; + // bytes==null && strValue==null; } - public void reset() { - recycle(); - } /** * Resets the message bytes to an uninitialized state. */ public void recycle() { type=T_NULL; - bytes = null; + byteC.recycle(); + charC.recycle(); + strValue=null; - // chars=null; caseSensitive=true; - enc=null; - hasByteValue=false; hasStrValue=false; - hasCharValue=false; hasHashCode=false; hasIntValue=false; hasDateValue=false; @@ -171,28 +154,23 @@ */ public void setBytes(byte[] b, int off, int len) { recycle(); // a new value is set, cached values must reset - bytes = b; - bytesOff = off; - bytesLen = len; + byteC.setBytes( b, off, len ); type=T_BYTES; - hasByteValue=true; } public void setEncoding( String enc ) { - if( hasByteValue ) { - hasCharValue=false; + if( !byteC.isNull() ) { + // if the encoding changes we need to reset the converion results + charC.recycle(); hasStrValue=false; } - this.enc=enc; + byteC.setEncoding(enc); } public void setChars( char[] c, int off, int len ) { recycle(); - chars=c; - charsOff=off; - charsLen=len; + charC.setChars( c, off, len ); type=T_CHARS; - hasCharValue=true; } public void setString( String s ) { @@ -203,97 +181,73 @@ } public void setTime(long t) { + // XXX replace it with a byte[] tool recycle(); if( dateValue==null) dateValue=new Date(t); else dateValue.setTime(t); - type = T_DATE; + strValue=DateTool.rfc1123Format.format(dateValue); + hasStrValue=true; hasDateValue=true; + type=T_STR; } + /** Set the buffer to the representation of an int + */ public void setInt(int i) { + // XXX replace it with a byte[] tool recycle(); - intValue = i; - type = T_INT; + strValue=String.valueOf( i ); + intValue=i; hasIntValue=true; + hasStrValue=true; + type=T_STR; } // -------------------- Conversion and getters -------------------- public String toString() { if( hasStrValue ) return strValue; hasStrValue=true; - + switch (type) { case T_CHARS: - strValue=new String( chars, charsOff, charsLen); + strValue=charC.toString(); return strValue; case T_BYTES: - try { - if( enc==null ) - strValue=toStringUTF8(); - else { - strValue=new String(bytes, bytesOff, bytesLen, enc); - // this will display when we implement I18N - System.out.println("Converting from bytes to string using " + enc + ":" + strValue ); - } - return strValue; - } catch (java.io.UnsupportedEncodingException e) { - return null; // can't happen - } - case T_DATE: - strValue=DateTool.rfc1123Format.format(dateValue); - return strValue; - case T_INT: - strValue=String.valueOf(intValue); + strValue=byteC.toString(); return strValue; } return null; } - - private String toStringUTF8() { - if (null == bytes) { - return null; - } - if( chars==null || bytesLen > chars.length ) { - chars=new char[bytesLen]; - } - - int j=bytesOff; - for( int i=0; i< bytesLen; i++ ) { - chars[i]=(char)bytes[j++]; - } - charsLen=bytesLen; - charsOff=0; - hasCharValue=true; - return new String( chars, 0, bytesLen); - } - + public long getTime() { - if( hasDateValue ) { + if( hasDateValue ) { if( dateValue==null) return -1; return dateValue.getTime(); - } - - long l=DateTool.parseDate( this ); - if( dateValue==null) - dateValue=new Date(l); - else - dateValue.setTime(l); - hasDateValue=true; - return l; + } + + long l=DateTool.parseDate( this ); + if( dateValue==null) + dateValue=new Date(l); + else + dateValue.setTime(l); + hasDateValue=true; + return l; } + - public int getInt() + /** Convert the buffer to an int, cache the value + */ + public int getInt() { if( hasIntValue ) return intValue; switch (type) { case T_BYTES: - intValue=Ascii.parseInt(bytes, bytesOff, - bytesLen); + intValue=byteC.getInt(); break; default: intValue=Integer.parseInt(toString()); @@ -310,47 +264,38 @@ /** * Returns the message bytes. */ - public byte[] getBytes() { - return bytes; + public ByteChunk getByteChunk() { + return byteC; } - public char[] getChars() - { - if( hasCharValue ) { - return chars; + public CharChunk getCharChunk() { + return charC; + } + + // Convert to bytes !!! + public void toBytes() { + // XXX todo - not used + } + + public void toChars() { + if( ! charC.isNull() ) { + return; } + // inefficient toString(); - hasCharValue=true; - chars=strValue.toCharArray(); - charsLen=chars.length; - charsOff=0; - return chars; + char cc[]=strValue.toCharArray(); + charC.setChars(cc, 0, cc.length); } - /** - * Returns the start offset of the bytes. - */ - public int getOffset() { - if(type==T_BYTES) - return bytesOff; - if(type==T_CHARS) { - if( ! hasCharValue ) - getChars(); - return charsOff; - } - return 0; - } /** - * Returns the length of the bytes. + * Returns the length of the buffer. */ public int getLength() { if(type==T_BYTES) - return bytesLen; + return byteC.getLength(); if(type==T_CHARS) { - if( ! hasCharValue ) - getChars(); - return charsLen; + return charC.getLength(); } if(type==T_STR) return strValue.length(); @@ -369,39 +314,13 @@ if( ! caseSensitive ) return equalsIgnoreCase( s ); switch (type) { - case T_INT: - case T_DATE: - toString(); - // now strValue is valid case T_STR: if( strValue==null && s!=null) return false; return strValue.equals( s ); case T_CHARS: - char[] c = chars; - int len = charsLen; - if (c == null || len != s.length()) { - return false; - } - int off = charsOff; - for (int i = 0; i < len; i++) { - if (c[off++] != s.charAt(i)) { - return false; - } - } - return true; + return charC.equals( s ); case T_BYTES: - byte[] b = bytes; - int blen = bytesLen; - if (b == null || blen != s.length()) { - return false; - } - int boff = bytesOff; - for (int i = 0; i < blen; i++) { - if (b[boff++] != s.charAt(i)) { - return false; - } - } - return true; + return byteC.equals( s ); default: return false; } @@ -414,38 +333,13 @@ */ public boolean equalsIgnoreCase(String s) { switch (type) { - case T_INT: - case T_DATE: - toString(); // now strValue is set case T_STR: if( strValue==null && s!=null) return false; return strValue.equalsIgnoreCase( s ); case T_CHARS: - char[] c = chars; - int len = charsLen; - if (c == null || len != s.length()) { - return false; - } - int off = charsOff; - for (int i = 0; i < len; i++) { - if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) { - return false; - } - } - return true; + return charC.equalsIgnoreCase( s ); case T_BYTES: - byte[] b = bytes; - int blen = bytesLen; - if (b == null || blen != s.length()) { - return false; - } - int boff = bytesOff; - for (int i = 0; i < blen; i++) { - if (Ascii.toLower(b[boff++]) != Ascii.toLower(s.charAt(i))) { - return false; - } - } - return true; + return byteC.equalsIgnoreCase( s ); default: return false; } @@ -453,9 +347,6 @@ public boolean equals(MessageBytes mb) { switch (type) { - case T_INT: - case T_DATE: - toString(); // now strValue is set case T_STR: return mb.equals( strValue ); } @@ -471,61 +362,18 @@ // Deal with the 4 cases ( in fact 3, one is simetric) if( mb.type == T_CHARS && type==T_CHARS ) { - char b1[]=chars; - char b2[]=mb.chars; - if (b1== null || b2==null || mb.charsLen != charsLen) { - return false; - } - int off1 = charsOff; - int off2 = mb.charsOff; - int len=charsLen; - while ( len-- > 0) { - if (b1[off1++] != b2[off2++]) { - return false; - } - } - return true; - } + return charC.equals( mb.charC ); + } if( mb.type==T_BYTES && type== T_BYTES ) { - byte b1[]=bytes; - byte b2[]=mb.bytes; - if (b1== null || b2==null || mb.bytesLen != bytesLen) { - return false; - } - int off1 = bytesOff; - int off2 = mb.bytesOff; - int len=bytesLen; - while ( len-- > 0) { - if (b1[off1++] != b2[off2++]) { - return false; - } - } - return true; + return byteC.equals( mb.byteC ); } - - // char/byte or byte/char - MessageBytes mbB=this; - MessageBytes mbC=mb; - - if( type == T_CHARS && mb.type==T_BYTES ) { - mbB=mb; - mbC=this; + if( mb.type== T_CHARS && type== T_BYTES ) { + return byteC.equals( mb.charC ); } - - byte b1[]=mbB.bytes; - char b2[]=mbC.chars; - if (b1== null || b2==null || mbB.bytesLen != mbC.charsLen) { - return false; + if( mb.type== T_BYTES && type== T_CHARS ) { + return mb.byteC.equals( charC ); } - int off1 = mbB.bytesOff; - int off2 = mbC.charsOff; - int len=mbB.bytesLen; - - while ( len-- > 0) { - if ( (char)b1[off1++] != b2[off2++]) { - return false; - } - } + // can't happen return true; } @@ -539,36 +387,9 @@ case T_STR: return strValue.startsWith( s ); case T_CHARS: - char[] c = chars; - int len = s.length(); - if (c == null || len > charsLen) { - return false; - } - int off = charsOff; - for (int i = 0; i < len; i++) { - if (c[off++] != s.charAt(i)) { - return false; - } - } - return true; + return charC.startsWith( s ); case T_BYTES: - byte[] b = bytes; - int blen = s.length(); - if (b == null || blen > bytesLen) { - return false; - } - int boff = bytesOff; - for (int i = 0; i < blen; i++) { - if (b[boff++] != s.charAt(i)) { - return false; - } - } - return true; - case T_INT: - case T_DATE: - String s1=toString(); - if( s1==null && s!=null) return false; - return s1.startsWith( s ); + return byteC.startsWith( s ); default: return false; } @@ -594,22 +415,16 @@ private int hash() { int code=0; switch (type) { - case T_INT: - case T_DATE: - String s1=toString(); - // continue with T_STR - it now have a strValue case T_STR: + // We need to use the same hash function for (int i = 0; i < strValue.length(); i++) { code = code * 37 + strValue.charAt( i ); } return code; case T_CHARS: - for (int i = charsOff; i < charsOff + charsLen; i++) { - code = code * 37 + chars[i]; - } - return code; + return charC.hash(); case T_BYTES: - return hashBytes( bytes, bytesOff, bytesLen); + return byteC.hash(); default: return 0; } @@ -619,49 +434,20 @@ private int hashIgnoreCase() { int code=0; switch (type) { - case T_INT: - case T_DATE: - String s1=toString(); - // continue with T_STR - it now have a strValue case T_STR: for (int i = 0; i < strValue.length(); i++) { code = code * 37 + Ascii.toLower(strValue.charAt( i )); } return code; case T_CHARS: - for (int i = charsOff; i < charsOff + charsLen; i++) { - code = code * 37 + Ascii.toLower(chars[i]); - } - return code; + return charC.hashIgnoreCase(); case T_BYTES: - return hashBytesIC( bytes, bytesOff, bytesLen ); + return byteC.hashIgnoreCase(); default: return 0; } } - private static int hashBytes( byte bytes[], int bytesOff, int bytesLen ) { - int max=bytesOff+bytesLen; - byte bb[]=bytes; - int code=0; - for (int i = bytesOff; i < max ; i++) { - code = code * 37 + bb[i]; - } - return code; - } - - private static int hashBytesIC( byte bytes[], int bytesOff, - int bytesLen ) - { - int max=bytesOff+bytesLen; - byte bb[]=bytes; - int code=0; - for (int i = bytesOff; i < max ; i++) { - code = code * 37 + Ascii.toLower(bb[i]); - } - return code; - } - public int indexOf(char c) { return indexOf( c, 0); } @@ -672,24 +458,12 @@ */ public int indexOf(char c, int starting) { switch (type) { - case T_INT: - case T_DATE: - String s1=toString(); - // continue with T_STR - it now have a strValue case T_STR: return strValue.indexOf( c, starting ); case T_CHARS: - for (int i = charsOff+starting; i < charsOff + charsLen; i++) { - if( c == chars[i] ) return i; - } - return -1; + return charC.indexOf( c, starting); case T_BYTES: - int max=bytesOff+bytesLen; - byte bb[]=bytes; - for (int i = bytesOff+starting; i < max ; i++) { - if( (byte)c == bb[i]) return i; - } - return -1; + return byteC.indexOf( c, starting ); default: return -1; } 1.1 jakarta-tomcat/src/share/org/apache/tomcat/util/ByteChunk.java Index: ByteChunk.java =================================================================== /* * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.tomcat.util; import java.text.*; import java.util.*; import java.io.Serializable; /** * This class is used to represent a chunk of bytes, and * utilities to manipulate byte[]. * * 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. * * @author [EMAIL PROTECTED] * @author James Todd [[EMAIL PROTECTED]] * @author Costin Manolache */ public final class ByteChunk implements Cloneable, Serializable { // byte[] private byte[] bytes; private int bytesOff; private int bytesLen; private String enc; private boolean isSet=false; /** * Creates a new, uninitialized ByteChunk object. */ public ByteChunk() { } public ByteChunk getClone() { try { return (ByteChunk)this.clone(); } catch( Exception ex) { return null; } } public boolean isNull() { return ! isSet; // bytes==null; } /** * Resets the message bytes to an uninitialized state. */ public void recycle() { bytes = null; enc=null; isSet=false; } /** * 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) { bytes = b; bytesOff = off; bytesLen = len; isSet=true; } public void setEncoding( String enc ) { this.enc=enc; } // convert an int to byte[] public void setInt(int i) { // XXX TODO } // -------------------- Conversion and getters -------------------- public static boolean isUTF8Compatible(String enc) { if( enc==null ) return true; // add known encodings return false; } public String toString() { if (null == bytes) { return null; } String strValue=null; try { if( enc==null ) strValue=toStringUTF8(); else { strValue=new String(bytes, bytesOff, bytesLen, enc); // this will display when we implement I18N System.out.println("Converting from bytes to string using " + enc + ":" + strValue ); } return strValue; } catch (java.io.UnsupportedEncodingException e) { return null; // can't happen } } private char[] conversionBuff; private String toStringUTF8() { if( conversionBuff==null || bytesLen > conversionBuff.length ) { conversionBuff=new char[bytesLen]; } int j=bytesOff; for( int i=0; i< bytesLen; i++ ) { conversionBuff[i]=(char)bytes[j++]; } return new String( conversionBuff, 0, bytesLen); } public int getInt() { return Ascii.parseInt(bytes, bytesOff,bytesLen); } // -------------------- /** * Returns the message bytes. */ public byte[] getBytes() { return bytes; } /** * Returns the start offset of the bytes. */ public int getOffset() { return bytesOff; } /** * Returns the length of the bytes. */ public int getLength() { return bytesLen; } // -------------------- 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 = bytes; int blen = bytesLen; if (b == null || blen != s.length()) { return false; } int boff = bytesOff; 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 = bytes; int blen = bytesLen; if (b == null || blen != s.length()) { return false; } int boff = bytesOff; 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.getOffset(), bb.getLength()); } public boolean equals( byte b2[], int off2, int len2) { byte b1[]=bytes; if( b1==null && b2==null ) return true; int len=bytesLen; if ( len2 != len || b1==null || b2==null ) return false; int off1 = bytesOff; while ( len-- > 0) { if (b1[off1++] != b2[off2++]) { return false; } } return true; } public boolean equals( CharChunk cc ) { return equals( cc.getChars(), cc.getOffset(), cc.getLength()); } public boolean equals( char c2[], int off2, int len2) { // XXX works only for enc compatible with ASCII/UTF !!! byte b1[]=bytes; if( c2==null && b1==null ) return true; if (b1== null || c2==null || bytesLen != len2 ) { return false; } int off1 = bytesOff; int len=bytesLen; 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 = bytes; int blen = s.length(); if (b == null || blen > bytesLen) { return false; } int boff = bytesOff; for (int i = 0; i < blen; i++) { if (b[boff++] != s.charAt(i)) { return false; } } return true; } // -------------------- Hash code -------------------- // normal hash. public int hash() { return hashBytes( bytes, bytesOff, bytesLen); } // hash ignoring case public int hashIgnoreCase() { return hashBytesIC( bytes, bytesOff, bytesLen ); } private static int hashBytes( byte bytes[], int bytesOff, int bytesLen ) { int max=bytesOff+bytesLen; byte bb[]=bytes; int code=0; for (int i = bytesOff; i < max ; i++) { code = code * 37 + bb[i]; } return code; } private static int hashBytesIC( byte bytes[], int bytesOff, int bytesLen ) { int max=bytesOff+bytesLen; byte bb[]=bytes; int code=0; for (int i = bytesOff; i < max ; i++) { code = code * 37 + Ascii.toLower(bb[i]); } return code; } /** * Returns true if the message bytes starts with the specified string. * @param s the string */ public int indexOf(char c, int starting) { // Works only for UTF int max=bytesOff+bytesLen; byte bb[]=bytes; for (int i = bytesOff+starting; i < max ; i++) { if( (byte)c == bb[i]) return i; } return -1; } /** Find a character, no side effects. * @returns 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. * @returns 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 * @returns 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; } } 1.1 jakarta-tomcat/src/share/org/apache/tomcat/util/CharChunk.java Index: CharChunk.java =================================================================== /* * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.tomcat.util; import java.text.*; import java.util.*; 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 [EMAIL PROTECTED] * @author James Todd [[EMAIL PROTECTED]] * @author Costin Manolache */ public final class CharChunk implements Cloneable, Serializable { // char[] private char chars[]; private int charsOff; private int charsLen; private boolean isSet=false; /** * Creates a new, uninitialized CharChunk object. */ public CharChunk() { } public CharChunk getClone() { try { return (CharChunk)this.clone(); } catch( Exception ex) { return null; } } public boolean isNull() { return !isSet; } /** * Resets the message bytes to an uninitialized state. */ public void recycle() { // chars=null; isSet=false; } public void setChars( char[] c, int off, int len ) { recycle(); chars=c; charsOff=off; charsLen=len; } // -------------------- Conversion and getters -------------------- public String toString() { if( chars==null ) return null; return new String( chars, charsOff, charsLen); } public int getInt() { return Ascii.parseInt(chars, charsOff, charsLen); } public char[] getChars() { return chars; } /** * Returns the start offset of the bytes. */ public int getOffset() { return charsOff; } /** * Returns the length of the bytes. */ public int getLength() { return charsLen; } // -------------------- 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 = chars; int len = charsLen; if (c == null || len != s.length()) { return false; } int off = charsOff; 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 = chars; int len = charsLen; if (c == null || len != s.length()) { return false; } int off = charsOff; 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[]=chars; if( b1==null && b2==null ) return true; if (b1== null || b2==null || charsLen != len2) { return false; } int off1 = charsOff; int len=charsLen; while ( len-- > 0) { if (b1[off1++] != b2[off2++]) { return false; } } return true; } public boolean equals(byte b2[], int off2, int len2) { char b1[]=chars; if( b2==null && b1==null ) return true; if (b1== null || b2==null || charsLen != len2) { return false; } int off1 = charsOff; int len=charsLen; 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 = chars; int len = s.length(); if (c == null || len > charsLen) { return false; } int off = charsOff; for (int i = 0; i < len; i++) { if (c[off++] != s.charAt(i)) { return false; } } return true; } // -------------------- Hash code -------------------- // normal hash. public int hash() { int code=0; for (int i = charsOff; i < charsOff + charsLen; i++) { code = code * 37 + chars[i]; } return code; } // hash ignoring case public int hashIgnoreCase() { int code=0; for (int i = charsOff; i < charsOff + charsLen; i++) { code = code * 37 + Ascii.toLower(chars[i]); } return code; } public int indexOf(char c) { return indexOf( c, charsOff); } /** * Returns true if the message bytes starts with the specified string. * @param s the string */ public int indexOf(char c, int starting) { for (int i = charsOff+starting; i < charsOff + charsLen; i++) { if( c == chars[i] ) return i; } return -1; } } 1.1 jakarta-tomcat/src/share/org/apache/tomcat/util/collections/MultiMapNamesEnumeration.java Index: MultiMapNamesEnumeration.java =================================================================== /* * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.tomcat.util.collections; import org.apache.tomcat.util.collections.*; import org.apache.tomcat.util.MessageBytes; import java.io.*; import java.util.*; import java.text.*; /** Enumerate the distinct header names. Each nextElement() is O(n) ( a comparation is done with all previous elements ). This is less frequesnt than add() - we want to keep add O(1). */ public final class MultiMapNamesEnumeration implements Enumeration { int pos; int size; String next; MultiMap headers; // toString and unique options are not implemented - // we allways to toString and unique. /** Create a new multi-map enumeration. * @param headers the collection to enumerate * @param toString convert each name to string * @param unique return only unique names */ MultiMapNamesEnumeration(MultiMap headers, boolean toString, boolean unique) { this.headers=headers; pos=0; size = headers.size(); findNext(); } private void findNext() { next=null; for( ; pos< size; pos++ ) { next=headers.getName( pos ).toString(); for( int j=0; j<pos ; j++ ) { if( headers.getName( j ).equalsIgnoreCase( next )) { // duplicate. next=null; break; } } if( next!=null ) { // it's not a duplicate break; } } // next time findNext is called it will try the // next element pos++; } public boolean hasMoreElements() { return next!=null; } public Object nextElement() { String current=next; findNext(); return current; } } 1.1 jakarta-tomcat/src/share/org/apache/tomcat/util/collections/MultiMapValuesEnumeration.java Index: MultiMapValuesEnumeration.java =================================================================== /* * ==================================================================== * * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.tomcat.util.collections; import org.apache.tomcat.util.collections.*; import org.apache.tomcat.util.MessageBytes; import java.io.*; import java.util.*; import java.text.*; /** Enumerate the values for a (possibly ) multiple * value element. */ class MultiMapValuesEnumeration implements Enumeration { int pos; int size; MessageBytes next; MultiMap headers; String name; MultiMapValuesEnumeration(MultiMap headers, String name, boolean toString) { this.name=name; this.headers=headers; pos=0; size = headers.size(); findNext(); } private void findNext() { next=null; for( ; pos< size; pos++ ) { MessageBytes n1=headers.getName( pos ); if( n1.equalsIgnoreCase( name )) { next=headers.getValue( pos ); break; } } pos++; } public boolean hasMoreElements() { return next!=null; } public Object nextElement() { MessageBytes current=next; findNext(); return current.toString(); } } 1.6 +5 -3 jakarta-tomcat/src/share/org/apache/tomcat/util/http/Cookies.java Index: Cookies.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/http/Cookies.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- Cookies.java 2000/12/01 21:39:21 1.5 +++ Cookies.java 2000/12/07 18:34:53 1.6 @@ -61,6 +61,7 @@ import org.apache.tomcat.util.collections.*; import org.apache.tomcat.util.MessageBytes; +import org.apache.tomcat.util.ByteChunk; import org.apache.tomcat.util.MimeHeaders; import org.apache.tomcat.util.DateTool; @@ -167,9 +168,10 @@ // Uncomment to test the new parsing code if( cookieValue.getType() == MessageBytes.T_BYTES ) { if( dbg>0 ) log( "Parsing b[]: " + cookieValue.toString()); - processCookieHeader( cookieValue.getBytes(), - cookieValue.getOffset(), - cookieValue.getLength()); + ByteChunk bc=cookieValue.getByteChunk(); + processCookieHeader( bc.getBytes(), + bc.getOffset(), + bc.getLength()); } else { if( dbg>0 ) log( "Parsing S: " + cookieValue.toString()); processCookieHeader( cookieValue.toString() ); 1.2 +1 -138 jakarta-tomcat/src/share/org/apache/tomcat/util/http/Headers.java Index: Headers.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/http/Headers.java,v retrieving revision 1.1 retrieving revision 1.2 diff -u -r1.1 -r1.2 --- Headers.java 2000/11/30 17:42:49 1.1 +++ Headers.java 2000/12/07 18:34:53 1.2 @@ -104,21 +104,6 @@ return super.findIgnoreCase( name, starting ); } - // -------------------- -------------------- - - /** - * Returns an enumeration of strings representing the header field names. - * Field names may appear multiple times in this enumeration, indicating - * that multiple fields with that name exist in this header. - */ - public Enumeration names() { - return new NamesEnumerator(this); - } - - public Enumeration values(String name) { - return new ValuesEnumerator(this, name); - } - // -------------------- Adding headers -------------------- /** Create a new named header , return the MessageBytes @@ -168,6 +153,7 @@ // bad shortcut - it'll convert to string ( too early probably, // encoding is guessed very late ) + public String getHeader(String name) { int pos=findIgnoreCase( name, 0 ); if( pos <0 ) return null; @@ -190,126 +176,3 @@ } } -/** Enumerate the distinct header names. - Each nextElement() is O(n) ( a comparation is - done with all previous elements ). - - This is less frequesnt than add() - - we want to keep add O(1). -*/ -class NamesEnumerator implements Enumeration { - int pos; - int size; - String next; - MultiMap headers; - - NamesEnumerator(MultiMap headers) { - this.headers=headers; - pos=0; - size = headers.size(); - findNext(); - } - - private void findNext() { - next=null; - for( ; pos< size; pos++ ) { - next=headers.getName( pos ).toString(); - for( int j=0; j<pos ; j++ ) { - if( headers.getName( j ).equalsIgnoreCase( next )) { - // duplicate. - next=null; - break; - } - } - if( next!=null ) { - // it's not a duplicate - break; - } - } - // next time findNext is called it will try the - // next element - pos++; - } - - public boolean hasMoreElements() { - return next!=null; - } - - public Object nextElement() { - String current=next; - findNext(); - return current; - } -} - -/** Enumerate the values for a (possibly ) multiple - value element. -*/ -class ValuesEnumerator implements Enumeration { - int pos; - int size; - MessageBytes next; - MultiMap headers; - String name; - - ValuesEnumerator(MultiMap headers, String name) { - this.name=name; - this.headers=headers; - pos=0; - size = headers.size(); - findNext(); - } - - private void findNext() { - next=null; - for( ; pos< size; pos++ ) { - MessageBytes n1=headers.getName( pos ); - if( n1.equalsIgnoreCase( name )) { - next=headers.getValue( pos ); - break; - } - } - pos++; - } - - public boolean hasMoreElements() { - return next!=null; - } - - public Object nextElement() { - MessageBytes current=next; - findNext(); - return current.toString(); - } -} - -class MimeHeaderField { - // multiple headers with same name - a linked list will - // speed up name enumerations and search ( both cpu and - // GC) - MimeHeaderField next; - MimeHeaderField prev; - - protected final MessageBytes nameB = new MessageBytes(); - protected final MessageBytes valueB = new MessageBytes(); - - /** - * Creates a new, uninitialized header field. - */ - public MimeHeaderField() { - } - - public void recycle() { - nameB.recycle(); - valueB.recycle(); - next=null; - } - - public MessageBytes getName() { - return nameB; - } - - public MessageBytes getValue() { - return valueB; - } -} 1.3 +8 -4 jakarta-tomcat/src/share/org/apache/tomcat/util/http/Parameters.java Index: Parameters.java =================================================================== RCS file: /home/cvs/jakarta-tomcat/src/share/org/apache/tomcat/util/http/Parameters.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- Parameters.java 2000/12/02 08:27:03 1.2 +++ Parameters.java 2000/12/07 18:34:54 1.3 @@ -182,11 +182,15 @@ if( data==null || data.getLength() <= 0 ) return; if( data.getType() == MessageBytes.T_BYTES ) { - processParameters( data.getBytes(), data.getOffset(), - data.getLength()); + ByteChunk bc=data.getByteChunk(); + processParameters( bc.getBytes(), bc.getOffset(), + bc.getLength()); } else { - processParameters( data.getChars(), data.getOffset(), - data.getLength()); + if (data.getType()!= MessageBytes.T_CHARS ) + data.toChars(); + CharChunk cc=data.getCharChunk(); + processParameters( cc.getChars(), cc.getOffset(), + cc.getLength()); } }