From 2328f40b7a0b301ad176e2657694457667be862e Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Mon, 7 Sep 2020 19:07:27 +0200 Subject: [PATCH 1/2] std: Add DEFLATE and zlib decompressors --- lib/std/compress.zig | 13 + lib/std/compress/deflate.zig | 521 ++++++++++++++ lib/std/compress/rfc1951.txt | 955 +++++++++++++++++++++++++ lib/std/compress/rfc1951.txt.fixed.z.9 | Bin 0 -> 12836 bytes lib/std/compress/rfc1951.txt.z.0 | Bin 0 -> 36960 bytes lib/std/compress/rfc1951.txt.z.9 | Bin 0 -> 11111 bytes lib/std/compress/zlib.zig | 178 +++++ lib/std/std.zig | 1 + 8 files changed, 1668 insertions(+) create mode 100644 lib/std/compress.zig create mode 100644 lib/std/compress/deflate.zig create mode 100644 lib/std/compress/rfc1951.txt create mode 100644 lib/std/compress/rfc1951.txt.fixed.z.9 create mode 100644 lib/std/compress/rfc1951.txt.z.0 create mode 100644 lib/std/compress/rfc1951.txt.z.9 create mode 100644 lib/std/compress/zlib.zig diff --git a/lib/std/compress.zig b/lib/std/compress.zig new file mode 100644 index 0000000000..5518f807df --- /dev/null +++ b/lib/std/compress.zig @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2020 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +const std = @import("std.zig"); + +pub const deflate = @import("compress/deflate.zig"); +pub const zlib = @import("compress/zlib.zig"); + +test "" { + _ = zlib; +} diff --git a/lib/std/compress/deflate.zig b/lib/std/compress/deflate.zig new file mode 100644 index 0000000000..bad23349e8 --- /dev/null +++ b/lib/std/compress/deflate.zig @@ -0,0 +1,521 @@ +// SPDX-License-Identifier: MIT +// Copyright (c) 2015-2020 Zig Contributors +// This file is part of [zig](https://ziglang.org/), which is MIT licensed. +// The MIT license requires this copyright notice to be included in all copies +// and substantial portions of the software. +// +// Decompressor for DEFLATE data streams (RFC1951) +// +// Heavily inspired by the simple decompressor puff.c by Mark Adler + +const std = @import("std"); +const io = std.io; +const math = std.math; +const mem = std.mem; + +const assert = std.debug.assert; + +const MAXBITS = 15; +const MAXLCODES = 286; +const MAXDCODES = 30; +const MAXCODES = MAXLCODES + MAXDCODES; +const FIXLCODES = 288; + +const Huffman = struct { + count: [MAXBITS + 1]u16, + symbol: [MAXCODES]u16, + + fn construct(self: *Huffman, length: []const u16) !void { + for (self.count) |*val| { + val.* = 0; + } + + for (length) |val| { + self.count[val] += 1; + } + + if (self.count[0] == length.len) + return; + + var left: isize = 1; + for (self.count[1..]) |val| { + left *= 2; + left -= @as(isize, @bitCast(i16, val)); + if (left < 0) + return error.InvalidTree; + } + + var offs: [MAXBITS + 1]u16 = undefined; + { + var len: usize = 1; + offs[1] = 0; + while (len < MAXBITS) : (len += 1) { + offs[len + 1] = offs[len] + self.count[len]; + } + } + + for (length) |val, symbol| { + if (val != 0) { + self.symbol[offs[val]] = @truncate(u16, symbol); + offs[val] += 1; + } + } + } +}; + +pub fn InflateStream(comptime ReaderType: type) type { + return struct { + const Self = @This(); + + pub const Error = ReaderType.Error || error{ + EndOfStream, + BadCounts, + InvalidBlockType, + InvalidDistance, + InvalidFixedCode, + InvalidLength, + InvalidStoredSize, + InvalidSymbol, + InvalidTree, + MissingEOBCode, + NoLastLength, + OutOfCodes, + }; + pub const Reader = io.Reader(*Self, Error, read); + + bit_reader: io.BitReader(.Little, ReaderType), + + // True if the decoder met the end of the compressed stream, no further + // data can be decompressed + seen_eos: bool, + + state: union(enum) { + // Parse a compressed block header and set up the internal state for + // decompressing its contents. + DecodeBlockHeader: void, + // Decode all the symbols in a compressed block. + DecodeBlockData: void, + // Copy N bytes of uncompressed data from the underlying stream into + // the window. + Copy: usize, + // Copy 1 byte into the window. + CopyLit: u8, + // Copy L bytes from the window itself, starting from D bytes + // behind. + CopyFrom: struct { distance: u16, length: u16 }, + }, + + // Sliding window for the LZ77 algorithm + window: struct { + const WSelf = @This(); + + // invariant: buffer length is always a power of 2 + buf: []u8, + // invariant: ri <= wi + wi: usize = 0, // Write index + ri: usize = 0, // Read index + el: usize = 0, // Number of readable elements + + fn readable(self: *WSelf) usize { + return self.el; + } + + fn writable(self: *WSelf) usize { + return self.buf.len - self.el; + } + + // Insert a single byte into the window. + // Returns 1 if there's enough space for the new byte and 0 + // otherwise. + fn append(self: *WSelf, value: u8) usize { + if (self.writable() < 1) return 0; + self.appendUnsafe(value); + return 1; + } + + // Insert a single byte into the window. + // Assumes there's enough space. + fn appendUnsafe(self: *WSelf, value: u8) void { + self.buf[self.wi] = value; + self.wi = (self.wi + 1) & (self.buf.len - 1); + self.el += 1; + } + + // Fill dest[] with data from the window, starting from the read + // position. This updates the read pointer. + // Returns the number of read bytes or 0 if there's nothing to read + // yet. + fn read(self: *WSelf, dest: []u8) usize { + const N = math.min(dest.len, self.readable()); + + if (N == 0) return 0; + + if (self.ri + N < self.buf.len) { + // The data doesn't wrap around + mem.copy(u8, dest, self.buf[self.ri .. self.ri + N]); + } else { + // The data wraps around the buffer, split the copy + std.mem.copy(u8, dest, self.buf[self.ri..]); + // How much data we've copied from `ri` to the end + const r = self.buf.len - self.ri; + std.mem.copy(u8, dest[r..], self.buf[0 .. N - r]); + } + + self.ri = (self.ri + N) & (self.buf.len - 1); + self.el -= N; + + return N; + } + + // Copy `length` bytes starting from `distance` bytes behind the + // write pointer. + // Be careful as the length may be greater than the distance, that's + // how the compressor encodes run-length encoded sequences. + fn copyFrom(self: *WSelf, distance: usize, length: usize) usize { + const N = math.min(length, self.writable()); + + if (N == 0) return 0; + + // TODO: Profile and, if needed, replace with smarter juggling + // of the window memory for the non-overlapping case. + var i: usize = 0; + while (i < N) : (i += 1) { + const index = (self.wi -% distance) % self.buf.len; + self.appendUnsafe(self.buf[index]); + } + + return N; + } + }, + + // Compressor-local Huffman tables used to decompress blocks with + // dynamic codes. + huffman_tables: [2]Huffman = undefined, + + // Huffman tables used for decoding length/distance pairs. + hdist: *Huffman, + hlen: *Huffman, + + fn stored(self: *Self) !void { + // Discard the remaining bits, the lenght field is always + // byte-aligned (and so is the data) + self.bit_reader.alignToByte(); + + const length = (try self.bit_reader.readBitsNoEof(u16, 16)); + const length_cpl = (try self.bit_reader.readBitsNoEof(u16, 16)); + + if (length != ~length_cpl) + return error.InvalidStoredSize; + + self.state = .{ .Copy = length }; + } + + fn fixed(self: *Self) !void { + comptime var lencode: Huffman = undefined; + comptime var distcode: Huffman = undefined; + + // The Huffman codes are specified in the RFC1951, section 3.2.6 + comptime { + @setEvalBranchQuota(100000); + + const len_lengths = // + [_]u16{8} ** 144 ++ + [_]u16{9} ** 112 ++ + [_]u16{7} ** 24 ++ + [_]u16{8} ** 8; + assert(len_lengths.len == FIXLCODES); + try lencode.construct(len_lengths[0..]); + + const dist_lengths = [_]u16{5} ** MAXDCODES; + try distcode.construct(dist_lengths[0..]); + } + + self.hlen = &lencode; + self.hdist = &distcode; + self.state = .DecodeBlockData; + } + + fn dynamic(self: *Self) !void { + // Number of length codes + const nlen = (try self.bit_reader.readBitsNoEof(usize, 5)) + 257; + // Number of distance codes + const ndist = (try self.bit_reader.readBitsNoEof(usize, 5)) + 1; + // Number of code length codes + const ncode = (try self.bit_reader.readBitsNoEof(usize, 4)) + 4; + + if (nlen > MAXLCODES or ndist > MAXDCODES) + return error.BadCounts; + + // Permutation of code length codes + const ORDER = [19]u16{ + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, + 12, 3, 13, 2, 14, 1, 15, + }; + + // Build the Huffman table to decode the code length codes + var lencode: Huffman = undefined; + { + var lengths = std.mem.zeroes([19]u16); + + // Read the code lengths, missing ones are left as zero + for (ORDER[0..ncode]) |val| { + lengths[val] = try self.bit_reader.readBitsNoEof(u16, 3); + } + + try lencode.construct(lengths[0..]); + } + + // Read the length/literal and distance code length tables. + // Zero the table by default so we can avoid explicitly writing out + // zeros for codes 17 and 18 + var lengths = std.mem.zeroes([MAXCODES]u16); + + var i: usize = 0; + while (i < nlen + ndist) { + const symbol = try self.decode(&lencode); + + switch (symbol) { + 0...15 => { + lengths[i] = symbol; + i += 1; + }, + 16 => { + // repeat last length 3..6 times + if (i == 0) return error.NoLastLength; + + const last_length = lengths[i - 1]; + const repeat = 3 + (try self.bit_reader.readBitsNoEof(usize, 2)); + const last_index = i + repeat; + while (i < last_index) : (i += 1) { + lengths[i] = last_length; + } + }, + 17 => { + // repeat zero 3..10 times + i += 3 + (try self.bit_reader.readBitsNoEof(usize, 3)); + }, + 18 => { + // repeat zero 11..138 times + i += 11 + (try self.bit_reader.readBitsNoEof(usize, 7)); + }, + else => return error.InvalidSymbol, + } + } + + if (i > nlen + ndist) + return error.InvalidLength; + + // Check if the end of block code is present + if (lengths[256] == 0) + return error.MissingEOBCode; + + try self.huffman_tables[0].construct(lengths[0..nlen]); + try self.huffman_tables[1].construct(lengths[nlen .. nlen + ndist]); + + self.hlen = &self.huffman_tables[0]; + self.hdist = &self.huffman_tables[1]; + self.state = .DecodeBlockData; + } + + fn codes(self: *Self, lencode: *Huffman, distcode: *Huffman) !bool { + // Size base for length codes 257..285 + const LENS = [29]u16{ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, + }; + // Extra bits for length codes 257..285 + const LEXT = [29]u16{ + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, + }; + // Offset base for distance codes 0..29 + const DISTS = [30]u16{ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, + }; + // Extra bits for distance codes 0..29 + const DEXT = [30]u16{ + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, + }; + + while (true) { + const symbol = try self.decode(lencode); + + switch (symbol) { + 0...255 => { + // Literal value + const c = @truncate(u8, symbol); + if (self.window.append(c) == 0) { + self.state = .{ .CopyLit = c }; + return false; + } + }, + 256 => { + // End of block symbol + return true; + }, + 257...285 => { + // Length/distance pair + const length_symbol = symbol - 257; + const length = LENS[length_symbol] + + try self.bit_reader.readBitsNoEof(u16, LEXT[length_symbol]); + + const distance_symbol = try self.decode(distcode); + const distance = DISTS[distance_symbol] + + try self.bit_reader.readBitsNoEof(u16, DEXT[distance_symbol]); + + if (distance > self.window.buf.len) + return error.InvalidDistance; + + const written = self.window.copyFrom(distance, length); + if (written != length) { + self.state = .{ + .CopyFrom = .{ + .distance = distance, + .length = length - @truncate(u16, written), + }, + }; + return false; + } + }, + else => return error.InvalidFixedCode, + } + } + } + + fn decode(self: *Self, h: *Huffman) !u16 { + var len: usize = 1; + var code: usize = 0; + var first: usize = 0; + var index: usize = 0; + + while (len <= MAXBITS) : (len += 1) { + code |= try self.bit_reader.readBitsNoEof(usize, 1); + const count = h.count[len]; + if (code < first + count) + return h.symbol[index + (code - first)]; + index += count; + first += count; + first <<= 1; + code <<= 1; + } + + return error.OutOfCodes; + } + + fn step(self: *Self) !void { + while (true) { + switch (self.state) { + .DecodeBlockHeader => { + // The compressed stream is done + if (self.seen_eos) return; + + const last = try self.bit_reader.readBitsNoEof(u1, 1); + const kind = try self.bit_reader.readBitsNoEof(u2, 2); + + self.seen_eos = last != 0; + + // The next state depends on the block type + switch (kind) { + 0 => try self.stored(), + 1 => try self.fixed(), + 2 => try self.dynamic(), + 3 => return error.InvalidBlockType, + } + }, + .DecodeBlockData => { + if (!try self.codes(self.hlen, self.hdist)) { + return; + } + + self.state = .DecodeBlockHeader; + }, + .Copy => |*length| { + const N = math.min(self.window.writable(), length.*); + + // TODO: This loop can be more efficient. On the other + // hand uncompressed blocks are not that common so... + var i: usize = 0; + while (i < N) : (i += 1) { + var tmp: [1]u8 = undefined; + if ((try self.bit_reader.read(&tmp)) != 1) { + // Unexpected end of stream, keep this error + // consistent with the use of readBitsNoEof + return error.EndOfStream; + } + self.window.appendUnsafe(tmp[0]); + } + + if (N != length.*) { + length.* -= N; + return; + } + + self.state = .DecodeBlockHeader; + }, + .CopyLit => |c| { + if (self.window.append(c) == 0) { + return; + } + + self.state = .DecodeBlockData; + }, + .CopyFrom => |*info| { + const written = self.window.copyFrom(info.distance, info.length); + if (written != info.length) { + info.length -= @truncate(u16, written); + return; + } + + self.state = .DecodeBlockData; + }, + } + } + } + + fn init(source: ReaderType, window_slice: []u8) Self { + assert(math.isPowerOfTwo(window_slice.len)); + + return Self{ + .bit_reader = io.bitReader(.Little, source), + .window = .{ .buf = window_slice }, + .seen_eos = false, + .state = .DecodeBlockHeader, + .hdist = undefined, + .hlen = undefined, + }; + } + + // Implements the io.Reader interface + pub fn read(self: *Self, buffer: []u8) Error!usize { + if (buffer.len == 0) + return 0; + + // Try reading as much as possible from the window + var read_amt: usize = self.window.read(buffer); + while (read_amt < buffer.len) { + // Run the state machine, we can detect the "effective" end of + // stream condition by checking if any progress was made. + // Why "effective"? Because even though `seen_eos` is true we + // may still have to finish processing other decoding steps. + try self.step(); + // No progress was made + if (self.window.readable() == 0) + break; + + read_amt += self.window.read(buffer[read_amt..]); + } + + return read_amt; + } + + pub fn reader(self: *Self) Reader { + return .{ .context = self }; + } + }; +} + +pub fn inflateStream(reader: anytype, window_slice: []u8) InflateStream(@TypeOf(reader)) { + return InflateStream(@TypeOf(reader)).init(reader, window_slice); +} diff --git a/lib/std/compress/rfc1951.txt b/lib/std/compress/rfc1951.txt new file mode 100644 index 0000000000..403c8c722f --- /dev/null +++ b/lib/std/compress/rfc1951.txt @@ -0,0 +1,955 @@ + + + + + + +Network Working Group P. Deutsch +Request for Comments: 1951 Aladdin Enterprises +Category: Informational May 1996 + + + DEFLATE Compressed Data Format Specification version 1.3 + +Status of This Memo + + This memo provides information for the Internet community. This memo + does not specify an Internet standard of any kind. Distribution of + this memo is unlimited. + +IESG Note: + + The IESG takes no position on the validity of any Intellectual + Property Rights statements contained in this document. + +Notices + + Copyright (c) 1996 L. Peter Deutsch + + Permission is granted to copy and distribute this document for any + purpose and without charge, including translations into other + languages and incorporation into compilations, provided that the + copyright notice and this notice are preserved, and that any + substantive changes or deletions from the original are clearly + marked. + + A pointer to the latest version of this and related documentation in + HTML format can be found at the URL + . + +Abstract + + This specification defines a lossless compressed data format that + compresses data using a combination of the LZ77 algorithm and Huffman + coding, with efficiency comparable to the best currently available + general-purpose compression methods. The data can be produced or + consumed, even for an arbitrarily long sequentially presented input + data stream, using only an a priori bounded amount of intermediate + storage. The format can be implemented readily in a manner not + covered by patents. + + + + + + + + +Deutsch Informational [Page 1] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + +Table of Contents + + 1. Introduction ................................................... 2 + 1.1. Purpose ................................................... 2 + 1.2. Intended audience ......................................... 3 + 1.3. Scope ..................................................... 3 + 1.4. Compliance ................................................ 3 + 1.5. Definitions of terms and conventions used ................ 3 + 1.6. Changes from previous versions ............................ 4 + 2. Compressed representation overview ............................. 4 + 3. Detailed specification ......................................... 5 + 3.1. Overall conventions ....................................... 5 + 3.1.1. Packing into bytes .................................. 5 + 3.2. Compressed block format ................................... 6 + 3.2.1. Synopsis of prefix and Huffman coding ............... 6 + 3.2.2. Use of Huffman coding in the "deflate" format ....... 7 + 3.2.3. Details of block format ............................. 9 + 3.2.4. Non-compressed blocks (BTYPE=00) ................... 11 + 3.2.5. Compressed blocks (length and distance codes) ...... 11 + 3.2.6. Compression with fixed Huffman codes (BTYPE=01) .... 12 + 3.2.7. Compression with dynamic Huffman codes (BTYPE=10) .. 13 + 3.3. Compliance ............................................... 14 + 4. Compression algorithm details ................................. 14 + 5. References .................................................... 16 + 6. Security Considerations ....................................... 16 + 7. Source code ................................................... 16 + 8. Acknowledgements .............................................. 16 + 9. Author's Address .............................................. 17 + +1. Introduction + + 1.1. Purpose + + The purpose of this specification is to define a lossless + compressed data format that: + * Is independent of CPU type, operating system, file system, + and character set, and hence can be used for interchange; + * Can be produced or consumed, even for an arbitrarily long + sequentially presented input data stream, using only an a + priori bounded amount of intermediate storage, and hence + can be used in data communications or similar structures + such as Unix filters; + * Compresses data with efficiency comparable to the best + currently available general-purpose compression methods, + and in particular considerably better than the "compress" + program; + * Can be implemented readily in a manner not covered by + patents, and hence can be practiced freely; + + + +Deutsch Informational [Page 2] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + * Is compatible with the file format produced by the current + widely used gzip utility, in that conforming decompressors + will be able to read data produced by the existing gzip + compressor. + + The data format defined by this specification does not attempt to: + + * Allow random access to compressed data; + * Compress specialized data (e.g., raster graphics) as well + as the best currently available specialized algorithms. + + A simple counting argument shows that no lossless compression + algorithm can compress every possible input data set. For the + format defined here, the worst case expansion is 5 bytes per 32K- + byte block, i.e., a size increase of 0.015% for large data sets. + English text usually compresses by a factor of 2.5 to 3; + executable files usually compress somewhat less; graphical data + such as raster images may compress much more. + + 1.2. Intended audience + + This specification is intended for use by implementors of software + to compress data into "deflate" format and/or decompress data from + "deflate" format. + + The text of the specification assumes a basic background in + programming at the level of bits and other primitive data + representations. Familiarity with the technique of Huffman coding + is helpful but not required. + + 1.3. Scope + + The specification specifies a method for representing a sequence + of bytes as a (usually shorter) sequence of bits, and a method for + packing the latter bit sequence into bytes. + + 1.4. Compliance + + Unless otherwise indicated below, a compliant decompressor must be + able to accept and decompress any data set that conforms to all + the specifications presented here; a compliant compressor must + produce data sets that conform to all the specifications presented + here. + + 1.5. Definitions of terms and conventions used + + Byte: 8 bits stored or transmitted as a unit (same as an octet). + For this specification, a byte is exactly 8 bits, even on machines + + + +Deutsch Informational [Page 3] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + which store a character on a number of bits different from eight. + See below, for the numbering of bits within a byte. + + String: a sequence of arbitrary bytes. + + 1.6. Changes from previous versions + + There have been no technical changes to the deflate format since + version 1.1 of this specification. In version 1.2, some + terminology was changed. Version 1.3 is a conversion of the + specification to RFC style. + +2. Compressed representation overview + + A compressed data set consists of a series of blocks, corresponding + to successive blocks of input data. The block sizes are arbitrary, + except that non-compressible blocks are limited to 65,535 bytes. + + Each block is compressed using a combination of the LZ77 algorithm + and Huffman coding. The Huffman trees for each block are independent + of those for previous or subsequent blocks; the LZ77 algorithm may + use a reference to a duplicated string occurring in a previous block, + up to 32K input bytes before. + + Each block consists of two parts: a pair of Huffman code trees that + describe the representation of the compressed data part, and a + compressed data part. (The Huffman trees themselves are compressed + using Huffman encoding.) The compressed data consists of a series of + elements of two types: literal bytes (of strings that have not been + detected as duplicated within the previous 32K input bytes), and + pointers to duplicated strings, where a pointer is represented as a + pair . The representation used in the + "deflate" format limits distances to 32K bytes and lengths to 258 + bytes, but does not limit the size of a block, except for + uncompressible blocks, which are limited as noted above. + + Each type of value (literals, distances, and lengths) in the + compressed data is represented using a Huffman code, using one code + tree for literals and lengths and a separate code tree for distances. + The code trees for each block appear in a compact form just before + the compressed data for that block. + + + + + + + + + + +Deutsch Informational [Page 4] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + +3. Detailed specification + + 3.1. Overall conventions In the diagrams below, a box like this: + + +---+ + | | <-- the vertical bars might be missing + +---+ + + represents one byte; a box like this: + + +==============+ + | | + +==============+ + + represents a variable number of bytes. + + Bytes stored within a computer do not have a "bit order", since + they are always treated as a unit. However, a byte considered as + an integer between 0 and 255 does have a most- and least- + significant bit, and since we write numbers with the most- + significant digit on the left, we also write bytes with the most- + significant bit on the left. In the diagrams below, we number the + bits of a byte so that bit 0 is the least-significant bit, i.e., + the bits are numbered: + + +--------+ + |76543210| + +--------+ + + Within a computer, a number may occupy multiple bytes. All + multi-byte numbers in the format described here are stored with + the least-significant byte first (at the lower memory address). + For example, the decimal number 520 is stored as: + + 0 1 + +--------+--------+ + |00001000|00000010| + +--------+--------+ + ^ ^ + | | + | + more significant byte = 2 x 256 + + less significant byte = 8 + + 3.1.1. Packing into bytes + + This document does not address the issue of the order in which + bits of a byte are transmitted on a bit-sequential medium, + since the final data format described here is byte- rather than + + + +Deutsch Informational [Page 5] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + bit-oriented. However, we describe the compressed block format + in below, as a sequence of data elements of various bit + lengths, not a sequence of bytes. We must therefore specify + how to pack these data elements into bytes to form the final + compressed byte sequence: + + * Data elements are packed into bytes in order of + increasing bit number within the byte, i.e., starting + with the least-significant bit of the byte. + * Data elements other than Huffman codes are packed + starting with the least-significant bit of the data + element. + * Huffman codes are packed starting with the most- + significant bit of the code. + + In other words, if one were to print out the compressed data as + a sequence of bytes, starting with the first byte at the + *right* margin and proceeding to the *left*, with the most- + significant bit of each byte on the left as usual, one would be + able to parse the result from right to left, with fixed-width + elements in the correct MSB-to-LSB order and Huffman codes in + bit-reversed order (i.e., with the first bit of the code in the + relative LSB position). + + 3.2. Compressed block format + + 3.2.1. Synopsis of prefix and Huffman coding + + Prefix coding represents symbols from an a priori known + alphabet by bit sequences (codes), one code for each symbol, in + a manner such that different symbols may be represented by bit + sequences of different lengths, but a parser can always parse + an encoded string unambiguously symbol-by-symbol. + + We define a prefix code in terms of a binary tree in which the + two edges descending from each non-leaf node are labeled 0 and + 1 and in which the leaf nodes correspond one-for-one with (are + labeled with) the symbols of the alphabet; then the code for a + symbol is the sequence of 0's and 1's on the edges leading from + the root to the leaf labeled with that symbol. For example: + + + + + + + + + + + +Deutsch Informational [Page 6] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + /\ Symbol Code + 0 1 ------ ---- + / \ A 00 + /\ B B 1 + 0 1 C 011 + / \ D 010 + A /\ + 0 1 + / \ + D C + + A parser can decode the next symbol from an encoded input + stream by walking down the tree from the root, at each step + choosing the edge corresponding to the next input bit. + + Given an alphabet with known symbol frequencies, the Huffman + algorithm allows the construction of an optimal prefix code + (one which represents strings with those symbol frequencies + using the fewest bits of any possible prefix codes for that + alphabet). Such a code is called a Huffman code. (See + reference [1] in Chapter 5, references for additional + information on Huffman codes.) + + Note that in the "deflate" format, the Huffman codes for the + various alphabets must not exceed certain maximum code lengths. + This constraint complicates the algorithm for computing code + lengths from symbol frequencies. Again, see Chapter 5, + references for details. + + 3.2.2. Use of Huffman coding in the "deflate" format + + The Huffman codes used for each alphabet in the "deflate" + format have two additional rules: + + * All codes of a given bit length have lexicographically + consecutive values, in the same order as the symbols + they represent; + + * Shorter codes lexicographically precede longer codes. + + + + + + + + + + + + +Deutsch Informational [Page 7] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + We could recode the example above to follow this rule as + follows, assuming that the order of the alphabet is ABCD: + + Symbol Code + ------ ---- + A 10 + B 0 + C 110 + D 111 + + I.e., 0 precedes 10 which precedes 11x, and 110 and 111 are + lexicographically consecutive. + + Given this rule, we can define the Huffman code for an alphabet + just by giving the bit lengths of the codes for each symbol of + the alphabet in order; this is sufficient to determine the + actual codes. In our example, the code is completely defined + by the sequence of bit lengths (2, 1, 3, 3). The following + algorithm generates the codes as integers, intended to be read + from most- to least-significant bit. The code lengths are + initially in tree[I].Len; the codes are produced in + tree[I].Code. + + 1) Count the number of codes for each code length. Let + bl_count[N] be the number of codes of length N, N >= 1. + + 2) Find the numerical value of the smallest code for each + code length: + + code = 0; + bl_count[0] = 0; + for (bits = 1; bits <= MAX_BITS; bits++) { + code = (code + bl_count[bits-1]) << 1; + next_code[bits] = code; + } + + 3) Assign numerical values to all codes, using consecutive + values for all codes of the same length with the base + values determined at step 2. Codes that are never used + (which have a bit length of zero) must not be assigned a + value. + + for (n = 0; n <= max_code; n++) { + len = tree[n].Len; + if (len != 0) { + tree[n].Code = next_code[len]; + next_code[len]++; + } + + + +Deutsch Informational [Page 8] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + } + + Example: + + Consider the alphabet ABCDEFGH, with bit lengths (3, 3, 3, 3, + 3, 2, 4, 4). After step 1, we have: + + N bl_count[N] + - ----------- + 2 1 + 3 5 + 4 2 + + Step 2 computes the following next_code values: + + N next_code[N] + - ------------ + 1 0 + 2 0 + 3 2 + 4 14 + + Step 3 produces the following code values: + + Symbol Length Code + ------ ------ ---- + A 3 010 + B 3 011 + C 3 100 + D 3 101 + E 3 110 + F 2 00 + G 4 1110 + H 4 1111 + + 3.2.3. Details of block format + + Each block of compressed data begins with 3 header bits + containing the following data: + + first bit BFINAL + next 2 bits BTYPE + + Note that the header bits do not necessarily begin on a byte + boundary, since a block does not necessarily occupy an integral + number of bytes. + + + + + +Deutsch Informational [Page 9] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + BFINAL is set if and only if this is the last block of the data + set. + + BTYPE specifies how the data are compressed, as follows: + + 00 - no compression + 01 - compressed with fixed Huffman codes + 10 - compressed with dynamic Huffman codes + 11 - reserved (error) + + The only difference between the two compressed cases is how the + Huffman codes for the literal/length and distance alphabets are + defined. + + In all cases, the decoding algorithm for the actual data is as + follows: + + do + read block header from input stream. + if stored with no compression + skip any remaining bits in current partially + processed byte + read LEN and NLEN (see next section) + copy LEN bytes of data to output + otherwise + if compressed with dynamic Huffman codes + read representation of code trees (see + subsection below) + loop (until end of block code recognized) + decode literal/length value from input stream + if value < 256 + copy value (literal byte) to output stream + otherwise + if value = end of block (256) + break from loop + otherwise (value = 257..285) + decode distance from input stream + + move backwards distance bytes in the output + stream, and copy length bytes from this + position to the output stream. + end loop + while not last block + + Note that a duplicated string reference may refer to a string + in a previous block; i.e., the backward distance may cross one + or more block boundaries. However a distance cannot refer past + the beginning of the output stream. (An application using a + + + +Deutsch Informational [Page 10] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + preset dictionary might discard part of the output stream; a + distance can refer to that part of the output stream anyway) + Note also that the referenced string may overlap the current + position; for example, if the last 2 bytes decoded have values + X and Y, a string reference with + adds X,Y,X,Y,X to the output stream. + + We now specify each compression method in turn. + + 3.2.4. Non-compressed blocks (BTYPE=00) + + Any bits of input up to the next byte boundary are ignored. + The rest of the block consists of the following information: + + 0 1 2 3 4... + +---+---+---+---+================================+ + | LEN | NLEN |... LEN bytes of literal data...| + +---+---+---+---+================================+ + + LEN is the number of data bytes in the block. NLEN is the + one's complement of LEN. + + 3.2.5. Compressed blocks (length and distance codes) + + As noted above, encoded data blocks in the "deflate" format + consist of sequences of symbols drawn from three conceptually + distinct alphabets: either literal bytes, from the alphabet of + byte values (0..255), or pairs, + where the length is drawn from (3..258) and the distance is + drawn from (1..32,768). In fact, the literal and length + alphabets are merged into a single alphabet (0..285), where + values 0..255 represent literal bytes, the value 256 indicates + end-of-block, and values 257..285 represent length codes + (possibly in conjunction with extra bits following the symbol + code) as follows: + + + + + + + + + + + + + + + + +Deutsch Informational [Page 11] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + Extra Extra Extra + Code Bits Length(s) Code Bits Lengths Code Bits Length(s) + ---- ---- ------ ---- ---- ------- ---- ---- ------- + 257 0 3 267 1 15,16 277 4 67-82 + 258 0 4 268 1 17,18 278 4 83-98 + 259 0 5 269 2 19-22 279 4 99-114 + 260 0 6 270 2 23-26 280 4 115-130 + 261 0 7 271 2 27-30 281 5 131-162 + 262 0 8 272 2 31-34 282 5 163-194 + 263 0 9 273 3 35-42 283 5 195-226 + 264 0 10 274 3 43-50 284 5 227-257 + 265 1 11,12 275 3 51-58 285 0 258 + 266 1 13,14 276 3 59-66 + + The extra bits should be interpreted as a machine integer + stored with the most-significant bit first, e.g., bits 1110 + represent the value 14. + + Extra Extra Extra + Code Bits Dist Code Bits Dist Code Bits Distance + ---- ---- ---- ---- ---- ------ ---- ---- -------- + 0 0 1 10 4 33-48 20 9 1025-1536 + 1 0 2 11 4 49-64 21 9 1537-2048 + 2 0 3 12 5 65-96 22 10 2049-3072 + 3 0 4 13 5 97-128 23 10 3073-4096 + 4 1 5,6 14 6 129-192 24 11 4097-6144 + 5 1 7,8 15 6 193-256 25 11 6145-8192 + 6 2 9-12 16 7 257-384 26 12 8193-12288 + 7 2 13-16 17 7 385-512 27 12 12289-16384 + 8 3 17-24 18 8 513-768 28 13 16385-24576 + 9 3 25-32 19 8 769-1024 29 13 24577-32768 + + 3.2.6. Compression with fixed Huffman codes (BTYPE=01) + + The Huffman codes for the two alphabets are fixed, and are not + represented explicitly in the data. The Huffman code lengths + for the literal/length alphabet are: + + Lit Value Bits Codes + --------- ---- ----- + 0 - 143 8 00110000 through + 10111111 + 144 - 255 9 110010000 through + 111111111 + 256 - 279 7 0000000 through + 0010111 + 280 - 287 8 11000000 through + 11000111 + + + +Deutsch Informational [Page 12] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + The code lengths are sufficient to generate the actual codes, + as described above; we show the codes in the table for added + clarity. Literal/length values 286-287 will never actually + occur in the compressed data, but participate in the code + construction. + + Distance codes 0-31 are represented by (fixed-length) 5-bit + codes, with possible additional bits as shown in the table + shown in Paragraph 3.2.5, above. Note that distance codes 30- + 31 will never actually occur in the compressed data. + + 3.2.7. Compression with dynamic Huffman codes (BTYPE=10) + + The Huffman codes for the two alphabets appear in the block + immediately after the header bits and before the actual + compressed data, first the literal/length code and then the + distance code. Each code is defined by a sequence of code + lengths, as discussed in Paragraph 3.2.2, above. For even + greater compactness, the code length sequences themselves are + compressed using a Huffman code. The alphabet for code lengths + is as follows: + + 0 - 15: Represent code lengths of 0 - 15 + 16: Copy the previous code length 3 - 6 times. + The next 2 bits indicate repeat length + (0 = 3, ... , 3 = 6) + Example: Codes 8, 16 (+2 bits 11), + 16 (+2 bits 10) will expand to + 12 code lengths of 8 (1 + 6 + 5) + 17: Repeat a code length of 0 for 3 - 10 times. + (3 bits of length) + 18: Repeat a code length of 0 for 11 - 138 times + (7 bits of length) + + A code length of 0 indicates that the corresponding symbol in + the literal/length or distance alphabet will not occur in the + block, and should not participate in the Huffman code + construction algorithm given earlier. If only one distance + code is used, it is encoded using one bit, not zero bits; in + this case there is a single code length of one, with one unused + code. One distance code of zero bits means that there are no + distance codes used at all (the data is all literals). + + We can now define the format of the block: + + 5 Bits: HLIT, # of Literal/Length codes - 257 (257 - 286) + 5 Bits: HDIST, # of Distance codes - 1 (1 - 32) + 4 Bits: HCLEN, # of Code Length codes - 4 (4 - 19) + + + +Deutsch Informational [Page 13] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + (HCLEN + 4) x 3 bits: code lengths for the code length + alphabet given just above, in the order: 16, 17, 18, + 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + + These code lengths are interpreted as 3-bit integers + (0-7); as above, a code length of 0 means the + corresponding symbol (literal/length or distance code + length) is not used. + + HLIT + 257 code lengths for the literal/length alphabet, + encoded using the code length Huffman code + + HDIST + 1 code lengths for the distance alphabet, + encoded using the code length Huffman code + + The actual compressed data of the block, + encoded using the literal/length and distance Huffman + codes + + The literal/length symbol 256 (end of data), + encoded using the literal/length Huffman code + + The code length repeat codes can cross from HLIT + 257 to the + HDIST + 1 code lengths. In other words, all code lengths form + a single sequence of HLIT + HDIST + 258 values. + + 3.3. Compliance + + A compressor may limit further the ranges of values specified in + the previous section and still be compliant; for example, it may + limit the range of backward pointers to some value smaller than + 32K. Similarly, a compressor may limit the size of blocks so that + a compressible block fits in memory. + + A compliant decompressor must accept the full range of possible + values defined in the previous section, and must accept blocks of + arbitrary size. + +4. Compression algorithm details + + While it is the intent of this document to define the "deflate" + compressed data format without reference to any particular + compression algorithm, the format is related to the compressed + formats produced by LZ77 (Lempel-Ziv 1977, see reference [2] below); + since many variations of LZ77 are patented, it is strongly + recommended that the implementor of a compressor follow the general + algorithm presented here, which is known not to be patented per se. + The material in this section is not part of the definition of the + + + +Deutsch Informational [Page 14] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + + specification per se, and a compressor need not follow it in order to + be compliant. + + The compressor terminates a block when it determines that starting a + new block with fresh trees would be useful, or when the block size + fills up the compressor's block buffer. + + The compressor uses a chained hash table to find duplicated strings, + using a hash function that operates on 3-byte sequences. At any + given point during compression, let XYZ be the next 3 input bytes to + be examined (not necessarily all different, of course). First, the + compressor examines the hash chain for XYZ. If the chain is empty, + the compressor simply writes out X as a literal byte and advances one + byte in the input. If the hash chain is not empty, indicating that + the sequence XYZ (or, if we are unlucky, some other 3 bytes with the + same hash function value) has occurred recently, the compressor + compares all strings on the XYZ hash chain with the actual input data + sequence starting at the current point, and selects the longest + match. + + The compressor searches the hash chains starting with the most recent + strings, to favor small distances and thus take advantage of the + Huffman encoding. The hash chains are singly linked. There are no + deletions from the hash chains; the algorithm simply discards matches + that are too old. To avoid a worst-case situation, very long hash + chains are arbitrarily truncated at a certain length, determined by a + run-time parameter. + + To improve overall compression, the compressor optionally defers the + selection of matches ("lazy matching"): after a match of length N has + been found, the compressor searches for a longer match starting at + the next input byte. If it finds a longer match, it truncates the + previous match to a length of one (thus producing a single literal + byte) and then emits the longer match. Otherwise, it emits the + original match, and, as described above, advances N bytes before + continuing. + + Run-time parameters also control this "lazy match" procedure. If + compression ratio is most important, the compressor attempts a + complete second search regardless of the length of the first match. + In the normal case, if the current match is "long enough", the + compressor reduces the search for a longer match, thus speeding up + the process. If speed is most important, the compressor inserts new + strings in the hash table only when no match was found, or when the + match is not "too long". This degrades the compression ratio but + saves time since there are both fewer insertions and fewer searches. + + + + + +Deutsch Informational [Page 15] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + +5. References + + [1] Huffman, D. A., "A Method for the Construction of Minimum + Redundancy Codes", Proceedings of the Institute of Radio + Engineers, September 1952, Volume 40, Number 9, pp. 1098-1101. + + [2] Ziv J., Lempel A., "A Universal Algorithm for Sequential Data + Compression", IEEE Transactions on Information Theory, Vol. 23, + No. 3, pp. 337-343. + + [3] Gailly, J.-L., and Adler, M., ZLIB documentation and sources, + available in ftp://ftp.uu.net/pub/archiving/zip/doc/ + + [4] Gailly, J.-L., and Adler, M., GZIP documentation and sources, + available as gzip-*.tar in ftp://prep.ai.mit.edu/pub/gnu/ + + [5] Schwartz, E. S., and Kallick, B. "Generating a canonical prefix + encoding." Comm. ACM, 7,3 (Mar. 1964), pp. 166-169. + + [6] Hirschberg and Lelewer, "Efficient decoding of prefix codes," + Comm. ACM, 33,4, April 1990, pp. 449-459. + +6. Security Considerations + + Any data compression method involves the reduction of redundancy in + the data. Consequently, any corruption of the data is likely to have + severe effects and be difficult to correct. Uncompressed text, on + the other hand, will probably still be readable despite the presence + of some corrupted bytes. + + It is recommended that systems using this data format provide some + means of validating the integrity of the compressed data. See + reference [3], for example. + +7. Source code + + Source code for a C language implementation of a "deflate" compliant + compressor and decompressor is available within the zlib package at + ftp://ftp.uu.net/pub/archiving/zip/zlib/. + +8. Acknowledgements + + Trademarks cited in this document are the property of their + respective owners. + + Phil Katz designed the deflate format. Jean-Loup Gailly and Mark + Adler wrote the related software described in this specification. + Glenn Randers-Pehrson converted this document to RFC and HTML format. + + + +Deutsch Informational [Page 16] + +RFC 1951 DEFLATE Compressed Data Format Specification May 1996 + + +9. Author's Address + + L. Peter Deutsch + Aladdin Enterprises + 203 Santa Margarita Ave. + Menlo Park, CA 94025 + + Phone: (415) 322-0103 (AM only) + FAX: (415) 322-1734 + EMail: + + Questions about the technical content of this specification can be + sent by email to: + + Jean-Loup Gailly and + Mark Adler + + Editorial comments on this specification can be sent by email to: + + L. Peter Deutsch and + Glenn Randers-Pehrson + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Deutsch Informational [Page 17] + diff --git a/lib/std/compress/rfc1951.txt.fixed.z.9 b/lib/std/compress/rfc1951.txt.fixed.z.9 new file mode 100644 index 0000000000000000000000000000000000000000..8ea5904770052de696de797b4ef5f3b1a5535e5b GIT binary patch literal 12836 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 literal 0 HcmV?d00001 diff --git a/lib/std/compress/rfc1951.txt.z.0 b/lib/std/compress/rfc1951.txt.z.0 new file mode 100644 index 0000000000000000000000000000000000000000..3f50fb68f8da776fc6d6833b2cbe11cf822fd075 GIT binary patch literal 36960 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 z

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.Mas?GOC8#}Ha4rJfOjZoK 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

Ar)`0^if1MYvcF1BE7D~QU`s1yP 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 4); + const FCHECK = @truncate(u5, header[1]); + const FDICT = @truncate(u1, header[1] >> 5); + + if ((@as(u16, header[0]) << 8 | header[1]) % 31 != 0) + return error.BadHeader; + + // The CM field must be 8 to indicate the use of DEFLATE + if (CM != 8) return error.InvalidCompression; + // CINFO is the base-2 logarithm of the window size, minus 8. + // Values above 7 are unspecified and therefore rejected. + if (CINFO > 7) return error.InvalidWindowSize; + const window_size: u16 = @as(u16, 1) << (CINFO + 8); + + // TODO: Support this case + if (FDICT != 0) + return error.Unsupported; + + var window_slice = try allocator.alloc(u8, window_size); + + return Self{ + .allocator = allocator, + .inflater = deflate.inflateStream(source, window_slice), + .in_reader = source, + .hasher = std.hash.Adler32.init(), + .window_slice = window_slice, + }; + } + + fn deinit(self: *Self) void { + self.allocator.free(self.window_slice); + } + + // Implements the io.Reader interface + pub fn read(self: *Self, buffer: []u8) Error!usize { + if (buffer.len == 0) + return 0; + + // Read from the compressed stream and update the computed checksum + const r = try self.inflater.read(buffer); + if (r != 0) { + self.hasher.update(buffer[0..r]); + return r; + } + + // We've reached the end of stream, check if the checksum matches + const hash = try self.in_reader.readIntBig(u32); + if (hash != self.hasher.final()) + return error.WrongChecksum; + + return 0; + } + + pub fn reader(self: *Self) Reader { + return .{ .context = self }; + } + }; +} + +pub fn zlibStream(allocator: *mem.Allocator, reader: anytype) !ZlibStream(@TypeOf(reader)) { + return ZlibStream(@TypeOf(reader)).init(allocator, reader); +} + +fn testReader(data: []const u8, comptime expected: []const u8) !void { + var in_stream = io.fixedBufferStream(data); + + var zlib_stream = try zlibStream(testing.allocator, in_stream.reader()); + defer zlib_stream.deinit(); + + // Read and decompress the whole file + const buf = try zlib_stream.reader().readAllAlloc(testing.allocator, std.math.maxInt(usize)); + defer testing.allocator.free(buf); + // Calculate its SHA256 hash and check it against the reference + var hash: [32]u8 = undefined; + std.crypto.hash.sha2.Sha256.hash(buf, hash[0..], .{}); + + assertEqual(expected, &hash); +} + +// Assert `expected` == `input` where `input` is a bytestring. +pub fn assertEqual(comptime expected: []const u8, input: []const u8) void { + var expected_bytes: [expected.len / 2]u8 = undefined; + for (expected_bytes) |*r, i| { + r.* = std.fmt.parseInt(u8, expected[2 * i .. 2 * i + 2], 16) catch unreachable; + } + + testing.expectEqualSlices(u8, &expected_bytes, input); +} + +// All the test cases are obtained by compressing the RFC1950 text +// +// https://tools.ietf.org/rfc/rfc1950.txt length=36944 bytes +// SHA256=5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009 +test "compressed data" { + // Compressed with compression level = 0 + try testReader( + @embedFile("rfc1951.txt.z.0"), + "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009", + ); + // Compressed with compression level = 9 + try testReader( + @embedFile("rfc1951.txt.z.9"), + "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009", + ); + // Compressed with compression level = 9 and fixed Huffman codes + try testReader( + @embedFile("rfc1951.txt.fixed.z.9"), + "5ebf4b5b7fe1c3a0c0ab9aa3ac8c0f3853a7dc484905e76e03b0b0f301350009", + ); +} + +test "sanity checks" { + // Truncated header + testing.expectError( + error.EndOfStream, + testReader(&[_]u8{0x78}, ""), + ); + // Failed FCHECK check + testing.expectError( + error.BadHeader, + testReader(&[_]u8{ 0x78, 0x9D }, ""), + ); + // Wrong CM + testing.expectError( + error.InvalidCompression, + testReader(&[_]u8{ 0x79, 0x94 }, ""), + ); + // Wrong CINFO + testing.expectError( + error.InvalidWindowSize, + testReader(&[_]u8{ 0x88, 0x98 }, ""), + ); + // Wrong checksum + testing.expectError( + error.WrongChecksum, + testReader(&[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 }, ""), + ); + // Truncated checksum + testing.expectError( + error.EndOfStream, + testReader(&[_]u8{ 0x78, 0xda, 0x03, 0x00, 0x00 }, ""), + ); +} diff --git a/lib/std/std.zig b/lib/std/std.zig index 330f3c253b..4236b29298 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -50,6 +50,7 @@ pub const builtin = @import("builtin.zig"); pub const c = @import("c.zig"); pub const cache_hash = @import("cache_hash.zig"); pub const coff = @import("coff.zig"); +pub const compress = @import("compress.zig"); pub const crypto = @import("crypto.zig"); pub const cstr = @import("cstr.zig"); pub const debug = @import("debug.zig"); From c15f39212e32c612cf51f647868be2bdd024d0de Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 7 Sep 2020 15:15:48 -0700 Subject: [PATCH 2/2] build.zig: ignore the compression test files --- build.zig | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 8ac2d4f8ba..3f7f1a9038 100644 --- a/build.zig +++ b/build.zig @@ -123,7 +123,13 @@ pub fn build(b: *Builder) !void { .source_dir = "lib", .install_dir = .Lib, .install_subdir = "zig", - .exclude_extensions = &[_][]const u8{ "test.zig", "README.md" }, + .exclude_extensions = &[_][]const u8{ + "test.zig", + "README.md", + ".z.0", + ".z.9", + "rfc1951.txt", + }, }); const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");