qubit-codec 0.17.4

Core codec traits and buffer conversion primitives for Rust
Documentation
// =============================================================================
//    Copyright (c) 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
//! Shared input/output state for one transcode call.

use core::num::NonZeroUsize;

use super::super::transcode_progress::TranscodeProgress;

/// Shared input/output state for one transcode call.
///
/// `TranscodeState` borrows the input/output slices visible to a single
/// buffered transcode call and tracks absolute input/output cursors. Concrete
/// encode/decode/convert states wrap it and keep domain-specific operations in
/// their own types.
///
/// Callers must keep input cursors within the input slice and prevent cursor
/// arithmetic from overflowing. An output cursor beyond the slice is tolerated
/// by capacity queries, which report zero remaining capacity.
///
/// # Type Parameters
///
/// - `'a`: Lifetime of the shared input and exclusive output borrows.
/// - `Input`: Source unit type read by the current transcode call.
/// - `Output`: Target unit type written by the current transcode call.
pub(in crate::transcode) struct TranscodeState<'a, Input, Output> {
    /// Complete input slice visible to the current call.
    input: &'a [Input],
    /// Complete output slice visible to the current call.
    output: &'a mut [Output],
    /// Absolute input index where this call starts.
    input_start: usize,
    /// Absolute output index where this call starts.
    output_start: usize,
    /// Absolute input index for the next operation.
    input_cursor: usize,
    /// Absolute output index for the next write.
    output_cursor: usize,
}

impl<'a, Input, Output> TranscodeState<'a, Input, Output> {
    /// Creates shared transcode state.
    ///
    /// # Parameters
    ///
    /// - `input`: Complete input slice visible to the current call.
    /// - `input_index`: Absolute input index where this call starts.
    /// - `output`: Complete output slice visible to the current call.
    /// - `output_index`: Absolute output index where this call starts.
    ///
    /// # Returns
    ///
    /// Returns initialized state with both cursors at their call starts.
    ///
    /// # Panics
    ///
    /// Panics in debug builds if `input_index` exceeds `input.len()`. Callers
    /// must uphold this input bound in all builds.
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn new(
        input: &'a [Input],
        input_index: usize,
        output: &'a mut [Output],
        output_index: usize,
    ) -> Self {
        debug_assert!(input_index <= input.len(), "input index must be within the input slice",);

        Self {
            input,
            output,
            input_start: input_index,
            output_start: output_index,
            input_cursor: input_index,
            output_cursor: output_index,
        }
    }

    /// Returns the complete input slice.
    ///
    /// # Returns
    ///
    /// Returns the full input slice visible to this call.
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn input(&self) -> &[Input] {
        self.input
    }

    /// Returns the complete output slice mutably.
    ///
    /// # Returns
    ///
    /// Returns the full mutable output slice visible to this call.
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn output_mut(&mut self) -> &mut [Output] {
        self.output
    }

    /// Returns input and output slices together.
    ///
    /// # Returns
    ///
    /// Returns the immutable input slice and mutable output slice. This helper
    /// lets callers borrow the two disjoint fields at the same time.
    #[inline]
    #[must_use = "use the returned slices to access the transcode buffers"]
    pub(in crate::transcode) fn input_output_mut(&mut self) -> (&[Input], &mut [Output]) {
        (self.input, self.output)
    }

    /// Returns the absolute input start index.
    ///
    /// # Returns
    ///
    /// Returns the input index recorded when this call began.
    #[inline]
    #[must_use]
    pub(in crate::transcode) const fn input_start(&self) -> usize {
        self.input_start
    }

    /// Returns the absolute output start index.
    ///
    /// # Returns
    ///
    /// Returns the output index recorded when this call began.
    #[inline]
    #[must_use]
    pub(in crate::transcode) const fn output_start(&self) -> usize {
        self.output_start
    }

    /// Returns the current absolute input cursor.
    ///
    /// # Returns
    ///
    /// Returns the absolute input index for the next read or consume operation.
    #[inline]
    #[must_use]
    pub(in crate::transcode) const fn input_cursor(&self) -> usize {
        self.input_cursor
    }

    /// Returns the current absolute output cursor.
    ///
    /// # Returns
    ///
    /// Returns the absolute output index for the next write operation.
    #[inline]
    #[must_use]
    pub(in crate::transcode) const fn output_cursor(&self) -> usize {
        self.output_cursor
    }

    /// Returns whether input remains.
    ///
    /// # Returns
    ///
    /// Returns `true` when the input cursor has not reached the input end.
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn has_input(&self) -> bool {
        self.input_cursor < self.input.len()
    }

    /// Returns whether output has no room for another value.
    ///
    /// # Returns
    ///
    /// Returns `true` when the output cursor is at the output end.
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn needs_output(&self) -> bool {
        self.output_cursor == self.output.len()
    }

    /// Returns input units visible from the current input cursor.
    ///
    /// # Returns
    ///
    /// Returns remaining input units visible from `input_cursor`.
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn available_input(&self) -> usize {
        self.input.len() - self.input_cursor
    }

    /// Returns output units writable from the current output cursor.
    ///
    /// # Returns
    ///
    /// Returns remaining output capacity from `output_cursor`.
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn available_output(&self) -> usize {
        self.output.len().saturating_sub(self.output_cursor)
    }

    /// Returns input units consumed since this call started.
    ///
    /// # Returns
    ///
    /// Returns consumed input units relative to `input_start`.
    #[inline]
    #[must_use]
    pub(in crate::transcode) const fn read(&self) -> usize {
        self.input_cursor - self.input_start
    }

    /// Returns output units written since this call started.
    ///
    /// # Returns
    ///
    /// Returns written output units relative to `output_start`.
    #[inline]
    #[must_use]
    pub(in crate::transcode) const fn written(&self) -> usize {
        self.output_cursor - self.output_start
    }

    /// Returns completed progress for the current cursors.
    ///
    /// # Returns
    ///
    /// Returns completed progress with consumed input and written output
    /// counters.
    #[inline]
    pub(in crate::transcode) fn complete_progress(&self) -> TranscodeProgress {
        TranscodeProgress::complete(self.read(), self.written())
    }

    /// Returns progress for missing input.
    ///
    /// # Parameters
    ///
    /// - `required`: Current minimum total input units required before retrying
    ///   from the current input position. A later retry may raise this lower
    ///   bound.
    ///
    /// # Returns
    ///
    /// Returns [`TranscodeProgress`] with need-input status.
    #[inline]
    pub(in crate::transcode) fn need_input_progress(&self, required: NonZeroUsize) -> TranscodeProgress {
        TranscodeProgress::need_input(required, self.read(), self.written())
    }

    /// Returns progress for missing output.
    ///
    /// # Parameters
    ///
    /// - `required`: Total output units required from the current output
    ///   position.
    ///
    /// # Returns
    ///
    /// Returns [`TranscodeProgress`] with need-output status.
    #[inline]
    pub(in crate::transcode) fn need_output_progress(&self, required: NonZeroUsize) -> TranscodeProgress {
        TranscodeProgress::need_output(required, self.read(), self.written())
    }

    /// Advances the input cursor.
    ///
    /// # Parameters
    ///
    /// - `read`: Number of input units consumed by the last operation; must not
    ///   exceed the available input.
    ///
    /// # Panics
    ///
    /// Panics when overflow checks are enabled if cursor addition overflows.
    #[inline]
    pub(in crate::transcode) fn advance_input(&mut self, read: usize) {
        self.input_cursor += read;
    }

    /// Advances the output cursor.
    ///
    /// # Parameters
    ///
    /// - `written`: Number of output units written by the last operation.
    ///   Callers must ensure cursor addition does not overflow.
    ///
    /// # Panics
    ///
    /// Panics when overflow checks are enabled if cursor addition overflows.
    #[inline]
    pub(in crate::transcode) fn advance_output(&mut self, written: usize) {
        self.output_cursor += written;
    }

    /// Advances both cursors.
    ///
    /// `read` must not exceed the available input. Neither cursor addition may
    /// overflow; this method delegates to the individual cursor operations.
    ///
    /// # Parameters
    ///
    /// - `read`: Number of input units consumed by the last operation.
    /// - `written`: Number of output units written by the last operation.
    ///   Callers must ensure cursor addition does not overflow.
    ///
    /// # Panics
    ///
    /// Panics when overflow checks are enabled if cursor addition overflows.
    #[inline]
    pub(in crate::transcode) fn advance(&mut self, read: usize, written: usize) {
        self.advance_input(read);
        self.advance_output(written);
    }
}