1
18
19 package io.undertow.util;
20
21
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.NoSuchElementException;
26
27
38 public class SubstringMap<V> {
39 private static final int ALL_BUT_LAST_BIT = ~1;
40
41 private volatile Object[] table = new Object[16];
42 private int size;
43
44 public SubstringMatch<V> get(String key, int length) {
45 return get(key, length, false);
46 }
47
48 public SubstringMatch<V> get(String key) {
49 return get(key, key.length(), false);
50 }
51
52 private SubstringMatch<V> get(String key, int length, boolean exact) {
53 if(key.length() < length) {
54 throw new IllegalArgumentException();
55 }
56 Object[] table = this.table;
57 int hash = hash(key, length);
58 int pos = tablePos(table, hash);
59 int start = pos;
60 while (table[pos] != null) {
61 if(exact) {
62 if(table[pos].equals(key)) {
63 return (SubstringMatch<V>) table[pos + 1];
64 }
65 } else {
66 if (doEquals((String) table[pos], key, length)) {
67 return (SubstringMatch<V>) table[pos + 1];
68 }
69 }
70 pos += 2;
71 if(pos >= table.length) {
72 pos = 0;
73 }
74 if(pos == start) {
75 return null;
76 }
77 }
78 return null;
79 }
80
81 private int tablePos(Object[] table, int hash) {
82 return (hash & (table.length - 1)) & ALL_BUT_LAST_BIT;
83 }
84
85 private boolean doEquals(String s1, String s2, int length) {
86 if(s1.length() != length || s2.length() < length) {
87 return false;
88 }
89 for(int i = 0; i < length; ++i) {
90 if(s1.charAt(i) != s2.charAt(i)) {
91 return false;
92 }
93 }
94 return true;
95 }
96
97 public synchronized void put(String key, V value) {
98 if (key == null) {
99 throw new NullPointerException();
100 }
101 Object[] newTable;
102 if (table.length / (double) size < 4 && table.length != Integer.MAX_VALUE) {
103 newTable = new Object[table.length << 1];
104 for (int i = 0; i < table.length; i += 2) {
105 if (table[i] != null) {
106 doPut(newTable, (String) table[i], table[i + 1]);
107 }
108 }
109 } else {
110 newTable = new Object[table.length];
111 System.arraycopy(table, 0, newTable, 0, table.length);
112 }
113 doPut(newTable, key, new SubstringMap.SubstringMatch<>(key, value));
114 this.table = newTable;
115 size++;
116 }
117
118 public synchronized V remove(String key) {
119 if (key == null) {
120 throw new NullPointerException();
121 }
122
123
124
125 V value = null;
126 Object[] newTable = new Object[table.length];
127 for (int i = 0; i < table.length; i += 2) {
128 if (table[i] != null && !table[i].equals(key)) {
129 doPut(newTable, (String) table[i], table[i + 1]);
130 } else if (table[i] != null) {
131 value = (V) table[i + 1];
132 size--;
133 }
134 }
135 this.table = newTable;
136 if(value == null) {
137 return null;
138 }
139 return ((SubstringMatch<V>)value).getValue();
140 }
141
142 private void doPut(Object[] newTable, String key, Object value) {
143 int hash = hash(key, key.length());
144 int pos = tablePos(newTable, hash);
145 while (newTable[pos] != null && !newTable[pos].equals(key)) {
146 pos += 2;
147 if (pos >= newTable.length) {
148 pos = 0;
149 }
150 }
151 newTable[pos] = key;
152 newTable[pos + 1] = value;
153 }
154
155 public Map<String, V> toMap() {
156 Map<String, V> map = new HashMap<>();
157 Object[] t = this.table;
158 for(int i = 0; i < t.length; i += 2) {
159 if(t[i] != null) {
160 map.put((String)t[i], ((SubstringMatch<V>)t[i+1]).value);
161 }
162 }
163 return map;
164 }
165
166 public synchronized void clear() {
167 size = 0;
168 table = new Object[16];
169 }
170
171 private static int hash(String value, int length) {
172 if (length == 0) {
173 return 0;
174 }
175 int h = 0;
176 for (int i = 0; i < length; i++) {
177 h = 31 * h + value.charAt(i);
178 }
179 return h;
180 }
181
182 public Iterable<String> keys() {
183 return new Iterable<String>() {
184 @Override
185 public Iterator<String> iterator() {
186 final Object[] tMap = table;
187 int i = 0;
188 while (i < table.length && tMap[i] == null) {
189 i += 2;
190 }
191 final int startPos = i;
192
193 return new Iterator<String>() {
194
195 private Object[] map = tMap;
196
197 private int pos = startPos;
198
199 @Override
200 public boolean hasNext() {
201 return pos < table.length;
202 }
203
204 @Override
205 public String next() {
206 if (!hasNext()) {
207 throw new NoSuchElementException();
208 }
209 String ret = (String) map[pos];
210
211 pos += 2;
212 while (pos < table.length && tMap[pos] == null) {
213 pos += 2;
214 }
215 return ret;
216 }
217
218 @Override
219 public void remove() {
220 throw new UnsupportedOperationException();
221 }
222 };
223 }
224 };
225
226 }
227
228 public static final class SubstringMatch<V> {
229 private final String key;
230 private final V value;
231
232 public SubstringMatch(String key, V value) {
233 this.key = key;
234 this.value = value;
235 }
236
237 public String getKey() {
238 return key;
239 }
240
241 public V getValue() {
242 return value;
243 }
244 }
245 }
246