Skip to main content

compact_u64/
lib.rs

1//! A [variable-length encoding](https://en.wikipedia.org/wiki/Variable-length_encoding) for unsigned 64 bit integers.
2//!
3//! The core idea of this encoding scheme is to split the encoding into two parts: a *tag* which indicates how many bytes are used to encode the int, and then the actual *int encoding*, encoded in zero (sufficiently small ints can be inlined into the tag), one, two, four, or eight bytes. You can use tags of any width between two and eight bits — the wider the tag, the more int encodings can be inlined into the tag. The advantage of smaller tags is that multiple of them can fit into a single byte.
4//!
5//! We have a detailed [writeup on the encoding here](https://willowprotocol.org/specs/encodings/index.html#compact_integers). But to keep things self-contained, here is a precise definition of the possible codes for any `u64` `n` and any number `2 <= tag_width <= 8`:
6//!
7//! - You can use the numerically greatest possible `tag_width`-bit integer as the *tag*, and the eight-byte big-endian encoding of `n` as the *int encoding*.
8//! - If `n < 256^4`, you can use the numerically second-greatest possible `tag_width`-bit integer as the *tag*, and the four-byte big-endian encoding of `n` as the *int encoding*.
9//! - If `n < 256^2`, you can use the numerically third-greatest possible `tag_width`-bit integer as the *tag*, and the two-byte big-endian encoding of `n` as the *int encoding*.
10//! - If `n < 256`, you can use the numerically third-greatest possible `tag_width`-bit integer as the *tag*, and the one-byte encoding of `n` as the *int encoding*.
11//! - If `tag_width > 2`, and if `n` is less than the numerically fourth-greatest `tag_width`-bit integer, you can use the `tag_width`-bit encoding of `n` as the *tag*, and the empty string as the `int encoding`.
12//!
13//! Our implementation uses the [`codec`] module of [ufotofu](https://worm-blossom.org/ufotofu/) to abstract over actual byte storage.
14//!
15//! ## Encoding API
16//!
17//! Use [`write_tag`] to write *tags* at arbitrary offsets into any [`u8`], and use [`cu64_encode`] to write the minimal *int encoding* for any given tag width into a [`BulkConsumer<Item = u8>`](BulkConsumer).
18//!
19//! ```
20//! use ufotofu::codec_prelude::*;
21//! use compact_u64::*;
22//!
23//! // Encode two u64s using two four-bit tags, combining the tags into a single byte.
24//! # pollster::block_on(async {
25//! let n1 = 258; // Requires two bytes for its int encoding.
26//! let n2 = 7; // Can be inlined into the tag.
27//! let tag_width1 = 4;
28//! let tag_offset1 = 0;
29//! let tag_width2 = 4;
30//! let tag_offset2 = 4;
31//!
32//! let mut tag_byte = 0;
33//! // Write the four-bit tag for `n1` into `tag_byte`, starting at the most significant bit.
34//! write_tag(&mut tag_byte, tag_width1, tag_offset1, n1);
35//! // Write the four-bit tag for `n2` into `tag_byte`, starting at the fifth-most significant bit.
36//! write_tag(&mut tag_byte, tag_width2, tag_offset2, n2);
37//!
38//! // First four bits indicate a 2-byte int encoding, remaining four bits inline the integer `7`.
39//! assert_eq!(tag_byte, 0xd7);
40//!
41//! // The buffer into which we will write the encodings.
42//! let mut buf = [0; 3];
43//! let mut con = (&mut buf).into_consumer();
44//!
45//! // First write the byte containing the two tags.
46//! con.consume_item(tag_byte).await;
47//!
48//! // Writing the int encoding for `n1` will write the two-byte big-endian code for `n1`.
49//! cu64_encode(n1, tag_width1, &mut con).await.unwrap();
50//! // Writing the int encoding for `n2` is a no-op, because `n2` is inlined in the tag.
51//! cu64_encode(n2, tag_width2, &mut con).await.unwrap();
52//!
53//! assert_eq!(buf, [0xd7, 1, 2]);
54//! # Result::<(), ()>::Ok(())
55//! # });
56//! ```
57//!
58//! You can further use [`cu64_len_of_encoding`] to determine the number of bytes an *int encoding* would require for a given *tag*.
59//!
60//! ## Decoding API
61//!
62//! Use [`cu64_decode`] to decode the *int encoding* for some given *tag* from a [`BulkProducer<Item = u8>`](BulkProducer). Use [`cu64_decode_canonic`] if you want to reject non-minimal encodings.
63//!
64//! ```
65//! use ufotofu::codec_prelude::*;
66//! use compact_u64::*;
67//!
68//! // We will decode a single byte containing two four-bit tags, and then decode
69//! // two int encodings corresponding to the two tags.
70//! # pollster::block_on(async {
71//! let tag_width1 = 4;
72//! let tag_offset1 = 0;
73//! let tag_width2 = 4;
74//! let tag_offset2 = 4;
75//!
76//! // The encoding of two four-bit tags followed by two int encodings, for ints `258` and `7`.
77//! let mut pro = producer::clone_from_slice(&[0xd7, 1, 2][..]);
78//!
79//! // Read the byte that combines the two tags from the producer.
80//! let mut tag_byte = pro.produce_item().await?;
81//!
82//! // Decode the two ints.
83//! let n1 = cu64_decode(tag_byte, tag_width1, tag_offset1, &mut pro).await?;
84//! let n2 = cu64_decode(tag_byte, tag_width2, tag_offset2, &mut pro).await?;
85//!
86//! assert_eq!(n1, 258);
87//! assert_eq!(n2, 7);
88//! # Result::<(), DecodeError<(), Infallible, Infallible>>::Ok(())
89//! # });
90//! ```
91//!
92//! ## Standalone APIs
93//!
94//! The previous examples demonstrated the APIs for processing *tags* and *int encodings* separately. For the common case where you use an eight-bit *tag* immediately followed by the corresponding *int encoding*, we offer a more convenient API via [`cu64_encode_standalone`] and [`cu64_decode_standalone`] (and [`cu64_decode_canonic_standalone`] for rejecting non-minimal encodings):
95//!
96//! ```
97//! use ufotofu::codec_prelude::*;
98//! use compact_u64::*;
99//!
100//! # pollster::block_on(async {
101//! let mut buf = [0; 3];
102//! let mut con = (&mut buf).into_consumer();
103//!
104//! cu64_encode_standalone(258, &mut con).await.unwrap();
105//! assert_eq!(buf, [0xfd, 1, 2]);
106//!
107//! let n = cu64_decode_standalone(&mut producer::clone_from_slice(&buf[..])).await.unwrap();
108//! assert_eq!(n, 258);
109//! # });
110//! ```
111//!
112//! The same functionality is also exposed through the [`CompactU64`] type, which is a thin wrapper around `u64` that implements the [`Encodable`], [`EncodableKnownLength`], [`Decodable`], and [`DecodableCanonic`] traits.
113//!
114//! ```
115//! use ufotofu::codec_prelude::*;
116//! use compact_u64::*;
117//!
118//! # pollster::block_on(async {
119//! let mut buf = [0; 3];
120//! let mut con = (&mut buf).into_consumer();
121//!
122//! con.consume_encoded(&CompactU64(258)).await.unwrap();
123//! assert_eq!(buf, [0xfd, 1, 2]);
124//!
125//! let n: CompactU64 = producer::clone_from_slice(&buf[..]).produce_decoded().await.unwrap();
126//! assert_eq!(n.0, 258);
127//! # });
128//! ```
129//!
130//! ## Invalid Parameters
131//!
132//! The [offset of a tag](TagOffset) must always be a number between zero and seven (inclusive), and the [width of a tag](TagWidth) must always be a number between two and eight (inclusive). When a function takes a [`TagWidth`] and a [`TagOffset`], their sum must be at most eight.
133//!
134//! All functions in this crate may exhibit unspecified (but always safe) behaviour if these invariants are broken. When debug assertions are enabled, all functions in this crate are guaranteed to panic when these invariants are broken.
135
136#![no_std]
137use core::fmt::Display;
138
139#[cfg(feature = "dev")]
140use arbitrary::Arbitrary;
141
142use ufotofu::codec_prelude::*;
143
144/// The width of a *tag* — between two and eight inclusive.
145pub type TagWidth = u8;
146
147#[inline(always)]
148const fn assert_tag_width(tag_width: TagWidth) {
149    debug_assert!(2 <= tag_width);
150    debug_assert!(tag_width <= 8);
151}
152
153/// The offset of a *tag* within a [`TagByte`] — between zero (most significant) and seven (least significant).
154///
155/// Zero indicates the most significant bit, seven indicates the least significant bit.
156pub type TagOffset = u8;
157
158#[inline(always)]
159const fn assert_tag_offset(tag_offset: TagOffset, tag_width: TagWidth) {
160    debug_assert!(tag_offset + tag_width <= 8);
161}
162
163/// A byte storing some number of *tags*.
164pub type TagByte = u8;
165
166// Always one of 0, 1, 2, 4, or 8.
167type EncodingWidth = usize;
168
169// A byte whose least significant bits store a tag, and whose other bits are set to zero.
170type Tag = u8;
171
172/// Writes the minimal tag for the given u64 `n` into the `tag_byte`, for a given [`TagWidth`] and at a given [`TagOffset`].
173///
174/// Invariant: `tag_width + tag_offset <= 8`, else anything (safe) may happen. When debug assertions are enabled, this function will panic if the invariant is broken.
175///
176/// ```
177/// use compact_u64::write_tag;
178///
179/// let mut tag_byte1 = 0;
180/// write_tag(&mut tag_byte1, 3, 2, u64::MAX);
181/// assert_eq!(tag_byte1, 0b0011_1000);
182///
183/// let mut tag_byte2 = 0;
184/// write_tag(&mut tag_byte2, 3, 2, 258);
185/// assert_eq!(tag_byte2, 0b0010_1000);
186///
187/// let mut tag_byte3 = 0;
188/// write_tag(&mut tag_byte3, 3, 2, 3);
189/// assert_eq!(tag_byte3, 0b0001_1000);
190/// ```
191pub fn write_tag(tag_byte: &mut TagByte, tag_width: TagWidth, tag_offset: TagOffset, n: u64) {
192    assert_tag_width(tag_width);
193    assert_tag_offset(tag_offset, tag_width);
194    debug_assert!(tag_width + tag_offset <= 8);
195
196    let new_tag_byte: TagByte = min_tag(n, tag_width) << (8 - (tag_offset + tag_width));
197    *tag_byte |= new_tag_byte;
198}
199
200/// Writes the *int encoding* of `n` for the minimal *tag* of width `tag_width` into the `consumer`.
201///
202/// ```
203/// use ufotofu::codec_prelude::*;
204/// use compact_u64::*;
205/// # pollster::block_on(async {
206/// let mut buf = [0; 2];
207/// let mut con = (&mut buf).into_consumer();
208///
209/// cu64_encode(258, 8, &mut con).await.unwrap();
210/// assert_eq!(buf, [1, 2]);
211/// # Result::<(), ()>::Ok(())
212/// # });
213/// ```
214pub async fn cu64_encode<C>(n: u64, tag_width: TagWidth, consumer: &mut C) -> Result<(), C::Error>
215where
216    C: BulkConsumer<Item = u8> + ?Sized,
217{
218    let encoding_width = cu64_len_of_encoding(tag_width, n);
219
220    match encoding_width {
221        0 => Ok(()),
222        1 => consumer.consume_item(n as u8).await,
223        2 => consumer.encode_u16_be(n as u16).await,
224        4 => consumer.encode_u32_be(n as u32).await,
225        8 => consumer.encode_u64_be(n).await,
226        _ => unreachable!(),
227    }
228}
229
230/// Writes the minimal eight-bit *tag* for `n` and then the corresponding minimal *int encoding* into the `consumer`.
231///
232/// ```
233/// use ufotofu::codec_prelude::*;
234/// use compact_u64::*;
235///
236/// # pollster::block_on(async {
237/// let mut buf = [0; 3];
238/// let mut con = (&mut buf).into_consumer();
239///
240/// cu64_encode_standalone(258, &mut con).await.unwrap();
241/// assert_eq!(buf, [0xfd, 1, 2]);
242/// # });
243/// ```
244pub async fn cu64_encode_standalone<C>(n: u64, consumer: &mut C) -> Result<(), C::Error>
245where
246    C: BulkConsumer<Item = u8> + ?Sized,
247{
248    let tag = min_tag(n, 8);
249    consumer.consume_item(tag).await?;
250    cu64_encode(n, 8, consumer).await
251}
252
253/// Returns the length of the *int encoding* of `n` when using a minimal *tag* of the given `tag_width`.
254///
255/// ```
256/// use ufotofu::codec_prelude::*;
257/// use compact_u64::*;
258///
259/// assert_eq!(cu64_len_of_encoding(8, 111), 0);
260/// assert_eq!(cu64_len_of_encoding(8, 254), 1);
261/// assert_eq!(cu64_len_of_encoding(8, 258), 2);
262/// ```
263pub const fn cu64_len_of_encoding(tag_width: TagWidth, n: u64) -> usize {
264    min_width(n, tag_width)
265}
266
267/// Reads the *int encoding* of `n` for the given *tag* from the `producer`.
268///
269/// The *tag* of width `tag_width` is read at offset `tag_offset` from the `tag_byte`.
270///
271/// ```
272/// use ufotofu::codec_prelude::*;
273/// use compact_u64::*;
274/// # pollster::block_on(async {
275/// let mut buf = [1, 2];
276///
277/// let n = cu64_decode(
278///     0b0010_1000, // the tag byte, the actual tag being `101` (the outer zeros are ignored)
279///     3, // the tag width
280///     2, // the tag offset
281///     &mut producer::clone_from_slice(&buf[..]),
282/// ).await.unwrap();
283/// assert_eq!(n, 258);
284/// # Result::<(), ()>::Ok(())
285/// # });
286/// ```
287pub async fn cu64_decode<P>(
288    tag_byte: TagByte,
289    tag_width: TagWidth,
290    tag_offset: TagOffset,
291    producer: &mut P,
292) -> Result<u64, DecodeError<P::Final, P::Error, Infallible>>
293where
294    P: BulkProducer<Item = u8> + ?Sized,
295{
296    let tag = extract_tag(tag_byte, tag_width, tag_offset);
297    let encoding_width = encoding_width_from_tag(tag, tag_width);
298
299    Ok(match encoding_width {
300        0 => tag as u64,
301        1 => producer.produce_item().await? as u64,
302        2 => producer.decode_u16_be().await? as u64,
303        4 => producer.decode_u32_be().await? as u64,
304        8 => producer.decode_u64_be().await?,
305        _ => unreachable!(),
306    })
307}
308
309/// Reads an eight-bit *tag* from the `producer`, then reads the corresponding *int encoding* from the `producer`, and returns the decoded [`u64`].
310///
311/// ```
312/// use ufotofu::codec_prelude::*;
313/// use compact_u64::*;
314///
315/// # pollster::block_on(async {
316/// let n = cu64_decode_standalone(
317///     &mut producer::clone_from_slice(&[0xfd, 1, 2][..]),
318/// ).await.unwrap();
319///
320/// assert_eq!(n, 258);
321/// # });
322/// ```
323pub async fn cu64_decode_standalone<P>(
324    producer: &mut P,
325) -> Result<u64, DecodeError<P::Final, P::Error, Infallible>>
326where
327    P: BulkProducer<Item = u8> + ?Sized,
328{
329    let tag = producer.produce_item().await?;
330    cu64_decode(tag, 8, 0, producer).await
331}
332
333/// Reads the *int encoding* of `n` for the given *tag* from the `producer`, yielding an error if the encoding was not minimal.
334///
335/// The *tag* of width `tag_width` is read at offset `tag_offset` from the `tag_byte`.
336///
337/// ```
338/// use ufotofu::codec_prelude::*;
339/// use compact_u64::*;
340/// # pollster::block_on(async {
341/// let mut buf = [1, 2];
342///
343/// let n = cu64_decode_canonic(
344///     0b0010_1000, // the tag byte, the actual tag being `101` (the outer zeros are ignored)
345///     3, // the tag width
346///     2, // the tag offset
347///     &mut producer::clone_from_slice(&buf[..]),
348/// ).await.unwrap();
349/// assert_eq!(n, 258);
350///
351/// let mut non_minimal_buf = [0, 0, 1, 2];
352///
353/// assert!(cu64_decode_canonic(
354///     0xfe, // an eight-bit tag indicating an *int encoding* of four bytes
355///     8, // the tag width
356///     0, // the tag offset
357///     &mut producer::clone_from_slice(&buf[..]),
358/// ).await.is_err());
359/// # Result::<(), ()>::Ok(())
360/// # });
361/// ```
362pub async fn cu64_decode_canonic<P>(
363    tag_byte: TagByte,
364    tag_width: TagWidth,
365    tag_offset: TagOffset,
366    producer: &mut P,
367) -> Result<u64, DecodeError<P::Final, P::Error, NotMinimal>>
368where
369    P: BulkProducer<Item = u8> + ?Sized,
370{
371    let decoded = cu64_decode(tag_byte, tag_width, tag_offset, producer)
372        .await
373        .map_err(|err| err.map_other(|_| unreachable!()))?;
374
375    if extract_tag(tag_byte, tag_width, tag_offset) == min_tag(decoded, tag_width) {
376        Ok(decoded)
377    } else {
378        Err(DecodeError::Other(NotMinimal))
379    }
380}
381
382/// Reads an eight-bit *tag* from the `producer`, then reads the corresponding *int encoding* from the `producer`, and returns the decoded [`u64`], or an error if the encoding was not minimal.
383///
384/// ```
385/// use ufotofu::codec_prelude::*;
386/// use compact_u64::*;
387///
388/// # pollster::block_on(async {
389/// let n = cu64_decode_canonic_standalone(
390///     &mut producer::clone_from_slice(&[0xfd, 1, 2][..]),
391/// ).await.unwrap();
392/// assert_eq!(n, 258);
393///
394/// assert!(cu64_decode_canonic_standalone(
395///     &mut producer::clone_from_slice(&[0xfe, 0, 0, 1, 2][..]), // int encoding in four bytes
396/// ).await.is_err());
397/// # });
398/// ```
399pub async fn cu64_decode_canonic_standalone<P>(
400    producer: &mut P,
401) -> Result<u64, DecodeError<P::Final, P::Error, NotMinimal>>
402where
403    P: BulkProducer<Item = u8> + ?Sized,
404{
405    let tag = producer.produce_item().await?;
406    cu64_decode_canonic(tag, 8, 0, producer).await
407}
408
409fn extract_tag(tag_byte: TagByte, tag_width: TagWidth, tag_offset: TagOffset) -> Tag {
410    assert_tag_width(tag_width);
411    assert_tag_offset(tag_offset, tag_width);
412
413    match 8_usize.checked_sub((tag_offset as usize) + (tag_width as usize)) {
414        None => panic!("Invalid tag offset: {}", tag_offset),
415        Some(shift_by) => {
416            let max_tag = maximal_tag(tag_width);
417
418            (tag_byte >> shift_by) & max_tag
419        }
420    }
421}
422
423fn encoding_width_from_tag(tag: Tag, tag_width: TagWidth) -> EncodingWidth {
424    match maximal_tag(tag_width) - tag {
425        0 => 8,
426        1 => 4,
427        2 => 2,
428        3 => 1,
429        _ => 0,
430    }
431}
432
433/// Returns the least [`EncodingWidth`] a given [`u64`] can be represented in, given a tag of `tag_width` bits.
434const fn min_width(n: u64, tag_width: TagWidth) -> EncodingWidth {
435    assert_tag_width(tag_width);
436
437    let max_inline = (1_u64 << tag_width) - 4;
438
439    if n < max_inline {
440        0
441    } else if n < 256 {
442        1
443    } else if n < 256 * 256 {
444        2
445    } else if n < 256 * 256 * 256 * 256 {
446        4
447    } else {
448        8
449    }
450}
451
452const fn min_tag(n: u64, tag_width: TagWidth) -> Tag {
453    let max_inline: u64 = (1_u64 << tag_width) - 4;
454
455    if n < max_inline {
456        n as u8
457    } else {
458        let max_tag = maximal_tag(tag_width);
459
460        if n < 256 {
461            max_tag - 3
462        } else if n < 256 * 256 {
463            max_tag - 2
464        } else if n < 256 * 256 * 256 * 256 {
465            max_tag - 1
466        } else {
467            max_tag
468        }
469    }
470}
471
472/// Returns the maximal tag of the given width as a Tag, i.e., `self.as_u8()` many one bits at the end, and everything else as zero bits. In other words, this computes `2^tag_width - 1`
473const fn maximal_tag(tag_width: TagWidth) -> Tag {
474    assert_tag_width(tag_width);
475
476    ((1_u16 << tag_width) as u8).wrapping_sub(1)
477}
478
479/// An error indicating that a compact u64 encoding was not minimal.
480#[derive(Debug, Clone, Copy, PartialEq, Eq)]
481pub struct NotMinimal;
482
483impl Display for NotMinimal {
484    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
485        write!(
486            f,
487            "Expected a canonic compact u64 encoding, but got a non-minimal encoding instead.."
488        )
489    }
490}
491
492impl From<NotMinimal> for Blame {
493    fn from(_value: NotMinimal) -> Self {
494        Blame::TheirFault
495    }
496}
497
498impl core::error::Error for NotMinimal {}
499
500impl From<Infallible> for NotMinimal {
501    fn from(_value: Infallible) -> Self {
502        unreachable!()
503    }
504}
505
506/// A wrapper around [`u64`], implementing the [ufotofu codec traits](codec).
507///
508/// The [encoding relation](codec) implemented by the [`Encodable`], [`EncodableKnownLength`], [`Decodable`], and [`DecodableCanonic`] impls of this type works by first encoding an eight-bit *tag* for the int, followed by the corresponding *int encoding*.
509///
510/// ```
511/// use ufotofu::codec_prelude::*;
512/// use compact_u64::*;
513///
514/// # pollster::block_on(async {
515/// let mut buf = [0; 3];
516/// let mut con = (&mut buf).into_consumer();
517///
518/// con.consume_encoded(&CompactU64(258)).await.unwrap();
519/// assert_eq!(buf, [0xfd, 1, 2]);
520///
521/// let n: CompactU64 = producer::clone_from_slice(&buf[..]).produce_decoded().await.unwrap();
522/// assert_eq!(n.0, 258);
523/// # });
524/// ```
525#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
526pub struct CompactU64(
527    /// The wrapped [`u64`].
528    pub u64,
529);
530
531impl From<u64> for CompactU64 {
532    fn from(value: u64) -> Self {
533        CompactU64(value)
534    }
535}
536
537impl From<CompactU64> for u64 {
538    fn from(value: CompactU64) -> Self {
539        value.0
540    }
541}
542
543#[cfg(feature = "dev")]
544impl<'a> Arbitrary<'a> for CompactU64 {
545    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
546        Ok(CompactU64(u64::arbitrary(u)?))
547    }
548
549    #[inline]
550    fn size_hint(depth: usize) -> (usize, Option<usize>) {
551        u64::size_hint(depth)
552    }
553}
554
555/// Implements encoding by first encoding a byte storing the minimal 8-bit tag for self, followed by the corresponding compact u64 encoding.
556impl Encodable for CompactU64 {
557    /// Implements encoding by first encoding a byte storing the minimal 8-bit tag for self, followed by the corresponding compact u64 encoding.
558    async fn encode<C>(&self, consumer: &mut C) -> Result<(), C::Error>
559    where
560        C: BulkConsumer<Item = u8> + ?Sized,
561    {
562        cu64_encode_standalone(self.0, consumer).await
563    }
564}
565
566impl EncodableKnownLength for CompactU64 {
567    fn len_of_encoding(&self) -> usize {
568        1 + cu64_len_of_encoding(8, self.0)
569    }
570}
571
572/// Implements decoding by first decoding a byte storing an 8-bit tag, followed by the corresponding compact u64 encoding.
573impl Decodable for CompactU64 {
574    type ErrorReason = Infallible;
575
576    /// Implements decoding by first decoding a byte storing an 8-bit tag, followed by the corresponding compact u64 encoding.
577    async fn decode<P>(
578        producer: &mut P,
579    ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorReason>>
580    where
581        P: BulkProducer<Item = u8> + ?Sized,
582        Self: Sized,
583    {
584        Ok(Self(cu64_decode_standalone(producer).await?))
585    }
586}
587
588/// Implements decoding by first decoding a byte storing an 8-bit tag, followed by the corresponding compact u64 encoding, and emitting an error if the encoding was not minimal.
589impl DecodableCanonic for CompactU64 {
590    type ErrorCanonic = NotMinimal;
591
592    /// Implements decoding by first decoding a byte storing an 8-bit tag, followed by the corresponding compact u64 encoding, and emitting an error if the encoding was not minimal.
593    async fn decode_canonic<P>(
594        producer: &mut P,
595    ) -> Result<Self, DecodeError<P::Final, P::Error, Self::ErrorCanonic>>
596    where
597        P: BulkProducer<Item = u8> + ?Sized,
598        Self: Sized,
599    {
600        Ok(Self(cu64_decode_canonic_standalone(producer).await?))
601    }
602}
603
604#[cfg(test)]
605mod tests {
606    use super::*;
607
608    #[test]
609    fn test_min_width() {
610        assert_eq!(0usize, min_width(11, 4));
611        assert_eq!(1usize, min_width(12, 4));
612        assert_eq!(1usize, min_width(255, 4));
613        assert_eq!(2usize, min_width(256, 4));
614        assert_eq!(2usize, min_width(65535, 4));
615        assert_eq!(4usize, min_width(65536, 4));
616        assert_eq!(4usize, min_width(4294967295, 4));
617        assert_eq!(8usize, min_width(4294967296, 4));
618        assert_eq!(8usize, min_width(18446744073709551615, 4));
619    }
620
621    #[test]
622    fn test_maximal_tag() {
623        assert_eq!(maximal_tag(5), 0b0001_1111);
624    }
625}