| ... | ... | @@ -976,6 +976,8 @@ pub const Repository = struct { |
| 976 | 976 | gitdir: nfs.Dir, |
| 977 | 977 | gpa: std.mem.Allocator, |
| 978 | 978 | raw_object_contents: std.StringArrayHashMapUnmanaged([]const u8), |
| 979 | idx_content: std.StringArrayHashMapUnmanaged([]const u8), |
| 980 | pack_content: std.StringArrayHashMapUnmanaged([]const u8), |
| 979 | 981 | commits: std.StringArrayHashMapUnmanaged(Commit), |
| 980 | 982 | trees: std.StringArrayHashMapUnmanaged(Tree), |
| 981 | 983 | |
| ... | ... | @@ -984,6 +986,8 @@ pub const Repository = struct { |
| 984 | 986 | .gitdir = gitdir, |
| 985 | 987 | .gpa = gpa, |
| 986 | 988 | .raw_object_contents = .empty, |
| 989 | .idx_content = .empty, |
| 990 | .pack_content = .empty, |
| 987 | 991 | .commits = .empty, |
| 988 | 992 | .trees = .empty, |
| 989 | 993 | }; |
| ... | ... | @@ -992,12 +996,16 @@ pub const Repository = struct { |
| 992 | 996 | pub fn deinit(r: *Repository) void { |
| 993 | 997 | for (r.raw_object_contents.values()) |v| r.gpa.free(v); |
| 994 | 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 | 1003 | r.commits.deinit(r.gpa); |
| 996 | 1004 | for (r.trees.values()) |v| r.gpa.free(v.children); |
| 997 | 1005 | r.trees.deinit(r.gpa); |
| 998 | 1006 | } |
| 999 | 1007 | |
| 1000 | | fn getObject(r: *Repository, obj: Id) !?[]const u8 { |
| 1008 | fn getObject(r: *Repository, obj: Id, arena: std.mem.Allocator) !?[]const u8 { |
| 1001 | 1009 | if (r.raw_object_contents.get(obj)) |content| { |
| 1002 | 1010 | return content; |
| 1003 | 1011 | } |
| ... | ... | @@ -1019,11 +1027,123 @@ pub const Repository = struct { |
| 1019 | 1027 | try r.raw_object_contents.put(r.gpa, obj, content); |
| 1020 | 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 | 1142 | return null; |
| 1023 | 1143 | } |
| 1024 | 1144 | |
| 1025 | 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 | 1147 | const header = data[0..std.mem.indexOfScalar(u8, data, 0).?]; |
| 1028 | 1148 | const _type = header[0..std.mem.indexOfScalar(u8, header, ' ').?]; |
| 1029 | 1149 | const content_len = try extras.parseDigits(u64, header[_type.len + 1 ..], 10); |