propose-patch
[org.ibex.core.git] / src / org / ibex / util / Hash.java
1 // Copyright (C) 2003 Adam Megacz <adam@ibex.org> all rights reserved.
2 //
3 // You may modify, copy, and redistribute this code under the terms of
4 // the GNU Library Public License version 2.1, with the exception of
5 // the portion of clause 6a after the semicolon (aka the "obnoxious
6 // relink clause")
7
8 package org.ibex.util;
9
10 import java.util.*;
11
12 /** Implementation of an unsynchronized hash table, with one or two
13  *  keys, using Radke's quadradic residue linear probing instead of
14  *  buckets to minimize object count (less allocations, faster GC).
15  *  See C. Radke, Communications of the ACM, 1970, 103-105
16  *
17  *  Not threadsafe.
18  */
19 public class Hash {
20     /** this object is inserted as key in a slot when the
21      *  corresponding value is removed -- this ensures that the
22      *  probing sequence for any given key remains the same even if
23      *  other keys are removed.
24      */
25     private static Object placeholder = new Object();
26
27     /** the number of entries with at least one non-null key */
28     private int usedslots = 0;
29
30     /** the number of entries with non-null values */
31     protected int size = 0;
32
33     /** when num_slots < loadFactor * size, rehash into a bigger table */
34     private final int loadFactor;
35
36     /** primary keys */
37     private Object[] keys1 = null;
38
39     /** secondary keys; null if no secondary key has ever been added */
40     private Object[] keys2 = null;
41
42     /** the values for the table */
43     private Object[] vals = null;
44     
45     /** the number of entries with a non-null value */
46     public int size() { return size; }
47
48     /** empties the table */
49     public void clear() {
50         size = 0;
51         usedslots = 0;
52         for(int i=0; i<vals.length; i++) {
53             vals[i] = null;
54             keys1[i] = null;
55             if (keys2 != null) keys2[i] = null;
56         }
57     }
58
59     /** returns all the primary keys in the table */
60     public Enumeration keys() { return new HashEnum(); }
61
62     public Hash() { this(25, 3); }
63     public Hash(int initialcapacity, int loadFactor) {
64         // using a pseudoprime in the form 4x+3 ensures full coverage
65         initialcapacity = initialcapacity / 4;
66         initialcapacity = 4 * initialcapacity + 3;
67         keys1 = new Object[initialcapacity];
68         vals = new Object[initialcapacity];
69         this.loadFactor = loadFactor;
70     }
71     
72     public void remove(Object k1) { remove(k1, null); }
73     public void remove(Object k1, Object k2) { put_(k1, k2, null); }
74
75     private void rehash() {
76         Object[] oldkeys1 = keys1;
77         Object[] oldkeys2 = keys2;
78         Object[] oldvals = vals;
79         keys1 = new Object[oldvals.length * 2];
80         keys2 = oldkeys2 == null ? null : new Object[oldvals.length * 2];
81         vals = new Object[oldvals.length * 2];
82         size = 0;
83         usedslots = 0;
84         for(int i=0; i<oldvals.length; i++)
85             if (((oldkeys1[i] != null && oldkeys1[i] != placeholder) || (oldkeys2 != null && oldkeys2[i] != null)) && oldvals[i] != null)
86                 put_(oldkeys1[i], oldkeys2 == null ? null : oldkeys2[i], oldvals[i]);
87     }
88
89     public Object get(Object k1) { return get(k1, null); }
90     public Object get(Object k1, Object k2) {
91         if (k2 != null && keys2 == null) return null;
92         int hash = (k1 == null ? 0 : k1.hashCode()) ^ (k2 == null ? 0 : k2.hashCode());
93         int dest = Math.abs(hash) % vals.length;
94         int odest = dest;
95         int tries = 1;
96         boolean plus = true;
97         while (keys1[dest] != null || (keys2 != null && keys2[dest] != null)) {
98             Object hk1 = keys1[dest];
99             Object hk2 = keys2 == null ? null : keys2[dest];
100             if ((k1 == hk1 || (k1 != null && hk1 != null && k1.equals(hk1))) &&
101                 (k2 == hk2 || (k2 != null && hk2 != null && k2.equals(hk2)))) {
102                 return vals[dest];
103             }
104             dest = Math.abs((odest + (plus ? 1 : -1 ) * tries * tries) % vals.length);
105             if (plus) tries++;
106             plus = !plus;
107         }
108         return null;
109     }
110
111     public void put(Object k1, Object v) { put(k1, null, v); }
112     public void put(Object k1, Object k2, Object v) { put_(k1, k2, v); }
113     private void put_(Object k1, Object k2, Object v) {
114         if (usedslots * loadFactor > vals.length) rehash();
115         int hash = (k1 == null ? 0 : k1.hashCode()) ^ (k2 == null ? 0 : k2.hashCode());
116         int dest = Math.abs(hash) % vals.length;
117         int odest = dest;
118         boolean plus = true;
119         int tries = 1;
120         while (true) {
121             Object hk1 = keys1[dest];
122             Object hk2 = keys2 == null ? null : keys2[dest];
123             if (hk1 == null && hk2 == null) {                                         // empty slot
124                 if (v == null) return;
125                 size++;
126                 usedslots++;
127                 break;
128             }
129
130             if ((k1 == hk1 || (k1 != null && hk1 != null && k1.equals(hk1))) &&       // replacing former entry
131                 (k2 == hk2 || (k2 != null && hk2 != null && k2.equals(hk2)))) {
132
133                 // we don't actually remove things from the table; rather, we insert a placeholder
134                 if (v == null) {
135                     k1 = placeholder;
136                     k2 = null;
137                     size--;
138                 }
139                 break;
140             }
141
142             dest = Math.abs((odest + (plus ? 1 : -1 ) * tries * tries) % vals.length);
143             if (plus) tries++;
144             plus = !plus;
145         }
146
147         keys1[dest] = k1;
148         if (k2 != null && keys2 == null) keys2 = new Object[keys1.length];
149         if (keys2 != null) keys2[dest] = k2;
150         vals[dest] = v;
151     }
152
153     private class HashEnum implements java.util.Enumeration {
154         private int iterator = 0;
155         private int found = 0;
156         
157         public HashEnum () { }
158         
159         public boolean hasMoreElements() {
160             return found < usedslots;
161         }
162
163         public Object nextElement() {
164             if (!hasMoreElements()) throw new java.util.NoSuchElementException();
165
166             Object o = null;
167             while (o == null) o = keys1[iterator++];
168             if (o == null) throw new IllegalStateException("Didn't find an element, when I should have.");
169             found++;
170             
171             return o;
172         }
173     }
174 }
175
176