1pub const MAX_VALUE: u32 = 2_097_151;
9
10pub const MAX_LEN: usize = 3;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub enum Error {
15 ValueTooLarge,
17 BufferTooSmall,
19 Truncated,
21 TooLong,
24}
25
26pub const fn encoded_len(value: u32) -> usize {
28 if value < 1 << 7 {
29 1
30 } else if value < 1 << 14 {
31 2
32 } else {
33 3
34 }
35}
36
37pub fn encode(value: u32, out: &mut [u8]) -> Result<usize, Error> {
39 if value > MAX_VALUE {
40 return Err(Error::ValueTooLarge);
41 }
42 let len = encoded_len(value);
43 if out.len() < len {
44 return Err(Error::BufferTooSmall);
45 }
46 let mut remaining = value;
47 for byte in out.iter_mut().take(len - 1) {
48 *byte = (remaining as u8 & 0x7F) | 0x80;
49 remaining >>= 7;
50 }
51 out[len - 1] = remaining as u8;
52 Ok(len)
53}
54
55pub fn decode(input: &[u8]) -> Result<(u32, usize), Error> {
59 let mut value = 0u32;
60 for (index, &byte) in input.iter().enumerate().take(MAX_LEN) {
61 value |= u32::from(byte & 0x7F) << (7 * index);
62 if byte & 0x80 == 0 {
63 return Ok((value, index + 1));
64 }
65 if index + 1 == MAX_LEN {
66 return Err(Error::TooLong);
67 }
68 }
69 Err(Error::Truncated)
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[track_caller]
77 fn round_trip(value: u32, expected: &[u8]) {
78 let mut buf = [0u8; MAX_LEN];
79 let len = encode(value, &mut buf).unwrap();
80 assert_eq!(&buf[..len], expected, "encoding of {value}");
81 assert_eq!(decode(expected).unwrap(), (value, expected.len()));
82 }
83
84 #[test]
85 fn spec_example() {
86 round_trip(1337, &[0xB9, 0x0A]);
88 }
89
90 #[test]
91 fn boundaries() {
92 round_trip(0, &[0x00]);
93 round_trip(127, &[0x7F]);
94 round_trip(128, &[0x80, 0x01]);
95 round_trip(16_383, &[0xFF, 0x7F]);
96 round_trip(16_384, &[0x80, 0x80, 0x01]);
97 round_trip(MAX_VALUE, &[0xFF, 0xFF, 0x7F]);
98 }
99
100 #[test]
101 fn known_identifiers() {
102 round_trip(113, &[0x71]);
104 round_trip(4820, &[0xD4, 0x25]);
105 round_trip(4822, &[0xD6, 0x25]);
106 }
107
108 #[test]
109 fn errors() {
110 let mut buf = [0u8; MAX_LEN];
111 assert_eq!(encode(MAX_VALUE + 1, &mut buf), Err(Error::ValueTooLarge));
112 assert_eq!(encode(128, &mut buf[..1]), Err(Error::BufferTooSmall));
113 assert_eq!(decode(&[]), Err(Error::Truncated));
114 assert_eq!(decode(&[0x80]), Err(Error::Truncated));
115 assert_eq!(decode(&[0x80, 0x80]), Err(Error::Truncated));
116 assert_eq!(decode(&[0x80, 0x80, 0x80]), Err(Error::TooLong));
117 }
118
119 #[test]
120 fn trailing_bytes_ignored() {
121 assert_eq!(decode(&[0x7F, 0xAA, 0xBB]).unwrap(), (127, 1));
122 }
123}