View Javadoc
1   /*
2    * Copyright (C) 2008-2010, Google Inc.
3    * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
4    * and other copyright owners as documented in the project's IP log.
5    *
6    * This program and the accompanying materials are made available
7    * under the terms of the Eclipse Distribution License v1.0 which
8    * accompanies this distribution, is reproduced below, and is
9    * available at http://www.eclipse.org/org/documents/edl-v10.php
10   *
11   * All rights reserved.
12   *
13   * Redistribution and use in source and binary forms, with or
14   * without modification, are permitted provided that the following
15   * conditions are met:
16   *
17   * - Redistributions of source code must retain the above copyright
18   *   notice, this list of conditions and the following disclaimer.
19   *
20   * - Redistributions in binary form must reproduce the above
21   *   copyright notice, this list of conditions and the following
22   *   disclaimer in the documentation and/or other materials provided
23   *   with the distribution.
24   *
25   * - Neither the name of the Eclipse Foundation, Inc. nor the
26   *   names of its contributors may be used to endorse or promote
27   *   products derived from this software without specific prior
28   *   written permission.
29   *
30   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
31   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
32   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
39   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
42   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43   */
44  
45  package org.eclipse.jgit.internal.storage.pack;
46  
47  import org.eclipse.jgit.lib.AnyObjectId;
48  import org.eclipse.jgit.lib.Constants;
49  import org.eclipse.jgit.lib.ObjectId;
50  import org.eclipse.jgit.transport.PackedObjectInfo;
51  
52  /**
53   * Per-object state used by {@link PackWriter}.
54   * <p>
55   * {@code PackWriter} uses this class to track the things it needs to include in
56   * the newly generated pack file, and how to efficiently obtain the raw data for
57   * each object as they are written to the output stream.
58   */
59  public class ObjectToPack extends PackedObjectInfo {
60  	private static final int REUSE_AS_IS = 1 << 0;
61  	private static final int DELTA_ATTEMPTED = 1 << 1;
62  	private static final int DO_NOT_DELTA = 1 << 2;
63  	private static final int EDGE = 1 << 3;
64  	private static final int ATTEMPT_DELTA_MASK = REUSE_AS_IS | DELTA_ATTEMPTED;
65  	private static final int TYPE_SHIFT = 5;
66  	private static final int EXT_SHIFT = 8;
67  	private static final int EXT_MASK = 0xf;
68  	private static final int DELTA_SHIFT = 12;
69  	private static final int NON_EXT_MASK = ~(EXT_MASK << EXT_SHIFT);
70  	private static final int NON_DELTA_MASK = 0xfff;
71  
72  	/** Other object being packed that this will delta against. */
73  	private ObjectId deltaBase;
74  
75  	/**
76  	 * Bit field, from bit 0 to bit 31:
77  	 * <ul>
78  	 * <li>1 bit: canReuseAsIs</li>
79  	 * <li>1 bit: deltaAttempted</li>
80  	 * <li>1 bit: doNotDelta</li>
81  	 * <li>1 bit: edgeObject</li>
82  	 * <li>1 bit: unused</li>
83  	 * <li>3 bits: type</li>
84  	 * <li>4 bits: subclass flags (if any)</li>
85  	 * <li>--</li>
86  	 * <li>20 bits: deltaDepth</li>
87  	 * </ul>
88  	 */
89  	private int flags;
90  
91  	/** Hash of the object's tree path. */
92  	private int pathHash;
93  
94  	/** If present, deflated delta instruction stream for this object. */
95  	private DeltaCache.Ref cachedDelta;
96  
97  	/**
98  	 * Construct for the specified object id.
99  	 *
100 	 * @param src
101 	 *            object id of object for packing
102 	 * @param type
103 	 *            real type code of the object, not its in-pack type.
104 	 */
105 	public ObjectToPack(AnyObjectId src, final int type) {
106 		super(src);
107 		flags = type << TYPE_SHIFT;
108 	}
109 
110 	/**
111 	 * @return delta base object id if object is going to be packed in delta
112 	 *         representation; null otherwise - if going to be packed as a
113 	 *         whole object.
114 	 */
115 	public final ObjectId getDeltaBaseId() {
116 		return deltaBase;
117 	}
118 
119 	/**
120 	 * @return delta base object to pack if object is going to be packed in
121 	 *         delta representation and delta is specified as object to
122 	 *         pack; null otherwise - if going to be packed as a whole
123 	 *         object or delta base is specified only as id.
124 	 */
125 	public final ObjectToPack getDeltaBase() {
126 		if (deltaBase instanceof ObjectToPack)
127 			return (ObjectToPack) deltaBase;
128 		return null;
129 	}
130 
131 	/**
132 	 * Set delta base for the object. Delta base set by this method is used
133 	 * by {@link PackWriter} to write object - determines its representation
134 	 * in a created pack.
135 	 *
136 	 * @param deltaBase
137 	 *            delta base object or null if object should be packed as a
138 	 *            whole object.
139 	 *
140 	 */
141 	final void setDeltaBase(ObjectId deltaBase) {
142 		this.deltaBase = deltaBase;
143 	}
144 
145 	final void setCachedDelta(DeltaCache.Ref data) {
146 		cachedDelta = data;
147 	}
148 
149 	final DeltaCache.Ref popCachedDelta() {
150 		DeltaCache.Ref r = cachedDelta;
151 		if (r != null)
152 			cachedDelta = null;
153 		return r;
154 	}
155 
156 	final void clearDeltaBase() {
157 		this.deltaBase = null;
158 
159 		if (cachedDelta != null) {
160 			cachedDelta.clear();
161 			cachedDelta.enqueue();
162 			cachedDelta = null;
163 		}
164 	}
165 
166 	/**
167 	 * @return true if object is going to be written as delta; false
168 	 *         otherwise.
169 	 */
170 	public final boolean isDeltaRepresentation() {
171 		return deltaBase != null;
172 	}
173 
174 	/**
175 	 * Check if object is already written in a pack. This information is
176 	 * used to achieve delta-base precedence in a pack file.
177 	 *
178 	 * @return true if object is already written; false otherwise.
179 	 */
180 	public final boolean isWritten() {
181 		return 1 < getOffset(); // markWantWrite sets 1.
182 	}
183 
184 	/** @return the type of this object. */
185 	public final int getType() {
186 		return (flags >> TYPE_SHIFT) & 0x7;
187 	}
188 
189 	final int getDeltaDepth() {
190 		return flags >>> DELTA_SHIFT;
191 	}
192 
193 	final void setDeltaDepth(int d) {
194 		flags = (d << DELTA_SHIFT) | (flags & NON_DELTA_MASK);
195 	}
196 
197 	final int getChainLength() {
198 		return getDeltaDepth();
199 	}
200 
201 	final void setChainLength(int len) {
202 		setDeltaDepth(len);
203 	}
204 
205 	final void clearChainLength() {
206 		flags &= NON_DELTA_MASK;
207 	}
208 
209 	final boolean wantWrite() {
210 		return getOffset() == 1;
211 	}
212 
213 	final void markWantWrite() {
214 		setOffset(1);
215 	}
216 
217 	/**
218 	 * @return true if an existing representation was selected to be reused
219 	 *         as-is into the pack stream.
220 	 */
221 	public final boolean isReuseAsIs() {
222 		return (flags & REUSE_AS_IS) != 0;
223 	}
224 
225 	final void setReuseAsIs() {
226 		flags |= REUSE_AS_IS;
227 	}
228 
229 	/**
230 	 * Forget the reuse information previously stored.
231 	 * <p>
232 	 * Implementations may subclass this method, but they must also invoke the
233 	 * super version with {@code super.clearReuseAsIs()} to ensure the flag is
234 	 * properly cleared for the writer.
235 	 */
236 	protected void clearReuseAsIs() {
237 		flags &= ~REUSE_AS_IS;
238 	}
239 
240 	final boolean isDoNotDelta() {
241 		return (flags & DO_NOT_DELTA) != 0;
242 	}
243 
244 	final void setDoNotDelta() {
245 		flags |= DO_NOT_DELTA;
246 	}
247 
248 	final boolean isEdge() {
249 		return (flags & EDGE) != 0;
250 	}
251 
252 	final void setEdge() {
253 		flags |= EDGE;
254 	}
255 
256 	final boolean doNotAttemptDelta() {
257 		// Do not attempt if delta attempted and object reuse.
258 		return (flags & ATTEMPT_DELTA_MASK) == ATTEMPT_DELTA_MASK;
259 	}
260 
261 	final void setDeltaAttempted(boolean deltaAttempted) {
262 		if (deltaAttempted)
263 			flags |= DELTA_ATTEMPTED;
264 		else
265 			flags &= ~DELTA_ATTEMPTED;
266 	}
267 
268 	/** @return the extended flags on this object, in the range [0x0, 0xf]. */
269 	protected final int getExtendedFlags() {
270 		return (flags >>> EXT_SHIFT) & EXT_MASK;
271 	}
272 
273 	/**
274 	 * Determine if a particular extended flag bit has been set.
275 	 *
276 	 * This implementation may be faster than calling
277 	 * {@link #getExtendedFlags()} and testing the result.
278 	 *
279 	 * @param flag
280 	 *            the flag mask to test, must be between 0x0 and 0xf.
281 	 * @return true if any of the bits matching the mask are non-zero.
282 	 */
283 	protected final boolean isExtendedFlag(int flag) {
284 		return (flags & (flag << EXT_SHIFT)) != 0;
285 	}
286 
287 	/**
288 	 * Set an extended flag bit.
289 	 *
290 	 * This implementation is more efficient than getting the extended flags,
291 	 * adding the bit, and setting them all back.
292 	 *
293 	 * @param flag
294 	 *            the bits to set, must be between 0x0 and 0xf.
295 	 */
296 	protected final void setExtendedFlag(int flag) {
297 		flags |= (flag & EXT_MASK) << EXT_SHIFT;
298 	}
299 
300 	/**
301 	 * Clear an extended flag bit.
302 	 *
303 	 * This implementation is more efficient than getting the extended flags,
304 	 * removing the bit, and setting them all back.
305 	 *
306 	 * @param flag
307 	 *            the bits to clear, must be between 0x0 and 0xf.
308 	 */
309 	protected final void clearExtendedFlag(int flag) {
310 		flags &= ~((flag & EXT_MASK) << EXT_SHIFT);
311 	}
312 
313 	/**
314 	 * Set the extended flags used by the subclass.
315 	 *
316 	 * Subclass implementations may store up to 4 bits of information inside of
317 	 * the internal flags field already used by the base ObjectToPack instance.
318 	 *
319 	 * @param extFlags
320 	 *            additional flag bits to store in the flags field. Due to space
321 	 *            constraints only values [0x0, 0xf] are permitted.
322 	 */
323 	protected final void setExtendedFlags(int extFlags) {
324 		flags = ((extFlags & EXT_MASK) << EXT_SHIFT) | (flags & NON_EXT_MASK);
325 	}
326 
327 	final int getFormat() {
328 		if (isReuseAsIs()) {
329 			if (isDeltaRepresentation())
330 				return StoredObjectRepresentation.PACK_DELTA;
331 			return StoredObjectRepresentation.PACK_WHOLE;
332 		}
333 		return StoredObjectRepresentation.FORMAT_OTHER;
334 	}
335 
336 	// Overload weight into CRC since we don't need them at the same time.
337 	final int getWeight() {
338 		return getCRC();
339 	}
340 
341 	final void setWeight(int weight) {
342 		setCRC(weight);
343 	}
344 
345 	final int getPathHash() {
346 		return pathHash;
347 	}
348 
349 	final void setPathHash(int hc) {
350 		pathHash = hc;
351 	}
352 
353 	final int getCachedSize() {
354 		return pathHash;
355 	}
356 
357 	final void setCachedSize(int sz) {
358 		pathHash = sz;
359 	}
360 
361 	/**
362 	 * Remember a specific representation for reuse at a later time.
363 	 * <p>
364 	 * Implementers should remember the representation chosen, so it can be
365 	 * reused at a later time. {@link PackWriter} may invoke this method
366 	 * multiple times for the same object, each time saving the current best
367 	 * representation found.
368 	 *
369 	 * @param ref
370 	 *            the object representation.
371 	 */
372 	public void select(StoredObjectRepresentation ref) {
373 		// Empty by default.
374 	}
375 
376 	@SuppressWarnings("nls")
377 	@Override
378 	public String toString() {
379 		StringBuilder buf = new StringBuilder();
380 		buf.append("ObjectToPack[");
381 		buf.append(Constants.typeString(getType()));
382 		buf.append(" ");
383 		buf.append(name());
384 		if (wantWrite())
385 			buf.append(" wantWrite");
386 		if (isReuseAsIs())
387 			buf.append(" reuseAsIs");
388 		if (isDoNotDelta())
389 			buf.append(" doNotDelta");
390 		if (isEdge())
391 			buf.append(" edge");
392 		if (getDeltaDepth() > 0)
393 			buf.append(" depth=").append(getDeltaDepth());
394 		if (isDeltaRepresentation()) {
395 			if (getDeltaBase() != null)
396 				buf.append(" base=inpack:").append(getDeltaBase().name());
397 			else
398 				buf.append(" base=edge:").append(getDeltaBaseId().name());
399 		}
400 		if (isWritten())
401 			buf.append(" offset=").append(getOffset());
402 		buf.append("]");
403 		return buf.toString();
404 	}
405 }