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