Redo PackWriter object reuse selection

The new selection implementation uses a public API on the
ObjectReader, allowing the storage library to enumerate its
candidates and select the best one for this packer without
needing to build a temporary list of the candidates first.

Change-Id: Ie01496434f7d3581d6d3bbb9e33c8f9fa649b6cd
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
Shawn O. Pearce 2010-06-26 14:16:06 -07:00
parent e0c9368f3e
commit bf4ffff07f
11 changed files with 415 additions and 129 deletions

View File

@ -175,4 +175,10 @@ ObjectLoader openObject2(WindowCursor curs, String objectName,
// This method should never be invoked.
throw new UnsupportedOperationException();
}
@Override
void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
WindowCursor curs) throws IOException {
wrapped.selectObjectRepresentation(packer, otp, curs);
}
}

View File

@ -45,7 +45,6 @@
import java.io.File;
import java.io.IOException;
import java.util.Collection;
abstract class FileObjectDatabase extends ObjectDatabase {
@Override
@ -160,9 +159,8 @@ final ObjectLoader openObjectImpl2(final WindowCursor curs,
return null;
}
void openObjectInAllPacks(Collection<PackedObjectLoader> reuseLoaders,
WindowCursor windowCursor, AnyObjectId otp) throws IOException {
}
abstract void selectObjectRepresentation(PackWriter packer,
ObjectToPack otp, WindowCursor curs) throws IOException;
abstract File getDirectory();

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2010, Google Inc.
* 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.lib;
import java.io.IOException;
class LocalObjectRepresentation extends StoredObjectRepresentation {
final PackedObjectLoader ldr;
LocalObjectRepresentation(PackedObjectLoader ldr) {
this.ldr = ldr;
}
@Override
public int getFormat() {
if (ldr instanceof DeltaPackedObjectLoader)
return PACK_DELTA;
if (ldr instanceof WholePackedObjectLoader)
return PACK_WHOLE;
return FORMAT_OTHER;
}
@Override
public int getWeight() {
long sz = ldr.getRawSize();
if (Integer.MAX_VALUE < sz)
return WEIGHT_UNKNOWN;
return (int) sz;
}
@Override
public ObjectId getDeltaBase() {
try {
return ldr.getDeltaBase();
} catch (IOException e) {
return null;
}
}
}

View File

@ -59,20 +59,14 @@ class LocalObjectToPack extends ObjectToPack {
super(obj);
}
boolean isCopyable() {
return copyFromPack != null;
}
PackedObjectLoader getCopyLoader(WindowCursor curs) throws IOException {
return copyFromPack.resolveBase(curs, copyOffset);
}
void setCopyFromPack(PackedObjectLoader loader) {
this.copyFromPack = loader.pack;
this.copyOffset = loader.objectOffset;
}
void clearSourcePack() {
copyFromPack = null;
@Override
public void select(StoredObjectRepresentation ref) {
LocalObjectRepresentation ptr = (LocalObjectRepresentation)ref;
this.copyFromPack = ptr.ldr.pack;
this.copyOffset = ptr.ldr.objectOffset;
}
}

View File

@ -283,17 +283,16 @@ ObjectLoader openObject1(final WindowCursor curs,
}
}
void openObjectInAllPacks(final Collection<PackedObjectLoader> out,
final WindowCursor curs, final AnyObjectId objectId)
throws IOException {
@Override
void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
WindowCursor curs) throws IOException {
PackList pList = packList.get();
SEARCH: for (;;) {
for (final PackFile p : pList.packs) {
try {
final PackedObjectLoader ldr = p.get(curs, objectId);
if (ldr != null) {
out.add(ldr);
}
PackedObjectLoader ldr = p.get(curs, otp);
if (ldr != null)
packer.select(otp, new LocalObjectRepresentation(ldr));
} catch (PackMismatchException e) {
// Pack was modified; refresh the entire pack list.
//
@ -309,7 +308,7 @@ void openObjectInAllPacks(final Collection<PackedObjectLoader> out,
}
for (AlternateHandle h : myAlternates())
h.db.openObjectInAllPacks(out, curs, objectId);
h.db.selectObjectRepresentation(packer, otp, curs);
}
boolean hasObject2(final String objectName) {

View File

@ -46,9 +46,13 @@
import java.io.IOException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.revwalk.RevObject;
/** Reads an {@link ObjectDatabase} for a single thread. */
/**
* Reads an {@link ObjectDatabase} for a single thread.
* <p>
* Readers that can support efficient reuse of pack encoded objects should also
* implement the companion interface {@link ObjectReuseAsIs}.
*/
public abstract class ObjectReader {
/** Type hint indicating the caller doesn't know the type. */
protected static final int OBJ_ANY = -1;
@ -103,29 +107,6 @@ public ObjectLoader openObject(AnyObjectId objectId)
public abstract ObjectLoader openObject(AnyObjectId objectId, int typeHint)
throws MissingObjectException, IOException;
/**
* Allocate a new {@code PackWriter} state structure for an object.
* <p>
* {@link PackWriter} allocates these objects to keep track of the
* per-object state, and how to load the objects efficiently into the
* generated stream. Implementers may override this method to provide their
* own subclass with additional object state, such as to remember what file
* and position contains the object's data.
* <p>
* The default implementation of this object does not provide very efficient
* packing support; it inflates the object on the fly through {@code
* openObject} and deflates it again into the generated stream.
*
* @param obj
* identity of the object that will be packed. The object's
* parsed status is undefined here. Implementers must not rely on
* the object being parsed.
* @return a new instance for this object.
*/
public ObjectToPack newObjectToPack(RevObject obj) {
return new ObjectToPack(obj, obj.getType());
}
/**
* Release any resources used by this reader.
* <p>

View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2010, Google Inc.
* 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.lib;
import java.io.IOException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.revwalk.RevObject;
/**
* Extension of {@link ObjectReader} that supports reusing objects in packs.
* <p>
* {@code ObjectReader} implementations may also optionally implement this
* interface to support {@link PackWriter} with a means of copying an object
* that is already in pack encoding format directly into the output stream,
* without incurring decompression and recompression overheads.
*/
public interface ObjectReuseAsIs {
/**
* Allocate a new {@code PackWriter} state structure for an object.
* <p>
* {@link PackWriter} allocates these objects to keep track of the
* per-object state, and how to load the objects efficiently into the
* generated stream. Implementers may subclass this type with additional
* object state, such as to remember what file and offset contains the
* object's pack encoded data.
*
* @param obj
* identity of the object that will be packed. The object's
* parsed status is undefined here. Implementers must not rely on
* the object being parsed.
* @return a new instance for this object.
*/
public ObjectToPack newObjectToPack(RevObject obj);
/**
* Select the best object representation for a packer.
* <p>
* Implementations should iterate through all available representations of
* an object, and pass them in turn to the PackWriter though
* {@link PackWriter#select(ObjectToPack, StoredObjectRepresentation)} so
* the writer can select the most suitable representation to reuse into the
* output stream.
*
* @param packer
* the packer that will write the object in the near future.
* @param otp
* the object to pack.
* @throws MissingObjectException
* there is no representation available for the object, as it is
* no longer in the repository. Packing will abort.
* @throws IOException
* the repository cannot be accessed. Packing will abort.
*/
public void selectObjectRepresentation(PackWriter packer, ObjectToPack otp)
throws IOException, MissingObjectException;
}

View File

@ -57,6 +57,8 @@
public class ObjectToPack extends PackedObjectInfo {
private static final int WANT_WRITE = 1 << 0;
private static final int REUSE_AS_IS = 1 << 1;
private static final int TYPE_SHIFT = 5;
private static final int DELTA_SHIFT = 8;
@ -70,7 +72,8 @@ public class ObjectToPack extends PackedObjectInfo {
* Bit field, from bit 0 to bit 31:
* <ul>
* <li>1 bit: wantWrite</li>
* <li>4 bits: unused</li>
* <li>1 bit: canReuseAsIs</li>
* <li>3 bits: unused</li>
* <li>3 bits: type</li>
* <li>--</li>
* <li>24 bits: deltaDepth</li>
@ -186,4 +189,49 @@ boolean wantWrite() {
void markWantWrite() {
flags |= WANT_WRITE;
}
boolean isReuseAsIs() {
return (flags & REUSE_AS_IS) != 0;
}
void setReuseAsIs() {
flags |= REUSE_AS_IS;
}
void clearReuseAsIs() {
flags &= ~REUSE_AS_IS;
}
int getFormat() {
if (isReuseAsIs()) {
if (isDeltaRepresentation())
return StoredObjectRepresentation.PACK_DELTA;
return StoredObjectRepresentation.PACK_WHOLE;
}
return StoredObjectRepresentation.FORMAT_OTHER;
}
// Overload weight into CRC since we don't need them at the same time.
int getWeight() {
return getCRC();
}
void setWeight(int weight) {
setCRC(weight);
}
/**
* Remember a specific representation for reuse at a later time.
* <p>
* Implementers should remember the representation chosen, so it can be
* reused at a later time. {@link PackWriter} may invoke this method
* multiple times for the same object, each time saving the current best
* representation found.
*
* @param ref
* the object representation.
*/
public void select(StoredObjectRepresentation ref) {
// Empty by default.
}
}

View File

@ -44,6 +44,9 @@
package org.eclipse.jgit.lib;
import static org.eclipse.jgit.lib.StoredObjectRepresentation.PACK_DELTA;
import static org.eclipse.jgit.lib.StoredObjectRepresentation.PACK_WHOLE;
import java.io.IOException;
import java.io.OutputStream;
import java.security.MessageDigest;
@ -605,82 +608,18 @@ public void writePack(OutputStream packStream) throws IOException {
private void searchForReuse() throws IOException {
initMonitor.beginTask(SEARCHING_REUSE_PROGRESS, getObjectsNumber());
final Collection<PackedObjectLoader> reuseLoaders = new ArrayList<PackedObjectLoader>();
for (List<LocalObjectToPack> list : objectsLists) {
for (LocalObjectToPack otp : list) {
for (ObjectToPack otp : list) {
if (initMonitor.isCancelled())
throw new IOException(
JGitText.get().packingCancelledDuringObjectsWriting);
reuseLoaders.clear();
searchForReuse(reuseLoaders, otp);
windowCursor.selectObjectRepresentation(this, otp);
initMonitor.update(1);
}
}
initMonitor.endTask();
}
private void searchForReuse(
final Collection<PackedObjectLoader> reuseLoaders,
final LocalObjectToPack otp) throws IOException {
windowCursor.openObjectInAllPacks(otp, reuseLoaders);
if (reuseDeltas) {
selectDeltaReuseForObject(otp, reuseLoaders);
}
// delta reuse is preferred over object reuse
if (reuseObjects && !otp.isCopyable()) {
selectObjectReuseForObject(otp, reuseLoaders);
}
}
private void selectDeltaReuseForObject(final LocalObjectToPack otp,
final Collection<PackedObjectLoader> loaders) throws IOException {
PackedObjectLoader bestLoader = null;
ObjectId bestBase = null;
for (PackedObjectLoader loader : loaders) {
ObjectId idBase = loader.getDeltaBase();
if (idBase == null)
continue;
ObjectToPack otpBase = objectsMap.get(idBase);
// only if base is in set of objects to write or thin-pack's edge
if ((otpBase != null || (thin && edgeObjects.get(idBase) != null))
// select smallest possible delta if > 1 available
&& isBetterDeltaReuseLoader(bestLoader, loader)) {
bestLoader = loader;
bestBase = (otpBase != null ? otpBase : idBase);
}
}
if (bestLoader != null) {
otp.setCopyFromPack(bestLoader);
otp.setDeltaBase(bestBase);
}
}
private static boolean isBetterDeltaReuseLoader(
PackedObjectLoader currentLoader, PackedObjectLoader loader)
throws IOException {
if (currentLoader == null)
return true;
if (loader.getRawSize() < currentLoader.getRawSize())
return true;
return (loader.getRawSize() == currentLoader.getRawSize()
&& loader.supportsFastCopyRawData() && !currentLoader
.supportsFastCopyRawData());
}
private void selectObjectReuseForObject(final LocalObjectToPack otp,
final Collection<PackedObjectLoader> loaders) {
for (final PackedObjectLoader loader : loaders) {
if (loader instanceof WholePackedObjectLoader) {
otp.setCopyFromPack(loader);
return;
}
}
}
private void writeHeader() throws IOException {
System.arraycopy(Constants.PACK_SIGNATURE, 0, buf, 0, 4);
NB.encodeInt32(buf, 4, PACK_VERSION_GENERATED);
@ -708,7 +647,7 @@ private void writeObject(final LocalObjectToPack otp) throws IOException {
if (deltaBase != null && !deltaBase.isWritten()) {
if (deltaBase.wantWrite()) {
otp.clearDeltaBase(); // cycle detected
otp.clearSourcePack();
otp.clearReuseAsIs();
} else {
writeObject(deltaBase);
}
@ -742,7 +681,7 @@ private void writeObject(final LocalObjectToPack otp) throws IOException {
}
private PackedObjectLoader open(final LocalObjectToPack otp) throws IOException {
while (otp.isCopyable()) {
while (otp.isReuseAsIs()) {
try {
PackedObjectLoader reuse = otp.getCopyLoader(windowCursor);
reuse.beginCopyRawData();
@ -752,8 +691,8 @@ private PackedObjectLoader open(final LocalObjectToPack otp) throws IOException
// it has been overwritten with a different layout.
//
otp.clearDeltaBase();
otp.clearSourcePack();
searchForReuse(new ArrayList<PackedObjectLoader>(), otp);
otp.clearReuseAsIs();
windowCursor.selectObjectRepresentation(this, otp);
continue;
}
}
@ -898,4 +837,61 @@ public void addObject(final RevObject object)
}
objectsMap.add(otp);
}
/**
* Select an object representation for this writer.
* <p>
* An {@link ObjectReader} implementation should invoke this method once for
* each representation available for an object, to allow the writer to find
* the most suitable one for the output.
*
* @param otp
* the object being packed.
* @param next
* the next available representation from the repository.
*/
public void select(ObjectToPack otp, StoredObjectRepresentation next) {
int nFmt = next.getFormat();
int nWeight;
if (otp.isReuseAsIs()) {
// We've already chosen to reuse a packed form, if next
// cannot beat that break out early.
//
if (PACK_WHOLE < nFmt)
return; // next isn't packed
else if (PACK_DELTA < nFmt && otp.isDeltaRepresentation())
return; // next isn't a delta, but we are
nWeight = next.getWeight();
if (otp.getWeight() <= nWeight)
return; // next would be bigger
} else
nWeight = next.getWeight();
if (nFmt == PACK_DELTA && reuseDeltas) {
ObjectId baseId = next.getDeltaBase();
ObjectToPack ptr = objectsMap.get(baseId);
if (ptr != null) {
otp.setDeltaBase(ptr);
otp.setReuseAsIs();
otp.setWeight(nWeight);
} else if (thin && edgeObjects.contains(baseId)) {
otp.setDeltaBase(baseId);
otp.setReuseAsIs();
otp.setWeight(nWeight);
} else {
otp.clearDeltaBase();
otp.clearReuseAsIs();
}
} else if (nFmt == PACK_WHOLE && reuseObjects) {
otp.clearDeltaBase();
otp.setReuseAsIs();
otp.setWeight(nWeight);
} else {
otp.clearDeltaBase();
otp.clearReuseAsIs();
}
otp.select(next);
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2010, Google Inc.
* 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.lib;
/**
* An object representation {@link PackWriter} can consider for packing.
*/
public class StoredObjectRepresentation {
/** Special unknown value for {@link #getWeight()}. */
public static final int WEIGHT_UNKNOWN = Integer.MAX_VALUE;
/** Stored in pack format, as a delta to another object. */
public static final int PACK_DELTA = 0;
/** Stored in pack format, without delta. */
public static final int PACK_WHOLE = 1;
/** Only available after inflating to canonical format. */
public static final int FORMAT_OTHER = 2;
/**
* @return relative size of this object's packed form. The special value
* {@link #WEIGHT_UNKNOWN} can be returned to indicate the
* implementation doesn't know, or cannot supply the weight up
* front.
*/
public int getWeight() {
return WEIGHT_UNKNOWN;
}
/**
* @return true if this is a delta against another object and this is stored
* in pack delta format.
*/
public int getFormat() {
return FORMAT_OTHER;
}
/**
* @return identity of the object this delta applies to in order to recover
* the original object content. This method should only be called if
* {@link #getFormat()} returned {@link #PACK_DELTA}.
*/
public ObjectId getDeltaBase() {
return null;
}
}

View File

@ -45,7 +45,6 @@
package org.eclipse.jgit.lib;
import java.io.IOException;
import java.util.Collection;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@ -53,7 +52,7 @@
import org.eclipse.jgit.revwalk.RevObject;
/** Active handle to a ByteWindow. */
final class WindowCursor extends ObjectReader {
final class WindowCursor extends ObjectReader implements ObjectReuseAsIs {
/** Temporary buffer large enough for at least one raw object id. */
final byte[] tempId = new byte[Constants.OBJECT_ID_LENGTH];
@ -82,14 +81,13 @@ public ObjectLoader openObject(AnyObjectId objectId, int typeHint)
return ldr;
}
@Override
public LocalObjectToPack newObjectToPack(RevObject obj) {
return new LocalObjectToPack(obj);
}
void openObjectInAllPacks(AnyObjectId otp,
Collection<PackedObjectLoader> reuseLoaders) throws IOException {
db.openObjectInAllPacks(reuseLoaders, this, otp);
public void selectObjectRepresentation(PackWriter packer, ObjectToPack otp)
throws IOException, MissingObjectException {
db.selectObjectRepresentation(packer, otp, this);
}
/**
@ -108,8 +106,8 @@ void openObjectInAllPacks(AnyObjectId otp,
* bytes remaining in the window starting at offset
* <code>pos</code>.
* @return number of bytes actually copied; this may be less than
* <code>cnt</code> if <code>cnt</code> exceeded the number of
* bytes available.
* <code>cnt</code> if <code>cnt</code> exceeded the number of bytes
* available.
* @throws IOException
* this cursor does not match the provider or id and the proper
* window could not be acquired through the provider's cache.
@ -161,8 +159,8 @@ int inflate(final PackFile pack, long position, final byte[] dstbuf,
}
}
void inflateVerify(final PackFile pack, long position)
throws IOException, DataFormatException {
void inflateVerify(final PackFile pack, long position) throws IOException,
DataFormatException {
prepareInflater();
for (;;) {
pin(pack, position);