///|
/// Half-open encoded range in the original file, never a memory address.
pub struct DataByteRange {
  offset : Int
  size : Int
} derive(Eq, Debug)

///|
pub enum DataSegmentMode {
  Active
  Passive
} derive(Eq, Debug)

///|
/// Metadata only: no payload copy and no evaluation of the offset expression.
pub struct DataSegment {
  index : Int
  mode : DataSegmentMode
  memory_index : Int?
  encoding : DataByteRange
  payload : DataByteRange
  offset_expression : DataByteRange?
  mode_size : Int
  memory_index_size : Int
  payload_length_size : Int
} derive(Eq, Debug)

///|
/// DataCount is separate and excluded from the data-section byte ledger.
/// Absent data has section=None and all ledger sizes zero; an empty section
/// still has a header and an encoded zero count. Entries remain in file order.
pub struct DataSegmentsReport {
  file_size : Int
  section : Section?
  datacount : Int?
  section_size : Int
  header_size : Int
  count_size : Int
  payload_size : Int
  entry_metadata_size : Int
  entries : Array[DataSegment]
}

///|
/// Inspect data encodings after the ordinary module structure checks. This is
/// not type/constant-expression validation, memory-index validation or an
/// instantiation check. Expression ranges include their final end instruction.
pub fn inspect_data_segments(
  data : Bytes,
) -> Result[DataSegmentsReport, WasmError] {
  let parsed = match parse_module(data) {
    Ok(value) => value
    Err(error) => return Err(error)
  }
  let mut section : Section? = None
  let mut datacount : Int? = None
  let mut datacount_offset = 0
  for item in parsed.sections {
    match item.kind {
      Data => section = Some(item)
      DataCount => {
        let reader = match reader_at_payload(data, item) {
          Ok(value) => value
          Err(error) => return Err(error)
        }
        datacount_offset = reader.position()
        datacount = match read_u32_as_int(reader) {
          Ok(value) => Some(value)
          Err(error) => return Err(error)
        }
        match finish_section(reader, DataCount) {
          Ok(_) => ()
          Err(error) => return Err(error)
        }
      }
      _ => ()
    }
  }
  let entries : Array[DataSegment] = []
  let mut count_size = 0
  let mut payload_size = 0
  let mut entry_metadata_size = 0
  let mut section_size = 0
  let mut header_size = 0
  if section is Some(item) {
    section_size = item.total_size
    header_size = item.total_size - item.payload_size
    let reader = match reader_at_payload(data, item) {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    let count_start = reader.position()
    let count = match read_u32_as_int(reader) {
      Ok(value) => value
      Err(error) => return Err(error)
    }
    count_size = reader.position() - count_start
    for index = 0; index < count; index = index + 1 {
      let start = reader.position()
      let flags = match read_u32_as_int(reader) {
        Ok(value) => value
        Err(error) => return Err(error)
      }
      let mode_size = reader.position() - start
      let mut memory_index : Int? = None
      let mut memory_index_size = 0
      let mode = match flags {
        0 => {
          memory_index = Some(0)
          Active
        }
        1 => Passive
        2 => {
          let memory_start = reader.position()
          memory_index = match read_u32_as_int(reader) {
            Ok(value) => Some(value)
            Err(error) => return Err(error)
          }
          memory_index_size = reader.position() - memory_start
          Active
        }
        _ =>
          return Err(
            WasmError::InvalidDataEncoding(
              start,
              "unsupported data segment mode " + flags.to_string(),
            ),
          )
      }
      let offset_expression = if mode == Active {
        let offset = reader.position()
        match skip_constant_expression(reader) {
          Ok(_) => ()
          Err(error) => return Err(error)
        }
        Some(DataByteRange::{ offset, size: reader.position() - offset, })
      } else {
        None
      }
      let length_start = reader.position()
      let length = match read_u32_as_int(reader) {
        Ok(value) => value
        Err(error) => return Err(error)
      }
      let payload_length_size = reader.position() - length_start
      let payload_offset = reader.position()
      // A checked view only, not a copy of the initializer bytes.
      match reader.read_bytes(length) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      let size = reader.position() - start
      payload_size += length
      entry_metadata_size += size - length
      entries.push({
        index,
        mode,
        memory_index,
        encoding: { offset: start, size, },
        payload: { offset: payload_offset, size: length, },
        offset_expression,
        mode_size,
        memory_index_size,
        payload_length_size,
      })
    }
    match finish_section(reader, Data) {
      Ok(_) => ()
      Err(error) => return Err(error)
    }
  }
  if datacount is Some(declared) && declared != entries.length() {
    return Err(
      WasmError::InvalidDataEncoding(
        datacount_offset,
        "datacount mismatch: " +
        declared.to_string() +
        " declared, " +
        entries.length().to_string() +
        " data entries",
      ),
    )
  }
  Ok({
    file_size: parsed.file_size,
    section,
    datacount,
    section_size,
    header_size,
    count_size,
    payload_size,
    entry_metadata_size,
    entries,
  })
}