diff --git a/build.zig b/build.zig index 6dd87ce..63dc8d2 100644 --- a/build.zig +++ b/build.zig @@ -32,9 +32,11 @@ pub fn build(b: *std.Build) !void { { const unzip = b.addExecutable(.{ .name = "unzip", - .root_source_file = b.path("unzip.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("unzip.zig"), + .target = target, + .optimize = optimize, + }), }); const install = b.addInstallArtifact(unzip, .{}); unzip_step.dependOn(&install.step); @@ -47,9 +49,11 @@ pub fn build(b: *std.Build) !void { { const zip = b.addExecutable(.{ .name = "zip", - .root_source_file = b.path("zip.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("zip.zig"), + .target = target, + .optimize = optimize, + }), }); const install = b.addInstallArtifact(zip, .{}); zip_step.dependOn(&install.step); @@ -57,8 +61,10 @@ pub fn build(b: *std.Build) !void { const host_zip_exe = b.addExecutable(.{ .name = "zip", - .root_source_file = b.path("zip.zig"), - .target = b.graph.host, + .root_module = b.createModule(.{ + .root_source_file = b.path("zip.zig"), + .target = b.graph.host, + }), }); const ci_step = b.step("ci", "The build/test step to run on the CI"); @@ -72,15 +78,17 @@ pub fn build(b: *std.Build) !void { fn addZigupExe( b: *std.Build, target: std.Build.ResolvedTarget, - optimize: std.builtin.Mode, + optimize: std.builtin.OptimizeMode, ) *std.Build.Step.Compile { const win32exelink_mod: ?*std.Build.Module = blk: { if (target.result.os.tag == .windows) { const exe = b.addExecutable(.{ .name = "win32exelink", - .root_source_file = b.path("win32exelink.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("win32exelink.zig"), + .target = target, + .optimize = optimize, + }), }); break :blk b.createModule(.{ .root_source_file = exe.getEmittedBin(), @@ -91,10 +99,12 @@ fn addZigupExe( const exe = b.addExecutable(.{ .name = "zigup", - .root_source_file = b.path("zigup.zig"), - .target = target, - .optimize = optimize, - .strip = true, + .root_module = b.createModule(.{ + .root_source_file = b.path("zigup.zig"), + .target = target, + .optimize = optimize, + .strip = true, + }), }); if (target.result.os.tag == .windows) { @@ -214,8 +224,10 @@ fn addTests( ) void { const runtest_exe = b.addExecutable(.{ .name = "runtest", - .root_source_file = b.path("runtest.zig"), - .target = target, + .root_module = b.createModule(.{ + .root_source_file = b.path("runtest.zig"), + .target = target, + }), }); const tests: Tests = .{ .b = b, @@ -235,6 +247,11 @@ fn addTests( .argv = &.{"--help"}, .check = .{ .expect_stderr_match = "Usage" }, }); + tests.addWithClean(.{ + .name = "test-usage-help-command", + .argv = &.{"help"}, + .check = .{ .expect_stderr_match = "Usage" }, + }); tests.addWithClean(.{ .name = "test-fetch-index", diff --git a/fixdeletetree.zig b/fixdeletetree.zig index 136dd70..736bdc0 100644 --- a/fixdeletetree.zig +++ b/fixdeletetree.zig @@ -20,7 +20,7 @@ pub fn deleteTree(dir: std.fs.Dir, sub_path: []const u8) !void { switch (err) { error.FileBusy => { std.log.warn("path '{s}' is busy (attempt {}), will retry", .{ sub_path, attempt }); - std.time.sleep(std.time.ns_per_ms * 100); // sleep for 100 ms + std.Thread.sleep(std.time.ns_per_ms * 100); // sleep for 100 ms }, else => |e| return e, } diff --git a/runtest.zig b/runtest.zig index 3c3a33f..6f98059 100644 --- a/runtest.zig +++ b/runtest.zig @@ -61,7 +61,7 @@ pub fn main() !void { try std.fs.cwd().makeDir(appdata); var file = try std.fs.cwd().createFile(install_dir_setting_path, .{}); defer file.close(); - try file.writer().writeAll(install_dir); + try file.writeAll(install_dir); } else { var shared_sibling_state: SharedSiblingState = .{}; try copyEnvDir( @@ -85,7 +85,7 @@ pub fn main() !void { std.debug.assert(std.mem.eql(u8, install_dir_parsed.cache_o, input_install_dir_parsed.cache_o)); var file = try std.fs.cwd().createFile(install_dir_setting_path, .{}); defer file.close(); - try file.writer().writeAll(install_dir); + try file.writeAll(install_dir); }, .bad => { // the install dir must have been customized, keep it @@ -114,26 +114,26 @@ pub fn main() !void { defer arena.free(fake_zig); var file = try std.fs.cwd().createFile(fake_zig, .{}); defer file.close(); - try file.writer().writeAll("a fake executable"); + try file.writeAll("a fake executable"); } else { std.log.err("unknown setup option '{s}'", .{setup_option}); std.process.exit(0xff); } - var argv = std.ArrayList([]const u8).init(arena); - try argv.append(zigup_exe); - try argv.append("--appdata"); - try argv.append(appdata); - try argv.append("--path-link"); - try argv.append(path_link); - try argv.appendSlice(zigup_args); + var argv: std.ArrayList([]const u8) = .empty; + try argv.append(arena, zigup_exe); + try argv.append(arena, "--appdata"); + try argv.append(arena, appdata); + try argv.append(arena, "--path-link"); + try argv.append(arena, path_link); + try argv.appendSlice(arena, zigup_args); if (true) { - try std.io.getStdErr().writer().writeAll("runtest exec: "); + try std.fs.File.stderr().deprecatedWriter().writeAll("runtest exec: "); for (argv.items) |arg| { - try std.io.getStdErr().writer().print(" {s}", .{arg}); + try std.fs.File.stderr().deprecatedWriter().print(" {s}", .{arg}); } - try std.io.getStdErr().writer().writeAll("\n"); + try std.fs.File.stderr().deprecatedWriter().writeAll("\n"); } var child = std.process.Child.init(argv.items, arena); @@ -141,15 +141,15 @@ pub fn main() !void { if (add_path) { var env_map = try std.process.getEnvMap(arena); // make sure the directory with our path-link comes first in PATH - var new_path = std.ArrayList(u8).init(arena); + var new_path: std.ArrayList(u8) = .empty; if (maybe_second_bin_dir) |second_bin_dir| { - try new_path.appendSlice(second_bin_dir); - try new_path.append(std.fs.path.delimiter); + try new_path.appendSlice(arena, second_bin_dir); + try new_path.append(arena, std.fs.path.delimiter); } - try new_path.appendSlice(out_env_dir); - try new_path.append(std.fs.path.delimiter); + try new_path.appendSlice(arena, out_env_dir); + try new_path.append(arena, std.fs.path.delimiter); if (env_map.get("PATH")) |path| { - try new_path.appendSlice(path); + try new_path.appendSlice(arena, path); } try env_map.put("PATH", new_path.items); child.env_map = &env_map; @@ -293,14 +293,29 @@ fn copyEnvDir( var out_target_buf: [std.fs.max_path_bytes]u8 = undefined; const out_target = blk: { if (std.fs.path.isAbsolute(in_target)) { - if (!std.mem.startsWith(u8, in_target, in_root)) std.debug.panic( + const maybe_absolute_in_root = if (std.fs.path.isAbsolute(in_root)) + null + else + try std.fs.cwd().realpathAlloc(allocator, in_root); + defer if (maybe_absolute_in_root) |absolute_in_root| allocator.free(absolute_in_root); + + var matched_in_root: ?[]const u8 = null; + if (std.mem.startsWith(u8, in_target, in_root)) { + matched_in_root = in_root; + } else if (maybe_absolute_in_root) |absolute_in_root| { + if (std.mem.startsWith(u8, in_target, absolute_in_root)) { + matched_in_root = absolute_in_root; + } + } + + const prefix = matched_in_root orelse std.debug.panic( "expected symlink target to start with '{s}' but got '{s}'", .{ in_root, in_target }, ); break :blk try std.fmt.bufPrint( &out_target_buf, "{s}{s}", - .{ out_root, in_target[in_root.len..] }, + .{ out_root, in_target[prefix.len..] }, ); } break :blk in_target; diff --git a/unzip.zig b/unzip.zig index ca8a46f..6a86877 100644 --- a/unzip.zig +++ b/unzip.zig @@ -10,7 +10,7 @@ fn fatal(comptime fmt: []const u8, args: anytype) noreturn { } fn usage() noreturn { - std.io.getStdErr().writer().print("Usage: unzip [-d DIR] ZIP_FILE\n", .{}) catch |e| @panic(@errorName(e)); + std.fs.File.stderr().deprecatedWriter().print("Usage: unzip [-d DIR] ZIP_FILE\n", .{}) catch |e| @panic(@errorName(e)); std.process.exit(1); } @@ -80,7 +80,9 @@ pub fn main() !void { const zip_file = std.fs.cwd().openFile(zip_file_arg, .{}) catch |err| fatal("open '{s}' failed: {s}", .{ zip_file_arg, @errorName(err) }); defer zip_file.close(); - try std.zip.extract(out_dir, zip_file.seekableStream(), .{ + var zip_reader_buffer: [4096]u8 = undefined; + var zip_reader = zip_file.reader(&zip_reader_buffer); + try std.zip.extract(out_dir, &zip_reader, .{ .allow_backslashes = true, }); } diff --git a/win32exelink.zig b/win32exelink.zig index dbc2837..3e6a4a7 100644 --- a/win32exelink.zig +++ b/win32exelink.zig @@ -37,7 +37,7 @@ pub fn main() !u8 { const args = try std.process.argsAlloc(global.arena); if (args.len >= 2 and std.mem.eql(u8, args[1], "exelink")) { - try std.io.getStdOut().writer().writeAll(zig_exe); + try std.fs.File.stdout().deprecatedWriter().writeAll(zig_exe); return 0; } args[0] = zig_exe; @@ -59,7 +59,7 @@ pub fn main() !u8 { }; } -fn consoleCtrlHandler(ctrl_type: u32) callconv(@import("std").os.windows.WINAPI) win32.BOOL { +fn consoleCtrlHandler(ctrl_type: u32) callconv(.winapi) win32.BOOL { // // NOTE: Do I need to synchronize this with the main thread? // @@ -94,16 +94,11 @@ const win32 = struct { pub const CTRL_LOGOFF_EVENT = @as(u32, 5); pub const CTRL_SHUTDOWN_EVENT = @as(u32, 6); pub const GetLastError = std.os.windows.kernel32.GetLastError; - pub const PHANDLER_ROUTINE = switch (builtin.zig_backend) { - .stage1 => fn ( - CtrlType: u32, - ) callconv(@import("std").os.windows.WINAPI) BOOL, - else => *const fn ( - CtrlType: u32, - ) callconv(@import("std").os.windows.WINAPI) BOOL, - }; + pub const PHANDLER_ROUTINE = *const fn ( + CtrlType: u32, + ) callconv(.winapi) BOOL; pub extern "kernel32" fn SetConsoleCtrlHandler( HandlerRoutine: ?PHANDLER_ROUTINE, Add: BOOL, - ) callconv(@import("std").os.windows.WINAPI) BOOL; + ) callconv(.winapi) BOOL; }; diff --git a/zigup.zig b/zigup.zig index 6f7e861..2c33711 100644 --- a/zigup.zig +++ b/zigup.zig @@ -7,6 +7,24 @@ const Allocator = mem.Allocator; const fixdeletetree = @import("fixdeletetree.zig"); +fn BoundedArray(comptime T: type, comptime capacity: usize) type { + return struct { + buffer: [capacity]T = undefined, + len: usize = 0, + + const Self = @This(); + + fn init(len: usize) error{Overflow}!Self { + if (len > capacity) return error.Overflow; + return .{ .len = len }; + } + + fn slice(self: anytype) if (@typeInfo(@TypeOf(self)).pointer.is_const) []const T else []T { + return self.buffer[0..self.len]; + } + }; +} + const arch = switch (builtin.cpu.arch) { .aarch64 => "aarch64", .arm => "armv7a", @@ -71,9 +89,7 @@ fn download(allocator: Allocator, url: []const u8, writer: anytype) DownloadResu .{@errorName(err)}, ) catch |e| oom(e) }; - var header_buffer: [4096]u8 = undefined; - var request = client.open(.GET, uri, .{ - .server_header_buffer = &header_buffer, + var request = client.request(.GET, uri, .{ .keep_alive = false, }) catch |err| return .{ .err = std.fmt.allocPrint( allocator, @@ -83,40 +99,66 @@ fn download(allocator: Allocator, url: []const u8, writer: anytype) DownloadResu defer request.deinit(); - request.send() catch |err| return .{ .err = std.fmt.allocPrint( + request.sendBodiless() catch |err| return .{ .err = std.fmt.allocPrint( allocator, "failed to send the HTTP request with {s}", .{@errorName(err)}, ) catch |e| oom(e) }; - request.wait() catch |err| return .{ .err = std.fmt.allocPrint( + + var redirect_buffer: [4096]u8 = undefined; + var response = request.receiveHead(&redirect_buffer) catch |err| return .{ .err = std.fmt.allocPrint( allocator, "failed to read the HTTP response headers with {s}", .{@errorName(err)}, ) catch |e| oom(e) }; - if (request.response.status != .ok) return .{ .err = std.fmt.allocPrint( + if (response.head.status != .ok) return .{ .err = std.fmt.allocPrint( allocator, "the HTTP server replied with unsuccessful response '{d} {s}'", - .{ @intFromEnum(request.response.status), request.response.status.phrase() orelse "" }, + .{ @intFromEnum(response.head.status), response.head.status.phrase() orelse "" }, ) catch |e| oom(e) }; - // TODO: we take advantage of request.response.content_length - - var buf: [4096]u8 = undefined; - while (true) { - const len = request.reader().read(&buf) catch |err| return .{ .err = std.fmt.allocPrint( - allocator, - "failed to read the HTTP response body with {s}'", - .{@errorName(err)}, - ) catch |e| oom(e) }; - if (len == 0) - return .ok; - writer.writeAll(buf[0..len]) catch |err| return .{ .err = std.fmt.allocPrint( + // TODO: we take advantage of response.head.content_length + + var transfer_buffer: [64]u8 = undefined; + var decompress: std.http.Decompress = undefined; + var decompress_storage: ?[]u8 = null; + defer if (decompress_storage) |buffer| allocator.free(buffer); + const decompress_buffer: []u8 = switch (response.head.content_encoding) { + .identity => &.{}, + .zstd => blk: { + const buffer = allocator.alloc(u8, std.compress.zstd.default_window_len) catch |e| oom(e); + decompress_storage = buffer; + break :blk buffer; + }, + .deflate, .gzip => blk: { + const buffer = allocator.alloc(u8, std.compress.flate.max_window_len) catch |e| oom(e); + decompress_storage = buffer; + break :blk buffer; + }, + .compress => return .{ .err = std.fmt.allocPrint( allocator, - "failed to write the HTTP response body with {s}'", - .{@errorName(err)}, - ) catch |e| oom(e) }; - } + "unsupported HTTP compression method '{s}'", + .{@tagName(response.head.content_encoding)}, + ) catch |e| oom(e) }, + }; + const reader = response.readerDecompressing(&transfer_buffer, &decompress, decompress_buffer); + var writer_adapter_buffer: [4096]u8 = undefined; + var writer_adapter = writer.adaptToNewApi(&writer_adapter_buffer); + _ = reader.streamRemaining(&writer_adapter.new_interface) catch |err| return .{ .err = std.fmt.allocPrint( + allocator, + "failed to read the HTTP response body with {s}'", + .{switch (err) { + error.ReadFailed => if (response.bodyErr()) |body_err| @errorName(body_err) else @errorName(err), + error.WriteFailed => if (writer_adapter.err) |write_err| @errorName(write_err) else @errorName(err), + }}, + ) catch |e| oom(e) }; + writer_adapter.new_interface.flush() catch |err| return .{ .err = std.fmt.allocPrint( + allocator, + "failed to write the HTTP response body with {s}'", + .{if (writer_adapter.err) |write_err| @errorName(write_err) else @errorName(err)}, + ) catch |e| oom(e) }; + return .ok; } const DownloadStringResult = union(enum) { @@ -125,9 +167,9 @@ const DownloadStringResult = union(enum) { }; fn downloadToString(allocator: Allocator, url: []const u8) DownloadStringResult { var response_array_list = ArrayList(u8).initCapacity(allocator, 50 * 1024) catch |e| oom(e); // 50 KB (modify if response is expected to be bigger) - defer response_array_list.deinit(); - switch (download(allocator, url, response_array_list.writer())) { - .ok => return .{ .ok = response_array_list.toOwnedSlice() catch |e| oom(e) }, + defer response_array_list.deinit(allocator); + switch (download(allocator, url, response_array_list.writer(allocator))) { + .ok => return .{ .ok = response_array_list.toOwnedSlice(allocator) catch |e| oom(e) }, .err => |e| return .{ .err = e }, } } @@ -217,7 +259,7 @@ fn saveInstallDir(allocator: Allocator, maybe_dir: ?[]const u8) !void { { const file = try std.fs.cwd().createFile(setting_path, .{}); defer file.close(); - try file.writer().writeAll(d); + try file.writeAll(d); } // sanity check, read it back @@ -310,7 +352,7 @@ fn help(allocator: Allocator) !void { break :blk "unavailable"; }; - try std.io.getStdErr().writer().print( + try std.fs.File.stderr().deprecatedWriter().print( \\Download and manage zig compilers. \\ \\Common Usage: @@ -422,6 +464,14 @@ pub fn main2() !u8 { try help(allocator); return 1; } + if (std.mem.eql(u8, "help", args[0])) { + if (args.len != 1) { + std.log.err("help does not accept any cmdline arguments", .{}); + return 1; + } + try help(allocator); + return 0; + } if (std.mem.eql(u8, "get-install-dir", args[0])) { if (args.len != 1) { std.log.err("get-install-dir does not accept any cmdline arguments", .{}); @@ -431,8 +481,8 @@ pub fn main2() !u8 { error.AlreadyReported => return 1, else => |e| return e, }; - try std.io.getStdOut().writer().writeAll(install_dir); - try std.io.getStdOut().writer().writeAll("\n"); + try std.fs.File.stdout().deprecatedWriter().writeAll(install_dir); + try std.fs.File.stdout().deprecatedWriter().writeAll("\n"); return 0; } if (std.mem.eql(u8, "set-install-dir", args[0])) { @@ -461,7 +511,7 @@ pub fn main2() !u8 { } var download_index = try fetchDownloadIndex(allocator, index_url); defer download_index.deinit(allocator); - try std.io.getStdOut().writeAll(download_index.text); + try std.fs.File.stdout().writeAll(download_index.text); return 0; } if (std.mem.eql(u8, "fetch", args[0])) { @@ -564,9 +614,9 @@ pub fn runCompiler(allocator: Allocator, args: []const []const u8) !u8 { return 1; } - var argv = std.ArrayList([]const u8).init(allocator); - try argv.append(try std.fs.path.join(allocator, &.{ compiler_dir, "files", comptime "zig" ++ builtin.target.exeFileExt() })); - try argv.appendSlice(args[1..]); + var argv: std.ArrayList([]const u8) = .empty; + try argv.append(allocator, try std.fs.path.join(allocator, &.{ compiler_dir, "files", comptime "zig" ++ builtin.target.exeFileExt() })); + try argv.appendSlice(allocator, args[1..]); // TODO: use "execve" if on linux var proc = std.process.Child.init(argv.items, allocator); @@ -622,7 +672,7 @@ fn fetchCompiler( if (builtin.os.tag == .windows) { var file = try std.fs.createFileAbsolute(master_symlink, .{}); defer file.close(); - try file.writer().writeAll(version_url.version); + try file.writeAll(version_url.version); } else { _ = try loggyUpdateSymlink(version_url.version, master_symlink, .{ .is_directory = true }); } @@ -723,6 +773,7 @@ pub fn loggyUpdateSymlink(target_path: []const u8, sym_link_path: []const u8, fl fn existsAbsolute(absolutePath: []const u8) !bool { std.fs.cwd().access(absolutePath, .{}) catch |e| switch (e) { error.FileNotFound => return false, + error.AccessDenied => return e, error.PermissionDenied => return e, error.InputOutput => return e, error.SystemResources => return e, @@ -748,7 +799,7 @@ fn listCompilers(allocator: Allocator) !void { }; defer install_dir.close(); - const stdout = std.io.getStdOut().writer(); + const stdout = std.fs.File.stdout().deprecatedWriter(); { var it = install_dir.iterate(); while (try it.next()) |entry| { @@ -892,7 +943,7 @@ fn getMasterDir(allocator: Allocator, install_dir: *std.fs.Dir) !?[]const u8 { fn printDefaultCompiler(allocator: Allocator) !void { const default_compiler_opt = try getDefaultCompiler(allocator); defer if (default_compiler_opt) |default_compiler| allocator.free(default_compiler); - const stdout = std.io.getStdOut().writer(); + const stdout = std.fs.File.stdout().deprecatedWriter(); if (default_compiler_opt) |default_compiler| { try stdout.print("{s}\n", .{default_compiler}); } else { @@ -1108,7 +1159,7 @@ const win32 = struct { pub extern "kernel32" fn GetFileInformationByHandle( hFile: ?@import("std").os.windows.HANDLE, lpFileInformation: ?*BY_HANDLE_FILE_INFORMATION, - ) callconv(@import("std").os.windows.WINAPI) BOOL; + ) callconv(.winapi) BOOL; }; const win32exelink = struct { @@ -1141,9 +1192,9 @@ fn createExeLink(link_target: []const u8, path_link: []const u8) !void { else => |e| return e, }; defer file.close(); - try file.writer().writeAll(win32exelink.content[0..win32exelink.exe_offset]); - try file.writer().writeAll(link_target); - try file.writer().writeAll(win32exelink.content[win32exelink.exe_offset + link_target.len ..]); + try file.writeAll(win32exelink.content[0..win32exelink.exe_offset]); + try file.writeAll(link_target); + try file.writeAll(win32exelink.content[win32exelink.exe_offset + link_target.len ..]); } const Release = struct { @@ -1168,12 +1219,12 @@ const SemanticVersion = struct { major: usize, minor: usize, patch: usize, - pre: ?std.BoundedArray(u8, max_pre), - build: ?std.BoundedArray(u8, max_build), + pre: ?BoundedArray(u8, max_pre), + build: ?BoundedArray(u8, max_build), - pub fn array(self: *const SemanticVersion) std.BoundedArray(u8, max_string) { - var result: std.BoundedArray(u8, max_string) = undefined; - const roundtrip = std.fmt.bufPrint(&result.buffer, "{}", .{self}) catch unreachable; + pub fn array(self: *const SemanticVersion) BoundedArray(u8, max_string) { + var result: BoundedArray(u8, max_string) = undefined; + const roundtrip = std.fmt.bufPrint(&result.buffer, "{f}", .{self}) catch unreachable; result.len = roundtrip.len; return result; } @@ -1188,10 +1239,10 @@ const SemanticVersion = struct { .major = parsed.major, .minor = parsed.minor, .patch = parsed.patch, - .pre = if (parsed.pre) |pre| std.BoundedArray(u8, max_pre).init(pre.len) catch |e| switch (e) { + .pre = if (parsed.pre) |pre| BoundedArray(u8, max_pre).init(pre.len) catch |e| switch (e) { error.Overflow => std.debug.panic("semantic version pre '{s}' is too long (max is {})", .{ pre, max_pre }), } else null, - .build = if (parsed.build) |build| std.BoundedArray(u8, max_build).init(build.len) catch |e| switch (e) { + .build = if (parsed.build) |build| BoundedArray(u8, max_build).init(build.len) catch |e| switch (e) { error.Overflow => std.debug.panic("semantic version build '{s}' is too long (max is {})", .{ build, max_build }), } else null, }; @@ -1218,13 +1269,8 @@ const SemanticVersion = struct { .build = if (self.build) |*build| build.slice() else null, }; } - pub fn format( - self: SemanticVersion, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, - writer: anytype, - ) !void { - try self.ref().format(fmt, options, writer); + pub fn format(self: SemanticVersion, writer: *std.Io.Writer) std.Io.Writer.Error!void { + try self.ref().format(writer); } }; @@ -1277,7 +1323,7 @@ fn installCompiler(allocator: Allocator, compiler_dir: []const u8, url: []const // note: important to close the file before we handle errors below // since it will delete the parent directory of this file defer file.close(); - break :blk download(allocator, url, file.writer()); + break :blk download(allocator, url, file.deprecatedWriter()); }) { .ok => {}, .err => |err| { @@ -1304,7 +1350,9 @@ fn installCompiler(allocator: Allocator, compiler_dir: []const u8, url: []const var timer = try std.time.Timer.start(); var archive_file = try std.fs.openFileAbsolute(archive_absolute, .{}); defer archive_file.close(); - try std.zip.extract(installing_dir_opened, archive_file.seekableStream(), .{}); + var archive_reader_buffer: [4096]u8 = undefined; + var archive_reader = archive_file.reader(&archive_reader_buffer); + try std.zip.extract(installing_dir_opened, &archive_reader, .{}); const time = timer.read(); loginfo("extracted archive in {d:.2} s", .{@as(f32, @floatFromInt(time)) / @as(f32, @floatFromInt(std.time.ns_per_s))}); } diff --git a/zip.zig b/zip.zig index bf9a61e..34fa1ba 100644 --- a/zip.zig +++ b/zip.zig @@ -10,7 +10,7 @@ fn fatal(comptime fmt: []const u8, args: anytype) noreturn { } fn usage() noreturn { - std.io.getStdErr().writer().writeAll( + std.fs.File.stderr().deprecatedWriter().writeAll( "Usage: zip [-options] ZIP_FILE FILES/DIRS..\n", ) catch |e| @panic(@errorName(e)); std.process.exit(1); @@ -124,7 +124,7 @@ pub fn main() !void { .filename_len = @intCast(file.path.len), .extra_len = 0, }; - try writeStructEndian(zip_file.writer(), hdr, .little); + try writeStructEndian(zip_file.deprecatedWriter(), hdr, .little); } } } @@ -139,11 +139,11 @@ fn writeZip( file_entries: []const FileEntry, store: []FileStore, ) !void { - var zipper = initZipper(out_zip.writer()); + var zipper = initZipper(out_zip.deprecatedWriter()); for (file_entries, 0..) |file_entry, i| { const file_offset = zipper.counting_writer.bytes_written; - const compression: std.zip.CompressionMethod = .deflate; + const compression: std.zip.CompressionMethod = .store; try zipper.writeFileHeader(file_entry.path, compression); @@ -152,15 +152,15 @@ fn writeZip( var crc32: u32 = undefined; - var compressed_size = file_entry.size; + const compressed_size = file_entry.size; switch (compression) { .store => { var hash = std.hash.Crc32.init(); - var full_rw_buf: [std.mem.page_size]u8 = undefined; + var full_rw_buf: [4096]u8 = undefined; var remaining = file_entry.size; while (remaining > 0) { const buf = full_rw_buf[0..@min(remaining, full_rw_buf.len)]; - const read_len = try file.reader().read(buf); + const read_len = try file.deprecatedReader().read(buf); std.debug.assert(read_len == buf.len); hash.update(buf); try zipper.counting_writer.writer().writeAll(buf); @@ -168,21 +168,6 @@ fn writeZip( } crc32 = hash.final(); }, - .deflate => { - const start_offset = zipper.counting_writer.bytes_written; - var br = std.io.bufferedReader(file.reader()); - var cr = Crc32Reader(@TypeOf(br.reader())){ .underlying_reader = br.reader() }; - - try std.compress.flate.deflate.compress( - .raw, - cr.reader(), - zipper.counting_writer.writer(), - .{ .level = .best }, - ); - if (br.end != br.start) fatal("deflate compressor didn't read all data", .{}); - compressed_size = zipper.counting_writer.bytes_written - start_offset; - crc32 = cr.crc32.final(); - }, else => @panic("codebug"), } store[i] = .{ @@ -250,12 +235,33 @@ pub const FileStore = struct { }; pub fn initZipper(writer: anytype) Zipper(@TypeOf(writer)) { - return .{ .counting_writer = std.io.countingWriter(writer) }; + return .{ .counting_writer = .{ .underlying_writer = writer } }; +} + +fn CountingWriter(comptime ChildWriter: type) type { + return struct { + underlying_writer: ChildWriter, + bytes_written: u64 = 0, + + const Self = @This(); + pub const Error = ChildWriter.Error; + pub const Writer = std.io.GenericWriter(*Self, Error, write); + + fn write(self: *Self, bytes: []const u8) Error!usize { + const len = try self.underlying_writer.write(bytes); + self.bytes_written += len; + return len; + } + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + }; } fn Zipper(comptime Writer: type) type { return struct { - counting_writer: std.io.CountingWriter(Writer), + counting_writer: CountingWriter(Writer), central_count: u64 = 0, first_central_offset: ?u64 = null, last_central_limit: ?u64 = null,