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