Skip to main content

cu_bincode/
uleb128.rs

1//! Selective ULEB128 integer encoding, independent of the enclosing configuration.
2
3use crate::de::{Decoder, read::Reader};
4use crate::enc::{Encoder, write::Writer};
5use crate::error::{DecodeError, EncodeError};
6use crate::{Decode, Encode};
7
8/// Encodes just this integer as unsigned LEB128, using zigzag for signed values.
9///
10/// Each byte carries seven low-order bits and a continuation bit. This wrapper
11/// ignores the configuration's integer encoding and endianness; ordinary values
12/// alongside it continue to use the enclosing configuration. Decode with the same
13/// wrapper and integer type. All Rust signed and unsigned integer types are supported.
14///
15/// ```
16/// use cu_bincode::{config, decode_from_slice, encode_into_slice, Uleb128};
17/// let mut bytes = [0; 16];
18/// let len = encode_into_slice((300u32, Uleb128(-150i128)), &mut bytes, config::standard()).unwrap();
19/// assert_eq!(&bytes[..len], &[251, 44, 1, 171, 2]);
20/// let (decoded, used) = decode_from_slice::<(u32, Uleb128<i128>), _>(&bytes[..len], config::standard()).unwrap();
21/// assert_eq!(decoded, (300, Uleb128(-150)));
22/// assert_eq!(used, len);
23/// ```
24#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
25#[repr(transparent)]
26pub struct Uleb128<T>(pub T);
27
28#[inline]
29fn encode<E: Encoder>(mut value: u128, encoder: &mut E) -> Result<(), EncodeError> {
30    loop {
31        let mut byte = (value & 0x7f) as u8;
32        value >>= 7;
33        if value != 0 {
34            byte |= 0x80;
35        }
36        encoder.writer().write(&[byte])?;
37        if value == 0 {
38            return Ok(());
39        }
40    }
41}
42
43#[inline]
44fn decode<D: Decoder>(bits: u32, decoder: &mut D) -> Result<u128, DecodeError> {
45    let mut value = 0u128;
46    for shift in (0..bits).step_by(7) {
47        let mut byte = [0];
48        decoder.reader().read(&mut byte)?;
49        let chunk = u128::from(byte[0] & 0x7f);
50        let remaining = bits - shift;
51        if remaining < 7 && chunk >= (1u128 << remaining) {
52            return Err(DecodeError::Other("ULEB128 integer overflow"));
53        }
54        value |= chunk << shift;
55        if byte[0] & 0x80 == 0 {
56            return Ok(value);
57        }
58    }
59    Err(DecodeError::Other("ULEB128 integer is too long"))
60}
61
62macro_rules! unsigned {
63    ($($ty:ty),* $(,)?) => {$ (
64        impl Encode for Uleb128<$ty> {
65            #[inline]
66            fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
67                encode(self.0 as u128, encoder)
68            }
69        }
70
71        impl<Context> Decode<Context> for Uleb128<$ty> {
72            #[inline]
73            fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
74                decoder.claim_bytes_read(core::mem::size_of::<$ty>())?;
75                decode(<$ty>::BITS, decoder).map(|value| Self(value as $ty))
76            }
77        }
78        crate::impl_borrow_decode!(Uleb128<$ty>);
79    )*};
80}
81
82macro_rules! signed {
83    ($($ty:ty => $unsigned:ty),* $(,)?) => {$ (
84        impl Encode for Uleb128<$ty> {
85            #[inline]
86            fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
87                let value = self.0;
88                let zigzag = ((value as $unsigned) << 1) ^ ((value >> (<$ty>::BITS - 1)) as $unsigned);
89                encode(zigzag as u128, encoder)
90            }
91        }
92
93        impl<Context> Decode<Context> for Uleb128<$ty> {
94            #[inline]
95            fn decode<D: Decoder<Context = Context>>(decoder: &mut D) -> Result<Self, DecodeError> {
96                decoder.claim_bytes_read(core::mem::size_of::<$ty>())?;
97                let value = decode(<$ty>::BITS, decoder)? as $unsigned;
98                Ok(Self(((value >> 1) as $ty) ^ -((value & 1) as $ty)))
99            }
100        }
101        crate::impl_borrow_decode!(Uleb128<$ty>);
102    )*};
103}
104
105unsigned!(u8, u16, u32, u64, u128, usize);
106signed!(i8 => u8, i16 => u16, i32 => u32, i64 => u64, i128 => u128, isize => usize);