Skip to main content

steel_utils/codec/
var_long.rs

1use std::io::{Cursor, Error, Write};
2
3use crate::serial::{ReadFrom, WriteTo};
4
5/// A variable-length long integer.
6#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7pub struct VarLong(pub i64);
8
9impl VarLong {
10    /// The maximum number of bytes a `VarLong` can be.
11    pub const MAX_SIZE: usize = 10;
12}
13
14impl ReadFrom for VarLong {
15    fn read(read: &mut Cursor<&[u8]>) -> Result<Self, Error> {
16        let mut val = 0i64;
17        for i in 0..Self::MAX_SIZE {
18            let byte = u8::read(read)?;
19            val |= (i64::from(byte) & 0x7F) << (i * 7);
20            if byte & 0x80 == 0 {
21                return Ok(Self(val));
22            }
23        }
24        Err(Error::other("VarLong too long"))
25    }
26}
27
28impl WriteTo for VarLong {
29    fn write(&self, writer: &mut impl Write) -> Result<(), Error> {
30        let mut val = self.0 as u64;
31        loop {
32            let b: u8 = val as u8 & 0x7F;
33            val >>= 7;
34            if val == 0 {
35                b.write(writer)?;
36                break;
37            }
38            (b | 0x80).write(writer)?;
39        }
40        Ok(())
41    }
42}
43
44impl From<i64> for VarLong {
45    fn from(value: i64) -> Self {
46        Self(value)
47    }
48}
49
50impl From<VarLong> for i64 {
51    fn from(value: VarLong) -> i64 {
52        value.0
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use std::io::Cursor;
60
61    #[test]
62    fn test_varlong_read_write() {
63        let test_values = vec![
64            0i64,
65            1i64,
66            127i64,
67            128i64,
68            255i64,
69            2_147_483_647_i64,
70            9_223_372_036_854_775_807_i64,
71            -1i64,
72            -2_147_483_648_i64,
73        ];
74
75        for val in test_values {
76            let var_long = VarLong(val);
77            let mut buf = Vec::new();
78            var_long.write(&mut buf).expect("write failed");
79
80            let mut cursor = Cursor::new(buf.as_slice());
81            let read_val = VarLong::read(&mut cursor).expect("read failed");
82            assert_eq!(read_val, var_long, "Failed for value {val}");
83        }
84    }
85}