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 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 public boolean hasNext() {
144 if (itr != null && itr.hasNext())
145 return true;
146
147 for (; cell < table.length; cell++) {
148 NoteBucket b = table[cell];
149 if (b == null)
150 continue;
151
152 try {
153 id.setByte(prefixLen >> 1, cell);
154 itr = b.iterator(id, reader);
155 } catch (IOException err) {
156 throw new RuntimeException(err);
157 }
158
159 if (itr.hasNext()) {
160 cell++;
161 return true;
162 }
163 }
164 return false;
165 }
166
167 public Note next() {
168 if (hasNext())
169 return itr.next();
170 else
171 throw new NoSuchElementException();
172 }
173
174 public void remove() {
175 throw new UnsupportedOperationException();
176 }
177 };
178 }
179
180 @Override
181 int estimateSize(AnyObjectId noteOn, ObjectReader or) throws IOException {
182
183 if (LeafBucket.MAX_SIZE * 3 / 4 <= cnt)
184 return 1 + LeafBucket.MAX_SIZE;
185
186
187
188
189
190 MutableObjectId id = new MutableObjectId();
191 id.fromObjectId(noteOn);
192
193 int sz = 0;
194 for (int cell = 0; cell < 256; cell++) {
195 NoteBucket b = table[cell];
196 if (b == null)
197 continue;
198
199 id.setByte(prefixLen >> 1, cell);
200 sz += b.estimateSize(id, or);
201 if (LeafBucket.MAX_SIZE < sz)
202 break;
203 }
204 return sz;
205 }
206
207 @Override
208 InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
209 ObjectReader or) throws IOException {
210 int cell = cell(noteOn);
211 NoteBucket b = table[cell];
212
213 if (b == null) {
214 if (noteData == null)
215 return this;
216
217 LeafBucket n = new LeafBucket(prefixLen + 2);
218 table[cell] = n.set(noteOn, noteData, or);
219 cnt++;
220 return this;
221
222 } else {
223 NoteBucket n = b.set(noteOn, noteData, or);
224 if (n == null) {
225 table[cell] = null;
226 cnt--;
227
228 if (cnt == 0)
229 return null;
230
231 return contractIfTooSmall(noteOn, or);
232
233 } else if (n != b) {
234 table[cell] = n;
235 }
236 return this;
237 }
238 }
239
240 InMemoryNoteBucket contractIfTooSmall(AnyObjectId noteOn, ObjectReader or)
241 throws IOException {
242 if (estimateSize(noteOn, or) < LeafBucket.MAX_SIZE) {
243
244 InMemoryNoteBucket r = new LeafBucket(prefixLen);
245 for (Iterator<Note> i = iterator(noteOn, or); i.hasNext();)
246 r = r.append(i.next());
247 r.nonNotes = nonNotes;
248 return r;
249 }
250
251 return this;
252 }
253
254 private static final byte[] hexchar = { '0', '1', '2', '3', '4', '5', '6',
255 '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
256
257 @Override
258 ObjectId writeTree(ObjectInserter inserter) throws IOException {
259 return inserter.insert(build(true, inserter));
260 }
261
262 ObjectId getTreeId() {
263 try (ObjectInserter.Formatter f = new ObjectInserter.Formatter()) {
264 return f.idFor(build(false, null));
265 } catch (IOException e) {
266
267 throw new RuntimeException(e);
268 }
269 }
270
271 private TreeFormatter build(boolean insert, ObjectInserter inserter)
272 throws IOException {
273 byte[] nameBuf = new byte[2];
274 TreeFormatter fmt = new TreeFormatter(treeSize());
275 NonNoteEntry e = nonNotes;
276
277 for (int cell = 0; cell < 256; cell++) {
278 NoteBucket b = table[cell];
279 if (b == null)
280 continue;
281
282 nameBuf[0] = hexchar[cell >>> 4];
283 nameBuf[1] = hexchar[cell & 0x0f];
284
285 while (e != null && e.pathCompare(nameBuf, 0, 2, TREE) < 0) {
286 e.format(fmt);
287 e = e.next;
288 }
289
290 ObjectId id;
291 if (insert) {
292 id = b.writeTree(inserter);
293 } else {
294 id = b.getTreeId();
295 }
296 fmt.append(nameBuf, 0, 2, TREE, id);
297 }
298
299 for (; e != null; e = e.next)
300 e.format(fmt);
301 return fmt;
302 }
303
304 private int treeSize() {
305 int sz = cnt * TreeFormatter.entrySize(TREE, 2);
306 for (NonNoteEntry e = nonNotes; e != null; e = e.next)
307 sz += e.treeEntrySize();
308 return sz;
309 }
310
311 @Override
312 InMemoryNoteBucket append(Note note) {
313 int cell = cell(note);
314 InMemoryNoteBucket b = (InMemoryNoteBucket) table[cell];
315
316 if (b == null) {
317 LeafBucket n = new LeafBucket(prefixLen + 2);
318 table[cell] = n.append(note);
319 cnt++;
320
321 } else {
322 InMemoryNoteBucket n = b.append(note);
323 if (n != b)
324 table[cell] = n;
325 }
326 return this;
327 }
328
329 private int cell(AnyObjectId id) {
330 return id.getByte(prefixLen >> 1);
331 }
332
333 private class LazyNoteBucket extends NoteBucket {
334 private final ObjectId treeId;
335
336 LazyNoteBucket(ObjectId treeId) {
337 this.treeId = treeId;
338 }
339
340 @Override
341 Note getNote(AnyObjectId objId, ObjectReader or) throws IOException {
342 return load(objId, or).getNote(objId, or);
343 }
344
345 @Override
346 Iterator<Note> iterator(AnyObjectId objId, ObjectReader reader)
347 throws IOException {
348 return load(objId, reader).iterator(objId, reader);
349 }
350
351 @Override
352 int estimateSize(AnyObjectId objId, ObjectReader or) throws IOException {
353 return load(objId, or).estimateSize(objId, or);
354 }
355
356 @Override
357 InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
358 ObjectReader or) throws IOException {
359 return load(noteOn, or).set(noteOn, noteData, or);
360 }
361
362 @Override
363 ObjectId writeTree(ObjectInserter inserter) {
364 return treeId;
365 }
366
367 @Override
368 ObjectId getTreeId() {
369 return treeId;
370 }
371
372 private InMemoryNoteBucket load(AnyObjectId prefix, ObjectReader or)
373 throws IOException {
374 AbbreviatedObjectId p = prefix.abbreviate(prefixLen + 2);
375 InMemoryNoteBucket self = NoteParser.parse(p, treeId, or);
376 table[cell(prefix)] = self;
377 return self;
378 }
379 }
380 }