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 Lesser General Public
6 * License, version 2.1, 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 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser 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
.errors
.CorruptObjectException
;
25 public class PackFile
{
26 private static final int IDX_HDR_LEN
= 256 * 4;
28 private static final byte[] SIGNATURE
= { 'P', 'A', 'C', 'K' };
30 private final Repository repo
;
32 private final WindowedFile pack
;
34 private final WindowedFile idx
;
36 private final long[] idxHeader
;
38 private long objectCnt
;
40 public PackFile(final Repository parentRepo
, final File packFile
)
43 // FIXME window size and mmap type should be configurable
44 pack
= new WindowedFile(repo
.getWindowCache(), packFile
,
45 64 * 1024 * 1024, false);
49 final String name
= packFile
.getName();
50 final int dot
= name
.lastIndexOf('.');
51 final File idxFile
= new File(packFile
.getParentFile(), name
54 // FIXME window size and mmap type should be configurable
55 idx
= new WindowedFile(repo
.getWindowCache(), idxFile
,
56 64 * 1024 * 1024, false);
58 idxHeader
= readIndexHeader();
59 } catch (IOException ioe
) {
62 } catch (IOException err2
) {
66 } catch (IOException ioe
) {
69 } catch (IOException err2
) {
75 ObjectLoader
resolveBase(final long ofs
) throws IOException
{
76 return reader(ofs
, new byte[Constants
.OBJECT_ID_LENGTH
]);
80 * Determine if an object is contained within the pack file.
82 * For performance reasons only the index file is searched; the main
83 * pack content is ignored entirely.
87 * the object to look for. Must not be null.
89 * a temporary buffer loaned to this pack for use during
90 * the search. This buffer must be at least
91 * {@link Constants#OBJECT_ID_LENGTH} bytes in size. The
92 * buffer will be overwritten during the search, but is
94 * @return true if the object is in this pack; false otherwise.
96 * there was an error reading data from the pack's index
99 public boolean hasObject(final ObjectId id
, final byte[] tmp
)
101 return findOffset(id
, tmp
) != -1;
105 * Get an object from this pack.
107 * For performance reasons the caller is responsible for supplying a
108 * temporary buffer of at least {@link Constants#OBJECT_ID_LENGTH} bytes
109 * for use during searching. If an object loader is returned this
110 * temporary buffer becomes the property of the object loader and must
111 * not be overwritten by the caller. If no object loader is returned
112 * then the temporary buffer remains the property of the caller and may
113 * be given to a different pack file to continue searching for the
118 * the object to obtain from the pack. Must not be null.
120 * a temporary buffer loaned to this pack for use during
121 * the search, and given to the returned loader if the
122 * object is found. This buffer must be at least
123 * {@link Constants#OBJECT_ID_LENGTH} bytes in size. The
124 * buffer will be overwritten during the search. The
125 * buffer will be given to the loader if a loader is
126 * returned. If null is returned the caller may reuse the
128 * @return the object loader for the requested object if it is contained
129 * in this pack; null if the object was not found.
130 * @throws IOException
131 * the pack file or the index could not be read.
133 public PackedObjectLoader
get(final ObjectId id
, final byte[] tmp
)
135 final long offset
= findOffset(id
, tmp
);
138 final PackedObjectLoader objReader
= reader(offset
, tmp
);
143 public void close() throws IOException
{
148 byte[] decompress(final long position
, final int totalSize
)
149 throws DataFormatException
, IOException
{
150 final byte[] dstbuf
= new byte[totalSize
];
151 pack
.readCompressed(position
, dstbuf
);
155 private void readPackHeader() throws IOException
{
157 final byte[] sig
= new byte[SIGNATURE
.length
];
158 final byte[] intbuf
= new byte[4];
161 if (pack
.read(position
, sig
) != SIGNATURE
.length
)
162 throw new IOException("Not a PACK file.");
163 for (int k
= 0; k
< SIGNATURE
.length
; k
++) {
164 if (sig
[k
] != SIGNATURE
[k
])
165 throw new IOException("Not a PACK file.");
167 position
+= SIGNATURE
.length
;
169 vers
= pack
.readUInt32(position
, intbuf
);
170 if (vers
!= 2 && vers
!= 3)
171 throw new IOException("Unsupported pack version " + vers
+ ".");
174 objectCnt
= pack
.readUInt32(position
, intbuf
);
177 private long[] readIndexHeader() throws CorruptObjectException
, IOException
{
178 if (idx
.length() != (IDX_HDR_LEN
+ (24 * objectCnt
) + (2 * Constants
.OBJECT_ID_LENGTH
)))
179 throw new CorruptObjectException("Invalid pack index");
181 final long[] idxHeader
= new long[256];
182 final byte[] intbuf
= new byte[4];
183 for (int k
= 0; k
< idxHeader
.length
; k
++)
184 idxHeader
[k
] = idx
.readUInt32(k
* 4, intbuf
);
188 private PackedObjectLoader
reader(final long objOffset
, final byte[] ib
)
190 long pos
= objOffset
;
193 pack
.readFully(pos
, ib
);
194 int c
= ib
[p
++] & 0xff;
195 final int typeCode
= (c
>> 4) & 7;
196 long dataSize
= c
& 15;
198 while ((c
& 0x80) != 0) {
200 dataSize
+= (c
& 0x7f) << shift
;
206 case Constants
.OBJ_COMMIT
:
207 return whole(Constants
.TYPE_COMMIT
, pos
, dataSize
);
208 case Constants
.OBJ_TREE
:
209 return whole(Constants
.TYPE_TREE
, pos
, dataSize
);
210 case Constants
.OBJ_BLOB
:
211 return whole(Constants
.TYPE_BLOB
, pos
, dataSize
);
212 case Constants
.OBJ_TAG
:
213 return whole(Constants
.TYPE_TAG
, pos
, dataSize
);
214 case Constants
.OBJ_OFS_DELTA
: {
215 pack
.readFully(pos
, ib
);
219 while ((c
& 128) != 0) {
225 return new DeltaOfsPackedObjectLoader(this, pos
+ p
,
226 (int) dataSize
, objOffset
- ofs
);
228 case Constants
.OBJ_REF_DELTA
: {
229 pack
.readFully(pos
, ib
);
230 return new DeltaRefPackedObjectLoader(this, pos
+ ib
.length
,
231 (int) dataSize
, new ObjectId(ib
));
234 throw new IOException("Unknown object type " + typeCode
+ ".");
238 private final WholePackedObjectLoader
whole(final String type
,
239 final long pos
, final long size
) {
240 return new WholePackedObjectLoader(this, pos
, type
, (int) size
);
243 private long findOffset(final ObjectId objId
, final byte[] tmpid
)
245 final int levelOne
= objId
.getFirstByte();
246 long high
= idxHeader
[levelOne
];
247 long low
= levelOne
== 0 ?
0 : idxHeader
[levelOne
- 1];
250 final long mid
= (low
+ high
) / 2;
251 final long pos
= IDX_HDR_LEN
252 + ((4 + Constants
.OBJECT_ID_LENGTH
) * mid
) + 4;
253 idx
.readFully(pos
, tmpid
);
254 final int cmp
= objId
.compareTo(tmpid
);
258 return idx
.readUInt32(pos
- 4, tmpid
);
261 } while (low
< high
);