teeint/
lib.rs

1//!
2//! [![Version](https://img.shields.io/crates/v/teeint)](https://crates.io/crates/teeint)
3//! [![Downloads](https://img.shields.io/crates/d/teeint)](https://crates.io/crates/teeint)
4//! [![License](https://img.shields.io/crates/l/teeint)](https://crates.io/crates/teeint)
5//! ![Rust](https://github.com/edg-l/teeint/workflows/Rust/badge.svg)
6//! [![Docs](https://docs.rs/teeint/badge.svg)](https://docs.rs/teeint)
7//!
8//! A teeworlds variable integer packer/unpacker.
9//!
10//!
11//! ## Packing
12//!
13//! ```
14//! use std::io::Cursor;
15//!
16//! let mut buff = Cursor::new([0; 2]);
17//!
18//! teeint::pack(&mut buff, 64).unwrap();
19//!
20//! let buff = buff.into_inner();
21//! assert_eq!(buff[0], 0b1000_0000);
22//! assert_eq!(buff[1], 0b0000_0001);
23//! ```
24//!
25//! Or using the trait [`PackTwInt`]:
26//! ```
27//! use std::io::Cursor;
28//! use teeint::PackTwInt;
29//!
30//! let mut buff = Cursor::new([0; 2]);
31//!
32//! 64.pack(&mut buff).unwrap();
33//!
34//! let buff = buff.into_inner();
35//! assert_eq!(buff[0], 0b1000_0000);
36//! assert_eq!(buff[1], 0b0000_0001);
37//! ```
38//!
39//! Or
40//! ```
41//! use teeint::PackTwInt;
42//!
43//! let mut buff = [0; 2];
44//! 64.pack(&mut buff.as_mut_slice()).unwrap();
45//! assert_eq!(buff[0], 0b1000_0000);
46//! assert_eq!(buff[1], 0b0000_0001);
47//! ```
48//!
49//! ## Unpacking
50//! ```
51//! use std::io::Cursor;
52//!
53//! let mut buff = Cursor::new([0b1000_0000, 0b0000_0001]);
54//! let data = teeint::unpack(&mut buff).unwrap();
55//! assert_eq!(data, 64);
56//! ```
57//!
58//! Or using the trait [`UnPackTwInt`]:
59//! ```
60//! use teeint::UnPackTwInt;
61//!
62//! let buff = [0b1000_0000, 0b0000_0001];
63//! let result = buff.as_slice().unpack().unwrap();
64//! assert_eq!(result, 64);
65//! ```
66
67#![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
76/// Max bytes packed in a variable int.
77pub const MAX_BYTES_PACKED: usize = 5;
78
79/// Pack a i32 into a teeworlds variable integer.
80///
81/// # Errors
82/// Returns `Err` if there is an error writing to `dst`.
83#[inline]
84pub fn pack<T: Write + ?Sized>(dst: &mut T, mut value: i32) -> Result<()> {
85    let mut current_byte: u8 = 0;
86
87    /*
88     *  First byte: BIT_EXTEND BIT_SIGN BITS[6]
89     *  Next byte:  BIT_EXTEND BITS[7]
90     *  Last byte:  BIT_PADDING[4] BITS[4]
91     */
92
93    // If value is negative, set the sign bit and flip all the bits.
94    if value < 0 {
95        current_byte = 0b0100_0000;
96        value = !value;
97    }
98
99    // First byte: Pack the remaining 6 bits
100    current_byte |= u8::try_from(value & 0b0011_1111).expect("should always be inside the range");
101    value >>= 6;
102
103    while value != 0 {
104        // We have more data, set BIT_EXTEND
105        current_byte |= 0b1000_0000;
106        dst.write_all(std::slice::from_ref(&current_byte))?;
107        // Write the BITS[7]
108        current_byte =
109            u8::try_from(value & 0b0111_1111).expect("should always be inside the range");
110        // Discard them.
111        value >>= 7;
112    }
113
114    dst.write_all(std::slice::from_ref(&current_byte))?;
115
116    Ok(())
117}
118
119/// Unpack a teeworlds variable int from the provided reader.
120///
121/// # Errors
122/// Returns `Err` if there is an error reading from `src`
123#[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    // Adapted from https://github.com/ddnet/ddnet/blob/79df5893ff26fa75d67e46f99e58f75b739ac362/src/engine/shared/compression.cpp#L10
129    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
150/// Trait implemented by values that can be packed to a teeworlds variable int.
151///
152/// Note that teeworlds only packs i32 values.
153///
154/// This trait is more of a convenience to allow writing `0i32.pack(&mut buff)`
155pub trait PackTwInt {
156    /// Pack this value into a teeworlds variable int.
157    ///
158    /// # Errors
159    /// Returns `Err` if there is an error writing to `dst`.
160    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
170/// Trait implemented by buffers holding a teeworlds variable int.
171///
172/// This trait is more of a convenience to allow writing `let data = buff.unpack()?;`
173pub trait UnPackTwInt: Read {
174    /// Unpack this reader holding a teeworlds variable int to a i32.
175    /// # Errors
176    /// Returns `Err` if there is an error reading from `Self`
177    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}