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
;
20 import java
.io
.IOException
;
21 import java
.util
.zip
.DataFormatException
;
23 import org
.spearce
.jgit
.util
.NB
;
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
;
42 * Construct a reader for an existing, pre-indexed packfile.
45 * Git repository holding this pack file
47 * path of the <code>.idx</code> file listing the contents.
49 * path of the <code>.pack</code> file holding the data.
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
{
56 pack
= new WindowedFile(repo
.getWindowCache(), packFile
) {
58 protected void onOpen() throws IOException
{
63 idx
= PackIndex
.open(idxFile
);
64 } catch (IOException ioe
) {
67 deltaBaseCache
= pack
.cache
.deltaBaseCache
;
70 final PackedObjectLoader
resolveBase(final WindowCursor curs
, final long ofs
)
72 return reader(curs
, ofs
);
76 * Determine if an object is contained within the pack file.
78 * For performance reasons only the index file is searched; the main pack
79 * content is ignored entirely.
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
);
91 * Get an object from this pack.
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.
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.
108 * temporary working space associated with the calling thread.
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
)
118 final long offset
= idx
.findOffset(id
);
121 final PackedObjectLoader objReader
= reader(curs
, offset
);
122 objReader
.setId(id
.toObjectId());
127 * Close the resources utilized by this repository
129 public void close() {
130 deltaBaseCache
.purge(pack
);
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
);
149 private void readPackHeader() throws IOException
{
150 final WindowCursor curs
= new WindowCursor();
152 final byte[] sig
= new byte[SIGNATURE
.length
];
153 final byte[] intbuf
= new byte[4];
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
+ ".");
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() + ": "
179 private PackedObjectLoader
reader(final WindowCursor curs
,
180 final long objOffset
)
182 long pos
= objOffset
;
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;
190 while ((c
& 0x80) != 0) {
192 dataSize
+= (c
& 0x7f) << shift
;
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
,
205 case Constants
.OBJ_OFS_DELTA
: {
206 pack
.readFully(pos
, ib
, curs
);
210 while ((c
& 128) != 0) {
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
));
225 throw new IOException("Unknown object type " + typeCode
+ ".");