jetty-9 replaced Trie with indexed implementation

This commit is contained in:
Greg Wilkins 2012-12-17 19:21:28 +11:00
parent 3ce8c2ba58
commit 4d34e83c1c
4 changed files with 222 additions and 146 deletions

View File

@ -32,8 +32,8 @@ import org.eclipse.jetty.util.Trie;
*/
public class HttpField
{
public final static Trie<HttpField> CACHE = new Trie<>();
public final static Trie<HttpField> CONTENT_TYPE = new Trie<>();
public final static Trie<HttpField> CACHE = new Trie<>(768);
public final static Trie<HttpField> CONTENT_TYPE = new Trie<>(512);
static
{

View File

@ -112,7 +112,7 @@ public enum HttpHeader
/* ------------------------------------------------------------ */
public final static Trie<HttpHeader> CACHE= new Trie<HttpHeader>();
public final static Trie<HttpHeader> CACHE= new Trie<>(512);
static
{
for (HttpHeader header : HttpHeader.values())

View File

@ -109,8 +109,8 @@ public class MimeTypes
/* ------------------------------------------------------------ */
private static final Logger LOG = Log.getLogger(MimeTypes.class);
public final static Trie<MimeTypes.Type> CACHE= new Trie<MimeTypes.Type>();
private final static Trie<ByteBuffer> TYPES= new Trie<ByteBuffer>();
public final static Trie<MimeTypes.Type> CACHE= new Trie<>(512);
private final static Trie<ByteBuffer> TYPES= new Trie<ByteBuffer>(512);
private final static Map<String,String> __dftMimeMap = new HashMap<String,String>();
private final static Map<String,String> __encodings = new HashMap<String,String>();

View File

@ -20,13 +20,28 @@ package org.eclipse.jetty.util;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/* ------------------------------------------------------------ */
/** A Trie String lookup data structure.
* @param <V>
*/
public class Trie<V>
{
/**
* The Size of a Trie row is how many characters can be looked
* up directly without going to a big index. This is set at
* 32 to cover case insensitive alphabet and a few other common
* characters.
*/
private static final int ROW_SIZE = 32;
/**
* The index lookup table, this maps a character as a byte
* (ISO-8859-1 or UTF8) to an index within a Trie row
*/
private static final int[] __lookup =
{ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
/*0*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
@ -38,33 +53,66 @@ public class Trie<V>
/*6*/-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/*7*/15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
};
private static final int INDEX = 32;
private final Trie<V>[] _nextIndex;
private final List<Trie<V>> _nextOther=new ArrayList<>();
private final char _c;
private String _key;
private V _value;
/**
* The Trie rows in a single array which allows a lookup of row,character
* to the next row in the Trie. This is actually a 2 dimensional
* array that has been flattened to achieve locality of reference.
* The first ROW_SIZE entries are for row 0, then next ROW_SIZE
* entries are for row 1 etc. So in general instead of using
* _rows[row][index], we use _rows[row*ROW_SIZE+index] to look up
* the next row for a given character.
*
* The array is of characters rather than integers to save space.
*/
private final char[] _rowIndex;
/**
* The key (if any) for a Trie row.
* A row may be a leaf, a node or both in the Trie tree.
*/
private final String[] _key;
/**
* The value (if any) for a Trie row.
* A row may be a leaf, a node or both in the Trie tree.
*/
private final Object[] _value;
/**
* A big index for each row.
* If a character outside of the lookup map is needed,
* then a big index will be created for the row, with
* 256 entries, one for each possible byte.
*/
private final char[][] _bigIndex;
/**
* The number of rows allocated
*/
private char _rows;
public Trie()
{
_nextIndex = new Trie[INDEX];
_c=0;
this(128);
}
private Trie(char c)
public Trie(int capacityInNodes)
{
_nextIndex = new Trie[INDEX];
this._c=c;
_value=new Object[capacityInNodes];
_rowIndex=new char[capacityInNodes*32];
_key=new String[capacityInNodes];
_bigIndex=new char[capacityInNodes][];
}
public V put(V v)
{
return put(v.toString(),v);
}
/* ------------------------------------------------------------ */
/** Put and entry into the Trie
* @param s The key for the entry
* @param v The value of the entry
* @return The last value for the key
*/
public V put(String s, V v)
{
Trie<V> t = this;
int t=0;
int k;
int limit = s.length();
for(k=0; k < limit; k++)
@ -74,34 +122,38 @@ public class Trie<V>
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
t._nextIndex[index] = new Trie<V>(c);
t = t._nextIndex[index];
int idx=t*ROW_SIZE+index;
t=_rowIndex[idx];
if (t==0)
t=_rowIndex[idx]=++_rows;
}
else
{
Trie<V> n=null;
for (int i=t._nextOther.size();i-->0;)
{
n=t._nextOther.get(i);
if (n._c==c)
break;
n=null;
}
if (n==null)
{
n=new Trie<V>(c);
t._nextOther.add(n);
}
t=n;
char[] big=_bigIndex[t];
if (big==null)
big=_bigIndex[t]=new char[256];
t=big[c];
if (t==0)
t=big[c]=++_rows;
}
}
t._key=v==null?null:s;
V old=t._value;
t._value = v;
_key[t]=v==null?null:s;
V old=(V)_value[t];
_value[t] = v;
return old;
}
/* ------------------------------------------------------------ */
/** Put a value as both a key and a value.
* @param v
* @return
*/
public V put(V v)
{
return put(v.toString(),v);
}
public V remove(String s)
{
V o=get(s);
@ -109,9 +161,14 @@ public class Trie<V>
return o;
}
/* ------------------------------------------------------------ */
/** Get and exact match from a String key
* @param s The key
* @return
*/
public V get(String s)
{
Trie<V> t = this;
int t = 0;
int len = s.length();
for(int i=0; i < len; i++)
{
@ -119,110 +176,124 @@ public class Trie<V>
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
int idx=t*ROW_SIZE+index;
t=_rowIndex[idx];
if (t==0)
return null;
t = t._nextIndex[index];
}
else
{
Trie<V> n=null;
for (int j=t._nextOther.size();j-->0;)
{
n=t._nextOther.get(j);
if (n._c==c)
break;
n=null;
}
if (n==null)
char[] big=_bigIndex[t];
if (big==null)
return null;
t=big[c];
if (t==0)
return null;
t=n;
}
}
return t._value;
return (V)_value[t];
}
/* ------------------------------------------------------------ */
/** Get and exact match from a segment of a ByteBuufer as key
* @param b The buffer
* @param offset The offset within the buffer of the key
* @param len the length of the key
* @return The value or null if not found
*/
public V get(ByteBuffer b,int offset,int len)
{
Trie<V> t = this;
int t = 0;
for(int i=0; i < len; i++)
{
byte c=b.get(offset+i);
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
int idx=t*ROW_SIZE+index;
t=_rowIndex[idx];
if (t==0)
return null;
t = t._nextIndex[index];
}
else
{
Trie<V> n=null;
for (int j=t._nextOther.size();j-->0;)
{
n=t._nextOther.get(j);
if (n._c==c)
break;
n=null;
}
if (n==null)
char[] big=_bigIndex[t];
if (big==null)
return null;
t=big[c];
if (t==0)
return null;
t=n;
}
}
return t._value;
return (V)_value[t];
}
/* ------------------------------------------------------------ */
/** Get the best match from key in a byte array.
* The key is assumed to by ISO_8859_1 characters.
* @param b The buffer
* @param offset The offset within the array of the key
* @param len the length of the key
* @return The value or null if not found
*/
public V getBest(byte[] b,int offset,int len)
{
Trie<V> t = this;
return getBest(0,b,offset,len);
}
private V getBest(int t,byte[] b,int offset,int len)
{
for(int i=0; i < len; i++)
{
byte c=b[offset+i];
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
int idx=t*ROW_SIZE+index;
t=_rowIndex[idx];
if (t==0)
return null;
t = t._nextIndex[index];
}
else
{
Trie<V> n=null;
for (int j=t._nextOther.size();j-->0;)
{
n=t._nextOther.get(j);
if (n._c==c)
break;
n=null;
}
if (n==null)
char[] big=_bigIndex[t];
if (big==null)
return null;
t=big[c];
if (t==0)
return null;
t=n;
}
// Is the next Trie is a match
if (t._key!=null)
if (_key[t]!=null)
{
// Recurse so we can remember this possibility
V best=t.getBest(b,offset+i+1,len-i-1);
V best=getBest(t,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return t._value;
return (V)_value[t];
}
}
return t._value;
return (V)_value[t];
}
/* ------------------------------------------------------------ */
/** Get the best match from key in a byte buffer.
* The key is assumed to by ISO_8859_1 characters.
* @param b The buffer
* @param offset The offset within the buffer of the key
* @param len the length of the key
* @return The value or null if not found
*/
public V getBest(ByteBuffer b,int offset,int len)
{
if (b.hasArray())
return getBest(b.array(),b.arrayOffset()+b.position()+offset,len);
return getBestByteBuffer(b,offset,len);
return getBest(0,b.array(),b.arrayOffset()+b.position()+offset,len);
return getBest(0,b,offset,len);
}
private V getBestByteBuffer(ByteBuffer b,int offset,int len)
private V getBest(int t,ByteBuffer b,int offset,int len)
{
Trie<V> t = this;
int pos=b.position()+offset;
for(int i=0; i < len; i++)
{
@ -230,36 +301,32 @@ public class Trie<V>
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
int idx=t*ROW_SIZE+index;
t=_rowIndex[idx];
if (t==0)
return null;
t = t._nextIndex[index];
}
else
{
Trie<V> n=null;
for (int j=t._nextOther.size();j-->0;)
{
n=t._nextOther.get(j);
if (n._c==c)
break;
n=null;
}
if (n==null)
char[] big=_bigIndex[t];
if (big==null)
return null;
t=big[c];
if (t==0)
return null;
t=n;
}
// Is the next Trie is a match
if (t._key!=null)
if (_key[t]!=null)
{
// Recurse so we can remember this possibility
V best=t.getBest(b,offset+i+1,len-i-1);
V best=getBest(t,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return t._value;
return (V)_value[t];
}
}
return t._value;
return (V)_value[t];
}
@ -269,7 +336,7 @@ public class Trie<V>
public String toString()
{
StringBuilder buf = new StringBuilder();
toString(buf,this);
toString(buf,0);
if (buf.length()==0)
return "{}";
@ -280,56 +347,65 @@ public class Trie<V>
}
private static <V> void toString(Appendable out, Trie<V> t)
private <V> void toString(Appendable out, int t)
{
if (t != null)
if (_value[t]!=null)
{
if (t._value!=null)
try
{
try
{
out.append(',');
out.append(t._key);
out.append('=');
out.append(t._value.toString());
}
catch (IOException e)
{
throw new RuntimeException(e);
}
out.append(',');
out.append(_key[t]);
out.append('=');
out.append(_value[t].toString());
}
for(int i=0; i < INDEX; i++)
catch (IOException e)
{
if (t._nextIndex[i] != null)
toString(out,t._nextIndex[i]);
throw new RuntimeException(e);
}
for (int i=t._nextOther.size();i-->0;)
toString(out,t._nextOther.get(i));
}
}
for(int i=0; i < ROW_SIZE; i++)
{
int idx=t*ROW_SIZE+i;
if (_rowIndex[idx] != 0)
toString(out,_rowIndex[idx]);
}
char[] big = _bigIndex[t];
if (big!=null)
{
for (int i:big)
if (i!=0)
toString(out,i);
}
}
public Set<String> keySet()
{
Set<String> keys = new HashSet<>();
keySet(keys,this);
keySet(keys,0);
return keys;
}
private static <V> void keySet(Set<String> set, Trie<V> t)
private void keySet(Set<String> set, int t)
{
if (t != null)
if (_value[t]!=null)
set.add(_key[t]);
for(int i=0; i < ROW_SIZE; i++)
{
if (t._key!=null)
set.add(t._key);
for(int i=0; i < INDEX; i++)
{
if (t._nextIndex[i] != null)
keySet(set,t._nextIndex[i]);
}
for (int i=t._nextOther.size();i-->0;)
keySet(set,t._nextOther.get(i));
}
int idx=t*ROW_SIZE+i;
if (_rowIndex[idx] != 0)
keySet(set,_rowIndex[idx]);
}
char[] big = _bigIndex[t];
if (big!=null)
{
for (int i:big)
if (i!=0)
keySet(set,i);
}
}
}