mc_varint/
lib.rs

1//! An implementation for Minecraft[^mc]'s VarInt and VarLong types, focusing on minimum memory
2//! usage and maximum performance.
3//!
4//! [^mc]: A well-known video game whose servers and clients are able to be built by third-party authors.
5//!
6//! This crate contains two structs for VarInt and VarLong, and four traits for conversations and
7//! IO operations on them. You may refer to the paragraphs following to get their usages.
8//!
9//! Algorithms and structures herein are built according to [a wiki.vg page]
10//!
11//! [a wiki.vg page]: http://wiki.vg/Protocol#VarInt_and_VarLong
12//!
13//! # VarInt and VarLong struct
14//!
15//! These two structs represents the two types mentioned above. Data stored in these two structs
16//! are guaranteed a valid VarInt or VarLong by their conversation traits.
17//!
18//! You may create these structs using function `VarInt::from(i32)` and `VarLong::from(i64)`.
19//! And using `i32::from(VarInt)` and `i64::from(VarLong)` can simply convert two structs into
20//! actual values in order to use them in following logic.
21//!
22//! These two structs implements `Default`, which leads to easier use in codes.
23//!
24//! # Two 'Read' traits and two 'Write' traits
25//!
26//! They are VarIntRead, VarLongRead for 'Read', and VarIntWrite, VarLongWrite for 'Write'.
27//!
28//! Both two 'Read' traits are implemented for all `R`'s where `R: io::Read`. You may use it to
29//! read `VarInt`'s and `VarLong`'s directly from IO streams, such as, network connections or files.
30//!
31//! And for the two 'Write' traits, they are implemented for all `W`'s where `W: io::Write` for your
32//! convenience.
33//!
34//! # How this crate reduces memory usage
35//!
36//! As only VarInt and VarLong struct performs the allocation, firstly we should minimize the space
37//! these two structs use in memory. As These two structs only stores the sized integer data
38//! instead of something combined with pointers and sizes, the memory usage is reduced to minimal,
39//! which means, the VarInt only uses 5 bytes and VarLong only uses 10.
40//!
41//! When writing to IO, reading from IO or performing type conversations, this crate only allocate
42//! one `[u8; 1]` array as buffer, and for the Rust's sake, can free it safely even without a GC.
43//! By this way we save more memory in calculating, resulting in more memory able to be used for
44//! network buffers, databases and your following logic code.
45
46#![deny(missing_docs)]
47
48use std::io;
49
50macro_rules! var_impl {
51    ($store_struct: ident, $read_trait: ident, $write_trait: ident, $read_func: ident, $write_func: ident,
52    $conversation_type: ident, $size: expr, $error_too_long: expr) => {
53
54/// The struct representing a VarInt or VarLong.
55#[derive(Debug, Eq, PartialEq)]
56pub struct $store_struct {
57    inner: [u8; $size]
58}
59
60impl Default for $store_struct {
61    fn default() -> Self {
62        $store_struct {
63            inner: [0u8; $size]
64        }
65    }
66}
67
68/// The Read trait for this VarInt or VarLong struct.
69///
70/// This trait is implemented for all `io::Read`'s.
71///
72/// # Examples
73///
74/// `Cursor`s implement `io::Read`, thus implement `VarIntRead` and `VarLongRead`:
75///
76/// ```
77/// extern crate mc_varint;
78///
79/// use mc_varint::{VarInt, VarIntRead};
80/// use std::io::Cursor;
81///
82/// fn main() {
83///     // firstly we create a Cursor
84///     let mut cur = Cursor::new(vec![0xff, 0xff, 0xff, 0xff, 0x07]);
85///     // secondly we write the VarInt to the Cursor
86///     let var_int = cur.read_var_int().unwrap();
87///     // the value of var_int is 2147483647
88///     assert_eq!(var_int, VarInt::from(2147483647));
89/// }
90/// ```
91pub trait $read_trait {
92    /// Reads a VarInt or Varlong from `self`.
93    ///
94    /// The current position is advanced according to the length of VarInt or VarLong.
95    ///
96    /// # Errors
97    ///
98    /// If the VarInt or VarLong to read from `self` is too long (is invalid) or this function
99    /// encounters any form of underlying I/O or other error, an error variant will be returned.
100    fn $read_func(&mut self) -> io::Result<$store_struct>;
101}
102
103impl<R> $read_trait for R where R: io::Read {
104    fn $read_func(&mut self) -> Result<$store_struct, io::Error> {
105        let mut ans = $store_struct {
106            inner: [0u8; $size]
107        };
108        let mut ptr = 0;
109        let mut buf = [0u8];
110        loop {
111            self.read_exact(&mut buf)?;
112            if ptr >= $size {
113                return Err(io::Error::new(io::ErrorKind::InvalidData, $error_too_long));
114            }
115            ans.inner[ptr] = buf[0];
116            ptr += 1;
117            if buf[0] & 0b1000_0000 == 0 {
118                return Ok(ans);
119            }
120        }
121    }
122}
123
124/// The Write trait for this VarInt or VarLong struct.
125///
126/// This trait is implemented for all `io::Write`'s.
127///
128/// # Examples
129///
130/// `Cursor`s implement `io::Write`, thus implement `VarIntWrite` and `VarLongWrite`:
131///
132/// ```
133/// extern crate mc_varint;
134///
135/// use mc_varint::{VarInt, VarIntWrite};
136/// use std::io::Cursor;
137///
138/// fn main() {
139///     // firstly we create a Cursor and a VarInt
140///     let mut cur = Cursor::new(Vec::with_capacity(5));
141///     let var_int = VarInt::from(2147483647);
142///     // secondly we write to it
143///     cur.write_var_int(var_int).unwrap();
144///     // now the var_int is written to cur.
145///     assert_eq!(cur.into_inner(), vec![0xff, 0xff, 0xff, 0xff, 0x07]);
146/// }
147/// ```
148pub trait $write_trait {
149    /// Writes a VarInt or Varlong to `self`.
150    ///
151    /// The current position is advanced according to the length of VarInt or VarLong.
152    ///
153    /// # Errors
154    ///
155    /// If this function encounters any form of underlying I/O or other error, an error variant
156    /// will be returned.
157    fn $write_func(&mut self, n: $store_struct) -> io::Result<()>;
158}
159
160impl<W> $write_trait for W where W: io::Write {
161    fn $write_func(&mut self, n: $store_struct) -> io::Result<()> {
162        let mut buf = [0x00];
163        let mut ptr = 0;
164        loop {
165            if n.inner[ptr] == 0 {
166                break;
167            }
168            buf[0] = n.inner[ptr];
169            self.write_all(&buf)?;
170            ptr += 1;
171            if ptr >= $size {
172                break;
173            }
174        }
175        // If no bytes written, that is, the $store_struct is equal to 0
176        if ptr == 0 {
177            // At that time, `buf` is still [0x00], let's write it
178            self.write_all(&buf)?;
179        }
180        Ok(())
181    }
182}
183
184impl From<$store_struct> for $conversation_type {
185    fn from(v: $store_struct) -> Self {
186        let mut ans = 0 as Self;
187        let mut ptr = 0;
188        loop {
189            let value = $conversation_type::from(v.inner[ptr] & 0b0111_1111);
190            ans |= value << (7 * ptr as Self);
191            if v.inner[ptr] & 0b1000_0000 == 0 {
192                return ans;
193            }
194            ptr += 1;
195        }
196    }
197}
198
199impl From<$conversation_type> for $store_struct {
200    fn from(n: $conversation_type) -> Self {
201        let mut ans = $store_struct {
202            inner: [0u8; $size]
203        };
204        let mut n = n;
205        let mut ptr = 0;
206        loop {
207            let mut tmp = (n & 0b0111_1111) as u8;
208            // There isn't a logical right-shift operator in Rust
209            n = (n >> 7) & ($conversation_type::max_value() >> 6);
210            if n != 0 {
211                tmp |= 0b1000_0000;
212            }
213            ans.inner[ptr] = tmp;
214            ptr += 1;
215            if n == 0 || ptr >= $size {
216                break;
217            }
218        }
219        ans
220    }
221}
222    };
223}
224
225var_impl!(VarInt, VarIntRead, VarIntWrite, read_var_int, write_var_int,
226            i32, 5, "varint too long (length > 5)");
227var_impl!(VarLong, VarLongRead, VarLongWrite, read_var_long, write_var_long,
228            i64, 10, "varlong too long (length > 10)");
229