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
45 package org.eclipse.jgit.internal.storage.file;
46
47 import java.io.IOException;
48 import java.util.Collection;
49 import java.util.Collections;
50 import java.util.HashSet;
51 import java.util.List;
52 import java.util.Set;
53 import java.util.zip.DataFormatException;
54 import java.util.zip.Inflater;
55
56 import org.eclipse.jgit.annotations.Nullable;
57 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
58 import org.eclipse.jgit.errors.MissingObjectException;
59 import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
60 import org.eclipse.jgit.internal.JGitText;
61 import org.eclipse.jgit.internal.storage.pack.CachedPack;
62 import org.eclipse.jgit.internal.storage.pack.ObjectReuseAsIs;
63 import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
64 import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
65 import org.eclipse.jgit.internal.storage.pack.PackWriter;
66 import org.eclipse.jgit.lib.AbbreviatedObjectId;
67 import org.eclipse.jgit.lib.AnyObjectId;
68 import org.eclipse.jgit.lib.BitmapIndex;
69 import org.eclipse.jgit.lib.BitmapIndex.BitmapBuilder;
70 import org.eclipse.jgit.lib.Constants;
71 import org.eclipse.jgit.lib.InflaterCache;
72 import org.eclipse.jgit.lib.ObjectId;
73 import org.eclipse.jgit.lib.ObjectInserter;
74 import org.eclipse.jgit.lib.ObjectLoader;
75 import org.eclipse.jgit.lib.ObjectReader;
76 import org.eclipse.jgit.lib.ProgressMonitor;
77
78
79 final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
80
81 final byte[] tempId = new byte[Constants.OBJECT_ID_LENGTH];
82
83 private Inflater inf;
84
85 private ByteWindow window;
86
87 private DeltaBaseCache baseCache;
88
89 @Nullable
90 private final ObjectInserter createdFromInserter;
91
92 final FileObjectDatabase db;
93
94 WindowCursor(FileObjectDatabase db) {
95 this.db = db;
96 this.createdFromInserter = null;
97 this.streamFileThreshold = WindowCache.getStreamFileThreshold();
98 }
99
100 WindowCursor(FileObjectDatabase db,
101 @Nullable ObjectDirectoryInserter createdFromInserter) {
102 this.db = db;
103 this.createdFromInserter = createdFromInserter;
104 this.streamFileThreshold = WindowCache.getStreamFileThreshold();
105 }
106
107 DeltaBaseCache getDeltaBaseCache() {
108 if (baseCache == null)
109 baseCache = new DeltaBaseCache();
110 return baseCache;
111 }
112
113
114 @Override
115 public ObjectReader newReader() {
116 return new WindowCursor(db);
117 }
118
119
120 @Override
121 public BitmapIndex getBitmapIndex() throws IOException {
122 for (PackFile pack : db.getPacks()) {
123 PackBitmapIndex index = pack.getBitmapIndex();
124 if (index != null)
125 return new BitmapIndexImpl(index);
126 }
127 return null;
128 }
129
130
131 @Override
132 public Collection<CachedPack> getCachedPacksAndUpdate(
133 BitmapBuilder needBitmap) throws IOException {
134 for (PackFile pack : db.getPacks()) {
135 PackBitmapIndex index = pack.getBitmapIndex();
136 if (needBitmap.removeAllOrNone(index))
137 return Collections.<CachedPack> singletonList(
138 new LocalCachedPack(Collections.singletonList(pack)));
139 }
140 return Collections.emptyList();
141 }
142
143
144 @Override
145 public Collection<ObjectId> resolve(AbbreviatedObjectId id)
146 throws IOException {
147 if (id.isComplete())
148 return Collections.singleton(id.toObjectId());
149 HashSet<ObjectId> matches = new HashSet<>(4);
150 db.resolve(matches, id);
151 return matches;
152 }
153
154
155 @Override
156 public boolean has(AnyObjectId objectId) throws IOException {
157 return db.has(objectId);
158 }
159
160
161 @Override
162 public ObjectLoader open(AnyObjectId objectId, int typeHint)
163 throws MissingObjectException, IncorrectObjectTypeException,
164 IOException {
165 final ObjectLoader ldr = db.openObject(this, objectId);
166 if (ldr == null) {
167 if (typeHint == OBJ_ANY)
168 throw new MissingObjectException(objectId.copy(),
169 JGitText.get().unknownObjectType2);
170 throw new MissingObjectException(objectId.copy(), typeHint);
171 }
172 if (typeHint != OBJ_ANY && ldr.getType() != typeHint)
173 throw new IncorrectObjectTypeException(objectId.copy(), typeHint);
174 return ldr;
175 }
176
177
178 @Override
179 public Set<ObjectId> getShallowCommits() throws IOException {
180 return db.getShallowCommits();
181 }
182
183
184 @Override
185 public long getObjectSize(AnyObjectId objectId, int typeHint)
186 throws MissingObjectException, IncorrectObjectTypeException,
187 IOException {
188 long sz = db.getObjectSize(this, objectId);
189 if (sz < 0) {
190 if (typeHint == OBJ_ANY)
191 throw new MissingObjectException(objectId.copy(),
192 JGitText.get().unknownObjectType2);
193 throw new MissingObjectException(objectId.copy(), typeHint);
194 }
195 return sz;
196 }
197
198
199 @Override
200 public LocalObjectToPack newObjectToPack(AnyObjectId objectId, int type) {
201 return new LocalObjectToPack(objectId, type);
202 }
203
204
205 @Override
206 public void selectObjectRepresentation(PackWriter packer,
207 ProgressMonitor monitor, Iterable<ObjectToPack> objects)
208 throws IOException, MissingObjectException {
209 for (ObjectToPack otp : objects) {
210 db.selectObjectRepresentation(packer, otp, this);
211 monitor.update(1);
212 }
213 }
214
215
216 @Override
217 public void copyObjectAsIs(PackOutputStream out, ObjectToPack otp,
218 boolean validate) throws IOException,
219 StoredObjectRepresentationNotAvailableException {
220 LocalObjectToPack src = (LocalObjectToPack) otp;
221 src.pack.copyAsIs(out, src, validate, this);
222 }
223
224
225 @Override
226 public void writeObjects(PackOutputStream out, List<ObjectToPack> list)
227 throws IOException {
228 for (ObjectToPack otp : list)
229 out.writeObject(otp);
230 }
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254 int copy(final PackFile pack, long position, final byte[] dstbuf,
255 int dstoff, final int cnt) throws IOException {
256 final long length = pack.length;
257 int need = cnt;
258 while (need > 0 && position < length) {
259 pin(pack, position);
260 final int r = window.copy(position, dstbuf, dstoff, need);
261 position += r;
262 dstoff += r;
263 need -= r;
264 }
265 return cnt - need;
266 }
267
268
269 @Override
270 public void copyPackAsIs(PackOutputStream out, CachedPack pack)
271 throws IOException {
272 ((LocalCachedPack) pack).copyAsIs(out, this);
273 }
274
275 void copyPackAsIs(final PackFile pack, final long length,
276 final PackOutputStream out) throws IOException {
277 long position = 12;
278 long remaining = length - (12 + 20);
279 while (0 < remaining) {
280 pin(pack, position);
281
282 int ptr = (int) (position - window.start);
283 int n = (int) Math.min(window.size() - ptr, remaining);
284 window.write(out, position, n);
285 position += n;
286 remaining -= n;
287 }
288 }
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311 int inflate(final PackFile pack, long position, final byte[] dstbuf,
312 boolean headerOnly) throws IOException, DataFormatException {
313 prepareInflater();
314 pin(pack, position);
315 position += window.setInput(position, inf);
316 for (int dstoff = 0;;) {
317 int n = inf.inflate(dstbuf, dstoff, dstbuf.length - dstoff);
318 dstoff += n;
319 if (inf.finished() || (headerOnly && dstoff == dstbuf.length))
320 return dstoff;
321 if (inf.needsInput()) {
322 pin(pack, position);
323 position += window.setInput(position, inf);
324 } else if (n == 0)
325 throw new DataFormatException();
326 }
327 }
328
329 ByteArrayWindow quickCopy(PackFile p, long pos, long cnt)
330 throws IOException {
331 pin(p, pos);
332 if (window instanceof ByteArrayWindow
333 && window.contains(p, pos + (cnt - 1)))
334 return (ByteArrayWindow) window;
335 return null;
336 }
337
338 Inflater inflater() {
339 prepareInflater();
340 return inf;
341 }
342
343 private void prepareInflater() {
344 if (inf == null)
345 inf = InflaterCache.get();
346 else
347 inf.reset();
348 }
349
350 void pin(PackFile pack, long position)
351 throws IOException {
352 final ByteWindow w = window;
353 if (w == null || !w.contains(pack, position)) {
354
355
356
357
358
359 window = null;
360 window = WindowCache.get(pack, position);
361 }
362 }
363
364
365 @Override
366 @Nullable
367 public ObjectInserter getCreatedFromInserter() {
368 return createdFromInserter;
369 }
370
371
372
373
374
375
376 @Override
377 public void close() {
378 window = null;
379 baseCache = null;
380 try {
381 InflaterCache.release(inf);
382 } finally {
383 inf = null;
384 }
385 }
386 }