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
48 import java.util.HashMap;
49 import java.util.Map;
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.storage.pack.PackStatistics;
54
55 /**
56 * Description of a DFS stored pack/index file.
57 * <p>
58 * Implementors may extend this class and add additional data members.
59 * <p>
60 * Instances of this class are cached with the DfsPackFile, and should not be
61 * modified once initialized and presented to the JGit DFS library.
62 */
63 public class DfsPackDescription implements Comparable<DfsPackDescription> {
64 private final DfsRepositoryDescription repoDesc;
65
66 private final String packName;
67
68 private PackSource packSource;
69
70 private long lastModified;
71
72 private final Map<PackExt, Long> sizeMap;
73
74 private long objectCount;
75
76 private long deltaCount;
77
78 private PackStatistics stats;
79
80 private int extensions;
81
82 private int indexVersion;
83
84 /**
85 * Initialize a description by pack name and repository.
86 * <p>
87 * The corresponding index file is assumed to exist. If this is not true
88 * implementors must extend the class and override
89 * {@link #getFileName(PackExt)}.
90 * <p>
91 * Callers should also try to fill in other fields if they are reasonably
92 * free to access at the time this instance is being initialized.
93 *
94 * @param name
95 * name of the pack file. Must end with ".pack".
96 * @param repoDesc
97 * description of the repo containing the pack file.
98 */
99 public DfsPackDescription(DfsRepositoryDescription repoDesc, String name) {
100 this.repoDesc = repoDesc;
101 int dot = name.lastIndexOf('.');
102 this.packName = (dot < 0) ? name : name.substring(0, dot);
103 this.sizeMap = new HashMap<PackExt, Long>(PackExt.values().length * 2);
104 }
105
106 /** @return description of the repository. */
107 public DfsRepositoryDescription getRepositoryDescription() {
108 return repoDesc;
109 }
110
111 /**
112 * Adds the pack file extension to the known list.
113 *
114 * @param ext
115 * the file extension
116 */
117 public void addFileExt(PackExt ext) {
118 extensions |= ext.getBit();
119 }
120
121 /**
122 * @param ext
123 * the file extension
124 * @return whether the pack file extensions is known to exist.
125 */
126 public boolean hasFileExt(PackExt ext) {
127 return (extensions & ext.getBit()) != 0;
128 }
129
130 /**
131 * @param ext
132 * the file extension
133 * @return name of the file.
134 */
135 public String getFileName(PackExt ext) {
136 return packName + '.' + ext.getExtension();
137 }
138
139 /** @return the source of the pack. */
140 public PackSource getPackSource() {
141 return packSource;
142 }
143
144 /**
145 * @param source
146 * the source of the pack.
147 * @return {@code this}
148 */
149 public DfsPackDescription setPackSource(PackSource source) {
150 packSource = source;
151 return this;
152 }
153
154 /** @return time the pack was created, in milliseconds. */
155 public long getLastModified() {
156 return lastModified;
157 }
158
159 /**
160 * @param timeMillis
161 * time the pack was created, in milliseconds. 0 if not known.
162 * @return {@code this}
163 */
164 public DfsPackDescription setLastModified(long timeMillis) {
165 lastModified = timeMillis;
166 return this;
167 }
168
169 /**
170 * @param ext
171 * the file extension.
172 * @param bytes
173 * size of the file in bytes. If 0 the file is not known and will
174 * be determined on first read.
175 * @return {@code this}
176 */
177 public DfsPackDescription setFileSize(PackExt ext, long bytes) {
178 sizeMap.put(ext, Long.valueOf(Math.max(0, bytes)));
179 return this;
180 }
181
182 /**
183 * @param ext
184 * the file extension.
185 * @return size of the file, in bytes. If 0 the file size is not yet known.
186 */
187 public long getFileSize(PackExt ext) {
188 Long size = sizeMap.get(ext);
189 return size == null ? 0 : size.longValue();
190 }
191
192 /** @return number of objects in the pack. */
193 public long getObjectCount() {
194 return objectCount;
195 }
196
197 /**
198 * @param cnt
199 * number of objects in the pack.
200 * @return {@code this}
201 */
202 public DfsPackDescription setObjectCount(long cnt) {
203 objectCount = Math.max(0, cnt);
204 return this;
205 }
206
207 /** @return number of delta compressed objects in the pack. */
208 public long getDeltaCount() {
209 return deltaCount;
210 }
211
212 /**
213 * @param cnt
214 * number of delta compressed objects in the pack.
215 * @return {@code this}
216 */
217 public DfsPackDescription setDeltaCount(long cnt) {
218 deltaCount = Math.max(0, cnt);
219 return this;
220 }
221
222 /**
223 * @return statistics from PackWriter, if the pack was built with it.
224 * Generally this is only available for packs created by
225 * DfsGarbageCollector or DfsPackCompactor, and only when the pack
226 * is being committed to the repository.
227 */
228 public PackStatistics getPackStats() {
229 return stats;
230 }
231
232 DfsPackDescription setPackStats(PackStatistics stats) {
233 this.stats = stats;
234 setFileSize(PACK, stats.getTotalBytes());
235 setObjectCount(stats.getTotalObjects());
236 setDeltaCount(stats.getTotalDeltas());
237 return this;
238 }
239
240 /**
241 * Discard the pack statistics, if it was populated.
242 *
243 * @return {@code this}
244 */
245 public DfsPackDescription clearPackStats() {
246 stats = null;
247 return this;
248 }
249
250 /** @return the version of the index file written. */
251 public int getIndexVersion() {
252 return indexVersion;
253 }
254
255 /**
256 * @param version
257 * the version of the index file written.
258 * @return {@code this}
259 */
260 public DfsPackDescription setIndexVersion(int version) {
261 indexVersion = version;
262 return this;
263 }
264
265 @Override
266 public int hashCode() {
267 return packName.hashCode();
268 }
269
270 @Override
271 public boolean equals(Object b) {
272 if (b instanceof DfsPackDescription) {
273 DfsPackDescription desc = (DfsPackDescription) b;
274 return packName.equals(desc.packName) &&
275 getRepositoryDescription().equals(desc.getRepositoryDescription());
276 }
277 return false;
278 }
279
280 /**
281 * Sort packs according to the optimal lookup ordering.
282 * <p>
283 * This method tries to position packs in the order readers should examine
284 * them when looking for objects by SHA-1. The default tries to sort packs
285 * with more recent modification dates before older packs, and packs with
286 * fewer objects before packs with more objects.
287 *
288 * @param b
289 * the other pack.
290 */
291 public int compareTo(DfsPackDescription b) {
292 // Cluster by PackSource, pushing UNREACHABLE_GARBAGE to the end.
293 PackSource as = getPackSource();
294 PackSource bs = b.getPackSource();
295 if (as != null && bs != null) {
296 int cmp = as.category - bs.category;
297 if (cmp != 0)
298 return cmp;
299 }
300
301 // Newer packs should sort first.
302 int cmp = Long.signum(b.getLastModified() - getLastModified());
303 if (cmp != 0)
304 return cmp;
305
306 // Break ties on smaller index. Readers may get lucky and find
307 // the object they care about in the smaller index. This also pushes
308 // big historical packs to the end of the list, due to more objects.
309 return Long.signum(getObjectCount() - b.getObjectCount());
310 }
311
312 @Override
313 public String toString() {
314 return getFileName(PackExt.PACK);
315 }
316 }