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 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 }