Switch jgit library to the EDL (3-clause BSD)
[jgit.git] / org.spearce.jgit / src / org / spearce / jgit / lib / PackFile.java
blob23a175c5149907f4a842b639650f3220d2b5b655
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.zip.DataFormatException;
45 import org.spearce.jgit.util.NB;
47 /**
48 * A Git version 2 pack file representation. A pack file contains
49 * Git objects in delta packed format yielding high compression of
50 * lots of object where some objects are similar.
52 public class PackFile {
53 private static final byte[] SIGNATURE = { 'P', 'A', 'C', 'K' };
55 private final WindowedFile pack;
57 private final PackIndex idx;
59 /**
60 * Construct a reader for an existing, pre-indexed packfile.
62 * @param parentRepo
63 * Git repository holding this pack file
64 * @param idxFile
65 * path of the <code>.idx</code> file listing the contents.
66 * @param packFile
67 * path of the <code>.pack</code> file holding the data.
68 * @throws IOException
69 * the index file cannot be accessed at this time.
71 public PackFile(final Repository parentRepo, final File idxFile,
72 final File packFile) throws IOException {
73 pack = new WindowedFile(packFile) {
74 @Override
75 protected void onOpen() throws IOException {
76 readPackHeader();
79 try {
80 idx = PackIndex.open(idxFile);
81 } catch (IOException ioe) {
82 throw ioe;
86 final PackedObjectLoader resolveBase(final WindowCursor curs, final long ofs)
87 throws IOException {
88 return reader(curs, ofs);
91 /**
92 * Determine if an object is contained within the pack file.
93 * <p>
94 * For performance reasons only the index file is searched; the main pack
95 * content is ignored entirely.
96 * </p>
98 * @param id
99 * the object to look for. Must not be null.
100 * @return true if the object is in this pack; false otherwise.
102 public boolean hasObject(final AnyObjectId id) {
103 return idx.hasObject(id);
107 * Get an object from this pack.
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 AnyObjectId id) throws IOException {
117 return get(new WindowCursor(), id);
121 * Get an object from this pack.
123 * @param curs
124 * temporary working space associated with the calling thread.
125 * @param id
126 * the object to obtain from the pack. Must not be null.
127 * @return the object loader for the requested object if it is contained in
128 * this pack; null if the object was not found.
129 * @throws IOException
130 * the pack file or the index could not be read.
132 public PackedObjectLoader get(final WindowCursor curs, final AnyObjectId id)
133 throws IOException {
134 final long offset = idx.findOffset(id);
135 if (offset == -1)
136 return null;
137 final PackedObjectLoader objReader = reader(curs, offset);
138 objReader.setId(id.toObjectId());
139 return objReader;
143 * Close the resources utilized by this repository
145 public void close() {
146 UnpackedObjectCache.purge(pack);
147 pack.close();
150 final UnpackedObjectCache.Entry readCache(final long position) {
151 return UnpackedObjectCache.get(pack, position);
154 final void saveCache(final long position, final byte[] data, final int type) {
155 UnpackedObjectCache.store(pack, position, data, type);
158 final byte[] decompress(final long position, final int totalSize,
159 final WindowCursor curs) throws DataFormatException, IOException {
160 final byte[] dstbuf = new byte[totalSize];
161 pack.readCompressed(position, dstbuf, curs);
162 return dstbuf;
165 private void readPackHeader() throws IOException {
166 final WindowCursor curs = new WindowCursor();
167 long position = 0;
168 final byte[] sig = new byte[SIGNATURE.length];
169 final byte[] intbuf = new byte[4];
170 final long vers;
172 if (pack.read(position, sig, curs) != SIGNATURE.length)
173 throw new IOException("Not a PACK file.");
174 for (int k = 0; k < SIGNATURE.length; k++) {
175 if (sig[k] != SIGNATURE[k])
176 throw new IOException("Not a PACK file.");
178 position += SIGNATURE.length;
180 pack.readFully(position, intbuf, curs);
181 vers = NB.decodeUInt32(intbuf, 0);
182 if (vers != 2 && vers != 3)
183 throw new IOException("Unsupported pack version " + vers + ".");
184 position += 4;
186 pack.readFully(position, intbuf, curs);
187 final long objectCnt = NB.decodeUInt32(intbuf, 0);
188 if (idx.getObjectCount() != objectCnt)
189 throw new IOException("Pack index"
190 + " object count mismatch; expected " + objectCnt
191 + " found " + idx.getObjectCount() + ": "
192 + pack.getName());
195 private PackedObjectLoader reader(final WindowCursor curs,
196 final long objOffset)
197 throws IOException {
198 long pos = objOffset;
199 int p = 0;
200 final byte[] ib = curs.tempId;
201 pack.readFully(pos, ib, curs);
202 int c = ib[p++] & 0xff;
203 final int typeCode = (c >> 4) & 7;
204 long dataSize = c & 15;
205 int shift = 4;
206 while ((c & 0x80) != 0) {
207 c = ib[p++] & 0xff;
208 dataSize += (c & 0x7f) << shift;
209 shift += 7;
211 pos += p;
213 switch (typeCode) {
214 case Constants.OBJ_COMMIT:
215 case Constants.OBJ_TREE:
216 case Constants.OBJ_BLOB:
217 case Constants.OBJ_TAG:
218 return new WholePackedObjectLoader(curs, this, pos, typeCode,
219 (int) dataSize);
221 case Constants.OBJ_OFS_DELTA: {
222 pack.readFully(pos, ib, curs);
223 p = 0;
224 c = ib[p++] & 0xff;
225 long ofs = c & 127;
226 while ((c & 128) != 0) {
227 ofs += 1;
228 c = ib[p++] & 0xff;
229 ofs <<= 7;
230 ofs += (c & 127);
232 return new DeltaOfsPackedObjectLoader(curs, this, pos + p,
233 (int) dataSize, objOffset - ofs);
235 case Constants.OBJ_REF_DELTA: {
236 pack.readFully(pos, ib, curs);
237 return new DeltaRefPackedObjectLoader(curs, this, pos + ib.length,
238 (int) dataSize, ObjectId.fromRaw(ib));
240 default:
241 throw new IOException("Unknown object type " + typeCode + ".");