qubit-codec 0.9.0

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.
// =============================================================================
//! Value decoder adapter backed by a low-level codec.

use core::fmt;

use super::ValueDecoder;
use crate::{
    Codec,
    CodecDecodeError,
    CodecValueExt,
    TranscodeError,
    codec::assert_unit_bounds,
};

/// Decodes one encoded unit slice into one owned value by using a [`Codec`].
///
/// `CodecValueDecoder` is the default bridge from the low-level unchecked
/// [`Codec`] contract to the convenience-layer [`ValueDecoder`] contract. The
/// supplied input slice must contain exactly one encoded value. After a
/// successful decode, the adapter calls [`Codec::decode_flush`] to reset
/// decode-side stream state for the next call.
///
/// # Type Parameters
///
/// - `C`: Low-level codec used to decode one value.
pub struct CodecValueDecoder<C>
where
    C: Codec,
{
    /// Low-level codec used for one-value decoding.
    codec: C,
    /// Reusable storage for values emitted by `Codec::decode_flush`.
    flush_scratch: Vec<C::Value>,
}

impl<C> fmt::Debug for CodecValueDecoder<C>
where
    C: Codec + fmt::Debug,
{
    /// Formats the decoder without requiring flushed values to be printable.
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("CodecValueDecoder")
            .field("codec", &self.codec)
            .field("flush_scratch_len", &self.flush_scratch.len())
            .field("flush_scratch_capacity", &self.flush_scratch.capacity())
            .finish()
    }
}

impl<C> Default for CodecValueDecoder<C>
where
    C: Codec + Default,
{
    /// Creates a decoder from the default codec.
    #[inline(always)]
    fn default() -> Self {
        Self::new(C::default())
    }
}

impl<C> CodecValueDecoder<C>
where
    C: Codec,
{
    /// Creates a decoder backed by `codec`.
    ///
    /// # Parameters
    ///
    /// - `codec`: Low-level codec used to decode one value.
    ///
    /// # Returns
    ///
    /// Returns a value decoder adapter for the supplied codec.
    ///
    /// # Panics
    ///
    /// In debug builds, panics when the supplied codec violates the
    /// [`Codec::MIN_UNITS_PER_VALUE`] / [`Codec::MAX_UNITS_PER_VALUE`] ordering
    /// invariant. Release builds skip this check because the invariant is the
    /// responsibility of the [`Codec`] implementation.
    #[inline]
    #[must_use]
    pub fn new(codec: C) -> Self {
        assert_unit_bounds::<C>();
        Self {
            codec,
            flush_scratch: Vec::new(),
        }
    }
}

impl<C> ValueDecoder<[C::Unit]> for CodecValueDecoder<C>
where
    C: Codec,
    C::Value: Default,
{
    type Output = C::Value;
    type Error = TranscodeError<CodecDecodeError<C::DecodeError>>;

    /// Decodes exactly one encoded value from `input`.
    ///
    /// # Parameters
    ///
    /// - `input`: Encoded units for exactly one value.
    ///
    /// # Returns
    ///
    /// Returns the decoded value.
    ///
    /// # Errors
    ///
    /// Returns [`CodecDecodeError::Incomplete`] when fewer than
    /// [`Codec::MIN_UNITS_PER_VALUE`] units are available. Returns
    /// [`CodecDecodeError::Decode`] when the wrapped codec rejects the input.
    /// Returns [`CodecDecodeError::TrailingInput`] when a value is decoded but
    /// extra input remains.
    ///
    /// # Panics
    ///
    /// Panics when the wrapped codec reports a consumed unit count larger than
    /// the input slice length, or when flush output exceeds
    /// [`Codec::MAX_DECODE_FLUSH_VALUES`].
    fn decode(
        &mut self,
        input: &[C::Unit],
    ) -> Result<Self::Output, Self::Error> {
        let flush_cap = C::MAX_DECODE_FLUSH_VALUES;
        let (value, _) = if flush_cap == 0 {
            self.codec
                .decode_exact_value_with_flush(input, &mut [], 0)?
        } else {
            if self.flush_scratch.len() < flush_cap {
                self.flush_scratch.resize_with(flush_cap, C::Value::default);
            }
            self.codec.decode_exact_value_with_flush(
                input,
                &mut self.flush_scratch,
                0,
            )?
        };

        Ok(value)
    }
}