1const std = @import("std");
2const Io = std.Io;
3const assert = std.debug.assert;
4const Allocator = std.mem.Allocator;
5
6const trace = @import("../../tracy.zig").trace;
7
8pub fn ParallelHasher(comptime Hasher: type) type {
9 const hash_size = Hasher.digest_length;
10
11 return struct {
12 pub fn hash(gpa: Allocator, io: Io, file: Io.File, out: [][hash_size]u8, opts: struct {
13 chunk_size: u64 = 0x4000,
14 max_file_size: ?u64 = null,
15 }) !void {
16 const tracy = trace(@src());
17 defer tracy.end();
18
19 const file_size = blk: {
20 const file_size = opts.max_file_size orelse try file.length(io);
21 break :blk std.math.cast(usize, file_size) orelse return error.Overflow;
22 };
23 const chunk_size = std.math.cast(usize, opts.chunk_size) orelse return error.Overflow;
24
25 const buffer = try gpa.alloc(u8, chunk_size * out.len);
26 defer gpa.free(buffer);
27
28 const results = try gpa.alloc(Io.File.ReadPositionalError!usize, out.len);
29 defer gpa.free(results);
30
31 {
32 var group: Io.Group = .init;
33 defer group.cancel(io);
34
35 for (out, results, 0..) |*out_buf, *result, i| {
36 const fstart = i * chunk_size;
37 const fsize = if (fstart + chunk_size > file_size)
38 file_size - fstart
39 else
40 chunk_size;
41 group.async(io, worker, .{
42 io,
43 file,
44 fstart,
45 buffer[fstart..][0..fsize],
46 &(out_buf.*),
47 &(result.*),
48 });
49 }
50
51 try group.await(io);
52 }
53 for (results) |result| _ = try result;
54 }
55
56 fn worker(
57 io: Io,
58 file: Io.File,
59 fstart: usize,
60 buffer: []u8,
61 out: *[hash_size]u8,
62 err: *Io.File.ReadPositionalError!usize,
63 ) void {
64 err.* = file.readPositionalAll(io, buffer, fstart);
65 Hasher.hash(buffer, out, .{});
66 }
67 };
68}