///|
/// Stateful LZX decoder configuration.
pub struct LzxConfig {
pub window_bits : Int
pub reset_interval : Int
pub max_output : Int
} derive(Show, Eq)
///|
/// Default safe LZX configuration.
pub let default_lzx_config : LzxConfig = {
window_bits: 15,
reset_interval: 32768,
max_output: 512 * 1024 * 1024,
}
///|
/// Stateful LZX decoder.
pub struct LzxDecoder {
pub config : LzxConfig
pub window : LzxWindow
pub mut recent0 : Int
pub mut recent1 : Int
pub mut recent2 : Int
pub mut produced : Int
} derive(Show)
///|
/// Create a stateful LZX decoder.
pub fn LzxDecoder::new(
config? : LzxConfig = default_lzx_config,
) -> LzxDecoder raise MspackError {
if config.reset_interval <= 0 || config.max_output <= 0 {
raise MspackError("invalid LZX decoder configuration")
}
{
config,
window: LzxWindow::new(config.window_bits),
recent0: 1,
recent1: 1,
recent2: 1,
produced: 0,
}
}
///|
/// Reset the decoder window and repeated offsets.
pub fn LzxDecoder::reset(self : LzxDecoder) -> Unit {
self.window.position = 0
self.recent0 = 1
self.recent1 = 1
self.recent2 = 1
self.produced = 0
}
///|
/// Record a newly used match distance in the repeated-offset queue.
pub fn LzxDecoder::remember_distance(
self : LzxDecoder,
distance : Int,
) -> Unit raise MspackError {
if distance <= 0 || distance > self.window.size {
raise MspackError("invalid LZX distance")
}
self.recent2 = self.recent1
self.recent1 = self.recent0
self.recent0 = distance
}
///|
/// Emit one decoded token while updating decoder state.
pub fn LzxDecoder::emit(
self : LzxDecoder,
token : LzxToken,
) -> Unit raise MspackError {
match token {
Literal(byte) => {
if self.produced >= self.config.max_output {
raise MspackError("LZX output exceeds limit")
}
self.window.put(byte)
self.produced = self.produced + 1
}
Match(distance~, length~) => {
if self.produced + length > self.config.max_output {
raise MspackError("LZX output exceeds limit")
}
self.window.copy(distance, length)
self.remember_distance(distance)
self.produced = self.produced + length
}
}
}
///|
/// Return the number of bytes produced by a decoder.
pub fn LzxDecoder::output_size(self : LzxDecoder) -> Int {
self.produced
}
///|
/// Decode a token sequence with persistent repeated-offset state.
pub fn decode_lzx_tokens(
tokens : Array[LzxToken],
config? : LzxConfig = default_lzx_config,
) -> Bytes raise MspackError {
let decoder = LzxDecoder::new(config~)
let output : Array[Byte] = []
for token in tokens {
decoder.emit(token)
match token {
Literal(byte) => output.push(byte)
Match(distance~, length~) => {
let start = decoder.window.position - length
for i in 0..