| // Copyright 2018 The Wuffs Authors. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| use "std/crc32" |
| use "std/deflate" |
| |
| pub status "?bad checksum" |
| pub status "?bad compression method" |
| pub status "?bad encoding flags" |
| pub status "?bad header" |
| |
| // TODO: reference deflate.decoder_workbuf_len_max_incl. |
| pub const decoder_workbuf_len_max_incl base.u64 = 32768 + 512 |
| |
| pub struct decoder?( |
| flate deflate.decoder, |
| checksum crc32.ieee_hasher, |
| ignore_checksum base.bool, |
| util base.utility, |
| ) |
| |
| pub func decoder.set_ignore_checksum!(ic base.bool) { |
| this.ignore_checksum = args.ic |
| } |
| |
| pub func decoder.workbuf_len() base.range_ii_u64 { |
| return this.util.make_range_ii_u64( |
| min_incl:decoder_workbuf_len_max_incl, |
| max_incl:decoder_workbuf_len_max_incl) |
| } |
| |
| pub func decoder.decode_io_writer?(dst base.io_writer, src base.io_reader, workbuf slice base.u8) { |
| var c base.u8 |
| var flags base.u8 |
| var xlen base.u16 |
| var checksum_got base.u32 |
| var decoded_length_got base.u32 |
| var status base.status |
| var checksum_want base.u32 |
| var decoded_length_want base.u32 |
| |
| // Read the header. |
| c = args.src.read_u8?() |
| if c != 0x1F { |
| return "?bad header" |
| } |
| c = args.src.read_u8?() |
| if c != 0x8B { |
| return "?bad header" |
| } |
| c = args.src.read_u8?() |
| if c != 0x08 { |
| return "?bad compression method" |
| } |
| flags = args.src.read_u8?() |
| // TODO: API for returning the header's MTIME field. |
| args.src.skip?(n:6) |
| |
| // Handle FEXTRA. |
| if (flags & 0x04) != 0 { |
| xlen = args.src.read_u16le?() |
| args.src.skip?(n:xlen as base.u32) |
| } |
| |
| // Handle FNAME. |
| // |
| // TODO: API for returning the header's FNAME field. This might require |
| // converting ISO 8859-1 to UTF-8. We may also want to cap the UTF-8 |
| // filename length to NAME_MAX, which is 255. |
| if (flags & 0x08) != 0 { |
| while true { |
| c = args.src.read_u8?() |
| if c == 0 { |
| break |
| } |
| } |
| } |
| |
| // Handle FCOMMENT. |
| if (flags & 0x10) != 0 { |
| while true { |
| c = args.src.read_u8?() |
| if c == 0 { |
| break |
| } |
| } |
| } |
| |
| // Handle FHCRC. |
| if (flags & 0x02) != 0 { |
| args.src.skip?(n:2) |
| } |
| |
| // Reserved flags bits must be zero. |
| if (flags & 0xE0) != 0 { |
| return "?bad encoding flags" |
| } |
| |
| // Decode and checksum the DEFLATE-encoded payload. |
| while true { |
| args.dst.set_mark!() |
| status =? this.flate.decode_io_writer?(dst:args.dst, src:args.src, workbuf:args.workbuf) |
| if not this.ignore_checksum { |
| checksum_got = this.checksum.update!(x:args.dst.since_mark()) |
| decoded_length_got ~mod+= ((args.dst.since_mark().length() & 0xFFFFFFFF) as base.u32) |
| } |
| if status.is_ok() { |
| break |
| } |
| yield status |
| } |
| checksum_want = args.src.read_u32le?() |
| decoded_length_want = args.src.read_u32le?() |
| if (not this.ignore_checksum) and |
| ((checksum_got != checksum_want) or (decoded_length_got != decoded_length_want)) { |
| return "?bad checksum" |
| } |
| } |