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.
This commit is contained in:
Andrew Kelley 2023-01-24 15:09:02 -07:00
parent ea9ded8758
commit d94613c1d0
2 changed files with 26 additions and 16 deletions

View File

@ -1,7 +1,7 @@
// //
// Decompressor for GZIP data streams (RFC1952) // Decompressor for GZIP data streams (RFC1952)
const std = @import("std"); const std = @import("../std.zig");
const io = std.io; const io = std.io;
const fs = std.fs; const fs = std.fs;
const testing = std.testing; const testing = std.testing;
@ -17,10 +17,7 @@ const FCOMMENT = 1 << 4;
const max_string_len = 1024; const max_string_len = 1024;
/// TODO: the fully qualified namespace to this declaration is pub fn Decompress(comptime ReaderType: type) type {
/// 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 {
return struct { return struct {
const Self = @This(); 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)) { pub fn decompress(allocator: mem.Allocator, reader: anytype) !Decompress(@TypeOf(reader)) {
return GzipStream(@TypeOf(reader)).init(allocator, reader); return Decompress(@TypeOf(reader)).init(allocator, reader);
} }
fn testReader(data: []const u8, comptime expected: []const u8) !void { fn testReader(data: []const u8, comptime expected: []const u8) !void {
var in_stream = io.fixedBufferStream(data); 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(); defer gzip_stream.deinit();
// Read and decompress the whole file // Read and decompress the whole file

View File

@ -370,14 +370,11 @@ fn fetchAndUnpack(
if (mem.endsWith(u8, uri.path, ".tar.gz")) { 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 // I observed the gzip stream to read 1 byte at a time, so I am using a
// buffered reader on the front of it. // buffered reader on the front of it.
var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, req.reader()); try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.gzip);
} else if (mem.endsWith(u8, uri.path, ".tar.xz")) {
var gzip_stream = try std.compress.gzip.gzipStream(gpa, br.reader()); // I have not checked what buffer sizes the xz decompression implementation uses
defer gzip_stream.deinit(); // by default, so the same logic applies for buffering the reader as for gzip.
try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz);
try std.tar.pipeToFileSystem(tmp_directory.handle, gzip_stream.reader(), .{
.strip_components = 1,
});
} else { } else {
return reportError( return reportError(
ini, ini,
@ -430,6 +427,22 @@ fn fetchAndUnpack(
return createWithDir(gpa, fqn, global_cache_directory, pkg_dir_sub_path, build_zig_basename); 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( fn reportError(
ini: std.Ini, ini: std.Ini,
comp_directory: Compilation.Directory, comp_directory: Compilation.Directory,