Skip to main content

rustmeter_beacon_core/
varint.rs

1/// Trait for efficient ZigZag encoding and decoding of signed integers.
2pub trait ZigZag: Sized + Copy {
3    type Unsigned;
4
5    /// Encodes a signed integer into an unsigned integer.
6    /// -1 -> 1, 0 -> 0, 1 -> 2, etc.
7    fn zigzag_encode(self) -> Self::Unsigned;
8
9    /// Decodes an unsigned integer back into a signed integer (inverse of encode).
10    fn zigzag_decode(encoded: Self::Unsigned) -> Self;
11}
12
13// Macro for implementing
14macro_rules! impl_zigzag {
15    ($signed:ty, $unsigned:ty) => {
16        impl ZigZag for $signed {
17            type Unsigned = $unsigned;
18
19            #[inline(always)]
20            fn zigzag_encode(self) -> Self::Unsigned {
21                const BITS: u32 = <$signed>::BITS;
22                // (n << 1) ^ (n >> 31)
23                // Uses arithmetic shift (>>), which preserves the sign bit.
24                ((self << 1) ^ (self >> (BITS - 1))) as $unsigned
25            }
26
27            #[inline(always)]
28            fn zigzag_decode(encoded: Self::Unsigned) -> Self {
29                // (n >> 1) ^ -(n & 1)
30                // -(n & 1) is either 0 (if even) or -1 (all bits 1, if odd)
31                (encoded >> 1) as $signed ^ -((encoded & 1) as $signed)
32            }
33        }
34    };
35}
36
37// Implementations for common types
38impl_zigzag!(i8, u8);
39impl_zigzag!(i16, u16);
40impl_zigzag!(i32, u32);
41impl_zigzag!(i64, u64);
42impl_zigzag!(isize, usize);
43
44#[cfg(test)]
45mod tests {
46    use super::ZigZag;
47    #[test]
48    fn test_zigzag() {
49        let enc = (-1i8).zigzag_encode();
50        assert_eq!(enc, 1u8);
51        let dec = i8::zigzag_decode(enc);
52        assert_eq!(dec, -1i8);
53
54        let enc = (0i16).zigzag_encode();
55        assert_eq!(enc, 0u16);
56        let dec = i16::zigzag_decode(enc);
57        assert_eq!(dec, 0i16);
58
59        let enc = (1i32).zigzag_encode();
60        assert_eq!(enc, 2u32);
61        let dec = i32::zigzag_decode(enc);
62        assert_eq!(dec, 1i32);
63
64        let enc = (-12345i64).zigzag_encode();
65        assert_eq!(enc, 24689u64);
66        let dec = i64::zigzag_decode(enc);
67        assert_eq!(dec, -12345i64);
68    }
69}
70
71/// Trait for writing VarInts
72/// Allows generic VarInt writing for unsigned integer types.
73pub trait VarIntWritable: Copy + PartialOrd {
74    /// Returns whether the value is zero.
75    fn is_zero(self) -> bool;
76
77    /// Returns the lower 7 bits as a byte.
78    fn low_7_bits(self) -> u8;
79
80    /// Shifts the value right by 7 bits (in-place).
81    fn shr_7(&mut self);
82}
83
84macro_rules! impl_varint_writable {
85    ($($t:ty),*) => {
86        $(
87            impl VarIntWritable for $t {
88                #[inline(always)]
89                fn is_zero(self) -> bool {
90                    self == 0
91                }
92
93                #[inline(always)]
94                fn low_7_bits(self) -> u8 {
95                    (self & 0x7F) as u8
96                }
97
98                #[inline(always)]
99                fn shr_7(&mut self) {
100                    *self >>= 7;
101                }
102            }
103        )*
104    };
105}
106
107impl_varint_writable!(u8, u16, u32, u64, usize, u128);