///|
/// Wire failures are explicit. NeedMore is recoverable only before session EOF.
pub(all) suberror RfbError {
  NeedMore
  Invalid(String)
  Limit(String)
  Unsupported(Int)
  AuthenticationFailed
} derive(Debug, Eq)

///|
/// Internal cursor over unsigned byte values. Transactional callers commit position only on success.
priv struct Reader {
  data : Array[Int]
  mut pos : Int
}

///|
fn Reader::new(data : Array[Int]) -> Reader {
  { data, pos: 0, }
}

///|
fn Reader::u8(self : Reader) -> Int raise RfbError {
  if self.pos >= self.data.length() {
    raise NeedMore
  }
  let value = self.data[self.pos]
  if value < 0 || value > 255 {
    raise Invalid("byte out of range")
  }
  self.pos += 1
  value
}

///|
fn Reader::u16(self : Reader) -> Int raise RfbError {
  (self.u8() << 8) | self.u8()
}

///|
fn Reader::i32(self : Reader) -> Int raise RfbError {
  (self.u8() << 24) | (self.u8() << 16) | (self.u8() << 8) | self.u8()
}

///|
fn Reader::take(self : Reader, n : Int) -> Array[Int] raise RfbError {
  if n < 0 {
    raise Invalid("negative length")
  }
  if n > self.data.length() - self.pos {
    raise NeedMore
  }
  Array::makei(n, _ => self.u8())
}

///|
fn put16(out : Array[Int], value : Int) -> Unit {
  out.push((value >> 8) & 255)
  out.push(value & 255)
}

///|
fn put32(out : Array[Int], value : Int) -> Unit {
  out.push((value >> 24) & 255)
  out.push((value >> 16) & 255)
  put16(out, value)
}

///|
fn check_u16(n : Int) -> Unit raise RfbError {
  if n < 0 || n > 65535 {
    raise Invalid("u16 out of range")
  }
}

///|
/// Copy bytes at the boundary to prevent mutation by callers.
fn checked_bytes(data : Array[Int]) -> Array[Int] raise RfbError {
  for n in data {
    if n < 0 || n > 255 {
      raise Invalid("byte out of range")
    }
  }
  data.copy()
}