monero_epee_bin_serde/
varint.rs1use 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; const FITS_IN_TWO_BYTES: usize = 16384; const FITS_IN_FOUR_BYTES: usize = 1073741824; 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; let number = number | size_marker; 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}