deepslate_protocol/
codec.rs1use bytes::{Buf, BufMut};
9
10use crate::types::ProtocolError;
11use crate::varint;
12
13const MAX_FRAME_SIZE: usize = 2 * 1024 * 1024;
16
17pub const MAX_UNCOMPRESSED_SIZE: usize = 8 * 1024 * 1024;
19
20#[allow(clippy::cast_sign_loss)]
36pub fn try_read_frame(data: &[u8]) -> Result<Option<(usize, usize)>, ProtocolError> {
37 let Some((frame_len, varint_size)) = varint::peek_var_int(data)? else {
38 return Ok(None);
39 };
40
41 if frame_len < 0 {
42 return Err(ProtocolError::InvalidData(
43 "negative frame length".to_string(),
44 ));
45 }
46
47 let frame_len = frame_len as usize;
48 if frame_len > MAX_FRAME_SIZE {
49 return Err(ProtocolError::FrameTooLarge {
50 size: frame_len,
51 max: MAX_FRAME_SIZE,
52 });
53 }
54
55 let total = varint_size + frame_len;
56 if data.len() < total {
57 return Ok(None);
58 }
59
60 Ok(Some((varint_size, frame_len)))
61}
62
63#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
66pub fn write_frame(dst: &mut impl BufMut, inner: &[u8]) {
67 varint::write_var_int(dst, inner.len() as i32);
68 dst.put_slice(inner);
69}
70
71#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
77pub fn write_compressed_frame(dst: &mut impl BufMut, uncompressed_size: i32, payload: &[u8]) {
78 let size_varint_len = varint::var_int_bytes(uncompressed_size);
79 let frame_len = size_varint_len + payload.len();
80 varint::write_var_int(dst, frame_len as i32);
81 varint::write_var_int(dst, uncompressed_size);
82 dst.put_slice(payload);
83}
84
85#[allow(clippy::cast_sign_loss)]
97pub fn read_compressed_frame(data: &[u8]) -> Result<(usize, &[u8]), ProtocolError> {
98 let mut cursor = data;
99 let uncompressed_size = varint::read_var_int(&mut cursor)?;
100 if uncompressed_size < 0 {
101 return Err(ProtocolError::InvalidData(
102 "negative uncompressed size".to_string(),
103 ));
104 }
105 let uncompressed_size = uncompressed_size as usize;
106 if uncompressed_size > MAX_UNCOMPRESSED_SIZE {
107 return Err(ProtocolError::UncompressedSizeTooLarge {
108 size: uncompressed_size,
109 max: MAX_UNCOMPRESSED_SIZE,
110 });
111 }
112 Ok((uncompressed_size, cursor))
113}
114
115pub fn encode_packet_data(buf: &mut Vec<u8>, packet_id: i32, encode_fn: impl FnOnce(&mut Vec<u8>)) {
121 buf.clear();
122 varint::write_var_int(buf, packet_id);
123 encode_fn(buf);
124}
125
126pub fn read_packet_id(buf: &mut impl Buf) -> Result<i32, ProtocolError> {
132 varint::read_var_int(buf)
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use proptest::prelude::*;
139
140 proptest! {
141 #[test]
142 fn write_frame_roundtrip_prop(payload in prop::collection::vec(any::<u8>(), 0..4096)) {
143 let mut buf = Vec::new();
144 write_frame(&mut buf, &payload);
145 let (varint_size, frame_len) = try_read_frame(&buf).unwrap().unwrap();
146 prop_assert_eq!(varint_size + frame_len, buf.len());
147 prop_assert_eq!(&buf[varint_size..varint_size + frame_len], &payload[..]);
148 }
149
150 #[test]
151 fn compressed_frame_roundtrip_prop(
152 uncompressed_size in 0..MAX_UNCOMPRESSED_SIZE,
153 payload in prop::collection::vec(any::<u8>(), 0..4096)
154 ) {
155 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
156 let uncompressed_size_i32 = uncompressed_size as i32;
157 let mut buf = Vec::new();
158 write_compressed_frame(&mut buf, uncompressed_size_i32, &payload);
159 let (varint_size, frame_len) = try_read_frame(&buf).unwrap().unwrap();
160 let frame_data = &buf[varint_size..varint_size + frame_len];
161 let (read_uncompressed_size, data) = read_compressed_frame(frame_data).unwrap();
162 prop_assert_eq!(read_uncompressed_size, uncompressed_size);
163 prop_assert_eq!(data, &payload[..]);
164 }
165 }
166
167 #[test]
168 fn test_try_read_frame_complete() {
169 let data = vec![0x03, 0x01, 0x02, 0x03];
171 let (varint_size, frame_len) = try_read_frame(&data).unwrap().unwrap();
172 assert_eq!(varint_size + frame_len, 4);
173 assert_eq!(
174 &data[varint_size..varint_size + frame_len],
175 &[0x01, 0x02, 0x03]
176 );
177 }
178
179 #[test]
180 fn test_try_read_frame_incomplete() {
181 let data = vec![0x03, 0x01]; assert!(try_read_frame(&data).unwrap().is_none());
183 }
184
185 #[test]
186 fn test_try_read_frame_empty() {
187 assert!(try_read_frame(&[]).unwrap().is_none());
188 }
189
190 #[test]
191 fn test_write_frame_roundtrip() {
192 let inner = vec![0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F]; let mut buf = Vec::new();
194 write_frame(&mut buf, &inner);
195 let (varint_size, frame_len) = try_read_frame(&buf).unwrap().unwrap();
196 assert_eq!(varint_size + frame_len, buf.len());
197 assert_eq!(&buf[varint_size..varint_size + frame_len], &inner[..]);
198 }
199
200 #[test]
201 fn test_compressed_frame_uncompressed() {
202 let mut buf = Vec::new();
203 let payload = vec![0x00, 0x48, 0x69];
204 write_compressed_frame(&mut buf, 0, &payload);
205
206 let (varint_size, frame_len) = try_read_frame(&buf).unwrap().unwrap();
207 let frame_data = &buf[varint_size..varint_size + frame_len];
208 let (uncompressed_size, data) = read_compressed_frame(frame_data).unwrap();
209 assert_eq!(uncompressed_size, 0);
210 assert_eq!(data, &payload[..]);
211 }
212}