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