mcproto_codec/varint.rs
1//! Reading and writing [Minecraft protocol VarInt] values.
2//!
3//! [Minecraft protocol VarInt]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#VarInt_and_VarLong
4
5use std::io::{Read, Write};
6
7use crate::error::{CodecError, CodecKind, InvalidEncodingReason};
8use crate::io::{read_exact_counted, write_all_counted};
9
10/// Extension methods for writing [Minecraft protocol VarInt] values.
11///
12/// This trait is implemented for every [`Write`] type.
13///
14/// # Example
15///
16/// ```
17/// use mcproto_codec::varint::VarIntWrite;
18///
19/// let mut output = Vec::new();
20/// output.write_varint(25565)?;
21///
22/// # Ok::<(), mcproto_codec::error::CodecError>(())
23/// ```
24///
25/// [Minecraft protocol VarInt]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#VarInt_and_VarLong
26pub trait VarIntWrite: Write {
27 /// Writes `value` to this writer as a VarInt.
28 ///
29 /// # Errors
30 ///
31 /// Returns a [`CodecError`] if the underlying writer fails. The error's
32 /// byte count reports how much of this value was written successfully.
33 ///
34 /// [`CodecError`]: crate::error::CodecError
35 #[inline]
36 fn write_varint(&mut self, value: i32) -> Result<(), CodecError> {
37 self.write_varint_with_size(value).map(|_| ())
38 }
39
40 /// Writes `value` as a VarInt and returns the number of bytes written.
41 ///
42 /// # Errors
43 ///
44 /// Returns a [`CodecError`] if the underlying writer fails. The error's
45 /// byte count reports how much of this value was written successfully.
46 ///
47 /// [`CodecError`]: crate::error::CodecError
48 #[inline]
49 fn write_varint_with_size(&mut self, value: i32) -> Result<usize, CodecError> {
50 let mut value = value as u32;
51 let mut bytes_processed = 0;
52
53 loop {
54 let byte = (value & 0x7F) as u8;
55 value >>= 7;
56 let has_next = value != 0;
57 let byte = if has_next { byte | 0x80 } else { byte };
58
59 write_all_counted(self, &[byte], CodecKind::VarInt, bytes_processed)?;
60 bytes_processed += 1;
61
62 if !has_next {
63 return Ok(bytes_processed);
64 }
65 }
66 }
67}
68
69/// Extension methods for reading [Minecraft protocol VarInt] values.
70///
71/// This trait is implemented for every [`Read`] type.
72///
73/// # Example
74///
75/// ```
76/// use mcproto_codec::varint::{VarIntRead, VarIntWrite};
77///
78/// let mut encoded = Vec::new();
79/// encoded.write_varint(25565)?;
80///
81/// let value = encoded.as_slice().read_varint()?;
82/// assert_eq!(value, 25565);
83///
84/// # Ok::<(), mcproto_codec::error::CodecError>(())
85/// ```
86///
87/// [Minecraft protocol VarInt]: https://minecraft.wiki/w/Java_Edition_protocol/Packets#VarInt_and_VarLong
88pub trait VarIntRead: Read {
89 /// Reads and returns one VarInt from this reader.
90 ///
91 /// # Errors
92 ///
93 /// Returns a [`CodecError`] if the input ends early, the underlying reader
94 /// fails, or the input is not a valid VarInt.
95 ///
96 /// [`CodecError`]: crate::error::CodecError
97 #[inline]
98 fn read_varint(&mut self) -> Result<i32, CodecError> {
99 self.read_varint_with_size().map(|(value, _)| value)
100 }
101
102 /// Reads one VarInt and returns its value and encoded size in bytes.
103 ///
104 /// # Errors
105 ///
106 /// Returns a [`CodecError`] if the input ends early, the underlying reader
107 /// fails, or the input is not a valid VarInt.
108 ///
109 /// [`CodecError`]: crate::error::CodecError
110 #[inline]
111 fn read_varint_with_size(&mut self) -> Result<(i32, usize), CodecError> {
112 let mut result = 0u32;
113 let mut shift = 0;
114
115 for i in 0..5 {
116 let mut buf = [0u8; 1];
117 read_exact_counted(self, &mut buf, CodecKind::VarInt, i)?;
118 let byte = buf[0];
119
120 if i == 4 {
121 if (byte & 0x80) != 0 {
122 return Err(CodecError::invalid_encoding(
123 CodecKind::VarInt,
124 i + 1,
125 InvalidEncodingReason::TooLong { max_bytes: 5 },
126 ));
127 }
128 }
129
130 let value = (byte & 0x7F) as u32;
131 result |= value << shift;
132
133 if (byte & 0x80) == 0 {
134 return Ok((result as i32, i + 1));
135 }
136
137 shift += 7;
138 }
139
140 unreachable!("the fifth VarInt byte always terminates or returns an error")
141 }
142}
143
144impl<R: Read> VarIntRead for R {}
145impl<W: Write> VarIntWrite for W {}