PackIndex: expose the position of an object-id in the index

The primary index returns the offset in the pack for an
objectId. Internally it keeps the object-ids in lexicographical order,
but doesn't expose an API to find the position of an object-id in that
list. This is needed for the object-size index, that we want to store
as "position-in-idx, size".

Add a #findPosition(object-id) method to the PackIndex interface to
know where an object-id sits in the ordered list of ids in the pack.

Note that this index position is over the list of ordered object-ids,
while reverse-index position is over the list of objects in packed
order.

Change-Id: I89fa146599e347a26d3012d3477d7f5bbbda7ba4
This commit is contained in:
Ivan Frade 2023-01-17 10:01:29 -08:00
parent df5b7959be
commit 5b9ca7df42
4 changed files with 109 additions and 7 deletions

View File

@ -25,6 +25,7 @@
import org.eclipse.jgit.internal.JGitText;
import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
import org.eclipse.jgit.junit.RepositoryTestCase;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
public abstract class PackIndexTestCase extends RepositoryTestCase {
@ -122,6 +123,37 @@ public void testIteratorReturnedValues1() {
assertFalse(iter.hasNext());
}
@Test
public void testEntriesPositionsRamdomAccess() {
assertEquals(4, smallIdx.findPosition(ObjectId
.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")));
assertEquals(7, smallIdx.findPosition(ObjectId
.fromString("c59759f143fb1fe21c197981df75a7ee00290799")));
assertEquals(0, smallIdx.findPosition(ObjectId
.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904")));
}
@Test
public void testEntriesPositionsWithIteratorOrder() {
int i = 0;
for (MutableEntry me : smallIdx) {
assertEquals(smallIdx.findPosition(me.toObjectId()), i);
i++;
}
i = 0;
for (MutableEntry me : denseIdx) {
assertEquals(denseIdx.findPosition(me.toObjectId()), i);
i++;
}
}
@Test
public void testEntriesPositionsObjectNotInPack() {
assertEquals(-1, smallIdx.findPosition(ObjectId.zeroId()));
assertEquals(-1, smallIdx.findPosition(ObjectId
.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")));
}
/**
* Compare offset from iterator entries with output of findOffset() method.
*/
@ -135,6 +167,13 @@ public void testCompareEntriesOffsetsWithFindOffsets() {
}
}
@Test
public void testEntriesOffsetsObjectNotInPack() {
assertEquals(-1, smallIdx.findOffset(ObjectId.zeroId()));
assertEquals(-1, smallIdx.findOffset(ObjectId
.fromString("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")));
}
/**
* Compare offset from iterator entries with output of getOffset() method.
*/

View File

@ -240,6 +240,17 @@ public final ObjectId getObjectId(int nthPosition) {
*/
public abstract long findOffset(AnyObjectId objId);
/**
* Locate the position of this id in the list of object-ids in the index
*
* @param objId
* name of the object to locate within the index
* @return position of the object-id in the lexicographically ordered list
* of ids stored in this index; -1 if the object does not exist in
* this index and is thus not stored in the associated pack.
*/
public abstract int findPosition(AnyObjectId objId);
/**
* Retrieve stored CRC32 checksum of the requested object raw-data
* (including header).

View File

@ -32,6 +32,8 @@
class PackIndexV1 extends PackIndex {
private static final int IDX_HDR_LEN = 256 * 4;
private static final int RECORD_SIZE = 4 + Constants.OBJECT_ID_LENGTH;
private final long[] idxHeader;
byte[][] idxdata;
@ -131,8 +133,50 @@ long getOffset(long nthPosition) {
public long findOffset(AnyObjectId objId) {
final int levelOne = objId.getFirstByte();
byte[] data = idxdata[levelOne];
if (data == null)
int pos = levelTwoPosition(objId, data);
if (pos < 0) {
return -1;
}
// The records are (offset, objectid), pos points to objectId
int b0 = data[pos - 4] & 0xff;
int b1 = data[pos - 3] & 0xff;
int b2 = data[pos - 2] & 0xff;
int b3 = data[pos - 1] & 0xff;
return (((long) b0) << 24) | (b1 << 16) | (b2 << 8) | (b3);
}
/** {@inheritDoc} */
@Override
public int findPosition(AnyObjectId objId) {
int levelOne = objId.getFirstByte();
int levelTwo = levelTwoPosition(objId, idxdata[levelOne]);
if (levelTwo < 0) {
return -1;
}
long objsBefore = levelOne == 0 ? 0 : idxHeader[levelOne - 1];
return (int) objsBefore + ((levelTwo - 4) / RECORD_SIZE);
}
/**
* Find position in level two data of this objectId
*
* Records are (offset, objectId), so to read the corresponding offset,
* caller must substract from this position.
*
* @param objId
* ObjectId we are looking for
* @param data
* Blob of second level data with a series of (offset, objectid)
* pairs where we should find objId
*
* @return position in the byte[] where the objectId starts. -1 if not
* found.
*/
private int levelTwoPosition(AnyObjectId objId, byte[] data) {
if (data == null || data.length == 0) {
return -1;
}
int high = data.length / (4 + Constants.OBJECT_ID_LENGTH);
int low = 0;
do {
@ -142,11 +186,7 @@ public long findOffset(AnyObjectId objId) {
if (cmp < 0)
high = mid;
else if (cmp == 0) {
int b0 = data[pos - 4] & 0xff;
int b1 = data[pos - 3] & 0xff;
int b2 = data[pos - 2] & 0xff;
int b3 = data[pos - 1] & 0xff;
return (((long) b0) << 24) | (b1 << 16) | (b2 << 8) | (b3);
return pos;
} else
low = mid + 1;
} while (low < high);
@ -204,7 +244,7 @@ else if (cmp == 0) {
}
private static int idOffset(int mid) {
return ((4 + Constants.OBJECT_ID_LENGTH) * mid) + 4;
return (RECORD_SIZE * mid) + 4;
}
private class IndexV1Iterator extends EntriesIterator {

View File

@ -192,6 +192,18 @@ public long findOffset(AnyObjectId objId) {
return getOffset(levelOne, levelTwo);
}
/** {@inheritDoc} */
@Override
public int findPosition(AnyObjectId objId) {
int levelOne = objId.getFirstByte();
int levelTwo = binarySearchLevelTwo(objId, levelOne);
if (levelTwo < 0) {
return -1;
}
long objsBefore = levelOne == 0 ? 0 : fanoutTable[levelOne - 1];
return (int) objsBefore + levelTwo;
}
private long getOffset(int levelOne, int levelTwo) {
final long p = NB.decodeUInt32(offset32[levelOne], levelTwo << 2);
if ((p & IS_O64) != 0)