Make PackIndex public and expose PackIndex.hasObject
[egit/florian.git] / org.spearce.jgit / src / org / spearce / jgit / lib / PackFile.java
blob7fc4686280bb77f062dc465c8e0c49992c3b9d13
1 /*
2 * Copyright (C) 2006 Shawn Pearce <spearce@spearce.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License, version 2, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.jgit.lib;
19 import java.io.File;
20 import java.io.IOException;
21 import java.util.zip.DataFormatException;
23 import org.spearce.jgit.util.NB;
25 /**
26 * A Git version 2 pack file representation. A pack file contains
27 * Git objects in delta packed format yielding high compression of
28 * lots of object where some objects are similar.
30 public class PackFile {
31 private static final byte[] SIGNATURE = { 'P', 'A', 'C', 'K' };
33 private final Repository repo;
35 private final WindowedFile pack;
37 private final PackIndex idx;
39 private final UnpackedObjectCache deltaBaseCache;
41 /**
42 * Construct a reader for an existing, pre-indexed packfile.
44 * @param parentRepo
45 * Git repository holding this pack file
46 * @param idxFile
47 * path of the <code>.idx</code> file listing the contents.
48 * @param packFile
49 * path of the <code>.pack</code> file holding the data.
50 * @throws IOException
51 * the index file cannot be accessed at this time.
53 public PackFile(final Repository parentRepo, final File idxFile,
54 final File packFile) throws IOException {
55 repo = parentRepo;
56 pack = new WindowedFile(repo.getWindowCache(), packFile) {
57 @Override
58 protected void onOpen() throws IOException {
59 readPackHeader();
62 try {
63 idx = PackIndex.open(idxFile);
64 } catch (IOException ioe) {
65 throw ioe;
67 deltaBaseCache = pack.cache.deltaBaseCache;
70 final PackedObjectLoader resolveBase(final WindowCursor curs, final long ofs)
71 throws IOException {
72 return reader(curs, ofs);
75 /**
76 * Determine if an object is contained within the pack file.
77 * <p>
78 * For performance reasons only the index file is searched; the main pack
79 * content is ignored entirely.
80 * </p>
82 * @param id
83 * the object to look for. Must not be null.
84 * @return true if the object is in this pack; false otherwise.
86 public boolean hasObject(final AnyObjectId id) {
87 return idx.hasObject(id);
90 /**
91 * Get an object from this pack.
93 * @param id
94 * the object to obtain from the pack. Must not be null.
95 * @return the object loader for the requested object if it is contained in
96 * this pack; null if the object was not found.
97 * @throws IOException
98 * the pack file or the index could not be read.
100 public PackedObjectLoader get(final AnyObjectId id) throws IOException {
101 return get(new WindowCursor(), id);
105 * Get an object from this pack.
107 * @param curs
108 * temporary working space associated with the calling thread.
109 * @param id
110 * the object to obtain from the pack. Must not be null.
111 * @return the object loader for the requested object if it is contained in
112 * this pack; null if the object was not found.
113 * @throws IOException
114 * the pack file or the index could not be read.
116 public PackedObjectLoader get(final WindowCursor curs, final AnyObjectId id)
117 throws IOException {
118 final long offset = idx.findOffset(id);
119 if (offset == -1)
120 return null;
121 final PackedObjectLoader objReader = reader(curs, offset);
122 objReader.setId(id.toObjectId());
123 return objReader;
127 * Close the resources utilized by this repository
129 public void close() {
130 deltaBaseCache.purge(pack);
131 pack.close();
134 final UnpackedObjectCache.Entry readCache(final long position) {
135 return deltaBaseCache.get(pack, position);
138 final void saveCache(final long position, final byte[] data, final int type) {
139 deltaBaseCache.store(pack, position, data, type);
142 final byte[] decompress(final long position, final int totalSize,
143 final WindowCursor curs) throws DataFormatException, IOException {
144 final byte[] dstbuf = new byte[totalSize];
145 pack.readCompressed(position, dstbuf, curs);
146 return dstbuf;
149 private void readPackHeader() throws IOException {
150 final WindowCursor curs = new WindowCursor();
151 long position = 0;
152 final byte[] sig = new byte[SIGNATURE.length];
153 final byte[] intbuf = new byte[4];
154 final long vers;
156 if (pack.read(position, sig, curs) != SIGNATURE.length)
157 throw new IOException("Not a PACK file.");
158 for (int k = 0; k < SIGNATURE.length; k++) {
159 if (sig[k] != SIGNATURE[k])
160 throw new IOException("Not a PACK file.");
162 position += SIGNATURE.length;
164 pack.readFully(position, intbuf, curs);
165 vers = NB.decodeUInt32(intbuf, 0);
166 if (vers != 2 && vers != 3)
167 throw new IOException("Unsupported pack version " + vers + ".");
168 position += 4;
170 pack.readFully(position, intbuf, curs);
171 final long objectCnt = NB.decodeUInt32(intbuf, 0);
172 if (idx.getObjectCount() != objectCnt)
173 throw new IOException("Pack index"
174 + " object count mismatch; expected " + objectCnt
175 + " found " + idx.getObjectCount() + ": "
176 + pack.getName());
179 private PackedObjectLoader reader(final WindowCursor curs,
180 final long objOffset)
181 throws IOException {
182 long pos = objOffset;
183 int p = 0;
184 final byte[] ib = curs.tempId;
185 pack.readFully(pos, ib, curs);
186 int c = ib[p++] & 0xff;
187 final int typeCode = (c >> 4) & 7;
188 long dataSize = c & 15;
189 int shift = 4;
190 while ((c & 0x80) != 0) {
191 c = ib[p++] & 0xff;
192 dataSize += (c & 0x7f) << shift;
193 shift += 7;
195 pos += p;
197 switch (typeCode) {
198 case Constants.OBJ_COMMIT:
199 case Constants.OBJ_TREE:
200 case Constants.OBJ_BLOB:
201 case Constants.OBJ_TAG:
202 return new WholePackedObjectLoader(curs, this, pos, typeCode,
203 (int) dataSize);
205 case Constants.OBJ_OFS_DELTA: {
206 pack.readFully(pos, ib, curs);
207 p = 0;
208 c = ib[p++] & 0xff;
209 long ofs = c & 127;
210 while ((c & 128) != 0) {
211 ofs += 1;
212 c = ib[p++] & 0xff;
213 ofs <<= 7;
214 ofs += (c & 127);
216 return new DeltaOfsPackedObjectLoader(curs, this, pos + p,
217 (int) dataSize, objOffset - ofs);
219 case Constants.OBJ_REF_DELTA: {
220 pack.readFully(pos, ib, curs);
221 return new DeltaRefPackedObjectLoader(curs, this, pos + ib.length,
222 (int) dataSize, ObjectId.fromRaw(ib));
224 default:
225 throw new IOException("Unknown object type " + typeCode + ".");