93 lines
2.0 KiB
V
93 lines
2.0 KiB
V
module lz
|
|
|
|
// compress_lzw compresses data using a pure-V LZW dictionary stream.
|
|
pub fn compress_lzw(data []u8) ![]u8 {
|
|
mut payload := []u8{}
|
|
mut dict := map[string]int{}
|
|
for i in 0 .. 256 {
|
|
dict[[u8(i)].bytestr()] = i
|
|
}
|
|
mut next_code := 256
|
|
mut word := ''
|
|
|
|
for b in data {
|
|
symbol := [b].bytestr()
|
|
candidate := word + symbol
|
|
if candidate in dict {
|
|
word = candidate
|
|
continue
|
|
}
|
|
if word != '' {
|
|
encode_uvarint(mut payload, u64(dict[word]))
|
|
}
|
|
dict[candidate] = next_code
|
|
next_code++
|
|
word = symbol
|
|
}
|
|
|
|
if word != '' {
|
|
encode_uvarint(mut payload, u64(dict[word]))
|
|
}
|
|
|
|
return wrap_payload(.lzw, data, payload)
|
|
}
|
|
|
|
// decompress_lzw decompresses data produced by compress_lzw.
|
|
pub fn decompress_lzw(data []u8) ![]u8 {
|
|
payload, expected_len := unwrap_payload(data, .lzw)!
|
|
if payload.len == 0 {
|
|
if expected_len == i64(0) {
|
|
return []u8{}
|
|
}
|
|
return error('invalid lzw stream: missing codes')
|
|
}
|
|
|
|
mut dict := map[int][]u8{}
|
|
for i in 0 .. 256 {
|
|
dict[i] = [u8(i)]
|
|
}
|
|
mut next_code := 256
|
|
mut pos := 0
|
|
|
|
first_code, next_pos, ok := decode_uvarint(payload, pos)
|
|
if !ok {
|
|
return error('invalid lzw stream: bad initial code')
|
|
}
|
|
pos = next_pos
|
|
if int(first_code) !in dict {
|
|
return error('invalid lzw stream: unknown initial code')
|
|
}
|
|
mut word := dict[int(first_code)].clone()
|
|
mut out := word.clone()
|
|
|
|
for pos < payload.len {
|
|
code_u64, new_pos, ok_code := decode_uvarint(payload, pos)
|
|
if !ok_code {
|
|
return error('invalid lzw stream: bad code')
|
|
}
|
|
pos = new_pos
|
|
code := int(code_u64)
|
|
mut entry := []u8{}
|
|
if code in dict {
|
|
entry = dict[code].clone()
|
|
} else if code == next_code {
|
|
entry = word.clone()
|
|
entry << word[0]
|
|
} else {
|
|
return error('invalid lzw stream: unknown code')
|
|
}
|
|
|
|
out << entry
|
|
mut new_entry := word.clone()
|
|
new_entry << entry[0]
|
|
dict[next_code] = new_entry
|
|
next_code++
|
|
word = entry.clone()
|
|
}
|
|
|
|
if i64(out.len) != expected_len {
|
|
return error('invalid lzw stream: length mismatch')
|
|
}
|
|
return out
|
|
}
|