///|
pub suberror BinaryDecodeError {
  IndexOutOfBounds
  InvalidNumTypeByte
  InvalidAbstractHeapType
  InvalidValType
  InvalidCompType
  UnexpectedEofDecodingMut
  InvalidMutByte
  InvalidStorageType
  InvalidPackedType
  InvalidUnsignedLebBitWidth
  UnexpectedEofInUnsignedLeb
  UnsignedLebTooManyBytes
  UnsignedLebOutOfRangeForBitWidth
  UnsignedLebTerminalHasNonZeroUnusedBits
  UnreachableDecodeState
  InvalidSignedLebBitWidth
  UnexpectedEofInSignedLeb
  SignedLebTooManyBytes
  SignedLebOutOfRangeForBitWidth
  SignedLebPositiveTerminalHasNonZeroUnusedBits
  SignedLebTerminalHasWrongSignExtensionBits
  InvalidS33Range
  InvalidBoolByte
  InvalidLaneIndex
  NegativeNameLength
  InvalidUtf8Name
  InvalidSectionHeader
  SectionPayloadLengthOutOfRange
  InvalidSectionPayloadTrailingBytes
  DuplicateNameSection
  DuplicateCompilerFactsSection
  InvalidCompilerFactsEnumTag
  InvalidCompilerFactsOptionTag
  TruncatedCompilerFactsPayload
  CompilerFactsTrailingBytes
  UnsupportedCompilerFactsVersion
  InvalidNameSubsectionId
  InvalidNameSubsectionOrder
  InvalidNameMapOrder
  InvalidMagicHeader
  InvalidModule
  InvalidElemHeader
  InvalidDataHeader
  CustomSectionPayloadLengthOutOfRange
  InvalidLimits
  InvalidTagType
  InvalidExternTypeHeader
  InvalidBlockType
  InvalidExternIndex
  InvalidOffset
  InvalidCatch
  InvalidCastOp
  InvalidMemArg
  InvalidStringRefSection
  InvalidStringLiteralIndex
  InvalidInstruction
  InvalidAtomicFenceImmediate
  InvalidAtomicInstruction
  InstructionNestingLimitExceeded
} derive(Eq, Debug)

///|
pub impl Show for BinaryDecodeError with fn output(self, logger) {
  match self {
    IndexOutOfBounds => logger.write_string("IndexOutOfBounds")
    InvalidNumTypeByte => logger.write_string("InvalidNumTypeByte")
    InvalidAbstractHeapType => logger.write_string("InvalidAbstractHeapType")
    InvalidValType => logger.write_string("InvalidValType")
    InvalidCompType => logger.write_string("InvalidCompType")
    UnexpectedEofDecodingMut => logger.write_string("UnexpectedEofDecodingMut")
    InvalidMutByte => logger.write_string("InvalidMutByte")
    InvalidStorageType => logger.write_string("InvalidStorageType")
    InvalidPackedType => logger.write_string("InvalidPackedType")
    InvalidUnsignedLebBitWidth =>
      logger.write_string("InvalidUnsignedLebBitWidth")
    UnexpectedEofInUnsignedLeb =>
      logger.write_string("UnexpectedEofInUnsignedLeb")
    UnsignedLebTooManyBytes => logger.write_string("UnsignedLebTooManyBytes")
    UnsignedLebOutOfRangeForBitWidth =>
      logger.write_string("UnsignedLebOutOfRangeForBitWidth")
    UnsignedLebTerminalHasNonZeroUnusedBits =>
      logger.write_string("UnsignedLebTerminalHasNonZeroUnusedBits")
    UnreachableDecodeState => logger.write_string("UnreachableDecodeState")
    InvalidSignedLebBitWidth => logger.write_string("InvalidSignedLebBitWidth")
    UnexpectedEofInSignedLeb => logger.write_string("UnexpectedEofInSignedLeb")
    SignedLebTooManyBytes => logger.write_string("SignedLebTooManyBytes")
    SignedLebOutOfRangeForBitWidth =>
      logger.write_string("SignedLebOutOfRangeForBitWidth")
    SignedLebPositiveTerminalHasNonZeroUnusedBits =>
      logger.write_string("SignedLebPositiveTerminalHasNonZeroUnusedBits")
    SignedLebTerminalHasWrongSignExtensionBits =>
      logger.write_string("SignedLebTerminalHasWrongSignExtensionBits")
    InvalidS33Range => logger.write_string("InvalidS33Range")
    InvalidBoolByte => logger.write_string("InvalidBoolByte")
    InvalidLaneIndex => logger.write_string("InvalidLaneIndex")
    NegativeNameLength => logger.write_string("NegativeNameLength")
    InvalidUtf8Name => logger.write_string("InvalidUtf8Name")
    InvalidSectionHeader => logger.write_string("InvalidSectionHeader")
    SectionPayloadLengthOutOfRange =>
      logger.write_string("SectionPayloadLengthOutOfRange")
    InvalidSectionPayloadTrailingBytes =>
      logger.write_string("InvalidSectionPayloadTrailingBytes")
    DuplicateNameSection => logger.write_string("DuplicateNameSection")
    DuplicateCompilerFactsSection =>
      logger.write_string("DuplicateCompilerFactsSection")
    InvalidCompilerFactsEnumTag =>
      logger.write_string("InvalidCompilerFactsEnumTag")
    InvalidCompilerFactsOptionTag =>
      logger.write_string("InvalidCompilerFactsOptionTag")
    TruncatedCompilerFactsPayload =>
      logger.write_string("TruncatedCompilerFactsPayload")
    CompilerFactsTrailingBytes =>
      logger.write_string("CompilerFactsTrailingBytes")
    UnsupportedCompilerFactsVersion =>
      logger.write_string("UnsupportedCompilerFactsVersion")
    InvalidNameSubsectionId => logger.write_string("InvalidNameSubsectionId")
    InvalidNameSubsectionOrder =>
      logger.write_string("InvalidNameSubsectionOrder")
    InvalidNameMapOrder => logger.write_string("InvalidNameMapOrder")
    InvalidMagicHeader => logger.write_string("InvalidMagicHeader")
    InvalidModule => logger.write_string("InvalidModule")
    InvalidElemHeader => logger.write_string("InvalidElemHeader")
    InvalidDataHeader => logger.write_string("InvalidDataHeader")
    CustomSectionPayloadLengthOutOfRange =>
      logger.write_string("CustomSectionPayloadLengthOutOfRange")
    InvalidLimits => logger.write_string("InvalidLimits")
    InvalidTagType => logger.write_string("InvalidTagType")
    InvalidExternTypeHeader => logger.write_string("InvalidExternTypeHeader")
    InvalidBlockType => logger.write_string("InvalidBlockType")
    InvalidExternIndex => logger.write_string("InvalidExternIndex")
    InvalidOffset => logger.write_string("InvalidOffset")
    InvalidCatch => logger.write_string("InvalidCatch")
    InvalidCastOp => logger.write_string("InvalidCastOp")
    InvalidMemArg => logger.write_string("InvalidMemArg")
    InvalidStringRefSection => logger.write_string("InvalidStringRefSection")
    InvalidStringLiteralIndex =>
      logger.write_string("InvalidStringLiteralIndex")
    InvalidInstruction => logger.write_string("InvalidInstruction")
    InvalidAtomicFenceImmediate =>
      logger.write_string("InvalidAtomicFenceImmediate")
    InvalidAtomicInstruction => logger.write_string("InvalidAtomicInstruction")
    InstructionNestingLimitExceeded =>
      logger.write_string("InstructionNestingLimitExceeded")
  }
}

///|
pub struct ModuleDecodeErrorDetail {
  err : BinaryDecodeError
  section_id : Int?
  section_offset : Int?
  section_len : Int?
  trailing_offset : Int?
} derive(Eq, Debug)

///|
pub trait Decode {
  fn decode(Bytes, Int) -> Result[(Self, Int), BinaryDecodeError]
}

///|
let binary_decode_stringrefs_context : Ref[Array[Bytes]?] = Ref::new(None)

///|
fn[T] with_binary_decode_stringrefs_context(
  stringrefs : Array[Bytes]?,
  f : () -> Result[T, ModuleDecodeErrorDetail],
) -> Result[T, ModuleDecodeErrorDetail] {
  let saved = binary_decode_stringrefs_context.val
  binary_decode_stringrefs_context.val = stringrefs
  let result = f()
  binary_decode_stringrefs_context.val = saved
  result
}

///|
fn decode_string_const_literal(
  idx : @lib.U32,
) -> Result[Bytes, BinaryDecodeError] {
  match binary_decode_stringrefs_context.val {
    Some(stringrefs) =>
      match stringrefs.get(idx.0.reinterpret_as_int()) {
        Some(bytes) => Ok(bytes)
        None => Err(BinaryDecodeError::InvalidStringLiteralIndex)
      }
    None => Err(BinaryDecodeError::InvalidStringLiteralIndex)
  }
}

///|
pub impl Decode for NumType with fn decode(buf, idx) {
  match buf.get(idx) {
    Some(0x7C) => Ok((NumType::f64(), idx + 1))
    Some(0x7D) => Ok((NumType::f32(), idx + 1))
    Some(0x7E) => Ok((NumType::i64(), idx + 1))
    Some(0x7F) => Ok((NumType::i32(), idx + 1))
    Some(_) => Err(BinaryDecodeError::InvalidNumTypeByte)
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
pub impl Decode for HeapType with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x65) =>
      match
        (
          Decode::decode(bytes, i + 1) :
          Result[(AbsHeapType, Int), BinaryDecodeError]) {
        Ok((abs, next)) => Ok((HeapType::shared_abs(abs), next))
        Err(err) => Err(err)
      }
    _ =>
      match Decode::decode(bytes, i) {
        Ok((abs, i)) => Ok((HeapType::abs(abs), i))
        Err(_) =>
          match Decode::decode(bytes, i) {
            Ok((@lib.S33(s), i)) => Ok((HeapType::new(TypeIdx::new(s)), i))
            Err(err) => Err(err)
          }
      }
  }
}

///|
pub impl Decode for AbsHeapType with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x5c) => Ok((AbsHeapType::waitqueue(), i + 1))
    Some(0x5b) => Ok((AbsHeapType::no_waitqueue(), i + 1))
    Some(0x67) => Ok((AbsHeapType::string_(), i + 1))
    Some(0x68) => Ok((AbsHeapType::cont(), i + 1))
    Some(0x69) => Ok((AbsHeapType::exn(), i + 1))
    Some(0x6A) => Ok((AbsHeapType::array(), i + 1))
    Some(0x6B) => Ok((AbsHeapType::struct_(), i + 1))
    Some(0x6C) => Ok((AbsHeapType::i31(), i + 1))
    Some(0x6D) => Ok((AbsHeapType::eq(), i + 1))
    Some(0x6E) => Ok((AbsHeapType::any(), i + 1))
    Some(0x6F) => Ok((AbsHeapType::extern_(), i + 1))
    Some(0x70) => Ok((AbsHeapType::func(), i + 1))
    Some(0x71) => Ok((AbsHeapType::none(), i + 1))
    Some(0x72) => Ok((AbsHeapType::no_extern(), i + 1))
    Some(0x73) => Ok((AbsHeapType::no_func(), i + 1))
    Some(0x74) => Ok((AbsHeapType::no_exn(), i + 1))
    Some(0x75) => Ok((AbsHeapType::no_cont(), i + 1))
    Some(_) => Err(BinaryDecodeError::InvalidAbstractHeapType)
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
fn decode_ref_heap_type(
  bytes : Bytes,
  i : Int,
) -> Result[(Bool, HeapType, Int), BinaryDecodeError] {
  match bytes.get(i) {
    Some(0x62) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.S33(s), next)) =>
          Ok((true, HeapType::new(TypeIdx::new(s)), next))
        Err(err) => Err(err)
      }
    _ =>
      match Decode::decode(bytes, i) {
        Ok((ht, next)) => Ok((false, ht, next))
        Err(err) => Err(err)
      }
  }
}

///|
pub impl Decode for RefType with fn decode(bytes, i) {
  match bytes.get(i) {
    None => Err(BinaryDecodeError::IndexOutOfBounds)
    Some(0x63) =>
      match decode_ref_heap_type(bytes, i + 1) {
        Err(err) => Err(err)
        Ok((exact, ht, i)) =>
          match (exact, ht) {
            (false, AbsHeapTypeHeapType(abs)) => Ok((RefType::abs(abs), i))
            _ => Ok((RefType::new(true, ht, exact~), i))
          }
      }
    Some(0x64) =>
      match decode_ref_heap_type(bytes, i + 1) {
        Err(err) => Err(err)
        Ok((exact, ht, i)) => Ok((RefType::new(false, ht, exact~), i))
      }
    Some(_) =>
      match Decode::decode(bytes, i) {
        Err(err) => Err(err)
        Ok((abs, i)) => Ok((RefType::abs(abs), i))
      }
  }
}

///|
pub impl Decode for ValType with fn decode(bytes, i) {
  match bytes.get(i) {
    None => Err(BinaryDecodeError::IndexOutOfBounds)
    Some(0x7B) => Ok((ValType::v128(), i + 1))
    Some(_) =>
      match Decode::decode(bytes, i) {
        Ok((t, i)) => Ok((ValType::num_type(t), i))
        _ =>
          match Decode::decode(bytes, i) {
            Ok((rt, i)) => Ok((ValType::ref_type(rt), i))
            Err(err) =>
              match (bytes.get(i), err) {
                (Some(0x64), BinaryDecodeError::UnexpectedEofInSignedLeb)
                | (Some(0x64), BinaryDecodeError::IndexOutOfBounds) =>
                  Ok((ValType::stringref(), i + 1))
                _ => Err(BinaryDecodeError::InvalidValType)
              }
          }
      }
  }
}

///|
pub impl[T : Decode] Decode for @list.List[T] with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Ok((@lib.U32(len), i)) => {
      let acc = []
      let mut left = len
      let mut pos = i
      while left > 0 {
        match T::decode(bytes, pos) {
          Ok((u, next_pos)) => {
            acc.push(u)
            pos = next_pos
            left = left - 1
          }
          Err(err) => return Err(err)
        }
      }
      Ok((@list.List::from_array(acc), pos))
    }
    Err(err) => Err(err)
  }
}

///|
pub impl Decode for CompType with fn decode(bytes, i) {
  match bytes.get(i) {
    None => Err(BinaryDecodeError::IndexOutOfBounds)
    Some(0x5E) =>
      match Decode::decode(bytes, i + 1) {
        Ok((ft, i)) => Ok((CompType::array(ft), i))
        Err(err) => Err(err)
      }
    Some(0x5F) =>
      match Decode::decode(bytes, i + 1) {
        Ok((fts, i)) => Ok((CompType::struct_(fts), i))
        Err(err) => Err(err)
      }
    Some(0x60) =>
      match Decode::decode(bytes, i + 1) {
        Ok((props, i)) =>
          match Decode::decode(bytes, i) {
            Ok((rets, i)) => Ok((CompType::func(props, rets), i))
            Err(err) => Err(err)
          }
        Err(err) => Err(err)
      }
    Some(0x5D) =>
      match Decode::decode(bytes, i + 1) {
        Ok((func_type, i)) => Ok((CompType::cont(func_type), i))
        Err(err) => Err(err)
      }
    Some(_) => Err(BinaryDecodeError::InvalidCompType)
  }
}

///|
pub impl Decode for Mut with fn decode(bytes, i) {
  if i >= bytes.length() {
    return Err(BinaryDecodeError::UnexpectedEofDecodingMut)
  }
  let b = bytes[i]
  match b {
    0x00 => Ok((Const, i + 1))
    0x01 => Ok((Var, i + 1))
    _ => Err(BinaryDecodeError::InvalidMutByte)
  }
}

///|
pub impl Decode for StorageType with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Ok((vt, i)) => Ok((StorageType::val_type(vt), i))
    Err(_) =>
      match Decode::decode(bytes, i) {
        Ok((pt, i)) => Ok((StorageType::pack_type(pt), i))
        Err(_) => Err(BinaryDecodeError::InvalidStorageType)
      }
  }
}

///|
pub impl Decode for PackType with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x77) => Ok((PackType::i16(), i + 1))
    Some(0x78) => Ok((PackType::i8(), i + 1))
    Some(_) => Err(BinaryDecodeError::InvalidPackedType)
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
pub impl Decode for AtomicOrder with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x00) => Ok((AtomicOrder::seq_cst(), i + 1))
    Some(0x01) => Ok((AtomicOrder::acq_rel(), i + 1))
    Some(0x02) => Ok((AtomicOrder::relaxed(), i + 1))
    Some(_) => Err(BinaryDecodeError::InvalidAtomicInstruction)
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
fn decode_aggregate_atomic_rmw_order(
  bytes : Bytes,
  i : Int,
) -> Result[(AtomicOrder, Int), BinaryDecodeError] {
  match bytes.get(i) {
    Some(0x00) => Ok((AtomicOrder::seq_cst(), i + 1))
    Some(0x11) => Ok((AtomicOrder::acq_rel(), i + 1))
    Some(0x22) => Ok((AtomicOrder::relaxed(), i + 1))
    Some(_) => Err(BinaryDecodeError::InvalidAtomicInstruction)
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
pub impl Decode for RecType with fn decode(buf, i) {
  match buf.get(i) {
    None => Err(BinaryDecodeError::IndexOutOfBounds)
    Some(0x4E) =>
      match Decode::decode(buf, i + 1) {
        Ok((sts, i)) => Ok((RecType::group(sts), i))
        Err(err) => Err(err)
      }
    Some(_) =>
      match Decode::decode(buf, i) {
        Ok((st, i)) => Ok((RecType::new(st), i))
        Err(err) => Err(err)
      }
  }
}

///|
fn decode_descriptor_clauses(
  buf : Bytes,
  i : Int,
) -> Result[(TypeMetadata, Int), BinaryDecodeError] {
  let mut cursor = i
  let mut describes : TypeIdx? = None
  let mut descriptor : TypeIdx? = None
  while true {
    match buf.get(cursor) {
      Some(0x4C) => {
        if describes is Some(_) || descriptor is Some(_) {
          return Err(BinaryDecodeError::InvalidCompType)
        }
        cursor = match
          (
            Decode::decode(buf, cursor + 1) :
            Result[(TypeIdx, Int), BinaryDecodeError]) {
          Ok((idx, next)) => {
            describes = Some(idx)
            next
          }
          Err(err) => return Err(err)
        }
      }
      Some(0x4D) => {
        if descriptor is Some(_) {
          return Err(BinaryDecodeError::InvalidCompType)
        }
        cursor = match
          (
            Decode::decode(buf, cursor + 1) :
            Result[(TypeIdx, Int), BinaryDecodeError]) {
          Ok((idx, next)) => {
            descriptor = Some(idx)
            next
          }
          Err(err) => return Err(err)
        }
      }
      _ => break
    }
  }
  Ok((TypeMetadata::new(describes~, descriptor~), cursor))
}

///|
fn decode_shared_comptype(
  buf : Bytes,
  i : Int,
  metadata : TypeMetadata,
) -> Result[(CompType, TypeMetadata, Int), BinaryDecodeError] {
  let (cursor, metadata) = if buf.get(i) == Some(0x65) {
    (i + 1, metadata.with_shared(true))
  } else {
    (i, metadata)
  }
  match Decode::decode(buf, cursor) {
    Ok((ct, next)) => Ok((ct, metadata, next))
    Err(err) => Err(err)
  }
}

///|
fn decode_subtype_tail(
  buf : Bytes,
  i : Int,
) -> Result[(CompType, TypeMetadata, Int), BinaryDecodeError] {
  let (cursor, shared) = if buf.get(i) == Some(0x65) {
    (i + 1, true)
  } else {
    (i, false)
  }
  let (metadata, cursor) = match decode_descriptor_clauses(buf, cursor) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  let metadata = metadata.with_shared(shared)
  match Decode::decode(buf, cursor) {
    Ok((ct, next)) => Ok((ct, metadata, next))
    Err(err) => Err(err)
  }
}

///|
pub impl Decode for SubType with fn decode(buf, i) {
  match buf.get(i) {
    // Shared definition wrapper.
    Some(0x65) =>
      match
        (Decode::decode(buf, i + 1) : Result[(SubType, Int), BinaryDecodeError]) {
        Ok((SubType(f, supers, meta, ct), next)) =>
          Ok(
            (SubType::new(f, supers, ct, metadata=meta.with_shared(true)), next),
          )
        Ok((CompTypeSubType(meta, ct), next)) =>
          Ok((SubType::comp_type(ct, metadata=meta.with_shared(true)), next))
        Err(err) => Err(err)
      }
    // Final
    Some(0x4F) =>
      match Decode::decode(buf, i + 1) {
        Ok((idxs, i)) =>
          match decode_subtype_tail(buf, i) {
            Ok((ct, meta, i)) =>
              Ok((SubType::new(true, idxs, ct, metadata=meta), i))
            Err(err) => Err(err)
          }
        Err(err) => Err(err)
      }
    // Subtypes
    Some(0x50) =>
      match Decode::decode(buf, i + 1) {
        Ok((idxs, i)) =>
          match decode_subtype_tail(buf, i) {
            Ok((ct, meta, i)) =>
              Ok((SubType::new(false, idxs, ct, metadata=meta), i))
            Err(err) => Err(err)
          }
        Err(err) => Err(err)
      }
    // CompType, defer to comp type decoder
    Some(_) => {
      let (meta, next) = match decode_descriptor_clauses(buf, i) {
        Ok(v) => v
        Err(err) => return Err(err)
      }
      match decode_shared_comptype(buf, next, meta) {
        Ok((ct, meta, i)) => Ok((SubType::comp_type(ct, metadata=meta), i))
        Err(err) => Err(err)
      }
    }
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
// Private success storage avoids boxing a tuple and Result before each typed
// decoder immediately unpacks them. The public Decode result stays unchanged.
#valtype
priv struct UnsignedDecodeValue {
  value : UInt64
  next : Int
  error : BinaryDecodeError?
}

///|
#inline
fn UnsignedDecodeValue::success(
  value : UInt64,
  next : Int,
) -> UnsignedDecodeValue {
  { value, next, error: None, }
}

///|
#inline
fn UnsignedDecodeValue::failure(
  error : BinaryDecodeError,
) -> UnsignedDecodeValue {
  { value: 0UL, next: 0, error: Some(error), }
}

///|
fn decode_unsigned_value(
  bytes : Bytes,
  index : Int,
  nbits : Int,
) -> UnsignedDecodeValue {
  if nbits <= 0 || nbits > 64 {
    return UnsignedDecodeValue::failure(
      BinaryDecodeError::InvalidUnsignedLebBitWidth,
    )
  }
  if index < 0 {
    return UnsignedDecodeValue::failure(BinaryDecodeError::IndexOutOfBounds)
  }
  let max_bytes = max_leb_bytes(nbits)
  let mut i = index
  let mut shift = 0
  let mut acc : UInt64 = 0UL
  let mut count = 0
  while true {
    if i >= bytes.length() {
      return UnsignedDecodeValue::failure(
        BinaryDecodeError::UnexpectedEofInUnsignedLeb,
      )
    }
    if count >= max_bytes {
      return UnsignedDecodeValue::failure(
        BinaryDecodeError::UnsignedLebTooManyBytes,
      )
    }
    let byte_u : UInt64 = bytes[i].to_uint64()
    i += 1
    count += 1
    let payload : UInt64 = byte_u & 0x7fUL
    acc = acc | (payload << shift)
    let cont = (byte_u & 0x80UL) != 0UL
    shift = shift + 7
    if !cont {
      // range check
      if nbits < 64 {
        let max_val = (1UL << nbits) - 1UL
        if acc > max_val {
          return UnsignedDecodeValue::failure(
            BinaryDecodeError::UnsignedLebOutOfRangeForBitWidth,
          )
        }
      }

      // terminal unused bits constraint - ONLY at max bytes
      if count == max_bytes {
        let rem = nbits % 7
        if rem != 0 {
          let limit = 1UL << rem
          if payload >= limit {
            return UnsignedDecodeValue::failure(
              BinaryDecodeError::UnsignedLebTerminalHasNonZeroUnusedBits,
            )
          }
        }
      }
      return UnsignedDecodeValue::success(acc, i)
    }
  }
  UnsignedDecodeValue::failure(BinaryDecodeError::UnreachableDecodeState)
}

///|
fn decode_unsigned(
  bytes : Bytes,
  index : Int,
  nbits : Int,
) -> Result[(UInt64, Int), BinaryDecodeError] {
  let decoded = decode_unsigned_value(bytes, index, nbits)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((decoded.value, decoded.next))
  }
}

///|
fn decode_signed(
  bytes : Bytes,
  index : Int,
  nbits : Int,
) -> Result[(Int64, Int), BinaryDecodeError] {
  if nbits <= 0 || nbits > 64 {
    return Err(BinaryDecodeError::InvalidSignedLebBitWidth)
  }
  if index < 0 {
    return Err(BinaryDecodeError::IndexOutOfBounds)
  }
  let max_bytes = max_leb_bytes(nbits)
  let mut i = index
  let mut shift = 0
  let mut acc : Int64 = 0
  let mut count = 0
  let mut last_byte : UInt64 = 0
  while true {
    if i >= bytes.length() {
      return Err(BinaryDecodeError::UnexpectedEofInSignedLeb)
    }
    if count >= max_bytes {
      return Err(BinaryDecodeError::SignedLebTooManyBytes)
    }
    let byte_u = bytes[i].to_uint64()
    last_byte = byte_u
    i += 1
    count += 1
    let payload = (byte_u & 0x7f).reinterpret_as_int64()
    acc = acc | (payload << shift)
    let cont = (byte_u & 0x80) != 0
    shift += 7
    if !cont {
      break
    }
  }

  // sign extend to full int64 based on last byte's sign bit (0x40)
  if (last_byte & 0x40) != 0 && shift < 64 {
    acc = acc | ((-1 : Int64) << shift)
  }

  // range check for N bits signed
  if nbits < 64 {
    let min = (-1 : Int64) << (nbits - 1)
    let max = ((1 : Int64) << (nbits - 1)) - 1
    if acc < min || acc > max {
      return Err(BinaryDecodeError::SignedLebOutOfRangeForBitWidth)
    }
  }

  // terminal-byte unused bits constraint - ONLY at max bytes
  if count == max_bytes {
    let rem = nbits % 7
    if rem != 0 {
      let payload_u = last_byte & 0x7f
      let used_mask = (1UL << rem) - 1UL
      let inv_used = UInt64::lnot(used_mask) & 0x7fUL
      let unused = payload_u & inv_used
      if acc >= 0 {
        if unused != 0 {
          return Err(
            BinaryDecodeError::SignedLebPositiveTerminalHasNonZeroUnusedBits,
          )
        }
      } else {
        let want = inv_used
        if unused != want {
          return Err(
            BinaryDecodeError::SignedLebTerminalHasWrongSignExtensionBits,
          )
        }
      }
    }
  }
  Ok((acc, i))
}

///|
pub impl Decode for S33 with fn decode(bytes, i) {
  match decode_signed(bytes, i, 33) {
    Err(err) => Err(err)
    Ok((val, i)) if val >= 0 && val <= 4294967295 =>
      Ok((S33(val.to_int().reinterpret_as_uint()), i))
    Ok(_) => Err(BinaryDecodeError::InvalidS33Range)
  }
}

///|
pub impl Decode for I32 with fn decode(bytes, i) {
  match decode_signed(bytes, i, 32) {
    Err(err) => Err(err)
    Ok((val, i)) => Ok((I32(val.to_int()), i))
  }
}

///|
pub impl Decode for @lib.U32 with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((U32(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for I64 with fn decode(bytes, i) {
  match decode_signed(bytes, i, 64) {
    Err(err) => Err(err)
    Ok((val, i)) => Ok((I64(val), i))
  }
}

///|
pub impl Decode for @lib.U64 with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 64)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((U64(decoded.value), decoded.next))
  }
}

///|
pub impl Decode for F32 with fn decode(bytes, i) {
  if i >= 0 && i <= bytes.length() && bytes.length() - i >= 4 {
    let value : Int = (bytes.unsafe_get(i + 3).to_int() << 24) |
      (bytes.unsafe_get(i + 2).to_int() << 16) |
      (bytes.unsafe_get(i + 1).to_int() << 8) |
      bytes.unsafe_get(i).to_int()
    Ok((F32(Float::reinterpret_from_int(value)), i + 4))
  } else {
    Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
pub impl Decode for F64 with fn decode(bytes, i) {
  if i >= 0 && i <= bytes.length() && bytes.length() - i >= 8 {
    let value : Int64 = (bytes.unsafe_get(i + 7).to_int64() << 56) |
      (bytes.unsafe_get(i + 6).to_int64() << 48) |
      (bytes.unsafe_get(i + 5).to_int64() << 40) |
      (bytes.unsafe_get(i + 4).to_int64() << 32) |
      (bytes.unsafe_get(i + 3).to_int64() << 24) |
      (bytes.unsafe_get(i + 2).to_int64() << 16) |
      (bytes.unsafe_get(i + 1).to_int64() << 8) |
      bytes.unsafe_get(i).to_int64()
    Ok((F64(value.reinterpret_as_double()), i + 8))
  } else {
    Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
pub impl Decode for Bool with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x00) => Ok((false, i + 1))
    Some(0x01) => Ok((true, i + 1))
    Some(_) => Err(BinaryDecodeError::InvalidBoolByte)
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
pub impl[T : Decode] Decode for T? with fn decode(bytes, i) {
  match T::decode(bytes, i) {
    Err(_) => Ok((None, i))
    Ok((val, i)) => Ok((Some(val), i))
  }
}

///|
pub impl Decode for TypeIdx with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((TypeIdx::new(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for FuncIdx with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((FuncIdx::new(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for TableIdx with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((TableIdx::new(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for MemIdx with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((MemIdx::new(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for GlobalIdx with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((GlobalIdx::new(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for TagIdx with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((TagIdx::new(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for ElemIdx with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((ElemIdx::new(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for DataIdx with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((DataIdx::new(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for LocalIdx with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((LocalIdx::new(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for LaneIdx with fn decode(bytes, i) {
  if bytes.get(i) is Some(n) && n < 16 {
    Ok((LaneIdx::new(n), i + 1))
  } else {
    Err(BinaryDecodeError::InvalidLaneIndex)
  }
}

///|
pub impl Decode for LabelIdx with fn decode(bytes, i) {
  let decoded = decode_unsigned_value(bytes, i, 32)
  match decoded.error {
    Some(err) => Err(err)
    None => Ok((LabelIdx::new(decoded.value.to_uint()), decoded.next))
  }
}

///|
pub impl Decode for Name with fn decode(bytes, i) {
  let (@lib.U32(len_u), j) = match Decode::decode(bytes, i) {
    Err(err) => return Err(err)
    Ok(t) => t
  }
  let len = len_u.reinterpret_as_int()
  if len < 0 {
    return Err(BinaryDecodeError::NegativeNameLength)
  }
  if len > bytes.length() - j {
    return Err(BinaryDecodeError::IndexOutOfBounds)
  }
  let end = j + len
  let name = @utf8.decode(bytes[j:end]) catch {
    _ => return Err(BinaryDecodeError::InvalidUtf8Name)
  }
  Ok((Name::new(name.to_string_view()), end))
}

///|
pub impl Decode for NameAssoc with fn decode(bytes, i) {
  let (@lib.U32(idx), i) = match Decode::decode(bytes, i) {
    Ok(t) => t
    Err(err) => return Err(err)
  }
  let (name, i) = match Decode::decode(bytes, i) {
    Ok(t) => t
    Err(err) => return Err(err)
  }
  Ok((NameAssoc::new(idx, name), i))
}

///|
pub impl Decode for NameMap with fn decode(bytes, i) {
  let (@lib.U32(len_u), i) = match Decode::decode(bytes, i) {
    Ok(t) => t
    Err(err) => return Err(err)
  }
  let entries : Array[NameAssoc] = []
  let mut next = i
  let mut prev_idx : UInt? = None
  let mut remaining = len_u
  while remaining > 0U {
    let (entry, decoded_next) = match
      (Decode::decode(bytes, next) : Result[(NameAssoc, Int), BinaryDecodeError]) {
      Ok(t) => t
      Err(err) => return Err(err)
    }
    let idx = entry.0
    match prev_idx {
      Some(prev) if idx <= prev =>
        return Err(BinaryDecodeError::InvalidNameMapOrder)
      _ => ()
    }
    prev_idx = Some(idx)
    entries.push(entry)
    next = decoded_next
    remaining = remaining - 1U
  }
  Ok((NameMap::new(entries), next))
}

///|
pub impl Decode for IndirectNameAssoc with fn decode(bytes, i) {
  let (@lib.U32(idx), i) = match Decode::decode(bytes, i) {
    Ok(t) => t
    Err(err) => return Err(err)
  }
  let (names, i) = match Decode::decode(bytes, i) {
    Ok(t) => t
    Err(err) => return Err(err)
  }
  Ok((IndirectNameAssoc::new(idx, names), i))
}

///|
pub impl Decode for IndirectNameMap with fn decode(bytes, i) {
  let (@lib.U32(len_u), i) = match Decode::decode(bytes, i) {
    Ok(t) => t
    Err(err) => return Err(err)
  }
  let entries : Array[IndirectNameAssoc] = []
  let mut next = i
  let mut prev_idx : UInt? = None
  let mut remaining = len_u
  while remaining > 0U {
    let (entry, decoded_next) = match
      (
        Decode::decode(bytes, next) :
        Result[(IndirectNameAssoc, Int), BinaryDecodeError]) {
      Ok(t) => t
      Err(err) => return Err(err)
    }
    let idx = entry.0
    match prev_idx {
      Some(prev) if idx <= prev =>
        return Err(BinaryDecodeError::InvalidNameMapOrder)
      _ => ()
    }
    prev_idx = Some(idx)
    entries.push(entry)
    next = decoded_next
    remaining = remaining - 1U
  }
  Ok((IndirectNameMap::new(entries), next))
}

///|
fn[T : Decode] decode_name_subsection_payload(
  bytes : Bytes,
  payload_start : Int,
  payload_end : Int,
) -> Result[T, BinaryDecodeError] {
  let (decoded, consumed) = match Decode::decode(bytes, payload_start) {
    Ok(t) => t
    Err(err) => return Err(err)
  }
  if consumed != payload_end {
    return Err(BinaryDecodeError::InvalidSectionPayloadTrailingBytes)
  }
  Ok(decoded)
}

///|
pub impl Decode for NameSec with fn decode(bytes, i) {
  let mut next = i
  let mut prev_subsection_id : Byte? = None
  let mut module_name : Name? = None
  let mut func_names : NameMap? = None
  let mut local_names : IndirectNameMap? = None
  let mut label_names : IndirectNameMap? = None
  let mut type_names : NameMap? = None
  let mut table_names : NameMap? = None
  let mut memory_names : NameMap? = None
  let mut global_names : NameMap? = None
  let mut elem_names : NameMap? = None
  let mut data_names : NameMap? = None
  let mut field_names : IndirectNameMap? = None
  let mut tag_names : NameMap? = None
  while next < bytes.length() {
    let subsection_id = match bytes.get(next) {
      Some(id) => id
      None => break
    }
    // Unknown optional subsections do not change the order of known names.
    if subsection_id <= 11 {
      match prev_subsection_id {
        Some(prev) if subsection_id <= prev =>
          return Err(BinaryDecodeError::InvalidNameSubsectionOrder)
        _ => ()
      }
      prev_subsection_id = Some(subsection_id)
    }
    let (@lib.U32(payload_len_u), payload_start) = match
      Decode::decode(bytes, next + 1) {
      Ok(t) => t
      Err(err) => return Err(err)
    }
    if payload_len_u > (bytes.length() - payload_start).reinterpret_as_uint() {
      return Err(BinaryDecodeError::SectionPayloadLengthOutOfRange)
    }
    let payload_end = payload_start + payload_len_u.reinterpret_as_int()
    match subsection_id {
      0 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(name) => module_name = Some(name)
          Err(err) => return Err(err)
        }
      1 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => func_names = Some(names)
          Err(err) => return Err(err)
        }
      2 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => local_names = Some(names)
          Err(err) => return Err(err)
        }
      3 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => label_names = Some(names)
          Err(err) => return Err(err)
        }
      4 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => type_names = Some(names)
          Err(err) => return Err(err)
        }
      5 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => table_names = Some(names)
          Err(err) => return Err(err)
        }
      6 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => memory_names = Some(names)
          Err(err) => return Err(err)
        }
      7 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => global_names = Some(names)
          Err(err) => return Err(err)
        }
      8 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => elem_names = Some(names)
          Err(err) => return Err(err)
        }
      9 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => data_names = Some(names)
          Err(err) => return Err(err)
        }
      10 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => field_names = Some(names)
          Err(err) => return Err(err)
        }
      11 =>
        match
          decode_name_subsection_payload(bytes, payload_start, payload_end) {
          Ok(names) => tag_names = Some(names)
          Err(err) => return Err(err)
        }
      _ => ()
    }
    next = payload_end
  }
  Ok(
    (
      NameSec::new(
        module_name~,
        func_names~,
        local_names~,
        label_names~,
        type_names~,
        table_names~,
        memory_names~,
        global_names~,
        elem_names~,
        data_names~,
        field_names~,
        tag_names~,
      ),
      next,
    ),
  )
}

///|
fn[T : Decode] decode_section_vector_payload(
  bytes : Bytes,
  i : Int,
  section_id : Byte,
) -> Result[(Array[T], Int), BinaryDecodeError] {
  if bytes.get(i) != Some(section_id) {
    return Err(BinaryDecodeError::InvalidSectionHeader)
  }
  let (@lib.U32(len_u), payload_start) = match Decode::decode(bytes, i + 1) {
    Ok(t) => t
    Err(t) => return Err(t)
  }
  if len_u > (bytes.length() - payload_start).reinterpret_as_uint() {
    return Err(BinaryDecodeError::SectionPayloadLengthOutOfRange)
  }
  let payload_end = payload_start + len_u.reinterpret_as_int()
  let (content, consumed) = match Decode::decode(bytes, payload_start) {
    Ok(t) => t
    Err(t) => return Err(t)
  }
  if consumed == payload_end {
    Ok((content, payload_end))
  } else {
    Err(BinaryDecodeError::InvalidSectionPayloadTrailingBytes)
  }
}

///|
fn[T : Decode] decode_section_payload(
  bytes : Bytes,
  i : Int,
  section_id : Byte,
) -> Result[(T, Int), BinaryDecodeError] {
  if bytes.get(i) != Some(section_id) {
    return Err(BinaryDecodeError::InvalidSectionHeader)
  }
  let (@lib.U32(len_u), payload_start) = match Decode::decode(bytes, i + 1) {
    Ok(t) => t
    Err(t) => return Err(t)
  }
  if len_u > (bytes.length() - payload_start).reinterpret_as_uint() {
    return Err(BinaryDecodeError::SectionPayloadLengthOutOfRange)
  }
  let payload_end = payload_start + len_u.reinterpret_as_int()
  let (content, consumed) = match Decode::decode(bytes, payload_start) {
    Ok(t) => t
    Err(t) => return Err(t)
  }
  if consumed == payload_end {
    Ok((content, payload_end))
  } else {
    Err(BinaryDecodeError::InvalidSectionPayloadTrailingBytes)
  }
}

///|
fn module_section_span(bytes : Bytes, section_start : Int) -> (Int, Int) {
  if section_start < 0 || section_start >= bytes.length() {
    return (section_start, 0)
  }
  let len_result : Result[(@lib.U32, Int), BinaryDecodeError] = Decode::decode(
    bytes,
    section_start + 1,
  )
  match len_result {
    Ok((@lib.U32(payload_len_u), payload_start)) => {
      let remaining = bytes.length() - payload_start
      let section_end = if payload_len_u <= remaining.reinterpret_as_uint() {
        payload_start + payload_len_u.reinterpret_as_int()
      } else {
        bytes.length()
      }
      (section_start, section_end - section_start)
    }
    Err(_) => (section_start, bytes.length() - section_start)
  }
}

///|
fn module_decode_error(
  err : BinaryDecodeError,
  section_id? : Int? = None,
  section_offset? : Int? = None,
  section_len? : Int? = None,
  trailing_offset? : Int? = None,
) -> ModuleDecodeErrorDetail {
  { err, section_id, section_offset, section_len, trailing_offset, }
}

///|
fn module_section_decode_error(
  bytes : Bytes,
  section_start : Int,
  err : BinaryDecodeError,
) -> ModuleDecodeErrorDetail {
  let (offset, len) = module_section_span(bytes, section_start)
  let section_id = bytes.get(section_start).map(fn(id) { id.to_int() })
  module_decode_error(
    err,
    section_id~,
    section_offset=Some(offset),
    section_len=Some(len),
  )
}

///|
fn decode_custom_sections_with_detail(
  bytes : Bytes,
  i : Int,
  out : Array[CustomSec],
  name_out : Array[NameSec],
  name_payload_out : Array[Bytes],
) -> Result[Int, ModuleDecodeErrorDetail] {
  let mut i = i
  while bytes.get(i) == Some(0) {
    let section_start = i
    match decode_custom_section_header(bytes, i) {
      Ok((name, payload_start, next)) => {
        let payload = bytes[payload_start:next].to_owned()
        if name == Name::new("name") {
          if name_out.length() > 0 {
            return Err(
              module_section_decode_error(
                bytes,
                section_start,
                BinaryDecodeError::DuplicateNameSection,
              ),
            )
          }
          match Decode::decode(payload, 0) {
            Ok((name_sec, consumed)) => {
              if consumed != payload.length() {
                return Err(
                  module_section_decode_error(
                    bytes,
                    section_start,
                    BinaryDecodeError::InvalidSectionPayloadTrailingBytes,
                  ),
                )
              }
              name_out.push(name_sec)
              name_payload_out.push(payload)
            }
            Err(err) =>
              return Err(module_section_decode_error(bytes, section_start, err))
          }
        } else if name == Name::new("compiler.facts") {
          if out.any(fn(section) {
              let CustomSec(existing_name, _) = section
              existing_name == Name::new("compiler.facts")
            }) {
            return Err(
              module_section_decode_error(
                bytes,
                section_start,
                BinaryDecodeError::DuplicateCompilerFactsSection,
              ),
            )
          }
          match
            (
              Decode::decode(payload, 0) :
              Result[(CompilerFactCustomSection, Int), BinaryDecodeError]) {
            Ok((_, consumed)) => {
              if consumed != payload.length() {
                return Err(
                  module_section_decode_error(
                    bytes,
                    section_start,
                    BinaryDecodeError::CompilerFactsTrailingBytes,
                  ),
                )
              }
              out.push(CustomSec::new(name, payload))
            }
            Err(err) =>
              return Err(
                module_section_decode_error(
                  bytes,
                  section_start,
                  compiler_facts_payload_error(err),
                ),
              )
          }
        } else {
          out.push(CustomSec::new(name, payload))
        }
        i = next
      }
      Err(err) =>
        return Err(module_section_decode_error(bytes, section_start, err))
    }
  }
  Ok(i)
}

///|
fn decode_custom_section_header(
  bytes : Bytes,
  i : Int,
) -> Result[(Name, Int, Int), BinaryDecodeError] {
  if bytes.get(i) != Some(0) {
    return Err(BinaryDecodeError::InvalidSectionHeader)
  }
  let (@lib.U32(len), i1) = match Decode::decode(bytes, i + 1) {
    Ok(t) => t
    Err(t) => return Err(t)
  }
  if len > (bytes.length() - i1).reinterpret_as_uint() {
    return Err(BinaryDecodeError::CustomSectionPayloadLengthOutOfRange)
  }
  let end = i1 + len.reinterpret_as_int()
  let (name, payload_start) = match Decode::decode(bytes, i1) {
    Ok(t) => t
    Err(t) => return Err(t)
  }
  if payload_start > end {
    return Err(BinaryDecodeError::CustomSectionPayloadLengthOutOfRange)
  }
  Ok((name, payload_start, end))
}

///|
fn[T : Decode] decode_optional_section_with_detail(
  bytes : Bytes,
  i : Int,
  section_id : Byte,
) -> Result[(T?, Int), ModuleDecodeErrorDetail] {
  if bytes.get(i) != Some(section_id) {
    return Ok((None, i))
  }
  let section_start = i
  match Decode::decode(bytes, i) {
    Ok((sec, next)) => Ok((Some(sec), next))
    Err(err) => Err(module_section_decode_error(bytes, section_start, err))
  }
}

///|
pub fn decode_module_with_detail(
  bytes : Bytes,
  i : Int,
) -> Result[(Module, Int), ModuleDecodeErrorDetail] {
  let mut i = i
  let custom = Array::new()
  let name_secs = Array::new()
  let name_payloads = Array::new()
  let mut mod = Module::new()
  for b in [0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00] {
    if bytes.get(i) != Some(b.to_byte()) {
      return Err(module_decode_error(BinaryDecodeError::InvalidMagicHeader))
    }
    i = i + 1
  }
  i = match
    decode_custom_sections_with_detail(
      bytes, i, custom, name_secs, name_payloads,
    ) {
    Ok(next) => next
    Err(detail) => return Err(detail)
  }
  let (type_sec, next) = match
    decode_optional_section_with_detail(bytes, i, 1) {
    Ok(t) => t
    Err(detail) => return Err(detail)
  }
  i = next
  if type_sec is Some(sec) {
    mod = mod.with_type_sec(sec)
  }

  i = match
    decode_custom_sections_with_detail(
      bytes, i, custom, name_secs, name_payloads,
    ) {
    Ok(next) => next
    Err(detail) => return Err(detail)
  }
  let (import_sec, next) = match
    decode_optional_section_with_detail(bytes, i, 2) {
    Ok(t) => t
    Err(detail) => return Err(detail)
  }
  i = next
  if import_sec is Some(sec) {
    mod = mod.with_import_sec(sec)
  }

  i = match
    decode_custom_sections_with_detail(
      bytes, i, custom, name_secs, name_payloads,
    ) {
    Ok(next) => next
    Err(detail) => return Err(detail)
  }
  let (func_sec, next) = match
    decode_optional_section_with_detail(bytes, i, 3) {
    Ok(t) => t
    Err(detail) => return Err(detail)
  }
  i = next
  if func_sec is Some(sec) {
    mod = mod.with_func_sec(sec)
  }

  i = match
    decode_custom_sections_with_detail(
      bytes, i, custom, name_secs, name_payloads,
    ) {
    Ok(next) => next
    Err(detail) => return Err(detail)
  }
  let (table_sec, next) = match
    decode_optional_section_with_detail(bytes, i, 4) {
    Ok(t) => t
    Err(detail) => return Err(detail)
  }
  i = next
  if table_sec is Some(sec) {
    mod = mod.with_table_sec(sec)
  }

  i = match
    decode_custom_sections_with_detail(
      bytes, i, custom, name_secs, name_payloads,
    ) {
    Ok(next) => next
    Err(detail) => return Err(detail)
  }
  let (mem_sec, next) = match decode_optional_section_with_detail(bytes, i, 5) {
    Ok(t) => t
    Err(detail) => return Err(detail)
  }
  i = next
  if mem_sec is Some(sec) {
    mod = mod.with_mem_sec(sec)
  }

  i = match
    decode_custom_sections_with_detail(
      bytes, i, custom, name_secs, name_payloads,
    ) {
    Ok(next) => next
    Err(detail) => return Err(detail)
  }
  let (tag_sec, next) = match
    decode_optional_section_with_detail(bytes, i, 13) {
    Ok(t) => t
    Err(detail) => return Err(detail)
  }
  i = next
  if tag_sec is Some(sec) {
    mod = mod.with_tag_sec(sec)
  }

  i = match
    decode_custom_sections_with_detail(
      bytes, i, custom, name_secs, name_payloads,
    ) {
    Ok(next) => next
    Err(detail) => return Err(detail)
  }
  let (stringrefs_sec, next) = match
    (
      decode_optional_section_with_detail(bytes, i, 14) :
      Result[(StringRefsSec?, Int), ModuleDecodeErrorDetail]) {
    Ok(t) => t
    Err(detail) => return Err(detail)
  }
  i = next
  let stringrefs_ctx = match stringrefs_sec {
    Some(sec) => {
      let StringRefsSec(strings) = sec
      mod = mod.with_stringrefs_sec(sec)
      Some(strings)
    }
    None => None
  }
  let tail = with_binary_decode_stringrefs_context(stringrefs_ctx, fn() {
    let mut i = i
    let mut mod = mod
    i = match
      decode_custom_sections_with_detail(
        bytes, i, custom, name_secs, name_payloads,
      ) {
      Ok(next) => next
      Err(detail) => return Err(detail)
    }
    let (global_sec, next) = match
      decode_optional_section_with_detail(bytes, i, 6) {
      Ok(t) => t
      Err(detail) => return Err(detail)
    }
    i = next
    if global_sec is Some(sec) {
      mod = mod.with_global_sec(sec)
    }

    i = match
      decode_custom_sections_with_detail(
        bytes, i, custom, name_secs, name_payloads,
      ) {
      Ok(next) => next
      Err(detail) => return Err(detail)
    }
    let (export_sec, next) = match
      decode_optional_section_with_detail(bytes, i, 7) {
      Ok(t) => t
      Err(detail) => return Err(detail)
    }
    i = next
    if export_sec is Some(sec) {
      mod = mod.with_export_sec(sec)
    }

    i = match
      decode_custom_sections_with_detail(
        bytes, i, custom, name_secs, name_payloads,
      ) {
      Ok(next) => next
      Err(detail) => return Err(detail)
    }
    let (start_sec, next) = match
      decode_optional_section_with_detail(bytes, i, 8) {
      Ok(t) => t
      Err(detail) => return Err(detail)
    }
    i = next
    if start_sec is Some(sec) {
      mod = mod.with_start_sec(sec)
    }

    i = match
      decode_custom_sections_with_detail(
        bytes, i, custom, name_secs, name_payloads,
      ) {
      Ok(next) => next
      Err(detail) => return Err(detail)
    }
    let (elem_sec, next) = match
      decode_optional_section_with_detail(bytes, i, 9) {
      Ok(t) => t
      Err(detail) => return Err(detail)
    }
    i = next
    if elem_sec is Some(sec) {
      mod = mod.with_elem_sec(sec)
    }

    i = match
      decode_custom_sections_with_detail(
        bytes, i, custom, name_secs, name_payloads,
      ) {
      Ok(next) => next
      Err(detail) => return Err(detail)
    }
    let (data_cnt_sec, next) = match
      decode_optional_section_with_detail(bytes, i, 12) {
      Ok(t) => t
      Err(detail) => return Err(detail)
    }
    i = next
    if data_cnt_sec is Some(sec) {
      mod = mod.with_data_cnt_sec(sec)
    }

    i = match
      decode_custom_sections_with_detail(
        bytes, i, custom, name_secs, name_payloads,
      ) {
      Ok(next) => next
      Err(detail) => return Err(detail)
    }
    let (code_sec, next) = match
      decode_optional_section_with_detail(bytes, i, 10) {
      Ok(t) => t
      Err(detail) => return Err(detail)
    }
    i = next
    if code_sec is Some(sec) {
      mod = mod.with_code_sec(sec)
    }

    i = match
      decode_custom_sections_with_detail(
        bytes, i, custom, name_secs, name_payloads,
      ) {
      Ok(next) => next
      Err(detail) => return Err(detail)
    }
    let (data_sec, next) = match
      decode_optional_section_with_detail(bytes, i, 11) {
      Ok(t) => t
      Err(detail) => return Err(detail)
    }
    i = next
    if data_sec is Some(sec) {
      mod = mod.with_data_sec(sec)
    }

    i = match
      decode_custom_sections_with_detail(
        bytes, i, custom, name_secs, name_payloads,
      ) {
      Ok(next) => next
      Err(detail) => return Err(detail)
    }
    Ok((mod, i))
  })
  let tail_result = match tail {
    Ok(result) => result
    Err(detail) => return Err(detail)
  }
  let tail_mod = tail_result.0
  let tail_i = tail_result.1
  mod = tail_mod
  i = tail_i

  let ordinary_custom : Array[CustomSec] = []
  for section in custom {
    let CustomSec(name, payload) = section
    if name == Name::new("compiler.facts") {
      match
        (
          Decode::decode(payload, 0) :
          Result[(CompilerFactCustomSection, Int), BinaryDecodeError]) {
        Ok((facts, _)) => mod = mod.with_compiler_fact_custom_section(facts)
        Err(err) => return Err(module_decode_error(err))
      }
    } else {
      ordinary_custom.push(section)
    }
  }
  mod = mod.with_custom_secs(ordinary_custom)
  match name_secs.get(0) {
    Some(name_sec) => {
      mod = mod.with_name_sec(name_sec)
      match name_payloads.get(0) {
        Some(payload) => mod = mod.with_raw_name_sec_payload(payload)
        None => ()
      }
    }
    None => ()
  }
  if i == bytes.length() {
    Ok((mod, i))
  } else {
    Err(
      module_decode_error(
        BinaryDecodeError::InvalidModule,
        trailing_offset=Some(i),
      ),
    )
  }
}

///|
pub impl Decode for Module with fn decode(bytes, i) {
  match decode_module_with_detail(bytes, i) {
    Ok(t) => Ok(t)
    Err(detail) => Err(detail.err)
  }
}

///|
pub impl Decode for Elem with fn decode(bytes, i) {
  let is_funcref = fn(rt : RefType) {
    rt == RefType::abs(AbsHeapType::func()) ||
    rt == RefType::new(true, HeapType::abs(AbsHeapType::func()))
  }
  let (header, next) = match
    (Decode::decode(bytes, i) : Result[(@lib.U32, Int), BinaryDecodeError]) {
    Ok((@lib.U32(v), next)) => (v, next)
    Err(t) => return Err(t)
  }
  match header {
    0U =>
      match Decode::decode(bytes, next) {
        Ok((expr, i)) =>
          match Decode::decode(bytes, i) {
            Ok((fidxs, i)) =>
              Ok(
                (
                  Elem::new(
                    ElemMode::active(TableIdx::new(0), expr),
                    ElemKind::funcs(fidxs),
                  ),
                  i,
                ),
              )
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    1U =>
      match bytes.get(next) {
        Some(0x00) =>
          match Decode::decode(bytes, next + 1) {
            Ok((fidxs, i)) =>
              Ok((Elem::new(ElemMode::passive(), ElemKind::funcs(fidxs)), i))
            Err(t) => Err(t)
          }
        _ => Err(BinaryDecodeError::InvalidElemHeader)
      }
    2U =>
      match Decode::decode(bytes, next) {
        Ok((@lib.TableIdx(idx), i)) =>
          match Decode::decode(bytes, i) {
            Ok((expr, i)) =>
              match bytes.get(i) {
                Some(0x00) =>
                  match Decode::decode(bytes, i + 1) {
                    Ok((fidxs, i)) =>
                      Ok(
                        (
                          Elem::new(
                            ElemMode::active(TableIdx::new(idx), expr),
                            ElemKind::funcs(fidxs),
                          ),
                          i,
                        ),
                      )
                    Err(t) => Err(t)
                  }
                _ => Err(BinaryDecodeError::InvalidElemHeader)
              }
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    3U =>
      match bytes.get(next) {
        Some(0x00) =>
          match Decode::decode(bytes, next + 1) {
            Ok((fidxs, i)) =>
              Ok(
                (Elem::new(ElemMode::declarative(), ElemKind::funcs(fidxs)), i),
              )
            Err(t) => Err(t)
          }
        _ => Err(BinaryDecodeError::InvalidElemHeader)
      }
    4U =>
      match Decode::decode(bytes, next) {
        Ok((expr, i)) =>
          match Decode::decode(bytes, i) {
            Ok((exprs, i)) =>
              Ok(
                (
                  Elem::new(
                    ElemMode::active(TableIdx::new(0), expr),
                    ElemKind::func_exprs(exprs),
                  ),
                  i,
                ),
              )
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    5U =>
      match Decode::decode(bytes, next) {
        Ok((rt, i)) =>
          match Decode::decode(bytes, i) {
            Ok((exprs, i)) =>
              Ok(
                (
                  Elem::new(
                    ElemMode::passive(),
                    ElemKind::typed_exprs(rt, exprs),
                  ),
                  i,
                ),
              )
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    6U =>
      match Decode::decode(bytes, next) {
        Ok((idx, i)) =>
          match Decode::decode(bytes, i) {
            Ok((expr, i)) =>
              match
                (
                  Decode::decode(bytes, i) :
                  Result[(RefType, Int), BinaryDecodeError]) {
                Ok((rt, i)) =>
                  match
                    (
                      Decode::decode(bytes, i) :
                      Result[(Array[Expr], Int), BinaryDecodeError]) {
                    Ok((exprs, i)) => {
                      let kind = if is_funcref(rt) {
                        ElemKind::func_exprs(exprs)
                      } else {
                        ElemKind::typed_exprs(rt, exprs)
                      }
                      Ok((Elem::new(ElemMode::active(idx, expr), kind), i))
                    }
                    Err(t) => Err(t)
                  }
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    7U =>
      match Decode::decode(bytes, next) {
        Ok((rt, i)) =>
          match Decode::decode(bytes, i) {
            Ok((exprs, i)) =>
              Ok(
                (
                  Elem::new(
                    ElemMode::declarative(),
                    ElemKind::typed_exprs(rt, exprs),
                  ),
                  i,
                ),
              )
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    _ => Err(BinaryDecodeError::InvalidElemHeader)
  }
}

///|
pub impl Decode for ElemSec with fn decode(bytes, i) {
  match decode_section_vector_payload(bytes, i, 9) {
    Ok((content, next)) => Ok((ElemSec::new(content), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for DataCntSec with fn decode(bytes, i) {
  match
    (
      decode_section_payload(bytes, i, 12) :
      Result[(@lib.U32, Int), BinaryDecodeError]) {
    Ok((count, i)) => Ok((DataCntSec::new(count), i))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for LocalRun with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Ok((@lib.U32(count), i)) =>
      match Decode::decode(bytes, i) {
        Ok((vt, i)) => Ok((LocalRun::new(count, vt), i))
        Err(t) => Err(t)
      }
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for Locals with fn decode(bytes, i) {
  match
    (
      Decode::decode(bytes, i) :
      Result[(Array[@lib.LocalRun], Int), BinaryDecodeError]) {
    Ok((runs, i)) => Ok((Locals::new(runs), i))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for Func with fn decode(bytes, i) {
  let (@lib.U32(size), body_start) = match Decode::decode(bytes, i) {
    Ok(t) => t
    Err(t) => return Err(t)
  }
  if size > (bytes.length() - body_start).reinterpret_as_uint() {
    return Err(BinaryDecodeError::SectionPayloadLengthOutOfRange)
  }
  let body_end = body_start + size.reinterpret_as_int()
  // A body needs a local-run count and its expression terminator.
  if size < 2U {
    return Err(BinaryDecodeError::InvalidModule)
  }
  let (locals, expr_start) = match
    (
      Decode::decode(bytes, body_start) :
      Result[(Array[@lib.LocalRun], Int), BinaryDecodeError]) {
    Ok(t) => t
    Err(t) => return Err(t)
  }
  if expr_start >= body_end {
    return Err(BinaryDecodeError::InvalidSectionPayloadTrailingBytes)
  }
  let mut total_locals : UInt64 = 0UL
  for local_entry in locals {
    total_locals += local_entry.count.to_uint64()
    if total_locals > 4294967295UL {
      return Err(BinaryDecodeError::InvalidModule)
    }
  }
  let (expr, next) = match Decode::decode(bytes, expr_start) {
    Ok(t) => t
    Err(t) => return Err(t)
  }
  if next != body_end {
    return Err(BinaryDecodeError::InvalidSectionPayloadTrailingBytes)
  }
  Ok((Func::new(Locals::new(locals), expr), next))
}

///|
pub impl Decode for CodeSec with fn decode(bytes, i) {
  match decode_section_vector_payload(bytes, i, 10) {
    Ok((funcs, next)) => Ok((CodeSec::new(funcs), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for Data with fn decode(bytes, i) {
  let (header, next) = match
    (Decode::decode(bytes, i) : Result[(@lib.U32, Int), BinaryDecodeError]) {
    Ok((@lib.U32(v), next)) => (v, next)
    Err(t) => return Err(t)
  }
  match header {
    0U =>
      match Decode::decode(bytes, next) {
        Ok((expr, i)) =>
          match Decode::decode(bytes, i) {
            Ok((bytes, i)) =>
              Ok((Data::new(DataMode::active(MemIdx::new(0), expr), bytes), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    1U =>
      match Decode::decode(bytes, next) {
        Ok((bytes, i)) => Ok((Data::new(DataMode::passive(), bytes), i))
        Err(t) => Err(t)
      }
    2U =>
      match Decode::decode(bytes, next) {
        Ok((idx, i)) =>
          match Decode::decode(bytes, i) {
            Ok((expr, i)) =>
              match Decode::decode(bytes, i) {
                Ok((bytes, i)) =>
                  Ok((Data::new(DataMode::active(idx, expr), bytes), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    _ => Err(BinaryDecodeError::InvalidDataHeader)
  }
}

///|
pub impl Decode for DataSec with fn decode(bytes, i) {
  match decode_section_vector_payload(bytes, i, 11) {
    Ok((content, next)) => Ok((DataSec::new(content), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for StringRefsSec with fn decode(bytes, i) {
  if bytes.get(i) != Some(14) {
    return Err(BinaryDecodeError::InvalidSectionHeader)
  }
  let (@lib.U32(len_u), payload_start) = match Decode::decode(bytes, i + 1) {
    Ok(t) => t
    Err(t) => return Err(t)
  }
  if len_u > (bytes.length() - payload_start).reinterpret_as_uint() {
    return Err(BinaryDecodeError::SectionPayloadLengthOutOfRange)
  }
  let payload_end = payload_start + len_u.reinterpret_as_int()
  if payload_start == payload_end || bytes[payload_start] != 0x00 {
    return Err(BinaryDecodeError::InvalidStringRefSection)
  }
  match
    (
      Decode::decode(bytes, payload_start + 1) :
      Result[(Array[Bytes], Int), BinaryDecodeError]) {
    Ok((strings, consumed)) =>
      if consumed == payload_end {
        Ok((StringRefsSec::new(strings), payload_end))
      } else {
        Err(BinaryDecodeError::InvalidStringRefSection)
      }
    Err(err) => Err(err)
  }
}

///|
pub impl Decode for CustomSec with fn decode(bytes, i) {
  match decode_custom_section_header(bytes, i) {
    Ok((name, payload_start, end)) =>
      Ok((CustomSec::new(name, bytes[payload_start:end].to_owned()), end))
    Err(err) => Err(err)
  }
}

///|
pub impl Decode for TypeSec with fn decode(bytes, i) {
  match decode_section_vector_payload(bytes, i, 1) {
    Ok((content, next)) => Ok((TypeSec::new(content), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for Import with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Ok((nm1, i)) =>
      match Decode::decode(bytes, i) {
        Ok((nm2, i)) =>
          match Decode::decode(bytes, i) {
            Ok((et, i)) => Ok((Import::new(nm1, nm2, et), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for Limits with fn decode(bytes, i) {
  match bytes.get(i) {
    None => Err(BinaryDecodeError::IndexOutOfBounds)
    Some(0x00) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U32(n), i)) => Ok((Limits::i32(n, None), i))
        Err(t) => Err(t)
      }
    Some(0x01) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U32(n), i)) =>
          match Decode::decode(bytes, i) {
            Ok((@lib.U32(m), i)) => Ok((Limits::i32(n, Some(m)), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0x04) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U64(n), i)) => Ok((Limits::i64(n, None), i))
        Err(t) => Err(t)
      }
    Some(0x05) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U64(n), i)) =>
          match Decode::decode(bytes, i) {
            Ok((@lib.U64(m), i)) => Ok((Limits::i64(n, Some(m)), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(_) => Err(BinaryDecodeError::InvalidLimits)
  }
}

///|
pub impl Decode for TableType with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Ok((rt, i)) =>
      match Decode::decode(bytes, i) {
        Ok((l, i)) => Ok((TableType::new(rt, l), i))
        Err(t) => Err(t)
      }
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for MemType with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x00) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U32(min), next)) =>
          Ok((MemType::new(Limits::i32(min, None)), next))
        Err(t) => Err(t)
      }
    Some(0x01) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U32(min), next)) =>
          match Decode::decode(bytes, next) {
            Ok((@lib.U32(max), end)) =>
              Ok((MemType::new(Limits::i32(min, Some(max))), end))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0x02) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U32(min), next)) =>
          Ok((MemType::new(Limits::i32(min, None), shared=true), next))
        Err(t) => Err(t)
      }
    Some(0x03) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U32(min), next)) =>
          match Decode::decode(bytes, next) {
            Ok((@lib.U32(max), end)) =>
              Ok((MemType::new(Limits::i32(min, Some(max)), shared=true), end))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0x04) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U64(min), next)) =>
          Ok((MemType::new(Limits::i64(min, None)), next))
        Err(t) => Err(t)
      }
    Some(0x05) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U64(min), next)) =>
          match Decode::decode(bytes, next) {
            Ok((@lib.U64(max), end)) =>
              Ok((MemType::new(Limits::i64(min, Some(max))), end))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0x06) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U64(min), next)) =>
          Ok((MemType::new(Limits::i64(min, None), shared=true), next))
        Err(t) => Err(t)
      }
    Some(0x07) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U64(min), next)) =>
          match Decode::decode(bytes, next) {
            Ok((@lib.U64(max), end)) =>
              Ok((MemType::new(Limits::i64(min, Some(max)), shared=true), end))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(_) => Err(BinaryDecodeError::InvalidLimits)
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
pub impl Decode for GlobalType with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Ok((vt, i)) =>
      match Decode::decode(bytes, i) {
        Ok((m, i)) => Ok((GlobalType::new(vt, m), i))
        Err(t) => Err(t)
      }
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for TagType with fn decode(bytes, i) {
  if bytes.get(i) != Some(0x00) {
    return Err(BinaryDecodeError::InvalidTagType)
  }
  match Decode::decode(bytes, i + 1) {
    Ok((idx, i)) => Ok((TagType::new(idx), i))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for ExternType with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x00) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((ExternType::func(idx), i))
        Err(t) => Err(t)
      }
    Some(0x01) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tt, i)) => Ok((ExternType::table(tt), i))
        Err(t) => Err(t)
      }
    Some(0x02) =>
      match Decode::decode(bytes, i + 1) {
        Ok((mt, i)) => Ok((ExternType::mem(mt), i))
        Err(t) => Err(t)
      }
    Some(0x03) =>
      match Decode::decode(bytes, i + 1) {
        Ok((gt, i)) => Ok((ExternType::global(gt), i))
        Err(t) => Err(t)
      }
    Some(0x04) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tt, i)) => Ok((ExternType::tag(tt), i))
        Err(t) => Err(t)
      }
    Some(_) => Err(BinaryDecodeError::InvalidExternTypeHeader)
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

// General pattern for section decoding

///|
pub impl Decode for ImportSec with fn decode(bytes, i) {
  if bytes.get(i) != Some(2) {
    return Err(BinaryDecodeError::InvalidSectionHeader)
  }
  let (U32(len), start) = match Decode::decode(bytes, i + 1) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  if len > (bytes.length() - start).reinterpret_as_uint() {
    return Err(BinaryDecodeError::SectionPayloadLengthOutOfRange)
  }
  let end = start + len.reinterpret_as_int()
  // Bound every nested string/type/vector read to this section.
  let payload = bytes[start:end].to_owned()
  let (U32(count), after_count) = match Decode::decode(payload, 0) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  let imports : Array[Import] = []
  let mut next = after_count
  for _ in 0U.. value
      Err(err) => return Err(err)
    }
    let (field_name, after_field) : (Name, Int) = match
      Decode::decode(payload, after_module) {
      Ok(value) => value
      Err(err) => return Err(err)
    }
    let kind = payload.get(after_field)
    if field_name == Name::new("") && (kind == Some(0x7f) || kind == Some(0x7e)) {
      next = after_field + 1
      let common_type : ExternType? = if kind == Some(0x7e) {
        let (ty, after_type) = match Decode::decode(payload, next) {
          Ok(value) => value
          Err(err) => return Err(err)
        }
        next = after_type
        Some(ty)
      } else {
        None
      }
      let (U32(group_count), after_group_count) = match
        Decode::decode(payload, next) {
        Ok(value) => value
        Err(err) => return Err(err)
      }
      next = after_group_count
      for _ in 0U.. value
          Err(err) => return Err(err)
        }
        next = after_name
        let ty = match common_type {
          Some(ty) => ty
          None => {
            let (ty, after_type) = match Decode::decode(payload, next) {
              Ok(value) => value
              Err(err) => return Err(err)
            }
            next = after_type
            ty
          }
        }
        imports.push(Import::new(module_name, name, ty))
      }
    } else {
      let (ty, after_type) = match Decode::decode(payload, after_field) {
        Ok(value) => value
        Err(err) => return Err(err)
      }
      imports.push(Import::new(module_name, field_name, ty))
      next = after_type
    }
  }
  if next != payload.length() {
    Err(BinaryDecodeError::InvalidSectionPayloadTrailingBytes)
  } else {
    Ok((ImportSec::new(imports), end))
  }
}

///|
pub impl Decode for FuncSec with fn decode(bytes, i) {
  match decode_section_vector_payload(bytes, i, 3) {
    Ok((content, next)) => Ok((FuncSec::new(content), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for BlockType with fn decode(bytes, i) {
  if bytes.get(i) is Some(0x40) {
    return Ok((BlockType::void_(), i + 1))
  }
  if Decode::decode(bytes, i) is Ok((vt, i)) {
    return Ok((BlockType::val_type(vt), i))
  }
  if Decode::decode(bytes, i) is Ok((@lib.S33(idx), i)) {
    return Ok((BlockType::type_idx(TypeIdx::new(idx)), i))
  }
  Err(BinaryDecodeError::InvalidBlockType)
}

///|
const MAX_INSTRUCTION_NESTING_DEPTH : Int = 20000

///|
fn decode_expr_with_depth(
  bytes : Bytes,
  i : Int,
  depth : Int,
) -> Result[(Expr, Int), BinaryDecodeError] {
  if depth > MAX_INSTRUCTION_NESTING_DEPTH {
    return Err(BinaryDecodeError::InstructionNestingLimitExceeded)
  }
  let a = Array::new()
  let mut i = i
  while true {
    match decode_instruction_with_depth(bytes, i, depth + 1) {
      Ok((inst, next)) => {
        a.push(inst)
        i = next
      }
      Err(_) if bytes.get(i) == Some(0x0B) => break
      Err(t) => return Err(t)
    }
  }
  Ok((Expr::new(a), i + 1))
}

///|
pub impl Decode for Expr with fn decode(bytes, i) {
  decode_expr_with_depth(bytes, i, 0)
}

///|
pub impl Decode for Table with fn decode(bytes, i) {
  match (bytes.get(i), bytes.get(i + 1)) {
    (Some(0x40), Some(0x00)) =>
      match Decode::decode(bytes, i + 2) {
        Ok((tt, i)) =>
          match Decode::decode(bytes, i) {
            Ok((e, i)) => Ok((Table::new(tt, Some(e)), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    _ =>
      match Decode::decode(bytes, i) {
        Ok((tt, i)) => Ok((Table::new(tt, None), i))
        Err(t) => Err(t)
      }
  }
}

///|
pub impl Decode for Global with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Ok((gt, i)) =>
      match Decode::decode(bytes, i) {
        Ok((expr, i)) => Ok((Global::new(gt, expr), i))
        Err(t) => Err(t)
      }
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for ExternIdx with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x00) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((ExternIdx::func(idx), i))
        Err(t) => Err(t)
      }
    Some(0x01) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((ExternIdx::table(idx), i))
        Err(t) => Err(t)
      }
    Some(0x02) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((ExternIdx::mem(idx), i))
        Err(t) => Err(t)
      }
    Some(0x03) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((ExternIdx::global(idx), i))
        Err(t) => Err(t)
      }
    Some(0x04) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((ExternIdx::tag(idx), i))
        Err(t) => Err(t)
      }
    Some(_) => Err(BinaryDecodeError::InvalidExternIndex)
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
pub impl Decode for Export with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Ok((n, i)) =>
      match Decode::decode(bytes, i) {
        Ok((idx, i)) => Ok((Export::new(n, idx), i))
        Err(t) => Err(t)
      }
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for TableSec with fn decode(bytes, i) {
  match decode_section_vector_payload(bytes, i, 4) {
    Ok((content, next)) => Ok((TableSec::new(content), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for MemSec with fn decode(bytes, i) {
  match decode_section_vector_payload(bytes, i, 5) {
    Ok((content, next)) => Ok((MemSec::new(content), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for TagSec with fn decode(bytes, i) {
  match decode_section_vector_payload(bytes, i, 13) {
    Ok((content, next)) => Ok((TagSec::new(content), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for GlobalSec with fn decode(bytes, i) {
  match decode_section_vector_payload(bytes, i, 6) {
    Ok((content, next)) => Ok((GlobalSec::new(content), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for ExportSec with fn decode(bytes, i) {
  match decode_section_vector_payload(bytes, i, 7) {
    Ok((content, next)) => Ok((ExportSec::new(content), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for StartSec with fn decode(bytes, i) {
  match
    (
      decode_section_payload(bytes, i, 8) :
      Result[(FuncIdx, Int), BinaryDecodeError]) {
    Ok((idx, next)) => Ok((StartSec::new(idx), next))
    Err(t) => Err(t)
  }
}

///|
pub impl Decode for Bytes with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Err(t) => Err(t)
    Ok((@lib.U32(len), i)) if len <= (bytes.length() - i).reinterpret_as_uint() => {
      let next = i + len.reinterpret_as_int()
      let val = bytes[i:next].to_owned()
      Ok((val, next))
    }
    Ok(_) => Err(BinaryDecodeError::InvalidOffset)
  }
}

///|
pub impl Decode for ResumeHandler with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x00) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tag, i)) =>
          match Decode::decode(bytes, i) {
            Ok((label, i)) => Ok((ResumeHandler::on_label(tag, label), i))
            Err(err) => Err(err)
          }
        Err(err) => Err(err)
      }
    Some(0x01) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tag, i)) => Ok((ResumeHandler::on_switch(tag), i))
        Err(err) => Err(err)
      }
    Some(_) => Err(BinaryDecodeError::InvalidInstruction)
    None => Err(BinaryDecodeError::IndexOutOfBounds)
  }
}

///|
pub impl Decode for Catch with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x00) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tidx, i)) =>
          match Decode::decode(bytes, i) {
            Ok((lidx, i)) => Ok((Catch::new(tidx, lidx), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0x01) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tidx, i)) =>
          match Decode::decode(bytes, i) {
            Ok((lidx, i)) => Ok((Catch::ref_(tidx, lidx), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0x02) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Catch::all(idx), i))
        Err(t) => Err(t)
      }
    Some(0x03) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Catch::all_ref(idx), i))
        Err(t) => Err(t)
      }
    _ => Err(BinaryDecodeError::InvalidCatch)
  }
}

///|
pub impl Decode for CastOp with fn decode(bytes, i) {
  match bytes.get(i) {
    Some(0x00) => Ok((CastOp::new(false, false), i + 1))
    Some(0x01) => Ok((CastOp::new(true, false), i + 1))
    Some(0x02) => Ok((CastOp::new(false, true), i + 1))
    Some(0x03) => Ok((CastOp::new(true, true), i + 1))
    _ => Err(BinaryDecodeError::InvalidCastOp)
  }
}

///|
pub impl Decode for MemArg with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Ok((@lib.U32(n), i)) if n >= 64 && n <= 72 =>
      match Decode::decode(bytes, i) {
        Ok((x, i)) =>
          match Decode::decode(bytes, i) {
            Ok((@lib.U64(m), i)) => Ok((MemArg::new(n - 64, Some(x), m), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Ok((@lib.U32(n), i)) if n <= 8 =>
      match Decode::decode(bytes, i) {
        Ok((@lib.U64(m), i)) => Ok((MemArg::new(n, None, m), i))
        Err(t) => Err(t)
      }
    Err(t) => Err(t)
    _ => Err(BinaryDecodeError::InvalidMemArg)
  }
}

///|
fn decode_atomic_memarg(
  bytes : Bytes,
  i : Int,
  is_rmw : Bool,
) -> Result[(@lib.AtomicOrder, MemArg, Int), BinaryDecodeError] {
  let (@lib.U32(raw), decoded_next) = match Decode::decode(bytes, i) {
    Ok(decoded) => decoded
    Err(err) => return Err(err)
  }
  if (raw & 0xffffff80U) != 0U {
    return Err(BinaryDecodeError::InvalidMemArg)
  }
  let mut next = decoded_next
  let has_order = (raw & 0x10U) != 0U
  let has_backing_array = (raw & 0x20U) != 0U
  let has_memory_index = (raw & 0x40U) != 0U
  if has_backing_array {
    return Err(BinaryDecodeError::InvalidMemArg)
  }
  let align = raw & 0x0fU
  if align > 8U {
    return Err(BinaryDecodeError::InvalidMemArg)
  }
  let memory = if has_memory_index {
    match Decode::decode(bytes, next) {
      Ok((memory, after_memory)) => {
        next = after_memory
        Some(memory)
      }
      Err(err) => return Err(err)
    }
  } else {
    None
  }
  let order = if has_order {
    let decoded = if is_rmw {
      decode_aggregate_atomic_rmw_order(bytes, next)
    } else {
      Decode::decode(bytes, next)
    }
    match decoded {
      Ok((order, after_order)) => {
        next = after_order
        order
      }
      Err(err) => return Err(err)
    }
  } else {
    @lib.AtomicOrder::seq_cst()
  }
  match Decode::decode(bytes, next) {
    Ok((@lib.U64(offset), after_offset)) =>
      Ok((order, MemArg::new(align, memory, offset), after_offset))
    Err(err) => Err(err)
  }
}

///|
fn decode_i8x16_shuffle_lanes(
  bytes : Bytes,
  i : Int,
) -> Result[(FixedArray[LaneIdx], Int), BinaryDecodeError] {
  let lanes : FixedArray[LaneIdx] = FixedArray::make(16, LaneIdx::new(0))
  let mut next = i
  for lane_i in 0..<16 {
    match bytes.get(next) {
      Some(n) if n < 32 => {
        lanes[lane_i] = LaneIdx::new(n)
        next += 1
      }
      _ => return Err(BinaryDecodeError::InvalidLaneIndex)
    }
  }
  Ok((lanes, next))
}

///|
fn i8x16_shuffle_from_lanes(lanes : FixedArray[LaneIdx]) -> Instruction {
  Instruction::i8x16_shuffle(
    lanes[0],
    lanes[1],
    lanes[2],
    lanes[3],
    lanes[4],
    lanes[5],
    lanes[6],
    lanes[7],
    lanes[8],
    lanes[9],
    lanes[10],
    lanes[11],
    lanes[12],
    lanes[13],
    lanes[14],
    lanes[15],
  )
}

///|
priv enum LegacyCatchDecodeFrame {
  LegacyTaggedCatchFrame(TagIdx, Array[Instruction])
  LegacyCatchAllFrame(Array[Instruction])
}

///|
priv enum StructuredDecodeFrame {
  BlockFrame(BlockType, Int, Array[Instruction])
  LoopFrame(BlockType, Int, Array[Instruction])
  IfFrame(BlockType, Int, Array[Instruction], Array[Instruction]?)
  LegacyTryFrame(
    BlockType,
    Int,
    Array[Instruction],
    Array[@lib.LegacyCatch],
    LegacyCatchDecodeFrame?,
    LabelIdx?
  )
  TryTableFrame(BlockType, Array[Catch], Int, Array[Instruction])
}

///|
fn structured_frame_body_instruction_depth(
  frame : StructuredDecodeFrame,
) -> Int {
  match frame {
    BlockFrame(_, body_depth, _) => body_depth
    LoopFrame(_, body_depth, _) => body_depth
    IfFrame(_, body_depth, _, _) => body_depth
    LegacyTryFrame(_, body_depth, _, _, _, _) => body_depth
    TryTableFrame(_, _, body_depth, _) => body_depth
  }
}

///|
fn structured_frame_push_instruction(
  frame : StructuredDecodeFrame,
  inst : Instruction,
) -> StructuredDecodeFrame {
  // Only the already-owned active body changes. Every frame field and active
  // catch stays the same, so keep the frame after appending instead of boxing
  // another frame (and another catch wrapper) for each decoded instruction.
  match frame {
    BlockFrame(_, _, body)
    | LoopFrame(_, _, body)
    | TryTableFrame(_, _, _, body) => body.push(inst)
    IfFrame(_, _, then_body, else_body) =>
      match else_body {
        Some(body) => body.push(inst)
        None => then_body.push(inst)
      }
    LegacyTryFrame(_, _, body, _, active_catch, _) =>
      match active_catch {
        Some(LegacyTaggedCatchFrame(_, catch_body))
        | Some(LegacyCatchAllFrame(catch_body)) => catch_body.push(inst)
        None => body.push(inst)
      }
  }
  frame
}

///|
fn structured_frame_finish(frame : StructuredDecodeFrame) -> Instruction {
  match frame {
    BlockFrame(bt, _, body) => Instruction::block(bt, Expr::new(body))
    LoopFrame(bt, _, body) => Instruction::loop_(bt, Expr::new(body))
    IfFrame(bt, _, then_body, else_body) =>
      match else_body {
        Some(else_body) => Instruction::if_(bt, then_body, Some(else_body))
        None => Instruction::if_(bt, then_body, None)
      }
    LegacyTryFrame(bt, _, body, catches, active_catch, delegate) => {
      match active_catch {
        Some(LegacyTaggedCatchFrame(tag, catch_body)) =>
          catches.push(@lib.LegacyCatch::tagged(tag, Expr::new(catch_body)))
        Some(LegacyCatchAllFrame(catch_body)) =>
          catches.push(@lib.LegacyCatch::all(Expr::new(catch_body)))
        None => ()
      }
      Instruction::try_(bt, Expr::new(body), catches, delegate~)
    }
    TryTableFrame(bt, catches, _, body) =>
      Instruction::try_table(bt, catches, Expr::new(body))
  }
}

///|
fn decode_structured_frame_header(
  bytes : Bytes,
  i : Int,
  depth : Int,
) -> Result[(StructuredDecodeFrame, Int), BinaryDecodeError] {
  if depth + 1 > MAX_INSTRUCTION_NESTING_DEPTH {
    return Err(BinaryDecodeError::InstructionNestingLimitExceeded)
  }
  let body_depth = depth + 2
  match bytes.get(i) {
    Some(0x02) =>
      match Decode::decode(bytes, i + 1) {
        Ok((bt, next)) => Ok((BlockFrame(bt, body_depth, []), next))
        Err(err) => Err(err)
      }
    Some(0x03) =>
      match Decode::decode(bytes, i + 1) {
        Ok((bt, next)) => Ok((LoopFrame(bt, body_depth, []), next))
        Err(err) => Err(err)
      }
    Some(0x04) =>
      match Decode::decode(bytes, i + 1) {
        Ok((bt, next)) => Ok((IfFrame(bt, body_depth, [], None), next))
        Err(err) => Err(err)
      }
    Some(0x06) =>
      match Decode::decode(bytes, i + 1) {
        Ok((bt, next)) =>
          Ok((LegacyTryFrame(bt, body_depth, [], [], None, None), next))
        Err(err) => Err(err)
      }
    Some(0x1F) =>
      match Decode::decode(bytes, i + 1) {
        Ok((bt, next)) =>
          match Decode::decode(bytes, next) {
            Ok((catches, next)) =>
              Ok((TryTableFrame(bt, catches, body_depth, []), next))
            Err(err) => Err(err)
          }
        Err(err) => Err(err)
      }
    _ => Err(BinaryDecodeError::InvalidInstruction)
  }
}

///|
fn decode_structured_instruction_with_depth(
  bytes : Bytes,
  i : Int,
  depth : Int,
) -> Result[(Instruction, Int), BinaryDecodeError] {
  let (initial_frame, next) = match
    decode_structured_frame_header(bytes, i, depth) {
    Ok(decoded) => decoded
    Err(err) => return Err(err)
  }
  let frames : Array[StructuredDecodeFrame] = [initial_frame]
  let mut pos = next
  while frames.length() > 0 {
    let frame = frames.pop().unwrap()
    match (frame, bytes.get(pos)) {
      (IfFrame(bt, body_depth, then_body, None), Some(0x05)) => {
        pos += 1
        frames.push(IfFrame(bt, body_depth, then_body, Some([])))
      }
      (
        LegacyTryFrame(bt, body_depth, body, catches, active_catch, None),
        Some(0x07),
      ) => {
        match active_catch {
          Some(LegacyTaggedCatchFrame(tag, catch_body)) =>
            catches.push(@lib.LegacyCatch::tagged(tag, Expr::new(catch_body)))
          Some(LegacyCatchAllFrame(_)) =>
            return Err(BinaryDecodeError::InvalidInstruction)
          None => ()
        }
        match Decode::decode(bytes, pos + 1) {
          Ok((tag, next)) => {
            frames.push(
              LegacyTryFrame(
                bt,
                body_depth,
                body,
                catches,
                Some(LegacyTaggedCatchFrame(tag, [])),
                None,
              ),
            )
            pos = next
          }
          Err(err) => return Err(err)
        }
      }
      (
        LegacyTryFrame(bt, body_depth, body, catches, active_catch, None),
        Some(0x19),
      ) => {
        match active_catch {
          Some(LegacyTaggedCatchFrame(tag, catch_body)) =>
            catches.push(@lib.LegacyCatch::tagged(tag, Expr::new(catch_body)))
          Some(LegacyCatchAllFrame(_)) =>
            return Err(BinaryDecodeError::InvalidInstruction)
          None => ()
        }
        frames.push(
          LegacyTryFrame(
            bt,
            body_depth,
            body,
            catches,
            Some(LegacyCatchAllFrame([])),
            None,
          ),
        )
        pos += 1
      }
      (LegacyTryFrame(bt, _, body, catches, None, None), Some(0x18)) => {
        if catches.length() > 0 {
          return Err(BinaryDecodeError::InvalidInstruction)
        }
        let (delegate, next) = match Decode::decode(bytes, pos + 1) {
          Ok(decoded) => decoded
          Err(err) => return Err(err)
        }
        let inst = Instruction::try_(
          bt,
          Expr::new(body),
          [],
          delegate=Some(delegate),
        )
        pos = next
        if frames.is_empty() {
          return Ok((inst, pos))
        }
        let parent = frames.pop().unwrap()
        frames.push(structured_frame_push_instruction(parent, inst))
      }
      (frame, Some(0x0B)) => {
        let inst = structured_frame_finish(frame)
        pos += 1
        if frames.is_empty() {
          return Ok((inst, pos))
        }
        let parent = frames.pop().unwrap()
        frames.push(structured_frame_push_instruction(parent, inst))
      }
      (frame, Some(0x02))
      | (frame, Some(0x03))
      | (frame, Some(0x04))
      | (frame, Some(0x06))
      | (frame, Some(0x1F)) => {
        let body_depth = structured_frame_body_instruction_depth(frame)
        frames.push(frame)
        match decode_structured_frame_header(bytes, pos, body_depth) {
          Ok((child, next)) => {
            frames.push(child)
            pos = next
          }
          Err(err) => return Err(err)
        }
      }
      (frame, _) => {
        let body_depth = structured_frame_body_instruction_depth(frame)
        match decode_instruction_with_depth(bytes, pos, body_depth) {
          Ok((inst, next)) => {
            frames.push(structured_frame_push_instruction(frame, inst))
            pos = next
          }
          Err(err) => return Err(err)
        }
      }
    }
  }
  Err(BinaryDecodeError::UnreachableDecodeState)
}

///|
fn decode_instruction_with_depth(
  bytes : Bytes,
  i : Int,
  depth : Int,
) -> Result[(Instruction, Int), BinaryDecodeError] {
  if depth > MAX_INSTRUCTION_NESTING_DEPTH {
    return Err(BinaryDecodeError::InstructionNestingLimitExceeded)
  }
  match bytes.get(i) {
    Some(0x00) => Ok((Instruction::unreachable_(), i + 1))
    Some(0x01) => Ok((Instruction::nop(), i + 1))
    Some(0x1A) => Ok((Instruction::drop(), i + 1))
    Some(0x1B) => Ok((Instruction::select(types=None), i + 1))
    Some(0x1C) =>
      match Decode::decode(bytes, i + 1) {
        Ok((vts, i)) => Ok((Instruction::select(types=Some(vts)), i))
        Err(t) => Err(t)
      }
    Some(0x02) | Some(0x03) | Some(0x04) | Some(0x06) | Some(0x1F) =>
      decode_structured_instruction_with_depth(bytes, i, depth)
    Some(0x08) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tag, i)) => Ok((Instruction::throw_(tag), i))
        Err(t) => Err(t)
      }
    Some(0x09) =>
      match Decode::decode(bytes, i + 1) {
        Ok((depth, i)) => Ok((Instruction::rethrow(depth), i))
        Err(t) => Err(t)
      }
    Some(0x0A) => Ok((Instruction::throw_ref(), i + 1))
    Some(0x0C) =>
      match Decode::decode(bytes, i + 1) {
        Ok((lbl, i)) => Ok((Instruction::br(lbl), i))
        Err(t) => Err(t)
      }
    Some(0x0D) =>
      match Decode::decode(bytes, i + 1) {
        Ok((lbl, i)) => Ok((Instruction::br_if(lbl), i))
        Err(t) => Err(t)
      }
    Some(0x0E) =>
      match Decode::decode(bytes, i + 1) {
        Ok((lbls, i)) =>
          match Decode::decode(bytes, i) {
            Ok((lbl, i)) => Ok((Instruction::br_table(lbls, lbl), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0x0F) => Ok((Instruction::return_(), i + 1))
    Some(0x10) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::call(idx), i))
        Err(t) => Err(t)
      }
    Some(0x11) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tidx, i)) =>
          match Decode::decode(bytes, i) {
            Ok((tblidx, i)) => Ok((Instruction::call_indirect(tidx, tblidx), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0x12) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::return_call(idx), i))
        Err(t) => Err(t)
      }
    Some(0x13) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tidx, i)) =>
          match Decode::decode(bytes, i) {
            Ok((tblidx, i)) =>
              Ok((Instruction::return_call_indirect(tidx, tblidx), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0x14) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tidx, i)) => Ok((Instruction::call_ref(tidx), i))
        Err(t) => Err(t)
      }
    Some(0x15) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tidx, i)) => Ok((Instruction::return_call_ref(tidx), i))
        Err(t) => Err(t)
      }
    Some(0xE0) =>
      match Decode::decode(bytes, i + 1) {
        Ok((type_idx, i)) => Ok((Instruction::cont_new(type_idx), i))
        Err(t) => Err(t)
      }
    Some(0xE1) =>
      match Decode::decode(bytes, i + 1) {
        Ok((source_type, i)) =>
          match Decode::decode(bytes, i) {
            Ok((target_type, i)) =>
              Ok((Instruction::cont_bind(source_type, target_type), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0xE2) =>
      match Decode::decode(bytes, i + 1) {
        Ok((tag, i)) => Ok((Instruction::suspend(tag), i))
        Err(t) => Err(t)
      }
    Some(0xE3) =>
      match Decode::decode(bytes, i + 1) {
        Ok((type_idx, i)) =>
          match Decode::decode(bytes, i) {
            Ok((handlers, i)) =>
              Ok((Instruction::resume_(type_idx, handlers), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0xE4) =>
      match Decode::decode(bytes, i + 1) {
        Ok((type_idx, i)) =>
          match Decode::decode(bytes, i) {
            Ok((tag, i)) =>
              match Decode::decode(bytes, i) {
                Ok((handlers, i)) =>
                  Ok((Instruction::resume_throw(type_idx, tag, handlers), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0xE5) =>
      match Decode::decode(bytes, i + 1) {
        Ok((type_idx, i)) =>
          match Decode::decode(bytes, i) {
            Ok((handlers, i)) =>
              Ok((Instruction::resume_throw_ref(type_idx, handlers), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0xE6) =>
      match Decode::decode(bytes, i + 1) {
        Ok((type_idx, i)) =>
          match Decode::decode(bytes, i) {
            Ok((tag, i)) => Ok((Instruction::stack_switch(type_idx, tag), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
      }
    Some(0xD5) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::br_on_null(idx), i))
        Err(t) => Err(t)
      }
    Some(0xD6) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::br_on_non_null(idx), i))
        Err(t) => Err(t)
      }
    Some(0x20) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::local_get(idx), i))
        Err(t) => Err(t)
      }
    Some(0x21) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::local_set(idx), i))
        Err(t) => Err(t)
      }
    Some(0x22) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::local_tee(idx), i))
        Err(t) => Err(t)
      }
    Some(0x23) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::global_get(idx), i))
        Err(t) => Err(t)
      }
    Some(0x24) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::global_set(idx), i))
        Err(t) => Err(t)
      }
    Some(0x25) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::table_get(idx), i))
        Err(t) => Err(t)
      }
    Some(0x26) =>
      match Decode::decode(bytes, i + 1) {
        Ok((idx, i)) => Ok((Instruction::table_set(idx), i))
        Err(t) => Err(t)
      }
    Some(0x28) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i32_load(), fn(ma) {
        Instruction::i32_load(ma)
      })
    Some(0x29) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i64_load(), fn(ma) {
        Instruction::i64_load(ma)
      })
    Some(0x2A) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::f32_load(), fn(ma) {
        Instruction::f32_load(ma)
      })
    Some(0x2B) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::f64_load(), fn(ma) {
        Instruction::f64_load(ma)
      })
    Some(0x2C) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i32_load8s(), fn(ma) {
        Instruction::i32_load8s(ma)
      })
    Some(0x2D) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i32_load8u(), fn(ma) {
        Instruction::i32_load8u(ma)
      })
    Some(0x2E) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i32_load16s(), fn(ma) {
        Instruction::i32_load16s(ma)
      })
    Some(0x2F) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i32_load16u(), fn(ma) {
        Instruction::i32_load16u(ma)
      })
    Some(0x30) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i64_load8s(), fn(ma) {
        Instruction::i64_load8s(ma)
      })
    Some(0x31) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i64_load8u(), fn(ma) {
        Instruction::i64_load8u(ma)
      })
    Some(0x32) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i64_load16s(), fn(ma) {
        Instruction::i64_load16s(ma)
      })
    Some(0x33) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i64_load16u(), fn(ma) {
        Instruction::i64_load16u(ma)
      })
    Some(0x34) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i64_load32s(), fn(ma) {
        Instruction::i64_load32s(ma)
      })
    Some(0x35) =>
      decode_memory_load(bytes, i + 1, @lib.LoadOp::i64_load32u(), fn(ma) {
        Instruction::i64_load32u(ma)
      })
    Some(0x36) =>
      decode_memory_store(bytes, i + 1, @lib.StoreOp::i32_store(), fn(ma) {
        Instruction::i32_store(ma)
      })
    Some(0x37) =>
      decode_memory_store(bytes, i + 1, @lib.StoreOp::i64_store(), fn(ma) {
        Instruction::i64_store(ma)
      })
    Some(0x38) =>
      decode_memory_store(bytes, i + 1, @lib.StoreOp::f32_store(), fn(ma) {
        Instruction::f32_store(ma)
      })
    Some(0x39) =>
      decode_memory_store(bytes, i + 1, @lib.StoreOp::f64_store(), fn(ma) {
        Instruction::f64_store(ma)
      })
    Some(0x3A) =>
      decode_memory_store(bytes, i + 1, @lib.StoreOp::i32_store8(), fn(ma) {
        Instruction::i32_store8(ma)
      })
    Some(0x3B) =>
      decode_memory_store(bytes, i + 1, @lib.StoreOp::i32_store16(), fn(ma) {
        Instruction::i32_store16(ma)
      })
    Some(0x3C) =>
      decode_memory_store(bytes, i + 1, @lib.StoreOp::i64_store8(), fn(ma) {
        Instruction::i64_store8(ma)
      })
    Some(0x3D) =>
      decode_memory_store(bytes, i + 1, @lib.StoreOp::i64_store16(), fn(ma) {
        Instruction::i64_store16(ma)
      })
    Some(0x3E) =>
      decode_memory_store(bytes, i + 1, @lib.StoreOp::i64_store32(), fn(ma) {
        Instruction::i64_store32(ma)
      })
    Some(0x3F) =>
      match Decode::decode(bytes, i + 1) {
        Ok((x, i)) => Ok((Instruction::memory_size(x), i))
        Err(t) => Err(t)
      }
    Some(0x40) =>
      match Decode::decode(bytes, i + 1) {
        Ok((x, i)) => Ok((Instruction::memory_grow(x), i))
        Err(t) => Err(t)
      }
    Some(0xD0) =>
      match decode_ref_heap_type(bytes, i + 1) {
        Ok((exact, ht, i)) =>
          Ok((Instruction::ref_null_type(RefType::new(true, ht, exact~)), i))
        Err(t) =>
          match Decode::decode(bytes, i + 1) {
            Ok((abs, i)) => Ok((Instruction::ref_null(HeapType::abs(abs)), i))
            Err(_) => Err(t)
          }
      }
    Some(0xD1) => Ok((Instruction::ref_is_null(), i + 1))
    Some(0xD2) =>
      match Decode::decode(bytes, i + 1) {
        Ok((x, i)) => Ok((Instruction::ref_func(x), i))
        Err(t) => Err(t)
      }
    Some(0xD3) => Ok((Instruction::ref_eq(), i + 1))
    Some(0xD4) => Ok((Instruction::ref_as_non_null(), i + 1))
    Some(0x45) => Ok((Instruction::i32_eqz(), i + 1))
    Some(0x46) => Ok((Instruction::i32_eq(), i + 1))
    Some(0x47) => Ok((Instruction::i32_ne(), i + 1))
    Some(0x48) => Ok((Instruction::i32_lt_s(), i + 1))
    Some(0x49) => Ok((Instruction::i32_lt_u(), i + 1))
    Some(0x4A) => Ok((Instruction::i32_gt_s(), i + 1))
    Some(0x4B) => Ok((Instruction::i32_gt_u(), i + 1))
    Some(0x4C) => Ok((Instruction::i32_le_s(), i + 1))
    Some(0x4D) => Ok((Instruction::i32_le_u(), i + 1))
    Some(0x4E) => Ok((Instruction::i32_ge_s(), i + 1))
    Some(0x4F) => Ok((Instruction::i32_ge_u(), i + 1))
    Some(0x50) => Ok((Instruction::i64_eqz(), i + 1))
    Some(0x51) => Ok((Instruction::i64_eq(), i + 1))
    Some(0x52) => Ok((Instruction::i64_ne(), i + 1))
    Some(0x53) => Ok((Instruction::i64_lt_s(), i + 1))
    Some(0x54) => Ok((Instruction::i64_lt_u(), i + 1))
    Some(0x55) => Ok((Instruction::i64_gt_s(), i + 1))
    Some(0x56) => Ok((Instruction::i64_gt_u(), i + 1))
    Some(0x57) => Ok((Instruction::i64_le_s(), i + 1))
    Some(0x58) => Ok((Instruction::i64_le_u(), i + 1))
    Some(0x59) => Ok((Instruction::i64_ge_s(), i + 1))
    Some(0x5A) => Ok((Instruction::i64_ge_u(), i + 1))
    Some(0x5B) => Ok((Instruction::f32_eq(), i + 1))
    Some(0x5C) => Ok((Instruction::f32_ne(), i + 1))
    Some(0x5D) => Ok((Instruction::f32_lt(), i + 1))
    Some(0x5E) => Ok((Instruction::f32_gt(), i + 1))
    Some(0x5F) => Ok((Instruction::f32_le(), i + 1))
    Some(0x60) => Ok((Instruction::f32_ge(), i + 1))
    Some(0x61) => Ok((Instruction::f64_eq(), i + 1))
    Some(0x62) => Ok((Instruction::f64_ne(), i + 1))
    Some(0x63) => Ok((Instruction::f64_lt(), i + 1))
    Some(0x64) => Ok((Instruction::f64_gt(), i + 1))
    Some(0x65) => Ok((Instruction::f64_le(), i + 1))
    Some(0x66) => Ok((Instruction::f64_ge(), i + 1))
    Some(0x67) => Ok((Instruction::i32_clz(), i + 1))
    Some(0x68) => Ok((Instruction::i32_ctz(), i + 1))
    Some(0x69) => Ok((Instruction::i32_popcnt(), i + 1))
    Some(0x6A) => Ok((Instruction::i32_add(), i + 1))
    Some(0x6B) => Ok((Instruction::i32_sub(), i + 1))
    Some(0x6C) => Ok((Instruction::i32_mul(), i + 1))
    Some(0x6D) => Ok((Instruction::i32_div_s(), i + 1))
    Some(0x6E) => Ok((Instruction::i32_div_u(), i + 1))
    Some(0x6F) => Ok((Instruction::i32_rem_s(), i + 1))
    Some(0x70) => Ok((Instruction::i32_rem_u(), i + 1))
    Some(0x71) => Ok((Instruction::i32_and(), i + 1))
    Some(0x72) => Ok((Instruction::i32_or(), i + 1))
    Some(0x73) => Ok((Instruction::i32_xor(), i + 1))
    Some(0x74) => Ok((Instruction::i32_shl(), i + 1))
    Some(0x75) => Ok((Instruction::i32_shr_s(), i + 1))
    Some(0x76) => Ok((Instruction::i32_shr_u(), i + 1))
    Some(0x77) => Ok((Instruction::i32_rotl(), i + 1))
    Some(0x78) => Ok((Instruction::i32_rotr(), i + 1))
    Some(0x79) => Ok((Instruction::i64_clz(), i + 1))
    Some(0x7A) => Ok((Instruction::i64_ctz(), i + 1))
    Some(0x7B) => Ok((Instruction::i64_popcnt(), i + 1))
    Some(0x7C) => Ok((Instruction::i64_add(), i + 1))
    Some(0x7D) => Ok((Instruction::i64_sub(), i + 1))
    Some(0x7E) => Ok((Instruction::i64_mul(), i + 1))
    Some(0x7F) => Ok((Instruction::i64_div_s(), i + 1))
    Some(0x80) => Ok((Instruction::i64_div_u(), i + 1))
    Some(0x81) => Ok((Instruction::i64_rem_s(), i + 1))
    Some(0x82) => Ok((Instruction::i64_rem_u(), i + 1))
    Some(0x83) => Ok((Instruction::i64_and(), i + 1))
    Some(0x84) => Ok((Instruction::i64_or(), i + 1))
    Some(0x85) => Ok((Instruction::i64_xor(), i + 1))
    Some(0x86) => Ok((Instruction::i64_shl(), i + 1))
    Some(0x87) => Ok((Instruction::i64_shr_s(), i + 1))
    Some(0x88) => Ok((Instruction::i64_shr_u(), i + 1))
    Some(0x89) => Ok((Instruction::i64_rotl(), i + 1))
    Some(0x8A) => Ok((Instruction::i64_rotr(), i + 1))
    Some(0x8B) => Ok((Instruction::f32_abs(), i + 1))
    Some(0x8C) => Ok((Instruction::f32_neg(), i + 1))
    Some(0x8D) => Ok((Instruction::f32_ceil(), i + 1))
    Some(0x8E) => Ok((Instruction::f32_floor(), i + 1))
    Some(0x8F) => Ok((Instruction::f32_trunc(), i + 1))
    Some(0x90) => Ok((Instruction::f32_nearest(), i + 1))
    Some(0x91) => Ok((Instruction::f32_sqrt(), i + 1))
    Some(0x92) => Ok((Instruction::f32_add(), i + 1))
    Some(0x93) => Ok((Instruction::f32_sub(), i + 1))
    Some(0x94) => Ok((Instruction::f32_mul(), i + 1))
    Some(0x95) => Ok((Instruction::f32_div(), i + 1))
    Some(0x96) => Ok((Instruction::f32_min(), i + 1))
    Some(0x97) => Ok((Instruction::f32_max(), i + 1))
    Some(0x98) => Ok((Instruction::f32_copysign(), i + 1))
    Some(0x99) => Ok((Instruction::f64_abs(), i + 1))
    Some(0x9A) => Ok((Instruction::f64_neg(), i + 1))
    Some(0x9B) => Ok((Instruction::f64_ceil(), i + 1))
    Some(0x9C) => Ok((Instruction::f64_floor(), i + 1))
    Some(0x9D) => Ok((Instruction::f64_trunc(), i + 1))
    Some(0x9E) => Ok((Instruction::f64_nearest(), i + 1))
    Some(0x9F) => Ok((Instruction::f64_sqrt(), i + 1))
    Some(0xA0) => Ok((Instruction::f64_add(), i + 1))
    Some(0xA1) => Ok((Instruction::f64_sub(), i + 1))
    Some(0xA2) => Ok((Instruction::f64_mul(), i + 1))
    Some(0xA3) => Ok((Instruction::f64_div(), i + 1))
    Some(0xA4) => Ok((Instruction::f64_min(), i + 1))
    Some(0xA5) => Ok((Instruction::f64_max(), i + 1))
    Some(0xA6) => Ok((Instruction::f64_copysign(), i + 1))
    Some(0xA7) => Ok((Instruction::i32_wrap_i64(), i + 1))
    Some(0xA8) => Ok((Instruction::i32_trunc_f32s(), i + 1))
    Some(0xA9) => Ok((Instruction::i32_trunc_f32u(), i + 1))
    Some(0xAA) => Ok((Instruction::i32_trunc_f64s(), i + 1))
    Some(0xAB) => Ok((Instruction::i32_trunc_f64u(), i + 1))
    Some(0xAC) => Ok((Instruction::i64_extend_i32s(), i + 1))
    Some(0xAD) => Ok((Instruction::i64_extend_i32u(), i + 1))
    Some(0xAE) => Ok((Instruction::i64_trunc_f32s(), i + 1))
    Some(0xAF) => Ok((Instruction::i64_trunc_f32u(), i + 1))
    Some(0xB0) => Ok((Instruction::i64_trunc_f64s(), i + 1))
    Some(0xB1) => Ok((Instruction::i64_trunc_f64u(), i + 1))
    Some(0xB2) => Ok((Instruction::f32_convert_i32s(), i + 1))
    Some(0xB3) => Ok((Instruction::f32_convert_i32u(), i + 1))
    Some(0xB4) => Ok((Instruction::f32_convert_i64s(), i + 1))
    Some(0xB5) => Ok((Instruction::f32_convert_i64u(), i + 1))
    Some(0xB6) => Ok((Instruction::f32_demote_f64(), i + 1))
    Some(0xB7) => Ok((Instruction::f64_convert_i32s(), i + 1))
    Some(0xB8) => Ok((Instruction::f64_convert_i32u(), i + 1))
    Some(0xB9) => Ok((Instruction::f64_convert_i64s(), i + 1))
    Some(0xBA) => Ok((Instruction::f64_convert_i64u(), i + 1))
    Some(0xBB) => Ok((Instruction::f64_promote_f32(), i + 1))
    Some(0xBC) => Ok((Instruction::i32_reinterpret_f32(), i + 1))
    Some(0xBD) => Ok((Instruction::i64_reinterpret_f64(), i + 1))
    Some(0xBE) => Ok((Instruction::f32_reinterpret_i32(), i + 1))
    Some(0xBF) => Ok((Instruction::f64_reinterpret_i64(), i + 1))
    Some(0xC0) => Ok((Instruction::i32_extend8s(), i + 1))
    Some(0xC1) => Ok((Instruction::i32_extend16s(), i + 1))
    Some(0xC2) => Ok((Instruction::i64_extend8s(), i + 1))
    Some(0xC3) => Ok((Instruction::i64_extend16s(), i + 1))
    Some(0xC4) => Ok((Instruction::i64_extend32s(), i + 1))
    Some(0x41) =>
      match Decode::decode(bytes, i + 1) {
        Ok((n, i)) => Ok((Instruction::i32_const(n), i))
        Err(t) => Err(t)
      }
    Some(0x42) =>
      match Decode::decode(bytes, i + 1) {
        Ok((n, i)) => Ok((Instruction::i64_const(n), i))
        Err(t) => Err(t)
      }
    Some(0x43) =>
      match Decode::decode(bytes, i + 1) {
        Ok((n, i)) => Ok((Instruction::f32_const(n), i))
        Err(t) => Err(t)
      }
    Some(0x44) =>
      match Decode::decode(bytes, i + 1) {
        Ok((n, i)) => Ok((Instruction::f64_const(n), i))
        Err(t) => Err(t)
      }

    // Section: Control Flow/Aggregate 0xFB
    Some(0xFB) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U32(0), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::struct_new(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(1), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::struct_new_default(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(32), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::struct_new_desc(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(33), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::struct_new_default_desc(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(2), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((n, i)) => Ok((Instruction::struct_get(x, n), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(3), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((n, i)) => Ok((Instruction::struct_get_s(x, n), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(4), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((n, i)) => Ok((Instruction::struct_get_u(x, n), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(5), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((n, i)) => Ok((Instruction::struct_set(x, n), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(6), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::array_new(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(7), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::array_new_default(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(8), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((n, i)) => Ok((Instruction::array_new_fixed(x, n), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(9), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((n, i)) => Ok((Instruction::array_new_data(x, n), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(10), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((n, i)) => Ok((Instruction::array_new_elem(x, n), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(11), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::array_get(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(12), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::array_get_s(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(13), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::array_get_u(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(14), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::array_set(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(15), i)) => Ok((Instruction::array_len(), i))
        Ok((@lib.U32(16), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::array_fill(x), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(17), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x1, i)) =>
              match Decode::decode(bytes, i) {
                Ok((x2, i)) => Ok((Instruction::array_copy(x1, x2), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(18), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x1, i)) =>
              match Decode::decode(bytes, i) {
                Ok((x2, i)) => Ok((Instruction::array_init_data(x1, x2), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(19), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x1, i)) =>
              match Decode::decode(bytes, i) {
                Ok((x2, i)) => Ok((Instruction::array_init_elem(x1, x2), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(0x82), i)) =>
          match Decode::decode(bytes, i) {
            Ok((idx, i)) =>
              match decode_string_const_literal(idx) {
                Ok(literal) => Ok((Instruction::string_const(literal), i))
                Err(err) => Err(err)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(0x85), i)) => Ok((Instruction::string_measure_wtf16(), i))
        Ok((@lib.U32(0x88), i)) => Ok((Instruction::string_concat(), i))
        Ok((@lib.U32(0x89), i)) => Ok((Instruction::string_eq(), i))
        Ok((@lib.U32(0x98), i)) => Ok((Instruction::string_as_wtf16(), i))
        Ok((@lib.U32(0x9A), i)) =>
          Ok((Instruction::stringview_wtf16_get_codeunit(), i))
        Ok((@lib.U32(0x9C), i)) =>
          Ok((Instruction::stringview_wtf16_slice(), i))
        Ok((@lib.U32(0xB0), i)) => Ok((Instruction::string_new_utf8_array(), i))
        Ok((@lib.U32(0xB1), i)) =>
          Ok((Instruction::string_new_wtf16_array(), i))
        Ok((@lib.U32(0xB2), i)) =>
          Ok((Instruction::string_encode_utf8_array(), i))
        Ok((@lib.U32(0xB3), i)) =>
          Ok((Instruction::string_encode_wtf16_array(), i))
        Ok((@lib.U32(0xB4), i)) =>
          Ok((Instruction::string_new_lossy_utf8_array(), i))
        Ok((@lib.U32(0xB5), i)) => Ok((Instruction::string_new_wtf8_array(), i))
        Ok((@lib.U32(0xB6), i)) =>
          Ok((Instruction::string_encode_lossy_utf8_array(), i))
        Ok((@lib.U32(0xB7), i)) =>
          Ok((Instruction::string_encode_wtf8_array(), i))
        Ok((@lib.U32(20), i)) =>
          match decode_ref_heap_type(bytes, i) {
            Ok((exact, ht, i)) =>
              Ok(
                (Instruction::ref_test_type(RefType::new(false, ht, exact~)), i),
              )
            Err(t) => Err(t)
          }
        Ok((@lib.U32(21), i)) =>
          match decode_ref_heap_type(bytes, i) {
            Ok((exact, ht, i)) =>
              Ok(
                (Instruction::ref_test_type(RefType::new(true, ht, exact~)), i),
              )
            Err(t) => Err(t)
          }
        Ok((@lib.U32(22), i)) =>
          match decode_ref_heap_type(bytes, i) {
            Ok((exact, ht, i)) =>
              Ok(
                (Instruction::ref_cast_type(RefType::new(false, ht, exact~)), i),
              )
            Err(t) => Err(t)
          }
        Ok((@lib.U32(23), i)) =>
          match decode_ref_heap_type(bytes, i) {
            Ok((exact, ht, i)) =>
              Ok(
                (Instruction::ref_cast_type(RefType::new(true, ht, exact~)), i),
              )
            Err(t) => Err(t)
          }
        Ok((U32(24), i)) => {
          let castop : Result[(CastOp, Int), BinaryDecodeError] = Decode::decode(
            bytes, i,
          )
          match castop {
            Ok((CastOp(source_nullable, target_nullable, _, _), i)) =>
              match Decode::decode(bytes, i) {
                Ok((label, i)) =>
                  match decode_ref_heap_type(bytes, i) {
                    Ok((source_exact, source_heap, i)) =>
                      match decode_ref_heap_type(bytes, i) {
                        Ok((target_exact, target_heap, i)) =>
                          Ok(
                            (
                              Instruction::br_on_cast(
                                label,
                                source_nullable,
                                source_heap,
                                target_nullable,
                                target_heap,
                                source_exact~,
                                target_exact~,
                              ),
                              i,
                            ),
                          )
                        Err(t) => Err(t)
                      }
                    Err(t) => Err(t)
                  }
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        }
        Ok((U32(25), i)) => {
          let castop : Result[(CastOp, Int), BinaryDecodeError] = Decode::decode(
            bytes, i,
          )
          match castop {
            Ok((CastOp(source_nullable, target_nullable, _, _), i)) =>
              match Decode::decode(bytes, i) {
                Ok((label, i)) =>
                  match decode_ref_heap_type(bytes, i) {
                    Ok((source_exact, source_heap, i)) =>
                      match decode_ref_heap_type(bytes, i) {
                        Ok((target_exact, target_heap, i)) =>
                          Ok(
                            (
                              Instruction::br_on_cast_fail(
                                label,
                                source_nullable,
                                source_heap,
                                target_nullable,
                                target_heap,
                                source_exact~,
                                target_exact~,
                              ),
                              i,
                            ),
                          )
                        Err(t) => Err(t)
                      }
                    Err(t) => Err(t)
                  }
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        }
        Ok((@lib.U32(id), i)) if id == 37U || id == 38U => {
          let castop : Result[(CastOp, Int), BinaryDecodeError] = Decode::decode(
            bytes, i,
          )
          match castop {
            Ok((CastOp(source_nullable, target_nullable, _, _), i)) =>
              match Decode::decode(bytes, i) {
                Ok((label, i)) =>
                  match decode_ref_heap_type(bytes, i) {
                    Ok((source_exact, source_heap, i)) =>
                      match decode_ref_heap_type(bytes, i) {
                        Ok((target_exact, target_heap, i)) => {
                          let source = RefType::new(
                            source_nullable,
                            source_heap,
                            exact=source_exact,
                          )
                          let target = RefType::new(
                            target_nullable,
                            target_heap,
                            exact=target_exact,
                          )
                          if id == 37U {
                            Ok(
                              (
                                Instruction::br_on_cast_desc_eq(
                                  label, source, target,
                                ),
                                i,
                              ),
                            )
                          } else {
                            Ok(
                              (
                                Instruction::br_on_cast_desc_eq_fail(
                                  label, source, target,
                                ),
                                i,
                              ),
                            )
                          }
                        }
                        Err(t) => Err(t)
                      }
                    Err(t) => Err(t)
                  }
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        }
        Ok((U32(26), i)) => Ok((Instruction::any_convert_extern(), i))
        Ok((U32(27), i)) => Ok((Instruction::extern_convert_any(), i))
        Ok((U32(28), i)) => Ok((Instruction::ref_i31(), i))
        Ok((U32(31), i)) => Ok((Instruction::ref_i31_shared(), i))
        Ok((U32(29), i)) => Ok((Instruction::i31_get_s(), i))
        Ok((U32(30), i)) => Ok((Instruction::i31_get_u(), i))
        Ok((U32(34), i)) =>
          match Decode::decode(bytes, i) {
            Ok((ti, i)) => Ok((Instruction::ref_get_desc(ti), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(35), i)) =>
          match decode_ref_heap_type(bytes, i) {
            Ok((exact, ht, i)) =>
              Ok(
                (
                  Instruction::ref_cast_desc_eq_type(
                    RefType::new(false, ht, exact~),
                  ),
                  i,
                ),
              )
            Err(t) => Err(t)
          }
        Ok((@lib.U32(36), i)) =>
          match decode_ref_heap_type(bytes, i) {
            Ok((exact, ht, i)) =>
              Ok(
                (
                  Instruction::ref_cast_desc_eq_type(
                    RefType::new(true, ht, exact~),
                  ),
                  i,
                ),
              )
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
        _ => Err(BinaryDecodeError::InvalidInstruction)
      }

    // Section: Memory/Table/Elem
    Some(0xFC) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U32(0), i)) => Ok((Instruction::i32_trunc_sat_f32s(), i))
        Ok((U32(1), i)) => Ok((Instruction::i32_trunc_sat_f32u(), i))
        Ok((U32(2), i)) => Ok((Instruction::i32_trunc_sat_f64s(), i))
        Ok((U32(3), i)) => Ok((Instruction::i32_trunc_sat_f64u(), i))
        Ok((U32(4), i)) => Ok((Instruction::i64_trunc_sat_f32s(), i))
        Ok((U32(5), i)) => Ok((Instruction::i64_trunc_sat_f32u(), i))
        Ok((U32(6), i)) => Ok((Instruction::i64_trunc_sat_f64s(), i))
        Ok((U32(7), i)) => Ok((Instruction::i64_trunc_sat_f64u(), i))
        Ok((U32(8), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((y, i)) => Ok((Instruction::memory_init(x, y), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(9), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::data_drop(x), i))
            Err(t) => Err(t)
          }
        Ok((U32(10), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((y, i)) => Ok((Instruction::memory_copy(x, y), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(11), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) => Ok((Instruction::memory_fill(x), i))
            Err(t) => Err(t)
          }
        Ok((U32(12), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((y, i)) => Ok((Instruction::table_init(x, y), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(13), i)) =>
          match Decode::decode(bytes, i) {
            Ok((y, i)) => Ok((Instruction::elem_drop(y), i))
            Err(t) => Err(t)
          }
        Ok((U32(14), i)) =>
          match Decode::decode(bytes, i) {
            Ok((x, i)) =>
              match Decode::decode(bytes, i) {
                Ok((y, i)) => Ok((Instruction::table_copy(x, y), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(15), i)) =>
          match Decode::decode(bytes, i) {
            Ok((y, i)) => Ok((Instruction::table_grow(y), i))
            Err(t) => Err(t)
          }
        Ok((U32(16), i)) =>
          match Decode::decode(bytes, i) {
            Ok((y, i)) => Ok((Instruction::table_size(y), i))
            Err(t) => Err(t)
          }
        Ok((U32(17), i)) =>
          match Decode::decode(bytes, i) {
            Ok((y, i)) => Ok((Instruction::table_fill(y), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
        _ => Err(BinaryDecodeError::InvalidInstruction)
      }

    // Section: Threads atomics
    Some(0xFE) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U32(0), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::memory_atomic_notify(m), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(1), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::memory_atomic_wait32(m), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(2), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::memory_atomic_wait64(m), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(3), i)) =>
          match Decode::decode(bytes, i) {
            Ok((order, i)) => Ok((Instruction::atomic_fence(order~), i))
            Err(_) => Err(BinaryDecodeError::InvalidAtomicFenceImmediate)
          }
        Ok((@lib.U32(5), i)) =>
          match Decode::decode(bytes, i) {
            Ok((type_idx, i)) =>
              match Decode::decode(bytes, i) {
                Ok((field, i)) =>
                  Ok((Instruction::struct_wait(type_idx, field), i))
                Err(err) => Err(err)
              }
            Err(err) => Err(err)
          }
        Ok((@lib.U32(6), i)) => Ok((Instruction::waitqueue_notify(), i))
        Ok((@lib.U32(7), i)) => Ok((Instruction::waitqueue_new(), i))
        Ok((@lib.U32(15), i)) => Ok((Instruction::publish(), i))
        Ok((@lib.U32(16), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i32_atomic_load(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(17), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i64_atomic_load(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(18), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i32_atomic_load8_u(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(19), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i32_atomic_load16_u(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(20), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i64_atomic_load8_u(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(21), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i64_atomic_load16_u(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(22), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i64_atomic_load32_u(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(id), i)) if id >= 0x5C && id <= 0x5F =>
          match Decode::decode(bytes, i) {
            Ok((order, i)) =>
              match Decode::decode(bytes, i) {
                Ok((type_idx, i)) =>
                  match Decode::decode(bytes, i) {
                    Ok((field_idx, i)) => {
                      let instr = match id {
                        0x5C =>
                          Instruction::struct_atomic_get(
                            order, type_idx, field_idx,
                          )
                        0x5D =>
                          Instruction::struct_atomic_get_s(
                            order, type_idx, field_idx,
                          )
                        0x5F =>
                          Instruction::struct_atomic_set(
                            order, type_idx, field_idx,
                          )
                        _ =>
                          Instruction::struct_atomic_get_u(
                            order, type_idx, field_idx,
                          )
                      }
                      Ok((instr, i))
                    }
                    Err(t) => Err(t)
                  }
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(id), i)) if id >= 0x60 && id <= 0x66 =>
          match decode_aggregate_atomic_rmw_order(bytes, i) {
            Ok((order, i)) =>
              match Decode::decode(bytes, i) {
                Ok((type_idx, i)) =>
                  match Decode::decode(bytes, i) {
                    Ok((field_idx, i)) => {
                      let instr = match id {
                        0x60 =>
                          Instruction::struct_atomic_rmw(
                            @lib.AggregateAtomicRmwOp::add(),
                            type_idx,
                            field_idx,
                            order~,
                          )
                        0x61 =>
                          Instruction::struct_atomic_rmw(
                            @lib.AggregateAtomicRmwOp::sub(),
                            type_idx,
                            field_idx,
                            order~,
                          )
                        0x62 =>
                          Instruction::struct_atomic_rmw(
                            @lib.AggregateAtomicRmwOp::and_(),
                            type_idx,
                            field_idx,
                            order~,
                          )
                        0x63 =>
                          Instruction::struct_atomic_rmw(
                            @lib.AggregateAtomicRmwOp::or_(),
                            type_idx,
                            field_idx,
                            order~,
                          )
                        0x64 =>
                          Instruction::struct_atomic_rmw(
                            @lib.AggregateAtomicRmwOp::xor(),
                            type_idx,
                            field_idx,
                            order~,
                          )
                        0x65 =>
                          Instruction::struct_atomic_rmw(
                            @lib.AggregateAtomicRmwOp::xchg(),
                            type_idx,
                            field_idx,
                            order~,
                          )
                        _ =>
                          Instruction::struct_atomic_cmpxchg(
                            type_idx,
                            field_idx,
                            order~,
                          )
                      }
                      Ok((instr, i))
                    }
                    Err(t) => Err(t)
                  }
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(id), i)) if id >= 0x67 && id <= 0x6A =>
          match Decode::decode(bytes, i) {
            Ok((order, i)) =>
              match Decode::decode(bytes, i) {
                Ok((type_idx, i)) => {
                  let instr = match id {
                    0x67 => Instruction::array_atomic_get(order, type_idx)
                    0x68 => Instruction::array_atomic_get_s(order, type_idx)
                    0x6A => Instruction::array_atomic_set(order, type_idx)
                    _ => Instruction::array_atomic_get_u(order, type_idx)
                  }
                  Ok((instr, i))
                }
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(id), i)) if id >= 0x6B && id <= 0x71 =>
          match decode_aggregate_atomic_rmw_order(bytes, i) {
            Ok((order, i)) =>
              match Decode::decode(bytes, i) {
                Ok((type_idx, i)) => {
                  let instr = match id {
                    0x6B =>
                      Instruction::array_atomic_rmw(
                        @lib.AggregateAtomicRmwOp::add(),
                        type_idx,
                        order~,
                      )
                    0x6C =>
                      Instruction::array_atomic_rmw(
                        @lib.AggregateAtomicRmwOp::sub(),
                        type_idx,
                        order~,
                      )
                    0x6D =>
                      Instruction::array_atomic_rmw(
                        @lib.AggregateAtomicRmwOp::and_(),
                        type_idx,
                        order~,
                      )
                    0x6E =>
                      Instruction::array_atomic_rmw(
                        @lib.AggregateAtomicRmwOp::or_(),
                        type_idx,
                        order~,
                      )
                    0x6F =>
                      Instruction::array_atomic_rmw(
                        @lib.AggregateAtomicRmwOp::xor(),
                        type_idx,
                        order~,
                      )
                    0x70 =>
                      Instruction::array_atomic_rmw(
                        @lib.AggregateAtomicRmwOp::xchg(),
                        type_idx,
                        order~,
                      )
                    _ => Instruction::array_atomic_cmpxchg(type_idx, order~)
                  }
                  Ok((instr, i))
                }
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(23), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i32_atomic_store(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(24), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i64_atomic_store(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(25), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i32_atomic_store8(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(26), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i32_atomic_store16(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(27), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i64_atomic_store8(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(28), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i64_atomic_store16(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(29), i)) =>
          match decode_atomic_memarg(bytes, i, false) {
            Ok((order, m, i)) =>
              Ok((Instruction::i64_atomic_store32(m, order~), i))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(id), i)) if id >= 30 && id <= 71 =>
          match decode_atomic_memarg(bytes, i, true) {
            Ok((order, m, i)) => {
              let op : AtomicRmwOp = match id {
                30 => AtomicRmwOp::i32_add()
                31 => AtomicRmwOp::i64_add()
                32 => AtomicRmwOp::i32_8_add_u()
                33 => AtomicRmwOp::i32_16_add_u()
                34 => AtomicRmwOp::i64_8_add_u()
                35 => AtomicRmwOp::i64_16_add_u()
                36 => AtomicRmwOp::i64_32_add_u()
                37 => AtomicRmwOp::i32_sub()
                38 => AtomicRmwOp::i64_sub()
                39 => AtomicRmwOp::i32_8_sub_u()
                40 => AtomicRmwOp::i32_16_sub_u()
                41 => AtomicRmwOp::i64_8_sub_u()
                42 => AtomicRmwOp::i64_16_sub_u()
                43 => AtomicRmwOp::i64_32_sub_u()
                44 => AtomicRmwOp::i32_and()
                45 => AtomicRmwOp::i64_and()
                46 => AtomicRmwOp::i32_8_and_u()
                47 => AtomicRmwOp::i32_16_and_u()
                48 => AtomicRmwOp::i64_8_and_u()
                49 => AtomicRmwOp::i64_16_and_u()
                50 => AtomicRmwOp::i64_32_and_u()
                51 => AtomicRmwOp::i32_or()
                52 => AtomicRmwOp::i64_or()
                53 => AtomicRmwOp::i32_8_or_u()
                54 => AtomicRmwOp::i32_16_or_u()
                55 => AtomicRmwOp::i64_8_or_u()
                56 => AtomicRmwOp::i64_16_or_u()
                57 => AtomicRmwOp::i64_32_or_u()
                58 => AtomicRmwOp::i32_xor()
                59 => AtomicRmwOp::i64_xor()
                60 => AtomicRmwOp::i32_8_xor_u()
                61 => AtomicRmwOp::i32_16_xor_u()
                62 => AtomicRmwOp::i64_8_xor_u()
                63 => AtomicRmwOp::i64_16_xor_u()
                64 => AtomicRmwOp::i64_32_xor_u()
                65 => AtomicRmwOp::i32_xchg()
                66 => AtomicRmwOp::i64_xchg()
                67 => AtomicRmwOp::i32_8_xchg_u()
                68 => AtomicRmwOp::i32_16_xchg_u()
                69 => AtomicRmwOp::i64_8_xchg_u()
                70 => AtomicRmwOp::i64_16_xchg_u()
                _ => AtomicRmwOp::i64_32_xchg_u()
              }
              Ok((Instruction::atomic_rmw(op, m, order~), i))
            }
            Err(t) => Err(t)
          }
        Ok((@lib.U32(id), i)) if id >= 72 && id <= 78 =>
          match decode_atomic_memarg(bytes, i, true) {
            Ok((order, m, i)) => {
              let op : AtomicCmpxchgOp = match id {
                72 => AtomicCmpxchgOp::i32()
                73 => AtomicCmpxchgOp::i64()
                74 => AtomicCmpxchgOp::i32_8_u()
                75 => AtomicCmpxchgOp::i32_16_u()
                76 => AtomicCmpxchgOp::i64_8_u()
                77 => AtomicCmpxchgOp::i64_16_u()
                _ => AtomicCmpxchgOp::i64_32_u()
              }
              Ok((Instruction::atomic_cmpxchg(op, m, order~), i))
            }
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
        _ => Err(BinaryDecodeError::InvalidAtomicInstruction)
      }

    // Section: Vector
    Some(0xFD) =>
      match Decode::decode(bytes, i + 1) {
        Ok((@lib.U32(0), i)) =>
          decode_memory_load(bytes, i, @lib.LoadOp::v128_load(), fn(ma) {
            Instruction::v128_load(ma)
          })
        Ok((U32(1), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load8x8s(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(2), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load8x8u(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(3), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load16x4s(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(4), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load16x4u(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(5), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load32x2s(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(6), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load32x2u(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(7), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load8_splat(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(8), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load16_splat(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(9), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load32_splat(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(10), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load64_splat(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(11), i)) =>
          decode_memory_store(bytes, i, @lib.StoreOp::v128_store(), fn(ma) {
            Instruction::v128_store(ma)
          })
        Ok((U32(12), i)) =>
          // 16 bytes const
          if bytes.length() >= i + 16 {
            Ok(
              (
                Instruction::v128_const(
                  bytes.unsafe_get(i),
                  bytes.unsafe_get(i + 1),
                  bytes.unsafe_get(i + 2),
                  bytes.unsafe_get(i + 3),
                  bytes.unsafe_get(i + 4),
                  bytes.unsafe_get(i + 5),
                  bytes.unsafe_get(i + 6),
                  bytes.unsafe_get(i + 7),
                  bytes.unsafe_get(i + 8),
                  bytes.unsafe_get(i + 9),
                  bytes.unsafe_get(i + 10),
                  bytes.unsafe_get(i + 11),
                  bytes.unsafe_get(i + 12),
                  bytes.unsafe_get(i + 13),
                  bytes.unsafe_get(i + 14),
                  bytes.unsafe_get(i + 15),
                ),
                i + 16,
              ),
            )
          } else {
            Err(BinaryDecodeError::InvalidInstruction)
          }
        Ok((U32(13), i)) =>
          match decode_i8x16_shuffle_lanes(bytes, i) {
            Ok((lanes, next)) => Ok((i8x16_shuffle_from_lanes(lanes), next))
            Err(t) => Err(t)
          }
        Ok((@lib.U32(14), i)) => Ok((Instruction::i8x16_swizzle(), i))
        Ok((@lib.U32(15), i)) => Ok((Instruction::i8x16_splat(), i))
        Ok((@lib.U32(16), i)) => Ok((Instruction::i16x8_splat(), i))
        Ok((@lib.U32(17), i)) => Ok((Instruction::i32x4_splat(), i))
        Ok((@lib.U32(18), i)) => Ok((Instruction::i64x2_splat(), i))
        Ok((@lib.U32(19), i)) => Ok((Instruction::f32x4_splat(), i))
        Ok((@lib.U32(20), i)) => Ok((Instruction::f64x2_splat(), i))
        Ok((@lib.U32(35), i)) => Ok((Instruction::i8x16_eq(), i))
        Ok((@lib.U32(36), i)) => Ok((Instruction::i8x16_ne(), i))
        Ok((@lib.U32(37), i)) => Ok((Instruction::i8x16_lt_s(), i))
        Ok((@lib.U32(38), i)) => Ok((Instruction::i8x16_lt_u(), i))
        Ok((@lib.U32(39), i)) => Ok((Instruction::i8x16_gt_s(), i))
        Ok((@lib.U32(40), i)) => Ok((Instruction::i8x16_gt_u(), i))
        Ok((@lib.U32(41), i)) => Ok((Instruction::i8x16_le_s(), i))
        Ok((@lib.U32(42), i)) => Ok((Instruction::i8x16_le_u(), i))
        Ok((@lib.U32(43), i)) => Ok((Instruction::i8x16_ge_s(), i))
        Ok((@lib.U32(44), i)) => Ok((Instruction::i8x16_ge_u(), i))
        Ok((@lib.U32(45), i)) => Ok((Instruction::i16x8_eq(), i))
        Ok((@lib.U32(46), i)) => Ok((Instruction::i16x8_ne(), i))
        Ok((@lib.U32(47), i)) => Ok((Instruction::i16x8_lt_s(), i))
        Ok((@lib.U32(48), i)) => Ok((Instruction::i16x8_lt_u(), i))
        Ok((@lib.U32(49), i)) => Ok((Instruction::i16x8_gt_s(), i))
        Ok((@lib.U32(50), i)) => Ok((Instruction::i16x8_gt_u(), i))
        Ok((@lib.U32(51), i)) => Ok((Instruction::i16x8_le_s(), i))
        Ok((@lib.U32(52), i)) => Ok((Instruction::i16x8_le_u(), i))
        Ok((@lib.U32(53), i)) => Ok((Instruction::i16x8_ge_s(), i))
        Ok((@lib.U32(54), i)) => Ok((Instruction::i16x8_ge_u(), i))
        Ok((@lib.U32(55), i)) => Ok((Instruction::i32x4_eq(), i))
        Ok((@lib.U32(56), i)) => Ok((Instruction::i32x4_ne(), i))
        Ok((@lib.U32(57), i)) => Ok((Instruction::i32x4_lt_s(), i))
        Ok((@lib.U32(58), i)) => Ok((Instruction::i32x4_lt_u(), i))
        Ok((@lib.U32(59), i)) => Ok((Instruction::i32x4_gt_s(), i))
        Ok((@lib.U32(60), i)) => Ok((Instruction::i32x4_gt_u(), i))
        Ok((@lib.U32(61), i)) => Ok((Instruction::i32x4_le_s(), i))
        Ok((@lib.U32(62), i)) => Ok((Instruction::i32x4_le_u(), i))
        Ok((@lib.U32(63), i)) => Ok((Instruction::i32x4_ge_s(), i))
        Ok((@lib.U32(64), i)) => Ok((Instruction::i32x4_ge_u(), i))
        Ok((@lib.U32(65), i)) => Ok((Instruction::f32x4_eq(), i))
        Ok((@lib.U32(66), i)) => Ok((Instruction::f32x4_ne(), i))
        Ok((@lib.U32(67), i)) => Ok((Instruction::f32x4_lt(), i))
        Ok((@lib.U32(68), i)) => Ok((Instruction::f32x4_gt(), i))
        Ok((@lib.U32(69), i)) => Ok((Instruction::f32x4_le(), i))
        Ok((@lib.U32(70), i)) => Ok((Instruction::f32x4_ge(), i))
        Ok((@lib.U32(71), i)) => Ok((Instruction::f64x2_eq(), i))
        Ok((@lib.U32(72), i)) => Ok((Instruction::f64x2_ne(), i))
        Ok((@lib.U32(73), i)) => Ok((Instruction::f64x2_lt(), i))
        Ok((@lib.U32(74), i)) => Ok((Instruction::f64x2_gt(), i))
        Ok((@lib.U32(75), i)) => Ok((Instruction::f64x2_le(), i))
        Ok((@lib.U32(76), i)) => Ok((Instruction::f64x2_ge(), i))
        Ok((@lib.U32(77), i)) => Ok((Instruction::v128_not(), i))
        Ok((@lib.U32(78), i)) => Ok((Instruction::v128_and(), i))
        Ok((@lib.U32(79), i)) => Ok((Instruction::v128_andnot(), i))
        Ok((@lib.U32(80), i)) => Ok((Instruction::v128_or(), i))
        Ok((@lib.U32(81), i)) => Ok((Instruction::v128_xor(), i))
        Ok((@lib.U32(82), i)) => Ok((Instruction::v128_bitselect(), i))
        Ok((@lib.U32(83), i)) => Ok((Instruction::v128_any_true(), i))
        Ok((@lib.U32(94), i)) => Ok((Instruction::f32x4_demote_f64x2_zero(), i))
        Ok((@lib.U32(95), i)) => Ok((Instruction::f64x2_promote_low_f32x4(), i))
        Ok((@lib.U32(96), i)) => Ok((Instruction::i8x16_abs(), i))
        Ok((@lib.U32(97), i)) => Ok((Instruction::i8x16_neg(), i))
        Ok((@lib.U32(98), i)) => Ok((Instruction::i8x16_popcnt(), i))
        Ok((@lib.U32(99), i)) => Ok((Instruction::i8x16_all_true(), i))
        Ok((@lib.U32(100), i)) => Ok((Instruction::i8x16_bitmask(), i))
        Ok((@lib.U32(101), i)) => Ok((Instruction::i8x16_narrow_i16x8s(), i))
        Ok((@lib.U32(102), i)) => Ok((Instruction::i8x16_narrow_i16x8u(), i))
        Ok((@lib.U32(103), i)) => Ok((Instruction::f32x4_ceil(), i))
        Ok((@lib.U32(104), i)) => Ok((Instruction::f32x4_floor(), i))
        Ok((@lib.U32(105), i)) => Ok((Instruction::f32x4_trunc(), i))
        Ok((@lib.U32(106), i)) => Ok((Instruction::f32x4_nearest(), i))
        Ok((@lib.U32(107), i)) => Ok((Instruction::i8x16_shl(), i))
        Ok((@lib.U32(108), i)) => Ok((Instruction::i8x16_shr_s(), i))
        Ok((@lib.U32(109), i)) => Ok((Instruction::i8x16_shr_u(), i))
        Ok((@lib.U32(110), i)) => Ok((Instruction::i8x16_add(), i))
        Ok((@lib.U32(111), i)) => Ok((Instruction::i8x16_add_sat_s(), i))
        Ok((@lib.U32(112), i)) => Ok((Instruction::i8x16_add_sat_u(), i))
        Ok((@lib.U32(113), i)) => Ok((Instruction::i8x16_sub(), i))
        Ok((@lib.U32(114), i)) => Ok((Instruction::i8x16_sub_sat_s(), i))
        Ok((@lib.U32(115), i)) => Ok((Instruction::i8x16_sub_sat_u(), i))
        Ok((@lib.U32(116), i)) => Ok((Instruction::f64x2_ceil(), i))
        Ok((@lib.U32(117), i)) => Ok((Instruction::f64x2_floor(), i))
        Ok((@lib.U32(118), i)) => Ok((Instruction::i8x16_min_s(), i))
        Ok((@lib.U32(119), i)) => Ok((Instruction::i8x16_min_u(), i))
        Ok((@lib.U32(120), i)) => Ok((Instruction::i8x16_max_s(), i))
        Ok((@lib.U32(121), i)) => Ok((Instruction::i8x16_max_u(), i))
        Ok((@lib.U32(122), i)) => Ok((Instruction::f64x2_trunc(), i))
        Ok((@lib.U32(123), i)) => Ok((Instruction::i8x16_avgr_u(), i))
        Ok((@lib.U32(124), i)) =>
          Ok((Instruction::i16x8_extadd_pairwise_i8x16s(), i))
        Ok((@lib.U32(125), i)) =>
          Ok((Instruction::i16x8_extadd_pairwise_i8x16u(), i))
        Ok((@lib.U32(126), i)) =>
          Ok((Instruction::i32x4_extadd_pairwise_i16x8s(), i))
        Ok((@lib.U32(127), i)) =>
          Ok((Instruction::i32x4_extadd_pairwise_i16x8u(), i))
        Ok((@lib.U32(128), i)) => Ok((Instruction::i16x8_abs(), i))
        Ok((@lib.U32(129), i)) => Ok((Instruction::i16x8_neg(), i))
        Ok((@lib.U32(130), i)) => Ok((Instruction::i16x8q15mulr_sat_s(), i))
        Ok((@lib.U32(131), i)) => Ok((Instruction::i16x8_all_true(), i))
        Ok((@lib.U32(132), i)) => Ok((Instruction::i16x8_bitmask(), i))
        Ok((@lib.U32(133), i)) => Ok((Instruction::i16x8_narrow_i32x4s(), i))
        Ok((@lib.U32(134), i)) => Ok((Instruction::i16x8_narrow_i32x4u(), i))
        Ok((@lib.U32(135), i)) =>
          Ok((Instruction::i16x8_extend_low_i8x16s(), i))
        Ok((@lib.U32(136), i)) =>
          Ok((Instruction::i16x8_extend_high_i8x16s(), i))
        Ok((@lib.U32(137), i)) =>
          Ok((Instruction::i16x8_extend_low_i8x16u(), i))
        Ok((@lib.U32(138), i)) =>
          Ok((Instruction::i16x8_extend_high_i8x16u(), i))
        Ok((@lib.U32(139), i)) => Ok((Instruction::i16x8_shl(), i))
        Ok((@lib.U32(140), i)) => Ok((Instruction::i16x8_shr_s(), i))
        Ok((@lib.U32(141), i)) => Ok((Instruction::i16x8_shr_u(), i))
        Ok((@lib.U32(142), i)) => Ok((Instruction::i16x8_add(), i))
        Ok((@lib.U32(143), i)) => Ok((Instruction::i16x8_add_sat_s(), i))
        Ok((@lib.U32(144), i)) => Ok((Instruction::i16x8_add_sat_u(), i))
        Ok((@lib.U32(145), i)) => Ok((Instruction::i16x8_sub(), i))
        Ok((@lib.U32(146), i)) => Ok((Instruction::i16x8_sub_sat_s(), i))
        Ok((@lib.U32(147), i)) => Ok((Instruction::i16x8_sub_sat_u(), i))
        Ok((@lib.U32(148), i)) => Ok((Instruction::f64x2_nearest(), i))
        Ok((@lib.U32(149), i)) => Ok((Instruction::i16x8_mul(), i))
        Ok((@lib.U32(150), i)) => Ok((Instruction::i16x8_min_s(), i))
        Ok((@lib.U32(151), i)) => Ok((Instruction::i16x8_min_u(), i))
        Ok((@lib.U32(152), i)) => Ok((Instruction::i16x8_max_s(), i))
        Ok((@lib.U32(153), i)) => Ok((Instruction::i16x8_max_u(), i))
        Ok((@lib.U32(155), i)) => Ok((Instruction::i16x8_avgr_u(), i))
        Ok((@lib.U32(156), i)) =>
          Ok((Instruction::i16x8_extmul_low_i8x16s(), i))
        Ok((@lib.U32(157), i)) =>
          Ok((Instruction::i16x8_extmul_high_i8x16s(), i))
        Ok((@lib.U32(158), i)) =>
          Ok((Instruction::i16x8_extmul_low_i8x16u(), i))
        Ok((@lib.U32(159), i)) =>
          Ok((Instruction::i16x8_extmul_high_i8x16u(), i))
        Ok((@lib.U32(160), i)) => Ok((Instruction::i32x4_abs(), i))
        Ok((@lib.U32(161), i)) => Ok((Instruction::i32x4_neg(), i))
        Ok((@lib.U32(163), i)) => Ok((Instruction::i32x4_all_true(), i))
        Ok((@lib.U32(164), i)) => Ok((Instruction::i32x4_bitmask(), i))
        Ok((@lib.U32(167), i)) =>
          Ok((Instruction::i32x4_extend_low_i16x8s(), i))
        Ok((@lib.U32(168), i)) =>
          Ok((Instruction::i32x4_extend_high_i16x8s(), i))
        Ok((@lib.U32(169), i)) =>
          Ok((Instruction::i32x4_extend_low_i16x8u(), i))
        Ok((@lib.U32(170), i)) =>
          Ok((Instruction::i32x4_extend_high_i16x8u(), i))
        Ok((@lib.U32(171), i)) => Ok((Instruction::i32x4_shl(), i))
        Ok((@lib.U32(172), i)) => Ok((Instruction::i32x4_shr_s(), i))
        Ok((@lib.U32(173), i)) => Ok((Instruction::i32x4_shr_u(), i))
        Ok((@lib.U32(174), i)) => Ok((Instruction::i32x4_add(), i))
        Ok((@lib.U32(177), i)) => Ok((Instruction::i32x4_sub(), i))
        Ok((@lib.U32(181), i)) => Ok((Instruction::i32x4_mul(), i))
        Ok((@lib.U32(182), i)) => Ok((Instruction::i32x4_min_s(), i))
        Ok((@lib.U32(183), i)) => Ok((Instruction::i32x4_min_u(), i))
        Ok((@lib.U32(184), i)) => Ok((Instruction::i32x4_max_s(), i))
        Ok((@lib.U32(185), i)) => Ok((Instruction::i32x4_max_u(), i))
        Ok((@lib.U32(186), i)) => Ok((Instruction::i32x4_dot_i16x8s(), i))
        Ok((@lib.U32(188), i)) =>
          Ok((Instruction::i32x4_extmul_low_i16x8s(), i))
        Ok((@lib.U32(189), i)) =>
          Ok((Instruction::i32x4_extmul_high_i16x8s(), i))
        Ok((@lib.U32(190), i)) =>
          Ok((Instruction::i32x4_extmul_low_i16x8u(), i))
        Ok((@lib.U32(191), i)) =>
          Ok((Instruction::i32x4_extmul_high_i16x8u(), i))
        Ok((@lib.U32(192), i)) => Ok((Instruction::i64x2_abs(), i))
        Ok((@lib.U32(193), i)) => Ok((Instruction::i64x2_neg(), i))
        Ok((@lib.U32(195), i)) => Ok((Instruction::i64x2_all_true(), i))
        Ok((@lib.U32(196), i)) => Ok((Instruction::i64x2_bitmask(), i))
        Ok((@lib.U32(199), i)) =>
          Ok((Instruction::i64x2_extend_low_i32x4s(), i))
        Ok((@lib.U32(200), i)) =>
          Ok((Instruction::i64x2_extend_high_i32x4s(), i))
        Ok((@lib.U32(201), i)) =>
          Ok((Instruction::i64x2_extend_low_i32x4u(), i))
        Ok((@lib.U32(202), i)) =>
          Ok((Instruction::i64x2_extend_high_i32x4u(), i))
        Ok((@lib.U32(203), i)) => Ok((Instruction::i64x2_shl(), i))
        Ok((@lib.U32(204), i)) => Ok((Instruction::i64x2_shr_s(), i))
        Ok((@lib.U32(205), i)) => Ok((Instruction::i64x2_shr_u(), i))
        Ok((@lib.U32(206), i)) => Ok((Instruction::i64x2_add(), i))
        Ok((@lib.U32(209), i)) => Ok((Instruction::i64x2_sub(), i))
        Ok((@lib.U32(213), i)) => Ok((Instruction::i64x2_mul(), i))
        Ok((@lib.U32(214), i)) => Ok((Instruction::i64x2_eq(), i))
        Ok((@lib.U32(215), i)) => Ok((Instruction::i64x2_ne(), i))
        Ok((@lib.U32(216), i)) => Ok((Instruction::i64x2_lt_s(), i))
        Ok((@lib.U32(217), i)) => Ok((Instruction::i64x2_gt_s(), i))
        Ok((@lib.U32(218), i)) => Ok((Instruction::i64x2_le_s(), i))
        Ok((@lib.U32(219), i)) => Ok((Instruction::i64x2_ge_s(), i))
        Ok((@lib.U32(220), i)) =>
          Ok((Instruction::i64x2_extmul_low_i32x4s(), i))
        Ok((@lib.U32(221), i)) =>
          Ok((Instruction::i64x2_extmul_high_i32x4s(), i))
        Ok((@lib.U32(222), i)) =>
          Ok((Instruction::i64x2_extmul_low_i32x4u(), i))
        Ok((@lib.U32(223), i)) =>
          Ok((Instruction::i64x2_extmul_high_i32x4u(), i))
        Ok((@lib.U32(224), i)) => Ok((Instruction::f32x4_abs(), i))
        Ok((@lib.U32(225), i)) => Ok((Instruction::f32x4_neg(), i))
        Ok((@lib.U32(227), i)) => Ok((Instruction::f32x4_sqrt(), i))
        Ok((@lib.U32(228), i)) => Ok((Instruction::f32x4_add(), i))
        Ok((@lib.U32(229), i)) => Ok((Instruction::f32x4_sub(), i))
        Ok((@lib.U32(230), i)) => Ok((Instruction::f32x4_mul(), i))
        Ok((@lib.U32(231), i)) => Ok((Instruction::f32x4_div(), i))
        Ok((@lib.U32(232), i)) => Ok((Instruction::f32x4_min(), i))
        Ok((@lib.U32(233), i)) => Ok((Instruction::f32x4_max(), i))
        Ok((@lib.U32(234), i)) => Ok((Instruction::f32x4_pmin(), i))
        Ok((@lib.U32(235), i)) => Ok((Instruction::f32x4_pmax(), i))
        Ok((@lib.U32(236), i)) => Ok((Instruction::f64x2_abs(), i))
        Ok((@lib.U32(237), i)) => Ok((Instruction::f64x2_neg(), i))
        Ok((@lib.U32(239), i)) => Ok((Instruction::f64x2_sqrt(), i))
        Ok((@lib.U32(240), i)) => Ok((Instruction::f64x2_add(), i))
        Ok((@lib.U32(241), i)) => Ok((Instruction::f64x2_sub(), i))
        Ok((@lib.U32(242), i)) => Ok((Instruction::f64x2_mul(), i))
        Ok((@lib.U32(243), i)) => Ok((Instruction::f64x2_div(), i))
        Ok((@lib.U32(244), i)) => Ok((Instruction::f64x2_min(), i))
        Ok((@lib.U32(245), i)) => Ok((Instruction::f64x2_max(), i))
        Ok((@lib.U32(246), i)) => Ok((Instruction::f64x2_pmin(), i))
        Ok((@lib.U32(247), i)) => Ok((Instruction::f64x2_pmax(), i))
        Ok((@lib.U32(248), i)) => Ok((Instruction::i32x4_trunc_sat_f32x4s(), i))
        Ok((@lib.U32(249), i)) => Ok((Instruction::i32x4_trunc_sat_f32x4u(), i))
        Ok((@lib.U32(250), i)) => Ok((Instruction::f32x4_convert_i32x4s(), i))
        Ok((@lib.U32(251), i)) => Ok((Instruction::f32x4_convert_i32x4u(), i))
        Ok((@lib.U32(252), i)) =>
          Ok((Instruction::i32x4_trunc_sat_f64x2s_zero(), i))
        Ok((@lib.U32(253), i)) =>
          Ok((Instruction::i32x4_trunc_sat_f64x2u_zero(), i))
        Ok((@lib.U32(254), i)) =>
          Ok((Instruction::f64x2_convert_low_i32x4s(), i))
        Ok((@lib.U32(255), i)) =>
          Ok((Instruction::f64x2_convert_low_i32x4u(), i))
        Ok((@lib.U32(256), i)) => Ok((Instruction::i8x16_relaxed_swizzle(), i))
        Ok((@lib.U32(257), i)) =>
          Ok((Instruction::i32x4_relaxed_trunc_f32x4s(), i))
        Ok((@lib.U32(258), i)) =>
          Ok((Instruction::i32x4_relaxed_trunc_f32x4u(), i))
        Ok((@lib.U32(259), i)) =>
          Ok((Instruction::i32x4_relaxed_trunc_zero_f64x2s(), i))
        Ok((@lib.U32(260), i)) =>
          Ok((Instruction::i32x4_relaxed_trunc_zero_f64x2u(), i))
        Ok((@lib.U32(261), i)) => Ok((Instruction::f32x4_relaxed_madd(), i))
        Ok((@lib.U32(262), i)) => Ok((Instruction::f32x4_relaxed_nmadd(), i))
        Ok((@lib.U32(263), i)) => Ok((Instruction::f64x2_relaxed_madd(), i))
        Ok((@lib.U32(264), i)) => Ok((Instruction::f64x2_relaxed_nmadd(), i))
        Ok((@lib.U32(265), i)) =>
          Ok((Instruction::i8x16_relaxed_laneselect(), i))
        Ok((@lib.U32(266), i)) =>
          Ok((Instruction::i16x8_relaxed_laneselect(), i))
        Ok((@lib.U32(267), i)) =>
          Ok((Instruction::i32x4_relaxed_laneselect(), i))
        Ok((@lib.U32(268), i)) =>
          Ok((Instruction::i64x2_relaxed_laneselect(), i))
        Ok((@lib.U32(269), i)) => Ok((Instruction::f32x4_relaxed_min(), i))
        Ok((@lib.U32(270), i)) => Ok((Instruction::f32x4_relaxed_max(), i))
        Ok((@lib.U32(271), i)) => Ok((Instruction::f64x2_relaxed_min(), i))
        Ok((@lib.U32(272), i)) => Ok((Instruction::f64x2_relaxed_max(), i))
        Ok((@lib.U32(273), i)) =>
          Ok((Instruction::i16x8_relaxed_q15mulr_s(), i))
        Ok((@lib.U32(274), i)) =>
          Ok((Instruction::i16x8_relaxed_dot_i8x16i7x16s(), i))
        Ok((@lib.U32(275), i)) =>
          Ok((Instruction::i32x4_relaxed_dot_i8x16i7x16_add_s(), i))
        Ok((@lib.U32(21), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::i8x16_extract_lane_s(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(22), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::i8x16_extract_lane_u(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(23), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::i8x16_replace_lane(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(24), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::i16x8_extract_lane_s(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(25), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::i16x8_extract_lane_u(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(26), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::i16x8_replace_lane(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(27), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::i32x4_extract_lane(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(28), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::i32x4_replace_lane(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(29), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::i64x2_extract_lane(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(30), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::i64x2_replace_lane(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(31), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::f32x4_extract_lane(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(32), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::f32x4_replace_lane(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(33), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::f64x2_extract_lane(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(34), i)) =>
          match Decode::decode(bytes, i) {
            Ok((l, i)) => Ok((Instruction::f64x2_replace_lane(l), i))
            Err(t) => Err(t)
          }
        Ok((U32(84), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) =>
              match Decode::decode(bytes, i) {
                Ok((l, i)) => Ok((Instruction::v128_load8_lane(m, l), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(85), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) =>
              match Decode::decode(bytes, i) {
                Ok((l, i)) => Ok((Instruction::v128_load16_lane(m, l), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(86), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) =>
              match Decode::decode(bytes, i) {
                Ok((l, i)) => Ok((Instruction::v128_load32_lane(m, l), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(87), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) =>
              match Decode::decode(bytes, i) {
                Ok((l, i)) => Ok((Instruction::v128_load64_lane(m, l), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(88), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) =>
              match Decode::decode(bytes, i) {
                Ok((l, i)) => Ok((Instruction::v128_store8_lane(m, l), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(89), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) =>
              match Decode::decode(bytes, i) {
                Ok((l, i)) => Ok((Instruction::v128_store16_lane(m, l), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(90), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) =>
              match Decode::decode(bytes, i) {
                Ok((l, i)) => Ok((Instruction::v128_store32_lane(m, l), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(91), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) =>
              match Decode::decode(bytes, i) {
                Ok((l, i)) => Ok((Instruction::v128_store64_lane(m, l), i))
                Err(t) => Err(t)
              }
            Err(t) => Err(t)
          }
        Ok((U32(92), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load32_zero(m), i))
            Err(t) => Err(t)
          }
        Ok((U32(93), i)) =>
          match Decode::decode(bytes, i) {
            Ok((m, i)) => Ok((Instruction::v128_load64_zero(m), i))
            Err(t) => Err(t)
          }
        Err(t) => Err(t)
        _ => Err(BinaryDecodeError::InvalidInstruction)
      }
    _ => Err(BinaryDecodeError::InvalidInstruction)
  }
}

///|
pub impl Decode for Instruction with fn decode(bytes, i) {
  decode_instruction_with_depth(bytes, i, 0)
}

///|
pub impl Decode for FieldType with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Err(err) => Err(err)
    Ok((st, i)) =>
      match Decode::decode(bytes, i) {
        Ok((m, i)) => Ok((FieldType::new(st, m), i))
        Err(err) => Err(err)
      }
  }
}

///|
pub impl[T : Decode] Decode for Array[T] with fn decode(bytes, i) {
  match Decode::decode(bytes, i) {
    Err(t) => Err(t)
    Ok((@lib.U32(count), i)) => {
      let mut remaining = count
      let result : Array[T] = []
      let mut pos = i
      while remaining > 0 {
        match T::decode(bytes, pos) {
          Ok((t, next_pos)) => {
            result.push(t)
            pos = next_pos
            remaining = remaining - 1
          }
          Err(t) => return Err(t)
        }
      }
      Ok((result, pos))
    }
  }
}

///|
fn decode_array_memory_argument(
  bytes : Bytes,
  i : Int,
) -> Result[(MemArg, @lib.TypeIdx, Int), BinaryDecodeError] {
  let (@lib.U32(flags), next) = match Decode::decode(bytes, i) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  if (flags & 0xfffffff0U) != 0x20U || (flags & 15U) > 8U {
    return Err(BinaryDecodeError::InvalidMemArg)
  }
  let (ti, next) : (@lib.TypeIdx, Int) = match Decode::decode(bytes, next) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  match Decode::decode(bytes, next) {
    Ok((@lib.U32(offset), next)) =>
      Ok((MemArg::new(flags & 15U, None, offset.to_uint64()), ti, next))
    Err(err) => Err(err)
  }
}

///|
fn decode_memory_load(
  bytes : Bytes,
  i : Int,
  op : @lib.LoadOp,
  linear : (MemArg) -> Instruction,
) -> Result[(Instruction, Int), BinaryDecodeError] {
  let (@lib.U32(flags), _) = match Decode::decode(bytes, i) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  if (flags & 0x20U) != 0U {
    match decode_array_memory_argument(bytes, i) {
      Ok((ma, ti, next)) =>
        Ok((Instruction::array_load(op, ti, memarg=ma), next))
      Err(err) => Err(err)
    }
  } else {
    match Decode::decode(bytes, i) {
      Ok((ma, next)) => Ok((linear(ma), next))
      Err(err) => Err(err)
    }
  }
}

///|
fn decode_memory_store(
  bytes : Bytes,
  i : Int,
  op : @lib.StoreOp,
  linear : (MemArg) -> Instruction,
) -> Result[(Instruction, Int), BinaryDecodeError] {
  let (@lib.U32(flags), _) = match Decode::decode(bytes, i) {
    Ok(value) => value
    Err(err) => return Err(err)
  }
  if (flags & 0x20U) != 0U {
    match decode_array_memory_argument(bytes, i) {
      Ok((ma, ti, next)) =>
        Ok((Instruction::array_store(op, ti, memarg=ma), next))
      Err(err) => Err(err)
    }
  } else {
    match Decode::decode(bytes, i) {
      Ok((ma, next)) => Ok((linear(ma), next))
      Err(err) => Err(err)
    }
  }
}