Merge "Split note leaf buckets at 256 elements"
This commit is contained in:
commit
1b3abe75f8
|
@ -47,6 +47,7 @@
|
|||
|
||||
import org.eclipse.jgit.junit.TestRepository;
|
||||
import org.eclipse.jgit.lib.CommitBuilder;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.MutableObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectInserter;
|
||||
import org.eclipse.jgit.lib.ObjectReader;
|
||||
|
@ -361,6 +362,46 @@ public void testEditFanout2_38() throws Exception {
|
|||
.forPath(reader, "zoo-animals.txt", n.getTree()).getObjectId(0));
|
||||
}
|
||||
|
||||
public void testLeafSplitsWhenFull() throws Exception {
|
||||
RevBlob data1 = tr.blob("data1");
|
||||
MutableObjectId idBuf = new MutableObjectId();
|
||||
|
||||
RevCommit r = tr.commit() //
|
||||
.add(data1.name(), data1) //
|
||||
.create();
|
||||
tr.parseBody(r);
|
||||
|
||||
NoteMap map = NoteMap.read(reader, r);
|
||||
for (int i = 0; i < 254; i++) {
|
||||
idBuf.setByte(Constants.OBJECT_ID_LENGTH - 1, i);
|
||||
map.set(idBuf, data1);
|
||||
}
|
||||
|
||||
RevCommit n = commitNoteMap(map);
|
||||
TreeWalk tw = new TreeWalk(reader);
|
||||
tw.reset(n.getTree());
|
||||
while (tw.next())
|
||||
assertFalse("no fan-out subtree", tw.isSubtree());
|
||||
|
||||
for (int i = 254; i < 256; i++) {
|
||||
idBuf.setByte(Constants.OBJECT_ID_LENGTH - 1, i);
|
||||
map.set(idBuf, data1);
|
||||
}
|
||||
idBuf.setByte(Constants.OBJECT_ID_LENGTH - 2, 1);
|
||||
map.set(idBuf, data1);
|
||||
n = commitNoteMap(map);
|
||||
|
||||
// The 00 bucket is fully split.
|
||||
String path = fanout(38, idBuf.name());
|
||||
tw = TreeWalk.forPath(reader, path, n.getTree());
|
||||
assertNotNull("has " + path, tw);
|
||||
|
||||
// The other bucket is not.
|
||||
path = fanout(2, data1.name());
|
||||
tw = TreeWalk.forPath(reader, path, n.getTree());
|
||||
assertNotNull("has " + path, tw);
|
||||
}
|
||||
|
||||
public void testRemoveDeletesTreeFanout2_38() throws Exception {
|
||||
RevBlob a = tr.blob("a");
|
||||
RevBlob data1 = tr.blob("data1");
|
||||
|
|
|
@ -228,6 +228,24 @@ private int treeSize() {
|
|||
return sz;
|
||||
}
|
||||
|
||||
@Override
|
||||
InMemoryNoteBucket append(Note note) {
|
||||
int cell = cell(note);
|
||||
InMemoryNoteBucket b = (InMemoryNoteBucket) table[cell];
|
||||
|
||||
if (b == null) {
|
||||
LeafBucket n = new LeafBucket(prefixLen + 2);
|
||||
table[cell] = n.append(note);
|
||||
cnt++;
|
||||
|
||||
} else {
|
||||
InMemoryNoteBucket n = b.append(note);
|
||||
if (n != b)
|
||||
table[cell] = n;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private int cell(AnyObjectId id) {
|
||||
return id.getByte(prefixLen >> 1);
|
||||
}
|
||||
|
|
|
@ -68,4 +68,6 @@ abstract class InMemoryNoteBucket extends NoteBucket {
|
|||
InMemoryNoteBucket(int prefixLen) {
|
||||
this.prefixLen = prefixLen;
|
||||
}
|
||||
|
||||
abstract InMemoryNoteBucket append(Note note);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,8 @@
|
|||
* A LeafBucket must be parsed from a tree object by {@link NoteParser}.
|
||||
*/
|
||||
class LeafBucket extends InMemoryNoteBucket {
|
||||
static final int MAX_SIZE = 256;
|
||||
|
||||
/** All note blobs in this bucket, sorted sequentially. */
|
||||
private Note[] notes;
|
||||
|
||||
|
@ -142,6 +144,10 @@ InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
|
|||
}
|
||||
|
||||
} else if (noteData != null) {
|
||||
if (shouldSplit()) {
|
||||
return split().set(noteOn, noteData, or);
|
||||
|
||||
} else {
|
||||
growIfFull();
|
||||
p = -(p + 1);
|
||||
if (p < cnt)
|
||||
|
@ -149,6 +155,7 @@ InMemoryNoteBucket set(AnyObjectId noteOn, AnyObjectId noteData,
|
|||
notes[p] = new Note(noteOn, noteData.copy());
|
||||
cnt++;
|
||||
return this;
|
||||
}
|
||||
|
||||
} else {
|
||||
return this;
|
||||
|
@ -193,6 +200,18 @@ void parseOneEntry(AnyObjectId noteOn, AnyObjectId noteData) {
|
|||
notes[cnt++] = new Note(noteOn, noteData.copy());
|
||||
}
|
||||
|
||||
@Override
|
||||
InMemoryNoteBucket append(Note note) {
|
||||
if (shouldSplit()) {
|
||||
return split().append(note);
|
||||
|
||||
} else {
|
||||
growIfFull();
|
||||
notes[cnt++] = note;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private void growIfFull() {
|
||||
if (notes.length == cnt) {
|
||||
Note[] n = new Note[notes.length * 2];
|
||||
|
@ -200,4 +219,16 @@ private void growIfFull() {
|
|||
notes = n;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldSplit() {
|
||||
return MAX_SIZE <= cnt && prefixLen + 2 < OBJECT_ID_STRING_LENGTH;
|
||||
}
|
||||
|
||||
private InMemoryNoteBucket split() {
|
||||
FanoutBucket n = new FanoutBucket(prefixLen);
|
||||
for (int i = 0; i < cnt; i++)
|
||||
n.append(notes[i]);
|
||||
n.nonNotes = nonNotes;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue