monero_epee_bin_serde/
varint.rs

1use byteorder::{LittleEndian, ReadBytesExt};
2use std::io;
3
4pub fn encode(number: usize) -> Vec<u8> {
5    const BITS_FOR_SIZE: u32 = 2;
6
7    const FITS_IN_ONE_BYTE: usize = 64; // 2usize.pow(8 - BITS_FOR_SIZE);
8    const FITS_IN_TWO_BYTES: usize = 16384; // 2usize.pow(16 - BITS_FOR_SIZE);
9    const FITS_IN_FOUR_BYTES: usize = 1073741824; // 2usize.pow(32 - BITS_FOR_SIZE);
10
11    let size_marker = if number < FITS_IN_ONE_BYTE {
12        0
13    } else if number < FITS_IN_TWO_BYTES {
14        1
15    } else if number < FITS_IN_FOUR_BYTES {
16        2
17    } else {
18        3
19    };
20
21    let number = number << BITS_FOR_SIZE; // make space for the size marker
22    let number = number | size_marker; // store the size marker in the number
23
24    match size_marker {
25        0 => vec![number as u8],
26        1 => (number as u16).to_le_bytes().to_vec(),
27        2 => (number as u32).to_le_bytes().to_vec(),
28        3 => (number as u64).to_le_bytes().to_vec(),
29        _ => unreachable!(),
30    }
31}
32
33pub fn decode(stream: &mut impl io::BufRead) -> Result<usize, io::Error> {
34    let v = stream.fill_buf()?[0];
35
36    let mask = v & 0x03;
37
38    let number = match mask {
39        0 => stream.read_u8()? as usize,
40        1 => stream.read_u16::<LittleEndian>()? as usize,
41        2 => stream.read_u32::<LittleEndian>()? as usize,
42        3 => stream.read_u64::<LittleEndian>()? as usize,
43        mask => {
44            return Err(io::Error::new(
45                io::ErrorKind::InvalidData,
46                format!("unexpected mask {}", mask),
47            ))
48        }
49    };
50
51    Ok(number >> 2)
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn test_encode_var_int() {
60        let test_cases = &[
61            (0, "00"),
62            (1, "04"),
63            (2, "08"),
64            (3, "0c"),
65            (32, "80"),
66            (64, "0101"),
67            (100, "9101"),
68            (123123123, "ced65a1d"),
69            (9999999999999999, "ffff03bfc91b8e00"),
70            (6472923, "6e138b01"),
71        ];
72
73        for (number, expected) in test_cases {
74            let actual = hex::encode(&encode(*number));
75
76            assert_eq!(&actual, expected)
77        }
78    }
79}