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