use core::cell::Cell;
use std::collections::VecDeque;
use std::io::Cursor;
use std::io::Error;
use std::io::ErrorKind;
use std::io::Seek;
use std::io::SeekFrom;
use std::io::Write;
use std::num::NonZeroUsize;
use qubit_codec as codec;
use qubit_codec::CapacityError;
use qubit_codec::Codec;
use qubit_codec::DecodeFailure;
use qubit_codec::TranscodeEncodeError;
use qubit_codec::TranscodeEncodeOutput;
use qubit_codec::TranscodeEncoder;
use qubit_codec::TranscodeProgress;
use qubit_codec::TranscodeStatus;
use qubit_codec::Transcoder;
use qubit_io::BufferedOutputRecovery;
use qubit_io::Output;
use thiserror::Error as ThisError;
#[test]
fn test_try_with_capacity_allocates_encode_buffer() {
let output = TranscodeEncodeOutput::try_with_capacity(Vec::<u8>::new(), 1).expect("encode buffer should allocate");
assert!(output.spare_capacity() >= 1);
assert!(TranscodeEncodeOutput::try_with_capacity(Vec::<u8>::new(), usize::MAX).is_err());
}
#[test]
fn test_transcode_step_completes_one_encoder_call() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 8);
let mut encoder = CountedEncoder::default();
let mut map_error = |_| Error::other("encode error");
let input = [0x1234_5678_u32, 0x9abc_def0];
let progress = output
.transcode_step(&mut encoder, &mut map_error, &input, 0, input.len())
.expect("step succeeds");
assert_eq!(progress, TranscodeProgress::complete(2, 2));
assert_eq!(encoder.calls, 1);
output.flush().expect("flush succeeds");
assert_eq!(output.inner().units, &[0x5678, 0xdef0]);
}
#[test]
fn test_transcode_step_returns_partial_progress() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 2);
let mut encoder = PairEncoder;
let mut map_error = |_| Error::other("encode error");
let input = [1_u32, 2, 3];
let progress = output
.transcode_step(&mut encoder, &mut map_error, &input, 0, input.len())
.expect("step succeeds");
assert_eq!(progress.read(), 1);
assert_eq!(progress.written(), 2);
assert!(matches!(progress.status(), TranscodeStatus::NeedOutput { .. }));
let mut offset = progress.read();
while offset < input.len() {
let next = output
.transcode_step(&mut encoder, &mut map_error, &input, offset, input.len() - offset)
.expect("next step succeeds");
offset += next.read();
if let TranscodeStatus::NeedOutput { required, .. } = next.status() {
output
.ensure_spare_capacity(required.get())
.expect("space can be provided");
}
}
output.flush().expect("flush succeeds");
assert_eq!(output.inner().units, &[0, 1, 0, 2, 0, 3]);
}
#[test]
fn test_transcode_step_keeps_window_bounded() {
for count in [64, 65_536] {
let values: Vec<u16> = (0..count).map(|value| value as u16).collect();
let mut output = TranscodeEncodeOutput::with_capacity(CountingSink::default(), 8);
let mut encoder = WindowEncoder::default();
let mut map_error = |_| Error::other("encode error");
let mut offset = 0;
while offset < values.len() {
let progress = output
.transcode_step(&mut encoder, &mut map_error, &values, offset, values.len() - offset)
.expect("step succeeds");
offset += progress.read();
if let TranscodeStatus::NeedOutput { required, .. } = progress.status() {
output
.ensure_spare_capacity(required.get())
.expect("space can be provided");
}
}
output.flush().expect("flush succeeds");
let expected_sum: u64 = values.iter().map(|value| u64::from(*value)).sum();
assert_eq!(output.inner().count, count);
assert_eq!(output.inner().sum, expected_sum);
assert!(encoder.max_window <= 8);
}
}
#[derive(Debug, Default)]
struct CountingSink {
count: usize,
sum: u64,
}
impl Output for CountingSink {
type Item = u16;
unsafe fn write_unchecked(&mut self, input: &[u16], index: usize, count: usize) -> std::io::Result<usize> {
for value in &input[index..index + count] {
self.sum += u64::from(*value);
}
self.count += count;
Ok(count)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[derive(Debug, Default)]
struct WindowEncoder {
max_window: usize,
}
impl Transcoder for WindowEncoder {
type Input = u16;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u16>;
fn max_transcode_output_len(&self, _input_len: usize) -> Result<usize, CapacityError> {
Ok(1)
}
fn reset(&mut self, output: &mut [u16], index: usize) -> Result<usize, Self::Error> {
codec::TranscodeFailure::ensure_output_index(output.len(), index)?;
Ok(0)
}
fn transcode(
&mut self,
input: &[u16],
index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
self.max_window = self.max_window.max(output.len() - output_index);
let mut read = 0;
while index + read < input.len() && output_index + read < output.len() {
output[output_index + read] = input[index + read];
read += 1;
}
if index + read < input.len() {
Ok(TranscodeProgress::need_output(crate::nonzero(1), read, read))
} else {
Ok(TranscodeProgress::complete(read, read))
}
}
fn finish(&mut self, output: &mut [u16], index: usize) -> Result<usize, Self::Error> {
codec::TranscodeFailure::ensure_output_index(output.len(), index)?;
Ok(0)
}
}
impl TranscodeEncoder for WindowEncoder {
type EncodeError = PairEncodeError;
}
#[test]
fn test_transcode_step_honors_indices() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 4);
let mut encoder = PairEncoder;
let mut map_error = |_| Error::other("encode error");
let input = [u32::MAX, 7, 8, u32::MAX];
let progress = output
.transcode_step(&mut encoder, &mut map_error, &input, 1, 2)
.expect("selected range encodes");
assert_eq!(progress, TranscodeProgress::complete(2, 4));
output.flush().expect("flush succeeds");
assert_eq!(output.inner().units, &[0, 7, 0, 8]);
}
#[test]
fn test_transcode_step_validates_empty_range() {
let mut output = TranscodeEncodeOutput::with_capacity(
UnitOutput {
fail_write: true,
..UnitOutput::default()
},
4,
);
let mut encoder = CountedEncoder::default();
let mut map_error = |_| Error::other("encode error");
let input = [1_u32];
unsafe {
output.advance(1);
}
let progress = output
.transcode_step(&mut encoder, &mut map_error, &input, 1, 0)
.expect("empty range is valid");
assert_eq!(progress, TranscodeProgress::complete(0, 0));
assert_eq!(encoder.calls, 0);
assert!(output.inner().units.is_empty(), "pending units are not delivered");
let error = output
.transcode_step(&mut encoder, &mut map_error, &input, 2, 0)
.expect_err("out-of-range empty range fails");
assert_eq!(error.kind(), ErrorKind::InvalidInput);
}
#[test]
fn test_transcode_step_rejects_invalid_progress() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 4);
let mut encoder = OverreadingProgressEncoder;
let mut map_error = |_| Error::other("encode error");
let error = output
.transcode_step(&mut encoder, &mut map_error, &[1_u32], 0, 1)
.expect_err("over-read is rejected");
assert_eq!(error.kind(), ErrorKind::InvalidData);
let mut encoder = NeedInputEncoder;
let error = output
.transcode_step(&mut encoder, &mut map_error, &[1_u32], 0, 1)
.expect_err("encoder cannot request more input");
assert_eq!(error.kind(), ErrorKind::InvalidData);
}
#[test]
fn test_transcode_step_returns_need_output_without_progress() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 1);
let mut encoder = AtomicPairEncoder;
let mut map_error = |_| Error::other("encode error");
let input = [3_u32, 4];
let progress = output
.transcode_step(&mut encoder, &mut map_error, &input, 0, input.len())
.expect("insufficient output is progress");
assert_eq!(progress, TranscodeProgress::need_output(crate::nonzero(2), 0, 0));
output.ensure_spare_capacity(2).expect("larger window can be requested");
let progress = output
.transcode_step(&mut encoder, &mut map_error, &input, 0, input.len())
.expect("retry succeeds");
assert_eq!(progress, TranscodeProgress::complete(2, 2));
}
#[derive(Debug, Default)]
struct AtomicPairEncoder;
impl Transcoder for AtomicPairEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, _input_len: usize) -> Result<usize, CapacityError> {
Ok(1)
}
fn reset(&mut self, output: &mut [u16], index: usize) -> Result<usize, Self::Error> {
codec::TranscodeFailure::ensure_output_index(output.len(), index)?;
Ok(0)
}
fn transcode(
&mut self,
input: &[u32],
index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
let count = (input.len() - index).min(2);
if output.len() - output_index < count {
return Ok(TranscodeProgress::need_output(crate::nonzero(count), 0, 0));
}
for offset in 0..count {
output[output_index + offset] = input[index + offset] as u16;
}
Ok(TranscodeProgress::complete(count, count))
}
fn finish(&mut self, output: &mut [u16], index: usize) -> Result<usize, Self::Error> {
codec::TranscodeFailure::ensure_output_index(output.len(), index)?;
Ok(0)
}
}
impl TranscodeEncoder for AtomicPairEncoder {
type EncodeError = PairEncodeError;
}
#[test]
fn test_transcode_step_does_not_call_encoder_on_prepare_error() {
let mut output = TranscodeEncodeOutput::with_capacity(
UnitOutput {
fail_write: true,
..UnitOutput::default()
},
1,
);
let mut encoder = CountedEncoder::default();
let mut map_error = |_| Error::other("encode error");
unsafe {
output.advance(1);
}
let error = output
.transcode_step(&mut encoder, &mut map_error, &[1_u32], 0, 1)
.expect_err("pending delivery fails");
assert_eq!(error.kind(), ErrorKind::BrokenPipe);
assert_eq!(encoder.calls, 0);
}
#[derive(Debug, Eq, PartialEq, ThisError)]
#[error("bad boundary input")]
struct BoundaryDomainError;
#[derive(Debug, Default)]
struct BoundaryEncoder {
encoded: Vec<u8>,
resets: usize,
}
impl Transcoder for BoundaryEncoder {
type Input = u8;
type Output = u8;
type Error = TranscodeEncodeError<BoundaryDomainError, u8>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
fn reset(&mut self, _output: &mut [u8], _output_index: usize) -> Result<usize, Self::Error> {
self.encoded.clear();
self.resets += 1;
Ok(0)
}
fn transcode(
&mut self,
input: &[u8],
input_index: usize,
output: &mut [u8],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
let mut read = 0;
while input_index + read < input.len() {
let value = input[input_index + read];
if value == u8::MAX {
return Err(TranscodeEncodeError::domain_main(
BoundaryDomainError,
input_index + read,
));
}
output[output_index + read] = value;
self.encoded.push(value);
read += 1;
}
Ok(TranscodeProgress::complete(read, read))
}
fn finish(&mut self, _output: &mut [u8], _output_index: usize) -> Result<usize, Self::Error> {
Ok(0)
}
}
impl TranscodeEncoder for BoundaryEncoder {
type EncodeError = BoundaryDomainError;
}
fn boundary_mapper(error: TranscodeEncodeError<BoundaryDomainError, u8>) -> Error {
Error::other(error)
}
fn assert_boundary_error(error: &Error) {
assert_eq!(ErrorKind::Other, error.kind());
assert!(
error
.get_ref()
.and_then(|source| source.downcast_ref::<TranscodeEncodeError<BoundaryDomainError, u8>>())
.and_then(TranscodeEncodeError::domain_ref)
== Some(&BoundaryDomainError)
);
}
#[test]
fn test_encode_error_commit_boundary_sync_batch() {
let mut output = TranscodeEncodeOutput::with_capacity(Cursor::new(Vec::new()), 8);
let mut encoder = BoundaryEncoder::default();
let mut map_error = boundary_mapper;
let (pending, index, _) = output.spare_raw_parts_mut();
pending[index] = 0x11;
unsafe {
output.advance(1);
}
let error = output
.transcode(&mut encoder, &mut map_error, &[0x22, u8::MAX], 0, 2)
.expect_err("bad second value fails the encoder call");
assert_boundary_error(&error);
assert_eq!(&[0x22], encoder.encoded.as_slice());
output.flush().expect("old committed pending unit flushes");
assert_eq!(&[0x11], output.inner().get_ref().as_slice());
}
#[test]
fn test_encode_error_commit_boundary_sync_step_and_reset() {
let mut output = TranscodeEncodeOutput::with_capacity(Cursor::new(Vec::new()), 8);
let mut encoder = BoundaryEncoder::default();
let mut map_error = boundary_mapper;
let (pending, index, _) = output.spare_raw_parts_mut();
pending[index] = 0x11;
unsafe {
output.advance(1);
}
let error = output
.transcode_step(&mut encoder, &mut map_error, &[0x22, u8::MAX], 0, 2)
.expect_err("bad second value fails the encoder call");
assert_boundary_error(&error);
assert_eq!(&[0x22], encoder.encoded.as_slice());
output
.reset(&mut encoder, &mut map_error)
.expect("new stream reset succeeds");
let progress = output
.transcode_step(&mut encoder, &mut map_error, &[0x33], 0, 1)
.expect("new stream value succeeds");
assert_eq!(TranscodeProgress::complete(1, 1), progress);
output.flush().expect("only committed units flush");
assert_eq!(&[0x11, 0x33], output.inner().get_ref().as_slice());
}
#[test]
fn test_encode_error_commit_boundary_sink_failure_keeps_old_pending() {
let mut output = TranscodeEncodeOutput::with_capacity(
UnitOutput {
fail_write: true,
..UnitOutput::default()
},
1,
);
let mut encoder = CountedEncoder::default();
let mut map_error = |_| Error::other("encoder error");
let (pending, index, _) = output.spare_raw_parts_mut();
pending[index] = 0x11;
unsafe {
output.advance(1);
}
let error = output
.transcode(&mut encoder, &mut map_error, &[1], 0, 1)
.expect_err("delivery of the older pending unit fails");
assert_eq!(ErrorKind::BrokenPipe, error.kind());
assert_eq!(0, encoder.calls);
let (_inner, recovery) = output.into_parts();
let BufferedOutputRecovery::Pending(pending) = recovery else {
panic!("failed sink write must retain pending units");
};
assert_eq!(&[0x11], pending.readable());
}
#[derive(Debug, Default)]
struct CountedEncoder {
calls: usize,
}
impl Transcoder for CountedEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, _input_len: usize) -> Result<usize, CapacityError> {
Ok(1)
}
fn reset(&mut self, output: &mut [u16], output_index: usize) -> Result<usize, Self::Error> {
codec::TranscodeFailure::ensure_output_index(output.len(), output_index)?;
Ok(0)
}
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
self.calls += 1;
let mut read = 0;
while input_index + read < input.len() && output_index + read < output.len() {
output[output_index + read] = input[input_index + read] as u16;
read += 1;
}
if input_index + read < input.len() {
Ok(TranscodeProgress::need_output(crate::nonzero(1), read, read))
} else {
Ok(TranscodeProgress::complete(read, read))
}
}
fn finish(&mut self, output: &mut [u16], output_index: usize) -> Result<usize, Self::Error> {
codec::TranscodeFailure::ensure_output_index(output.len(), output_index)?;
Ok(0)
}
}
impl TranscodeEncoder for CountedEncoder {
type EncodeError = PairEncodeError;
}
#[derive(Debug, Default)]
struct HugeEncodeBoundCodec;
impl Codec for HugeEncodeBoundCodec {
type Value = u8;
type Unit = u8;
type DecodeError = core::convert::Infallible;
type EncodeError = core::convert::Infallible;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = usize::MAX;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
unsafe fn decode(
&mut self,
input: &[u8],
input_index: usize,
) -> Result<(u8, NonZeroUsize), DecodeFailure<Self::DecodeError>> {
Ok((input[input_index], crate::nonzero(1)))
}
unsafe fn encode(
&mut self,
_value: &u8,
_output: &mut [u8],
_output_index: usize,
) -> Result<usize, Self::EncodeError> {
unreachable!("allocation failure prevents encoding")
}
}
#[derive(Debug, Eq, PartialEq, ThisError)]
enum PairEncodeError {
#[error("bad input index")]
BadInputIndex,
#[error("bad output index")]
BadOutputIndex,
#[error("invalid output index {index} for output length {len}")]
InvalidOutputIndex { index: usize, len: usize },
#[error("insufficient output at index {output_index}: required {required}, available {available}")]
InsufficientOutput {
output_index: usize,
required: usize,
available: usize,
},
#[error("capacity overflow")]
CapacityOverflow,
}
fn domain<Value>(error: PairEncodeError) -> TranscodeEncodeError<PairEncodeError, Value> {
TranscodeEncodeError::domain_main(error, 0)
}
#[derive(Debug, Default)]
struct CompleteEncodeLifecycleCodec {
state: u8,
}
impl Codec for CompleteEncodeLifecycleCodec {
type Value = u32;
type Unit = u16;
type DecodeError = PairEncodeError;
type EncodeError = PairEncodeError;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_RESET_UNITS: usize = 1;
const MAX_ENCODE_FINISH_UNITS: usize = 1;
unsafe fn encode_reset(&mut self, output: &mut [u16], output_index: usize) -> Result<usize, Self::EncodeError> {
assert_eq!(0, self.state, "encode reset must start each lifecycle");
output[output_index] = 0xaaaa;
self.state = 1;
Ok(1)
}
unsafe fn decode(
&mut self,
input: &[u16],
input_index: usize,
) -> Result<(u32, NonZeroUsize), DecodeFailure<Self::DecodeError>> {
Ok((u32::from(input[input_index]), crate::nonzero(1)))
}
unsafe fn encode(
&mut self,
value: &u32,
output: &mut [u16],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
assert_eq!(1, self.state, "encode must run after reset");
output[output_index] = *value as u16;
self.state = 2;
Ok(1)
}
unsafe fn encode_finish(&mut self, output: &mut [u16], output_index: usize) -> Result<usize, Self::EncodeError> {
assert_eq!(2, self.state, "encode finish must run after encode");
output[output_index] = 0xbbbb;
self.state = 0;
Ok(1)
}
}
#[derive(Debug, Default)]
struct ResetWidthCodec {
pre_reset_queries: Cell<usize>,
reset: bool,
}
impl Codec for ResetWidthCodec {
type Value = u32;
type Unit = u16;
type DecodeError = PairEncodeError;
type EncodeError = PairEncodeError;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 2;
const MAX_DECODE_UNITS_PER_VALUE: usize = 2;
fn encode_len(&self, _value: &u32) -> usize {
if self.reset {
2
} else {
self.pre_reset_queries.set(self.pre_reset_queries.get() + 1);
1
}
}
unsafe fn decode(
&mut self,
input: &[u16],
input_index: usize,
) -> Result<(u32, NonZeroUsize), DecodeFailure<Self::DecodeError>> {
Ok((u32::from(input[input_index]), crate::nonzero(1)))
}
unsafe fn encode(
&mut self,
value: &u32,
output: &mut [u16],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
assert!(self.reset, "encode must observe reset state");
output[output_index] = *value as u16;
output[output_index + 1] = (*value >> 16) as u16;
Ok(2)
}
unsafe fn encode_reset(&mut self, _output: &mut [u16], _output_index: usize) -> Result<usize, Self::EncodeError> {
self.reset = true;
Ok(0)
}
unsafe fn encode_finish(&mut self, _output: &mut [u16], _output_index: usize) -> Result<usize, Self::EncodeError> {
self.reset = false;
Ok(0)
}
}
macro_rules! noop_reset {
($output:ty) => {
fn reset(
&mut self,
output: &mut [$output],
output_index: usize,
) -> Result<usize, TranscodeEncodeError<PairEncodeError, u32>> {
codec::TranscodeFailure::ensure_output_index(output.len(), output_index)?;
Ok(0)
}
};
}
macro_rules! noop_finish {
($output:ty) => {
fn finish(
&mut self,
output: &mut [$output],
output_index: usize,
) -> Result<usize, TranscodeEncodeError<PairEncodeError, u32>> {
codec::TranscodeFailure::ensure_output_index(output.len(), output_index)?;
Ok(0)
}
};
}
#[derive(Debug, Default)]
struct PairEncoder;
impl Transcoder for PairEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
input_len.checked_mul(2).ok_or(CapacityError::OutputLengthOverflow)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
if output_index > output.len() {
return Err(domain(PairEncodeError::BadOutputIndex));
}
let mut read = 0;
let mut written = 0;
while input_index + read < input.len() {
if input[input_index + read] == u32::MAX {
return Err(domain(PairEncodeError::BadInputIndex));
}
if output_index + written + 2 > output.len() {
return Ok(TranscodeProgress::need_output(crate::nonzero(2), read, written));
}
let value = input[input_index + read];
output[output_index + written] = (value >> 16) as u16;
output[output_index + written + 1] = value as u16;
read += 1;
written += 2;
}
Ok(TranscodeProgress::complete(read, written))
}
fn finish(
&mut self,
_output: &mut [u16],
_output_index: usize,
) -> Result<usize, TranscodeEncodeError<PairEncodeError, u32>> {
Ok(0)
}
}
#[derive(Debug, Default)]
struct ZeroOutputEncoder;
impl Transcoder for ZeroOutputEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, _input_len: usize) -> Result<usize, CapacityError> {
Ok(0)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
_output: &mut [u16],
_output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
Ok(TranscodeProgress::complete(input.len() - input_index, 0))
}
noop_finish!(u16);
}
impl TranscodeEncoder for ZeroOutputEncoder {
type EncodeError = PairEncodeError;
}
#[derive(Debug, Default)]
struct FinishEncoder {
finished: bool,
fail_finish: bool,
transcode_calls: usize,
}
#[derive(Debug, Default)]
struct ResetEncoder {
reset_calls: usize,
}
impl Transcoder for ResetEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
Ok(1)
}
fn reset(&mut self, output: &mut [u16], output_index: usize) -> Result<usize, Self::Error> {
self.reset_calls += 1;
output[output_index] = 0xaaaa;
Ok(1)
}
fn transcode(
&mut self,
_input: &[u32],
_input_index: usize,
_output: &mut [u16],
_output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
Ok(TranscodeProgress::complete(0, 0))
}
fn finish(&mut self, _output: &mut [u16], _output_index: usize) -> Result<usize, Self::Error> {
Ok(0)
}
}
impl Transcoder for FinishEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
Ok(1)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
self.transcode_calls += 1;
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
if output_index > output.len() {
return Err(domain(PairEncodeError::BadOutputIndex));
}
if input_index == input.len() {
return Ok(TranscodeProgress::complete(0, 0));
}
if output_index == output.len() {
return Ok(TranscodeProgress::need_output(crate::nonzero(1), 0, 0));
}
output[output_index] = input[input_index] as u16;
Ok(TranscodeProgress::complete(1, 1))
}
fn finish(
&mut self,
output: &mut [u16],
output_index: usize,
) -> Result<usize, TranscodeEncodeError<PairEncodeError, u32>> {
if self.fail_finish {
return Err(domain(PairEncodeError::CapacityOverflow));
}
if self.finished {
return Ok(0);
}
if output_index >= output.len() {
return Err(domain(PairEncodeError::InsufficientOutput {
output_index,
required: 1,
available: 0,
}));
}
output[output_index] = 0xeeee;
self.finished = true;
Ok(1)
}
}
#[derive(Debug, Default)]
struct TwoUnitFinishEncoder;
impl Transcoder for TwoUnitFinishEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
Ok(2)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
_output: &mut [u16],
_output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
Ok(TranscodeProgress::complete(0, 0))
}
fn finish(
&mut self,
output: &mut [u16],
output_index: usize,
) -> Result<usize, TranscodeEncodeError<PairEncodeError, u32>> {
output[output_index] = 0xaaaa;
output[output_index + 1] = 0xbbbb;
Ok(2)
}
}
#[derive(Debug, Default)]
struct OverreportedFinishEncoder;
impl Transcoder for OverreportedFinishEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
Ok(1)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
_output: &mut [u16],
_output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
Ok(TranscodeProgress::complete(input.len() - input_index, 0))
}
fn finish(
&mut self,
output: &mut [u16],
output_index: usize,
) -> Result<usize, TranscodeEncodeError<PairEncodeError, u32>> {
output[output_index] = 0xaaaa;
Ok(2)
}
}
#[derive(Debug, Default)]
struct ZeroWidthFailingFinishEncoder;
impl Transcoder for ZeroWidthFailingFinishEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
Ok(0)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
if output_index > output.len() {
return Err(domain(PairEncodeError::BadOutputIndex));
}
Ok(TranscodeProgress::complete(0, 0))
}
fn finish(
&mut self,
_output: &mut [u16],
_output_index: usize,
) -> Result<usize, TranscodeEncodeError<PairEncodeError, u32>> {
Err(domain(PairEncodeError::BadInputIndex))
}
}
#[derive(Debug, Default)]
struct UnitOutput {
units: Vec<u16>,
flushed: bool,
fail_write: bool,
fail_flush: bool,
}
impl Output for UnitOutput {
type Item = u16;
unsafe fn write_unchecked(&mut self, input: &[u16], index: usize, count: usize) -> std::io::Result<usize> {
if self.fail_write {
return Err(Error::new(ErrorKind::BrokenPipe, "output write failure"));
}
self.units.extend_from_slice(&input[index..index + count]);
Ok(count)
}
fn flush(&mut self) -> std::io::Result<()> {
if self.fail_flush {
return Err(Error::new(ErrorKind::BrokenPipe, "output flush failure"));
}
self.flushed = true;
Ok(())
}
}
#[derive(Debug, Default)]
struct CapacityBoundEncoder;
impl Transcoder for CapacityBoundEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
Err(CapacityError::OutputLengthOverflow)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
_output: &mut [u16],
_output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
Ok(TranscodeProgress::complete(0, 0))
}
noop_finish!(u16);
}
#[derive(Clone, Copy, Debug)]
enum FinishFailure {
InvalidIndex,
InsufficientOutput,
}
#[derive(Debug)]
struct FailingFinishEncoder {
failure: FinishFailure,
}
impl Transcoder for FailingFinishEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
Ok(0)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
_output: &mut [u16],
_output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
Ok(TranscodeProgress::complete(0, 0))
}
fn finish(
&mut self,
_output: &mut [u16],
_output_index: usize,
) -> Result<usize, TranscodeEncodeError<PairEncodeError, u32>> {
match self.failure {
FinishFailure::InvalidIndex => Err(domain(PairEncodeError::InvalidOutputIndex { index: 4, len: 1 })),
FinishFailure::InsufficientOutput => Err(domain(PairEncodeError::InsufficientOutput {
output_index: 0,
required: 2,
available: 1,
})),
}
}
}
#[derive(Debug, Default)]
struct NeedInputEncoder;
impl Transcoder for NeedInputEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
_output: &mut [u16],
_output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
Ok(TranscodeProgress::need_input(crate::nonzero(2), 0, 0))
}
noop_finish!(u16);
}
#[derive(Clone, Copy, Debug)]
enum HugeBoundPhase {
Reset,
NeedOutput,
Finish,
}
#[derive(Debug)]
struct HugeBoundEncoder {
phase: HugeBoundPhase,
}
impl Transcoder for HugeBoundEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(if matches!(self.phase, HugeBoundPhase::NeedOutput) {
usize::MAX
} else {
input_len
})
}
fn max_reset_output_len(&self) -> Result<usize, CapacityError> {
Ok(match self.phase {
HugeBoundPhase::Reset => usize::MAX,
_ => 0,
})
}
fn reset(&mut self, _output: &mut [u16], _output_index: usize) -> Result<usize, Self::Error> {
unreachable!("huge reset bound prevents reset")
}
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
if matches!(self.phase, HugeBoundPhase::NeedOutput) {
output[output_index] = input[input_index] as u16;
return Ok(TranscodeProgress::need_output(
NonZeroUsize::new(usize::MAX).expect("non-zero maximum"),
1,
1,
));
}
Ok(TranscodeProgress::complete(1, 0))
}
fn max_finish_output_len(&self) -> Result<usize, CapacityError> {
Ok(match self.phase {
HugeBoundPhase::Finish => usize::MAX,
_ => 0,
})
}
fn finish(&mut self, _output: &mut [u16], _output_index: usize) -> Result<usize, Self::Error> {
unreachable!("huge finish bound prevents finish")
}
}
impl TranscodeEncoder for HugeBoundEncoder {
type EncodeError = PairEncodeError;
}
#[cfg(debug_assertions)]
#[derive(Debug, Default)]
struct NeedOutputAfterReadEncoder;
#[cfg(debug_assertions)]
impl Transcoder for NeedOutputAfterReadEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
_output: &mut [u16],
_output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
Ok(TranscodeProgress::need_output(crate::nonzero(1), 1, 0))
}
noop_finish!(u16);
}
#[derive(Debug, Default)]
struct NeedOutputAfterWriteEncoder;
impl Transcoder for NeedOutputAfterWriteEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index >= input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
output[output_index] = input[input_index] as u16;
Ok(TranscodeProgress::need_output(crate::nonzero(1), 1, 1))
}
noop_finish!(u16);
}
#[derive(Debug, Default)]
struct NeedOutputAfterReadPastCapacityEncoder;
impl Transcoder for NeedOutputAfterReadPastCapacityEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index >= input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
output[output_index] = input[input_index] as u16;
Ok(TranscodeProgress::need_output(crate::nonzero(2), 1, 1))
}
noop_finish!(u16);
}
#[derive(Debug, Default)]
struct PrefixBeforeReadEncoder {
emitted_prefix: bool,
}
impl Transcoder for PrefixBeforeReadEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
input_len.checked_add(1).ok_or(CapacityError::OutputLengthOverflow)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
if output_index > output.len() {
return Err(domain(PairEncodeError::BadOutputIndex));
}
if !self.emitted_prefix {
if output_index == output.len() {
return Ok(TranscodeProgress::need_output(crate::nonzero(1), 0, 0));
}
output[output_index] = 0xaaaa;
self.emitted_prefix = true;
return Ok(TranscodeProgress::need_output(crate::nonzero(1), 0, 1));
}
if input_index == input.len() {
return Ok(TranscodeProgress::complete(0, 0));
}
if output_index == output.len() {
return Ok(TranscodeProgress::need_output(crate::nonzero(1), 0, 0));
}
output[output_index] = input[input_index] as u16;
Ok(TranscodeProgress::complete(1, 1))
}
noop_finish!(u16);
}
#[derive(Debug, Default)]
struct OverreadingProgressEncoder;
impl Transcoder for OverreadingProgressEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
_output: &mut [u16],
_output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
Ok(TranscodeProgress::complete(input.len() + 1, 0))
}
noop_finish!(u16);
}
#[derive(Debug, Default)]
struct OverwritingProgressEncoder;
impl Transcoder for OverwritingProgressEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len + 1)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
let available = output.len() - output_index;
Ok(TranscodeProgress::complete(0, available + 1))
}
noop_finish!(u16);
}
#[cfg(debug_assertions)]
#[derive(Debug, Default)]
struct OverflowingNeedOutputEncoder;
#[cfg(debug_assertions)]
impl Transcoder for OverflowingNeedOutputEncoder {
type Input = u32;
type Output = u16;
type Error = TranscodeEncodeError<PairEncodeError, u32>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len)
}
noop_reset!(u16);
fn transcode(
&mut self,
input: &[u32],
input_index: usize,
_output: &mut [u16],
_output_index: usize,
) -> Result<TranscodeProgress, TranscodeEncodeError<PairEncodeError, u32>> {
if input_index > input.len() {
return Err(domain(PairEncodeError::BadInputIndex));
}
Ok(TranscodeProgress::need_output(crate::nonzero(1), 0, 0))
}
noop_finish!(u16);
}
macro_rules! impl_pair_transcode_encoder {
($($encoder:ty),+ $(,)?) => {
$(
impl TranscodeEncoder for $encoder {
type EncodeError = PairEncodeError;
}
)+
};
}
impl_pair_transcode_encoder!(
PairEncoder,
FinishEncoder,
TwoUnitFinishEncoder,
OverreportedFinishEncoder,
ZeroWidthFailingFinishEncoder,
CapacityBoundEncoder,
FailingFinishEncoder,
NeedInputEncoder,
NeedOutputAfterWriteEncoder,
NeedOutputAfterReadPastCapacityEncoder,
PrefixBeforeReadEncoder,
OverreadingProgressEncoder,
OverwritingProgressEncoder,
);
#[cfg(debug_assertions)]
impl_pair_transcode_encoder!(NeedOutputAfterReadEncoder, OverflowingNeedOutputEncoder,);
#[derive(Debug)]
struct FixedCapacityOutput {
units: Vec<u16>,
flushed: bool,
capacity: usize,
}
impl FixedCapacityOutput {
fn new(capacity: usize) -> Self {
Self {
units: Vec::new(),
flushed: false,
capacity,
}
}
}
impl Output for FixedCapacityOutput {
type Item = u16;
unsafe fn write_unchecked(&mut self, input: &[u16], index: usize, count: usize) -> std::io::Result<usize> {
if self.units.len() + count > self.capacity {
return Err(Error::new(ErrorKind::InvalidInput, "fixed output capacity exceeded"));
}
self.units.extend_from_slice(&input[index..index + count]);
Ok(count)
}
fn flush(&mut self) -> std::io::Result<()> {
self.flushed = true;
Ok(())
}
}
fn map_error(error: TranscodeEncodeError<PairEncodeError, u32>) -> Error {
Error::new(ErrorKind::InvalidData, format!("{error:?}"))
}
fn map_codec_error(error: PairEncodeError) -> Error {
Error::new(ErrorKind::InvalidData, error)
}
fn panic_codec_error(_error: PairEncodeError) -> Error {
panic!("framework failures must not call the domain mapper")
}
fn encode_with<E>(
output: &mut TranscodeEncodeOutput<UnitOutput>,
encoder: &mut E,
input: &[u32],
input_index: usize,
count: usize,
) -> std::io::Result<usize>
where
E: TranscodeEncoder<
Input = u32,
Output = u16,
EncodeError = PairEncodeError,
Error = TranscodeEncodeError<PairEncodeError, u32>,
>,
{
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
output.transcode(encoder, &mut mapper, input, input_index, count)
}
fn finish_with<E>(output: &mut TranscodeEncodeOutput<UnitOutput>, encoder: &mut E) -> std::io::Result<()>
where
E: Transcoder<Input = u32, Output = u16, Error = TranscodeEncodeError<PairEncodeError, u32>>,
{
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
output.finish(encoder, &mut mapper)
}
#[test]
fn test_buffered_encode_output_reset_buffers_prefix_without_flushing() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 1);
let mut encoder = ResetEncoder::default();
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
output
.reset(&mut encoder, &mut mapper)
.expect("reset should buffer its prefix");
assert_eq!(1, encoder.reset_calls);
assert!(output.inner().units.is_empty());
output.flush().expect("flush should deliver the reset prefix");
assert_eq!(&[0xaaaa], output.inner().units.as_slice());
}
#[test]
fn test_buffered_encode_output_exposes_parts_and_debug() {
let output = UnitOutput::default();
let output = TranscodeEncodeOutput::with_capacity(output, 3);
let debug = format!("{output:?}");
assert!(debug.contains("TranscodeEncodeOutput"));
assert!(output.inner().units.is_empty());
let (inner, recovery) = output.into_parts();
let BufferedOutputRecovery::Pending(pending) = recovery else {
panic!("unexpected indeterminate recovery");
};
assert!(inner.units.is_empty());
assert!(pending.is_empty());
}
#[test]
fn test_buffered_encode_output_exposes_raw_byte_write_and_seek_adapters() {
let mut output = TranscodeEncodeOutput::new(Cursor::new(Vec::new()));
let written = Write::write(&mut output, &[1, 2]).expect("raw unit write should succeed");
assert_eq!(2, written);
let written = Write::write(&mut output, &[3, 4]).expect("raw unit write should succeed");
assert_eq!(2, written);
assert_eq!(
1,
Write::write(&mut output, &[5]).expect("std::io::Write should delegate to raw unit writes")
);
Write::write_all(&mut output, &[6, 7]).expect("std::io::Write::write_all should delegate to raw units");
Write::flush(&mut output).expect("std::io::Write::flush should drain");
assert_eq!(&[1, 2, 3, 4, 5, 6, 7], output.inner().get_ref().as_slice(),);
assert_eq!(
1,
Seek::seek(&mut output, SeekFrom::Start(1)).expect("std::io::Seek should flush then delegate")
);
Write::write_all(&mut output, &[8]).expect("write after seek should update the wrapped cursor");
output.flush().expect("flush should drain after seek");
assert_eq!(&[1, 8, 3, 4, 5, 6, 7], output.inner().get_ref().as_slice(),);
}
#[test]
fn test_buffered_encode_output_writes_one_codec_value() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 2);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::Success);
output
.write_encoded_with(&mut codec, &0x1234_5678, map_codec_error)
.expect("one codec value should encode into the output buffer");
output.flush().expect("encoded units should flush");
assert_eq!(&[0x5678, 0x1234], output.inner().units.as_slice());
}
#[test]
fn test_buffered_encode_output_returns_zero_for_zero_count() {
let output = UnitOutput::default();
let mut encoder = PairEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let written =
encode_with(&mut output, &mut encoder, &[0x0001_0002], 0, 0).expect("zero-count write should be a no-op");
assert_eq!(0, written);
assert!(output.inner().units.is_empty());
}
#[test]
fn test_buffered_encode_output_transcode_respects_input_range() {
let output = UnitOutput::default();
let mut encoder = PairEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 4);
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
let written = output
.transcode(&mut encoder, &mut mapper, &[0x0001_0002], 0, 1)
.expect("checked encode should accept a valid input range");
output.flush().expect("flush should drain encoded units");
assert_eq!(1, written);
assert_eq!(&[1, 2], output.inner().units.as_slice());
}
#[test]
fn test_buffered_encode_output_transcode_accepts_nonzero_input_index() {
let output = UnitOutput::default();
let mut encoder = PairEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 2);
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
let written = output
.transcode(&mut encoder, &mut mapper, &[0, 0x0003_0004, 0], 1, 1)
.expect("a valid nonzero source index should be encoded");
output.flush().expect("flush should deliver encoded units");
assert_eq!(1, written);
assert_eq!(&[3, 4], output.inner().units.as_slice());
}
#[test]
fn test_buffered_encode_output_accepts_zero_output_encoder() {
let output = UnitOutput::default();
let mut encoder = ZeroOutputEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 0);
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
assert_eq!(1, output.transcode(&mut encoder, &mut mapper, &[7], 0, 1).unwrap());
assert!(output.inner().units.is_empty());
}
#[test]
fn test_buffered_encode_output_transcode_rejects_invalid_input_range() {
let output = UnitOutput::default();
let mut encoder = PairEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 4);
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
let error = output
.transcode(&mut encoder, &mut mapper, &[0x0001_0002], 1, 1)
.expect_err("invalid input range should be rejected before encoding");
assert_eq!(ErrorKind::InvalidInput, error.kind());
assert_eq!("encode input range exceeds source buffer", error.to_string(),);
}
#[test]
fn test_buffered_encode_output_encodes_and_flushes_units() {
let output = UnitOutput::default();
let mut encoder = PairEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let written = encode_with(&mut output, &mut encoder, &[0x0001_0002, 0x0003_0004], 0, 2)
.expect("encoding should accept both values");
assert_eq!(2, written);
output.flush().expect("flush should drain buffered units");
assert_eq!(&[1, 2, 3, 4], output.inner().units.as_slice());
assert!(output.inner().flushed);
}
#[test]
fn test_buffered_encode_output_delivers_before_accepting_next_full_batch() {
let output = UnitOutput::default();
let mut encoder = PairEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 2);
let (buffer, _, _) = output.spare_raw_parts_mut();
let capacity = buffer.len();
let written =
encode_with(&mut output, &mut encoder, &[0x0001_0002], 0, 1).expect("first value should fill the unit buffer");
assert_eq!(1, written);
assert!(output.inner().units.is_empty());
let written = encode_with(&mut output, &mut encoder, &[0x0003_0004], 0, 1)
.expect("second value should flush the full buffer first");
assert_eq!(1, written);
assert_eq!(&[1, 2], output.inner().units.as_slice());
for value in 2..1000 {
encode_with(&mut output, &mut encoder, &[value], 0, 1).expect("each full batch should be accepted");
let (buffer, _, _) = output.spare_raw_parts_mut();
assert_eq!(capacity, buffer.len());
}
output.flush().expect("flush should drain buffered units");
assert_eq!(2000, output.inner().units.len());
}
#[test]
fn test_buffered_encode_output_reports_delivery_error_before_consuming_next_batch() {
let output = FixedCapacityOutput::new(0);
let mut encoder = FinishEncoder::default();
let mut output = TranscodeEncodeOutput::with_capacity(output, 1);
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
let written = output
.transcode(&mut encoder, &mut mapper, &[0x1234], 0, 1)
.expect("first value should remain pending in the internal buffer");
assert_eq!(1, written);
assert_eq!(1, encoder.transcode_calls);
assert_eq!(0, output.spare_capacity());
let error = output
.transcode(&mut encoder, &mut mapper, &[0x5678], 0, 1)
.expect_err("the next batch must be rejected before consuming its source");
assert_eq!(ErrorKind::InvalidInput, error.kind());
assert!(error.to_string().contains("fixed output capacity exceeded"));
assert_eq!(1, encoder.transcode_calls);
}
#[test]
fn test_buffered_encode_output_reserves_the_complete_operation_bound() {
let output = UnitOutput::default();
let mut encoder = PairEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 1);
assert_eq!(1, encode_with(&mut output, &mut encoder, &[0x0001_0002], 0, 1).unwrap());
}
#[test]
fn test_buffered_encode_output_reserves_and_consumes_a_batch_larger_than_capacity() {
let output = UnitOutput::default();
let mut encoder = PairEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 1);
let input = [0x0001_0002, 0x0003_0004, 0x0005_0006];
assert_eq!(
3,
encode_with(&mut output, &mut encoder, &input, 0, input.len()).unwrap()
);
assert!(output.inner().units.is_empty());
let (buffer, initialized_len, capacity) = output.spare_raw_parts_mut();
assert_eq!(6, initialized_len);
assert!(buffer.len() >= 6);
assert!(initialized_len + capacity >= 6);
output.flush().unwrap();
assert_eq!([1, 2, 3, 4, 5, 6], output.inner().units.as_slice());
}
#[cfg(debug_assertions)]
#[test]
fn test_buffered_encode_output_rejects_satisfied_need_output_after_consuming_input() {
let output = UnitOutput::default();
let mut encoder = NeedOutputAfterReadEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 1);
let error = encode_with(&mut output, &mut encoder, &[0x1234], 0, 1)
.expect_err("NeedOutput with available space violates progress");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert!(error.to_string().contains("reported required"));
}
#[test]
fn test_buffered_encode_output_reports_transcoder_errors_as_io_errors() {
let output = UnitOutput::default();
let mut encoder = PairEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let input = [u32::MAX];
let error =
encode_with(&mut output, &mut encoder, &input, 0, 1).expect_err("encoder error should be mapped to I/O error");
assert_eq!(ErrorKind::InvalidData, error.kind());
}
#[test]
fn test_buffered_encode_output_rejects_need_input_status() {
let output = UnitOutput::default();
let mut encoder = NeedInputEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let error = encode_with(&mut output, &mut encoder, &[0x1234], 0, 1)
.expect_err("encoder NeedInput status should be rejected");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert!(error.to_string().contains("violated the TranscodeEncoder contract"));
}
#[test]
fn test_buffered_encode_output_rejects_overreported_read_progress() {
let output = UnitOutput::default();
let mut encoder = OverreadingProgressEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let error = encode_with(&mut output, &mut encoder, &[0x1234], 0, 1)
.expect_err("overreported input progress should be rejected");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert!(error.to_string().contains("consumed"));
assert!(error.to_string().contains("only"));
}
#[test]
fn test_buffered_encode_output_rejects_overreported_write_progress() {
let output = UnitOutput::default();
let mut encoder = OverwritingProgressEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 1);
let error = encode_with(&mut output, &mut encoder, &[0x1234], 0, 1)
.expect_err("overreported output progress should be rejected");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert!(error.to_string().contains("wrote"));
assert!(error.to_string().contains("output slots"));
}
#[cfg(debug_assertions)]
#[test]
fn test_buffered_encode_output_rejects_satisfied_overflowing_need_output() {
let output = UnitOutput::default();
let mut encoder = OverflowingNeedOutputEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let error = encode_with(&mut output, &mut encoder, &[0x1234], 0, 1)
.expect_err("satisfied NeedOutput requirement should be rejected");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert!(error.to_string().contains("reported required"));
}
#[test]
fn test_buffered_encode_output_flush_does_not_finish_encoder() {
let output = UnitOutput::default();
let mut encoder = FinishEncoder::default();
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let written = encode_with(&mut output, &mut encoder, &[0x1234], 0, 1).expect("encoding should accept the value");
assert_eq!(1, written);
output.flush().expect("flush should only drain buffered units");
assert_eq!(&[0x1234], output.inner().units.as_slice());
finish_with(&mut output, &mut encoder).expect("finish should write encoder trailer");
assert_eq!(&[0x1234, 0xeeee], output.inner().units.as_slice());
}
#[test]
fn test_buffered_encode_output_finish_writes_and_flushes() {
let output = UnitOutput::default();
let mut encoder = FinishEncoder::default();
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
assert_eq!(Ok(1), encoder.max_finish_output_len());
finish_with(&mut output, &mut encoder).expect("finish should write trailer and flush");
assert_eq!(&[0xeeee], output.inner().units.as_slice());
assert!(output.inner().flushed);
assert_eq!(Ok(1), encoder.max_finish_output_len());
output.flush().expect("explicit flush should be harmless after finish");
assert_eq!(&[0xeeee], output.inner().units.as_slice());
}
#[test]
fn test_buffered_encode_output_finish_to_buffer_defers_delivery() {
let output = UnitOutput {
fail_write: true,
..UnitOutput::default()
};
let mut encoder = FinishEncoder::default();
let mut output = TranscodeEncodeOutput::with_capacity(output, 0);
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
output
.finish_to_buffer(&mut encoder, &mut mapper)
.expect("finish should retain the encoder trailer");
let (inner, recovery) = output.into_parts();
let BufferedOutputRecovery::Pending(pending) = recovery else {
panic!("finish_to_buffer must not attempt output I/O");
};
assert!(inner.units.is_empty());
assert_eq!(&[0xeeee], pending.readable());
}
#[test]
#[should_panic(expected = "finish wrote beyond its bound")]
fn test_buffered_encode_output_finish_panics_when_encoder_overreports_bound() {
let output = UnitOutput::default();
let mut encoder = OverreportedFinishEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
let _ = output.finish(&mut encoder, &mut mapper);
}
#[test]
fn test_buffered_encode_output_maps_finish_capacity_bound_error() {
let output = UnitOutput::default();
let mut encoder = CapacityBoundEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let error =
finish_with(&mut output, &mut encoder).expect_err("finish bound overflow should be mapped to I/O error");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert!(error.to_string().contains("output length overflow"));
}
#[test]
fn test_buffered_encode_output_maps_finish_failure_variants() {
let output = UnitOutput::default();
let mut encoder = FinishEncoder {
fail_finish: true,
..FinishEncoder::default()
};
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let error = finish_with(&mut output, &mut encoder).expect_err("finish capacity failure should be mapped to I/O");
assert_eq!(ErrorKind::InvalidData, error.kind());
for failure in [FinishFailure::InvalidIndex, FinishFailure::InsufficientOutput] {
let output = UnitOutput::default();
let mut encoder = FailingFinishEncoder { failure };
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let error = finish_with(&mut output, &mut encoder).expect_err("finish failure should be mapped to I/O error");
assert_eq!(ErrorKind::InvalidData, error.kind());
}
}
#[test]
fn test_buffered_encode_output_finish_delegates_zero_width_finish() {
let output = UnitOutput::default();
let mut encoder = ZeroWidthFailingFinishEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 3);
let error = finish_with(&mut output, &mut encoder).expect_err("zero-width finish errors should not be skipped");
assert_eq!(ErrorKind::InvalidData, error.kind());
}
#[test]
fn test_buffered_encode_output_takes_encoder_per_call() {
let output = UnitOutput::default();
let mut output = TranscodeEncodeOutput::with_capacity(output, 4);
let mut first_encoder = PairEncoder;
let mut second_encoder = PairEncoder;
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
let first = output
.transcode(&mut first_encoder, &mut mapper, &[0x0001_0002], 0, 1)
.expect("first encoder should write one value");
let second = output
.transcode(&mut second_encoder, &mut mapper, &[0x0003_0004], 0, 1)
.expect("second encoder should reuse the same buffer");
output.flush().expect("flush should drain buffered units");
assert_eq!(1, first);
assert_eq!(1, second);
assert_eq!(&[1, 2, 3, 4], output.inner().units.as_slice());
}
#[test]
fn test_buffered_encode_output_exposes_spare_buffer_api() {
let output = UnitOutput::default();
let mut output = TranscodeEncodeOutput::with_capacity(output, 4);
assert!(output.spare_capacity() >= 4);
let (units, index, available) = output.spare_raw_parts_mut();
assert!(available >= 4);
units[index] = 0x00aa;
units[index + 1] = 0x00bb;
unsafe {
output.advance(2);
}
output
.ensure_spare_capacity(2)
.expect("spare capacity should remain available");
output.flush().expect("flush should drain spare units");
assert_eq!(&[0x00aa, 0x00bb], output.inner().units.as_slice());
}
#[test]
fn test_buffered_encode_output_maps_spare_allocation_failure() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 1);
let error = output
.ensure_spare_capacity(usize::MAX)
.expect_err("an impossible allocation should fail");
assert_eq!(ErrorKind::OutOfMemory, error.kind());
}
#[test]
fn test_buffered_encode_output_transcode_flushes_when_spare_is_empty() {
let output = UnitOutput::default();
let mut encoder = PairEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 2);
let first =
encode_with(&mut output, &mut encoder, &[0x0001_0002], 0, 1).expect("first value should fill the spare buffer");
assert_eq!(1, first);
assert_eq!(0, output.spare_capacity());
let second = encode_with(&mut output, &mut encoder, &[0x0003_0004], 0, 1)
.expect("second value should flush before encoding");
assert_eq!(1, second);
output.flush().expect("flush should drain buffered units");
assert_eq!(&[1, 2, 3, 4], output.inner().units.as_slice());
}
#[test]
fn test_buffered_encode_output_rejects_satisfied_partial_need_output_progress() {
let output = UnitOutput::default();
let mut encoder = NeedOutputAfterWriteEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 1);
let error = encode_with(&mut output, &mut encoder, &[0x1234], 0, 1)
.expect_err("NeedOutput with already sufficient spare capacity is invalid progress");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert!(error.to_string().contains("encoder output bound was insufficient"));
assert!(output.inner().units.is_empty());
}
#[test]
fn test_buffered_encode_output_rejects_post_read_need_output_beyond_bound() {
let output = FixedCapacityOutput::new(1);
let mut encoder = NeedOutputAfterReadPastCapacityEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 1);
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
let error = output
.transcode(&mut encoder, &mut mapper, &[0x1234], 0, 1)
.expect_err("NeedOutput beyond the complete output bound must be rejected");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert!(error.to_string().contains("encoder output bound was insufficient"));
}
#[test]
fn test_buffered_encode_output_retries_after_need_output_without_reading() {
let output = UnitOutput::default();
let mut encoder = PrefixBeforeReadEncoder::default();
let mut output = TranscodeEncodeOutput::with_capacity(output, 1);
let error = encode_with(&mut output, &mut encoder, &[0x1234], 0, 1)
.expect_err("NeedOutput while the reserved batch bound has enough space is invalid");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert!(error.to_string().contains("reported required"));
assert!(output.inner().units.is_empty());
output
.flush()
.expect("flush should remain harmless without committed units");
assert!(output.inner().units.is_empty());
}
#[test]
fn test_buffered_encode_output_finish_reports_spare_capacity_error() {
let output = FixedCapacityOutput::new(0);
let mut encoder = FinishEncoder::default();
let mut output = TranscodeEncodeOutput::with_capacity(output, 1);
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
let error = output
.finish(&mut encoder, &mut mapper)
.expect_err("finish should report spare-capacity errors");
assert_eq!(ErrorKind::InvalidInput, error.kind());
}
#[test]
fn test_buffered_encode_output_finish_grows_for_required_spare_capacity() {
let output = UnitOutput::default();
let mut encoder = TwoUnitFinishEncoder;
let mut output = TranscodeEncodeOutput::with_capacity(output, 1);
let mut mapper: fn(TranscodeEncodeError<PairEncodeError, u32>) -> Error = map_error;
output
.finish(&mut encoder, &mut mapper)
.expect("finish should grow and reserve its full bound before writing");
assert_eq!(2, output.inner().units.len());
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ScriptedEncodeMode {
Success,
Reject,
EncodeError,
FinishError,
ExactLenExceedsBound,
}
#[derive(Debug)]
struct ScriptedEncodeCodec {
mode: ScriptedEncodeMode,
}
impl ScriptedEncodeCodec {
fn new(mode: ScriptedEncodeMode) -> Self {
Self { mode }
}
}
impl Codec for ScriptedEncodeCodec {
type Value = u32;
type Unit = u16;
type DecodeError = PairEncodeError;
type EncodeError = PairEncodeError;
const MIN_UNITS_PER_VALUE: usize = 1;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 2;
const MAX_DECODE_UNITS_PER_VALUE: usize = 2;
const MAX_ENCODE_RESET_UNITS: usize = 1;
const MAX_ENCODE_FINISH_UNITS: usize = 1;
fn can_encode_value(&self, _value: &u32) -> bool {
self.mode != ScriptedEncodeMode::Reject
}
fn encode_len(&self, _value: &u32) -> usize {
match self.mode {
ScriptedEncodeMode::ExactLenExceedsBound => 4,
_ => 2,
}
}
unsafe fn decode(
&mut self,
input: &[u16],
input_index: usize,
) -> Result<(u32, NonZeroUsize), DecodeFailure<Self::DecodeError>> {
Ok((u32::from(input[input_index]), crate::nonzero(1)))
}
unsafe fn encode(
&mut self,
value: &u32,
output: &mut [u16],
output_index: usize,
) -> Result<usize, Self::EncodeError> {
if self.mode == ScriptedEncodeMode::EncodeError {
return Err(PairEncodeError::BadOutputIndex);
}
output[output_index] = *value as u16;
output[output_index + 1] = (*value >> 16) as u16;
Ok(2)
}
unsafe fn encode_reset(&mut self, _output: &mut [u16], _output_index: usize) -> Result<usize, Self::EncodeError> {
Ok(0)
}
unsafe fn encode_finish(&mut self, _output: &mut [u16], _output_index: usize) -> Result<usize, Self::EncodeError> {
if self.mode == ScriptedEncodeMode::FinishError {
return Err(PairEncodeError::BadInputIndex);
}
Ok(0)
}
}
#[derive(Debug, Default)]
struct ZeroWriteOutput;
impl Output for ZeroWriteOutput {
type Item = u8;
unsafe fn write_unchecked(&mut self, _input: &[u8], _index: usize, _count: usize) -> std::io::Result<usize> {
Ok(0)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[test]
fn test_buffered_encode_output_write_encoded_grows_persistent_buffer() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 0);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::Success);
output
.write_encoded_with(&mut codec, &0x0001_0002, map_codec_error)
.expect("large value should grow the persistent buffer");
output.flush().expect("encoded units should flush");
assert_eq!(&[0x0002, 0x0001], output.inner().units.as_slice());
assert!(output.spare_capacity() >= 2);
}
#[test]
fn test_buffered_encode_output_maps_codec_bound_allocation_failure() {
let mut output = TranscodeEncodeOutput::with_capacity(Vec::<u8>::new(), 1);
let mut codec = HugeEncodeBoundCodec;
let error = output
.write_encoded_with(&mut codec, &1, |_| Error::other("codec error"))
.expect_err("an impossible codec bound must fail allocation");
assert_eq!(ErrorKind::OutOfMemory, error.kind());
}
#[test]
fn test_buffered_encode_output_maps_lifecycle_allocation_failures() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 1);
let mut reset_encoder = HugeBoundEncoder {
phase: HugeBoundPhase::Reset,
};
let error = output
.reset(&mut reset_encoder, &mut map_error)
.expect_err("reset allocation failure must be mapped");
assert_eq!(ErrorKind::OutOfMemory, error.kind());
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 1);
let mut transcode_encoder = HugeBoundEncoder {
phase: HugeBoundPhase::NeedOutput,
};
let error = output
.transcode(&mut transcode_encoder, &mut map_error, &[1], 0, 1)
.expect_err("required output allocation failure must be mapped");
assert_eq!(ErrorKind::OutOfMemory, error.kind());
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 1);
let mut finish_encoder = HugeBoundEncoder {
phase: HugeBoundPhase::Finish,
};
let error = output
.finish(&mut finish_encoder, &mut map_error)
.expect_err("finish allocation failure must be mapped");
assert_eq!(ErrorKind::OutOfMemory, error.kind());
}
#[test]
fn test_buffered_encode_output_write_encoded_runs_complete_lifecycle() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 3);
let mut codec = CompleteEncodeLifecycleCodec::default();
output
.write_encoded_with(&mut codec, &0x1234, map_codec_error)
.expect("first value should complete its encode lifecycle");
output
.write_encoded_with(&mut codec, &0x5678, map_codec_error)
.expect("second value should start a fresh encode lifecycle");
output.flush().expect("encoded units should flush");
assert_eq!(
&[0xaaaa, 0x1234, 0xbbbb, 0xaaaa, 0x5678, 0xbbbb],
output.inner().units.as_slice(),
);
}
#[test]
fn test_write_encoded_with_queries_width_after_reset() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 2);
let mut codec = ResetWidthCodec::default();
output
.write_encoded_with(&mut codec, &0x1234_5678, map_codec_error)
.expect("reset-state width should fit the reserved codec bound");
output.flush().expect("encoded units should flush");
assert_eq!(&[0x5678, 0x1234], output.inner().units.as_slice());
assert_eq!(0, codec.pre_reset_queries.get());
}
#[test]
fn test_buffered_encode_output_write_encoded_maps_domain_error() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 4);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::EncodeError);
let error = output
.write_encoded_with(&mut codec, &0x0001_0002, map_codec_error)
.expect_err("domain encode failure should be mapped");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert_eq!("bad output index", error.to_string());
}
#[test]
fn test_buffered_encode_output_write_encoded_reports_unencodable() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 4);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::Reject);
let error = output
.write_encoded_with(&mut codec, &0x0001_0002, map_codec_error)
.expect_err("unencodable values should be rejected");
assert_eq!(ErrorKind::InvalidInput, error.kind());
assert_eq!("codec cannot encode value", error.to_string());
}
#[test]
fn test_buffered_encode_output_write_all_reports_write_zero() {
let mut output = TranscodeEncodeOutput::with_capacity(ZeroWriteOutput, 0);
let error = Write::write_all(&mut output, &[1, 2, 3]).expect_err("zero-length writes should surface WriteZero");
assert_eq!(ErrorKind::WriteZero, error.kind());
}
#[derive(Debug)]
struct ScriptedByteOutput {
steps: VecDeque<Result<usize, ErrorKind>>,
received: Vec<u8>,
calls: usize,
}
impl ScriptedByteOutput {
fn new(steps: impl IntoIterator<Item = Result<usize, ErrorKind>>) -> Self {
Self {
steps: steps.into_iter().collect(),
received: Vec::new(),
calls: 0,
}
}
}
impl Output for ScriptedByteOutput {
type Item = u8;
unsafe fn write_unchecked(&mut self, input: &[u8], index: usize, count: usize) -> std::io::Result<usize> {
self.calls += 1;
let result = self.steps.pop_front().unwrap_or(Ok(count));
let written = match result {
Ok(written) => written,
Err(kind) => return Err(Error::new(kind, "scripted byte output error")),
};
assert!(written <= count);
self.received.extend_from_slice(&input[index..index + written]);
Ok(written)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[test]
fn test_buffered_encode_output_write_all_retries_interrupted_direct_write() {
let inner = ScriptedByteOutput::new([Err(ErrorKind::Interrupted), Ok(3)]);
let mut output = TranscodeEncodeOutput::with_capacity(inner, 1);
Write::write_all(&mut output, b"abc").expect("interrupted direct write should be retried");
assert_eq!(b"abc", output.inner().received.as_slice());
assert_eq!(2, output.inner().calls);
}
#[test]
fn test_buffered_encode_output_write_all_retries_interrupted_after_short_write() {
let inner = ScriptedByteOutput::new([Ok(1), Err(ErrorKind::Interrupted), Ok(2)]);
let mut output = TranscodeEncodeOutput::with_capacity(inner, 1);
Write::write_all(&mut output, b"abc").expect("short write followed by interruption should complete");
assert_eq!(b"abc", output.inner().received.as_slice());
assert_eq!(3, output.inner().calls);
}
#[test]
fn test_buffered_encode_output_write_all_preserves_prefix_on_error_after_short_write() {
let inner = ScriptedByteOutput::new([Ok(1), Err(ErrorKind::Other)]);
let mut output = TranscodeEncodeOutput::with_capacity(inner, 1);
let error = Write::write_all(&mut output, b"abc").expect_err("non-interrupted error should be returned");
assert_eq!(ErrorKind::Other, error.kind());
assert_eq!(b"a", output.inner().received.as_slice());
assert_eq!(2, output.inner().calls);
}
#[test]
fn test_buffered_encode_output_write_all_empty_input_does_not_call_output() {
let inner = ScriptedByteOutput::new([]);
let mut output = TranscodeEncodeOutput::with_capacity(inner, 1);
Write::write_all(&mut output, &[]).expect("empty input should succeed");
assert_eq!(0, output.inner().calls);
assert!(output.inner().received.is_empty());
}
#[derive(Debug, Default)]
struct OverflowEncodeBoundCodec;
impl Codec for OverflowEncodeBoundCodec {
type Value = u32;
type Unit = u16;
type DecodeError = PairEncodeError;
type EncodeError = PairEncodeError;
const MIN_UNITS_PER_VALUE: usize = 2;
const MAX_ENCODE_UNITS_PER_VALUE: usize = 2;
const MAX_DECODE_UNITS_PER_VALUE: usize = 2;
const MAX_ENCODE_RESET_UNITS: usize = usize::MAX;
const MAX_ENCODE_FINISH_UNITS: usize = usize::MAX;
unsafe fn decode(
&mut self,
input: &[u16],
input_index: usize,
) -> Result<(u32, NonZeroUsize), DecodeFailure<Self::DecodeError>> {
Ok((u32::from(input[input_index]), crate::nonzero(1)))
}
unsafe fn encode(
&mut self,
_value: &u32,
_output: &mut [u16],
_output_index: usize,
) -> Result<usize, Self::EncodeError> {
Ok(2)
}
}
#[derive(Debug)]
struct BrokenPipeByteOutput;
impl Output for BrokenPipeByteOutput {
type Item = u8;
unsafe fn write_unchecked(&mut self, _input: &[u8], _index: usize, _count: usize) -> std::io::Result<usize> {
Err(Error::new(ErrorKind::BrokenPipe, "byte output failure"))
}
fn flush(&mut self) -> std::io::Result<()> {
Err(Error::new(ErrorKind::BrokenPipe, "byte output flush failure"))
}
}
#[test]
fn test_buffered_encode_output_write_encoded_reports_output_bound_overflow() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 4);
let mut codec = OverflowEncodeBoundCodec;
let error = output
.write_encoded_with(&mut codec, &0x0001_0002, map_codec_error)
.expect_err("encode bound overflow should be mapped to invalid input");
assert_eq!(ErrorKind::InvalidInput, error.kind());
assert_eq!("codec output bound overflow", error.to_string());
}
#[test]
#[should_panic(expected = "Codec::encode_len exceeded Codec::MAX_ENCODE_UNITS_PER_VALUE")]
fn test_buffered_encode_output_write_encoded_rejects_width_beyond_bound() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 4);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::ExactLenExceedsBound);
let _ = output.write_encoded_with(&mut codec, &0x0001_0002, panic_codec_error);
}
#[test]
#[should_panic(expected = "Codec::encode_len exceeded Codec::MAX_ENCODE_UNITS_PER_VALUE")]
fn test_buffered_encode_output_write_encoded_rejects_width_beyond_bound_via_scratch() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 0);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::ExactLenExceedsBound);
let _ = output.write_encoded_with(&mut codec, &0x0001_0002, panic_codec_error);
}
#[test]
fn test_buffered_encode_output_write_encoded_propagates_non_capacity_spare_errors() {
let inner = UnitOutput {
fail_write: true,
fail_flush: true,
..UnitOutput::default()
};
let mut output = TranscodeEncodeOutput::with_capacity(inner, 0);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::Success);
let error = output
.write_encoded_with(&mut codec, &0x0001_0002, map_codec_error)
.and_then(|()| output.flush())
.expect_err("buffer flush errors should propagate");
assert_eq!(ErrorKind::BrokenPipe, error.kind());
}
#[test]
fn test_buffered_encode_output_write_encoded_scratch_propagates_flush_errors() {
let inner = UnitOutput {
fail_flush: true,
..UnitOutput::default()
};
let mut output = TranscodeEncodeOutput::with_capacity(inner, 0);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::Success);
let error = output
.write_encoded_with(&mut codec, &0x0001_0002, map_codec_error)
.and_then(|()| output.flush())
.expect_err("persistent-buffer flush should propagate flush failures");
assert_eq!(ErrorKind::BrokenPipe, error.kind());
}
#[test]
fn test_buffered_encode_output_write_encoded_scratch_propagates_write_errors() {
let inner = UnitOutput {
fail_write: true,
..UnitOutput::default()
};
let mut output = TranscodeEncodeOutput::with_capacity(inner, 0);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::Success);
let error = output
.write_encoded_with(&mut codec, &0x0001_0002, map_codec_error)
.and_then(|()| output.flush())
.expect_err("persistent-buffer flush should propagate write failures");
assert_eq!(ErrorKind::BrokenPipe, error.kind());
}
#[test]
fn test_buffered_encode_output_write_encoded_maps_domain_error_via_scratch() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 0);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::EncodeError);
let error = output
.write_encoded_with(&mut codec, &0x0001_0002, map_codec_error)
.expect_err("scratch encode should map domain failures");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert_eq!("bad output index", error.to_string());
}
#[test]
fn test_buffered_encode_output_write_encoded_maps_finish_error_via_scratch() {
let mut output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 0);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::FinishError);
let error = output
.write_encoded_with(&mut codec, &0x0001_0002, map_codec_error)
.expect_err("scratch encode should map finish failures");
assert_eq!(ErrorKind::InvalidData, error.kind());
assert_eq!("bad input index", error.to_string());
}
#[test]
fn test_buffered_encode_output_write_all_propagates_write_errors() {
let mut output = TranscodeEncodeOutput::with_capacity(BrokenPipeByteOutput, 1);
let error = Write::write_all(&mut output, &[1, 2]).expect_err("write_all should propagate output write errors");
assert_eq!(ErrorKind::BrokenPipe, error.kind());
}
#[test]
fn test_buffered_encode_output_write_encoded_reports_delivery_error_before_encoding() {
let inner = UnitOutput {
fail_write: true,
fail_flush: true,
..UnitOutput::default()
};
let mut output = TranscodeEncodeOutput::with_capacity(inner, 4);
let mut codec = ScriptedEncodeCodec::new(ScriptedEncodeMode::Success);
output
.write_encoded_with(&mut codec, &0x0001_0002, map_codec_error)
.expect("first value should leave less than one complete bound");
assert_eq!(2, output.spare_capacity());
let error = output
.write_encoded_with(&mut codec, &0x0003_0004, map_codec_error)
.expect_err("the next value must report a delivery error before encoding");
assert_eq!(ErrorKind::BrokenPipe, error.kind());
}
#[test]
fn test_buffered_encode_output_debug_shows_wrapped_output() {
let output = TranscodeEncodeOutput::with_capacity(UnitOutput::default(), 2);
let debug = format!("{output:?}");
assert!(debug.contains("TranscodeEncodeOutput"));
assert!(debug.contains("output"));
}
#[derive(Default)]
struct PanicOutput {
bytes: Vec<u8>,
}
impl Output for PanicOutput {
type Item = u8;
unsafe fn write_unchecked(&mut self, input: &[u8], index: usize, count: usize) -> std::io::Result<usize> {
let committed = count.min(1);
self.bytes.extend_from_slice(&input[index..index + committed]);
panic!("scripted output panic after committing a prefix");
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[test]
fn test_into_parts_preserves_indeterminate_after_inner_write_panics() {
let mut output = TranscodeEncodeOutput::new(PanicOutput::default());
Write::write_all(&mut output, b"buffered").expect("bytes should be buffered");
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| Write::flush(&mut output)));
assert!(result.is_err());
let (inner, recovery) = output.into_parts();
assert_eq!(b"b", inner.bytes.as_slice());
assert!(matches!(recovery, BufferedOutputRecovery::Indeterminate(_)));
}