Interface struct ‣ File - chung-leong/zigar GitHub Wiki
The std.fs.File struct provides an interface to a file in Zig. Objects implementing the file interface can be passed to Zig functions accepting this struct as arguments.
A Zig function running in the main thread can only read from a synchronous source. Data must be available immediately, because the main thread runs the event loop and therefore cannot wait. A JavaScript string (for read purpose) or an array (for write purpose) will automatically get converted into a virtual file that satisfies this requirement:
A virtual file descriptor gets created during the conversion process. A close() function is
attached to the original object, allowing you to release the descriptor:
const std = @import("std");
pub fn print(file: std.fs.File) !void {
var write_buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&write_buffer);
const stdout = &stdout_writer.interface;
var read_buffer: [1024]u8 = undefined;
while (true) {
const len = try file.read(&read_buffer);
if (len == 0) break;
_ = try stdout.write(read_buffer[0..len]);
}
try stdout.flush();
}
import { print } from './file-example-1.zig';
const s = Object('Hello world\n');
print(s);
s.close();
Hello world
Alternately, you can free the file on the Zig side:
const std = @import("std");
pub fn print(file: std.fs.File) !void {
defer file.close();
var write_buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&write_buffer);
const stdout = &stdout_writer.interface;
var read_buffer: [1024]u8 = undefined;
while (true) {
const len = try file.read(&read_buffer);
if (len == 0) break;
_ = try stdout.write(read_buffer[0..len]);
}
try stdout.flush();
}
import { print } from './file-example-5.zig';
print('Hello world\n');
Hello world
Using TransformStream
Currently there's no support for using Zig readers and writers from JavaScript. What you can do is
create a pass-thru
TransformStream,
pass its writable end to Zig as a virtual file, then and stream into it. The following example
demonstrates how to decompress a HTTP stream from the
fetch API with the help of
std.compress.flate.Decompress:
const std = @import("std");
const zigar = @import("zigar");
var work_queue: zigar.thread.WorkQueue(worker) = .{};
pub const shutdown = work_queue.promisify(.shutdown);
pub const decompress = work_queue.promisify(worker.decompress);
const worker = struct {
pub fn decompress(src: std.fs.File, dest: std.fs.File) !usize {
var read_buffer: [16 * 1024]u8 = undefined;
var reader = src.reader(&read_buffer);
var decompress_buffer: [std.compress.flate.max_window_len]u8 = undefined;
var decompressor: std.compress.flate.Decompress = .init(&reader.interface, .gzip, &decompress_buffer);
var write_buffer: [16 * 1024]u8 = undefined;
var writer = dest.writer(&write_buffer);
const len = try decompressor.reader.streamRemaining(&writer.interface);
try writer.interface.flush();
return len;
}
};
import { decompress, shutdown } from './file-example-4.zig';
const url = 'https://github.com/chung-leong/zigar/raw/refs/heads/main/zigar-compiler/test/integration/stream-handling/data/test.txt.gz';
try {
const { body } = await fetch(url);
const transform = new TransformStream(undefined, { highWaterMark: 1024 * 16 });
decompress(body, transform.writable).then(() => transform.writable.close());
const decoder = new TextDecoder();
for await (const data of transform.readable) {
console.log(decoder.decode(data));
}
} finally {
await shutdown();
}
Four score and seven years ago ...
Note:
Calling std.fs.File.stat() with a virtual file will crash on Linux on certain platforms due the use of direct syscalls.