///|
pub suberror BinaryEncodeError {
  CannotEncodeRecursiveIndexHeapType
  CannotEncodeDefTypeHeapType
  CannotEncodeBottomValType
  InvalidUnsignedLebBitWidth
  UnsignedLebValueOutOfRange
  InvalidSignedLebBitWidth
  SignedLebValueOutOfRange
  UnsignedLebExceedsMaxByteLimit
  SignedLebExceedsMaxByteLimit
  CannotEncodeNonStandardTypeIndex
  CannotEncodeRecursiveIndexBlockType
  InvalidMemArgEncoding
  InvalidActiveElemSequence
  InvalidTypedFunctionLocals
  InvalidNameMapOrder
  RawNameCustomSectionUnsupported
  ReservedCompilerFactsCustomSection
  UnsupportedCompilerFactsVersion
  CannotEncodeStringConstWithoutModuleContext
  StringConstLiteralMissingFromSection
  UnsupportedBackingArrayMemoryOp
  InvalidLegacyTryShape
} derive(Eq, Debug)

///|
pub impl Show for BinaryEncodeError with fn output(self, logger) {
  match self {
    CannotEncodeRecursiveIndexHeapType =>
      logger.write_string("CannotEncodeRecursiveIndexHeapType")
    CannotEncodeDefTypeHeapType =>
      logger.write_string("CannotEncodeDefTypeHeapType")
    CannotEncodeBottomValType =>
      logger.write_string("CannotEncodeBottomValType")
    InvalidUnsignedLebBitWidth =>
      logger.write_string("InvalidUnsignedLebBitWidth")
    UnsignedLebValueOutOfRange =>
      logger.write_string("UnsignedLebValueOutOfRange")
    InvalidSignedLebBitWidth => logger.write_string("InvalidSignedLebBitWidth")
    SignedLebValueOutOfRange => logger.write_string("SignedLebValueOutOfRange")
    UnsignedLebExceedsMaxByteLimit =>
      logger.write_string("UnsignedLebExceedsMaxByteLimit")
    SignedLebExceedsMaxByteLimit =>
      logger.write_string("SignedLebExceedsMaxByteLimit")
    CannotEncodeNonStandardTypeIndex =>
      logger.write_string("CannotEncodeNonStandardTypeIndex")
    CannotEncodeRecursiveIndexBlockType =>
      logger.write_string("CannotEncodeRecursiveIndexBlockType")
    InvalidMemArgEncoding => logger.write_string("InvalidMemArgEncoding")
    InvalidActiveElemSequence =>
      logger.write_string("InvalidActiveElemSequence")
    InvalidTypedFunctionLocals =>
      logger.write_string("InvalidTypedFunctionLocals")
    InvalidNameMapOrder => logger.write_string("InvalidNameMapOrder")
    RawNameCustomSectionUnsupported =>
      logger.write_string("RawNameCustomSectionUnsupported")
    ReservedCompilerFactsCustomSection =>
      logger.write_string("ReservedCompilerFactsCustomSection")
    UnsupportedCompilerFactsVersion =>
      logger.write_string("UnsupportedCompilerFactsVersion")
    CannotEncodeStringConstWithoutModuleContext =>
      logger.write_string("CannotEncodeStringConstWithoutModuleContext")
    StringConstLiteralMissingFromSection =>
      logger.write_string("StringConstLiteralMissingFromSection")
    UnsupportedBackingArrayMemoryOp =>
      logger.write_string("UnsupportedBackingArrayMemoryOp")
    InvalidLegacyTryShape => logger.write_string("InvalidLegacyTryShape")
  }
}

///|
pub trait Encode {
  fn encode(Self, @buffer.Buffer) -> Result[Unit, BinaryEncodeError]
}

///|
priv struct BinaryEncodeStringRefsContext {
  strings : Array[Bytes]
  mut index : @hashmap.HashMap[Bytes, Int]?
}

///|
fn BinaryEncodeStringRefsContext::new(
  strings : Array[Bytes],
  index : @hashmap.HashMap[Bytes, Int]?,
) -> BinaryEncodeStringRefsContext {
  { strings, index, }
}

///|
let binary_encode_stringrefs_context : Ref[BinaryEncodeStringRefsContext?] = Ref::new(
  None,
)

///|
/// Bind a module string pool and its index for this encoding call, restoring
/// the enclosing context on both success and encoding errors.
fn[T] with_binary_encode_stringrefs_context(
  stringrefs : Array[Bytes]?,
  f : () -> Result[T, BinaryEncodeError],
  known_index? : @hashmap.HashMap[Bytes, Int]? = None,
) -> Result[T, BinaryEncodeError] {
  let saved = binary_encode_stringrefs_context.val
  binary_encode_stringrefs_context.val = stringrefs.map(fn(strings) {
    BinaryEncodeStringRefsContext::new(strings, known_index)
  })
  let result = f()
  binary_encode_stringrefs_context.val = saved
  result
}

///|
/// Map a string constant literal to its position in the active stringrefs pool.
/// If encoding runs without context or with a missing literal, this returns a hard
/// failure instead of inventing a synthetic index.
fn encode_string_const_index(
  bytes : Bytes,
) -> Result[@lib.U32, BinaryEncodeError] {
  encode_string_const_index_impl(bytes, None)
}

///|
#inline
fn encode_string_const_index_impl(
  bytes : Bytes,
  candidate_checks : Array[Int]?,
) -> Result[@lib.U32, BinaryEncodeError] {
  match binary_encode_stringrefs_context.val {
    Some(context) => {
      if context.index is Some(index) {
        return encode_string_index_lookup(index, bytes, candidate_checks)
      }
      let stringrefs = context.strings
      let mut i = 0
      while i < stringrefs.length() {
        // A short prefix stays cheaper for sparse use of a large external
        // pool. A distant lookup builds one index for this encoding scope.
        if i == 32 {
          let index : @hashmap.HashMap[Bytes, Int] = @hashmap.new(
            capacity=stringrefs.length(),
          )
          for position = 0
              position < stringrefs.length()
              position = position + 1 {
            let literal = stringrefs[position]
            if candidate_checks is Some(count) {
              count[0] += 1
            }
            if index.get(literal) is None {
              index.set(literal, position)
            }
          }
          context.index = Some(index)
          return encode_string_index_lookup(index, bytes, candidate_checks)
        }
        if candidate_checks is Some(count) {
          count[0] += 1
        }
        if stringrefs[i] == bytes {
          return Ok(@lib.U32(i.reinterpret_as_uint()))
        }
        i += 1
      }
      Err(BinaryEncodeError::StringConstLiteralMissingFromSection)
    }
    None => Err(BinaryEncodeError::CannotEncodeStringConstWithoutModuleContext)
  }
}

///|
fn encode_string_index_lookup(
  index : @hashmap.HashMap[Bytes, Int],
  bytes : Bytes,
  candidate_checks : Array[Int]?,
) -> Result[@lib.U32, BinaryEncodeError] {
  if candidate_checks is Some(count) {
    count[0] += 1
  }
  match index.get(bytes) {
    Some(position) => Ok(@lib.U32(position.reinterpret_as_uint()))
    None => Err(BinaryEncodeError::StringConstLiteralMissingFromSection)
  }
}

///|
pub impl Encode for NumType with fn encode(val, buf) {
  match val {
    F64NumType => buf.write_byte(0x7C)
    F32NumType => buf.write_byte(0x7D)
    I64NumType => buf.write_byte(0x7E)
    I32NumType => buf.write_byte(0x7F)
  }
  Ok(())
}

///|
pub impl Encode for HeapType with fn encode(val, buf) {
  match val {
    AbsHeapTypeHeapType(ht) => Encode::encode(ht, buf)
    SharedAbsHeapTypeHeapType(ht) => {
      buf.write_byte(0x65)
      Encode::encode(ht, buf)
    }
    HeapType(TypeIdx(i)) => Encode::encode(@lib.S33(i), buf)
    HeapType(RecIdx(_)) =>
      Err(BinaryEncodeError::CannotEncodeRecursiveIndexHeapType)
    DefTypeHeapType(_) => Err(BinaryEncodeError::CannotEncodeDefTypeHeapType)
  }
}

///|
pub impl Encode for AbsHeapType with fn encode(val, buf) {
  match val {
    ExnAbsHeapType => buf.write_byte(0x69)
    StringAbsHeapType => buf.write_byte(0x67)
    ContAbsHeapType => buf.write_byte(0x68)
    ArrayAbsHeapType => buf.write_byte(0x6A)
    StructAbsHeapType => buf.write_byte(0x6B)
    I31AbsHeapType => buf.write_byte(0x6C)
    EqAbsHeapType => buf.write_byte(0x6D)
    AnyAbsHeapType => buf.write_byte(0x6E)
    ExternAbsHeapType => buf.write_byte(0x6F)
    FuncAbsHeapType => buf.write_byte(0x70)
    NoneAbsHeapType => buf.write_byte(0x71)
    NoExternAbsHeapType => buf.write_byte(0x72)
    NoFuncAbsHeapType => buf.write_byte(0x73)
    NoExnAbsHeapType => buf.write_byte(0x74)
    NoContAbsHeapType => buf.write_byte(0x75)
    WaitqueueAbsHeapType => buf.write_byte(0x5c)
    NoWaitqueueAbsHeapType => buf.write_byte(0x5b)
  }
  Ok(())
}

///|
pub impl Encode for RefType with fn encode(val, buf) {
  match val {
    HeapTypeRefType(true, false, AbsHeapTypeHeapType(StringAbsHeapType)) => {
      buf.write_byte(0x63)
      Encode::encode(AbsHeapType::string_(), buf)
    }
    HeapTypeRefType(true, false, AbsHeapTypeHeapType(abs)) =>
      Encode::encode(abs, buf)
    HeapTypeRefType(true, exact, ht) => {
      buf.write_byte(0x63)
      if exact {
        buf.write_byte(0x62)
      }
      Encode::encode(ht, buf)
    }
    HeapTypeRefType(false, exact, ht) => {
      buf.write_byte(0x64)
      if exact {
        buf.write_byte(0x62)
      }
      Encode::encode(ht, buf)
    }
    AbsHeapTypeRefType(StringAbsHeapType) => {
      buf.write_byte(0x63)
      Encode::encode(AbsHeapType::string_(), buf)
    }
    AbsHeapTypeRefType(abs) => Encode::encode(abs, buf)
  }
}

///|
fn encode_ref_null_immediate(
  rt : RefType,
  buf : @buffer.Buffer,
) -> Result[Unit, BinaryEncodeError] {
  match rt {
    HeapTypeRefType(_, exact, ht) => encode_ref_heap_type(exact, ht, buf)
    AbsHeapTypeRefType(StringAbsHeapType) =>
      Encode::encode(AbsHeapType::string_(), buf)
    AbsHeapTypeRefType(abs) => Encode::encode(abs, buf)
  }
}

///|
fn encode_ref_heap_type(
  exact : Bool,
  ht : HeapType,
  buf : @buffer.Buffer,
) -> Result[Unit, BinaryEncodeError] {
  if exact {
    buf.write_byte(0x62)
  }
  Encode::encode(ht, buf)
}

///|
pub impl Encode for ValType with fn encode(val, buf) {
  match val {
    VecTypeValType => {
      buf.write_byte(0x7B)
      Ok(())
    }
    NumTypeValType(num) => Encode::encode(num, buf)
    RefTypeValType(rt) => Encode::encode(rt, buf)
    BotValType => Err(BinaryEncodeError::CannotEncodeBottomValType)
  }
}

///|
pub impl[T : Encode] Encode for @list.List[T] with fn encode(val, buf) {
  if Encode::encode(@lib.U32(val.length().reinterpret_as_uint()), buf) is Err(t) {
    return Err(t)
  }
  let mut cursor = val
  while cursor is More(val, tail~) {
    if Encode::encode(val, buf) is Err(t) {
      return Err(t)
    }
    cursor = tail
  }
  Ok(())
}

///|
pub impl Encode for CompType with fn encode(val, buf) {
  match val {
    StructCompType(fts) => {
      buf.write_byte(0x5F)
      Encode::encode(fts, buf)
    }
    ArrayCompType(ft) => {
      buf.write_byte(0x5E)
      Encode::encode(ft, buf)
    }
    FuncCompType(t1, t2) => {
      buf.write_byte(0x60)
      if Encode::encode(t1, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(t2, buf)
    }
    ContCompType(func_type) => {
      buf.write_byte(0x5D)
      Encode::encode(func_type, buf)
    }
  }
}

///|
pub impl Encode for FieldType with fn encode(val, buf) {
  let FieldType(st, m) = val
  if Encode::encode(st, buf) is Err(t) {
    return Err(t)
  }
  if Encode::encode(m, buf) is Err(t) {
    return Err(t)
  }
  Ok(())
}

///|
pub impl Encode for Mut with fn encode(val, buf) {
  match val {
    Const => buf.write_byte(0x00)
    Var => buf.write_byte(0x01)
  }
  Ok(())
}

///|
pub impl Encode for StorageType with fn encode(val, buf) {
  match val {
    ValTypeStorageType(vt) => Encode::encode(vt, buf)
    PackTypeStorageType(pt) => Encode::encode(pt, buf)
  }
}

///|
pub impl Encode for PackType with fn encode(val, buf) {
  match val {
    I16PackType => {
      buf.write_byte(0x77)
      Ok(())
    }
    I8PackType => {
      buf.write_byte(0x78)
      Ok(())
    }
  }
}

///|
pub impl Encode for AtomicOrder with fn encode(val, buf) {
  match val {
    SeqCst => buf.write_byte(0x00)
    AcqRel => buf.write_byte(0x01)
    Relaxed => buf.write_byte(0x02)
  }
  Ok(())
}

///|
fn encode_aggregate_atomic_rmw_order(
  order : AtomicOrder,
  buf : @buffer.Buffer,
) -> Unit {
  match order {
    SeqCst => buf.write_byte(0x00)
    AcqRel => buf.write_byte(0x11)
    Relaxed => buf.write_byte(0x22)
  }
}

///|
pub impl Encode for RecType with fn encode(val, buf) {
  match val {
    SingleRecType(st) => Encode::encode(st, buf)
    GroupRecType(sts) => {
      buf.write_byte(0x4E)
      Encode::encode(sts, buf)
    }
  }
}

///|
pub impl Encode for SubType with fn encode(val, buf) {
  match val {
    SubType(f, indexes, meta, ct) => {
      match f {
        true => buf.write_byte(0x4F)
        false => buf.write_byte(0x50)
      }
      if Encode::encode(indexes, buf) is Err(t) {
        return Err(t)
      }
      if meta.shared {
        buf.write_byte(0x65)
      }
      match meta.describes {
        Some(i) => {
          buf.write_byte(0x4C)
          if Encode::encode(i, buf) is Err(t) {
            return Err(t)
          }
        }
        None => ()
      }
      match meta.descriptor {
        Some(i) => {
          buf.write_byte(0x4D)
          if Encode::encode(i, buf) is Err(t) {
            return Err(t)
          }
        }
        None => ()
      }
      Encode::encode(ct, buf)
    }
    CompTypeSubType(meta, ct) => {
      if meta.shared {
        buf.write_byte(0x65)
      }
      match meta.describes {
        Some(i) => {
          buf.write_byte(0x4C)
          if Encode::encode(i, buf) is Err(t) {
            return Err(t)
          }
        }
        None => ()
      }
      match meta.descriptor {
        Some(i) => {
          buf.write_byte(0x4D)
          if Encode::encode(i, buf) is Err(t) {
            return Err(t)
          }
        }
        None => ()
      }
      Encode::encode(ct, buf)
    }
  }
}

///|
pub impl Encode for ExternIdx with fn encode(val, buf) {
  match val {
    FuncExternIdx(idx) => {
      buf.write_byte(0x00)
      Encode::encode(idx, buf)
    }
    TableExternIdx(idx) => {
      buf.write_byte(0x01)
      Encode::encode(idx, buf)
    }
    MemExternIdx(idx) => {
      buf.write_byte(0x02)
      Encode::encode(idx, buf)
    }
    GlobalExternIdx(idx) => {
      buf.write_byte(0x03)
      Encode::encode(idx, buf)
    }
    TagExternIdx(idx) => {
      buf.write_byte(0x04)
      Encode::encode(idx, buf)
    }
  }
}

///|
pub fn size_unsigned(
  val : UInt64,
  nbits : Int,
) -> Result[Int, BinaryEncodeError] {
  if nbits <= 0 || nbits > 64 {
    return Err(BinaryEncodeError::InvalidUnsignedLebBitWidth)
  }
  if nbits < 64 {
    let max = (1UL << nbits) - 1UL
    if val > max {
      return Err(BinaryEncodeError::UnsignedLebValueOutOfRange)
    }
  }
  let mut x = val
  let mut count = 0
  while true {
    x = x >> 7
    count += 1
    if x == 0 {
      break
    }
  }
  Ok(count)
}

///|
pub fn size_signed(val : Int64, nbits : Int) -> Result[Int, BinaryEncodeError] {
  if nbits <= 0 || nbits > 64 {
    return Err(BinaryEncodeError::InvalidSignedLebBitWidth)
  }
  if nbits < 64 {
    let min = -1L << (nbits - 1)
    let max = (1L << (nbits - 1)) - 1
    if val < min || val > max {
      return Err(BinaryEncodeError::SignedLebValueOutOfRange)
    }
  }
  let mut x = val
  let mut count = 0
  while true {
    let payload_u = (x & 0x7f).reinterpret_as_uint64()
    let sign_bit = (payload_u & 0x40UL) != 0UL
    x = x >> 7
    let done = (x == 0 && !sign_bit) || (x == -1 && sign_bit)
    count += 1
    if done {
      break
    }
  }
  Ok(count)
}

///|
fn encode_unsigned(
  val : UInt64,
  buf : @buffer.Buffer,
  nbits : Int,
) -> Result[Unit, BinaryEncodeError] {
  match encode_unsigned_error(val, buf, nbits) {
    None => Ok(())
    Some(error) => Err(error)
  }
}

///|
fn encode_signed(
  val : Int64,
  buf : @buffer.Buffer,
  nbits : Int,
) -> Result[Unit, BinaryEncodeError] {
  match encode_signed_error(val, buf, nbits) {
    None => Ok(())
    Some(error) => Err(error)
  }
}

///|
fn encode_unsigned_error(
  val : UInt64,
  buf : @buffer.Buffer,
  nbits : Int,
) -> BinaryEncodeError? {
  if nbits <= 0 || nbits > 64 {
    return Some(BinaryEncodeError::InvalidUnsignedLebBitWidth)
  }
  if nbits < 64 {
    let max = (1UL << nbits) - 1UL
    if val > max {
      return Some(BinaryEncodeError::UnsignedLebValueOutOfRange)
    }
  }
  let max_bytes = max_leb_bytes(nbits)
  let mut x = val
  let mut count = 0
  while true {
    if count >= max_bytes {
      return Some(BinaryEncodeError::UnsignedLebExceedsMaxByteLimit)
    }
    let payload = x & 0x7fUL
    x = x >> 7
    let out = if x == 0UL { payload } else { payload | 0x80UL }
    buf.write_byte(out.to_byte())
    count += 1
    if x == 0UL {
      break
    }
  }
  None
}

///|
fn encode_signed_error(
  val : Int64,
  buf : @buffer.Buffer,
  nbits : Int,
) -> BinaryEncodeError? {
  if nbits <= 0 || nbits > 64 {
    return Some(BinaryEncodeError::InvalidSignedLebBitWidth)
  }
  if nbits < 64 {
    let min = -1L << (nbits - 1)
    let max = (1L << (nbits - 1)) - 1
    if val < min || val > max {
      return Some(BinaryEncodeError::SignedLebValueOutOfRange)
    }
  }
  let max_bytes = max_leb_bytes(nbits)
  let mut x = val
  let mut count = 0
  while true {
    if count >= max_bytes {
      return Some(BinaryEncodeError::SignedLebExceedsMaxByteLimit)
    }
    let payload_u = (x & 0x7f).reinterpret_as_uint64()
    let sign_bit = (payload_u & 0x40UL) != 0UL
    x = x >> 7
    let done = (x == 0 && !sign_bit) || (x == -1 && sign_bit)
    let out = if done { payload_u } else { payload_u | 0x80UL }
    buf.write_byte(out.to_byte())
    count += 1
    if done {
      break
    }
  }
  None
}

///|
fn ceil_div(a : Int, b : Int) -> Int {
  (a + b - 1) / b
}

///|
const MAX_LEB128_BYTES_32 : Int = 5

///|
const MAX_LEB128_BYTES_33 : Int = 5

///|
const MAX_LEB128_BYTES_64 : Int = 10

///|
fn max_leb_bytes(nbits : Int) -> Int {
  match nbits {
    32 => MAX_LEB128_BYTES_32
    33 => MAX_LEB128_BYTES_33
    64 => MAX_LEB128_BYTES_64
    _ => ceil_div(nbits, 7)
  }
}

///|
pub impl Encode for S33 with fn encode(val, buf) {
  let S33(val) = val
  encode_signed(val.to_int64(), buf, 33)
}

///|
pub impl Encode for I32 with fn encode(val, buf) {
  let I32(val) = val
  encode_signed(val.to_int64(), buf, 32)
}

///|
pub impl Encode for U32 with fn encode(val, buf) {
  let @lib.U32(val) = val
  encode_unsigned(val.to_uint64(), buf, 32)
}

///|
pub impl Encode for U64 with fn encode(val, buf) {
  let @lib.U64(val) = val
  encode_unsigned(val, buf, 64)
}

///|
pub impl Encode for I64 with fn encode(val, buf) {
  let I64(val) = val
  encode_signed(val, buf, 64)
}

///|
pub impl Encode for F32 with fn encode(val, buf) {
  let F32(val) = val
  buf.write_float_le(val)
  Ok(())
}

///|
pub impl Encode for F64 with fn encode(val, buf) {
  let F64(val) = val
  buf.write_double_le(val)
  Ok(())
}

///|
pub impl Encode for Bool with fn encode(val, buf) {
  if val {
    buf.write_byte(0x01)
  } else {
    buf.write_byte(0x00)
  }
  Ok(())
}

///|
pub impl[T : Encode] Encode for T? with fn encode(val, buf) {
  match val {
    None => Ok(())
    Some(val) => Encode::encode(val, buf)
  }
}

///|
pub impl Encode for TypeIdx with fn encode(val, buf) {
  match val {
    TypeIdx(id) => encode_unsigned(id.to_uint64(), buf, 32)
    _ => Err(BinaryEncodeError::CannotEncodeNonStandardTypeIndex)
  }
}

///|
pub impl Encode for FuncIdx with fn encode(val, buf) {
  let FuncIdx(id) = val
  encode_unsigned(id.to_uint64(), buf, 32)
}

///|
pub impl Encode for TableIdx with fn encode(val, buf) {
  let TableIdx(id) = val
  encode_unsigned(id.to_uint64(), buf, 32)
}

///|
pub impl Encode for MemIdx with fn encode(val, buf) {
  let MemIdx(id) = val
  encode_unsigned(id.to_uint64(), buf, 32)
}

///|
pub impl Encode for GlobalIdx with fn encode(val, buf) {
  let GlobalIdx(id) = val
  encode_unsigned(id.to_uint64(), buf, 32)
}

///|
pub impl Encode for TagIdx with fn encode(val, buf) {
  let TagIdx(id) = val
  encode_unsigned(id.to_uint64(), buf, 32)
}

///|
pub impl Encode for ElemIdx with fn encode(val, buf) {
  let ElemIdx(id) = val
  encode_unsigned(id.to_uint64(), buf, 32)
}

///|
pub impl Encode for DataIdx with fn encode(val, buf) {
  let DataIdx(id) = val
  encode_unsigned(id.to_uint64(), buf, 32)
}

///|
pub impl Encode for LocalIdx with fn encode(val, buf) {
  let LocalIdx(id) = val
  encode_unsigned(id.to_uint64(), buf, 32)
}

///|
pub impl Encode for LabelIdx with fn encode(val, buf) {
  let LabelIdx(id) = val
  encode_unsigned(id.to_uint64(), buf, 32)
}

///|
pub impl Encode for LaneIdx with fn encode(val, buf) {
  buf.write_byte(val.0)
  Ok(())
}

///|
pub impl Encode for Name with fn encode(val, buf) {
  let Name(name) = val
  let bytes = @utf8.encode(name)
  let len = bytes.length()
  match Encode::encode(@lib.U32(len.reinterpret_as_uint()), buf) {
    Err(err) => Err(err)
    Ok(_) => {
      buf.write_bytes(bytes)
      Ok(())
    }
  }
}

///|
pub impl Encode for NameAssoc with fn encode(val, buf) {
  let NameAssoc(idx, name) = val
  match Encode::encode(@lib.U32(idx), buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  Encode::encode(name, buf)
}

///|
pub impl Encode for NameMap with fn encode(val, buf) {
  let NameMap(entries) = val
  match Encode::encode(@lib.U32(entries.length().reinterpret_as_uint()), buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  let mut prev_idx : UInt? = None
  for entry in entries {
    let NameAssoc(idx, _) = entry
    match prev_idx {
      Some(prev) if idx <= prev =>
        return Err(BinaryEncodeError::InvalidNameMapOrder)
      _ => ()
    }
    prev_idx = Some(idx)
    if Encode::encode(entry, buf) is Err(err) {
      return Err(err)
    }
  }
  Ok(())
}

///|
pub impl Encode for IndirectNameAssoc with fn encode(val, buf) {
  let IndirectNameAssoc(idx, names) = val
  match Encode::encode(@lib.U32(idx), buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  Encode::encode(names, buf)
}

///|
pub impl Encode for IndirectNameMap with fn encode(val, buf) {
  let IndirectNameMap(entries) = val
  match Encode::encode(@lib.U32(entries.length().reinterpret_as_uint()), buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  let mut prev_idx : UInt? = None
  for entry in entries {
    let IndirectNameAssoc(idx, _) = entry
    match prev_idx {
      Some(prev) if idx <= prev =>
        return Err(BinaryEncodeError::InvalidNameMapOrder)
      _ => ()
    }
    prev_idx = Some(idx)
    if Encode::encode(entry, buf) is Err(err) {
      return Err(err)
    }
  }
  Ok(())
}

///|
fn write_name_subsection(
  subsection_id : Byte,
  buf : @buffer.Buffer,
  write_payload : (@buffer.Buffer) -> Result[Unit, BinaryEncodeError],
) -> Result[Unit, BinaryEncodeError] {
  buf.write_byte(subsection_id)
  let payload = @buffer.new()
  if write_payload(payload) is Err(err) {
    return Err(err)
  }
  let payload_bytes = payload.to_bytes()
  if Encode::encode(@lib.U32(payload_bytes.length().reinterpret_as_uint()), buf)
    is Err(err) {
    return Err(err)
  }
  buf.write_bytes(payload_bytes)
  Ok(())
}

///|
pub impl Encode for NameSec with fn encode(val, buf) {
  let {
    module_name,
    func_names,
    local_names,
    label_names,
    type_names,
    table_names,
    memory_names,
    global_names,
    elem_names,
    data_names,
    field_names,
    tag_names,
  } = val
  match module_name {
    Some(name) =>
      if write_name_subsection(0, buf, fn(payload) {
          Encode::encode(name, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match func_names {
    Some(names) =>
      if write_name_subsection(1, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match local_names {
    Some(names) =>
      if write_name_subsection(2, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match label_names {
    Some(names) =>
      if write_name_subsection(3, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match type_names {
    Some(names) =>
      if write_name_subsection(4, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match table_names {
    Some(names) =>
      if write_name_subsection(5, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match memory_names {
    Some(names) =>
      if write_name_subsection(6, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match global_names {
    Some(names) =>
      if write_name_subsection(7, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match elem_names {
    Some(names) =>
      if write_name_subsection(8, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match data_names {
    Some(names) =>
      if write_name_subsection(9, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match field_names {
    Some(names) =>
      if write_name_subsection(10, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  match tag_names {
    Some(names) =>
      if write_name_subsection(11, buf, fn(payload) {
          Encode::encode(names, payload)
        })
        is Err(err) {
        return Err(err)
      }
    None => ()
  }
  Ok(())
}

///|
fn write_section(
  section_id : Int,
  buf : @buffer.Buffer,
  write_payload : (@buffer.Buffer) -> Result[Unit, BinaryEncodeError],
) -> Result[Unit, BinaryEncodeError] {
  buf.write_byte(section_id.to_byte())
  let payload = @buffer.new()
  if write_payload(payload) is Err(t) {
    return Err(t)
  }
  let payload_bytes = payload.to_bytes()
  if Encode::encode(@lib.U32(payload_bytes.length().reinterpret_as_uint()), buf)
    is Err(t) {
    return Err(t)
  }
  buf.write_bytes(payload_bytes)
  Ok(())
}

///|
fn encode_name_sec_as_custom_section(
  val : NameSec,
  buf : @buffer.Buffer,
) -> Result[Unit, BinaryEncodeError] {
  write_section(0, buf, fn(payload) {
    if Encode::encode(Name::new("name"), payload) is Err(err) {
      return Err(err)
    }
    Encode::encode(val, payload)
  })
}

///|
fn encode_raw_name_sec_payload_as_custom_section(
  payload_bytes : Bytes,
  buf : @buffer.Buffer,
) -> Result[Unit, BinaryEncodeError] {
  write_section(0, buf, fn(payload) {
    if Encode::encode(Name::new("name"), payload) is Err(err) {
      return Err(err)
    }
    payload.write_bytes(payload_bytes)
    Ok(())
  })
}

///|
pub impl Encode for CustomSec with fn encode(val, buf) {
  let CustomSec(name, bytes) = val
  write_section(0, buf, fn(payload) {
    if Encode::encode(name, payload) is Err(t) {
      return Err(t)
    }
    payload.write_bytes(bytes)
    Ok(())
  })
}

///|
pub impl Encode for TypeSec with fn encode(val, buf) {
  write_section(1, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
pub impl Encode for ImportSec with fn encode(val, buf) {
  write_section(2, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
fn encode_compact_import_section(
  sec : ImportSec,
  buf : @buffer.Buffer,
) -> Result[Unit, BinaryEncodeError] {
  let ImportSec(imports) = sec
  let groups : Array[(Int, Int, Byte)] = []
  let mut start = 0
  while start < imports.length() {
    let Import(module_name, _, ty) = imports[start]
    let mut end = start + 1
    while end < imports.length() {
      let Import(next_module, _, next_type) = imports[end]
      if next_module != module_name || next_type != ty {
        break
      }
      end += 1
    }
    let mut kind : Byte = 0x7e
    if end == start + 1 {
      kind = 0x7f
      while end < imports.length() {
        let Import(next_module, _, _) = imports[end]
        if next_module != module_name {
          break
        }
        end += 1
      }
    }
    groups.push((start, end, kind))
    start = end
  }
  write_section(2, buf, fn(payload) {
    if Encode::encode(U32(groups.length().reinterpret_as_uint()), payload)
      is Err(err) {
      return Err(err)
    }
    for group in groups {
      let (start, end, kind) = group
      if end == start + 1 {
        if Encode::encode(imports[start], payload) is Err(err) {
          return Err(err)
        }
        continue
      }
      let Import(module_name, _, ty) = imports[start]
      if Encode::encode(module_name, payload) is Err(err) {
        return Err(err)
      }
      payload.write_byte(0)
      payload.write_byte(kind)
      if kind == 0x7e {
        if Encode::encode(ty, payload) is Err(err) {
          return Err(err)
        }
      }
      if Encode::encode(U32((end - start).reinterpret_as_uint()), payload)
        is Err(err) {
        return Err(err)
      }
      for idx in start.. return Err(err)
    Ok(_) => ()
  }
  match Encode::encode(name, buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  Encode::encode(et, buf)
}

///|
pub impl Encode for ExternType with fn encode(val, buf) {
  match val {
    FuncExternType(idx) => {
      buf.write_byte(0x00)
      Encode::encode(idx, buf)
    }
    TableExternType(tt) => {
      buf.write_byte(0x01)
      Encode::encode(tt, buf)
    }
    MemExternType(mt) => {
      buf.write_byte(0x02)
      Encode::encode(mt, buf)
    }
    GlobalExternType(gt) => {
      buf.write_byte(0x03)
      Encode::encode(gt, buf)
    }
    TagExternType(tt) => {
      buf.write_byte(0x04)
      Encode::encode(tt, buf)
    }
  }
}

///|
pub impl Encode for TableType with fn encode(val, buf) {
  let TableType(rt, l) = val
  match Encode::encode(rt, buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  Encode::encode(l, buf)
}

///|
pub impl Encode for Limits with fn encode(val, buf) {
  match val {
    I32Limits(min, None) => {
      buf.write_byte(0x00)
      Encode::encode(@lib.U64(min.to_uint64()), buf)
    }
    I32Limits(min, Some(max)) => {
      buf.write_byte(0x01)
      match Encode::encode(@lib.U64(min.to_uint64()), buf) {
        Err(err) => return Err(err)
        _ => ()
      }
      Encode::encode(@lib.U64(max.to_uint64()), buf)
    }
    I64Limits(min, None) => {
      buf.write_byte(0x04)
      Encode::encode(@lib.U64(min), buf)
    }
    I64Limits(min, Some(max)) => {
      buf.write_byte(0x05)
      match Encode::encode(@lib.U64(min), buf) {
        Err(err) => return Err(err)
        _ => ()
      }
      Encode::encode(@lib.U64(max), buf)
    }
  }
}

///|
pub impl Encode for MemType with fn encode(val, buf) {
  let MemType(limits, shared) = val
  match (limits, shared) {
    (I32Limits(min, None), false) => {
      buf.write_byte(0x00)
      Encode::encode(@lib.U64(min.to_uint64()), buf)
    }
    (I32Limits(min, Some(max)), false) => {
      buf.write_byte(0x01)
      match Encode::encode(@lib.U64(min.to_uint64()), buf) {
        Err(err) => return Err(err)
        _ => ()
      }
      Encode::encode(@lib.U64(max.to_uint64()), buf)
    }
    (I32Limits(min, None), true) => {
      buf.write_byte(0x02)
      Encode::encode(@lib.U64(min.to_uint64()), buf)
    }
    (I32Limits(min, Some(max)), true) => {
      buf.write_byte(0x03)
      match Encode::encode(@lib.U64(min.to_uint64()), buf) {
        Err(err) => return Err(err)
        _ => ()
      }
      Encode::encode(@lib.U64(max.to_uint64()), buf)
    }
    (I64Limits(min, None), false) => {
      buf.write_byte(0x04)
      Encode::encode(@lib.U64(min), buf)
    }
    (I64Limits(min, Some(max)), false) => {
      buf.write_byte(0x05)
      match Encode::encode(@lib.U64(min), buf) {
        Err(err) => return Err(err)
        _ => ()
      }
      Encode::encode(@lib.U64(max), buf)
    }
    (I64Limits(min, None), true) => {
      buf.write_byte(0x06)
      Encode::encode(@lib.U64(min), buf)
    }
    (I64Limits(min, Some(max)), true) => {
      buf.write_byte(0x07)
      match Encode::encode(@lib.U64(min), buf) {
        Err(err) => return Err(err)
        _ => ()
      }
      Encode::encode(@lib.U64(max), buf)
    }
  }
}

///|
pub impl Encode for GlobalType with fn encode(val, buf) {
  let GlobalType(vt, m) = val
  match Encode::encode(vt, buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  Encode::encode(m, buf)
}

///|
pub impl Encode for TagType with fn encode(val, buf) {
  buf.write_byte(0x00)
  Encode::encode(val.0, buf)
}

///|
pub impl Encode for FuncSec with fn encode(val, buf) {
  write_section(3, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
pub impl Encode for TableSec with fn encode(val, buf) {
  write_section(4, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
pub impl Encode for Table with fn encode(val, buf) {
  match val {
    Table(tt, None) => Encode::encode(tt, buf)
    Table(tt, Some(e)) => {
      buf.write_bytes(b"\x40\x00")
      match Encode::encode(tt, buf) {
        Err(err) => return Err(err)
        Ok(_) => Encode::encode(e, buf)
      }
    }
  }
}

///|
pub impl Encode for MemSec with fn encode(val, buf) {
  write_section(5, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
pub impl Encode for GlobalSec with fn encode(val, buf) {
  write_section(6, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
pub impl Encode for Global with fn encode(val, buf) {
  let Global(gt, e) = val
  match Encode::encode(gt, buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  Encode::encode(e, buf)
}

///|
pub impl Encode for ExportSec with fn encode(val, buf) {
  write_section(7, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
pub impl Encode for Export with fn encode(val, buf) {
  let Export(n, eidx) = val
  match Encode::encode(n, buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  Encode::encode(eidx, buf)
}

///|
pub impl Encode for StartSec with fn encode(val, buf) {
  // Canonical section framing: id + payload_len + payload.
  write_section(8, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
pub impl Encode for ElemSec with fn encode(val, buf) {
  write_section(9, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
pub impl Encode for Elem with fn encode(val, buf) {
  match val {
    Elem(Active(TableIdx(0), e), FuncsElemKind(y)) => {
      buf.write_byte(0x00)
      if Encode::encode(e, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(y, buf)
    }
    Elem(Passive, FuncsElemKind(y)) => {
      buf.write_bytes(b"\x01\x00")
      Encode::encode(y, buf)
    }
    Elem(Active(ti, e), FuncsElemKind(y)) => {
      buf.write_byte(0x02)
      if Encode::encode(ti, buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(e, buf) is Err(t) {
        return Err(t)
      }
      buf.write_byte(0x00)
      Encode::encode(y, buf)
    }
    Elem(Declarative, FuncsElemKind(y)) => {
      buf.write_bytes(b"\x03\x00")
      Encode::encode(y, buf)
    }
    Elem(Active(TableIdx(0), e), FuncExprsElemKind(es)) => {
      buf.write_byte(0x04)
      if Encode::encode(e, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(es, buf)
    }
    // Encoder fixes - add funcref reftype
    Elem(Passive, FuncExprsElemKind(es)) => {
      buf.write_byte(0x05)
      let rt = RefType::abs(AbsHeapType::func())
      if Encode::encode(rt, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(es, buf)
    }
    Elem(Active(ti, e), FuncExprsElemKind(es)) => {
      buf.write_byte(0x06)
      if Encode::encode(ti, buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(e, buf) is Err(t) {
        return Err(t)
      }
      let rt = RefType::abs(AbsHeapType::func())
      if Encode::encode(rt, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(es, buf)
    }
    Elem(Declarative, FuncExprsElemKind(es)) => {
      let rt = RefType::abs(AbsHeapType::func())
      buf.write_byte(0x07)
      if Encode::encode(rt, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(es, buf)
    }
    Elem(Passive, TypedExprsElemKind(rt, es)) => {
      buf.write_byte(0x05)
      if Encode::encode(rt, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(es, buf)
    }
    Elem(Declarative, TypedExprsElemKind(rt, es)) => {
      buf.write_byte(0x07)
      if Encode::encode(rt, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(es, buf)
    }
    Elem(Active(ti, e), TypedExprsElemKind(rt, es)) => {
      buf.write_byte(0x06)
      if Encode::encode(ti, buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(e, buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(rt, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(es, buf)
    }
  }
}

///|
pub impl Encode for CodeSec with fn encode(val, buf) {
  let CodeSec(fns) = val
  // Canonical section framing: id + payload_len + payload.
  write_section(10, buf, fn(payload) { Encode::encode(fns, payload) })
}

///|
pub impl Encode for Func with fn encode(val, buf) {
  let fn_body = @buffer.new()
  let Func(locals, expr) = val
  if Encode::encode(locals, fn_body) is Err(t) {
    return Err(t)
  }
  if Encode::encode(expr, fn_body) is Err(t) {
    return Err(t)
  }
  let fn_bytes = fn_body.to_bytes()
  if Encode::encode(@lib.U32(fn_bytes.length().reinterpret_as_uint()), buf)
    is Err(t) {
    return Err(t)
  }
  buf.write_bytes(fn_bytes) // Don't use Encode::encode here - just write raw bytes
  Ok(())
}

///|
pub impl Encode for DataSec with fn encode(val, buf) {
  write_section(11, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
pub impl Encode for Data with fn encode(val, buf) {
  match val {
    Data(Active(MemIdx(0), e), bytes) => {
      buf.write_byte(0x00)
      if Encode::encode(e, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(bytes, buf)
    }
    Data(Passive, bytes) => {
      buf.write_byte(0x01)
      Encode::encode(bytes, buf)
    }
    Data(Active(idx, e), bytes) => {
      buf.write_byte(0x02)
      if Encode::encode(idx, buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(e, buf) is Err(t) {
        return Err(t)
      }
      Encode::encode(bytes, buf)
    }
  }
}

///|
pub impl Encode for Locals with fn encode(val, buf) {
  Encode::encode(val.runs(), buf)
}

///|
pub impl Encode for LocalRun with fn encode(val, buf) {
  match Encode::encode(@lib.U32(val.count), buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  Encode::encode(val.vt, buf)
}

///|
pub impl Encode for DataCntSec with fn encode(val, buf) {
  let DataCntSec(count) = val
  // Canonical section framing: id + payload_len + payload.
  write_section(12, buf, fn(payload) { Encode::encode(count, payload) })
}

///|
pub impl Encode for TagSec with fn encode(val, buf) {
  write_section(13, buf, fn(payload) { Encode::encode(val.0, payload) })
}

///|
pub impl Encode for StringRefsSec with fn encode(val, buf) {
  let StringRefsSec(strings) = val
  write_section(14, buf, fn(payload) {
    payload.write_byte(0x00)
    Encode::encode(strings, payload)
  })
}

///|
pub impl Encode for CastOp with fn encode(val, buf) {
  match val {
    CastOp(false, false, _, _) => buf.write_byte(0x00)
    CastOp(true, false, _, _) => buf.write_byte(0x01)
    CastOp(false, true, _, _) => buf.write_byte(0x02)
    CastOp(true, true, _, _) => buf.write_byte(0x03)
  }
  Ok(())
}

///|
priv struct EncodeStringRefsPool {
  values : Array[Bytes]
  mut seen : @hashmap.HashMap[Bytes, Int]?
  membership_work : Array[Int]?
}

///|
fn EncodeStringRefsPool::new(
  membership_work : Array[Int]?,
) -> EncodeStringRefsPool {
  { values: [], seen: None, membership_work, }
}

///|
fn EncodeStringRefsPool::push_unique(
  self : EncodeStringRefsPool,
  bytes : Bytes,
) -> Unit {
  if self.seen is None && self.values.length() >= 32 {
    let seen : @hashmap.HashMap[Bytes, Int] = @hashmap.new(capacity=64)
    for index = 0; index < self.values.length(); index = index + 1 {
      seen.set(self.values[index], index)
    }
    self.seen = Some(seen)
  }
  if self.seen is Some(seen) {
    // Count membership requests, not internal hash-table collision comparisons.
    if self.membership_work is Some(work) {
      work[0] += 1
    }
    if seen.get(bytes) is Some(_) {
      return
    }
    seen.set(bytes, self.values.length())
    self.values.push(bytes)
    return
  }
  for existing in self.values {
    if self.membership_work is Some(work) {
      work[0] += 1
    }
    if existing == bytes {
      return
    }
  }
  self.values.push(bytes)
}

///|
/// Collect unique `string.const` payloads in declaration/global/code order.
/// The membership index never determines output order and belongs to this call.
fn encode_module_stringrefs(
  mod_ : Module,
  membership_work? : Array[Int]? = None,
) -> Array[Bytes] {
  encode_module_stringrefs_pool(mod_, membership_work~).values
}

///|
fn encode_module_stringrefs_pool(
  mod_ : Module,
  membership_work? : Array[Int]? = None,
  string_free_funcs? : Array[Func]? = None,
) -> EncodeStringRefsPool {
  /// Recurse through structured control instructions and collect string constants
  /// that can be reached in nested blocks for local declarations and exports.
  fn collect_instruction(
    instr : Instruction,
    out : EncodeStringRefsPool,
  ) -> Unit {
    match instr {
      StringConst(bytes) => out.push_unique(bytes)
      Block(_, Expr(instrs))
      | Loop(_, Expr(instrs))
      | TryTable(_, _, Expr(instrs)) =>
        for child in instrs {
          collect_instruction(child, out)
        }
      Try(_, Expr(instrs), catches, _) => {
        for child in instrs {
          collect_instruction(child, out)
        }
        for catch_ in catches {
          let Expr(catch_instrs) = match catch_ {
            @lib.LegacyCatch(_, body) | @lib.LegacyCatchAll(body) => body
          }
          for child in catch_instrs {
            collect_instruction(child, out)
          }
        }
      }
      If(_, if_block, else_block) => {
        for child in if_block {
          collect_instruction(child, out)
        }
        match else_block {
          Some(instrs) =>
            for child in instrs {
              collect_instruction(child, out)
            }
          None => ()
        }
      }
      _ => ()
    }
  }

  let out = EncodeStringRefsPool::new(membership_work)
  match mod_.stringrefs_sec {
    Some(StringRefsSec(strings)) =>
      for bytes in strings {
        out.push_unique(bytes)
      }
    None => ()
  }
  match mod_.global_sec {
    Some(GlobalSec(globals)) =>
      for global in globals {
        let Global(_, Expr(instrs)) = global
        for instr in instrs {
          collect_instruction(instr, out)
        }
      }
    None => ()
  }
  match mod_.code_sec {
    Some(CodeSec(funcs)) =>
      for index, func in funcs {
        // Only the exact-size comparison supplies this proof: its completed
        // first encode found no strings anywhere. Shared function objects
        // cannot acquire strings during that immutable comparison call.
        if string_free_funcs is Some(previous) &&
          index < previous.length() &&
          physical_equal(func, previous[index]) {
          continue
        }
        let Func(_, expr) = func
        let Expr(instrs) = expr
        for instr in instrs {
          collect_instruction(instr, out)
        }
      }
    None => ()
  }
  out
}

///|
pub impl Encode for Module with fn encode(val, buf) {
  encode_module_sections(val, buf, false)
}

///|
fn encode_module_sections(
  val : Module,
  buf : @buffer.Buffer,
  compact_imports : Bool,
  size_state? : EncodedModuleSizeState? = None,
) -> Result[Unit, BinaryEncodeError] {
  buf.write_bytes(b"\x00\x61\x73\x6D\x01\x00\x00\x00")
  // custom sections
  for sec in val.custom_secs {
    let CustomSec(name, _) = sec
    if name == Name::new("name") {
      return Err(BinaryEncodeError::RawNameCustomSectionUnsupported)
    }
    if name == Name::new("compiler.facts") {
      return Err(BinaryEncodeError::ReservedCompilerFactsCustomSection)
    }
    if Encode::encode(sec, buf) is Err(t) {
      return Err(t)
    }
  }
  match val.compiler_fact_custom_section {
    Some(section) =>
      if encode_compiler_facts_as_custom_section(section, buf) is Err(t) {
        return Err(t)
      }
    None => ()
  }
  if Encode::encode(val.type_sec, buf) is Err(t) {
    return Err(t)
  }
  match val.import_sec {
    Some(sec) => {
      let result = if compact_imports {
        encode_compact_import_section(sec, buf)
      } else {
        Encode::encode(sec, buf)
      }
      if result is Err(err) {
        return Err(err)
      }
    }
    None => ()
  }
  if Encode::encode(val.func_sec, buf) is Err(t) {
    return Err(t)
  }
  if Encode::encode(val.table_sec, buf) is Err(t) {
    return Err(t)
  }
  if Encode::encode(val.mem_sec, buf) is Err(t) {
    return Err(t)
  }
  if Encode::encode(val.tag_sec, buf) is Err(t) {
    return Err(t)
  }
  let string_free_funcs = match size_state {
    Some(state) =>
      match state.reuse {
        Some(previous) if previous.strings.is_empty() => Some(previous.funcs)
        _ => None
      }
    None => None
  }
  let string_pool = encode_module_stringrefs_pool(val, string_free_funcs~)
  let stringrefs = string_pool.values
  let stringrefs_sec = if stringrefs.length() > 0 ||
    val.stringrefs_sec is Some(_) {
    Some(StringRefsSec::new(stringrefs))
  } else {
    None
  }
  if with_binary_encode_stringrefs_context(
      if stringrefs.length() > 0 {
        Some(stringrefs)
      } else {
        None
      },
      fn() {
        if Encode::encode(stringrefs_sec, buf) is Err(t) {
          return Err(t)
        }
        if Encode::encode(val.global_sec, buf) is Err(t) {
          return Err(t)
        }
        if Encode::encode(val.export_sec, buf) is Err(t) {
          return Err(t)
        }
        if Encode::encode(val.start_sec, buf) is Err(t) {
          return Err(t)
        }
        if Encode::encode(val.elem_sec, buf) is Err(t) {
          return Err(t)
        }
        if Encode::encode(val.data_cnt_sec, buf) is Err(t) {
          return Err(t)
        }
        match val.code_sec {
          Some(CodeSec(funcs)) => {
            let encoded = match size_state {
              Some(state) => measure_module_code(funcs, stringrefs, state)
              None => Encode::encode(Some(CodeSec::new(funcs)), buf)
            }
            if encoded is Err(t) {
              return Err(t)
            }
          }
          None => ()
        }
        if Encode::encode(val.data_sec, buf) is Err(t) {
          return Err(t)
        }
        Ok(())
      },
      known_index=string_pool.seen,
    )
    is Err(t) {
    return Err(t)
  }
  match val.name_sec {
    Some(name_sec) =>
      match val.raw_name_sec_payload {
        Some(payload) =>
          if encode_raw_name_sec_payload_as_custom_section(payload, buf)
            is Err(t) {
            return Err(t)
          }
        None =>
          if encode_name_sec_as_custom_section(name_sec, buf) is Err(t) {
            return Err(t)
          }
      }
    None => ()
  }
  Ok(())
}

///|
pub impl Encode for Bytes with fn encode(val, buf) {
  match Encode::encode(@lib.U32(val.length().reinterpret_as_uint()), buf) {
    Err(err) => return Err(err)
    Ok(_) => ()
  }
  buf.write_bytes(val)
  Ok(())
}

///|
pub impl Encode for BlockType with fn encode(val, buf) {
  match val {
    VoidBlockType => {
      buf.write_byte(0x40)
      Ok(())
    }
    ValTypeBlockType(vt) => Encode::encode(vt, buf)
    TypeIdxBlockType(TypeIdx(idx)) => Encode::encode(@lib.S33(idx), buf)
    _ => Err(BinaryEncodeError::CannotEncodeRecursiveIndexBlockType)
  }
}

///|
pub impl Encode for Expr with fn encode(val, buf) {
  encode_control_work([ControlByte(0x0B), ControlSequence(val.0, 0)], buf)
}

///|
pub impl Encode for ResumeHandler with fn encode(val, buf) {
  match val {
    ResumeOnLabel(tag, label) => {
      buf.write_byte(0x00)
      if Encode::encode(tag, buf) is Err(err) {
        return Err(err)
      }
      Encode::encode(label, buf)
    }
    ResumeOnSwitch(tag) => {
      buf.write_byte(0x01)
      Encode::encode(tag, buf)
    }
  }
}

///|
pub impl Encode for Catch with fn encode(val, buf) {
  match val {
    Catch(t, l) => {
      buf.write_byte(0x00)
      if Encode::encode(t, buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Err(t)
      }
    }
    CatchRef(t, l) => {
      buf.write_byte(0x01)
      if Encode::encode(t, buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Err(t)
      }
    }
    CatchAll(l) => {
      buf.write_byte(0x02)
      if Encode::encode(l, buf) is Err(t) {
        return Err(t)
      }
    }
    CatchAllRef(l) => {
      buf.write_byte(0x03)
      if Encode::encode(l, buf) is Err(t) {
        return Err(t)
      }
    }
  }
  Ok(())
}

///|
pub impl Encode for MemArg with fn encode(val, buf) {
  match val {
    MemArg(U32(n), Some(@lib.MemIdx(0U)), m) if n <= 8 => {
      if Encode::encode(@lib.U32(n), buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Err(t)
      }
    }
    MemArg(U32(n), Some(i), m) if n <= 8 => {
      if Encode::encode(@lib.U32(n + 64), buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Err(t)
      }
    }
    MemArg(U32(n), None, m) if n <= 8 => {
      if Encode::encode(@lib.U32(n), buf) is Err(t) {
        return Err(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Err(t)
      }
    }
    _ => return Err(BinaryEncodeError::InvalidMemArgEncoding)
  }
  Ok(())
}

///|
fn encode_atomic_memarg(
  order : AtomicOrder,
  memarg : MemArg,
  buf : @buffer.Buffer,
  is_rmw : Bool,
) -> Result[Unit, BinaryEncodeError] {
  let MemArg(U32(align), memory, offset) = memarg
  if align >= 16U {
    return Err(BinaryEncodeError::InvalidMemArgEncoding)
  }
  let has_order = order != AtomicOrder::seq_cst()
  let memory_idx = match memory {
    Some(@lib.MemIdx(raw)) => raw
    None => 0U
  }
  let mut raw_align = align
  if has_order {
    raw_align = raw_align | 0x10U
  }
  if memory_idx > 0U {
    raw_align = raw_align | 0x40U
  }
  if Encode::encode(@lib.U32(raw_align), buf) is Err(err) {
    return Err(err)
  }
  if memory_idx > 0U &&
    Encode::encode(@lib.MemIdx::new(memory_idx), buf) is Err(err) {
    return Err(err)
  }
  if has_order {
    if is_rmw {
      encode_aggregate_atomic_rmw_order(order, buf)
    } else if Encode::encode(order, buf) is Err(err) {
      return Err(err)
    }
  }
  Encode::encode(offset, buf)
}

///|
fn encode_simd_error(id : UInt, buf : @buffer.Buffer) -> BinaryEncodeError? {
  buf.write_byte(0xFD)
  if Encode::encode(@lib.U32(id), buf) is Err(t) {
    return Some(t)
  }
  None
}

///|
fn atomic_inst(
  id : UInt,
  buf : @buffer.Buffer,
) -> Result[Unit, BinaryEncodeError] {
  buf.write_byte(0xFE)
  if Encode::encode(@lib.U32(id), buf) is Err(t) {
    return Err(t)
  }
  Ok(())
}

///|
fn atomic_rmw_op_id(op : AtomicRmwOp) -> UInt {
  match op {
    I32AtomicRmwAddOp => 30
    I64AtomicRmwAddOp => 31
    I32AtomicRmw8AddUOp => 32
    I32AtomicRmw16AddUOp => 33
    I64AtomicRmw8AddUOp => 34
    I64AtomicRmw16AddUOp => 35
    I64AtomicRmw32AddUOp => 36
    I32AtomicRmwSubOp => 37
    I64AtomicRmwSubOp => 38
    I32AtomicRmw8SubUOp => 39
    I32AtomicRmw16SubUOp => 40
    I64AtomicRmw8SubUOp => 41
    I64AtomicRmw16SubUOp => 42
    I64AtomicRmw32SubUOp => 43
    I32AtomicRmwAndOp => 44
    I64AtomicRmwAndOp => 45
    I32AtomicRmw8AndUOp => 46
    I32AtomicRmw16AndUOp => 47
    I64AtomicRmw8AndUOp => 48
    I64AtomicRmw16AndUOp => 49
    I64AtomicRmw32AndUOp => 50
    I32AtomicRmwOrOp => 51
    I64AtomicRmwOrOp => 52
    I32AtomicRmw8OrUOp => 53
    I32AtomicRmw16OrUOp => 54
    I64AtomicRmw8OrUOp => 55
    I64AtomicRmw16OrUOp => 56
    I64AtomicRmw32OrUOp => 57
    I32AtomicRmwXorOp => 58
    I64AtomicRmwXorOp => 59
    I32AtomicRmw8XorUOp => 60
    I32AtomicRmw16XorUOp => 61
    I64AtomicRmw8XorUOp => 62
    I64AtomicRmw16XorUOp => 63
    I64AtomicRmw32XorUOp => 64
    I32AtomicRmwXchgOp => 65
    I64AtomicRmwXchgOp => 66
    I32AtomicRmw8XchgUOp => 67
    I32AtomicRmw16XchgUOp => 68
    I64AtomicRmw8XchgUOp => 69
    I64AtomicRmw16XchgUOp => 70
    I64AtomicRmw32XchgUOp => 71
  }
}

///|
fn atomic_cmpxchg_op_id(op : AtomicCmpxchgOp) -> UInt {
  match op {
    I32AtomicRmwCmpxchgOp => 72
    I64AtomicRmwCmpxchgOp => 73
    I32AtomicRmw8CmpxchgUOp => 74
    I32AtomicRmw16CmpxchgUOp => 75
    I64AtomicRmw8CmpxchgUOp => 76
    I64AtomicRmw16CmpxchgUOp => 77
    I64AtomicRmw32CmpxchgUOp => 78
  }
}

///|
fn encode_backing_array_load_opcode(
  op : @lib.LoadOp,
  buf : @buffer.Buffer,
) -> Result[Unit, BinaryEncodeError] {
  match op {
    @lib.I32LoadOp => buf.write_byte(0x28)
    @lib.I64LoadOp => buf.write_byte(0x29)
    @lib.F32LoadOp => buf.write_byte(0x2A)
    @lib.F64LoadOp => buf.write_byte(0x2B)
    @lib.I32Load8SOp => buf.write_byte(0x2C)
    @lib.I32Load8UOp => buf.write_byte(0x2D)
    @lib.I32Load16SOp => buf.write_byte(0x2E)
    @lib.I32Load16UOp => buf.write_byte(0x2F)
    @lib.I64Load8SOp => buf.write_byte(0x30)
    @lib.I64Load8UOp => buf.write_byte(0x31)
    @lib.I64Load16SOp => buf.write_byte(0x32)
    @lib.I64Load16UOp => buf.write_byte(0x33)
    @lib.I64Load32SOp => buf.write_byte(0x34)
    @lib.I64Load32UOp => buf.write_byte(0x35)
    @lib.V128LoadOp => {
      buf.write_byte(0xfd)
      buf.write_byte(0x00)
    }
    _ => return Err(BinaryEncodeError::UnsupportedBackingArrayMemoryOp)
  }
  Ok(())
}

///|
fn encode_backing_array_store_opcode(
  op : @lib.StoreOp,
  buf : @buffer.Buffer,
) -> Result[Unit, BinaryEncodeError] {
  match op {
    @lib.I32StoreOp => buf.write_byte(0x36)
    @lib.I64StoreOp => buf.write_byte(0x37)
    @lib.F32StoreOp => buf.write_byte(0x38)
    @lib.F64StoreOp => buf.write_byte(0x39)
    @lib.I32Store8Op => buf.write_byte(0x3A)
    @lib.I32Store16Op => buf.write_byte(0x3B)
    @lib.I64Store8Op => buf.write_byte(0x3C)
    @lib.I64Store16Op => buf.write_byte(0x3D)
    @lib.I64Store32Op => buf.write_byte(0x3E)
    @lib.V128StoreOp => {
      buf.write_byte(0xfd)
      buf.write_byte(0x0b)
    }
    _ => return Err(BinaryEncodeError::UnsupportedBackingArrayMemoryOp)
  }
  Ok(())
}

///|
pub impl Encode for Instruction with fn encode(val, buf) {
  match val {
    Block(_, _)
    | Loop(_, _)
    | If(_, _, _)
    | Try(_, _, _, _)
    | TryTable(_, _, _) => encode_control_work([ControlInstruction(val)], buf)
    _ => encode_leaf_instruction(val, buf)
  }
}

///|
fn encode_leaf_instruction(
  val : Instruction,
  buf : @buffer.Buffer,
) -> Result[Unit, BinaryEncodeError] {
  match encode_leaf_instruction_error(val, buf) {
    None => Ok(())
    Some(err) => Err(err)
  }
}

///|
// Sequence encoding consumes an error only. None avoids one success Result
// allocation per leaf; the public Encode boundary retains its Result contract.
fn encode_leaf_instruction_error(
  val : Instruction,
  buf : @buffer.Buffer,
) -> BinaryEncodeError? {
  match val {
    Unreachable => buf.write_byte(0x00)
    Nop => buf.write_byte(0x01)
    Block(_, _)
    | Loop(_, _)
    | If(_, _, _)
    | Try(_, _, _, _)
    | TryTable(_, _, _) =>
      abort("structured instruction reached the leaf encoder")
    Throw(t) => {
      buf.write_byte(0x08)
      if Encode::encode(t, buf) is Err(t) {
        return Some(t)
      }
    }
    ThrowRef => buf.write_byte(0x0A)
    Rethrow(depth) => {
      buf.write_byte(0x09)
      if Encode::encode(depth, buf) is Err(t) {
        return Some(t)
      }
    }
    Br(i) => {
      buf.write_byte(0x0C)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    BrIf(i) => {
      buf.write_byte(0x0D)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    BrTable(ls, i) => {
      buf.write_byte(0x0E)
      if Encode::encode(ls, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    Return => buf.write_byte(0x0F)
    Call(i) => {
      buf.write_byte(0x10)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    CallIndirect(ty, ta) => {
      buf.write_byte(0x11)
      if Encode::encode(ty, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ta, buf) is Err(t) {
        return Some(t)
      }
    }
    ReturnCall(f) => {
      buf.write_byte(0x12)
      if Encode::encode(f, buf) is Err(t) {
        return Some(t)
      }
    }
    ReturnCallIndirect(ty, ta) => {
      buf.write_byte(0x13)
      if Encode::encode(ty, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ta, buf) is Err(t) {
        return Some(t)
      }
    }
    CallRef(t) => {
      buf.write_byte(0x14)
      if Encode::encode(t, buf) is Err(t) {
        return Some(t)
      }
    }
    ReturnCallRef(t) => {
      buf.write_byte(0x15)
      if Encode::encode(t, buf) is Err(t) {
        return Some(t)
      }
    }
    WaitqueueNew => {
      buf.write_byte(0xfe)
      buf.write_byte(0x07)
    }
    WaitqueueNotify => {
      buf.write_byte(0xfe)
      buf.write_byte(0x06)
    }
    Publish => {
      buf.write_byte(0xfe)
      buf.write_byte(0x0f)
    }
    StructWait(type_idx, field) => {
      buf.write_byte(0xfe)
      buf.write_byte(0x05)
      match Encode::encode(type_idx, buf) {
        Err(err) => return Some(err)
        Ok(_) => ()
      }
      match Encode::encode(field, buf) {
        Err(err) => return Some(err)
        Ok(_) => ()
      }
    }
    ContNew(type_idx) => {
      buf.write_byte(0xE0)
      if Encode::encode(type_idx, buf) is Err(t) {
        return Some(t)
      }
    }
    ContBind(source_type, target_type) => {
      buf.write_byte(0xE1)
      if Encode::encode(source_type, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(target_type, buf) is Err(t) {
        return Some(t)
      }
    }
    Suspend(tag) => {
      buf.write_byte(0xE2)
      if Encode::encode(tag, buf) is Err(t) {
        return Some(t)
      }
    }
    Resume(type_idx, handlers) => {
      buf.write_byte(0xE3)
      if Encode::encode(type_idx, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(handlers, buf) is Err(t) {
        return Some(t)
      }
    }
    ResumeThrow(type_idx, tag, handlers) => {
      buf.write_byte(0xE4)
      if Encode::encode(type_idx, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(tag, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(handlers, buf) is Err(t) {
        return Some(t)
      }
    }
    ResumeThrowRef(type_idx, handlers) => {
      buf.write_byte(0xE5)
      if Encode::encode(type_idx, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(handlers, buf) is Err(t) {
        return Some(t)
      }
    }
    StackSwitch(type_idx, tag) => {
      buf.write_byte(0xE6)
      if Encode::encode(type_idx, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(tag, buf) is Err(t) {
        return Some(t)
      }
    }
    Drop => buf.write_byte(0x1A)
    Select(None) => buf.write_byte(0x1b)
    Select(Some(vts)) => {
      buf.write_byte(0x1C)
      if Encode::encode(vts, buf) is Err(t) {
        return Some(t)
      }
    }
    LocalGet(l) => {
      buf.write_byte(0x20)
      let LocalIdx(index) = l
      if encode_unsigned_error(index.to_uint64(), buf, 32) is Some(t) {
        return Some(t)
      }
    }
    LocalSet(l) => {
      buf.write_byte(0x21)
      let LocalIdx(index) = l
      if encode_unsigned_error(index.to_uint64(), buf, 32) is Some(t) {
        return Some(t)
      }
    }
    LocalTee(l) => {
      buf.write_byte(0x22)
      let LocalIdx(index) = l
      if encode_unsigned_error(index.to_uint64(), buf, 32) is Some(t) {
        return Some(t)
      }
    }
    GlobalGet(g) => {
      buf.write_byte(0x23)
      if Encode::encode(g, buf) is Err(t) {
        return Some(t)
      }
    }
    GlobalSet(g) => {
      buf.write_byte(0x24)
      if Encode::encode(g, buf) is Err(t) {
        return Some(t)
      }
    }
    TableGet(i) => {
      buf.write_byte(0x25)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    TableSet(i) => {
      buf.write_byte(0x26)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    I32Load(m) => {
      buf.write_byte(0x28)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Load(m) => {
      buf.write_byte(0x29)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    F32Load(m) => {
      buf.write_byte(0x2A)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    F64Load(m) => {
      buf.write_byte(0x2B)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I32Load8S(m) => {
      buf.write_byte(0x2C)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I32Load8U(m) => {
      buf.write_byte(0x2D)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I32Load16S(m) => {
      buf.write_byte(0x2E)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I32Load16U(m) => {
      buf.write_byte(0x2F)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Load8S(m) => {
      buf.write_byte(0x30)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Load8U(m) => {
      buf.write_byte(0x31)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Load16S(m) => {
      buf.write_byte(0x32)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Load16U(m) => {
      buf.write_byte(0x33)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Load32S(m) => {
      buf.write_byte(0x34)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Load32U(m) => {
      buf.write_byte(0x35)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I32Store(m) => {
      buf.write_byte(0x36)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Store(m) => {
      buf.write_byte(0x37)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    F32Store(m) => {
      buf.write_byte(0x38)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    F64Store(m) => {
      buf.write_byte(0x39)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I32Store8(m) => {
      buf.write_byte(0x3A)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I32Store16(m) => {
      buf.write_byte(0x3B)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Store8(m) => {
      buf.write_byte(0x3C)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Store16(m) => {
      buf.write_byte(0x3D)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    I64Store32(m) => {
      buf.write_byte(0x3E)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    MemorySize(m) => {
      buf.write_byte(0x3F)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    MemoryGrow(m) => {
      buf.write_byte(0x40)
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    MemoryAtomicNotify(m) => {
      if atomic_inst(0, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    MemoryAtomicWait32(m) => {
      if atomic_inst(1, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    MemoryAtomicWait64(m) => {
      if atomic_inst(2, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    AtomicFence(order) => {
      if atomic_inst(3, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(order, buf) is Err(t) {
        return Some(t)
      }
    }
    I32AtomicLoad(order, m) => {
      if atomic_inst(16, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I64AtomicLoad(order, m) => {
      if atomic_inst(17, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I32AtomicLoad8U(order, m) => {
      if atomic_inst(18, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I32AtomicLoad16U(order, m) => {
      if atomic_inst(19, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I64AtomicLoad8U(order, m) => {
      if atomic_inst(20, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I64AtomicLoad16U(order, m) => {
      if atomic_inst(21, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I64AtomicLoad32U(order, m) => {
      if atomic_inst(22, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I32AtomicStore(order, m) => {
      if atomic_inst(23, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I64AtomicStore(order, m) => {
      if atomic_inst(24, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I32AtomicStore8(order, m) => {
      if atomic_inst(25, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I32AtomicStore16(order, m) => {
      if atomic_inst(26, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I64AtomicStore8(order, m) => {
      if atomic_inst(27, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I64AtomicStore16(order, m) => {
      if atomic_inst(28, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    I64AtomicStore32(order, m) => {
      if atomic_inst(29, buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, false) is Err(t) {
        return Some(t)
      }
    }
    AtomicRmw(order, op, m) => {
      if atomic_inst(atomic_rmw_op_id(op), buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, true) is Err(t) {
        return Some(t)
      }
    }
    AtomicCmpxchg(order, op, m) => {
      if atomic_inst(atomic_cmpxchg_op_id(op), buf) is Err(t) {
        return Some(t)
      }
      if encode_atomic_memarg(order, m, buf, true) is Err(t) {
        return Some(t)
      }
    }
    I32Const(c) => {
      buf.write_byte(0x41)
      let I32(value) = c
      if encode_signed_error(value.to_int64(), buf, 32) is Some(t) {
        return Some(t)
      }
    }
    I64Const(c) => {
      buf.write_byte(0x42)
      let I64(value) = c
      if encode_signed_error(value, buf, 64) is Some(t) {
        return Some(t)
      }
    }
    F32Const(c) => {
      buf.write_byte(0x43)
      if Encode::encode(c, buf) is Err(t) {
        return Some(t)
      }
    }
    F64Const(c) => {
      buf.write_byte(0x44)
      if Encode::encode(c, buf) is Err(t) {
        return Some(t)
      }
    }
    I32Eqz => buf.write_byte(0x45)
    I32Eq => buf.write_byte(0x46)
    I32Ne => buf.write_byte(0x47)
    I32LtS => buf.write_byte(0x48)
    I32LtU => buf.write_byte(0x49)
    I32GtS => buf.write_byte(0x4A)
    I32GtU => buf.write_byte(0x4B)
    I32LeS => buf.write_byte(0x4C)
    I32LeU => buf.write_byte(0x4D)
    I32GeS => buf.write_byte(0x4E)
    I32GeU => buf.write_byte(0x4F)
    I64Eqz => buf.write_byte(0x50)
    I64Eq => buf.write_byte(0x51)
    I64Ne => buf.write_byte(0x52)
    I64LtS => buf.write_byte(0x53)
    I64LtU => buf.write_byte(0x54)
    I64GtS => buf.write_byte(0x55)
    I64GtU => buf.write_byte(0x56)
    I64LeS => buf.write_byte(0x57)
    I64LeU => buf.write_byte(0x58)
    I64GeS => buf.write_byte(0x59)
    I64GeU => buf.write_byte(0x5A)
    F32Eq => buf.write_byte(0x5B)
    F32Ne => buf.write_byte(0x5C)
    F32Lt => buf.write_byte(0x5D)
    F32Gt => buf.write_byte(0x5E)
    F32Le => buf.write_byte(0x5F)
    F32Ge => buf.write_byte(0x60)
    F64Eq => buf.write_byte(0x61)
    F64Ne => buf.write_byte(0x62)
    F64Lt => buf.write_byte(0x63)
    F64Gt => buf.write_byte(0x64)
    F64Le => buf.write_byte(0x65)
    F64Ge => buf.write_byte(0x66)
    I32Clz => buf.write_byte(0x67)
    I32Ctz => buf.write_byte(0x68)
    I32Popcnt => buf.write_byte(0x69)
    I32Add => buf.write_byte(0x6A)
    I32Sub => buf.write_byte(0x6B)
    I32Mul => buf.write_byte(0x6C)
    I32DivS => buf.write_byte(0x6D)
    I32DivU => buf.write_byte(0x6E)
    I32RemS => buf.write_byte(0x6F)
    I32RemU => buf.write_byte(0x70)
    I32And => buf.write_byte(0x71)
    I32Or => buf.write_byte(0x72)
    I32Xor => buf.write_byte(0x73)
    I32Shl => buf.write_byte(0x74)
    I32ShrS => buf.write_byte(0x75)
    I32ShrU => buf.write_byte(0x76)
    I32Rotl => buf.write_byte(0x77)
    I32Rotr => buf.write_byte(0x78)
    I64Clz => buf.write_byte(0x79)
    I64Ctz => buf.write_byte(0x7A)
    I64Popcnt => buf.write_byte(0x7B)
    I64Add => buf.write_byte(0x7C)
    I64Sub => buf.write_byte(0x7D)
    I64Mul => buf.write_byte(0x7E)
    I64DivS => buf.write_byte(0x7F)
    I64DivU => buf.write_byte(0x80)
    I64RemS => buf.write_byte(0x81)
    I64RemU => buf.write_byte(0x82)
    I64And => buf.write_byte(0x83)
    I64Or => buf.write_byte(0x84)
    I64Xor => buf.write_byte(0x85)
    I64Shl => buf.write_byte(0x86)
    I64ShrS => buf.write_byte(0x87)
    I64ShrU => buf.write_byte(0x88)
    I64Rotl => buf.write_byte(0x89)
    I64Rotr => buf.write_byte(0x8A)
    F32Abs => buf.write_byte(0x8B)
    F32Neg => buf.write_byte(0x8C)
    F32Ceil => buf.write_byte(0x8D)
    F32Floor => buf.write_byte(0x8E)
    F32Trunc => buf.write_byte(0x8F)
    F32Nearest => buf.write_byte(0x90)
    F32Sqrt => buf.write_byte(0x91)
    F32Add => buf.write_byte(0x92)
    F32Sub => buf.write_byte(0x93)
    F32Mul => buf.write_byte(0x94)
    F32Div => buf.write_byte(0x95)
    F32Min => buf.write_byte(0x96)
    F32Max => buf.write_byte(0x97)
    F32Copysign => buf.write_byte(0x98)
    F64Abs => buf.write_byte(0x99)
    F64Neg => buf.write_byte(0x9A)
    F64Ceil => buf.write_byte(0x9B)
    F64Floor => buf.write_byte(0x9C)
    F64Trunc => buf.write_byte(0x9D)
    F64Nearest => buf.write_byte(0x9E)
    F64Sqrt => buf.write_byte(0x9F)
    F64Add => buf.write_byte(0xA0)
    F64Sub => buf.write_byte(0xA1)
    F64Mul => buf.write_byte(0xA2)
    F64Div => buf.write_byte(0xA3)
    F64Min => buf.write_byte(0xA4)
    F64Max => buf.write_byte(0xA5)
    F64Copysign => buf.write_byte(0xA6)
    I32WrapI64 => buf.write_byte(0xA7)
    I32TruncF32S => buf.write_byte(0xA8)
    I32TruncF32U => buf.write_byte(0xA9)
    I32TruncF64S => buf.write_byte(0xAA)
    I32TruncF64U => buf.write_byte(0xAB)
    I64ExtendI32S => buf.write_byte(0xAC)
    I64ExtendI32U => buf.write_byte(0xAD)
    I64TruncF32S => buf.write_byte(0xAE)
    I64TruncF32U => buf.write_byte(0xAF)
    I64TruncF64S => buf.write_byte(0xB0)
    I64TruncF64U => buf.write_byte(0xB1)
    F32ConvertI32S => buf.write_byte(0xB2)
    F32ConvertI32U => buf.write_byte(0xB3)
    F32ConvertI64S => buf.write_byte(0xB4)
    F32ConvertI64U => buf.write_byte(0xB5)
    F32DemoteF64 => buf.write_byte(0xB6)
    F64ConvertI32S => buf.write_byte(0xB7)
    F64ConvertI32U => buf.write_byte(0xB8)
    F64ConvertI64S => buf.write_byte(0xB9)
    F64ConvertI64U => buf.write_byte(0xBA)
    F64PromoteF32 => buf.write_byte(0xBB)
    I32ReinterpretF32 => buf.write_byte(0xBC)
    I64ReinterpretF64 => buf.write_byte(0xBD)
    F32ReinterpretI32 => buf.write_byte(0xBE)
    F64ReinterpretI64 => buf.write_byte(0xBF)
    I32Extend8S => buf.write_byte(0xC0)
    I32Extend16S => buf.write_byte(0xC1)
    I64Extend8S => buf.write_byte(0xC2)
    I64Extend16S => buf.write_byte(0xC3)
    I64Extend32S => buf.write_byte(0xC4)
    RefNull(rt) => {
      buf.write_byte(0xD0)
      if encode_ref_null_immediate(rt, buf) is Err(t) {
        return Some(t)
      }
    }
    RefIsNull => buf.write_byte(0xD1)
    RefFunc(i) => {
      buf.write_byte(0xD2)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    RefEq => buf.write_byte(0xD3)
    RefAsNonNull => buf.write_byte(0xD4)
    BrOnNull(i) => {
      buf.write_byte(0xD5)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    BrOnNonNull(i) => {
      buf.write_byte(0xD6)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    StructNew(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    StructNewDefault(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(1), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    StructNewDesc(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(32), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    StructNewDefaultDesc(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(33), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    StructGet(i, idx) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(2), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    StructGetS(i, idx) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(3), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    StructGetU(i, idx) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(4), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    StructAtomicSet(order, i, idx) => {
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(0x5F), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(order, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    StructAtomicGet(order, i, idx) => {
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(0x5C), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(order, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    StructAtomicGetS(order, i, idx) => {
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(0x5D), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(order, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    StructAtomicGetU(order, i, idx) => {
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(0x5E), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(order, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    StructAtomicRmw(order, op, i, idx) => {
      let id = match op {
        AggregateAtomicRmwAdd => 0x60U
        AggregateAtomicRmwSub => 0x61U
        AggregateAtomicRmwAnd => 0x62U
        AggregateAtomicRmwOr => 0x63U
        AggregateAtomicRmwXor => 0x64U
        AggregateAtomicRmwXchg => 0x65U
      }
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(id), buf) is Err(t) {
        return Some(t)
      }
      encode_aggregate_atomic_rmw_order(order, buf)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    StructAtomicCmpxchg(order, i, idx) => {
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(0x66), buf) is Err(t) {
        return Some(t)
      }
      encode_aggregate_atomic_rmw_order(order, buf)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    StructSet(i, idx) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(5), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayNew(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(6), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayNewDefault(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(7), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayNewFixed(i, n) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(8), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(n, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayNewData(i, idx) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(9), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayNewElem(i, idx) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(10), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayGet(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(11), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayGetS(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(12), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayGetU(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(13), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayAtomicSet(order, i) => {
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(0x6A), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(order, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayAtomicGet(order, i) => {
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(0x67), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(order, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayAtomicGetS(order, i) => {
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(0x68), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(order, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayAtomicGetU(order, i) => {
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(0x69), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(order, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArraySet(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(14), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayAtomicRmw(order, op, i) => {
      let id = match op {
        AggregateAtomicRmwAdd => 0x6BU
        AggregateAtomicRmwSub => 0x6CU
        AggregateAtomicRmwAnd => 0x6DU
        AggregateAtomicRmwOr => 0x6EU
        AggregateAtomicRmwXor => 0x6FU
        AggregateAtomicRmwXchg => 0x70U
      }
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(id), buf) is Err(t) {
        return Some(t)
      }
      encode_aggregate_atomic_rmw_order(order, buf)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayAtomicCmpxchg(order, i) => {
      buf.write_byte(0xFE)
      if Encode::encode(@lib.U32(0x71), buf) is Err(t) {
        return Some(t)
      }
      encode_aggregate_atomic_rmw_order(order, buf)
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayLoad(op, i, memarg) => {
      if encode_backing_array_load_opcode(op, buf) is Err(t) {
        return Some(t)
      }
      if encode_array_memory_argument(memarg, i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayStore(op, i, memarg) => {
      if encode_backing_array_store_opcode(op, buf) is Err(t) {
        return Some(t)
      }
      if encode_array_memory_argument(memarg, i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayLen => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(15), buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayFill(i) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(16), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(i, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayCopy(x0, x1) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(17), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(x0, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(x1, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayInitData(x, y) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(18), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(x, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(y, buf) is Err(t) {
        return Some(t)
      }
    }
    ArrayInitElem(x, y) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(19), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(x, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(y, buf) is Err(t) {
        return Some(t)
      }
    }
    StringConst(bytes) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0x82), buf) is Err(t) {
        return Some(t)
      }
      let idx = match encode_string_const_index(bytes) {
        Ok(idx) => idx
        Err(err) => return Some(err)
      }
      if Encode::encode(idx, buf) is Err(t) {
        return Some(t)
      }
    }
    StringMeasureWtf16 => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0x85), buf) is Err(t) {
        return Some(t)
      }
    }
    StringConcat => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0x88), buf) is Err(t) {
        return Some(t)
      }
    }
    StringEq => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0x89), buf) is Err(t) {
        return Some(t)
      }
    }
    StringAsWtf16 => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0x98), buf) is Err(t) {
        return Some(t)
      }
    }
    StringViewWtf16GetCodeunit => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0x9A), buf) is Err(t) {
        return Some(t)
      }
    }
    StringViewWtf16Slice => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0x9C), buf) is Err(t) {
        return Some(t)
      }
    }
    StringNewUtf8Array => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0xB0), buf) is Err(t) {
        return Some(t)
      }
    }
    StringNewWtf16Array => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0xB1), buf) is Err(t) {
        return Some(t)
      }
    }
    StringEncodeUtf8Array => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0xB2), buf) is Err(t) {
        return Some(t)
      }
    }
    StringEncodeWtf16Array => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0xB3), buf) is Err(t) {
        return Some(t)
      }
    }
    StringNewLossyUtf8Array => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0xB4), buf) is Err(t) {
        return Some(t)
      }
    }
    StringNewWtf8Array => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0xB5), buf) is Err(t) {
        return Some(t)
      }
    }
    StringEncodeLossyUtf8Array => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0xB6), buf) is Err(t) {
        return Some(t)
      }
    }
    StringEncodeWtf8Array => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(0xB7), buf) is Err(t) {
        return Some(t)
      }
    }
    RefTest(false, exact, ht) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(20), buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(exact, ht, buf) is Err(t) {
        return Some(t)
      }
    }
    RefTest(true, exact, ht) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(21), buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(exact, ht, buf) is Err(t) {
        return Some(t)
      }
    }
    RefCast(false, exact, ht) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(22), buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(exact, ht, buf) is Err(t) {
        return Some(t)
      }
    }
    RefCast(true, exact, ht) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(23), buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(exact, ht, buf) is Err(t) {
        return Some(t)
      }
    }
    RefGetDesc(ti) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(34), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ti, buf) is Err(t) {
        return Some(t)
      }
    }
    RefTestDesc(false, ht) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(35), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ht, buf) is Err(t) {
        return Some(t)
      }
    }
    RefTestDesc(true, ht) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(36), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ht, buf) is Err(t) {
        return Some(t)
      }
    }
    RefCastDescEq(false, exact, ht) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(35), buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(exact, ht, buf) is Err(t) {
        return Some(t)
      }
    }
    RefCastDescEq(true, exact, ht) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(36), buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(exact, ht, buf) is Err(t) {
        return Some(t)
      }
    }
    BrOnCast(l, castop, ht0, ht1) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(24), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(castop, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(castop.source_exact(), ht0, buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(castop.target_exact(), ht1, buf) is Err(t) {
        return Some(t)
      }
    }
    BrOnCastFail(l, castop, ht0, ht1) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(25), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(castop, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(castop.source_exact(), ht0, buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(castop.target_exact(), ht1, buf) is Err(t) {
        return Some(t)
      }
    }
    BrOnCastDescEq(l, source, target) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(37), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(
          CastOp::new(source.is_nullable(), target.is_nullable()),
          buf,
        )
        is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(source.is_exact(), source.get_heap_type(), buf)
        is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(target.is_exact(), target.get_heap_type(), buf)
        is Err(t) {
        return Some(t)
      }
    }
    BrOnCastDescEqFail(l, source, target) => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(38), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(
          CastOp::new(source.is_nullable(), target.is_nullable()),
          buf,
        )
        is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(source.is_exact(), source.get_heap_type(), buf)
        is Err(t) {
        return Some(t)
      }
      if encode_ref_heap_type(target.is_exact(), target.get_heap_type(), buf)
        is Err(t) {
        return Some(t)
      }
    }
    AnyConvertExtern => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(26), buf) is Err(t) {
        return Some(t)
      }
    }
    ExternConvertAny => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(27), buf) is Err(t) {
        return Some(t)
      }
    }
    RefI31 => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(28), buf) is Err(t) {
        return Some(t)
      }
    }
    RefI31Shared => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(31), buf) is Err(t) {
        return Some(t)
      }
    }
    I31GetS => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(29), buf) is Err(t) {
        return Some(t)
      }
    }
    I31GetU => {
      buf.write_byte(0xFB)
      if Encode::encode(@lib.U32(30), buf) is Err(t) {
        return Some(t)
      }
    }
    I32TruncSatF32S => buf.write_bytes(b"\xFC\x00")
    I32TruncSatF32U => buf.write_bytes(b"\xFC\x01")
    I32TruncSatF64S => buf.write_bytes(b"\xFC\x02")
    I32TruncSatF64U => buf.write_bytes(b"\xFC\x03")
    I64TruncSatF32S => buf.write_bytes(b"\xFC\x04")
    I64TruncSatF32U => buf.write_bytes(b"\xFC\x05")
    I64TruncSatF64S => buf.write_bytes(b"\xFC\x06")
    I64TruncSatF64U => buf.write_bytes(b"\xFC\x07")
    MemoryInit(di, mi) => {
      buf.write_byte(0xFC)
      if Encode::encode(@lib.U32(8), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(di, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(mi, buf) is Err(t) {
        return Some(t)
      }
    }
    DataDrop(di) => {
      buf.write_byte(0xFC)
      if Encode::encode(@lib.U32(9), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(di, buf) is Err(t) {
        return Some(t)
      }
    }
    MemoryCopy(mi0, mi1) => {
      buf.write_byte(0xFC)
      if Encode::encode(@lib.U32(10), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(mi0, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(mi1, buf) is Err(t) {
        return Some(t)
      }
    }
    MemoryFill(mi) => {
      buf.write_byte(0xFC)
      if Encode::encode(@lib.U32(11), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(mi, buf) is Err(t) {
        return Some(t)
      }
    }
    TableInit(ei, ti) => {
      buf.write_byte(0xFC)
      if Encode::encode(@lib.U32(12), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ei, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ti, buf) is Err(t) {
        return Some(t)
      }
    }
    ElemDrop(ei) => {
      buf.write_byte(0xFC)
      if Encode::encode(@lib.U32(13), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ei, buf) is Err(t) {
        return Some(t)
      }
    }
    TableCopy(ti0, ti1) => {
      buf.write_byte(0xFC)
      if Encode::encode(@lib.U32(14), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ti0, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ti1, buf) is Err(t) {
        return Some(t)
      }
    }
    TableGrow(ti) => {
      buf.write_byte(0xFC)
      if Encode::encode(@lib.U32(15), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ti, buf) is Err(t) {
        return Some(t)
      }
    }
    TableSize(ti) => {
      buf.write_byte(0xFC)
      if Encode::encode(@lib.U32(16), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ti, buf) is Err(t) {
        return Some(t)
      }
    }
    TableFill(ti) => {
      buf.write_byte(0xFC)
      if Encode::encode(@lib.U32(17), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(ti, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(0), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load8x8S(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(1), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load8x8U(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(2), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load16x4S(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(3), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load16x4U(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(4), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load32x2S(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(5), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load32x2U(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(6), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load8Splat(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(7), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load16Splat(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(8), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load32Splat(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(9), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load64Splat(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(10), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Store(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(11), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Const(
      b0,
      b1,
      b2,
      b3,
      b4,
      b5,
      b6,
      b7,
      b8,
      b9,
      b10,
      b11,
      b12,
      b13,
      b14,
      b15
    ) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(12), buf) is Err(t) {
        return Some(t)
      }
      buf.write_byte(b0)
      buf.write_byte(b1)
      buf.write_byte(b2)
      buf.write_byte(b3)
      buf.write_byte(b4)
      buf.write_byte(b5)
      buf.write_byte(b6)
      buf.write_byte(b7)
      buf.write_byte(b8)
      buf.write_byte(b9)
      buf.write_byte(b10)
      buf.write_byte(b11)
      buf.write_byte(b12)
      buf.write_byte(b13)
      buf.write_byte(b14)
      buf.write_byte(b15)
    }
    I8x16Shuffle(
      b0,
      b1,
      b2,
      b3,
      b4,
      b5,
      b6,
      b7,
      b8,
      b9,
      b10,
      b11,
      b12,
      b13,
      b14,
      b15
    ) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(13), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b0, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b1, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b2, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b3, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b4, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b5, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b6, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b7, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b8, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b9, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b10, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b11, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b12, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b13, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b14, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(b15, buf) is Err(t) {
        return Some(t)
      }
    }
    I8x16Swizzle => return encode_simd_error(14, buf)
    I8x16Splat => return encode_simd_error(15, buf)
    I16x8Splat => return encode_simd_error(16, buf)
    I32x4Splat => return encode_simd_error(17, buf)
    I64x2Splat => return encode_simd_error(18, buf)
    F32x4Splat => return encode_simd_error(19, buf)
    F64x2Splat => return encode_simd_error(20, buf)
    I8x16ExtractLaneS(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(21), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    I8x16ExtractLaneU(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(22), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    I8x16ReplaceLane(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(23), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    I16x8ExtractLaneS(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(24), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    I16x8ExtractLaneU(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(25), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    I16x8ReplaceLane(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(26), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    I32x4ExtractLane(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(27), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    I32x4ReplaceLane(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(28), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    I64x2ExtractLane(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(29), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    I64x2ReplaceLane(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(30), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    F32x4ExtractLane(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(31), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    F32x4ReplaceLane(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(32), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    F64x2ExtractLane(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(33), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    F64x2ReplaceLane(l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(34), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    I8x16Eq => return encode_simd_error(35, buf)
    I8x16Ne => return encode_simd_error(36, buf)
    I8x16LtS => return encode_simd_error(37, buf)
    I8x16LtU => return encode_simd_error(38, buf)
    I8x16GtS => return encode_simd_error(39, buf)
    I8x16GtU => return encode_simd_error(40, buf)
    I8x16LeS => return encode_simd_error(41, buf)
    I8x16LeU => return encode_simd_error(42, buf)
    I8x16GeS => return encode_simd_error(43, buf)
    I8x16GeU => return encode_simd_error(44, buf)
    I16x8Eq => return encode_simd_error(45, buf)
    I16x8Ne => return encode_simd_error(46, buf)
    I16x8LtS => return encode_simd_error(47, buf)
    I16x8LtU => return encode_simd_error(48, buf)
    I16x8GtS => return encode_simd_error(49, buf)
    I16x8GtU => return encode_simd_error(50, buf)
    I16x8LeS => return encode_simd_error(51, buf)
    I16x8LeU => return encode_simd_error(52, buf)
    I16x8GeS => return encode_simd_error(53, buf)
    I16x8GeU => return encode_simd_error(54, buf)
    I32x4Eq => return encode_simd_error(55, buf)
    I32x4Ne => return encode_simd_error(56, buf)
    I32x4LtS => return encode_simd_error(57, buf)
    I32x4LtU => return encode_simd_error(58, buf)
    I32x4GtS => return encode_simd_error(59, buf)
    I32x4GtU => return encode_simd_error(60, buf)
    I32x4LeS => return encode_simd_error(61, buf)
    I32x4LeU => return encode_simd_error(62, buf)
    I32x4GeS => return encode_simd_error(63, buf)
    I32x4GeU => return encode_simd_error(64, buf)
    F32x4Eq => return encode_simd_error(65, buf)
    F32x4Ne => return encode_simd_error(66, buf)
    F32x4Lt => return encode_simd_error(67, buf)
    F32x4Gt => return encode_simd_error(68, buf)
    F32x4Le => return encode_simd_error(69, buf)
    F32x4Ge => return encode_simd_error(70, buf)
    F64x2Eq => return encode_simd_error(71, buf)
    F64x2Ne => return encode_simd_error(72, buf)
    F64x2Lt => return encode_simd_error(73, buf)
    F64x2Gt => return encode_simd_error(74, buf)
    F64x2Le => return encode_simd_error(75, buf)
    F64x2Ge => return encode_simd_error(76, buf)
    V128Not => return encode_simd_error(77, buf)
    V128And => return encode_simd_error(78, buf)
    V128Andnot => return encode_simd_error(79, buf)
    V128Or => return encode_simd_error(80, buf)
    V128Xor => return encode_simd_error(81, buf)
    V128Bitselect => return encode_simd_error(82, buf)
    V128AnyTrue => return encode_simd_error(83, buf)
    V128Load8Lane(m, l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(84), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load16Lane(m, l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(85), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load32Lane(m, l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(86), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load64Lane(m, l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(87), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Store8Lane(m, l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(88), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Store16Lane(m, l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(89), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Store32Lane(m, l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(90), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Store64Lane(m, l) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(91), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(l, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load32Zero(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(92), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    V128Load64Zero(m) => {
      buf.write_byte(0xFD)
      if Encode::encode(@lib.U32(93), buf) is Err(t) {
        return Some(t)
      }
      if Encode::encode(m, buf) is Err(t) {
        return Some(t)
      }
    }
    F32x4DemoteF64x2Zero => return encode_simd_error(94, buf)
    F64x2PromoteLowF32x4 => return encode_simd_error(95, buf)
    I8x16Abs => return encode_simd_error(96, buf)
    I8x16Neg => return encode_simd_error(97, buf)
    I8x16Popcnt => return encode_simd_error(98, buf)
    I8x16AllTrue => return encode_simd_error(99, buf)
    I8x16Bitmask => return encode_simd_error(100, buf)
    I8x16NarrowI16x8S => return encode_simd_error(101, buf)
    I8x16NarrowI16x8U => return encode_simd_error(102, buf)
    F32x4Ceil => return encode_simd_error(103, buf)
    F32x4Floor => return encode_simd_error(104, buf)
    F32x4Trunc => return encode_simd_error(105, buf)
    F32x4Nearest => return encode_simd_error(106, buf)
    I8x16Shl => return encode_simd_error(107, buf)
    I8x16ShrS => return encode_simd_error(108, buf)
    I8x16ShrU => return encode_simd_error(109, buf)
    I8x16Add => return encode_simd_error(110, buf)
    I8x16AddSatS => return encode_simd_error(111, buf)
    I8x16AddSatU => return encode_simd_error(112, buf)
    I8x16Sub => return encode_simd_error(113, buf)
    I8x16SubSatS => return encode_simd_error(114, buf)
    I8x16SubSatU => return encode_simd_error(115, buf)
    F64x2Ceil => return encode_simd_error(116, buf)
    F64x2Floor => return encode_simd_error(117, buf)
    I8x16MinS => return encode_simd_error(118, buf)
    I8x16MinU => return encode_simd_error(119, buf)
    I8x16MaxS => return encode_simd_error(120, buf)
    I8x16MaxU => return encode_simd_error(121, buf)
    F64x2Trunc => return encode_simd_error(122, buf)
    I8x16AvgrU => return encode_simd_error(123, buf)
    I16x8ExtaddPairwiseI8x16S => return encode_simd_error(124, buf)
    I16x8ExtaddPairwiseI8x16U => return encode_simd_error(125, buf)
    I32x4ExtaddPairwiseI16x8S => return encode_simd_error(126, buf)
    I32x4ExtaddPairwiseI16x8U => return encode_simd_error(127, buf)
    I16x8Abs => return encode_simd_error(128, buf)
    I16x8Neg => return encode_simd_error(129, buf)
    I16x8Q15mulrSatS => return encode_simd_error(130, buf)
    I16x8AllTrue => return encode_simd_error(131, buf)
    I16x8Bitmask => return encode_simd_error(132, buf)
    I16x8NarrowI32x4S => return encode_simd_error(133, buf)
    I16x8NarrowI32x4U => return encode_simd_error(134, buf)
    I16x8ExtendLowI8x16S => return encode_simd_error(135, buf)
    I16x8ExtendHighI8x16S => return encode_simd_error(136, buf)
    I16x8ExtendLowI8x16U => return encode_simd_error(137, buf)
    I16x8ExtendHighI8x16U => return encode_simd_error(138, buf)
    I16x8Shl => return encode_simd_error(139, buf)
    I16x8ShrS => return encode_simd_error(140, buf)
    I16x8ShrU => return encode_simd_error(141, buf)
    I16x8Add => return encode_simd_error(142, buf)
    I16x8AddSatS => return encode_simd_error(143, buf)
    I16x8AddSatU => return encode_simd_error(144, buf)
    I16x8Sub => return encode_simd_error(145, buf)
    I16x8SubSatS => return encode_simd_error(146, buf)
    I16x8SubSatU => return encode_simd_error(147, buf)
    F64x2Nearest => return encode_simd_error(148, buf)
    I16x8Mul => return encode_simd_error(149, buf)
    I16x8MinS => return encode_simd_error(150, buf)
    I16x8MinU => return encode_simd_error(151, buf)
    I16x8MaxS => return encode_simd_error(152, buf)
    I16x8MaxU => return encode_simd_error(153, buf)
    I16x8AvgrU => return encode_simd_error(155, buf)
    I16x8ExtmulLowI8x16S => return encode_simd_error(156, buf)
    I16x8ExtmulHighI8x16S => return encode_simd_error(157, buf)
    I16x8ExtmulLowI8x16U => return encode_simd_error(158, buf)
    I16x8ExtmulHighI8x16U => return encode_simd_error(159, buf)
    I32x4Abs => return encode_simd_error(160, buf)
    I32x4Neg => return encode_simd_error(161, buf)
    I32x4AllTrue => return encode_simd_error(163, buf)
    I32x4Bitmask => return encode_simd_error(164, buf)
    I32x4ExtendLowI16x8S => return encode_simd_error(167, buf)
    I32x4ExtendHighI16x8S => return encode_simd_error(168, buf)
    I32x4ExtendLowI16x8U => return encode_simd_error(169, buf)
    I32x4ExtendHighI16x8U => return encode_simd_error(170, buf)
    I32x4Shl => return encode_simd_error(171, buf)
    I32x4ShrS => return encode_simd_error(172, buf)
    I32x4ShrU => return encode_simd_error(173, buf)
    I32x4Add => return encode_simd_error(174, buf)
    I32x4Sub => return encode_simd_error(177, buf)
    I32x4Mul => return encode_simd_error(181, buf)
    I32x4MinS => return encode_simd_error(182, buf)
    I32x4MinU => return encode_simd_error(183, buf)
    I32x4MaxS => return encode_simd_error(184, buf)
    I32x4MaxU => return encode_simd_error(185, buf)
    I32x4DotI16x8S => return encode_simd_error(186, buf)
    I32x4ExtmulLowI16x8S => return encode_simd_error(188, buf)
    I32x4ExtmulHighI16x8S => return encode_simd_error(189, buf)
    I32x4ExtmulLowI16x8U => return encode_simd_error(190, buf)
    I32x4ExtmulHighI16x8U => return encode_simd_error(191, buf)
    I64x2Abs => return encode_simd_error(192, buf)
    I64x2Neg => return encode_simd_error(193, buf)
    I64x2AllTrue => return encode_simd_error(195, buf)
    I64x2Bitmask => return encode_simd_error(196, buf)
    I64x2ExtendLowI32x4S => return encode_simd_error(199, buf)
    I64x2ExtendHighI32x4S => return encode_simd_error(200, buf)
    I64x2ExtendLowI32x4U => return encode_simd_error(201, buf)
    I64x2ExtendHighI32x4U => return encode_simd_error(202, buf)
    I64x2Shl => return encode_simd_error(203, buf)
    I64x2ShrS => return encode_simd_error(204, buf)
    I64x2ShrU => return encode_simd_error(205, buf)
    I64x2Add => return encode_simd_error(206, buf)
    I64x2Sub => return encode_simd_error(209, buf)
    I64x2Mul => return encode_simd_error(213, buf)
    I64x2Eq => return encode_simd_error(214, buf)
    I64x2Ne => return encode_simd_error(215, buf)
    I64x2LtS => return encode_simd_error(216, buf)
    I64x2GtS => return encode_simd_error(217, buf)
    I64x2LeS => return encode_simd_error(218, buf)
    I64x2GeS => return encode_simd_error(219, buf)
    I64x2ExtmulLowI32x4S => return encode_simd_error(220, buf)
    I64x2ExtmulHighI32x4S => return encode_simd_error(221, buf)
    I64x2ExtmulLowI32x4U => return encode_simd_error(222, buf)
    I64x2ExtmulHighI32x4U => return encode_simd_error(223, buf)
    F32x4Abs => return encode_simd_error(224, buf)
    F32x4Neg => return encode_simd_error(225, buf)
    F32x4Sqrt => return encode_simd_error(227, buf)
    F32x4Add => return encode_simd_error(228, buf)
    F32x4Sub => return encode_simd_error(229, buf)
    F32x4Mul => return encode_simd_error(230, buf)
    F32x4Div => return encode_simd_error(231, buf)
    F32x4Min => return encode_simd_error(232, buf)
    F32x4Max => return encode_simd_error(233, buf)
    F32x4Pmin => return encode_simd_error(234, buf)
    F32x4Pmax => return encode_simd_error(235, buf)
    F64x2Abs => return encode_simd_error(236, buf)
    F64x2Neg => return encode_simd_error(237, buf)
    F64x2Sqrt => return encode_simd_error(239, buf)
    F64x2Add => return encode_simd_error(240, buf)
    F64x2Sub => return encode_simd_error(241, buf)
    F64x2Mul => return encode_simd_error(242, buf)
    F64x2Div => return encode_simd_error(243, buf)
    F64x2Min => return encode_simd_error(244, buf)
    F64x2Max => return encode_simd_error(245, buf)
    F64x2Pmin => return encode_simd_error(246, buf)
    F64x2Pmax => return encode_simd_error(247, buf)
    I32x4TruncSatF32x4S => return encode_simd_error(248, buf)
    I32x4TruncSatF32x4U => return encode_simd_error(249, buf)
    F32x4ConvertI32x4S => return encode_simd_error(250, buf)
    F32x4ConvertI32x4U => return encode_simd_error(251, buf)
    I32x4TruncSatF64x2SZero => return encode_simd_error(252, buf)
    I32x4TruncSatF64x2UZero => return encode_simd_error(253, buf)
    F64x2ConvertLowI32x4S => return encode_simd_error(254, buf)
    F64x2ConvertLowI32x4U => return encode_simd_error(255, buf)
    I8x16RelaxedSwizzle => return encode_simd_error(256, buf)
    I32x4RelaxedTruncF32x4S => return encode_simd_error(257, buf)
    I32x4RelaxedTruncF32x4U => return encode_simd_error(258, buf)
    I32x4RelaxedTruncZeroF64x2S => return encode_simd_error(259, buf)
    I32x4RelaxedTruncZeroF64x2U => return encode_simd_error(260, buf)
    F32x4RelaxedMadd => return encode_simd_error(261, buf)
    F32x4RelaxedNmadd => return encode_simd_error(262, buf)
    F64x2RelaxedMadd => return encode_simd_error(263, buf)
    F64x2RelaxedNmadd => return encode_simd_error(264, buf)
    I8x16RelaxedLaneselect => return encode_simd_error(265, buf)
    I16x8RelaxedLaneselect => return encode_simd_error(266, buf)
    I32x4RelaxedLaneselect => return encode_simd_error(267, buf)
    I64x2RelaxedLaneselect => return encode_simd_error(268, buf)
    F32x4RelaxedMin => return encode_simd_error(269, buf)
    F32x4RelaxedMax => return encode_simd_error(270, buf)
    F64x2RelaxedMin => return encode_simd_error(271, buf)
    F64x2RelaxedMax => return encode_simd_error(272, buf)
    I16x8RelaxedQ15mulrS => return encode_simd_error(273, buf)
    I16x8RelaxedDotI8x16I7x16S => return encode_simd_error(274, buf)
    I32x4RelaxedDotI8x16I7x16AddS => return encode_simd_error(275, buf)
  }
  None
}

///|
pub impl[T : Encode] Encode for Array[T] with fn encode(val, buf) {
  if Encode::encode(@lib.U32(val.length().reinterpret_as_uint()), buf) is Err(t) {
    return Err(t)
  }
  for v in val {
    if Encode::encode(v, buf) is Err(t) {
      return Err(t)
    }
  }
  Ok(())
}

///|
fn encode_array_memory_argument(
  ma : MemArg,
  ti : @lib.TypeIdx,
  buf : @buffer.Buffer,
) -> Result[Unit, BinaryEncodeError] {
  let MemArg(U32(align), memory, @lib.U64(offset)) = ma
  if memory is Some(_) || align > 8U || offset > 0xffffffffUL {
    return Err(BinaryEncodeError::InvalidMemArgEncoding)
  }
  if Encode::encode(@lib.U32(align | 0x20U), buf) is Err(err) {
    return Err(err)
  }
  if Encode::encode(ti, buf) is Err(err) {
    return Err(err)
  }
  Encode::encode(@lib.U32(offset.to_uint()), buf)
}