1use bacnet_encoding::primitives;
4use bacnet_encoding::tags;
5use bacnet_types::enums::PropertyIdentifier;
6use bacnet_types::error::Error;
7use bytes::{BufMut, BytesMut};
8
9pub const MAX_DECODED_ITEMS: usize = 10_000;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct PropertyReference {
26 pub property_identifier: PropertyIdentifier,
27 pub property_array_index: Option<u32>,
28}
29
30impl PropertyReference {
31 pub fn encode(&self, buf: &mut BytesMut) {
32 primitives::encode_ctx_unsigned(buf, 0, self.property_identifier.to_raw() as u64);
33 if let Some(idx) = self.property_array_index {
34 primitives::encode_ctx_unsigned(buf, 1, idx as u64);
35 }
36 }
37
38 pub fn decode(data: &[u8], offset: usize) -> Result<(Self, usize), Error> {
39 let (tag, pos) = tags::decode_tag(data, offset)?;
41 let end = pos + tag.length as usize;
42 if end > data.len() {
43 return Err(Error::decoding(
44 pos,
45 "PropertyReference truncated at property-id",
46 ));
47 }
48 let prop_id = primitives::decode_unsigned(&data[pos..end])? as u32;
49 let mut offset = end;
50
51 let mut array_index = None;
53 if offset < data.len() {
54 let (tag, new_pos) = tags::decode_tag(data, offset)?;
55 if tag.is_context(1) {
56 let end = new_pos + tag.length as usize;
57 if end > data.len() {
58 return Err(Error::decoding(
59 new_pos,
60 "PropertyReference truncated at array-index",
61 ));
62 }
63 array_index = Some(primitives::decode_unsigned(&data[new_pos..end])? as u32);
64 offset = end;
65 }
66 }
67
68 Ok((
69 Self {
70 property_identifier: PropertyIdentifier::from_raw(prop_id),
71 property_array_index: array_index,
72 },
73 offset,
74 ))
75 }
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
96pub struct BACnetPropertyValue {
97 pub property_identifier: PropertyIdentifier,
98 pub property_array_index: Option<u32>,
99 pub value: Vec<u8>,
100 pub priority: Option<u8>,
101}
102
103impl BACnetPropertyValue {
104 pub fn encode(&self, buf: &mut BytesMut) {
105 primitives::encode_ctx_unsigned(buf, 0, self.property_identifier.to_raw() as u64);
107 if let Some(idx) = self.property_array_index {
109 primitives::encode_ctx_unsigned(buf, 1, idx as u64);
110 }
111 tags::encode_opening_tag(buf, 2);
113 buf.put_slice(&self.value);
114 tags::encode_closing_tag(buf, 2);
115 if let Some(prio) = self.priority {
117 primitives::encode_ctx_unsigned(buf, 3, prio as u64);
118 }
119 }
120
121 pub fn decode(data: &[u8], offset: usize) -> Result<(Self, usize), Error> {
122 let (tag, pos) = tags::decode_tag(data, offset)?;
124 let end = pos + tag.length as usize;
125 if end > data.len() {
126 return Err(Error::decoding(
127 pos,
128 "BACnetPropertyValue truncated at property-id",
129 ));
130 }
131 let prop_id = primitives::decode_unsigned(&data[pos..end])? as u32;
132 let mut offset = end;
133
134 let mut array_index = None;
136 if offset < data.len() {
137 let (tag, new_pos) = tags::decode_tag(data, offset)?;
138 if tag.is_context(1) {
139 let end = new_pos + tag.length as usize;
140 if end > data.len() {
141 return Err(Error::decoding(
142 new_pos,
143 "BACnetPropertyValue truncated at array-index",
144 ));
145 }
146 array_index = Some(primitives::decode_unsigned(&data[new_pos..end])? as u32);
147 offset = end;
148 }
149 }
150
151 let (tag, tag_end) = tags::decode_tag(data, offset)?;
154 if !tag.is_opening_tag(2) {
155 return Err(Error::decoding(
156 offset,
157 "BACnetPropertyValue expected opening tag 2",
158 ));
159 }
160 let (value_bytes, offset) = tags::extract_context_value(data, tag_end, 2)?;
161 let value = value_bytes.to_vec();
162
163 let mut priority = None;
165 if offset < data.len() {
166 let (tag, new_pos) = tags::decode_tag(data, offset)?;
167 if tag.is_context(3) {
168 let end = new_pos + tag.length as usize;
169 if end > data.len() {
170 return Err(Error::decoding(
171 new_pos,
172 "BACnetPropertyValue truncated at priority",
173 ));
174 }
175 let prio = primitives::decode_unsigned(&data[new_pos..end])? as u8;
176 if !(1..=16).contains(&prio) {
177 return Err(Error::decoding(
178 new_pos,
179 format!("BACnetPropertyValue priority {prio} out of range 1-16"),
180 ));
181 }
182 priority = Some(prio);
183 return Ok((
184 Self {
185 property_identifier: PropertyIdentifier::from_raw(prop_id),
186 property_array_index: array_index,
187 value,
188 priority,
189 },
190 end,
191 ));
192 }
193 }
194
195 Ok((
196 Self {
197 property_identifier: PropertyIdentifier::from_raw(prop_id),
198 property_array_index: array_index,
199 value,
200 priority,
201 },
202 offset,
203 ))
204 }
205}
206
207#[cfg(test)]
212mod tests {
213 use super::*;
214
215 #[test]
216 fn property_reference_round_trip() {
217 let pr = PropertyReference {
218 property_identifier: PropertyIdentifier::PRESENT_VALUE,
219 property_array_index: None,
220 };
221 let mut buf = BytesMut::new();
222 pr.encode(&mut buf);
223 let (decoded, _) = PropertyReference::decode(&buf, 0).unwrap();
224 assert_eq!(pr, decoded);
225 }
226
227 #[test]
228 fn property_reference_with_index_round_trip() {
229 let pr = PropertyReference {
230 property_identifier: PropertyIdentifier::PRIORITY_ARRAY,
231 property_array_index: Some(8),
232 };
233 let mut buf = BytesMut::new();
234 pr.encode(&mut buf);
235 let (decoded, _) = PropertyReference::decode(&buf, 0).unwrap();
236 assert_eq!(pr, decoded);
237 }
238
239 #[test]
240 fn bacnet_property_value_round_trip() {
241 let pv = BACnetPropertyValue {
242 property_identifier: PropertyIdentifier::PRESENT_VALUE,
243 property_array_index: None,
244 value: vec![0x44, 0x42, 0x90, 0x00, 0x00], priority: None,
246 };
247 let mut buf = BytesMut::new();
248 pv.encode(&mut buf);
249 let (decoded, _) = BACnetPropertyValue::decode(&buf, 0).unwrap();
250 assert_eq!(pv, decoded);
251 }
252
253 #[test]
254 fn bacnet_property_value_with_all_fields() {
255 let pv = BACnetPropertyValue {
256 property_identifier: PropertyIdentifier::PRESENT_VALUE,
257 property_array_index: Some(5),
258 value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
259 priority: Some(8),
260 };
261 let mut buf = BytesMut::new();
262 pv.encode(&mut buf);
263 let (decoded, _) = BACnetPropertyValue::decode(&buf, 0).unwrap();
264 assert_eq!(pv, decoded);
265 }
266
267 #[test]
268 fn bacnet_property_value_priority_validation() {
269 let pv = BACnetPropertyValue {
270 property_identifier: PropertyIdentifier::PRESENT_VALUE,
271 property_array_index: None,
272 value: vec![0x10], priority: Some(8),
274 };
275 let mut buf = BytesMut::new();
276 pv.encode(&mut buf);
277
278 let data = buf.to_vec();
280 let mut corrupted = data.clone();
281 let last = corrupted.len() - 1;
283 corrupted[last] = 0; assert!(BACnetPropertyValue::decode(&corrupted, 0).is_err());
285 }
286
287 #[test]
292 fn test_decode_property_reference_empty_input() {
293 assert!(PropertyReference::decode(&[], 0).is_err());
294 }
295
296 #[test]
297 fn test_decode_property_reference_truncated_1_byte() {
298 let pr = PropertyReference {
299 property_identifier: PropertyIdentifier::PRESENT_VALUE,
300 property_array_index: Some(8),
301 };
302 let mut buf = BytesMut::new();
303 pr.encode(&mut buf);
304 assert!(PropertyReference::decode(&buf[..1], 0).is_err());
305 }
306
307 #[test]
308 fn test_decode_property_reference_invalid_tag() {
309 assert!(PropertyReference::decode(&[0xFF, 0xFF, 0xFF], 0).is_err());
310 }
311
312 #[test]
313 fn test_decode_bacnet_property_value_empty_input() {
314 assert!(BACnetPropertyValue::decode(&[], 0).is_err());
315 }
316
317 #[test]
318 fn test_decode_bacnet_property_value_truncated_1_byte() {
319 let pv = BACnetPropertyValue {
320 property_identifier: PropertyIdentifier::PRESENT_VALUE,
321 property_array_index: None,
322 value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
323 priority: None,
324 };
325 let mut buf = BytesMut::new();
326 pv.encode(&mut buf);
327 assert!(BACnetPropertyValue::decode(&buf[..1], 0).is_err());
328 }
329
330 #[test]
331 fn test_decode_bacnet_property_value_truncated_2_bytes() {
332 let pv = BACnetPropertyValue {
333 property_identifier: PropertyIdentifier::PRESENT_VALUE,
334 property_array_index: None,
335 value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
336 priority: None,
337 };
338 let mut buf = BytesMut::new();
339 pv.encode(&mut buf);
340 assert!(BACnetPropertyValue::decode(&buf[..2], 0).is_err());
341 }
342
343 #[test]
344 fn test_decode_bacnet_property_value_truncated_3_bytes() {
345 let pv = BACnetPropertyValue {
346 property_identifier: PropertyIdentifier::PRESENT_VALUE,
347 property_array_index: None,
348 value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
349 priority: None,
350 };
351 let mut buf = BytesMut::new();
352 pv.encode(&mut buf);
353 assert!(BACnetPropertyValue::decode(&buf[..3], 0).is_err());
354 }
355
356 #[test]
357 fn test_decode_bacnet_property_value_invalid_tag() {
358 assert!(BACnetPropertyValue::decode(&[0xFF, 0xFF, 0xFF], 0).is_err());
359 }
360
361 #[test]
362 fn test_decode_bacnet_property_value_oversized_length() {
363 assert!(BACnetPropertyValue::decode(&[0x05, 0xFF], 0).is_err());
365 }
366}