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 UncheckedSlice,
26};
27
28use crate::{
29 TranscodeError,
30 TranscodeStatus,
31 Transcoder,
32};
33
34/// Decodes an [`Input`] unit stream into an [`Input`] value stream.
35///
36/// This type owns only the unit-level [`qubit_io::BufferedInput`]. Callers pass
37/// a streaming [`Transcoder`] and error mapper to each decode operation, which
38/// lets one buffered input drive different decoders without nesting buffers or
39/// storing decoder-specific state in the buffer owner.
40///
41/// # Type Parameters
42///
43/// * `I` - Wrapped unit input.
44pub struct TranscodeDecodeInput<I>
45where
46 I: Input,
47 I::Item: Copy + Default,
48{
49 input: BufferedInput<I>,
50}
51
52impl<I> TranscodeDecodeInput<I>
53where
54 I: Input,
55 I::Item: Copy + Default,
56{
57 /// Creates a decoder input with the default unit buffer capacity.
58 ///
59 /// # Parameters
60 ///
61 /// * `inner` - Unit input read by this adapter.
62 ///
63 /// # Returns
64 ///
65 /// A new buffered decoder input.
66 #[inline]
67 #[must_use]
68 pub fn new(inner: I) -> Self {
69 Self {
70 input: BufferedInput::new(inner),
71 }
72 }
73
74 /// Creates a decoder input with a unit buffer of at least `capacity`.
75 ///
76 /// # Parameters
77 ///
78 /// * `inner` - Unit input read by this adapter.
79 /// * `capacity` - Requested internal unit buffer capacity.
80 ///
81 /// # Returns
82 ///
83 /// A new buffered decoder input.
84 #[inline]
85 #[must_use]
86 pub fn with_capacity(inner: I, capacity: usize) -> Self {
87 Self {
88 input: BufferedInput::with_capacity(inner, capacity),
89 }
90 }
91
92 /// Returns a shared reference to the wrapped unit input.
93 ///
94 /// # Returns
95 ///
96 /// A shared reference to the wrapped unit input.
97 #[inline(always)]
98 #[must_use]
99 pub const fn inner(&self) -> &I {
100 self.input.inner()
101 }
102
103 /// Returns a mutable reference to the wrapped unit input.
104 ///
105 /// # Returns
106 ///
107 /// A mutable reference to the wrapped unit input.
108 #[inline(always)]
109 pub fn inner_mut(&mut self) -> &mut I {
110 self.input.inner_mut()
111 }
112
113 /// Returns the number of unread units currently buffered.
114 ///
115 /// # Returns
116 ///
117 /// The number of unread units in the internal buffer.
118 #[inline(always)]
119 #[must_use]
120 pub fn available(&self) -> usize {
121 self.input.available()
122 }
123
124 /// Returns the currently buffered unread units.
125 ///
126 /// # Returns
127 ///
128 /// Returns a shared slice over the unread portion of the internal unit
129 /// buffer. The slice is valid until this adapter is mutated.
130 #[inline(always)]
131 #[must_use]
132 pub fn unread(&self) -> &[I::Item] {
133 self.input.unread()
134 }
135
136 /// Returns the internal unit buffer capacity.
137 ///
138 /// # Returns
139 ///
140 /// The maximum number of units retained in the internal buffer.
141 #[inline(always)]
142 #[must_use]
143 pub fn capacity(&self) -> usize {
144 self.input.capacity()
145 }
146
147 /// Refills the internal buffer until at least `count` unread units are
148 /// available.
149 ///
150 /// # Parameters
151 ///
152 /// * `count` - Minimum number of unread units required.
153 ///
154 /// # Errors
155 ///
156 /// Returns I/O errors from the wrapped input while refilling.
157 #[inline(always)]
158 pub fn fill_until(&mut self, count: usize) -> std::io::Result<bool> {
159 self.input.fill_until(count)
160 }
161
162 /// Consumes unread units from the current buffer window.
163 ///
164 /// # Parameters
165 ///
166 /// * `count` - Number of unread units to discard.
167 ///
168 /// # Panics
169 ///
170 /// In debug builds, panics when `count` exceeds [`Self::available`].
171 #[inline(always)]
172 pub fn consume(&mut self, count: usize) {
173 debug_assert!(
174 count <= self.available(),
175 "cannot consume beyond buffered input",
176 );
177 // SAFETY: The caller-provided count is within the unread window.
178 unsafe {
179 self.input.consume(count);
180 }
181 }
182
183 /// Copies unread units into an indexed output range without consuming them.
184 ///
185 /// # Parameters
186 ///
187 /// * `output` - Destination storage that receives a copy of unread units.
188 /// * `output_index` - Start index inside `output`.
189 /// * `count` - Number of unread units to copy.
190 ///
191 /// # Safety
192 ///
193 /// The caller must guarantee that `output_index..output_index + count` is
194 /// a valid range inside `output`, that the addition does not overflow, that
195 /// `count <= self.available()`, and that the destination range does not
196 /// overlap with the unread units stored inside this buffer.
197 #[inline(always)]
198 pub unsafe fn copy_unread_to(
199 &mut self,
200 output: &mut [I::Item],
201 output_index: usize,
202 count: usize,
203 ) {
204 // SAFETY: The caller guarantees the destination range and non-overlap
205 // requirements for the unread copy.
206 let unread = self.input.unread();
207 debug_assert!(
208 UncheckedSlice::range_fits(unread.len(), 0, count),
209 "unchecked unread copy range exceeds unread source",
210 );
211 debug_assert!(
212 UncheckedSlice::range_fits(output.len(), output_index, count),
213 "unchecked copy destination range exceeds output buffer",
214 );
215 unsafe {
216 UncheckedSlice::copy_nonoverlapping(
217 unread,
218 0,
219 output,
220 output_index,
221 count,
222 );
223 }
224 }
225
226 /// Consumes this adapter and returns its parts.
227 ///
228 /// # Returns
229 ///
230 /// The wrapped input and the buffer holding unread units.
231 #[inline]
232 #[must_use]
233 pub fn into_parts(self) -> (I, Buffer<I::Item>) {
234 self.input.into_parts()
235 }
236
237 /// Reads buffered units into an indexed output range.
238 ///
239 /// # Parameters
240 ///
241 /// * `output` - Destination unit storage.
242 /// * `output_index` - Start index inside `output`.
243 /// * `count` - Maximum number of units to read.
244 ///
245 /// # Returns
246 ///
247 /// The number of units copied into `output`.
248 ///
249 /// # Errors
250 ///
251 /// Returns input or buffer validation errors from the wrapped
252 /// [`qubit_io::BufferedInput`].
253 ///
254 /// # Safety
255 ///
256 /// The caller must guarantee that `output_index..output_index + count` is
257 /// a valid range inside `output` and that the addition does not overflow.
258 #[inline(always)]
259 pub unsafe fn read_unchecked(
260 &mut self,
261 output: &mut [I::Item],
262 output_index: usize,
263 count: usize,
264 ) -> Result<usize> {
265 // SAFETY: The caller guarantees the destination range is valid.
266 unsafe { self.input.read_unchecked(output, output_index, count) }
267 }
268
269 /// Decodes values into an indexed output range using a streaming
270 /// [`Transcoder`].
271 ///
272 /// # Parameters
273 ///
274 /// * `decoder` - Streaming decoder used for this operation.
275 /// * `map_error` - Function mapping decoder errors into I/O errors.
276 /// * `output` - Destination value storage.
277 /// * `output_index` - Start index inside `output`.
278 /// * `count` - Maximum number of values to write.
279 ///
280 /// # Returns
281 ///
282 /// The number of values written. Incomplete EOF tails are left buffered
283 /// and reported as `Ok(written)`, so callers can apply their own EOF
284 /// policy.
285 ///
286 /// # Errors
287 ///
288 /// Returns input errors, invalid output ranges, capacity errors from the
289 /// internal buffer, or decoder errors mapped by `map_error`.
290 #[inline]
291 pub fn transcode_into<D, M, Value>(
292 &mut self,
293 decoder: &mut D,
294 map_error: &mut M,
295 output: &mut [Value],
296 output_index: usize,
297 count: usize,
298 ) -> Result<usize>
299 where
300 D: Transcoder<I::Item, Value>,
301 M: FnMut(TranscodeError<D::Error>) -> Error,
302 {
303 let output_end = UncheckedSlice::checked_range_end(
304 output.len(),
305 output_index,
306 count,
307 "decoded output range exceeds destination buffer",
308 )?;
309 if count == 0 {
310 return Ok(0);
311 }
312 let output = &mut output[..output_end];
313 let mut written_total = 0;
314 loop {
315 if self.input.available() == 0 && !self.input.fill_more()? {
316 return Ok(written_total);
317 }
318 let units = self.input.unread();
319 let available_input = units.len();
320 let remaining_output = count - written_total;
321 let progress = decoder
322 .transcode(units, 0, output, output_index + written_total)
323 .map_err(&mut *map_error)?;
324 progress
325 .validate(
326 0,
327 available_input,
328 output_index + written_total,
329 remaining_output,
330 )
331 .map_err(|error| Error::new(ErrorKind::InvalidData, error))?;
332 let consumed = progress.read();
333 let written = progress.written();
334 // SAFETY: TranscodeProgress::validate proved that the decoder
335 // consumed no more than the currently unread input window.
336 unsafe {
337 self.input.consume(consumed);
338 }
339 written_total += written;
340 match progress.status() {
341 TranscodeStatus::Complete => {
342 if written_total == count || consumed == 0 {
343 return Ok(written_total);
344 }
345 }
346 TranscodeStatus::NeedOutput { .. } => {
347 return Ok(written_total);
348 }
349 TranscodeStatus::NeedInput { required, .. } => {
350 if self.input.fill_until(required.get())? {
351 continue;
352 }
353 return Ok(written_total);
354 }
355 }
356 }
357 }
358
359 /// Finishes a streaming decoder into an indexed output range.
360 ///
361 /// # Parameters
362 ///
363 /// * `decoder` - Streaming decoder whose final output is being collected.
364 /// * `map_error` - Function mapping decoder errors into I/O errors.
365 /// * `output` - Destination value storage.
366 /// * `output_index` - Start index inside `output`.
367 /// * `count` - Maximum number of finish values to write.
368 ///
369 /// # Returns
370 ///
371 /// The number of values written by the decoder finish operation.
372 ///
373 /// # Errors
374 ///
375 /// Returns invalid output ranges, capacity errors, or decoder finalization
376 /// errors mapped to I/O errors.
377 #[inline]
378 pub fn finish_transcode_into<D, M, Value>(
379 &mut self,
380 decoder: &mut D,
381 map_error: &mut M,
382 output: &mut [Value],
383 output_index: usize,
384 count: usize,
385 ) -> Result<usize>
386 where
387 D: Transcoder<I::Item, Value>,
388 M: FnMut(TranscodeError<D::Error>) -> Error,
389 {
390 let required = decoder
391 .max_finish_output_len()
392 .map_err(capacity_to_io_error)?;
393 // Validate the caller-supplied count range first (InvalidInput).
394 let output_end = UncheckedSlice::checked_range_end(
395 output.len(),
396 output_index,
397 count,
398 "finish output range exceeds destination buffer",
399 )?;
400 // Then verify the finish bound fits in the available space
401 // (InvalidData).
402 let available = output.len().saturating_sub(output_index);
403 if available < required {
404 return Err(Error::new(
405 ErrorKind::InvalidData,
406 "insufficient output for decoder finish bound",
407 ));
408 }
409 let output_end = output_end.max(output_index + required);
410 let output = &mut output[..output_end];
411 let written = decoder
412 .finish(output, output_index)
413 .map_err(&mut *map_error)?;
414 debug_assert!(written <= required, "finish wrote beyond its bound");
415 Ok(written)
416 }
417}
418
419impl<I> TranscodeDecodeInput<I>
420where
421 I: Input<Item = u8> + Seekable<Item = u8>,
422{
423 /// Seeks the wrapped byte input and discards buffered bytes after success.
424 ///
425 /// # Parameters
426 ///
427 /// * `position` - Target seek position.
428 ///
429 /// # Returns
430 ///
431 /// The new stream position reported by the wrapped input.
432 ///
433 /// # Errors
434 ///
435 /// Returns seek errors from the wrapped input.
436 #[inline]
437 pub fn seek(&mut self, position: SeekFrom) -> Result<u64> {
438 self.input.seek_to(position)
439 }
440}
441
442impl<I> Read for TranscodeDecodeInput<I>
443where
444 I: Input<Item = u8>,
445{
446 /// Reads raw bytes through the internal buffer.
447 #[inline]
448 fn read(&mut self, output: &mut [u8]) -> Result<usize> {
449 // SAFETY: The full output slice is a valid destination range.
450 unsafe { self.input.read_unchecked(output, 0, output.len()) }
451 }
452}
453
454impl<I> Seek for TranscodeDecodeInput<I>
455where
456 I: Input<Item = u8> + Seekable<Item = u8>,
457{
458 /// Seeks the wrapped byte input and discards buffered bytes after success.
459 #[inline]
460 fn seek(&mut self, position: SeekFrom) -> Result<u64> {
461 self.seek(position)
462 }
463}
464
465impl<I> fmt::Debug for TranscodeDecodeInput<I>
466where
467 I: Input,
468 I::Item: Copy + Default,
469 BufferedInput<I>: fmt::Debug,
470{
471 /// Formats this buffered decode input for debugging.
472 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
473 formatter
474 .debug_struct("TranscodeDecodeInput")
475 .field("input", &self.input)
476 .finish()
477 }
478}
479
480/// Converts a capacity planning failure into an I/O error.
481fn capacity_to_io_error(error: crate::CapacityError) -> Error {
482 Error::new(ErrorKind::InvalidData, error)
483}