// Bit readers over a buffered block. FSE and Huffman streams are read
// backwards from their end mark (RFC 8878 section 4.1); table descriptions
// are read forwards. Both read little-endian values.

///|
/// `len` bits of `data` starting at bit `lo` from `start`, least significant
/// first. `len` is at most 24 and may be zero; the caller keeps every bit it
/// reads inside the stream.
fn bits_at(
  data : FixedArray[Byte],
  start : Int32,
  lo : Int32,
  len : Int32,
) -> Int32 {
  guard len > 0 else { return 0 }
  let i = start + (lo >> 3)
  let shift = lo & 7
  let mut v = data[i].to_uint()
  let last = start + ((lo + len - 1) >> 3)
  if last > i {
    v = v | (data[i + 1].to_uint() << 8)
    if last > i + 1 {
      v = v | (data[i + 2].to_uint() << 16)
      if last > i + 2 {
        v = v | (data[i + 3].to_uint() << 24)
      }
    }
  }
  ((v >> shift) & ((1U << len) - 1)).reinterpret_as_int()
}

///|
/// A stream read backwards: `pos` bits are left before the read position.
/// Reads past the start give zero bits and make `pos` negative, which the
/// caller treats as corruption once the stream should be used up.
priv struct BackwardBits {
  data : FixedArray[Byte]
  start : Int32
  mut pos : Int32
}

///|
/// The stream in `data[start:end]`, positioned after its end mark: the
/// highest set bit of the last byte.
fn BackwardBits::new(
  data : FixedArray[Byte],
  start : Int32,
  end : Int32,
) -> BackwardBits raise Stop {
  if end <= start {
    raise corrupt("an empty bitstream")
  }
  let last = data[end - 1]
  if last == 0 {
    raise corrupt("a bitstream without its end mark")
  }
  let pos = (end - start - 1) * 8 + (31 - last.to_int().clz())
  { data, start, pos, }
}

///|
/// The next `n` (at most 24) bits without consuming them.
fn BackwardBits::peek(self : BackwardBits, n : Int32) -> Int32 {
  let lo = self.pos - n
  if lo >= 0 {
    bits_at(self.data, self.start, lo, n)
  } else if self.pos <= 0 {
    0
  } else {
    bits_at(self.data, self.start, 0, self.pos) << -lo
  }
}

///|
fn BackwardBits::skip(self : BackwardBits, n : Int32) -> Unit {
  self.pos -= n
}

///|
/// The next `n` (at most 24) bits.
fn BackwardBits::read(self : BackwardBits, n : Int32) -> Int32 {
  let v = self.peek(n)
  self.pos -= n
  v
}

///|
/// The next `n` (at most 30) bits.
fn BackwardBits::read_long(self : BackwardBits, n : Int32) -> Int32 {
  if n <= 24 {
    self.read(n)
  } else {
    let high = self.read(n - 16)
    (high << 16) | self.read(16)
  }
}

///|
/// A stream read forwards, `pos` bits after `start`, ending at `end`.
priv struct ForwardBits {
  data : FixedArray[Byte]
  start : Int32
  end : Int32
  mut pos : Int32
}

///|
fn ForwardBits::new(
  data : FixedArray[Byte],
  start : Int32,
  end : Int32,
) -> ForwardBits {
  { data, start, end, pos: 0, }
}

///|
/// The next `n` (at most 16) bits without consuming them; bits past `end`
/// read as zero, as in the reference decoder.
fn ForwardBits::peek(self : ForwardBits, n : Int32) -> Int32 {
  let total = (self.end - self.start) * 8
  if self.pos + n <= total {
    bits_at(self.data, self.start, self.pos, n)
  } else if self.pos >= total {
    0
  } else {
    bits_at(self.data, self.start, self.pos, total - self.pos)
  }
}

///|
fn ForwardBits::read(self : ForwardBits, n : Int32) -> Int32 {
  let v = self.peek(n)
  self.pos += n
  v
}

///|
/// Whether the reads so far stayed within `end`.
fn ForwardBits::in_bounds(self : ForwardBits) -> Bool {
  self.pos <= (self.end - self.start) * 8
}

///|
/// Whole bytes used so far.
fn ForwardBits::bytes_used(self : ForwardBits) -> Int32 {
  (self.pos + 7) >> 3
}