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 }