123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689 |
- /*
- * Copyright (C) 2008-2011, Google Inc.
- * Copyright (C) 2007-2008, Robin Rosenberg <robin.rosenberg@dewire.com>
- * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
- * and other copyright owners as documented in the project's IP log.
- *
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Distribution License v1.0 which
- * accompanies this distribution, is reproduced below, and is
- * available at http://www.eclipse.org/org/documents/edl-v10.php
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided
- * with the distribution.
- *
- * - Neither the name of the Eclipse Foundation, Inc. nor the
- * names of its contributors may be used to endorse or promote
- * products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
- package org.eclipse.jgit.transport;
-
- import java.io.EOFException;
- import java.io.IOException;
- import java.io.InputStream;
- import java.security.MessageDigest;
- import java.text.MessageFormat;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Comparator;
- import java.util.List;
- import java.util.concurrent.TimeUnit;
- import java.util.zip.DataFormatException;
- import java.util.zip.Inflater;
-
- import org.eclipse.jgit.errors.CorruptObjectException;
- import org.eclipse.jgit.errors.MissingObjectException;
- import org.eclipse.jgit.errors.TooLargeObjectInPackException;
- import org.eclipse.jgit.internal.JGitText;
- import org.eclipse.jgit.lib.AnyObjectId;
- import org.eclipse.jgit.lib.BatchingProgressMonitor;
- import org.eclipse.jgit.lib.Constants;
- import org.eclipse.jgit.lib.InflaterCache;
- import org.eclipse.jgit.lib.MutableObjectId;
- import org.eclipse.jgit.lib.NullProgressMonitor;
- import org.eclipse.jgit.lib.ObjectChecker;
- import org.eclipse.jgit.lib.ObjectDatabase;
- import org.eclipse.jgit.lib.ObjectId;
- import org.eclipse.jgit.lib.ObjectIdOwnerMap;
- import org.eclipse.jgit.lib.ObjectIdSubclassMap;
- import org.eclipse.jgit.lib.ObjectInserter;
- import org.eclipse.jgit.lib.ObjectLoader;
- import org.eclipse.jgit.lib.ObjectReader;
- import org.eclipse.jgit.lib.ObjectStream;
- import org.eclipse.jgit.lib.ProgressMonitor;
- import org.eclipse.jgit.storage.file.PackLock;
- import org.eclipse.jgit.storage.pack.BinaryDelta;
- import org.eclipse.jgit.util.BlockList;
- import org.eclipse.jgit.util.IO;
- import org.eclipse.jgit.util.NB;
-
- /**
- * Parses a pack stream and imports it for an {@link ObjectInserter}.
- * <p>
- * Applications can acquire an instance of a parser from ObjectInserter's
- * {@link ObjectInserter#newPackParser(InputStream)} method.
- * <p>
- * Implementations of {@link ObjectInserter} should subclass this type and
- * provide their own logic for the various {@code on*()} event methods declared
- * to be abstract.
- */
- public abstract class PackParser {
- /** Size of the internal stream buffer. */
- private static final int BUFFER_SIZE = 8192;
-
- /** Location data is being obtained from. */
- public static enum Source {
- /** Data is read from the incoming stream. */
- INPUT,
-
- /** Data is read back from the database's buffers. */
- DATABASE;
- }
-
- /** Object database used for loading existing objects. */
- private final ObjectDatabase objectDatabase;
-
- private InflaterStream inflater;
-
- private byte[] tempBuffer;
-
- private byte[] hdrBuf;
-
- private final MessageDigest objectDigest;
-
- private final MutableObjectId tempObjectId;
-
- private InputStream in;
-
- private byte[] buf;
-
- /** Position in the input stream of {@code buf[0]}. */
- private long bBase;
-
- private int bOffset;
-
- private int bAvail;
-
- private ObjectChecker objCheck;
-
- private boolean allowThin;
-
- private boolean checkObjectCollisions;
-
- private boolean needBaseObjectIds;
-
- private boolean checkEofAfterPackFooter;
-
- private long objectCount;
-
- private PackedObjectInfo[] entries;
-
- /**
- * Every object contained within the incoming pack.
- * <p>
- * This is a subset of {@link #entries}, as thin packs can add additional
- * objects to {@code entries} by copying already existing objects from the
- * repository onto the end of the thin pack to make it self-contained.
- */
- private ObjectIdSubclassMap<ObjectId> newObjectIds;
-
- private int deltaCount;
-
- private int entryCount;
-
- private ObjectIdOwnerMap<DeltaChain> baseById;
-
- /**
- * Objects referenced by their name from deltas, that aren't in this pack.
- * <p>
- * This is the set of objects that were copied onto the end of this pack to
- * make it complete. These objects were not transmitted by the remote peer,
- * but instead were assumed to already exist in the local repository.
- */
- private ObjectIdSubclassMap<ObjectId> baseObjectIds;
-
- private LongMap<UnresolvedDelta> baseByPos;
-
- /** Blobs whose contents need to be double-checked after indexing. */
- private BlockList<PackedObjectInfo> deferredCheckBlobs;
-
- private MessageDigest packDigest;
-
- private ObjectReader readCurs;
-
- /** Message to protect the pack data from garbage collection. */
- private String lockMessage;
-
- /** Git object size limit */
- private long maxObjectSizeLimit;
-
- /**
- * Initialize a pack parser.
- *
- * @param odb
- * database the parser will write its objects into.
- * @param src
- * the stream the parser will read.
- */
- protected PackParser(final ObjectDatabase odb, final InputStream src) {
- objectDatabase = odb.newCachedDatabase();
- in = src;
-
- inflater = new InflaterStream();
- readCurs = objectDatabase.newReader();
- buf = new byte[BUFFER_SIZE];
- tempBuffer = new byte[BUFFER_SIZE];
- hdrBuf = new byte[64];
- objectDigest = Constants.newMessageDigest();
- tempObjectId = new MutableObjectId();
- packDigest = Constants.newMessageDigest();
- checkObjectCollisions = true;
- }
-
- /** @return true if a thin pack (missing base objects) is permitted. */
- public boolean isAllowThin() {
- return allowThin;
- }
-
- /**
- * Configure this index pack instance to allow a thin pack.
- * <p>
- * Thin packs are sometimes used during network transfers to allow a delta
- * to be sent without a base object. Such packs are not permitted on disk.
- *
- * @param allow
- * true to enable a thin pack.
- */
- public void setAllowThin(final boolean allow) {
- allowThin = allow;
- }
-
- /** @return if true received objects are verified to prevent collisions. */
- public boolean isCheckObjectCollisions() {
- return checkObjectCollisions;
- }
-
- /**
- * Enable checking for collisions with existing objects.
- * <p>
- * By default PackParser looks for each received object in the repository.
- * If the object already exists, the existing object is compared
- * byte-for-byte with the newly received copy to ensure they are identical.
- * The receive is aborted with an exception if any byte differs. This check
- * is necessary to prevent an evil attacker from supplying a replacement
- * object into this repository in the event that a discovery enabling SHA-1
- * collisions is made.
- * <p>
- * This check may be very costly to perform, and some repositories may have
- * other ways to segregate newly received object data. The check is enabled
- * by default, but can be explicitly disabled if the implementation can
- * provide the same guarantee, or is willing to accept the risks associated
- * with bypassing the check.
- *
- * @param check
- * true to enable collision checking (strongly encouraged).
- */
- public void setCheckObjectCollisions(boolean check) {
- checkObjectCollisions = check;
- }
-
- /**
- * Configure this index pack instance to keep track of new objects.
- * <p>
- * By default an index pack doesn't save the new objects that were created
- * when it was instantiated. Setting this flag to {@code true} allows the
- * caller to use {@link #getNewObjectIds()} to retrieve that list.
- *
- * @param b
- * {@code true} to enable keeping track of new objects.
- */
- public void setNeedNewObjectIds(boolean b) {
- if (b)
- newObjectIds = new ObjectIdSubclassMap<ObjectId>();
- else
- newObjectIds = null;
- }
-
- private boolean needNewObjectIds() {
- return newObjectIds != null;
- }
-
- /**
- * Configure this index pack instance to keep track of the objects assumed
- * for delta bases.
- * <p>
- * By default an index pack doesn't save the objects that were used as delta
- * bases. Setting this flag to {@code true} will allow the caller to use
- * {@link #getBaseObjectIds()} to retrieve that list.
- *
- * @param b
- * {@code true} to enable keeping track of delta bases.
- */
- public void setNeedBaseObjectIds(boolean b) {
- this.needBaseObjectIds = b;
- }
-
- /** @return true if the EOF should be read from the input after the footer. */
- public boolean isCheckEofAfterPackFooter() {
- return checkEofAfterPackFooter;
- }
-
- /**
- * Ensure EOF is read from the input stream after the footer.
- *
- * @param b
- * true if the EOF should be read; false if it is not checked.
- */
- public void setCheckEofAfterPackFooter(boolean b) {
- checkEofAfterPackFooter = b;
- }
-
- /** @return the new objects that were sent by the user */
- public ObjectIdSubclassMap<ObjectId> getNewObjectIds() {
- if (newObjectIds != null)
- return newObjectIds;
- return new ObjectIdSubclassMap<ObjectId>();
- }
-
- /** @return set of objects the incoming pack assumed for delta purposes */
- public ObjectIdSubclassMap<ObjectId> getBaseObjectIds() {
- if (baseObjectIds != null)
- return baseObjectIds;
- return new ObjectIdSubclassMap<ObjectId>();
- }
-
- /**
- * Configure the checker used to validate received objects.
- * <p>
- * Usually object checking isn't necessary, as Git implementations only
- * create valid objects in pack files. However, additional checking may be
- * useful if processing data from an untrusted source.
- *
- * @param oc
- * the checker instance; null to disable object checking.
- */
- public void setObjectChecker(final ObjectChecker oc) {
- objCheck = oc;
- }
-
- /**
- * Configure the checker used to validate received objects.
- * <p>
- * Usually object checking isn't necessary, as Git implementations only
- * create valid objects in pack files. However, additional checking may be
- * useful if processing data from an untrusted source.
- * <p>
- * This is shorthand for:
- *
- * <pre>
- * setObjectChecker(on ? new ObjectChecker() : null);
- * </pre>
- *
- * @param on
- * true to enable the default checker; false to disable it.
- */
- public void setObjectChecking(final boolean on) {
- setObjectChecker(on ? new ObjectChecker() : null);
- }
-
- /** @return the message to record with the pack lock. */
- public String getLockMessage() {
- return lockMessage;
- }
-
- /**
- * Set the lock message for the incoming pack data.
- *
- * @param msg
- * if not null, the message to associate with the incoming data
- * while it is locked to prevent garbage collection.
- */
- public void setLockMessage(String msg) {
- lockMessage = msg;
- }
-
- /**
- * Set the maximum allowed Git object size.
- * <p>
- * If an object is larger than the given size the pack-parsing will throw an
- * exception aborting the parsing.
- *
- * @param limit
- * the Git object size limit. If zero then there is not limit.
- */
- public void setMaxObjectSizeLimit(long limit) {
- maxObjectSizeLimit = limit;
- }
-
- /**
- * Get the number of objects in the stream.
- * <p>
- * The object count is only available after {@link #parse(ProgressMonitor)}
- * has returned. The count may have been increased if the stream was a thin
- * pack, and missing bases objects were appending onto it by the subclass.
- *
- * @return number of objects parsed out of the stream.
- */
- public int getObjectCount() {
- return entryCount;
- }
-
- /***
- * Get the information about the requested object.
- * <p>
- * The object information is only available after
- * {@link #parse(ProgressMonitor)} has returned.
- *
- * @param nth
- * index of the object in the stream. Must be between 0 and
- * {@link #getObjectCount()}-1.
- * @return the object information.
- */
- public PackedObjectInfo getObject(int nth) {
- return entries[nth];
- }
-
- /**
- * Get all of the objects, sorted by their name.
- * <p>
- * The object information is only available after
- * {@link #parse(ProgressMonitor)} has returned.
- * <p>
- * To maintain lower memory usage and good runtime performance, this method
- * sorts the objects in-place and therefore impacts the ordering presented
- * by {@link #getObject(int)}.
- *
- * @param cmp
- * comparison function, if null objects are stored by ObjectId.
- * @return sorted list of objects in this pack stream.
- */
- public List<PackedObjectInfo> getSortedObjectList(
- Comparator<PackedObjectInfo> cmp) {
- Arrays.sort(entries, 0, entryCount, cmp);
- List<PackedObjectInfo> list = Arrays.asList(entries);
- if (entryCount < entries.length)
- list = list.subList(0, entryCount);
- return list;
- }
-
- /**
- * Parse the pack stream.
- *
- * @param progress
- * callback to provide progress feedback during parsing. If null,
- * {@link NullProgressMonitor} will be used.
- * @return the pack lock, if one was requested by setting
- * {@link #setLockMessage(String)}.
- * @throws IOException
- * the stream is malformed, or contains corrupt objects.
- */
- public final PackLock parse(ProgressMonitor progress) throws IOException {
- return parse(progress, progress);
- }
-
- /**
- * Parse the pack stream.
- *
- * @param receiving
- * receives progress feedback during the initial receiving
- * objects phase. If null, {@link NullProgressMonitor} will be
- * used.
- * @param resolving
- * receives progress feedback during the resolving objects phase.
- * @return the pack lock, if one was requested by setting
- * {@link #setLockMessage(String)}.
- * @throws IOException
- * the stream is malformed, or contains corrupt objects.
- */
- public PackLock parse(ProgressMonitor receiving, ProgressMonitor resolving)
- throws IOException {
- if (receiving == null)
- receiving = NullProgressMonitor.INSTANCE;
- if (resolving == null)
- resolving = NullProgressMonitor.INSTANCE;
-
- if (receiving == resolving)
- receiving.start(2 /* tasks */);
- try {
- readPackHeader();
-
- entries = new PackedObjectInfo[(int) objectCount];
- baseById = new ObjectIdOwnerMap<DeltaChain>();
- baseByPos = new LongMap<UnresolvedDelta>();
- deferredCheckBlobs = new BlockList<PackedObjectInfo>();
-
- receiving.beginTask(JGitText.get().receivingObjects,
- (int) objectCount);
- try {
- for (int done = 0; done < objectCount; done++) {
- indexOneObject();
- receiving.update(1);
- if (receiving.isCancelled())
- throw new IOException(JGitText.get().downloadCancelled);
- }
- readPackFooter();
- endInput();
- } finally {
- receiving.endTask();
- }
-
- if (!deferredCheckBlobs.isEmpty())
- doDeferredCheckBlobs();
- if (deltaCount > 0) {
- if (resolving instanceof BatchingProgressMonitor) {
- ((BatchingProgressMonitor) resolving).setDelayStart(
- 1000,
- TimeUnit.MILLISECONDS);
- }
- resolving.beginTask(JGitText.get().resolvingDeltas, deltaCount);
- resolveDeltas(resolving);
- if (entryCount < objectCount) {
- if (!isAllowThin()) {
- throw new IOException(MessageFormat.format(JGitText
- .get().packHasUnresolvedDeltas,
- (objectCount - entryCount)));
- }
-
- resolveDeltasWithExternalBases(resolving);
-
- if (entryCount < objectCount) {
- throw new IOException(MessageFormat.format(JGitText
- .get().packHasUnresolvedDeltas,
- (objectCount - entryCount)));
- }
- }
- resolving.endTask();
- }
-
- packDigest = null;
- baseById = null;
- baseByPos = null;
- } finally {
- try {
- if (readCurs != null)
- readCurs.release();
- } finally {
- readCurs = null;
- }
-
- try {
- inflater.release();
- } finally {
- inflater = null;
- }
- }
- return null; // By default there is no locking.
- }
-
- private void resolveDeltas(final ProgressMonitor progress)
- throws IOException {
- final int last = entryCount;
- for (int i = 0; i < last; i++) {
- resolveDeltas(entries[i], progress);
- if (progress.isCancelled())
- throw new IOException(
- JGitText.get().downloadCancelledDuringIndexing);
- }
- }
-
- private void resolveDeltas(final PackedObjectInfo oe,
- ProgressMonitor progress) throws IOException {
- UnresolvedDelta children = firstChildOf(oe);
- if (children == null)
- return;
-
- DeltaVisit visit = new DeltaVisit();
- visit.nextChild = children;
-
- ObjectTypeAndSize info = openDatabase(oe, new ObjectTypeAndSize());
- switch (info.type) {
- case Constants.OBJ_COMMIT:
- case Constants.OBJ_TREE:
- case Constants.OBJ_BLOB:
- case Constants.OBJ_TAG:
- visit.data = inflateAndReturn(Source.DATABASE, info.size);
- visit.id = oe;
- break;
- default:
- throw new IOException(MessageFormat.format(
- JGitText.get().unknownObjectType, info.type));
- }
-
- if (!checkCRC(oe.getCRC())) {
- throw new IOException(MessageFormat.format(
- JGitText.get().corruptionDetectedReReadingAt, oe
- .getOffset()));
- }
-
- resolveDeltas(visit.next(), info.type, info, progress);
- }
-
- private void resolveDeltas(DeltaVisit visit, final int type,
- ObjectTypeAndSize info, ProgressMonitor progress)
- throws IOException {
- do {
- progress.update(1);
- info = openDatabase(visit.delta, info);
- switch (info.type) {
- case Constants.OBJ_OFS_DELTA:
- case Constants.OBJ_REF_DELTA:
- break;
-
- default:
- throw new IOException(MessageFormat.format(
- JGitText.get().unknownObjectType, info.type));
- }
-
- byte[] delta = inflateAndReturn(Source.DATABASE, info.size);
- checkIfTooLarge(type, BinaryDelta.getResultSize(delta));
-
- visit.data = BinaryDelta.apply(visit.parent.data, delta);
- delta = null;
-
- if (!checkCRC(visit.delta.crc))
- throw new IOException(MessageFormat.format(
- JGitText.get().corruptionDetectedReReadingAt,
- visit.delta.position));
-
- objectDigest.update(Constants.encodedTypeString(type));
- objectDigest.update((byte) ' ');
- objectDigest.update(Constants.encodeASCII(visit.data.length));
- objectDigest.update((byte) 0);
- objectDigest.update(visit.data);
- tempObjectId.fromRaw(objectDigest.digest(), 0);
-
- verifySafeObject(tempObjectId, type, visit.data);
-
- PackedObjectInfo oe;
- oe = newInfo(tempObjectId, visit.delta, visit.parent.id);
- oe.setOffset(visit.delta.position);
- onInflatedObjectData(oe, type, visit.data);
- addObjectAndTrack(oe);
- visit.id = oe;
-
- visit.nextChild = firstChildOf(oe);
- visit = visit.next();
- } while (visit != null);
- }
-
- private final void checkIfTooLarge(int typeCode, long size)
- throws IOException {
- if (0 < maxObjectSizeLimit && maxObjectSizeLimit < size)
- switch (typeCode) {
- case Constants.OBJ_COMMIT:
- case Constants.OBJ_TREE:
- case Constants.OBJ_BLOB:
- case Constants.OBJ_TAG:
- throw new TooLargeObjectInPackException(size, maxObjectSizeLimit);
-
- case Constants.OBJ_OFS_DELTA:
- case Constants.OBJ_REF_DELTA:
- throw new TooLargeObjectInPackException(maxObjectSizeLimit);
-
- default:
- throw new IOException(MessageFormat.format(
- JGitText.get().unknownObjectType, typeCode));
- }
- }
-
- /**
- * Read the header of the current object.
- * <p>
- * After the header has been parsed, this method automatically invokes
- * {@link #onObjectHeader(Source, byte[], int, int)} to allow the
- * implementation to update its internal checksums for the bytes read.
- * <p>
- * When this method returns the database will be positioned on the first
- * byte of the deflated data stream.
- *
- * @param info
- * the info object to populate.
- * @return {@code info}, after populating.
- * @throws IOException
- * the size cannot be read.
- */
- protected ObjectTypeAndSize readObjectHeader(ObjectTypeAndSize info)
- throws IOException {
- int hdrPtr = 0;
- int c = readFrom(Source.DATABASE);
- hdrBuf[hdrPtr++] = (byte) c;
-
- info.type = (c >> 4) & 7;
- long sz = c & 15;
- int shift = 4;
- while ((c & 0x80) != 0) {
- c = readFrom(Source.DATABASE);
- hdrBuf[hdrPtr++] = (byte) c;
- sz += ((long) (c & 0x7f)) << shift;
- shift += 7;
- }
- info.size = sz;
-
- switch (info.type) {
- case Constants.OBJ_COMMIT:
- case Constants.OBJ_TREE:
- case Constants.OBJ_BLOB:
- case Constants.OBJ_TAG:
- onObjectHeader(Source.DATABASE, hdrBuf, 0, hdrPtr);
- break;
-
- case Constants.OBJ_OFS_DELTA:
- c = readFrom(Source.DATABASE);
- hdrBuf[hdrPtr++] = (byte) c;
- while ((c & 128) != 0) {
- c = readFrom(Source.DATABASE);
- hdrBuf[hdrPtr++] = (byte) c;
- }
- onObjectHeader(Source.DATABASE, hdrBuf, 0, hdrPtr);
- break;
-
- case Constants.OBJ_REF_DELTA:
- System.arraycopy(buf, fill(Source.DATABASE, 20), hdrBuf, hdrPtr, 20);
- hdrPtr += 20;
- use(20);
- onObjectHeader(Source.DATABASE, hdrBuf, 0, hdrPtr);
- break;
-
- default:
- throw new IOException(MessageFormat.format(
- JGitText.get().unknownObjectType, info.type));
- }
- return info;
- }
-
- private UnresolvedDelta removeBaseById(final AnyObjectId id) {
- final DeltaChain d = baseById.get(id);
- return d != null ? d.remove() : null;
- }
-
- private static UnresolvedDelta reverse(UnresolvedDelta c) {
- UnresolvedDelta tail = null;
- while (c != null) {
- final UnresolvedDelta n = c.next;
- c.next = tail;
- tail = c;
- c = n;
- }
- return tail;
- }
-
- private UnresolvedDelta firstChildOf(PackedObjectInfo oe) {
- UnresolvedDelta a = reverse(removeBaseById(oe));
- UnresolvedDelta b = reverse(baseByPos.remove(oe.getOffset()));
-
- if (a == null)
- return b;
- if (b == null)
- return a;
-
- UnresolvedDelta first = null;
- UnresolvedDelta last = null;
- while (a != null || b != null) {
- UnresolvedDelta curr;
- if (b == null || (a != null && a.position < b.position)) {
- curr = a;
- a = a.next;
- } else {
- curr = b;
- b = b.next;
- }
- if (last != null)
- last.next = curr;
- else
- first = curr;
- last = curr;
- curr.next = null;
- }
- return first;
- }
-
- private void resolveDeltasWithExternalBases(final ProgressMonitor progress)
- throws IOException {
- growEntries(baseById.size());
-
- if (needBaseObjectIds)
- baseObjectIds = new ObjectIdSubclassMap<ObjectId>();
-
- final List<DeltaChain> missing = new ArrayList<DeltaChain>(64);
- for (final DeltaChain baseId : baseById) {
- if (baseId.head == null)
- continue;
-
- if (needBaseObjectIds)
- baseObjectIds.add(baseId);
-
- final ObjectLoader ldr;
- try {
- ldr = readCurs.open(baseId);
- } catch (MissingObjectException notFound) {
- missing.add(baseId);
- continue;
- }
-
- final DeltaVisit visit = new DeltaVisit();
- visit.data = ldr.getCachedBytes(Integer.MAX_VALUE);
- visit.id = baseId;
- final int typeCode = ldr.getType();
- final PackedObjectInfo oe = newInfo(baseId, null, null);
-
- if (onAppendBase(typeCode, visit.data, oe))
- entries[entryCount++] = oe;
-
- visit.nextChild = firstChildOf(oe);
- resolveDeltas(visit.next(), typeCode,
- new ObjectTypeAndSize(), progress);
-
- if (progress.isCancelled())
- throw new IOException(
- JGitText.get().downloadCancelledDuringIndexing);
- }
-
- for (final DeltaChain base : missing) {
- if (base.head != null)
- throw new MissingObjectException(base, "delta base");
- }
-
- onEndThinPack();
- }
-
- private void growEntries(int extraObjects) {
- final PackedObjectInfo[] ne;
-
- ne = new PackedObjectInfo[(int) objectCount + extraObjects];
- System.arraycopy(entries, 0, ne, 0, entryCount);
- entries = ne;
- }
-
- private void readPackHeader() throws IOException {
- final int hdrln = Constants.PACK_SIGNATURE.length + 4 + 4;
- final int p = fill(Source.INPUT, hdrln);
- for (int k = 0; k < Constants.PACK_SIGNATURE.length; k++)
- if (buf[p + k] != Constants.PACK_SIGNATURE[k])
- throw new IOException(JGitText.get().notAPACKFile);
-
- final long vers = NB.decodeUInt32(buf, p + 4);
- if (vers != 2 && vers != 3)
- throw new IOException(MessageFormat.format(
- JGitText.get().unsupportedPackVersion, vers));
- objectCount = NB.decodeUInt32(buf, p + 8);
- use(hdrln);
-
- onPackHeader(objectCount);
- }
-
- private void readPackFooter() throws IOException {
- sync();
- final byte[] actHash = packDigest.digest();
-
- final int c = fill(Source.INPUT, 20);
- final byte[] srcHash = new byte[20];
- System.arraycopy(buf, c, srcHash, 0, 20);
- use(20);
-
- // The input stream should be at EOF at this point. We do not support
- // yielding back any remaining buffered data after the pack footer, so
- // protocols that embed a pack stream are required to either end their
- // stream with the pack, or embed the pack with a framing system like
- // the SideBandInputStream does.
-
- if (bAvail != 0)
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().expectedEOFReceived,
- "\\x" + Integer.toHexString(buf[bOffset] & 0xff)));
-
- if (isCheckEofAfterPackFooter()) {
- int eof = in.read();
- if (0 <= eof)
- throw new CorruptObjectException(MessageFormat.format(
- JGitText.get().expectedEOFReceived,
- "\\x" + Integer.toHexString(eof)));
- }
-
- if (!Arrays.equals(actHash, srcHash))
- throw new CorruptObjectException(
- JGitText.get().corruptObjectPackfileChecksumIncorrect);
-
- onPackFooter(srcHash);
- }
-
- // Cleanup all resources associated with our input parsing.
- private void endInput() {
- in = null;
- }
-
- // Read one entire object or delta from the input.
- private void indexOneObject() throws IOException {
- final long streamPosition = streamPosition();
-
- int hdrPtr = 0;
- int c = readFrom(Source.INPUT);
- hdrBuf[hdrPtr++] = (byte) c;
-
- final int typeCode = (c >> 4) & 7;
- long sz = c & 15;
- int shift = 4;
- while ((c & 0x80) != 0) {
- c = readFrom(Source.INPUT);
- hdrBuf[hdrPtr++] = (byte) c;
- sz += ((long) (c & 0x7f)) << shift;
- shift += 7;
- }
-
- checkIfTooLarge(typeCode, sz);
-
- switch (typeCode) {
- case Constants.OBJ_COMMIT:
- case Constants.OBJ_TREE:
- case Constants.OBJ_BLOB:
- case Constants.OBJ_TAG:
- onBeginWholeObject(streamPosition, typeCode, sz);
- onObjectHeader(Source.INPUT, hdrBuf, 0, hdrPtr);
- whole(streamPosition, typeCode, sz);
- break;
-
- case Constants.OBJ_OFS_DELTA: {
- c = readFrom(Source.INPUT);
- hdrBuf[hdrPtr++] = (byte) c;
- long ofs = c & 127;
- while ((c & 128) != 0) {
- ofs += 1;
- c = readFrom(Source.INPUT);
- hdrBuf[hdrPtr++] = (byte) c;
- ofs <<= 7;
- ofs += (c & 127);
- }
- final long base = streamPosition - ofs;
- onBeginOfsDelta(streamPosition, base, sz);
- onObjectHeader(Source.INPUT, hdrBuf, 0, hdrPtr);
- inflateAndSkip(Source.INPUT, sz);
- UnresolvedDelta n = onEndDelta();
- n.position = streamPosition;
- n.next = baseByPos.put(base, n);
- deltaCount++;
- break;
- }
-
- case Constants.OBJ_REF_DELTA: {
- c = fill(Source.INPUT, 20);
- final ObjectId base = ObjectId.fromRaw(buf, c);
- System.arraycopy(buf, c, hdrBuf, hdrPtr, 20);
- hdrPtr += 20;
- use(20);
- DeltaChain r = baseById.get(base);
- if (r == null) {
- r = new DeltaChain(base);
- baseById.add(r);
- }
- onBeginRefDelta(streamPosition, base, sz);
- onObjectHeader(Source.INPUT, hdrBuf, 0, hdrPtr);
- inflateAndSkip(Source.INPUT, sz);
- UnresolvedDelta n = onEndDelta();
- n.position = streamPosition;
- r.add(n);
- deltaCount++;
- break;
- }
-
- default:
- throw new IOException(MessageFormat.format(
- JGitText.get().unknownObjectType, typeCode));
- }
- }
-
- private void whole(final long pos, final int type, final long sz)
- throws IOException {
- objectDigest.update(Constants.encodedTypeString(type));
- objectDigest.update((byte) ' ');
- objectDigest.update(Constants.encodeASCII(sz));
- objectDigest.update((byte) 0);
-
- final byte[] data;
- boolean checkContentLater = false;
- if (type == Constants.OBJ_BLOB) {
- byte[] readBuffer = buffer();
- InputStream inf = inflate(Source.INPUT, sz);
- long cnt = 0;
- while (cnt < sz) {
- int r = inf.read(readBuffer);
- if (r <= 0)
- break;
- objectDigest.update(readBuffer, 0, r);
- cnt += r;
- }
- inf.close();
- tempObjectId.fromRaw(objectDigest.digest(), 0);
- checkContentLater = isCheckObjectCollisions()
- && readCurs.has(tempObjectId);
- data = null;
-
- } else {
- data = inflateAndReturn(Source.INPUT, sz);
- objectDigest.update(data);
- tempObjectId.fromRaw(objectDigest.digest(), 0);
- verifySafeObject(tempObjectId, type, data);
- }
-
- PackedObjectInfo obj = newInfo(tempObjectId, null, null);
- obj.setOffset(pos);
- onEndWholeObject(obj);
- if (data != null)
- onInflatedObjectData(obj, type, data);
- addObjectAndTrack(obj);
- if (checkContentLater)
- deferredCheckBlobs.add(obj);
- }
-
- private void verifySafeObject(final AnyObjectId id, final int type,
- final byte[] data) throws IOException {
- if (objCheck != null) {
- try {
- objCheck.check(type, data);
- } catch (CorruptObjectException e) {
- throw new IOException(MessageFormat.format(
- JGitText.get().invalidObject, Constants
- .typeString(type), id.name(), e.getMessage()));
- }
- }
-
- if (isCheckObjectCollisions()) {
- try {
- final ObjectLoader ldr = readCurs.open(id, type);
- final byte[] existingData = ldr.getCachedBytes(data.length);
- if (!Arrays.equals(data, existingData)) {
- throw new IOException(MessageFormat.format(
- JGitText.get().collisionOn, id.name()));
- }
- } catch (MissingObjectException notLocal) {
- // This is OK, we don't have a copy of the object locally
- // but the API throws when we try to read it as usually its
- // an error to read something that doesn't exist.
- }
- }
- }
-
- private void doDeferredCheckBlobs() throws IOException {
- final byte[] readBuffer = buffer();
- final byte[] curBuffer = new byte[readBuffer.length];
- ObjectTypeAndSize info = new ObjectTypeAndSize();
-
- for (PackedObjectInfo obj : deferredCheckBlobs) {
- info = openDatabase(obj, info);
-
- if (info.type != Constants.OBJ_BLOB)
- throw new IOException(MessageFormat.format(
- JGitText.get().unknownObjectType, info.type));
-
- ObjectStream cur = readCurs.open(obj, info.type).openStream();
- try {
- long sz = info.size;
- if (cur.getSize() != sz)
- throw new IOException(MessageFormat.format(
- JGitText.get().collisionOn, obj.name()));
- InputStream pck = inflate(Source.DATABASE, sz);
- while (0 < sz) {
- int n = (int) Math.min(readBuffer.length, sz);
- IO.readFully(cur, curBuffer, 0, n);
- IO.readFully(pck, readBuffer, 0, n);
- for (int i = 0; i < n; i++) {
- if (curBuffer[i] != readBuffer[i])
- throw new IOException(MessageFormat.format(JGitText
- .get().collisionOn, obj.name()));
- }
- sz -= n;
- }
- pck.close();
- } finally {
- cur.close();
- }
- }
- }
-
- /** @return current position of the input stream being parsed. */
- private long streamPosition() {
- return bBase + bOffset;
- }
-
- private ObjectTypeAndSize openDatabase(PackedObjectInfo obj,
- ObjectTypeAndSize info) throws IOException {
- bOffset = 0;
- bAvail = 0;
- return seekDatabase(obj, info);
- }
-
- private ObjectTypeAndSize openDatabase(UnresolvedDelta delta,
- ObjectTypeAndSize info) throws IOException {
- bOffset = 0;
- bAvail = 0;
- return seekDatabase(delta, info);
- }
-
- // Consume exactly one byte from the buffer and return it.
- private int readFrom(final Source src) throws IOException {
- if (bAvail == 0)
- fill(src, 1);
- bAvail--;
- return buf[bOffset++] & 0xff;
- }
-
- // Consume cnt bytes from the buffer.
- private void use(final int cnt) {
- bOffset += cnt;
- bAvail -= cnt;
- }
-
- // Ensure at least need bytes are available in in {@link #buf}.
- private int fill(final Source src, final int need) throws IOException {
- while (bAvail < need) {
- int next = bOffset + bAvail;
- int free = buf.length - next;
- if (free + bAvail < need) {
- switch (src) {
- case INPUT:
- sync();
- break;
- case DATABASE:
- if (bAvail > 0)
- System.arraycopy(buf, bOffset, buf, 0, bAvail);
- bOffset = 0;
- break;
- }
- next = bAvail;
- free = buf.length - next;
- }
- switch (src) {
- case INPUT:
- next = in.read(buf, next, free);
- break;
- case DATABASE:
- next = readDatabase(buf, next, free);
- break;
- }
- if (next <= 0)
- throw new EOFException(JGitText.get().packfileIsTruncated);
- bAvail += next;
- }
- return bOffset;
- }
-
- // Store consumed bytes in {@link #buf} up to {@link #bOffset}.
- private void sync() throws IOException {
- packDigest.update(buf, 0, bOffset);
- onStoreStream(buf, 0, bOffset);
- if (bAvail > 0)
- System.arraycopy(buf, bOffset, buf, 0, bAvail);
- bBase += bOffset;
- bOffset = 0;
- }
-
- /** @return a temporary byte array for use by the caller. */
- protected byte[] buffer() {
- return tempBuffer;
- }
-
- /**
- * Construct a PackedObjectInfo instance for this parser.
- *
- * @param id
- * identity of the object to be tracked.
- * @param delta
- * if the object was previously an unresolved delta, this is the
- * delta object that was tracking it. Otherwise null.
- * @param deltaBase
- * if the object was previously an unresolved delta, this is the
- * ObjectId of the base of the delta. The base may be outside of
- * the pack stream if the stream was a thin-pack.
- * @return info object containing this object's data.
- */
- protected PackedObjectInfo newInfo(AnyObjectId id, UnresolvedDelta delta,
- ObjectId deltaBase) {
- PackedObjectInfo oe = new PackedObjectInfo(id);
- if (delta != null)
- oe.setCRC(delta.crc);
- return oe;
- }
-
- /**
- * Store bytes received from the raw stream.
- * <p>
- * This method is invoked during {@link #parse(ProgressMonitor)} as data is
- * consumed from the incoming stream. Implementors may use this event to
- * archive the raw incoming stream to the destination repository in large
- * chunks, without paying attention to object boundaries.
- * <p>
- * The only component of the pack not supplied to this method is the last 20
- * bytes of the pack that comprise the trailing SHA-1 checksum. Those are
- * passed to {@link #onPackFooter(byte[])}.
- *
- * @param raw
- * buffer to copy data out of.
- * @param pos
- * first offset within the buffer that is valid.
- * @param len
- * number of bytes in the buffer that are valid.
- * @throws IOException
- * the stream cannot be archived.
- */
- protected abstract void onStoreStream(byte[] raw, int pos, int len)
- throws IOException;
-
- /**
- * Store (and/or checksum) an object header.
- * <p>
- * Invoked after any of the {@code onBegin()} events. The entire header is
- * supplied in a single invocation, before any object data is supplied.
- *
- * @param src
- * where the data came from
- * @param raw
- * buffer to read data from.
- * @param pos
- * first offset within buffer that is valid.
- * @param len
- * number of bytes in buffer that are valid.
- * @throws IOException
- * the stream cannot be archived.
- */
- protected abstract void onObjectHeader(Source src, byte[] raw, int pos,
- int len) throws IOException;
-
- /**
- * Store (and/or checksum) a portion of an object's data.
- * <p>
- * This method may be invoked multiple times per object, depending on the
- * size of the object, the size of the parser's internal read buffer, and
- * the alignment of the object relative to the read buffer.
- * <p>
- * Invoked after {@link #onObjectHeader(Source, byte[], int, int)}.
- *
- * @param src
- * where the data came from
- * @param raw
- * buffer to read data from.
- * @param pos
- * first offset within buffer that is valid.
- * @param len
- * number of bytes in buffer that are valid.
- * @throws IOException
- * the stream cannot be archived.
- */
- protected abstract void onObjectData(Source src, byte[] raw, int pos,
- int len) throws IOException;
-
- /**
- * Invoked for commits, trees, tags, and small blobs.
- *
- * @param obj
- * the object info, populated.
- * @param typeCode
- * the type of the object.
- * @param data
- * inflated data for the object.
- * @throws IOException
- * the object cannot be archived.
- */
- protected abstract void onInflatedObjectData(PackedObjectInfo obj,
- int typeCode, byte[] data) throws IOException;
-
- /**
- * Provide the implementation with the original stream's pack header.
- *
- * @param objCnt
- * number of objects expected in the stream.
- * @throws IOException
- * the implementation refuses to work with this many objects.
- */
- protected abstract void onPackHeader(long objCnt) throws IOException;
-
- /**
- * Provide the implementation with the original stream's pack footer.
- *
- * @param hash
- * the trailing 20 bytes of the pack, this is a SHA-1 checksum of
- * all of the pack data.
- * @throws IOException
- * the stream cannot be archived.
- */
- protected abstract void onPackFooter(byte[] hash) throws IOException;
-
- /**
- * Provide the implementation with a base that was outside of the pack.
- * <p>
- * This event only occurs on a thin pack for base objects that were outside
- * of the pack and came from the local repository. Usually an implementation
- * uses this event to compress the base and append it onto the end of the
- * pack, so the pack stays self-contained.
- *
- * @param typeCode
- * type of the base object.
- * @param data
- * complete content of the base object.
- * @param info
- * packed object information for this base. Implementors must
- * populate the CRC and offset members if returning true.
- * @return true if the {@code info} should be included in the object list
- * returned by {@link #getSortedObjectList(Comparator)}, false if it
- * should not be included.
- * @throws IOException
- * the base could not be included into the pack.
- */
- protected abstract boolean onAppendBase(int typeCode, byte[] data,
- PackedObjectInfo info) throws IOException;
-
- /**
- * Event indicating a thin pack has been completely processed.
- * <p>
- * This event is invoked only if a thin pack has delta references to objects
- * external from the pack. The event is called after all of those deltas
- * have been resolved.
- *
- * @throws IOException
- * the pack cannot be archived.
- */
- protected abstract void onEndThinPack() throws IOException;
-
- /**
- * Reposition the database to re-read a previously stored object.
- * <p>
- * If the database is computing CRC-32 checksums for object data, it should
- * reset its internal CRC instance during this method call.
- *
- * @param obj
- * the object position to begin reading from. This is from
- * {@link #newInfo(AnyObjectId, UnresolvedDelta, ObjectId)}.
- * @param info
- * object to populate with type and size.
- * @return the {@code info} object.
- * @throws IOException
- * the database cannot reposition to this location.
- */
- protected abstract ObjectTypeAndSize seekDatabase(PackedObjectInfo obj,
- ObjectTypeAndSize info) throws IOException;
-
- /**
- * Reposition the database to re-read a previously stored object.
- * <p>
- * If the database is computing CRC-32 checksums for object data, it should
- * reset its internal CRC instance during this method call.
- *
- * @param delta
- * the object position to begin reading from. This is an instance
- * previously returned by {@link #onEndDelta()}.
- * @param info
- * object to populate with type and size.
- * @return the {@code info} object.
- * @throws IOException
- * the database cannot reposition to this location.
- */
- protected abstract ObjectTypeAndSize seekDatabase(UnresolvedDelta delta,
- ObjectTypeAndSize info) throws IOException;
-
- /**
- * Read from the database's current position into the buffer.
- *
- * @param dst
- * the buffer to copy read data into.
- * @param pos
- * position within {@code dst} to start copying data into.
- * @param cnt
- * ideal target number of bytes to read. Actual read length may
- * be shorter.
- * @return number of bytes stored.
- * @throws IOException
- * the database cannot be accessed.
- */
- protected abstract int readDatabase(byte[] dst, int pos, int cnt)
- throws IOException;
-
- /**
- * Check the current CRC matches the expected value.
- * <p>
- * This method is invoked when an object is read back in from the database
- * and its data is used during delta resolution. The CRC is validated after
- * the object has been fully read, allowing the parser to verify there was
- * no silent data corruption.
- * <p>
- * Implementations are free to ignore this check by always returning true if
- * they are performing other data integrity validations at a lower level.
- *
- * @param oldCRC
- * the prior CRC that was recorded during the first scan of the
- * object from the pack stream.
- * @return true if the CRC matches; false if it does not.
- */
- protected abstract boolean checkCRC(int oldCRC);
-
- /**
- * Event notifying the start of an object stored whole (not as a delta).
- *
- * @param streamPosition
- * position of this object in the incoming stream.
- * @param type
- * type of the object; one of {@link Constants#OBJ_COMMIT},
- * {@link Constants#OBJ_TREE}, {@link Constants#OBJ_BLOB}, or
- * {@link Constants#OBJ_TAG}.
- * @param inflatedSize
- * size of the object when fully inflated. The size stored within
- * the pack may be larger or smaller, and is not yet known.
- * @throws IOException
- * the object cannot be recorded.
- */
- protected abstract void onBeginWholeObject(long streamPosition, int type,
- long inflatedSize) throws IOException;
-
- /**
- * Event notifying the the current object.
- *
- *@param info
- * object information.
- * @throws IOException
- * the object cannot be recorded.
- */
- protected abstract void onEndWholeObject(PackedObjectInfo info)
- throws IOException;
-
- /**
- * Event notifying start of a delta referencing its base by offset.
- *
- * @param deltaStreamPosition
- * position of this object in the incoming stream.
- * @param baseStreamPosition
- * position of the base object in the incoming stream. The base
- * must be before the delta, therefore {@code baseStreamPosition
- * < deltaStreamPosition}. This is <b>not</b> the position
- * returned by a prior end object event.
- * @param inflatedSize
- * size of the delta when fully inflated. The size stored within
- * the pack may be larger or smaller, and is not yet known.
- * @throws IOException
- * the object cannot be recorded.
- */
- protected abstract void onBeginOfsDelta(long deltaStreamPosition,
- long baseStreamPosition, long inflatedSize) throws IOException;
-
- /**
- * Event notifying start of a delta referencing its base by ObjectId.
- *
- * @param deltaStreamPosition
- * position of this object in the incoming stream.
- * @param baseId
- * name of the base object. This object may be later in the
- * stream, or might not appear at all in the stream (in the case
- * of a thin-pack).
- * @param inflatedSize
- * size of the delta when fully inflated. The size stored within
- * the pack may be larger or smaller, and is not yet known.
- * @throws IOException
- * the object cannot be recorded.
- */
- protected abstract void onBeginRefDelta(long deltaStreamPosition,
- AnyObjectId baseId, long inflatedSize) throws IOException;
-
- /**
- * Event notifying the the current object.
- *
- *@return object information that must be populated with at least the
- * offset.
- * @throws IOException
- * the object cannot be recorded.
- */
- protected UnresolvedDelta onEndDelta() throws IOException {
- return new UnresolvedDelta();
- }
-
- /** Type and size information about an object in the database buffer. */
- public static class ObjectTypeAndSize {
- /** The type of the object. */
- public int type;
-
- /** The inflated size of the object. */
- public long size;
- }
-
- private void inflateAndSkip(final Source src, final long inflatedSize)
- throws IOException {
- final InputStream inf = inflate(src, inflatedSize);
- IO.skipFully(inf, inflatedSize);
- inf.close();
- }
-
- private byte[] inflateAndReturn(final Source src, final long inflatedSize)
- throws IOException {
- final byte[] dst = new byte[(int) inflatedSize];
- final InputStream inf = inflate(src, inflatedSize);
- IO.readFully(inf, dst, 0, dst.length);
- inf.close();
- return dst;
- }
-
- private InputStream inflate(final Source src, final long inflatedSize)
- throws IOException {
- inflater.open(src, inflatedSize);
- return inflater;
- }
-
- private static class DeltaChain extends ObjectIdOwnerMap.Entry {
- UnresolvedDelta head;
-
- DeltaChain(final AnyObjectId id) {
- super(id);
- }
-
- UnresolvedDelta remove() {
- final UnresolvedDelta r = head;
- if (r != null)
- head = null;
- return r;
- }
-
- void add(final UnresolvedDelta d) {
- d.next = head;
- head = d;
- }
- }
-
- /** Information about an unresolved delta in this pack stream. */
- public static class UnresolvedDelta {
- long position;
-
- int crc;
-
- UnresolvedDelta next;
-
- /** @return offset within the input stream. */
- public long getOffset() {
- return position;
- }
-
- /** @return the CRC-32 checksum of the stored delta data. */
- public int getCRC() {
- return crc;
- }
-
- /**
- * @param crc32
- * the CRC-32 checksum of the stored delta data.
- */
- public void setCRC(int crc32) {
- crc = crc32;
- }
- }
-
- private static class DeltaVisit {
- final UnresolvedDelta delta;
-
- ObjectId id;
-
- byte[] data;
-
- DeltaVisit parent;
-
- UnresolvedDelta nextChild;
-
- DeltaVisit() {
- this.delta = null; // At the root of the stack we have a base.
- }
-
- DeltaVisit(DeltaVisit parent) {
- this.parent = parent;
- this.delta = parent.nextChild;
- parent.nextChild = delta.next;
- }
-
- DeltaVisit next() {
- // If our parent has no more children, discard it.
- if (parent != null && parent.nextChild == null) {
- parent.data = null;
- parent = parent.parent;
- }
-
- if (nextChild != null)
- return new DeltaVisit(this);
-
- // If we have no child ourselves, our parent must (if it exists),
- // due to the discard rule above. With no parent, we are done.
- if (parent != null)
- return new DeltaVisit(parent);
- return null;
- }
- }
-
- private void addObjectAndTrack(PackedObjectInfo oe) {
- entries[entryCount++] = oe;
- if (needNewObjectIds())
- newObjectIds.add(oe);
- }
-
- private class InflaterStream extends InputStream {
- private final Inflater inf;
-
- private final byte[] skipBuffer;
-
- private Source src;
-
- private long expectedSize;
-
- private long actualSize;
-
- private int p;
-
- InflaterStream() {
- inf = InflaterCache.get();
- skipBuffer = new byte[512];
- }
-
- void release() {
- inf.reset();
- InflaterCache.release(inf);
- }
-
- void open(Source source, long inflatedSize) throws IOException {
- src = source;
- expectedSize = inflatedSize;
- actualSize = 0;
-
- p = fill(src, 1);
- inf.setInput(buf, p, bAvail);
- }
-
- @Override
- public long skip(long toSkip) throws IOException {
- long n = 0;
- while (n < toSkip) {
- final int cnt = (int) Math.min(skipBuffer.length, toSkip - n);
- final int r = read(skipBuffer, 0, cnt);
- if (r <= 0)
- break;
- n += r;
- }
- return n;
- }
-
- @Override
- public int read() throws IOException {
- int n = read(skipBuffer, 0, 1);
- return n == 1 ? skipBuffer[0] & 0xff : -1;
- }
-
- @Override
- public int read(byte[] dst, int pos, int cnt) throws IOException {
- try {
- int n = 0;
- while (n < cnt) {
- int r = inf.inflate(dst, pos + n, cnt - n);
- if (r == 0) {
- if (inf.finished())
- break;
- if (inf.needsInput()) {
- onObjectData(src, buf, p, bAvail);
- use(bAvail);
-
- p = fill(src, 1);
- inf.setInput(buf, p, bAvail);
- } else {
- throw new CorruptObjectException(
- MessageFormat
- .format(
- JGitText.get().packfileCorruptionDetected,
- JGitText.get().unknownZlibError));
- }
- } else {
- n += r;
- }
- }
- actualSize += n;
- return 0 < n ? n : -1;
- } catch (DataFormatException dfe) {
- throw new CorruptObjectException(MessageFormat.format(JGitText
- .get().packfileCorruptionDetected, dfe.getMessage()));
- }
- }
-
- @Override
- public void close() throws IOException {
- // We need to read here to enter the loop above and pump the
- // trailing checksum into the Inflater. It should return -1 as the
- // caller was supposed to consume all content.
- //
- if (read(skipBuffer) != -1 || actualSize != expectedSize) {
- throw new CorruptObjectException(MessageFormat.format(JGitText
- .get().packfileCorruptionDetected,
- JGitText.get().wrongDecompressedLength));
- }
-
- int used = bAvail - inf.getRemaining();
- if (0 < used) {
- onObjectData(src, buf, p, used);
- use(used);
- }
-
- inf.reset();
- }
- }
- }
|