Commit d94613c1d0

Andrew Kelley <andrew@ziglang.org>
2023-01-24 23:09:02
support xz compressed tarballs in the package manager
This includes a breaking change: std.compress.gzip.GzipStream renamed to std.compress.gzip.Decompress This follows the same naming convention as std.compress.xz so that the stream type can be passed as a comptime parameter.
1 parent ea9ded8
Changed files (2)
lib
std
compress
src
lib/std/compress/gzip.zig
@@ -1,7 +1,7 @@
 //
 // Decompressor for GZIP data streams (RFC1952)
 
-const std = @import("std");
+const std = @import("../std.zig");
 const io = std.io;
 const fs = std.fs;
 const testing = std.testing;
@@ -17,10 +17,7 @@ const FCOMMENT = 1 << 4;
 
 const max_string_len = 1024;
 
-/// TODO: the fully qualified namespace to this declaration is
-/// std.compress.gzip.GzipStream which has a redundant "gzip" in the name.
-/// Instead, it should be `std.compress.gzip.Stream`.
-pub fn GzipStream(comptime ReaderType: type) type {
+pub fn Decompress(comptime ReaderType: type) type {
     return struct {
         const Self = @This();
 
@@ -154,14 +151,14 @@ pub fn GzipStream(comptime ReaderType: type) type {
     };
 }
 
-pub fn gzipStream(allocator: mem.Allocator, reader: anytype) !GzipStream(@TypeOf(reader)) {
-    return GzipStream(@TypeOf(reader)).init(allocator, reader);
+pub fn decompress(allocator: mem.Allocator, reader: anytype) !Decompress(@TypeOf(reader)) {
+    return Decompress(@TypeOf(reader)).init(allocator, reader);
 }
 
 fn testReader(data: []const u8, comptime expected: []const u8) !void {
     var in_stream = io.fixedBufferStream(data);
 
-    var gzip_stream = try gzipStream(testing.allocator, in_stream.reader());
+    var gzip_stream = try decompress(testing.allocator, in_stream.reader());
     defer gzip_stream.deinit();
 
     // Read and decompress the whole file
src/Package.zig
@@ -370,14 +370,11 @@ fn fetchAndUnpack(
         if (mem.endsWith(u8, uri.path, ".tar.gz")) {
             // I observed the gzip stream to read 1 byte at a time, so I am using a
             // buffered reader on the front of it.
-            var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, req.reader());
-
-            var gzip_stream = try std.compress.gzip.gzipStream(gpa, br.reader());
-            defer gzip_stream.deinit();
-
-            try std.tar.pipeToFileSystem(tmp_directory.handle, gzip_stream.reader(), .{
-                .strip_components = 1,
-            });
+            try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.gzip);
+        } else if (mem.endsWith(u8, uri.path, ".tar.xz")) {
+            // I have not checked what buffer sizes the xz decompression implementation uses
+            // by default, so the same logic applies for buffering the reader as for gzip.
+            try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz);
         } else {
             return reportError(
                 ini,
@@ -430,6 +427,22 @@ fn fetchAndUnpack(
     return createWithDir(gpa, fqn, global_cache_directory, pkg_dir_sub_path, build_zig_basename);
 }
 
+fn unpackTarball(
+    gpa: Allocator,
+    req: *std.http.Client.Request,
+    out_dir: fs.Dir,
+    comptime compression: type,
+) !void {
+    var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, req.reader());
+
+    var decompress = try compression.decompress(gpa, br.reader());
+    defer decompress.deinit();
+
+    try std.tar.pipeToFileSystem(out_dir, decompress.reader(), .{
+        .strip_components = 1,
+    });
+}
+
 fn reportError(
     ini: std.Ini,
     comp_directory: Compilation.Directory,