jetty-9 temporary tree map impl

This commit is contained in:
Greg Wilkins 2012-02-09 18:02:51 +11:00
parent ceac0f8525
commit 9c1d3ff0b3
4 changed files with 90 additions and 509 deletions

View File

@ -1,151 +0,0 @@
// ========================================================================
// Copyright (c) 2004-2009 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.io;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import org.eclipse.jetty.io.BufferCache.CachedBuffer;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.junit.Before;
import org.junit.Test;
import java.nio.ByteBuffer;
/**
*
*/
public class BufferCacheTest
{
private final static String[] S = {"S0", "S1", "s2", "s3" };
private BufferCache cache;
@Before
public void init() throws Exception
{
cache=new BufferCache();
cache.add(S[1],1);
cache.add(S[2],2);
cache.add(S[3],3);
}
@Test
public void testLookupIndex()
{
for (int i=0; i<S.length; i++)
{
String s="S0S1s2s3s0s1S2S3";
ByteBuffer buf=ByteBuffer.wrap(s.getBytes(StringUtil.__ISO_8859_1_CHARSET),i*2,2);
BufferCache.CachedBuffer b=cache.get(buf);
int index=b==null?-1:b.getOrdinal();
if (i>0)
assertEquals(i,index);
else
assertEquals(-1,index);
}
}
@Test
public void testGetBuffer()
{
for (int i=0; i<S.length; i++)
{
String s="S0S1s2s3s0s1S2S3";
ByteBuffer buf=ByteBuffer.wrap(s.getBytes(StringUtil.__ISO_8859_1_CHARSET),i*2,2);
ByteBuffer b=cache.getBuffer(buf);
assertEquals(i,b.get(1)-'0');
}
}
@Test
public void testGet()
{
for (int i=0; i<S.length; i++)
{
String s="S0S1s2s3s0s1S2S3";
ByteBuffer buf=ByteBuffer.wrap(s.getBytes(StringUtil.__ISO_8859_1_CHARSET),i*2,2);
CachedBuffer b=cache.get(buf);
if (i>0)
assertEquals(S[i],b.toString());
else
assertEquals(null,b);
}
}
@Test
public void testLookupBuffer()
{
for (int i=0; i<S.length; i++)
{
String s="S0S1s2s3s0s1S2S3";
ByteBuffer buf=ByteBuffer.wrap(s.getBytes(StringUtil.__ISO_8859_1_CHARSET),i*2,2);
ByteBuffer b=cache.lookup(buf);
assertEquals(S[i],BufferUtil.toString(b));
if (i>0)
assertEquals(""+i, S[i], BufferUtil.toString(b));
else
{
assertNotSame(""+i, S[i], BufferUtil.toString(b));
assertEquals(""+i, S[i], BufferUtil.toString(b));
}
}
}
@Test
public void testLookupPartialBuffer()
{
String key="44444";
cache.add(key,4);
ByteBuffer buf=BufferUtil.toBuffer("44444");
ByteBuffer b=cache.get(buf).getBuffer();
assertEquals("44444",BufferUtil.toString(b));
assertEquals(4,cache.getOrdinal(b));
buf=BufferUtil.toBuffer("4444");
assertEquals(null,cache.get(buf));
assertSame(buf,cache.getBuffer(buf));
assertEquals(-1,cache.getOrdinal(buf));
buf=BufferUtil.toBuffer("44444x");
assertEquals("44444",cache.get(buf).toString());
assertEquals(4,cache.getOrdinal(buf));
}
@Test
public void testToString()
{
for (int i=0; i<S.length; i++)
{
String s="S0S1s2s3s0s1S2S3";
ByteBuffer buf=ByteBuffer.wrap(s.getBytes(StringUtil.__ISO_8859_1_CHARSET),i*2,2);
String str=cache.getString(buf);
assertEquals(S[i],str);
if (i>0)
assertSame(S[i], str);
else
assertNotSame(S[i], str);
}
}
}

View File

@ -16,9 +16,10 @@ package org.eclipse.jetty.util;
import java.nio.ByteBuffer;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashSet;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/* ------------------------------------------------------------ */
/** Map implementation Optimized for Strings keys..
@ -34,39 +35,83 @@ import java.util.Set;
*/
public class StringMap<O> extends AbstractMap<String,O>
{
private final TreeMap<Object, O> _map;
public static final boolean CASE_INSENSTIVE=true;
protected static final int __HASH_WIDTH=17;
/* ------------------------------------------------------------ */
private final boolean _ignoreCase;
private final boolean _caseInsensitive;
protected Node<O> _root;
protected HashSet<Map.Entry<String,O>> _entrySet=new HashSet<>(3);
/* ------------------------------------------------------------ */
/** Constructor.
*/
public StringMap()
{
_ignoreCase=false;
this(false);
}
/* ------------------------------------------------------------ */
/** Constructor.
* @param ignoreCase
*/
public StringMap(boolean ignoreCase)
public StringMap(final boolean ignoreCase)
{
_ignoreCase=ignoreCase;
_caseInsensitive=ignoreCase;
_map = new TreeMap<>(new Comparator<Object>()
{
@Override
public int compare(Object o1, Object o2)
{
String s1=(o1 instanceof String)?(String)o1:null;
ByteBuffer b1=(o1 instanceof ByteBuffer)?(ByteBuffer)o1:null;
if (s1==null && b1==null)
s1=o1.toString();
String s2=(String)o2;
int n1 = s1==null?b1.remaining():s1.length();
int n2 = s2.length();
int min = Math.min(n1, n2);
for (int i = 0; i < min; i++) {
char c1 = s1==null?(char)b1.get(b1.position()+i):s1.charAt(i);
char c2 = s2.charAt(i);
if (c1 != c2) {
if (ignoreCase)
{
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if (c1 != c2) {
c1 = Character.toLowerCase(c1);
c2 = Character.toLowerCase(c2);
if (c1 != c2) {
// No overflow because of numeric promotion
return c1 - c2;
}
}
}
else
return c1 - c2;
}
}
return n1 - n2;
}
});
}
/* ------------------------------------------------------------ */
public boolean isIgnoreCase()
{
return _ignoreCase;
return _caseInsensitive;
}
/* ------------------------------------------------------------ */
public static void main(String[] arg)
{
StringMap<String> map = new StringMap<>();
@ -92,366 +137,103 @@ public class StringMap<O> extends AbstractMap<String,O>
System.err.println("3="+map.get("foob"));
System.err.println("1="+map.get("foo"));
map.put("fool","4");
map.put("fop","5");
map.put("fred","6");
System.err.println("2="+map.get(BufferUtil.toBuffer("foobar")));
System.err.println("3="+map.get(BufferUtil.toBuffer("foob")));
System.err.println("1="+map.get(BufferUtil.toBuffer("foo")));
}
/* ------------------------------------------------------------ */
@Override
public O put(String key, O value)
{
if (key==null)
throw new IllegalArgumentException();
Node<O> node = _root;
Node<O> last = null;
for (int i=0;i<key.length();i++)
{
char ch = key.charAt(i);
if (_ignoreCase)
ch=Character.toLowerCase(ch);
if (node==null)
{
node = new Node<O>(key,value,_ignoreCase);
_entrySet.add(node);
if (last==null)
_root=node;
else
{
throw new Error("unimplemented");
}
return null;
}
else
{
last=node;
node=node.nextNode(i,ch);
if (node==null)
{
node = new Node<O>(key,value,_ignoreCase);
_entrySet.add(node);
last.addNext(node);
return null;
}
}
}
if (node!=null)
{
if (key.equals(node.getKey()))
return node.setValue(value);
}
return null;
return _map.put(key,value);
}
/* ------------------------------------------------------------ */
@Override
public O get(Object key)
{
if (key==null)
throw new IllegalArgumentException();
return get(key.toString());
return _map.get(key);
}
/* ------------------------------------------------------------ */
public O get(String key)
{
if (key==null)
throw new IllegalArgumentException();
Map.Entry<String,O> entry = getEntry(key,0,key.length());
if (entry==null)
return null;
return entry.getValue();
return _map.get(key);
}
/* ------------------------------------------------------------ */
public O get(String key,int offset,int length)
{
return _map.get(key.substring(offset,offset+length));
}
/* ------------------------------------------------------------ */
public O get(ByteBuffer buffer, int position, int length)
{
Map.Entry<String,O> entry = getEntry(buffer,position,length);
if (entry==null)
return null;
return entry.getValue();
ByteBuffer slice=buffer.slice();
slice.position(position);
slice.limit(position+length);
return _map.get(slice);
}
/* ------------------------------------------------------------ */
/** Get a map entry by substring key.
* @param key String containing the key
* @param offset Offset of the key within the String.
* @param length The length of the key
* @return The Map.Entry for the key or null if the key is not in
* the map.
*/
public Map.Entry<String,O> getEntry(String key,int offset, int length)
{
if (key==null)
throw new IllegalArgumentException();
Node<O> node = _root;
for (int i=0;i<length;i++)
{
char ch = key.charAt(offset+i);
if (_ignoreCase)
ch=Character.toLowerCase(ch);
if (node==null)
return null;
node=node.nextNode(i,ch);
}
return node.getKey()==null&&node.length()==length?null:node;
}
/* ------------------------------------------------------------ */
/** Get a map entry by char array key.
* @param key char array containing the key
* @param offset Offset of the key within the array.
* @param length The length of the key
* @return The Map.Entry for the key or null if the key is not in
* the map.
*/
public Map.Entry<String, O> getEntry(char[] key,int offset, int length)
{
if (key==null)
throw new IllegalArgumentException();
Node<O> node = _root;
return node;
}
/* ------------------------------------------------------------ */
/** Get a map entry by byte array key, using as much of the passed key as needed for a match.
* A simple 8859-1 byte to char mapping is assumed.
* @param key byte array containing the key
* @param offset Offset of the key within the array.
* @param length The length of the key
* @return The Map.Entry for the key or null if the key is not in
* the map.
*/
public Map.Entry<String,O> getEntry(byte[] key,int offset, int length)
{
if (key==null)
throw new IllegalArgumentException();
Node<O> node = _root;
return node;
}
/* ------------------------------------------------------------ */
/** Get a map entry by ByteBuffer key, using as much of the passed key as needed for a match.
* A simple 8859-1 byte to char mapping is assumed.
* @param key ByteBuffer containing the key
* @return The Map.Entry for the key or null if the key is not in
* the map.
*/
public Map.Entry<String,O> getEntry(ByteBuffer key)
{
return getEntry(key,key.position(),key.remaining());
}
/* ------------------------------------------------------------ */
/** Get a map entry by ByteBuffer key, using as much of the passed key as needed for a match.
* A simple 8859-1 byte to char mapping is assumed.
* @param key ByteBuffer containing the key
* @return The Map.Entry for the key or null if the key is not in
* the map.
*/
public Map.Entry<String,O> getEntry(ByteBuffer key,int position,int length)
{
if (key==null)
throw new IllegalArgumentException();
if (!key.isReadOnly() && !key.isDirect())
return getEntry(key.array(),key.position(),key.remaining());
Node<O> node = _root;
return node;
}
/* ------------------------------------------------------------ */
@Override
public O remove(Object key)
{
if (key==null)
return remove(null);
return remove(key.toString());
return _map.remove(key);
}
/* ------------------------------------------------------------ */
public O remove(String key)
{
if (key==null)
throw new IllegalArgumentException();
Node<O> node = _root;
O old = node._value;
_entrySet.remove(node);
node._value=null;
return old;
return _map.remove(key);
}
/* ------------------------------------------------------------ */
@Override
public Set<Map.Entry<String,O>> entrySet()
{
return Collections.unmodifiableSet(_entrySet);
Object o=_map.entrySet();
return Collections.unmodifiableSet((Set<Map.Entry<String,O>>)o);
}
/* ------------------------------------------------------------ */
@Override
public int size()
{
return _entrySet.size();
return _map.size();
}
/* ------------------------------------------------------------ */
@Override
public boolean isEmpty()
{
return _entrySet.isEmpty();
return _map.isEmpty();
}
/* ------------------------------------------------------------ */
@Override
public boolean containsKey(Object key)
{
if (key==null)
return false;
return
getEntry(key.toString(),0,key==null?0:key.toString().length())!=null;
return _map.containsKey(key);
}
/* ------------------------------------------------------------ */
@Override
public void clear()
{
_root=null;
_entrySet.clear();
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private static class Node<O> implements Map.Entry<String,O>
{
private final String _key;
private O _value;
private final char[] _chars;
private final int _length;
private final Node<O>[] _splits;
private Node<O> _next;
Node(String key, O value, boolean caseInsensitive)
{
_key=key;
_value=value;
_chars = _key.toCharArray();
_length=_chars.length;
if (caseInsensitive)
for (int i=_length;i-->0;)
_chars[i]=Character.toLowerCase(_chars[i]);
_splits=null;
}
public int length()
{
return _length;
}
Node(char[] chars,int length)
{
_key=null;
_value=null;
_chars = chars;
_length=length;
_splits=new Node[__HASH_WIDTH];
}
Node<O> nextNode(int index,char c)
{
if (index<_chars.length)
{
if (_chars[index]==c)
return this;
if (_next!=null)
return _next.nextNode(index,c);
}
if (index==_chars.length && _splits!=null)
{
Node<O> split=_splits[c%_splits.length];
if (split!=null)
return split.nextNode(index,c);
}
if (_next!=null)
return _next.nextNode(index,c);
return null;
}
Node<O> split(int index)
{
Node<O> pre = new Node(_chars,index);
pre.addSplit(this);
return pre;
}
void addSplit(Node<O> split)
{
char c=_chars[_length];
int i=c%__HASH_WIDTH;
Node<O> s=_splits[i];
if (s==null)
_splits[i]=split;
else
_splits[i].addNext(split);
}
void addNext(Node<O> next)
{
Node<O> s = this;
while (s._next!=null)
s=s._next;
s._next=next;
}
@Override
public String getKey()
{
return _key;
}
@Override
public O getValue()
{
return _value;
}
@Override
public O setValue(O value)
{
O old=_value;
_value=value;
return old;
}
_map.clear();
}
}

View File

@ -80,8 +80,8 @@ public class StringUtil
*/
public static String normalizeCharset(String s,int offset,int length)
{
Map.Entry<String,String> n=CHARSETS.getEntry(s,offset,length);
return (n==null)?s.substring(offset,offset+length):n.getValue();
String n=CHARSETS.get(s,offset,length);
return (n==null)?s.substring(offset,offset+length):n;
}
/* ------------------------------------------------------------ */
@ -90,9 +90,9 @@ public class StringUtil
*/
public static String normalizeCharset(ByteBuffer b,int position,int length)
{
Map.Entry<String,String> n=CHARSETS.getEntry(b,position,length);
String n=CHARSETS.get(b,position,length);
if (n!=null)
return n.getValue();
return n;
ByteBuffer slice = b.slice();
slice.position(position);
slice.limit(position+length);

View File

@ -131,56 +131,6 @@ public class StringMapTest
}
/*
* Test for Map.Entry getEntry(String, int, int)
*/
@Test
public void testGetEntryStringintint()
{
Map.Entry<String,String> entry;
entry=m5.getEntry("xabcyz",1,3);
assertTrue(entry!=null);
assertEquals("abc",entry.getKey());
assertEquals("2",entry.getValue());
entry=m5.getEntry("xaBcyz",1,3);
assertTrue(entry==null);
entry=m5i.getEntry("xaBcyz",1,3);
assertTrue(entry!=null);
assertEquals("abc",entry.getKey());
assertEquals("2",entry.getValue());
entry.setValue("x");
assertEquals("{[c:abc=x]}",entry.toString());
}
/*
* Test for Map.Entry getEntry(char[], int, int)
*/
@Test
public void testGetEntrycharArrayintint()
{
char[] xabcyz = {'x','a','b','c','y','z'};
char[] xaBcyz = {'x','a','B','c','y','z'};
Map.Entry<String,String> entry;
entry=m5.getEntry(xabcyz,1,3);
assertTrue(entry!=null);
assertEquals("abc",entry.getKey());
assertEquals("2",entry.getValue());
entry=m5.getEntry(xaBcyz,1,3);
assertTrue(entry==null);
entry=m5i.getEntry(xaBcyz,1,3);
assertTrue(entry!=null);
assertEquals("abc",entry.getKey());
assertEquals("2",entry.getValue());
}
/*
* Test for Object remove(Object)
*/