///|
/// Core binary order, independent of numeric section ids (Tag=13, DataCount=12).
/// https://webassembly.github.io/spec/core/binary/modules.html#binary-module
fn section_order(kind : SectionId) -> Int {
  match kind {
    Custom => 0
    Type => 1
    Import => 2
    Function => 3
    Table => 4
    Memory => 5
    Tag => 6
    Global => 7
    Export => 8
    Start => 9
    Element => 10
    DataCount => 11
    Code => 12
    Data => 13
  }
}

///|
fn finish_section(reader : Reader, kind : SectionId) -> Result[Unit, WasmError] {
  if !reader.at_end() {
    Err(WasmError::TrailingSectionBytes(reader.position(), kind))
  } else {
    Ok(())
  }
}

///|
/// Read local declarations' type indices without validating their referents.
fn function_declaration_count(
  data : Bytes,
  section : Section,
) -> Result[Int, WasmError] {
  let reader = match reader_at_payload(data, section) {
    Ok(reader) => reader
    Err(error) => return Err(error)
  }
  let count = match read_u32_as_int(reader) {
    Ok(count) => count
    Err(error) => return Err(error)
  }
  for i = 0; i < count; i = i + 1 {
    match read_u32_as_int(reader) {
      Ok(_) => ()
      Err(error) => return Err(error)
    }
  }
  match finish_section(reader, Function) {
    Ok(_) => Ok(count)
    Err(error) => Err(error)
  }
}

///|
/// Missing function/code sections each contribute zero. Imports do not enter
/// this comparison. Every code body length is checked by the public parser's
/// shared walker, without building an analysis or allocating body records.
fn validate_function_code(
  data : Bytes,
  sections : Array[Section],
) -> Result[Unit, WasmError] {
  let mut declarations = 0
  let mut bodies = 0
  let mut related : Section? = None
  for section in sections {
    match section.kind {
      Function => {
        declarations = match function_declaration_count(data, section) {
          Ok(count) => count
          Err(error) => return Err(error)
        }
        related = Some(section)
      }
      Code => {
        bodies = match walk_code_section(data, section, 0, None) {
          Ok(count) => count
          Err(error) => return Err(error)
        }
        related = Some(section)
      }
      _ => ()
    }
  }
  if declarations != bodies {
    match related {
      Some(section) =>
        return Err(
          WasmError::FunctionCodeCountMismatch(
            section.offset,
            section.kind,
            declarations,
            bodies,
          ),
        )
      None => () // Both missing implies zero == zero.
    }
  }
  Ok(())
}