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