View Javadoc
1   /*
2    * Copyright (C) 2011, 2013 Google Inc., and others.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.internal.storage.dfs;
45  
46  import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
47  import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
48  
49  import java.util.Arrays;
50  
51  import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
52  import org.eclipse.jgit.internal.storage.pack.PackExt;
53  import org.eclipse.jgit.internal.storage.reftable.ReftableWriter;
54  import org.eclipse.jgit.storage.pack.PackStatistics;
55  
56  /**
57   * Description of a DFS stored pack/index file.
58   * <p>
59   * Implementors may extend this class and add additional data members.
60   * <p>
61   * Instances of this class are cached with the DfsPackFile, and should not be
62   * modified once initialized and presented to the JGit DFS library.
63   */
64  public class DfsPackDescription implements Comparable<DfsPackDescription> {
65  	private final DfsRepositoryDescription repoDesc;
66  	private final String packName;
67  	private PackSource packSource;
68  	private long lastModified;
69  	private long[] sizeMap;
70  	private int[] blockSizeMap;
71  	private long objectCount;
72  	private long deltaCount;
73  	private long minUpdateIndex;
74  	private long maxUpdateIndex;
75  
76  	private PackStatistics packStats;
77  	private ReftableWriter.Stats refStats;
78  	private int extensions;
79  	private int indexVersion;
80  	private long estimatedPackSize;
81  
82  	/**
83  	 * Initialize a description by pack name and repository.
84  	 * <p>
85  	 * The corresponding index file is assumed to exist. If this is not true
86  	 * implementors must extend the class and override
87  	 * {@link #getFileName(PackExt)}.
88  	 * <p>
89  	 * Callers should also try to fill in other fields if they are reasonably
90  	 * free to access at the time this instance is being initialized.
91  	 *
92  	 * @param name
93  	 *            name of the pack file. Must end with ".pack".
94  	 * @param repoDesc
95  	 *            description of the repo containing the pack file.
96  	 */
97  	public DfsPackDescription(DfsRepositoryDescription repoDesc, String name) {
98  		this.repoDesc = repoDesc;
99  		int dot = name.lastIndexOf('.');
100 		this.packName = (dot < 0) ? name : name.substring(0, dot);
101 
102 		int extCnt = PackExt.values().length;
103 		sizeMap = new long[extCnt];
104 		blockSizeMap = new int[extCnt];
105 	}
106 
107 	/**
108 	 * Get description of the repository.
109 	 *
110 	 * @return description of the repository.
111 	 */
112 	public DfsRepositoryDescription getRepositoryDescription() {
113 		return repoDesc;
114 	}
115 
116 	/**
117 	 * Adds the pack file extension to the known list.
118 	 *
119 	 * @param ext
120 	 *            the file extension
121 	 */
122 	public void addFileExt(PackExt ext) {
123 		extensions |= ext.getBit();
124 	}
125 
126 	/**
127 	 * Whether the pack file extension is known to exist.
128 	 *
129 	 * @param ext
130 	 *            the file extension
131 	 * @return whether the pack file extension is known to exist.
132 	 */
133 	public boolean hasFileExt(PackExt ext) {
134 		return (extensions & ext.getBit()) != 0;
135 	}
136 
137 	/**
138 	 * Get file name
139 	 *
140 	 * @param ext
141 	 *            the file extension
142 	 * @return name of the file.
143 	 */
144 	public String getFileName(PackExt ext) {
145 		return packName + '.' + ext.getExtension();
146 	}
147 
148 	/**
149 	 * Get cache key for use by the block cache.
150 	 *
151 	 * @param ext
152 	 *            the file extension.
153 	 * @return cache key for use by the block cache.
154 	 */
155 	public DfsStreamKey getStreamKey(PackExt ext) {
156 		return DfsStreamKey.of(getRepositoryDescription(), getFileName(ext),
157 				ext);
158 	}
159 
160 	/**
161 	 * Get the source of the pack.
162 	 *
163 	 * @return the source of the pack.
164 	 */
165 	public PackSource getPackSource() {
166 		return packSource;
167 	}
168 
169 	/**
170 	 * Set the source of the pack.
171 	 *
172 	 * @param source
173 	 *            the source of the pack.
174 	 * @return {@code this}
175 	 */
176 	public DfsPackDescription setPackSource(PackSource source) {
177 		packSource = source;
178 		return this;
179 	}
180 
181 	/**
182 	 * Get time the pack was created, in milliseconds.
183 	 *
184 	 * @return time the pack was created, in milliseconds.
185 	 */
186 	public long getLastModified() {
187 		return lastModified;
188 	}
189 
190 	/**
191 	 * Set time the pack was created, in milliseconds.
192 	 *
193 	 * @param timeMillis
194 	 *            time the pack was created, in milliseconds. 0 if not known.
195 	 * @return {@code this}
196 	 */
197 	public DfsPackDescription setLastModified(long timeMillis) {
198 		lastModified = timeMillis;
199 		return this;
200 	}
201 
202 	/**
203 	 * Get minUpdateIndex for the reftable, if present.
204 	 *
205 	 * @return minUpdateIndex for the reftable, if present.
206 	 */
207 	public long getMinUpdateIndex() {
208 		return minUpdateIndex;
209 	}
210 
211 	/**
212 	 * Set minUpdateIndex for the reftable.
213 	 *
214 	 * @param min
215 	 *            minUpdateIndex for the reftable.
216 	 * @return {@code this}
217 	 */
218 	public DfsPackDescription setMinUpdateIndex(long min) {
219 		minUpdateIndex = min;
220 		return this;
221 	}
222 
223 	/**
224 	 * Get maxUpdateIndex for the reftable, if present.
225 	 *
226 	 * @return maxUpdateIndex for the reftable, if present.
227 	 */
228 	public long getMaxUpdateIndex() {
229 		return maxUpdateIndex;
230 	}
231 
232 	/**
233 	 * Set maxUpdateIndex for the reftable.
234 	 *
235 	 * @param max
236 	 *            maxUpdateIndex for the reftable.
237 	 * @return {@code this}
238 	 */
239 	public DfsPackDescription setMaxUpdateIndex(long max) {
240 		maxUpdateIndex = max;
241 		return this;
242 	}
243 
244 	/**
245 	 * Set size of the file in bytes.
246 	 *
247 	 * @param ext
248 	 *            the file extension.
249 	 * @param bytes
250 	 *            size of the file in bytes. If 0 the file is not known and will
251 	 *            be determined on first read.
252 	 * @return {@code this}
253 	 */
254 	public DfsPackDescription setFileSize(PackExt ext, long bytes) {
255 		int i = ext.getPosition();
256 		if (i >= sizeMap.length) {
257 			sizeMap = Arrays.copyOf(sizeMap, i + 1);
258 		}
259 		sizeMap[i] = Math.max(0, bytes);
260 		return this;
261 	}
262 
263 	/**
264 	 * Get size of the file, in bytes.
265 	 *
266 	 * @param ext
267 	 *            the file extension.
268 	 * @return size of the file, in bytes. If 0 the file size is not yet known.
269 	 */
270 	public long getFileSize(PackExt ext) {
271 		int i = ext.getPosition();
272 		return i < sizeMap.length ? sizeMap[i] : 0;
273 	}
274 
275 	/**
276 	 * Get blockSize of the file, in bytes.
277 	 *
278 	 * @param ext
279 	 *            the file extension.
280 	 * @return blockSize of the file, in bytes. If 0 the blockSize size is not
281 	 *         yet known and may be discovered when opening the file.
282 	 */
283 	public int getBlockSize(PackExt ext) {
284 		int i = ext.getPosition();
285 		return i < blockSizeMap.length ? blockSizeMap[i] : 0;
286 	}
287 
288 	/**
289 	 * Set blockSize of the file, in bytes.
290 	 *
291 	 * @param ext
292 	 *            the file extension.
293 	 * @param blockSize
294 	 *            blockSize of the file, in bytes. If 0 the blockSize is not
295 	 *            known and will be determined on first read.
296 	 * @return {@code this}
297 	 */
298 	public DfsPackDescription setBlockSize(PackExt ext, int blockSize) {
299 		int i = ext.getPosition();
300 		if (i >= blockSizeMap.length) {
301 			blockSizeMap = Arrays.copyOf(blockSizeMap, i + 1);
302 		}
303 		blockSizeMap[i] = Math.max(0, blockSize);
304 		return this;
305 	}
306 
307 	/**
308 	 * Set estimated size of the .pack file in bytes.
309 	 *
310 	 * @param estimatedPackSize
311 	 *            estimated size of the .pack file in bytes. If 0 the pack file
312 	 *            size is unknown.
313 	 * @return {@code this}
314 	 */
315 	public DfsPackDescription setEstimatedPackSize(long estimatedPackSize) {
316 		this.estimatedPackSize = Math.max(0, estimatedPackSize);
317 		return this;
318 	}
319 
320 	/**
321 	 * Get estimated size of the .pack file in bytes.
322 	 *
323 	 * @return estimated size of the .pack file in bytes. If 0 the pack file
324 	 *         size is unknown.
325 	 */
326 	public long getEstimatedPackSize() {
327 		return estimatedPackSize;
328 	}
329 
330 	/**
331 	 * Get number of objects in the pack.
332 	 *
333 	 * @return number of objects in the pack.
334 	 */
335 	public long getObjectCount() {
336 		return objectCount;
337 	}
338 
339 	/**
340 	 * Set number of objects in the pack.
341 	 *
342 	 * @param cnt
343 	 *            number of objects in the pack.
344 	 * @return {@code this}
345 	 */
346 	public DfsPackDescription setObjectCount(long cnt) {
347 		objectCount = Math.max(0, cnt);
348 		return this;
349 	}
350 
351 	/**
352 	 * Get number of delta compressed objects in the pack.
353 	 *
354 	 * @return number of delta compressed objects in the pack.
355 	 */
356 	public long getDeltaCount() {
357 		return deltaCount;
358 	}
359 
360 	/**
361 	 * Set number of delta compressed objects in the pack.
362 	 *
363 	 * @param cnt
364 	 *            number of delta compressed objects in the pack.
365 	 * @return {@code this}
366 	 */
367 	public DfsPackDescription setDeltaCount(long cnt) {
368 		deltaCount = Math.max(0, cnt);
369 		return this;
370 	}
371 
372 	/**
373 	 * Get statistics from PackWriter, if the pack was built with it.
374 	 *
375 	 * @return statistics from PackWriter, if the pack was built with it.
376 	 *         Generally this is only available for packs created by
377 	 *         DfsGarbageCollector or DfsPackCompactor, and only when the pack
378 	 *         is being committed to the repository.
379 	 */
380 	public PackStatistics getPackStats() {
381 		return packStats;
382 	}
383 
384 	DfsPackDescription setPackStats(PackStatistics stats) {
385 		this.packStats = stats;
386 		setFileSize(PACK, stats.getTotalBytes());
387 		setObjectCount(stats.getTotalObjects());
388 		setDeltaCount(stats.getTotalDeltas());
389 		return this;
390 	}
391 
392 	/**
393 	 * Get stats from the sibling reftable, if created.
394 	 *
395 	 * @return stats from the sibling reftable, if created.
396 	 */
397 	public ReftableWriter.Stats getReftableStats() {
398 		return refStats;
399 	}
400 
401 	void setReftableStats(ReftableWriter.Stats stats) {
402 		this.refStats = stats;
403 		setMinUpdateIndex(stats.minUpdateIndex());
404 		setMaxUpdateIndex(stats.maxUpdateIndex());
405 		setFileSize(REFTABLE, stats.totalBytes());
406 		setBlockSize(REFTABLE, stats.refBlockSize());
407 	}
408 
409 	/**
410 	 * Discard the pack statistics, if it was populated.
411 	 *
412 	 * @return {@code this}
413 	 */
414 	public DfsPackDescription clearPackStats() {
415 		packStats = null;
416 		refStats = null;
417 		return this;
418 	}
419 
420 	/**
421 	 * Get the version of the index file written.
422 	 *
423 	 * @return the version of the index file written.
424 	 */
425 	public int getIndexVersion() {
426 		return indexVersion;
427 	}
428 
429 	/**
430 	 * Set the version of the index file written.
431 	 *
432 	 * @param version
433 	 *            the version of the index file written.
434 	 * @return {@code this}
435 	 */
436 	public DfsPackDescription setIndexVersion(int version) {
437 		indexVersion = version;
438 		return this;
439 	}
440 
441 	/** {@inheritDoc} */
442 	@Override
443 	public int hashCode() {
444 		return packName.hashCode();
445 	}
446 
447 	/** {@inheritDoc} */
448 	@Override
449 	public boolean equals(Object b) {
450 		if (b instanceof DfsPackDescription) {
451 			DfsPackDescription desc = (DfsPackDescription) b;
452 			return packName.equals(desc.packName) &&
453 					getRepositoryDescription().equals(desc.getRepositoryDescription());
454 		}
455 		return false;
456 	}
457 
458 	/**
459 	 * {@inheritDoc}
460 	 * <p>
461 	 * Sort packs according to the optimal lookup ordering.
462 	 * <p>
463 	 * This method tries to position packs in the order readers should examine
464 	 * them when looking for objects by SHA-1. The default tries to sort packs
465 	 * with more recent modification dates before older packs, and packs with
466 	 * fewer objects before packs with more objects.
467 	 */
468 	@Override
469 	public int compareTo(DfsPackDescription b) {
470 		// Cluster by PackSource, pushing UNREACHABLE_GARBAGE to the end.
471 		PackSource as = getPackSource();
472 		PackSource bs = b.getPackSource();
473 		if (as != null && bs != null) {
474 			int cmp = as.category - bs.category;
475 			if (cmp != 0)
476 				return cmp;
477 		}
478 
479 		// Tie break GC type packs by smallest first. There should be at most
480 		// one of each source, but when multiple exist concurrent GCs may have
481 		// run. Preferring the smaller file selects higher quality delta
482 		// compression, placing less demand on the DfsBlockCache.
483 		if (as != null && as == bs && isGC(as)) {
484 			int cmp = Long.signum(getFileSize(PACK) - b.getFileSize(PACK));
485 			if (cmp != 0) {
486 				return cmp;
487 			}
488 		}
489 
490 		// Newer packs should sort first.
491 		int cmp = Long.signum(b.getLastModified() - getLastModified());
492 		if (cmp != 0)
493 			return cmp;
494 
495 		// Break ties on smaller index. Readers may get lucky and find
496 		// the object they care about in the smaller index. This also pushes
497 		// big historical packs to the end of the list, due to more objects.
498 		return Long.signum(getObjectCount() - b.getObjectCount());
499 	}
500 
501 	static boolean isGC(PackSource s) {
502 		switch (s) {
503 		case GC:
504 		case GC_REST:
505 		case GC_TXN:
506 			return true;
507 		default:
508 			return false;
509 		}
510 	}
511 
512 	/** {@inheritDoc} */
513 	@Override
514 	public String toString() {
515 		return getFileName(PackExt.PACK);
516 	}
517 }