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.Constants.OBJECT_ID_STRING_LENGTH;
47 import static org.eclipse.jgit.lib.FileMode.REGULAR_FILE;
48
49 import java.io.IOException;
50 import java.util.Iterator;
51 import java.util.NoSuchElementException;
52
53 import org.eclipse.jgit.lib.AnyObjectId;
54 import org.eclipse.jgit.lib.ObjectId;
55 import org.eclipse.jgit.lib.ObjectInserter;
56 import org.eclipse.jgit.lib.ObjectInserter.Formatter;
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 class LeafBucket extends InMemoryNoteBucket {
77 static final int MAX_SIZE = 256;
78
79
80 private Note[] notes;
81
82
83 private int cnt;
84
85 LeafBucket(int prefixLen) {
86 super(prefixLen);
87 notes = new Note[4];
88 }
89
90 private int search(AnyObjectId objId) {
91 int low = 0;
92 int high = cnt;
93 while (low < high) {
94 int mid = (low + high) >>> 1;
95 int cmp = objId.compareTo(notes[mid]);
96 if (cmp < 0)
97 high = mid;
98 else if (cmp == 0)
99 return mid;
100 else
101 low = mid + 1;
102 }
103 return -(low + 1);
104 }
105
106 @Override
107 Note getNote(AnyObjectId objId, ObjectReader or) {
108 int idx = search(objId);
109 return 0 <= idx ? notes[idx] : null;
110 }
111
112 Note get(int index) {
113 return notes[index];
114 }
115
116 int size() {
117 return cnt;
118 }
119
120 @Override
121 Iterator<Note> iterator(AnyObjectId objId, ObjectReader reader) {
122 return new Iterator<Note>() {
123 private int idx;
124
125 public boolean hasNext() {
126 return idx < cnt;
127 }
128
129 public Note next() {
130 if (hasNext())
131 return notes[idx++];
132 else
133 throw new NoSuchElementException();
134 }
135
136 public void remove() {
137 throw new UnsupportedOperationException();
138 }
139 };
140 }
141
142 @Override
143 int estimateSize(AnyObjectId noteOn, ObjectReader or) throws IOException {
144 return cnt;
145 }
146
147 InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
148 ObjectReader or) throws IOException {
149 int p = search(noteOn);
150 if (0 <= p) {
151 if (noteData != null) {
152 notes[p].setData(noteData.copy());
153 return this;
154
155 } else {
156 System.arraycopy(notes, p + 1, notes, p, cnt - p - 1);
157 cnt--;
158 return 0 < cnt ? this : null;
159 }
160
161 } else if (noteData != null) {
162 if (shouldSplit()) {
163 return split().set(noteOn, noteData, or);
164
165 } else {
166 growIfFull();
167 p = -(p + 1);
168 if (p < cnt)
169 System.arraycopy(notes, p, notes, p + 1, cnt - p);
170 notes[p] = new Note(noteOn, noteData.copy());
171 cnt++;
172 return this;
173 }
174
175 } else {
176 return this;
177 }
178 }
179
180 @Override
181 ObjectId writeTree(ObjectInserter inserter) throws IOException {
182 return inserter.insert(build());
183 }
184
185 @Override
186 ObjectId getTreeId() {
187 try (Formatter f = new ObjectInserter.Formatter()) {
188 return f.idFor(build());
189 }
190 }
191
192 private TreeFormatter build() {
193 byte[] nameBuf = new byte[OBJECT_ID_STRING_LENGTH];
194 int nameLen = OBJECT_ID_STRING_LENGTH - prefixLen;
195 TreeFormatter fmt = new TreeFormatter(treeSize(nameLen));
196 NonNoteEntry e = nonNotes;
197
198 for (int i = 0; i < cnt; i++) {
199 Note n = notes[i];
200
201 n.copyTo(nameBuf, 0);
202
203 while (e != null
204 && e.pathCompare(nameBuf, prefixLen, nameLen, REGULAR_FILE) < 0) {
205 e.format(fmt);
206 e = e.next;
207 }
208
209 fmt.append(nameBuf, prefixLen, nameLen, REGULAR_FILE, n.getData());
210 }
211
212 for (; e != null; e = e.next)
213 e.format(fmt);
214 return fmt;
215 }
216
217 private int treeSize(final int nameLen) {
218 int sz = cnt * TreeFormatter.entrySize(REGULAR_FILE, nameLen);
219 for (NonNoteEntry e = nonNotes; e != null; e = e.next)
220 sz += e.treeEntrySize();
221 return sz;
222 }
223
224 void parseOneEntry(AnyObjectId noteOn, AnyObjectId noteData) {
225 growIfFull();
226 notes[cnt++] = new Note(noteOn, noteData.copy());
227 }
228
229 @Override
230 InMemoryNoteBucket append(Note note) {
231 if (shouldSplit()) {
232 return split().append(note);
233
234 } else {
235 growIfFull();
236 notes[cnt++] = note;
237 return this;
238 }
239 }
240
241 private void growIfFull() {
242 if (notes.length == cnt) {
243 Note[] n = new Note[notes.length * 2];
244 System.arraycopy(notes, 0, n, 0, cnt);
245 notes = n;
246 }
247 }
248
249 private boolean shouldSplit() {
250 return MAX_SIZE <= cnt && prefixLen + 2 < OBJECT_ID_STRING_LENGTH;
251 }
252
253 FanoutBucket split() {
254 FanoutBucket n = new FanoutBucket(prefixLen);
255 for (int i = 0; i < cnt; i++)
256 n.append(notes[i]);
257 n.nonNotes = nonNotes;
258 return n;
259 }
260 }