mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 08:45:52 +00:00
std.tar.Writer: update reader/writer API usage
This commit is contained in:
parent
24441b184f
commit
e9fd9798f4
@ -202,34 +202,33 @@ fn serveSourcesTar(request: *std.http.Server.Request, context: *Context) !void {
|
||||
var walker = try std_dir.walk(gpa);
|
||||
defer walker.deinit();
|
||||
|
||||
var archiver = std.tar.writer(response.writer());
|
||||
archiver.prefix = "std";
|
||||
var tar_buffer: [@sizeOf(std.tar.Writer.Header)]u8 = undefined;
|
||||
var response_bw = response.writer().buffered(&tar_buffer);
|
||||
var tar_writer: std.tar.Writer = .{ .underlying_writer = &response_bw };
|
||||
tar_writer.prefix = "std";
|
||||
|
||||
while (try walker.next()) |entry| {
|
||||
switch (entry.kind) {
|
||||
.file => {
|
||||
if (!std.mem.endsWith(u8, entry.basename, ".zig"))
|
||||
continue;
|
||||
if (std.mem.endsWith(u8, entry.basename, "test.zig"))
|
||||
continue;
|
||||
if (!std.mem.endsWith(u8, entry.basename, ".zig")) continue;
|
||||
if (std.mem.endsWith(u8, entry.basename, "test.zig")) continue;
|
||||
},
|
||||
else => continue,
|
||||
}
|
||||
var file = try entry.dir.openFile(entry.basename, .{});
|
||||
defer file.close();
|
||||
try archiver.writeFile(entry.path, file);
|
||||
const stat = try file.stat();
|
||||
try tar_writer.writeFile(entry.path, file, stat);
|
||||
}
|
||||
|
||||
{
|
||||
// Since this command is JIT compiled, the builtin module available in
|
||||
// this source file corresponds to the user's host system.
|
||||
const builtin_zig = @embedFile("builtin");
|
||||
archiver.prefix = "builtin";
|
||||
try archiver.writeFileBytes("builtin.zig", builtin_zig, .{});
|
||||
tar_writer.prefix = "builtin";
|
||||
try tar_writer.writeFileBytes("builtin.zig", builtin_zig, .{});
|
||||
}
|
||||
|
||||
// intentionally omitting the pointless trailer
|
||||
//try archiver.finish();
|
||||
try response.end();
|
||||
}
|
||||
|
||||
@ -255,16 +254,27 @@ fn serveWasm(
|
||||
}) catch unreachable) catch unreachable),
|
||||
.output_mode = .Exe,
|
||||
});
|
||||
// std.http.Server does not have a sendfile API yet.
|
||||
const bin_path = try wasm_base_path.join(arena, bin_name);
|
||||
const file_contents = try bin_path.root_dir.handle.readFileAlloc(gpa, bin_path.sub_path, 10 * 1024 * 1024);
|
||||
defer gpa.free(file_contents);
|
||||
try request.respond(file_contents, .{
|
||||
.extra_headers = &.{
|
||||
.{ .name = "content-type", .value = "application/wasm" },
|
||||
cache_control_header,
|
||||
const file = try bin_path.root_dir.handle.openFile(bin_path.sub_path, .{});
|
||||
defer file.close();
|
||||
const content_length = std.math.cast(usize, (try file.stat()).size) orelse return error.FileTooBig;
|
||||
|
||||
var response = try request.respondStreaming(.{
|
||||
.content_length = content_length,
|
||||
.respond_options = .{
|
||||
.extra_headers = &.{
|
||||
.{ .name = "content-type", .value = "application/wasm" },
|
||||
cache_control_header,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
var bw = response.writer().unbuffered();
|
||||
try bw.writeFileAll(file, .{
|
||||
.offset = .zero,
|
||||
.limit = .limited(content_length),
|
||||
});
|
||||
try response.end();
|
||||
}
|
||||
|
||||
const autodoc_root_name = "autodoc";
|
||||
@ -396,8 +406,8 @@ fn receiveWasmMessage(
|
||||
},
|
||||
.error_bundle => {
|
||||
const eb_hdr = try br.takeStructEndian(std.zig.Server.Message.ErrorBundle, .little);
|
||||
const extra_array = try br.readArrayEndianAlloc(arena, u32, eb_hdr.extra_len, .little);
|
||||
const string_bytes = try br.readAlloc(arena, eb_hdr.string_bytes_len);
|
||||
const extra_array = try br.readSliceEndianAlloc(arena, u32, eb_hdr.extra_len, .little);
|
||||
const string_bytes = try br.readSliceAlloc(arena, eb_hdr.string_bytes_len);
|
||||
result_error_bundle.* = .{
|
||||
.string_bytes = string_bytes,
|
||||
.extra = extra_array,
|
||||
|
||||
@ -704,7 +704,7 @@ pub fn BitReader(comptime T: type) type {
|
||||
n += 1;
|
||||
}
|
||||
// Then use forward reader for all other bytes.
|
||||
try self.forward_reader.read(buf[n..]);
|
||||
try self.forward_reader.readSlice(buf[n..]);
|
||||
}
|
||||
|
||||
/// Alias for readF(U, 0).
|
||||
|
||||
@ -51,6 +51,19 @@ pub fn readVec(br: *BufferedReader, data: []const []u8) Reader.Error!usize {
|
||||
return passthruReadVec(br, data);
|
||||
}
|
||||
|
||||
pub fn read(br: *BufferedReader, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {
|
||||
return passthruRead(br, bw, limit);
|
||||
}
|
||||
|
||||
/// "Pump" data from the reader to the writer.
|
||||
pub fn readAll(br: *BufferedReader, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!void {
|
||||
var remaining = limit;
|
||||
while (true) {
|
||||
const n = try passthruRead(br, bw, remaining);
|
||||
remaining = remaining.subtract(n).?;
|
||||
}
|
||||
}
|
||||
|
||||
fn passthruRead(ctx: ?*anyopaque, bw: *BufferedWriter, limit: Reader.Limit) Reader.RwError!usize {
|
||||
const br: *BufferedReader = @alignCast(@ptrCast(ctx));
|
||||
const buffer = br.buffer[0..br.end];
|
||||
@ -134,7 +147,6 @@ pub fn seekForwardBy(br: *BufferedReader, seek_by: u64) !void {
|
||||
///
|
||||
/// See also:
|
||||
/// * `peek`
|
||||
/// * `tryPeekArray`
|
||||
/// * `toss`
|
||||
pub fn peek(br: *BufferedReader, n: usize) Reader.Error![]u8 {
|
||||
assert(n <= br.buffer.len);
|
||||
@ -155,7 +167,6 @@ pub fn peek(br: *BufferedReader, n: usize) Reader.Error![]u8 {
|
||||
///
|
||||
/// See also:
|
||||
/// * `peek`
|
||||
/// * `tryPeekGreedy`
|
||||
/// * `toss`
|
||||
pub fn peekGreedy(br: *BufferedReader, n: usize) Reader.Error![]u8 {
|
||||
assert(n <= br.buffer.len);
|
||||
@ -280,7 +291,7 @@ pub fn discardRemaining(br: *BufferedReader) Reader.ShortError!usize {
|
||||
///
|
||||
/// See also:
|
||||
/// * `peek`
|
||||
pub fn read(br: *BufferedReader, buffer: []u8) Reader.Error!void {
|
||||
pub fn readSlice(br: *BufferedReader, buffer: []u8) Reader.Error!void {
|
||||
const in_buffer = br.buffer[br.seek..br.end];
|
||||
const copy_len = @min(buffer.len, in_buffer.len);
|
||||
@memcpy(buffer[0..copy_len], in_buffer[0..copy_len]);
|
||||
@ -313,7 +324,7 @@ pub fn readShort(br: *BufferedReader, buffer: []u8) Reader.ShortError!usize {
|
||||
|
||||
/// The function is inline to avoid the dead code in case `endian` is
|
||||
/// comptime-known and matches host endianness.
|
||||
pub inline fn readArrayEndianAlloc(
|
||||
pub inline fn readSliceEndianAlloc(
|
||||
br: *BufferedReader,
|
||||
allocator: Allocator,
|
||||
Elem: type,
|
||||
@ -322,17 +333,17 @@ pub inline fn readArrayEndianAlloc(
|
||||
) ReadAllocError![]Elem {
|
||||
const dest = try allocator.alloc(Elem, len);
|
||||
errdefer allocator.free(dest);
|
||||
try read(br, @ptrCast(dest));
|
||||
try readSlice(br, @ptrCast(dest));
|
||||
if (native_endian != endian) std.mem.byteSwapAllFields(Elem, dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
pub const ReadAllocError = Reader.Error || Allocator.Error;
|
||||
|
||||
pub fn readAlloc(br: *BufferedReader, allocator: Allocator, len: usize) ReadAllocError![]u8 {
|
||||
pub fn readSliceAlloc(br: *BufferedReader, allocator: Allocator, len: usize) ReadAllocError![]u8 {
|
||||
const dest = try allocator.alloc(u8, len);
|
||||
errdefer allocator.free(dest);
|
||||
try read(br, dest);
|
||||
try readSlice(br, dest);
|
||||
return dest;
|
||||
}
|
||||
|
||||
|
||||
@ -453,24 +453,24 @@ pub inline fn writeStructEndian(bw: *BufferedWriter, value: anytype, endian: std
|
||||
}
|
||||
}
|
||||
|
||||
pub inline fn writeArrayEndian(
|
||||
pub inline fn writeSliceEndian(
|
||||
bw: *BufferedWriter,
|
||||
Elem: type,
|
||||
array: []const Elem,
|
||||
slice: []const Elem,
|
||||
endian: std.builtin.Endian,
|
||||
) Writer.Error!void {
|
||||
if (native_endian == endian) {
|
||||
return writeAll(bw, @ptrCast(array));
|
||||
return writeAll(bw, @ptrCast(slice));
|
||||
} else {
|
||||
return bw.writeArraySwap(bw, Elem, array);
|
||||
return bw.writeArraySwap(bw, Elem, slice);
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that the buffer storage capacity is at least enough to store `@sizeOf(Elem)`
|
||||
pub fn writeArraySwap(bw: *BufferedWriter, Elem: type, array: []const Elem) Writer.Error!void {
|
||||
pub fn writeSliceSwap(bw: *BufferedWriter, Elem: type, slice: []const Elem) Writer.Error!void {
|
||||
// copy to storage first, then swap in place
|
||||
_ = bw;
|
||||
_ = array;
|
||||
_ = slice;
|
||||
@panic("TODO");
|
||||
}
|
||||
|
||||
|
||||
@ -45,18 +45,26 @@ pub const VTable = struct {
|
||||
discard: *const fn (context: ?*anyopaque, limit: Limit) Error!usize,
|
||||
};
|
||||
|
||||
pub const RwError = RwAllError || error{
|
||||
pub const RwError = error{
|
||||
/// See the `Reader` implementation for detailed diagnostics.
|
||||
ReadFailed,
|
||||
/// See the `Writer` implementation for detailed diagnostics.
|
||||
WriteFailed,
|
||||
/// End of stream indicated from the `Reader`. This error cannot originate
|
||||
/// from the `Writer`.
|
||||
EndOfStream,
|
||||
};
|
||||
|
||||
pub const Error = ShortError || error{
|
||||
pub const Error = error{
|
||||
/// See the `Reader` implementation for detailed diagnostics.
|
||||
ReadFailed,
|
||||
EndOfStream,
|
||||
};
|
||||
|
||||
/// For functions that handle end of stream as a success case.
|
||||
pub const RwAllError = ShortError || error{
|
||||
pub const RwAllError = error{
|
||||
/// See the `Reader` implementation for detailed diagnostics.
|
||||
ReadFailed,
|
||||
/// See the `Writer` implementation for detailed diagnostics.
|
||||
WriteFailed,
|
||||
};
|
||||
|
||||
@ -4,7 +4,6 @@ const testing = std.testing;
|
||||
const Writer = @This();
|
||||
|
||||
const block_size = @sizeOf(Header);
|
||||
const empty_block: [block_size]u8 = [_]u8{0} ** block_size;
|
||||
|
||||
/// Options for writing file/dir/link. If left empty 0o664 is used for
|
||||
/// file mode and current time for mtime.
|
||||
@ -14,80 +13,91 @@ pub const Options = struct {
|
||||
/// File system modification time.
|
||||
mtime: u64 = 0,
|
||||
};
|
||||
const Self = @This();
|
||||
|
||||
underlying_writer: *std.io.BufferedWriter,
|
||||
prefix: []const u8 = "",
|
||||
mtime_now: u64 = 0,
|
||||
|
||||
const Error = error{
|
||||
WriteFailed,
|
||||
OctalOverflow,
|
||||
NameTooLong,
|
||||
};
|
||||
|
||||
/// Sets prefix for all other write* method paths.
|
||||
pub fn setRoot(self: *Self, root: []const u8) !void {
|
||||
pub fn setRoot(w: *Writer, root: []const u8) Error!void {
|
||||
if (root.len > 0)
|
||||
try self.writeDir(root, .{});
|
||||
try w.writeDir(root, .{});
|
||||
|
||||
self.prefix = root;
|
||||
w.prefix = root;
|
||||
}
|
||||
|
||||
/// Writes directory.
|
||||
pub fn writeDir(self: *Self, sub_path: []const u8, opt: Options) !void {
|
||||
try self.writeHeader(.directory, sub_path, "", 0, opt);
|
||||
pub fn writeDir(w: *Writer, sub_path: []const u8, options: Options) Error!void {
|
||||
try w.writeHeader(.directory, sub_path, "", 0, options);
|
||||
}
|
||||
|
||||
/// Writes file system file.
|
||||
pub fn writeFile(self: *Self, sub_path: []const u8, file: std.fs.File) !void {
|
||||
const stat = try file.stat();
|
||||
pub const WriteFileError = std.io.Writer.FileError || Error;
|
||||
|
||||
pub fn writeFile(
|
||||
w: *Writer,
|
||||
sub_path: []const u8,
|
||||
file: std.fs.File,
|
||||
stat: std.fs.File.Stat,
|
||||
) WriteFileError!void {
|
||||
const mtime: u64 = @intCast(@divFloor(stat.mtime, std.time.ns_per_s));
|
||||
|
||||
var header = Header{};
|
||||
try self.setPath(&header, sub_path);
|
||||
var header: Header = .{};
|
||||
try w.setPath(&header, sub_path);
|
||||
try header.setSize(stat.size);
|
||||
try header.setMtime(mtime);
|
||||
try header.write(self.underlying_writer);
|
||||
try header.write(w.underlying_writer);
|
||||
|
||||
try self.underlying_writer.writeFileAll(file, .{ .limit = .limited(stat.size) });
|
||||
try self.writePadding(stat.size);
|
||||
try w.underlying_writer.writeFileAll(file, .{ .limit = .limited(stat.size) });
|
||||
try w.writePadding(stat.size);
|
||||
}
|
||||
|
||||
/// Writes file reading file content from `reader`. Number of bytes in
|
||||
/// reader must be equal to `size`.
|
||||
pub fn writeFileStream(self: *Self, sub_path: []const u8, size: usize, reader: anytype, opt: Options) !void {
|
||||
try self.writeHeader(.regular, sub_path, "", @intCast(size), opt);
|
||||
|
||||
var counting_reader = std.io.countingReader(reader);
|
||||
var fifo = std.fifo.LinearFifo(u8, .{ .Static = 4096 }).init();
|
||||
try fifo.pump(counting_reader.reader(), self.underlying_writer);
|
||||
if (counting_reader.bytes_read != size) return error.WrongReaderSize;
|
||||
try self.writePadding(size);
|
||||
/// Writes file reading file content from `reader`. Reads exactly `size` bytes
|
||||
/// from `reader`, or returns `error.EndOfStream`.
|
||||
pub fn writeFileStream(
|
||||
w: *Writer,
|
||||
sub_path: []const u8,
|
||||
size: usize,
|
||||
reader: *std.io.BufferedReader,
|
||||
options: Options,
|
||||
) std.io.Reader.RwError!void {
|
||||
try w.writeHeader(.regular, sub_path, "", @intCast(size), options);
|
||||
try reader.readAll(w.underlying_writer, .limited(size));
|
||||
try w.writePadding(size);
|
||||
}
|
||||
|
||||
/// Writes file using bytes buffer `content` for size and file content.
|
||||
pub fn writeFileBytes(self: *Self, sub_path: []const u8, content: []const u8, opt: Options) !void {
|
||||
try self.writeHeader(.regular, sub_path, "", @intCast(content.len), opt);
|
||||
try self.underlying_writer.writeAll(content);
|
||||
try self.writePadding(content.len);
|
||||
pub fn writeFileBytes(w: *Writer, sub_path: []const u8, content: []const u8, options: Options) Error!void {
|
||||
try w.writeHeader(.regular, sub_path, "", @intCast(content.len), options);
|
||||
try w.underlying_writer.writeAll(content);
|
||||
try w.writePadding(content.len);
|
||||
}
|
||||
|
||||
/// Writes symlink.
|
||||
pub fn writeLink(self: *Self, sub_path: []const u8, link_name: []const u8, opt: Options) !void {
|
||||
try self.writeHeader(.symbolic_link, sub_path, link_name, 0, opt);
|
||||
pub fn writeLink(w: *Writer, sub_path: []const u8, link_name: []const u8, options: Options) Error!void {
|
||||
try w.writeHeader(.symbolic_link, sub_path, link_name, 0, options);
|
||||
}
|
||||
|
||||
/// Writes fs.Dir.WalkerEntry. Uses `mtime` from file system entry and
|
||||
/// default for entry mode .
|
||||
pub fn writeEntry(self: *Self, entry: std.fs.Dir.Walker.Entry) !void {
|
||||
pub fn writeEntry(w: *Writer, entry: std.fs.Dir.Walker.Entry) Error!void {
|
||||
switch (entry.kind) {
|
||||
.directory => {
|
||||
try self.writeDir(entry.path, .{ .mtime = try entryMtime(entry) });
|
||||
try w.writeDir(entry.path, .{ .mtime = try entryMtime(entry) });
|
||||
},
|
||||
.file => {
|
||||
var file = try entry.dir.openFile(entry.basename, .{});
|
||||
defer file.close();
|
||||
try self.writeFile(entry.path, file);
|
||||
const stat = try file.stat();
|
||||
try w.writeFile(entry.path, file, stat);
|
||||
},
|
||||
.sym_link => {
|
||||
var link_name_buffer: [std.fs.max_path_bytes]u8 = undefined;
|
||||
const link_name = try entry.dir.readLink(entry.basename, &link_name_buffer);
|
||||
try self.writeLink(entry.path, link_name, .{ .mtime = try entryMtime(entry) });
|
||||
try w.writeLink(entry.path, link_name, .{ .mtime = try entryMtime(entry) });
|
||||
},
|
||||
else => {
|
||||
return error.UnsupportedWalkerEntryKind;
|
||||
@ -96,31 +106,31 @@ pub fn writeEntry(self: *Self, entry: std.fs.Dir.Walker.Entry) !void {
|
||||
}
|
||||
|
||||
fn writeHeader(
|
||||
self: *Self,
|
||||
w: *Writer,
|
||||
typeflag: Header.FileType,
|
||||
sub_path: []const u8,
|
||||
link_name: []const u8,
|
||||
size: u64,
|
||||
opt: Options,
|
||||
) !void {
|
||||
options: Options,
|
||||
) Error!void {
|
||||
var header = Header.init(typeflag);
|
||||
try self.setPath(&header, sub_path);
|
||||
try w.setPath(&header, sub_path);
|
||||
try header.setSize(size);
|
||||
try header.setMtime(if (opt.mtime != 0) opt.mtime else self.mtimeNow());
|
||||
if (opt.mode != 0)
|
||||
try header.setMode(opt.mode);
|
||||
try header.setMtime(if (options.mtime != 0) options.mtime else w.mtimeNow());
|
||||
if (options.mode != 0)
|
||||
try header.setMode(options.mode);
|
||||
if (typeflag == .symbolic_link)
|
||||
header.setLinkname(link_name) catch |err| switch (err) {
|
||||
error.NameTooLong => try self.writeExtendedHeader(.gnu_long_link, &.{link_name}),
|
||||
error.NameTooLong => try w.writeExtendedHeader(.gnu_long_link, &.{link_name}),
|
||||
else => return err,
|
||||
};
|
||||
try header.write(self.underlying_writer);
|
||||
try header.write(w.underlying_writer);
|
||||
}
|
||||
|
||||
fn mtimeNow(self: *Self) u64 {
|
||||
if (self.mtime_now == 0)
|
||||
self.mtime_now = @intCast(std.time.timestamp());
|
||||
return self.mtime_now;
|
||||
fn mtimeNow(w: *Writer) u64 {
|
||||
if (w.mtime_now == 0)
|
||||
w.mtime_now = @intCast(std.time.timestamp());
|
||||
return w.mtime_now;
|
||||
}
|
||||
|
||||
fn entryMtime(entry: std.fs.Dir.Walker.Entry) !u64 {
|
||||
@ -130,52 +140,51 @@ fn entryMtime(entry: std.fs.Dir.Walker.Entry) !u64 {
|
||||
|
||||
/// Writes path in posix header, if don't fit (in name+prefix; 100+155
|
||||
/// bytes) writes it in gnu extended header.
|
||||
fn setPath(self: *Self, header: *Header, sub_path: []const u8) !void {
|
||||
header.setPath(self.prefix, sub_path) catch |err| switch (err) {
|
||||
fn setPath(w: *Writer, header: *Header, sub_path: []const u8) Error!void {
|
||||
header.setPath(w.prefix, sub_path) catch |err| switch (err) {
|
||||
error.NameTooLong => {
|
||||
// write extended header
|
||||
const buffers: []const []const u8 = if (self.prefix.len == 0)
|
||||
const buffers: []const []const u8 = if (w.prefix.len == 0)
|
||||
&.{sub_path}
|
||||
else
|
||||
&.{ self.prefix, "/", sub_path };
|
||||
try self.writeExtendedHeader(.gnu_long_name, buffers);
|
||||
&.{ w.prefix, "/", sub_path };
|
||||
try w.writeExtendedHeader(.gnu_long_name, buffers);
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
}
|
||||
|
||||
/// Writes gnu extended header: gnu_long_name or gnu_long_link.
|
||||
fn writeExtendedHeader(self: *Self, typeflag: Header.FileType, buffers: []const []const u8) !void {
|
||||
fn writeExtendedHeader(w: *Writer, typeflag: Header.FileType, buffers: []const []const u8) Error!void {
|
||||
var len: usize = 0;
|
||||
for (buffers) |buf|
|
||||
len += buf.len;
|
||||
for (buffers) |buf| len += buf.len;
|
||||
|
||||
var header = Header.init(typeflag);
|
||||
var header: Header = .init(typeflag);
|
||||
try header.setSize(len);
|
||||
try header.write(self.underlying_writer);
|
||||
try header.write(w.underlying_writer);
|
||||
for (buffers) |buf|
|
||||
try self.underlying_writer.writeAll(buf);
|
||||
try self.writePadding(len);
|
||||
try w.underlying_writer.writeAll(buf);
|
||||
try w.writePadding(len);
|
||||
}
|
||||
|
||||
fn writePadding(self: *Self, bytes: u64) !void {
|
||||
const pos: usize = @intCast(bytes % block_size);
|
||||
fn writePadding(w: *Writer, bytes: usize) std.io.Writer.Error!void {
|
||||
const pos = bytes % block_size;
|
||||
if (pos == 0) return;
|
||||
try self.underlying_writer.writeAll(empty_block[pos..]);
|
||||
try w.underlying_writer.splatByteAll(0, block_size - pos);
|
||||
}
|
||||
|
||||
/// Tar should finish with two zero blocks, but 'reasonable system must
|
||||
/// not assume that such a block exists when reading an archive' (from
|
||||
/// reference). In practice it is safe to skip this finish.
|
||||
pub fn finish(self: *Self) !void {
|
||||
try self.underlying_writer.writeAll(&empty_block);
|
||||
try self.underlying_writer.writeAll(&empty_block);
|
||||
/// According to the specification, tar should finish with two zero blocks, but
|
||||
/// "reasonable system must not assume that such a block exists when reading an
|
||||
/// archive". Therefore, the Zig standard library recommends to not call this
|
||||
/// function.
|
||||
pub fn finishPedantically(w: *Writer) std.io.Writer.Error!void {
|
||||
try w.underlying_writer.writeSplatAll(&.{&.{0}}, block_size * 2);
|
||||
}
|
||||
|
||||
/// A struct that is exactly 512 bytes and matches tar file format. This is
|
||||
/// intended to be used for outputting tar files; for parsing there is
|
||||
/// `std.tar.Header`.
|
||||
const Header = extern struct {
|
||||
pub const Header = extern struct {
|
||||
// This struct was originally copied from
|
||||
// https://github.com/mattnite/tar/blob/main/src/main.zig which is MIT
|
||||
// licensed.
|
||||
@ -230,11 +239,11 @@ const Header = extern struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn setSize(self: *Header, size: u64) !void {
|
||||
try octal(&self.size, size);
|
||||
pub fn setSize(w: *Header, size: u64) error{OctalOverflow}!void {
|
||||
try octal(&w.size, size);
|
||||
}
|
||||
|
||||
fn octal(buf: []u8, value: u64) !void {
|
||||
fn octal(buf: []u8, value: u64) error{OctalOverflow}!void {
|
||||
var remainder: u64 = value;
|
||||
var pos: usize = buf.len;
|
||||
while (remainder > 0 and pos > 0) {
|
||||
@ -246,36 +255,36 @@ const Header = extern struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setMode(self: *Header, mode: u32) !void {
|
||||
try octal(&self.mode, mode);
|
||||
pub fn setMode(w: *Header, mode: u32) error{OctalOverflow}!void {
|
||||
try octal(&w.mode, mode);
|
||||
}
|
||||
|
||||
// Integer number of seconds since January 1, 1970, 00:00 Coordinated Universal Time.
|
||||
// mtime == 0 will use current time
|
||||
pub fn setMtime(self: *Header, mtime: u64) !void {
|
||||
try octal(&self.mtime, mtime);
|
||||
pub fn setMtime(w: *Header, mtime: u64) error{OctalOverflow}!void {
|
||||
try octal(&w.mtime, mtime);
|
||||
}
|
||||
|
||||
pub fn updateChecksum(self: *Header) !void {
|
||||
var checksum: usize = ' '; // other 7 self.checksum bytes are initialized to ' '
|
||||
for (std.mem.asBytes(self)) |val|
|
||||
pub fn updateChecksum(w: *Header) !void {
|
||||
var checksum: usize = ' '; // other 7 w.checksum bytes are initialized to ' '
|
||||
for (std.mem.asBytes(w)) |val|
|
||||
checksum += val;
|
||||
try octal(&self.checksum, checksum);
|
||||
try octal(&w.checksum, checksum);
|
||||
}
|
||||
|
||||
pub fn write(self: *Header, output_writer: anytype) !void {
|
||||
try self.updateChecksum();
|
||||
try output_writer.writeAll(std.mem.asBytes(self));
|
||||
pub fn write(h: *Header, bw: *std.io.BufferedWriter) error{ OctalOverflow, WriteFailed }!void {
|
||||
try h.updateChecksum();
|
||||
try bw.writeAll(std.mem.asBytes(h));
|
||||
}
|
||||
|
||||
pub fn setLinkname(self: *Header, link: []const u8) !void {
|
||||
if (link.len > self.linkname.len) return error.NameTooLong;
|
||||
@memcpy(self.linkname[0..link.len], link);
|
||||
pub fn setLinkname(w: *Header, link: []const u8) !void {
|
||||
if (link.len > w.linkname.len) return error.NameTooLong;
|
||||
@memcpy(w.linkname[0..link.len], link);
|
||||
}
|
||||
|
||||
pub fn setPath(self: *Header, prefix: []const u8, sub_path: []const u8) !void {
|
||||
const max_prefix = self.prefix.len;
|
||||
const max_name = self.name.len;
|
||||
pub fn setPath(w: *Header, prefix: []const u8, sub_path: []const u8) !void {
|
||||
const max_prefix = w.prefix.len;
|
||||
const max_name = w.name.len;
|
||||
const sep = std.fs.path.sep_posix;
|
||||
|
||||
if (prefix.len + sub_path.len > max_name + max_prefix or prefix.len > max_prefix)
|
||||
@ -283,32 +292,32 @@ const Header = extern struct {
|
||||
|
||||
// both fit into name
|
||||
if (prefix.len > 0 and prefix.len + sub_path.len < max_name) {
|
||||
@memcpy(self.name[0..prefix.len], prefix);
|
||||
self.name[prefix.len] = sep;
|
||||
@memcpy(self.name[prefix.len + 1 ..][0..sub_path.len], sub_path);
|
||||
@memcpy(w.name[0..prefix.len], prefix);
|
||||
w.name[prefix.len] = sep;
|
||||
@memcpy(w.name[prefix.len + 1 ..][0..sub_path.len], sub_path);
|
||||
return;
|
||||
}
|
||||
|
||||
// sub_path fits into name
|
||||
// there is no prefix or prefix fits into prefix
|
||||
if (sub_path.len <= max_name) {
|
||||
@memcpy(self.name[0..sub_path.len], sub_path);
|
||||
@memcpy(self.prefix[0..prefix.len], prefix);
|
||||
@memcpy(w.name[0..sub_path.len], sub_path);
|
||||
@memcpy(w.prefix[0..prefix.len], prefix);
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefix.len > 0) {
|
||||
@memcpy(self.prefix[0..prefix.len], prefix);
|
||||
self.prefix[prefix.len] = sep;
|
||||
@memcpy(w.prefix[0..prefix.len], prefix);
|
||||
w.prefix[prefix.len] = sep;
|
||||
}
|
||||
const prefix_pos = if (prefix.len > 0) prefix.len + 1 else 0;
|
||||
|
||||
// add as much to prefix as you can, must split at /
|
||||
const prefix_remaining = max_prefix - prefix_pos;
|
||||
if (std.mem.lastIndexOf(u8, sub_path[0..@min(prefix_remaining, sub_path.len)], &.{'/'})) |sep_pos| {
|
||||
@memcpy(self.prefix[prefix_pos..][0..sep_pos], sub_path[0..sep_pos]);
|
||||
@memcpy(w.prefix[prefix_pos..][0..sep_pos], sub_path[0..sep_pos]);
|
||||
if ((sub_path.len - sep_pos - 1) > max_name) return error.NameTooLong;
|
||||
@memcpy(self.name[0..][0 .. sub_path.len - sep_pos - 1], sub_path[sep_pos + 1 ..]);
|
||||
@memcpy(w.name[0..][0 .. sub_path.len - sep_pos - 1], sub_path[sep_pos + 1 ..]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -173,7 +173,7 @@ pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
|
||||
.bytes_len = @intCast(bytes_len),
|
||||
});
|
||||
try s.out.writeStructEndian(eb_hdr, .little);
|
||||
try s.out.writeArrayEndian(u32, error_bundle.extra, .little);
|
||||
try s.out.writeSliceEndian(u32, error_bundle.extra, .little);
|
||||
try s.out.writeAll(error_bundle.string_bytes);
|
||||
try s.out.flush();
|
||||
}
|
||||
@ -198,8 +198,8 @@ pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {
|
||||
.bytes_len = @intCast(bytes_len),
|
||||
});
|
||||
try s.out.writeStructEndian(header, .little);
|
||||
try s.out.writeArrayEndian(u32, test_metadata.names, .little);
|
||||
try s.out.writeArrayEndian(u32, test_metadata.expected_panic_msgs, .little);
|
||||
try s.out.writeSliceEndian(u32, test_metadata.names, .little);
|
||||
try s.out.writeSliceEndian(u32, test_metadata.expected_panic_msgs, .little);
|
||||
try s.out.writeAll(test_metadata.string_bytes);
|
||||
try s.out.flush();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user