///|
priv struct WireWriter {
  bytes : Array[Byte]
  mut nodes : Int
}

///|
fn WireWriter::new() -> WireWriter {
  { bytes: [], nodes: 0, }
}

///|
fn WireWriter::uint(
  self : WireWriter,
  value : UInt64,
  width : Int,
) -> Unit raise FrameError {
  if self.bytes.length() > 16777208 - width {
    raise Invalid("payload byte limit")
  }
  for i = width - 1; i >= 0; i = i - 1 {
    self.bytes.push((value >> (i * 8)).to_byte())
  }
}

///|
fn WireWriter::raw(self : WireWriter, data : Bytes) -> Unit raise FrameError {
  if data.length() > 16777208 - self.bytes.length() {
    raise Invalid("payload byte limit")
  }
  for byte in data {
    self.bytes.push(byte)
  }
}

///|
fn WireWriter::shortstr(
  self : WireWriter,
  text : String,
) -> Unit raise FrameError {
  if text.length() > 255 {
    raise Invalid("shortstr exceeds 255 UTF-8 bytes")
  }
  let bytes = @utf8.encode(text)
  if bytes.length() > 255 {
    raise Invalid("shortstr exceeds 255 UTF-8 bytes")
  }
  self.uint(bytes.length().to_uint64(), 1)
  self.raw(bytes)
}

///|
fn WireWriter::longstr(
  self : WireWriter,
  bytes : Bytes,
) -> Unit raise FrameError {
  self.uint(bytes.length().to_uint64(), 4)
  self.raw(bytes)
}

///|
fn WireWriter::patch_size(self : WireWriter, start : Int) -> Unit {
  let length = self.bytes.length() - start - 4
  for i in 0..<4 {
    self.bytes[start + i] = (length >> ((3 - i) * 8)).to_byte()
  }
}

///|
fn WireWriter::node(self : WireWriter, depth : Int) -> Unit raise FrameError {
  self.nodes += 1
  if depth > 32 || self.nodes > 65536 {
    raise Invalid("field nesting or element limit")
  }
}

///|
priv struct WireReader {
  bytes : Bytes
  mut pos : Int
  mut end : Int
  mut nodes : Int
}

///|
fn WireReader::new(bytes : Bytes) -> WireReader raise FrameError {
  if bytes.length() > 16777208 {
    raise Invalid("payload byte limit")
  }
  { bytes, pos: 0, end: bytes.length(), nodes: 0, }
}

///|
fn WireReader::uint(self : WireReader, width : Int) -> UInt64 raise FrameError {
  if width > self.end - self.pos {
    raise Invalid("truncated field")
  }
  let mut value = 0UL
  for _ in 0.. Bytes raise FrameError {
  if size > (self.end - self.pos).to_uint64() {
    raise Invalid("truncated string or container")
  }
  let start = self.pos
  self.pos += size.to_int()
  self.bytes[start:self.pos].to_owned()
}

///|
fn WireReader::shortstr(self : WireReader) -> String raise FrameError {
  let data = self.raw(self.uint(1))
  @utf8.decode(data, ignore_bom=false) catch {
    _ => raise Invalid("invalid UTF-8 shortstr")
  }
}

///|
fn WireReader::longstr(self : WireReader) -> Bytes raise FrameError {
  self.raw(self.uint(4))
}

///|
fn WireReader::container_end(self : WireReader) -> Int raise FrameError {
  let size = self.uint(4)
  if size > (self.end - self.pos).to_uint64() {
    raise Invalid("container length exceeds parent")
  }
  self.pos + size.to_int()
}

///|
fn WireReader::node(self : WireReader, depth : Int) -> Unit raise FrameError {
  self.nodes += 1
  if depth > 32 || self.nodes > 65536 {
    raise Invalid("field nesting or element limit")
  }
}

///|
fn WireReader::finish(self : WireReader) -> Unit raise FrameError {
  if self.pos != self.end {
    raise Invalid("trailing payload bytes")
  }
}