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
54   * {@link org.eclipse.jgit.internal.storage.pack.PackWriter}.
55   * <p>
56   * {@code PackWriter} uses this class to track the things it needs to include in
57   * the newly generated pack file, and how to efficiently obtain the raw data for
58   * each object as they are written to the output stream.
59   */
60  public class ObjectToPack extends PackedObjectInfo {
61  	private static final int REUSE_AS_IS = 1 << 0;
62  	private static final int DELTA_ATTEMPTED = 1 << 1;
63  	private static final int DO_NOT_DELTA = 1 << 2;
64  	private static final int EDGE = 1 << 3;
65  	private static final int ATTEMPT_DELTA_MASK = REUSE_AS_IS | DELTA_ATTEMPTED;
66  	private static final int TYPE_SHIFT = 5;
67  	private static final int EXT_SHIFT = 8;
68  	private static final int EXT_MASK = 0xf;
69  	private static final int DELTA_SHIFT = 12;
70  	private static final int NON_EXT_MASK = ~(EXT_MASK << EXT_SHIFT);
71  	private static final int NON_DELTA_MASK = 0xfff;
72  
73  	/** Other object being packed that this will delta against. */
74  	private ObjectId deltaBase;
75  
76  	/**
77  	 * Bit field, from bit 0 to bit 31:
78  	 * <ul>
79  	 * <li>1 bit: canReuseAsIs</li>
80  	 * <li>1 bit: deltaAttempted</li>
81  	 * <li>1 bit: doNotDelta</li>
82  	 * <li>1 bit: edgeObject</li>
83  	 * <li>1 bit: unused</li>
84  	 * <li>3 bits: type</li>
85  	 * <li>4 bits: subclass flags (if any)</li>
86  	 * <li>--</li>
87  	 * <li>20 bits: deltaDepth</li>
88  	 * </ul>
89  	 */
90  	private int flags;
91  
92  	/** Hash of the object's tree path. */
93  	private int pathHash;
94  
95  	/** If present, deflated delta instruction stream for this object. */
96  	private DeltaCache.Ref cachedDelta;
97  
98  	/**
99  	 * Construct for the specified object id.
100 	 *
101 	 * @param src
102 	 *            object id of object for packing
103 	 * @param type
104 	 *            real type code of the object, not its in-pack type.
105 	 */
106 	public ObjectToPack(AnyObjectId src, int type) {
107 		super(src);
108 		flags = type << TYPE_SHIFT;
109 	}
110 
111 	/**
112 	 * Get delta base object id if object is going to be packed in delta
113 	 * representation
114 	 *
115 	 * @return delta base object id if object is going to be packed in delta
116 	 *         representation; null otherwise - if going to be packed as a whole
117 	 *         object.
118 	 */
119 	public final ObjectId getDeltaBaseId() {
120 		return deltaBase;
121 	}
122 
123 	/**
124 	 * Get delta base object to pack if object is going to be packed in delta
125 	 * representation and delta is specified as object to pack
126 	 *
127 	 * @return delta base object to pack if object is going to be packed in
128 	 *         delta representation and delta is specified as object to pack;
129 	 *         null otherwise - if going to be packed as a whole object or delta
130 	 *         base is specified only as id.
131 	 */
132 	public final ObjectToPack getDeltaBase() {
133 		if (deltaBase instanceof ObjectToPack)
134 			return (ObjectToPack) deltaBase;
135 		return null;
136 	}
137 
138 	/**
139 	 * Set delta base for the object. Delta base set by this method is used
140 	 * by {@link PackWriter} to write object - determines its representation
141 	 * in a created pack.
142 	 *
143 	 * @param deltaBase
144 	 *            delta base object or null if object should be packed as a
145 	 *            whole object.
146 	 *
147 	 */
148 	final void setDeltaBase(ObjectId deltaBase) {
149 		this.deltaBase = deltaBase;
150 	}
151 
152 	final void setCachedDelta(DeltaCache.Ref data) {
153 		cachedDelta = data;
154 	}
155 
156 	final DeltaCache.Ref popCachedDelta() {
157 		DeltaCache.Ref r = cachedDelta;
158 		if (r != null)
159 			cachedDelta = null;
160 		return r;
161 	}
162 
163 	final void clearDeltaBase() {
164 		this.deltaBase = null;
165 
166 		if (cachedDelta != null) {
167 			cachedDelta.clear();
168 			cachedDelta.enqueue();
169 			cachedDelta = null;
170 		}
171 	}
172 
173 	/**
174 	 * Whether object is going to be written as delta
175 	 *
176 	 * @return true if object is going to be written as delta; false otherwise.
177 	 */
178 	public final boolean isDeltaRepresentation() {
179 		return deltaBase != null;
180 	}
181 
182 	/**
183 	 * Check if object is already written in a pack. This information is
184 	 * used to achieve delta-base precedence in a pack file.
185 	 *
186 	 * @return true if object is already written; false otherwise.
187 	 */
188 	public final boolean isWritten() {
189 		return 1 < getOffset(); // markWantWrite sets 1.
190 	}
191 
192 	/** {@inheritDoc} */
193 	@Override
194 	public final int getType() {
195 		return (flags >> TYPE_SHIFT) & 0x7;
196 	}
197 
198 	final int getDeltaDepth() {
199 		return flags >>> DELTA_SHIFT;
200 	}
201 
202 	final void setDeltaDepth(int d) {
203 		flags = (d << DELTA_SHIFT) | (flags & NON_DELTA_MASK);
204 	}
205 
206 	final int getChainLength() {
207 		return getDeltaDepth();
208 	}
209 
210 	final void setChainLength(int len) {
211 		setDeltaDepth(len);
212 	}
213 
214 	final void clearChainLength() {
215 		flags &= NON_DELTA_MASK;
216 	}
217 
218 	final boolean wantWrite() {
219 		return getOffset() == 1;
220 	}
221 
222 	final void markWantWrite() {
223 		setOffset(1);
224 	}
225 
226 	/**
227 	 * Whether an existing representation was selected to be reused as-is into
228 	 * the pack stream.
229 	 *
230 	 * @return true if an existing representation was selected to be reused
231 	 *         as-is into the pack stream.
232 	 */
233 	public final boolean isReuseAsIs() {
234 		return (flags & REUSE_AS_IS) != 0;
235 	}
236 
237 	final void setReuseAsIs() {
238 		flags |= REUSE_AS_IS;
239 	}
240 
241 	/**
242 	 * Forget the reuse information previously stored.
243 	 * <p>
244 	 * Implementations may subclass this method, but they must also invoke the
245 	 * super version with {@code super.clearReuseAsIs()} to ensure the flag is
246 	 * properly cleared for the writer.
247 	 */
248 	protected void clearReuseAsIs() {
249 		flags &= ~REUSE_AS_IS;
250 	}
251 
252 	final boolean isDoNotDelta() {
253 		return (flags & DO_NOT_DELTA) != 0;
254 	}
255 
256 	final void setDoNotDelta() {
257 		flags |= DO_NOT_DELTA;
258 	}
259 
260 	final boolean isEdge() {
261 		return (flags & EDGE) != 0;
262 	}
263 
264 	final void setEdge() {
265 		flags |= EDGE;
266 	}
267 
268 	final boolean doNotAttemptDelta() {
269 		// Do not attempt if delta attempted and object reuse.
270 		return (flags & ATTEMPT_DELTA_MASK) == ATTEMPT_DELTA_MASK;
271 	}
272 
273 	final void setDeltaAttempted(boolean deltaAttempted) {
274 		if (deltaAttempted)
275 			flags |= DELTA_ATTEMPTED;
276 		else
277 			flags &= ~DELTA_ATTEMPTED;
278 	}
279 
280 	/**
281 	 * Get the extended flags on this object, in the range [0x0, 0xf].
282 	 *
283 	 * @return the extended flags on this object, in the range [0x0, 0xf].
284 	 */
285 	protected final int getExtendedFlags() {
286 		return (flags >>> EXT_SHIFT) & EXT_MASK;
287 	}
288 
289 	/**
290 	 * Determine if a particular extended flag bit has been set.
291 	 *
292 	 * This implementation may be faster than calling
293 	 * {@link #getExtendedFlags()} and testing the result.
294 	 *
295 	 * @param flag
296 	 *            the flag mask to test, must be between 0x0 and 0xf.
297 	 * @return true if any of the bits matching the mask are non-zero.
298 	 */
299 	protected final boolean isExtendedFlag(int flag) {
300 		return (flags & (flag << EXT_SHIFT)) != 0;
301 	}
302 
303 	/**
304 	 * Set an extended flag bit.
305 	 *
306 	 * This implementation is more efficient than getting the extended flags,
307 	 * adding the bit, and setting them all back.
308 	 *
309 	 * @param flag
310 	 *            the bits to set, must be between 0x0 and 0xf.
311 	 */
312 	protected final void setExtendedFlag(int flag) {
313 		flags |= (flag & EXT_MASK) << EXT_SHIFT;
314 	}
315 
316 	/**
317 	 * Clear an extended flag bit.
318 	 *
319 	 * This implementation is more efficient than getting the extended flags,
320 	 * removing the bit, and setting them all back.
321 	 *
322 	 * @param flag
323 	 *            the bits to clear, must be between 0x0 and 0xf.
324 	 */
325 	protected final void clearExtendedFlag(int flag) {
326 		flags &= ~((flag & EXT_MASK) << EXT_SHIFT);
327 	}
328 
329 	/**
330 	 * Set the extended flags used by the subclass.
331 	 *
332 	 * Subclass implementations may store up to 4 bits of information inside of
333 	 * the internal flags field already used by the base ObjectToPack instance.
334 	 *
335 	 * @param extFlags
336 	 *            additional flag bits to store in the flags field. Due to space
337 	 *            constraints only values [0x0, 0xf] are permitted.
338 	 */
339 	protected final void setExtendedFlags(int extFlags) {
340 		flags = ((extFlags & EXT_MASK) << EXT_SHIFT) | (flags & NON_EXT_MASK);
341 	}
342 
343 	final int getFormat() {
344 		if (isReuseAsIs()) {
345 			if (isDeltaRepresentation())
346 				return StoredObjectRepresentation.PACK_DELTA;
347 			return StoredObjectRepresentation.PACK_WHOLE;
348 		}
349 		return StoredObjectRepresentation.FORMAT_OTHER;
350 	}
351 
352 	// Overload weight into CRC since we don't need them at the same time.
353 	final int getWeight() {
354 		return getCRC();
355 	}
356 
357 	final void setWeight(int weight) {
358 		setCRC(weight);
359 	}
360 
361 	final int getPathHash() {
362 		return pathHash;
363 	}
364 
365 	final void setPathHash(int hc) {
366 		pathHash = hc;
367 	}
368 
369 	final int getCachedSize() {
370 		return pathHash;
371 	}
372 
373 	final void setCachedSize(int sz) {
374 		pathHash = sz;
375 	}
376 
377 	/**
378 	 * Remember a specific representation for reuse at a later time.
379 	 * <p>
380 	 * Implementers should remember the representation chosen, so it can be
381 	 * reused at a later time.
382 	 * {@link org.eclipse.jgit.internal.storage.pack.PackWriter} may invoke this
383 	 * method multiple times for the same object, each time saving the current
384 	 * best representation found.
385 	 *
386 	 * @param ref
387 	 *            the object representation.
388 	 */
389 	public void select(StoredObjectRepresentation ref) {
390 		// Empty by default.
391 	}
392 
393 	/** {@inheritDoc} */
394 	@SuppressWarnings("nls")
395 	@Override
396 	public String toString() {
397 		StringBuilder buf = new StringBuilder();
398 		buf.append("ObjectToPack[");
399 		buf.append(Constants.typeString(getType()));
400 		buf.append(" ");
401 		buf.append(name());
402 		if (wantWrite())
403 			buf.append(" wantWrite");
404 		if (isReuseAsIs())
405 			buf.append(" reuseAsIs");
406 		if (isDoNotDelta())
407 			buf.append(" doNotDelta");
408 		if (isEdge())
409 			buf.append(" edge");
410 		if (getDeltaDepth() > 0)
411 			buf.append(" depth=").append(getDeltaDepth());
412 		if (isDeltaRepresentation()) {
413 			if (getDeltaBase() != null)
414 				buf.append(" base=inpack:").append(getDeltaBase().name());
415 			else
416 				buf.append(" base=edge:").append(getDeltaBaseId().name());
417 		}
418 		if (isWritten())
419 			buf.append(" offset=").append(getOffset());
420 		buf.append("]");
421 		return buf.toString();
422 	}
423 }