qubit-codec 0.17.1

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.
// =============================================================================

use core::convert::Infallible;
use core::num::NonZeroUsize;

use qubit_codec as codec;
use qubit_codec::TranscodeDecodeError;
use qubit_codec::engine::DecodeContext;
use qubit_codec::engine::DecodeInvalidAction;
use qubit_codec::engine::TranscodeDecodeHooks;

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct UnitCodec;

#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
#[error("decode failed")]
struct UnitDecodeError;

impl codec::Codec for UnitCodec {
    type Value = u8;
    type Unit = u8;
    type DecodeError = UnitDecodeError;
    type EncodeError = Infallible;

    const MIN_UNITS_PER_VALUE: usize = 1;

    const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;

    const MAX_DECODE_UNITS_PER_VALUE: usize = 1;

    unsafe fn decode(
        &mut self,
        input: &[u8],
        input_index: usize,
    ) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
        Ok((input[input_index], NonZeroUsize::MIN))
    }

    unsafe fn encode(
        &mut self,
        value: &u8,
        output: &mut [u8],
        output_index: usize,
    ) -> Result<usize, Self::EncodeError> {
        output[output_index] = *value;
        Ok(1)
    }
}

#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct DefaultOnlyHooks;

impl TranscodeDecodeHooks<UnitCodec> for DefaultOnlyHooks {
    fn handle_invalid_decode(
        &mut self,
        _codec: &mut UnitCodec,
        error: &UnitDecodeError,
        _consumed: Option<NonZeroUsize>,
        _context: DecodeContext,
    ) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<UnitCodec>> {
        Err(TranscodeDecodeError::domain_main(*error, 0))
    }
}

#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct WideUnitCodec;

impl codec::Codec for WideUnitCodec {
    type Value = u8;
    type Unit = u8;
    type DecodeError = UnitDecodeError;
    type EncodeError = Infallible;

    const MIN_UNITS_PER_VALUE: usize = 2;
    const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
    const MAX_DECODE_UNITS_PER_VALUE: usize = 2;

    unsafe fn decode(
        &mut self,
        input: &[u8],
        input_index: usize,
    ) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
        Ok((input[input_index], NonZeroUsize::new(2).expect("two is non-zero")))
    }

    unsafe fn encode(
        &mut self,
        value: &u8,
        output: &mut [u8],
        output_index: usize,
    ) -> Result<usize, Self::EncodeError> {
        output[output_index] = *value;
        Ok(1)
    }
}

#[derive(Clone, Debug, Default, Eq, PartialEq)]
struct WideDefaultOnlyHooks;

impl TranscodeDecodeHooks<WideUnitCodec> for WideDefaultOnlyHooks {
    fn handle_invalid_decode(
        &mut self,
        _codec: &mut WideUnitCodec,
        error: &UnitDecodeError,
        _consumed: Option<NonZeroUsize>,
        _context: DecodeContext,
    ) -> Result<DecodeInvalidAction<u8>, codec::TranscodeDecodeErrorOf<WideUnitCodec>> {
        Err(TranscodeDecodeError::domain_main(*error, 0))
    }
}

#[test]
fn test_transcode_decode_hooks_default_finish_is_noop() {
    let mut hooks = DefaultOnlyHooks;
    let mut codec = UnitCodec;
    let mut output = [0_u8; 1];

    let written = TranscodeDecodeHooks::<UnitCodec>::finish_hooks(&mut hooks, &mut codec, &mut output, 0)
        .expect("default finish should be a no-op");

    assert_eq!(0, written);
}

#[test]
fn test_transcode_decode_hooks_default_before_reset_is_noop() {
    let mut hooks = DefaultOnlyHooks;
    let mut codec = UnitCodec;

    TranscodeDecodeHooks::<UnitCodec>::reset_hooks(&mut hooks, &mut codec);
}

#[test]
fn test_transcode_decode_hooks_default_output_bound_covers_replacement_policy() {
    let hooks = WideDefaultOnlyHooks;
    let codec = WideUnitCodec;

    let bound = TranscodeDecodeHooks::<WideUnitCodec>::max_transcode_output_len(&hooks, &codec, 5)
        .expect("the default output bound should not overflow");

    assert_eq!(5, bound);
}