Skip to main content

basalt_types/
opaque.rs

1//! Opaque byte buffer for protocol types that can't be parsed.
2//!
3//! `OpaqueBytes` wraps a `Vec<u8>` to distinguish intentionally opaque
4//! protocol data (native types, switch fallbacks) from other byte
5//! vectors that happen to use `Vec<u8>`. It encodes/decodes as a
6//! length-prefixed byte array, same as `Vec<u8>`.
7
8use crate::{Decode, Encode, EncodedSize, Error, Result, VarInt};
9
10/// A byte buffer for protocol data whose internal structure is not
11/// parsed by the codec.
12///
13/// Used for native protocol types (`entityMetadata`, `registryEntryHolder`,
14/// etc.) and switch field fallbacks where the wire format is known
15/// but too complex to represent in the type system.
16///
17/// Unlike raw `Vec<u8>`, `OpaqueBytes` makes the intent explicit:
18/// "this data is intentionally unparsed".
19#[derive(Debug, Clone, Default, PartialEq)]
20pub struct OpaqueBytes(pub Vec<u8>);
21
22impl OpaqueBytes {
23    /// Creates an empty opaque buffer.
24    pub fn new() -> Self {
25        Self(Vec::new())
26    }
27
28    /// Creates an opaque buffer from raw bytes.
29    pub fn from_bytes(bytes: Vec<u8>) -> Self {
30        Self(bytes)
31    }
32
33    /// Returns a reference to the inner bytes.
34    pub fn as_bytes(&self) -> &[u8] {
35        &self.0
36    }
37
38    /// Returns the number of bytes.
39    pub fn len(&self) -> usize {
40        self.0.len()
41    }
42
43    /// Returns true if the buffer is empty.
44    pub fn is_empty(&self) -> bool {
45        self.0.is_empty()
46    }
47}
48
49impl Encode for OpaqueBytes {
50    /// Encodes as a VarInt length prefix followed by the raw bytes.
51    fn encode(&self, buf: &mut Vec<u8>) -> Result<()> {
52        VarInt(self.0.len() as i32).encode(buf)?;
53        buf.extend_from_slice(&self.0);
54        Ok(())
55    }
56}
57
58impl Decode for OpaqueBytes {
59    /// Decodes a VarInt length prefix then reads that many bytes.
60    fn decode(buf: &mut &[u8]) -> Result<Self> {
61        let raw_len = VarInt::decode(buf)?.0;
62        if raw_len < 0 {
63            return Err(Error::InvalidData(format!(
64                "negative opaque length: {raw_len}"
65            )));
66        }
67        let len = raw_len as usize;
68        if buf.len() < len {
69            return Err(Error::BufferUnderflow {
70                needed: len,
71                available: buf.len(),
72            });
73        }
74        let (bytes, rest) = buf.split_at(len);
75        let value = bytes.to_vec();
76        *buf = rest;
77        Ok(Self(value))
78    }
79}
80
81impl EncodedSize for OpaqueBytes {
82    fn encoded_size(&self) -> usize {
83        VarInt(self.0.len() as i32).encoded_size() + self.0.len()
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn empty_roundtrip() {
93        let original = OpaqueBytes::new();
94        let mut buf = Vec::new();
95        original.encode(&mut buf).unwrap();
96        assert_eq!(buf.len(), 1); // VarInt(0)
97
98        let mut cursor = buf.as_slice();
99        let decoded = OpaqueBytes::decode(&mut cursor).unwrap();
100        assert!(cursor.is_empty());
101        assert_eq!(decoded, original);
102    }
103
104    #[test]
105    fn data_roundtrip() {
106        let original = OpaqueBytes::from_bytes(vec![1, 2, 3, 4, 5]);
107        let mut buf = Vec::with_capacity(original.encoded_size());
108        original.encode(&mut buf).unwrap();
109        assert_eq!(buf.len(), original.encoded_size());
110
111        let mut cursor = buf.as_slice();
112        let decoded = OpaqueBytes::decode(&mut cursor).unwrap();
113        assert!(cursor.is_empty());
114        assert_eq!(decoded, original);
115    }
116
117    #[test]
118    fn accessors() {
119        let opaque = OpaqueBytes::from_bytes(vec![0xAB, 0xCD]);
120        assert_eq!(opaque.len(), 2);
121        assert!(!opaque.is_empty());
122        assert_eq!(opaque.as_bytes(), &[0xAB, 0xCD]);
123    }
124}