diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index f59548589d7..67c95111e16 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -39,12 +39,14 @@ import java.util.Set; import java.util.StringTokenizer; import java.util.TimeZone; +import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.DateCache; import org.eclipse.jetty.util.LazyList; import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.StringMap; import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.Trie; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -1007,7 +1009,7 @@ public class HttpFields implements Iterable private static final Float __one = new Float("1.0"); private static final Float __zero = new Float("0.0"); - private static final StringMap __qualities = new StringMap<>(); + private static final Trie __qualities = new ArrayTernaryTrie<>(); static { __qualities.put("*", __one); diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java b/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java index d8e25434d36..0385c8ed663 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/PathMap.java @@ -22,11 +22,11 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.StringTokenizer; +import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.LazyList; -import org.eclipse.jetty.util.StringMap; +import org.eclipse.jetty.util.Trie; import org.eclipse.jetty.util.URIUtil; /* ------------------------------------------------------------ */ @@ -78,14 +78,13 @@ public class PathMap extends HashMap } /* --------------------------------------------------------------- */ - final StringMap> _prefixMap=new StringMap<>(); - final StringMap> _suffixMap=new StringMap<>(); - final StringMap> _exactMap=new StringMap<>(); + Trie> _prefixMap=new ArrayTernaryTrie<>(false); + Trie> _suffixMap=new ArrayTernaryTrie<>(false); + final Map> _exactMap=new HashMap<>(); - List _defaultSingletonList=null; + List> _defaultSingletonList=null; MappedEntry _prefixDefault=null; MappedEntry _default=null; - final Set _entrySet; boolean _nodefault=false; /* --------------------------------------------------------------- */ @@ -111,7 +110,6 @@ public class PathMap extends HashMap { super(capacity); _nodefault=noDefault; - _entrySet=entrySet(); } /* --------------------------------------------------------------- */ @@ -120,7 +118,6 @@ public class PathMap extends HashMap public PathMap(Map m) { putAll(m); - _entrySet=entrySet(); } /* --------------------------------------------------------------- */ @@ -163,12 +160,15 @@ public class PathMap extends HashMap { String mapped=spec.substring(0,spec.length()-2); entry.setMapped(mapped); - _prefixMap.put(mapped,entry); - _exactMap.put(mapped,entry); - _exactMap.put(spec.substring(0,spec.length()-1),entry); + while (!_prefixMap.put(mapped,entry)) + _prefixMap=new ArrayTernaryTrie<>((ArrayTernaryTrie>)_prefixMap,1.5); } else if (spec.startsWith("*.")) - _suffixMap.put(spec.substring(2),entry); + { + String suffix=spec.substring(2); + while(!_suffixMap.put(suffix,entry)) + _suffixMap=new ArrayTernaryTrie<>((ArrayTernaryTrie>)_suffixMap,1.5); + } else if (spec.equals(URIUtil.SLASH)) { if (_nodefault) @@ -176,8 +176,7 @@ public class PathMap extends HashMap else { _default=entry; - _defaultSingletonList= - Collections.singletonList(_default); + _defaultSingletonList=Collections.singletonList(_default); } } else @@ -228,17 +227,22 @@ public class PathMap extends HashMap } // try exact match - entry=_exactMap.get(path,0,l); + entry=_exactMap.get(path); if (entry!=null) return entry; // prefix search int i=l; - while((i=path.lastIndexOf('/',i-1))>=0) + final Trie> prefix_map=_prefixMap; + while(i>=0) { - entry=_prefixMap.get(path,0,i); - if (entry!=null) + entry=prefix_map.getBest(path,0,i); + if (entry==null) + break; + String key = entry.getKey(); + if (key.length()-2>=path.length() || path.charAt(key.length()-2)=='/') return entry; + i=key.length()-3; } // Prefix Default @@ -247,9 +251,10 @@ public class PathMap extends HashMap // Extension search i=0; + final Trie> suffix_map=_suffixMap; while ((i=path.indexOf('.',i+1))>0) { - entry=_suffixMap.get(path,i+1,l-i-1); + entry=suffix_map.get(path,i+1,l-i-1); if (entry!=null) return entry; } @@ -266,26 +271,31 @@ public class PathMap extends HashMap */ public Object getLazyMatches(String path) { - MappedEntry entry; + MappedEntry entry; Object entries=null; if (path==null) return LazyList.getList(entries); - int l=path.length(); - // try exact match - entry=_exactMap.get(path,0,l); + entry=_exactMap.get(path); if (entry!=null) entries=LazyList.add(entries,entry); // prefix search - int i=l-1; - while((i=path.lastIndexOf('/',i-1))>=0) + int l=path.length(); + int i=l; + final Trie> prefix_map=_prefixMap; + while(i>=0) { - entry=_prefixMap.get(path,0,i); - if (entry!=null) + entry=prefix_map.getBest(path,0,i); + if (entry==null) + break; + String key = entry.getKey(); + if (key.length()-2>=path.length() || path.charAt(key.length()-2)=='/') entries=LazyList.add(entries,entry); + + i=key.length()-3; } // Prefix Default @@ -294,9 +304,10 @@ public class PathMap extends HashMap // Extension search i=0; + final Trie> suffix_map=_suffixMap; while ((i=path.indexOf('.',i+1))>0) { - entry=_suffixMap.get(path,i+1,l-i-1); + entry=suffix_map.get(path,i+1,l-i-1); if (entry!=null) entries=LazyList.add(entries,entry); } @@ -320,7 +331,7 @@ public class PathMap extends HashMap * @param path Path to match * @return List of Map.Entry instances key=pathSpec */ - public List getMatches(String path) + public List> getMatches(String path) { return LazyList.getList(getLazyMatches(path)); } @@ -333,7 +344,7 @@ public class PathMap extends HashMap */ public boolean containsMatch(String path) { - MappedEntry match = getMatch(path); + MappedEntry match = getMatch(path); return match!=null && !match.equals(_default); } @@ -347,11 +358,7 @@ public class PathMap extends HashMap if (spec.equals("/*")) _prefixDefault=null; else if (spec.endsWith("/*")) - { _prefixMap.remove(spec.substring(0,spec.length()-2)); - _exactMap.remove(spec.substring(0,spec.length()-1)); - _exactMap.remove(spec.substring(0,spec.length()-2)); - } else if (spec.startsWith("*.")) _suffixMap.remove(spec.substring(2)); else if (spec.equals(URIUtil.SLASH)) @@ -370,8 +377,8 @@ public class PathMap extends HashMap public void clear() { _exactMap.clear(); - _prefixMap.clear(); - _suffixMap.clear(); + _prefixMap=new ArrayTernaryTrie<>(false); + _suffixMap=new ArrayTernaryTrie<>(false); _default=null; _defaultSingletonList=null; super.clear(); @@ -382,18 +389,18 @@ public class PathMap extends HashMap * @return true if match. */ public static boolean match(String pathSpec, String path) - throws IllegalArgumentException - { + throws IllegalArgumentException + { return match(pathSpec, path, false); - } + } /* --------------------------------------------------------------- */ /** * @return true if match. */ public static boolean match(String pathSpec, String path, boolean noDefault) - throws IllegalArgumentException - { + throws IllegalArgumentException + { char c = pathSpec.charAt(0); if (c=='/') { @@ -407,7 +414,7 @@ public class PathMap extends HashMap return path.regionMatches(path.length()-pathSpec.length()+1, pathSpec,1,pathSpec.length()-1); return false; - } + } /* --------------------------------------------------------------- */ private static boolean isPathWildcardMatch(String pathSpec, String path) diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java index cda9f11e63b..36e9b2e1581 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/PathMapTest.java @@ -44,6 +44,7 @@ public class PathMapTest p.put("/", "8"); p.put("/XXX:/YYY", "9"); p.put("", "10"); + p.put("/\u20ACuro/*", "11"); String[][] tests = { { "/abs/path", "1"}, @@ -62,7 +63,9 @@ public class PathMapTest { "/suffix/path.tar.gz", "6"}, { "/suffix/path.gz", "7"}, { "/animal/path.gz", "5"}, - { "/Other/path", "8"},}; + { "/Other/path", "8"}, + { "/\u20ACuro/path", "11"}, + }; for (String[] test : tests) { diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java index caa37762497..d4298be1d9a 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/MsieSslRule.java @@ -25,7 +25,9 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; +import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.StringMap; +import org.eclipse.jetty.util.Trie; /** * MSIE (Microsoft Internet Explorer) SSL Rule. @@ -38,7 +40,7 @@ public class MsieSslRule extends Rule { private static final int IEv5 = '5'; private static final int IEv6 = '6'; - private static StringMap __IE6_BadOS = new StringMap(); + private static Trie __IE6_BadOS = new ArrayTernaryTrie<>(); { __IE6_BadOS.put("NT 5.01", Boolean.TRUE); __IE6_BadOS.put("NT 5.0",Boolean.TRUE); diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java b/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java index 4867016204d..9e290ab2ec3 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java @@ -234,27 +234,19 @@ public class PartialRFC2616Test } @Test - public void test3_9() + public void test3_9() throws Exception { - try - { - HttpFields fields=new HttpFields(); + HttpFields fields=new HttpFields(); - fields.put("Q","bbb;q=0.5,aaa,ccc;q=0.002,d;q=0,e;q=0.0001,ddd;q=0.001,aa2,abb;q=0.7"); - Enumeration qualities=fields.getValues("Q",", \t"); - List list=HttpFields.qualityList(qualities); - assertEquals("Quality parameters","aaa",HttpFields.valueParameters(list.get(0),null)); - assertEquals("Quality parameters","aa2",HttpFields.valueParameters(list.get(1),null)); - assertEquals("Quality parameters","abb",HttpFields.valueParameters(list.get(2),null)); - assertEquals("Quality parameters","bbb",HttpFields.valueParameters(list.get(3),null)); - assertEquals("Quality parameters","ccc",HttpFields.valueParameters(list.get(4),null)); - assertEquals("Quality parameters","ddd",HttpFields.valueParameters(list.get(5),null)); - } - catch (Exception e) - { - e.printStackTrace(); - assertTrue(false); - } + fields.put("Q","bbb;q=0.5,aaa,ccc;q=0.002,d;q=0,e;q=0.0001,ddd;q=0.001,aa2,abb;q=0.7"); + Enumeration qualities=fields.getValues("Q",", \t"); + List list=HttpFields.qualityList(qualities); + assertEquals("Quality parameters","aaa",HttpFields.valueParameters(list.get(0),null)); + assertEquals("Quality parameters","aa2",HttpFields.valueParameters(list.get(1),null)); + assertEquals("Quality parameters","abb",HttpFields.valueParameters(list.get(2),null)); + assertEquals("Quality parameters","bbb",HttpFields.valueParameters(list.get(3),null)); + assertEquals("Quality parameters","ccc",HttpFields.valueParameters(list.get(4),null)); + assertEquals("Quality parameters","ddd",HttpFields.valueParameters(list.get(5),null)); } @Test diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java new file mode 100644 index 00000000000..dd2f53c933d --- /dev/null +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/AbstractTrie.java @@ -0,0 +1,103 @@ +// +// ======================================================================== +// Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.util; + +import java.nio.ByteBuffer; + + +/* ------------------------------------------------------------ */ +/** Abstract Trie implementation. + *

Provides some common implementations, which may not be the most + * efficient. For byte operations, the assumption is made that the charset + * is ISO-8859-1

+ * @param + */ +public abstract class AbstractTrie implements Trie +{ + final boolean _caseInsensitive; + + protected AbstractTrie(boolean insensitive) + { + _caseInsensitive=insensitive; + } + + @Override + public boolean put(V v) + { + return put(v.toString(),v); + } + + @Override + public V remove(String s) + { + V o=get(s); + put(s,null); + return o; + } + + @Override + public V get(String s) + { + return get(s,0,s.length()); + } + + + @Override + public V get(ByteBuffer b, int offset, int len) + { + b=b.duplicate(); + b.position(b.position()+offset); + b.limit(b.position()+len); + return get(BufferUtil.toString(b,StringUtil.__ISO_8859_1_CHARSET)); + } + + @Override + public V get(ByteBuffer b) + { + return get(b,0,b.remaining()); + } + + @Override + public V getBest(String s) + { + return getBest(s,0,s.length()); + } + + @Override + public V getBest(byte[] b, int offset, int len) + { + return getBest(new String(b,offset,len,StringUtil.__ISO_8859_1_CHARSET)); + } + + @Override + public V getBest(ByteBuffer b, int offset, int len) + { + b=b.duplicate(); + b.position(b.position()+offset); + b.limit(b.position()+len); + return getBest(BufferUtil.toString(b,StringUtil.__ISO_8859_1_CHARSET)); + } + + @Override + public boolean isCaseInsensitive() + { + return _caseInsensitive; + } + +} diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java index 73413232a0d..7c92ac5477e 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ArrayTernaryTrie.java @@ -18,7 +18,6 @@ package org.eclipse.jetty.util; -import java.nio.ByteBuffer; import java.util.HashSet; import java.util.Set; @@ -28,7 +27,7 @@ import java.util.Set; * This Trie is of a fixed size and cannot grow (which can be a good thing with regards to DOS when used as a cache). * @param */ -public class ArrayTernaryTrie implements Trie +public class ArrayTernaryTrie extends AbstractTrie { private static int LO=1; private static int EQ=2; @@ -70,13 +69,37 @@ public class ArrayTernaryTrie implements Trie this(128); } + public ArrayTernaryTrie(boolean insensitive) + { + this(insensitive,128); + } + public ArrayTernaryTrie(int capacityInNodes) { + this(true,capacityInNodes); + } + + public ArrayTernaryTrie(boolean insensitive, int capacityInNodes) + { + super(insensitive); _value=new Object[capacityInNodes]; _tree=new char[capacityInNodes*ROW_SIZE]; _key=new String[capacityInNodes]; } - + + /* ------------------------------------------------------------ */ + /** Copy Trie and change capacity by a factor + * @param trie + * @param factor + */ + public ArrayTernaryTrie(ArrayTernaryTrie trie, double factor) + { + this(trie.isCaseInsensitive(),(int)(trie._value.length*factor)); + _rows=trie._rows; + System.arraycopy(trie._value,0,_value,0,trie._value.length); + System.arraycopy(trie._tree,0,_tree,0,trie._tree.length); + System.arraycopy(trie._key,0,_key,0,trie._key.length); + } /* ------------------------------------------------------------ */ @Override @@ -90,8 +113,8 @@ public class ArrayTernaryTrie implements Trie for(k=0; k < limit; k++) { char c=s.charAt(k); - if (c<128) - c=StringUtil.lowercases[c&0x7f]; + if(isCaseInsensitive() && c<128) + c=StringUtil.lowercases[c]; while (true) { @@ -132,67 +155,20 @@ public class ArrayTernaryTrie implements Trie return true; } - /* ------------------------------------------------------------ */ @Override - public boolean put(V v) - { - return put(v.toString(),v); - } - - /* ------------------------------------------------------------ */ - @Override - public V remove(String s) - { - V o=get(s); - put(s,null); - return o; - } - - /* ------------------------------------------------------------ */ - @Override - public V get(String s) + public V get(String s,int offset, int length) { int t = _tree[EQ]; - int len = s.length(); - int node=0; - for(int i=0; t!=0 && i < len ; i++) + int len = length; + int i=0; + while(i implements Trie if (diff==0) { - node=t; + if (i==len) + return (V)_value[t]; t=_tree[row+EQ]; + if (t==0) + return null; break; } - if (diff<0) - t=_tree[row+LO]; - else - t=_tree[row+HI]; + t=_tree[row+((diff<0)?LO:HI)]; + if (t==0) + return null; } } - return (V)_value[node]; + return null; } + /* ------------------------------------------------------------ */ @Override - public V getBest(byte[] b,int offset,int len) + public V getBest(String s) { - return getBest(_tree[EQ],b,offset,len); - } - - /* ------------------------------------------------------------ */ - @Override - public V getBest(ByteBuffer b,int offset,int len) - { - if (b.hasArray()) - return getBest(_tree[EQ],b.array(),b.arrayOffset()+b.position()+offset,len); - return getBest(_tree[EQ],b,offset,len); + return getBest(_tree[EQ],s,0,s.length()); } - private V getBest(int t,byte[] b,int offset,int len) + /* ------------------------------------------------------------ */ + @Override + public V getBest(String s, int offset, int length) + { + return getBest(_tree[EQ],s,offset,length); + } + + /* ------------------------------------------------------------ */ + private V getBest(int t,String s,int offset,int len) { int node=0; for(int i=0; t!=0 && i implements Trie // if this node is a match, recurse to remember if (_key[node]!=null) { - V best=getBest(t,b,offset+i+1,len-i-1); + V best=getBest(t,s,offset+i+1,len-i-1); if (best!=null) return best; return (V)_value[node]; @@ -261,57 +241,12 @@ public class ArrayTernaryTrie implements Trie break; } - if (diff<0) - t=_tree[row+LO]; - else - t=_tree[row+HI]; + t=_tree[row+((diff<0)?LO:HI)]; } } return null; } - private V getBest(int t,ByteBuffer b,int offset,int len) - { - int pos=b.position()+offset; - int node=0; - for(int i=0; t!=0 && iThis implementation is always case insensitive and is optimal for + * a small number of fixed strings with few special characters. + *

* @param */ -public class ArrayTrie implements Trie +public class ArrayTrie extends AbstractTrie { /** * The Size of a Trie row is how many characters can be looked @@ -99,6 +101,7 @@ public class ArrayTrie implements Trie public ArrayTrie(int capacityInNodes) { + super(true); _value=new Object[capacityInNodes]; _rowIndex=new char[capacityInNodes*32]; _key=new String[capacityInNodes]; @@ -154,32 +157,14 @@ public class ArrayTrie implements Trie return true; } - /* ------------------------------------------------------------ */ @Override - public boolean put(V v) - { - return put(v.toString(),v); - } - - /* ------------------------------------------------------------ */ - @Override - public V remove(String s) - { - V o=get(s); - put(s,null); - return o; - } - - /* ------------------------------------------------------------ */ - @Override - public V get(String s) + public V get(String s, int offset, int len) { int t = 0; - int len = s.length(); for(int i=0; i < len; i++) { - char c=s.charAt(i); + char c=s.charAt(offset+i); int index=__lookup[c&0x7f]; if (index>=0) { @@ -245,7 +230,53 @@ public class ArrayTrie implements Trie return getBest(0,b.array(),b.arrayOffset()+b.position()+offset,len); return getBest(0,b,offset,len); } + + /* ------------------------------------------------------------ */ + @Override + public V getBest(String s, int offset, int len) + { + return getBest(0,s,offset,len); + } + /* ------------------------------------------------------------ */ + private V getBest(int t, String s, int offset, int len) + { + int pos=offset; + for(int i=0; i < len; i++) + { + char c=s.charAt(pos++); + int index=__lookup[c&0x7f]; + if (index>=0) + { + int idx=t*ROW_SIZE+index; + t=_rowIndex[idx]; + if (t==0) + return null; + } + else + { + char[] big = _bigIndex==null?null:_bigIndex[t]; + if (big==null) + return null; + t=big[c]; + if (t==0) + return null; + } + + // Is the next Trie is a match + if (_key[t]!=null) + { + // Recurse so we can remember this possibility + V best=getBest(t,s,offset+i+1,len-i-1); + if (best!=null) + return best; + return (V)_value[t]; + } + } + return (V)_value[t]; + } + + /* ------------------------------------------------------------ */ private V getBest(int t,byte[] b,int offset,int len) { for(int i=0; i < len; i++) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java index a62df9fa468..b8846de5b91 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringMap.java @@ -37,6 +37,7 @@ import java.util.TreeMap; * objects from being created just to look up in the map. * * This map is NOT synchronized. + * @deprecated Use {@link Trie} */ public class StringMap extends AbstractMap { diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java index 59426d48ea3..29db2113ad1 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java @@ -39,7 +39,7 @@ public class StringUtil private static final Logger LOG = Log.getLogger(StringUtil.class); - private final static StringMap CHARSETS= new StringMap(true); + private final static Trie CHARSETS= new ArrayTrie<>(256); public static final String ALL_INTERFACES="0.0.0.0"; public static final String CRLF="\015\012"; @@ -87,28 +87,9 @@ public class StringUtil String n=CHARSETS.get(s,offset,length); return (n==null)?s.substring(offset,offset+length):n; } + /* ------------------------------------------------------------ */ - /** Convert alternate charset names (eg utf8) to normalized - * name (eg UTF-8). - */ - public static String normalizeCharset(ByteBuffer b,int position,int length) - { - ByteBuffer ro=b.asReadOnlyBuffer(); - ro.limit(ro.capacity()); - ro.position(position); - ro.limit(position+length); - String n=CHARSETS.get(ro); - if (n!=null) - return n; - ByteBuffer slice = b.slice(); - slice.position(position); - slice.limit(position+length); - return BufferUtil.toString(slice,__UTF8_CHARSET); - } - - - public static char[] lowercases = { '\000','\001','\002','\003','\004','\005','\006','\007', '\010','\011','\012','\013','\014','\015','\016','\017', diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java index 417bbce30b2..484a642040f 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TreeTrie.java @@ -25,7 +25,14 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -public class TreeTrie implements Trie +/* ------------------------------------------------------------ */ +/** A Trie String lookup data structure using a tree + *

This implementation is always case insensitive and is optimal for + * a variable number of fixed strings with few special characters. + *

+ * @param + */ +public class TreeTrie extends AbstractTrie { private static final int[] __lookup = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F @@ -47,22 +54,18 @@ public class TreeTrie implements Trie public TreeTrie() { + super(true); _nextIndex = new TreeTrie[INDEX]; _c=0; } private TreeTrie(char c) { + super(true); _nextIndex = new TreeTrie[INDEX]; this._c=c; } - @Override - public boolean put(V v) - { - return put(v.toString(),v); - } - @Override public boolean put(String s, V v) { @@ -105,21 +108,12 @@ public class TreeTrie implements Trie } @Override - public V remove(String s) - { - V o=get(s); - put(s,null); - return o; - } - - @Override - public V get(String s) + public V get(String s,int offset, int len) { TreeTrie t = this; - int len = s.length(); for(int i=0; i < len; i++) { - char c=s.charAt(i); + char c=s.charAt(offset+i); int index=c>=0&&c<0x7f?__lookup[c]:-1; if (index>=0) { @@ -219,6 +213,14 @@ public class TreeTrie implements Trie return t._value; } + @Override + public V getBest(String s, int offset, int len) + { + // TODO inefficient + byte[] b=s.substring(offset,offset+len).getBytes(StringUtil.__ISO_8859_1_CHARSET); + return getBest(b,0,b.length); + } + @Override public V getBest(ByteBuffer b,int offset,int len) { @@ -343,5 +345,6 @@ public class TreeTrie implements Trie { return false; } - + + } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java index 3bf17ac20e9..1b095215307 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Trie.java @@ -52,7 +52,23 @@ public interface Trie * @return */ public V get(String s); - + + /* ------------------------------------------------------------ */ + /** Get and exact match from a String key + * @param s The key + * @param offset The offset within the string of the key + * @param len the length of the key + * @return + */ + public V get(String s,int offset,int len); + + /* ------------------------------------------------------------ */ + /** Get and exact match from a segment of a ByteBuufer as key + * @param b The buffer + * @return The value or null if not found + */ + public V get(ByteBuffer b); + /* ------------------------------------------------------------ */ /** Get and exact match from a segment of a ByteBuufer as key * @param b The buffer @@ -61,6 +77,22 @@ public interface Trie * @return The value or null if not found */ public V get(ByteBuffer b,int offset,int len); + + /* ------------------------------------------------------------ */ + /** Get the best match from key in a String. + * @param s The string + * @return The value or null if not found + */ + public V getBest(String s); + + /* ------------------------------------------------------------ */ + /** Get the best match from key in a String. + * @param s The string + * @param offset The offset within the string of the key + * @param len the length of the key + * @return The value or null if not found + */ + public V getBest(String s,int offset,int len); /* ------------------------------------------------------------ */ /** Get the best match from key in a byte array. @@ -81,10 +113,14 @@ public interface Trie * @return The value or null if not found */ public V getBest(ByteBuffer b,int offset,int len); - + /* ------------------------------------------------------------ */ public Set keySet(); - + + /* ------------------------------------------------------------ */ public boolean isFull(); - + + /* ------------------------------------------------------------ */ + public boolean isCaseInsensitive(); + } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java index 1febc2f26d5..8fe5f6ea486 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/TrieTest.java @@ -82,7 +82,8 @@ public class TrieTest Assert.assertEquals(5,trie.get("wobble").intValue()); Assert.assertEquals(6,trie.get("Foo-bar").intValue()); Assert.assertEquals(7,trie.get("FOO+bar").intValue()); - + + Assert.assertEquals(null,trie.get("helloworld")); Assert.assertEquals(null,trie.get("Help")); Assert.assertEquals(null,trie.get("Blah")); } @@ -105,7 +106,8 @@ public class TrieTest Assert.assertEquals(5,trie.get(BufferUtil.toBuffer("xwobble"),1,6).intValue()); Assert.assertEquals(6,trie.get(BufferUtil.toBuffer("xFOO-barx"),1,7).intValue()); Assert.assertEquals(7,trie.get(BufferUtil.toBuffer("xFOO+barx"),1,7).intValue()); - + + Assert.assertEquals(null,trie.get(BufferUtil.toBuffer("xHelloworldx"),1,10)); Assert.assertEquals(null,trie.get(BufferUtil.toBuffer("xHelpx"),1,4)); Assert.assertEquals(null,trie.get(BufferUtil.toBuffer("xBlahx"),1,4)); } @@ -128,7 +130,8 @@ public class TrieTest Assert.assertEquals(5,trie.get(BufferUtil.toDirectBuffer("xwobble"),1,6).intValue()); Assert.assertEquals(6,trie.get(BufferUtil.toDirectBuffer("xFOO-barx"),1,7).intValue()); Assert.assertEquals(7,trie.get(BufferUtil.toDirectBuffer("xFOO+barx"),1,7).intValue()); - + + Assert.assertEquals(null,trie.get(BufferUtil.toDirectBuffer("xHelloworldx"),1,10)); Assert.assertEquals(null,trie.get(BufferUtil.toDirectBuffer("xHelpx"),1,4)); Assert.assertEquals(null,trie.get(BufferUtil.toDirectBuffer("xBlahx"),1,4)); }