Module refinery.lib.fast.lzfse
Pure-Python implementation of the LZFSE and LZVN decompression algorithms used by Apple. The implementation is designed for readability and Cython compatibility: all hot-path functions are module-level with simple typed parameters, lookup tables are plain lists, and no Python objects are allocated in inner loops.
Reference: https://github.com/lzfse/lzfse
Expand source code Browse git
"""
Pure-Python implementation of the LZFSE and LZVN decompression algorithms used by Apple. The
implementation is designed for readability and Cython compatibility: all hot-path functions
are module-level with simple typed parameters, lookup tables are plain lists, and no Python
objects are allocated in inner loops.
Reference: https://github.com/lzfse/lzfse
"""
from __future__ import annotations
from refinery.lib.exceptions import RefineryPartialResult
from refinery.lib.fast.lzfse_header import BlockHeader, BlockKind, read_block_header
_ENCODE_L_SYMBOLS: int = 20
_ENCODE_M_SYMBOLS: int = 20
_ENCODE_D_SYMBOLS: int = 64
_ENCODE_LITERAL_SYMBOLS: int = 256
_ENCODE_L_STATES: int = 64
_ENCODE_M_STATES: int = 64
_ENCODE_D_STATES: int = 256
_ENCODE_LITERAL_STATES: int = 1024
_L_EXTRA_BITS: list[int] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 8]
_L_BASE_VALUE: list[int] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 28, 60]
_M_EXTRA_BITS: list[int] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 8, 11]
_M_BASE_VALUE: list[int] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 24, 56, 312]
_D_EXTRA_BITS: list[int] = [
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7,
8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11,
12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15,
]
_D_BASE_VALUE: list[int] = [
0, 1, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24, 28, 36, 44, 52,
60, 76, 92, 108, 124, 156, 188, 220, 252, 316, 380, 444, 508, 636, 764, 892,
1020, 1276, 1532, 1788, 2044, 2556, 3068, 3580, 4092, 5116, 6140, 7164,
8188, 10236, 12284, 14332, 16380, 20476, 24572, 28668, 32764, 40956,
49148, 57340, 65532, 81916, 98300, 114684, 131068, 163836, 196604, 229372,
]
_LZVN_EOS = 0
_LZVN_NOP = 1
_LZVN_UDEF = 2
_LZVN_SML_D = 3
_LZVN_MED_D = 4
_LZVN_LRG_D = 5
_LZVN_PRE_D = 6
_LZVN_SML_L = 7
_LZVN_LRG_L = 8
_LZVN_SML_M = 9
_LZVN_LRG_M = 10
_LZVN_OPC_TABLE: list[int] = [
3, 3, 3, 3, 3, 3, 0, 5, 3, 3, 3, 3, 3, 3, 1, 5,
3, 3, 3, 3, 3, 3, 1, 5, 3, 3, 3, 3, 3, 3, 2, 5,
3, 3, 3, 3, 3, 3, 2, 5, 3, 3, 3, 3, 3, 3, 2, 5,
3, 3, 3, 3, 3, 3, 2, 5, 3, 3, 3, 3, 3, 3, 2, 5,
3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 3, 3, 3, 3, 6, 5,
3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 3, 3, 3, 3, 6, 5,
3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 3, 3, 3, 3, 6, 5,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 3, 3, 3, 3, 6, 5,
3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 3, 3, 3, 3, 6, 5,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 3, 3, 3, 3, 6, 5,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
8, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
]
_LZVN_INSTRUCTION_SIZE: list[int] = [1, 1, 1, 2, 3, 3, 1, 1, 2, 1, 2]
_FREQ_NBITS_TABLE: list[int] = [
2, 3, 2, 5, 2, 3, 2, 8, 2, 3, 2, 5, 2, 3, 2, 14,
2, 3, 2, 5, 2, 3, 2, 8, 2, 3, 2, 5, 2, 3, 2, 14,
]
_FREQ_VALUE_TABLE: list[int] = [
0, 2, 1, 4, 0, 3, 1, -1, 0, 2, 1, 5, 0, 3, 1, -1,
0, 2, 1, 6, 0, 3, 1, -1, 0, 2, 1, 7, 0, 3, 1, -1,
]
def _clz32(x: int) -> int:
"""
Count leading zeros of a 32-bit integer.
"""
if x == 0:
return 32
return 32 - x.bit_length()
def _fse_in_load(state: list, pos: int) -> int:
"""
Read 8 bytes at `pos` as LE uint64; bytes outside the payload of the stream read as zero.
"""
lo = max(pos, state[4])
hi = min(pos + 8, state[5])
if lo >= hi:
return 0
return int.from_bytes(state[0][lo:hi], 'little') << (8 * (lo - pos))
def _fse_in_init(buf: memoryview, start: int, end: int, nbits: int) -> list:
"""
Initialise a backward bit-stream reader for the 64-bit FSE decoding path over the payload from
`start` to `end`.
"""
state = [buf, 0, 0, 0, start, end]
if nbits:
state[1] = end - 8
state[2] = _fse_in_load(state, end - 8)
state[3] = nbits + 64
else:
state[1] = end - 7
state[2] = _fse_in_load(state, end - 7) & 0xFFFFFFFFFFFFFF
state[3] = 56
if state[2] >> state[3]:
raise ValueError('the unused bits of an LZFSE bit stream are not zero')
return state
def _fse_in_remaining(state: list) -> int:
"""
The number of payload bits that were not consumed yet; it is negative when bits from before the
start of the payload were consumed.
"""
return state[3] + 8 * (state[1] - state[4])
def _fse_in_flush(state: list) -> None:
"""
Refill the accumulator (64-bit path).
Matches fse_in_checked_flush64 from the C reference: left-shift the
existing accumulator, then OR in new bits at the bottom.
"""
if _fse_in_remaining(state) < 0:
raise ValueError('an LZFSE bit stream ends before its last symbol')
accum_nbits = state[3]
nbits = (63 - accum_nbits) & ~7
if nbits <= 0:
return
buf_pos = state[1] - (nbits >> 3)
state[1] = buf_pos
incoming = _fse_in_load(state, buf_pos)
state[2] = (state[2] << nbits) | (incoming & ((1 << nbits) - 1))
state[3] = accum_nbits + nbits
def _fse_in_pull(state: list, n: int) -> int:
"""
Pull n bits from the top of the accumulator.
"""
state[3] -= n
result = state[2] >> state[3]
state[2] &= (1 << state[3]) - 1
return result
def _fse_init_decoder_table(
nstates: int,
nsymbols: int,
freq: list[int],
) -> list[int]:
"""
Build a literal FSE decoder table.
Each entry is packed as: (k << 0) | (symbol << 8) | (delta << 16)
where k is the number of bits to consume and delta is the signed state
increment (stored as a 16-bit signed integer in the upper 16 bits).
Returns a list of nstates packed int32 entries.
"""
n_clz = _clz32(nstates)
table: list[int] = [0] * nstates
idx = 0
for symbol in range(nsymbols):
f = freq[symbol]
if f == 0:
continue
k = _clz32(f) - n_clz
j0 = ((2 * nstates) >> k) - f
for j in range(f):
if j < j0:
bits = k
delta = ((f + j) << k) - nstates
else:
bits = k - 1
delta = (j - j0) << (k - 1)
table[idx] = bits | (symbol << 8) | (delta << 16)
idx += 1
return table
def _fse_init_value_decoder_table(
nstates: int,
nsymbols: int,
freq: list[int],
symbol_vbits: list[int],
symbol_vbase: list[int],
) -> list[list[int]]:
"""
Build a value FSE decoder table (for L, M, or D).
Each entry is [total_bits, value_bits, delta, vbase].
Returns a list of nstates entries.
"""
n_clz = _clz32(nstates)
table: list[list[int]] = []
for symbol in range(nsymbols):
f = freq[symbol]
if f == 0:
continue
k = _clz32(f) - n_clz
j0 = ((2 * nstates) >> k) - f
vbits = symbol_vbits[symbol]
vbase = symbol_vbase[symbol]
for j in range(f):
if j < j0:
total = k + vbits
delta = ((f + j) << k) - nstates
else:
total = (k - 1) + vbits
delta = (j - j0) << (k - 1)
table.append([total, vbits, delta, vbase])
return table
def _decode_v2_freq_table(buf: memoryview, pos: int, end: int) -> list[int]:
"""
Decode the variable-length compressed frequency table from a V2 header, which must consume the
bytes from `pos` to `end`.
Returns a list of 360 frequency values (L:20 + M:20 + D:64 + literal:256).
"""
total_freq_count = _ENCODE_L_SYMBOLS + _ENCODE_M_SYMBOLS + _ENCODE_D_SYMBOLS + _ENCODE_LITERAL_SYMBOLS
accum = 0
accum_nbits = 0
freqs: list[int] = []
while len(freqs) < total_freq_count:
while accum_nbits < 14 and pos < end:
accum |= buf[pos] << accum_nbits
pos += 1
accum_nbits += 8
lookup = accum & 0x1F
nbits = _FREQ_NBITS_TABLE[lookup]
if nbits > accum_nbits:
raise ValueError('an LZFSE frequency table ends before its last value')
if nbits == 8:
freqs.append(8 + ((accum >> 4) & 0xF))
elif nbits == 14:
freqs.append(24 + ((accum >> 4) & 0x3FF))
else:
freqs.append(_FREQ_VALUE_TABLE[lookup])
accum >>= nbits
accum_nbits -= nbits
if pos < end or accum_nbits >= 8:
raise ValueError('an LZFSE frequency table does not fill its header')
return freqs
def _decode_lzfse_block(buf: memoryview, header: BlockHeader, output: bytearray) -> None:
"""
Decode an LZFSE V1 or V2 block and append the decompressed data to output.
"""
if header.kind is BlockKind.LZFSE_V2:
freqs = _decode_v2_freq_table(buf, header.freq_table, header.payload)
else:
freqs = list(header.freqs)
i = 0
l_freq = freqs[i:(i := i + _ENCODE_L_SYMBOLS)]
m_freq = freqs[i:(i := i + _ENCODE_M_SYMBOLS)]
d_freq = freqs[i:(i := i + _ENCODE_D_SYMBOLS)]
literal_freq = freqs[i:i + _ENCODE_LITERAL_SYMBOLS]
if (
sum(l_freq) != _ENCODE_L_STATES
or sum(m_freq) != _ENCODE_M_STATES
or sum(d_freq) != _ENCODE_D_STATES
or sum(literal_freq) != _ENCODE_LITERAL_STATES
):
raise ValueError('an LZFSE frequency table does not sum to its number of states')
literal_state0, literal_state1, literal_state2, literal_state3 = header.literal_states
_decode_lzfse_payload(
buf,
header.payload,
header.literal_payload_size,
header.lmd_payload_size,
header.raw_size,
header.n_literals,
header.n_matches,
header.literal_bits,
literal_state0,
literal_state1,
literal_state2,
literal_state3,
header.lmd_bits,
header.l_state,
header.m_state,
header.d_state,
l_freq,
m_freq,
d_freq,
literal_freq,
output,
)
def _decode_lzfse_payload(
buf: memoryview,
payload: int,
literal_payload_size: int,
lmd_payload_size: int,
raw_size: int,
n_literals: int,
n_matches: int,
literal_bits: int,
literal_state0: int,
literal_state1: int,
literal_state2: int,
literal_state3: int,
lmd_bits: int,
l_state: int,
m_state: int,
d_state: int,
l_freq: list[int],
m_freq: list[int],
d_freq: list[int],
literal_freq: list[int],
output: bytearray,
) -> None:
"""
Decode the FSE-encoded literal and LMD payloads, apply LZ77, append to output.
"""
lit_table = _fse_init_decoder_table(_ENCODE_LITERAL_STATES, _ENCODE_LITERAL_SYMBOLS, literal_freq)
l_table = _fse_init_value_decoder_table(_ENCODE_L_STATES, _ENCODE_L_SYMBOLS, l_freq, _L_EXTRA_BITS, _L_BASE_VALUE)
m_table = _fse_init_value_decoder_table(_ENCODE_M_STATES, _ENCODE_M_SYMBOLS, m_freq, _M_EXTRA_BITS, _M_BASE_VALUE)
d_table = _fse_init_value_decoder_table(_ENCODE_D_STATES, _ENCODE_D_SYMBOLS, d_freq, _D_EXTRA_BITS, _D_BASE_VALUE)
lmd_payload = payload + literal_payload_size
lit_stream = _fse_in_init(buf, payload, lmd_payload, literal_bits)
literals = bytearray(n_literals)
s0 = literal_state0
s1 = literal_state1
s2 = literal_state2
s3 = literal_state3
i = 0
while i + 3 < n_literals:
_fse_in_flush(lit_stream)
entry = lit_table[s0]
k = entry & 0xFF
sym = (entry >> 8) & 0xFF
delta = entry >> 16
literals[i] = sym
s0 = delta + _fse_in_pull(lit_stream, k)
entry = lit_table[s1]
k = entry & 0xFF
sym = (entry >> 8) & 0xFF
delta = entry >> 16
literals[i + 1] = sym
s1 = delta + _fse_in_pull(lit_stream, k)
entry = lit_table[s2]
k = entry & 0xFF
sym = (entry >> 8) & 0xFF
delta = entry >> 16
literals[i + 2] = sym
s2 = delta + _fse_in_pull(lit_stream, k)
entry = lit_table[s3]
k = entry & 0xFF
sym = (entry >> 8) & 0xFF
delta = entry >> 16
literals[i + 3] = sym
s3 = delta + _fse_in_pull(lit_stream, k)
i += 4
if _fse_in_remaining(lit_stream) != 0:
raise ValueError('an LZFSE literal stream does not end with its last literal')
lmd_stream = _fse_in_init(buf, lmd_payload, lmd_payload + lmd_payload_size, lmd_bits)
lit_pos = 0
prev_d = -1
block_start = len(output)
for _ in range(n_matches):
_fse_in_flush(lmd_stream)
le = l_table[l_state]
total_bits = le[0]
value_bits = le[1]
l_delta = le[2]
l_vbase = le[3]
bits = _fse_in_pull(lmd_stream, total_bits)
l_state = l_delta + (bits >> value_bits)
l_value = l_vbase + (bits & ((1 << value_bits) - 1)) if value_bits else l_vbase
me = m_table[m_state]
total_bits = me[0]
value_bits = me[1]
m_delta = me[2]
m_vbase = me[3]
bits = _fse_in_pull(lmd_stream, total_bits)
m_state = m_delta + (bits >> value_bits)
m_value = m_vbase + (bits & ((1 << value_bits) - 1)) if value_bits else m_vbase
de = d_table[d_state]
total_bits = de[0]
value_bits = de[1]
d_delta = de[2]
d_vbase = de[3]
bits = _fse_in_pull(lmd_stream, total_bits)
d_state = d_delta + (bits >> value_bits)
d_value = d_vbase + (bits & ((1 << value_bits) - 1)) if value_bits else d_vbase
if d_value == 0:
d_value = prev_d
else:
prev_d = d_value
if lit_pos + l_value > n_literals:
raise ValueError('an LZFSE match uses more literals than its block has')
if len(output) - block_start + l_value + m_value > raw_size:
raise ValueError('an LZFSE block decodes to more than the size in its header')
if m_value > 0 and not 0 < d_value <= len(output) + l_value:
raise ValueError('an LZFSE match starts before the start of the output')
if l_value > 0:
output.extend(literals[lit_pos:lit_pos + l_value])
lit_pos += l_value
if m_value > 0:
_copy_match(output, d_value, m_value)
if _fse_in_remaining(lmd_stream) != 64 or any(buf[lmd_payload:lmd_payload + 8]):
raise ValueError('an LZFSE match stream does not end with its eight bytes of zero padding')
if len(output) - block_start != raw_size:
raise ValueError('an LZFSE block decodes to less than the size in its header')
def _copy_match(output: bytearray, distance: int, length: int) -> None:
"""
Append `length` bytes that repeat the output from `distance` bytes before its end.
"""
start = len(output) - distance
if distance >= length:
output.extend(output[start:start + length])
else:
for k in range(start, start + length):
output.append(output[k])
def _lzvn_decode(
src: memoryview,
src_pos: int,
src_end: int,
n_raw_bytes: int,
output: bytearray,
) -> None:
"""
Decode the LZVN payload of a block up to its end-of-stream instruction, which must append
exactly `n_raw_bytes` bytes to the output.
"""
dst_end = len(output) + n_raw_bytes
D = 0
while True:
if src_pos >= src_end:
raise ValueError('an LZVN block ends before its end-of-stream instruction')
opc = src[src_pos]
kind = _LZVN_OPC_TABLE[opc]
if kind == _LZVN_EOS:
break
if kind == _LZVN_NOP:
src_pos += 1
continue
if kind == _LZVN_UDEF:
raise ValueError(F'an LZVN block contains the undefined instruction 0x{opc:02X}')
size = _LZVN_INSTRUCTION_SIZE[kind]
if src_pos + size > src_end:
raise ValueError('an LZVN block ends inside an instruction')
L = opc >> 6
M = ((opc >> 3) & 7) + 3
if kind == _LZVN_SML_D:
D = ((opc & 7) << 8) | src[src_pos + 1]
elif kind == _LZVN_MED_D:
L = (opc >> 3) & 3
M = (((opc & 7) << 2) | (src[src_pos + 1] & 3)) + 3
D = (src[src_pos + 1] >> 2) | (src[src_pos + 2] << 6)
elif kind == _LZVN_LRG_D:
D = src[src_pos + 1] | (src[src_pos + 2] << 8)
elif kind == _LZVN_SML_L:
L, M = opc & 0x0F, 0
elif kind == _LZVN_LRG_L:
L, M = src[src_pos + 1] + 16, 0
elif kind == _LZVN_SML_M:
L, M = 0, opc & 0x0F
elif kind == _LZVN_LRG_M:
L, M = 0, src[src_pos + 1] + 16
src_pos += size
if len(output) + L + M > dst_end:
raise ValueError('an LZVN block decodes to more than the size in its header')
if L:
if src_pos + L > src_end:
raise ValueError('an LZVN block ends inside a run of literals')
output.extend(src[src_pos:src_pos + L])
src_pos += L
if M:
if not 0 < D <= len(output):
raise ValueError(F'an LZVN match distance of {D} is outside the output of size {len(output)}')
_copy_match(output, D, M)
if len(output) != dst_end:
raise ValueError('an LZVN block decodes to less than the size in its header')
def lzfse_decompress(src: bytes | bytearray | memoryview) -> bytearray:
"""
Decompress an LZFSE stream, which ends with an end-of-stream block. Input that ends before that
block, or that cannot be decoded after some output, raises a `RefineryPartialResult` with the
output of the blocks decoded so far.
"""
view = memoryview(src)
output = bytearray()
decoded = 0
pos = 0
try:
while (header := read_block_header(view, pos)).kind is not BlockKind.END:
if header.kind is BlockKind.RAW:
output.extend(view[header.payload:header.end])
elif header.kind is BlockKind.LZVN:
_lzvn_decode(view, header.payload, header.end, header.raw_size, output)
else:
_decode_lzfse_block(view, header, output)
decoded = len(output)
pos = header.end
except ValueError as error:
if not decoded:
raise
raise RefineryPartialResult(F'Decoding failed after {decoded} bytes: {error!s}', output[:decoded]) from error
return output
Functions
def lzfse_decompress(src)-
Decompress an LZFSE stream, which ends with an end-of-stream block. Input that ends before that block, or that cannot be decoded after some output, raises a
RefineryPartialResultwith the output of the blocks decoded so far.Expand source code Browse git
def lzfse_decompress(src: bytes | bytearray | memoryview) -> bytearray: """ Decompress an LZFSE stream, which ends with an end-of-stream block. Input that ends before that block, or that cannot be decoded after some output, raises a `RefineryPartialResult` with the output of the blocks decoded so far. """ view = memoryview(src) output = bytearray() decoded = 0 pos = 0 try: while (header := read_block_header(view, pos)).kind is not BlockKind.END: if header.kind is BlockKind.RAW: output.extend(view[header.payload:header.end]) elif header.kind is BlockKind.LZVN: _lzvn_decode(view, header.payload, header.end, header.raw_size, output) else: _decode_lzfse_block(view, header, output) decoded = len(output) pos = header.end except ValueError as error: if not decoded: raise raise RefineryPartialResult(F'Decoding failed after {decoded} bytes: {error!s}', output[:decoded]) from error return output