// The encoder's bit writer: bits are packed least significant first (RFC
// 7932 section 2) into a byte buffer that the encoder hands out.

///|
priv struct BitPacker {
  mut buf : FixedArray[Byte]
  // Complete bytes in `buf`.
  mut len : Int32
  // `nbits` (below 8 between writes) bits not yet in `buf`.
  mut acc : UInt64
  mut nbits : Int32
}

///|
fn BitPacker::new() -> BitPacker {
  { buf: FixedArray::make(0, b'\x00'), len: 0, acc: 0, nbits: 0, }
}

///|
/// Makes room for `n` more bytes.
fn BitPacker::reserve(self : BitPacker, n : Int32) -> Unit {
  let need = self.len + n
  guard need > self.buf.length() else { return }
  let grown = FixedArray::make(need.max(self.buf.length() * 2), b'\x00')
  FixedArray::unsafe_blit(grown, 0, self.buf, 0, self.len)
  self.buf = grown
}

///|
/// Writes the low `n` (at most 32) bits of `value`.
fn BitPacker::write(self : BitPacker, n : Int32, value : Int32) -> Unit {
  self.acc = self.acc | (value.reinterpret_as_uint().to_uint64() << self.nbits)
  self.nbits += n
  if self.nbits >= 8 {
    self.reserve(5)
    while self.nbits >= 8 {
      self.buf[self.len] = self.acc.to_byte()
      self.len += 1
      self.acc = self.acc >> 8
      self.nbits -= 8
    }
  }
}

///|
/// Pads with zero bits to the next byte boundary.
fn BitPacker::align(self : BitPacker) -> Unit {
  if self.nbits > 0 {
    self.write(8 - self.nbits, 0)
  }
}

///|
/// Appends `n` bytes of `data` from `start`; the writer is byte-aligned.
fn BitPacker::write_bytes(
  self : BitPacker,
  data : FixedArray[Byte],
  start : Int32,
  n : Int32,
) -> Unit {
  self.reserve(n)
  FixedArray::unsafe_blit(self.buf, self.len, data, start, n)
  self.len += n
}

///|
/// Bits written since the buffer was last emptied.
fn BitPacker::position(self : BitPacker) -> Int32 {
  self.len * 8 + self.nbits
}

///|
/// Goes back to `position`, which is at most one partial byte before the
/// buffer's end at the time: those bits are kept in `acc`.
fn BitPacker::rewind(self : BitPacker, position : Int32, acc : UInt64) -> Unit {
  self.len = position / 8
  self.nbits = position % 8
  self.acc = acc
}

///|
/// Empties the buffer of complete bytes, keeping the partial byte.
fn BitPacker::clear(self : BitPacker) -> Unit {
  self.len = 0
}