Skip to content

zstd (zstandard) decompression stream heap allocates #18937

@andrewrk

Description

@andrewrk

Decompression should use O(1) memory.

#18923 removed the requirement of an allocator from decompressing deflate and gzip streams. This is great, but zstd is left behind, leaving this sad situation:

        switch (res.request.compression) {
            .none => {},
            .deflate => {},
            .gzip => {},
            .zstd => |*zstd| zstd.deinit(),
        }

and

                .deflate => res.request.compression = .{
                    .deflate = std.compress.zlib.decompressStream(res.transferReader()),
                },
                .gzip, .@"x-gzip" => res.request.compression = .{
                    .gzip = std.compress.gzip.decompress(res.transferReader()),
                },
                .zstd => res.request.compression = .{
                    .zstd = std.compress.zstd.decompressStream(res.allocator, res.transferReader()),
                },

zstd is holding us back.

Here's a diff to get started on this issue:

diff --git a/lib/std/compress/zstandard.zig b/lib/std/compress/zstandard.zig
index 4d9421acac..a3dcd37c2a 100644
--- a/lib/std/compress/zstandard.zig
+++ b/lib/std/compress/zstandard.zig
@@ -20,7 +20,6 @@ pub fn DecompressStream(
     return struct {
         const Self = @This();
 
-        allocator: Allocator,
         source: std.io.CountingReader(ReaderType),
         state: enum { NewFrame, InFrame, LastBlock },
         decode_state: decompress.block.DecodeState,
@@ -44,9 +43,8 @@ pub fn DecompressStream(
 
         pub const Reader = std.io.Reader(*Self, Error, read);
 
-        pub fn init(allocator: Allocator, source: ReaderType) Self {
+        pub fn init(source: ReaderType) Self {
             return Self{
-                .allocator = allocator,
                 .source = std.io.countingReader(source),
                 .state = .NewFrame,
                 .decode_state = undefined,
@@ -126,16 +124,6 @@ pub fn DecompressStream(
             }
         }
 
-        pub fn deinit(self: *Self) void {
-            if (self.state == .NewFrame) return;
-            self.allocator.free(self.decode_state.literal_fse_buffer);
-            self.allocator.free(self.decode_state.match_fse_buffer);
-            self.allocator.free(self.decode_state.offset_fse_buffer);
-            self.allocator.free(self.literals_buffer);
-            self.allocator.free(self.sequence_buffer);
-            self.buffer.deinit(self.allocator);
-        }
-
         pub fn reader(self: *Self) Reader {
             return .{ .context = self };
         }
@@ -235,18 +223,16 @@ pub fn DecompressStream(
 }
 
 pub fn decompressStreamOptions(
-    allocator: Allocator,
     reader: anytype,
     comptime options: DecompressStreamOptions,
 ) DecompressStream(@TypeOf(reader, options)) {
-    return DecompressStream(@TypeOf(reader), options).init(allocator, reader);
+    return DecompressStream(@TypeOf(reader), options).init(reader);
 }
 
 pub fn decompressStream(
-    allocator: Allocator,
     reader: anytype,
 ) DecompressStream(@TypeOf(reader), .{}) {
-    return DecompressStream(@TypeOf(reader), .{}).init(allocator, reader);
+    return DecompressStream(@TypeOf(reader), .{}).init(reader);
 }
 
 fn testDecompress(data: []const u8) ![]u8 {

Metadata

Metadata

Assignees

No one assigned

    Labels

    breakingImplementing this issue could cause existing code to no longer compile or have different behavior.enhancementSolving this issue will likely involve adding new logic or components to the codebase.standard libraryThis issue involves writing Zig code for the standard library.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions