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}