Skip to main content

zerodds_qos/policies/
generic_data.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3//! User/Group/Topic DataQosPolicy (DDS 1.4 §2.2.3.1–3).
4//!
5//! Alle drei teilen dieselbe Wire-Form: opaque `sequence<octet>` =
6//! u32 length + N × octet (plus CDR-Alignment-Padding auf 4 byte).
7
8use alloc::vec::Vec;
9
10use zerodds_cdr::{BufferReader, BufferWriter, DecodeError, EncodeError};
11
12/// DoS-Cap pro opaque-Data-Block. 64 KiB deckt realistische
13/// UserData/TopicData/GroupData-Payloads ab (ueblich <1 KiB); groesseres
14/// wird als `LengthExceeded` abgelehnt. Spec legt keine harte Grenze
15/// fest, aber Cyclone DDS nutzt ~1 MiB als "vernuenftig".
16pub const MAX_OPAQUE_LEN: usize = 64 * 1024;
17
18/// Gemeinsame Wire-Form: opaque sequence<octet>.
19fn encode_opaque(w: &mut BufferWriter, bytes: &[u8]) -> Result<(), EncodeError> {
20    let len = u32::try_from(bytes.len()).map_err(|_| EncodeError::ValueOutOfRange {
21        message: "opaque data length exceeds u32::MAX",
22    })?;
23    w.write_u32(len)?;
24    w.write_bytes(bytes)
25}
26
27fn decode_opaque(r: &mut BufferReader<'_>) -> Result<Vec<u8>, DecodeError> {
28    let len = r.read_u32()? as usize;
29    // Harter DoS-Cap: MAX_OPAQUE_LEN.
30    if len > MAX_OPAQUE_LEN {
31        return Err(DecodeError::LengthExceeded {
32            announced: len,
33            remaining: MAX_OPAQUE_LEN,
34            offset: 0,
35        });
36    }
37    // read_bytes liefert einen Slice — direkt kopieren, keine Vec-
38    // with_capacity-Optimierung (die extend_from_slice re-allokiert
39    // sowieso wenn noetig).
40    Ok(r.read_bytes(len)?.to_vec())
41}
42
43/// UserDataQosPolicy.
44#[derive(Debug, Clone, PartialEq, Eq, Default)]
45pub struct UserDataQosPolicy {
46    /// Opaque Bytes.
47    pub value: Vec<u8>,
48}
49
50impl UserDataQosPolicy {
51    /// Wire-Encoding.
52    ///
53    /// # Errors
54    /// `ValueOutOfRange` bei u32-Ueberlauf.
55    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
56        encode_opaque(w, &self.value)
57    }
58
59    /// Wire-Decoding.
60    ///
61    /// # Errors
62    /// Buffer-Underflow.
63    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
64        Ok(Self {
65            value: decode_opaque(r)?,
66        })
67    }
68}
69
70/// TopicDataQosPolicy.
71#[derive(Debug, Clone, PartialEq, Eq, Default)]
72pub struct TopicDataQosPolicy {
73    /// Opaque Bytes.
74    pub value: Vec<u8>,
75}
76
77impl TopicDataQosPolicy {
78    /// Wire-Encoding.
79    ///
80    /// # Errors
81    /// `ValueOutOfRange` bei u32-Ueberlauf.
82    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
83        encode_opaque(w, &self.value)
84    }
85
86    /// Wire-Decoding.
87    ///
88    /// # Errors
89    /// Buffer-Underflow.
90    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
91        Ok(Self {
92            value: decode_opaque(r)?,
93        })
94    }
95}
96
97/// GroupDataQosPolicy.
98#[derive(Debug, Clone, PartialEq, Eq, Default)]
99pub struct GroupDataQosPolicy {
100    /// Opaque Bytes.
101    pub value: Vec<u8>,
102}
103
104impl GroupDataQosPolicy {
105    /// Wire-Encoding.
106    ///
107    /// # Errors
108    /// `ValueOutOfRange` bei u32-Ueberlauf.
109    pub fn encode_into(&self, w: &mut BufferWriter) -> Result<(), EncodeError> {
110        encode_opaque(w, &self.value)
111    }
112
113    /// Wire-Decoding.
114    ///
115    /// # Errors
116    /// Buffer-Underflow.
117    pub fn decode_from(r: &mut BufferReader<'_>) -> Result<Self, DecodeError> {
118        Ok(Self {
119            value: decode_opaque(r)?,
120        })
121    }
122}
123
124#[cfg(test)]
125#[allow(clippy::unwrap_used)]
126mod tests {
127    use super::*;
128    use zerodds_cdr::Endianness;
129
130    #[test]
131    fn user_data_roundtrip() {
132        let p = UserDataQosPolicy {
133            value: alloc::vec![1, 2, 3, 4],
134        };
135        let mut w = BufferWriter::new(Endianness::Little);
136        p.encode_into(&mut w).unwrap();
137        let bytes = w.into_bytes();
138        let mut r = BufferReader::new(&bytes, Endianness::Little);
139        assert_eq!(UserDataQosPolicy::decode_from(&mut r).unwrap(), p);
140    }
141
142    #[test]
143    fn empty_data_roundtrip() {
144        let p = TopicDataQosPolicy::default();
145        let mut w = BufferWriter::new(Endianness::Little);
146        p.encode_into(&mut w).unwrap();
147        let bytes = w.into_bytes();
148        let mut r = BufferReader::new(&bytes, Endianness::Little);
149        assert_eq!(TopicDataQosPolicy::decode_from(&mut r).unwrap(), p);
150    }
151
152    #[test]
153    fn decoder_rejects_oversized_opaque() {
154        let mut bytes = alloc::vec::Vec::new();
155        bytes.extend_from_slice(&(MAX_OPAQUE_LEN as u32 + 1).to_le_bytes());
156        let mut r = BufferReader::new(&bytes, Endianness::Little);
157        let err = UserDataQosPolicy::decode_from(&mut r).unwrap_err();
158        assert!(matches!(err, DecodeError::LengthExceeded { .. }));
159    }
160
161    #[test]
162    fn group_data_large_payload() {
163        let p = GroupDataQosPolicy {
164            value: (0..200u8).collect(),
165        };
166        let mut w = BufferWriter::new(Endianness::Little);
167        p.encode_into(&mut w).unwrap();
168        let bytes = w.into_bytes();
169        let mut r = BufferReader::new(&bytes, Endianness::Little);
170        assert_eq!(GroupDataQosPolicy::decode_from(&mut r).unwrap(), p);
171    }
172}