1pub const MAX_LEN: usize = 4;
15
16#[derive(Clone, Copy, Debug, PartialEq, Eq)]
17pub enum Error {
18 BufferTooSmall,
20 Malformed,
22}
23
24pub const fn encoded_len(value: i32) -> usize {
26 if value >= i8::MIN as i32 && value <= i8::MAX as i32 {
27 1
28 } else if value >= i16::MIN as i32 && value <= i16::MAX as i32 {
29 2
30 } else if value >= -(1 << 23) && value < (1 << 23) {
31 3
32 } else {
33 4
34 }
35}
36
37pub fn encode(value: i32, out: &mut [u8]) -> Result<usize, Error> {
39 let len = encoded_len(value);
40 let dst = out.get_mut(..len).ok_or(Error::BufferTooSmall)?;
41 dst.copy_from_slice(&value.to_le_bytes()[..len]);
42 Ok(len)
43}
44
45pub fn decode(input: &[u8]) -> Result<i32, Error> {
50 let (&last, rest) = input.split_last().ok_or(Error::Malformed)?;
51 if input.len() > MAX_LEN {
52 return Err(Error::Malformed);
53 }
54 let fill = if last & 0x80 != 0 { 0xFF } else { 0x00 };
55 let mut bytes = [fill; MAX_LEN];
56 bytes[..rest.len()].copy_from_slice(rest);
57 bytes[rest.len()] = last;
58 Ok(i32::from_le_bytes(bytes))
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64
65 fn roundtrip(value: i32, expected_len: usize) {
66 let mut buf = [0u8; MAX_LEN];
67 let len = encode(value, &mut buf).expect("encoding fits");
68 assert_eq!(len, expected_len, "width of {value}");
69 assert_eq!(decode(&buf[..len]), Ok(value));
70 }
71
72 #[test]
73 fn widths_follow_the_value() {
74 roundtrip(0, 1);
75 roundtrip(100, 1);
76 roundtrip(127, 1);
77 roundtrip(-128, 1);
78 roundtrip(128, 2);
79 roundtrip(-129, 2);
80 roundtrip(200, 2);
81 roundtrip(32767, 2);
82 roundtrip(-32768, 2);
83 roundtrip(32768, 3);
84 roundtrip(-32769, 3);
85 roundtrip(8_388_607, 3);
86 roundtrip(-8_388_608, 3);
87 roundtrip(8_388_608, 4);
88 roundtrip(-8_388_609, 4);
89 roundtrip(i32::MAX, 4);
90 roundtrip(i32::MIN, 4);
91 }
92
93 #[test]
94 fn a_padded_encoding_decodes_to_the_same_value() {
95 assert_eq!(decode(&[0x64]), Ok(100));
97 assert_eq!(decode(&[0x64, 0x00]), Ok(100));
98 assert_eq!(decode(&[0x64, 0x00, 0x00, 0x00]), Ok(100));
99 assert_eq!(decode(&[0x9C, 0xFF]), Ok(-100));
100 assert_eq!(decode(&[0x9C, 0xFF, 0xFF, 0xFF]), Ok(-100));
101 }
102
103 #[test]
104 fn lengths_outside_one_through_four_are_malformed() {
105 assert_eq!(decode(&[]), Err(Error::Malformed));
106 assert_eq!(decode(&[0; 5]), Err(Error::Malformed));
107 }
108
109 #[test]
110 fn a_buffer_shorter_than_the_value_is_refused() {
111 let mut buf = [0u8; 1];
112 assert_eq!(encode(1000, &mut buf), Err(Error::BufferTooSmall));
113 }
114}