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(packet_id: i32, encode_fn: impl FnOnce(&mut Vec<u8>)) -> Vec<u8> {
117 let mut buf = Vec::with_capacity(64);
118 varint::write_var_int(&mut buf, packet_id);
119 encode_fn(&mut buf);
120 buf
121}
122
123pub fn read_packet_id(buf: &mut impl Buf) -> Result<i32, ProtocolError> {
129 varint::read_var_int(buf)
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use proptest::prelude::*;
136
137 proptest! {
138 #[test]
139 fn write_frame_roundtrip_prop(payload in prop::collection::vec(any::<u8>(), 0..4096)) {
140 let mut buf = Vec::new();
141 write_frame(&mut buf, &payload);
142 let (varint_size, frame_len) = try_read_frame(&buf).unwrap().unwrap();
143 prop_assert_eq!(varint_size + frame_len, buf.len());
144 prop_assert_eq!(&buf[varint_size..varint_size + frame_len], &payload[..]);
145 }
146
147 #[test]
148 fn compressed_frame_roundtrip_prop(
149 uncompressed_size in 0..MAX_UNCOMPRESSED_SIZE,
150 payload in prop::collection::vec(any::<u8>(), 0..4096)
151 ) {
152 #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
153 let uncompressed_size_i32 = uncompressed_size as i32;
154 let mut buf = Vec::new();
155 write_compressed_frame(&mut buf, uncompressed_size_i32, &payload);
156 let (varint_size, frame_len) = try_read_frame(&buf).unwrap().unwrap();
157 let frame_data = &buf[varint_size..varint_size + frame_len];
158 let (read_uncompressed_size, data) = read_compressed_frame(frame_data).unwrap();
159 prop_assert_eq!(read_uncompressed_size, uncompressed_size);
160 prop_assert_eq!(data, &payload[..]);
161 }
162 }
163
164 #[test]
165 fn test_try_read_frame_complete() {
166 let data = vec![0x03, 0x01, 0x02, 0x03];
168 let (varint_size, frame_len) = try_read_frame(&data).unwrap().unwrap();
169 assert_eq!(varint_size + frame_len, 4);
170 assert_eq!(
171 &data[varint_size..varint_size + frame_len],
172 &[0x01, 0x02, 0x03]
173 );
174 }
175
176 #[test]
177 fn test_try_read_frame_incomplete() {
178 let data = vec![0x03, 0x01]; assert!(try_read_frame(&data).unwrap().is_none());
180 }
181
182 #[test]
183 fn test_try_read_frame_empty() {
184 assert!(try_read_frame(&[]).unwrap().is_none());
185 }
186
187 #[test]
188 fn test_write_frame_roundtrip() {
189 let inner = vec![0x00, 0x48, 0x65, 0x6C, 0x6C, 0x6F]; let mut buf = Vec::new();
191 write_frame(&mut buf, &inner);
192 let (varint_size, frame_len) = try_read_frame(&buf).unwrap().unwrap();
193 assert_eq!(varint_size + frame_len, buf.len());
194 assert_eq!(&buf[varint_size..varint_size + frame_len], &inner[..]);
195 }
196
197 #[test]
198 fn test_compressed_frame_uncompressed() {
199 let mut buf = Vec::new();
200 let payload = vec![0x00, 0x48, 0x69];
201 write_compressed_frame(&mut buf, 0, &payload);
202
203 let (varint_size, frame_len) = try_read_frame(&buf).unwrap().unwrap();
204 let frame_data = &buf[varint_size..varint_size + frame_len];
205 let (uncompressed_size, data) = read_compressed_frame(frame_data).unwrap();
206 assert_eq!(uncompressed_size, 0);
207 assert_eq!(data, &payload[..]);
208 }
209}