Skip to main content

basalt_types/
byte_array.rs

1use crate::{Decode, Encode, EncodedSize, Error, Result, VarInt};
2
3/// Encodes a byte vector as a Minecraft protocol byte array.
4///
5/// Minecraft byte arrays are raw byte sequences prefixed by a VarInt
6/// indicating the length. They are used for plugin channel data, chunk
7/// sections, encryption payloads, and other binary blobs in the protocol.
8/// Unlike strings, byte arrays have no encoding or length constraints
9/// beyond what the packet format imposes.
10impl Encode for Vec<u8> {
11    /// Writes a VarInt length prefix followed by the raw bytes.
12    fn encode(&self, buf: &mut Vec<u8>) -> Result<()> {
13        VarInt(self.len() as i32).encode(buf)?;
14        buf.extend_from_slice(self);
15        Ok(())
16    }
17}
18
19/// Decodes a Minecraft protocol byte array into a `Vec<u8>`.
20///
21/// Reads a VarInt byte length, then reads exactly that many bytes from
22/// the buffer. No validation is performed on the byte content — the
23/// caller is responsible for interpreting the data.
24impl Decode for Vec<u8> {
25    /// Reads the VarInt length prefix, then copies the raw payload.
26    ///
27    /// Fails with `Error::BufferUnderflow` if the buffer is shorter
28    /// than the declared length.
29    fn decode(buf: &mut &[u8]) -> Result<Self> {
30        let raw_len = VarInt::decode(buf)?.0;
31        if raw_len < 0 {
32            return Err(Error::InvalidData(format!(
33                "negative byte array length: {raw_len}"
34            )));
35        }
36        let len = raw_len as usize;
37        if buf.len() < len {
38            return Err(Error::BufferUnderflow {
39                needed: len,
40                available: buf.len(),
41            });
42        }
43        let (bytes, rest) = buf.split_at(len);
44        let value = bytes.to_vec();
45        *buf = rest;
46        Ok(value)
47    }
48}
49
50/// Computes the wire size of a Minecraft protocol byte array.
51///
52/// The total size is the VarInt-encoded length prefix plus the byte count.
53/// This enables exact buffer pre-allocation before encoding.
54impl EncodedSize for Vec<u8> {
55    /// Returns the VarInt prefix size plus the array's byte length.
56    fn encoded_size(&self) -> usize {
57        VarInt(self.len() as i32).encoded_size() + self.len()
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    fn roundtrip(data: &[u8]) {
66        let original = data.to_vec();
67        let mut buf = Vec::with_capacity(original.encoded_size());
68        original.encode(&mut buf).unwrap();
69        assert_eq!(buf.len(), original.encoded_size());
70
71        let mut cursor = buf.as_slice();
72        let decoded = Vec::<u8>::decode(&mut cursor).unwrap();
73        assert!(cursor.is_empty());
74        assert_eq!(decoded, original);
75    }
76
77    #[test]
78    fn empty() {
79        roundtrip(&[]);
80    }
81
82    #[test]
83    fn small() {
84        roundtrip(&[0x01, 0x02, 0x03]);
85    }
86
87    #[test]
88    fn large() {
89        roundtrip(&vec![0xAB; 1024]);
90    }
91
92    #[test]
93    fn truncated_buffer() {
94        let mut buf = Vec::new();
95        VarInt(10).encode(&mut buf).unwrap();
96        buf.extend_from_slice(&[0x01; 5]);
97
98        let mut cursor = buf.as_slice();
99        assert!(matches!(
100            Vec::<u8>::decode(&mut cursor),
101            Err(Error::BufferUnderflow { .. })
102        ));
103    }
104
105    #[test]
106    fn encoded_size_accounts_for_varint_prefix() {
107        assert_eq!(Vec::<u8>::new().encoded_size(), 1);
108        assert_eq!(vec![0u8; 3].encoded_size(), 4);
109    }
110
111    mod proptests {
112        use super::*;
113        use proptest::prelude::*;
114
115        proptest! {
116            #[test]
117            fn byte_array_roundtrip(data in proptest::collection::vec(any::<u8>(), 0..1000)) {
118                roundtrip(&data);
119            }
120        }
121    }
122}