Skip to main content

basalt_types/
varint.rs

1use crate::{Decode, Encode, EncodedSize, Error, Result};
2
3const SEGMENT_BITS: u8 = 0x7F;
4const CONTINUE_BIT: u8 = 0x80;
5
6/// A variable-length encoded 32-bit signed integer, occupying 1 to 5 bytes.
7///
8/// VarInt is the most common type in the Minecraft protocol. It is used for
9/// packet IDs, packet lengths, string length prefixes, array lengths, enum
10/// discriminants, and many field values. The encoding uses the MSB of each
11/// byte as a continuation bit (1 = more bytes follow, 0 = last byte), with
12/// the lower 7 bits carrying the value in little-endian order (least
13/// significant group first). Negative values use two's complement and
14/// always require 5 bytes.
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16pub struct VarInt(pub i32);
17
18impl VarInt {
19    /// Maximum number of bytes a VarInt can occupy on the wire.
20    pub const MAX_BYTES: usize = 5;
21}
22
23/// Encodes the VarInt as 1-5 bytes using MSB continuation bit encoding.
24///
25/// Each byte carries 7 bits of the value (least significant first). The MSB
26/// is set to 1 if more bytes follow, 0 on the final byte. Negative values
27/// are encoded as their unsigned two's complement representation and always
28/// produce 5 bytes.
29impl Encode for VarInt {
30    /// Writes the variable-length encoded bytes to the buffer.
31    fn encode(&self, buf: &mut Vec<u8>) -> Result<()> {
32        let mut value = self.0 as u32;
33        loop {
34            if value & !(SEGMENT_BITS as u32) == 0 {
35                buf.push(value as u8);
36                return Ok(());
37            }
38            buf.push((value as u8 & SEGMENT_BITS) | CONTINUE_BIT);
39            value >>= 7;
40        }
41    }
42}
43
44/// Decodes a VarInt by reading 1-5 bytes from the buffer.
45///
46/// Reads bytes one at a time, accumulating 7-bit groups until a byte
47/// without the continuation bit is found. If more than 5 bytes have
48/// the continuation bit set, the value would exceed 32 bits and the
49/// decoder returns `Error::VarIntTooLarge`. If the buffer runs out
50/// mid-VarInt, returns `Error::BufferUnderflow`.
51impl Decode for VarInt {
52    /// Reads and decodes a VarInt, advancing the cursor past all consumed bytes.
53    fn decode(buf: &mut &[u8]) -> Result<Self> {
54        let mut value: u32 = 0;
55        let mut position: u32 = 0;
56        let start = *buf;
57
58        loop {
59            if buf.is_empty() {
60                return Err(Error::BufferUnderflow {
61                    needed: 1,
62                    available: 0,
63                });
64            }
65
66            let byte = buf[0];
67            *buf = &buf[1..];
68
69            value |= ((byte & SEGMENT_BITS) as u32) << position;
70            position += 7;
71
72            if byte & CONTINUE_BIT == 0 {
73                return Ok(VarInt(value as i32));
74            }
75
76            if position >= 32 {
77                // Reset cursor to start for accurate error reporting
78                *buf = start;
79                return Err(Error::VarIntTooLarge);
80            }
81        }
82    }
83}
84
85/// Computes the number of bytes this VarInt will occupy when encoded.
86///
87/// Returns 1-5 based on the unsigned magnitude of the value. Small
88/// positive values (0-127) take 1 byte, while negative values always
89/// take 5 bytes due to two's complement sign extension.
90impl EncodedSize for VarInt {
91    fn encoded_size(&self) -> usize {
92        let value = self.0 as u32;
93        match value {
94            0..=0x7F => 1,
95            0x80..=0x3FFF => 2,
96            0x4000..=0x1FFFFF => 3,
97            0x200000..=0xFFFFFFF => 4,
98            _ => 5,
99        }
100    }
101}
102
103/// Wraps a raw `i32` into a `VarInt` for protocol encoding.
104impl From<i32> for VarInt {
105    fn from(value: i32) -> Self {
106        VarInt(value)
107    }
108}
109
110/// Extracts the inner `i32` value from a `VarInt`.
111impl From<VarInt> for i32 {
112    fn from(value: VarInt) -> Self {
113        value.0
114    }
115}
116
117/// A variable-length encoded 64-bit signed integer, occupying 1 to 10 bytes.
118///
119/// VarLong uses the same MSB continuation bit encoding as [`VarInt`] but
120/// for 64-bit values. It is used in the protocol for large values like
121/// entity UUIDs in some contexts, timestamps, and world seed. Negative
122/// values use two's complement and always require 10 bytes.
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
124pub struct VarLong(pub i64);
125
126impl VarLong {
127    /// Maximum number of bytes a VarLong can occupy on the wire.
128    pub const MAX_BYTES: usize = 10;
129}
130
131/// Encodes the VarLong as 1-10 bytes using MSB continuation bit encoding.
132///
133/// Same algorithm as [`VarInt`] but operating on 64-bit values. Negative
134/// values are encoded as their unsigned two's complement representation
135/// and always produce 10 bytes.
136impl Encode for VarLong {
137    fn encode(&self, buf: &mut Vec<u8>) -> Result<()> {
138        let mut value = self.0 as u64;
139        loop {
140            if value & !(SEGMENT_BITS as u64) == 0 {
141                buf.push(value as u8);
142                return Ok(());
143            }
144            buf.push((value as u8 & SEGMENT_BITS) | CONTINUE_BIT);
145            value >>= 7;
146        }
147    }
148}
149
150/// Decodes a VarLong by reading 1-10 bytes from the buffer.
151///
152/// Same algorithm as [`VarInt`] decoding but allowing up to 10 bytes
153/// (64 bits). Returns `Error::VarIntTooLarge` if more than 10 bytes
154/// carry continuation bits, `Error::BufferUnderflow` if the buffer
155/// ends mid-value.
156impl Decode for VarLong {
157    fn decode(buf: &mut &[u8]) -> Result<Self> {
158        let mut value: u64 = 0;
159        let mut position: u32 = 0;
160        let start = *buf;
161
162        loop {
163            if buf.is_empty() {
164                return Err(Error::BufferUnderflow {
165                    needed: 1,
166                    available: 0,
167                });
168            }
169
170            let byte = buf[0];
171            *buf = &buf[1..];
172
173            value |= ((byte & SEGMENT_BITS) as u64) << position;
174            position += 7;
175
176            if byte & CONTINUE_BIT == 0 {
177                return Ok(VarLong(value as i64));
178            }
179
180            if position >= 64 {
181                *buf = start;
182                return Err(Error::VarIntTooLarge);
183            }
184        }
185    }
186}
187
188/// Computes the number of bytes this VarLong will occupy when encoded.
189///
190/// Returns 1-10 based on the unsigned magnitude. Small positive values
191/// (0-127) take 1 byte, `i64::MAX` takes 9, and negative values always
192/// take 10 bytes.
193impl EncodedSize for VarLong {
194    fn encoded_size(&self) -> usize {
195        let value = self.0 as u64;
196        match value {
197            0..=0x7F => 1,
198            0x80..=0x3FFF => 2,
199            0x4000..=0x1FFFFF => 3,
200            0x200000..=0xFFFFFFF => 4,
201            0x10000000..=0x7FFFFFFFF => 5,
202            0x800000000..=0x3FFFFFFFFFF => 6,
203            0x40000000000..=0x1FFFFFFFFFFFF => 7,
204            0x2000000000000..=0xFFFFFFFFFFFFFF => 8,
205            0x100000000000000..=0x7FFFFFFFFFFFFFFF => 9,
206            _ => 10,
207        }
208    }
209}
210
211/// Wraps a raw `i64` into a `VarLong` for protocol encoding.
212impl From<i64> for VarLong {
213    fn from(value: i64) -> Self {
214        VarLong(value)
215    }
216}
217
218/// Extracts the inner `i64` value from a `VarLong`.
219impl From<VarLong> for i64 {
220    fn from(value: VarLong) -> Self {
221        value.0
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228
229    fn roundtrip_varint(value: i32) {
230        let var = VarInt(value);
231        let mut buf = Vec::with_capacity(var.encoded_size());
232        var.encode(&mut buf).unwrap();
233        assert_eq!(buf.len(), var.encoded_size());
234
235        let mut cursor = buf.as_slice();
236        let decoded = VarInt::decode(&mut cursor).unwrap();
237        assert!(cursor.is_empty());
238        assert_eq!(decoded.0, value);
239    }
240
241    fn roundtrip_varlong(value: i64) {
242        let var = VarLong(value);
243        let mut buf = Vec::with_capacity(var.encoded_size());
244        var.encode(&mut buf).unwrap();
245        assert_eq!(buf.len(), var.encoded_size());
246
247        let mut cursor = buf.as_slice();
248        let decoded = VarLong::decode(&mut cursor).unwrap();
249        assert!(cursor.is_empty());
250        assert_eq!(decoded.0, value);
251    }
252
253    // -- VarInt known values (from wiki.vg) --
254
255    #[test]
256    fn varint_zero() {
257        let mut buf = Vec::new();
258        VarInt(0).encode(&mut buf).unwrap();
259        assert_eq!(buf, [0x00]);
260        roundtrip_varint(0);
261    }
262
263    #[test]
264    fn varint_one() {
265        let mut buf = Vec::new();
266        VarInt(1).encode(&mut buf).unwrap();
267        assert_eq!(buf, [0x01]);
268        roundtrip_varint(1);
269    }
270
271    #[test]
272    fn varint_127() {
273        let mut buf = Vec::new();
274        VarInt(127).encode(&mut buf).unwrap();
275        assert_eq!(buf, [0x7F]);
276        roundtrip_varint(127);
277    }
278
279    #[test]
280    fn varint_128() {
281        let mut buf = Vec::new();
282        VarInt(128).encode(&mut buf).unwrap();
283        assert_eq!(buf, [0x80, 0x01]);
284        roundtrip_varint(128);
285    }
286
287    #[test]
288    fn varint_255() {
289        let mut buf = Vec::new();
290        VarInt(255).encode(&mut buf).unwrap();
291        assert_eq!(buf, [0xFF, 0x01]);
292        roundtrip_varint(255);
293    }
294
295    #[test]
296    fn varint_25565() {
297        let mut buf = Vec::new();
298        VarInt(25565).encode(&mut buf).unwrap();
299        assert_eq!(buf, [0xDD, 0xC7, 0x01]);
300        roundtrip_varint(25565);
301    }
302
303    #[test]
304    fn varint_max() {
305        let mut buf = Vec::new();
306        VarInt(i32::MAX).encode(&mut buf).unwrap();
307        assert_eq!(buf, [0xFF, 0xFF, 0xFF, 0xFF, 0x07]);
308        roundtrip_varint(i32::MAX);
309    }
310
311    #[test]
312    fn varint_minus_one() {
313        let mut buf = Vec::new();
314        VarInt(-1).encode(&mut buf).unwrap();
315        assert_eq!(buf, [0xFF, 0xFF, 0xFF, 0xFF, 0x0F]);
316        roundtrip_varint(-1);
317    }
318
319    #[test]
320    fn varint_min() {
321        let mut buf = Vec::new();
322        VarInt(i32::MIN).encode(&mut buf).unwrap();
323        assert_eq!(buf, [0x80, 0x80, 0x80, 0x80, 0x08]);
324        roundtrip_varint(i32::MIN);
325    }
326
327    // -- VarInt errors --
328
329    #[test]
330    fn varint_empty_buffer() {
331        let mut cursor: &[u8] = &[];
332        assert!(matches!(
333            VarInt::decode(&mut cursor),
334            Err(Error::BufferUnderflow { .. })
335        ));
336    }
337
338    #[test]
339    fn varint_too_large() {
340        // 6 continuation bytes — exceeds 5-byte limit
341        let mut cursor: &[u8] = &[0x80, 0x80, 0x80, 0x80, 0x80, 0x01];
342        assert!(matches!(
343            VarInt::decode(&mut cursor),
344            Err(Error::VarIntTooLarge)
345        ));
346    }
347
348    #[test]
349    fn varint_truncated() {
350        // Continuation bit set but no next byte
351        let mut cursor: &[u8] = &[0x80];
352        assert!(matches!(
353            VarInt::decode(&mut cursor),
354            Err(Error::BufferUnderflow { .. })
355        ));
356    }
357
358    // -- VarInt encoded_size --
359
360    #[test]
361    fn varint_encoded_size() {
362        assert_eq!(VarInt(0).encoded_size(), 1);
363        assert_eq!(VarInt(127).encoded_size(), 1);
364        assert_eq!(VarInt(128).encoded_size(), 2);
365        assert_eq!(VarInt(16383).encoded_size(), 2);
366        assert_eq!(VarInt(16384).encoded_size(), 3);
367        assert_eq!(VarInt(i32::MAX).encoded_size(), 5);
368        assert_eq!(VarInt(-1).encoded_size(), 5);
369        assert_eq!(VarInt(i32::MIN).encoded_size(), 5);
370    }
371
372    // -- VarInt conversions --
373
374    #[test]
375    fn varint_from_i32() {
376        let v: VarInt = 42.into();
377        assert_eq!(v.0, 42);
378    }
379
380    #[test]
381    fn varint_into_i32() {
382        let v: i32 = VarInt(42).into();
383        assert_eq!(v, 42);
384    }
385
386    // -- VarLong known values --
387
388    #[test]
389    fn varlong_zero() {
390        roundtrip_varlong(0);
391    }
392
393    #[test]
394    fn varlong_one() {
395        roundtrip_varlong(1);
396    }
397
398    #[test]
399    fn varlong_max() {
400        roundtrip_varlong(i64::MAX);
401    }
402
403    #[test]
404    fn varlong_min() {
405        roundtrip_varlong(i64::MIN);
406    }
407
408    #[test]
409    fn varlong_minus_one() {
410        roundtrip_varlong(-1);
411    }
412
413    // -- VarLong errors --
414
415    #[test]
416    fn varlong_empty_buffer() {
417        let mut cursor: &[u8] = &[];
418        assert!(matches!(
419            VarLong::decode(&mut cursor),
420            Err(Error::BufferUnderflow { .. })
421        ));
422    }
423
424    #[test]
425    fn varlong_too_large() {
426        // 11 continuation bytes — exceeds 10-byte limit
427        let mut cursor: &[u8] = &[0x80; 11];
428        assert!(matches!(
429            VarLong::decode(&mut cursor),
430            Err(Error::VarIntTooLarge)
431        ));
432    }
433
434    // -- VarLong encoded_size --
435
436    #[test]
437    fn varlong_encoded_size() {
438        assert_eq!(VarLong(0).encoded_size(), 1);
439        assert_eq!(VarLong(127).encoded_size(), 1);
440        assert_eq!(VarLong(128).encoded_size(), 2);
441        assert_eq!(VarLong(i64::MAX).encoded_size(), 9);
442        assert_eq!(VarLong(-1).encoded_size(), 10);
443        assert_eq!(VarLong(i64::MIN).encoded_size(), 10);
444    }
445
446    // -- VarLong conversions --
447
448    #[test]
449    fn varlong_from_i64() {
450        let v: VarLong = 42i64.into();
451        assert_eq!(v.0, 42);
452    }
453
454    #[test]
455    fn varlong_into_i64() {
456        let v: i64 = VarLong(42).into();
457        assert_eq!(v, 42);
458    }
459
460    // -- proptest --
461
462    mod proptests {
463        use super::*;
464        use proptest::prelude::*;
465
466        proptest! {
467            #[test]
468            fn varint_roundtrip(v: i32) {
469                roundtrip_varint(v);
470            }
471
472            #[test]
473            fn varlong_roundtrip(v: i64) {
474                roundtrip_varlong(v);
475            }
476        }
477    }
478}