A streaming Zstandard (RFC 8878) decoder in pure MoonBit: arbitrary input chunks, bounded output slices, memory bounded by the window.
Dependencies
moon add gaato/zstd///|
test "decompress a whole stream" {
let compressed = b"\x28\xb5\x2f\xfd\x20\x05\x29\x00\x00\x68\x65\x6c\x6c\x6f" // zstd.compress(b"hello")
let text = @zstd.decompress(compressed, max_output=16 * 1024 * 1024)
assert_eq(text, b"hello")
}///|
fn decode_all(chunks : Array[Bytes], sink : (BytesView) -> Unit) -> Unit raise {
let decoder = @zstd.Decoder(max_window_bits=23) // 8 MiB, as RFC 9659 asks of HTTP
let out = FixedArray::make(64 * 1024, b'\x00')
for i, chunk in chunks {
let mut input = chunk[:]
for ;; {
let status = decoder.step_into(input, out, end=i == chunks.length() - 1)
input = input[decoder.last_consumed():]
sink(Bytes::from_array(out[:decoder.last_produced()]))
match status {
Done => return
NeedMoreInput => break
NeedMoreOutput => ()
}
}
}
}
///|
test "decode a stream chunk by chunk" {
let decoded = Buffer()
decode_all(
[b"\x28\xb5\x2f", b"\xfd\x20\x05\x29\x00\x00\x68", b"\x65\x6c\x6c\x6f"],
bytes => decoded.write_bytes(bytes.to_owned()),
)
assert_eq(decoded.to_bytes(), b"hello")
}///|
test "stop after the first frame" {
let frame = b"\x28\xb5\x2f\xfd\x20\x05\x29\x00\x00\x68\x65\x6c\x6c\x6f"
let input = Bytes::from_array([..frame[:], ..b"rest"])
let decoder = @zstd.Decoder(until=EndOfMember)
let out = FixedArray::make(64, b'\x00')
let status = decoder.step_into(input, out, end=true)
assert_true(status is Done)
assert_eq(Bytes::from_array(out[:decoder.last_produced()]), b"hello")
assert_eq(input[decoder.last_consumed():], b"rest")
}moon add gaato/zstd///|
test "decompress a whole stream" {
let compressed = b"\x28\xb5\x2f\xfd\x20\x05\x29\x00\x00\x68\x65\x6c\x6c\x6f" // zstd.compress(b"hello")
let text = @zstd.decompress(compressed, max_output=16 * 1024 * 1024)
assert_eq(text, b"hello")
}///|
fn decode_all(chunks : Array[Bytes], sink : (BytesView) -> Unit) -> Unit raise {
let decoder = @zstd.Decoder(max_window_bits=23) // 8 MiB, as RFC 9659 asks of HTTP
let out = FixedArray::make(64 * 1024, b'\x00')
for i, chunk in chunks {
let mut input = chunk[:]
for ;; {
let status = decoder.step_into(input, out, end=i == chunks.length() - 1)
input = input[decoder.last_consumed():]
sink(Bytes::from_array(out[:decoder.last_produced()]))
match status {
Done => return
NeedMoreInput => break
NeedMoreOutput => ()
}
}
}
}
///|
test "decode a stream chunk by chunk" {
let decoded = Buffer()
decode_all(
[b"\x28\xb5\x2f", b"\xfd\x20\x05\x29\x00\x00\x68", b"\x65\x6c\x6c\x6f"],
bytes => decoded.write_bytes(bytes.to_owned()),
)
assert_eq(decoded.to_bytes(), b"hello")
}///|
test "stop after the first frame" {
let frame = b"\x28\xb5\x2f\xfd\x20\x05\x29\x00\x00\x68\x65\x6c\x6c\x6f"
let input = Bytes::from_array([..frame[:], ..b"rest"])
let decoder = @zstd.Decoder(until=EndOfMember)
let out = FixedArray::make(64, b'\x00')
let status = decoder.step_into(input, out, end=true)
assert_true(status is Done)
assert_eq(Bytes::from_array(out[:decoder.last_produced()]), b"hello")
assert_eq(input[decoder.last_consumed():], b"rest")
}pub struct Decoder {
// private fields
}fn Decoder::step(self : Decoder, input : BytesView, output : MutArrayView[Byte], end~ : Bool) -> Status raise CodecErrorfn Decoder::step_into(self : Decoder, input : BytesView, output : FixedArray[Byte], end~ : Bool) -> Status raise CodecErrorfn decompress(input : BytesView, max_output? : Int, max_window_bits? : Int) -> Bytes raise CodecErrorInstall
Download zipA streaming Zstandard (RFC 8878) decoder in pure MoonBit: arbitrary input chunks, bounded output slices, memory bounded by the window.
Dependencies