1use alloc::vec::Vec;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum IntegerError {
14 Truncated,
16 TooLarge,
19}
20
21impl core::fmt::Display for IntegerError {
22 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
23 match self {
24 Self::Truncated => f.write_str("integer truncated"),
25 Self::TooLarge => f.write_str("integer too large"),
26 }
27 }
28}
29
30#[cfg(feature = "std")]
31impl std::error::Error for IntegerError {}
32
33#[must_use]
39pub fn encode_integer(value: u64, prefix_bits: u8, out_byte_prefix_bits: u8) -> Vec<u8> {
40 let mask = (1u64 << prefix_bits) - 1;
41 let mut out = Vec::new();
42 if value < mask {
43 out.push(out_byte_prefix_bits | (value as u8));
44 return out;
45 }
46 out.push(out_byte_prefix_bits | mask as u8);
47 let mut v = value - mask;
48 while v >= 128 {
49 out.push((v & 0x7f) as u8 | 0x80);
50 v >>= 7;
51 }
52 out.push(v as u8);
53 out
54}
55
56pub fn decode_integer(input: &[u8], prefix_bits: u8) -> Result<(u64, usize), IntegerError> {
63 if input.is_empty() {
64 return Err(IntegerError::Truncated);
65 }
66 let mask = (1u64 << prefix_bits) - 1;
67 let first = u64::from(input[0]) & mask;
68 if first < mask {
69 return Ok((first, 1));
70 }
71 let mut value = mask;
72 let mut shift: u32 = 0;
73 let mut idx = 1;
74 while idx < input.len() {
75 let b = input[idx];
76 idx += 1;
77 if shift >= 56 {
78 return Err(IntegerError::TooLarge);
79 }
80 value = value
81 .checked_add(u64::from(b & 0x7f) << shift)
82 .ok_or(IntegerError::TooLarge)?;
83 shift += 7;
84 if (b & 0x80) == 0 {
85 return Ok((value, idx));
86 }
87 }
88 Err(IntegerError::Truncated)
89}
90
91#[cfg(test)]
92#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn rfc7541_c1_1_encoded_10_in_5bit_prefix() {
98 let buf = encode_integer(10, 5, 0);
100 assert_eq!(buf, alloc::vec![0x0a]);
101 assert_eq!(decode_integer(&buf, 5).unwrap(), (10, 1));
102 }
103
104 #[test]
105 fn rfc7541_c1_2_encoded_1337_in_5bit_prefix() {
106 let buf = encode_integer(1337, 5, 0);
108 assert_eq!(buf, alloc::vec![0x1f, 0x9a, 0x0a]);
109 assert_eq!(decode_integer(&buf, 5).unwrap(), (1337, 3));
110 }
111
112 #[test]
113 fn rfc7541_c1_3_encoded_42_in_8bit_prefix() {
114 let buf = encode_integer(42, 8, 0);
116 assert_eq!(buf, alloc::vec![0x2a]);
117 assert_eq!(decode_integer(&buf, 8).unwrap(), (42, 1));
118 }
119
120 #[test]
121 fn round_trip_small_values() {
122 for v in 0..256u64 {
123 let buf = encode_integer(v, 7, 0);
124 let (decoded, _) = decode_integer(&buf, 7).unwrap();
125 assert_eq!(decoded, v);
126 }
127 }
128
129 #[test]
130 fn round_trip_large_values() {
131 for v in [u64::from(u32::MAX), 1_000_000, 1u64 << 32] {
132 let buf = encode_integer(v, 5, 0);
133 let (decoded, _) = decode_integer(&buf, 5).unwrap();
134 assert_eq!(decoded, v);
135 }
136 }
137
138 #[test]
139 fn truncated_continuation_rejected() {
140 let buf = alloc::vec![0x1f];
142 assert_eq!(decode_integer(&buf, 5), Err(IntegerError::Truncated));
143 }
144
145 #[test]
146 fn empty_input_truncated() {
147 assert_eq!(decode_integer(&[], 5), Err(IntegerError::Truncated));
148 }
149
150 #[test]
151 fn prefix_bits_left_other_bits_alone() {
152 let buf = encode_integer(5, 5, 0xc0); assert_eq!(buf[0] & 0xe0, 0xc0);
154 assert_eq!(buf[0] & 0x1f, 5);
155 }
156}