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 @Override
126 public boolean hasNext() {
127 return idx < cnt;
128 }
129
130 @Override
131 public Note next() {
132 if (hasNext())
133 return notes[idx++];
134 else
135 throw new NoSuchElementException();
136 }
137
138 @Override
139 public void remove() {
140 throw new UnsupportedOperationException();
141 }
142 };
143 }
144
145 @Override
146 int estimateSize(AnyObjectId noteOn, ObjectReader or) throws IOException {
147 return cnt;
148 }
149
150 @Override
151 InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
152 ObjectReader or) throws IOException {
153 int p = search(noteOn);
154 if (0 <= p) {
155 if (noteData != null) {
156 notes[p].setData(noteData.copy());
157 return this;
158
159 } else {
160 System.arraycopy(notes, p + 1, notes, p, cnt - p - 1);
161 cnt--;
162 return 0 < cnt ? this : null;
163 }
164
165 } else if (noteData != null) {
166 if (shouldSplit()) {
167 return split().set(noteOn, noteData, or);
168
169 } else {
170 growIfFull();
171 p = -(p + 1);
172 if (p < cnt)
173 System.arraycopy(notes, p, notes, p + 1, cnt - p);
174 notes[p] = new Note(noteOn, noteData.copy());
175 cnt++;
176 return this;
177 }
178
179 } else {
180 return this;
181 }
182 }
183
184 @Override
185 ObjectId writeTree(ObjectInserter inserter) throws IOException {
186 return inserter.insert(build());
187 }
188
189 @Override
190 ObjectId getTreeId() {
191 try (Formatter f = new ObjectInserter.Formatter()) {
192 return f.idFor(build());
193 }
194 }
195
196 private TreeFormatter build() {
197 byte[] nameBuf = new byte[OBJECT_ID_STRING_LENGTH];
198 int nameLen = OBJECT_ID_STRING_LENGTH - prefixLen;
199 TreeFormatter fmt = new TreeFormatter(treeSize(nameLen));
200 NonNoteEntry e = nonNotes;
201
202 for (int i = 0; i < cnt; i++) {
203 Note n = notes[i];
204
205 n.copyTo(nameBuf, 0);
206
207 while (e != null
208 && e.pathCompare(nameBuf, prefixLen, nameLen, REGULAR_FILE) < 0) {
209 e.format(fmt);
210 e = e.next;
211 }
212
213 fmt.append(nameBuf, prefixLen, nameLen, REGULAR_FILE, n.getData());
214 }
215
216 for (; e != null; e = e.next)
217 e.format(fmt);
218 return fmt;
219 }
220
221 private int treeSize(int nameLen) {
222 int sz = cnt * TreeFormatter.entrySize(REGULAR_FILE, nameLen);
223 for (NonNoteEntry e = nonNotes; e != null; e = e.next)
224 sz += e.treeEntrySize();
225 return sz;
226 }
227
228 void parseOneEntry(AnyObjectId noteOn, AnyObjectId noteData) {
229 growIfFull();
230 notes[cnt++] = new Note(noteOn, noteData.copy());
231 }
232
233 @Override
234 InMemoryNoteBucket append(Note note) {
235 if (shouldSplit()) {
236 return split().append(note);
237
238 } else {
239 growIfFull();
240 notes[cnt++] = note;
241 return this;
242 }
243 }
244
245 private void growIfFull() {
246 if (notes.length == cnt) {
247 Note[] n = new Note[notes.length * 2];
248 System.arraycopy(notes, 0, n, 0, cnt);
249 notes = n;
250 }
251 }
252
253 private boolean shouldSplit() {
254 return MAX_SIZE <= cnt && prefixLen + 2 < OBJECT_ID_STRING_LENGTH;
255 }
256
257 FanoutBucket split() {
258 FanoutBucket n = new FanoutBucket(prefixLen);
259 for (int i = 0; i < cnt; i++)
260 n.append(notes[i]);
261 n.nonNotes = nonNotes;
262 return n;
263 }
264 }