1#![doc= include_str!("../.rustme/docs.md")]
2#![forbid(unsafe_code)]
3#![warn(
4 clippy::cargo,
5 missing_docs,
6 clippy::nursery,
8 clippy::pedantic,
9 future_incompatible,
10 rust_2018_idioms,
11)]
12#![cfg_attr(doc, allow(unknown_lints), warn(rustdoc::all))] #![allow(
14 clippy::missing_errors_doc, clippy::option_if_let_else,
16 clippy::module_name_repetitions,
17 clippy::cast_sign_loss,
18 clippy::cast_possible_truncation,
19)]
20
21mod signed;
22mod unsigned;
23
24use std::io::{Read, Write};
25
26pub use self::signed::*;
27pub use self::unsigned::*;
28
29pub trait Variable: Sized {
31 fn encode_variable<W: Write>(&self, destination: W) -> std::io::Result<usize>;
33 fn decode_variable<R: Read>(source: R) -> std::io::Result<Self>;
35
36 fn to_variable_vec(&self) -> std::io::Result<Vec<u8>> {
38 let mut output = Vec::with_capacity(16);
39 self.encode_variable(&mut output)?;
40 Ok(output)
41 }
42}
43
44macro_rules! impl_primitive_variable {
45 ($ty:ty, $dest:ty) => {
46 impl Variable for $ty {
47 fn encode_variable<W: Write>(&self, destination: W) -> std::io::Result<usize> {
48 <$dest>::encode_be_bytes(self.to_be_bytes(), destination)
49 }
50
51 fn decode_variable<R: Read>(source: R) -> std::io::Result<Self> {
52 <$dest>::decode_variable_bytes(source).map(<Self>::from_be_bytes)
53 }
54 }
55 };
56}
57
58impl_primitive_variable!(u8, Unsigned);
59impl_primitive_variable!(u16, Unsigned);
60impl_primitive_variable!(u32, Unsigned);
61impl_primitive_variable!(u64, Unsigned);
62impl_primitive_variable!(u128, Unsigned);
63impl_primitive_variable!(usize, Unsigned);
64
65impl_primitive_variable!(i8, Signed);
66impl_primitive_variable!(i16, Signed);
67impl_primitive_variable!(i32, Signed);
68impl_primitive_variable!(i64, Signed);
69impl_primitive_variable!(i128, Signed);
70impl_primitive_variable!(isize, Signed);
71
72#[cfg(test)]
73mod tests {
74 use std::fmt::Debug;
75
76 use super::*;
77
78 trait TestType: Variable {
79 type Variable: From<Self> + TryInto<Self> + Variable + Eq + Debug;
80 }
81
82 macro_rules! impl_test_type {
83 ($name:ident, $kind:ident) => {
84 impl TestType for $name {
85 type Variable = $kind;
86 }
87 };
88 }
89
90 impl_test_type!(u8, Unsigned);
91 impl_test_type!(u16, Unsigned);
92 impl_test_type!(u32, Unsigned);
93 impl_test_type!(u64, Unsigned);
94 impl_test_type!(u128, Unsigned);
95 impl_test_type!(usize, Unsigned);
96
97 impl_test_type!(i8, Signed);
98 impl_test_type!(i16, Signed);
99 impl_test_type!(i32, Signed);
100 impl_test_type!(i64, Signed);
101 impl_test_type!(i128, Signed);
102 impl_test_type!(isize, Signed);
103
104 fn roundtrip<T: TestType + Eq + Debug + Copy>(value: T, expected_bytes: usize) {
105 let mut bytes = Vec::new();
106 let encoded_length = value.encode_variable(&mut bytes).unwrap();
107 println!("Encoded {value:?} to {bytes:02x?}");
108 assert_eq!(
109 encoded_length, expected_bytes,
110 "expected {expected_bytes} encoded bytes, got {encoded_length}"
111 );
112 assert_eq!(
113 encoded_length,
114 bytes.len(),
115 "vec has more bytes than returned value"
116 );
117 let decoded = T::decode_variable(&bytes[..]).unwrap();
118 assert_eq!(
119 decoded, value,
120 "decoded value did not match: {value:?} vs {decoded:?}",
121 );
122
123 let variable = <T::Variable as From<T>>::from(value);
126 let mut bytes = Vec::new();
127 let encoded_length = variable.encode_variable(&mut bytes).unwrap();
128 assert_eq!(
129 encoded_length, expected_bytes,
130 "expected {expected_bytes} encoded bytes, got {encoded_length}"
131 );
132 assert_eq!(
133 encoded_length,
134 bytes.len(),
135 "vec has more bytes than returned value"
136 );
137 let decoded = <T::Variable as Variable>::decode_variable(&bytes[..]).unwrap();
138 assert_eq!(
139 decoded, variable,
140 "decoded value did not match: {variable:?} vs {decoded:?}",
141 );
142 }
143
144 #[test]
145 fn roundtrip_u8() {
146 roundtrip(u8::MIN, 1);
147 roundtrip(2_u8.pow(4) - 1, 1);
148 roundtrip(2_u8.pow(4), 2);
149 }
150
151 #[test]
152 fn roundtrip_i8() {
153 roundtrip(0_i8, 1);
154 roundtrip(2_i8.pow(3) - 1, 1);
155 roundtrip(-(2_i8.pow(3)), 1);
156
157 roundtrip(2_i8.pow(3), 2);
158 roundtrip(-(2_i8.pow(3) + 1), 2);
159
160 roundtrip(-1_i8, 1);
161 }
162
163 #[test]
164 fn roundtrip_u16() {
165 roundtrip(u16::from(u8::MAX), 2);
166 roundtrip(2_u16.pow(12) - 1, 2);
167 roundtrip(2_u16.pow(12), 3);
168 }
169
170 #[test]
171 fn roundtrip_i16() {
172 roundtrip(i16::from(i8::MAX), 2);
173 roundtrip(i16::from(i8::MIN), 2);
174 roundtrip(2_i16.pow(11) - 1, 2);
175 roundtrip(-(2_i16.pow(11)), 2);
176
177 roundtrip(2_i16.pow(11), 3);
178 roundtrip(-(2_i16.pow(11) + 1), 3);
179
180 roundtrip(-1_i16, 1);
181 }
182
183 #[test]
184 fn roundtrip_u32() {
185 roundtrip(u32::from(u16::MAX), 3);
186 roundtrip(2_u32.pow(20) - 1, 3);
187 roundtrip(2_u32.pow(20), 4);
188 roundtrip(2_u32.pow(28) - 1, 4);
189 roundtrip(2_u32.pow(28), 5);
190 }
191
192 #[test]
193 fn roundtrip_i32() {
194 roundtrip(i32::from(i16::MAX), 3);
195 roundtrip(i32::from(i16::MIN), 3);
196 roundtrip(2_i32.pow(19) - 1, 3);
197 roundtrip(-(2_i32.pow(19)), 3);
198 roundtrip(2_i32.pow(19), 4);
199 roundtrip(-(2_i32.pow(19) + 1), 4);
200
201 roundtrip(2_i32.pow(27), 5);
202 roundtrip(-(2_i32.pow(27) + 1), 5);
203
204 roundtrip(-1_i32, 1);
205 }
206
207 #[test]
208 fn roundtrip_u64() {
209 roundtrip(u64::from(u32::MAX), 5);
210 roundtrip(2_u64.pow(36) - 1, 5);
211 roundtrip(2_u64.pow(36), 6);
212 roundtrip(2_u64.pow(44) - 1, 6);
213 roundtrip(2_u64.pow(44), 7);
214 roundtrip(2_u64.pow(52) - 1, 7);
215 roundtrip(2_u64.pow(52), 8);
216 roundtrip(2_u64.pow(60) - 1, 8);
217 roundtrip(2_u64.pow(60), 9);
218 }
219
220 #[test]
221 fn roundtrip_i64() {
222 roundtrip(i64::from(i32::MAX), 5);
223 roundtrip(i64::from(i32::MIN), 5);
224 roundtrip(2_i64.pow(35) - 1, 5);
225 roundtrip(-(2_i64.pow(35)), 5);
226 roundtrip(2_i64.pow(35), 6);
227 roundtrip(-(2_i64.pow(35) + 1), 6);
228
229 roundtrip(2_i64.pow(43), 7);
230 roundtrip(-(2_i64.pow(43) + 1), 7);
231
232 roundtrip(2_i64.pow(51), 8);
233 roundtrip(-(2_i64.pow(51) + 1), 8);
234
235 roundtrip(2_i64.pow(59), 9);
236 roundtrip(-(2_i64.pow(59) + 1), 9);
237
238 roundtrip(-1_i64, 1);
239 }
240
241 #[test]
242 fn roundtrip_u128() {
243 roundtrip(u128::from(u64::MAX), 9);
244 roundtrip(2_u128.pow(68) - 1, 9);
245 roundtrip(2_u128.pow(68), 10);
246 roundtrip(2_u128.pow(76) - 1, 10);
247 roundtrip(2_u128.pow(76), 11);
248 roundtrip(2_u128.pow(84) - 1, 11);
249 roundtrip(2_u128.pow(84), 12);
250 roundtrip(2_u128.pow(92) - 1, 12);
251 roundtrip(2_u128.pow(92), 13);
252 roundtrip(2_u128.pow(100) - 1, 13);
253 roundtrip(2_u128.pow(100), 14);
254 roundtrip(2_u128.pow(108) - 1, 14);
255 roundtrip(2_u128.pow(108), 15);
256 roundtrip(2_u128.pow(116) - 1, 15);
257 roundtrip(2_u128.pow(116), 16);
258
259 roundtrip(2_u128.pow(124) - 1, 16);
261
262 assert!(2_u128.pow(124).encode_variable(&mut Vec::new()).is_err());
264 }
265
266 #[test]
267 fn roundtrip_i128() {
268 roundtrip(i128::from(i64::MAX), 9);
269 roundtrip(i128::from(i64::MIN), 9);
270 roundtrip(2_i128.pow(67) - 1, 9);
271 roundtrip(-(2_i128.pow(67)), 9);
272 roundtrip(2_i128.pow(67), 10);
273 roundtrip(-(2_i128.pow(67) + 1), 10);
274
275 roundtrip(2_i128.pow(75), 11);
276 roundtrip(-(2_i128.pow(75) + 1), 11);
277
278 roundtrip(2_i128.pow(83), 12);
279 roundtrip(-(2_i128.pow(83) + 1), 12);
280
281 roundtrip(2_i128.pow(91), 13);
282 roundtrip(-(2_i128.pow(91) + 1), 13);
283
284 roundtrip(2_i128.pow(99), 14);
285 roundtrip(-(2_i128.pow(99) + 1), 14);
286
287 roundtrip(2_i128.pow(107), 15);
288 roundtrip(-(2_i128.pow(107) + 1), 15);
289
290 roundtrip(2_i128.pow(115), 16);
291 roundtrip(-(2_i128.pow(115) + 1), 16);
292
293 roundtrip(2_i128.pow(123) - 1, 16);
295 roundtrip(-(2_i128.pow(123)), 16);
297
298 assert!(2_i128.pow(123).encode_variable(&mut Vec::new()).is_err());
300 assert!((-(2_i128.pow(123)) - 1)
302 .encode_variable(&mut Vec::new())
303 .is_err());
304 }
305
306 #[test]
307 fn roundtrip_sizes() {
308 roundtrip(usize::MAX, std::mem::size_of::<usize>() + 1);
311 roundtrip(usize::MIN, 1);
312 roundtrip(isize::MAX, std::mem::size_of::<isize>() + 1);
313 roundtrip(isize::MIN, std::mem::size_of::<isize>() + 1);
314 roundtrip(0_isize, 1);
315 }
316
317 #[test]
318 fn conversions() {
319 assert_eq!(
320 isize::try_from(Signed::from(isize::MAX)).unwrap(),
321 isize::MAX
322 );
323 assert_eq!(
324 usize::try_from(Unsigned::from(usize::MAX)).unwrap(),
325 usize::MAX
326 );
327 assert_eq!(i128::try_from(Signed::from(i128::MAX)).unwrap(), i128::MAX);
328 assert_eq!(
329 u128::try_from(Unsigned::from(u128::MAX)).unwrap(),
330 u128::MAX
331 );
332 }
333
334 #[test]
335 fn test_signed_ordering() {
336 let mut entries = Vec::new();
337 for i in i16::MIN..=i16::MAX {
338 println!("{} => {:02X?}", i, i.to_variable_vec().unwrap());
339 entries.push(i.to_variable_vec().unwrap());
340 }
341 let originals = entries.clone();
342 entries.sort();
343 assert_eq!(originals, entries);
344 }
345
346 #[test]
347 fn test_unsigned_ordering() {
348 let mut entries = Vec::new();
349 for i in u16::MIN..=u16::MAX {
350 println!("{} => {:02X?}", i, i.to_variable_vec().unwrap());
351 entries.push(i.to_variable_vec().unwrap());
352 }
353 let originals = entries.clone();
354 entries.sort();
355 assert_eq!(originals, entries);
356 }
357
358 #[test]
359 fn overflow_decode() {
360 let unsigned_max = u64::MAX.to_variable_vec().unwrap();
361 u32::decode_variable(&unsigned_max[..]).expect_err("u32 should overflow");
362 let signed_min = i64::MIN.to_variable_vec().unwrap();
363 i32::decode_variable(&signed_min[..]).expect_err("i32 should overflow");
364 let signed_max = i64::MAX.to_variable_vec().unwrap();
365 i32::decode_variable(&signed_max[..]).expect_err("i32 should overflow");
366 }
367}