qubit-codec 0.10.0

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
// =============================================================================
//    Copyright (c) 2026 Haixing Hu.
//
//    SPDX-License-Identifier: Apache-2.0
//
//    Licensed under the Apache License, Version 2.0.
// =============================================================================
//! Low-level value codec trait.

use core::num::NonZeroUsize;

use super::decode_failure::DecodeFailure;

/// Encodes and decodes one value or codec quantum against a unit buffer.
///
/// `Codec` is the lowest-level abstraction in the codec stack. It is intended
/// for hot paths that have already validated buffer capacity and want to avoid
/// constructing subslices for every value. Higher-level transcoders and
/// convenience APIs are responsible for checked buffer management and owned
/// output allocation.
///
/// `MIN_UNITS_PER_VALUE` and `MAX_DECODE_UNITS_PER_VALUE` describe the readable
/// representation-width bounds for one decoded value. If fewer than the
/// minimum units are available, no complete value can exist, so a streaming
/// caller can request more input or report an incomplete EOF tail. The minimum
/// is also the smallest safety precondition checked callers must satisfy before
/// entering [`decode`](Self::decode). The decode maximum bounds successful
/// consumption and incomplete-input requirements for one value.
///
/// `MAX_ENCODE_UNITS_PER_VALUE` is the independent value-agnostic upper bound
/// for main-phase encode output. Reserving that many units makes encoding safe
/// for every encodable value. Once a value and encode state are known, callers
/// may instead reserve the exact [`encode_len`](Self::encode_len), which must
/// not exceed the encode maximum. Complete-lifecycle adapters that still need
/// to run [`encode_reset`](Self::encode_reset) must reserve the maximum first
/// because reset may change the exact width.
///
/// A codec may keep decode-side and encode-side stream state. That state is an
/// implementation detail owned by the codec. Callers do not snapshot or restore
/// it; implementations must keep their own state internally consistent across
/// every public operation. A failed decode attempt must not advance decode
/// state, so callers can retry the same input after refilling their buffer or
/// applying an invalid-input policy.
///
/// Decode operations see only the currently supplied input slice and codec
/// state. [`decode`](Self::decode) treats that slice as an open stream segment;
/// [`decode_eof`](Self::decode_eof) receives the same preconditions after the
/// caller has established that no more units will arrive. Returning
/// [`DecodeFailure::Incomplete`] from `decode` requests more input for the
/// current value; it is not itself an EOF error. Codecs that need EOF-aware
/// maximal-munch parsing or reinterpretation of a trailing prefix can override
/// `decode_eof` without changing their streaming behavior.
/// An incomplete failure may also retain codec-specific context through
/// [`DecodeFailure::incomplete_with_source`]; stream drivers may ignore that
/// context while retrying and use it for detailed EOF diagnostics.
///
/// # Associated Types
///
/// - `Value`: Logical value decoded from or encoded into the buffer. This may
///   be a scalar such as `u8`, `u16`, `u64`, a `char`, a fixed quantum such as
///   `[u8; 3]`, or an owned value such as `String`/`Vec<u8>`. Adapters that
///   need scratch initialization add their own bounds at the use site.
/// - `Unit`: Buffer unit used by the encoded representation. Implementations
///   are typically scalar storage units such as `u8`, `u16`, or `char`.
///   Adapters that allocate owned output add their own initialization bounds at
///   the use site.
///
/// Implementors must uphold the safety contract documented by
/// [`decode`](Self::decode), [`encode`](Self::encode),
/// [`encode_reset`](Self::encode_reset), and
/// [`decode_finish`](Self::decode_finish). Unchecked implementations must not
/// read or write outside the caller-provided ranges. Implementations should use
/// `debug_assert!` to state the expected buffer bounds at the unchecked entry
/// point.
///
/// Implementations must also guarantee that
/// [`MIN_UNITS_PER_VALUE`](Self::MIN_UNITS_PER_VALUE) is non-zero and no larger
/// than [`MAX_DECODE_UNITS_PER_VALUE`](Self::MAX_DECODE_UNITS_PER_VALUE), which
/// must also be non-zero. `MAX_ENCODE_UNITS_PER_VALUE` may be zero for a codec
/// that always buffers each accepted value instead of immediately emitting
/// units. Checked adapters enforce the decode-bound invariants with
/// compile-time assertions before using codec-provided bounds.
///
/// # Why the low-level methods are unchecked
///
/// The `unsafe` entry points are intentional: checked adapters validate the
/// index and capacity preconditions once, then call [`encode`](Self::encode)
/// and [`decode`](Self::decode) without rebuilding subslices or repeating
/// bounds checks for every value. The `safe_vs_unchecked` benchmark in
/// `benches/transcode.rs` compares the same 64 KiB copy loop in release mode.
/// Its result is workload- and toolchain-specific, so callers should use
/// checked adapters unless they can establish the documented preconditions at
/// a trusted boundary. Measure representative codec workloads before choosing
/// an unchecked entry point. Codec implementations should keep the unchecked
/// methods small and use `debug_assert!` for those assumptions.
pub trait Codec {
    /// The type of logical values decoded from or encoded into the buffer.
    type Value;

    /// The type of buffer units used by the encoded representation.
    type Unit;

    /// The type of errors reported when decoding malformed units.
    type DecodeError;

    /// The type of errors reported when encoding an unsupported value.
    type EncodeError;

    /// The minimum possible non-zero readable unit count for one decoded value.
    ///
    /// This is a lower bound used by checked callers for planning and fast
    /// impossibility checks. If a streaming decoder has fewer than this many
    /// readable units, no complete value can be present at the current
    /// position. It is also the denominator used by default decode hooks when
    /// estimating the maximum number of values that can be produced from an
    /// input unit count.
    const MIN_UNITS_PER_VALUE: usize;

    /// The maximum unit count emitted when encoding one value.
    ///
    /// This is a value-independent upper bound for the main encode phase. When
    /// no concrete value is available, reserving this many writable units is
    /// sufficient for any encodable value, without first calling
    /// [`encode_len`](Self::encode_len). For a known value, callers may reserve
    /// only the exact length returned by `encode_len` instead. This bound does
    /// not include output produced by encode reset or finish phases.
    ///
    /// This is also the multiplier used by default encode hooks when estimating
    /// the maximum number of output units needed for an input value count.
    ///
    /// The bound may be zero for a codec that buffers every accepted value and
    /// emits retained output only during a later lifecycle phase.
    const MAX_ENCODE_UNITS_PER_VALUE: usize;

    /// The maximum non-zero unit count consumed to decode one value.
    ///
    /// This is a value-independent upper bound for one complete encoded value
    /// or codec quantum on the decode side. Successful decode consumption and
    /// the `required_total` carried by [`DecodeFailure::Incomplete`] must not
    /// exceed this bound.
    const MAX_DECODE_UNITS_PER_VALUE: usize;

    /// The maximum unit count emitted when resetting encode state.
    ///
    /// This bound covers only output written by
    /// [`encode_reset`](Self::encode_reset). It does not include hook-owned
    /// reset output or any ordinary encoded input values. It must cover every
    /// reachable pre-reset encode state. Stateless codecs should use the
    /// default `0`.
    const MAX_ENCODE_RESET_UNITS: usize = 0;

    /// The maximum unit count emitted when finishing encode state at EOF.
    ///
    /// This bound covers only output written by
    /// [`encode_finish`](Self::encode_finish). It does not include hook-owned
    /// finish output and must cover every reachable encode state, even when the
    /// current stream has no pending finish output. Stateless codecs should use
    /// the default `0`. Codecs that emit a stream trailer (padding, checksum,
    /// or end-of-stream marker) should override this with the exact maximum
    /// unit count.
    const MAX_ENCODE_FINISH_UNITS: usize = 0;

    /// The maximum value count emitted when resetting decode state.
    ///
    /// This bound covers only values written by
    /// [`decode_reset`](Self::decode_reset). It does not include hook-owned
    /// reset output or values decoded from source units. It must cover every
    /// reachable pre-reset decode state. Stateless codecs should use the
    /// default `0`. Codecs that emit a stream-start sentinel or BOM on reset
    /// should override this.
    const MAX_DECODE_RESET_VALUES: usize = 0;

    /// The maximum value count emitted when finishing decode state.
    ///
    /// This bound covers only values written by
    /// [`decode_finish`](Self::decode_finish). It does not include hook-owned
    /// finish output and must cover every reachable decode state, even when the
    /// current stream has no pending finish output. Stateless codecs should use
    /// the default `0`.
    const MAX_DECODE_FINISH_VALUES: usize = 0;

    /// Returns whether `value` is in this codec's encodable value domain.
    ///
    /// The default implementation returns `true`, which is correct for codecs
    /// whose [`Value`](Self::Value) type contains only values they can encode.
    /// Codecs whose logical value type is broader than their representation
    /// domain, such as an ASCII codec with `Value = char`, must override this
    /// method.
    ///
    /// Checked encoder adapters call this method in the same codec state used
    /// to query [`encode_len`](Self::encode_len) and enter the unsafe
    /// [`encode`](Self::encode) method. Complete-lifecycle adapters do so after
    /// [`encode_reset`](Self::encode_reset). Direct unsafe callers must uphold
    /// the same ordering.
    ///
    /// # Parameters
    ///
    /// - `value`: Value whose encodability is queried.
    ///
    /// # Returns
    ///
    /// Returns `true` when `value` may be passed to
    /// [`encode_len`](Self::encode_len) and [`encode`](Self::encode).
    #[inline(always)]
    #[must_use]
    fn can_encode_value(&self, _value: &Self::Value) -> bool {
        true
    }

    /// Returns the exact unit count this codec will write when encoding
    /// `value`.
    ///
    /// The default implementation returns
    /// [`MAX_ENCODE_UNITS_PER_VALUE`](Self::MAX_ENCODE_UNITS_PER_VALUE). This
    /// default is correct only when every successful main encode writes exactly
    /// that many units in the relevant codec state, as fixed-width codecs
    /// normally do.
    ///
    /// Variable-width, buffering, or otherwise state-dependent codecs must
    /// override this method to report the true encoded length for encodable
    /// `value`s. The return value is never an estimate or an "unknown" marker:
    /// it must equal the unit count [`encode`](Self::encode) subsequently
    /// writes for the same `value` under the same codec state. It must also
    /// never exceed
    /// [`MAX_ENCODE_UNITS_PER_VALUE`](Self::MAX_ENCODE_UNITS_PER_VALUE).
    /// Callers that do not yet have a concrete value should use
    /// `MAX_ENCODE_UNITS_PER_VALUE` directly instead of calling this method.
    ///
    /// Default codec-backed encoders use this exact value for per-value output
    /// capacity. The contract requires callers to use this method only when
    /// [`can_encode_value`](Self::can_encode_value) returned `true` for the
    /// same `value`.
    ///
    /// # Parameters
    ///
    /// - `value`: Value whose encoded length is queried.
    ///
    /// # Returns
    ///
    /// Returns the unit count [`encode`](Self::encode) will write for an
    /// encodable `value`.
    ///
    /// A return value of `0` is valid for stateful encoders that accept a
    /// value into internal state without immediately emitting units. Examples
    /// include codecs that aggregate values into fixed-size quanta, shift-state
    /// encodings, or framing layers that defer output until enough values have
    /// been seen or EOF is finalized. Stateless and directly value-to-unit
    /// codecs should continue returning a positive length.
    #[inline(always)]
    #[must_use]
    fn encode_len(&self, _value: &Self::Value) -> usize {
        Self::MAX_ENCODE_UNITS_PER_VALUE
    }

    /// Emits stream-start output and resets encode-side state.
    ///
    /// # Parameters
    ///
    /// - `output`: Destination unit buffer.
    /// - `output_index`: Start index in `output`.
    ///
    /// # Returns
    ///
    /// Returns the number of reset units written.
    ///
    /// # Errors
    ///
    /// Returns `Self::EncodeError` when reset output cannot be emitted.
    /// Implementations must leave encode state unchanged when returning an
    /// error.
    ///
    /// # Safety
    ///
    /// The caller must guarantee that the implementation can write up to
    /// [`MAX_ENCODE_RESET_UNITS`](Self::MAX_ENCODE_RESET_UNITS) units starting
    /// at `output_index`.
    #[inline(always)]
    #[must_use = "reset output and reset errors must be handled"]
    unsafe fn encode_reset(
        &mut self,
        _output: &mut [Self::Unit],
        _output_index: usize,
    ) -> Result<usize, Self::EncodeError> {
        Ok(0)
    }

    /// Encodes one borrowed value into `output` starting at `output_index`.
    ///
    /// # Parameters
    ///
    /// - `value`: Value to encode.
    /// - `output`: Destination unit buffer.
    /// - `output_index`: Start index in `output`.
    ///
    /// # Returns
    ///
    /// Returns the number of written units.
    ///
    /// A successful encode may return `0` when the codec accepted `value` into
    /// internal encode-side state but intentionally deferred output. This is
    /// the normal shape for accumulative encoders such as base64-byte
    /// aggregators, shift-state encodings, or frame builders. In that case
    /// [`encode_len`](Self::encode_len) for the same `value` and state must
    /// also return `0`, and the retained output must be emitted by a later
    /// successful [`encode`](Self::encode),
    /// [`encode_finish`](Self::encode_finish), or codec-specific facade.
    ///
    /// # Errors
    ///
    /// Returns `Self::EncodeError` for encode-side state or representation
    /// failures other than a value being outside the codec's encodable domain.
    /// Checked callers reject values for which
    /// [`can_encode_value`](Self::can_encode_value) returns `false` before
    /// entering this unsafe method. Implementations must leave their internal
    /// state consistent when returning an error.
    ///
    /// # Safety
    ///
    /// The caller must guarantee that
    /// [`can_encode_value`](Self::can_encode_value) returned `true` for
    /// `value`. Starting at `output_index`, the caller must also provide either
    /// at least
    /// [`MAX_ENCODE_UNITS_PER_VALUE`](Self::MAX_ENCODE_UNITS_PER_VALUE)
    /// writable
    /// units, which is sufficient for every encodable value, or at least the
    /// exact [`encode_len`](Self::encode_len) for the same `value` and codec
    /// state. On success, implementations must return that exact written unit
    /// count, including `0` for deliberate buffering, and the count must be no
    /// larger than `MAX_ENCODE_UNITS_PER_VALUE`. Implementations must not write
    /// beyond the exact `encode_len` capacity on either the success or error
    /// path.
    #[must_use = "encoded length and encode errors must be handled"]
    unsafe fn encode(
        &mut self,
        value: &Self::Value,
        output: &mut [Self::Unit],
        output_index: usize,
    ) -> Result<usize, Self::EncodeError>;

    /// Emits EOF trailer output and finishes encode-side state.
    ///
    /// This is the encode-side counterpart of
    /// [`decode_finish`](Self::decode_finish). Codecs that append stream
    /// trailers (padding, checksums, end-of-stream markers) emit them here.
    /// Stateless codecs use the default no-op.
    ///
    /// # Parameters
    ///
    /// - `output`: Destination unit buffer.
    /// - `output_index`: Start index in `output`.
    ///
    /// # Returns
    ///
    /// Returns the number of finish units written.
    ///
    /// # Errors
    ///
    /// Returns `Self::EncodeError` when finish output cannot be emitted.
    /// Implementations must leave their internal state consistent when
    /// returning an error.
    ///
    /// # Safety
    ///
    /// The caller must guarantee that the implementation can write up to
    /// [`MAX_ENCODE_FINISH_UNITS`](Self::MAX_ENCODE_FINISH_UNITS) units
    /// starting at `output_index`.
    #[inline(always)]
    #[must_use = "finish output and finish errors must be handled"]
    unsafe fn encode_finish(
        &mut self,
        _output: &mut [Self::Unit],
        _output_index: usize,
    ) -> Result<usize, Self::EncodeError> {
        Ok(0)
    }

    /// Emits stream-start values and resets decode-side state.
    ///
    /// This is the decode-side counterpart of
    /// [`encode_reset`](Self::encode_reset). Codecs that emit a
    /// stream-start marker or BOM before decoding a new stream emit them
    /// here. Stateless codecs use the default no-op.
    ///
    /// # Parameters
    ///
    /// - `output`: Destination value buffer.
    /// - `output_index`: Start index in `output`.
    ///
    /// # Returns
    ///
    /// Returns the number of reset values written.
    ///
    /// # Errors
    ///
    /// Returns `Self::DecodeError` when reset output cannot be emitted.
    /// Implementations must leave their internal state consistent when
    /// returning an error.
    ///
    /// # Safety
    ///
    /// The caller must guarantee that the implementation can write up to
    /// [`MAX_DECODE_RESET_VALUES`](Self::MAX_DECODE_RESET_VALUES) values
    /// starting at `output_index`.
    #[inline(always)]
    #[must_use = "reset output and reset errors must be handled"]
    unsafe fn decode_reset(
        &mut self,
        _output: &mut [Self::Value],
        _output_index: usize,
    ) -> Result<usize, Self::DecodeError> {
        Ok(0)
    }

    /// Decodes one value from `input` starting at `input_index`.
    ///
    /// # Parameters
    ///
    /// - `input`: Source unit buffer.
    /// - `input_index`: Start index in `input`.
    ///
    /// # Returns
    ///
    /// Returns the decoded value and the non-zero number of consumed units.
    ///
    /// # Errors
    ///
    /// Returns [`DecodeFailure::Incomplete`] when the visible input is a
    /// valid prefix but more units are needed to decide or complete a value.
    /// This reports a streaming boundary, not a final EOF condition; the
    /// caller or higher-level adapter decides what an incomplete tail means
    /// when the upstream source is closed. Codec-specific context may be
    /// retained in the failure's optional `source` field.
    /// Returns [`DecodeFailure::Invalid`] when the units are malformed,
    /// non-canonical, unmappable, or otherwise invalid for this codec and the
    /// span is known or unknown. Unknown span is represented as
    /// [`DecodeFailure::Invalid`] with an unknown consumed-unit hint.
    /// Implementations must leave their internal state consistent when
    /// returning an error.
    ///
    /// # Safety
    ///
    /// The caller must guarantee that `input_index` is a valid boundary in
    /// `input` and that at least
    /// [`MIN_UNITS_PER_VALUE`](Self::MIN_UNITS_PER_VALUE)
    /// units are readable from `input_index`. Implementations must not read
    /// beyond the currently available units under that precondition. They
    /// may return [`DecodeFailure::Incomplete`] when those units are a
    /// valid but incomplete prefix.
    ///
    /// On success, implementations must return a consumed unit count no larger
    /// than either the available input or
    /// [`MAX_DECODE_UNITS_PER_VALUE`](Self::MAX_DECODE_UNITS_PER_VALUE). An
    /// incomplete result's `required_total` must also not exceed that maximum.
    /// The return type guarantees that successful decoding always consumes at
    /// least one unit. Implementations should use `debug_assert!` to state
    /// these unchecked entry-point assumptions.
    #[must_use = "decoded value, consumed length, and decode errors must be handled"]
    unsafe fn decode(
        &mut self,
        input: &[Self::Unit],
        input_index: usize,
    ) -> Result<(Self::Value, NonZeroUsize), DecodeFailure<Self::DecodeError>>;

    /// Decodes one value after the caller has established end of input.
    ///
    /// This method has the same safety and state guarantees as
    /// [`decode`](Self::decode), but may resolve an otherwise incomplete
    /// trailing prefix using EOF-aware format rules. The default preserves the
    /// open-stream behavior by delegating to [`decode`](Self::decode).
    ///
    /// # Safety
    ///
    /// The caller must satisfy the same input-index and minimum-readable-unit
    /// preconditions as [`decode`](Self::decode). Implementations must not
    /// read beyond the currently available units.
    #[inline(always)]
    #[must_use = "decoded value, consumed length, and decode errors must be handled"]
    unsafe fn decode_eof(
        &mut self,
        input: &[Self::Unit],
        input_index: usize,
    ) -> Result<(Self::Value, NonZeroUsize), DecodeFailure<Self::DecodeError>>
    {
        // SAFETY: `decode_eof` has the same preconditions as `decode`.
        unsafe { self.decode(input, input_index) }
    }

    /// Finishes decode-side EOF state into `output`.
    ///
    /// `decode_finish` receives no source input. Callers must have already
    /// handled any tail reported by [`DecodeFailure::Incomplete`] before
    /// finishing decode state. Implementations may emit retained values or
    /// validate internal EOF state, but they must not depend on re-reading the
    /// incomplete source tail.
    ///
    /// # Parameters
    ///
    /// - `output`: Destination value buffer.
    /// - `output_index`: Start index in `output`.
    ///
    /// # Returns
    ///
    /// Returns the number of finished values written.
    ///
    /// # Errors
    ///
    /// Returns `Self::DecodeError` when retained decode state is invalid at
    /// EOF. Implementations must leave their internal state consistent when
    /// returning an error.
    ///
    /// # Safety
    ///
    /// The caller must guarantee that the implementation can write up to
    /// [`MAX_DECODE_FINISH_VALUES`](Self::MAX_DECODE_FINISH_VALUES) values
    /// starting at `output_index`.
    #[inline(always)]
    #[must_use = "finish output length and finish errors must be handled"]
    unsafe fn decode_finish(
        &mut self,
        _output: &mut [Self::Value],
        _output_index: usize,
    ) -> Result<usize, Self::DecodeError> {
        Ok(0)
    }
}

/// Compile-time asserts the public unit-bound invariants required by [`Codec`].
///
/// # Type Parameters
///
/// - `C`: Codec implementation to validate.
///
/// # Returns
///
/// Returns unit `()`.
///
/// # Panics
///
/// Panics at compile time when either decode width bound is zero or when
/// [`Codec::MIN_UNITS_PER_VALUE`] is greater than
/// [`Codec::MAX_DECODE_UNITS_PER_VALUE`], because these invariants must hold
/// for any well-formed [`Codec`] implementation and violating them is always a
/// bug.
#[inline(always)]
pub(crate) fn assert_unit_bounds<C>()
where
    C: Codec,
{
    const {
        assert!(
            C::MIN_UNITS_PER_VALUE > 0,
            "Codec::MIN_UNITS_PER_VALUE must be non-zero",
        );
        assert!(
            C::MAX_DECODE_UNITS_PER_VALUE > 0,
            "Codec::MAX_DECODE_UNITS_PER_VALUE must be non-zero",
        );
        assert!(
            C::MIN_UNITS_PER_VALUE <= C::MAX_DECODE_UNITS_PER_VALUE,
            "Codec::MIN_UNITS_PER_VALUE must not exceed Codec::MAX_DECODE_UNITS_PER_VALUE",
        );
    }
}