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