2
This code is derived from jgit (http://eclipse.org/jgit).
3
Copyright owners are documented in jgit's IP log.
5
This program and the accompanying materials are made available
6
under the terms of the Eclipse Distribution License v1.0 which
7
accompanies this distribution, is reproduced below, and is
8
available at http://www.eclipse.org/org/documents/edl-v10.php
12
Redistribution and use in source and binary forms, with or
13
without modification, are permitted provided that the following
16
- Redistributions of source code must retain the above copyright
17
notice, this list of conditions and the following disclaimer.
19
- Redistributions in binary form must reproduce the above
20
copyright notice, this list of conditions and the following
21
disclaimer in the documentation and/or other materials provided
22
with the distribution.
24
- Neither the name of the Eclipse Foundation, Inc. nor the
25
names of its contributors may be used to endorse or promote
26
products derived from this software without specific prior
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44
using System.Collections.Generic;
48
using NGit.Storage.File;
52
namespace NGit.Storage.File
54
/// <summary>Support for the pack index v2 format.</summary>
55
/// <remarks>Support for the pack index v2 format.</remarks>
56
internal class PackIndexV2 : PackIndex
58
private const long IS_O64 = 1L << 31;
60
private const int FANOUT = 256;
62
private static readonly int[] NO_INTS = new int[] { };
64
private static readonly byte[] NO_BYTES = new byte[] { };
66
private long objectCnt;
68
private readonly long[] fanoutTable;
70
/// <summary>256 arrays of contiguous object names.</summary>
71
/// <remarks>256 arrays of contiguous object names.</remarks>
72
private int[][] names;
75
/// 256 arrays of the 32 bit offset data, matching
76
/// <see cref="names">names</see>
79
private byte[][] offset32;
82
/// 256 arrays of the CRC-32 of objects, matching
83
/// <see cref="names">names</see>
86
private byte[][] crc32;
88
/// <summary>64 bit offset table.</summary>
89
/// <remarks>64 bit offset table.</remarks>
90
private byte[] offset64;
92
/// <exception cref="System.IO.IOException"></exception>
93
internal PackIndexV2(InputStream fd)
95
byte[] fanoutRaw = new byte[4 * FANOUT];
96
IOUtil.ReadFully(fd, fanoutRaw, 0, fanoutRaw.Length);
97
fanoutTable = new long[FANOUT];
98
for (int k = 0; k < FANOUT; k++)
100
fanoutTable[k] = NB.DecodeUInt32(fanoutRaw, k * 4);
102
objectCnt = fanoutTable[FANOUT - 1];
103
names = new int[FANOUT][];
104
offset32 = new byte[FANOUT][];
105
crc32 = new byte[FANOUT][];
106
// Object name table. The size we can permit per fan-out bucket
107
// is limited to Java's 2 GB per byte array limitation. That is
108
// no more than 107,374,182 objects per fan-out.
110
for (int k_1 = 0; k_1 < FANOUT; k_1++)
115
bucketCnt = fanoutTable[k_1];
119
bucketCnt = fanoutTable[k_1] - fanoutTable[k_1 - 1];
123
names[k_1] = NO_INTS;
124
offset32[k_1] = NO_BYTES;
125
crc32[k_1] = NO_BYTES;
128
long nameLen = bucketCnt * Constants.OBJECT_ID_LENGTH;
129
if (nameLen > int.MaxValue)
131
throw new IOException(JGitText.Get().indexFileIsTooLargeForJgit);
133
int intNameLen = (int)nameLen;
134
byte[] raw = new byte[intNameLen];
135
int[] bin = new int[(int)(((uint)intNameLen) >> 2)];
136
IOUtil.ReadFully(fd, raw, 0, raw.Length);
137
for (int i = 0; i < bin.Length; i++)
139
bin[i] = NB.DecodeInt32(raw, i << 2);
142
offset32[k_1] = new byte[(int)(bucketCnt * 4)];
143
crc32[k_1] = new byte[(int)(bucketCnt * 4)];
146
for (int k_2 = 0; k_2 < FANOUT; k_2++)
148
IOUtil.ReadFully(fd, crc32[k_2], 0, crc32[k_2].Length);
150
// 32 bit offset table. Any entries with the most significant bit
151
// set require a 64 bit offset entry in another table.
154
for (int k_3 = 0; k_3 < FANOUT; k_3++)
156
byte[] ofs = offset32[k_3];
157
IOUtil.ReadFully(fd, ofs, 0, ofs.Length);
158
for (int p = 0; p < ofs.Length; p += 4)
160
if (((sbyte)ofs[p]) < 0)
166
// 64 bit offset table. Most objects should not require an entry.
170
offset64 = new byte[o64cnt * 8];
171
IOUtil.ReadFully(fd, offset64, 0, offset64.Length);
177
packChecksum = new byte[20];
178
IOUtil.ReadFully(fd, packChecksum, 0, packChecksum.Length);
181
internal override long GetObjectCount()
186
internal override long GetOffset64Count()
188
return offset64.Length / 8;
191
internal override ObjectId GetObjectId(long nthPosition)
193
int levelOne = System.Array.BinarySearch(fanoutTable, nthPosition + 1);
197
// If we hit the bucket exactly the item is in the bucket, or
198
// any bucket before it which has the same object count.
200
@base = fanoutTable[levelOne];
201
while (levelOne > 0 && @base == fanoutTable[levelOne - 1])
208
// The item is in the bucket we would insert it into.
210
levelOne = -(levelOne + 1);
212
@base = levelOne > 0 ? fanoutTable[levelOne - 1] : 0;
213
int p = (int)(nthPosition - @base);
215
return ObjectId.FromRaw(names[levelOne], p4 + p);
219
internal override long FindOffset(AnyObjectId objId)
221
int levelOne = objId.FirstByte;
222
int levelTwo = BinarySearchLevelTwo(objId, levelOne);
227
long p = NB.DecodeUInt32(offset32[levelOne], levelTwo << 2);
228
if ((p & IS_O64) != 0)
230
return NB.DecodeUInt64(offset64, (8 * (int)(p & ~IS_O64)));
235
/// <exception cref="NGit.Errors.MissingObjectException"></exception>
236
internal override long FindCRC32(AnyObjectId objId)
238
int levelOne = objId.FirstByte;
239
int levelTwo = BinarySearchLevelTwo(objId, levelOne);
242
throw new MissingObjectException(objId.Copy(), "unknown");
244
return NB.DecodeUInt32(crc32[levelOne], levelTwo << 2);
247
internal override bool HasCRC32Support()
252
public override Sharpen.Iterator<PackIndex.MutableEntry> Iterator()
254
return new PackIndexV2.EntriesIteratorV2(this);
257
/// <exception cref="System.IO.IOException"></exception>
258
internal override void Resolve(ICollection<ObjectId> matches, AbbreviatedObjectId
261
int[] data = names[id.FirstByte];
262
int max = (int)(((uint)offset32[id.FirstByte].Length) >> 2);
271
int p = (int)(((uint)(low + high)) >> 1);
272
int cmp = id.PrefixCompare(data, IdOffset(p));
281
// We may have landed in the middle of the matches. Move
282
// backwards to the start of matches, then walk forwards.
284
while (0 < p && id.PrefixCompare(data, IdOffset(p - 1)) == 0)
288
for (; p < max && id.PrefixCompare(data, IdOffset(p)) == 0; p++)
290
matches.AddItem(ObjectId.FromRaw(data, IdOffset(p)));
291
if (matches.Count > matchLimit)
307
private static int IdOffset(int p)
313
private int BinarySearchLevelTwo(AnyObjectId objId, int levelOne)
315
int[] data = names[levelOne];
316
int high = (int)(((uint)offset32[levelOne].Length) >> 2);
324
int mid = (int)(((uint)(low + high)) >> 1);
327
cmp = objId.CompareTo(data, mid4 + mid);
349
private class EntriesIteratorV2 : PackIndex.EntriesIterator
351
private int levelOne;
353
private int levelTwo;
355
protected internal override PackIndex.MutableEntry InitEntry()
357
return new _MutableEntry_290(this);
360
private sealed class _MutableEntry_290 : PackIndex.MutableEntry
362
public _MutableEntry_290(EntriesIteratorV2 _enclosing)
364
this._enclosing = _enclosing;
367
internal override void EnsureId()
369
this.idBuffer.FromRaw(this._enclosing._enclosing.names[this._enclosing.levelOne],
370
this._enclosing.levelTwo - Constants.OBJECT_ID_LENGTH / 4);
373
private readonly EntriesIteratorV2 _enclosing;
376
public override PackIndex.MutableEntry Next()
378
for (; this.levelOne < this._enclosing.names.Length; this.levelOne++)
380
if (this.levelTwo < this._enclosing.names[this.levelOne].Length)
382
int idx = this.levelTwo / (Constants.OBJECT_ID_LENGTH / 4) * 4;
383
long offset = NB.DecodeUInt32(this._enclosing.offset32[this.levelOne], idx);
384
if ((offset & PackIndexV2.IS_O64) != 0)
386
idx = (8 * (int)(offset & ~PackIndexV2.IS_O64));
387
offset = NB.DecodeUInt64(this._enclosing.offset64, idx);
389
this.entry.offset = offset;
390
this.levelTwo += Constants.OBJECT_ID_LENGTH / 4;
391
this.returnedNumber++;
396
throw new NoSuchElementException();
399
internal EntriesIteratorV2(PackIndexV2 _enclosing) : base(_enclosing)
401
this._enclosing = _enclosing;
404
private readonly PackIndexV2 _enclosing;