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 package org.eclipse.jgit.notes;
45
46 import static org.eclipse.jgit.lib.FileMode.TREE;
47
48 import java.io.IOException;
49 import java.util.Iterator;
50 import java.util.NoSuchElementException;
51
52 import org.eclipse.jgit.lib.AbbreviatedObjectId;
53 import org.eclipse.jgit.lib.AnyObjectId;
54 import org.eclipse.jgit.lib.MutableObjectId;
55 import org.eclipse.jgit.lib.ObjectId;
56 import org.eclipse.jgit.lib.ObjectInserter;
57 import org.eclipse.jgit.lib.ObjectReader;
58 import org.eclipse.jgit.lib.TreeFormatter;
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84 class FanoutBucket extends InMemoryNoteBucket {
85
86
87
88
89
90
91
92 private final NoteBucket[] table;
93
94
95 private int cnt;
96
97 FanoutBucket(int prefixLen) {
98 super(prefixLen);
99 table = new NoteBucket[256];
100 }
101
102 void setBucket(int cell, ObjectId id) {
103 table[cell] = new LazyNoteBucket(id);
104 cnt++;
105 }
106
107 void setBucket(int cell, InMemoryNoteBucket bucket) {
108 table[cell] = bucket;
109 cnt++;
110 }
111
112 @Override
113 Note getNote(AnyObjectId objId, ObjectReader or) throws IOException {
114 NoteBucket b = table[cell(objId)];
115 return b != null ? b.getNote(objId, or) : null;
116
117 }
118
119 NoteBucket getBucket(int cell) {
120 return table[cell];
121 }
122
123 static InMemoryNoteBucket loadIfLazy(NoteBucket b, AnyObjectId prefix,
124 ObjectReader or) throws IOException {
125 if (b == null)
126 return null;
127 if (b instanceof InMemoryNoteBucket)
128 return (InMemoryNoteBucket) b;
129 return ((LazyNoteBucket) b).load(prefix, or);
130 }
131
132 @Override
133 Iterator<Note> iterator(AnyObjectId objId, final ObjectReader reader)
134 throws IOException {
135 final MutableObjectIdtId.html#MutableObjectId">MutableObjectId id = new MutableObjectId();
136 id.fromObjectId(objId);
137
138 return new Iterator<Note>() {
139 private int cell;
140
141 private Iterator<Note> itr;
142
143 @Override
144 public boolean hasNext() {
145 if (itr != null && itr.hasNext())
146 return true;
147
148 for (; cell < table.length; cell++) {
149 NoteBucket b = table[cell];
150 if (b == null)
151 continue;
152
153 try {
154 id.setByte(prefixLen >> 1, cell);
155 itr = b.iterator(id, reader);
156 } catch (IOException err) {
157 throw new RuntimeException(err);
158 }
159
160 if (itr.hasNext()) {
161 cell++;
162 return true;
163 }
164 }
165 return false;
166 }
167
168 @Override
169 public Note next() {
170 if (hasNext())
171 return itr.next();
172 else
173 throw new NoSuchElementException();
174 }
175
176 @Override
177 public void remove() {
178 throw new UnsupportedOperationException();
179 }
180 };
181 }
182
183 @Override
184 int estimateSize(AnyObjectId noteOn, ObjectReader or) throws IOException {
185
186 if (LeafBucket.MAX_SIZE * 3 / 4 <= cnt)
187 return 1 + LeafBucket.MAX_SIZE;
188
189
190
191
192
193 MutableObjectId id = new MutableObjectId();
194 id.fromObjectId(noteOn);
195
196 int sz = 0;
197 for (int cell = 0; cell < 256; cell++) {
198 NoteBucket b = table[cell];
199 if (b == null)
200 continue;
201
202 id.setByte(prefixLen >> 1, cell);
203 sz += b.estimateSize(id, or);
204 if (LeafBucket.MAX_SIZE < sz)
205 break;
206 }
207 return sz;
208 }
209
210 @Override
211 InMemoryNoteBucket set(AnyObjectId../../../org/eclipse/jgit/lib/AnyObjectId.html#AnyObjectId">AnyObjectId noteOn, AnyObjectId noteData,
212 ObjectReader or) throws IOException {
213 int cell = cell(noteOn);
214 NoteBucket b = table[cell];
215
216 if (b == null) {
217 if (noteData == null)
218 return this;
219
220 LeafBucket n = new LeafBucket(prefixLen + 2);
221 table[cell] = n.set(noteOn, noteData, or);
222 cnt++;
223 return this;
224
225 } else {
226 NoteBucket n = b.set(noteOn, noteData, or);
227 if (n == null) {
228 table[cell] = null;
229 cnt--;
230
231 if (cnt == 0)
232 return null;
233
234 return contractIfTooSmall(noteOn, or);
235
236 } else if (n != b) {
237 table[cell] = n;
238 }
239 return this;
240 }
241 }
242
243 InMemoryNoteBucket contractIfTooSmall(AnyObjectId noteOn, ObjectReader or)
244 throws IOException {
245 if (estimateSize(noteOn, or) < LeafBucket.MAX_SIZE) {
246
247 InMemoryNoteBucket r = new LeafBucket(prefixLen);
248 for (Iterator<Note> i = iterator(noteOn, or); i.hasNext();)
249 r = r.append(i.next());
250 r.nonNotes = nonNotes;
251 return r;
252 }
253
254 return this;
255 }
256
257 private static final byte[] hexchar = { '0', '1', '2', '3', '4', '5', '6',
258 '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
259
260 @Override
261 ObjectId writeTree(ObjectInserter inserter) throws IOException {
262 return inserter.insert(build(true, inserter));
263 }
264
265 @Override
266 ObjectId getTreeId() {
267 try (ObjectInserter.Formatter f = new ObjectInserter.Formatter()) {
268 return f.idFor(build(false, null));
269 } catch (IOException e) {
270
271 throw new RuntimeException(e);
272 }
273 }
274
275 private TreeFormatter build(boolean insert, ObjectInserter inserter)
276 throws IOException {
277 byte[] nameBuf = new byte[2];
278 TreeFormatter fmt = new TreeFormatter(treeSize());
279 NonNoteEntry e = nonNotes;
280
281 for (int cell = 0; cell < 256; cell++) {
282 NoteBucket b = table[cell];
283 if (b == null)
284 continue;
285
286 nameBuf[0] = hexchar[cell >>> 4];
287 nameBuf[1] = hexchar[cell & 0x0f];
288
289 while (e != null && e.pathCompare(nameBuf, 0, 2, TREE) < 0) {
290 e.format(fmt);
291 e = e.next;
292 }
293
294 ObjectId id;
295 if (insert) {
296 id = b.writeTree(inserter);
297 } else {
298 id = b.getTreeId();
299 }
300 fmt.append(nameBuf, 0, 2, TREE, id);
301 }
302
303 for (; e != null; e = e.next)
304 e.format(fmt);
305 return fmt;
306 }
307
308 private int treeSize() {
309 int sz = cnt * TreeFormatter.entrySize(TREE, 2);
310 for (NonNoteEntry e = nonNotes; e != null; e = e.next)
311 sz += e.treeEntrySize();
312 return sz;
313 }
314
315 @Override
316 InMemoryNoteBucket append(Note note) {
317 int cell = cell(note);
318 InMemoryNoteBucket b = (InMemoryNoteBucket) table[cell];
319
320 if (b == null) {
321 LeafBucket n = new LeafBucket(prefixLen + 2);
322 table[cell] = n.append(note);
323 cnt++;
324
325 } else {
326 InMemoryNoteBucket n = b.append(note);
327 if (n != b)
328 table[cell] = n;
329 }
330 return this;
331 }
332
333 private int cell(AnyObjectId id) {
334 return id.getByte(prefixLen >> 1);
335 }
336
337 private class LazyNoteBucket extends NoteBucket {
338 private final ObjectId treeId;
339
340 LazyNoteBucket(ObjectId treeId) {
341 this.treeId = treeId;
342 }
343
344 @Override
345 Note getNote(AnyObjectId objId, ObjectReader or) throws IOException {
346 return load(objId, or).getNote(objId, or);
347 }
348
349 @Override
350 Iterator<Note> iterator(AnyObjectId objId, ObjectReader reader)
351 throws IOException {
352 return load(objId, reader).iterator(objId, reader);
353 }
354
355 @Override
356 int estimateSize(AnyObjectId objId, ObjectReader or) throws IOException {
357 return load(objId, or).estimateSize(objId, or);
358 }
359
360 @Override
361 InMemoryNoteBucket set(AnyObjectId../../../org/eclipse/jgit/lib/AnyObjectId.html#AnyObjectId">AnyObjectId noteOn, AnyObjectId noteData,
362 ObjectReader or) throws IOException {
363 return load(noteOn, or).set(noteOn, noteData, or);
364 }
365
366 @Override
367 ObjectId writeTree(ObjectInserter inserter) {
368 return treeId;
369 }
370
371 @Override
372 ObjectId getTreeId() {
373 return treeId;
374 }
375
376 private InMemoryNoteBucket load(AnyObjectId prefix, ObjectReader or)
377 throws IOException {
378 AbbreviatedObjectId p = prefix.abbreviate(prefixLen + 2);
379 InMemoryNoteBucket self = NoteParser.parse(p, treeId, or);
380 table[cell(prefix)] = self;
381 return self;
382 }
383 }
384 }