qubit_codec/transcode/io/transcode_encode_output.rs
1// =============================================================================
2// Copyright (c) 2026 Haixing Hu.
3//
4// SPDX-License-Identifier: Apache-2.0
5//
6// Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Buffered output driver that encodes values into units.
9
10use core::fmt;
11use std::io::{
12 Error,
13 ErrorKind,
14 Result,
15 Seek,
16 SeekFrom,
17 Write,
18};
19
20use qubit_io::{
21 Buffer,
22 BufferedOutput,
23 Output,
24 Seekable,
25 UncheckedSlice,
26};
27
28use crate::{
29 TranscodeError,
30 TranscodeStatus,
31 Transcoder,
32};
33
34/// Encodes an [`Output`] value stream into an [`Output`] unit stream.
35///
36/// This type owns only the unit-level [`qubit_io::BufferedOutput`]. Callers
37/// pass a [`crate::Codec`] and error mapper to each encode operation, which
38/// lets one buffered output drive different encoders without nesting buffers or
39/// storing codec-specific state in the buffer owner.
40///
41/// [`Self::flush`] only drains already buffered units. State-aware streaming
42/// encoders can use [`Self::transcode_from`] and [`Self::finish`] explicitly.
43///
44/// # Type Parameters
45///
46/// * `O` - Wrapped unit output.
47pub struct TranscodeEncodeOutput<O>
48where
49 O: Output,
50 O::Item: Copy + Default,
51{
52 output: BufferedOutput<O>,
53}
54
55impl<O> TranscodeEncodeOutput<O>
56where
57 O: Output,
58 O::Item: Copy + Default,
59{
60 /// Creates an encoder output with the default unit buffer capacity.
61 ///
62 /// # Parameters
63 ///
64 /// * `inner` - Unit output written by this adapter.
65 ///
66 /// # Returns
67 ///
68 /// A new buffered encoder output.
69 #[inline]
70 #[must_use]
71 pub fn new(inner: O) -> Self {
72 Self {
73 output: BufferedOutput::new(inner),
74 }
75 }
76
77 /// Creates an encoder output with a unit buffer of at least `capacity`.
78 ///
79 /// # Parameters
80 ///
81 /// * `inner` - Unit output written by this adapter.
82 /// * `capacity` - Requested internal unit buffer capacity.
83 ///
84 /// # Returns
85 ///
86 /// A new buffered encoder output.
87 #[inline]
88 #[must_use]
89 pub fn with_capacity(inner: O, capacity: usize) -> Self {
90 Self {
91 output: BufferedOutput::with_capacity(inner, capacity),
92 }
93 }
94
95 /// Returns a shared reference to the wrapped unit output.
96 ///
97 /// # Returns
98 ///
99 /// A shared reference to the wrapped unit output.
100 #[inline(always)]
101 #[must_use]
102 pub const fn inner(&self) -> &O {
103 self.output.inner()
104 }
105
106 /// Returns a mutable reference to the wrapped unit output.
107 ///
108 /// # Returns
109 ///
110 /// A mutable reference to the wrapped unit output.
111 #[inline(always)]
112 pub fn inner_mut(&mut self) -> &mut O {
113 self.output.inner_mut()
114 }
115
116 /// Returns the available capacity of the spare output buffer.
117 ///
118 /// # Returns
119 ///
120 /// The number of output units that can still be appended without flushing.
121 #[inline(always)]
122 #[must_use]
123 pub fn spare_capacity(&self) -> usize {
124 self.output.spare_capacity()
125 }
126
127 /// Returns raw spare-buffer parts for the internal output buffer.
128 ///
129 /// # Returns
130 ///
131 /// The full backing storage, the spare start index, and the spare unit
132 /// count.
133 #[inline(always)]
134 #[must_use]
135 pub fn spare_raw_parts_mut(&mut self) -> (&mut [O::Item], usize, usize) {
136 self.output.spare_raw_parts_mut()
137 }
138
139 /// Marks `count` units from [`Self::spare_raw_parts_mut`] as written.
140 ///
141 /// # Safety
142 ///
143 /// The caller must guarantee that `count <= Self::spare_capacity()` and
144 /// that the corresponding units in the returned spare slice have been
145 /// initialized.
146 #[inline(always)]
147 pub unsafe fn advance(&mut self, count: usize) {
148 // SAFETY: The caller guarantees `count` and initialization invariants.
149 unsafe { self.output.advance(count) }
150 }
151
152 /// Ensures that at least `count` spare units are available.
153 ///
154 /// # Parameters
155 ///
156 /// * `count` - Number of spare units required.
157 ///
158 /// # Errors
159 ///
160 /// Returns I/O errors from the wrapped output while flushing pending units.
161 #[inline(always)]
162 pub fn ensure_spare_capacity(&mut self, count: usize) -> Result<()> {
163 self.output.ensure_spare_capacity(count)
164 }
165
166 /// Consumes this adapter and returns its parts.
167 ///
168 /// # Returns
169 ///
170 /// The wrapped output and the buffer holding pending units.
171 #[inline]
172 #[must_use]
173 pub fn into_parts(self) -> (O, Buffer<O::Item>) {
174 self.output.into_parts()
175 }
176
177 /// Flushes buffered units without finishing any encoder stream.
178 ///
179 /// # Errors
180 ///
181 /// Returns errors from the wrapped output while flushing pending units.
182 #[inline]
183 pub fn flush(&mut self) -> Result<()> {
184 self.output.flush()
185 }
186
187 /// Encodes values from an indexed input range using a streaming
188 /// [`Transcoder`].
189 ///
190 /// # Parameters
191 ///
192 /// * `encoder` - Streaming encoder used for this operation.
193 /// * `map_error` - Function mapping encoder errors into I/O errors.
194 /// * `input` - Source values.
195 /// * `input_index` - Start index inside `input`.
196 /// * `count` - Maximum number of values to encode.
197 ///
198 /// # Returns
199 ///
200 /// The number of source values consumed.
201 ///
202 /// # Errors
203 ///
204 /// Returns invalid input ranges, capacity, encoder, or output errors.
205 #[inline]
206 pub fn transcode_from<E, M, Value>(
207 &mut self,
208 encoder: &mut E,
209 map_error: &mut M,
210 input: &[Value],
211 input_index: usize,
212 count: usize,
213 ) -> Result<usize>
214 where
215 E: Transcoder<Value, O::Item>,
216 M: FnMut(TranscodeError<E::Error>) -> Error,
217 {
218 let input_end = UncheckedSlice::checked_range_end(
219 input.len(),
220 input_index,
221 count,
222 "encode input range exceeds source buffer",
223 )?;
224 if count == 0 {
225 return Ok(0);
226 }
227 let input = &input[..input_end];
228 let mut read_total = 0;
229 while read_total < count {
230 // Each encoder step writes into the spare output window. When the
231 // buffer is full of pending units, spare capacity drops to zero and
232 // `transcode` cannot make progress. Reserving one spare slot drains
233 // pending units to the wrapped output only when needed.
234 self.output.ensure_spare_capacity(1)?;
235 let (units, output_index, available_output) =
236 self.output.spare_raw_parts_mut();
237 let remaining_input = count - read_total;
238 let progress = encoder
239 .transcode(input, input_index + read_total, units, output_index)
240 .map_err(&mut *map_error)?;
241 progress
242 .validate(
243 input_index + read_total,
244 remaining_input,
245 output_index,
246 available_output,
247 )
248 .map_err(|error| Error::new(ErrorKind::InvalidData, error))?;
249 let read = progress.read();
250 let written = progress.written();
251 // SAFETY: TranscodeProgress::validate proved that the encoder
252 // initialized no more than the available spare output window.
253 unsafe {
254 self.output.advance(written);
255 }
256 read_total += read;
257 match progress.status() {
258 TranscodeStatus::Complete => return Ok(read_total),
259 TranscodeStatus::NeedOutput { required, .. } => {
260 self.output.ensure_spare_capacity(required.get())?;
261 }
262 TranscodeStatus::NeedInput { .. } => {
263 return Err(Error::new(
264 ErrorKind::InvalidData,
265 "encoder unexpectedly requested more input",
266 ));
267 }
268 }
269 }
270 Ok(read_total)
271 }
272
273 /// Finishes the encoder and flushes the wrapped unit output.
274 ///
275 /// # Parameters
276 ///
277 /// * `encoder` - Encoder whose final units are being collected.
278 /// * `map_error` - Function mapping encoder errors into I/O errors.
279 ///
280 /// # Errors
281 ///
282 /// Returns capacity, encoder finalization, or wrapped output flush errors.
283 #[inline]
284 pub fn finish<E, M, Value>(
285 &mut self,
286 encoder: &mut E,
287 map_error: &mut M,
288 ) -> Result<()>
289 where
290 E: Transcoder<Value, O::Item>,
291 M: FnMut(TranscodeError<E::Error>) -> Error,
292 {
293 let required = encoder
294 .max_finish_output_len()
295 .map_err(|e| Error::new(ErrorKind::InvalidData, e))?;
296 self.output.ensure_spare_capacity(required)?;
297 let (units, output_index, available) =
298 self.output.spare_raw_parts_mut();
299 debug_assert!(
300 available >= required,
301 "insufficient finish capacity reserved in spare output buffer",
302 );
303 let written = encoder
304 .finish(units, output_index)
305 .map_err(&mut *map_error)?;
306 debug_assert!(written <= required, "finish wrote beyond its bound");
307 // SAFETY: The encoder reported initialized units within the spare
308 // range that was reserved above.
309 unsafe {
310 self.output.advance(written);
311 }
312 self.output.flush()
313 }
314}
315
316impl<O> TranscodeEncodeOutput<O>
317where
318 O: Output<Item = u8> + Seekable<Item = u8>,
319{
320 /// Flushes pending bytes, then seeks the wrapped byte output.
321 ///
322 /// # Parameters
323 ///
324 /// * `position` - Target seek position.
325 ///
326 /// # Returns
327 ///
328 /// The new stream position reported by the wrapped output.
329 ///
330 /// # Errors
331 ///
332 /// Returns flush or seek errors from the wrapped output.
333 #[inline]
334 pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
335 self.output.seek_to(position)
336 }
337}
338
339impl<O> Write for TranscodeEncodeOutput<O>
340where
341 O: Output<Item = u8>,
342{
343 /// Writes raw bytes through the internal buffer.
344 #[inline]
345 fn write(&mut self, input: &[u8]) -> Result<usize> {
346 Output::write(&mut self.output, input)
347 }
348
349 /// Writes all raw bytes through the internal buffer.
350 #[inline]
351 fn write_all(&mut self, input: &[u8]) -> Result<()> {
352 self.output.write_all(input)
353 }
354
355 /// Flushes buffered bytes to the wrapped output.
356 #[inline]
357 fn flush(&mut self) -> Result<()> {
358 TranscodeEncodeOutput::flush(self)
359 }
360}
361
362impl<O> Seek for TranscodeEncodeOutput<O>
363where
364 O: Output<Item = u8> + Seekable<Item = u8>,
365{
366 /// Flushes pending bytes, then seeks the wrapped byte output.
367 #[inline]
368 fn seek(&mut self, position: SeekFrom) -> Result<u64> {
369 self.seek(position)
370 }
371}
372
373impl<O> fmt::Debug for TranscodeEncodeOutput<O>
374where
375 O: Output,
376 O::Item: Copy + Default,
377 BufferedOutput<O>: fmt::Debug,
378{
379 /// Formats this buffered encode output for debugging.
380 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
381 formatter
382 .debug_struct("TranscodeEncodeOutput")
383 .field("output", &self.output)
384 .finish()
385 }
386}