///|
/// Returns a read-only view of the next `length` bytes without advancing.
///
/// Raises `PdfError::InvalidReadLength` for negative lengths and
/// `PdfError::EndOfInput` if the requested range extends past the input.
pub fn ByteCursor::peek_view(
  self : ByteCursor,
  length : Int,
) -> BytesView raise PdfError {
  if length < 0 {
    raise InvalidReadLength(length)
  }
  let end = self.position + length
  if end > self.data.length() {
    raise EndOfInput
  }
  self.data[self.position:end]
}

///|
/// Copies the next `length` bytes without advancing.
///
/// Raises the same errors as `peek_view`.
pub fn ByteCursor::peek_bytes(
  self : ByteCursor,
  length : Int,
) -> PdfBytes raise PdfError {
  self.peek_view(length).to_owned()
}

///|
/// Returns a read-only view at an absolute position without advancing.
pub fn ByteCursor::view_at_absolute(
  self : ByteCursor,
  position : Int,
  length : Int,
) -> BytesView raise PdfError {
  if position < 0 {
    raise InvalidCursorPosition(position)
  }
  if length < 0 {
    raise InvalidReadLength(length)
  }
  let end = position + length
  if end < position {
    raise InvalidReadLength(length)
  }
  if end > self.data.length() {
    raise EndOfInput
  }
  self.data[position:end]
}

///|
/// Returns a read-only view at a logical position without advancing.
pub fn ByteCursor::view_at(
  self : ByteCursor,
  position : Int,
  length : Int,
) -> BytesView raise PdfError {
  if position < 0 {
    raise InvalidCursorPosition(position)
  }
  self.view_at_absolute(position + self.offset, length)
}

///|
/// Advances the cursor by one byte, ignoring the value read.
pub fn ByteCursor::nudge(self : ByteCursor) -> Unit {
  ignore(self.input_byte())
}

///|
/// Moves the cursor one byte backward.
///
/// Raises `PdfError::InvalidCursorPosition` if the cursor is already at the
/// logical start position.
pub fn ByteCursor::rewind(self : ByteCursor) -> Unit raise PdfError {
  if self.position <= self.offset {
    raise InvalidCursorPosition(self.position() - 1)
  }
  self.position -= 1
}

///|
/// Reads the previous byte and moves the cursor backward.
///
/// Returns `None` when the cursor is at the logical start or when the resulting
/// position is outside the underlying view.
pub fn ByteCursor::read_byte_back(self : ByteCursor) -> Byte? {
  if self.position <= self.offset {
    None
  } else {
    self.position -= 1
    if self.position >= self.data.length() {
      None
    } else {
      Some(self.data[self.position])
    }
  }
}

///|
/// Returns a view of all bytes from the current position to the end of input.
pub fn ByteCursor::remaining_view(self : ByteCursor) -> BytesView {
  if self.position >= self.data.length() {
    self.data[self.data.length():]
  } else {
    self.data[self.position:]
  }
}

///|
/// Reads `length` bytes as a view and advances past them.
///
/// Raises `PdfError::InvalidReadLength` for negative lengths and
/// `PdfError::EndOfInput` if the requested range extends past the input.
pub fn ByteCursor::read_view(
  self : ByteCursor,
  length : Int,
) -> BytesView raise PdfError {
  if length < 0 {
    raise InvalidReadLength(length)
  }
  let end = self.position + length
  if end > self.data.length() {
    raise EndOfInput
  }
  let view = self.data[self.position:end]
  self.position = end
  view
}

///|
/// Seeks to `position`, then reads `length` bytes as a view.
///
/// The returned view borrows the cursor input; use `read_bytes_at` when owned
/// bytes are required.
pub fn ByteCursor::read_view_at(
  self : ByteCursor,
  position : Int,
  length : Int,
) -> BytesView raise PdfError {
  self.seek(position)
  self.read_view(length)
}

///|
/// Reads until `stop` matches the next byte and returns the bytes before it.
///
/// The delimiter byte is not consumed. If EOF is reached and `eof_is_delimiter`
/// is true, the remaining bytes are returned; otherwise `PdfError::EndOfInput`
/// is raised.
pub fn ByteCursor::read_until_view(
  self : ByteCursor,
  stop : (Int) -> Bool,
  eof_is_delimiter? : Bool = true,
) -> BytesView raise PdfError {
  let start = self.position
  let mut done = false
  while !done {
    let value = self.peek_byte()
    if value == pdf_no_more {
      if eof_is_delimiter {
        done = true
      } else {
        raise EndOfInput
      }
    } else if stop(value) {
      done = true
    } else {
      self.nudge()
    }
  }
  self.data[start:self.position]
}

///|
/// Advances until `stop` matches the next byte.
///
/// The delimiter byte is left unread. EOF handling follows `read_until_view`.
pub fn ByteCursor::ignore_until(
  self : ByteCursor,
  stop : (Int) -> Bool,
  eof_is_delimiter? : Bool = true,
) -> Unit raise PdfError {
  let mut done = false
  while !done {
    let value = self.peek_byte()
    if value == pdf_no_more {
      if eof_is_delimiter {
        done = true
      } else {
        raise EndOfInput
      }
    } else if stop(value) {
      done = true
    } else {
      self.nudge()
    }
  }
}

///|
/// Reads `length` bytes into owned PDF bytes and advances past them.
///
/// Raises the same errors as `read_view`.
pub fn ByteCursor::read_bytes(
  self : ByteCursor,
  length : Int,
) -> PdfBytes raise PdfError {
  self.read_view(length).to_owned()
}

///|
/// Reads all remaining bytes into owned PDF bytes and advances to EOF.
pub fn ByteCursor::read_remaining_bytes(self : ByteCursor) -> PdfBytes {
  let view = self.remaining_view()
  self.position = self.data.length()
  view.to_owned()
}

///|
/// Seeks to `position`, reads `length` bytes into owned PDF bytes, and advances
/// past them.
pub fn ByteCursor::read_bytes_at(
  self : ByteCursor,
  position : Int,
  length : Int,
) -> PdfBytes raise PdfError {
  self.read_view_at(position, length).to_owned()
}