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.
// =============================================================================
//! Mutable state for one buffered decode call.

use core::num::NonZeroUsize;

use super::super::engine::DecodeContext;
use super::super::engine::DecodeOutcome;
use super::super::transcode_progress::TranscodeProgress;
use super::transcode_state::TranscodeState;

/// Mutable state for one buffered decode call.
///
/// Borrows input and exclusively borrows output for the call lifetime `'a`.
/// Cursor updates do not allocate or perform I/O.
///
/// # Type Parameters
///
/// - `'a`: Lifetime of the input and exclusive output borrows.
/// - `Unit`: Encoded input element.
/// - `Value`: Decoded output element.
pub(in crate::transcode) struct DecodeState<'a, Unit, Value> {
    /// Shared input/output state for this decode call.
    state: TranscodeState<'a, Unit, Value>,
}

impl<'a, Unit, Value> DecodeState<'a, Unit, Value> {
    /// Creates mutable decode state.
    ///
    /// # Parameters
    ///
    /// - `input`: Complete input unit slice visible to the decoder.
    /// - `input_index`: Absolute source index where decoding starts.
    /// - `output`: Complete output value slice visible to the decoder.
    /// - `output_index`: Absolute output index where writing starts.
    ///
    /// # Returns
    ///
    /// Returns initialized decode state with cursors at the requested start
    /// positions.
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn new(
        input: &'a [Unit],
        input_index: usize,
        output: &'a mut [Value],
        output_index: usize,
    ) -> Self {
        Self {
            state: TranscodeState::new(input, input_index, output, output_index),
        }
    }

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

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

    /// Returns whether there is still input to decode.
    ///
    /// # Returns
    ///
    /// Returns `true` when there are input units remaining.
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn has_input(&self) -> bool {
        self.state.has_input()
    }

    /// Returns whether the output slice has no slot for the next value.
    ///
    /// # Returns
    ///
    /// Returns `true` when no output slot remains for the next value.
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn needs_output(&self) -> bool {
        self.state.needs_output()
    }

    /// Returns a public decode context snapshot.
    ///
    /// # Returns
    ///
    /// Returns the current [`DecodeContext`].
    #[inline]
    #[must_use]
    pub(in crate::transcode) fn context(&self) -> DecodeContext {
        DecodeContext::new(
            self.state.input_start(),
            self.state.input_cursor(),
            self.state.output_start(),
            self.state.output_cursor(),
            self.available(),
        )
    }

    /// Returns completed progress for the current cursors.
    ///
    /// # Returns
    ///
    /// Returns a completed [`TranscodeProgress`].
    #[inline]
    pub(in crate::transcode) fn complete_progress(&self) -> TranscodeProgress {
        self.state.complete_progress()
    }

    /// Returns progress for a missing output slot.
    ///
    /// # Returns
    ///
    /// Returns progress with [`crate::TranscodeStatus::NeedOutput`].
    #[inline]
    pub(in crate::transcode) fn need_output_progress(&self) -> TranscodeProgress {
        self.state.need_output_progress(NonZeroUsize::MIN)
    }

    /// Returns progress for a policy-selected need-input stop.
    ///
    /// # Parameters
    ///
    /// - `required`: Current minimum total source units required before
    ///   retrying from the current input position. A later retry may raise this
    ///   lower bound.
    ///
    /// # Returns
    ///
    /// Returns progress at the current decode cursor.
    #[inline]
    pub(in crate::transcode) fn need_input_progress_with(&self, required: NonZeroUsize) -> TranscodeProgress {
        self.state.need_input_progress(required)
    }

    /// Advances the input cursor without emitted output.
    ///
    /// # Parameters
    ///
    /// - `read`: Non-zero input units consumed by the current operation.
    ///
    /// # Panics
    ///
    /// Panics when `read` exceeds the input units available at the current
    /// cursor.
    #[inline]
    pub(in crate::transcode) fn skip(&mut self, read: NonZeroUsize) {
        let read = read.get();
        assert!(
            read <= self.available(),
            "DecodeOutcome::Skipped read beyond available input",
        );
        self.state.advance_input(read);
    }

    /// Accepts emitted output and advances both cursors.
    ///
    /// # Parameters
    ///
    /// - `read`: Input units consumed by this decode outcome.
    /// - `emitted`: Logical output values emitted by this decode outcome.
    ///
    /// # Panics
    ///
    /// Panics when `read` exceeds available input or `emitted` exceeds
    /// available output.
    #[inline]
    pub(in crate::transcode) fn accept_emitted(&mut self, read: NonZeroUsize, emitted: NonZeroUsize) {
        let read = read.get();
        let emitted = emitted.get();
        assert!(
            read <= self.available(),
            "DecodeOutcome::Emitted read beyond available input",
        );
        assert!(
            emitted <= self.state.available_output(),
            "DecodeOutcome::Emitted exceeded available output",
        );
        self.state.advance(read, emitted);
    }

    /// Applies one decode outcome to this decode state.
    ///
    /// # Parameters
    ///
    /// - `outcome`: Decode outcome produced by the decode engine.
    ///
    /// # Returns
    ///
    /// Returns `Some(progress)` when more input is required and this call must
    /// stop, or `None` after applying emitted/skipped progress so decoding can
    /// continue.
    ///
    /// # Panics
    ///
    /// Panics when an emitted or skipped outcome exceeds the input or output
    /// units available at the current cursors.
    #[inline]
    pub(in crate::transcode) fn apply_decode_outcome(&mut self, outcome: DecodeOutcome) -> Option<TranscodeProgress> {
        match outcome {
            DecodeOutcome::Emitted { read, emitted } => {
                self.accept_emitted(read, emitted);
                None
            }
            DecodeOutcome::Skipped { read } => {
                self.skip(read);
                None
            }
            DecodeOutcome::NeedInput { required } => Some(self.need_input_progress_with(required)),
        }
    }

    /// Returns input units visible from the current input cursor.
    ///
    /// # Returns
    ///
    /// The remaining readable unit count, without advancing the cursor.
    #[inline]
    #[must_use]
    fn available(&self) -> usize {
        self.state.available_input()
    }
}