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