Entries iterator in PackIndex and indirectly PackFile
[egit.git] / org.spearce.jgit / src / org / spearce / jgit / lib / PackFile.java
blob1b2c167d24ad1d58b96d8aca72ee467fd7abea17
1 /*
2 * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
3 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
22 * written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org.spearce.jgit.lib;
41 import java.io.File;
42 import java.io.IOException;
43 import java.util.Iterator;
44 import java.util.zip.DataFormatException;
46 import org.spearce.jgit.util.NB;
48 /**
49 * A Git version 2 pack file representation. A pack file contains Git objects in
50 * delta packed format yielding high compression of lots of object where some
51 * objects are similar.
53 public class PackFile implements Iterable<PackIndex.MutableEntry> {
54 private final WindowedFile pack;
56 private final PackIndex idx;
58 /**
59 * Construct a reader for an existing, pre-indexed packfile.
61 * @param parentRepo
62 * Git repository holding this pack file
63 * @param idxFile
64 * path of the <code>.idx</code> file listing the contents.
65 * @param packFile
66 * path of the <code>.pack</code> file holding the data.
67 * @throws IOException
68 * the index file cannot be accessed at this time.
70 public PackFile(final Repository parentRepo, final File idxFile,
71 final File packFile) throws IOException {
72 pack = new WindowedFile(packFile) {
73 @Override
74 protected void onOpen() throws IOException {
75 readPackHeader();
78 try {
79 idx = PackIndex.open(idxFile);
80 } catch (IOException ioe) {
81 throw ioe;
85 final PackedObjectLoader resolveBase(final WindowCursor curs, final long ofs)
86 throws IOException {
87 return reader(curs, ofs);
90 /**
91 * Determine if an object is contained within the pack file.
92 * <p>
93 * For performance reasons only the index file is searched; the main pack
94 * content is ignored entirely.
95 * </p>
97 * @param id
98 * the object to look for. Must not be null.
99 * @return true if the object is in this pack; false otherwise.
101 public boolean hasObject(final AnyObjectId id) {
102 return idx.hasObject(id);
106 * Get an object from this pack.
108 * @param id
109 * the object to obtain from the pack. Must not be null.
110 * @return the object loader for the requested object if it is contained in
111 * this pack; null if the object was not found.
112 * @throws IOException
113 * the pack file or the index could not be read.
115 public PackedObjectLoader get(final AnyObjectId id) throws IOException {
116 return get(new WindowCursor(), id);
120 * Get an object from this pack.
122 * @param curs
123 * temporary working space associated with the calling thread.
124 * @param id
125 * the object to obtain from the pack. Must not be null.
126 * @return the object loader for the requested object if it is contained in
127 * this pack; null if the object was not found.
128 * @throws IOException
129 * the pack file or the index could not be read.
131 public PackedObjectLoader get(final WindowCursor curs, final AnyObjectId id)
132 throws IOException {
133 final long offset = idx.findOffset(id);
134 if (offset == -1)
135 return null;
136 final PackedObjectLoader objReader = reader(curs, offset);
137 objReader.setId(id.toObjectId());
138 return objReader;
142 * Close the resources utilized by this repository
144 public void close() {
145 UnpackedObjectCache.purge(pack);
146 pack.close();
150 * Provide iterator over entries in associated pack index, that should also
151 * exist in this pack file. Objects returned by such iterator are mutable
152 * during iteration.
153 * <p>
154 * Iterator returns objects in SHA-1 lexicographical order.
155 * </p>
157 * @return iterator over entries of associated pack index
159 * @see PackIndex#iterator()
161 public Iterator<PackIndex.MutableEntry> iterator() {
162 return idx.iterator();
166 * Obtain the total number of objects available in this pack. This method
167 * relies on pack index, giving number of effectively available objects.
169 * @return number of objects in index of this pack, likewise in this pack
171 long getObjectCount() {
172 return idx.getObjectCount();
175 final UnpackedObjectCache.Entry readCache(final long position) {
176 return UnpackedObjectCache.get(pack, position);
179 final void saveCache(final long position, final byte[] data, final int type) {
180 UnpackedObjectCache.store(pack, position, data, type);
183 final byte[] decompress(final long position, final int totalSize,
184 final WindowCursor curs) throws DataFormatException, IOException {
185 final byte[] dstbuf = new byte[totalSize];
186 pack.readCompressed(position, dstbuf, curs);
187 return dstbuf;
190 private void readPackHeader() throws IOException {
191 final WindowCursor curs = new WindowCursor();
192 long position = 0;
193 final byte[] sig = new byte[Constants.PACK_SIGNATURE.length];
194 final byte[] intbuf = new byte[4];
195 final long vers;
197 if (pack.read(position, sig, curs) != Constants.PACK_SIGNATURE.length)
198 throw new IOException("Not a PACK file.");
199 for (int k = 0; k < Constants.PACK_SIGNATURE.length; k++) {
200 if (sig[k] != Constants.PACK_SIGNATURE[k])
201 throw new IOException("Not a PACK file.");
203 position += Constants.PACK_SIGNATURE.length;
205 pack.readFully(position, intbuf, curs);
206 vers = NB.decodeUInt32(intbuf, 0);
207 if (vers != 2 && vers != 3)
208 throw new IOException("Unsupported pack version " + vers + ".");
209 position += 4;
211 pack.readFully(position, intbuf, curs);
212 final long objectCnt = NB.decodeUInt32(intbuf, 0);
213 if (idx.getObjectCount() != objectCnt)
214 throw new IOException("Pack index"
215 + " object count mismatch; expected " + objectCnt
216 + " found " + idx.getObjectCount() + ": " + pack.getName());
219 private PackedObjectLoader reader(final WindowCursor curs,
220 final long objOffset) throws IOException {
221 long pos = objOffset;
222 int p = 0;
223 final byte[] ib = curs.tempId;
224 pack.readFully(pos, ib, curs);
225 int c = ib[p++] & 0xff;
226 final int typeCode = (c >> 4) & 7;
227 long dataSize = c & 15;
228 int shift = 4;
229 while ((c & 0x80) != 0) {
230 c = ib[p++] & 0xff;
231 dataSize += (c & 0x7f) << shift;
232 shift += 7;
234 pos += p;
236 switch (typeCode) {
237 case Constants.OBJ_COMMIT:
238 case Constants.OBJ_TREE:
239 case Constants.OBJ_BLOB:
240 case Constants.OBJ_TAG:
241 return new WholePackedObjectLoader(curs, this, pos, typeCode,
242 (int) dataSize);
244 case Constants.OBJ_OFS_DELTA: {
245 pack.readFully(pos, ib, curs);
246 p = 0;
247 c = ib[p++] & 0xff;
248 long ofs = c & 127;
249 while ((c & 128) != 0) {
250 ofs += 1;
251 c = ib[p++] & 0xff;
252 ofs <<= 7;
253 ofs += (c & 127);
255 return new DeltaOfsPackedObjectLoader(curs, this, pos + p,
256 (int) dataSize, objOffset - ofs);
258 case Constants.OBJ_REF_DELTA: {
259 pack.readFully(pos, ib, curs);
260 return new DeltaRefPackedObjectLoader(curs, this, pos + ib.length,
261 (int) dataSize, ObjectId.fromRaw(ib));
263 default:
264 throw new IOException("Unknown object type " + typeCode + ".");