1#![forbid(unsafe_code)]
68#![deny(missing_docs)]
69#![deny(warnings)]
70#![deny(clippy::nursery)]
71#![deny(clippy::pedantic)]
72#![deny(clippy::all)]
73
74use std::io::{Read, Result, Write};
75
76pub const MAX_BYTES_PACKED: usize = 5;
78
79#[inline]
84pub fn pack<T: Write + ?Sized>(dst: &mut T, mut value: i32) -> Result<()> {
85 let mut current_byte: u8 = 0;
86
87 if value < 0 {
95 current_byte = 0b0100_0000;
96 value = !value;
97 }
98
99 current_byte |= u8::try_from(value & 0b0011_1111).expect("should always be inside the range");
101 value >>= 6;
102
103 while value != 0 {
104 current_byte |= 0b1000_0000;
106 dst.write_all(std::slice::from_ref(¤t_byte))?;
107 current_byte =
109 u8::try_from(value & 0b0111_1111).expect("should always be inside the range");
110 value >>= 7;
112 }
113
114 dst.write_all(std::slice::from_ref(¤t_byte))?;
115
116 Ok(())
117}
118
119#[inline]
124pub fn unpack<T: Read + ?Sized>(src: &mut T) -> Result<i32> {
125 const MASKS: [i32; 4] = [0x7F, 0x7F, 0x7F, 0x0F];
126 const SHIFTS: [i32; 4] = [6, 6 + 7, 6 + 7 + 7, 6 + 7 + 7 + 7];
127
128 let mut result: i32;
130 let mut current_byte: u8 = 0;
131
132 src.read_exact(std::slice::from_mut(&mut current_byte))?;
133 let sign = (current_byte >> 6) & 1;
134 result = i32::from(current_byte & 0x3F);
135
136 for (mask, shift) in MASKS.into_iter().zip(SHIFTS.into_iter()) {
137 if (current_byte & 0x80) == 0 {
138 break;
139 }
140
141 src.read_exact(std::slice::from_mut(&mut current_byte))?;
142 result |= (i32::from(current_byte) & mask) << shift;
143 }
144
145 result ^= -i32::from(sign);
146
147 Ok(result)
148}
149
150pub trait PackTwInt {
156 fn pack<T: Write + ?Sized>(self, dst: &mut T) -> Result<()>;
161}
162
163impl PackTwInt for i32 {
164 #[inline]
165 fn pack<T: Write + ?Sized>(self, dst: &mut T) -> Result<()> {
166 pack(dst, self)
167 }
168}
169
170pub trait UnPackTwInt: Read {
174 fn unpack(&mut self) -> Result<i32>;
178}
179
180impl<T: Read + ?Sized> UnPackTwInt for T {
181 #[inline]
182 fn unpack(&mut self) -> Result<i32> {
183 unpack(self)
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use std::io::Cursor;
190
191 use super::*;
192
193 #[test]
194 pub fn unpack_0() {
195 let mut buff = Cursor::new([0; 1]);
196 assert!(pack(&mut buff, 0).is_ok());
197 buff.set_position(0);
198 assert_eq!(0, unpack(&mut buff).unwrap());
199 }
200
201 #[test]
202 pub fn pack_0() {
203 let mut buff = Cursor::new([0; 1]);
204 assert!(pack(&mut buff, 0).is_ok());
205 let buff = buff.into_inner();
206 assert_eq!(buff[0], 0b0000_0000);
207 }
208
209 #[test]
210 pub fn pack_1() {
211 let mut buff = Cursor::new([0; 1]);
212 assert!(pack(&mut buff, 1).is_ok());
213 let buff = buff.into_inner();
214 assert_eq!(buff[0], 0b0000_0001);
215 }
216
217 #[test]
218 pub fn unpack_1() {
219 let mut buff = Cursor::new([0; 1]);
220 assert!(pack(&mut buff, 1).is_ok());
221 buff.set_position(0);
222 assert_eq!(1, unpack(&mut buff).unwrap());
223 }
224
225 #[test]
226 pub fn pack_2() {
227 let mut buff = Cursor::new([0; 1]);
228 assert!(pack(&mut buff, 2).is_ok());
229 let buff = buff.into_inner();
230 assert_eq!(buff[0], 0b0000_0010);
231 }
232
233 #[test]
234 pub fn unpack_2() {
235 let mut buff = Cursor::new([0; 1]);
236 assert!(pack(&mut buff, 2).is_ok());
237 buff.set_position(0);
238 assert_eq!(2, unpack(&mut buff).unwrap());
239 }
240
241 #[test]
242 pub fn pack_minus_2() {
243 let mut buff = Cursor::new([0; 1]);
244 assert!(pack(&mut buff, -2).is_ok());
245 let buff = buff.into_inner();
246 assert_eq!(buff[0], 0b0100_0001);
247 }
248
249 #[test]
250 pub fn unpack_minus_2() {
251 let mut buff = Cursor::new([0; 1]);
252 assert!(pack(&mut buff, -2).is_ok());
253 buff.set_position(0);
254 assert_eq!(-2, unpack(&mut buff).unwrap());
255 }
256
257 #[test]
258 pub fn pack_minus_1() {
259 let mut buff = Cursor::new([0; 1]);
260 assert!(pack(&mut buff, -1).is_ok());
261 let buff = buff.into_inner();
262 assert_eq!(buff[0], 0b0100_0000);
263 }
264
265 #[test]
266 pub fn unpack_minus_1() {
267 let mut buff = Cursor::new([0; 1]);
268 assert!(pack(&mut buff, -1).is_ok());
269 buff.set_position(0);
270 assert_eq!(-1, unpack(&mut buff).unwrap());
271 }
272
273 #[test]
274 pub fn pack_0_to_63() {
275 for i in 0..64 {
276 let mut buff = Cursor::new([0; 1]);
277 assert!(pack(&mut buff, i).is_ok());
278 let buff = buff.into_inner();
279 assert_eq!(i32::from(buff[0]), i);
280 }
281 }
282
283 #[test]
284 pub fn unpack_0_to_63() {
285 for i in 0..64 {
286 let mut buff = Cursor::new([0; 1]);
287 assert!(pack(&mut buff, i).is_ok());
288 buff.set_position(0);
289 assert_eq!(i, unpack(&mut buff).unwrap());
290 }
291 }
292
293 #[test]
294 pub fn pack_64() {
295 let mut buff = Cursor::new([0; 2]);
296 assert!(pack(&mut buff, 64).is_ok());
297 let buff = buff.into_inner();
298 assert_eq!(buff[0], 0b1000_0000);
299 assert_eq!(buff[1], 0b0000_0001);
300 }
301
302 #[test]
303 pub fn unpack_64() {
304 let mut buff = Cursor::new([0; 2]);
305 assert!(pack(&mut buff, 64).is_ok());
306 buff.set_position(0);
307 assert_eq!(64, unpack(&mut buff).unwrap());
308 }
309
310 #[test]
311 pub fn pack_64_trait() {
312 let mut buff = Cursor::new([0; 2]);
313 assert!(64.pack(&mut buff).is_ok());
314 let buff = buff.into_inner();
315 assert_eq!(buff[0], 0b1000_0000);
316 assert_eq!(buff[1], 0b0000_0001);
317 }
318
319 #[test]
320 pub fn pack_64_trait_slice() {
321 let mut buff = [0; 2];
322 assert!(64.pack(&mut buff.as_mut_slice()).is_ok());
323 assert_eq!(buff[0], 0b1000_0000);
324 assert_eq!(buff[1], 0b0000_0001);
325 }
326
327 #[test]
328 pub fn unpack_64_trait() {
329 let mut buff = Cursor::new([0b1000_0000, 0b0000_0001]);
330 let result = buff.unpack().unwrap();
331 assert_eq!(result, 64);
332 }
333
334 #[test]
335 pub fn unpack_64_trait_slice() {
336 let buff = [0b1000_0000, 0b0000_0001];
337 let result = buff.as_slice().unpack().unwrap();
338 assert_eq!(result, 64);
339 }
340
341 #[test]
342 pub fn roundtrip_256_trait() {
343 let mut buff = Cursor::new([0; MAX_BYTES_PACKED]);
344 256.pack(&mut buff).unwrap();
345 buff.set_position(0);
346
347 let result = buff.unpack().unwrap();
348 assert_eq!(256, result);
349 }
350
351 static DATA: [i32; 14] = [
352 0,
353 1,
354 -1,
355 32,
356 64,
357 256,
358 -512,
359 12345,
360 -123_456,
361 1_234_567,
362 12_345_678,
363 123_456_789,
364 2_147_483_647,
365 (-2_147_483_647 - 1),
366 ];
367 static SIZES: [u64; 14] = [1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5];
368
369 #[test]
370 pub fn roundtrip_pack_unpack() {
371 for i in 0..DATA.len() {
372 let mut buff = Cursor::new([0; MAX_BYTES_PACKED]);
373 DATA[i].pack(&mut buff).unwrap();
374 assert_eq!(buff.position(), SIZES[i]);
375 buff.set_position(0);
376
377 let result = buff.unpack().unwrap();
378 assert_eq!(buff.position(), SIZES[i]);
379 assert_eq!(DATA[i], result);
380 }
381 }
382}