alex

/

v Public
0 Issues 1 Contributor 0 Releases 4 Branches
Additions: 65 Deletions: 33 View patch
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
1 fn test_gzip_with_ftext() {
2 uncompressed := 'Hello world!'
3 mut compressed := compress(uncompressed.bytes())!
4- compressed[4] |= 0b0000_0001 // FTEXT
5+ compressed[3] |= ftext
6 decompressed := decompress(compressed)!
7 assert decompressed == uncompressed.bytes()
8 }
9 fn test_gzip_with_fname() {
10 uncompressed := 'Hello world!'
11 mut compressed := compress(uncompressed.bytes())!
12- compressed[4] |= 0b0000_1000
13+ compressed[3] |= fname
14 compressed.insert(10, `h`)
15 compressed.insert(11, `i`)
16 compressed.insert(12, 0x00)
17 fn test_gzip_with_fcomment() {
18 uncompressed := 'Hello world!'
19 mut compressed := compress(uncompressed.bytes())!
20- compressed[4] |= 0b0001_0000
21+ compressed[3] |= fcomment
22 compressed.insert(10, `h`)
23 compressed.insert(11, `i`)
24 compressed.insert(12, 0x00)
25 fn test_gzip_with_fname_fcomment() {
26 uncompressed := 'Hello world!'
27 mut compressed := compress(uncompressed.bytes())!
28- compressed[4] |= 0b0001_1000
29+ compressed[3] |= (fname | fcomment)
30 compressed.insert(10, `h`)
31 compressed.insert(11, `i`)
32 compressed.insert(12, 0x00)
33 fn test_gzip_with_fextra() {
34 uncompressed := 'Hello world!'
35 mut compressed := compress(uncompressed.bytes())!
36- compressed[4] |= 0b0000_0100
37+ compressed[3] |= fextra
38 compressed.insert(10, 2)
39 compressed.insert(11, `h`)
40 compressed.insert(12, `i`)
41 fn test_gzip_with_hcrc() {
42 uncompressed := 'Hello world!'
43 mut compressed := compress(uncompressed.bytes())!
44- compressed[4] |= 0b0000_0010
45+ compressed[3] |= fhcrc
46 checksum := crc32.sum(compressed[..10])
47 compressed.insert(10, u8(checksum >> 24))
48 compressed.insert(11, u8(checksum >> 16))
49 fn test_gzip_with_invalid_hcrc() {
50 uncompressed := 'Hello world!'
51 mut compressed := compress(uncompressed.bytes())!
52- compressed[4] |= 0b0000_0010
53+ compressed[3] |= fhcrc
54 checksum := crc32.sum(compressed[..10])
55 compressed.insert(10, u8(checksum >> 24))
56 compressed.insert(11, u8(checksum >> 16))
57 fn test_gzip_with_invalid_flags() {
58 uncompressed := 'Hello world!'
59 mut compressed := compress(uncompressed.bytes())!
60- compressed[4] |= 0b1000_0000
61+ compressed[3] |= 0b1000_0000
62 assert_decompress_error(compressed, 'reserved flags are set, unsupported field detected')!
63 }
64