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