Wtfs: fast bulk stat() with the getattrlistbulk syscall on macOS

I just today started making this. It could be useful for making file system scans faster on Darwin. Without further ado:

A library that provides efficient bulk retrieval of file attributes using the native getattrlistbulk syscall. This allows fetching multiple file attributes for directory entries in a single system call, which is significantly more efficient than individual stat calls.

The library uses Zig’s comptime features to generate type-safe structs based on the attributes you request, ensuring you only pay for what you use.

const std = @import("std");
const wtfs = @import("wtfs");

// Open directory with .iterate flag (required for getattrlistbulk)
const dir = try std.fs.cwd().openDir(".", .{ .iterate = true });
defer dir.close();

// Configure which attributes to retrieve at compile time
const mask = wtfs.AttrGroupMask{
    .common = .{ 
        .name = true,        // File/directory name
        .obj_type = true,    // Object type (file/dir/symlink)
        .file_id = true,     // inode number
    },
    .dir = .{ 
        .entry_count = true,  // Number of entries in directory
    },
    .file = .{ 
        .total_size = true,   // Logical file size
        .alloc_size = true,   // Allocated size on disk
    },
};

// The scanner type is generated at compile time based on your mask
const Scanner = wtfs.DirScanner(mask);

// Provide your own buffer for the syscall results
var buffer: [16384]u8 = undefined;
var scanner = Scanner.init(dir.handle, &buffer);

// Iterate through entries
while (try scanner.next()) |entry| {
    // The entry type has only the fields you requested
    std.debug.print("{s}: ", .{entry.name});
    
    switch (entry.kind) {
        .dir => {
            // dir-specific fields are available when requested
            std.debug.print("directory with {} entries\n", .{entry.details.dir.entrycount});
        },
        .file => {
            // file-specific fields are available when requested
            std.debug.print("file, {}B ({}B allocated)\n", .{
                entry.details.file.totalsize,
                entry.details.file.allocsize,
            });
        },
        .symlink => std.debug.print("symlink\n", .{}),
        .other => std.debug.print("other\n", .{}),
    }
}

I’m making a disk usage scanner! My thread pool work queue stuff is definitely suboptimal and haphazardly coded right now; I expect to be able to make it faster. But it’s really fun to watch it rip through my whole MacBook’s / with the std.Progress live view (thanks @andrewrk, what an awesome module!).