Skip to main content

commonware_codec/
varint.rs

1//! Variable-length integer encoding and decoding
2//!
3//! # Overview
4//!
5//! This module implements Google's Protocol Buffers variable-length integer encoding.
6//! Each byte uses:
7//! - 7 bits for the value
8//! - 1 "continuation" bit to indicate if more bytes follow
9//!
10//! `u8` and `i8` are omitted since those types do not benefit from varint encoding.
11//!
12//! `usize` and `isize` are omitted to prevent behavior from depending on the target architecture.
13//!
14//! # Usage Example
15//!
16//! ```rust
17//! use commonware_codec::{Encode, DecodeExt, varint::{UInt, SInt}};
18//!
19//! // Unsigned example
20//! let one = UInt(42u128).encode();
21//! assert_eq!(one.len(), 1); // 42 fits in a single byte
22//! let decoded: u128 = UInt::decode(one).unwrap().into();
23//! assert_eq!(decoded, 42);
24//!
25//! // Signed example (ZigZag)
26//! let neg = SInt(-3i32).encode();
27//! assert_eq!(neg.len(), 1);
28//! let decoded: i32 = SInt::decode(neg).unwrap().into();
29//! assert_eq!(decoded, -3);
30//! ```
31
32use crate::{Buf, EncodeSize, Error, FixedSize, MaxEncodeSize, Read, Write};
33use bytes::BufMut;
34use core::{fmt::Debug, mem::size_of};
35use sealed::{SPrim, UPrim};
36
37/// The number of bits in a byte.
38const BITS_PER_BYTE: usize = 8;
39
40/// The number of data-bearing bits in a byte.
41/// That is, the number of bits in a byte excluding the continuation bit.
42const DATA_BITS_PER_BYTE: usize = 7;
43
44/// The mask for the data-bearing bits in a byte.
45const DATA_BITS_MASK: u8 = 0x7F;
46
47/// The mask for the continuation bit in a byte.
48///
49/// In varint encoding, each byte uses 7 bits for data and 1 bit (the most significant)
50/// to indicate whether more bytes follow. If this bit is set, the varint continues,
51/// if clear this is the final byte.
52const CONTINUATION_BIT_MASK: u8 = 0x80;
53
54/// Maximum encoded size of a u16 varint in bytes.
55pub const MAX_U16_VARINT_SIZE: usize =
56    (size_of::<u16>() * BITS_PER_BYTE).div_ceil(DATA_BITS_PER_BYTE);
57
58/// Maximum encoded size of a u32 varint in bytes.
59pub const MAX_U32_VARINT_SIZE: usize =
60    (size_of::<u32>() * BITS_PER_BYTE).div_ceil(DATA_BITS_PER_BYTE);
61
62/// Maximum encoded size of a u64 varint in bytes.
63pub const MAX_U64_VARINT_SIZE: usize =
64    (size_of::<u64>() * BITS_PER_BYTE).div_ceil(DATA_BITS_PER_BYTE);
65
66/// Maximum encoded size of a u128 varint in bytes.
67pub const MAX_U128_VARINT_SIZE: usize =
68    (size_of::<u128>() * BITS_PER_BYTE).div_ceil(DATA_BITS_PER_BYTE);
69
70/// An incremental varint decoder for reading varints byte-by-byte from streams.
71///
72/// This is useful when reading from async streams where you receive one byte at a time
73/// and need to know when the varint is complete while also building up the decoded value.
74///
75/// # Example
76///
77/// ```
78/// use commonware_codec::varint::Decoder;
79///
80/// let mut decoder = Decoder::<u32>::new();
81///
82/// // Feed bytes one at a time (e.g., from a stream)
83/// // 300 encodes as [0xAC, 0x02]
84/// assert_eq!(decoder.feed(0xAC).unwrap(), None); // continuation bit set, need more
85/// assert_eq!(decoder.feed(0x02).unwrap(), Some(300)); // complete!
86/// ```
87#[derive(Debug, Clone)]
88pub struct Decoder<U: UPrim> {
89    result: U,
90    bits_read: usize,
91}
92
93impl<U: UPrim> Default for Decoder<U> {
94    fn default() -> Self {
95        Self::new()
96    }
97}
98
99impl<U: UPrim> Decoder<U> {
100    /// Creates a new decoder ready to receive bytes.
101    #[inline]
102    pub fn new() -> Self {
103        Self {
104            result: U::from(0),
105            bits_read: 0,
106        }
107    }
108
109    /// Feeds a byte to the decoder.
110    ///
111    /// Returns:
112    /// - `Ok(Some(value))` when the varint is complete
113    /// - `Ok(None)` when more bytes are needed
114    /// - `Err(InvalidVarint)` if the varint is malformed (overflow or invalid encoding)
115    #[inline]
116    pub fn feed(&mut self, byte: u8) -> Result<Option<U>, Error> {
117        let max_bits = U::SIZE * BITS_PER_BYTE;
118
119        // If this is not the first byte, but the byte is completely zero, we have an invalid
120        // varint. This is because this byte has no data bits and no continuation, so there was no
121        // point in continuing to this byte in the first place. While the output could still result
122        // in a valid value, we ensure that every value has exactly one unique, valid encoding.
123        if byte == 0 && self.bits_read > 0 {
124            return Err(Error::InvalidVarint(U::SIZE));
125        }
126
127        // If this must be the last byte, check for overflow (i.e. set bits beyond the size of T).
128        // Because the continuation bit is the most-significant bit, this check also happens to
129        // check for an invalid continuation bit.
130        //
131        // If we have reached what must be the last byte, this check prevents continuing to read
132        // from the buffer by ensuring that the conditional (`if byte & CONTINUATION_BIT_MASK == 0`)
133        // always evaluates to true.
134        let remaining_bits = max_bits.checked_sub(self.bits_read).unwrap();
135        if remaining_bits <= DATA_BITS_PER_BYTE {
136            let relevant_bits = BITS_PER_BYTE - byte.leading_zeros() as usize;
137            if relevant_bits > remaining_bits {
138                return Err(Error::InvalidVarint(U::SIZE));
139            }
140        }
141
142        // Write the 7 bits of data to the result.
143        self.result |= U::from(byte & DATA_BITS_MASK) << self.bits_read;
144
145        // Check if complete (continuation bit not set)
146        if byte & CONTINUATION_BIT_MASK == 0 {
147            return Ok(Some(self.result));
148        }
149
150        self.bits_read += DATA_BITS_PER_BYTE;
151        Ok(None)
152    }
153}
154
155#[doc(hidden)]
156mod sealed {
157    use super::*;
158    use core::ops::{BitOrAssign, Shl, ShrAssign};
159
160    /// A trait for unsigned integer primitives that can be varint encoded.
161    pub trait UPrim:
162        Copy
163        + From<u8>
164        + Sized
165        + FixedSize
166        + ShrAssign<usize>
167        + Shl<usize, Output = Self>
168        + BitOrAssign<Self>
169        + PartialOrd
170        + Debug
171    {
172        /// Returns the number of leading zeros in the integer.
173        fn leading_zeros(self) -> u32;
174
175        /// Returns the least significant byte of the integer.
176        fn as_u8(self) -> u8;
177    }
178
179    // Implements the `UPrim` trait for all unsigned integer types.
180    macro_rules! impl_uint {
181        ($type:ty) => {
182            impl UPrim for $type {
183                #[inline(always)]
184                fn leading_zeros(self) -> u32 {
185                    self.leading_zeros()
186                }
187
188                #[inline(always)]
189                fn as_u8(self) -> u8 {
190                    self as u8
191                }
192            }
193        };
194    }
195    impl_uint!(u16);
196    impl_uint!(u32);
197    impl_uint!(u64);
198    impl_uint!(u128);
199
200    /// A trait for signed integer primitives that can be converted to and from unsigned integer
201    /// primitives of the equivalent size.
202    ///
203    /// When converted to unsigned integers, the encoding is done using ZigZag encoding, which moves the
204    /// sign bit to the least significant bit (shifting all other bits to the left by one). This allows
205    /// for more efficient encoding of numbers that are close to zero, even if they are negative.
206    pub trait SPrim: Copy + Sized + FixedSize + PartialOrd + Debug {
207        /// The unsigned equivalent type of the signed integer.
208        /// This type must be the same size as the signed integer type.
209        type UnsignedEquivalent: UPrim;
210
211        /// Converts the signed integer to an unsigned integer using ZigZag encoding.
212        fn as_zigzag(&self) -> Self::UnsignedEquivalent;
213
214        /// Converts a (ZigZag'ed) unsigned integer back to a signed integer.
215        fn un_zigzag(value: Self::UnsignedEquivalent) -> Self;
216    }
217
218    /// Asserts that two types are the same size at compile time.
219    #[inline(always)]
220    const fn assert_equal_size<T: Sized, U: Sized>() {
221        assert!(
222            size_of::<T>() == size_of::<U>(),
223            "Unsigned integer must be the same size as the signed integer"
224        );
225    }
226
227    // Implements the `SPrim` trait for all signed integer types.
228    macro_rules! impl_sint {
229        ($type:ty, $utype:ty) => {
230            impl SPrim for $type {
231                type UnsignedEquivalent = $utype;
232
233                #[inline]
234                fn as_zigzag(&self) -> $utype {
235                    // TODO: Re-evaluate assertion placement after `generic_const_exprs` is stable.
236                    const {
237                        assert_equal_size::<$type, $utype>();
238                    }
239
240                    let shr = size_of::<$utype>() * 8 - 1;
241                    ((self << 1) ^ (self >> shr)) as $utype
242                }
243                #[inline]
244                fn un_zigzag(value: $utype) -> Self {
245                    // TODO: Re-evaluate assertion placement after `generic_const_exprs` is stable.
246                    const {
247                        assert_equal_size::<$type, $utype>();
248                    }
249
250                    ((value >> 1) as $type) ^ (-((value & 1) as $type))
251                }
252            }
253        };
254    }
255    impl_sint!(i16, u16);
256    impl_sint!(i32, u32);
257    impl_sint!(i64, u64);
258    impl_sint!(i128, u128);
259}
260
261/// An ergonomic wrapper to allow for encoding and decoding of primitive unsigned integers as
262/// varints rather than the default fixed-width integers.
263#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
264pub struct UInt<U: UPrim>(pub U);
265
266// Implements `Into<U>` for `UInt<U>` for all unsigned integer types.
267// This allows for easy conversion from `UInt<U>` to `U` using `.into()`.
268macro_rules! impl_varuint_into {
269    ($($type:ty),+) => {
270        $(
271            impl From<UInt<$type>> for $type {
272                fn from(val: UInt<$type>) -> Self {
273                    val.0
274                }
275            }
276        )+
277    };
278}
279impl_varuint_into!(u16, u32, u64, u128);
280
281impl<U: UPrim> Write for UInt<U> {
282    fn write(&self, buf: &mut impl BufMut) {
283        write(self.0, buf);
284    }
285}
286
287impl<U: UPrim> Read for UInt<U> {
288    type Cfg = ();
289    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
290        read(buf).map(UInt)
291    }
292}
293
294impl<U: UPrim> EncodeSize for UInt<U> {
295    fn encode_size(&self) -> usize {
296        size(self.0)
297    }
298}
299
300// Each varint width has a known maximum encoded size.
301macro_rules! impl_varuint_max {
302    ($($type:ty => $max:expr),+ $(,)?) => {
303        $(
304            impl MaxEncodeSize for UInt<$type> {
305                const MAX_ENCODE_SIZE: usize = $max;
306            }
307        )+
308    };
309}
310impl_varuint_max!(
311    u16 => MAX_U16_VARINT_SIZE,
312    u32 => MAX_U32_VARINT_SIZE,
313    u64 => MAX_U64_VARINT_SIZE,
314    u128 => MAX_U128_VARINT_SIZE,
315);
316
317#[cfg(feature = "arbitrary")]
318impl<U: UPrim> arbitrary::Arbitrary<'_> for UInt<U>
319where
320    U: for<'a> arbitrary::Arbitrary<'a>,
321{
322    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
323        let value = U::arbitrary(u)?;
324        Ok(Self(value))
325    }
326}
327
328/// An ergonomic wrapper to allow for encoding and decoding of primitive signed integers as
329/// varints rather than the default fixed-width integers.
330#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
331pub struct SInt<S: SPrim>(pub S);
332
333// Implements `Into<U>` for `SInt<U>` for all signed integer types.
334// This allows for easy conversion from `SInt<S>` to `S` using `.into()`.
335macro_rules! impl_varsint_into {
336    ($($type:ty),+) => {
337        $(
338            impl From<SInt<$type>> for $type {
339                fn from(val: SInt<$type>) -> Self {
340                    val.0
341                }
342            }
343        )+
344    };
345}
346impl_varsint_into!(i16, i32, i64, i128);
347
348impl<S: SPrim> Write for SInt<S> {
349    fn write(&self, buf: &mut impl BufMut) {
350        write_signed::<S>(self.0, buf);
351    }
352}
353
354impl<S: SPrim> Read for SInt<S> {
355    type Cfg = ();
356    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, Error> {
357        read_signed::<S>(buf).map(SInt)
358    }
359}
360
361impl<S: SPrim> EncodeSize for SInt<S> {
362    fn encode_size(&self) -> usize {
363        size_signed::<S>(self.0)
364    }
365}
366
367#[cfg(feature = "arbitrary")]
368impl<S: SPrim> arbitrary::Arbitrary<'_> for SInt<S>
369where
370    S: for<'a> arbitrary::Arbitrary<'a>,
371{
372    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
373        let value = S::arbitrary(u)?;
374        Ok(Self(value))
375    }
376}
377
378/// Encodes an unsigned integer as a varint
379fn write<T: UPrim>(value: T, buf: &mut impl BufMut) {
380    let continuation_threshold = T::from(CONTINUATION_BIT_MASK);
381    if value < continuation_threshold {
382        // Fast path for small values (common case for lengths).
383        // `as_u8()` does not truncate the value or leave a continuation bit.
384        buf.put_u8(value.as_u8());
385        return;
386    }
387
388    // Stage the encoded bytes on the stack so the buffer receives a single bulk write.
389    let mut bytes = [0u8; MAX_U128_VARINT_SIZE];
390    let mut len = 0;
391    let mut val = value;
392    while val >= continuation_threshold {
393        bytes[len] = val.as_u8() | CONTINUATION_BIT_MASK;
394        len += 1;
395        val >>= 7;
396    }
397    bytes[len] = val.as_u8();
398    buf.put_slice(&bytes[..=len]);
399}
400
401/// Decodes an unsigned integer from a varint.
402///
403/// Returns an error if:
404/// - The varint is invalid (too long or malformed)
405/// - The buffer ends while reading
406fn read<T: UPrim>(buf: &mut impl Buf) -> Result<T, Error> {
407    // Fast path for single-byte values.
408    let mut byte = buf.try_get_u8().map_err(|_| Error::EndOfBuffer)?;
409    if byte & CONTINUATION_BIT_MASK == 0 {
410        return Ok(T::from(byte));
411    }
412
413    // The decoder enforces canonical encodings and rejects overflow for multi-byte values,
414    // starting with the byte already read.
415    let mut decoder = Decoder::<T>::new();
416    loop {
417        if let Some(value) = decoder.feed(byte)? {
418            return Ok(value);
419        }
420        byte = buf.try_get_u8().map_err(|_| Error::EndOfBuffer)?;
421    }
422}
423
424/// Calculates the number of bytes needed to encode an unsigned integer as a varint.
425fn size<T: UPrim>(value: T) -> usize {
426    let total_bits = size_of::<T>() * 8;
427    let leading_zeros = value.leading_zeros() as usize;
428    let data_bits = total_bits - leading_zeros;
429    usize::max(1, data_bits.div_ceil(DATA_BITS_PER_BYTE))
430}
431
432/// Encodes a signed integer as a varint using ZigZag encoding.
433fn write_signed<S: SPrim>(value: S, buf: &mut impl BufMut) {
434    write(value.as_zigzag(), buf);
435}
436
437/// Decodes a signed integer from varint ZigZag encoding.
438fn read_signed<S: SPrim>(buf: &mut impl Buf) -> Result<S, Error> {
439    Ok(S::un_zigzag(read(buf)?))
440}
441
442/// Calculates the number of bytes needed to encode a signed integer as a varint.
443fn size_signed<S: SPrim>(value: S) -> usize {
444    size(value.as_zigzag())
445}
446
447#[cfg(test)]
448mod tests {
449    use super::*;
450    use crate::{Copying, DecodeExt, Encode, error::Error};
451    use bytes::{Buf as _, Bytes};
452
453    #[test]
454    fn test_fragmented_varint() {
455        let mut buf = Bytes::from_static(&[0x80]).chain(Bytes::from_static(&[0x01, 0x07]));
456        assert_eq!(read::<u32>(&mut buf).unwrap(), 128);
457        assert_eq!(read::<u32>(&mut buf).unwrap(), 7);
458        assert_eq!(buf.remaining(), 0);
459    }
460
461    #[test]
462    fn test_end_of_buffer() {
463        let mut buf: Bytes = Bytes::from_static(&[]);
464        assert!(matches!(read::<u32>(&mut buf), Err(Error::EndOfBuffer)));
465
466        let mut buf: Bytes = Bytes::from_static(&[0x80, 0x8F]);
467        assert!(matches!(read::<u32>(&mut buf), Err(Error::EndOfBuffer)));
468
469        let mut buf: Bytes = Bytes::from_static(&[0xFF, 0x8F]);
470        assert!(matches!(read::<u32>(&mut buf), Err(Error::EndOfBuffer)));
471    }
472
473    #[test]
474    fn test_overflow() {
475        let mut buf: Bytes = Bytes::from_static(&[0xFF, 0xFF, 0xFF, 0xFF, 0x0F]);
476        assert_eq!(read::<u32>(&mut buf).unwrap(), u32::MAX);
477
478        let mut buf: Bytes = Bytes::from_static(&[0xFF, 0xFF, 0xFF, 0xFF, 0x1F]);
479        assert!(matches!(
480            read::<u32>(&mut buf),
481            Err(Error::InvalidVarint(u32::SIZE))
482        ));
483
484        let mut buf =
485            Bytes::from_static(&[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02]);
486        assert!(matches!(
487            read::<u64>(&mut buf),
488            Err(Error::InvalidVarint(u64::SIZE))
489        ));
490    }
491
492    #[test]
493    fn test_overcontinuation() {
494        let mut buf: Bytes = Bytes::from_static(&[0x80, 0x80, 0x80, 0x80, 0x80]);
495        let result = read::<u32>(&mut buf);
496        assert!(matches!(result, Err(Error::InvalidVarint(u32::SIZE))));
497    }
498
499    #[test]
500    fn test_zeroed_byte() {
501        let mut buf = Bytes::from_static(&[0xFF, 0x00]);
502        let result = read::<u64>(&mut buf);
503        assert!(matches!(result, Err(Error::InvalidVarint(u64::SIZE))));
504    }
505
506    /// Core round-trip check, generic over any UPrim.
507    fn varuint_round_trip<T: UPrim + TryFrom<u128>>() {
508        const CASES: &[u128] = &[
509            0,
510            1,
511            127,
512            128,
513            129,
514            0xFF,
515            0x100,
516            0x3FFF,
517            0x4000,
518            0x1_FFFF,
519            0xFF_FFFF,
520            0x1_FF_FF_FF_FF,
521            0xFF_FF_FF_FF_FF_FF,
522            0x1_FF_FF_FF_FF_FF_FF_FF_FF_FF_FF_FF_FF,
523            u16::MAX as u128,
524            u32::MAX as u128,
525            u64::MAX as u128,
526            u128::MAX,
527        ];
528
529        for &raw in CASES {
530            // skip values that don't fit into T
531            let Ok(value) = raw.try_into() else { continue };
532            let value: T = value;
533
534            // size matches encoding length
535            let mut buf = Vec::new();
536            write(value, &mut buf);
537            assert_eq!(buf.len(), size(value));
538
539            // decode matches original value
540            let mut slice = Copying(&buf);
541            let decoded: T = read(&mut slice).unwrap();
542            assert_eq!(decoded, value);
543            assert!(slice.0.is_empty());
544
545            // UInt wrapper
546            let encoded = UInt(value).encode();
547            assert_eq!(UInt::<T>::decode(encoded).unwrap(), UInt(value));
548        }
549    }
550
551    #[test]
552    fn test_varuint() {
553        varuint_round_trip::<u16>();
554        varuint_round_trip::<u32>();
555        varuint_round_trip::<u64>();
556        varuint_round_trip::<u128>();
557    }
558
559    fn varsint_round_trip<T: SPrim + TryFrom<i128>>() {
560        const CASES: &[i128] = &[
561            0,
562            1,
563            -1,
564            2,
565            -2,
566            127,
567            -127,
568            128,
569            -128,
570            129,
571            -129,
572            0x7FFFFFFF,
573            -0x7FFFFFFF,
574            0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF,
575            -0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF,
576            i16::MIN as i128,
577            i16::MAX as i128,
578            i32::MIN as i128,
579            i32::MAX as i128,
580            i64::MIN as i128,
581            i64::MAX as i128,
582        ];
583
584        for &raw in CASES {
585            // skip values that don't fit into T
586            let Ok(value) = raw.try_into() else { continue };
587            let value: T = value;
588
589            // size matches encoding length
590            let mut buf = Vec::new();
591            write_signed(value, &mut buf);
592            assert_eq!(buf.len(), size_signed(value));
593
594            // decode matches original value
595            let mut slice = Copying(&buf);
596            let decoded: T = read_signed(&mut slice).unwrap();
597            assert_eq!(decoded, value);
598            assert!(slice.0.is_empty());
599
600            // SInt wrapper
601            let encoded = SInt(value).encode();
602            assert_eq!(SInt::<T>::decode(encoded).unwrap(), SInt(value));
603        }
604    }
605
606    #[test]
607    fn test_varsint() {
608        varsint_round_trip::<i16>();
609        varsint_round_trip::<i32>();
610        varsint_round_trip::<i64>();
611        varsint_round_trip::<i128>();
612    }
613
614    #[test]
615    fn test_varuint_into() {
616        let v32: u32 = 0x1_FFFF;
617        let out32: u32 = UInt(v32).into();
618        assert_eq!(v32, out32);
619
620        let v64: u64 = 0x1_FF_FF_FF_FF;
621        let out64: u64 = UInt(v64).into();
622        assert_eq!(v64, out64);
623    }
624
625    #[test]
626    fn test_varsint_into() {
627        let s32: i32 = -123_456;
628        let out32: i32 = SInt(s32).into();
629        assert_eq!(s32, out32);
630
631        let s64: i64 = 987_654_321;
632        let out64: i64 = SInt(s64).into();
633        assert_eq!(s64, out64);
634    }
635
636    #[test]
637    fn test_conformity() {
638        assert_eq!(0usize.encode(), &[0x00][..]);
639        assert_eq!(1usize.encode(), &[0x01][..]);
640        assert_eq!(127usize.encode(), &[0x7F][..]);
641        assert_eq!(128usize.encode(), &[0x80, 0x01][..]);
642        assert_eq!(16383usize.encode(), &[0xFF, 0x7F][..]);
643        assert_eq!(16384usize.encode(), &[0x80, 0x80, 0x01][..]);
644        assert_eq!(2097151usize.encode(), &[0xFF, 0xFF, 0x7F][..]);
645        assert_eq!(2097152usize.encode(), &[0x80, 0x80, 0x80, 0x01][..]);
646        assert_eq!(
647            (u32::MAX as usize).encode(),
648            &[0xFF, 0xFF, 0xFF, 0xFF, 0x0F][..]
649        );
650    }
651
652    #[test]
653    fn test_all_u16_values() {
654        // Exhaustively test all u16 values to ensure size matches encoding
655        for i in 0..=u16::MAX {
656            let value = i;
657            let calculated_size = size(value);
658
659            let mut buf = Vec::new();
660            write(value, &mut buf);
661
662            assert_eq!(
663                buf.len(),
664                calculated_size,
665                "Size mismatch for u16 value {value}",
666            );
667
668            // Also verify UInt wrapper
669            let uint = UInt(value);
670            assert_eq!(
671                uint.encode_size(),
672                buf.len(),
673                "UInt encode_size mismatch for value {value}",
674            );
675        }
676    }
677
678    #[test]
679    fn test_all_i16_values() {
680        // Exhaustively test all i16 values to ensure size matches encoding
681        for i in i16::MIN..=i16::MAX {
682            let value = i;
683            let calculated_size = size_signed(value);
684
685            let mut buf = Vec::new();
686            write_signed(value, &mut buf);
687
688            assert_eq!(
689                buf.len(),
690                calculated_size,
691                "Size mismatch for i16 value {value}",
692            );
693
694            // Also verify SInt wrapper
695            let sint = SInt(value);
696            assert_eq!(
697                sint.encode_size(),
698                buf.len(),
699                "SInt encode_size mismatch for value {value}",
700            );
701
702            // Verify we can decode it back correctly
703            let mut slice = Copying(&buf);
704            let decoded: i16 = read_signed(&mut slice).unwrap();
705            assert_eq!(decoded, value, "Decode mismatch for value {value}");
706            assert!(
707                slice.0.is_empty(),
708                "Buffer not fully consumed for value {value}",
709            );
710        }
711    }
712
713    #[test]
714    fn test_exact_bit_boundaries() {
715        // Test values with exactly N bits set
716        fn test_exact_bits<T: UPrim + TryFrom<u128> + core::fmt::Display>() {
717            for bits in 1..=128 {
718                // Create a value with exactly 'bits' bits
719                // e.g., bits=3 -> 0b111 = 7
720                let val = if bits == 128 {
721                    u128::MAX
722                } else {
723                    (1u128 << bits) - 1
724                };
725                let Ok(value) = T::try_from(val) else {
726                    continue;
727                };
728
729                // Compute expected size
730                let expected_size = (bits as usize).div_ceil(DATA_BITS_PER_BYTE);
731                let calculated_size = size(value);
732                assert_eq!(
733                    calculated_size, expected_size,
734                    "Size calculation wrong for {val} with {bits} bits",
735                );
736
737                // Compare encoded size
738                let mut buf = Vec::new();
739                write(value, &mut buf);
740                assert_eq!(
741                    buf.len(),
742                    expected_size,
743                    "Encoded size wrong for {val} with {bits} bits",
744                );
745            }
746        }
747
748        test_exact_bits::<u16>();
749        test_exact_bits::<u32>();
750        test_exact_bits::<u64>();
751        test_exact_bits::<u128>();
752    }
753
754    #[test]
755    fn test_single_bit_boundaries() {
756        // Test values with only a single bit set at different positions
757        fn test_single_bits<T: UPrim + TryFrom<u128> + core::fmt::Display>() {
758            for bit_pos in 0..128 {
759                // Create a value with only a single bit set at the given position
760                let val = 1u128 << bit_pos;
761                let Ok(value) = T::try_from(val) else {
762                    continue;
763                };
764
765                // Compute expected size
766                let expected_size = ((bit_pos + 1) as usize).div_ceil(DATA_BITS_PER_BYTE);
767                let calculated_size = size(value);
768                assert_eq!(
769                    calculated_size, expected_size,
770                    "Size wrong for 1<<{bit_pos} = {val}",
771                );
772
773                // Compare encoded size
774                let mut buf = Vec::new();
775                write(value, &mut buf);
776                assert_eq!(
777                    buf.len(),
778                    expected_size,
779                    "Encoded size wrong for 1<<{bit_pos} = {val}",
780                );
781            }
782        }
783
784        test_single_bits::<u16>();
785        test_single_bits::<u32>();
786        test_single_bits::<u64>();
787        test_single_bits::<u128>();
788    }
789
790    #[test]
791    fn test_max_varint_size_constants() {
792        let mut buf = Vec::new();
793
794        write(u16::MAX, &mut buf);
795        assert_eq!(buf.len(), MAX_U16_VARINT_SIZE);
796        assert_eq!(buf.len(), UInt::<u16>::MAX_ENCODE_SIZE);
797        buf.clear();
798
799        write(u32::MAX, &mut buf);
800        assert_eq!(buf.len(), MAX_U32_VARINT_SIZE);
801        assert_eq!(buf.len(), UInt::<u32>::MAX_ENCODE_SIZE);
802        buf.clear();
803
804        write(u64::MAX, &mut buf);
805        assert_eq!(buf.len(), MAX_U64_VARINT_SIZE);
806        assert_eq!(buf.len(), UInt::<u64>::MAX_ENCODE_SIZE);
807        buf.clear();
808
809        write(u128::MAX, &mut buf);
810        assert_eq!(buf.len(), MAX_U128_VARINT_SIZE);
811        assert_eq!(buf.len(), UInt::<u128>::MAX_ENCODE_SIZE);
812    }
813
814    #[cfg(feature = "arbitrary")]
815    mod conformance {
816        use super::*;
817        use crate::conformance::CodecConformance;
818
819        commonware_conformance::conformance_tests! {
820            CodecConformance<UInt<u16>>,
821            CodecConformance<UInt<u32>>,
822            CodecConformance<UInt<u64>>,
823            CodecConformance<UInt<u128>>,
824            CodecConformance<SInt<i16>>,
825            CodecConformance<SInt<i32>>,
826            CodecConformance<SInt<i64>>,
827            CodecConformance<SInt<i128>>,
828        }
829    }
830}