std.tar.iterator: make the buffers configurable

Provides more API flexibility and correctness, while still preserving
the handy high level `pipeToFileSystem` API.
This commit is contained in:
Andrew Kelley 2024-02-28 20:32:09 -07:00
parent b3ad45f267
commit beca85e644
2 changed files with 41 additions and 14 deletions

View File

@ -231,12 +231,27 @@ fn nullStr(str: []const u8) []const u8 {
return str; return str;
} }
pub const IteratorOptions = struct {
/// Use a buffer with length `std.fs.MAX_PATH_BYTES` to match file system capabilities.
file_name_buffer: []u8,
/// Use a buffer with length `std.fs.MAX_PATH_BYTES` to match file system capabilities.
link_name_buffer: []u8,
diagnostics: ?*Diagnostics = null,
pub const Diagnostics = Options.Diagnostics;
};
/// Iterates over files in tar archive. /// Iterates over files in tar archive.
/// `next` returns each file in `reader` tar archive. /// `next` returns each file in `reader` tar archive.
pub fn iterator(reader: anytype, diagnostics: ?*Options.Diagnostics) Iterator(@TypeOf(reader)) { pub fn iterator(reader: anytype, options: IteratorOptions) Iterator(@TypeOf(reader)) {
return .{ return .{
.reader = reader, .reader = reader,
.diagnostics = diagnostics, .diagnostics = options.diagnostics,
.header_buffer = undefined,
.file_name_buffer = options.file_name_buffer,
.link_name_buffer = options.link_name_buffer,
.padding = 0,
.file = undefined,
}; };
} }
@ -246,14 +261,14 @@ fn Iterator(comptime ReaderType: type) type {
diagnostics: ?*Options.Diagnostics, diagnostics: ?*Options.Diagnostics,
// buffers for heeader and file attributes // buffers for heeader and file attributes
header_buffer: [Header.SIZE]u8 = undefined, header_buffer: [Header.SIZE]u8,
file_name_buffer: [1024]u8 = undefined, file_name_buffer: []u8,
link_name_buffer: [1024]u8 = undefined, link_name_buffer: []u8,
// bytes of padding to the end of the block // bytes of padding to the end of the block
padding: usize = 0, padding: usize,
// current tar file // current tar file
file: File = undefined, file: File,
pub const File = struct { pub const File = struct {
name: []const u8, // name of file, symlink or directory name: []const u8, // name of file, symlink or directory
@ -305,7 +320,7 @@ fn Iterator(comptime ReaderType: type) type {
} }
fn initFile(self: *Self) void { fn initFile(self: *Self) void {
self.file = File{ self.file = .{
.name = self.file_name_buffer[0..0], .name = self.file_name_buffer[0..0],
.link_name = self.link_name_buffer[0..0], .link_name = self.link_name_buffer[0..0],
.size = 0, .size = 0,
@ -357,10 +372,10 @@ fn Iterator(comptime ReaderType: type) type {
}, },
// Prefix header types // Prefix header types
.gnu_long_name => { .gnu_long_name => {
self.file.name = try self.readString(@intCast(size), &self.file_name_buffer); self.file.name = try self.readString(@intCast(size), self.file_name_buffer);
}, },
.gnu_long_link => { .gnu_long_link => {
self.file.link_name = try self.readString(@intCast(size), &self.link_name_buffer); self.file.link_name = try self.readString(@intCast(size), self.link_name_buffer);
}, },
.extended_header => { .extended_header => {
// Use just attributes from last extended header. // Use just attributes from last extended header.
@ -370,10 +385,10 @@ fn Iterator(comptime ReaderType: type) type {
while (try rdr.next()) |attr| { while (try rdr.next()) |attr| {
switch (attr.kind) { switch (attr.kind) {
.path => { .path => {
self.file.name = try attr.value(&self.file_name_buffer); self.file.name = try attr.value(self.file_name_buffer);
}, },
.linkpath => { .linkpath => {
self.file.link_name = try attr.value(&self.link_name_buffer); self.file.link_name = try attr.value(self.link_name_buffer);
}, },
.size => { .size => {
var buf: [pax_max_size_attr_len]u8 = undefined; var buf: [pax_max_size_attr_len]u8 = undefined;
@ -536,7 +551,13 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
}, },
} }
var iter = iterator(reader, options.diagnostics); var file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var iter = iterator(reader, .{
.file_name_buffer = &file_name_buffer,
.link_name_buffer = &link_name_buffer,
.diagnostics = options.diagnostics,
});
while (try iter.next()) |file| { while (try iter.next()) |file| {
switch (file.kind) { switch (file.kind) {
.directory => { .directory => {

View File

@ -323,9 +323,15 @@ test "tar run Go test cases" {
}, },
}; };
var file_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
var link_name_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
for (cases) |case| { for (cases) |case| {
var fsb = std.io.fixedBufferStream(case.data); var fsb = std.io.fixedBufferStream(case.data);
var iter = tar.iterator(fsb.reader(), null); var iter = tar.iterator(fsb.reader(), .{
.file_name_buffer = &file_name_buffer,
.link_name_buffer = &link_name_buffer,
});
var i: usize = 0; var i: usize = 0;
while (iter.next() catch |err| { while (iter.next() catch |err| {
if (case.err) |e| { if (case.err) |e| {