pub trait Transcoder {
type Input;
type Output;
type Error: From<TranscodeFailure>;
// Required methods
fn max_transcode_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError>;
fn reset(
&mut self,
output: &mut [Self::Output],
output_index: usize,
) -> Result<usize, Self::Error>;
fn transcode(
&mut self,
input: &[Self::Input],
input_index: usize,
output: &mut [Self::Output],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error>;
fn finish(
&mut self,
output: &mut [Self::Output],
output_index: usize,
) -> Result<usize, Self::Error>;
// Provided methods
fn max_reset_output_len(&self) -> Result<usize, CapacityError> { ... }
fn max_total_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError> { ... }
fn max_finish_output_len(&self) -> Result<usize, CapacityError> { ... }
fn transcode_eof(
&mut self,
input: &[Self::Input],
input_index: usize,
output: &mut [Self::Output],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error> { ... }
fn transcode_complete_into(
&mut self,
input: &[Self::Input],
output: &mut [Self::Output],
) -> Result<usize, Self::Error> { ... }
}Expand description
Converts one logical stream of input units into one logical stream of output units.
transcode is the main streaming API. It transforms a provided input
segment and writes as much output as available buffer space allows. A
successful transcode call must stop with exactly one of three meanings:
all visible input was consumed (Complete), an incomplete tail remains
caller-owned (NeedInput), or output capacity prevented further progress
(NeedOutput).
A transcoder instance has a simple lifecycle:
- A newly created instance is uninitialized; call
Transcoder::resetbefore the first stream. A reset instance is ready for a new logical stream. - Call
Transcoder::transcodezero or more times while input is available. - Preserve any tail reported by
crate::TranscodeStatus::NeedInputin the caller-owned input buffer. - Call
Transcoder::finishafter the caller knows no more input remains and has handled any incomplete tail. Size this final output withTranscoder::max_finish_output_len. - After
Transcoder::finishsucceeds, callTranscoder::resetwith a buffer sized byTranscoder::max_reset_output_lenbefore starting another logical stream with the same instance.
The method is suitable for:
- pull-style consumers that call conversion repeatedly as buffers arrive;
- bounded output sinks that use
NeedOutputprogress duringtranscode; - stateless and stateful codecs that all return progress-oriented stopping reasons.
finish finalizes retained state only; it does not receive source input and
does not reinterpret a tail previously reported by NeedInput. For
codec-backed decoders, streaming uses
Codec::decode and an explicit EOF call may use
Codec::decode_eof
through Transcoder::transcode_eof. If a format needs EOF-aware
maximal-munch parsing or must delay whether a prefix is complete until the
next chunk or EOF, implement that policy in the codec or override
transcode_eof.
Transcoder is intentionally independent from any charset
semantics:
- Use
Transcoderdirectly for custom, policy-free unit transforms. - Use
Transcoderwhen you want to own malformed/unmappable decisions at the call site.
§Examples
Streaming byte-to-word decoding:
use core::convert::Infallible;
use core::num::NonZeroUsize;
use qubit_codec::CapacityError;
use qubit_codec::TranscodeDecodeError;
use qubit_codec::TranscodeFailure;
use qubit_codec::TranscodeProgress;
use qubit_codec::TranscodeStatus;
use qubit_codec::Transcoder;
#[derive(Default)]
struct U16BeBytesDecoder;
impl Transcoder for U16BeBytesDecoder {
type Input = u8;
type Output = u16;
type Error = TranscodeDecodeError<Infallible>;
fn max_transcode_output_len(&self, input_len: usize) -> Result<usize, CapacityError> {
Ok(input_len / 2)
}
fn reset(
&mut self,
output: &mut [u16],
output_index: usize,
) -> Result<usize, Self::Error> {
TranscodeFailure::ensure_output_index(output.len(), output_index)?;
Ok(0)
}
fn transcode(
&mut self,
input: &[u8],
input_index: usize,
output: &mut [u16],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
TranscodeFailure::ensure_transcode_indices(
input.len(),
input_index,
output.len(),
output_index,
)?;
let mut read = 0;
let mut written = 0;
while input_index + read + 1 < input.len() {
if output_index + written == output.len() {
let status = TranscodeStatus::NeedOutput {
required: NonZeroUsize::MIN,
};
return Ok(TranscodeProgress::new(status, read, written));
}
let high = input[input_index + read] as u16;
let low = input[input_index + read + 1] as u16;
output[output_index + written] = (high << 8) | low;
read += 2;
written += 1;
}
if input_index + read == input.len() {
Ok(TranscodeProgress::complete(read, written))
} else {
let status = TranscodeStatus::NeedInput {
required: NonZeroUsize::new(2).expect("two is non-zero"),
};
Ok(TranscodeProgress::new(status, read, written))
}
}
fn finish(
&mut self,
output: &mut [u16],
output_index: usize,
) -> Result<usize, Self::Error> {
TranscodeFailure::ensure_output_index(output.len(), output_index)?;
Ok(0)
}
}
let mut transcoder = U16BeBytesDecoder::default();
let mut reset_output = [];
transcoder
.reset(&mut reset_output, 0)
.expect("stateless decoder reset cannot fail");
let mut output = [0_u16; 1];
let progress = transcoder
.transcode(&[0x12, 0x34, 0xab, 0xcd], 0, &mut output, 0)
.expect("decoding cannot fail");
assert_eq!(TranscodeStatus::NeedOutput {
required: NonZeroUsize::MIN,
}, progress.status());
assert_eq!(2, progress.read());
assert_eq!(1, progress.written());
assert_eq!([0x1234], output);
let mut output = [0_u16; 2];
let progress = transcoder
.transcode(&[0x12, 0x34, 0xab], 0, &mut output, 0)
.expect("decoding cannot fail");
assert_eq!(TranscodeStatus::NeedInput {
required: NonZeroUsize::new(2).expect("two is non-zero"),
}, progress.status());
assert_eq!(2, progress.read());
assert_eq!(1, progress.written());
assert_eq!([0x1234, 0], output);
assert!(matches!(
transcoder.transcode(&[0x12], 2, &mut output, 0),
Err(TranscodeDecodeError::Failure(
TranscodeFailure::InvalidInputIndex { .. }
)),
));
assert!(matches!(
transcoder.transcode(&[0x12], 0, &mut output, 3),
Err(TranscodeDecodeError::Failure(
TranscodeFailure::InvalidOutputIndex { .. }
)),
));The trait is intentionally independent from charset concepts. Implementors
use input_index and output_index as absolute positions in the supplied
slices. Returned progress counters are relative counts from those positions.
For raw codecs this gives a compact API; higher-level workflows can wrap
this trait with their own semantic policies.
Required Associated Types§
Sourcetype Error: From<TranscodeFailure>
type Error: From<TranscodeFailure>
Complete error type produced by this transcoder.
Required Methods§
Sourcefn max_transcode_output_len(
&self,
input_len: usize,
) -> Result<usize, CapacityError>
fn max_transcode_output_len( &self, input_len: usize, ) -> Result<usize, CapacityError>
Returns an upper bound for the output capacity needed to transcode
input_len units during the streaming phase.
This bound excludes stream-start output emitted by
Transcoder::reset and final output emitted by
Transcoder::finish. Callers that need a complete one-shot stream
bound should use Transcoder::max_total_output_len.
The bound covers both Transcoder::transcode and
Transcoder::transcode_eof, including any conservative capacity
preflight required by those operations. It must provide enough writable
capacity for the implementation to complete its preflight, even when
the actual output would use fewer units.
The bound may depend on immutable transcoder configuration and
input_len, but it must cover the streaming output reachable from every
transient stream state. In particular, it must include the maximum
retained output that may be emitted before or alongside output derived
from the supplied input, even when no output is currently retained.
§Parameters
input_len: Number of input units the caller plans to transcode.
§Returns
Returns Ok(bound) when the upper bound can be represented as usize.
§Errors
Returns CapacityError::OutputLengthOverflow when capacity arithmetic
overflows.
Sourcefn reset(
&mut self,
output: &mut [Self::Output],
output_index: usize,
) -> Result<usize, Self::Error>
fn reset( &mut self, output: &mut [Self::Output], output_index: usize, ) -> Result<usize, Self::Error>
Resets stream state and emits stream-start output into output.
This starts a new logical stream while keeping configuration such as
byte order, charset policy, replacement values, and cryptographic keys.
Pending input, pending output, and completed-stream state must be
discarded by stateful implementations. The caller must provide enough
output capacity for Transcoder::max_reset_output_len.
§Parameters
output: Complete output unit slice visible to the transcoder.output_index: Absolute output unit index where writing starts.
§Returns
Returns the number of units written while resetting stream state.
Stateless transcoders return 0. Implementations must not return a
count greater than Transcoder::max_reset_output_len or the output
units available from output_index.
§Errors
Returns contract errors (invalid_output_index, insufficient_output)
when capacity checks fail, or policy errors when reset itself fails.
Sourcefn transcode(
&mut self,
input: &[Self::Input],
input_index: usize,
output: &mut [Self::Output],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error>
fn transcode( &mut self, input: &[Self::Input], input_index: usize, output: &mut [Self::Output], output_index: usize, ) -> Result<TranscodeProgress, Self::Error>
Converts available input units into output units.
This method processes an input segment without closing the logical input
stream. When the current segment ends in a partial value, the transcoder
reports crate::TranscodeStatus::NeedInput without consuming that
tail. The caller owns input-buffer refill and EOF incomplete-tail
policy.
Returning crate::TranscodeStatus::Complete means all input visible
from input_index was consumed. Implementations must not return
Complete after consuming only a prefix of the supplied input; they
must instead continue processing, report NeedInput for an
incomplete tail, or report NeedOutput if output capacity prevents
progress. This invariant is checked by
crate::TranscodeProgress::validate.
§Parameters
input: Complete input unit slice visible to the transcoder.input_index: Absolute input unit index where conversion starts.output: Complete output unit slice visible to the transcoder.output_index: Absolute output unit index where writing starts.
§Returns
Returns progress describing how many units were consumed and produced
and why conversion stopped. Implementations must keep the returned
counters and status fields consistent with the supplied input and
output ranges. For Complete, read must equal input.len() - input_index. The default one-shot helper rejects incomplete Complete
progress and asserts the remaining progress contract in all builds;
streaming I/O drivers may convert validation failures before advancing
unsafe cursors.
§Errors
Returns framework failures for invalid indices, insufficient output, incomplete input, and capacity overflow. Returns domain errors for semantic conversion failures that the transcoder’s policy does not absorb. An error does not provide general transactional rollback or complete progress: the transcoder may already have advanced. Callers must not replay the original input range unless the concrete transcoder provides an explicit recovery protocol. Reset may begin a new logical stream but cannot undo output already delivered.
Sourcefn finish(
&mut self,
output: &mut [Self::Output],
output_index: usize,
) -> Result<usize, Self::Error>
fn finish( &mut self, output: &mut [Self::Output], output_index: usize, ) -> Result<usize, Self::Error>
Finishes internally retained output after all input has been supplied.
transcode handles ordinary input consumption. finish is called once
after the caller knows no more input remains and has handled any
incomplete input tail reported by transcode. It emits final output
derived from internal state, such as reset bytes, checksums, digests, or
trailers. The caller must provide enough output capacity for
Transcoder::max_finish_output_len.
After finish succeeds, the logical stream is closed. Portable callers
should call Transcoder::reset with a buffer sized by
Transcoder::max_reset_output_len before passing input for another
logical stream to the same instance.
§Examples
use core::convert::Infallible;
use core::num::NonZeroUsize;
use qubit_codec::CapacityError;
use qubit_codec::TranscodeDecodeError;
use qubit_codec::TranscodeFailure;
use qubit_codec::TranscodeProgress;
use qubit_codec::TranscodeStatus;
use qubit_codec::Transcoder;
#[derive(Default)]
struct ByteCopy;
impl Transcoder for ByteCopy {
type Input = u8;
type Output = u8;
type Error = TranscodeDecodeError<Infallible>;
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> {
TranscodeFailure::ensure_output_index(output.len(), output_index)?;
Ok(0)
}
fn transcode(
&mut self,
input: &[u8],
input_index: usize,
output: &mut [u8],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error> {
TranscodeFailure::ensure_transcode_indices(
input.len(),
input_index,
output.len(),
output_index,
)?;
let mut read = 0;
let mut written = 0;
while input_index + read < input.len() && output_index + written < output.len() {
output[output_index + written] = input[input_index + read];
read += 1;
written += 1;
}
if input_index + read == input.len() {
Ok(TranscodeProgress::complete(read, written))
} else {
let status = TranscodeStatus::NeedOutput {
required: NonZeroUsize::MIN,
};
Ok(TranscodeProgress::new(
status,
read,
written,
))
}
}
fn finish(
&mut self,
output: &mut [u8],
output_index: usize,
) -> Result<usize, Self::Error> {
TranscodeFailure::ensure_output_index(output.len(), output_index)?;
Ok(0)
}
}
let mut transcoder = ByteCopy::default();
let mut reset_output = [];
transcoder
.reset(&mut reset_output, 0)
.expect("stateless transcoder reset cannot fail");
let mut output = [1_u8; 1];
let progress = transcoder
.transcode(&[7], 0, &mut output, 0)
.expect("writer consumes one unit");
assert_eq!(TranscodeStatus::Complete, progress.status());
let error = transcoder.transcode(&[7], 2, &mut output, 0).unwrap_err();
assert!(matches!(
error.failure_ref(),
Some(TranscodeFailure::InvalidInputIndex { index: 2, input_len: 1 })
));
let error = transcoder.transcode(&[7], 0, &mut output, 2).unwrap_err();
assert!(matches!(
error.failure_ref(),
Some(TranscodeFailure::InvalidOutputIndex { index: 2, output_len: 1 })
));
assert_eq!([7], output);
let written = transcoder
.finish(&mut output, 1)
.expect("finish does not emit final state for no-op transcoders");
assert_eq!(0, written);§Parameters
output: Complete output unit slice visible to the transcoder.output_index: Absolute output unit index where writing starts.
§Returns
Returns the number of units written during finalization. Stateless
transcoders return 0. Implementations must not return a count greater
than Transcoder::max_finish_output_len or the output units available
from output_index.
§Errors
Returns contract errors (invalid_output_index, insufficient_output)
when capacity checks fail, or policy errors when finish itself
fails.
Provided Methods§
Sourcefn max_reset_output_len(&self) -> Result<usize, CapacityError>
fn max_reset_output_len(&self) -> Result<usize, CapacityError>
Returns an upper bound for output units emitted when resetting stream state.
Stateful encoders may need a stream-start sequence, such as a byte
order mark, before the first encoded value. Callers use this bound to
size the output buffer passed to Transcoder::reset.
The bound may depend on immutable transcoder configuration, but it must cover reset output from every reachable transient stream state. It must not shrink merely because the current stream has already been reset or finished.
§Returns
Returns Ok(bound) when the upper bound can be represented as usize.
Stateless transcoders default to Ok(0).
§Errors
Implementations return CapacityError::OutputLengthOverflow when
capacity arithmetic overflows. The default never fails.
Sourcefn max_total_output_len(&self, input_len: usize) -> Result<usize, CapacityError>
fn max_total_output_len(&self, input_len: usize) -> Result<usize, CapacityError>
Returns an upper bound for a complete reset -> transcode_eof -> finish
stream.
This is the one-shot bound for Transcoder::transcode_complete_into
and a convenience sum of Transcoder::max_reset_output_len,
Transcoder::max_transcode_output_len, and
Transcoder::max_finish_output_len. Each component is independent of
transient stream state, so the sum is valid before a full one-shot
stream regardless of how the previous logical stream ended.
§Parameters
input_len: Number of input units in the complete stream.
§Returns
Returns Ok(bound) when the full-stream upper bound can be represented
as usize.
§Errors
Returns CapacityError::OutputLengthOverflow when a component bound
or the sum of lifecycle bounds overflows.
Sourcefn max_finish_output_len(&self) -> Result<usize, CapacityError>
fn max_finish_output_len(&self) -> Result<usize, CapacityError>
Returns an upper bound for output units produced by stream finalization.
The bound may depend on immutable transcoder configuration, but it must
cover final output from every reachable transient stream state. For
example, an encoder that can emit one checksum byte must return at least
1 before input, while accumulating the checksum, and after finishing a
previous stream. It must not shrink merely because no finish output is
currently pending.
§Returns
Returns Ok(bound) when the upper bound can be represented as usize.
Stateless transcoders default to Ok(0).
§Errors
Implementations return CapacityError::OutputLengthOverflow when
capacity arithmetic overflows. The default never fails.
Sourcefn transcode_eof(
&mut self,
input: &[Self::Input],
input_index: usize,
output: &mut [Self::Output],
output_index: usize,
) -> Result<TranscodeProgress, Self::Error>
fn transcode_eof( &mut self, input: &[Self::Input], input_index: usize, output: &mut [Self::Output], output_index: usize, ) -> Result<TranscodeProgress, Self::Error>
Converts source units after the caller has established end of input.
The default preserves ordinary streaming behavior. Implementations that
can resolve a trailing prefix using EOF-aware format rules should
override it. If the default still reports
TranscodeStatus::NeedInput, this method returns
TranscodeFailure::IncompleteInput instead. The default validates
its input and output indices before delegating, then validates returned
progress before calculating the incomplete-input context. A broken
implementation therefore returns TranscodeFailure::InvalidProgress
rather than causing arithmetic overflow or advancing invalid cursors.
§Parameters
input: Complete input slice, including the final source segment.input_index: Absolute position where final decoding starts.output: Destination output slice.output_index: Absolute output position where writing starts.
§Returns
Validated complete or output-limited progress. EOF results should be
Complete, NeedOutput, or an error; the default never returns a
successful NeedInput status at EOF.
§Errors
Returns invalid-index errors before calling transcode, forwards its
errors, and rejects invalid progress or an incomplete final input tail.
It has the same non-transactional error boundary as Self::transcode:
the transcoder may have advanced, and callers must not replay the
original input range without an explicit recovery protocol. Reset only
begins a new logical stream; it cannot undo output already delivered.
Sourcefn transcode_complete_into(
&mut self,
input: &[Self::Input],
output: &mut [Self::Output],
) -> Result<usize, Self::Error>
fn transcode_complete_into( &mut self, input: &[Self::Input], output: &mut [Self::Output], ) -> Result<usize, Self::Error>
Runs a complete one-shot reset -> transcode -> finish stream.
The input slice is treated as complete input at EOF, and output is
written from the beginning of output. Callers that need to operate on
a range inside a larger buffer should slice the input or output
before calling this method.
Before invoking any lifecycle method, this method queries the
state-independent reset, transcode, and finish bounds and requires
output to provide at least the complete upper bound returned by
Transcoder::max_total_output_len for input.len() units.
§Parameters
input: Complete input unit slice.output: Complete output unit slice where the stream starts at index0.
§Returns
Returns the number of output units written to output.
§Errors
Returns framework errors when the output buffer is too small, when
capacity arithmetic overflows, or when the complete input ends with
an incomplete value. Capacity-bound overflow and insufficient
complete-stream capacity are reported before reset, transcode, or
finish is called, so those preflight failures do not write to
output or advance transient stream state through lifecycle methods.
Once reset begins, domain errors, incomplete input, runtime
backpressure caused by an invalid bound implementation, or contract
violations may leave partial output and advanced state; this method
does not provide general transactional rollback.
§Panics
Panics when reset or finish reports writing beyond its declared
bound or the available output, when an overridden transcode_eof
returns invalid progress, or when a returned write count cannot be
added to the output cursor. The default transcode_eof converts
invalid transcode progress into TranscodeFailure::InvalidProgress.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".