///|
/// Scan a byte view for reconstructable classic xref sections.
///
/// The caller supplies `read_section_at` because parsing a complete section may
/// depend on higher-level reader state outside this package.
pub fn pdf_reader_reconstruction_classic_xref_sections(
  data : BytesView,
  read_section_at : (@core.ByteCursor, Int) -> (PdfXRefSection, Int) raise @core.PdfError,
) -> Array[(Int, PdfXRefSection)] {
  let sections : Array[(Int, PdfXRefSection)] = Array(capacity=1)
  let cursor = @core.byte_cursor_of_view(data)
  let mut position = 0
  while position < data.length() {
    if @core.pdf_view_matches_ascii_at(data, position, [120, 114, 101, 102]) {
      let parsed : Result[(PdfXRefSection, Int), Error] = try
        read_section_at(cursor, position)
      catch {
        err => Err(err)
      } noraise {
        value => Ok(value)
      }
      match parsed {
        Ok((section, next_position)) => {
          sections.push((position, section))
          position = next_position
        }
        Err(_) => position += 4
      }
    } else {
      position += 1
    }
  }
  sections
}

///|
/// Scan a byte view for reconstructable xref-stream sections.
///
/// Malformed indirect object candidates are skipped using the reconstruction
/// scanner so object-like bytes inside a bad object do not become candidates.
pub fn pdf_reader_reconstruction_xref_stream_sections(
  data : BytesView,
  read_section_at : (@core.ByteCursor, Int) -> (PdfXRefSection, Int) raise @core.PdfError,
) -> Array[PdfXRefSection] raise @core.PdfError {
  let sections : Array[PdfXRefSection] = Array(capacity=1)
  let cursor = @core.byte_cursor_of_view(data)
  let mut position = 0
  while position < data.length() {
    if pdf_reader_reconstruction_indirect_header_at(data, position) {
      let parsed : Result[(PdfXRefSection, Int), Error] = try
        read_section_at(cursor, position)
      catch {
        err => Err(err)
      } noraise {
        value => Ok(value)
      }
      match parsed {
        Ok((section, next_position)) => {
          sections.push(section)
          position = next_position
        }
        Err(_) =>
          match
            pdf_reader_reconstruction_skip_malformed_object(data, position) {
            Some(next) => position = next
            // Defensive: the candidate matched an indirect-object header
            // above, so the skip scanner accepts the same position.
            None => raise XRefEntryExpected
          }
      }
    } else {
      position += 1
    }
  }
  sections
}