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::{
11 fmt,
12 num::NonZeroUsize,
13};
14use std::collections::TryReserveError;
15use std::io::{
16 Error,
17 ErrorKind,
18 Result,
19 Seek,
20 SeekFrom,
21 Write,
22};
23
24use qubit_io::{
25 Buffer,
26 BufferedOutput,
27 Output,
28 Seekable,
29 UncheckedSlice,
30};
31
32use crate::{
33 CapacityError,
34 Codec,
35 TranscodeEncodeError,
36 TranscodeEncodeErrorOf,
37 TranscodeEncoder,
38 TranscodeStatus,
39 Transcoder,
40 value::codec_value_lifecycle::{
41 encode_complete_value_into_reserved,
42 max_complete_encode_units,
43 },
44};
45
46use super::transcode_progress_validation::validate_encode_progress;
47
48/// Encodes an [`Output`] value stream into an [`Output`] unit stream.
49///
50/// This type owns only the unit-level [`qubit_io::BufferedOutput`]. Callers
51/// pass a [`crate::Codec`] and error mapper to each encode operation, which
52/// lets one buffered output drive different encoders without nesting buffers or
53/// storing codec-specific state in the buffer owner.
54///
55/// [`Self::flush`] only drains already buffered units. State-aware streaming
56/// encoders can use [`Self::reset`], [`Self::transcode`], and [`Self::finish`]
57/// explicitly.
58///
59/// # Type Parameters
60///
61/// * `O` - Wrapped unit output.
62pub struct TranscodeEncodeOutput<O>
63where
64 O: Output,
65 O::Item: Copy + Default,
66{
67 output: BufferedOutput<O>,
68}
69
70impl<O> TranscodeEncodeOutput<O>
71where
72 O: Output,
73 O::Item: Copy + Default,
74{
75 /// Creates an encoder output with the default unit buffer capacity.
76 ///
77 /// # Parameters
78 ///
79 /// * `inner` - Unit output written by this adapter.
80 ///
81 /// # Returns
82 ///
83 /// A new buffered encoder output.
84 #[must_use]
85 pub fn new(inner: O) -> Self {
86 Self {
87 output: BufferedOutput::new(inner),
88 }
89 }
90
91 /// Creates an encoder output with a unit buffer of at least `capacity`.
92 ///
93 /// # Parameters
94 ///
95 /// * `inner` - Unit output written by this adapter.
96 /// * `capacity` - Requested internal unit buffer capacity.
97 ///
98 /// # Returns
99 ///
100 /// A new buffered encoder output.
101 #[must_use]
102 pub fn with_capacity(inner: O, capacity: usize) -> Self {
103 Self {
104 output: BufferedOutput::with_capacity(inner, capacity),
105 }
106 }
107
108 /// Creates an encoder output with a unit buffer of at least `capacity`.
109 ///
110 /// # Parameters
111 ///
112 /// * `inner` - Unit output written by this adapter.
113 /// * `capacity` - Requested internal unit buffer capacity.
114 ///
115 /// # Errors
116 ///
117 /// Returns an allocation error when the requested buffer cannot be
118 /// allocated.
119 #[inline]
120 pub fn try_with_capacity(
121 inner: O,
122 capacity: usize,
123 ) -> std::result::Result<Self, TryReserveError> {
124 Ok(Self {
125 output: BufferedOutput::try_with_capacity(inner, capacity)?,
126 })
127 }
128
129 /// Returns a shared reference to the wrapped unit output.
130 ///
131 /// Pending units remain in this adapter's internal buffer and are not
132 /// visible through the returned output until [`Self::flush`] succeeds.
133 ///
134 /// # Returns
135 ///
136 /// A shared reference to the wrapped unit output.
137 #[must_use]
138 pub const fn inner(&self) -> &O {
139 self.output.inner()
140 }
141
142 /// Returns the available capacity of the spare output buffer.
143 ///
144 /// # Returns
145 ///
146 /// The number of output units that can still be appended without flushing.
147 #[must_use]
148 pub fn spare_capacity(&self) -> usize {
149 self.output.spare_capacity()
150 }
151
152 /// Returns raw spare-buffer parts for the internal output buffer.
153 ///
154 /// # Returns
155 ///
156 /// The full backing storage, the spare start index, and the spare unit
157 /// count.
158 #[must_use]
159 pub fn spare_raw_parts_mut(&mut self) -> (&mut [O::Item], usize, usize) {
160 self.output.spare_raw_parts_mut()
161 }
162
163 /// Marks `count` units from [`Self::spare_raw_parts_mut`] as written.
164 ///
165 /// # Safety
166 ///
167 /// The caller must guarantee that `count <= Self::spare_capacity()` and
168 /// that the corresponding units in the returned spare slice have been
169 /// initialized.
170 pub unsafe fn advance(&mut self, count: usize) {
171 // SAFETY: The caller guarantees `count` and initialization invariants.
172 unsafe { self.output.advance(count) }
173 }
174
175 /// Ensures that at least `count` spare units are available.
176 ///
177 /// # Parameters
178 ///
179 /// * `count` - Number of spare units required.
180 ///
181 /// # Errors
182 ///
183 /// Returns allocation errors mapped to [`ErrorKind::OutOfMemory`], or I/O
184 /// errors from the wrapped output while flushing pending units.
185 pub fn ensure_spare_capacity(&mut self, count: usize) -> Result<()> {
186 let pending = self.output.capacity() - self.output.spare_capacity();
187 let required_capacity = pending.saturating_add(count);
188 self.output
189 .try_reserve_capacity(required_capacity)
190 .map_err(allocation_to_io_error)?;
191 self.output.ensure_spare_capacity(count)
192 }
193
194 fn ensure_transcode_spare_capacity(
195 &mut self,
196 required: NonZeroUsize,
197 ) -> Result<()> {
198 self.ensure_spare_capacity(required.get())
199 }
200
201 /// Consumes this adapter without flushing the wrapped output.
202 ///
203 /// This method does not call [`Self::flush`] and performs no I/O. Pending
204 /// units remain in the returned buffer, which transfers their delivery
205 /// responsibility to the caller.
206 ///
207 /// # Returns
208 ///
209 /// The wrapped output and the buffer holding pending units.
210 #[must_use = "the returned output and pending buffer must be handled"]
211 pub fn into_parts(self) -> (O, Buffer<O::Item>) {
212 self.output.into_parts()
213 }
214
215 /// Flushes buffered units without finishing any encoder stream.
216 ///
217 /// # Errors
218 ///
219 /// Returns errors from the wrapped output while flushing pending units.
220 pub fn flush(&mut self) -> Result<()> {
221 self.output.flush()
222 }
223
224 /// Encodes one codec value into this buffered unit output.
225 ///
226 /// The method grows the persistent internal buffer when necessary, then
227 /// writes the complete encoded value into its spare window.
228 ///
229 /// # Parameters
230 ///
231 /// * `codec` - Codec used for this single-value encode.
232 /// * `value` - Value to encode.
233 /// * `map_error` - Mapper for codec-domain encode errors.
234 ///
235 /// # Errors
236 ///
237 /// Returns I/O errors from the wrapped output, `InvalidInput` when the
238 /// codec output bound overflows or the value is outside the codec domain,
239 /// or the error returned by `map_error` for codec encode, reset, and finish
240 /// failures.
241 ///
242 /// # Panics
243 ///
244 /// Panics when the codec violates its declared reset, value-width, or
245 /// finish bounds, or when `encode` writes a length different from
246 /// `encode_len` in the same reset state.
247 pub fn write_encoded_with<C, M>(
248 &mut self,
249 codec: &mut C,
250 value: &C::Value,
251 mut map_error: M,
252 ) -> Result<()>
253 where
254 C: Codec<Unit = O::Item>,
255 M: FnMut(C::EncodeError) -> Error,
256 {
257 let max_units = map_encode_value_result(
258 max_complete_encode_units::<C>()
259 .map_err(TranscodeEncodeErrorOf::<C>::from),
260 &mut map_error,
261 )?;
262 self.ensure_spare_capacity(max_units)?;
263 let (units, output_index, available) =
264 self.output.spare_raw_parts_mut();
265 debug_assert!(
266 available >= max_units,
267 "reserved spare buffer is smaller than codec upper bound",
268 );
269 let written = map_encode_value_result(
270 encode_complete_value_into_reserved(
271 codec,
272 value,
273 units,
274 output_index,
275 max_units,
276 ),
277 &mut map_error,
278 )?;
279 // SAFETY: The spare buffer has the conservative complete lifecycle
280 // capacity, and the helper reports how many initialized units it
281 // actually wrote.
282 unsafe {
283 self.output.advance(written);
284 }
285 Ok(())
286 }
287
288 /// Runs encoder reset and buffers any stream-prefix units.
289 ///
290 /// This method reserves enough persistent buffer capacity for all reset
291 /// output before calling `encoder`. It does not flush pending units.
292 ///
293 /// # Errors
294 ///
295 /// Returns capacity errors, allocation errors, or reset errors mapped by
296 /// `map_error`.
297 ///
298 /// # Panics
299 ///
300 /// Panics when `encoder` writes more than its declared reset bound.
301 pub fn reset<E, M, Value>(
302 &mut self,
303 encoder: &mut E,
304 map_error: &mut M,
305 ) -> Result<()>
306 where
307 E: Transcoder<Input = Value, Output = O::Item>,
308 M: FnMut(E::Error) -> Error,
309 {
310 let required = encoder
311 .max_reset_output_len()
312 .map_err(capacity_error_to_invalid_data)?;
313 self.ensure_spare_capacity(required)?;
314 let (units, output_index, available) =
315 self.output.spare_raw_parts_mut();
316 debug_assert!(
317 available >= required,
318 "insufficient reset capacity reserved in spare output buffer",
319 );
320 let written = encoder
321 .reset(units, output_index)
322 .map_err(&mut *map_error)?;
323 assert!(written <= required, "reset wrote beyond its bound");
324 // SAFETY: The encoder reported initialized units within the spare
325 // range reserved above.
326 unsafe {
327 self.output.advance(written);
328 }
329 Ok(())
330 }
331
332 /// Encodes values from an indexed input range using a streaming
333 /// [`Transcoder`].
334 ///
335 /// This streaming path writes directly into the internal spare buffer and
336 /// grows it for any larger `NeedOutput.required` value reported by
337 /// `encoder`.
338 ///
339 /// # Parameters
340 ///
341 /// * `encoder` - Streaming encoder used for this operation.
342 /// * `map_error` - Function mapping transcode errors into I/O errors.
343 /// * `input` - Source values.
344 /// * `input_index` - Start index inside `input`.
345 /// * `count` - Maximum number of values to encode.
346 ///
347 /// # Returns
348 ///
349 /// The number of source values consumed.
350 ///
351 /// # Errors
352 ///
353 /// Returns invalid input ranges, capacity, transcode, or output errors.
354 ///
355 /// # Examples
356 ///
357 /// A value that only implements [`Transcoder`] is not an encoder and cannot
358 /// be passed to this method.
359 ///
360 /// ```compile_fail
361 /// use qubit_codec::{
362 /// CapacityError, TranscodeEncodeError, TranscodeEncodeOutput,
363 /// TranscodeProgress, Transcoder,
364 /// };
365 ///
366 /// struct GenericTranscoder;
367 ///
368 /// impl Transcoder for GenericTranscoder {
369 /// type Input = u8;
370 /// type Output = u8;
371 /// type Error = TranscodeEncodeError<(), u8>;
372 ///
373 /// fn max_transcode_output_len(
374 /// &self,
375 /// input_len: usize,
376 /// ) -> Result<usize, CapacityError> {
377 /// Ok(input_len)
378 /// }
379 ///
380 /// fn reset(
381 /// &mut self,
382 /// _output: &mut [u8],
383 /// _output_index: usize,
384 /// ) -> Result<usize, Self::Error> {
385 /// Ok(0)
386 /// }
387 ///
388 /// fn transcode(
389 /// &mut self,
390 /// input: &[u8],
391 /// input_index: usize,
392 /// _output: &mut [u8],
393 /// _output_index: usize,
394 /// ) -> Result<TranscodeProgress, Self::Error> {
395 /// Ok(TranscodeProgress::complete(input.len() - input_index, 0))
396 /// }
397 ///
398 /// fn finish(
399 /// &mut self,
400 /// _output: &mut [u8],
401 /// _output_index: usize,
402 /// ) -> Result<usize, Self::Error> {
403 /// Ok(0)
404 /// }
405 /// }
406 ///
407 /// let mut output = TranscodeEncodeOutput::with_capacity(std::io::sink(), 1);
408 /// let mut transcoder = GenericTranscoder;
409 /// let mut map_error = |_| std::io::Error::other("transcode error");
410 /// let _ = output.transcode(
411 /// &mut transcoder,
412 /// &mut map_error,
413 /// &[0_u8],
414 /// 0,
415 /// 1,
416 /// );
417 /// ```
418 pub fn transcode<E, M, Value>(
419 &mut self,
420 encoder: &mut E,
421 map_error: &mut M,
422 input: &[Value],
423 input_index: usize,
424 count: usize,
425 ) -> Result<usize>
426 where
427 E: TranscodeEncoder<Input = Value, Output = O::Item>,
428 M: FnMut(E::Error) -> Error,
429 {
430 let input_end = UncheckedSlice::checked_range_end(
431 input.len(),
432 input_index,
433 count,
434 "encode input range exceeds source buffer",
435 )?;
436 if count == 0 {
437 return Ok(0);
438 }
439 let input = &input[..input_end];
440 let mut read_total = 0;
441 let mut required_spare = NonZeroUsize::MIN;
442 while read_total < count {
443 self.ensure_transcode_spare_capacity(required_spare)?;
444 let (units, output_index, available_output) =
445 self.output.spare_raw_parts_mut();
446 debug_assert!(
447 available_output >= required_spare.get(),
448 "reserved spare buffer is smaller than required encoder output",
449 );
450 let remaining_input = count - read_total;
451 let progress = encoder
452 .transcode(input, input_index + read_total, units, output_index)
453 .map_err(&mut *map_error)?;
454 let progress = validate_encode_progress(
455 progress,
456 input_index + read_total,
457 remaining_input,
458 output_index,
459 available_output,
460 )?;
461 let read = progress.read();
462 let written = progress.written();
463 // SAFETY: The progress bounds check above proved that the encoder
464 // initialized no more than the available spare output window.
465 unsafe {
466 self.output.advance(written);
467 }
468 read_total += read;
469 if progress.is_complete() {
470 return Ok(read_total);
471 }
472 if let TranscodeStatus::NeedOutput { required, .. } =
473 progress.status()
474 {
475 required_spare = required;
476 if read_total == count {
477 self.ensure_transcode_spare_capacity(required)?;
478 }
479 }
480 }
481 Ok(read_total)
482 }
483
484 /// Finishes the encoder and flushes the wrapped unit output.
485 ///
486 /// This method writes final units directly into the internal spare buffer,
487 /// growing it when [`Transcoder::max_finish_output_len`] exceeds the
488 /// current capacity.
489 ///
490 /// # Parameters
491 ///
492 /// * `encoder` - Encoder whose final units are being collected.
493 /// * `map_error` - Function mapping transcode errors into I/O errors.
494 ///
495 /// # Errors
496 ///
497 /// Returns capacity, transcode finalization, or wrapped output flush
498 /// errors.
499 pub fn finish<E, M, Value>(
500 &mut self,
501 encoder: &mut E,
502 map_error: &mut M,
503 ) -> Result<()>
504 where
505 E: Transcoder<Input = Value, Output = O::Item>,
506 M: FnMut(E::Error) -> Error,
507 {
508 self.finish_to_buffer(encoder, map_error)?;
509 self.output.flush()
510 }
511
512 /// Finishes the encoder while retaining final units in the output buffer.
513 ///
514 /// This method separates encoder finalization from output delivery. It is
515 /// useful when a caller must record successful finalization before a later
516 /// flush can fail. Call [`Self::flush`] to deliver the retained units.
517 ///
518 /// # Parameters
519 ///
520 /// * `encoder` - Encoder whose final units are being collected.
521 /// * `map_error` - Function mapping transcode errors into I/O errors.
522 ///
523 /// # Errors
524 ///
525 /// Returns capacity planning, allocation, or transcoder finalization
526 /// errors. It does not perform output I/O.
527 ///
528 /// # Panics
529 ///
530 /// Panics when `encoder` writes more units than its declared finish bound.
531 pub fn finish_to_buffer<E, M, Value>(
532 &mut self,
533 encoder: &mut E,
534 map_error: &mut M,
535 ) -> Result<()>
536 where
537 E: Transcoder<Input = Value, Output = O::Item>,
538 M: FnMut(E::Error) -> Error,
539 {
540 let required = match encoder.max_finish_output_len() {
541 Ok(required) => required,
542 Err(error) => return Err(capacity_error_to_invalid_data(error)),
543 };
544 self.ensure_spare_capacity(required)?;
545 let (units, output_index, available) =
546 self.output.spare_raw_parts_mut();
547 debug_assert!(
548 available >= required,
549 "insufficient finish capacity reserved in spare output buffer",
550 );
551 let written = encoder
552 .finish(units, output_index)
553 .map_err(&mut *map_error)?;
554 assert!(written <= required, "finish wrote beyond its bound");
555 // SAFETY: The encoder reported initialized units within the spare
556 // range that was reserved above.
557 unsafe {
558 self.output.advance(written);
559 }
560 Ok(())
561 }
562}
563
564impl<O> TranscodeEncodeOutput<O>
565where
566 O: Output<Item = u8> + Seekable<Unit = u8>,
567{
568 /// Flushes pending bytes, then seeks the wrapped byte output.
569 ///
570 /// # Parameters
571 ///
572 /// * `position` - Target seek position.
573 ///
574 /// # Returns
575 ///
576 /// The new stream position reported by the wrapped output.
577 ///
578 /// # Errors
579 ///
580 /// Returns flush or seek errors from the wrapped output.
581 pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
582 self.output.seek_to(position)
583 }
584}
585
586impl<O> Write for TranscodeEncodeOutput<O>
587where
588 O: Output<Item = u8>,
589{
590 /// Writes raw bytes through the internal buffer.
591 fn write(&mut self, input: &[u8]) -> Result<usize> {
592 Output::write(&mut self.output, input)
593 }
594
595 /// Writes all raw bytes through the internal buffer.
596 fn write_all(&mut self, input: &[u8]) -> Result<()> {
597 let mut written = 0;
598 while written < input.len() {
599 let count = Output::write(&mut self.output, &input[written..])?;
600 if count == 0 {
601 return Err(Error::from(ErrorKind::WriteZero));
602 }
603 assert!(
604 count <= input.len() - written,
605 "Output::write returned a count beyond the input length",
606 );
607 written += count;
608 }
609 Ok(())
610 }
611
612 /// Flushes buffered bytes to the wrapped output.
613 fn flush(&mut self) -> Result<()> {
614 TranscodeEncodeOutput::flush(self)
615 }
616}
617
618impl<O> Seek for TranscodeEncodeOutput<O>
619where
620 O: Output<Item = u8> + Seekable<Unit = u8>,
621{
622 /// Flushes pending bytes, then seeks the wrapped byte output.
623 fn seek(&mut self, position: SeekFrom) -> Result<u64> {
624 self.seek(position)
625 }
626}
627
628impl<O> fmt::Debug for TranscodeEncodeOutput<O>
629where
630 O: Output,
631 O::Item: Copy + Default,
632 BufferedOutput<O>: fmt::Debug,
633{
634 /// Formats this buffered encode output for debugging.
635 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
636 formatter
637 .debug_struct("TranscodeEncodeOutput")
638 .field("output", &self.output)
639 .finish()
640 }
641}
642
643/// Converts an allocation failure into an I/O boundary error.
644fn allocation_to_io_error(error: std::collections::TryReserveError) -> Error {
645 Error::new(ErrorKind::OutOfMemory, error)
646}
647
648/// Maps a streaming capacity error into an I/O error.
649fn capacity_error_to_invalid_data(error: CapacityError) -> Error {
650 Error::new(ErrorKind::InvalidData, error)
651}
652
653/// Maps a one-value codec result into the I/O surface used by this adapter.
654fn map_encode_value_result<T, E, Value>(
655 result: core::result::Result<T, TranscodeEncodeError<E, Value>>,
656 map_error: &mut dyn FnMut(E) -> Error,
657) -> Result<T> {
658 match result {
659 Ok(value) => Ok(value),
660 Err(error) => Err(map_encode_value_error(error, map_error)),
661 }
662}
663
664/// Maps one-value codec encode errors into the I/O surface used by this
665/// adapter.
666#[inline(never)]
667fn map_encode_value_error<E, Value>(
668 error: TranscodeEncodeError<E, Value>,
669 map_error: &mut dyn FnMut(E) -> Error,
670) -> Error {
671 match error {
672 TranscodeEncodeError::Domain(error) => map_error(error.into_source()),
673 TranscodeEncodeError::Unencodable { .. } => {
674 Error::new(ErrorKind::InvalidInput, "codec cannot encode value")
675 }
676 TranscodeEncodeError::Failure(_) => {
677 Error::new(ErrorKind::InvalidInput, "codec output bound overflow")
678 }
679 }
680}