authorgravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-04-21 21:56:00 -07:00
committergravatar for hello@nektro.netMeghan Denny <hello@nektro.net> 2026-04-21 21:56:00 -07:00
log220ea9f68ae2fadb0ec3985228259467e001ba94
tree3803c317c760ab1fbc5d17413d276fb35f86c78d
parent487852c217fd23097a0dba82efa6edfaed13a9d4
signaturebadge-check Signed by SSH key SHA256:4hHJbtBRU58AYXwjL7fkz2fnQHdiye8x1QpTCQ0sHNw

Tree: replace children with MultiArrayList to enable binary search in get

only sort folders with more than 1000 entries the speedup to search has to be worth the time to sort

1 files changed, 38 insertions(+), 14 deletions(-)

git.zig+38-14
...@@ -270,15 +270,19 @@ fn parseTreeMode(input: string) !Tree.Object.Mode {...@@ -270,15 +270,19 @@ fn parseTreeMode(input: string) !Tree.Object.Mode {
270}270}
271271
272pub const Tree = struct {272pub const Tree = struct {
273 children: []const Object,273 children: std.MultiArrayList(Object).Slice,
274274
275 pub fn get(self: Tree, name: string) ?Object {275 pub fn get(self: Tree, name: string) ?Object {
276 for (self.children) |item| {276 if (self.children.len <= 1000) {
277 if (std.mem.eql(u8, item.name, name)) {277 for (self.children.items(.name), 0..) |a_name, i| {
278 return item;278 if (std.mem.eql(u8, a_name, name)) {
279 return self.children.get(i);
280 }
279 }281 }
282 return null;
280 }283 }
281 return null;284 const i = std.sort.binarySearch([]const u8, self.children.items(.name), name, extras.compareFnSlice(u8)) orelse return null;
285 return self.children.get(i);
282 }286 }
283287
284 pub fn getBlob(self: Tree, name: string) ?Object {288 pub fn getBlob(self: Tree, name: string) ?Object {
...@@ -288,9 +292,9 @@ pub const Tree = struct {...@@ -288,9 +292,9 @@ pub const Tree = struct {
288 }292 }
289293
290 pub fn find(self: Tree, name: string) ?Object {294 pub fn find(self: Tree, name: string) ?Object {
291 for (self.children) |item| {295 for (self.children.items(.name), 0..) |item, i| {
292 if (std.ascii.eqlIgnoreCase(item.name, name)) {296 if (std.ascii.eqlIgnoreCase(item, name)) {
293 return item;297 return self.children.get(i);
294 }298 }
295 }299 }
296 return null;300 return null;
...@@ -860,7 +864,7 @@ pub const Repository = struct {...@@ -860,7 +864,7 @@ pub const Repository = struct {
860 for (r.pack_content.values()) |v| nfs.munmap(v);864 for (r.pack_content.values()) |v| nfs.munmap(v);
861 r.pack_content.deinit(r.gpa);865 r.pack_content.deinit(r.gpa);
862 r.commits.deinit(r.gpa);866 r.commits.deinit(r.gpa);
863 for (r.trees.values()) |v| r.gpa.free(v.children);867 for (r.trees.values()) |*v| v.children.deinit(r.gpa);
864 r.trees.deinit(r.gpa);868 r.trees.deinit(r.gpa);
865 r.tags.deinit(r.gpa);869 r.tags.deinit(r.gpa);
866 }870 }
...@@ -1192,8 +1196,9 @@ pub const Repository = struct {...@@ -1192,8 +1196,9 @@ pub const Repository = struct {
1192 }1196 }
1193 if (try r.getObject(arena, id.id)) |obj| {1197 if (try r.getObject(arena, id.id)) |obj| {
1194 if (obj.type == .tree) {1198 if (obj.type == .tree) {
1195 var children = std.ArrayList(Tree.Object).init(r.gpa);1199 var children: std.MultiArrayList(Tree.Object) = .empty;
1196 errdefer children.deinit();1200 errdefer children.deinit(r.gpa);
1201 try children.ensureUnusedCapacity(r.gpa, 33);
1197 var i: usize = 0;1202 var i: usize = 0;
1198 while (i < obj.content.len) {1203 while (i < obj.content.len) {
1199 const mode_end = std.mem.indexOfScalar(u8, obj.content[i..], ' ').?;1204 const mode_end = std.mem.indexOfScalar(u8, obj.content[i..], ' ').?;
...@@ -1213,7 +1218,7 @@ pub const Repository = struct {...@@ -1213,7 +1218,7 @@ pub const Repository = struct {
1213 @memcpy(mode_buf[6 - mode.len ..], mode);1218 @memcpy(mode_buf[6 - mode.len ..], mode);
1214 const mode_real = try parseTreeMode(&mode_buf);1219 const mode_real = try parseTreeMode(&mode_buf);
12151220
1216 try children.append(.{1221 try children.append(r.gpa, .{
1217 .mode = mode_real,1222 .mode = mode_real,
1218 .name = name,1223 .name = name,
1219 .id = switch (mode_real.type) {1224 .id = switch (mode_real.type) {
...@@ -1225,7 +1230,26 @@ pub const Repository = struct {...@@ -1225,7 +1230,26 @@ pub const Repository = struct {
1225 },1230 },
1226 });1231 });
1227 }1232 }
1228 const tree: Tree = .{ .children = try children.toOwnedSlice() };1233
1234 const S = struct {
1235 list: *std.MultiArrayList(Tree.Object),
1236
1237 pub fn lessThan(s: @This(), a: usize, b: usize) bool {
1238 const items = s.list.items(.name);
1239 return std.mem.order(u8, items[a], items[b]) == .lt;
1240 }
1241 pub fn swap(s: @This(), a: usize, b: usize) void {
1242 // Remove after updating to Zig 0.17. Ref: https://codeberg.org/ziglang/zig/pulls/32016
1243 inline for (@typeInfo(Tree.Object).@"struct".fields) |field| {
1244 const its = s.list.items(@field(std.MultiArrayList(Tree.Object).Field, field.name));
1245 std.mem.swap(@FieldType(Tree.Object, field.name), &its[a], &its[b]);
1246 }
1247 }
1248 };
1249 if (children.len > 1000) std.mem.sortContext(0, children.len, S{ .list = &children });
1250
1251 var tree: Tree = .{ .children = children.toOwnedSlice() };
1252 errdefer tree.children.deinit(r.gpa);
1229 try r.trees.put(r.gpa, id.id, tree);1253 try r.trees.put(r.gpa, id.id, tree);
1230 return .{ id, tree };1254 return .{ id, tree };
1231 }1255 }
...@@ -1342,7 +1366,7 @@ pub const Repository = struct {...@@ -1342,7 +1366,7 @@ pub const Repository = struct {
1342 var found: usize = 0;1366 var found: usize = 0;
1343 var result: std.StringArrayHashMapUnmanaged(CommitId) = .empty;1367 var result: std.StringArrayHashMapUnmanaged(CommitId) = .empty;
1344 defer result.deinit(r.gpa);1368 defer result.deinit(r.gpa);
1345 for (base_tree.children) |item| try result.put(r.gpa, item.name, undefined);1369 for (base_tree.children.items(.name)) |name| try result.put(r.gpa, name, undefined);
13461370
1347 var set: std.bit_set.DynamicBitSetUnmanaged = try .initEmpty(r.gpa, total);1371 var set: std.bit_set.DynamicBitSetUnmanaged = try .initEmpty(r.gpa, total);
1348 defer set.deinit(r.gpa);1372 defer set.deinit(r.gpa);