| // 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. |
| |
| packageid "zlib" |
| |
| use "std/adler32" |
| use "std/deflate" |
| |
| pub error "checksum mismatch" |
| pub error "invalid compression method" |
| pub error "invalid compression window size" |
| pub error "invalid parity check" |
| |
| pub error "TODO: unsupported preset dictionary" |
| |
| pub struct decoder?( |
| flate deflate.decoder, |
| checksum adler32.hasher, |
| ignore_checksum base.bool, |
| ) |
| |
| pub func decoder.set_ignore_checksum!(ic base.bool)() { |
| this.ignore_checksum = in.ic |
| } |
| |
| pub func decoder.decode?(dst base.io_writer, src base.io_reader)() { |
| var x base.u16 = in.src.read_u16be?() |
| if ((x >> 8) & 0x0F) != 0x08 { |
| return error "invalid compression method" |
| } |
| if (x >> 12) > 0x07 { |
| return error "invalid compression window size" |
| } |
| if (x & 0x20) != 0 { |
| return error "TODO: unsupported preset dictionary" |
| } |
| if (x % 31) != 0 { |
| return error "invalid parity check" |
| } |
| |
| // Decode and checksum the DEFLATE-encoded payload. |
| var checksum_got base.u32 |
| while true { |
| in.dst.set_mark!() |
| var z base.status = try this.flate.decode?(dst:in.dst, src:in.src) |
| if not this.ignore_checksum { |
| checksum_got = this.checksum.update!(x:in.dst.since_mark()) |
| } |
| if z.is_ok() { |
| break |
| } |
| yield z |
| } |
| var checksum_want base.u32 = in.src.read_u32be?() |
| if (not this.ignore_checksum) and (checksum_got != checksum_want) { |
| return error "checksum mismatch" |
| } |
| } |