1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
|
/*
* Copyright (C) 2012, Google Inc. and others
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0 which is available at
* https://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package org.eclipse.jgit.internal.storage.file;
import java.io.DataInput;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectIdOwnerMap;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.NB;
import com.googlecode.javaewah.EWAHCompressedBitmap;
/**
* Support for the pack bitmap index v1 format.
*
* @see PackBitmapIndex
*/
class PackBitmapIndexV1 extends BasePackBitmapIndex {
static final byte[] MAGIC = { 'B', 'I', 'T', 'M' };
static final int OPT_FULL = 1;
private static final int MAX_XOR_OFFSET = 126;
private byte[] packChecksum;
private static final ExecutorService executor = Executors
.newCachedThreadPool(new ThreadFactory() {
private final ThreadFactory baseFactory = Executors
.defaultThreadFactory();
private final AtomicInteger threadNumber = new AtomicInteger(0);
@Override
public Thread newThread(Runnable runnable) {
Thread thread = baseFactory.newThread(runnable);
thread.setName("JGit-PackBitmapIndexV1-" //$NON-NLS-1$
+ threadNumber.getAndIncrement());
thread.setDaemon(true);
return thread;
}
});
private final PackIndex packIndex;
private final PackReverseIndex reverseIndex;
private final EWAHCompressedBitmap commits;
private final EWAHCompressedBitmap trees;
private final EWAHCompressedBitmap blobs;
private final EWAHCompressedBitmap tags;
private final ObjectIdOwnerMap<StoredBitmap> bitmaps;
PackBitmapIndexV1(final InputStream fd, PackIndex packIndex,
PackReverseIndex reverseIndex) throws IOException {
this(fd, () -> packIndex, () -> reverseIndex, false);
}
PackBitmapIndexV1(final InputStream fd,
SupplierWithIOException<PackIndex> packIndexSupplier,
SupplierWithIOException<PackReverseIndex> reverseIndexSupplier,
boolean loadParallelRevIndex)
throws IOException {
// An entry is object id, xor offset, flag byte, and a length encoded
// bitmap. The object id is an int32 of the nth position sorted by name.
super(new ObjectIdOwnerMap<>());
this.bitmaps = getBitmaps();
// Optionally start loading reverse index in parallel to loading bitmap
// from storage.
Future<PackReverseIndex> reverseIndexFuture = null;
if (loadParallelRevIndex) {
reverseIndexFuture = executor.submit(reverseIndexSupplier::get);
}
final byte[] scratch = new byte[32];
IO.readFully(fd, scratch, 0, scratch.length);
// Check the magic bytes
for (int i = 0; i < MAGIC.length; i++) {
if (scratch[i] != MAGIC[i]) {
byte[] actual = new byte[MAGIC.length];
System.arraycopy(scratch, 0, actual, 0, MAGIC.length);
throw new IOException(MessageFormat.format(
JGitText.get().expectedGot, Arrays.toString(MAGIC),
Arrays.toString(actual)));
}
}
// Read the version (2 bytes)
final int version = NB.decodeUInt16(scratch, 4);
if (version != 1)
throw new IOException(MessageFormat.format(
JGitText.get().unsupportedPackIndexVersion,
Integer.valueOf(version)));
// Read the options (2 bytes)
final int opts = NB.decodeUInt16(scratch, 6);
if ((opts & OPT_FULL) == 0)
throw new IOException(MessageFormat.format(
JGitText.get().expectedGot, Integer.valueOf(OPT_FULL),
Integer.valueOf(opts)));
// Read the number of entries (1 int32)
long numEntries = NB.decodeUInt32(scratch, 8);
if (numEntries > Integer.MAX_VALUE)
throw new IOException(JGitText.get().indexFileIsTooLargeForJgit);
// Checksum applied on the bottom of the corresponding pack file.
this.packChecksum = new byte[20];
System.arraycopy(scratch, 12, packChecksum, 0, packChecksum.length);
// Read the bitmaps for the Git types
SimpleDataInput dataInput = new SimpleDataInput(fd);
this.commits = readBitmap(dataInput);
this.trees = readBitmap(dataInput);
this.blobs = readBitmap(dataInput);
this.tags = readBitmap(dataInput);
// Read full bitmap from storage first.
List<IdxPositionBitmap> idxPositionBitmapList = new ArrayList<>();
// The xor offset is a single byte offset back in the list of entries.
IdxPositionBitmap[] recentBitmaps = new IdxPositionBitmap[MAX_XOR_OFFSET];
for (int i = 0; i < (int) numEntries; i++) {
IO.readFully(fd, scratch, 0, 6);
int nthObjectId = NB.decodeInt32(scratch, 0);
int xorOffset = scratch[4];
int flags = scratch[5];
EWAHCompressedBitmap bitmap = readBitmap(dataInput);
if (nthObjectId < 0) {
throw new IOException(MessageFormat.format(
JGitText.get().invalidId, String.valueOf(nthObjectId)));
}
if (xorOffset < 0) {
throw new IOException(MessageFormat.format(
JGitText.get().invalidId, String.valueOf(xorOffset)));
}
if (xorOffset > MAX_XOR_OFFSET) {
throw new IOException(MessageFormat.format(
JGitText.get().expectedLessThanGot,
String.valueOf(MAX_XOR_OFFSET),
String.valueOf(xorOffset)));
}
if (xorOffset > i) {
throw new IOException(MessageFormat.format(
JGitText.get().expectedLessThanGot, String.valueOf(i),
String.valueOf(xorOffset)));
}
IdxPositionBitmap xorIdxPositionBitmap = null;
if (xorOffset > 0) {
int index = (i - xorOffset);
xorIdxPositionBitmap = recentBitmaps[index
% recentBitmaps.length];
if (xorIdxPositionBitmap == null) {
throw new IOException(MessageFormat.format(
JGitText.get().invalidId,
String.valueOf(xorOffset)));
}
}
IdxPositionBitmap idxPositionBitmap = new IdxPositionBitmap(
nthObjectId, xorIdxPositionBitmap, bitmap, flags);
idxPositionBitmapList.add(idxPositionBitmap);
recentBitmaps[i % recentBitmaps.length] = idxPositionBitmap;
}
this.packIndex = packIndexSupplier.get();
for (int i = 0; i < idxPositionBitmapList.size(); ++i) {
IdxPositionBitmap idxPositionBitmap = idxPositionBitmapList.get(i);
ObjectId objectId = packIndex
.getObjectId(idxPositionBitmap.nthObjectId);
StoredBitmap sb = new StoredBitmap(objectId,
idxPositionBitmap.bitmap,
idxPositionBitmap.getXorStoredBitmap(),
idxPositionBitmap.flags);
// Save the StoredBitmap for a possible future XorStoredBitmap
// reference.
idxPositionBitmap.sb = sb;
bitmaps.add(sb);
}
PackReverseIndex computedReverseIndex;
if (loadParallelRevIndex && reverseIndexFuture != null) {
try {
computedReverseIndex = reverseIndexFuture.get();
} catch (InterruptedException | ExecutionException e) {
// Fallback to loading reverse index through a supplier.
computedReverseIndex = reverseIndexSupplier.get();
}
} else {
computedReverseIndex = reverseIndexSupplier.get();
}
this.reverseIndex = computedReverseIndex;
}
@Override
public int findPosition(AnyObjectId objectId) {
long offset = packIndex.findOffset(objectId);
if (offset == -1)
return -1;
return reverseIndex.findPosition(offset);
}
@Override
public ObjectId getObject(int position) throws IllegalArgumentException {
ObjectId objectId = reverseIndex.findObjectByPosition(position);
if (objectId == null)
throw new IllegalArgumentException();
return objectId;
}
@Override
public int getObjectCount() {
return (int) packIndex.getObjectCount();
}
@Override
public EWAHCompressedBitmap ofObjectType(
EWAHCompressedBitmap bitmap, int type) {
switch (type) {
case Constants.OBJ_BLOB:
return blobs.and(bitmap);
case Constants.OBJ_TREE:
return trees.and(bitmap);
case Constants.OBJ_COMMIT:
return commits.and(bitmap);
case Constants.OBJ_TAG:
return tags.and(bitmap);
}
throw new IllegalArgumentException();
}
@Override
public int getBitmapCount() {
return bitmaps.size();
}
@Override
public boolean equals(Object o) {
// TODO(cranger): compare the pack checksum?
if (o instanceof PackBitmapIndexV1)
return getPackIndex() == ((PackBitmapIndexV1) o).getPackIndex();
return false;
}
@Override
public int hashCode() {
return getPackIndex().hashCode();
}
@Override
public byte[] getPackChecksum() {
return this.packChecksum;
}
PackIndex getPackIndex() {
return packIndex;
}
private static EWAHCompressedBitmap readBitmap(DataInput dataInput)
throws IOException {
EWAHCompressedBitmap bitmap = new EWAHCompressedBitmap();
bitmap.deserialize(dataInput);
return bitmap;
}
/**
* Temporary holder of object position in pack index and other metadata for
* {@code StoredBitmap}.
*/
private static final class IdxPositionBitmap {
int nthObjectId;
IdxPositionBitmap xorIdxPositionBitmap;
EWAHCompressedBitmap bitmap;
int flags;
StoredBitmap sb;
IdxPositionBitmap(int nthObjectId,
@Nullable IdxPositionBitmap xorIdxPositionBitmap,
EWAHCompressedBitmap bitmap, int flags) {
this.nthObjectId = nthObjectId;
this.xorIdxPositionBitmap = xorIdxPositionBitmap;
this.bitmap = bitmap;
this.flags = flags;
}
StoredBitmap getXorStoredBitmap() {
return xorIdxPositionBitmap == null ? null
: xorIdxPositionBitmap.sb;
}
}
}
|