ls but with io_uring

entry: move symlinks to a hashmap

To prepare for other sort options which require the statx field (ie time
sorting), move the symlink data off of Entry to reduce it's size. This
improves sorting speed due to the Entry struct being smaller in memory.
Symlinks are generally rare so using a hashmap should be ok. This was
tested locally in /usr/bin which has a decent amount of symlinks and no
noticeable performance hit was seen as a result of this approach.

rockorager.dev c6c153d5 d13dcf28

verified
+51 -23
+51 -23
src/main.zig
··· 544 544 } 545 545 try writer.writeAll(colors.reset); 546 546 547 - if (entry.kind == .sym_link) { 548 - try writer.writeAll(" -> "); 549 - const color = if (entry.symlink_missing) 550 - colors.symlink_missing 551 - else 552 - colors.symlink_target; 547 + switch (entry.kind) { 548 + .sym_link => { 549 + try writer.writeAll(" -> "); 550 + 551 + const symlink: Symlink = cmd.symlinks.get(entry.name) orelse .{ 552 + .name = "[missing]", 553 + .exists = false, 554 + }; 555 + 556 + const color = if (symlink.exists) colors.symlink_target else colors.symlink_missing; 557 + 558 + try writer.writeAll(color); 559 + if (cmd.opts.useHyperlinks() and symlink.exists) { 560 + try writer.print("\x1b]8;;file://{s}\x1b\\", .{symlink.name}); 561 + try writer.writeAll(symlink.name); 562 + try writer.writeAll("\x1b]8;;\x1b\\"); 563 + } else { 564 + try writer.writeAll(symlink.name); 565 + } 566 + try writer.writeAll(colors.reset); 567 + }, 553 568 554 - try writer.writeAll(color); 555 - if (cmd.opts.useHyperlinks()) { 556 - try writer.print("\x1b]8;;file://{s}\x1b\\", .{entry.link_name}); 557 - try writer.writeAll(entry.link_name); 558 - try writer.writeAll("\x1b]8;;\x1b\\"); 559 - } else { 560 - try writer.writeAll(entry.link_name); 561 - } 562 - try writer.writeAll(colors.reset); 569 + else => {}, 563 570 } 564 571 565 572 try writer.writeAll("\r\n"); ··· 571 578 opts: Options = .{}, 572 579 entries: []Entry = &.{}, 573 580 entry_idx: usize = 0, 581 + symlinks: std.StringHashMapUnmanaged(Symlink) = .empty, 574 582 575 583 tz: ?zeit.TimeZone = null, 576 584 groups: std.ArrayListUnmanaged(Group) = .empty, ··· 637 645 } 638 646 }; 639 647 648 + const Symlink = struct { 649 + name: [:0]const u8, 650 + exists: bool = true, 651 + }; 652 + 640 653 const Entry = struct { 641 654 name: [:0]const u8, 642 655 kind: std.fs.File.Kind, 643 656 statx: ourio.Statx, 644 - link_name: [:0]const u8 = "", 645 - symlink_missing: bool = false, 657 + 658 + fn lessThan(opts: Options, lhs: Entry, rhs: Entry) bool { 659 + if (opts.@"group-directories-first" and 660 + lhs.kind != rhs.kind and 661 + (lhs.kind == .directory or rhs.kind == .directory)) 662 + { 663 + return lhs.kind == .directory; 664 + } 665 + 666 + return std.ascii.lessThanIgnoreCase(lhs.name, rhs.name); 667 + } 646 668 647 669 fn modeStr(self: Entry) [10]u8 { 648 670 var mode = [_]u8{'-'} ** 10; ··· 790 812 791 813 // NOTE: Sadly, we can't do readlink via io_uring 792 814 const link = try posix.readlink(path, &buf); 793 - entry.link_name = try cmd.arena.dupeZ(u8, link); 815 + const symlink: Symlink = .{ .name = try cmd.arena.dupeZ(u8, link) }; 816 + try cmd.symlinks.put(cmd.arena, entry.name, symlink); 794 817 } 795 818 _ = try io.stat(path, &entry.statx, .{ 796 819 .cb = onCompletion, ··· 909 932 }, 910 933 911 934 .stat => { 912 - _ = result.statx catch { 935 + _ = result.statx catch |err| { 913 936 const entry: *Entry = @fieldParentPtr("statx", task.req.statx.result); 914 - if (entry.symlink_missing) { 915 - // we already got here. Just zero out the statx; 937 + const symlink = cmd.symlinks.getPtr(entry.name) orelse return err; 938 + 939 + if (!symlink.exists) { 940 + // We already lstated this and found an error. Just zero out statx and move 941 + // along 916 942 entry.statx = std.mem.zeroInit(ourio.Statx, entry.statx); 917 943 return; 918 944 } 919 945 920 - entry.symlink_missing = true; 946 + symlink.exists = false; 947 + 921 948 _ = try io.lstat(task.req.statx.path, task.req.statx.result, .{ 922 949 .cb = onCompletion, 923 950 .ptr = cmd, ··· 940 967 941 968 // NOTE: Sadly, we can't do readlink via io_uring 942 969 const link = try posix.readlink(path, &buf); 943 - entry.link_name = try cmd.arena.dupeZ(u8, link); 970 + const symlink: Symlink = .{ .name = try cmd.arena.dupeZ(u8, link) }; 971 + try cmd.symlinks.put(cmd.arena, entry.name, symlink); 944 972 } 945 973 _ = try io.stat(path, &entry.statx, .{ 946 974 .cb = onCompletion,