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
.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
;
35 private byte[][] idxdata
;
37 private long objectCnt
;
39 public PackFile(final Repository parentRepo
, final File packFile
)
42 // FIXME window size and mmap type should be configurable
43 pack
= new WindowedFile(repo
.getWindowCache(), packFile
,
44 64 * 1024 * 1024, true);
48 final String name
= packFile
.getName();
49 final int dot
= name
.lastIndexOf('.');
50 final File idxFile
= new File(packFile
.getParentFile(), name
53 // FIXME window size and mmap type should be configurable
54 idx
= new WindowedFile(new WindowCache(8*1024*1024,1), idxFile
, 8*1024*1024, true);
57 } catch (IOException ioe
) {
60 } catch (IOException err2
) {
65 } catch (IOException ioe
) {
68 } catch (IOException err2
) {
75 ObjectLoader
resolveBase(final long ofs
) throws IOException
{
80 * Determine if an object is contained within the pack file.
82 * For performance reasons only the index file is searched; the main pack
83 * content is ignored entirely.
87 * the object to look for. Must not be null.
88 * @return true if the object is in this pack; false otherwise.
90 public boolean hasObject(final ObjectId id
) {
91 return findOffset(id
) != -1;
95 * Get an object from this pack.
97 * For performance reasons the caller is responsible for supplying a
98 * temporary buffer of at least {@link Constants#OBJECT_ID_LENGTH} bytes for
99 * use during searching. If an object loader is returned this temporary
100 * buffer becomes the property of the object loader and must not be
101 * overwritten by the caller. If no object loader is returned then the
102 * temporary buffer remains the property of the caller and may be given to a
103 * different pack file to continue searching for the needed object.
107 * the object to obtain from the pack. Must not be null.
108 * @return the object loader for the requested object if it is contained in
109 * this pack; null if the object was not found.
110 * @throws IOException
111 * the pack file or the index could not be read.
113 public PackedObjectLoader
get(final ObjectId id
)
115 final long offset
= findOffset(id
);
118 final PackedObjectLoader objReader
= reader(offset
);
123 public void close() throws IOException
{
128 byte[] decompress(final long position
, final int totalSize
)
129 throws DataFormatException
, IOException
{
130 final byte[] dstbuf
= new byte[totalSize
];
131 pack
.readCompressed(position
, dstbuf
);
135 private void readPackHeader() throws IOException
{
137 final byte[] sig
= new byte[SIGNATURE
.length
];
138 final byte[] intbuf
= new byte[4];
141 if (pack
.read(position
, sig
) != SIGNATURE
.length
)
142 throw new IOException("Not a PACK file.");
143 for (int k
= 0; k
< SIGNATURE
.length
; k
++) {
144 if (sig
[k
] != SIGNATURE
[k
])
145 throw new IOException("Not a PACK file.");
147 position
+= SIGNATURE
.length
;
149 vers
= pack
.readUInt32(position
, intbuf
);
150 if (vers
!= 2 && vers
!= 3)
151 throw new IOException("Unsupported pack version " + vers
+ ".");
154 objectCnt
= pack
.readUInt32(position
, intbuf
);
157 private void readIndexHeader() throws CorruptObjectException
, IOException
{
158 if (idx
.length() != (IDX_HDR_LEN
+ (24 * objectCnt
) + (2 * Constants
.OBJECT_ID_LENGTH
)))
159 throw new CorruptObjectException("Invalid pack index");
161 final long[] idxHeader
= new long[256]; // really unsigned 32-bit...
162 final byte[] intbuf
= new byte[4];
163 for (int k
= 0; k
< idxHeader
.length
; k
++)
164 idxHeader
[k
] = idx
.readUInt32(k
* 4, intbuf
);
165 idxdata
= new byte[idxHeader
.length
][];
166 for (int k
= 0; k
< idxHeader
.length
; k
++) {
169 n
= (int)(idxHeader
[k
]);
171 n
= (int)(idxHeader
[k
]-idxHeader
[k
-1]);
174 idxdata
[k
] = new byte[n
* (Constants
.OBJECT_ID_LENGTH
+ 4)];
175 int off
= (int) ((k
== 0) ?
0 : idxHeader
[k
-1] * (Constants
.OBJECT_ID_LENGTH
+ 4));
176 idx
.read(off
+ IDX_HDR_LEN
, idxdata
[k
]);
181 private PackedObjectLoader
reader(final long objOffset
)
183 long pos
= objOffset
;
185 final byte[] ib
= new byte[Constants
.OBJECT_ID_LENGTH
];
186 pack
.readFully(pos
, ib
);
187 int c
= ib
[p
++] & 0xff;
188 final int typeCode
= (c
>> 4) & 7;
189 long dataSize
= c
& 15;
191 while ((c
& 0x80) != 0) {
193 dataSize
+= (c
& 0x7f) << shift
;
199 case Constants
.OBJ_COMMIT
:
200 return whole(Constants
.TYPE_COMMIT
, pos
, dataSize
);
201 case Constants
.OBJ_TREE
:
202 return whole(Constants
.TYPE_TREE
, pos
, dataSize
);
203 case Constants
.OBJ_BLOB
:
204 return whole(Constants
.TYPE_BLOB
, pos
, dataSize
);
205 case Constants
.OBJ_TAG
:
206 return whole(Constants
.TYPE_TAG
, pos
, dataSize
);
207 case Constants
.OBJ_OFS_DELTA
: {
208 pack
.readFully(pos
, ib
);
212 while ((c
& 128) != 0) {
218 return new DeltaOfsPackedObjectLoader(this, pos
+ p
,
219 (int) dataSize
, objOffset
- ofs
);
221 case Constants
.OBJ_REF_DELTA
: {
222 pack
.readFully(pos
, ib
);
223 return new DeltaRefPackedObjectLoader(this, pos
+ ib
.length
,
224 (int) dataSize
, new ObjectId(ib
));
227 throw new IOException("Unknown object type " + typeCode
+ ".");
231 private final WholePackedObjectLoader
whole(final String type
,
232 final long pos
, final long size
) {
233 return new WholePackedObjectLoader(this, pos
, type
, (int) size
);
236 private long findOffset(final ObjectId objId
) {
237 final int levelOne
= objId
.getFirstByte();
238 byte[] data
= idxdata
[levelOne
];
241 long high
= data
.length
/ (4 + Constants
.OBJECT_ID_LENGTH
);
244 final long mid
= (low
+ high
) / 2;
245 final long pos
= ((4 + Constants
.OBJECT_ID_LENGTH
) * mid
) + 4;
246 final int cmp
= objId
.compareTo(data
, pos
);
250 int b0
= data
[(int)pos
-4] & 0xff;
251 int b1
= data
[(int)pos
-3] & 0xff;
252 int b2
= data
[(int)pos
-2] & 0xff;
253 int b3
= data
[(int)pos
-1] & 0xff;
254 return (((long)b0
) << 24) | ( b1
<< 16 ) | ( b2
<< 8 ) | (b3
);
257 } while (low
< high
);