blob: 11e374602025554fccdc16658866cde9741ca98e [file] [log] [blame]
/*
* Copyright (C) 2022, Google LLC 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 static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.transport.PackedObjectInfo;
import org.eclipse.jgit.util.BlockList;
import org.junit.Test;
public class PackObjectSizeIndexV1Test {
private static final ObjectId OBJ_ID = ObjectId
.fromString("b8b1d53172fb3fb19647adce4b38fab4371c2454");
private static final long GB = 1 << 30;
private static final int MAX_24BITS_UINT = 0xffffff;
@Test
public void write_24bPositions_32bSizes() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 0);
List<PackedObjectInfo> objs = new ArrayList<>();
objs.add(blobWithSize(100));
objs.add(blobWithSize(400));
objs.add(blobWithSize(200));
writer.write(objs);
byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
0x01, // version
0x00, 0x00, 0x00, 0x00, // minimum object size
0x00, 0x00, 0x00, 0x03, // obj count
0x18, // Unsigned 3 bytes
0x00, 0x00, 0x00, 0x03, // 3 positions
0x00, 0x00, 0x00, // positions
0x00, 0x00, 0x01, //
0x00, 0x00, 0x02, //
0x00, // No more positions
0x00, 0x00, 0x00, 0x64, // size 100
0x00, 0x00, 0x01, (byte) 0x90, // size 400
0x00, 0x00, 0x00, (byte) 0xc8, // size 200
0x00, 0x00, 0x00, 0x00 // 64bit sizes counter
};
byte[] output = out.toByteArray();
assertArrayEquals(expected, output);
}
@Test
public void write_32bPositions_32bSizes() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 0);
List<PackedObjectInfo> objs = new BlockList<>(9_000_000);
// The 24 bit range is full of commits and trees
PackedObjectInfo commit = objInfo(Constants.OBJ_COMMIT, 100);
for (int i = 0; i <= MAX_24BITS_UINT; i++) {
objs.add(commit);
}
objs.add(blobWithSize(100));
objs.add(blobWithSize(400));
objs.add(blobWithSize(200));
writer.write(objs);
byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
0x01, // version
0x00, 0x00, 0x00, 0x00, // minimum object size
0x00, 0x00, 0x00, 0x03, // obj count
(byte) 0x20, // Signed 4 bytes
0x00, 0x00, 0x00, 0x03, // 3 positions
0x01, 0x00, 0x00, 0x00, // positions
0x01, 0x00, 0x00, 0x01, //
0x01, 0x00, 0x00, 0x02, //
0x00, // No more positions
0x00, 0x00, 0x00, 0x64, // size 100
0x00, 0x00, 0x01, (byte) 0x90, // size 400
0x00, 0x00, 0x00, (byte) 0xc8, // size 200
0x00, 0x00, 0x00, 0x00 // 64bit sizes counter
};
byte[] output = out.toByteArray();
assertArrayEquals(expected, output);
}
@Test
public void write_24b32bPositions_32bSizes() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 0);
List<PackedObjectInfo> objs = new BlockList<>(9_000_000);
// The 24 bit range is full of commits and trees
PackedObjectInfo commit = objInfo(Constants.OBJ_COMMIT, 100);
for (int i = 0; i < MAX_24BITS_UINT; i++) {
objs.add(commit);
}
objs.add(blobWithSize(100));
objs.add(blobWithSize(400));
objs.add(blobWithSize(200));
writer.write(objs);
byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
0x01, // version
0x00, 0x00, 0x00, 0x00, // minimum object size
0x00, 0x00, 0x00, 0x03, // obj count
0x18, // 3 bytes
0x00, 0x00, 0x00, 0x01, // 1 position
(byte) 0xff, (byte) 0xff, (byte) 0xff,
(byte) 0x20, // 4 bytes (32 bits)
0x00, 0x00, 0x00, 0x02, // 2 positions
0x01, 0x00, 0x00, 0x00, // positions
0x01, 0x00, 0x00, 0x01, //
0x00, // No more positions
0x00, 0x00, 0x00, 0x64, // size 100
0x00, 0x00, 0x01, (byte) 0x90, // size 400
0x00, 0x00, 0x00, (byte) 0xc8, // size 200
0x00, 0x00, 0x00, 0x00 // 64bit sizes counter
};
byte[] output = out.toByteArray();
assertArrayEquals(expected, output);
}
@Test
public void write_64bitsSize() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 0);
List<PackedObjectInfo> objs = new ArrayList<>();
objs.add(blobWithSize(100));
objs.add(blobWithSize(3 * GB));
objs.add(blobWithSize(4 * GB));
objs.add(blobWithSize(400));
writer.write(objs);
byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
0x01, // version
0x00, 0x00, 0x00, 0x00, // minimum object size
0x00, 0x00, 0x00, 0x04, // Object count
0x18, // Unsigned 3 byte positions
0x00, 0x00, 0x00, 0x04, // 4 positions
0x00, 0x00, 0x00, // positions
0x00, 0x00, 0x01, //
0x00, 0x00, 0x02, //
0x00, 0x00, 0x03, //
0x00, // No more positions
0x00, 0x00, 0x00, 0x64, // size 100
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, // -1 (3GB)
(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xfe, // -2 (4GB)
0x00, 0x00, 0x01, (byte) 0x90, // size 400
0x00, 0x00, 0x00, (byte) 0x02, // 64bit sizes counter (2)
0x00, 0x00, 0x00, 0x00, // size 3Gb
(byte) 0xc0, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x01, // size 4GB
(byte) 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x00, 0x00 // 128bit sizes counter
};
byte[] output = out.toByteArray();
assertArrayEquals(expected, output);
}
@Test
public void write_allObjectsTooSmall() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 1 << 10);
List<PackedObjectInfo> objs = new ArrayList<>();
// too small blobs
objs.add(blobWithSize(100));
objs.add(blobWithSize(200));
objs.add(blobWithSize(400));
writer.write(objs);
byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
0x01, // version
0x00, 0x00, 0x04, 0x00, // minimum object size
0x00, 0x00, 0x00, 0x00, // Object count
};
byte[] output = out.toByteArray();
assertArrayEquals(expected, output);
}
@Test
public void write_onlyBlobsIndexed() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 0);
List<PackedObjectInfo> objs = new ArrayList<>();
objs.add(objInfo(Constants.OBJ_COMMIT, 1000));
objs.add(blobWithSize(100));
objs.add(objInfo(Constants.OBJ_TAG, 1000));
objs.add(blobWithSize(400));
objs.add(blobWithSize(200));
writer.write(objs);
byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
0x01, // version
0x00, 0x00, 0x00, 0x00, // minimum object size
0x00, 0x00, 0x00, 0x03, // Object count
0x18, // Positions in 3 bytes
0x00, 0x00, 0x00, 0x03, // 3 entries
0x00, 0x00, 0x01, // positions
0x00, 0x00, 0x03, //
0x00, 0x00, 0x04, //
0x00, // No more positions
0x00, 0x00, 0x00, 0x64, // size 100
0x00, 0x00, 0x01, (byte) 0x90, // size 400
0x00, 0x00, 0x00, (byte) 0xc8, // size 200
0x00, 0x00, 0x00, 0x00 // 64bit sizes counter
};
byte[] output = out.toByteArray();
assertArrayEquals(expected, output);
}
@Test
public void write_noObjects() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 0);
List<PackedObjectInfo> objs = new ArrayList<>();
writer.write(objs);
byte[] expected = new byte[] { -1, 's', 'i', 'z', // header
0x01, // version
0x00, 0x00, 0x00, 0x00, // minimum object size
0x00, 0x00, 0x00, 0x00, // Object count
};
byte[] output = out.toByteArray();
assertArrayEquals(expected, output);
}
@Test
public void read_empty() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 0);
List<PackedObjectInfo> objs = new ArrayList<>();
writer.write(objs);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
assertEquals(-1, index.getSize(0));
assertEquals(-1, index.getSize(1));
assertEquals(-1, index.getSize(1 << 30));
assertEquals(0, index.getThreshold());
}
@Test
public void read_only24bitsPositions() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 0);
List<PackedObjectInfo> objs = new ArrayList<>();
objs.add(blobWithSize(100));
objs.add(blobWithSize(200));
objs.add(blobWithSize(400));
objs.add(blobWithSize(1500));
writer.write(objs);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
assertEquals(100, index.getSize(0));
assertEquals(200, index.getSize(1));
assertEquals(400, index.getSize(2));
assertEquals(1500, index.getSize(3));
assertEquals(0, index.getThreshold());
}
@Test
public void read_only32bitsPositions() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 2000);
List<PackedObjectInfo> objs = new ArrayList<>();
PackedObjectInfo smallObj = blobWithSize(100);
for (int i = 0; i <= MAX_24BITS_UINT; i++) {
objs.add(smallObj);
}
objs.add(blobWithSize(1000));
objs.add(blobWithSize(3000));
objs.add(blobWithSize(2500));
objs.add(blobWithSize(1000));
writer.write(objs);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
assertEquals(-1, index.getSize(5));
assertEquals(-1, index.getSize(MAX_24BITS_UINT+1));
assertEquals(3000, index.getSize(MAX_24BITS_UINT+2));
assertEquals(2500, index.getSize(MAX_24BITS_UINT+3));
assertEquals(-1, index.getSize(MAX_24BITS_UINT+4)); // Not indexed
assertEquals(2000, index.getThreshold());
}
@Test
public void read_24and32BitsPositions() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 2000);
List<PackedObjectInfo> objs = new ArrayList<>();
PackedObjectInfo smallObj = blobWithSize(100);
for (int i = 0; i <= MAX_24BITS_UINT; i++) {
if (i == 500 || i == 1000 || i == 1500) {
objs.add(blobWithSize(2500));
continue;
}
objs.add(smallObj);
}
objs.add(blobWithSize(3000));
objs.add(blobWithSize(1000));
objs.add(blobWithSize(2500));
objs.add(blobWithSize(1000));
writer.write(objs);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
// 24 bit positions
assertEquals(-1, index.getSize(5));
assertEquals(2500, index.getSize(500));
// 32 bit positions
assertEquals(3000, index.getSize(MAX_24BITS_UINT+1));
assertEquals(-1, index.getSize(MAX_24BITS_UINT+2));
assertEquals(2500, index.getSize(MAX_24BITS_UINT+3));
assertEquals(-1, index.getSize(MAX_24BITS_UINT+4)); // Not indexed
assertEquals(2000, index.getThreshold());
}
@Test
public void read_only64bits() throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, 0);
List<PackedObjectInfo> objs = new ArrayList<>();
objs.add(blobWithSize(3 * GB));
objs.add(blobWithSize(8 * GB));
writer.write(objs);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
assertEquals(3 * GB, index.getSize(0));
assertEquals(8 * GB, index.getSize(1));
assertEquals(0, index.getThreshold());
}
@Test
public void read_withMinSize() throws IOException {
int minSize = 1000;
ByteArrayOutputStream out = new ByteArrayOutputStream();
PackObjectSizeIndexWriter writer = PackObjectSizeIndexWriter
.createWriter(out, minSize);
List<PackedObjectInfo> objs = new ArrayList<>();
objs.add(blobWithSize(3 * GB));
objs.add(blobWithSize(1500));
objs.add(blobWithSize(500));
objs.add(blobWithSize(1000));
objs.add(blobWithSize(2000));
writer.write(objs);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
PackObjectSizeIndex index = PackObjectSizeIndexLoader.load(in);
assertEquals(3 * GB, index.getSize(0));
assertEquals(1500, index.getSize(1));
assertEquals(-1, index.getSize(2));
assertEquals(1000, index.getSize(3));
assertEquals(2000, index.getSize(4));
assertEquals(minSize, index.getThreshold());
}
private static PackedObjectInfo blobWithSize(long size) {
return objInfo(Constants.OBJ_BLOB, size);
}
private static PackedObjectInfo objInfo(int type, long size) {
PackedObjectInfo objectInfo = new PackedObjectInfo(OBJ_ID);
objectInfo.setType(type);
objectInfo.setFullSize(size);
return objectInfo;
}
}