blob: 297b1919fd93044c2fcb3093c3413243dc2593f0 [file] [log] [blame]
// 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_worst_case.
pub const decoder_workbuf_len_max_incl_worst_case base.u64 = 1
pub struct decoder?(
ignore_checksum base.bool,
checksum crc32.ieee_hasher,
flate deflate.decoder,
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_worst_case,
max_incl:decoder_workbuf_len_max_incl_worst_case)
}
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"
}
}