1 | verify_checksum bool = true |
2 | } |
3 | |
4 | -// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array |
5 | -// Example: decompressed := gzip.decompress(b)? |
6 | -pub fn decompress(data []u8, params DecompressParams) ![]u8 { |
7 | - if data.len < 18 { |
8 | +pub const ( |
9 | + reserved_bits = 0b1110_0000 |
10 | + ftext = 0b0000_0001 |
11 | + fextra = 0b0000_0100 |
12 | + fname = 0b0000_1000 |
13 | + fcomment = 0b0001_0000 |
14 | + fhcrc = 0b0000_0010 |
15 | +) |
16 | + |
17 | +const min_header_length = 18 |
18 | + |
19 | +[noinit] |
20 | +pub struct GzipHeader { |
21 | +pub mut: |
22 | + length int = 10 |
23 | + extra []u8 |
24 | + filename []u8 |
25 | + comment []u8 |
26 | + modification_time u32 |
27 | + operating_system u8 |
28 | +} |
29 | + |
30 | +// validate validates the header and returns its details if valid |
31 | +pub fn validate(data []u8, params DecompressParams) !GzipHeader { |
32 | + if data.len < gzip.min_header_length { |
33 | return error('data is too short, not gzip compressed?') |
34 | } else if data[0] != 0x1f || data[1] != 0x8b { |
35 | return error('wrong magic numbers, not gzip compressed?') |
36 | } else if data[2] != 0x08 { |
37 | return error('gzip data is not compressed with DEFLATE') |
38 | } |
39 | - mut header_length := 10 |
40 | + mut header := GzipHeader{} |
41 | |
42 | // parse flags, we ignore most of them, but we still need to parse them |
43 | // correctly, so we dont accidently decompress something that belongs |
44 | // to the header |
45 | |
46 | - if data[4] & 0b1110_0000 > 0 { // reserved bits |
47 | + if data[3] & gzip.reserved_bits > 0 { |
48 | // rfc 1952 2.3.1.2 Compliance |
49 | // A compliant decompressor must give an error indication if any |
50 | // reserved bit is non-zero, since such a bit could indicate the |
51 | return error('reserved flags are set, unsupported field detected') |
52 | } |
53 | |
54 | - // if data[4] & 0b0000_0001 {} // FTEXT |
55 | - if data[4] & 0b0000_0100 > 0 { // FEXTRA, extra data |
56 | - xlen := data[header_length] |
57 | - header_length += xlen + 1 |
58 | + if data[3] & gzip.fextra > 0 { |
59 | + xlen := data[header.length] |
60 | + header.extra = data[header.length + 1..header.length + 1 + xlen] |
61 | + header.length += xlen + 1 |
62 | } |
63 | - if data[4] & 0b0000_1000 > 0 { // FNAME, file name |
64 | + if data[3] & gzip.fname > 0 { |
65 | // filename is zero-terminated, so skip until we hit a zero byte |
66 | - for header_length < data.len && data[header_length] != 0x00 { |
67 | - header_length++ |
68 | + for header.length < data.len && data[header.length] != 0x00 { |
69 | + header.filename << data[header.length] |
70 | + header.length++ |
71 | } |
72 | - header_length++ |
73 | + header.length++ |
74 | } |
75 | - if data[4] & 0b0001_0000 > 0 { // FCOMMENT |
76 | + if data[3] & gzip.fcomment > 0 { |
77 | // comment is zero-terminated, so skip until we hit a zero byte |
78 | - for header_length < data.len && data[header_length] != 0x00 { |
79 | - header_length++ |
80 | + for header.length < data.len && data[header.length] != 0x00 { |
81 | + header.comment << data[header.length] |
82 | + header.length++ |
83 | } |
84 | - header_length++ |
85 | + header.length++ |
86 | } |
87 | - if data[4] & 0b0000_0010 > 0 { // FHCRC, flag header crc |
88 | - if header_length + 12 > data.len { |
89 | + if data[3] & gzip.fhcrc > 0 { |
90 | + if header.length + 12 > data.len { |
91 | return error('data too short') |
92 | } |
93 | - checksum_header := crc32.sum(data[..header_length]) |
94 | - checksum_header_expected := (u32(data[header_length]) << 24) | (u32(data[header_length + 1]) << 16) | (u32(data[ |
95 | - header_length + 2]) << 8) | data[header_length + 3] |
96 | + checksum_header := crc32.sum(data[..header.length]) |
97 | + checksum_header_expected := (u32(data[header.length]) << 24) | (u32(data[header.length + 1]) << 16) | (u32(data[ |
98 | + header.length + 2]) << 8) | data[header.length + 3] |
99 | if params.verify_header_checksum && checksum_header != checksum_header_expected { |
100 | return error('header checksum verification failed') |
101 | } |
102 | - header_length += 4 |
103 | + header.length += 4 |
104 | } |
105 | - if header_length + 8 > data.len { |
106 | + if header.length + 8 > data.len { |
107 | return error('data too short') |
108 | } |
109 | + header.operating_system = data[9] |
110 | + return header |
111 | +} |
112 | + |
113 | +// decompresses an array of bytes using zlib and returns the decompressed bytes in a new array |
114 | +// Example: decompressed := gzip.decompress(b)? |
115 | +pub fn decompress(data []u8, params DecompressParams) ![]u8 { |
116 | + gzip_header := validate(data, params)! |
117 | + header_length := gzip_header.length |
118 | |
119 | decompressed := compress.decompress(data[header_length..data.len - 8], 0)! |
120 | length_expected := (u32(data[data.len - 4]) << 24) | (u32(data[data.len - 3]) << 16) | (u32(data[data.len - 2]) << 8) | data[data.len - 1] |
121 | |