/* * Copyright (C) 2008-2010, Google Inc. * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> * 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.internal.storage.pack; import org.eclipse.jgit.lib.AnyObjectId; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.transport.PackedObjectInfo; /** * 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. */ public class ObjectToPack extends PackedObjectInfo { private static final int REUSE_AS_IS = 1 << 0; private static final int DELTA_ATTEMPTED = 1 << 1; private static final int DO_NOT_DELTA = 1 << 2; private static final int EDGE = 1 << 3; private static final int ATTEMPT_DELTA_MASK = REUSE_AS_IS | DELTA_ATTEMPTED; private static final int TYPE_SHIFT = 5; private static final int EXT_SHIFT = 8; private static final int EXT_MASK = 0xf; private static final int DELTA_SHIFT = 12; private static final int NON_EXT_MASK = ~(EXT_MASK << EXT_SHIFT); private static final int NON_DELTA_MASK = 0xfff; /** Other object being packed that this will delta against. */ private ObjectId deltaBase; /** * Bit field, from bit 0 to bit 31: * <ul> * <li>1 bit: canReuseAsIs</li> * <li>1 bit: deltaAttempted</li> * <li>1 bit: doNotDelta</li> * <li>1 bit: edgeObject</li> * <li>1 bit: unused</li> * <li>3 bits: type</li> * <li>4 bits: subclass flags (if any)</li> * <li>--</li> * <li>20 bits: deltaDepth</li> * </ul> */ private int flags; /** Hash of the object's tree path. */ private int pathHash; /** If present, deflated delta instruction stream for this object. */ private DeltaCache.Ref cachedDelta; /** * 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. */ public ObjectToPack(AnyObjectId src, final int type) { super(src); flags = type << TYPE_SHIFT; } /** * @return delta base object id if object is going to be packed in delta * representation; null otherwise - if going to be packed as a * whole object. */ public final ObjectId getDeltaBaseId() { return deltaBase; } /** * @return delta base object to pack if object is going to be packed in * delta representation and delta is specified as object to * pack; null otherwise - if going to be packed as a whole * object or delta base is specified only as id. */ public final ObjectToPack getDeltaBase() { if (deltaBase instanceof ObjectToPack) return (ObjectToPack) deltaBase; return null; } /** * Set delta base for the object. Delta base set by this method is used * by {@link PackWriter} to write object - determines its representation * in a created pack. * * @param deltaBase * delta base object or null if object should be packed as a * whole object. * */ final void setDeltaBase(ObjectId deltaBase) { this.deltaBase = deltaBase; } final void setCachedDelta(DeltaCache.Ref data) { cachedDelta = data; } final DeltaCache.Ref popCachedDelta() { DeltaCache.Ref r = cachedDelta; if (r != null) cachedDelta = null; return r; } final void clearDeltaBase() { this.deltaBase = null; if (cachedDelta != null) { cachedDelta.clear(); cachedDelta.enqueue(); cachedDelta = null; } } /** * @return true if object is going to be written as delta; false * otherwise. */ public final boolean isDeltaRepresentation() { return deltaBase != null; } /** * Check if object is already written in a pack. This information is * used to achieve delta-base precedence in a pack file. * * @return true if object is already written; false otherwise. */ public final boolean isWritten() { return 1 < getOffset(); // markWantWrite sets 1. } /** @return the type of this object. */ public final int getType() { return (flags >> TYPE_SHIFT) & 0x7; } final int getDeltaDepth() { return flags >>> DELTA_SHIFT; } final void setDeltaDepth(int d) { flags = (d << DELTA_SHIFT) | (flags & NON_DELTA_MASK); } final int getChainLength() { return getDeltaDepth(); } final void setChainLength(int len) { setDeltaDepth(len); } final void clearChainLength() { flags &= NON_DELTA_MASK; } final boolean wantWrite() { return getOffset() == 1; } final void markWantWrite() { setOffset(1); } /** * @return true if an existing representation was selected to be reused * as-is into the pack stream. */ public final boolean isReuseAsIs() { return (flags & REUSE_AS_IS) != 0; } final void setReuseAsIs() { flags |= REUSE_AS_IS; } /** * Forget the reuse information previously stored. * <p> * Implementations may subclass this method, but they must also invoke the * super version with {@code super.clearReuseAsIs()} to ensure the flag is * properly cleared for the writer. */ protected void clearReuseAsIs() { flags &= ~REUSE_AS_IS; } final boolean isDoNotDelta() { return (flags & DO_NOT_DELTA) != 0; } final void setDoNotDelta() { flags |= DO_NOT_DELTA; } final boolean isEdge() { return (flags & EDGE) != 0; } final void setEdge() { flags |= EDGE; } final boolean doNotAttemptDelta() { // Do not attempt if delta attempted and object reuse. return (flags & ATTEMPT_DELTA_MASK) == ATTEMPT_DELTA_MASK; } final void setDeltaAttempted(boolean deltaAttempted) { if (deltaAttempted) flags |= DELTA_ATTEMPTED; else flags &= ~DELTA_ATTEMPTED; } /** @return the extended flags on this object, in the range [0x0, 0xf]. */ protected final int getExtendedFlags() { return (flags >>> EXT_SHIFT) & EXT_MASK; } /** * Determine if a particular extended flag bit has been set. * * This implementation may be faster than calling * {@link #getExtendedFlags()} and testing the result. * * @param flag * the flag mask to test, must be between 0x0 and 0xf. * @return true if any of the bits matching the mask are non-zero. */ protected final boolean isExtendedFlag(int flag) { return (flags & (flag << EXT_SHIFT)) != 0; } /** * Set an extended flag bit. * * This implementation is more efficient than getting the extended flags, * adding the bit, and setting them all back. * * @param flag * the bits to set, must be between 0x0 and 0xf. */ protected final void setExtendedFlag(int flag) { flags |= (flag & EXT_MASK) << EXT_SHIFT; } /** * Clear an extended flag bit. * * This implementation is more efficient than getting the extended flags, * removing the bit, and setting them all back. * * @param flag * the bits to clear, must be between 0x0 and 0xf. */ protected final void clearExtendedFlag(int flag) { flags &= ~((flag & EXT_MASK) << EXT_SHIFT); } /** * Set the extended flags used by the subclass. * * Subclass implementations may store up to 4 bits of information inside of * the internal flags field already used by the base ObjectToPack instance. * * @param extFlags * additional flag bits to store in the flags field. Due to space * constraints only values [0x0, 0xf] are permitted. */ protected final void setExtendedFlags(int extFlags) { flags = ((extFlags & EXT_MASK) << EXT_SHIFT) | (flags & NON_EXT_MASK); } final 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. final int getWeight() { return getCRC(); } final void setWeight(int weight) { setCRC(weight); } final int getPathHash() { return pathHash; } final void setPathHash(int hc) { pathHash = hc; } final int getCachedSize() { return pathHash; } final void setCachedSize(int sz) { pathHash = sz; } /** * 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. } @SuppressWarnings("nls") @Override public String toString() { StringBuilder buf = new StringBuilder(); buf.append("ObjectToPack["); buf.append(Constants.typeString(getType())); buf.append(" "); buf.append(name()); if (wantWrite()) buf.append(" wantWrite"); if (isReuseAsIs()) buf.append(" reuseAsIs"); if (isDoNotDelta()) buf.append(" doNotDelta"); if (isEdge()) buf.append(" edge"); if (getDeltaDepth() > 0) buf.append(" depth=").append(getDeltaDepth()); if (isDeltaRepresentation()) { if (getDeltaBase() != null) buf.append(" base=inpack:").append(getDeltaBase().name()); else buf.append(" base=edge:").append(getDeltaBaseId().name()); } if (isWritten()) buf.append(" offset=").append(getOffset()); buf.append("]"); return buf.toString(); } }