From 06ce15e8f719756cc12d928cfdae12be99a9e4c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Mon, 23 Jan 2023 11:46:40 -0800 Subject: [PATCH 1/5] Add an xz decoder to the standard library --- build.zig | 2 + lib/std/compress/xz.zig | 5 + lib/std/compress/xz/block.zig | 319 +++++++++ lib/std/compress/xz/check.zig | 7 + lib/std/compress/xz/lzma.zig | 658 ++++++++++++++++++ lib/std/compress/xz/multibyte.zig | 23 + lib/std/compress/xz/stream.zig | 136 ++++ lib/std/compress/xz/stream_test.zig | 80 +++ lib/std/compress/xz/testdata/good-0-empty.xz | Bin 0 -> 32 bytes .../compress/xz/testdata/good-0cat-empty.xz | Bin 0 -> 64 bytes .../xz/testdata/good-0catpad-empty.xz | Bin 0 -> 68 bytes .../compress/xz/testdata/good-0pad-empty.xz | Bin 0 -> 36 bytes .../xz/testdata/good-1-3delta-lzma2.xz | Bin 0 -> 528 bytes .../xz/testdata/good-1-arm64-lzma2-1.xz | Bin 0 -> 512 bytes .../xz/testdata/good-1-arm64-lzma2-2.xz | Bin 0 -> 488 bytes .../xz/testdata/good-1-block_header-1.xz | Bin 0 -> 72 bytes .../xz/testdata/good-1-block_header-2.xz | Bin 0 -> 68 bytes .../xz/testdata/good-1-block_header-3.xz | Bin 0 -> 68 bytes .../xz/testdata/good-1-check-crc32.xz | Bin 0 -> 68 bytes .../xz/testdata/good-1-check-crc64.xz | Bin 0 -> 72 bytes .../compress/xz/testdata/good-1-check-none.xz | Bin 0 -> 64 bytes .../xz/testdata/good-1-check-sha256.xz | Bin 0 -> 96 bytes .../xz/testdata/good-1-delta-lzma2.tiff.xz | Bin 0 -> 51316 bytes .../xz/testdata/good-1-empty-bcj-lzma2.xz | Bin 0 -> 52 bytes .../compress/xz/testdata/good-1-lzma2-1.xz | Bin 0 -> 424 bytes .../compress/xz/testdata/good-1-lzma2-2.xz | Bin 0 -> 424 bytes .../compress/xz/testdata/good-1-lzma2-3.xz | Bin 0 -> 408 bytes .../compress/xz/testdata/good-1-lzma2-4.xz | Bin 0 -> 464 bytes .../compress/xz/testdata/good-1-lzma2-5.xz | Bin 0 -> 52 bytes .../xz/testdata/good-1-sparc-lzma2.xz | Bin 0 -> 612 bytes .../compress/xz/testdata/good-1-x86-lzma2.xz | Bin 0 -> 716 bytes lib/std/compress/xz/testdata/good-2-lzma2.xz | Bin 0 -> 92 bytes 32 files changed, 1230 insertions(+) create mode 100644 lib/std/compress/xz.zig create mode 100644 lib/std/compress/xz/block.zig create mode 100644 lib/std/compress/xz/check.zig create mode 100644 lib/std/compress/xz/lzma.zig create mode 100644 lib/std/compress/xz/multibyte.zig create mode 100644 lib/std/compress/xz/stream.zig create mode 100644 lib/std/compress/xz/stream_test.zig create mode 100644 lib/std/compress/xz/testdata/good-0-empty.xz create mode 100644 lib/std/compress/xz/testdata/good-0cat-empty.xz create mode 100644 lib/std/compress/xz/testdata/good-0catpad-empty.xz create mode 100644 lib/std/compress/xz/testdata/good-0pad-empty.xz create mode 100644 lib/std/compress/xz/testdata/good-1-3delta-lzma2.xz create mode 100644 lib/std/compress/xz/testdata/good-1-arm64-lzma2-1.xz create mode 100644 lib/std/compress/xz/testdata/good-1-arm64-lzma2-2.xz create mode 100644 lib/std/compress/xz/testdata/good-1-block_header-1.xz create mode 100644 lib/std/compress/xz/testdata/good-1-block_header-2.xz create mode 100644 lib/std/compress/xz/testdata/good-1-block_header-3.xz create mode 100644 lib/std/compress/xz/testdata/good-1-check-crc32.xz create mode 100644 lib/std/compress/xz/testdata/good-1-check-crc64.xz create mode 100644 lib/std/compress/xz/testdata/good-1-check-none.xz create mode 100644 lib/std/compress/xz/testdata/good-1-check-sha256.xz create mode 100644 lib/std/compress/xz/testdata/good-1-delta-lzma2.tiff.xz create mode 100644 lib/std/compress/xz/testdata/good-1-empty-bcj-lzma2.xz create mode 100644 lib/std/compress/xz/testdata/good-1-lzma2-1.xz create mode 100644 lib/std/compress/xz/testdata/good-1-lzma2-2.xz create mode 100644 lib/std/compress/xz/testdata/good-1-lzma2-3.xz create mode 100644 lib/std/compress/xz/testdata/good-1-lzma2-4.xz create mode 100644 lib/std/compress/xz/testdata/good-1-lzma2-5.xz create mode 100644 lib/std/compress/xz/testdata/good-1-sparc-lzma2.xz create mode 100644 lib/std/compress/xz/testdata/good-1-x86-lzma2.xz create mode 100644 lib/std/compress/xz/testdata/good-2-lzma2.xz diff --git a/build.zig b/build.zig index 4161a6396e68..3a7468243f49 100644 --- a/build.zig +++ b/build.zig @@ -122,6 +122,8 @@ pub fn build(b: *Builder) !void { "compress-gettysburg.txt", "compress-pi.txt", "rfc1951.txt", + // exclude files from lib/std/compress/xz/testdata + ".xz", // exclude files from lib/std/tz/ ".tzif", // others diff --git a/lib/std/compress/xz.zig b/lib/std/compress/xz.zig new file mode 100644 index 000000000000..3af2d91cfba9 --- /dev/null +++ b/lib/std/compress/xz.zig @@ -0,0 +1,5 @@ +pub usingnamespace @import("xz/stream.zig"); + +test { + _ = @import("xz/stream.zig"); +} diff --git a/lib/std/compress/xz/block.zig b/lib/std/compress/xz/block.zig new file mode 100644 index 000000000000..27b2fc0b5f07 --- /dev/null +++ b/lib/std/compress/xz/block.zig @@ -0,0 +1,319 @@ +const std = @import("std"); +const check = @import("check.zig"); +const lzma = @import("lzma.zig"); +const multibyte = @import("multibyte.zig"); +const Allocator = std.mem.Allocator; +const Crc32 = std.hash.Crc32; +const Crc64 = std.hash.crc.Crc64Xz; +const Sha256 = std.crypto.hash.sha2.Sha256; + +const DecodeError = error{ + CorruptInput, + EndOfStream, + EndOfStreamWithNoError, + WrongChecksum, + Unsupported, + Overflow, +}; + +pub fn decoder(allocator: Allocator, reader: anytype, check_kind: check.Kind) !Decoder(@TypeOf(reader)) { + return Decoder(@TypeOf(reader)).init(allocator, reader, check_kind); +} + +pub fn Decoder(comptime ReaderType: type) type { + return struct { + const Self = @This(); + pub const Error = + ReaderType.Error || + DecodeError || + Allocator.Error; + pub const Reader = std.io.Reader(*Self, Error, read); + + allocator: Allocator, + inner_reader: ReaderType, + check_kind: check.Kind, + err: ?Error, + accum: lzma.LzAccumBuffer, + lzma_state: lzma.DecoderState, + block_count: usize, + + fn init(allocator: Allocator, in_reader: ReaderType, check_kind: check.Kind) !Self { + return Self{ + .allocator = allocator, + .inner_reader = in_reader, + .check_kind = check_kind, + .err = null, + .accum = .{}, + .lzma_state = try lzma.DecoderState.init(allocator), + .block_count = 0, + }; + } + + pub fn deinit(self: *Self) void { + self.accum.deinit(self.allocator); + self.lzma_state.deinit(self.allocator); + } + + pub fn reader(self: *Self) Reader { + return .{ .context = self }; + } + + pub fn read(self: *Self, output: []u8) Error!usize { + while (true) { + if (self.accum.to_read.items.len > 0) { + const n = self.accum.read(output); + if (self.accum.to_read.items.len == 0 and self.err != null) { + if (self.err.? == DecodeError.EndOfStreamWithNoError) { + return n; + } + return self.err.?; + } + return n; + } + if (self.err != null) { + if (self.err.? == DecodeError.EndOfStreamWithNoError) { + return 0; + } + return self.err.?; + } + self.readBlock() catch |e| { + self.err = e; + if (self.accum.to_read.items.len == 0) { + try self.accum.reset(self.allocator); + } + }; + } + } + + fn readBlock(self: *Self) Error!void { + const unpacked_pos = self.accum.to_read.items.len; + + var block_counter = std.io.countingReader(self.inner_reader); + const block_reader = block_counter.reader(); + + var packed_size: ?u64 = null; + var unpacked_size: ?u64 = null; + + // Block Header + { + var header_hasher = std.compress.hashedReader(block_reader, Crc32.init()); + const header_reader = header_hasher.reader(); + + const header_size = try header_reader.readByte() * 4; + if (header_size == 0) + return error.EndOfStreamWithNoError; + + const Flags = packed struct(u8) { + last_filter_index: u2, + reserved: u4, + has_packed_size: bool, + has_unpacked_size: bool, + }; + + const flags = try header_reader.readStruct(Flags); + const filter_count = @as(u3, flags.last_filter_index) + 1; + if (filter_count > 1) + return error.Unsupported; + + if (flags.has_packed_size) + packed_size = try multibyte.readInt(header_reader); + + if (flags.has_unpacked_size) + unpacked_size = try multibyte.readInt(header_reader); + + const FilterId = enum(u64) { + lzma2 = 0x21, + _, + }; + + const filter_id = @intToEnum( + FilterId, + try multibyte.readInt(header_reader), + ); + + if (@enumToInt(filter_id) >= 0x4000_0000_0000_0000) + return error.CorruptInput; + + if (filter_id != .lzma2) + return error.Unsupported; + + const properties_size = try multibyte.readInt(header_reader); + if (properties_size != 1) + return error.CorruptInput; + + // TODO: use filter properties + _ = try header_reader.readByte(); + + while (block_counter.bytes_read != header_size) { + if (try header_reader.readByte() != 0) + return error.CorruptInput; + } + + const hash_a = header_hasher.hasher.final(); + const hash_b = try header_reader.readIntLittle(u32); + if (hash_a != hash_b) + return error.WrongChecksum; + } + + // Compressed Data + var packed_counter = std.io.countingReader(block_reader); + const packed_reader = packed_counter.reader(); + while (try self.readLzma2Chunk(packed_reader)) {} + + if (packed_size) |s| { + if (s != packed_counter.bytes_read) + return error.CorruptInput; + } + + const unpacked_bytes = self.accum.to_read.items[unpacked_pos..]; + if (unpacked_size) |s| { + if (s != unpacked_bytes.len) + return error.CorruptInput; + } + + // Block Padding + while (block_counter.bytes_read % 4 != 0) { + if (try block_reader.readByte() != 0) + return error.CorruptInput; + } + + // Check + switch (self.check_kind) { + .none => {}, + .crc32 => { + const hash_a = Crc32.hash(unpacked_bytes); + const hash_b = try self.inner_reader.readIntLittle(u32); + if (hash_a != hash_b) + return error.WrongChecksum; + }, + .crc64 => { + const hash_a = Crc64.hash(unpacked_bytes); + const hash_b = try self.inner_reader.readIntLittle(u64); + if (hash_a != hash_b) + return error.WrongChecksum; + }, + .sha256 => { + var hash_a: [Sha256.digest_length]u8 = undefined; + Sha256.hash(unpacked_bytes, &hash_a, .{}); + + var hash_b: [Sha256.digest_length]u8 = undefined; + try self.inner_reader.readNoEof(&hash_b); + + if (!std.mem.eql(u8, &hash_a, &hash_b)) + return error.WrongChecksum; + }, + else => return error.Unsupported, + } + + self.block_count += 1; + } + + fn readLzma2Chunk(self: *Self, packed_reader: anytype) Error!bool { + const status = try packed_reader.readByte(); + switch (status) { + 0 => { + try self.accum.reset(self.allocator); + return false; + }, + 1, 2 => { + if (status == 1) + try self.accum.reset(self.allocator); + + const size = try packed_reader.readIntBig(u16) + 1; + try self.accum.ensureUnusedCapacity(self.allocator, size); + + var i: usize = 0; + while (i < size) : (i += 1) + self.accum.appendAssumeCapacity(try packed_reader.readByte()); + + return true; + }, + else => { + if (status & 0x80 == 0) + return error.CorruptInput; + + const Reset = struct { + dict: bool, + state: bool, + props: bool, + }; + + const reset = switch ((status >> 5) & 0x3) { + 0 => Reset{ + .dict = false, + .state = false, + .props = false, + }, + 1 => Reset{ + .dict = false, + .state = true, + .props = false, + }, + 2 => Reset{ + .dict = false, + .state = true, + .props = true, + }, + 3 => Reset{ + .dict = true, + .state = true, + .props = true, + }, + else => unreachable, + }; + + const unpacked_size = blk: { + var tmp: u64 = status & 0x1F; + tmp <<= 16; + tmp |= try packed_reader.readIntBig(u16); + break :blk tmp + 1; + }; + + const packed_size = blk: { + var tmp: u64 = try packed_reader.readIntBig(u16); + break :blk tmp + 1; + }; + + if (reset.dict) + try self.accum.reset(self.allocator); + + if (reset.state) { + var new_props = self.lzma_state.lzma_props; + + if (reset.props) { + var props = try packed_reader.readByte(); + if (props >= 225) + return error.CorruptInput; + + const lc = @intCast(u4, props % 9); + props /= 9; + const lp = @intCast(u3, props % 5); + props /= 5; + const pb = @intCast(u3, props); + + if (lc + lp > 4) + return error.CorruptInput; + + new_props = .{ .lc = lc, .lp = lp, .pb = pb }; + } + + try self.lzma_state.reset_state(self.allocator, new_props); + } + + self.lzma_state.unpacked_size = unpacked_size + self.accum.len(); + + const buffer = try self.allocator.alloc(u8, packed_size); + defer self.allocator.free(buffer); + + for (buffer) |*b| + b.* = try packed_reader.readByte(); + + var rangecoder = try lzma.RangeDecoder.init(buffer); + try self.lzma_state.process(self.allocator, &self.accum, &rangecoder); + + return true; + }, + } + } + }; +} diff --git a/lib/std/compress/xz/check.zig b/lib/std/compress/xz/check.zig new file mode 100644 index 000000000000..20151ad4cf3a --- /dev/null +++ b/lib/std/compress/xz/check.zig @@ -0,0 +1,7 @@ +pub const Kind = enum(u4) { + none = 0x00, + crc32 = 0x01, + crc64 = 0x04, + sha256 = 0x0A, + _, +}; diff --git a/lib/std/compress/xz/lzma.zig b/lib/std/compress/xz/lzma.zig new file mode 100644 index 000000000000..ead707e0be6b --- /dev/null +++ b/lib/std/compress/xz/lzma.zig @@ -0,0 +1,658 @@ +// Ported from https://github.com/gendx/lzma-rs + +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const ArrayListUnmanaged = std.ArrayListUnmanaged; + +const LzmaProperties = struct { + lc: u4, + lp: u3, + pb: u3, + + fn validate(self: LzmaProperties) void { + assert(self.lc <= 8); + assert(self.lp <= 4); + assert(self.pb <= 4); + } +}; + +pub const DecoderState = struct { + lzma_props: LzmaProperties, + unpacked_size: ?u64, + literal_probs: Vec2D(u16), + pos_slot_decoder: [4]BitTree, + align_decoder: BitTree, + pos_decoders: [115]u16, + is_match: [192]u16, + is_rep: [12]u16, + is_rep_g0: [12]u16, + is_rep_g1: [12]u16, + is_rep_g2: [12]u16, + is_rep_0long: [192]u16, + state: usize, + rep: [4]usize, + len_decoder: LenDecoder, + rep_len_decoder: LenDecoder, + + pub fn init(allocator: Allocator) !DecoderState { + return .{ + .lzma_props = LzmaProperties{ .lc = 0, .lp = 0, .pb = 0 }, + .unpacked_size = null, + .literal_probs = try Vec2D(u16).init(allocator, 0x400, 1, 0x300), + .pos_slot_decoder = .{ + try BitTree.init(allocator, 6), + try BitTree.init(allocator, 6), + try BitTree.init(allocator, 6), + try BitTree.init(allocator, 6), + }, + .align_decoder = try BitTree.init(allocator, 4), + .pos_decoders = .{0x400} ** 115, + .is_match = .{0x400} ** 192, + .is_rep = .{0x400} ** 12, + .is_rep_g0 = .{0x400} ** 12, + .is_rep_g1 = .{0x400} ** 12, + .is_rep_g2 = .{0x400} ** 12, + .is_rep_0long = .{0x400} ** 192, + .state = 0, + .rep = .{0} ** 4, + .len_decoder = try LenDecoder.init(allocator), + .rep_len_decoder = try LenDecoder.init(allocator), + }; + } + + pub fn deinit(self: *DecoderState, allocator: Allocator) void { + self.literal_probs.deinit(allocator); + for (self.pos_slot_decoder) |*t| t.deinit(allocator); + self.align_decoder.deinit(allocator); + self.len_decoder.deinit(allocator); + self.rep_len_decoder.deinit(allocator); + } + + pub fn reset_state(self: *DecoderState, allocator: Allocator, new_props: LzmaProperties) !void { + new_props.validate(); + if (self.lzma_props.lc + self.lzma_props.lp == new_props.lc + new_props.lp) { + self.literal_probs.fill(0x400); + } else { + self.literal_probs.deinit(allocator); + self.literal_probs = try Vec2D(u16).init(allocator, 0x400, @as(usize, 1) << (new_props.lc + new_props.lp), 0x300); + } + + self.lzma_props = new_props; + for (self.pos_slot_decoder) |*t| t.reset(); + self.align_decoder.reset(); + self.pos_decoders = .{0x400} ** 115; + self.is_match = .{0x400} ** 192; + self.is_rep = .{0x400} ** 12; + self.is_rep_g0 = .{0x400} ** 12; + self.is_rep_g1 = .{0x400} ** 12; + self.is_rep_g2 = .{0x400} ** 12; + self.is_rep_0long = .{0x400} ** 192; + self.state = 0; + self.rep = .{0} ** 4; + self.len_decoder.reset(); + self.rep_len_decoder.reset(); + } + + fn processNextInner( + self: *DecoderState, + allocator: Allocator, + output: *LzAccumBuffer, + rangecoder: *RangeDecoder, + update: bool, + ) !ProcessingStatus { + const pos_state = output.len() & ((@as(usize, 1) << self.lzma_props.pb) - 1); + + if (!try rangecoder.decodeBit( + &self.is_match[(self.state << 4) + pos_state], + update, + )) { + const byte: u8 = try self.decodeLiteral(output, rangecoder, update); + + if (update) { + try output.appendLiteral(allocator, byte); + + self.state = if (self.state < 4) + 0 + else if (self.state < 10) + self.state - 3 + else + self.state - 6; + } + return .continue_; + } + + var len: usize = undefined; + if (try rangecoder.decodeBit(&self.is_rep[self.state], update)) { + if (!try rangecoder.decodeBit(&self.is_rep_g0[self.state], update)) { + if (!try rangecoder.decodeBit( + &self.is_rep_0long[(self.state << 4) + pos_state], + update, + )) { + if (update) { + self.state = if (self.state < 7) 9 else 11; + const dist = self.rep[0] + 1; + try output.appendLz(allocator, 1, dist); + } + return .continue_; + } + } else { + const idx: usize = if (!try rangecoder.decodeBit(&self.is_rep_g1[self.state], update)) + 1 + else if (!try rangecoder.decodeBit(&self.is_rep_g2[self.state], update)) + 2 + else + 3; + if (update) { + const dist = self.rep[idx]; + var i = idx; + while (i > 0) : (i -= 1) { + self.rep[i] = self.rep[i - 1]; + } + self.rep[0] = dist; + } + } + + len = try self.rep_len_decoder.decode(rangecoder, pos_state, update); + + if (update) { + self.state = if (self.state < 7) 8 else 11; + } + } else { + if (update) { + self.rep[3] = self.rep[2]; + self.rep[2] = self.rep[1]; + self.rep[1] = self.rep[0]; + } + + len = try self.len_decoder.decode(rangecoder, pos_state, update); + + if (update) { + self.state = if (self.state < 7) 7 else 10; + } + + const rep_0 = try self.decodeDistance(rangecoder, len, update); + + if (update) { + self.rep[0] = rep_0; + if (self.rep[0] == 0xFFFF_FFFF) { + if (rangecoder.isFinished()) { + return .finished; + } + return error.CorruptInput; + } + } + } + + if (update) { + len += 2; + + const dist = self.rep[0] + 1; + try output.appendLz(allocator, len, dist); + } + + return .continue_; + } + + fn processNext( + self: *DecoderState, + allocator: Allocator, + output: *LzAccumBuffer, + rangecoder: *RangeDecoder, + ) !ProcessingStatus { + return self.processNextInner(allocator, output, rangecoder, true); + } + + pub fn process( + self: *DecoderState, + allocator: Allocator, + output: *LzAccumBuffer, + rangecoder: *RangeDecoder, + ) !void { + while (true) { + if (self.unpacked_size) |unpacked_size| { + if (output.len() >= unpacked_size) { + break; + } + } else if (rangecoder.isFinished()) { + break; + } + + if (try self.processNext(allocator, output, rangecoder) == .finished) { + break; + } + } + + if (self.unpacked_size) |len| { + if (len != output.len()) { + return error.CorruptInput; + } + } + } + + fn decodeLiteral( + self: *DecoderState, + output: *LzAccumBuffer, + rangecoder: *RangeDecoder, + update: bool, + ) !u8 { + const def_prev_byte = 0; + const prev_byte = @as(usize, output.lastOr(def_prev_byte)); + + var result: usize = 1; + const lit_state = ((output.len() & ((@as(usize, 1) << self.lzma_props.lp) - 1)) << self.lzma_props.lc) + + (prev_byte >> (8 - self.lzma_props.lc)); + const probs = try self.literal_probs.get(lit_state); + + if (self.state >= 7) { + var match_byte = @as(usize, try output.lastN(self.rep[0] + 1)); + + while (result < 0x100) { + const match_bit = (match_byte >> 7) & 1; + match_byte <<= 1; + const bit = @boolToInt(try rangecoder.decodeBit( + &probs[((@as(usize, 1) + match_bit) << 8) + result], + update, + )); + result = (result << 1) ^ bit; + if (match_bit != bit) { + break; + } + } + } + + while (result < 0x100) { + result = (result << 1) ^ @boolToInt(try rangecoder.decodeBit(&probs[result], update)); + } + + return @truncate(u8, result - 0x100); + } + + fn decodeDistance( + self: *DecoderState, + rangecoder: *RangeDecoder, + length: usize, + update: bool, + ) !usize { + const len_state = if (length > 3) 3 else length; + + const pos_slot = @as(usize, try self.pos_slot_decoder[len_state].parse(rangecoder, update)); + if (pos_slot < 4) + return pos_slot; + + const num_direct_bits = @intCast(u5, (pos_slot >> 1) - 1); + var result = (2 ^ (pos_slot & 1)) << num_direct_bits; + + if (pos_slot < 14) { + result += try rangecoder.parseReverseBitTree( + num_direct_bits, + &self.pos_decoders, + result - pos_slot, + update, + ); + } else { + result += @as(usize, try rangecoder.get(num_direct_bits - 4)) << 4; + result += try self.align_decoder.parseReverse(rangecoder, update); + } + + return result; + } +}; + +const ProcessingStatus = enum { + continue_, + finished, +}; + +pub const LzAccumBuffer = struct { + to_read: ArrayListUnmanaged(u8) = .{}, + buf: ArrayListUnmanaged(u8) = .{}, + + pub fn deinit(self: *LzAccumBuffer, allocator: Allocator) void { + self.to_read.deinit(allocator); + self.buf.deinit(allocator); + } + + pub fn read(self: *LzAccumBuffer, output: []u8) usize { + const input = self.to_read.items; + const n = std.math.min(input.len, output.len); + std.mem.copy(u8, output[0..n], input[0..n]); + std.mem.copy(u8, input, input[n..]); + self.to_read.shrinkRetainingCapacity(input.len - n); + return n; + } + + pub fn ensureUnusedCapacity( + self: *LzAccumBuffer, + allocator: Allocator, + additional_count: usize, + ) !void { + try self.buf.ensureUnusedCapacity(allocator, additional_count); + } + + pub fn appendAssumeCapacity(self: *LzAccumBuffer, byte: u8) void { + self.buf.appendAssumeCapacity(byte); + } + + pub fn reset(self: *LzAccumBuffer, allocator: Allocator) !void { + try self.to_read.appendSlice(allocator, self.buf.items); + self.buf.clearRetainingCapacity(); + } + + pub fn len(self: *const LzAccumBuffer) usize { + return self.buf.items.len; + } + + pub fn lastOr(self: *const LzAccumBuffer, lit: u8) u8 { + const buf_len = self.buf.items.len; + return if (buf_len == 0) + lit + else + self.buf.items[buf_len - 1]; + } + + pub fn lastN(self: *const LzAccumBuffer, dist: usize) !u8 { + const buf_len = self.buf.items.len; + if (dist > buf_len) { + return error.CorruptInput; + } + + return self.buf.items[buf_len - dist]; + } + + pub fn appendLiteral(self: *LzAccumBuffer, allocator: Allocator, lit: u8) !void { + try self.buf.append(allocator, lit); + } + + pub fn appendLz(self: *LzAccumBuffer, allocator: Allocator, length: usize, dist: usize) !void { + const buf_len = self.buf.items.len; + if (dist > buf_len) { + return error.CorruptInput; + } + + var offset = buf_len - dist; + var i: usize = 0; + while (i < length) : (i += 1) { + const x = self.buf.items[offset]; + try self.buf.append(allocator, x); + offset += 1; + } + } +}; + +pub const RangeDecoder = struct { + stream: std.io.FixedBufferStream([]const u8), + range: u32, + code: u32, + + pub fn init(buffer: []const u8) !RangeDecoder { + var dec = RangeDecoder{ + .stream = std.io.fixedBufferStream(buffer), + .range = 0xFFFF_FFFF, + .code = 0, + }; + const reader = dec.stream.reader(); + _ = try reader.readByte(); + dec.code = try reader.readIntBig(u32); + return dec; + } + + pub fn fromParts( + buffer: []const u8, + range: u32, + code: u32, + ) RangeDecoder { + return .{ + .stream = std.io.fixedBufferStream(buffer), + .range = range, + .code = code, + }; + } + + pub fn set(self: *RangeDecoder, range: u32, code: u32) void { + self.range = range; + self.code = code; + } + + pub fn readInto(self: *RangeDecoder, dest: []u8) !usize { + return self.stream.read(dest); + } + + pub inline fn isFinished(self: *const RangeDecoder) bool { + return self.code == 0 and self.isEof(); + } + + pub inline fn isEof(self: *const RangeDecoder) bool { + return self.stream.pos == self.stream.buffer.len; + } + + inline fn normalize(self: *RangeDecoder) !void { + if (self.range < 0x0100_0000) { + self.range <<= 8; + self.code = (self.code << 8) ^ @as(u32, try self.stream.reader().readByte()); + } + } + + inline fn getBit(self: *RangeDecoder) !bool { + self.range >>= 1; + + const bit = self.code >= self.range; + if (bit) + self.code -= self.range; + + try self.normalize(); + return bit; + } + + fn get(self: *RangeDecoder, count: usize) !u32 { + var result: u32 = 0; + var i: usize = 0; + while (i < count) : (i += 1) + result = (result << 1) ^ @boolToInt(try self.getBit()); + return result; + } + + pub inline fn decodeBit(self: *RangeDecoder, prob: *u16, update: bool) !bool { + const bound = (self.range >> 11) * prob.*; + + if (self.code < bound) { + if (update) + prob.* += (0x800 - prob.*) >> 5; + self.range = bound; + + try self.normalize(); + return false; + } else { + if (update) + prob.* -= prob.* >> 5; + self.code -= bound; + self.range -= bound; + + try self.normalize(); + return true; + } + } + + fn parseBitTree( + self: *RangeDecoder, + num_bits: u5, + probs: []u16, + update: bool, + ) !u32 { + var tmp: u32 = 1; + var i: u5 = 0; + while (i < num_bits) : (i += 1) { + const bit = try self.decodeBit(&probs[tmp], update); + tmp = (tmp << 1) ^ @boolToInt(bit); + } + return tmp - (@as(u32, 1) << num_bits); + } + + pub fn parseReverseBitTree( + self: *RangeDecoder, + num_bits: u5, + probs: []u16, + offset: usize, + update: bool, + ) !u32 { + var result: u32 = 0; + var tmp: usize = 1; + var i: u5 = 0; + while (i < num_bits) : (i += 1) { + const bit = @boolToInt(try self.decodeBit(&probs[offset + tmp], update)); + tmp = (tmp << 1) ^ bit; + result ^= @as(u32, bit) << i; + } + return result; + } +}; + +fn Vec2D(comptime T: type) type { + return struct { + data: []T, + cols: usize, + + const Self = @This(); + + pub fn init(allocator: Allocator, data: T, rows: usize, cols: usize) !Self { + const len = try std.math.mul(usize, rows, cols); + var vec2d = Self{ + .data = try allocator.alloc(T, len), + .cols = cols, + }; + vec2d.fill(data); + return vec2d; + } + + pub fn deinit(self: *Self, allocator: Allocator) void { + allocator.free(self.data); + } + + pub fn fill(self: *Self, value: T) void { + std.mem.set(T, self.data, value); + } + + pub fn get(self: *Self, row: usize) ![]T { + const start_row = try std.math.mul(usize, row, self.cols); + return self.data[start_row .. start_row + self.cols]; + } + }; +} + +const BitTree = struct { + num_bits: u5, + probs: ArrayListUnmanaged(u16), + + pub fn init(allocator: Allocator, num_bits: u5) !BitTree { + var probs_len = @as(usize, 1) << num_bits; + var probs = try ArrayListUnmanaged(u16).initCapacity(allocator, probs_len); + while (probs_len > 0) : (probs_len -= 1) + probs.appendAssumeCapacity(0x400); + return .{ .num_bits = num_bits, .probs = probs }; + } + + pub fn deinit(self: *BitTree, allocator: Allocator) void { + self.probs.deinit(allocator); + } + + pub fn parse( + self: *BitTree, + rangecoder: *RangeDecoder, + update: bool, + ) !u32 { + return rangecoder.parseBitTree(self.num_bits, self.probs.items, update); + } + + pub fn parseReverse( + self: *BitTree, + rangecoder: *RangeDecoder, + update: bool, + ) !u32 { + return rangecoder.parseReverseBitTree(self.num_bits, self.probs.items, 0, update); + } + + pub fn reset(self: *BitTree) void { + std.mem.set(u16, self.probs.items, 0x400); + } +}; + +const LenDecoder = struct { + choice: u16, + choice2: u16, + low_coder: [16]BitTree, + mid_coder: [16]BitTree, + high_coder: BitTree, + + pub fn init(allocator: Allocator) !LenDecoder { + return .{ + .choice = 0x400, + .choice2 = 0x400, + .low_coder = .{ + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + }, + .mid_coder = .{ + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + try BitTree.init(allocator, 3), + }, + .high_coder = try BitTree.init(allocator, 8), + }; + } + + pub fn deinit(self: *LenDecoder, allocator: Allocator) void { + for (self.low_coder) |*t| t.deinit(allocator); + for (self.mid_coder) |*t| t.deinit(allocator); + self.high_coder.deinit(allocator); + } + + pub fn decode( + self: *LenDecoder, + rangecoder: *RangeDecoder, + pos_state: usize, + update: bool, + ) !usize { + if (!try rangecoder.decodeBit(&self.choice, update)) { + return @as(usize, try self.low_coder[pos_state].parse(rangecoder, update)); + } else if (!try rangecoder.decodeBit(&self.choice2, update)) { + return @as(usize, try self.mid_coder[pos_state].parse(rangecoder, update)) + 8; + } else { + return @as(usize, try self.high_coder.parse(rangecoder, update)) + 16; + } + } + + pub fn reset(self: *LenDecoder) void { + self.choice = 0x400; + self.choice2 = 0x400; + for (self.low_coder) |*t| t.reset(); + for (self.mid_coder) |*t| t.reset(); + self.high_coder.reset(); + } +}; diff --git a/lib/std/compress/xz/multibyte.zig b/lib/std/compress/xz/multibyte.zig new file mode 100644 index 000000000000..1226ffcfb241 --- /dev/null +++ b/lib/std/compress/xz/multibyte.zig @@ -0,0 +1,23 @@ +const Multibyte = packed struct(u8) { + value: u7, + more: bool, +}; + +pub fn readInt(reader: anytype) !u64 { + const max_size = 9; + + var chunk = try reader.readStruct(Multibyte); + var num: u64 = chunk.value; + var i: u6 = 0; + + while (chunk.more) { + chunk = try reader.readStruct(Multibyte); + i += 1; + if (i >= max_size or @bitCast(u8, chunk) == 0x00) + return error.CorruptInput; + + num |= @as(u64, chunk.value) << (i * 7); + } + + return num; +} diff --git a/lib/std/compress/xz/stream.zig b/lib/std/compress/xz/stream.zig new file mode 100644 index 000000000000..33916e20df9f --- /dev/null +++ b/lib/std/compress/xz/stream.zig @@ -0,0 +1,136 @@ +const std = @import("std"); +const block = @import("block.zig"); +const check = @import("check.zig"); +const multibyte = @import("multibyte.zig"); +const Allocator = std.mem.Allocator; +const Crc32 = std.hash.Crc32; + +test { + _ = @import("stream_test.zig"); +} + +const Flags = packed struct(u16) { + reserved1: u8, + check_kind: check.Kind, + reserved2: u4, +}; + +pub fn stream(allocator: Allocator, reader: anytype) !Stream(@TypeOf(reader)) { + return Stream(@TypeOf(reader)).init(allocator, reader); +} + +pub fn Stream(comptime ReaderType: type) type { + return struct { + const Self = @This(); + + pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error; + pub const Reader = std.io.Reader(*Self, Error, read); + + allocator: Allocator, + block_decoder: block.Decoder(ReaderType), + in_reader: ReaderType, + + fn init(allocator: Allocator, source: ReaderType) !Self { + const Header = extern struct { + magic: [6]u8, + flags: Flags, + crc32: u32, + }; + + const header = try source.readStruct(Header); + + if (!std.mem.eql(u8, &header.magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 })) + return error.BadHeader; + + if (header.flags.reserved1 != 0 or header.flags.reserved2 != 0) + return error.BadHeader; + + const hash = Crc32.hash(std.mem.asBytes(&header.flags)); + if (hash != header.crc32) + return error.WrongChecksum; + + return Self{ + .allocator = allocator, + .block_decoder = try block.decoder(allocator, source, header.flags.check_kind), + .in_reader = source, + }; + } + + pub fn deinit(self: *Self) void { + self.block_decoder.deinit(); + } + + pub fn reader(self: *Self) Reader { + return .{ .context = self }; + } + + pub fn read(self: *Self, buffer: []u8) Error!usize { + if (buffer.len == 0) + return 0; + + const r = try self.block_decoder.read(buffer); + if (r != 0) + return r; + + const index_size = blk: { + var hasher = std.compress.hashedReader(self.in_reader, Crc32.init()); + hasher.hasher.update(&[1]u8{0x00}); + + var counter = std.io.countingReader(hasher.reader()); + counter.bytes_read += 1; + + const counting_reader = counter.reader(); + + const record_count = try multibyte.readInt(counting_reader); + if (record_count != self.block_decoder.block_count) + return error.CorruptInput; + + var i: usize = 0; + while (i < record_count) : (i += 1) { + // TODO: validate records + _ = try multibyte.readInt(counting_reader); + _ = try multibyte.readInt(counting_reader); + } + + while (counter.bytes_read % 4 != 0) { + if (try counting_reader.readByte() != 0) + return error.CorruptInput; + } + + const hash_a = hasher.hasher.final(); + const hash_b = try counting_reader.readIntLittle(u32); + if (hash_a != hash_b) + return error.WrongChecksum; + + break :blk counter.bytes_read; + }; + + const Footer = extern struct { + crc32: u32, + backward_size: u32, + flags: Flags, + magic: [2]u8, + }; + + const footer = try self.in_reader.readStruct(Footer); + const backward_size = (footer.backward_size + 1) * 4; + if (backward_size != index_size) + return error.CorruptInput; + + if (footer.flags.reserved1 != 0 or footer.flags.reserved2 != 0) + return error.CorruptInput; + + var hasher = Crc32.init(); + hasher.update(std.mem.asBytes(&footer.backward_size)); + hasher.update(std.mem.asBytes(&footer.flags)); + const hash = hasher.final(); + if (hash != footer.crc32) + return error.WrongChecksum; + + if (!std.mem.eql(u8, &footer.magic, &.{ 'Y', 'Z' })) + return error.CorruptInput; + + return 0; + } + }; +} diff --git a/lib/std/compress/xz/stream_test.zig b/lib/std/compress/xz/stream_test.zig new file mode 100644 index 000000000000..beaeedf535a9 --- /dev/null +++ b/lib/std/compress/xz/stream_test.zig @@ -0,0 +1,80 @@ +const std = @import("std"); +const testing = std.testing; +const stream = @import("stream.zig").stream; + +fn decompress(data: []const u8) ![]u8 { + var in_stream = std.io.fixedBufferStream(data); + + var xz_stream = try stream(testing.allocator, in_stream.reader()); + defer xz_stream.deinit(); + + return xz_stream.reader().readAllAlloc(testing.allocator, std.math.maxInt(usize)); +} + +fn testReader(data: []const u8, comptime expected: []const u8) !void { + const buf = try decompress(data); + defer testing.allocator.free(buf); + + try testing.expectEqualSlices(u8, expected, buf); +} + +test "compressed data" { + try testReader(@embedFile("testdata/good-0-empty.xz"), ""); + + inline for ([_][]const u8{ + "good-1-check-none.xz", + "good-1-check-crc32.xz", + "good-1-check-crc64.xz", + "good-1-check-sha256.xz", + "good-2-lzma2.xz", + "good-1-block_header-1.xz", + "good-1-block_header-2.xz", + "good-1-block_header-3.xz", + }) |filename| { + try testReader(@embedFile("testdata/" ++ filename), + \\Hello + \\World! + \\ + ); + } + + inline for ([_][]const u8{ + "good-1-lzma2-1.xz", + "good-1-lzma2-2.xz", + "good-1-lzma2-3.xz", + "good-1-lzma2-4.xz", + }) |filename| { + try testReader(@embedFile("testdata/" ++ filename), + \\Lorem ipsum dolor sit amet, consectetur adipisicing + \\elit, sed do eiusmod tempor incididunt ut + \\labore et dolore magna aliqua. Ut enim + \\ad minim veniam, quis nostrud exercitation ullamco + \\laboris nisi ut aliquip ex ea commodo + \\consequat. Duis aute irure dolor in reprehenderit + \\in voluptate velit esse cillum dolore eu + \\fugiat nulla pariatur. Excepteur sint occaecat cupidatat + \\non proident, sunt in culpa qui officia + \\deserunt mollit anim id est laborum. + \\ + ); + } + + try testReader(@embedFile("testdata/good-1-lzma2-5.xz"), ""); +} + +test "unsupported" { + inline for ([_][]const u8{ + "good-1-delta-lzma2.tiff.xz", + "good-1-x86-lzma2.xz", + "good-1-sparc-lzma2.xz", + "good-1-arm64-lzma2-1.xz", + "good-1-arm64-lzma2-2.xz", + "good-1-3delta-lzma2.xz", + "good-1-empty-bcj-lzma2.xz", + }) |filename| { + try testing.expectError( + error.Unsupported, + decompress(@embedFile("testdata/" ++ filename)), + ); + } +} diff --git a/lib/std/compress/xz/testdata/good-0-empty.xz b/lib/std/compress/xz/testdata/good-0-empty.xz new file mode 100644 index 0000000000000000000000000000000000000000..83b95e05bc814cb60f759a30e89d243960797b60 GIT binary patch literal 32 kcmexsUKJ6=z`&TPbkB?d2xRWNC{A#i$;$}hFh)iJ0Fqb-jsO4v literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-0cat-empty.xz b/lib/std/compress/xz/testdata/good-0cat-empty.xz new file mode 100644 index 0000000000000000000000000000000000000000..e6fc31461db459ae9075e704581317d910b4afee GIT binary patch literal 64 ncmexsUKJ6=z`&TPbkB?d2xRWNC{A#i$;$}hFh)lGB_Iy~7H$t4 literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-0catpad-empty.xz b/lib/std/compress/xz/testdata/good-0catpad-empty.xz new file mode 100644 index 0000000000000000000000000000000000000000..4f86b7d152d477b0da914aff1590000e89bf1183 GIT binary patch literal 68 qcmexsUKJ6=z`&TPbkB?d2xRWNC{A#i$;$}hFh&BU7#RK%Py+xorVkqc literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-0pad-empty.xz b/lib/std/compress/xz/testdata/good-0pad-empty.xz new file mode 100644 index 0000000000000000000000000000000000000000..c51e3a69c0de7e68ad78338a6c86cb2980eb1a2f GIT binary patch literal 36 mcmexsUKJ6=z`&TPbkB?d2xRWNC{A#i$;$}hFh&BU7#IMskOz(c literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-3delta-lzma2.xz b/lib/std/compress/xz/testdata/good-1-3delta-lzma2.xz new file mode 100644 index 0000000000000000000000000000000000000000..a0be1d007a206f8a7849400b1343c67784dc4809 GIT binary patch literal 528 zcmV+r0`L9(H+ooF000E$*0e?h0|Nm70|5a80RkZb2mk=Q6=lW&0mw`vw}j#eaZmcX zq4!Z^2&}I1SV9eE?x+FHU@nMQ*KXTACIAKT`>!--7r>uC`TPwI1El7!X|ZCSN0&px zFGr)J^#oeLn>_){TRQr)!c3h{SN*%MQ~w74^`}u@q?}{4v|{&1pipkF;|o&5r#HY< zNddXE|2tmwZ8yUJ^Q+|Uc32$NxO8Zp-%n0rcCj_ zRA%p_tx+K35AF;Cq+>h(sK-_Uuiddw!DshN@3dM+2De|MO{(J;WxJ>_2DMUPr}Xg` z75PD}w>lNL>YrxEQPE);?FhX8TXFGkdS{LWrd678{+yx&U10mCw(?F7M=dFx>EAL@10)4R_I;( z1fpA0{kN)Hu{Krk>y9EkKRQZh;&ACC{yaY4=000282Sn)?GfRX30qFzD0{{S$ S`@Oue#Ao{g000001X)_0Yy40E literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-arm64-lzma2-1.xz b/lib/std/compress/xz/testdata/good-1-arm64-lzma2-1.xz new file mode 100644 index 0000000000000000000000000000000000000000..78169f14e55bab6df64b171e02d275d1c878f095 GIT binary patch literal 512 zcmV+b0{{K}H+ooF0004LBHlIv0SW*i0SEx1*naWgA%6kGZU6viBZzTh!jDr5=CR+< zimn@>uz0H|Lla(qI_!Jbnq4vWOm!4B`dTUMI zf2*~D8PbQdj#Sbkr5HuKm#?*r6OD!0ixJf`b2ir4#$zA@8waxQC$A18F-0{2}WS>SKs z-OTxr%O_c&qi>dTg3nEHAs$Y@&(JBd&sJ_BkH~kG>uT>j+yHj4lw)v=lwW@zgZUrM zWKTuC_!C-q3>x3$3*5Q~Gwf5T`!VIr!-tiS^^=YQ?&DUb`O~9#r3fUv9xADscuVNJ zb4Shy3|axs;l-VW+|(IawLP!(q|as&{agMDdhYjOfeHo*UCR)~Z)i{L?U%o#3}QZ7oP3 zjDb;5VDF&-aoOXwPyJW64*Wn!(*OXVeBsmp0owzBLjV9n$BAP;Fb#_W000000a;o` ChV}3O literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-arm64-lzma2-2.xz b/lib/std/compress/xz/testdata/good-1-arm64-lzma2-2.xz new file mode 100644 index 0000000000000000000000000000000000000000..e0302fe99e994db8e02e58b5deebb0059c90bdf4 GIT binary patch literal 488 zcmV)t{1c60fMoJHkN$TUSJebxP_`X z7)fn4$7IFT8LOEEk7OxZIpYez9Oh~Cddvz2-Q^}z$*Hd|L?S)qmODUDNM`jN-lcN6MFEp*`GzEE0iV*SI z^QXwyBTGKmlwSorwDZJTrl@CHbCSk*q%(OA70=FcM6l&R7n)du1lSNRR40$f3k z48I+C<7lCVHqH*qCcQ{=EvTI;r$8yn=f}iXi>l*~##Di{8U#;nB6plx-xJUP0001> eeBsmp0m1`-LjV8}lu$=LFb#_W000000a;oZ9p=jb literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-block_header-1.xz b/lib/std/compress/xz/testdata/good-1-block_header-1.xz new file mode 100644 index 0000000000000000000000000000000000000000..fea5ad2f9980d5d9145700561a980e6e106b4e32 GIT binary patch literal 72 zcmexsUKJ6=z`&TPbkB_WfFQ3TBL@Qr)Zepw&B(yxk(!f}&lR3ul#`;!1rm2&yhxOR VQI)q)^2oLcPBVEKL4u5tQ2?t(5zGJp literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-block_header-2.xz b/lib/std/compress/xz/testdata/good-1-block_header-2.xz new file mode 100644 index 0000000000000000000000000000000000000000..6b5dcb347b24d1a5abf4150b1e3c9039b1b65a0d GIT binary patch literal 68 zcmexsUKJ6=z`&TPbkB^*K~RyAgMq=yr{f_b1CK{)PEI~ocz#h%iXs;S5I8ShB+9_3 U$Xj}6#ia>OGkF<7f{c+-0BF(?ZvX%Q literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-block_header-3.xz b/lib/std/compress/xz/testdata/good-1-block_header-3.xz new file mode 100644 index 0000000000000000000000000000000000000000..156531206342a87baa8029915aae0c940301c5ed GIT binary patch literal 68 zcmexsUKJ6=z`&TPbkB^bfme}{gMlGXurZR6fyW~?CnujPJijO>MUjgE2%Hx$5@ldi U2vp0!7#RfsADItp literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-check-crc64.xz b/lib/std/compress/xz/testdata/good-1-check-crc64.xz new file mode 100644 index 0000000000000000000000000000000000000000..5a9915d2f9fc4711b26ed2d399027583bf824340 GIT binary patch literal 72 zcmexsUKJ6=z`*kC+7>q^21P~=1_p*3{K~?N46GihIXU@UObl${`9(P?id+ou^*RLS b+D|*hz^K7%lb*#azwL7^BTzj9OJo!P#4r-r literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-check-none.xz b/lib/std/compress/xz/testdata/good-1-check-none.xz new file mode 100644 index 0000000000000000000000000000000000000000..1e85faf3d06eff1bc420925a1af096762b00db51 GIT binary patch literal 64 zcmexsUKJ6=z`*cd=%ynRgCZjbkbQ$+S(uT5)gv`0C!dRnfh|10C?`dci-Cbrf!E~i R=F@CN^Qstu>KGUzqX7L^4~{4qDc`tbuGQ?AKGtlV_0G|UM AumAu6 literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-delta-lzma2.tiff.xz b/lib/std/compress/xz/testdata/good-1-delta-lzma2.tiff.xz new file mode 100644 index 0000000000000000000000000000000000000000..1f033bc52b5d3e8950a34ed118b8837c964771b7 GIT binary patch literal 51316 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 literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-lzma2-1.xz b/lib/std/compress/xz/testdata/good-1-lzma2-1.xz new file mode 100644 index 0000000000000000000000000000000000000000..d8d6489c872156e67ba5efb74bb5215524017c90 GIT binary patch literal 424 zcmV;Z0ayP0H+ooF0004LBHlIv03iVg0002k4vH<%0_2e@^`p>hS)*d z1vSlsrF;^l1>2ClFBH>pa+auyeS}tVjcOv8tDZfOfGx*f3=&e)KL-eV9He*i6Sx<^ z?va5xvxo5i0JCaRLG3QE@?@8!nx12!t{HRDzhLwWI`Eq@>)$`vuccLzMg>?R36H2?2n(a>KD@LR+sC7#z!udM zry}St=(Nyx7sZR@^1Ex+@0x)$P95CrHs(ZfzbUmm1uvH<%0_2e@^`p>hS)*d z1vSlsrF;^l1>2ClFBH>pa+auyeS}tVjcOv8tDZfOfGx*f3=&e)KL-eV9He*i6Sx<^ z?va5xvxo5i0JCaRLG3QE@?@8!nx12!t{HRF!iN;a*~>rB7^clCOI@hPUjb(*Of2Ng)l#05 z0)7x(^#ie(FDC7VdscrFd03A`5ZT!emgzgcwYb_V;nsoi$p8TRM@ZWM0fPg{0{{T7 S*U}X}Fb#_W000000a;o&?Zkcn literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-lzma2-3.xz b/lib/std/compress/xz/testdata/good-1-lzma2-3.xz new file mode 100644 index 0000000000000000000000000000000000000000..c4c72be6561465781670b5a9b6067b1cfa016f7a GIT binary patch literal 408 zcmV;J0cZaGH+ooF0004LBHlIv03iVg0002k4fCOZ6IWCY;STP zb7^!SVQpn}EFfcVZgXX0bY*mPav))3X>e(CX=7<_XCMl|0h0kFT>vth32ao?E|qNg z_czrX;zk03b^A$njdN4B{cYRI+u1}5u`a$7wz{a|^X+w1+(97BzcyPSnOzSdp$4Ot zEw`7P@O%E^5{xZT@Y{BGeWZKSmFfEh=Jl74V(Hs3yR>jU7I*`9I*WSMxcEvZCH0P~ zBowA8C~Gfs;GuD}d!&F+K+5miw3Yq4!HkYzm)pyKA??5raT}jB^kOtXvnab% zHr;v-95y95SO)kNqs|z}{gxhSDFeg3C-diz+jF!lPdHd4zk?Jxesk{r&&s{?A!;aq z$c|ls$GjKNER(eemAVd+f@ZG&xB~$DM@ZWM0rUdN0{{SL!<9ZhFb#_W000000a;q- Ccebnm literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-lzma2-4.xz b/lib/std/compress/xz/testdata/good-1-lzma2-4.xz new file mode 100644 index 0000000000000000000000000000000000000000..e0d623a001ce5dbf48b55cff0168d01e1b012272 GIT binary patch literal 464 zcmV;>0WbdjH+ooF000E$*0e?f03iVg0002k4vH<%0_2e@^`p>hS)*d z1vSlsrF;^l1>2ClFBH>pa+auyeS}tVjcOv8tDZfOfGx*f3=&e)KL-eV9He*i6Sx<^ z?vZoKywB@2W~@k?Y1xwpuFg=eXbCS{h#$fTqAXZNu>WES>+aiQPjBG!AI}mun3hiV z^0NDGuS5dkBHq>JG?zJ}MK7+IFL`N?={Jc;{>*;rsVJ?(=G?j3suq3$048i%TbRc1DX>oOFa3E!PAZ1}7V{dJ3Z)9&E3cvv60KHuRF`LR6#Z>IJROhjM z#8;kdiLFX)7T7!mln@q~I3c6C#eo0(?$p&k-Hg{gk9O>csxqr()Hv_=`HH{y_@!3Y zZP!1A3!XpXM`$7l1vR)qa{1f7w~mItLrabgLoY#T-(d{I=r;&T-okbkxJXW(xl>Rd zGlS?Upl8TtI)5S(V*5wry!25I6^KdL$_pinUg+BWIKa+Ka~uEAo9Ilp6Zkio;@X@q zwd%N~jp|o|nw9kMTBTHiU|rqP*<4q zy+h3=>BhIS_~FMS3&JYf15Z3o zyondruX%`tHjHl7JTz)4phYKzLsG9%o=aYyG{OIl!M))O)*(<{6gw$YWhjtxPAz3o zHIOBCw_%E(@Dz19s;+ext0~(y_O8YPIy#dDY=xtaRqzJGH#U&Fo18HLYuujtZ!Y+u z#L8|bd~_cu=z4ZP!;&N+j?)Xm6ZI0#I~o~RmHM4O-WJk$5CIxO5!rjdjx2be%R{g3 zR->*1aJLh)h&) z96V|7(S12}MZ`&3hN6B&xsEv=jiVF|m4Ca`-r&;!q?mn1g4W@U~wYPXwrg_J?F!F1N)NX5XRZ z(1}y(Ml850zHt_lK_F;o3Sl`))VC-%27MTVFn-r;9;VghK){x)ipy@zV|LuklqG;P z!rNYWc5e{wW?h9(9)n_Va&L&6_w!)jpwsjs>F?1ZsDlZc6H}}b>SNBxs5(#a$ynNH y0{I&%5|0loy-2><}$swDcc#Ao{g000001X)@K86SNB literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-1-x86-lzma2.xz b/lib/std/compress/xz/testdata/good-1-x86-lzma2.xz new file mode 100644 index 0000000000000000000000000000000000000000..8053917bad7d4f43169312c43713afcd4cf51bec GIT binary patch literal 716 zcmV;-0yF*nH+ooF000E$*0e?f0R#Xc0SExnxpd3m1#1F|T>w9kMTBTO-rlH71K2fF zq>*!@eAV{Ae?1%0dPRjv;AqaK52~C5` z#1O&XZ{}_(no)!cXEany3*BG3=({7HK2SR}f@TKWA!=eaD}u!er+XteMni# zlx-j18Iprum`|dPwFOxHhPD{hja?DcjZ5=vHv@)q&`TSIA#l z_133ZPo`QuX|ch8IU8q&UhQ}{21O2q^AW;v0FtC;8ywtk#aO59jBm|Nm#*f%QeK>2 z8r}vY+8zdtc55>RYwV;n{m}Mu%moSC(cT86GWRjbVidoO^VjY-+JxHXiu z>=vy%8-AKCrG$+9vrRt+Q;>aAJ{Zwuu#uCOSA3q+uBmNO*w5&E^2M{9_y1Mz8<1Mz z1HvGOjE?aDdJ0D3a-}E0Z->q|yE{__=Sq6ph)$m3iGgE?@^U#2J8qL_N%2}!6la1&^clV*sX3g*TXb0 zZ&XK)oap%OJ|FS~>!a9Mh2nLH4iws-W?+1p^Kd7ktj3%Q;jf#raGq@^7d%;(AWpmj y-+M9950;Kv`Q?PC0002Y{;&Ts9Ye4H0j34)3IG5I(#}<}#Ao{g000001X)^L%US6F literal 0 HcmV?d00001 diff --git a/lib/std/compress/xz/testdata/good-2-lzma2.xz b/lib/std/compress/xz/testdata/good-2-lzma2.xz new file mode 100644 index 0000000000000000000000000000000000000000..bed5085c1f6c30d42970ddceef99d846986b527c GIT binary patch literal 92 zcmexsUKJ6=z`&TPbkB^5L6MPzfq~%$zp^kR1FJ`BPEI}-kS}IB%@9?NEj+&{Cq)q` gdH3R}U Date: Tue, 24 Jan 2023 15:04:56 -0700 Subject: [PATCH 2/5] std.compress.xz public API cleanup * add xz to std.compress * prefer importing std.zig by file name, to reduce reliance on the standard library being a special case. * extract some types from inside generic functions. These types are the same regardless of the generic parameters. * expose some more types in the std.compress.xz namespace. * rename xz.stream to xz.decompress * rename check.Kind to Check * use std.leb for LEB instead of a redundant implementation --- lib/std/compress.zig | 2 + lib/std/compress/xz.zig | 141 +++++++++++++++++- lib/std/compress/xz/block.zig | 26 ++-- lib/std/compress/xz/check.zig | 7 - lib/std/compress/xz/lzma.zig | 2 +- lib/std/compress/xz/multibyte.zig | 23 --- lib/std/compress/xz/stream.zig | 136 ----------------- .../compress/xz/{stream_test.zig => test.zig} | 6 +- 8 files changed, 157 insertions(+), 186 deletions(-) delete mode 100644 lib/std/compress/xz/check.zig delete mode 100644 lib/std/compress/xz/multibyte.zig delete mode 100644 lib/std/compress/xz/stream.zig rename lib/std/compress/xz/{stream_test.zig => test.zig} (94%) diff --git a/lib/std/compress.zig b/lib/std/compress.zig index 3c52002cfc86..334d7bfcb8f9 100644 --- a/lib/std/compress.zig +++ b/lib/std/compress.zig @@ -3,6 +3,7 @@ const std = @import("std.zig"); pub const deflate = @import("compress/deflate.zig"); pub const gzip = @import("compress/gzip.zig"); pub const zlib = @import("compress/zlib.zig"); +pub const xz = @import("compress/xz.zig"); pub fn HashedReader( comptime ReaderType: anytype, @@ -38,4 +39,5 @@ test { _ = deflate; _ = gzip; _ = zlib; + _ = xz; } diff --git a/lib/std/compress/xz.zig b/lib/std/compress/xz.zig index 3af2d91cfba9..2c56be9c77ba 100644 --- a/lib/std/compress/xz.zig +++ b/lib/std/compress/xz.zig @@ -1,5 +1,142 @@ -pub usingnamespace @import("xz/stream.zig"); +const std = @import("std"); +const block = @import("xz/block.zig"); +const Allocator = std.mem.Allocator; +const Crc32 = std.hash.Crc32; + +pub const Flags = packed struct(u16) { + reserved1: u8, + check_kind: Check, + reserved2: u4, +}; + +pub const Header = extern struct { + magic: [6]u8, + flags: Flags, + crc32: u32, +}; + +pub const Footer = extern struct { + crc32: u32, + backward_size: u32, + flags: Flags, + magic: [2]u8, +}; + +pub const Check = enum(u4) { + none = 0x00, + crc32 = 0x01, + crc64 = 0x04, + sha256 = 0x0A, + _, +}; + +pub fn decompress(allocator: Allocator, reader: anytype) !Decompress(@TypeOf(reader)) { + return Decompress(@TypeOf(reader)).init(allocator, reader); +} + +pub fn Decompress(comptime ReaderType: type) type { + return struct { + const Self = @This(); + + pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error; + pub const Reader = std.io.Reader(*Self, Error, read); + + allocator: Allocator, + block_decoder: block.Decoder(ReaderType), + in_reader: ReaderType, + + fn init(allocator: Allocator, source: ReaderType) !Self { + const header = try source.readStruct(Header); + + if (!std.mem.eql(u8, &header.magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 })) + return error.BadHeader; + + if (header.flags.reserved1 != 0 or header.flags.reserved2 != 0) + return error.BadHeader; + + const hash = Crc32.hash(std.mem.asBytes(&header.flags)); + if (hash != header.crc32) + return error.WrongChecksum; + + return Self{ + .allocator = allocator, + .block_decoder = try block.decoder(allocator, source, header.flags.check_kind), + .in_reader = source, + }; + } + + pub fn deinit(self: *Self) void { + self.block_decoder.deinit(); + } + + pub fn reader(self: *Self) Reader { + return .{ .context = self }; + } + + pub fn read(self: *Self, buffer: []u8) Error!usize { + if (buffer.len == 0) + return 0; + + const r = try self.block_decoder.read(buffer); + if (r != 0) + return r; + + const index_size = blk: { + var hasher = std.compress.hashedReader(self.in_reader, Crc32.init()); + hasher.hasher.update(&[1]u8{0x00}); + + var counter = std.io.countingReader(hasher.reader()); + counter.bytes_read += 1; + + const counting_reader = counter.reader(); + + const record_count = try std.leb.readULEB128(u64, counting_reader); + if (record_count != self.block_decoder.block_count) + return error.CorruptInput; + + var i: usize = 0; + while (i < record_count) : (i += 1) { + // TODO: validate records + _ = try std.leb.readULEB128(u64, counting_reader); + _ = try std.leb.readULEB128(u64, counting_reader); + } + + while (counter.bytes_read % 4 != 0) { + if (try counting_reader.readByte() != 0) + return error.CorruptInput; + } + + const hash_a = hasher.hasher.final(); + const hash_b = try counting_reader.readIntLittle(u32); + if (hash_a != hash_b) + return error.WrongChecksum; + + break :blk counter.bytes_read; + }; + + const footer = try self.in_reader.readStruct(Footer); + const backward_size = (footer.backward_size + 1) * 4; + if (backward_size != index_size) + return error.CorruptInput; + + if (footer.flags.reserved1 != 0 or footer.flags.reserved2 != 0) + return error.CorruptInput; + + var hasher = Crc32.init(); + hasher.update(std.mem.asBytes(&footer.backward_size)); + hasher.update(std.mem.asBytes(&footer.flags)); + const hash = hasher.final(); + if (hash != footer.crc32) + return error.WrongChecksum; + + if (!std.mem.eql(u8, &footer.magic, &.{ 'Y', 'Z' })) + return error.CorruptInput; + + return 0; + } + }; +} test { - _ = @import("xz/stream.zig"); + _ = @import("xz/test.zig"); } diff --git a/lib/std/compress/xz/block.zig b/lib/std/compress/xz/block.zig index 27b2fc0b5f07..1ceaea49849d 100644 --- a/lib/std/compress/xz/block.zig +++ b/lib/std/compress/xz/block.zig @@ -1,11 +1,10 @@ -const std = @import("std"); -const check = @import("check.zig"); +const std = @import("../../std.zig"); const lzma = @import("lzma.zig"); -const multibyte = @import("multibyte.zig"); const Allocator = std.mem.Allocator; const Crc32 = std.hash.Crc32; const Crc64 = std.hash.crc.Crc64Xz; const Sha256 = std.crypto.hash.sha2.Sha256; +const xz = std.compress.xz; const DecodeError = error{ CorruptInput, @@ -16,8 +15,8 @@ const DecodeError = error{ Overflow, }; -pub fn decoder(allocator: Allocator, reader: anytype, check_kind: check.Kind) !Decoder(@TypeOf(reader)) { - return Decoder(@TypeOf(reader)).init(allocator, reader, check_kind); +pub fn decoder(allocator: Allocator, reader: anytype, check: xz.Check) !Decoder(@TypeOf(reader)) { + return Decoder(@TypeOf(reader)).init(allocator, reader, check); } pub fn Decoder(comptime ReaderType: type) type { @@ -31,17 +30,17 @@ pub fn Decoder(comptime ReaderType: type) type { allocator: Allocator, inner_reader: ReaderType, - check_kind: check.Kind, + check: xz.Check, err: ?Error, accum: lzma.LzAccumBuffer, lzma_state: lzma.DecoderState, block_count: usize, - fn init(allocator: Allocator, in_reader: ReaderType, check_kind: check.Kind) !Self { + fn init(allocator: Allocator, in_reader: ReaderType, check: xz.Check) !Self { return Self{ .allocator = allocator, .inner_reader = in_reader, - .check_kind = check_kind, + .check = check, .err = null, .accum = .{}, .lzma_state = try lzma.DecoderState.init(allocator), @@ -116,10 +115,10 @@ pub fn Decoder(comptime ReaderType: type) type { return error.Unsupported; if (flags.has_packed_size) - packed_size = try multibyte.readInt(header_reader); + packed_size = try std.leb.readULEB128(u64, header_reader); if (flags.has_unpacked_size) - unpacked_size = try multibyte.readInt(header_reader); + unpacked_size = try std.leb.readULEB128(u64, header_reader); const FilterId = enum(u64) { lzma2 = 0x21, @@ -128,7 +127,7 @@ pub fn Decoder(comptime ReaderType: type) type { const filter_id = @intToEnum( FilterId, - try multibyte.readInt(header_reader), + try std.leb.readULEB128(u64, header_reader), ); if (@enumToInt(filter_id) >= 0x4000_0000_0000_0000) @@ -137,7 +136,7 @@ pub fn Decoder(comptime ReaderType: type) type { if (filter_id != .lzma2) return error.Unsupported; - const properties_size = try multibyte.readInt(header_reader); + const properties_size = try std.leb.readULEB128(u64, header_reader); if (properties_size != 1) return error.CorruptInput; @@ -177,8 +176,7 @@ pub fn Decoder(comptime ReaderType: type) type { return error.CorruptInput; } - // Check - switch (self.check_kind) { + switch (self.check) { .none => {}, .crc32 => { const hash_a = Crc32.hash(unpacked_bytes); diff --git a/lib/std/compress/xz/check.zig b/lib/std/compress/xz/check.zig deleted file mode 100644 index 20151ad4cf3a..000000000000 --- a/lib/std/compress/xz/check.zig +++ /dev/null @@ -1,7 +0,0 @@ -pub const Kind = enum(u4) { - none = 0x00, - crc32 = 0x01, - crc64 = 0x04, - sha256 = 0x0A, - _, -}; diff --git a/lib/std/compress/xz/lzma.zig b/lib/std/compress/xz/lzma.zig index ead707e0be6b..9fe941e2b1e8 100644 --- a/lib/std/compress/xz/lzma.zig +++ b/lib/std/compress/xz/lzma.zig @@ -1,6 +1,6 @@ // Ported from https://github.com/gendx/lzma-rs -const std = @import("std"); +const std = @import("../../std.zig"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; const ArrayListUnmanaged = std.ArrayListUnmanaged; diff --git a/lib/std/compress/xz/multibyte.zig b/lib/std/compress/xz/multibyte.zig deleted file mode 100644 index 1226ffcfb241..000000000000 --- a/lib/std/compress/xz/multibyte.zig +++ /dev/null @@ -1,23 +0,0 @@ -const Multibyte = packed struct(u8) { - value: u7, - more: bool, -}; - -pub fn readInt(reader: anytype) !u64 { - const max_size = 9; - - var chunk = try reader.readStruct(Multibyte); - var num: u64 = chunk.value; - var i: u6 = 0; - - while (chunk.more) { - chunk = try reader.readStruct(Multibyte); - i += 1; - if (i >= max_size or @bitCast(u8, chunk) == 0x00) - return error.CorruptInput; - - num |= @as(u64, chunk.value) << (i * 7); - } - - return num; -} diff --git a/lib/std/compress/xz/stream.zig b/lib/std/compress/xz/stream.zig deleted file mode 100644 index 33916e20df9f..000000000000 --- a/lib/std/compress/xz/stream.zig +++ /dev/null @@ -1,136 +0,0 @@ -const std = @import("std"); -const block = @import("block.zig"); -const check = @import("check.zig"); -const multibyte = @import("multibyte.zig"); -const Allocator = std.mem.Allocator; -const Crc32 = std.hash.Crc32; - -test { - _ = @import("stream_test.zig"); -} - -const Flags = packed struct(u16) { - reserved1: u8, - check_kind: check.Kind, - reserved2: u4, -}; - -pub fn stream(allocator: Allocator, reader: anytype) !Stream(@TypeOf(reader)) { - return Stream(@TypeOf(reader)).init(allocator, reader); -} - -pub fn Stream(comptime ReaderType: type) type { - return struct { - const Self = @This(); - - pub const Error = ReaderType.Error || block.Decoder(ReaderType).Error; - pub const Reader = std.io.Reader(*Self, Error, read); - - allocator: Allocator, - block_decoder: block.Decoder(ReaderType), - in_reader: ReaderType, - - fn init(allocator: Allocator, source: ReaderType) !Self { - const Header = extern struct { - magic: [6]u8, - flags: Flags, - crc32: u32, - }; - - const header = try source.readStruct(Header); - - if (!std.mem.eql(u8, &header.magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 })) - return error.BadHeader; - - if (header.flags.reserved1 != 0 or header.flags.reserved2 != 0) - return error.BadHeader; - - const hash = Crc32.hash(std.mem.asBytes(&header.flags)); - if (hash != header.crc32) - return error.WrongChecksum; - - return Self{ - .allocator = allocator, - .block_decoder = try block.decoder(allocator, source, header.flags.check_kind), - .in_reader = source, - }; - } - - pub fn deinit(self: *Self) void { - self.block_decoder.deinit(); - } - - pub fn reader(self: *Self) Reader { - return .{ .context = self }; - } - - pub fn read(self: *Self, buffer: []u8) Error!usize { - if (buffer.len == 0) - return 0; - - const r = try self.block_decoder.read(buffer); - if (r != 0) - return r; - - const index_size = blk: { - var hasher = std.compress.hashedReader(self.in_reader, Crc32.init()); - hasher.hasher.update(&[1]u8{0x00}); - - var counter = std.io.countingReader(hasher.reader()); - counter.bytes_read += 1; - - const counting_reader = counter.reader(); - - const record_count = try multibyte.readInt(counting_reader); - if (record_count != self.block_decoder.block_count) - return error.CorruptInput; - - var i: usize = 0; - while (i < record_count) : (i += 1) { - // TODO: validate records - _ = try multibyte.readInt(counting_reader); - _ = try multibyte.readInt(counting_reader); - } - - while (counter.bytes_read % 4 != 0) { - if (try counting_reader.readByte() != 0) - return error.CorruptInput; - } - - const hash_a = hasher.hasher.final(); - const hash_b = try counting_reader.readIntLittle(u32); - if (hash_a != hash_b) - return error.WrongChecksum; - - break :blk counter.bytes_read; - }; - - const Footer = extern struct { - crc32: u32, - backward_size: u32, - flags: Flags, - magic: [2]u8, - }; - - const footer = try self.in_reader.readStruct(Footer); - const backward_size = (footer.backward_size + 1) * 4; - if (backward_size != index_size) - return error.CorruptInput; - - if (footer.flags.reserved1 != 0 or footer.flags.reserved2 != 0) - return error.CorruptInput; - - var hasher = Crc32.init(); - hasher.update(std.mem.asBytes(&footer.backward_size)); - hasher.update(std.mem.asBytes(&footer.flags)); - const hash = hasher.final(); - if (hash != footer.crc32) - return error.WrongChecksum; - - if (!std.mem.eql(u8, &footer.magic, &.{ 'Y', 'Z' })) - return error.CorruptInput; - - return 0; - } - }; -} diff --git a/lib/std/compress/xz/stream_test.zig b/lib/std/compress/xz/test.zig similarity index 94% rename from lib/std/compress/xz/stream_test.zig rename to lib/std/compress/xz/test.zig index beaeedf535a9..848f518c7883 100644 --- a/lib/std/compress/xz/stream_test.zig +++ b/lib/std/compress/xz/test.zig @@ -1,11 +1,11 @@ -const std = @import("std"); +const std = @import("../../std.zig"); const testing = std.testing; -const stream = @import("stream.zig").stream; +const xz = std.compress.xz; fn decompress(data: []const u8) ![]u8 { var in_stream = std.io.fixedBufferStream(data); - var xz_stream = try stream(testing.allocator, in_stream.reader()); + var xz_stream = try xz.decompress(testing.allocator, in_stream.reader()); defer xz_stream.deinit(); return xz_stream.reader().readAllAlloc(testing.allocator, std.math.maxInt(usize)); From d94613c1d06fbeaf0cee88a04842cda64a10c8f9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jan 2023 15:09:02 -0700 Subject: [PATCH 3/5] 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. --- lib/std/compress/gzip.zig | 13 +++++-------- src/Package.zig | 29 +++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/std/compress/gzip.zig b/lib/std/compress/gzip.zig index 4d1f8e28f498..7e9fea6814fa 100644 --- a/lib/std/compress/gzip.zig +++ b/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 diff --git a/src/Package.zig b/src/Package.zig index f8823cc74e1e..ebe84b8444d3 100644 --- a/src/Package.zig +++ b/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, From 92ea7bce5dc31f0abbf9dcfcb9b0b749e604bba9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 24 Jan 2023 15:23:34 -0700 Subject: [PATCH 4/5] std.compress.xz: fix compile error on 32-bit systems --- lib/std/compress/xz/block.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/compress/xz/block.zig b/lib/std/compress/xz/block.zig index 1ceaea49849d..5036eb567566 100644 --- a/lib/std/compress/xz/block.zig +++ b/lib/std/compress/xz/block.zig @@ -268,7 +268,7 @@ pub fn Decoder(comptime ReaderType: type) type { }; const packed_size = blk: { - var tmp: u64 = try packed_reader.readIntBig(u16); + const tmp: u17 = try packed_reader.readIntBig(u16); break :blk tmp + 1; }; From d0dedefde97aef61db31de6f0cd66846082720d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?fn=20=E2=8C=83=20=E2=8C=A5?= <70830482+FnControlOption@users.noreply.github.com> Date: Wed, 25 Jan 2023 08:43:13 -0800 Subject: [PATCH 5/5] std.compress.xz: fix for big-endian targets --- lib/std/compress/xz.zig | 81 ++++++++++++++++++----------------- lib/std/compress/xz/block.zig | 2 +- 2 files changed, 43 insertions(+), 40 deletions(-) diff --git a/lib/std/compress/xz.zig b/lib/std/compress/xz.zig index 2c56be9c77ba..1b2a543ad1b8 100644 --- a/lib/std/compress/xz.zig +++ b/lib/std/compress/xz.zig @@ -3,25 +3,6 @@ const block = @import("xz/block.zig"); const Allocator = std.mem.Allocator; const Crc32 = std.hash.Crc32; -pub const Flags = packed struct(u16) { - reserved1: u8, - check_kind: Check, - reserved2: u4, -}; - -pub const Header = extern struct { - magic: [6]u8, - flags: Flags, - crc32: u32, -}; - -pub const Footer = extern struct { - crc32: u32, - backward_size: u32, - flags: Flags, - magic: [2]u8, -}; - pub const Check = enum(u4) { none = 0x00, crc32 = 0x01, @@ -30,6 +11,20 @@ pub const Check = enum(u4) { _, }; +fn readStreamFlags(reader: anytype, check: *Check) !void { + var bit_reader = std.io.bitReader(.Little, reader); + + const reserved1 = try bit_reader.readBitsNoEof(u8, 8); + if (reserved1 != 0) + return error.CorruptInput; + + check.* = @intToEnum(Check, try bit_reader.readBitsNoEof(u4, 4)); + + const reserved2 = try bit_reader.readBitsNoEof(u4, 4); + if (reserved2 != 0) + return error.CorruptInput; +} + pub fn decompress(allocator: Allocator, reader: anytype) !Decompress(@TypeOf(reader)) { return Decompress(@TypeOf(reader)).init(allocator, reader); } @@ -46,21 +41,24 @@ pub fn Decompress(comptime ReaderType: type) type { in_reader: ReaderType, fn init(allocator: Allocator, source: ReaderType) !Self { - const header = try source.readStruct(Header); - - if (!std.mem.eql(u8, &header.magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 })) + const magic = try source.readBytesNoEof(6); + if (!std.mem.eql(u8, &magic, &.{ 0xFD, '7', 'z', 'X', 'Z', 0x00 })) return error.BadHeader; - if (header.flags.reserved1 != 0 or header.flags.reserved2 != 0) - return error.BadHeader; + var check: Check = undefined; + const hash_a = blk: { + var hasher = std.compress.hashedReader(source, Crc32.init()); + try readStreamFlags(hasher.reader(), &check); + break :blk hasher.hasher.final(); + }; - const hash = Crc32.hash(std.mem.asBytes(&header.flags)); - if (hash != header.crc32) + const hash_b = try source.readIntLittle(u32); + if (hash_a != hash_b) return error.WrongChecksum; return Self{ .allocator = allocator, - .block_decoder = try block.decoder(allocator, source, header.flags.check_kind), + .block_decoder = try block.decoder(allocator, source, check), .in_reader = source, }; } @@ -114,22 +112,27 @@ pub fn Decompress(comptime ReaderType: type) type { break :blk counter.bytes_read; }; - const footer = try self.in_reader.readStruct(Footer); - const backward_size = (footer.backward_size + 1) * 4; - if (backward_size != index_size) - return error.CorruptInput; + const hash_a = try self.in_reader.readIntLittle(u32); - if (footer.flags.reserved1 != 0 or footer.flags.reserved2 != 0) - return error.CorruptInput; + const hash_b = blk: { + var hasher = std.compress.hashedReader(self.in_reader, Crc32.init()); + const hashed_reader = hasher.reader(); + + const backward_size = (try hashed_reader.readIntLittle(u32) + 1) * 4; + if (backward_size != index_size) + return error.CorruptInput; + + var check: Check = undefined; + try readStreamFlags(hashed_reader, &check); + + break :blk hasher.hasher.final(); + }; - var hasher = Crc32.init(); - hasher.update(std.mem.asBytes(&footer.backward_size)); - hasher.update(std.mem.asBytes(&footer.flags)); - const hash = hasher.final(); - if (hash != footer.crc32) + if (hash_a != hash_b) return error.WrongChecksum; - if (!std.mem.eql(u8, &footer.magic, &.{ 'Y', 'Z' })) + const magic = try self.in_reader.readBytesNoEof(2); + if (!std.mem.eql(u8, &magic, &.{ 'Y', 'Z' })) return error.CorruptInput; return 0; diff --git a/lib/std/compress/xz/block.zig b/lib/std/compress/xz/block.zig index 5036eb567566..1b909beaf4d7 100644 --- a/lib/std/compress/xz/block.zig +++ b/lib/std/compress/xz/block.zig @@ -109,7 +109,7 @@ pub fn Decoder(comptime ReaderType: type) type { has_unpacked_size: bool, }; - const flags = try header_reader.readStruct(Flags); + const flags = @bitCast(Flags, try header_reader.readByte()); const filter_count = @as(u3, flags.last_filter_index) + 1; if (filter_count > 1) return error.Unsupported;