1use bacnet_types::error::Error;
7use bacnet_types::primitives::{BACnetTimeStamp, Date, ObjectIdentifier, PropertyValue, Time};
8use bytes::{BufMut, BytesMut};
9
10use crate::tags::{self, app_tag, TagClass};
11
12pub fn encode_unsigned(buf: &mut BytesMut, value: u64) {
20 if value <= 0xFF {
21 buf.put_u8(value as u8);
22 } else if value <= 0xFFFF {
23 buf.put_u16(value as u16);
24 } else if value <= 0xFF_FFFF {
25 buf.put_u8((value >> 16) as u8);
26 buf.put_u16(value as u16);
27 } else if value <= 0xFFFF_FFFF {
28 buf.put_u32(value as u32);
29 } else if value <= 0xFF_FFFF_FFFF {
30 buf.put_u8((value >> 32) as u8);
31 buf.put_u32(value as u32);
32 } else if value <= 0xFFFF_FFFF_FFFF {
33 buf.put_u16((value >> 32) as u16);
34 buf.put_u32(value as u32);
35 } else if value <= 0xFF_FFFF_FFFF_FFFF {
36 buf.put_u8((value >> 48) as u8);
37 buf.put_u16((value >> 32) as u16);
38 buf.put_u32(value as u32);
39 } else {
40 buf.put_u64(value);
41 }
42}
43
44pub fn unsigned_len(value: u64) -> u32 {
46 if value <= 0xFF {
47 1
48 } else if value <= 0xFFFF {
49 2
50 } else if value <= 0xFF_FFFF {
51 3
52 } else if value <= 0xFFFF_FFFF {
53 4
54 } else if value <= 0xFF_FFFF_FFFF {
55 5
56 } else if value <= 0xFFFF_FFFF_FFFF {
57 6
58 } else if value <= 0xFF_FFFF_FFFF_FFFF {
59 7
60 } else {
61 8
62 }
63}
64
65pub fn decode_unsigned(data: &[u8]) -> Result<u64, Error> {
67 if data.is_empty() || data.len() > 8 {
68 return Err(Error::Decoding {
69 offset: 0,
70 message: format!("unsigned requires 1-8 bytes, got {}", data.len()),
71 });
72 }
73 let mut value: u64 = 0;
74 for &b in data {
75 value = (value << 8) | b as u64;
76 }
77 Ok(value)
78}
79
80pub fn encode_signed(buf: &mut BytesMut, value: i32) {
84 let n = signed_len(value);
85 let bytes = value.to_be_bytes();
86 buf.put_slice(&bytes[4 - n as usize..]);
87}
88
89pub fn signed_len(value: i32) -> u32 {
91 if (-128..=127).contains(&value) {
92 1
93 } else if (-32768..=32767).contains(&value) {
94 2
95 } else if (-8_388_608..=8_388_607).contains(&value) {
96 3
97 } else {
98 4
99 }
100}
101
102pub fn decode_signed(data: &[u8]) -> Result<i32, Error> {
104 if data.is_empty() || data.len() > 4 {
105 return Err(Error::Decoding {
106 offset: 0,
107 message: format!("signed requires 1-4 bytes, got {}", data.len()),
108 });
109 }
110 let sign_extend = if data[0] & 0x80 != 0 { 0xFF } else { 0x00 };
111 let mut bytes = [sign_extend; 4];
112 bytes[4 - data.len()..].copy_from_slice(data);
113 Ok(i32::from_be_bytes(bytes))
114}
115
116pub fn encode_real(buf: &mut BytesMut, value: f32) {
120 buf.put_f32(value);
121}
122
123pub fn decode_real(data: &[u8]) -> Result<f32, Error> {
125 if data.len() < 4 {
126 return Err(Error::buffer_too_short(4, data.len()));
127 }
128 Ok(f32::from_be_bytes([data[0], data[1], data[2], data[3]]))
129}
130
131pub fn encode_double(buf: &mut BytesMut, value: f64) {
135 buf.put_f64(value);
136}
137
138pub fn decode_double(data: &[u8]) -> Result<f64, Error> {
140 if data.len() < 8 {
141 return Err(Error::buffer_too_short(8, data.len()));
142 }
143 let bytes: [u8; 8] = [
144 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
145 ];
146 Ok(f64::from_be_bytes(bytes))
147}
148
149pub mod charset {
153 pub const UTF8: u8 = 0;
155 pub const IBM_MICROSOFT_DBCS: u8 = 1;
157 pub const JIS_X_0208: u8 = 2;
159 pub const UCS4: u8 = 3;
161 pub const UCS2: u8 = 4;
163 pub const ISO_8859_1: u8 = 5;
165}
166
167pub fn encode_character_string(buf: &mut BytesMut, value: &str) {
169 buf.put_u8(charset::UTF8);
170 buf.put_slice(value.as_bytes());
171}
172
173pub fn character_string_len(value: &str) -> Result<u32, Error> {
175 u32::try_from(value.len())
176 .ok()
177 .and_then(|n| n.checked_add(1))
178 .ok_or_else(|| Error::Encoding("CharacterString too long for BACnet encoding".into()))
179}
180
181pub fn decode_character_string(data: &[u8]) -> Result<String, Error> {
190 if data.is_empty() {
191 return Err(Error::Decoding {
192 offset: 0,
193 message: "CharacterString requires at least 1 byte for charset".into(),
194 });
195 }
196 let charset_id = data[0];
197 let payload = &data[1..];
198 match charset_id {
199 charset::UTF8 => String::from_utf8(payload.to_vec()).map_err(|e| Error::Decoding {
200 offset: 1,
201 message: format!("invalid UTF-8: {e}"),
202 }),
203 charset::UCS2 => {
204 if !payload.len().is_multiple_of(2) {
205 return Err(Error::Decoding {
206 offset: 1,
207 message: "UCS-2 data must have even length".into(),
208 });
209 }
210 let mut s = String::new();
211 for (i, chunk) in payload.chunks_exact(2).enumerate() {
212 let code_point = u16::from_be_bytes([chunk[0], chunk[1]]);
213 if let Some(c) = char::from_u32(code_point as u32) {
214 s.push(c);
215 } else {
216 return Err(Error::Decoding {
217 offset: 1 + i * 2,
218 message: "invalid UCS-2 code point".into(),
219 });
220 }
221 }
222 Ok(s)
223 }
224 charset::ISO_8859_1 => Ok(payload.iter().map(|&b| b as char).collect()),
225 charset::IBM_MICROSOFT_DBCS | charset::JIS_X_0208 | charset::UCS4 => Err(Error::Decoding {
226 offset: 0,
227 message: format!("unsupported charset: {charset_id}"),
228 }),
229 other => Err(Error::Decoding {
230 offset: 0,
231 message: format!("unknown charset: {other}"),
232 }),
233 }
234}
235
236pub fn encode_bit_string(buf: &mut BytesMut, unused_bits: u8, data: &[u8]) {
240 buf.put_u8(unused_bits);
241 buf.put_slice(data);
242}
243
244pub fn decode_bit_string(data: &[u8]) -> Result<(u8, Vec<u8>), Error> {
248 if data.is_empty() {
249 return Err(Error::Decoding {
250 offset: 0,
251 message: "BitString requires at least 1 byte for unused-bits count".into(),
252 });
253 }
254 let unused = data[0];
255 if unused > 7 {
256 return Err(Error::Decoding {
257 offset: 0,
258 message: format!("BitString unused_bits must be 0-7, got {unused}"),
259 });
260 }
261 Ok((unused, data[1..].to_vec()))
262}
263
264pub fn encode_app_null(buf: &mut BytesMut) {
270 tags::encode_tag(buf, app_tag::NULL, TagClass::Application, 0);
271}
272
273pub fn encode_app_boolean(buf: &mut BytesMut, value: bool) {
277 tags::encode_tag(
278 buf,
279 app_tag::BOOLEAN,
280 TagClass::Application,
281 if value { 1 } else { 0 },
282 );
283}
284
285pub fn encode_app_unsigned(buf: &mut BytesMut, value: u64) {
287 let len = unsigned_len(value);
288 tags::encode_tag(buf, app_tag::UNSIGNED, TagClass::Application, len);
289 encode_unsigned(buf, value);
290}
291
292pub fn encode_app_signed(buf: &mut BytesMut, value: i32) {
294 let len = signed_len(value);
295 tags::encode_tag(buf, app_tag::SIGNED, TagClass::Application, len);
296 encode_signed(buf, value);
297}
298
299pub fn encode_app_real(buf: &mut BytesMut, value: f32) {
301 tags::encode_tag(buf, app_tag::REAL, TagClass::Application, 4);
302 encode_real(buf, value);
303}
304
305pub fn encode_app_double(buf: &mut BytesMut, value: f64) {
307 tags::encode_tag(buf, app_tag::DOUBLE, TagClass::Application, 8);
308 encode_double(buf, value);
309}
310
311pub fn encode_app_octet_string(buf: &mut BytesMut, data: &[u8]) {
313 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
314 tags::encode_tag(buf, app_tag::OCTET_STRING, TagClass::Application, data_len);
315 buf.put_slice(data);
316}
317
318pub fn encode_app_character_string(buf: &mut BytesMut, value: &str) -> Result<(), Error> {
320 let len = character_string_len(value)?;
321 tags::encode_tag(buf, app_tag::CHARACTER_STRING, TagClass::Application, len);
322 encode_character_string(buf, value);
323 Ok(())
324}
325
326pub fn encode_app_bit_string(buf: &mut BytesMut, unused_bits: u8, data: &[u8]) {
328 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
329 let len = data_len.saturating_add(1);
330 tags::encode_tag(buf, app_tag::BIT_STRING, TagClass::Application, len);
331 encode_bit_string(buf, unused_bits, data);
332}
333
334pub fn encode_app_enumerated(buf: &mut BytesMut, value: u32) {
336 let len = unsigned_len(value as u64);
337 tags::encode_tag(buf, app_tag::ENUMERATED, TagClass::Application, len);
338 encode_unsigned(buf, value as u64);
339}
340
341pub fn encode_app_date(buf: &mut BytesMut, date: &Date) {
343 tags::encode_tag(buf, app_tag::DATE, TagClass::Application, 4);
344 buf.put_slice(&date.encode());
345}
346
347pub fn encode_app_time(buf: &mut BytesMut, time: &Time) {
349 tags::encode_tag(buf, app_tag::TIME, TagClass::Application, 4);
350 buf.put_slice(&time.encode());
351}
352
353pub fn encode_app_object_id(buf: &mut BytesMut, oid: &ObjectIdentifier) {
355 tags::encode_tag(buf, app_tag::OBJECT_IDENTIFIER, TagClass::Application, 4);
356 buf.put_slice(&oid.encode());
357}
358
359pub fn encode_ctx_unsigned(buf: &mut BytesMut, tag: u8, value: u64) {
365 let len = unsigned_len(value);
366 tags::encode_tag(buf, tag, TagClass::Context, len);
367 encode_unsigned(buf, value);
368}
369
370pub fn encode_ctx_signed(buf: &mut BytesMut, tag: u8, value: i32) {
372 let len = signed_len(value);
373 tags::encode_tag(buf, tag, TagClass::Context, len);
374 encode_signed(buf, value);
375}
376
377pub fn encode_ctx_real(buf: &mut BytesMut, tag: u8, value: f32) {
379 tags::encode_tag(buf, tag, TagClass::Context, 4);
380 encode_real(buf, value);
381}
382
383pub fn encode_ctx_double(buf: &mut BytesMut, tag: u8, value: f64) {
385 tags::encode_tag(buf, tag, TagClass::Context, 8);
386 encode_double(buf, value);
387}
388
389pub fn encode_ctx_enumerated(buf: &mut BytesMut, tag: u8, value: u32) {
391 let len = unsigned_len(value as u64);
392 tags::encode_tag(buf, tag, TagClass::Context, len);
393 encode_unsigned(buf, value as u64);
394}
395
396pub fn encode_ctx_boolean(buf: &mut BytesMut, tag: u8, value: bool) {
400 tags::encode_tag(buf, tag, TagClass::Context, 1);
401 buf.put_u8(if value { 1 } else { 0 });
402}
403
404pub fn encode_ctx_object_id(buf: &mut BytesMut, tag: u8, oid: &ObjectIdentifier) {
406 tags::encode_tag(buf, tag, TagClass::Context, 4);
407 buf.put_slice(&oid.encode());
408}
409
410pub fn encode_ctx_octet_string(buf: &mut BytesMut, tag: u8, data: &[u8]) {
412 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
413 tags::encode_tag(buf, tag, TagClass::Context, data_len);
414 buf.put_slice(data);
415}
416
417pub fn encode_ctx_character_string(buf: &mut BytesMut, tag: u8, value: &str) -> Result<(), Error> {
419 let len = character_string_len(value)?;
420 tags::encode_tag(buf, tag, TagClass::Context, len);
421 encode_character_string(buf, value);
422 Ok(())
423}
424
425pub fn encode_ctx_date(buf: &mut BytesMut, tag: u8, date: &Date) {
427 tags::encode_tag(buf, tag, TagClass::Context, 4);
428 buf.put_slice(&date.encode());
429}
430
431pub fn encode_ctx_bit_string(buf: &mut BytesMut, tag: u8, unused_bits: u8, data: &[u8]) {
433 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
434 let len = data_len.saturating_add(1);
435 tags::encode_tag(buf, tag, TagClass::Context, len);
436 encode_bit_string(buf, unused_bits, data);
437}
438
439pub fn decode_application_value(
447 data: &[u8],
448 offset: usize,
449) -> Result<(PropertyValue, usize), Error> {
450 let (tag, new_offset) = tags::decode_tag(data, offset)?;
451 if tag.class != TagClass::Application {
452 return Err(Error::decoding(
453 offset,
454 format!("expected application tag, got context tag {}", tag.number),
455 ));
456 }
457 if tag.is_opening || tag.is_closing {
458 return Err(Error::decoding(offset, "unexpected opening/closing tag"));
459 }
460
461 let content_start = new_offset;
462 let content_len = tag.length as usize;
463 let content_end = content_start
464 .checked_add(content_len)
465 .ok_or_else(|| Error::decoding(content_start, "length overflow"))?;
466
467 if tag.number == app_tag::BOOLEAN {
468 return Ok((PropertyValue::Boolean(tag.length != 0), content_start));
469 }
470
471 if data.len() < content_end {
472 return Err(Error::buffer_too_short(content_end, data.len()));
473 }
474
475 let content = &data[content_start..content_end];
476
477 let value = match tag.number {
478 app_tag::NULL => PropertyValue::Null,
479 app_tag::UNSIGNED => PropertyValue::Unsigned(decode_unsigned(content)?),
480 app_tag::SIGNED => PropertyValue::Signed(decode_signed(content)?),
481 app_tag::REAL => PropertyValue::Real(decode_real(content)?),
482 app_tag::DOUBLE => PropertyValue::Double(decode_double(content)?),
483 app_tag::OCTET_STRING => PropertyValue::OctetString(content.to_vec()),
484 app_tag::CHARACTER_STRING => {
485 PropertyValue::CharacterString(decode_character_string(content)?)
486 }
487 app_tag::BIT_STRING => {
488 let (unused, bits) = decode_bit_string(content)?;
489 PropertyValue::BitString {
490 unused_bits: unused,
491 data: bits,
492 }
493 }
494 app_tag::ENUMERATED => PropertyValue::Enumerated(decode_unsigned(content)? as u32),
495 app_tag::DATE => PropertyValue::Date(Date::decode(content)?),
496 app_tag::TIME => PropertyValue::Time(Time::decode(content)?),
497 app_tag::OBJECT_IDENTIFIER => {
498 PropertyValue::ObjectIdentifier(ObjectIdentifier::decode(content)?)
499 }
500 other => {
501 return Err(Error::decoding(
502 offset,
503 format!("unknown application tag number {other}"),
504 ));
505 }
506 };
507
508 Ok((value, content_end))
509}
510
511pub fn encode_property_value(buf: &mut BytesMut, value: &PropertyValue) -> Result<(), Error> {
513 match value {
514 PropertyValue::Null => encode_app_null(buf),
515 PropertyValue::Boolean(v) => encode_app_boolean(buf, *v),
516 PropertyValue::Unsigned(v) => encode_app_unsigned(buf, *v),
517 PropertyValue::Signed(v) => encode_app_signed(buf, *v),
518 PropertyValue::Real(v) => encode_app_real(buf, *v),
519 PropertyValue::Double(v) => encode_app_double(buf, *v),
520 PropertyValue::OctetString(v) => encode_app_octet_string(buf, v),
521 PropertyValue::CharacterString(v) => encode_app_character_string(buf, v)?,
522 PropertyValue::BitString { unused_bits, data } => {
523 encode_app_bit_string(buf, *unused_bits, data)
524 }
525 PropertyValue::Enumerated(v) => encode_app_enumerated(buf, *v),
526 PropertyValue::Date(v) => encode_app_date(buf, v),
527 PropertyValue::Time(v) => encode_app_time(buf, v),
528 PropertyValue::ObjectIdentifier(v) => encode_app_object_id(buf, v),
529 PropertyValue::List(values) => {
530 for v in values {
531 encode_property_value(buf, v)?;
532 }
533 }
534 }
535 Ok(())
536}
537
538pub fn encode_timestamp(buf: &mut BytesMut, tag_number: u8, ts: &BACnetTimeStamp) {
548 tags::encode_opening_tag(buf, tag_number);
549 match ts {
550 BACnetTimeStamp::Time(t) => {
551 tags::encode_tag(buf, 0, TagClass::Context, 4);
552 buf.put_slice(&t.encode());
553 }
554 BACnetTimeStamp::SequenceNumber(n) => {
555 encode_ctx_unsigned(buf, 1, *n);
556 }
557 BACnetTimeStamp::DateTime { date, time } => {
558 tags::encode_opening_tag(buf, 2);
559 encode_app_date(buf, date);
560 encode_app_time(buf, time);
561 tags::encode_closing_tag(buf, 2);
562 }
563 }
564 tags::encode_closing_tag(buf, tag_number);
565}
566
567pub fn decode_timestamp(
572 data: &[u8],
573 offset: usize,
574 tag_number: u8,
575) -> Result<(BACnetTimeStamp, usize), Error> {
576 let (tag, pos) = tags::decode_tag(data, offset)?;
577 if !tag.is_opening_tag(tag_number) {
578 return Err(Error::decoding(
579 offset,
580 format!("expected opening tag {tag_number} for BACnetTimeStamp"),
581 ));
582 }
583
584 let (inner_tag, inner_pos) = tags::decode_tag(data, pos)?;
585
586 let (ts, after_inner) = if inner_tag.is_context(0) {
587 let end = inner_pos
588 .checked_add(inner_tag.length as usize)
589 .ok_or_else(|| Error::decoding(inner_pos, "BACnetTimeStamp Time length overflow"))?;
590 if end > data.len() {
591 return Err(Error::decoding(inner_pos, "BACnetTimeStamp Time truncated"));
592 }
593 let t = Time::decode(&data[inner_pos..end])?;
594 (BACnetTimeStamp::Time(t), end)
595 } else if inner_tag.is_context(1) {
596 let end = inner_pos
597 .checked_add(inner_tag.length as usize)
598 .ok_or_else(|| {
599 Error::decoding(inner_pos, "BACnetTimeStamp SequenceNumber length overflow")
600 })?;
601 if end > data.len() {
602 return Err(Error::decoding(
603 inner_pos,
604 "BACnetTimeStamp SequenceNumber truncated",
605 ));
606 }
607 let n = decode_unsigned(&data[inner_pos..end])?;
608 (BACnetTimeStamp::SequenceNumber(n), end)
609 } else if inner_tag.is_opening_tag(2) {
610 let (date_tag, date_pos) = tags::decode_tag(data, inner_pos)?;
611 if date_tag.class != TagClass::Application || date_tag.number != app_tag::DATE {
612 return Err(Error::decoding(
613 inner_pos,
614 "BACnetTimeStamp DateTime expected Date",
615 ));
616 }
617 let date_end = date_pos
618 .checked_add(date_tag.length as usize)
619 .ok_or_else(|| {
620 Error::decoding(date_pos, "BACnetTimeStamp DateTime Date length overflow")
621 })?;
622 if date_end > data.len() {
623 return Err(Error::decoding(
624 date_pos,
625 "BACnetTimeStamp DateTime Date truncated",
626 ));
627 }
628 let date = Date::decode(&data[date_pos..date_end])?;
629
630 let (time_tag, time_pos) = tags::decode_tag(data, date_end)?;
631 if time_tag.class != TagClass::Application || time_tag.number != app_tag::TIME {
632 return Err(Error::decoding(
633 date_end,
634 "BACnetTimeStamp DateTime expected Time",
635 ));
636 }
637 let time_end = time_pos
638 .checked_add(time_tag.length as usize)
639 .ok_or_else(|| {
640 Error::decoding(time_pos, "BACnetTimeStamp DateTime Time length overflow")
641 })?;
642 if time_end > data.len() {
643 return Err(Error::decoding(
644 time_pos,
645 "BACnetTimeStamp DateTime Time truncated",
646 ));
647 }
648 let time = Time::decode(&data[time_pos..time_end])?;
649
650 let (close_tag, close_pos) = tags::decode_tag(data, time_end)?;
651 if !close_tag.is_closing_tag(2) {
652 return Err(Error::decoding(
653 time_end,
654 "BACnetTimeStamp DateTime missing closing tag 2",
655 ));
656 }
657 (BACnetTimeStamp::DateTime { date, time }, close_pos)
658 } else {
659 return Err(Error::decoding(
660 pos,
661 "BACnetTimeStamp: unexpected inner choice tag",
662 ));
663 };
664
665 let (close, final_pos) = tags::decode_tag(data, after_inner)?;
666 if !close.is_closing_tag(tag_number) {
667 return Err(Error::decoding(
668 after_inner,
669 format!("expected closing tag {tag_number} for BACnetTimeStamp"),
670 ));
671 }
672
673 Ok((ts, final_pos))
674}
675
676#[cfg(test)]
681mod tests {
682 use super::*;
683 use bacnet_types::enums::ObjectType;
684
685 fn encode_to_vec<F: FnOnce(&mut BytesMut)>(f: F) -> Vec<u8> {
686 let mut buf = BytesMut::new();
687 f(&mut buf);
688 buf.to_vec()
689 }
690
691 #[test]
694 fn unsigned_encode_decode_1byte() {
695 let mut buf = BytesMut::new();
696 encode_unsigned(&mut buf, 42);
697 assert_eq!(&buf[..], &[42]);
698 assert_eq!(decode_unsigned(&buf).unwrap(), 42);
699 }
700
701 #[test]
702 fn unsigned_encode_decode_2bytes() {
703 let mut buf = BytesMut::new();
704 encode_unsigned(&mut buf, 0x1234);
705 assert_eq!(&buf[..], &[0x12, 0x34]);
706 assert_eq!(decode_unsigned(&buf).unwrap(), 0x1234);
707 }
708
709 #[test]
710 fn unsigned_encode_decode_3bytes() {
711 let mut buf = BytesMut::new();
712 encode_unsigned(&mut buf, 0x12_3456);
713 assert_eq!(&buf[..], &[0x12, 0x34, 0x56]);
714 assert_eq!(decode_unsigned(&buf).unwrap(), 0x12_3456);
715 }
716
717 #[test]
718 fn unsigned_encode_decode_4bytes() {
719 let mut buf = BytesMut::new();
720 encode_unsigned(&mut buf, 0xDEAD_BEEF);
721 assert_eq!(&buf[..], &[0xDE, 0xAD, 0xBE, 0xEF]);
722 assert_eq!(decode_unsigned(&buf).unwrap(), 0xDEAD_BEEF);
723 }
724
725 #[test]
726 fn unsigned_zero() {
727 let mut buf = BytesMut::new();
728 encode_unsigned(&mut buf, 0);
729 assert_eq!(&buf[..], &[0]);
730 assert_eq!(decode_unsigned(&buf).unwrap(), 0);
731 }
732
733 #[test]
734 fn unsigned_max_u64() {
735 let mut buf = BytesMut::new();
736 encode_unsigned(&mut buf, u64::MAX);
737 assert_eq!(buf.len(), 8);
738 assert_eq!(decode_unsigned(&buf).unwrap(), u64::MAX);
739 }
740
741 #[test]
744 fn signed_encode_decode_positive() {
745 let mut buf = BytesMut::new();
746 encode_signed(&mut buf, 42);
747 assert_eq!(&buf[..], &[42]);
748 assert_eq!(decode_signed(&buf).unwrap(), 42);
749 }
750
751 #[test]
752 fn signed_encode_decode_negative() {
753 let mut buf = BytesMut::new();
754 encode_signed(&mut buf, -1);
755 assert_eq!(&buf[..], &[0xFF]);
756 assert_eq!(decode_signed(&buf).unwrap(), -1);
757 }
758
759 #[test]
760 fn signed_encode_decode_neg128() {
761 let mut buf = BytesMut::new();
762 encode_signed(&mut buf, -128);
763 assert_eq!(&buf[..], &[0x80]);
764 assert_eq!(decode_signed(&buf).unwrap(), -128);
765 }
766
767 #[test]
768 fn signed_encode_decode_neg129() {
769 let mut buf = BytesMut::new();
770 encode_signed(&mut buf, -129);
771 assert_eq!(&buf[..], &[0xFF, 0x7F]);
772 assert_eq!(decode_signed(&buf).unwrap(), -129);
773 }
774
775 #[test]
776 fn signed_encode_decode_min() {
777 let mut buf = BytesMut::new();
778 encode_signed(&mut buf, i32::MIN);
779 assert_eq!(buf.len(), 4);
780 assert_eq!(decode_signed(&buf).unwrap(), i32::MIN);
781 }
782
783 #[test]
784 fn signed_encode_decode_max() {
785 let mut buf = BytesMut::new();
786 encode_signed(&mut buf, i32::MAX);
787 assert_eq!(buf.len(), 4);
788 assert_eq!(decode_signed(&buf).unwrap(), i32::MAX);
789 }
790
791 #[test]
794 fn real_round_trip() {
795 let mut buf = BytesMut::new();
796 encode_real(&mut buf, 72.5);
797 assert_eq!(decode_real(&buf).unwrap(), 72.5);
798 }
799
800 #[test]
801 fn double_round_trip() {
802 let mut buf = BytesMut::new();
803 encode_double(&mut buf, core::f64::consts::PI);
804 assert_eq!(decode_double(&buf).unwrap(), core::f64::consts::PI);
805 }
806
807 #[test]
810 fn character_string_round_trip() {
811 let mut buf = BytesMut::new();
812 encode_character_string(&mut buf, "hello");
813 let decoded = decode_character_string(&buf).unwrap();
814 assert_eq!(decoded, "hello");
815 }
816
817 #[test]
818 fn character_string_empty() {
819 let mut buf = BytesMut::new();
820 encode_character_string(&mut buf, "");
821 let decoded = decode_character_string(&buf).unwrap();
822 assert_eq!(decoded, "");
823 }
824
825 #[test]
828 fn bit_string_round_trip() {
829 let mut buf = BytesMut::new();
830 encode_bit_string(&mut buf, 3, &[0b1010_0000]);
831 let (unused, data) = decode_bit_string(&buf).unwrap();
832 assert_eq!(unused, 3);
833 assert_eq!(data, vec![0b1010_0000]);
834 }
835
836 #[test]
837 fn bit_string_invalid_unused() {
838 assert!(decode_bit_string(&[8]).is_err());
839 }
840
841 #[test]
844 fn app_null_round_trip() {
845 let bytes = encode_to_vec(encode_app_null);
846 let (val, offset) = decode_application_value(&bytes, 0).unwrap();
847 assert_eq!(val, PropertyValue::Null);
848 assert_eq!(offset, bytes.len());
849 }
850
851 #[test]
852 fn app_boolean_true_round_trip() {
853 let bytes = encode_to_vec(|buf| encode_app_boolean(buf, true));
854 let (val, _) = decode_application_value(&bytes, 0).unwrap();
855 assert_eq!(val, PropertyValue::Boolean(true));
856 }
857
858 #[test]
859 fn app_boolean_false_round_trip() {
860 let bytes = encode_to_vec(|buf| encode_app_boolean(buf, false));
861 let (val, _) = decode_application_value(&bytes, 0).unwrap();
862 assert_eq!(val, PropertyValue::Boolean(false));
863 }
864
865 #[test]
866 fn app_unsigned_round_trip() {
867 for &v in &[0u64, 1, 255, 256, 65535, 65536, 0xFFFF_FFFF, u64::MAX] {
868 let bytes = encode_to_vec(|buf| encode_app_unsigned(buf, v));
869 let (val, end) = decode_application_value(&bytes, 0).unwrap();
870 assert_eq!(val, PropertyValue::Unsigned(v), "failed for {v}");
871 assert_eq!(end, bytes.len());
872 }
873 }
874
875 #[test]
876 fn app_signed_round_trip() {
877 for &v in &[0i32, 1, -1, 127, -128, 128, -129, i32::MIN, i32::MAX] {
878 let bytes = encode_to_vec(|buf| encode_app_signed(buf, v));
879 let (val, end) = decode_application_value(&bytes, 0).unwrap();
880 assert_eq!(val, PropertyValue::Signed(v), "failed for {v}");
881 assert_eq!(end, bytes.len());
882 }
883 }
884
885 #[test]
886 fn app_real_round_trip() {
887 let bytes = encode_to_vec(|buf| encode_app_real(buf, 72.5));
888 let (val, end) = decode_application_value(&bytes, 0).unwrap();
889 assert_eq!(val, PropertyValue::Real(72.5));
890 assert_eq!(end, bytes.len());
891 }
892
893 #[test]
894 fn app_double_round_trip() {
895 let bytes = encode_to_vec(|buf| encode_app_double(buf, core::f64::consts::PI));
896 let (val, end) = decode_application_value(&bytes, 0).unwrap();
897 assert_eq!(val, PropertyValue::Double(core::f64::consts::PI));
898 assert_eq!(end, bytes.len());
899 }
900
901 #[test]
902 fn app_octet_string_round_trip() {
903 let data = vec![0xDE, 0xAD, 0xBE, 0xEF];
904 let bytes = encode_to_vec(|buf| encode_app_octet_string(buf, &data));
905 let (val, end) = decode_application_value(&bytes, 0).unwrap();
906 assert_eq!(val, PropertyValue::OctetString(data));
907 assert_eq!(end, bytes.len());
908 }
909
910 #[test]
911 fn app_character_string_round_trip() {
912 let bytes = encode_to_vec(|buf| encode_app_character_string(buf, "BACnet").unwrap());
913 let (val, end) = decode_application_value(&bytes, 0).unwrap();
914 assert_eq!(val, PropertyValue::CharacterString("BACnet".into()));
915 assert_eq!(end, bytes.len());
916 }
917
918 #[test]
919 fn app_enumerated_round_trip() {
920 let bytes = encode_to_vec(|buf| encode_app_enumerated(buf, 8));
921 let (val, end) = decode_application_value(&bytes, 0).unwrap();
922 assert_eq!(val, PropertyValue::Enumerated(8));
923 assert_eq!(end, bytes.len());
924 }
925
926 #[test]
927 fn app_date_round_trip() {
928 let date = Date {
929 year: 124,
930 month: 6,
931 day: 15,
932 day_of_week: 6,
933 };
934 let bytes = encode_to_vec(|buf| encode_app_date(buf, &date));
935 let (val, end) = decode_application_value(&bytes, 0).unwrap();
936 assert_eq!(val, PropertyValue::Date(date));
937 assert_eq!(end, bytes.len());
938 }
939
940 #[test]
941 fn app_time_round_trip() {
942 let time = Time {
943 hour: 14,
944 minute: 30,
945 second: 0,
946 hundredths: 0,
947 };
948 let bytes = encode_to_vec(|buf| encode_app_time(buf, &time));
949 let (val, end) = decode_application_value(&bytes, 0).unwrap();
950 assert_eq!(val, PropertyValue::Time(time));
951 assert_eq!(end, bytes.len());
952 }
953
954 #[test]
955 fn app_object_id_round_trip() {
956 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 1234).unwrap();
957 let bytes = encode_to_vec(|buf| encode_app_object_id(buf, &oid));
958 let (val, end) = decode_application_value(&bytes, 0).unwrap();
959 assert_eq!(val, PropertyValue::ObjectIdentifier(oid));
960 assert_eq!(end, bytes.len());
961 }
962
963 #[test]
964 fn app_bit_string_round_trip() {
965 let bytes = encode_to_vec(|buf| encode_app_bit_string(buf, 4, &[0xF0]));
966 let (val, end) = decode_application_value(&bytes, 0).unwrap();
967 assert_eq!(
968 val,
969 PropertyValue::BitString {
970 unused_bits: 4,
971 data: vec![0xF0],
972 }
973 );
974 assert_eq!(end, bytes.len());
975 }
976
977 #[test]
980 fn property_value_encode_decode_all_types() {
981 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
982 let values = vec![
983 PropertyValue::Null,
984 PropertyValue::Boolean(true),
985 PropertyValue::Boolean(false),
986 PropertyValue::Unsigned(12345),
987 PropertyValue::Signed(-42),
988 PropertyValue::Real(72.5),
989 PropertyValue::Double(3.125),
990 PropertyValue::OctetString(vec![1, 2, 3]),
991 PropertyValue::CharacterString("test".into()),
992 PropertyValue::BitString {
993 unused_bits: 0,
994 data: vec![0xFF],
995 },
996 PropertyValue::Enumerated(8),
997 PropertyValue::Date(Date {
998 year: 124,
999 month: 1,
1000 day: 1,
1001 day_of_week: 1,
1002 }),
1003 PropertyValue::Time(Time {
1004 hour: 12,
1005 minute: 0,
1006 second: 0,
1007 hundredths: 0,
1008 }),
1009 PropertyValue::ObjectIdentifier(oid),
1010 ];
1011
1012 for original in &values {
1013 let bytes = encode_to_vec(|buf| encode_property_value(buf, original).unwrap());
1014 let (decoded, end) = decode_application_value(&bytes, 0).unwrap();
1015 assert_eq!(&decoded, original, "round-trip failed for {original:?}");
1016 assert_eq!(end, bytes.len(), "offset mismatch for {original:?}");
1017 }
1018 }
1019
1020 #[test]
1023 fn ctx_unsigned_encoding() {
1024 let bytes = encode_to_vec(|buf| encode_ctx_unsigned(buf, 1, 42));
1025 assert_eq!(bytes, vec![0x19, 42]);
1027 }
1028
1029 #[test]
1030 fn ctx_boolean_encoding() {
1031 let bytes = encode_to_vec(|buf| encode_ctx_boolean(buf, 0, true));
1032 assert_eq!(bytes, vec![0x09, 0x01]);
1034 }
1035
1036 #[test]
1037 fn ctx_object_id_encoding() {
1038 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 1234).unwrap();
1039 let bytes = encode_to_vec(|buf| encode_ctx_object_id(buf, 2, &oid));
1040 let mut expected = vec![0x2C];
1042 expected.extend_from_slice(&oid.encode());
1043 assert_eq!(bytes, expected);
1044 }
1045
1046 #[test]
1049 fn timestamp_sequence_number_round_trip() {
1050 let ts = BACnetTimeStamp::SequenceNumber(42);
1051 let mut buf = BytesMut::new();
1052 encode_timestamp(&mut buf, 3, &ts);
1053 let (decoded, end) = decode_timestamp(&buf, 0, 3).unwrap();
1054 assert_eq!(decoded, ts);
1055 assert_eq!(end, buf.len());
1056 }
1057
1058 #[test]
1059 fn timestamp_time_round_trip() {
1060 let ts = BACnetTimeStamp::Time(Time {
1061 hour: 14,
1062 minute: 30,
1063 second: 45,
1064 hundredths: 50,
1065 });
1066 let mut buf = BytesMut::new();
1067 encode_timestamp(&mut buf, 3, &ts);
1068 let (decoded, end) = decode_timestamp(&buf, 0, 3).unwrap();
1069 assert_eq!(decoded, ts);
1070 assert_eq!(end, buf.len());
1071 }
1072
1073 #[test]
1074 fn timestamp_datetime_round_trip() {
1075 let ts = BACnetTimeStamp::DateTime {
1076 date: Date {
1077 year: 126,
1078 month: 2,
1079 day: 28,
1080 day_of_week: 6,
1081 },
1082 time: Time {
1083 hour: 10,
1084 minute: 15,
1085 second: 0,
1086 hundredths: 0,
1087 },
1088 };
1089 let mut buf = BytesMut::new();
1090 encode_timestamp(&mut buf, 5, &ts);
1091 let (decoded, end) = decode_timestamp(&buf, 0, 5).unwrap();
1092 assert_eq!(decoded, ts);
1093 assert_eq!(end, buf.len());
1094 }
1095
1096 #[test]
1097 fn ucs2_decode_ascii() {
1098 let data = [charset::UCS2, 0x00, 0x41, 0x00, 0x42];
1100 let result = decode_character_string(&data).unwrap();
1101 assert_eq!(result, "AB");
1102 }
1103
1104 #[test]
1105 fn ucs2_decode_non_ascii() {
1106 let data = [charset::UCS2, 0x00, 0xE9];
1108 let result = decode_character_string(&data).unwrap();
1109 assert_eq!(result, "é");
1110 }
1111
1112 #[test]
1113 fn unsupported_charset_errors() {
1114 for &cs in &[
1115 charset::IBM_MICROSOFT_DBCS,
1116 charset::JIS_X_0208,
1117 charset::UCS4,
1118 ] {
1119 let data = [cs, 0x41, 0x42];
1120 let result = decode_character_string(&data);
1121 assert!(result.is_err(), "charset {cs} should return an error");
1122 }
1123 }
1124}