| // Copyright 2017 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/adler32" |
| use "std/deflate" |
| |
| pub status "@dictionary required" |
| |
| pub status "#bad checksum" |
| pub status "#bad compression method" |
| pub status "#bad compression window size" |
| pub status "#bad parity check" |
| pub status "#incorrect dictionary" |
| |
| // 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? implements base.io_transformer( |
| bad_call_sequence : base.bool, |
| header_complete : base.bool, |
| |
| got_dictionary : base.bool, |
| want_dictionary : base.bool, |
| |
| ignore_checksum : base.bool, |
| checksum : adler32.hasher, |
| |
| dict_id_hasher : adler32.hasher, |
| dict_id_got : base.u32, |
| dict_id_want : base.u32, |
| |
| flate : deflate.decoder, |
| |
| util : base.utility, |
| ) |
| |
| pub func decoder.dictionary_id() base.u32 { |
| return this.dict_id_want |
| } |
| |
| pub func decoder.add_dictionary!(dict: slice base.u8) { |
| if this.header_complete { |
| this.bad_call_sequence = true |
| } else { |
| this.dict_id_got = this.dict_id_hasher.update_u32!(x: args.dict) |
| this.flate.add_history!(hist: args.dict) |
| } |
| this.got_dictionary = true |
| } |
| |
| pub func decoder.set_ignore_checksum!(ic: base.bool) { |
| this.ignore_checksum = args.ic |
| } |
| |
| pub func decoder.set_quirk_enabled!(quirk: base.u32, enabled: base.bool) { |
| } |
| |
| 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.transform_io?(dst: base.io_writer, src: base.io_reader, workbuf: slice base.u8) { |
| var x : base.u16 |
| var checksum_got : base.u32 |
| var status : base.status |
| var checksum_want : base.u32 |
| var mark : base.u64 |
| |
| if this.bad_call_sequence { |
| return base."#bad call sequence" |
| } else if not this.want_dictionary { |
| x = args.src.read_u16be?() |
| if ((x >> 8) & 0x0F) <> 0x08 { |
| return "#bad compression method" |
| } |
| if (x >> 12) > 0x07 { |
| return "#bad compression window size" |
| } |
| if (x % 31) <> 0 { |
| return "#bad parity check" |
| } |
| this.want_dictionary = (x & 0x20) <> 0 |
| if this.want_dictionary { |
| this.dict_id_got = 1 // Adler-32 initial value. |
| this.dict_id_want = args.src.read_u32be?() |
| return "@dictionary required" |
| } else if this.got_dictionary { |
| return "#incorrect dictionary" |
| } |
| } else if this.dict_id_got <> this.dict_id_want { |
| if this.got_dictionary { |
| return "#incorrect dictionary" |
| } |
| return "@dictionary required" |
| } |
| |
| this.header_complete = true |
| |
| // Decode and checksum the DEFLATE-encoded payload. |
| while true { |
| mark = args.dst.mark() |
| status =? this.flate.transform_io?(dst: args.dst, src: args.src, workbuf: args.workbuf) |
| if not this.ignore_checksum { |
| checksum_got = this.checksum.update_u32!(x: args.dst.since(mark: mark)) |
| } |
| if status.is_ok() { |
| break |
| } |
| yield? status |
| } endwhile |
| checksum_want = args.src.read_u32be?() |
| if (not this.ignore_checksum) and (checksum_got <> checksum_want) { |
| return "#bad checksum" |
| } |
| |
| // TODO: reset state (e.g. want_dictionary), so that we can read concat'ed |
| // zlib streams? |
| } |