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