///|
/// Consume an unknown wire value without constructing its container/value tree.
fn Reader::skip(
  self : Reader,
  tag : Kind,
  depth : Int,
) -> Unit raise CodecError {
  if depth > 64 {
    raise Invalid("skip depth limit")
  }
  match tag {
    BinaryKind => {
      self.nodes += 1
      let size = if self.compact { self.varint() } else { self.fixed(4, false) }
      if size > 1048576UL || size > (self.data.length() - self.pos).to_uint64() {
        raise Invalid("invalid skipped binary length")
      }
      self.pos += size.to_int()
    }
    StructKind => {
      self.nodes += 1
      let mut previous = 0
      while true {
        let header = self.byte()
        if header == 0 {
          break
        }
        let raw = if self.compact { header & 15 } else { header }
        let field_kind = kind(raw, self.compact)
        let id = self.field_id(header, previous)
        previous = id
        if self.compact && field_kind == BoolKind {
          self.nodes += 1
        } else {
          self.skip(field_kind, depth + 1)
        }
        if self.nodes > 100000 {
          raise Invalid("skip node limit")
        }
      }
    }
    ListKind | SetKind => {
      self.nodes += 1
      let header = self.byte()
      let element = kind(
        if self.compact {
          header & 15
        } else {
          header
        },
        self.compact,
      )
      let size = if self.compact {
        if header >> 4 == 15 {
          self.varint()
        } else {
          (header >> 4).to_uint64()
        }
      } else {
        self.fixed(4, false)
      }
      if size > 100000UL {
        raise Invalid("skip container limit")
      }
      for _ in 0.. {
      self.nodes += 1
      let (key, value, size) = if self.compact {
        let size = self.varint()
        if size == 0UL {
          (0, 0, size)
        } else {
          let types = self.byte()
          (types >> 4, types & 15, size)
        }
      } else {
        let key = self.byte()
        let value = self.byte()
        (key, value, self.fixed(4, false))
      }
      if size > 100000UL {
        raise Invalid("skip map limit")
      }
      if size > 0UL {
        let key_kind = kind(key, self.compact)
        let value_kind = kind(value, self.compact)
        for _ in 0.. ignore(self.value(tag, depth))
  }
  if self.nodes > 100000 {
    raise Invalid("skip node limit")
  }
}

///|
fn Reader::field_id(
  self : Reader,
  header : Int,
  previous : Int,
) -> Int raise CodecError {
  let id = if self.compact {
    if header >> 4 > 0 {
      (previous + (header >> 4)).to_int64()
    } else {
      unzigzag(self.varint())
    }
  } else {
    self.signed(2)
  }
  if id < -32768L || id > 32767L {
    raise Invalid("field id overflow")
  }
  id.to_int()
}

///|
/// Read one value from a byte stream; returns the consumed byte count.
pub fn decode_prefix(
  data : Bytes,
  root : Kind,
  protocol : Protocol,
) -> (Value, Int) raise CodecError {
  if data.length() > 1048576 {
    raise Invalid("input exceeds one MiB")
  }
  let reader : Reader = {
    data,
    pos: 0,
    nodes: 0,
    compact: protocol == CompactProtocol,
  }
  let value = reader.value(root, 0)
  (value, reader.pos)
}

///|
/// Return the byte length of the first wire value, validating skipped data.
pub fn skip_prefix(
  data : Bytes,
  root : Kind,
  protocol : Protocol,
) -> Int raise CodecError {
  if data.length() > 1048576 {
    raise Invalid("input exceeds one MiB")
  }
  let reader : Reader = {
    data,
    pos: 0,
    nodes: 0,
    compact: protocol == CompactProtocol,
  }
  reader.skip(root, 0)
  reader.pos
}

///|
/// Decode only requested fields of a struct. Unknown and mismatched fields are
/// skipped on the wire. Duplicates retain wire order, as in the generic decoder.
pub fn decode_selected(
  data : Bytes,
  fields : Array[(Int, Kind)],
  protocol : Protocol,
) -> Value raise CodecError {
  if data.length() > 1048576 || fields.length() > 100000 {
    raise Invalid("selection resource limit")
  }
  let wanted : Map[Int, Kind] = Map([])
  for (id, tag) in fields {
    if id < -32768 || id > 32767 || wanted.contains(id) {
      raise Invalid("invalid or duplicate selection id")
    }
    wanted[id] = tag
  }
  let reader : Reader = {
    data,
    pos: 0,
    nodes: 1,
    compact: protocol == CompactProtocol,
  }
  let out : Array[(Int, Value)] = []
  let mut previous = 0
  while true {
    let header = reader.byte()
    if header == 0 {
      break
    }
    let raw = if reader.compact { header & 15 } else { header }
    let tag = kind(raw, reader.compact)
    let id = reader.field_id(header, previous)
    previous = id
    if wanted.get(id) == Some(tag) {
      let value = if reader.compact && tag == BoolKind {
        reader.nodes += 1
        Bool(raw == 1)
      } else {
        reader.value(tag, 1)
      }
      out.push((id, value))
    } else if reader.compact && tag == BoolKind {
      reader.nodes += 1
    } else {
      reader.skip(tag, 1)
    }
    if reader.nodes > 100000 {
      raise Invalid("selection node limit")
    }
  }
  if reader.pos != data.length() {
    raise Invalid("trailing struct bytes")
  }
  Struct(out)
}