qubit_codec/transcode/io/transcode_decode_input.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 input driver that decodes units into values.
9
10use core::fmt;
11use std::io::{
12 Error,
13 ErrorKind,
14 Read,
15 Result,
16 Seek,
17 SeekFrom,
18};
19
20use qubit_io::{
21 Buffer,
22 BufferedInput,
23 Input,
24 Seekable,
25};
26
27use crate::codec::assert_unit_bounds;
28use crate::{
29 Codec,
30 TranscodeError,
31 TranscodeStatus,
32 Transcoder,
33};
34
35/// Decodes an [`Input`] unit stream into an [`Input`] value stream.
36///
37/// This type owns only the unit-level [`qubit_io::BufferedInput`]. Callers pass
38/// a [`Codec`] and error mapper to each decode operation, which lets one
39/// buffered input drive different decoders without nesting buffers or storing
40/// codec-specific state in the buffer owner.
41///
42/// A [`Codec`] has no decoder-owned finish state. Callers that need a stateful
43/// streaming decoder should use [`Self::transcode_into`] and
44/// [`Self::finish_transcode_into`] instead.
45///
46/// # Type Parameters
47///
48/// * `I` - Wrapped unit input.
49pub struct TranscodeDecodeInput<I>
50where
51 I: Input,
52 I::Item: Copy + Default,
53{
54 input: BufferedInput<I>,
55}
56
57impl<I> TranscodeDecodeInput<I>
58where
59 I: Input,
60 I::Item: Copy + Default,
61{
62 /// Creates a decoder input with the default unit buffer capacity.
63 ///
64 /// # Parameters
65 ///
66 /// * `inner` - Unit input read by this adapter.
67 ///
68 /// # Returns
69 ///
70 /// A new buffered decoder input.
71 #[must_use]
72 #[inline]
73 pub fn new(inner: I) -> Self {
74 Self {
75 input: BufferedInput::new(inner),
76 }
77 }
78
79 /// Creates a decoder input with a unit buffer of at least `capacity`.
80 ///
81 /// # Parameters
82 ///
83 /// * `inner` - Unit input read by this adapter.
84 /// * `capacity` - Requested internal unit buffer capacity.
85 ///
86 /// # Returns
87 ///
88 /// A new buffered decoder input.
89 #[must_use]
90 #[inline]
91 pub fn with_capacity(inner: I, capacity: usize) -> Self {
92 Self {
93 input: BufferedInput::with_capacity(inner, capacity),
94 }
95 }
96
97 /// Returns a shared reference to the wrapped unit input.
98 ///
99 /// # Returns
100 ///
101 /// A shared reference to the wrapped unit input.
102 #[must_use]
103 #[inline(always)]
104 pub const fn inner(&self) -> &I {
105 self.input.inner()
106 }
107
108 /// Returns a mutable reference to the wrapped unit input.
109 ///
110 /// # Returns
111 ///
112 /// A mutable reference to the wrapped unit input.
113 #[inline(always)]
114 pub fn inner_mut(&mut self) -> &mut I {
115 self.input.inner_mut()
116 }
117
118 /// Returns the number of unread units currently buffered.
119 ///
120 /// # Returns
121 ///
122 /// The number of unread units in the internal buffer.
123 #[must_use]
124 #[inline(always)]
125 pub fn available(&self) -> usize {
126 self.input.available()
127 }
128
129 /// Returns the currently buffered unread units.
130 ///
131 /// # Returns
132 ///
133 /// Returns a shared slice over the unread portion of the internal unit
134 /// buffer. The slice is valid until this adapter is mutated.
135 #[must_use]
136 #[inline(always)]
137 pub fn unread(&self) -> &[I::Item] {
138 self.input.unread()
139 }
140
141 /// Returns the internal unit buffer capacity.
142 ///
143 /// # Returns
144 ///
145 /// The maximum number of units retained in the internal buffer.
146 #[must_use]
147 #[inline(always)]
148 pub fn capacity(&self) -> usize {
149 self.input.capacity()
150 }
151
152 /// Refills the internal buffer until at least `count` unread units are
153 /// available.
154 ///
155 /// # Parameters
156 ///
157 /// * `count` - Minimum number of unread units required.
158 ///
159 /// # Errors
160 ///
161 /// Returns I/O errors from the wrapped input while refilling.
162 #[inline(always)]
163 pub fn fill_until(&mut self, count: usize) -> std::io::Result<bool> {
164 self.input.fill_until(count)
165 }
166
167 /// Consumes unread units from the current buffer window.
168 ///
169 /// # Parameters
170 ///
171 /// * `count` - Number of unread units to discard.
172 ///
173 /// # Panics
174 ///
175 /// Panics when `count` exceeds [`Self::available`].
176 #[inline(always)]
177 pub fn consume(&mut self, count: usize) {
178 assert!(
179 count <= self.available(),
180 "cannot consume beyond buffered input",
181 );
182 // SAFETY: The assertion above validates the unread input range.
183 unsafe {
184 self.input.consume(count);
185 }
186 }
187
188 /// Copies unread units into an indexed output range without consuming them.
189 ///
190 /// # Parameters
191 ///
192 /// * `output` - Destination storage that receives a copy of unread units.
193 /// * `output_index` - Start index inside `output`.
194 /// * `count` - Number of unread units to copy.
195 ///
196 /// # Safety
197 ///
198 /// The caller must guarantee that `output_index..output_index + count` is
199 /// a valid range inside `output`, that the addition does not overflow, that
200 /// `count <= self.available()`, and that the destination range does not
201 /// overlap with the unread units stored inside this buffer.
202 #[inline(always)]
203 pub unsafe fn copy_unread_to(
204 &mut self,
205 output: &mut [I::Item],
206 output_index: usize,
207 count: usize,
208 ) {
209 // SAFETY: The caller guarantees the destination range and non-overlap
210 // requirements for the unread copy.
211 let unread = self.input.unread();
212 debug_assert!(
213 qubit_io::UncheckedSlice::range_fits(unread.len(), 0, count),
214 "unchecked unread copy range exceeds unread source",
215 );
216 debug_assert!(
217 qubit_io::UncheckedSlice::range_fits(
218 output.len(),
219 output_index,
220 count
221 ),
222 "unchecked copy destination range exceeds output buffer",
223 );
224 unsafe {
225 qubit_io::UncheckedSlice::copy_nonoverlapping(
226 unread,
227 0,
228 output,
229 output_index,
230 count,
231 );
232 }
233 }
234
235 /// Consumes this adapter and returns its parts.
236 ///
237 /// # Returns
238 ///
239 /// The wrapped input and the buffer holding unread units.
240 #[must_use]
241 #[inline]
242 pub fn into_parts(self) -> (I, Buffer<I::Item>) {
243 self.input.into_parts()
244 }
245
246 /// Reads buffered units into an indexed output range.
247 ///
248 /// # Parameters
249 ///
250 /// * `output` - Destination unit storage.
251 /// * `output_index` - Start index inside `output`.
252 /// * `count` - Maximum number of units to read.
253 ///
254 /// # Returns
255 ///
256 /// The number of units copied into `output`.
257 ///
258 /// # Errors
259 ///
260 /// Returns input or buffer validation errors from the wrapped
261 /// [`qubit_io::BufferedInput`].
262 ///
263 /// # Safety
264 ///
265 /// The caller must guarantee that `output_index..output_index + count` is
266 /// a valid range inside `output` and that the addition does not overflow.
267 #[inline(always)]
268 pub unsafe fn read_unchecked(
269 &mut self,
270 output: &mut [I::Item],
271 output_index: usize,
272 count: usize,
273 ) -> Result<usize> {
274 // SAFETY: The caller guarantees the destination range is valid.
275 unsafe { self.input.read_unchecked(output, output_index, count) }
276 }
277
278 /// Decodes values into an indexed output range using a [`Codec`].
279 ///
280 /// # Parameters
281 ///
282 /// * `decoder` - Codec used for this operation.
283 /// * `map_error` - Function mapping decoder errors into I/O errors.
284 /// * `output` - Destination value storage.
285 /// * `output_index` - Start index inside `output`.
286 /// * `count` - Maximum number of values to write.
287 ///
288 /// # Returns
289 ///
290 /// The number of values written. If EOF occurs before
291 /// [`Codec::min_units_per_value`] units are available for the next value,
292 /// the incomplete tail is left buffered and `Ok(written)` is returned.
293 ///
294 /// # Errors
295 ///
296 /// Returns input errors, buffer refill errors, or decoder errors mapped by
297 /// `map_error`.
298 ///
299 /// # Safety
300 ///
301 /// The caller must guarantee that `output_index..output_index + count` is
302 /// a valid range inside `output` and that the addition does not overflow.
303 pub unsafe fn decode_into<C, M>(
304 &mut self,
305 decoder: &mut C,
306 map_error: &mut M,
307 output: &mut [C::Value],
308 output_index: usize,
309 count: usize,
310 ) -> Result<usize>
311 where
312 C: Codec<Unit = I::Item>,
313 M: FnMut(C::DecodeError) -> Error,
314 {
315 debug_assert!(
316 qubit_io::UncheckedSlice::range_fits(
317 output.len(),
318 output_index,
319 count
320 ),
321 "unchecked decoded output range exceeds destination buffer",
322 );
323 if count == 0 {
324 return Ok(0);
325 }
326 assert_unit_bounds::<C>(decoder);
327 let min_units = decoder.min_units_per_value().get();
328 let max_units = decoder.max_units_per_value().get();
329 let mut written_total = 0;
330
331 while written_total < count {
332 if self.input.available() < min_units
333 && !self.input.fill_until(min_units)?
334 {
335 return Ok(written_total);
336 }
337
338 if self.input.available() < max_units
339 && max_units <= self.input.capacity()
340 {
341 let _ = self.input.fill_until(max_units)?;
342 }
343
344 let available = self.input.available();
345 let (value, consumed) = unsafe {
346 // SAFETY: The unread window contains at least
347 // `min_units_per_value` units from index zero.
348 decoder.decode(self.input.unread(), 0)
349 }
350 .map_err(&mut *map_error)?;
351 let consumed = consumed.get();
352 assert!(
353 consumed <= available,
354 "Codec::decode consumed beyond available input",
355 );
356 output[output_index + written_total] = value;
357 unsafe {
358 // SAFETY: The codec-reported consumed count was checked
359 // against the current unread input window.
360 self.input.consume(consumed);
361 }
362 written_total += 1;
363 }
364 Ok(written_total)
365 }
366
367 /// Decodes values into an indexed output range using a streaming
368 /// [`Transcoder`].
369 ///
370 /// # Parameters
371 ///
372 /// * `decoder` - Streaming decoder used for this operation.
373 /// * `map_error` - Function mapping decoder errors into I/O errors.
374 /// * `output` - Destination value storage.
375 /// * `output_index` - Start index inside `output`.
376 /// * `count` - Maximum number of values to write.
377 ///
378 /// # Returns
379 ///
380 /// The number of values written. Incomplete EOF tails are left buffered
381 /// and reported as `Ok(written)`, so callers can apply their own EOF
382 /// policy.
383 ///
384 /// # Errors
385 ///
386 /// Returns input errors, capacity errors from the internal buffer, or
387 /// decoder errors mapped by `map_error`.
388 ///
389 /// # Safety
390 ///
391 /// The caller must guarantee that `output_index..output_index + count` is
392 /// a valid range inside `output` and that the addition does not overflow.
393 pub unsafe fn transcode_into<D, M, Value>(
394 &mut self,
395 decoder: &mut D,
396 map_error: &mut M,
397 output: &mut [Value],
398 output_index: usize,
399 count: usize,
400 ) -> Result<usize>
401 where
402 D: Transcoder<I::Item, Value>,
403 M: FnMut(TranscodeError<D::Error>) -> Error,
404 {
405 debug_assert!(
406 qubit_io::UncheckedSlice::range_fits(
407 output.len(),
408 output_index,
409 count
410 ),
411 "unchecked decoded output range exceeds destination buffer",
412 );
413 if count == 0 {
414 return Ok(0);
415 }
416 let output_end = output_index + count;
417 let output = &mut output[..output_end];
418 let mut written_total = 0;
419 loop {
420 if self.input.available() == 0 && !self.input.fill_more()? {
421 return Ok(written_total);
422 }
423 let units = self.input.unread();
424 let available_input = units.len();
425 let remaining_output = count - written_total;
426 let progress = decoder
427 .transcode(units, 0, output, output_index + written_total)
428 .map_err(&mut *map_error)?;
429 let consumed = progress.read();
430 let written = progress.written();
431 if consumed > available_input {
432 return Err(Error::new(
433 ErrorKind::InvalidData,
434 "transcoder consumed beyond available input",
435 ));
436 }
437 if written > remaining_output {
438 return Err(Error::new(
439 ErrorKind::InvalidData,
440 "transcoder wrote beyond output range",
441 ));
442 }
443 // SAFETY: The decoder reported consumed units from the currently
444 // unread input window, and the count was validated above.
445 unsafe {
446 self.input.consume(consumed);
447 }
448 written_total += written;
449 match progress.status() {
450 TranscodeStatus::Complete => {
451 if written_total == count || consumed == 0 {
452 return Ok(written_total);
453 }
454 }
455 TranscodeStatus::NeedOutput {
456 output_index: status_output_index,
457 ..
458 } => {
459 if status_output_index != output_index + written_total {
460 return Err(Error::new(
461 ErrorKind::InvalidData,
462 "transcoder reported inconsistent NeedOutput index",
463 ));
464 }
465 return Ok(written_total);
466 }
467 TranscodeStatus::NeedInput {
468 input_index,
469 additional,
470 available,
471 ..
472 } => {
473 if input_index != consumed {
474 return Err(Error::new(
475 ErrorKind::InvalidData,
476 "transcoder reported inconsistent NeedInput index",
477 ));
478 }
479 let required = available
480 .checked_add(additional.get())
481 .ok_or_else(|| {
482 Error::new(
483 ErrorKind::InvalidData,
484 "transcoder input requirement overflowed",
485 )
486 })?;
487 if self.input.fill_until(required)? {
488 continue;
489 }
490 return Ok(written_total);
491 }
492 }
493 }
494 }
495
496 /// Finishes a streaming decoder into an indexed output range.
497 ///
498 /// # Parameters
499 ///
500 /// * `decoder` - Streaming decoder whose final output is being collected.
501 /// * `map_error` - Function mapping decoder errors into I/O errors.
502 /// * `output` - Destination value storage.
503 /// * `output_index` - Start index inside `output`.
504 /// * `count` - Maximum number of finish values to write.
505 ///
506 /// # Returns
507 ///
508 /// The number of values written by the decoder finish operation.
509 ///
510 /// # Errors
511 ///
512 /// Returns capacity or decoder finalization errors mapped to I/O errors.
513 ///
514 /// # Safety
515 ///
516 /// The caller must guarantee that `output_index..output_index + count` is
517 /// a valid range inside `output` and that the addition does not overflow.
518 pub unsafe fn finish_transcode_into<D, M, Value>(
519 &mut self,
520 decoder: &mut D,
521 map_error: &mut M,
522 output: &mut [Value],
523 output_index: usize,
524 count: usize,
525 ) -> Result<usize>
526 where
527 D: Transcoder<I::Item, Value>,
528 M: FnMut(TranscodeError<D::Error>) -> Error,
529 {
530 debug_assert!(
531 qubit_io::UncheckedSlice::range_fits(
532 output.len(),
533 output_index,
534 count
535 ),
536 "unchecked finish output range exceeds destination buffer",
537 );
538 let required = decoder
539 .max_finish_output_len()
540 .map_err(capacity_to_io_error)?;
541 TranscodeError::<core::convert::Infallible>::ensure_output_range(
542 output.len(),
543 output_index,
544 count,
545 required,
546 )
547 .map_err(transcode_contract_to_io_error)?;
548 let output_end = output_index + count;
549 let output = &mut output[..output_end];
550 let written = decoder
551 .finish(output, output_index)
552 .map_err(&mut *map_error)?;
553 assert!(written <= required, "finish wrote beyond its bound");
554 Ok(written)
555 }
556}
557
558impl<I> TranscodeDecodeInput<I>
559where
560 I: Input<Item = u8> + Seekable<Item = u8>,
561{
562 /// Seeks the wrapped byte input and discards buffered bytes after success.
563 ///
564 /// # Parameters
565 ///
566 /// * `position` - Target seek position.
567 ///
568 /// # Returns
569 ///
570 /// The new stream position reported by the wrapped input.
571 ///
572 /// # Errors
573 ///
574 /// Returns seek errors from the wrapped input.
575 #[inline]
576 pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
577 self.input.seek_to(position)
578 }
579}
580
581impl<I> Read for TranscodeDecodeInput<I>
582where
583 I: Input<Item = u8>,
584{
585 /// Reads raw bytes through the internal buffer.
586 #[inline]
587 fn read(&mut self, output: &mut [u8]) -> Result<usize> {
588 // SAFETY: The full output slice is a valid destination range.
589 unsafe { self.input.read_unchecked(output, 0, output.len()) }
590 }
591}
592
593impl<I> Seek for TranscodeDecodeInput<I>
594where
595 I: Input<Item = u8> + Seekable<Item = u8>,
596{
597 /// Seeks the wrapped byte input and discards buffered bytes after success.
598 #[inline]
599 fn seek(&mut self, position: SeekFrom) -> Result<u64> {
600 self.seek(position)
601 }
602}
603
604impl<I> fmt::Debug for TranscodeDecodeInput<I>
605where
606 I: Input,
607 I::Item: Copy + Default,
608 BufferedInput<I>: fmt::Debug,
609{
610 /// Formats this buffered decode input for debugging.
611 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
612 formatter
613 .debug_struct("TranscodeDecodeInput")
614 .field("input", &self.input)
615 .finish()
616 }
617}
618
619/// Converts a capacity planning failure into an I/O error.
620fn capacity_to_io_error(error: crate::CapacityError) -> Error {
621 Error::new(ErrorKind::InvalidData, error)
622}
623
624/// Converts a framework transcode contract failure into an I/O error.
625fn transcode_contract_to_io_error(
626 error: TranscodeError<core::convert::Infallible>,
627) -> Error {
628 Error::new(ErrorKind::InvalidData, error)
629}