404511 Replaced all StringMap usage with Tries

This commit is contained in:
Greg Wilkins 2013-03-28 12:38:01 +11:00
parent de0c0910dd
commit fd099aa77d
13 changed files with 360 additions and 261 deletions

View File

@ -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<HttpField>
private static final Float __one = new Float("1.0");
private static final Float __zero = new Float("0.0");
private static final StringMap<Float> __qualities = new StringMap<>();
private static final Trie<Float> __qualities = new ArrayTernaryTrie<>();
static
{
__qualities.put("*", __one);

View File

@ -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<O> extends HashMap<String,O>
}
/* --------------------------------------------------------------- */
final StringMap<MappedEntry<O>> _prefixMap=new StringMap<>();
final StringMap<MappedEntry<O>> _suffixMap=new StringMap<>();
final StringMap<MappedEntry<O>> _exactMap=new StringMap<>();
Trie<MappedEntry<O>> _prefixMap=new ArrayTernaryTrie<>(false);
Trie<MappedEntry<O>> _suffixMap=new ArrayTernaryTrie<>(false);
final Map<String,MappedEntry<O>> _exactMap=new HashMap<>();
List _defaultSingletonList=null;
List<MappedEntry<O>> _defaultSingletonList=null;
MappedEntry<O> _prefixDefault=null;
MappedEntry<O> _default=null;
final Set _entrySet;
boolean _nodefault=false;
/* --------------------------------------------------------------- */
@ -111,7 +110,6 @@ public class PathMap<O> extends HashMap<String,O>
{
super(capacity);
_nodefault=noDefault;
_entrySet=entrySet();
}
/* --------------------------------------------------------------- */
@ -120,7 +118,6 @@ public class PathMap<O> extends HashMap<String,O>
public PathMap(Map<String, ? extends O> m)
{
putAll(m);
_entrySet=entrySet();
}
/* --------------------------------------------------------------- */
@ -163,12 +160,15 @@ public class PathMap<O> extends HashMap<String,O>
{
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<MappedEntry<O>>)_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<MappedEntry<O>>)_suffixMap,1.5);
}
else if (spec.equals(URIUtil.SLASH))
{
if (_nodefault)
@ -176,8 +176,7 @@ public class PathMap<O> extends HashMap<String,O>
else
{
_default=entry;
_defaultSingletonList=
Collections.singletonList(_default);
_defaultSingletonList=Collections.singletonList(_default);
}
}
else
@ -228,17 +227,22 @@ public class PathMap<O> extends HashMap<String,O>
}
// 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<PathMap.MappedEntry<O>> 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<O> extends HashMap<String,O>
// Extension search
i=0;
final Trie<PathMap.MappedEntry<O>> 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<O> extends HashMap<String,O>
*/
public Object getLazyMatches(String path)
{
MappedEntry entry;
MappedEntry<O> 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<PathMap.MappedEntry<O>> 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<O> extends HashMap<String,O>
// Extension search
i=0;
final Trie<PathMap.MappedEntry<O>> 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<O> extends HashMap<String,O>
* @param path Path to match
* @return List of Map.Entry instances key=pathSpec
*/
public List getMatches(String path)
public List<Map.Entry<String,O>> getMatches(String path)
{
return LazyList.getList(getLazyMatches(path));
}
@ -333,7 +344,7 @@ public class PathMap<O> extends HashMap<String,O>
*/
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<O> extends HashMap<String,O>
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<O> extends HashMap<String,O>
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<O> extends HashMap<String,O>
* @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<O> extends HashMap<String,O>
return path.regionMatches(path.length()-pathSpec.length()+1,
pathSpec,1,pathSpec.length()-1);
return false;
}
}
/* --------------------------------------------------------------- */
private static boolean isPathWildcardMatch(String pathSpec, String path)

View File

@ -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)
{

View File

@ -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<Boolean> __IE6_BadOS = new ArrayTernaryTrie<>();
{
__IE6_BadOS.put("NT 5.01", Boolean.TRUE);
__IE6_BadOS.put("NT 5.0",Boolean.TRUE);

View File

@ -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<String> qualities=fields.getValues("Q",", \t");
List<String> 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<String> qualities=fields.getValues("Q",", \t");
List<String> 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

View File

@ -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.
* <p>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</p>
* @param <V>
*/
public abstract class AbstractTrie<V> implements Trie<V>
{
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;
}
}

View File

@ -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 <V>
*/
public class ArrayTernaryTrie<V> implements Trie<V>
public class ArrayTernaryTrie<V> extends AbstractTrie<V>
{
private static int LO=1;
private static int EQ=2;
@ -70,13 +69,37 @@ public class ArrayTernaryTrie<V> implements Trie<V>
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<V> 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<V> implements Trie<V>
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<V> implements Trie<V>
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<len)
{
char c=StringUtil.lowercases[s.charAt(i)&0x7f];
while (t!=0)
{
int row = ROW_SIZE*t;
char n=_tree[row];
int diff=n-c;
if (diff==0)
{
node=t;
t=_tree[row+EQ];
break;
}
if (diff<0)
t=_tree[row+LO];
else
t=_tree[row+HI];
}
}
return (V)_value[node];
}
/* ------------------------------------------------------------ */
@Override
public V get(ByteBuffer b,int offset,int len)
{
int t = _tree[EQ];
int node=0;
for(int i=0; t!=0 && i<len; i++)
{
char c=StringUtil.lowercases[b.get(offset+i)&0x7f];
char c=s.charAt(offset+i++);
if(isCaseInsensitive() && c<128)
c=StringUtil.lowercases[c];
while (t!=0)
while (true)
{
int row = ROW_SIZE*t;
char n=_tree[row];
@ -200,43 +176,47 @@ public class ArrayTernaryTrie<V> implements Trie<V>
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<len; i++)
{
char c=StringUtil.lowercases[b[offset+i]&0x7f];
char c=s.charAt(offset+i);
if(isCaseInsensitive() && c<128)
c=StringUtil.lowercases[c];
while (t!=0)
{
@ -252,7 +232,7 @@ public class ArrayTernaryTrie<V> implements Trie<V>
// 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<V> implements Trie<V>
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 && i<len; i++)
{
char c=StringUtil.lowercases[b.get(pos++)&0x7f];
while (t!=0)
{
int row = ROW_SIZE*t;
char n=_tree[row];
int diff=n-c;
if (diff==0)
{
node=t;
t=_tree[row+EQ];
// if this node is a match, recurse to remember
if (_key[node]!=null)
{
V best=getBest(t,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return (V)_value[node];
}
break;
}
if (diff<0)
t=_tree[row+LO];
else
t=_tree[row+HI];
}
}
return null;
}
@Override
public String toString()

View File

@ -23,12 +23,14 @@ import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
/* ------------------------------------------------------------ */
/** A Trie String lookup data structure.
/** A Trie String lookup data structure using a fixed size array.
* <p>This implementation is always case insensitive and is optimal for
* a small number of fixed strings with few special characters.
* </p>
* @param <V>
*/
public class ArrayTrie<V> implements Trie<V>
public class ArrayTrie<V> extends AbstractTrie<V>
{
/**
* The Size of a Trie row is how many characters can be looked
@ -99,6 +101,7 @@ public class ArrayTrie<V> implements Trie<V>
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<V> implements Trie<V>
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<V> implements Trie<V>
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++)

View File

@ -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<O> extends AbstractMap<String,O>
{

View File

@ -39,7 +39,7 @@ public class StringUtil
private static final Logger LOG = Log.getLogger(StringUtil.class);
private final static StringMap<String> CHARSETS= new StringMap<String>(true);
private final static Trie<String> 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',

View File

@ -25,7 +25,14 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TreeTrie<V> implements Trie<V>
/* ------------------------------------------------------------ */
/** A Trie String lookup data structure using a tree
* <p>This implementation is always case insensitive and is optimal for
* a variable number of fixed strings with few special characters.
* </p>
* @param <V>
*/
public class TreeTrie<V> extends AbstractTrie<V>
{
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<V> implements Trie<V>
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<V> implements Trie<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)
{
TreeTrie<V> 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<V> implements Trie<V>
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<V> implements Trie<V>
{
return false;
}
}

View File

@ -52,7 +52,23 @@ public interface Trie<V>
* @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<V>
* @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<V>
* @return The value or null if not found
*/
public V getBest(ByteBuffer b,int offset,int len);
/* ------------------------------------------------------------ */
public Set<String> keySet();
/* ------------------------------------------------------------ */
public boolean isFull();
/* ------------------------------------------------------------ */
public boolean isCaseInsensitive();
}

View File

@ -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));
}