use core::convert::Infallible;
use core::num::NonZeroUsize;
use qubit_codec as codec;
use qubit_codec::CapacityError;
use qubit_codec::Codec;
use qubit_codec::CodecTranscodeEncoder;
use qubit_codec::TranscodeEncodeError;
use qubit_codec::TranscodeEncoder;
use qubit_codec::TranscodeStatus;
use qubit_codec::Transcoder;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct PairByteCodec;
impl Codec for PairByteCodec {
type Value = u8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 2;
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>> {
debug_assert!(input_index < input.len());
let value = unsafe { *input.as_ptr().add(input_index) };
Ok((value, NonZeroUsize::MIN))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
debug_assert!(output_index + 2 <= output.len());
unsafe {
*output.as_mut_ptr().add(output_index) = *value;
*output.as_mut_ptr().add(output_index + 1) = value.wrapping_add(1);
}
Ok(2)
}
}
#[test]
fn test_codec_transcode_encoder_exposes_codec_accessors() {
let mut encoder = CodecTranscodeEncoder::new(PairByteCodec);
assert_eq!(&PairByteCodec, encoder.codec());
*encoder.codec_mut() = PairByteCodec;
assert_eq!(PairByteCodec, encoder.into_codec());
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct VariableWidthCodec;
impl Codec for VariableWidthCodec {
type Value = u8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 3;
const MAX_DECODE_UNITS_PER_VALUE: usize = 3;
fn encode_len(&self, value: &u8) -> usize {
match *value {
0..=9 => 1,
10..=99 => 2,
_ => 3,
}
}
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
debug_assert!(input_index < input.len());
let value = unsafe { *input.as_ptr().add(input_index) };
Ok((value, NonZeroUsize::MIN))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
let written = self.encode_len(value);
debug_assert!(output_index + written <= output.len());
for offset in 0..written {
unsafe {
*output.as_mut_ptr().add(output_index + offset) = *value;
}
}
Ok(written)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
struct RejectOddCodec;
impl Codec for RejectOddCodec {
type Value = u8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = &'static str;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
fn can_encode_value(&self, value: &u8) -> bool {
value.is_multiple_of(2)
}
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), codec::DecodeFailure<Self::DecodeError>> {
debug_assert!(input_index < input.len());
let value = unsafe { *input.as_ptr().add(input_index) };
Ok((value, NonZeroUsize::MIN))
}
unsafe fn encode(
&mut self,
value: &u8,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
debug_assert!(self.can_encode_value(value));
debug_assert!(output_index < output.len());
unsafe {
*output.as_mut_ptr().add(output_index) = *value;
}
Ok(1)
}
}
#[test]
fn test_codec_transcode_encoder_encodes_until_output_needs_more_capacity() {
fn assert_transcode_encoder<T: TranscodeEncoder<Input = u8, Output = u8>>() {}
assert_transcode_encoder::<CodecTranscodeEncoder<PairByteCodec>>();
let mut encoder = CodecTranscodeEncoder::new(PairByteCodec);
let mut reset_output = [];
encoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 4];
let progress = encoder
.transcode(&[3, 5, 7], 0, &mut output, 0)
.expect("encoding should be infallible");
assert_eq!(
TranscodeStatus::NeedOutput {
required: crate::nonzero(2),
},
progress.status(),
);
assert_eq!(2, progress.read());
assert_eq!(4, progress.written());
assert_eq!([3, 4, 5, 6], output);
assert_eq!(Ok(6), encoder.max_transcode_output_len(3));
assert_eq!(Ok(0), encoder.max_finish_output_len());
assert_eq!(
Err(CapacityError::OutputLengthOverflow),
encoder.max_transcode_output_len(usize::MAX),
);
encoder.reset(&mut [], 0).expect("reset");
}
#[test]
fn test_codec_transcode_encoder_respects_absolute_indices() {
let mut encoder = CodecTranscodeEncoder::new(PairByteCodec);
let mut reset_output = [];
encoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 4];
let progress = encoder
.transcode(&[3, 5], 1, &mut output, 1)
.expect("encoding should be infallible");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(1, progress.read());
assert_eq!(2, progress.written());
assert_eq!([0, 5, 6, 0], output);
}
#[test]
fn test_codec_transcode_encoder_reports_partial_output_capacity() {
let mut encoder = CodecTranscodeEncoder::new(PairByteCodec);
let mut reset_output = [];
encoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 1];
let progress = encoder
.transcode(&[3], 0, &mut output, 0)
.expect("encoding should stop before unsafe call");
assert_eq!(
TranscodeStatus::NeedOutput {
required: crate::nonzero(2),
},
progress.status(),
);
assert_eq!(0, progress.read());
assert_eq!(0, progress.written());
assert_eq!([0], output);
}
#[test]
fn test_codec_transcode_encoder_uses_encode_len_for_output_capacity() {
let mut encoder = CodecTranscodeEncoder::new(VariableWidthCodec);
let mut reset_output = [];
encoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 2];
let progress = encoder
.transcode(&[7, 8], 0, &mut output, 0)
.expect("short values should fit exactly");
assert_eq!(TranscodeStatus::Complete, progress.status());
assert_eq!(2, progress.read());
assert_eq!(2, progress.written());
assert_eq!([7, 8], output);
}
#[test]
fn test_codec_transcode_encoder_reports_output_index_beyond_buffer() {
let mut encoder = CodecTranscodeEncoder::new(PairByteCodec);
let mut reset_output = [];
encoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [];
let error = encoder
.transcode(&[3], 0, &mut output, 1)
.expect_err("out-of-range output index should fail");
assert_eq!(
codec::TranscodeEncodeError::Failure(codec::TranscodeFailure::invalid_output_index(1, 0)),
error
);
}
#[test]
fn test_codec_transcode_encoder_finish_reports_output_index_beyond_buffer() {
let mut encoder = CodecTranscodeEncoder::new(PairByteCodec);
let mut reset_output = [];
encoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [];
let error = encoder
.finish(&mut output, 1)
.expect_err("out-of-range finish output index should be rejected");
assert_eq!(
codec::TranscodeEncodeError::Failure(codec::TranscodeFailure::invalid_output_index(1, 0)),
error
);
}
#[test]
fn test_codec_transcode_encoder_reports_invalid_input_index() {
let mut encoder = CodecTranscodeEncoder::new(PairByteCodec);
let mut reset_output = [];
encoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [];
let error = encoder
.transcode(&[3], 2, &mut output, 0)
.expect_err("invalid input index should fail");
assert_eq!(
codec::TranscodeEncodeError::Failure(codec::TranscodeFailure::invalid_input_index(2, 1)),
error
);
}
#[test]
fn test_codec_transcode_encoder_propagates_encode_error() {
let mut encoder = CodecTranscodeEncoder::new(RejectOddCodec);
let mut reset_output = [];
encoder.reset(&mut reset_output, 0).expect("initialize stream");
let mut output = [0_u8; 2];
let error = encoder
.transcode(&[2, 3], 0, &mut output, 0)
.expect_err("odd value should be rejected before unsafe encode");
assert_eq!(TranscodeEncodeError::unencodable_without_context(1), error,);
assert_eq!([2, 0], output);
}
#[test]
fn test_codec_transcode_encoder_reports_max_reset_output_len() {
let encoder = CodecTranscodeEncoder::<PairByteCodec>::new(PairByteCodec);
assert_eq!(Ok(0), Transcoder::max_reset_output_len(&encoder));
}
#[test]
fn test_codec_transcode_encoder_default_builds_encoder() {
let mut encoder = CodecTranscodeEncoder::<PairByteCodec>::default();
encoder
.reset(&mut [], 0)
.expect("reset before default encoder transcode");
let mut output = [0_u8; 2];
let progress = encoder
.transcode(&[7], 0, &mut output, 0)
.expect("default encoder should transcode one value");
assert_eq!(1, progress.read());
assert_eq!(2, progress.written());
assert_eq!([7, 8], output);
}