mirror of
https://github.com/google/wuffs.git
synced 2026-01-18 17:11:32 +01:00
398 lines
14 KiB
Plaintext
398 lines
14 KiB
Plaintext
// Copyright 2017 The Wuffs Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
|
|
// option. This file may not be copied, modified, or distributed
|
|
// except according to those terms.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
|
|
pub status "#bad code"
|
|
pub status "#truncated input"
|
|
|
|
pri status "#internal error: inconsistent I/O"
|
|
|
|
pub const DECODER_DST_HISTORY_RETAIN_LENGTH_MAX_INCL_WORST_CASE : base.u64 = 0
|
|
|
|
// TODO: move bulk data buffers like decoder.suffixes or decoder.output into
|
|
// the workbuf? The first attempt at this was a performance regression for
|
|
// decoding all but the smallest GIFs. See these git commits for numbers:
|
|
// - 49627b4 Flatten the lzw.decoder.suffixes array
|
|
// - f877fb2 Use the workbuf instead of lzw.decoder.suffixes
|
|
// - 85be5b9 Delete the obsolete lzw.decoder.suffixes array
|
|
// and the roll back has combined numbers:
|
|
// - 3056a84 Roll back 3 recent lzw.decoder.suffixes commits
|
|
pub const DECODER_WORKBUF_LEN_MAX_INCL_WORST_CASE : base.u64 = 0
|
|
|
|
pub struct decoder? implements base.io_transformer(
|
|
// pending_literal_width_plus_one is 1 plus the saved argument passed
|
|
// to set_quirk. This is assigned to the literal_width field at the
|
|
// start of transform_io. During that method, calling set_quirk will
|
|
// change pending_literal_width_plus_one but not literal_width.
|
|
pending_literal_width_plus_one : base.u32[..= 9],
|
|
|
|
// read_from state that does not change during a decode call.
|
|
literal_width : base.u32[..= 8],
|
|
clear_code : base.u32[..= 256],
|
|
end_code : base.u32[..= 257],
|
|
|
|
// read_from state that does change during a decode call.
|
|
save_code : base.u32[..= 4096],
|
|
prev_code : base.u32[..= 4095],
|
|
width : base.u32[..= 12],
|
|
bits : base.u32,
|
|
n_bits : base.u32[..= 31],
|
|
output_ri : base.u32[..= 8191],
|
|
output_wi : base.u32[..= 8191],
|
|
|
|
// read_from return value. The read_from method effectively returns a
|
|
// base.u32 to show how decode should continue after calling write_to. That
|
|
// value needs to be saved across write_to's possible suspension, so we
|
|
// might as well save it explicitly as a decoder field.
|
|
read_from_return_value : base.u32,
|
|
|
|
// read_from per-code state.
|
|
prefixes : array[4096] base.u16[..= 4095],
|
|
|
|
util : base.utility,
|
|
) + (
|
|
// read_from per-code state.
|
|
suffixes : array[4096] array[8] base.u8,
|
|
// lm1s is the "length minus 1"s of the values for the implicit key-value
|
|
// table in this decoder. See std/lzw/README.md for more detail.
|
|
lm1s : array[4096] base.u16,
|
|
|
|
// output[output_ri:output_wi] is the buffered output, connecting read_from
|
|
// with write_to and flush.
|
|
output : array[8192 + 7] base.u8,
|
|
)
|
|
|
|
pub func decoder.get_quirk(key: base.u32) base.u64 {
|
|
if args.key == QUIRK_LITERAL_WIDTH_PLUS_ONE {
|
|
return this.pending_literal_width_plus_one as base.u64
|
|
}
|
|
return 0
|
|
}
|
|
|
|
pub func decoder.set_quirk!(key: base.u32, value: base.u64) base.status {
|
|
if args.key == QUIRK_LITERAL_WIDTH_PLUS_ONE {
|
|
if args.value > 9 {
|
|
return base."#bad argument"
|
|
}
|
|
this.pending_literal_width_plus_one = args.value as base.u32
|
|
return ok
|
|
}
|
|
return base."#unsupported option"
|
|
}
|
|
|
|
pub func decoder.dst_history_retain_length() base.optional_u63 {
|
|
return this.util.make_optional_u63(has_value: true, value: 0)
|
|
}
|
|
|
|
pub func decoder.workbuf_len() base.range_ii_u64 {
|
|
return this.util.make_range_ii_u64(min_incl: 0, max_incl: 0)
|
|
}
|
|
|
|
pub func decoder.transform_io?(dst: base.io_writer, src: base.io_reader, workbuf: slice base.u8) {
|
|
var i : base.u32[..= 8191]
|
|
|
|
// Initialize read_from state.
|
|
this.literal_width = 8
|
|
if this.pending_literal_width_plus_one > 0 {
|
|
this.literal_width = this.pending_literal_width_plus_one - 1
|
|
}
|
|
this.clear_code = (1 as base.u32) << this.literal_width
|
|
this.end_code = this.clear_code + 1
|
|
this.save_code = this.end_code
|
|
this.prev_code = this.end_code
|
|
this.width = this.literal_width + 1
|
|
this.bits = 0
|
|
this.n_bits = 0
|
|
this.output_ri = 0
|
|
this.output_wi = 0
|
|
i = 0
|
|
while i < this.clear_code {
|
|
assert i < 256 via "a < b: a < c; c <= b"(c: this.clear_code)
|
|
this.lm1s[i] = 0
|
|
this.suffixes[i][0] = i as base.u8
|
|
i += 1
|
|
}
|
|
|
|
while true {
|
|
this.read_from!(src: args.src)
|
|
|
|
if this.output_wi > 0 {
|
|
this.write_to?(dst: args.dst)
|
|
}
|
|
|
|
if this.read_from_return_value == 0 {
|
|
break
|
|
} else if this.read_from_return_value == 1 {
|
|
continue
|
|
} else if this.read_from_return_value == 2 {
|
|
yield? base."$short read"
|
|
} else if this.read_from_return_value == 3 {
|
|
return "#truncated input"
|
|
} else if this.read_from_return_value == 4 {
|
|
return "#bad code"
|
|
} else {
|
|
return "#internal error: inconsistent I/O"
|
|
}
|
|
}
|
|
}
|
|
|
|
pri func decoder.read_from!(src: base.io_reader) {
|
|
var clear_code : base.u32[..= 256]
|
|
var end_code : base.u32[..= 257]
|
|
|
|
var save_code : base.u32[..= 4096]
|
|
var prev_code : base.u32[..= 4095]
|
|
var width : base.u32[..= 12]
|
|
var bits : base.u32
|
|
var n_bits : base.u32[..= 31]
|
|
var output_wi : base.u32[..= 8191]
|
|
|
|
var code : base.u32[..= 4095]
|
|
var c : base.u32[..= 4095]
|
|
var o : base.u32[..= 8191]
|
|
var steps : base.u32
|
|
var first_byte : base.u8
|
|
var lm1_b : base.u16[..= 4095]
|
|
var lm1_a : base.u16[..= 4095]
|
|
|
|
clear_code = this.clear_code
|
|
end_code = this.end_code
|
|
|
|
save_code = this.save_code
|
|
prev_code = this.prev_code
|
|
width = this.width
|
|
bits = this.bits
|
|
n_bits = this.n_bits
|
|
output_wi = this.output_wi
|
|
|
|
while true {
|
|
if n_bits < width {
|
|
assert n_bits < 12 via "a < b: a < c; c <= b"(c: width)
|
|
if args.src.length() >= 4 {
|
|
// Read 4 bytes, using the "Variant 4" technique of
|
|
// https://fgiesen.wordpress.com/2018/02/20/reading-bits-in-far-too-many-ways-part-2/
|
|
bits |= args.src.peek_u32le() ~mod<< n_bits
|
|
args.src.skip_u32_fast!(actual: (31 - n_bits) >> 3, worst_case: 3)
|
|
n_bits |= 24
|
|
assert width <= n_bits via "a <= b: a <= c; c <= b"(c: 12)
|
|
assert n_bits >= width via "a >= b: b <= a"()
|
|
} else if args.src.length() <= 0 {
|
|
if args.src.is_closed() {
|
|
this.read_from_return_value = 3
|
|
} else {
|
|
this.read_from_return_value = 2
|
|
}
|
|
break
|
|
} else {
|
|
bits |= args.src.peek_u8_as_u32() << n_bits
|
|
args.src.skip_u32_fast!(actual: 1, worst_case: 1)
|
|
n_bits += 8
|
|
if n_bits >= width {
|
|
// No-op.
|
|
} else if args.src.length() <= 0 {
|
|
if args.src.is_closed() {
|
|
this.read_from_return_value = 3
|
|
} else {
|
|
this.read_from_return_value = 2
|
|
}
|
|
break
|
|
} else {
|
|
bits |= args.src.peek_u8_as_u32() << n_bits
|
|
args.src.skip_u32_fast!(actual: 1, worst_case: 1)
|
|
n_bits += 8
|
|
assert width <= n_bits via "a <= b: a <= c; c <= b"(c: 12)
|
|
assert n_bits >= width via "a >= b: b <= a"()
|
|
|
|
// This if condition is always false, but for some unknown
|
|
// reason, removing it worsens the benchmarks slightly.
|
|
if n_bits < width {
|
|
this.read_from_return_value = 5
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
code = bits.low_bits(n: width)
|
|
bits >>= width
|
|
n_bits -= width
|
|
|
|
if code < clear_code {
|
|
assert code < 256 via "a < b: a < c; c <= b"(c: clear_code)
|
|
this.output[output_wi] = code as base.u8
|
|
output_wi = (output_wi + 1) & 8191
|
|
if save_code <= 4095 {
|
|
lm1_a = (this.lm1s[prev_code] ~mod+ 1) & 4095
|
|
this.lm1s[save_code] = lm1_a
|
|
|
|
if (lm1_a % 8) <> 0 {
|
|
this.prefixes[save_code] = this.prefixes[prev_code]
|
|
this.suffixes[save_code] = this.suffixes[prev_code]
|
|
this.suffixes[save_code][lm1_a % 8] = code as base.u8
|
|
} else {
|
|
this.prefixes[save_code] = prev_code as base.u16
|
|
this.suffixes[save_code][0] = code as base.u8
|
|
}
|
|
|
|
save_code += 1
|
|
if width < 12 {
|
|
width += 1 & (save_code >> width)
|
|
}
|
|
prev_code = code
|
|
}
|
|
|
|
} else if code <= end_code {
|
|
if code == end_code {
|
|
this.read_from_return_value = 0
|
|
break
|
|
}
|
|
save_code = end_code
|
|
prev_code = end_code
|
|
width = this.literal_width + 1
|
|
|
|
} else if code <= save_code {
|
|
c = code
|
|
if code == save_code {
|
|
c = prev_code
|
|
}
|
|
|
|
// Letting old_wi and new_wi denote the values of output_wi before
|
|
// and after these two lines of code, the decoded bytes will be
|
|
// written to output[old_wi:new_wi]. They will be written
|
|
// back-to-front, 8 bytes at a time, starting by writing
|
|
// output[o:o + 8], which will contain output[new_wi - 1].
|
|
//
|
|
// In the special case that code == save_code, the decoded bytes
|
|
// contain an extra copy (at the end) of the first byte, and will
|
|
// be written to output[old_wi:new_wi + 1].
|
|
o = (output_wi + ((this.lm1s[c] as base.u32) & 0xFFFF_FFF8)) & 8191
|
|
output_wi = (output_wi + 1 + (this.lm1s[c] as base.u32)) & 8191
|
|
|
|
steps = (this.lm1s[c] as base.u32) >> 3
|
|
while true {
|
|
assert o <= (o + 8) via "a <= (a + b): 0 <= b"(b: 8)
|
|
|
|
// The final "8" is redundant semantically, but helps the
|
|
// wuffs-c code generator recognize that both slices have the
|
|
// same constant length, and hence produce efficient C code.
|
|
this.output[o .. o + 8].copy_from_slice!(s: this.suffixes[c][.. 8])
|
|
|
|
if steps <= 0 {
|
|
break
|
|
}
|
|
steps -= 1
|
|
|
|
// This line is essentially "o -= 8". The "& 8191" is a no-op
|
|
// in practice, but is necessary for the overflow checker.
|
|
o = (o ~mod- 8) & 8191
|
|
c = this.prefixes[c] as base.u32
|
|
}
|
|
first_byte = this.suffixes[c][0]
|
|
|
|
if code == save_code {
|
|
this.output[output_wi] = first_byte
|
|
output_wi = (output_wi + 1) & 8191
|
|
}
|
|
|
|
if save_code <= 4095 {
|
|
lm1_b = (this.lm1s[prev_code] ~mod+ 1) & 4095
|
|
this.lm1s[save_code] = lm1_b
|
|
|
|
if (lm1_b % 8) <> 0 {
|
|
this.prefixes[save_code] = this.prefixes[prev_code]
|
|
this.suffixes[save_code] = this.suffixes[prev_code]
|
|
this.suffixes[save_code][lm1_b % 8] = first_byte
|
|
} else {
|
|
this.prefixes[save_code] = prev_code as base.u16
|
|
this.suffixes[save_code][0] = first_byte as base.u8
|
|
}
|
|
|
|
save_code += 1
|
|
if width < 12 {
|
|
width += 1 & (save_code >> width)
|
|
}
|
|
prev_code = code
|
|
}
|
|
|
|
} else {
|
|
this.read_from_return_value = 4
|
|
break
|
|
}
|
|
|
|
// Flush the output if it could be too full to contain the entire
|
|
// decoding of the next code. The longest possible decoding is slightly
|
|
// less than 4096 and output's length is 8192, so a conservative
|
|
// threshold is ensuring that output_wi <= 4095.
|
|
if output_wi > 4095 {
|
|
this.read_from_return_value = 1
|
|
break
|
|
}
|
|
}
|
|
|
|
// Rewind args.src, if we're not in "$short read" and we've read too many
|
|
// bits.
|
|
if this.read_from_return_value <> 2 {
|
|
while n_bits >= 8 {
|
|
n_bits -= 8
|
|
if args.src.can_undo_byte() {
|
|
args.src.undo_byte!()
|
|
} else {
|
|
this.read_from_return_value = 5
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
this.save_code = save_code
|
|
this.prev_code = prev_code
|
|
this.width = width
|
|
this.bits = bits
|
|
this.n_bits = n_bits
|
|
this.output_wi = output_wi
|
|
}
|
|
|
|
pri func decoder.write_to?(dst: base.io_writer) {
|
|
var s : slice base.u8
|
|
var n : base.u64
|
|
|
|
while this.output_wi > 0 {
|
|
if this.output_ri > this.output_wi {
|
|
return "#internal error: inconsistent I/O"
|
|
}
|
|
s = this.output[this.output_ri .. this.output_wi]
|
|
n = args.dst.copy_from_slice!(s: s)
|
|
if n == s.length() {
|
|
this.output_ri = 0
|
|
this.output_wi = 0
|
|
return ok
|
|
}
|
|
this.output_ri = (this.output_ri ~mod+ ((n & 0xFFFF_FFFF) as base.u32)) & 8191
|
|
yield? base."$short write"
|
|
}
|
|
}
|
|
|
|
// Deprecated. Use transform_io and an io_writer instead.
|
|
//
|
|
// We'd like to deprecate (and eventually remove) methods that return anything
|
|
// pointer-y (including slices).
|
|
pub func decoder.flush!() roslice base.u8 {
|
|
var ri : base.u32[..= 8191]
|
|
var wi : base.u32[..= 8191]
|
|
|
|
ri = this.output_ri
|
|
wi = this.output_wi
|
|
this.output_ri = 0
|
|
this.output_wi = 0
|
|
|
|
if ri <= wi {
|
|
return this.output[ri .. wi]
|
|
}
|
|
return this.output[.. 0]
|
|
}
|