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