]> WPIA git - gigi.git/blobdiff - lib/jetty/org/eclipse/jetty/util/ArrayTrie.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / util / ArrayTrie.java
diff --git a/lib/jetty/org/eclipse/jetty/util/ArrayTrie.java b/lib/jetty/org/eclipse/jetty/util/ArrayTrie.java
new file mode 100644 (file)
index 0000000..73ccc42
--- /dev/null
@@ -0,0 +1,445 @@
+//
+//  ========================================================================
+//  Copyright (c) 1995-2014 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 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> extends AbstractTrie<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)
+    {
+        super(true);
+        _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][];
+                if (t>=_bigIndex.length)
+                    return false;
+                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 V get(String s, int offset, int len)
+    {
+        int t = 0;
+        for(int i=0; i < len; i++)
+        {
+            char c=s.charAt(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 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);
+    }
+
+    /* ------------------------------------------------------------ */
+    @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);
+    }
+
+    /* ------------------------------------------------------------ */
+    @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;
+                int nt=_rowIndex[idx];
+                if (nt==0)
+                    break;
+                t=nt;
+            }
+            else
+            {
+                char[] big = _bigIndex==null?null:_bigIndex[t];
+                if (big==null)
+                    return null;
+                int nt=big[c];
+                if (nt==0)
+                    break;
+                t=nt;
+            }
+            
+            // 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++)
+        {
+            byte c=b[offset+i];
+            int index=__lookup[c&0x7f];
+            if (index>=0)
+            {
+                int idx=t*ROW_SIZE+index;
+                int nt=_rowIndex[idx];
+                if (nt==0)
+                    break;
+                t=nt;
+            }
+            else
+            {
+                char[] big = _bigIndex==null?null:_bigIndex[t];
+                if (big==null)
+                    return null;
+                int nt=big[c];
+                if (nt==0)
+                    break;
+                t=nt;
+            }
+            
+            // 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;
+                break;
+            }
+        }
+        return (V)_value[t];
+    }
+    
+    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;
+                int nt=_rowIndex[idx];
+                if (nt==0)
+                    break;
+                t=nt;
+            }
+            else
+            {
+                char[] big = _bigIndex==null?null:_bigIndex[t];
+                if (big==null)
+                    return null;
+                int nt=big[c];
+                if (nt==0)
+                    break;
+                t=nt;
+            }
+            
+            // 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;
+                break;
+            }
+        }
+        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 (t<_value.length&&_value[t]!=null)
+            set.add(_key[t]);
+
+        for(int i=0; i < ROW_SIZE; i++)
+        {
+            int idx=t*ROW_SIZE+i;
+            if (idx<_rowIndex.length && _rowIndex[idx] != 0)
+                keySet(set,_rowIndex[idx]);
+        }
+        
+        char[] big = _bigIndex==null||t>=_bigIndex.length?null:_bigIndex[t];
+        if (big!=null)
+        {
+            for (int i:big)
+                if (i!=0)
+                    keySet(set,i);
+        }
+    }
+    
+    @Override
+    public boolean isFull()
+    {
+        return _rows+1==_key.length;
+    }
+}