mirror of
https://github.com/jetty/jetty.project.git
synced 2025-03-01 03:19:13 +00:00
404511 Replaced all StringMap usage with Tries
This commit is contained in:
parent
de0c0910dd
commit
fd099aa77d
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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()
|
||||
|
@ -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++)
|
||||
|
@ -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>
|
||||
{
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user