1 /*
2 * Copyright (C) 2010, Google Inc. and others
3 *
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Distribution License v. 1.0 which is available at
6 * https://www.eclipse.org/org/documents/edl-v10.php.
7 *
8 * SPDX-License-Identifier: BSD-3-Clause
9 */
10
11 package org.eclipse.jgit.lib;
12
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.Set;
19
20 import org.eclipse.jgit.annotations.NonNull;
21 import org.eclipse.jgit.annotations.Nullable;
22 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
23 import org.eclipse.jgit.errors.MissingObjectException;
24 import org.eclipse.jgit.internal.revwalk.BitmappedObjectReachabilityChecker;
25 import org.eclipse.jgit.internal.revwalk.BitmappedReachabilityChecker;
26 import org.eclipse.jgit.internal.revwalk.PedestrianObjectReachabilityChecker;
27 import org.eclipse.jgit.internal.revwalk.PedestrianReachabilityChecker;
28 import org.eclipse.jgit.revwalk.ObjectReachabilityChecker;
29 import org.eclipse.jgit.revwalk.ObjectWalk;
30 import org.eclipse.jgit.revwalk.ReachabilityChecker;
31 import org.eclipse.jgit.revwalk.RevWalk;
32
33 /**
34 * Reads an {@link org.eclipse.jgit.lib.ObjectDatabase} for a single thread.
35 * <p>
36 * Readers that can support efficient reuse of pack encoded objects should also
37 * implement the companion interface
38 * {@link org.eclipse.jgit.internal.storage.pack.ObjectReuseAsIs}.
39 */
40 public abstract class ObjectReader implements AutoCloseable {
41 /** Type hint indicating the caller doesn't know the type. */
42 public static final int OBJ_ANY = -1;
43
44 /**
45 * The threshold at which a file will be streamed rather than loaded
46 * entirely into memory.
47 * @since 4.6
48 */
49 protected int streamFileThreshold;
50
51 /**
52 * Construct a new reader from the same data.
53 * <p>
54 * Applications can use this method to build a new reader from the same data
55 * source, but for an different thread.
56 *
57 * @return a brand new reader, using the same data source.
58 */
59 public abstract ObjectReader newReader();
60
61 /**
62 * Obtain a unique abbreviation (prefix) of an object SHA-1.
63 *
64 * This method uses a reasonable default for the minimum length. Callers who
65 * don't care about the minimum length should prefer this method.
66 *
67 * The returned abbreviation would expand back to the argument ObjectId when
68 * passed to {@link #resolve(AbbreviatedObjectId)}, assuming no new objects
69 * are added to this repository between calls.
70 *
71 * @param objectId
72 * object identity that needs to be abbreviated.
73 * @return SHA-1 abbreviation.
74 * @throws java.io.IOException
75 * the object store cannot be read.
76 */
77 public AbbreviatedObjectId abbreviate(AnyObjectId objectId)
78 throws IOException {
79 return abbreviate(objectId, 7);
80 }
81
82 /**
83 * Obtain a unique abbreviation (prefix) of an object SHA-1.
84 *
85 * The returned abbreviation would expand back to the argument ObjectId when
86 * passed to {@link #resolve(AbbreviatedObjectId)}, assuming no new objects
87 * are added to this repository between calls.
88 *
89 * The default implementation of this method abbreviates the id to the
90 * minimum length, then resolves it to see if there are multiple results.
91 * When multiple results are found, the length is extended by 1 and resolve
92 * is tried again.
93 *
94 * @param objectId
95 * object identity that needs to be abbreviated.
96 * @param len
97 * minimum length of the abbreviated string. Must be in the range
98 * [2, {@value Constants#OBJECT_ID_STRING_LENGTH}].
99 * @return SHA-1 abbreviation. If no matching objects exist in the
100 * repository, the abbreviation will match the minimum length.
101 * @throws java.io.IOException
102 * the object store cannot be read.
103 */
104 public AbbreviatedObjectId abbreviate(AnyObjectId objectId, int len)
105 throws IOException {
106 if (len == Constants.OBJECT_ID_STRING_LENGTH)
107 return AbbreviatedObjectId.fromObjectId(objectId);
108
109 AbbreviatedObjectId abbrev = objectId.abbreviate(len);
110 Collection<ObjectId> matches = resolve(abbrev);
111 while (1 < matches.size() && len < Constants.OBJECT_ID_STRING_LENGTH) {
112 abbrev = objectId.abbreviate(++len);
113 List<ObjectId> n = new ArrayList<>(8);
114 for (ObjectId candidate : matches) {
115 if (abbrev.prefixCompare(candidate) == 0)
116 n.add(candidate);
117 }
118 if (1 < n.size())
119 matches = n;
120 else
121 matches = resolve(abbrev);
122 }
123 return abbrev;
124 }
125
126 /**
127 * Resolve an abbreviated ObjectId to its full form.
128 *
129 * This method searches for an ObjectId that begins with the abbreviation,
130 * and returns at least some matching candidates.
131 *
132 * If the returned collection is empty, no objects start with this
133 * abbreviation. The abbreviation doesn't belong to this repository, or the
134 * repository lacks the necessary objects to complete it.
135 *
136 * If the collection contains exactly one member, the abbreviation is
137 * (currently) unique within this database. There is a reasonably high
138 * probability that the returned id is what was previously abbreviated.
139 *
140 * If the collection contains 2 or more members, the abbreviation is not
141 * unique. In this case the implementation is only required to return at
142 * least 2 candidates to signal the abbreviation has conflicts. User
143 * friendly implementations should return as many candidates as reasonably
144 * possible, as the caller may be able to disambiguate further based on
145 * context. However since databases can be very large (e.g. 10 million
146 * objects) returning 625,000 candidates for the abbreviation "0" is simply
147 * unreasonable, so implementors should draw the line at around 256 matches.
148 *
149 * @param id
150 * abbreviated id to resolve to a complete identity. The
151 * abbreviation must have a length of at least 2.
152 * @return candidates that begin with the abbreviated identity.
153 * @throws java.io.IOException
154 * the object store cannot be read.
155 */
156 public abstract Collection<ObjectId> resolve(AbbreviatedObjectId id)
157 throws IOException;
158
159 /**
160 * Does the requested object exist in this database?
161 *
162 * @param objectId
163 * identity of the object to test for existence of.
164 * @return true if the specified object is stored in this database.
165 * @throws java.io.IOException
166 * the object store cannot be accessed.
167 */
168 public boolean has(AnyObjectId objectId) throws IOException {
169 return has(objectId, OBJ_ANY);
170 }
171
172 /**
173 * Does the requested object exist in this database?
174 *
175 * @param objectId
176 * identity of the object to test for existence of.
177 * @param typeHint
178 * hint about the type of object being requested, e.g.
179 * {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB};
180 * {@link #OBJ_ANY} if the object type is not known, or does not
181 * matter to the caller.
182 * @return true if the specified object is stored in this database.
183 * @throws IncorrectObjectTypeException
184 * typeHint was not OBJ_ANY, and the object's actual type does
185 * not match typeHint.
186 * @throws java.io.IOException
187 * the object store cannot be accessed.
188 */
189 public boolean has(AnyObjectId objectId, int typeHint) throws IOException {
190 try {
191 open(objectId, typeHint);
192 return true;
193 } catch (MissingObjectException notFound) {
194 return false;
195 }
196 }
197
198 /**
199 * Open an object from this database.
200 *
201 * @param objectId
202 * identity of the object to open.
203 * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the
204 * object.
205 * @throws org.eclipse.jgit.errors.MissingObjectException
206 * the object does not exist.
207 * @throws java.io.IOException
208 * the object store cannot be accessed.
209 */
210 public ObjectLoader open(AnyObjectId objectId)
211 throws MissingObjectException, IOException {
212 return open(objectId, OBJ_ANY);
213 }
214
215 /**
216 * Open an object from this database.
217 *
218 * @param objectId
219 * identity of the object to open.
220 * @param typeHint
221 * hint about the type of object being requested, e.g.
222 * {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB};
223 * {@link #OBJ_ANY} if the object type is not known, or does not
224 * matter to the caller.
225 * @return a {@link org.eclipse.jgit.lib.ObjectLoader} for accessing the
226 * object.
227 * @throws org.eclipse.jgit.errors.MissingObjectException
228 * the object does not exist.
229 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
230 * typeHint was not OBJ_ANY, and the object's actual type does
231 * not match typeHint.
232 * @throws java.io.IOException
233 * the object store cannot be accessed.
234 */
235 public abstract ObjectLoader open(AnyObjectId objectId, int typeHint)
236 throws MissingObjectException, IncorrectObjectTypeException,
237 IOException;
238
239 /**
240 * Returns IDs for those commits which should be considered as shallow.
241 *
242 * @return IDs of shallow commits
243 * @throws java.io.IOException
244 */
245 public abstract Set<ObjectId> getShallowCommits() throws IOException;
246
247 /**
248 * Asynchronous object opening.
249 *
250 * @param objectIds
251 * objects to open from the object store. The supplied collection
252 * must not be modified until the queue has finished.
253 * @param reportMissing
254 * if true missing objects are reported by calling failure with a
255 * MissingObjectException. This may be more expensive for the
256 * implementation to guarantee. If false the implementation may
257 * choose to report MissingObjectException, or silently skip over
258 * the object with no warning.
259 * @return queue to read the objects from.
260 */
261 public <T extends ObjectId> AsyncObjectLoaderQueue<T> open(
262 Iterable<T> objectIds, final boolean reportMissing) {
263 final Iterator<T> idItr = objectIds.iterator();
264 return new AsyncObjectLoaderQueue<T>() {
265 private T cur;
266
267 @Override
268 public boolean next() throws MissingObjectException, IOException {
269 if (idItr.hasNext()) {
270 cur = idItr.next();
271 return true;
272 }
273 return false;
274 }
275
276 @Override
277 public T getCurrent() {
278 return cur;
279 }
280
281 @Override
282 public ObjectId getObjectId() {
283 return cur;
284 }
285
286 @Override
287 public ObjectLoader open() throws IOException {
288 return ObjectReader.this.open(cur, OBJ_ANY);
289 }
290
291 @Override
292 public boolean cancel(boolean mayInterruptIfRunning) {
293 return true;
294 }
295
296 @Override
297 public void release() {
298 // Since we are sequential by default, we don't
299 // have any state to clean up if we terminate early.
300 }
301 };
302 }
303
304 /**
305 * Get only the size of an object.
306 * <p>
307 * The default implementation of this method opens an ObjectLoader.
308 * Databases are encouraged to override this if a faster access method is
309 * available to them.
310 *
311 * @param objectId
312 * identity of the object to open.
313 * @param typeHint
314 * hint about the type of object being requested, e.g.
315 * {@link org.eclipse.jgit.lib.Constants#OBJ_BLOB};
316 * {@link #OBJ_ANY} if the object type is not known, or does not
317 * matter to the caller.
318 * @return size of object in bytes.
319 * @throws org.eclipse.jgit.errors.MissingObjectException
320 * the object does not exist.
321 * @throws org.eclipse.jgit.errors.IncorrectObjectTypeException
322 * typeHint was not OBJ_ANY, and the object's actual type does
323 * not match typeHint.
324 * @throws java.io.IOException
325 * the object store cannot be accessed.
326 */
327 public long getObjectSize(AnyObjectId objectId, int typeHint)
328 throws MissingObjectException, IncorrectObjectTypeException,
329 IOException {
330 return open(objectId, typeHint).getSize();
331 }
332
333 /**
334 * Asynchronous object size lookup.
335 *
336 * @param objectIds
337 * objects to get the size of from the object store. The supplied
338 * collection must not be modified until the queue has finished.
339 * @param reportMissing
340 * if true missing objects are reported by calling failure with a
341 * MissingObjectException. This may be more expensive for the
342 * implementation to guarantee. If false the implementation may
343 * choose to report MissingObjectException, or silently skip over
344 * the object with no warning.
345 * @return queue to read object sizes from.
346 */
347 public <T extends ObjectId> AsyncObjectSizeQueue<T> getObjectSize(
348 Iterable<T> objectIds, final boolean reportMissing) {
349 final Iterator<T> idItr = objectIds.iterator();
350 return new AsyncObjectSizeQueue<T>() {
351 private T cur;
352
353 private long sz;
354
355 @Override
356 public boolean next() throws MissingObjectException, IOException {
357 if (idItr.hasNext()) {
358 cur = idItr.next();
359 sz = getObjectSize(cur, OBJ_ANY);
360 return true;
361 }
362 return false;
363 }
364
365 @Override
366 public T getCurrent() {
367 return cur;
368 }
369
370 @Override
371 public ObjectId getObjectId() {
372 return cur;
373 }
374
375 @Override
376 public long getSize() {
377 return sz;
378 }
379
380 @Override
381 public boolean cancel(boolean mayInterruptIfRunning) {
382 return true;
383 }
384
385 @Override
386 public void release() {
387 // Since we are sequential by default, we don't
388 // have any state to clean up if we terminate early.
389 }
390 };
391 }
392
393 /**
394 * Advise the reader to avoid unreachable objects.
395 * <p>
396 * While enabled the reader will skip over anything previously proven to be
397 * unreachable. This may be dangerous in the face of concurrent writes.
398 *
399 * @param avoid
400 * true to avoid unreachable objects.
401 * @since 3.0
402 */
403 public void setAvoidUnreachableObjects(boolean avoid) {
404 // Do nothing by default.
405 }
406
407 /**
408 * An index that can be used to speed up ObjectWalks.
409 *
410 * @return the index or null if one does not exist.
411 * @throws java.io.IOException
412 * when the index fails to load
413 * @since 3.0
414 */
415 public BitmapIndex getBitmapIndex() throws IOException {
416 return null;
417 }
418
419 /**
420 * Create a reachability checker that will use bitmaps if possible.
421 *
422 * @param rw
423 * revwalk for use by the reachability checker
424 * @return the most efficient reachability checker for this repository.
425 * @throws IOException
426 * if it cannot open any of the underlying indices.
427 *
428 * @since 5.11
429 */
430 @NonNull
431 public ReachabilityChecker createReachabilityChecker(RevWalk rw)
432 throws IOException {
433 if (getBitmapIndex() != null) {
434 return new BitmappedReachabilityChecker(rw);
435 }
436
437 return new PedestrianReachabilityChecker(true, rw);
438 }
439
440 /**
441 * Create an object reachability checker that will use bitmaps if possible.
442 *
443 * This reachability checker accepts any object as target. For checks
444 * exclusively between commits, use
445 * {@link #createReachabilityChecker(RevWalk)}.
446 *
447 * @param ow
448 * objectwalk for use by the reachability checker
449 * @return the most efficient object reachability checker for this
450 * repository.
451 *
452 * @throws IOException
453 * if it cannot open any of the underlying indices.
454 *
455 * @since 5.11
456 */
457 @NonNull
458 public ObjectReachabilityChecker createObjectReachabilityChecker(
459 ObjectWalk ow) throws IOException {
460 if (getBitmapIndex() != null) {
461 return new BitmappedObjectReachabilityChecker(ow);
462 }
463
464 return new PedestrianObjectReachabilityChecker(ow);
465 }
466
467 /**
468 * Get the {@link org.eclipse.jgit.lib.ObjectInserter} from which this
469 * reader was created using {@code inserter.newReader()}
470 *
471 * @return the {@link org.eclipse.jgit.lib.ObjectInserter} from which this
472 * reader was created using {@code inserter.newReader()}, or null if
473 * this reader was not created from an inserter.
474 * @since 4.4
475 */
476 @Nullable
477 public ObjectInserter getCreatedFromInserter() {
478 return null;
479 }
480
481 /**
482 * {@inheritDoc}
483 * <p>
484 * Release any resources used by this reader.
485 * <p>
486 * A reader that has been released can be used again, but may need to be
487 * released after the subsequent usage.
488 *
489 * @since 4.0
490 */
491 @Override
492 public abstract void close();
493
494 /**
495 * Sets the threshold at which a file will be streamed rather than loaded
496 * entirely into memory
497 *
498 * @param threshold
499 * the new threshold
500 * @since 4.6
501 */
502 public void setStreamFileThreshold(int threshold) {
503 streamFileThreshold = threshold;
504 }
505
506 /**
507 * Returns the threshold at which a file will be streamed rather than loaded
508 * entirely into memory
509 *
510 * @return the threshold in bytes
511 * @since 4.6
512 */
513 public int getStreamFileThreshold() {
514 return streamFileThreshold;
515 }
516
517 /**
518 * Wraps a delegate ObjectReader.
519 *
520 * @since 4.4
521 */
522 public abstract static class Filter extends ObjectReader {
523 /**
524 * @return delegate ObjectReader to handle all processing.
525 * @since 4.4
526 */
527 protected abstract ObjectReader delegate();
528
529 @Override
530 public ObjectReader newReader() {
531 return delegate().newReader();
532 }
533
534 @Override
535 public AbbreviatedObjectId abbreviate(AnyObjectId objectId)
536 throws IOException {
537 return delegate().abbreviate(objectId);
538 }
539
540 @Override
541 public AbbreviatedObjectId abbreviate(AnyObjectId objectId, int len)
542 throws IOException {
543 return delegate().abbreviate(objectId, len);
544 }
545
546 @Override
547 public Collection<ObjectId> resolve(AbbreviatedObjectId id)
548 throws IOException {
549 return delegate().resolve(id);
550 }
551
552 @Override
553 public boolean has(AnyObjectId objectId) throws IOException {
554 return delegate().has(objectId);
555 }
556
557 @Override
558 public boolean has(AnyObjectId objectId, int typeHint) throws IOException {
559 return delegate().has(objectId, typeHint);
560 }
561
562 @Override
563 public ObjectLoader open(AnyObjectId objectId)
564 throws MissingObjectException, IOException {
565 return delegate().open(objectId);
566 }
567
568 @Override
569 public ObjectLoader open(AnyObjectId objectId, int typeHint)
570 throws MissingObjectException, IncorrectObjectTypeException,
571 IOException {
572 return delegate().open(objectId, typeHint);
573 }
574
575 @Override
576 public Set<ObjectId> getShallowCommits() throws IOException {
577 return delegate().getShallowCommits();
578 }
579
580 @Override
581 public <T extends ObjectId> AsyncObjectLoaderQueue<T> open(
582 Iterable<T> objectIds, boolean reportMissing) {
583 return delegate().open(objectIds, reportMissing);
584 }
585
586 @Override
587 public long getObjectSize(AnyObjectId objectId, int typeHint)
588 throws MissingObjectException, IncorrectObjectTypeException,
589 IOException {
590 return delegate().getObjectSize(objectId, typeHint);
591 }
592
593 @Override
594 public <T extends ObjectId> AsyncObjectSizeQueue<T> getObjectSize(
595 Iterable<T> objectIds, boolean reportMissing) {
596 return delegate().getObjectSize(objectIds, reportMissing);
597 }
598
599 @Override
600 public void setAvoidUnreachableObjects(boolean avoid) {
601 delegate().setAvoidUnreachableObjects(avoid);
602 }
603
604 @Override
605 public BitmapIndex getBitmapIndex() throws IOException {
606 return delegate().getBitmapIndex();
607 }
608
609 @Override
610 @Nullable
611 public ObjectInserter getCreatedFromInserter() {
612 return delegate().getCreatedFromInserter();
613 }
614
615 @Override
616 public void close() {
617 delegate().close();
618 }
619 }
620 }