///|
/// Produce the canonical segment for a struct-root message, WITHOUT a segment table.
/// The input message's traversal and nesting limits still apply. Capabilities
/// cannot be canonicalized because their identity requires an external table.
pub fn canonicalize(
  message : Message,
  max_words? : Int = 8_388_608,
  max_depth? : Int = 64,
) -> Bytes raise CapnpError {
  if max_depth < 1 || max_depth > 256 {
    raise Limit("canonical depth must be between 1 and 256")
  }
  let owner = Builder::new(max_words~)
  if message.root_pointer().kind() == Capability {
    raise Unsupported("canonical encoding cannot contain capabilities")
  }
  // The standard canonical message root is a struct. A null root denotes an
  // empty struct and is normalized to the non-null offset -1 representation.
  let source = message.root()
  let (words, pointers) = canonical_shape(source)
  let target = owner.init_root(words, pointers)
  canonical_struct_into(target, source, max_depth - 1)
  let framed = owner.finish()
  framed[8:].to_owned()
}

///|
fn canonical_shape(source : StructReader) -> (Int, Int) raise CapnpError {
  let mut data_words = (source.data_bytes + 7) / 8
  let mut pointers = source.pointer_count
  while data_words > 0 && source.get_uint64((data_words - 1) * 8) == 0UL {
    data_words -= 1
  }
  while pointers > 0 && source.get_pointer(pointers - 1).is_null() {
    pointers -= 1
  }
  (data_words, pointers)
}

///|
fn canonical_struct_into(
  target : StructBuilder,
  source : StructReader,
  depth : Int,
) -> Unit raise CapnpError {
  for i in 0.. Unit raise CapnpError {
  if source.is_null() {
    b_write(owner, slot, 0UL, 8)
    return
  }
  if depth == 0 {
    raise Limit("canonical nesting limit exceeded")
  }
  let (_, _, tag) = PointerReader::read_resolve(source)
  match tag.to_uint() & 3 {
    0 => {
      let value = source.as_struct()
      let (words, pointers) = canonical_shape(value)
      let target = b_init_struct(owner, slot, words, pointers)
      canonical_struct_into(target, value, depth - 1)
    }
    1 => canonical_list(owner, slot, source.as_list(), depth - 1)
    3 => raise Unsupported("canonical encoding cannot contain capabilities")
    _ => raise InvalidMessage("unresolved pointer during canonicalization")
  }
}

///|
fn canonical_list(
  owner : Builder,
  slot : Int,
  source : ListReader,
  depth : Int,
) -> Unit raise CapnpError {
  if source.code == 7 {
    if source.data_words == 0 && source.pointer_count == 0 {
      ignore(b_init_struct_list(owner, slot, source.count, 0, 0))
      return
    }
    let mut words = 0
    let mut pointers = 0
    for i in 0.. words { w } else { words }
      pointers = if p > pointers { p } else { pointers }
    }
    let target = b_init_struct_list(owner, slot, source.count, words, pointers)
    // Two passes keep auxiliary memory constant rather than retaining one
    // reader object per input element. Both passes consume traversal budget.
    for i in 0.. 0 {
      let length = if source.code == 1 {
        (source.count + 7) / 8
      } else {
        source.count * source.stride
      }
      let begin = source.state.offsets[source.segment] + source.start
      for i in 0..