///|
/// Appends one little-endian `u32` to an archive under construction.
fn append_u32_le(out : Array[Byte], value : UInt) -> Unit {
  out.push((value & 0xffU).to_byte())
  out.push(((value >> 8) & 0xffU).to_byte())
  out.push(((value >> 16) & 0xffU).to_byte())
  out.push(((value >> 24) & 0xffU).to_byte())
}

///|
/// Appends one little-endian `i32` by preserving its two's-complement bits.
fn append_i32_le(out : Array[Byte], value : Int) -> Unit {
  append_u32_le(out, value.reinterpret_as_uint())
}

///|
/// Pads an archive to an alignment boundary using rkyv's zero padding.
fn pad_to(out : Array[Byte], alignment : Int) -> Unit {
  while out.length() % alignment != 0 {
    out.push(b'\x00')
  }
}

///|
/// Encodes a root `Vec` in rkyv's default format. The element data is
/// emitted before the `ArchivedVec` root header, as required by rkyv.
pub fn encode_vec_u32(values : Array[UInt]) -> Bytes {
  let out : Array[Byte] = []
  for value in values {
    append_u32_le(out, value)
  }
  pad_to(out, 4)
  let header_offset = out.length()
  append_i32_le(out, -header_offset)
  append_u32_le(out, values.length().reinterpret_as_uint())
  Bytes::from_array(out)
}

///|
/// Encodes a root `String` in rkyv 0.8's default format. Strings up to eight
/// UTF-8 bytes are inline; longer strings are written before their root repr.
pub fn encode_string(value : String) -> Bytes {
  let encoded = @utf8.encode(value)
  let length = encoded.length()
  let out : Array[Byte] = []
  if length <= 8 {
    for index in 0..