1use bytes::{Buf, BufMut};
8
9use crate::types::ProtocolError;
10
11const MAX_VARINT_BYTES: usize = 5;
13
14const VAR_INT_LENGTHS: [u8; 33] = [
17 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1,
18 1,
19];
20
21#[must_use]
23#[allow(clippy::cast_sign_loss)]
24pub const fn var_int_bytes(value: i32) -> usize {
25 let value = value as u32;
27 VAR_INT_LENGTHS[value.leading_zeros() as usize] as usize
28}
29
30pub fn read_var_int(buf: &mut impl Buf) -> Result<i32, ProtocolError> {
37 let mut value: i32 = 0;
38 let mut position: u32 = 0;
39
40 loop {
41 if !buf.has_remaining() {
42 return Err(ProtocolError::UnexpectedEof);
43 }
44
45 let byte = buf.get_u8();
46 value |= i32::from(byte & 0x7F) << position;
47
48 if byte & 0x80 == 0 {
49 return Ok(value);
50 }
51
52 position += 7;
53 if position >= 32 {
54 return Err(ProtocolError::VarIntTooLong);
55 }
56 }
57}
58
59#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
61pub fn write_var_int(buf: &mut impl BufMut, value: i32) {
62 let mut unsigned = value as u32;
64
65 if unsigned < 0x80 {
67 buf.put_u8(unsigned as u8);
68 return;
69 }
70
71 if unsigned < 0x4000 {
73 let encoded = ((unsigned & 0x7F | 0x80) << 8) | (unsigned >> 7);
74 buf.put_u16(encoded as u16);
75 return;
76 }
77
78 loop {
80 #[allow(clippy::cast_possible_truncation)]
81 if unsigned & !0x7F == 0 {
82 buf.put_u8(unsigned as u8);
83 return;
84 }
85 buf.put_u8((unsigned & 0x7F | 0x80) as u8);
86 unsigned >>= 7;
87 }
88}
89
90pub fn peek_var_int(data: &[u8]) -> Result<Option<(i32, usize)>, ProtocolError> {
99 let mut value: i32 = 0;
100 let mut position: u32 = 0;
101
102 for (i, &byte) in data.iter().enumerate() {
103 if i >= MAX_VARINT_BYTES {
104 return Err(ProtocolError::VarIntTooLong);
105 }
106
107 value |= i32::from(byte & 0x7F) << position;
108
109 if byte & 0x80 == 0 {
110 return Ok(Some((value, i + 1)));
111 }
112
113 position += 7;
114 }
115
116 Ok(None)
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use proptest::prelude::*;
124
125 proptest! {
126 #[test]
127 fn roundtrip_any_i32(v in any::<i32>()) {
128 let mut buf = Vec::new();
129 write_var_int(&mut buf, v);
130 let result = read_var_int(&mut &buf[..]).unwrap();
131 prop_assert_eq!(result, v);
132 }
133
134 #[test]
135 fn var_int_bytes_matches_encoded_length(v in any::<i32>()) {
136 let mut buf = Vec::new();
137 write_var_int(&mut buf, v);
138 prop_assert_eq!(var_int_bytes(v), buf.len());
139 }
140
141 #[test]
142 fn peek_agrees_with_read(v in any::<i32>()) {
143 let mut buf = Vec::new();
144 write_var_int(&mut buf, v);
145 let peeked = peek_var_int(&buf).unwrap().unwrap();
146 let read = read_var_int(&mut &buf[..]).unwrap();
147 prop_assert_eq!(peeked.0, read);
148 prop_assert_eq!(peeked.1, buf.len());
149 }
150 }
151
152 #[test]
153 fn test_roundtrip_zero() {
154 let mut buf = Vec::new();
155 write_var_int(&mut buf, 0);
156 assert_eq!(buf, vec![0x00]);
157 assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 0);
158 }
159
160 #[test]
161 fn test_roundtrip_one() {
162 let mut buf = Vec::new();
163 write_var_int(&mut buf, 1);
164 assert_eq!(buf, vec![0x01]);
165 assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 1);
166 }
167
168 #[test]
169 fn test_roundtrip_127() {
170 let mut buf = Vec::new();
171 write_var_int(&mut buf, 127);
172 assert_eq!(buf, vec![0x7F]);
173 assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 127);
174 }
175
176 #[test]
177 fn test_roundtrip_128() {
178 let mut buf = Vec::new();
179 write_var_int(&mut buf, 128);
180 assert_eq!(buf, vec![0x80, 0x01]);
181 assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 128);
182 }
183
184 #[test]
185 fn test_roundtrip_255() {
186 let mut buf = Vec::new();
187 write_var_int(&mut buf, 255);
188 assert_eq!(buf, vec![0xFF, 0x01]);
189 assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 255);
190 }
191
192 #[test]
193 fn test_roundtrip_25565() {
194 let mut buf = Vec::new();
195 write_var_int(&mut buf, 25565);
196 assert_eq!(buf, vec![0xDD, 0xC7, 0x01]);
197 assert_eq!(read_var_int(&mut &buf[..]).unwrap(), 25565);
198 }
199
200 #[test]
201 fn test_roundtrip_max() {
202 let mut buf = Vec::new();
203 write_var_int(&mut buf, i32::MAX);
204 assert_eq!(read_var_int(&mut &buf[..]).unwrap(), i32::MAX);
205 }
206
207 #[test]
208 fn test_roundtrip_negative_one() {
209 let mut buf = Vec::new();
210 write_var_int(&mut buf, -1);
211 assert_eq!(buf, vec![0xFF, 0xFF, 0xFF, 0xFF, 0x0F]);
212 assert_eq!(read_var_int(&mut &buf[..]).unwrap(), -1);
213 }
214
215 #[test]
216 fn test_var_int_bytes() {
217 assert_eq!(var_int_bytes(0), 1);
218 assert_eq!(var_int_bytes(1), 1);
219 assert_eq!(var_int_bytes(127), 1);
220 assert_eq!(var_int_bytes(128), 2);
221 assert_eq!(var_int_bytes(16383), 2);
222 assert_eq!(var_int_bytes(16384), 3);
223 assert_eq!(var_int_bytes(-1), 5);
224 }
225
226 #[test]
227 fn test_peek_var_int() {
228 let data = [0xDD, 0xC7, 0x01, 0xFF];
229 let result = peek_var_int(&data).unwrap().unwrap();
230 assert_eq!(result, (25565, 3));
231
232 let data = [0x80];
234 assert!(peek_var_int(&data).unwrap().is_none());
235
236 assert!(peek_var_int(&[]).unwrap().is_none());
238 }
239}