qubit-codec 0.17.4

Core codec traits and buffer conversion primitives for Rust
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
// =============================================================================
//    Copyright (c) 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
//! Buffered asynchronous output driver for streaming transcoders.

use core::fmt;
use std::collections::TryReserveError;
use std::future::poll_fn;
use std::io::Error;
use std::io::Result;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;

use qubit_io::AsyncBufferedOutput;
use qubit_io::AsyncOutput;
use qubit_io::Buffer;
use qubit_utils::SliceRange;
use qubit_utils::allocation_error;

use super::internal::capacity_error_to_invalid_data;
use super::internal::validate_encode_progress;
use crate::TranscodeEncoder;
use crate::TranscodeProgress;
use crate::TranscodeStatus;
use crate::Transcoder;

/// Buffers asynchronous transcoder output while preserving pending units.
///
/// This adapter is the asynchronous counterpart to
/// [`super::TranscodeEncodeOutput`]. It owns the unit-level buffer, so a
/// future cancelled while the wrapped output is pending retains every unit
/// that the transcoder has already produced. Each transcode operation returns
/// after exactly one encoder invocation, so its progress is the explicit
/// source-consumption commit boundary.
///
/// Cancellation and pending-output recovery assume that wrapped I/O calls
/// obey their normal return contracts. This adapter does not track poisoning
/// or indeterminate delivery after an inner I/O panic. If such a panic is
/// caught, reconcile external side effects before retrying or replaying
/// pending units.
///
/// # Type Parameters
///
/// * `O` - Wrapped asynchronous unit output.
///
/// # Examples
///
/// ```
/// use core::convert::Infallible;
/// use core::num::NonZeroUsize;
/// use std::future::Future;
/// use std::io::Result;
/// use std::pin::{pin, Pin};
/// use std::task::{Context, Poll, Waker};
///
/// use qubit_codec::{AsyncTranscodeEncodeOutput, Codec, CodecTranscodeEncoder};
/// use qubit_codec::{DecodeFailure, TranscodeEncoder};
/// use qubit_io::AsyncOutput;
///
/// struct ByteCodec;
/// impl Codec for ByteCodec {
///     type Value = u8;
///     type Unit = u8;
///     type DecodeError = Infallible;
///     type EncodeError = Infallible;
///     const MIN_UNITS_PER_VALUE: usize = 1;
///     const MAX_DECODE_UNITS_PER_VALUE: usize = 1;
///     const MAX_ENCODE_UNITS_PER_VALUE: usize = 1;
///     unsafe fn decode(&mut self, input: &[u8], index: usize)
///         -> std::result::Result<(u8, NonZeroUsize), DecodeFailure<Infallible>> {
///         Ok((input[index], NonZeroUsize::MIN))
///     }
///     unsafe fn encode(&mut self, value: &u8, output: &mut [u8], index: usize)
///         -> std::result::Result<usize, Infallible> {
///         output[index] = *value;
///         Ok(1)
///     }
/// }
///
/// #[derive(Default)]
/// struct Sink { bytes: Vec<u8>, flushes: usize }
/// impl AsyncOutput for Sink {
///     type Item = u8;
///     unsafe fn poll_write_unchecked(
///         self: Pin<&mut Self>, _: &mut Context<'_>, units: &[u8], index: usize, count: usize,
///     ) -> Poll<Result<usize>> {
///         self.get_mut().bytes.extend_from_slice(&units[index..index + count]);
///         Poll::Ready(Ok(count))
///     }
///     fn poll_flush(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Result<()>> {
///         self.get_mut().flushes += 1;
///         Poll::Ready(Ok(()))
///     }
/// }
///
/// async fn run_example() -> Result<()> {
///     let mut encoder = CodecTranscodeEncoder::new(ByteCodec);
///     let mut output = AsyncTranscodeEncodeOutput::with_capacity(Sink::default(), 8);
///     let mut map_error = |_| std::io::Error::other("encode error");
///     output.reset_async(&mut encoder, &mut map_error).await?;
///     let values = [1_u8, 2, 3];
///     let mut offset = 0;
///     while offset < values.len() {
///         let progress = output.transcode_async(
///             &mut encoder, &mut map_error, &values, offset, values.len() - offset,
///         ).await?;
///         assert!(progress.read() > 0);
///         offset += progress.read();
///     }
///     output.finish_async(&mut encoder, &mut map_error).await?;
///     assert_eq!(output.pending_len(), values.len());
///     output.flush_async().await?;
///     let (sink, pending) = output.into_parts();
///     assert!(pending.is_empty());
///     assert_eq!(sink.bytes, vec![1, 2, 3]);
///     assert_eq!(sink.flushes, 1);
///     Ok(())
/// }
///
/// let mut future = pin!(run_example());
/// let mut context = Context::from_waker(Waker::noop());
/// match future.as_mut().poll(&mut context) {
///     Poll::Ready(result) => result.unwrap(),
///     Poll::Pending => panic!("the example sink always completes immediately"),
/// }
/// ```
///
/// `finish_async` commits final units to the adapter buffer. `flush_async`
/// delivers those units and flushes the wrapped sink.
#[must_use]
pub struct AsyncTranscodeEncodeOutput<O>
where
    O: AsyncOutput,
    O::Item: Clone + Default,
{
    /// Owned output buffer retaining units until the wrapped sink accepts them.
    output: AsyncBufferedOutput<O>,
    /// Minimum spare units requested by the previous encoder progress report.
    required_spare: usize,
}

impl<O> AsyncTranscodeEncodeOutput<O>
where
    O: AsyncOutput,
    O::Item: Clone + Default,
{
    /// Creates an asynchronous encoder output with the default unit capacity.
    ///
    /// # Parameters
    ///
    /// * `inner` - Unit output receiving transcoder data.
    ///
    /// # Returns
    ///
    /// Returns a buffered asynchronous output adapter.
    ///
    /// # Panics
    ///
    /// Panics if initializing the default buffer requires `O::Item::default()`
    /// or `O::Item::clone()` and either operation panics, or if the default
    /// backing length exceeds `Vec`'s supported capacity.
    pub fn new(inner: O) -> Self {
        Self {
            output: AsyncBufferedOutput::new(inner),
            required_spare: 1,
        }
    }

    /// Creates an asynchronous encoder output with at least `capacity` units.
    ///
    /// # Parameters
    ///
    /// * `inner` - Unit output receiving transcoder data.
    /// * `capacity` - Requested internal unit capacity.
    ///
    /// # Returns
    ///
    /// Returns a buffered asynchronous output adapter.
    ///
    /// # Panics
    ///
    /// Panics if initializing the buffer requires `O::Item::default()` or
    /// `O::Item::clone()` and either operation panics, or if the requested
    /// backing length exceeds `Vec`'s supported capacity.
    pub fn with_capacity(inner: O, capacity: usize) -> Self {
        Self {
            output: AsyncBufferedOutput::with_capacity(inner, capacity),
            required_spare: 1,
        }
    }

    /// Tries to create an adapter with at least `capacity` buffered units.
    ///
    /// # Parameters
    ///
    /// * `inner` - Output moved into the adapter, including on allocation
    ///   failure.
    /// * `capacity` - Minimum number of units to reserve.
    ///
    /// # Returns
    ///
    /// Returns an empty buffered adapter on successful allocation.
    ///
    /// # Errors
    ///
    /// Returns an allocation error when the internal unit buffer cannot be
    /// allocated.
    ///
    /// # Panics
    ///
    /// Panics if initializing the buffer requires `O::Item::default()` or
    /// `O::Item::clone()` and either operation panics.
    pub fn try_with_capacity(inner: O, capacity: usize) -> std::result::Result<Self, TryReserveError> {
        Ok(Self {
            output: AsyncBufferedOutput::try_with_capacity(inner, capacity)?,
            required_spare: 1,
        })
    }

    /// Returns the total internal unit-buffer capacity.
    ///
    /// # Returns
    ///
    /// The allocated unit capacity, including occupied and spare slots.
    #[must_use]
    #[inline]
    pub fn capacity(&self) -> usize {
        self.output.capacity()
    }

    /// Returns a shared reference to the wrapped asynchronous output.
    ///
    /// # Returns
    ///
    /// Returns the wrapped output. Units can still be pending in this adapter.
    #[must_use]
    #[inline]
    pub const fn inner(&self) -> &O {
        self.output.inner()
    }

    /// Returns a mutable reference to the wrapped asynchronous output.
    ///
    /// Pending adapter units are not automatically delivered before direct
    /// output operations. Call [`Self::flush_async`] first when ordering with
    /// previously transcoded data matters.
    ///
    /// # Returns
    ///
    /// Returns the wrapped output.
    #[must_use]
    #[inline]
    pub fn inner_mut(&mut self) -> &mut O {
        self.output.inner_mut()
    }

    /// Returns the number of units retained pending delivery.
    ///
    /// # Returns
    ///
    /// Returns the length of the pending unit window.
    #[must_use]
    #[inline]
    pub const fn pending_len(&self) -> usize {
        self.output.pending_len()
    }

    /// Consumes this adapter without flushing the wrapped output.
    ///
    /// This method performs no asynchronous I/O. The returned buffer holds
    /// units that must be delivered before the logical stream can continue.
    ///
    /// Recovery assumes that wrapped I/O calls obey their normal return
    /// contracts. If an inner I/O panic is caught, the pending units may
    /// overlap a prefix already delivered. The buffer does not track this
    /// indeterminate delivery; reconcile external side effects before
    /// retrying or replaying those units.
    ///
    /// # Returns
    ///
    /// Returns the wrapped output and its pending unit buffer, assuming
    /// wrapped I/O calls obey their normal return contracts.
    #[must_use = "the returned output and pending buffer must be handled"]
    #[inline]
    pub fn into_parts(self) -> (O, Buffer<O::Item>) {
        self.output.into_parts()
    }
}

impl<O> AsyncTranscodeEncodeOutput<O>
where
    O: AsyncOutput + Unpin,
    O::Item: Clone + Default + Unpin,
{
    /// Flushes pending transcoded units and the wrapped asynchronous output.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` after pending units are delivered and the sink is
    /// flushed.
    ///
    /// # Errors
    ///
    /// Returns output-delivery or flush errors from the wrapped output. A
    /// failed delivery retains every unit that was not accepted.
    pub async fn flush_async(&mut self) -> Result<()> {
        self.output.flush_async().await
    }

    /// Delivers pending transcoded units without flushing the wrapped output.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` when the pending buffer is empty.
    ///
    /// # Errors
    ///
    /// Returns output-delivery errors from the wrapped output. A failed
    /// delivery retains every unit that was not accepted.
    pub async fn drain_async(&mut self) -> Result<()> {
        let capacity = self.output.capacity();
        self.output.ensure_spare_capacity_async(capacity).await
    }

    /// Runs encoder reset and buffers its stream-prefix units.
    ///
    /// # Type Parameters
    ///
    /// * `E` - Encoder producing the wrapped output item type.
    /// * `M` - Error mapper invoked for encoder failures.
    /// * `Value` - Logical input value type accepted by the encoder.
    ///
    /// # Parameters
    ///
    /// * `encoder` - Streaming encoder whose lifecycle is being started.
    /// * `map_error` - Maps transcoder errors into I/O errors.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` with the reset prefix retained in the buffer.
    /// A successful reset discards the previous stream's spare-capacity hint.
    /// Previously committed pending units remain buffered in delivery order.
    ///
    /// # Errors
    ///
    /// Returns capacity planning, output delivery, allocation, or mapped reset
    /// errors. Pending units remain retained if asynchronous delivery fails.
    ///
    /// # Panics
    ///
    /// Panics when `encoder` writes more units than its declared reset bound.
    pub async fn reset_async<E, M, Value>(&mut self, encoder: &mut E, map_error: &mut M) -> Result<()>
    where
        E: Transcoder<Input = Value, Output = O::Item>,
        M: FnMut(E::Error) -> Error,
    {
        let required = encoder.max_reset_output_len().map_err(capacity_error_to_invalid_data)?;
        self.ensure_spare_capacity_async(required).await?;
        let (units, output_index, available) = self.output.spare_raw_parts_mut();
        debug_assert!(available >= required);
        let written = encoder.reset(units, output_index).map_err(&mut *map_error)?;
        assert!(written <= required, "reset wrote beyond its bound");
        // SAFETY: `written` is bounded by the reserved spare range above.
        unsafe {
            self.output.advance(written);
        }
        self.required_spare = 1;
        Ok(())
    }

    /// Polls one cancellation-safe encoder operation.
    ///
    /// The adapter may first deliver previously pending output to make a
    /// conservative spare range available. Once it invokes `encoder`, it
    /// advances the produced units and returns immediately without another
    /// poll. The returned [`TranscodeProgress`] is therefore the exact source
    /// range committed by this call.
    ///
    /// # Type Parameters
    ///
    /// * `E` - Encoder producing the wrapped output item type.
    /// * `M` - Error mapper invoked for encoder failures.
    /// * `Value` - Logical input value type accepted by the encoder.
    ///
    /// # Parameters
    ///
    /// * `cx` - Task context used by the sink to register readiness.
    /// * `encoder` - Streaming encoder used for the conversion.
    /// * `map_error` - Maps transcoder errors into I/O errors.
    /// * `input` - Source values.
    /// * `input_index` - First source value to transcode.
    /// * `count` - Number of source values available from `input_index`.
    ///
    /// # Returns
    ///
    /// Returns `Pending` while making output space, without consuming input,
    /// or `Ready` with one validated encoder progress report or an error.
    ///
    /// # Errors
    ///
    /// Returns invalid input ranges, output delivery, allocation, contract, or
    /// mapped transcoder errors. Pending units remain retained after a failed
    /// asynchronous delivery. An encoder error does not provide general
    /// transactional rollback or complete progress: the encoder may already
    /// have advanced, but units written by its failing call are not committed
    /// to this adapter's pending output. Do not replay the original input
    /// range unless the concrete encoder provides an explicit recovery
    /// protocol. Previously committed pending units follow the wrapped output's
    /// recovery contract; reset starts a new logical stream and does not undo
    /// output already delivered.
    pub fn poll_transcode<E, M, Value>(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
        encoder: &mut E,
        map_error: &mut M,
        input: &[Value],
        input_index: usize,
        count: usize,
    ) -> Poll<Result<TranscodeProgress>>
    where
        E: TranscodeEncoder<Input = Value, Output = O::Item>,
        M: FnMut(E::Error) -> Error,
    {
        let input_end = SliceRange::checked_range_end(
            input.len(),
            input_index,
            count,
            "encode input range exceeds source buffer",
        )?;
        if count == 0 {
            return Poll::Ready(Ok(TranscodeProgress::complete(0, 0)));
        }
        let input = &input[..input_end];
        let this = self.as_mut().get_mut();
        let per_value = match encoder.max_transcode_output_len(1) {
            Ok(required) => required.max(1),
            Err(error) => {
                return Poll::Ready(Err(capacity_error_to_invalid_data(error)));
            }
        };
        let required_spare = this.required_spare.max(per_value);
        if let Err(error) = this.output.try_reserve_capacity(required_spare) {
            return Poll::Ready(Err(allocation_error(error)));
        }
        match Pin::new(&mut this.output).poll_ensure_spare_capacity(cx, required_spare) {
            Poll::Ready(Ok(())) => {}
            Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
            Poll::Pending => return Poll::Pending,
        }
        let (units, output_index, available_output) = this.output.spare_raw_parts_mut();
        let progress = encoder
            .transcode(input, input_index, units, output_index)
            .map_err(&mut *map_error)
            .and_then(|progress| {
                validate_encode_progress(progress, input_index, count, output_index, available_output)
            });
        let progress = match progress {
            Ok(progress) => progress,
            Err(error) => return Poll::Ready(Err(error)),
        };
        // SAFETY: Progress validation proves the initialized output count fits
        // the spare window provided to the encoder.
        unsafe {
            this.output.advance(progress.written());
        }
        this.required_spare = if let TranscodeStatus::NeedOutput { required, .. } = progress.status() {
            required.get()
        } else {
            // `validate_encode_progress` rejects `NeedInput` before this
            // point.
            debug_assert!(progress.is_complete());
            1
        };
        Poll::Ready(Ok(progress))
    }

    /// Transcodes one cancellation-safe progress step into the retained buffer.
    ///
    /// This is the async wrapper for [`Self::poll_transcode`]. It returns after
    /// one encoder invocation; callers that need to consume a whole source
    /// range must advance the source index by [`TranscodeProgress::read`] and
    /// call it again.
    ///
    /// # Type Parameters
    ///
    /// * `E` - Encoder producing the wrapped output item type.
    /// * `M` - Error mapper invoked for encoder failures.
    /// * `Value` - Logical input value type accepted by the encoder.
    ///
    /// # Parameters
    ///
    /// * `encoder` - Encoder invoked once after output space is available.
    /// * `map_error` - Maps encoder errors to I/O errors.
    /// * `input` - Source values borrowed for this operation.
    /// * `input_index` - Starting source offset.
    /// * `count` - Number of source values available at that offset.
    ///
    /// # Returns
    ///
    /// One validated progress report describing committed source consumption.
    ///
    /// # Errors
    ///
    /// Returns range, capacity, allocation, delivery, contract or mapped
    /// encoder errors, with pending units retained as described by
    /// [`Self::poll_transcode`].
    pub async fn transcode_async<E, M, Value>(
        &mut self,
        encoder: &mut E,
        map_error: &mut M,
        input: &[Value],
        input_index: usize,
        count: usize,
    ) -> Result<TranscodeProgress>
    where
        E: TranscodeEncoder<Input = Value, Output = O::Item>,
        M: FnMut(E::Error) -> Error,
    {
        poll_fn(|cx| Pin::new(&mut *self).poll_transcode(cx, encoder, map_error, input, input_index, count)).await
    }

    /// Finishes the encoder and retains its final output for delivery.
    ///
    /// # Type Parameters
    ///
    /// * `E` - Encoder producing the wrapped output item type.
    /// * `M` - Error mapper invoked for encoder failures.
    /// * `Value` - Logical input value type accepted by the encoder.
    ///
    /// # Parameters
    ///
    /// * `encoder` - Streaming encoder whose lifecycle is being finished.
    /// * `map_error` - Maps transcoder errors into I/O errors.
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` after final units are committed to the buffer.
    ///
    /// # Errors
    ///
    /// Returns capacity planning, output delivery, allocation, or mapped finish
    /// errors. Call [`Self::flush_async`] after this method to deliver
    /// final units. Keeping finalization separate from delivery lets a
    /// caller record the completed lifecycle before any later suspension
    /// point.
    ///
    /// # Panics
    ///
    /// Panics when `encoder` writes more units than its declared finish bound.
    pub async fn finish_async<E, M, Value>(&mut self, encoder: &mut E, map_error: &mut M) -> Result<()>
    where
        E: Transcoder<Input = Value, Output = O::Item>,
        M: FnMut(E::Error) -> Error,
    {
        let required = encoder
            .max_finish_output_len()
            .map_err(capacity_error_to_invalid_data)?;
        self.ensure_spare_capacity_async(required).await?;
        let (units, output_index, available) = self.output.spare_raw_parts_mut();
        debug_assert!(available >= required);
        let written = encoder.finish(units, output_index).map_err(&mut *map_error)?;
        assert!(written <= required, "finish wrote beyond its bound");
        // SAFETY: `written` is bounded by the reserved spare range above.
        unsafe {
            self.output.advance(written);
        }
        Ok(())
    }

    /// Reserves enough total buffer capacity and makes `count` spare slots
    /// available for one transcoder operation. Pending units may be delivered
    /// before the codec operation, and an async poll can return `Pending` until
    /// the wrapped output accepts them.
    ///
    /// # Parameters
    ///
    /// * `count` - Minimum spare units needed after any pending delivery.
    ///
    /// # Returns
    ///
    /// `Ok(())` when the requested spare capacity is available.
    ///
    /// # Errors
    ///
    /// Returns allocation or wrapped output errors; undelivered units remain
    /// buffered.
    async fn ensure_spare_capacity_async(&mut self, count: usize) -> Result<()> {
        self.output.try_reserve_capacity(count).map_err(allocation_error)?;
        self.output.ensure_spare_capacity_async(count).await
    }
}

impl<O> fmt::Debug for AsyncTranscodeEncodeOutput<O>
where
    O: AsyncOutput,
    O::Item: Clone + Default,
    AsyncBufferedOutput<O>: fmt::Debug,
{
    /// Formats this asynchronous output adapter for debugging.
    ///
    /// # Parameters
    ///
    /// * `formatter` - Destination for the adapter and wrapped buffer
    ///   description.
    ///
    /// # Returns
    ///
    /// `Ok(())` after the description is written.
    ///
    /// # Errors
    ///
    /// Propagates errors reported by the formatter.
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("AsyncTranscodeEncodeOutput")
            .field("output", &self.output)
            .finish()
    }
}