1use deku::{
2 bitvec::{BitSlice, BitVec, Msb0},
3 ctx::{BitSize, Endian},
4 prelude::*,
5};
6
7#[derive(Debug, Clone, PartialEq)]
8pub enum VarIntError {
9 ValueTooLarge(u32),
10 ExponentTooLarge(u8),
11 MantissaTooLarge(u8),
12 Unknown,
13}
14
15impl Into<DekuError> for VarIntError {
16 fn into(self) -> DekuError {
17 match self {
18 VarIntError::ValueTooLarge(value) => DekuError::InvalidParam(format!(
19 "VarInt: Value too large: {:?}. Max: {:?}",
20 value,
21 VarInt::MAX
22 )),
23 VarIntError::ExponentTooLarge(exponent) => DekuError::InvalidParam(format!(
24 "VarInt: Exponent too large {:?}. Max: {:?}",
25 exponent,
26 2 ^ 3
27 )),
28 VarIntError::MantissaTooLarge(mantissa) => DekuError::InvalidParam(format!(
29 "VarInt: Mantissa too large {:?}. Max: {:?}",
30 mantissa,
31 2 ^ 5
32 )),
33 VarIntError::Unknown => DekuError::Unexpected("VarInt: Unknown error".to_string()),
34 }
35 }
36}
37
38#[derive(DekuRead, DekuWrite, Default, Debug, Clone, Copy, PartialEq)]
41pub struct VarInt {
42 #[deku(
43 reader = "VarInt::read(deku::rest)",
44 writer = "VarInt::write(deku::output, &self.value, &self.ceil)"
45 )]
46 value: u32,
47
48 #[deku(skip, default = "false")]
49 ceil: bool,
50}
51
52impl VarInt {
53 pub const MAX: u32 = 507904;
54
55 pub fn new(value: u32, ceil: bool) -> Result<Self, VarIntError> {
56 if !Self::is_valid(value) {
57 Err(VarIntError::ValueTooLarge(value))
58 } else {
59 Ok(Self { value, ceil })
60 }
61 }
62
63 pub fn new_unchecked(value: u32, ceil: bool) -> Self {
64 Self { value, ceil }
65 }
66
67 pub fn decompress(exponent: u8, mantissa: u8) -> Result<u32, VarIntError> {
68 if exponent & 0b11111000 > 0 {
69 return Err(VarIntError::ExponentTooLarge(exponent));
70 }
71
72 if mantissa & 0b11100000 > 0 {
73 return Err(VarIntError::ExponentTooLarge(mantissa));
74 }
75
76 Ok(4u32.pow(exponent as u32) * mantissa as u32)
77 }
78
79 pub fn compress(
80 value: u32,
81 ceil: bool,
82 ) -> Result<(u8, u8), VarIntError> {
83 if !Self::is_valid(value) {
84 return Err(VarIntError::ValueTooLarge(value));
85 }
86
87 for i in 0..8 {
88 let exp = 4u32.pow(i);
89
90 if value <= (exp * 31) {
91 let mut mantissa = value / exp;
92 let remainder = value % exp;
93
94 if ceil && remainder > 0 {
95 mantissa += 1;
96 }
97 return Ok((i as u8, mantissa as u8));
98 }
99 }
100
101 Err(VarIntError::Unknown)
103 }
104
105 pub fn is_valid(n: u32) -> bool {
108 n <= Self::MAX
109 }
110
111 fn read(rest: &BitSlice<u8, Msb0>) -> Result<(&BitSlice<u8, Msb0>, u32), DekuError> {
112 let (rest, exponent) = <u8 as DekuRead<'_, _>>::read(rest, (Endian::Big, BitSize(3)))?;
113 let (rest, mantissa) = <u8 as DekuRead<'_, _>>::read(rest, (Endian::Big, BitSize(5)))?;
114
115 Self::decompress(exponent, mantissa)
116 .map_err(Into::into)
117 .map(|value| (rest, value))
118 }
119
120 fn write(output: &mut BitVec<u8, Msb0>, value: &u32, ceil: &bool) -> Result<(), DekuError> {
121 match Self::compress(*value, *ceil) {
122 Ok((exponent, mantissa)) => {
123 DekuWrite::write(&exponent, output, (Endian::Big, BitSize(3)))?;
124 DekuWrite::write(&mantissa, output, (Endian::Big, BitSize(5)))?;
125 Ok(())
126 }
127 Err(err) => Err(err.into()),
128 }
129 }
130}
131
132impl Into<u32> for VarInt {
133 fn into(self) -> u32 {
134 self.value as u32
135 }
136}
137
138impl From<u32> for VarInt {
139 fn from(value: u32) -> Self {
140 Self { value, ceil: false }
141 }
142}
143
144#[cfg(test)]
145mod test {
146 use super::*;
147 use crate::test_tools::test_item;
148
149 #[test]
150 fn test_is_valid() {
151 assert!(VarInt::is_valid(507904));
152 assert!(!VarInt::is_valid(0x40_00_00_00));
153 }
154
155 #[test]
156 fn test_decompress() {
157 assert_eq!(0, VarInt::decompress(0, 0).unwrap());
158 assert_eq!(4, VarInt::decompress(1, 1).unwrap());
159 assert_eq!(32, VarInt::decompress(2, 2).unwrap());
160 assert_eq!(192, VarInt::decompress(3, 3).unwrap());
161 assert_eq!(507904, VarInt::decompress(7, 31).unwrap());
162 }
163
164 #[test]
165 fn test() {
166 test_item(VarInt::default(), &[0x00]);
167 test_item(VarInt::new_unchecked(1, false), &[0x01u8]);
168 test_item(VarInt::new_unchecked(32, false), &[0b00101000u8]);
169 test_item(VarInt::new_unchecked(507904, false), &[0xFFu8]);
170 }
171}