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)?;
153 if !tag.is_opening_tag(2) {
154 return Err(Error::decoding(
155 offset,
156 "BACnetPropertyValue expected opening tag 2",
157 ));
158 }
159 let (value_bytes, offset) = tags::extract_context_value(data, tag_end, 2)?;
160 let value = value_bytes.to_vec();
161
162 let mut priority = None;
164 if offset < data.len() {
165 let (tag, new_pos) = tags::decode_tag(data, offset)?;
166 if tag.is_context(3) {
167 let end = new_pos + tag.length as usize;
168 if end > data.len() {
169 return Err(Error::decoding(
170 new_pos,
171 "BACnetPropertyValue truncated at priority",
172 ));
173 }
174 let prio = primitives::decode_unsigned(&data[new_pos..end])? as u8;
175 if !(1..=16).contains(&prio) {
176 return Err(Error::decoding(
177 new_pos,
178 format!("BACnetPropertyValue priority {prio} out of range 1-16"),
179 ));
180 }
181 priority = Some(prio);
182 return Ok((
183 Self {
184 property_identifier: PropertyIdentifier::from_raw(prop_id),
185 property_array_index: array_index,
186 value,
187 priority,
188 },
189 end,
190 ));
191 }
192 }
193
194 Ok((
195 Self {
196 property_identifier: PropertyIdentifier::from_raw(prop_id),
197 property_array_index: array_index,
198 value,
199 priority,
200 },
201 offset,
202 ))
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn property_reference_round_trip() {
212 let pr = PropertyReference {
213 property_identifier: PropertyIdentifier::PRESENT_VALUE,
214 property_array_index: None,
215 };
216 let mut buf = BytesMut::new();
217 pr.encode(&mut buf);
218 let (decoded, _) = PropertyReference::decode(&buf, 0).unwrap();
219 assert_eq!(pr, decoded);
220 }
221
222 #[test]
223 fn property_reference_with_index_round_trip() {
224 let pr = PropertyReference {
225 property_identifier: PropertyIdentifier::PRIORITY_ARRAY,
226 property_array_index: Some(8),
227 };
228 let mut buf = BytesMut::new();
229 pr.encode(&mut buf);
230 let (decoded, _) = PropertyReference::decode(&buf, 0).unwrap();
231 assert_eq!(pr, decoded);
232 }
233
234 #[test]
235 fn bacnet_property_value_round_trip() {
236 let pv = BACnetPropertyValue {
237 property_identifier: PropertyIdentifier::PRESENT_VALUE,
238 property_array_index: None,
239 value: vec![0x44, 0x42, 0x90, 0x00, 0x00], priority: None,
241 };
242 let mut buf = BytesMut::new();
243 pv.encode(&mut buf);
244 let (decoded, _) = BACnetPropertyValue::decode(&buf, 0).unwrap();
245 assert_eq!(pv, decoded);
246 }
247
248 #[test]
249 fn bacnet_property_value_with_all_fields() {
250 let pv = BACnetPropertyValue {
251 property_identifier: PropertyIdentifier::PRESENT_VALUE,
252 property_array_index: Some(5),
253 value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
254 priority: Some(8),
255 };
256 let mut buf = BytesMut::new();
257 pv.encode(&mut buf);
258 let (decoded, _) = BACnetPropertyValue::decode(&buf, 0).unwrap();
259 assert_eq!(pv, decoded);
260 }
261
262 #[test]
263 fn bacnet_property_value_priority_validation() {
264 let pv = BACnetPropertyValue {
265 property_identifier: PropertyIdentifier::PRESENT_VALUE,
266 property_array_index: None,
267 value: vec![0x10], priority: Some(8),
269 };
270 let mut buf = BytesMut::new();
271 pv.encode(&mut buf);
272
273 let data = buf.to_vec();
275 let mut corrupted = data.clone();
276 let last = corrupted.len() - 1;
278 corrupted[last] = 0; assert!(BACnetPropertyValue::decode(&corrupted, 0).is_err());
280 }
281
282 #[test]
287 fn test_decode_property_reference_empty_input() {
288 assert!(PropertyReference::decode(&[], 0).is_err());
289 }
290
291 #[test]
292 fn test_decode_property_reference_truncated_1_byte() {
293 let pr = PropertyReference {
294 property_identifier: PropertyIdentifier::PRESENT_VALUE,
295 property_array_index: Some(8),
296 };
297 let mut buf = BytesMut::new();
298 pr.encode(&mut buf);
299 assert!(PropertyReference::decode(&buf[..1], 0).is_err());
300 }
301
302 #[test]
303 fn test_decode_property_reference_invalid_tag() {
304 assert!(PropertyReference::decode(&[0xFF, 0xFF, 0xFF], 0).is_err());
305 }
306
307 #[test]
308 fn test_decode_bacnet_property_value_empty_input() {
309 assert!(BACnetPropertyValue::decode(&[], 0).is_err());
310 }
311
312 #[test]
313 fn test_decode_bacnet_property_value_truncated_1_byte() {
314 let pv = BACnetPropertyValue {
315 property_identifier: PropertyIdentifier::PRESENT_VALUE,
316 property_array_index: None,
317 value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
318 priority: None,
319 };
320 let mut buf = BytesMut::new();
321 pv.encode(&mut buf);
322 assert!(BACnetPropertyValue::decode(&buf[..1], 0).is_err());
323 }
324
325 #[test]
326 fn test_decode_bacnet_property_value_truncated_2_bytes() {
327 let pv = BACnetPropertyValue {
328 property_identifier: PropertyIdentifier::PRESENT_VALUE,
329 property_array_index: None,
330 value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
331 priority: None,
332 };
333 let mut buf = BytesMut::new();
334 pv.encode(&mut buf);
335 assert!(BACnetPropertyValue::decode(&buf[..2], 0).is_err());
336 }
337
338 #[test]
339 fn test_decode_bacnet_property_value_truncated_3_bytes() {
340 let pv = BACnetPropertyValue {
341 property_identifier: PropertyIdentifier::PRESENT_VALUE,
342 property_array_index: None,
343 value: vec![0x44, 0x42, 0x90, 0x00, 0x00],
344 priority: None,
345 };
346 let mut buf = BytesMut::new();
347 pv.encode(&mut buf);
348 assert!(BACnetPropertyValue::decode(&buf[..3], 0).is_err());
349 }
350
351 #[test]
352 fn test_decode_bacnet_property_value_invalid_tag() {
353 assert!(BACnetPropertyValue::decode(&[0xFF, 0xFF, 0xFF], 0).is_err());
354 }
355
356 #[test]
357 fn test_decode_bacnet_property_value_oversized_length() {
358 assert!(BACnetPropertyValue::decode(&[0x05, 0xFF], 0).is_err());
360 }
361}