jetty-9 made Trie abstract and added TreeTrie for the HttpParser cache to save space

This commit is contained in:
Greg Wilkins 2013-01-10 17:31:57 +11:00
parent 3efcc2af41
commit 25324b666c
28 changed files with 816 additions and 402 deletions

View File

@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
@ -32,8 +33,8 @@ import org.eclipse.jetty.util.Trie;
*/
public class HttpField
{
public final static Trie<HttpField> CACHE = new Trie<>(768);
public final static Trie<HttpField> CONTENT_TYPE = new Trie<>(512);
public final static Trie<HttpField> CACHE = new ArrayTrie<>(768);
public final static Trie<HttpField> CONTENT_TYPE = new ArrayTrie<>(512);
static
{

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
@ -112,7 +113,7 @@ public enum HttpHeader
/* ------------------------------------------------------------ */
public final static Trie<HttpHeader> CACHE= new Trie<>(512);
public final static Trie<HttpHeader> CACHE= new ArrayTrie<>(512);
static
{
for (HttpHeader header : HttpHeader.values())

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import java.util.EnumSet;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Trie;
@ -44,7 +45,7 @@ public enum HttpHeaderValue
UNKNOWN("::UNKNOWN::");
/* ------------------------------------------------------------ */
public final static Trie<HttpHeaderValue> CACHE= new Trie<HttpHeaderValue>();
public final static Trie<HttpHeaderValue> CACHE= new ArrayTrie<HttpHeaderValue>();
static
{
for (HttpHeaderValue value : HttpHeaderValue.values())

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
@ -113,7 +114,7 @@ public enum HttpMethod
}
/* ------------------------------------------------------------ */
public final static Trie<HttpMethod> CACHE= new Trie<HttpMethod>();
public final static Trie<HttpMethod> CACHE= new ArrayTrie<HttpMethod>();
static
{
for (HttpMethod method : HttpMethod.values())

View File

@ -24,8 +24,8 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TreeTrie;
import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -87,7 +87,7 @@ public class HttpParser
private int _chunkPosition;
private boolean _headResponse;
private ByteBuffer _contentChunk;
private final Trie<HttpField> _connectionFields=new Trie<>(512);
private final Trie<HttpField> _connectionFields=new TreeTrie<>();
private int _length;
private final StringBuilder _string=new StringBuilder();

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Trie;
@ -34,7 +35,7 @@ public enum HttpScheme
WSS("wss");
/* ------------------------------------------------------------ */
public final static Trie<HttpScheme> CACHE= new Trie<HttpScheme>();
public final static Trie<HttpScheme> CACHE= new ArrayTrie<HttpScheme>();
static
{
for (HttpScheme version : HttpScheme.values())

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
@ -33,7 +34,7 @@ public enum HttpVersion
HTTP_2_0("HTTP/2.0",20);
/* ------------------------------------------------------------ */
public final static Trie<HttpVersion> CACHE= new Trie<HttpVersion>();
public final static Trie<HttpVersion> CACHE= new ArrayTrie<HttpVersion>();
static
{
for (HttpVersion version : HttpVersion.values())

View File

@ -27,6 +27,7 @@ import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.eclipse.jetty.util.ArrayTrie;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Trie;
@ -109,8 +110,8 @@ public class MimeTypes
/* ------------------------------------------------------------ */
private static final Logger LOG = Log.getLogger(MimeTypes.class);
public final static Trie<MimeTypes.Type> CACHE= new Trie<>(512);
private final static Trie<ByteBuffer> TYPES= new Trie<ByteBuffer>(512);
public final static Trie<MimeTypes.Type> CACHE= new ArrayTrie<>(512);
private final static Trie<ByteBuffer> TYPES= new ArrayTrie<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

@ -23,7 +23,6 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.eclipse.jetty.http.HttpParser.State;
import org.eclipse.jetty.util.BufferUtil;

View File

@ -22,7 +22,6 @@ import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;

View File

@ -69,7 +69,6 @@ import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandler.Context;
import org.eclipse.jetty.server.session.AbstractSession;
import org.eclipse.jetty.server.session.AbstractSessionManager;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.AttributesMap;
import org.eclipse.jetty.util.MultiException;

View File

@ -20,7 +20,6 @@ package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.nio.channels.IllegalSelectorException;
import java.util.ArrayList;
import java.util.Collection;

View File

@ -24,7 +24,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

View File

@ -53,8 +53,8 @@ import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.MultiPartInputStreamParser;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StdErrLog;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;

View File

@ -42,9 +42,7 @@ import org.eclipse.jetty.server.ConnectorStatistics;
import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

View File

@ -446,10 +446,10 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
if (resource!=null && resource.exists() && !resource.isDirectory())
{
// Tell caches that response may vary by accept-encoding
response.setHeader(HttpHeaders.VARY.asString(),HttpHeaders.ACCEPT_ENCODING.asString());
response.setHeader(HttpHeader.VARY.asString(),HttpHeader.ACCEPT_ENCODING.asString());
// Does the client accept gzip?
String accept=request.getHeader(HttpHeaders.ACCEPT_ENCODING.asString());
String accept=request.getHeader(HttpHeader.ACCEPT_ENCODING.asString());
if (accept!=null && accept.indexOf("gzip")>=0)
gzip=true;
}

View File

@ -29,11 +29,11 @@ import javax.servlet.http.HttpServletResponse;
import junit.framework.Assert;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
import org.eclipse.jetty.servlets.gzip.GzipTester;
import org.eclipse.jetty.testing.HttpTester;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.Rule;
import org.junit.Test;
@ -117,8 +117,8 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseGzipCompressed("file.txt");
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
HttpTester.Response http = tester.assertIsResponseGzipCompressed("file.txt");
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
}
finally
{
@ -141,8 +141,8 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseGzipCompressed("file.txt");
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
HttpTester.Response http = tester.assertIsResponseGzipCompressed("file.txt");
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
}
finally
{
@ -165,8 +165,8 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseGzipCompressed("file.txt");
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
HttpTester.Response http = tester.assertIsResponseGzipCompressed("file.txt");
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
}
finally
{
@ -189,8 +189,8 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseGzipCompressed("file.txt");
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
HttpTester.Response http = tester.assertIsResponseGzipCompressed("file.txt");
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
}
finally
{
@ -212,8 +212,8 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseNotGzipCompressed("file.txt", filesize, HttpStatus.OK_200);
Assert.assertEquals("Accept-Encoding",http.getHeader("Vary"));
HttpTester.Response http = tester.assertIsResponseNotGzipCompressed("file.txt", filesize, HttpStatus.OK_200);
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
}
finally
{
@ -235,8 +235,8 @@ public class GzipFilterDefaultTest
try
{
tester.start();
HttpTester http = tester.assertIsResponseNotGzipCompressed("file.mp3", filesize, HttpStatus.OK_200);
Assert.assertNull(http.getHeader("Vary"));
HttpTester.Response http = tester.assertIsResponseNotGzipCompressed("file.mp3", filesize, HttpStatus.OK_200);
Assert.assertNull(http.get("Vary"));
}
finally
{

View File

@ -73,12 +73,12 @@ public class GzipTester
// DOES NOT WORK IN WINDOWS - this.testdir.ensureEmpty();
}
public HttpTester assertIsResponseGzipCompressed(String filename) throws Exception
public HttpTester.Response assertIsResponseGzipCompressed(String filename) throws Exception
{
return assertIsResponseGzipCompressed(filename,filename);
}
public HttpTester assertIsResponseGzipCompressed(String requestedFilename, String serverFilename) throws Exception
public HttpTester.Response assertIsResponseGzipCompressed(String requestedFilename, String serverFilename) throws Exception
{
// System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename);
HttpTester.Request request = HttpTester.newRequest();
@ -237,7 +237,7 @@ public class GzipTester
* passing -1 will disable the Content-Length assertion)
* @throws Exception
*/
public HttpTester assertIsResponseNotGzipCompressed(String filename, int expectedFilesize, int status) throws Exception
public HttpTester.Response assertIsResponseNotGzipCompressed(String filename, int expectedFilesize, int status) throws Exception
{
String uri = "/context/"+filename;
HttpTester.Response response = executeRequest(uri);

View File

@ -0,0 +1,400 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
/* ------------------------------------------------------------ */
/** A Trie String lookup data structure.
* @param <V>
*/
public class ArrayTrie<V> implements 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,
/*1*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 30,
/*2*/31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, 27, -1, -1,
/*3*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, -1, -1, -1, -1,
/*4*/-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/*5*/15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
/*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,
};
/**
* 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 char[][] _bigIndex;
/**
* The number of rows allocated
*/
private char _rows;
public ArrayTrie()
{
this(128);
}
public ArrayTrie(int capacityInNodes)
{
_value=new Object[capacityInNodes];
_rowIndex=new char[capacityInNodes*32];
_key=new String[capacityInNodes];
}
/* ------------------------------------------------------------ */
@Override
public boolean put(String s, V v)
{
int t=0;
int k;
int limit = s.length();
for(k=0; k < limit; k++)
{
char c=s.charAt(k);
int index=__lookup[c&0x7f];
if (index>=0)
{
int idx=t*ROW_SIZE+index;
t=_rowIndex[idx];
if (t==0)
{
if (_rows==_value.length)
return false;
t=_rowIndex[idx]=++_rows;
}
}
else if (c>127)
throw new IllegalArgumentException("non ascii character");
else
{
if (_bigIndex==null)
_bigIndex=new char[_value.length][];
char[] big=_bigIndex[t];
if (big==null)
big=_bigIndex[t]=new char[128];
t=big[c];
if (t==0)
{
if (_rows==_value.length)
return false;
t=big[c]=++_rows;
}
}
}
_key[t]=v==null?null:s;
V old=(V)_value[t];
_value[t] = 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)
{
int t = 0;
int len = s.length();
for(int i=0; i < len; i++)
{
char c=s.charAt(i);
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;
}
}
return (V)_value[t];
}
/* ------------------------------------------------------------ */
@Override
public V get(ByteBuffer b,int offset,int len)
{
int t = 0;
for(int i=0; i < len; i++)
{
byte c=b.get(offset+i);
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;
}
}
return (V)_value[t];
}
/* ------------------------------------------------------------ */
@Override
public V getBest(byte[] b,int offset,int len)
{
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=__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,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return (V)_value[t];
}
}
return (V)_value[t];
}
/* ------------------------------------------------------------ */
@Override
public V getBest(ByteBuffer b,int offset,int len)
{
if (b.hasArray())
return getBest(0,b.array(),b.arrayOffset()+b.position()+offset,len);
return getBest(0,b,offset,len);
}
private V getBest(int t,ByteBuffer b,int offset,int len)
{
int pos=b.position()+offset;
for(int i=0; i < len; i++)
{
byte c=b.get(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,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return (V)_value[t];
}
}
return (V)_value[t];
}
@Override
public String toString()
{
StringBuilder buf = new StringBuilder();
toString(buf,0);
if (buf.length()==0)
return "{}";
buf.setCharAt(0,'{');
buf.append('}');
return buf.toString();
}
private <V> void toString(Appendable out, int t)
{
if (_value[t]!=null)
{
try
{
out.append(',');
out.append(_key[t]);
out.append('=');
out.append(_value[t].toString());
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
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==null?null:_bigIndex[t];
if (big!=null)
{
for (int i:big)
if (i!=0)
toString(out,i);
}
}
@Override
public Set<String> keySet()
{
Set<String> keys = new HashSet<>();
keySet(keys,0);
return keys;
}
private void keySet(Set<String> set, int t)
{
if (_value[t]!=null)
set.add(_key[t]);
for(int i=0; i < ROW_SIZE; i++)
{
int idx=t*ROW_SIZE+i;
if (_rowIndex[idx] != 0)
keySet(set,_rowIndex[idx]);
}
char[] big = _bigIndex==null?null:_bigIndex[t];
if (big!=null)
{
for (int i:big)
if (i!=0)
keySet(set,i);
}
}
}

View File

@ -0,0 +1,335 @@
//
// ========================================================================
// Copyright (c) 1995-2012 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.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TreeTrie<V> implements Trie<V>
{
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,
/*1*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 30,
/*2*/31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, 27, -1, -1,
/*3*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, -1, -1, -1, -1,
/*4*/-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/*5*/15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
/*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 TreeTrie<V>[] _nextIndex;
private final List<TreeTrie<V>> _nextOther=new ArrayList<>();
private final char _c;
private String _key;
private V _value;
public TreeTrie()
{
_nextIndex = new TreeTrie[INDEX];
_c=0;
}
private TreeTrie(char c)
{
_nextIndex = new TreeTrie[INDEX];
this._c=c;
}
public boolean put(V v)
{
return put(v.toString(),v);
}
public boolean put(String s, V v)
{
TreeTrie<V> t = this;
int k;
int limit = s.length();
for(k=0; k < limit; k++)
{
char c=s.charAt(k);
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
t._nextIndex[index] = new TreeTrie<V>(c);
t = t._nextIndex[index];
}
else
{
TreeTrie<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 TreeTrie<V>(c);
t._nextOther.add(n);
}
t=n;
}
}
t._key=v==null?null:s;
V old=t._value;
t._value = v;
return true;
}
public V remove(String s)
{
V o=get(s);
put(s,null);
return o;
}
public V get(String s)
{
TreeTrie<V> t = this;
int len = s.length();
for(int i=0; i < len; i++)
{
char c=s.charAt(i);
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
return null;
t = t._nextIndex[index];
}
else
{
TreeTrie<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)
return null;
t=n;
}
}
return t._value;
}
public V get(ByteBuffer b,int offset,int len)
{
TreeTrie<V> t = this;
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)
return null;
t = t._nextIndex[index];
}
else
{
TreeTrie<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)
return null;
t=n;
}
}
return t._value;
}
public V getBest(byte[] b,int offset,int len)
{
TreeTrie<V> t = this;
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)
return null;
t = t._nextIndex[index];
}
else
{
TreeTrie<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)
return null;
t=n;
}
// Is the next Trie is a match
if (t._key!=null)
{
// Recurse so we can remember this possibility
V best=t.getBest(b,offset+i+1,len-i-1);
if (best!=null)
return best;
return t._value;
}
}
return t._value;
}
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);
}
private V getBestByteBuffer(ByteBuffer b,int offset,int len)
{
TreeTrie<V> t = this;
int pos=b.position()+offset;
for(int i=0; i < len; i++)
{
byte c=b.get(pos++);
int index=c>=0&&c<0x7f?__lookup[c]:-1;
if (index>=0)
{
if (t._nextIndex[index] == null)
return null;
t = t._nextIndex[index];
}
else
{
TreeTrie<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)
return null;
t=n;
}
// Is the next Trie is a match
if (t._key!=null)
{
// Recurse so we can remember this possibility
V best=t.getBest(b,offset+i+1,len-i-1);
if (best!=null)
return best;
return t._value;
}
}
return t._value;
}
@Override
public String toString()
{
StringBuilder buf = new StringBuilder();
toString(buf,this);
if (buf.length()==0)
return "{}";
buf.setCharAt(0,'{');
buf.append('}');
return buf.toString();
}
private static <V> void toString(Appendable out, TreeTrie<V> t)
{
if (t != null)
{
if (t._value!=null)
{
try
{
out.append(',');
out.append(t._key);
out.append('=');
out.append(t._value.toString());
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
for(int i=0; i < INDEX; i++)
{
if (t._nextIndex[i] != null)
toString(out,t._nextIndex[i]);
}
for (int i=t._nextOther.size();i-->0;)
toString(out,t._nextOther.get(i));
}
}
public Set<String> keySet()
{
Set<String> keys = new HashSet<>();
keySet(keys,this);
return keys;
}
private static <V> void keySet(Set<String> set, TreeTrie<V> t)
{
if (t != null)
{
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));
}
}
}

View File

@ -18,10 +18,7 @@
package org.eclipse.jetty.util;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@ -29,185 +26,33 @@ import java.util.Set;
/** A Trie String lookup data structure.
* @param <V>
*/
public class Trie<V>
public interface 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,
/*1*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 30,
/*2*/31, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, 27, -1, -1,
/*3*/-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 28, 29, -1, -1, -1, -1,
/*4*/-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
/*5*/15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
/*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,
};
/**
* 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 char[][] _bigIndex;
/**
* The number of rows allocated
*/
private char _rows;
public Trie()
{
this(128);
}
public Trie(int capacityInNodes)
{
_value=new Object[capacityInNodes];
_rowIndex=new char[capacityInNodes*32];
_key=new String[capacityInNodes];
}
/* ------------------------------------------------------------ */
/** Put and entry into the Trie
* @param s The key for the entry
* @param v The value of the entry
* @return True if the Trie had capacity to add the field.
*/
public boolean put(String s, V v)
{
int t=0;
int k;
int limit = s.length();
for(k=0; k < limit; k++)
{
char c=s.charAt(k);
int index=__lookup[c&0x7f];
if (index>=0)
{
int idx=t*ROW_SIZE+index;
t=_rowIndex[idx];
if (t==0)
{
if (_rows==_value.length)
return false;
t=_rowIndex[idx]=++_rows;
}
}
else if (c>127)
throw new IllegalArgumentException("non ascii character");
else
{
if (_bigIndex==null)
_bigIndex=new char[_value.length][];
char[] big=_bigIndex[t];
if (big==null)
big=_bigIndex[t]=new char[128];
t=big[c];
if (t==0)
{
if (_rows==_value.length)
return false;
t=big[c]=++_rows;
}
}
}
_key[t]=v==null?null:s;
V old=(V)_value[t];
_value[t] = v;
return true;
}
public boolean put(String s, V v);
/* ------------------------------------------------------------ */
/** Put a value as both a key and a value.
* @param v
* @return
* @param v The value and key
* @return True if the Trie had capacity to add the field.
*/
public boolean put(V v)
{
return put(v.toString(),v);
}
public V remove(String s)
{
V o=get(s);
put(s,null);
return o;
}
public boolean put(V v);
/* ------------------------------------------------------------ */
public V remove(String s);
/* ------------------------------------------------------------ */
/** Get and exact match from a String key
* @param s The key
* @return
*/
public V get(String s)
{
int t = 0;
int len = s.length();
for(int i=0; i < len; i++)
{
char c=s.charAt(i);
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;
}
}
return (V)_value[t];
}
public V get(String s);
/* ------------------------------------------------------------ */
/** Get and exact match from a segment of a ByteBuufer as key
* @param b The buffer
@ -215,32 +60,7 @@ public class Trie<V>
* @param len the length of the key
* @return The value or null if not found
*/
public V get(ByteBuffer b,int offset,int len)
{
int t = 0;
for(int i=0; i < len; i++)
{
byte c=b.get(offset+i);
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;
}
}
return (V)_value[t];
}
public V get(ByteBuffer b,int offset,int len);
/* ------------------------------------------------------------ */
/** Get the best match from key in a byte array.
@ -250,46 +70,7 @@ public class Trie<V>
* @param len the length of the key
* @return The value or null if not found
*/
public V getBest(byte[] b,int offset,int len)
{
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=__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,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return (V)_value[t];
}
}
return (V)_value[t];
}
public V getBest(byte[] b,int offset,int len);
/* ------------------------------------------------------------ */
/** Get the best match from key in a byte buffer.
@ -299,127 +80,8 @@ public class Trie<V>
* @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(0,b.array(),b.arrayOffset()+b.position()+offset,len);
return getBest(0,b,offset,len);
}
private V getBest(int t,ByteBuffer b,int offset,int len)
{
int pos=b.position()+offset;
for(int i=0; i < len; i++)
{
byte c=b.get(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,b,offset+i+1,len-i-1);
if (best!=null)
return best;
return (V)_value[t];
}
}
return (V)_value[t];
}
public V getBest(ByteBuffer b,int offset,int len);
@Override
public String toString()
{
StringBuilder buf = new StringBuilder();
toString(buf,0);
if (buf.length()==0)
return "{}";
buf.setCharAt(0,'{');
buf.append('}');
return buf.toString();
}
private <V> void toString(Appendable out, int t)
{
if (_value[t]!=null)
{
try
{
out.append(',');
out.append(_key[t]);
out.append('=');
out.append(_value[t].toString());
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
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==null?null:_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,0);
return keys;
}
private void keySet(Set<String> set, int t)
{
if (_value[t]!=null)
set.add(_key[t]);
for(int i=0; i < ROW_SIZE; i++)
{
int idx=t*ROW_SIZE+i;
if (_rowIndex[idx] != 0)
keySet(set,_rowIndex[idx]);
}
char[] big = _bigIndex==null?null:_bigIndex[t];
if (big!=null)
{
for (int i:big)
if (i!=0)
keySet(set,i);
}
}
/* ------------------------------------------------------------ */
public Set<String> keySet();
}

View File

@ -19,7 +19,6 @@
package org.eclipse.jetty.util;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

View File

@ -24,7 +24,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;

View File

@ -35,8 +35,6 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.Collection;
import javax.servlet.MultipartConfigElement;
@ -44,9 +42,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.Part;
import org.eclipse.jetty.util.MultiPartInputStreamParser.MultiPart;
import org.hamcrest.core.IsNot;
import org.junit.Test;
import org.hamcrest.core.IsNot;
/**
* MultiPartInputStreamTest

View File

@ -18,7 +18,9 @@
package org.eclipse.jetty.util;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;

View File

@ -20,14 +20,35 @@ package org.eclipse.jetty.util;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(value = Parameterized.class)
public class TrieTest
{
Trie<Integer> trie = new Trie<>();
@Parameterized.Parameters
public static Collection<Object[]> data()
{
Object[][] data = new Object[][]{
{new ArrayTrie<String>()},
{new TreeTrie<Integer>()}
};
return Arrays.asList(data);
}
Trie<Integer> trie;
public TrieTest(Trie<Integer> t)
{
trie=t;
}
@Before
public void before()

View File

@ -18,7 +18,7 @@
package org.eclipse.jetty.util.resource;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.is;
import java.io.File;
import java.io.IOException;

View File

@ -18,6 +18,8 @@
package org.eclipse.jetty.util.thread;
import static org.junit.Assert.assertTrue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@ -28,8 +30,6 @@ import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertTrue;
@RunWith(AdvancedRunner.class)
public class QueuedThreadPoolTest
{