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