///|
/// SDO transaction layer: complete SDO Upload/Download using
/// mailbox transport, RMSM counter management, and CoE encoding/decoding.
/// Implements ETG.1500 #501 (SDO Up/Download shall).

///|
fn rmsm_begin_transaction(
  rmsm : @mailbox.Rmsm,
  retry : Bool,
) -> Byte raise @hal.EcError {
  if retry {
    rmsm.begin_retry_transaction()
  } else {
    rmsm.begin_transaction()
  }
}

///|
/// Maximum number of stale/repeat frames to discard before giving up.
let max_stale_retries : Int = 3

///|
/// Receive and validate an SDO response (FP addressing).
/// Discards stale/repeat frames by re-reading until a fresh response arrives
/// or the stale-retry budget is exhausted.
fn[N : @hal.Nic] recv_sdo_validated_fp(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  timeout : @hal.Duration,
) -> @mailbox.SdoResponse raise @hal.EcError {
  for attempt = 0; attempt < max_stale_retries + 1; attempt = attempt + 1 {
    let response_raw = mailbox_recv_rmsm(nic, station, mb_config, rmsm, timeout)
    let (mbh, resp) = @mailbox.decode_sdo_response(response_raw)
    if rmsm.validate_response(mbh.counter) {
      return resp
    }
    // Stale/repeat frame: discard and re-read
  }
  raise @hal.EcError::MailboxProtocol("RMSM: too many stale frames received")
}

///|
/// Receive and validate an SDO response (AP addressing).
/// Discards stale/repeat frames by re-reading until a fresh response arrives
/// or the stale-retry budget is exhausted.
fn[N : @hal.Nic] recv_sdo_validated_ap(
  nic : N,
  position : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  timeout : @hal.Duration,
) -> @mailbox.SdoResponse raise @hal.EcError {
  for attempt = 0; attempt < max_stale_retries + 1; attempt = attempt + 1 {
    let response_raw = mailbox_recv_ap_rmsm(
      nic, position, mb_config, rmsm, timeout,
    )
    let (mbh, resp) = @mailbox.decode_sdo_response(response_raw)
    if rmsm.validate_response(mbh.counter) {
      return resp
    }
    // Stale/repeat frame: discard and re-read
  }
  raise @hal.EcError::MailboxProtocol("RMSM: too many stale frames received")
}

///|
/// Receive and validate an SDO Info response (FP addressing).
/// Discards stale/repeat frames by re-reading until a fresh response arrives
/// or the stale-retry budget is exhausted.
fn[N : @hal.Nic] recv_sdo_info_validated_fp(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  timeout : @hal.Duration,
) -> @mailbox.SdoInfoResponse raise @hal.EcError {
  for attempt = 0; attempt < max_stale_retries + 1; attempt = attempt + 1 {
    let raw = mailbox_recv_rmsm(nic, station, mb_config, rmsm, timeout)
    let (mbh, info) = @mailbox.decode_sdo_info_response(raw)
    if rmsm.validate_response(mbh.counter) {
      return info
    }
    // Stale/repeat frame: discard and re-read
  }
  raise @hal.EcError::MailboxProtocol("RMSM: too many stale frames received")
}

///|
fn[N : @hal.Nic] sdo_download_once(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  data : Bytes,
  timeout : @hal.Duration,
  retry : Bool,
) -> Unit raise @hal.EcError {
  if data.length() > 4 {
    return sdo_download_segmented(
      nic, station, mb_config, rmsm, index, subindex, data, timeout, false, retry,
    )
  }

  let counter = rmsm_begin_transaction(rmsm, retry)
  let txn = @mailbox.build_sdo_download(counter, station, index, subindex, data)
  let frame = txn.encode()
  mailbox_send(nic, station, mb_config, frame, timeout)
  let resp = recv_sdo_validated_fp(nic, station, mb_config, rmsm, timeout)
  if resp.command != @mailbox.SdoCommand::DownloadResponse {
    raise @hal.EcError::MailboxProtocol(
      "SDO download: expected DownloadResponse, got " +
      @debug.to_string(resp.command),
    )
  }
  if resp.index != index || resp.subindex != subindex {
    raise @hal.EcError::MailboxProtocol(
      "SDO download: index/subindex mismatch in response",
    )
  }
}

///|
fn[N : @hal.Nic] sdo_upload_once(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  timeout : @hal.Duration,
  retry : Bool,
) -> Bytes raise @hal.EcError {
  let counter = rmsm_begin_transaction(rmsm, retry)
  let txn = @mailbox.build_sdo_upload(counter, station, index, subindex)
  let frame = txn.encode()
  mailbox_send(nic, station, mb_config, frame, timeout)
  let resp = recv_sdo_validated_fp(nic, station, mb_config, rmsm, timeout)
  if resp.command != @mailbox.SdoCommand::UploadResponse {
    raise @hal.EcError::MailboxProtocol(
      "SDO upload: expected UploadResponse, got " +
      @debug.to_string(resp.command),
    )
  }
  if resp.index != index || resp.subindex != subindex {
    raise @hal.EcError::MailboxProtocol(
      "SDO upload: index/subindex mismatch in response",
    )
  }
  if !resp.segmented {
    return resp.data
  }

  sdo_upload_collect_segmented(
    nic, station, mb_config, rmsm, timeout, resp, "SDO upload", retry,
  )
}

///|
fn[N : @hal.Nic] sdo_upload_ap_once(
  nic : N,
  position : UInt,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  timeout : @hal.Duration,
  retry : Bool,
) -> Bytes raise @hal.EcError {
  let counter = rmsm_begin_transaction(rmsm, retry)
  let txn = @mailbox.build_sdo_upload(counter, station, index, subindex)
  let frame = txn.encode()
  mailbox_send_ap(nic, position, mb_config, frame, timeout)
  let resp = recv_sdo_validated_ap(nic, position, mb_config, rmsm, timeout)
  if resp.command != @mailbox.SdoCommand::UploadResponse {
    raise @hal.EcError::MailboxProtocol(
      "SDO upload: expected UploadResponse, got " +
      @debug.to_string(resp.command),
    )
  }
  if resp.index != index || resp.subindex != subindex {
    raise @hal.EcError::MailboxProtocol(
      "SDO upload: index/subindex mismatch in response",
    )
  }
  if !resp.segmented {
    return resp.data
  }

  sdo_upload_collect_segmented_ap(
    nic, position, station, mb_config, rmsm, timeout, resp, "SDO upload", retry,
  )
}

///|
/// Execute an SDO Download (write to slave Object Dictionary).
/// Encodes the request, sends via mailbox, waits for response, validates.
/// Supports expedited transfer (data ≤ 4 bytes).
pub fn[N : @hal.Nic] sdo_download(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  data : Bytes,
  timeout : @hal.Duration,
) -> Unit raise @hal.EcError {
  sdo_download_once(
    nic, station, mb_config, rmsm, index, subindex, data, timeout, false,
  )
}

///|
/// Execute an SDO Upload (read from slave Object Dictionary).
/// Encodes the request, sends via mailbox, waits for response, returns data.
/// Supports expedited transfer (data ≤ 4 bytes).
pub fn[N : @hal.Nic] sdo_upload(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  timeout : @hal.Duration,
) -> Bytes raise @hal.EcError {
  sdo_upload_once(
    nic, station, mb_config, rmsm, index, subindex, timeout, false,
  )
}

///|
/// Execute an SDO Upload using auto-increment mailbox transport.
pub fn[N : @hal.Nic] sdo_upload_ap(
  nic : N,
  position : UInt,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  timeout : @hal.Duration,
) -> Bytes raise @hal.EcError {
  sdo_upload_ap_once(
    nic, position, station, mb_config, rmsm, index, subindex, timeout, false,
  )
}

///|
/// Execute an SDO Download with RMSM retry support.
/// Retries on MailboxTimeout up to RMSM's max_retries limit.
pub fn[N : @hal.Nic] sdo_download_retry(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  data : Bytes,
  timeout : @hal.Duration,
) -> Unit raise @hal.EcError {
  // First attempt
  let result = Result::Ok(
    sdo_download(nic, station, mb_config, rmsm, index, subindex, data, timeout),
  ) catch {
    err => Result::Err(err)
  }
  match result {
    Ok(_) => return
    Err(@hal.EcError::MailboxTimeout) => ()
    Err(e) => raise e
  }
  // Retry loop
  for ;; {
    if !rmsm.record_timeout() {
      raise @hal.EcError::MailboxTimeout
    }
    rmsm.reset_for_retry()
    let result = Result::Ok(
      sdo_download_once(
        nic, station, mb_config, rmsm, index, subindex, data, timeout, true,
      ),
    ) catch {
      err => Result::Err(err)
    }
    match result {
      Ok(_) => return
      Err(@hal.EcError::MailboxTimeout) => continue
      Err(e) => raise e
    }
  }
}

///|
/// Execute an SDO Upload with RMSM retry support.
/// Retries on MailboxTimeout up to RMSM's max_retries limit.
pub fn[N : @hal.Nic] sdo_upload_retry(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  timeout : @hal.Duration,
) -> Bytes raise @hal.EcError {
  // First attempt
  let result = Result::Ok(
    sdo_upload(nic, station, mb_config, rmsm, index, subindex, timeout),
  ) catch {
    err => Result::Err(err)
  }
  match result {
    Ok(data) => return data
    Err(@hal.EcError::MailboxTimeout) => ()
    Err(e) => raise e
  }
  // Retry loop
  for ;; {
    if !rmsm.record_timeout() {
      raise @hal.EcError::MailboxTimeout
    }
    rmsm.reset_for_retry()
    let result = Result::Ok(
      sdo_upload_once(
        nic, station, mb_config, rmsm, index, subindex, timeout, true,
      ),
    ) catch {
      err => Result::Err(err)
    }
    match result {
      Ok(data) => return data
      Err(@hal.EcError::MailboxTimeout) => continue
      Err(e) => raise e
    }
  }
}

///|
/// Execute an SDO Upload with RMSM retry support using auto-increment transport.
pub fn[N : @hal.Nic] sdo_upload_retry_ap(
  nic : N,
  position : UInt,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  timeout : @hal.Duration,
) -> Bytes raise @hal.EcError {
  let result = Result::Ok(
    sdo_upload_ap(
      nic, position, station, mb_config, rmsm, index, subindex, timeout,
    ),
  ) catch {
    err => Result::Err(err)
  }
  match result {
    Ok(data) => return data
    Err(@hal.EcError::MailboxTimeout) => ()
    Err(e) => raise e
  }
  for ;; {
    if !rmsm.record_timeout() {
      raise @hal.EcError::MailboxTimeout
    }
    rmsm.reset_for_retry()
    let retry = Result::Ok(
      sdo_upload_ap_once(
        nic, position, station, mb_config, rmsm, index, subindex, timeout, true,
      ),
    ) catch {
      err => Result::Err(err)
    }
    match retry {
      Ok(data) => return data
      Err(@hal.EcError::MailboxTimeout) => continue
      Err(e) => raise e
    }
  }
}

///|
/// Execute an SDO Download with Complete Access enabled.
pub fn[N : @hal.Nic] sdo_download_complete_access(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  data : Bytes,
  timeout : @hal.Duration,
) -> Unit raise @hal.EcError {
  if data.length() > 4 {
    return sdo_download_segmented(
      nic, station, mb_config, rmsm, index, subindex, data, timeout, true, false,
    )
  }

  let counter = rmsm.begin_transaction()
  let txn = @mailbox.build_sdo_download_ca(
    counter, station, index, subindex, data,
  )
  let frame = txn.encode()
  mailbox_send(nic, station, mb_config, frame, timeout)
  let resp = recv_sdo_validated_fp(nic, station, mb_config, rmsm, timeout)
  if resp.command != @mailbox.SdoCommand::DownloadResponse {
    raise @hal.EcError::MailboxProtocol(
      "SDO CA download: expected DownloadResponse, got " +
      @debug.to_string(resp.command),
    )
  }
}

///|
/// Execute an SDO Upload with Complete Access enabled.
pub fn[N : @hal.Nic] sdo_upload_complete_access(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  timeout : @hal.Duration,
) -> Bytes raise @hal.EcError {
  let counter = rmsm.begin_transaction()
  let txn = @mailbox.build_sdo_upload_ca(counter, station, index, subindex)
  let frame = txn.encode()
  mailbox_send(nic, station, mb_config, frame, timeout)
  let resp = recv_sdo_validated_fp(nic, station, mb_config, rmsm, timeout)
  if resp.command != @mailbox.SdoCommand::UploadResponse {
    raise @hal.EcError::MailboxProtocol(
      "SDO CA upload: expected UploadResponse, got " +
      @debug.to_string(resp.command),
    )
  }
  if !resp.segmented {
    return resp.data
  }

  sdo_upload_collect_segmented(
    nic, station, mb_config, rmsm, timeout, resp, "SDO CA upload", false,
  )
}

///|
/// Read CoE SDO Info OD list and return object indices.
pub fn[N : @hal.Nic] sdo_info_get_od_list(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  list_type : UInt,
  timeout : @hal.Duration,
) -> Array[UInt] raise @hal.EcError {
  let counter = rmsm.begin_transaction()
  let req = @mailbox.build_sdo_info_get_od_list_request(
    counter, station, list_type,
  )
  let payload = sdo_info_collect_payload(
    nic, station, mb_config, rmsm, req, b'\x02', 2, timeout, "Get OD List",
  )
  @mailbox.decode_sdo_info_od_list_payload(payload)
}

///|
/// Read SDO Info Get OD (object description) for one object index.
pub fn[N : @hal.Nic] sdo_info_get_od_description(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  timeout : @hal.Duration,
) -> @mailbox.SdoInfoObjectDescription raise @hal.EcError {
  let counter = rmsm.begin_transaction()
  let req = @mailbox.build_sdo_info_get_od_request(counter, station, index)
  let payload = sdo_info_collect_payload(
    nic, station, mb_config, rmsm, req, b'\x04', 0, timeout, "Get OD",
  )
  @mailbox.decode_sdo_info_od_description_payload(payload)
}

///|
/// Read SDO Info Get OE (entry description) for one object entry.
pub fn[N : @hal.Nic] sdo_info_get_oe_description(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  value_info : Byte,
  timeout : @hal.Duration,
) -> @mailbox.SdoInfoEntryDescription raise @hal.EcError {
  let counter = rmsm.begin_transaction()
  let req = @mailbox.build_sdo_info_get_oe_request(
    counter, station, index, subindex, value_info,
  )
  let payload = sdo_info_collect_payload(
    nic, station, mb_config, rmsm, req, b'\x06', 0, timeout, "Get OE",
  )
  @mailbox.decode_sdo_info_oe_description_payload(payload)
}

///|
fn[N : @hal.Nic] sdo_upload_collect_segmented(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  timeout : @hal.Duration,
  initial : @mailbox.SdoResponse,
  context : String,
  retry : Bool,
) -> Bytes raise @hal.EcError {
  let acc = Buffer(
    size_hint=match initial.complete_size {
      Some(sz) => sz.reinterpret_as_int()
      None => 64
    },
  )
  for b in initial.data {
    acc.write_byte(b)
  }

  let mut current = initial
  let mut toggle = false
  while !current.last_segment {
    let seg_counter = rmsm_begin_transaction(rmsm, retry)
    let seg_req = @mailbox.build_sdo_upload_segment_request(
      seg_counter, station, toggle,
    )
    mailbox_send(nic, station, mb_config, seg_req, timeout)
    let seg_resp = recv_sdo_validated_fp(nic, station, mb_config, rmsm, timeout)
    if seg_resp.command != @mailbox.SdoCommand::UploadResponse ||
      !seg_resp.segmented {
      raise @hal.EcError::MailboxProtocol(
        context + ": expected segmented UploadResponse",
      )
    }
    if seg_resp.toggle != toggle {
      raise @hal.EcError::MailboxProtocol(context + ": segment toggle mismatch")
    }
    for b in seg_resp.data {
      acc.write_byte(b)
    }
    current = seg_resp
    toggle = !toggle
  }

  let out = acc.to_bytes()
  match current.complete_size {
    Some(sz) =>
      if out.length() >= sz.reinterpret_as_int() {
        Bytes::makei(sz.reinterpret_as_int(), fn(i) { out[i] })
      } else {
        out
      }
    None => out
  }
}

///|
fn[N : @hal.Nic] sdo_upload_collect_segmented_ap(
  nic : N,
  position : UInt,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  timeout : @hal.Duration,
  initial : @mailbox.SdoResponse,
  context : String,
  retry : Bool,
) -> Bytes raise @hal.EcError {
  let acc = Buffer(
    size_hint=match initial.complete_size {
      Some(sz) => sz.reinterpret_as_int()
      None => 64
    },
  )
  for b in initial.data {
    acc.write_byte(b)
  }

  let mut current = initial
  let mut toggle = false
  while !current.last_segment {
    let seg_counter = rmsm_begin_transaction(rmsm, retry)
    let seg_req = @mailbox.build_sdo_upload_segment_request(
      seg_counter, station, toggle,
    )
    mailbox_send_ap(nic, position, mb_config, seg_req, timeout)
    let seg_resp = recv_sdo_validated_ap(
      nic, position, mb_config, rmsm, timeout,
    )
    if seg_resp.command != @mailbox.SdoCommand::UploadResponse ||
      !seg_resp.segmented {
      raise @hal.EcError::MailboxProtocol(
        context + ": expected segmented UploadResponse",
      )
    }
    if seg_resp.toggle != toggle {
      raise @hal.EcError::MailboxProtocol(context + ": segment toggle mismatch")
    }
    for b in seg_resp.data {
      acc.write_byte(b)
    }
    current = seg_resp
    toggle = !toggle
  }

  let out = acc.to_bytes()
  match current.complete_size {
    Some(sz) =>
      if out.length() >= sz.reinterpret_as_int() {
        Bytes::makei(sz.reinterpret_as_int(), fn(i) { out[i] })
      } else {
        out
      }
    None => out
  }
}

///|
fn[N : @hal.Nic] sdo_download_segmented(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  index : UInt,
  subindex : Byte,
  data : Bytes,
  timeout : @hal.Duration,
  complete_access : Bool,
  retry : Bool,
) -> Unit raise @hal.EcError {
  let total_len = data.length().reinterpret_as_uint()
  let size_payload = Bytes::from_array([
    (total_len & 0xFFU).to_byte(),
    ((total_len >> 8) & 0xFFU).to_byte(),
    ((total_len >> 16) & 0xFFU).to_byte(),
    ((total_len >> 24) & 0xFFU).to_byte(),
  ])
  let init_counter = rmsm_begin_transaction(rmsm, retry)
  let init_txn = if complete_access {
    @mailbox.build_sdo_download_ca(
      init_counter, station, index, subindex, size_payload,
    )
  } else {
    @mailbox.build_sdo_download(
      init_counter, station, index, subindex, size_payload,
    )
  }
  mailbox_send(nic, station, mb_config, init_txn.encode(), timeout)
  let init_resp = recv_sdo_validated_fp(nic, station, mb_config, rmsm, timeout)
  if init_resp.command != @mailbox.SdoCommand::DownloadResponse {
    raise @hal.EcError::MailboxProtocol(
      "SDO download segmented: expected DownloadResponse for initiate",
    )
  }

  let mut offset = 0
  let mut toggle = false
  while offset < data.length() {
    let remain = data.length() - offset
    let chunk_len = if remain > 7 { 7 } else { remain }
    let chunk = Bytes::makei(chunk_len, fn(i) { data[offset + i] })
    let last = offset + chunk_len >= data.length()
    let seg_counter = rmsm_begin_transaction(rmsm, retry)
    let seg_frame = @mailbox.build_sdo_download_segment_request(
      seg_counter, station, toggle, last, chunk,
    )
    mailbox_send(nic, station, mb_config, seg_frame, timeout)
    let seg_resp = recv_sdo_validated_fp(nic, station, mb_config, rmsm, timeout)
    if seg_resp.command != @mailbox.SdoCommand::DownloadResponse ||
      !seg_resp.segmented {
      raise @hal.EcError::MailboxProtocol(
        "SDO download segmented: expected segmented DownloadResponse",
      )
    }
    if seg_resp.toggle != toggle {
      raise @hal.EcError::MailboxProtocol(
        "SDO download segmented: segment toggle mismatch",
      )
    }
    offset += chunk_len
    toggle = !toggle
  }
}

///|
fn[N : @hal.Nic] sdo_info_collect_payload(
  nic : N,
  station : UInt,
  mb_config : @mailbox.MailboxConfig,
  rmsm : @mailbox.Rmsm,
  request_frame : Bytes,
  expected_opcode : Byte,
  first_fragment_skip : Int,
  timeout : @hal.Duration,
  context : String,
) -> Bytes raise @hal.EcError {
  mailbox_send(nic, station, mb_config, request_frame, timeout)
  let acc = Buffer(size_hint=64)
  let mut first = true
  let mut done = false
  while !done {
    // For continuation fragments, begin a new transaction so RMSM
    // transitions from Idle → WaitingForResponse for the next counter.
    if !first {
      let _ = rmsm.begin_transaction()
    }
    let info = recv_sdo_info_validated_fp(
      nic, station, mb_config, rmsm, timeout,
    )
    if info.opcode != expected_opcode {
      raise @hal.EcError::MailboxProtocol(
        "SDO Info " +
        context +
        ": unexpected opcode " +
        info.opcode.to_int().to_string(),
      )
    }
    let payload = if first && first_fragment_skip > 0 {
      first = false
      if info.payload.length() < first_fragment_skip {
        raise @hal.EcError::MailboxProtocol(
          "SDO Info " + context + ": payload too short",
        )
      }
      Bytes::makei(info.payload.length() - first_fragment_skip, fn(i) {
        info.payload[first_fragment_skip + i]
      })
    } else {
      first = false
      info.payload
    }
    for b in payload {
      acc.write_byte(b)
    }
    done = !info.incomplete
  }
  acc.to_bytes()
}