1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47 package org.eclipse.jgit.treewalk.filter;
48
49 import org.eclipse.jgit.util.RawParseUtils;
50
51
52
53
54
55
56
57
58
59
60
61 class ByteArraySet {
62
63 private int size;
64
65 private int grow;
66
67 private int mask;
68
69 private byte[][] table;
70
71
72
73
74
75
76 ByteArraySet(int capacity) {
77 initTable(1 << Integer.highestOneBit((capacity * 2) - 1));
78 }
79
80 private byte[] get(byte[] toFind, int length, int hash) {
81 final int msk = mask;
82 int i = hash & msk;
83 final byte[][] tbl = table;
84 byte[] obj;
85
86 while ((obj = tbl[i]) != null) {
87 if (equals(obj, toFind, length))
88 return obj;
89 i = (i + 1) & msk;
90 }
91 return null;
92 }
93
94 private static boolean equals(byte[] storedObj, byte[] toFind, int length) {
95 if (storedObj.length != length || toFind.length < length)
96 return false;
97 for (int i = 0; i < length; ++i) {
98 if (storedObj[i] != toFind[i])
99 return false;
100 }
101 return true;
102 }
103
104
105
106
107
108
109
110
111
112
113
114
115 boolean contains(byte[] toFind, int length, int hash) {
116 return get(toFind, length, hash) != null;
117 }
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142 byte[] addIfAbsent(final byte[] newValue, int length, int hash) {
143 final int msk = mask;
144 int i = hash & msk;
145 final byte[][] tbl = table;
146 byte[] obj;
147
148 while ((obj = tbl[i]) != null) {
149 if (equals(obj, newValue, length))
150 return obj;
151 i = (i + 1) & msk;
152 }
153
154 byte[] valueToInsert = copyIfNotSameSize(newValue, length);
155 if (++size == grow) {
156 grow();
157 insert(valueToInsert, hash);
158 } else
159 tbl[i] = valueToInsert;
160 return valueToInsert;
161 }
162
163 private static byte[] copyIfNotSameSize(byte[] newValue, int length) {
164 if (newValue.length == length)
165 return newValue;
166 byte[] ret = new byte[length];
167 System.arraycopy(newValue, 0, ret, 0, length);
168 return ret;
169 }
170
171
172
173
174 int size() {
175 return size;
176 }
177
178
179 boolean isEmpty() {
180 return size == 0;
181 }
182
183 private void insert(byte[] newValue, int hash) {
184 final int msk = mask;
185 int j = hash & msk;
186 final byte[][] tbl = table;
187 while (tbl[j] != null)
188 j = (j + 1) & msk;
189 tbl[j] = newValue;
190 }
191
192 private Hasher hasher = new Hasher(null, 0);
193
194 private void grow() {
195 final byte[][] oldTable = table;
196 final int oldSize = table.length;
197
198 initTable(oldSize << 1);
199 for (int i = 0; i < oldSize; i++) {
200 final byte[] obj = oldTable[i];
201 if (obj != null) {
202 hasher.init(obj, obj.length);
203 insert(obj, hasher.hash());
204 }
205 }
206 }
207
208 private void initTable(int sz) {
209 if (sz < 2)
210 sz = 2;
211 grow = sz >> 1;
212 mask = sz - 1;
213 table = new byte[sz][];
214 }
215
216
217 @Override
218 public String toString() {
219 StringBuilder sb = new StringBuilder();
220 sb.append('[');
221 for (byte[] b : table) {
222 if (b == null)
223 continue;
224 if (sb.length() > 1)
225 sb.append(" , ");
226 sb.append('"');
227 sb.append(RawParseUtils.decode(b));
228 sb.append('"');
229 sb.append('(');
230 sb.append(chainlength(b));
231 sb.append(')');
232 }
233 sb.append(']');
234 return sb.toString();
235 }
236
237 private int chainlength(byte[] b) {
238 Hasher h = new Hasher(b, b.length);
239 int hash = h.hash();
240 final int msk = mask;
241 int i = hash & msk;
242 final byte[][] tbl = table;
243 byte[] obj;
244
245 int n = 0;
246 while ((obj = tbl[i]) != null) {
247 if (equals(obj, b, b.length))
248 return n;
249 i = (i + 1) & msk;
250 ++n;
251 }
252 return -1;
253 }
254
255
256
257
258 static class Hasher {
259 private int hash;
260
261 private int pos;
262
263 private byte[] data;
264
265 private int length;
266
267 Hasher(byte[] data, int length) {
268 init(data, length);
269 }
270
271 void init(byte[] d, int l) {
272 this.data = d;
273 this.length = l;
274 pos = 0;
275 hash = 0;
276 }
277
278 int hash() {
279 while (pos < length)
280 hash = hash * 31 + data[pos++];
281 return hash;
282 }
283
284 int nextHash() {
285 for (;;) {
286 hash = hash * 31 + data[pos];
287 ++pos;
288 if (pos == length || data[pos] == '/')
289 return hash;
290 }
291 }
292
293 int getHash() {
294 return hash;
295 }
296
297 boolean hasNext() {
298 return pos < length;
299 }
300
301 public int length() {
302 return pos;
303 }
304
305 @Override
306 public String toString() {
307 StringBuilder sb = new StringBuilder();
308 for (int i = 0; i < pos; ++i)
309 sb.append((char) data[i]);
310 sb.append(" | ");
311 for (int i = pos; i < length; ++i)
312 sb.append((char) data[i]);
313 return sb.toString();
314 }
315 }
316
317 byte[][] toArray() {
318 byte[][] ret = new byte[size][];
319 int i = 0;
320 for (byte[] entry : table) {
321 if (entry != null)
322 ret[i++] = entry;
323 }
324 return ret;
325 }
326
327 }