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