+ pub fn parseIndex(self: *PackFile) !void {
+ const idxReader = self.idxFile.reader().any();
+
+ var fanoutTable: [256]u32 = undefined;
+
+ for (0..256) |i| {
+ try self.idxFile.seekTo(8 + i * 4);
+ fanoutTable[i] = try idxReader.readVarInt(u32, .big, 4);
+
+ const numObjects =
+ if (i > 0) fanoutTable[i] - fanoutTable[i - 1] else fanoutTable[i];
+
+ for (0..numObjects) |j| {
+ const idOffset =
+ 4 + 4 + 4 * 256 + (j + if (i > 0) fanoutTable[i - 1] else 0) * 20;
+ try self.idxFile.seekTo(idOffset);
+ const id = try idxReader.readVarInt(Id, .big, 20);
+
+ try self.objectOffsets.put(id, 0);
+ }
+ }
+
+ const numObjects = self.objectOffsets.keys().len;
+ for (0..numObjects) |i| {
+ const offsetOffset =
+ 4 + 4 + 4 * 256 + numObjects * (20 + 4) + i * 4;
+ try self.idxFile.seekTo(offsetOffset);
+ const offset = try idxReader.readVarInt(u32, .big, 4);
+
+ self.objectOffsets.values()[i] = offset;
+ }
+ }
+
+ fn getSize(reader: Reader, ignoreTypeBits: bool) !struct { size: u64, bytelen: u64 } {
+ var size: u64 = 0;
+ var counter: u6 = 0;
+ while (true) {
+ const byte = try reader.readByte();
+
+ if (counter == 0) {
+ if (ignoreTypeBits) {
+ const bits: u4 = @truncate(byte);
+ size = bits;
+ } else {
+ const bits: u7 = @truncate(byte);
+ size = bits;
+ }
+ } else {
+ if (ignoreTypeBits) {
+ const bits: u7 = @truncate(byte);
+ size += @as(u64, bits) << (7 * (counter - 1) + 4);
+ } else {
+ const bits: u7 = @truncate(byte);
+ size += @as(u64, bits) << (7 * (counter));
+ }
+ }
+
+ if (byte & 0b10000000 == 0) {
+ break;
+ }
+
+ counter += 1;
+ }
+
+ const nBytes = counter + 1;
+
+ return .{
+ .size = size,
+ .bytelen = nBytes,
+ };
+ }
+
+ fn getOffset(reader: Reader) !struct { offset: u64, bytelen: u64 } {
+ var offset: u64 = 0;
+ var counter: u4 = 0;
+ while (true) {
+ const byte = try reader.readByte();
+
+ const bits: u7 = @truncate(byte);
+ offset <<= 7;
+ offset += @as(u64, bits);
+
+ if (byte & 0b10000000 == 0) {
+ break;
+ }
+
+ counter += 1;
+ }
+
+ const nBytes = counter + 1;
+
+ if (nBytes >= 2) {
+ for (1..nBytes) |i| {
+ offset += std.math.pow(u64, 2, 7 * i);
+ }
+ }
+ return .{
+ .offset = offset,
+ .bytelen = nBytes,
+ };
+ }
+
+ fn applyDelta(alloc: Alloc, baseData: []const u8, deltData: []const u8) ![]u8 {
+ var fbs = std.io.fixedBufferStream(deltData);
+ const deltDataReader = fbs.reader().any();
+ const baseObjectSize = try getSize(deltDataReader, false);
+ const resultObjectSize = try getSize(deltDataReader, false);
+ const deltaDataOffset = baseObjectSize.bytelen + resultObjectSize.bytelen;
+
+ const result = try alloc.alloc(u8, resultObjectSize.size);
+ var resultCounter: u64 = 0;
+
+ var counter: u64 = 0;
+ while (true) {
+ const b = deltData[deltaDataOffset + counter];
+
+ if (b & 0b10000000 != 0) {
+ var dataOffset: u64 = 0;
+ var dataSize: u64 = 0;
+ var bitsSet: u8 = 0;
+ for (0..4) |i| { // offset bits
+ if (b & (@as(u64, 1) << @min(3, i)) != 0) {
+ dataOffset += @as(u64, deltData[deltaDataOffset + counter + 1 + bitsSet]) << @min(3 * 8, i * 8);
+ bitsSet += 1;
+ }
+ }
+ for (4..7) |i| { // size bits
+ if (b & (@as(u64, 1) << @min(6, i)) != 0) {
+ dataSize += @as(u64, deltData[deltaDataOffset + counter + 1 + bitsSet]) << @min(6 * 8, (i - 4) * 8);
+ bitsSet += 1;
+ }
+ }
+ counter += bitsSet;
+
+ if (dataSize == 0)
+ dataSize = 0x10000;
+
+ std.mem.copyForwards(
+ u8,
+ result[resultCounter..result.len],
+ baseData[dataOffset .. dataOffset + dataSize],
+ );
+
+ resultCounter += dataSize;
+ } else {
+ const dataSize: u7 = @truncate(b);
+
+ std.mem.copyForwards(
+ u8,
+ result[resultCounter..result.len],
+ deltData[deltaDataOffset + counter + 1 .. deltaDataOffset + counter + 1 + dataSize],
+ );
+
+ resultCounter += dataSize;
+ counter += dataSize;
+ }
+
+ counter += 1;
+ if (deltaDataOffset + counter >= deltData.len)
+ break;
+ }
+
+ return result;
+ }
+
+ fn ofsDelta(self: *PackFile, offset: i64) anyerror!Object {
+ const pckReader = self.pckFile.reader().any();
+
+ const pos = try self.pckFile.getPos();
+
+ try self.pckFile.seekBy(-offset);
+ const baseObject = try self.readObject(pckReader);
+ defer self.alloc.free(baseObject.data);
+
+ try self.pckFile.seekTo(pos);
+ const deltaData = try decompress(self.alloc, pckReader);
+ defer self.alloc.free(deltaData);
+
+ const objectData = try applyDelta(self.alloc, baseObject.data, deltaData);
+ return Object.init(baseObject.kind, objectData);
+ }
+
+ fn readObject(self: *PackFile, reader: Reader) anyerror!Object {
+ const firstByte = try reader.readByte();
+ const objectKind: u3 = @truncate(firstByte >> 4);
+ try self.pckFile.seekBy(-1);
+ const objectSize = try getSize(reader, true);
+
+ if (objectKind == 6) {
+ const offset = try getOffset(reader);
+
+ return try self.ofsDelta(
+ @intCast(offset.offset + objectSize.bytelen + offset.bytelen),
+ );
+ } else {
+ const objectData = try decompress(self.alloc, reader);
+ return Object.init(objectKind, objectData);
+ }
+ }
+
+ pub fn getObject(self: *PackFile, id: Id) !?Object {
+ if (self.objectOffsets.get(id)) |offset| {
+ const pckReader = self.pckFile.reader().any();
+ try self.pckFile.seekTo(offset);
+
+ const o = try self.readObject(pckReader);
+
+ return o;
+ }
+ return null;
+ }