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:
parent
e0c9368f3e
commit
bf4ffff07f
|
@ -175,4 +175,10 @@ ObjectLoader openObject2(WindowCursor curs, String objectName,
|
||||||
// This method should never be invoked.
|
// This method should never be invoked.
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
|
||||||
|
WindowCursor curs) throws IOException {
|
||||||
|
wrapped.selectObjectRepresentation(packer, otp, curs);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,6 @@
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
abstract class FileObjectDatabase extends ObjectDatabase {
|
abstract class FileObjectDatabase extends ObjectDatabase {
|
||||||
@Override
|
@Override
|
||||||
|
@ -160,9 +159,8 @@ final ObjectLoader openObjectImpl2(final WindowCursor curs,
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void openObjectInAllPacks(Collection<PackedObjectLoader> reuseLoaders,
|
abstract void selectObjectRepresentation(PackWriter packer,
|
||||||
WindowCursor windowCursor, AnyObjectId otp) throws IOException {
|
ObjectToPack otp, WindowCursor curs) throws IOException;
|
||||||
}
|
|
||||||
|
|
||||||
abstract File getDirectory();
|
abstract File getDirectory();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,20 +59,14 @@ class LocalObjectToPack extends ObjectToPack {
|
||||||
super(obj);
|
super(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isCopyable() {
|
|
||||||
return copyFromPack != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
PackedObjectLoader getCopyLoader(WindowCursor curs) throws IOException {
|
PackedObjectLoader getCopyLoader(WindowCursor curs) throws IOException {
|
||||||
return copyFromPack.resolveBase(curs, copyOffset);
|
return copyFromPack.resolveBase(curs, copyOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCopyFromPack(PackedObjectLoader loader) {
|
@Override
|
||||||
this.copyFromPack = loader.pack;
|
public void select(StoredObjectRepresentation ref) {
|
||||||
this.copyOffset = loader.objectOffset;
|
LocalObjectRepresentation ptr = (LocalObjectRepresentation)ref;
|
||||||
}
|
this.copyFromPack = ptr.ldr.pack;
|
||||||
|
this.copyOffset = ptr.ldr.objectOffset;
|
||||||
void clearSourcePack() {
|
|
||||||
copyFromPack = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -283,17 +283,16 @@ ObjectLoader openObject1(final WindowCursor curs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void openObjectInAllPacks(final Collection<PackedObjectLoader> out,
|
@Override
|
||||||
final WindowCursor curs, final AnyObjectId objectId)
|
void selectObjectRepresentation(PackWriter packer, ObjectToPack otp,
|
||||||
throws IOException {
|
WindowCursor curs) throws IOException {
|
||||||
PackList pList = packList.get();
|
PackList pList = packList.get();
|
||||||
SEARCH: for (;;) {
|
SEARCH: for (;;) {
|
||||||
for (final PackFile p : pList.packs) {
|
for (final PackFile p : pList.packs) {
|
||||||
try {
|
try {
|
||||||
final PackedObjectLoader ldr = p.get(curs, objectId);
|
PackedObjectLoader ldr = p.get(curs, otp);
|
||||||
if (ldr != null) {
|
if (ldr != null)
|
||||||
out.add(ldr);
|
packer.select(otp, new LocalObjectRepresentation(ldr));
|
||||||
}
|
|
||||||
} catch (PackMismatchException e) {
|
} catch (PackMismatchException e) {
|
||||||
// Pack was modified; refresh the entire pack list.
|
// Pack was modified; refresh the entire pack list.
|
||||||
//
|
//
|
||||||
|
@ -309,7 +308,7 @@ void openObjectInAllPacks(final Collection<PackedObjectLoader> out,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (AlternateHandle h : myAlternates())
|
for (AlternateHandle h : myAlternates())
|
||||||
h.db.openObjectInAllPacks(out, curs, objectId);
|
h.db.selectObjectRepresentation(packer, otp, curs);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasObject2(final String objectName) {
|
boolean hasObject2(final String objectName) {
|
||||||
|
|
|
@ -46,9 +46,13 @@
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
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 {
|
public abstract class ObjectReader {
|
||||||
/** Type hint indicating the caller doesn't know the type. */
|
/** Type hint indicating the caller doesn't know the type. */
|
||||||
protected static final int OBJ_ANY = -1;
|
protected static final int OBJ_ANY = -1;
|
||||||
|
@ -103,29 +107,6 @@ public ObjectLoader openObject(AnyObjectId objectId)
|
||||||
public abstract ObjectLoader openObject(AnyObjectId objectId, int typeHint)
|
public abstract ObjectLoader openObject(AnyObjectId objectId, int typeHint)
|
||||||
throws MissingObjectException, IOException;
|
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.
|
* Release any resources used by this reader.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -57,6 +57,8 @@
|
||||||
public class ObjectToPack extends PackedObjectInfo {
|
public class ObjectToPack extends PackedObjectInfo {
|
||||||
private static final int WANT_WRITE = 1 << 0;
|
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 TYPE_SHIFT = 5;
|
||||||
|
|
||||||
private static final int DELTA_SHIFT = 8;
|
private static final int DELTA_SHIFT = 8;
|
||||||
|
@ -70,7 +72,8 @@ public class ObjectToPack extends PackedObjectInfo {
|
||||||
* Bit field, from bit 0 to bit 31:
|
* Bit field, from bit 0 to bit 31:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>1 bit: wantWrite</li>
|
* <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>3 bits: type</li>
|
||||||
* <li>--</li>
|
* <li>--</li>
|
||||||
* <li>24 bits: deltaDepth</li>
|
* <li>24 bits: deltaDepth</li>
|
||||||
|
@ -186,4 +189,49 @@ boolean wantWrite() {
|
||||||
void markWantWrite() {
|
void markWantWrite() {
|
||||||
flags |= WANT_WRITE;
|
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.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,9 @@
|
||||||
|
|
||||||
package org.eclipse.jgit.lib;
|
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.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
|
@ -605,82 +608,18 @@ public void writePack(OutputStream packStream) throws IOException {
|
||||||
|
|
||||||
private void searchForReuse() throws IOException {
|
private void searchForReuse() throws IOException {
|
||||||
initMonitor.beginTask(SEARCHING_REUSE_PROGRESS, getObjectsNumber());
|
initMonitor.beginTask(SEARCHING_REUSE_PROGRESS, getObjectsNumber());
|
||||||
final Collection<PackedObjectLoader> reuseLoaders = new ArrayList<PackedObjectLoader>();
|
|
||||||
for (List<LocalObjectToPack> list : objectsLists) {
|
for (List<LocalObjectToPack> list : objectsLists) {
|
||||||
for (LocalObjectToPack otp : list) {
|
for (ObjectToPack otp : list) {
|
||||||
if (initMonitor.isCancelled())
|
if (initMonitor.isCancelled())
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
JGitText.get().packingCancelledDuringObjectsWriting);
|
JGitText.get().packingCancelledDuringObjectsWriting);
|
||||||
reuseLoaders.clear();
|
windowCursor.selectObjectRepresentation(this, otp);
|
||||||
searchForReuse(reuseLoaders, otp);
|
|
||||||
initMonitor.update(1);
|
initMonitor.update(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
initMonitor.endTask();
|
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 {
|
private void writeHeader() throws IOException {
|
||||||
System.arraycopy(Constants.PACK_SIGNATURE, 0, buf, 0, 4);
|
System.arraycopy(Constants.PACK_SIGNATURE, 0, buf, 0, 4);
|
||||||
NB.encodeInt32(buf, 4, PACK_VERSION_GENERATED);
|
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 != null && !deltaBase.isWritten()) {
|
||||||
if (deltaBase.wantWrite()) {
|
if (deltaBase.wantWrite()) {
|
||||||
otp.clearDeltaBase(); // cycle detected
|
otp.clearDeltaBase(); // cycle detected
|
||||||
otp.clearSourcePack();
|
otp.clearReuseAsIs();
|
||||||
} else {
|
} else {
|
||||||
writeObject(deltaBase);
|
writeObject(deltaBase);
|
||||||
}
|
}
|
||||||
|
@ -742,7 +681,7 @@ private void writeObject(final LocalObjectToPack otp) throws IOException {
|
||||||
}
|
}
|
||||||
|
|
||||||
private PackedObjectLoader open(final LocalObjectToPack otp) throws IOException {
|
private PackedObjectLoader open(final LocalObjectToPack otp) throws IOException {
|
||||||
while (otp.isCopyable()) {
|
while (otp.isReuseAsIs()) {
|
||||||
try {
|
try {
|
||||||
PackedObjectLoader reuse = otp.getCopyLoader(windowCursor);
|
PackedObjectLoader reuse = otp.getCopyLoader(windowCursor);
|
||||||
reuse.beginCopyRawData();
|
reuse.beginCopyRawData();
|
||||||
|
@ -752,8 +691,8 @@ private PackedObjectLoader open(final LocalObjectToPack otp) throws IOException
|
||||||
// it has been overwritten with a different layout.
|
// it has been overwritten with a different layout.
|
||||||
//
|
//
|
||||||
otp.clearDeltaBase();
|
otp.clearDeltaBase();
|
||||||
otp.clearSourcePack();
|
otp.clearReuseAsIs();
|
||||||
searchForReuse(new ArrayList<PackedObjectLoader>(), otp);
|
windowCursor.selectObjectRepresentation(this, otp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -898,4 +837,61 @@ public void addObject(final RevObject object)
|
||||||
}
|
}
|
||||||
objectsMap.add(otp);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,7 +45,6 @@
|
||||||
package org.eclipse.jgit.lib;
|
package org.eclipse.jgit.lib;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.zip.DataFormatException;
|
import java.util.zip.DataFormatException;
|
||||||
import java.util.zip.Inflater;
|
import java.util.zip.Inflater;
|
||||||
|
|
||||||
|
@ -53,7 +52,7 @@
|
||||||
import org.eclipse.jgit.revwalk.RevObject;
|
import org.eclipse.jgit.revwalk.RevObject;
|
||||||
|
|
||||||
/** Active handle to a ByteWindow. */
|
/** 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. */
|
/** Temporary buffer large enough for at least one raw object id. */
|
||||||
final byte[] tempId = new byte[Constants.OBJECT_ID_LENGTH];
|
final byte[] tempId = new byte[Constants.OBJECT_ID_LENGTH];
|
||||||
|
|
||||||
|
@ -82,14 +81,13 @@ public ObjectLoader openObject(AnyObjectId objectId, int typeHint)
|
||||||
return ldr;
|
return ldr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public LocalObjectToPack newObjectToPack(RevObject obj) {
|
public LocalObjectToPack newObjectToPack(RevObject obj) {
|
||||||
return new LocalObjectToPack(obj);
|
return new LocalObjectToPack(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
void openObjectInAllPacks(AnyObjectId otp,
|
public void selectObjectRepresentation(PackWriter packer, ObjectToPack otp)
|
||||||
Collection<PackedObjectLoader> reuseLoaders) throws IOException {
|
throws IOException, MissingObjectException {
|
||||||
db.openObjectInAllPacks(reuseLoaders, this, otp);
|
db.selectObjectRepresentation(packer, otp, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,8 +106,8 @@ void openObjectInAllPacks(AnyObjectId otp,
|
||||||
* bytes remaining in the window starting at offset
|
* bytes remaining in the window starting at offset
|
||||||
* <code>pos</code>.
|
* <code>pos</code>.
|
||||||
* @return number of bytes actually copied; this may be less than
|
* @return number of bytes actually copied; this may be less than
|
||||||
* <code>cnt</code> if <code>cnt</code> exceeded the number of
|
* <code>cnt</code> if <code>cnt</code> exceeded the number of bytes
|
||||||
* bytes available.
|
* available.
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* this cursor does not match the provider or id and the proper
|
* this cursor does not match the provider or id and the proper
|
||||||
* window could not be acquired through the provider's cache.
|
* 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)
|
void inflateVerify(final PackFile pack, long position) throws IOException,
|
||||||
throws IOException, DataFormatException {
|
DataFormatException {
|
||||||
prepareInflater();
|
prepareInflater();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
pin(pack, position);
|
pin(pack, position);
|
||||||
|
|
Loading…
Reference in New Issue