Skip to main content

qubit_io/coder/
coder.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10use super::coder_progress::CoderProgress;
11
12/// Converts one sequence of code units into another sequence of code units.
13///
14/// `convert` is the main streaming API. It transforms a provided input segment and
15/// writes as much output as available buffer space allows, without automatically
16/// flushing internal pending state.
17///
18/// The method is suitable for:
19/// - pull-style consumers that call conversion repeatedly as buffers arrive;
20/// - bounded output sinks that need `NeedOutput` progress when capacity is hit;
21/// - stateless and stateful codecs that all return progress-oriented stopping
22///   reasons.
23///
24/// `Coder` is intentionally independent from any charset semantics:
25///
26/// - Use `Coder` directly for custom, policy-free unit transforms.
27/// - Use `Coder` when you want to own malformed/unmappable decisions at the call site.
28///
29/// # Example: streaming byte-to-word decoder
30///
31/// ```rust
32/// use qubit_io::{Coder, CoderProgress, CoderStatus};
33///
34/// #[derive(Default)]
35/// struct U16BeBytesDecoder;
36///
37/// impl Coder<u8, u16> for U16BeBytesDecoder {
38///     type Error = core::convert::Infallible;
39///
40///     fn max_output_len(&self, input_len: usize) -> Option<usize> {
41///         Some(input_len / 2)
42///     }
43///
44///     fn convert(
45///         &mut self,
46///         input: &[u8],
47///         input_index: usize,
48///         output: &mut [u16],
49///         output_index: usize,
50///     ) -> Result<CoderProgress, Self::Error> {
51///         let mut read = 0;
52///         let mut written = 0;
53///         while input_index + read + 1 < input.len() {
54///             if output_index + written == output.len() {
55///                 let status = CoderStatus::NeedOutput {
56///                     output_index: output_index + written,
57///                     required: 1,
58///                     available: 0,
59///                 };
60///                 return Ok(CoderProgress::new(status, read, written));
61///             }
62///             let high = input[input_index + read] as u16;
63///             let low = input[input_index + read + 1] as u16;
64///             output[output_index + written] = (high << 8) | low;
65///             read += 2;
66///             written += 1;
67///         }
68///         if input_index + read == input.len() {
69///             Ok(CoderProgress::complete(read, written))
70///         } else {
71///             let status = CoderStatus::NeedInput {
72///                 input_index: input_index + read,
73///                 required: 2,
74///                 available: input.len() - (input_index + read),
75///             };
76///             Ok(CoderProgress::new(status, read, written))
77///         }
78///     }
79/// }
80///
81/// let mut coder = U16BeBytesDecoder;
82/// let mut output = [0_u16; 1];
83/// let progress = coder
84///     .convert(&[0x12, 0x34, 0xab, 0xcd], 0, &mut output, 0)
85///     .expect("decoding cannot fail");
86/// assert_eq!(CoderStatus::NeedOutput {
87///     output_index: 1,
88///     required: 1,
89///     available: 0,
90/// }, progress.status());
91/// assert_eq!(2, progress.read());
92/// assert_eq!(1, progress.written());
93/// assert_eq!([0x1234], output);
94///
95/// let mut output = [0_u16; 2];
96/// let progress = coder
97///     .convert(&[0x12, 0x34, 0xab], 0, &mut output, 0)
98///     .expect("decoding cannot fail");
99/// assert_eq!(CoderStatus::NeedInput {
100///     input_index: 2,
101///     required: 2,
102///     available: 1,
103/// }, progress.status());
104/// assert_eq!(2, progress.read());
105/// assert_eq!(1, progress.written());
106/// assert_eq!([0x1234, 0], output);
107/// ```
108///
109/// The trait is intentionally independent from charset concepts. Implementors
110/// use `input_index` and `output_index` as absolute positions in the supplied
111/// slices. Returned progress counters are relative counts from those positions.
112/// For raw codecs this gives a compact API; higher-level workflows can wrap this
113/// trait with their own semantic policies.
114///
115/// # Type Parameters
116///
117/// - `Input`: Input unit type accepted by this coder.
118/// - `Output`: Output unit type produced by this coder.
119pub trait Coder<Input, Output> {
120    /// Error reported for semantic conversion failures.
121    type Error;
122
123    /// Returns an upper bound for output units produced from `input_len` units.
124    ///
125    /// # Parameters
126    ///
127    /// - `input_len`: Number of input units the caller plans to convert.
128    ///
129    /// # Returns
130    ///
131    /// Returns `Some(bound)` when the coder can provide a finite upper bound.
132    /// Returns `None` when the bound is not known.
133    #[must_use]
134    fn max_output_len(&self, input_len: usize) -> Option<usize>;
135
136    /// Resets state retained between conversion calls.
137    ///
138    /// Stateless coders may keep the default no-op implementation.
139    #[inline]
140    fn reset(&mut self) {}
141
142    /// Converts input units into output units.
143    ///
144    /// # Parameters
145    ///
146    /// - `input`: Complete input unit slice visible to the coder.
147    /// - `input_index`: Absolute input unit index where conversion starts.
148    /// - `output`: Complete output unit slice visible to the coder.
149    /// - `output_index`: Absolute output unit index where writing starts.
150    ///
151    /// # Returns
152    ///
153    /// Returns progress describing how many units were consumed and produced and
154    /// why conversion stopped.
155    ///
156    /// # Errors
157    ///
158    /// Returns `Self::Error` for semantic conversion failures that the coder's
159    /// policy does not absorb.
160    fn convert(
161        &mut self,
162        input: &[Input],
163        input_index: usize,
164        output: &mut [Output],
165        output_index: usize,
166    ) -> Result<CoderProgress, Self::Error>;
167
168    /// Flushes any buffered output after input conversion is complete.
169    ///
170    /// `convert` handles input consumption. `finish` is called only after all
171    /// source input has been provided and is used to flush buffered state
172    /// (for example, a pending decoded character).
173    ///
174    /// # Example
175    ///
176    /// ```rust
177    /// use qubit_io::{Coder, CoderStatus};
178    ///
179    /// #[derive(Default)]
180    /// struct ByteCopy;
181    ///
182    /// impl Coder<u8, u8> for ByteCopy {
183    ///     type Error = core::convert::Infallible;
184    ///
185    ///     fn max_output_len(&self, input_len: usize) -> Option<usize> {
186    ///         Some(input_len)
187    ///     }
188    ///
189    ///     fn convert(
190    ///         &mut self,
191    ///         input: &[u8],
192    ///         input_index: usize,
193    ///         output: &mut [u8],
194    ///         output_index: usize,
195    ///     ) -> Result<qubit_io::CoderProgress, Self::Error> {
196    ///         let mut read = 0;
197    ///         let mut written = 0;
198    ///         while input_index + read < input.len() && output_index + written < output.len() {
199    ///             output[output_index + written] = input[input_index + read];
200    ///             read += 1;
201    ///             written += 1;
202    ///         }
203    ///         if input_index + read == input.len() {
204    ///             Ok(qubit_io::CoderProgress::complete(read, written))
205    ///         } else {
206    ///             let status = qubit_io::CoderStatus::NeedOutput {
207    ///                 output_index: output_index + written,
208    ///                 required: 1,
209    ///                 available: output.len().saturating_sub(output_index + written),
210    ///             };
211    ///             Ok(qubit_io::CoderProgress::new(
212    ///                 status,
213    ///                 read,
214    ///                 written,
215    ///             ))
216    ///         }
217    ///     }
218    /// }
219    ///
220    /// let mut coder = ByteCopy;
221    /// let mut output = [1_u8; 1];
222    /// let progress = coder
223    ///     .convert(&[7], 0, &mut output, 0)
224    ///     .expect("writer consumes one unit");
225    /// assert_eq!(CoderStatus::Complete, progress.status());
226    ///
227    /// let finish = coder
228    ///     .finish(&mut output, 1)
229    ///     .expect("finish does not emit buffered state for no-op coders");
230    /// assert_eq!(CoderStatus::Complete, finish.status());
231    /// ```
232    ///
233    /// # Parameters
234    ///
235    /// - `output`: Complete output unit slice visible to the coder.
236    /// - `output_index`: Absolute output unit index where writing starts.
237    ///
238    /// # Returns
239    ///
240    /// Returns progress for units written during flushing. Stateless coders
241    /// return a completed progress value with zero counters.
242    ///
243    /// # Errors
244    ///
245    /// Returns `Self::Error` if pending state cannot be flushed according to the
246    /// coder's policy.
247    #[inline]
248    fn finish(&mut self, _output: &mut [Output], _output_index: usize) -> Result<CoderProgress, Self::Error> {
249        Ok(CoderProgress::complete(0, 0))
250    }
251}