Browse Source

Extract PackFile specific code to ObjectToPack subclass

The ObjectReader class is dual-purposed into being a factory for the
ObjectToPack, permitting specific ObjectDatabase implementations
to override the method and offer their own custom subclass of the
generic ObjectToPack class.  By allowing them to directly extend the
type, each implementation can add custom fields to support tracking
where an object is stored, without incurring any additional penalties
like a parallel Map<ObjectId,Object> would cost.

The reader was chosen to act as a factory rather than the database,
as the reader will eventually be tied more tightly with the
ObjectWalk and TreeWalk.  During object enumeration the reader
would have had to load the object for the RevWalk, and may chose
to cache object position data internally so it can later be reused
and fed into the ObjectToPack instance supplied to the PackWriter.
Since a reader is not thread-safe, and is scoped to this PackWriter
and its internal ObjectWalk, its a great place for the database to
perform caching, if any.

Right now this change goes a bit backwards by changing what should
be generic ObjectToPack references inside of PackWriter to the very
PackFile specific LocalObjectToPack subclass.  We will correct these
in a later commit as we start to refine what the ObjectToPack API
will eventually look like in order to better support the PackWriter.

Change-Id: I9f047d26b97e46dee3bc0ccb4060bbebedbe8ea9
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
tags/v0.9.1
Shawn O. Pearce 14 years ago
parent
commit
6fc3ecac84

+ 78
- 0
org.eclipse.jgit/src/org/eclipse/jgit/lib/LocalObjectToPack.java View File

@@ -0,0 +1,78 @@
/*
* 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.revwalk.RevObject;

/** {@link ObjectToPack} for {@link ObjectDirectory}. */
class LocalObjectToPack extends ObjectToPack {
/** Pack to reuse compressed data from, otherwise null. */
private PackFile copyFromPack;

/** Offset of the object's header in {@link #copyFromPack}. */
private long copyOffset;

LocalObjectToPack(RevObject obj) {
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;
}
}

+ 24
- 0
org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectReader.java View File

@@ -46,6 +46,7 @@ package org.eclipse.jgit.lib;
import java.io.IOException;

import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.revwalk.RevObject;

/** Reads an {@link ObjectDatabase} for a single thread. */
public abstract class ObjectReader {
@@ -102,6 +103,29 @@ public abstract class ObjectReader {
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>

+ 21
- 33
org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectToPack.java View File

@@ -44,26 +44,20 @@

package org.eclipse.jgit.lib;

import java.io.IOException;

import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.transport.PackedObjectInfo;

/**
* Class holding information about object that is going to be packed by
* {@link PackWriter}. Information include object representation in a
* pack-file and object status.
*
* Per-object state used by {@link PackWriter}.
* <p>
* {@code PackWriter} uses this class to track the things it needs to include in
* the newly generated pack file, and how to efficiently obtain the raw data for
* each object as they are written to the output stream.
*/
class ObjectToPack extends PackedObjectInfo {
public class ObjectToPack extends PackedObjectInfo {
/** Other object being packed that this will delta against. */
private ObjectId deltaBase;

/** Pack to reuse compressed data from, otherwise null. */
private PackFile copyFromPack;

/** Offset of the object's header in {@link #copyFromPack}. */
private long copyOffset;

/**
* Bit field, from bit 0 to bit 31:
* <ul>
@@ -75,19 +69,30 @@ class ObjectToPack extends PackedObjectInfo {
private int flags;

/**
* Construct object for specified object id. <br/> By default object is
* marked as not written and non-delta packed (as a whole object).
* Construct for the specified object id.
*
* @param src
* object id of object for packing
* @param type
* real type code of the object, not its in-pack type.
*/
ObjectToPack(AnyObjectId src, final int type) {
public ObjectToPack(AnyObjectId src, final int type) {
super(src);
flags |= type << 1;
}

/**
* Construct for the specified object.
*
* @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.
*/
public ObjectToPack(RevObject obj) {
this(obj, obj.getType());
}

/**
* @return delta base object id if object is going to be packed in delta
* representation; null otherwise - if going to be packed as a
@@ -145,23 +150,6 @@ class ObjectToPack extends PackedObjectInfo {
return getOffset() != 0;
}

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;
}

int getType() {
return (flags>>1) & 0x7;
}

+ 19
- 19
org.eclipse.jgit/src/org/eclipse/jgit/lib/PackWriter.java View File

@@ -152,13 +152,13 @@ public class PackWriter {
private static final int PACK_VERSION_GENERATED = 2;

@SuppressWarnings("unchecked")
private final List<ObjectToPack> objectsLists[] = new List[Constants.OBJ_TAG + 1];
private final List<LocalObjectToPack> objectsLists[] = new List[Constants.OBJ_TAG + 1];
{
objectsLists[0] = Collections.<ObjectToPack> emptyList();
objectsLists[Constants.OBJ_COMMIT] = new ArrayList<ObjectToPack>();
objectsLists[Constants.OBJ_TREE] = new ArrayList<ObjectToPack>();
objectsLists[Constants.OBJ_BLOB] = new ArrayList<ObjectToPack>();
objectsLists[Constants.OBJ_TAG] = new ArrayList<ObjectToPack>();
objectsLists[0] = Collections.<LocalObjectToPack> emptyList();
objectsLists[Constants.OBJ_COMMIT] = new ArrayList<LocalObjectToPack>();
objectsLists[Constants.OBJ_TREE] = new ArrayList<LocalObjectToPack>();
objectsLists[Constants.OBJ_BLOB] = new ArrayList<LocalObjectToPack>();
objectsLists[Constants.OBJ_TAG] = new ArrayList<LocalObjectToPack>();
}

private final ObjectIdSubclassMap<ObjectToPack> objectsMap = new ObjectIdSubclassMap<ObjectToPack>();
@@ -557,8 +557,8 @@ public class PackWriter {
private List<ObjectToPack> sortByName() {
if (sortedByName == null) {
sortedByName = new ArrayList<ObjectToPack>(objectsMap.size());
for (List<ObjectToPack> list : objectsLists) {
for (ObjectToPack otp : list)
for (List<LocalObjectToPack> list : objectsLists) {
for (LocalObjectToPack otp : list)
sortedByName.add(otp);
}
Collections.sort(sortedByName);
@@ -606,8 +606,8 @@ public class PackWriter {
private void searchForReuse() throws IOException {
initMonitor.beginTask(SEARCHING_REUSE_PROGRESS, getObjectsNumber());
final Collection<PackedObjectLoader> reuseLoaders = new ArrayList<PackedObjectLoader>();
for (List<ObjectToPack> list : objectsLists) {
for (ObjectToPack otp : list) {
for (List<LocalObjectToPack> list : objectsLists) {
for (LocalObjectToPack otp : list) {
if (initMonitor.isCancelled())
throw new IOException(
JGitText.get().packingCancelledDuringObjectsWriting);
@@ -622,7 +622,7 @@ public class PackWriter {

private void searchForReuse(
final Collection<PackedObjectLoader> reuseLoaders,
final ObjectToPack otp) throws IOException {
final LocalObjectToPack otp) throws IOException {
windowCursor.openObjectInAllPacks(otp, reuseLoaders);
if (reuseDeltas) {
selectDeltaReuseForObject(otp, reuseLoaders);
@@ -633,7 +633,7 @@ public class PackWriter {
}
}

private void selectDeltaReuseForObject(final ObjectToPack otp,
private void selectDeltaReuseForObject(final LocalObjectToPack otp,
final Collection<PackedObjectLoader> loaders) throws IOException {
PackedObjectLoader bestLoader = null;
ObjectId bestBase = null;
@@ -671,7 +671,7 @@ public class PackWriter {
.supportsFastCopyRawData());
}

private void selectObjectReuseForObject(final ObjectToPack otp,
private void selectObjectReuseForObject(final LocalObjectToPack otp,
final Collection<PackedObjectLoader> loaders) {
for (final PackedObjectLoader loader : loaders) {
if (loader instanceof WholePackedObjectLoader) {
@@ -689,8 +689,8 @@ public class PackWriter {
}

private void writeObjects() throws IOException {
for (List<ObjectToPack> list : objectsLists) {
for (ObjectToPack otp : list) {
for (List<LocalObjectToPack> list : objectsLists) {
for (LocalObjectToPack otp : list) {
if (writeMonitor.isCancelled())
throw new IOException(
JGitText.get().packingCancelledDuringObjectsWriting);
@@ -700,10 +700,10 @@ public class PackWriter {
}
}

private void writeObject(final ObjectToPack otp) throws IOException {
private void writeObject(final LocalObjectToPack otp) throws IOException {
otp.markWantWrite();
if (otp.isDeltaRepresentation()) {
ObjectToPack deltaBase = otp.getDeltaBase();
LocalObjectToPack deltaBase = (LocalObjectToPack)otp.getDeltaBase();
assert deltaBase != null || thin;
if (deltaBase != null && !deltaBase.isWritten()) {
if (deltaBase.wantWrite()) {
@@ -741,7 +741,7 @@ public class PackWriter {
writeMonitor.update(1);
}

private PackedObjectLoader open(final ObjectToPack otp) throws IOException {
private PackedObjectLoader open(final LocalObjectToPack otp) throws IOException {
while (otp.isCopyable()) {
try {
PackedObjectLoader reuse = otp.getCopyLoader(windowCursor);
@@ -885,7 +885,7 @@ public class PackWriter {
return;
}

final ObjectToPack otp = new ObjectToPack(object, object.getType());
final LocalObjectToPack otp = windowCursor.newObjectToPack(object);
try {
objectsLists[object.getType()].add(otp);
} catch (ArrayIndexOutOfBoundsException x) {

+ 6
- 0
org.eclipse.jgit/src/org/eclipse/jgit/lib/WindowCursor.java View File

@@ -50,6 +50,7 @@ import java.util.zip.DataFormatException;
import java.util.zip.Inflater;

import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.revwalk.RevObject;

/** Active handle to a ByteWindow. */
final class WindowCursor extends ObjectReader {
@@ -81,6 +82,11 @@ final class WindowCursor extends ObjectReader {
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);

Loading…
Cancel
Save