blob: f878c5ea4dc6ea3922d639097aecc8ef485df819 [file] [log] [blame]
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
// TODO: some mechanism for fixing the error code's value, such as a trailing
// "= 100", to maintain API stability.
pub error "LZW code is out of range"
pub error "LZW prefix chain is cyclical"
pub struct lzw_decoder?(
literal_width u32[2..8] = 8,
stack[4096] u8,
suffixes[4096] u8,
prefixes[4096] u16[..4095],
)
pub func lzw_decoder.set_literal_width(lw u32[2..8] = 8)() {
this.literal_width = in.lw
}
pub func lzw_decoder.decode?(dst ptr buf1, src ptr buf1, src_final bool)() {
// These variables don't change over the lifetime of this func.
var clear_code u32[4..256] = (1 as u32) << this.literal_width
var end_code u32[5..257] = clear_code + 1
// These variables do change.
var use_save_code bool
var save_code u32[..4095] = end_code
var prev_code u32[..4095]
var width u32[..12] = this.literal_width + 1
// These variables yield src's bits in Least Significant Bits order.
var bits u32
var n_bits u32
while true,
pre n_bits < 8,
{
assert n_bits < (width + 8) via "a < (b + c): a < c; 0 <= b"()
while n_bits < width,
inv n_bits < (width + 8),
post n_bits >= width,
{
assert n_bits < 12 via "a < b: a < c; c <= b"(c:width)
bits |= (in.src.read_u8?() as u32) << n_bits // TODO: read_u8 should take src_final.
n_bits += 8
}
var code u32[..4095] = 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)
in.dst.write_u8?(x:code as u8)
if use_save_code {
this.suffixes[save_code] = code as u8
this.prefixes[save_code] = prev_code as u16
}
} else if code == clear_code {
use_save_code = false
save_code = end_code
prev_code = 0
width = this.literal_width + 1
continue
} else if code == end_code {
return
} else if code <= save_code {
var s u32[..4095] = 4095
var c u32[..4095] = code
if (code == save_code) and use_save_code {
s -= 1
c = prev_code
}
while c >= clear_code,
inv n_bits < 8,
post c < 256 via "a < b: a < c; c <= b"(c:clear_code),
{
this.stack[s] = this.suffixes[c]
if s == 0 {
return error "LZW prefix chain is cyclical"
}
s -= 1
c = this.prefixes[c] as u32
}
this.stack[s] = c as u8
if (code == save_code) and use_save_code {
this.stack[4095] = c as u8
}
// TODO: the "x:s" arg is a placeholder, and should be something
// like (this.stack[s:]), which means defining a slice type (and
// type checking, bounds checking and code generation for it).
in.dst.write?(x:s)
if use_save_code {
this.suffixes[save_code] = c as u8
this.prefixes[save_code] = prev_code as u16
}
} else {
return error "LZW code is out of range"
}
use_save_code = save_code < 4095
if use_save_code {
save_code += 1
if (save_code == ((1 as u32) << width)) and (width < 12) {
width += 1
}
}
prev_code = code
}
}