Sha256 while compressing a file

How do I get the sha256 of the compressed file while I’m writing it?
I’m pretty sure I should be able to do this without reading everything I have written at the end.

const std = @import("std");

pub fn main() !void {
    var io_impl = std.Io.Threaded.init_single_threaded;
    defer io_impl.deinit();
    const io = io_impl.io();

    // setup input file (just for this example)
    try std.fs.cwd().writeFile(.{ .data = "hello world\n", .sub_path = "hello.txt" });

    const output_file = try std.fs.cwd().createFile("hello.txt.gz", .{});
    defer output_file.close();

    var file_writer_buffer: [4096]u8 = undefined;
    var file_writer = output_file.writer(&file_writer_buffer);

    var compress_buffer: [std.compress.flate.max_window_len]u8 = undefined;
    var compressor = try std.compress.flate.Compress.init(
        &file_writer.interface,
        &compress_buffer,
        .gzip,
        .best,
    );

    var input_file = try std.fs.cwd().openFile("hello.txt", .{});
    defer input_file.close();
    var input_file_buffer: [4096]u8 = undefined;
    var input_file_reader = input_file.reader(io, &input_file_buffer);

    _ = try compressor.writer.sendFileAll(&input_file_reader, .unlimited);
    try compressor.writer.flush();
    try file_writer.interface.flush();
}

Got half way through implementing my own interface it until I found std.Io.Writer.hashed

const std = @import("std");

pub fn main() !void {
    var io_impl = std.Io.Threaded.init_single_threaded;
    defer io_impl.deinit();
    const io = io_impl.io();

    // setup input file (just for this example)
    try std.fs.cwd().writeFile(.{ .data = "hello world\n", .sub_path = "hello.txt" });

    const output_file = try std.fs.cwd().createFile("hello.txt.gz", .{});
    defer output_file.close();

    var file_writer_buffer: [4096]u8 = undefined;
    var file_writer = output_file.writer(&file_writer_buffer);

    var hashed_buffer: [4096]u8 = undefined;
    var hasher = std.crypto.hash.sha2.Sha256.init(.{});
    var hashed = file_writer.interface.hashed(&hasher, &hashed_buffer);

    var compress_buffer: [std.compress.flate.max_window_len]u8 = undefined;
    var compressor = try std.compress.flate.Compress.init(
        &hashed.writer,
        &compress_buffer,
        .gzip,
        .best,
    );

    var input_file = try std.fs.cwd().openFile("hello.txt", .{});
    defer input_file.close();
    var input_file_buffer: [4096]u8 = undefined;
    var input_file_reader = input_file.reader(io, &input_file_buffer);

    _ = try compressor.writer.sendFileAll(&input_file_reader, .unlimited);
    try compressor.writer.flush();
    try hashed.writer.flush();
    try file_writer.interface.flush();

    var hash_result: [32]u8 = undefined;
    hasher.final(hash_result[0..]);
    std.debug.print("hash: sha256:{x}\n", .{hash_result});
}

hash: sha256:691d5610f9f7c327facbf8856c5293c7a741b8ad2c4fa31775f3cca51c62e9dd

hash is correct according to

6 Likes

What’s even cooler is that my use case also requires the uncompressed hash. So I can insert another hasher and get that!

1 Like