authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-04-10 02:01:11 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-04-10 02:01:11 -07:00
logce6630e5778fc3deb1c3cf0c2cd76d3796bff2ee
tree9cef155fb629978bf43c18e843e8b83d7185f80f
parent5a5c5a4d3fecd3ec489ff57474ddecd8b86aa845
signaturebadge-check Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

Repository: enhance getObject to read .idx and .pack files


1 files changed, 122 insertions(+), 2 deletions(-)

git.zig+122-2
...@@ -976,6 +976,8 @@ pub const Repository = struct {...@@ -976,6 +976,8 @@ pub const Repository = struct {
976 gitdir: nfs.Dir,976 gitdir: nfs.Dir,
977 gpa: std.mem.Allocator,977 gpa: std.mem.Allocator,
978 raw_object_contents: std.StringArrayHashMapUnmanaged([]const u8),978 raw_object_contents: std.StringArrayHashMapUnmanaged([]const u8),
979 idx_content: std.StringArrayHashMapUnmanaged([]const u8),
980 pack_content: std.StringArrayHashMapUnmanaged([]const u8),
979 commits: std.StringArrayHashMapUnmanaged(Commit),981 commits: std.StringArrayHashMapUnmanaged(Commit),
980 trees: std.StringArrayHashMapUnmanaged(Tree),982 trees: std.StringArrayHashMapUnmanaged(Tree),
981983
...@@ -984,6 +986,8 @@ pub const Repository = struct {...@@ -984,6 +986,8 @@ pub const Repository = struct {
984 .gitdir = gitdir,986 .gitdir = gitdir,
985 .gpa = gpa,987 .gpa = gpa,
986 .raw_object_contents = .empty,988 .raw_object_contents = .empty,
989 .idx_content = .empty,
990 .pack_content = .empty,
987 .commits = .empty,991 .commits = .empty,
988 .trees = .empty,992 .trees = .empty,
989 };993 };
...@@ -992,12 +996,16 @@ pub const Repository = struct {...@@ -992,12 +996,16 @@ pub const Repository = struct {
992 pub fn deinit(r: *Repository) void {996 pub fn deinit(r: *Repository) void {
993 for (r.raw_object_contents.values()) |v| r.gpa.free(v);997 for (r.raw_object_contents.values()) |v| r.gpa.free(v);
994 r.raw_object_contents.deinit(r.gpa);998 r.raw_object_contents.deinit(r.gpa);
999 for (r.idx_content.values()) |v| nfs.munmap(v);
1000 r.idx_content.deinit(r.gpa);
1001 for (r.pack_content.values()) |v| nfs.munmap(v);
1002 r.pack_content.deinit(r.gpa);
995 r.commits.deinit(r.gpa);1003 r.commits.deinit(r.gpa);
996 for (r.trees.values()) |v| r.gpa.free(v.children);1004 for (r.trees.values()) |v| r.gpa.free(v.children);
997 r.trees.deinit(r.gpa);1005 r.trees.deinit(r.gpa);
998 }1006 }
9991007
1000 fn getObject(r: *Repository, obj: Id) !?[]const u8 {1008 fn getObject(r: *Repository, obj: Id, arena: std.mem.Allocator) !?[]const u8 {
1001 if (r.raw_object_contents.get(obj)) |content| {1009 if (r.raw_object_contents.get(obj)) |content| {
1002 return content;1010 return content;
1003 }1011 }
...@@ -1019,11 +1027,123 @@ pub const Repository = struct {...@@ -1019,11 +1027,123 @@ pub const Repository = struct {
1019 try r.raw_object_contents.put(r.gpa, obj, content);1027 try r.raw_object_contents.put(r.gpa, obj, content);
1020 return content;1028 return content;
1021 }1029 }
1030
1031 // read .idx
1032 if (r.idx_content.count() == 0) {
1033 const packdir = try r.gitdir.openDir("objects/pack", .{});
1034 defer packdir.close();
1035 var iter = packdir.iterate();
1036 while (try iter.next()) |entry| {
1037 if (entry.type != .REG) continue;
1038 if (!std.mem.endsWith(u8, entry.name, ".idx")) continue;
1039 std.log.debug("packdir iterate: {s}", .{entry.name});
1040
1041 const idx_file = try packdir.openFile(entry.name, .{});
1042 defer idx_file.close();
1043 const idx_content = try idx_file.mmap();
1044 errdefer nfs.munmap(idx_content);
1045 try r.idx_content.put(
1046 r.gpa,
1047 try arena.dupe(u8, entry.name),
1048 idx_content,
1049 );
1050 }
1051 }
1052 const pack_index, const pack_offset = blk: for (r.idx_content.keys(), r.idx_content.values()) |idx_path, idx_content| {
1053 if (std.mem.startsWith(u8, idx_content, "\xfftOc")) {
1054 std.debug.assert(std.mem.readInt(u32, idx_content[4..][0..4], .big) == 2);
1055 const flfo_table_bytes = idx_content[8..][0 .. 256 * 4];
1056 const object_count = std.mem.readInt(u32, flfo_table_bytes[255 * 4 ..][0..4], .big);
1057 const name_bytes = idx_content[8..][1024..][0 .. object_count * 20];
1058 const crc32_bytes = idx_content[8..][1024..][name_bytes.len..][0 .. object_count * 4];
1059 const offset_bytes = idx_content[8..][1024..][name_bytes.len..][crc32_bytes.len..][0 .. object_count * 4];
1060 for (0..object_count) |i| {
1061 const object_id = &extras.to_hex(name_bytes[i * 20 ..][0..20].*);
1062 const pack_offset = std.mem.readInt(u32, offset_bytes[i * 4 ..][0..4], .big);
1063 if (std.mem.eql(u8, object_id, obj)) {
1064 std.log.debug("found {s} in {s} at offset {d}", .{ obj, idx_path, pack_offset });
1065 const pack_index = r.pack_content.getIndex(idx_path) orelse clk: {
1066 var pack_path: [128]u8 = @splat(0);
1067 @memcpy(pack_path[0..13], "objects/pack/");
1068 @memcpy(pack_path[13..][0..idx_path.len], idx_path);
1069 @memcpy(pack_path[13..][idx_path.len - 4 ..][0..5], ".pack");
1070 const pack_name_nidx = std.mem.indexOfScalar(u8, &pack_path, 0).?;
1071 const pack_file = try r.gitdir.openFile(pack_path[0..pack_name_nidx :0], .{});
1072 defer pack_file.close();
1073 const pack_content = try pack_file.mmap();
1074 errdefer nfs.munmap(pack_content);
1075 try r.pack_content.put(r.gpa, idx_path, pack_content);
1076 break :clk r.pack_content.count() - 1;
1077 };
1078 break :blk .{ pack_index, pack_offset };
1079 }
1080 }
1081 } else {
1082 return error.Version1Idx;
1083 }
1084 } else return null;
1085
1086 // parse .pack
1087 std.log.warn("pack_index={d} pack_offset={d}", .{ pack_index, pack_offset });
1088
1089 const pack_content = r.pack_content.values()[pack_index];
1090 if (!std.mem.eql(u8, pack_content[0..4], "PACK")) return error.InvalidGitPack;
1091 const pack_version = std.mem.readInt(u32, pack_content[4..][0..4], .big);
1092 switch (pack_version) {
1093 2 => {
1094 const packedobj_content = pack_content[pack_offset..];
1095 // var packedobj_fbs = std.ArrayListUnmanaged(u8).initBuffer(packedobj_content);
1096 var packedobj_fbs = nio.FixedBufferStream([]const u8).init(packedobj_content);
1097
1098 const PackedObjType = enum(u3) { none, commit, tree, blob, tag, reserved, ofs_delta, ref_delta };
1099
1100 var c: usize = packedobj_fbs.takeByte();
1101 const ty: PackedObjType = @enumFromInt((c >> 4) & 7);
1102 var size: usize = c & 15;
1103 var shift: u6 = 4;
1104 while (c & 0x80 > 0) {
1105 c = packedobj_fbs.takeByte();
1106 size += (c & 0x7f) << shift;
1107 shift += 7;
1108 }
1109 std.log.warn("type={s} size={d}", .{ @tagName(ty), size });
1110
1111 switch (ty) {
1112 .none, .reserved => {
1113 return null;
1114 },
1115 .commit, .tree, .blob, .tag => {
1116 const compressed_content = packedobj_fbs.rest()[0..size];
1117 var bufr = nio.FixedBufferStream([]const u8).init(compressed_content);
1118 var list: std.ArrayList(u8) = .init(r.gpa);
1119 errdefer list.deinit();
1120 try list.ensureUnusedCapacity(512);
1121 try list.appendSlice(@tagName(ty));
1122 try list.writer().print(" {d}\x00", .{size});
1123 try std.compress.flate.inflate.decompress(.zlib, bufr.anyReadable(), list.writer());
1124 const content = try list.toOwnedSlice();
1125 try r.raw_object_contents.put(r.gpa, obj, content);
1126 return content;
1127 },
1128 .ofs_delta => {
1129 return null;
1130 },
1131 .ref_delta => {
1132 return null;
1133 },
1134 }
1135 comptime unreachable;
1136 },
1137 3 => {
1138 return error.ReservedPackVersion;
1139 },
1140 else => return error.InvalidGitPack,
1141 }
1022 return null;1142 return null;
1023 }1143 }
10241144
1025 fn getGitObject(r: *Repository, obj: Id, arena: std.mem.Allocator) !?GitObject {1145 fn getGitObject(r: *Repository, obj: Id, arena: std.mem.Allocator) !?GitObject {
1026 if (try r.getObject(obj)) |data| {1146 if (try r.getObject(obj, arena)) |data| {
1027 const header = data[0..std.mem.indexOfScalar(u8, data, 0).?];1147 const header = data[0..std.mem.indexOfScalar(u8, data, 0).?];
1028 const _type = header[0..std.mem.indexOfScalar(u8, header, ' ').?];1148 const _type = header[0..std.mem.indexOfScalar(u8, header, ' ').?];
1029 const content_len = try extras.parseDigits(u64, header[_type.len + 1 ..], 10);1149 const content_len = try extras.parseDigits(u64, header[_type.len + 1 ..], 10);