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;
154 pub const JIS_X0201: u8 = 1;
155 pub const JIS_C6226: u8 = 2;
156 pub const UCS4: u8 = 3;
157 pub const UCS2: u8 = 4;
158 pub const ISO_8859_1: u8 = 5;
159}
160
161pub fn encode_character_string(buf: &mut BytesMut, value: &str) {
163 buf.put_u8(charset::UTF8);
164 buf.put_slice(value.as_bytes());
165}
166
167pub fn character_string_len(value: &str) -> Result<u32, Error> {
169 u32::try_from(value.len())
170 .ok()
171 .and_then(|n| n.checked_add(1))
172 .ok_or_else(|| Error::Encoding("CharacterString too long for BACnet encoding".into()))
173}
174
175pub fn decode_character_string(data: &[u8]) -> Result<String, Error> {
185 if data.is_empty() {
186 return Err(Error::Decoding {
187 offset: 0,
188 message: "CharacterString requires at least 1 byte for charset".into(),
189 });
190 }
191 let charset_id = data[0];
192 let payload = &data[1..];
193 match charset_id {
194 charset::UTF8 => String::from_utf8(payload.to_vec()).map_err(|e| Error::Decoding {
195 offset: 1,
196 message: format!("invalid UTF-8: {e}"),
197 }),
198 charset::UCS2 => {
199 if !payload.len().is_multiple_of(2) {
201 return Err(Error::Decoding {
202 offset: 1,
203 message: "UCS-2 data must have even length".into(),
204 });
205 }
206 let mut s = String::new();
207 for (i, chunk) in payload.chunks_exact(2).enumerate() {
208 let code_point = u16::from_be_bytes([chunk[0], chunk[1]]);
209 if let Some(c) = char::from_u32(code_point as u32) {
210 s.push(c);
211 } else {
212 return Err(Error::Decoding {
213 offset: 1 + i * 2,
214 message: "invalid UCS-2 code point".into(),
215 });
216 }
217 }
218 Ok(s)
219 }
220 charset::ISO_8859_1 => {
221 Ok(payload.iter().map(|&b| b as char).collect())
223 }
224 charset::JIS_X0201 | charset::JIS_C6226 | charset::UCS4 => Err(Error::Decoding {
225 offset: 0,
226 message: format!("unsupported charset: {charset_id}"),
227 }),
228 other => Err(Error::Decoding {
229 offset: 0,
230 message: format!("unknown charset: {other}"),
231 }),
232 }
233}
234
235pub fn encode_bit_string(buf: &mut BytesMut, unused_bits: u8, data: &[u8]) {
239 buf.put_u8(unused_bits);
240 buf.put_slice(data);
241}
242
243pub fn decode_bit_string(data: &[u8]) -> Result<(u8, Vec<u8>), Error> {
247 if data.is_empty() {
248 return Err(Error::Decoding {
249 offset: 0,
250 message: "BitString requires at least 1 byte for unused-bits count".into(),
251 });
252 }
253 let unused = data[0];
254 if unused > 7 {
255 return Err(Error::Decoding {
256 offset: 0,
257 message: format!("BitString unused_bits must be 0-7, got {unused}"),
258 });
259 }
260 Ok((unused, data[1..].to_vec()))
261}
262
263pub fn encode_app_null(buf: &mut BytesMut) {
269 tags::encode_tag(buf, app_tag::NULL, TagClass::Application, 0);
270}
271
272pub 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 tags::encode_tag(
314 buf,
315 app_tag::OCTET_STRING,
316 TagClass::Application,
317 data.len() as u32,
318 );
319 buf.put_slice(data);
320}
321
322pub fn encode_app_character_string(buf: &mut BytesMut, value: &str) -> Result<(), Error> {
324 let len = character_string_len(value)?;
325 tags::encode_tag(buf, app_tag::CHARACTER_STRING, TagClass::Application, len);
326 encode_character_string(buf, value);
327 Ok(())
328}
329
330pub fn encode_app_bit_string(buf: &mut BytesMut, unused_bits: u8, data: &[u8]) {
332 let len = 1 + data.len() as u32;
333 tags::encode_tag(buf, app_tag::BIT_STRING, TagClass::Application, len);
334 encode_bit_string(buf, unused_bits, data);
335}
336
337pub fn encode_app_enumerated(buf: &mut BytesMut, value: u32) {
339 let len = unsigned_len(value as u64);
340 tags::encode_tag(buf, app_tag::ENUMERATED, TagClass::Application, len);
341 encode_unsigned(buf, value as u64);
342}
343
344pub fn encode_app_date(buf: &mut BytesMut, date: &Date) {
346 tags::encode_tag(buf, app_tag::DATE, TagClass::Application, 4);
347 buf.put_slice(&date.encode());
348}
349
350pub fn encode_app_time(buf: &mut BytesMut, time: &Time) {
352 tags::encode_tag(buf, app_tag::TIME, TagClass::Application, 4);
353 buf.put_slice(&time.encode());
354}
355
356pub fn encode_app_object_id(buf: &mut BytesMut, oid: &ObjectIdentifier) {
358 tags::encode_tag(buf, app_tag::OBJECT_IDENTIFIER, TagClass::Application, 4);
359 buf.put_slice(&oid.encode());
360}
361
362pub fn encode_ctx_unsigned(buf: &mut BytesMut, tag: u8, value: u64) {
368 let len = unsigned_len(value);
369 tags::encode_tag(buf, tag, TagClass::Context, len);
370 encode_unsigned(buf, value);
371}
372
373pub fn encode_ctx_signed(buf: &mut BytesMut, tag: u8, value: i32) {
375 let len = signed_len(value);
376 tags::encode_tag(buf, tag, TagClass::Context, len);
377 encode_signed(buf, value);
378}
379
380pub fn encode_ctx_real(buf: &mut BytesMut, tag: u8, value: f32) {
382 tags::encode_tag(buf, tag, TagClass::Context, 4);
383 encode_real(buf, value);
384}
385
386pub fn encode_ctx_double(buf: &mut BytesMut, tag: u8, value: f64) {
388 tags::encode_tag(buf, tag, TagClass::Context, 8);
389 encode_double(buf, value);
390}
391
392pub fn encode_ctx_enumerated(buf: &mut BytesMut, tag: u8, value: u32) {
394 let len = unsigned_len(value as u64);
395 tags::encode_tag(buf, tag, TagClass::Context, len);
396 encode_unsigned(buf, value as u64);
397}
398
399pub fn encode_ctx_boolean(buf: &mut BytesMut, tag: u8, value: bool) {
403 tags::encode_tag(buf, tag, TagClass::Context, 1);
404 buf.put_u8(if value { 1 } else { 0 });
405}
406
407pub fn encode_ctx_object_id(buf: &mut BytesMut, tag: u8, oid: &ObjectIdentifier) {
409 tags::encode_tag(buf, tag, TagClass::Context, 4);
410 buf.put_slice(&oid.encode());
411}
412
413pub fn encode_ctx_octet_string(buf: &mut BytesMut, tag: u8, data: &[u8]) {
415 tags::encode_tag(buf, tag, TagClass::Context, data.len() as u32);
416 buf.put_slice(data);
417}
418
419pub fn encode_ctx_character_string(buf: &mut BytesMut, tag: u8, value: &str) -> Result<(), Error> {
421 let len = character_string_len(value)?;
422 tags::encode_tag(buf, tag, TagClass::Context, len);
423 encode_character_string(buf, value);
424 Ok(())
425}
426
427pub fn encode_ctx_date(buf: &mut BytesMut, tag: u8, date: &Date) {
429 tags::encode_tag(buf, tag, TagClass::Context, 4);
430 buf.put_slice(&date.encode());
431}
432
433pub fn encode_ctx_bit_string(buf: &mut BytesMut, tag: u8, unused_bits: u8, data: &[u8]) {
435 let len = 1 + data.len() as u32;
436 tags::encode_tag(buf, tag, TagClass::Context, len);
437 encode_bit_string(buf, unused_bits, data);
438}
439
440pub fn decode_application_value(
448 data: &[u8],
449 offset: usize,
450) -> Result<(PropertyValue, usize), Error> {
451 let (tag, new_offset) = tags::decode_tag(data, offset)?;
452 if tag.class != TagClass::Application {
453 return Err(Error::decoding(
454 offset,
455 format!("expected application tag, got context tag {}", tag.number),
456 ));
457 }
458 if tag.is_opening || tag.is_closing {
459 return Err(Error::decoding(offset, "unexpected opening/closing tag"));
460 }
461
462 let content_start = new_offset;
463 let content_len = tag.length as usize;
464 let content_end = content_start
465 .checked_add(content_len)
466 .ok_or_else(|| Error::decoding(content_start, "length overflow"))?;
467
468 if tag.number == app_tag::BOOLEAN {
470 return Ok((PropertyValue::Boolean(tag.length != 0), content_start));
471 }
472
473 if data.len() < content_end {
474 return Err(Error::buffer_too_short(content_end, data.len()));
475 }
476
477 let content = &data[content_start..content_end];
478
479 let value = match tag.number {
480 app_tag::NULL => PropertyValue::Null,
481 app_tag::UNSIGNED => PropertyValue::Unsigned(decode_unsigned(content)?),
482 app_tag::SIGNED => PropertyValue::Signed(decode_signed(content)?),
483 app_tag::REAL => PropertyValue::Real(decode_real(content)?),
484 app_tag::DOUBLE => PropertyValue::Double(decode_double(content)?),
485 app_tag::OCTET_STRING => PropertyValue::OctetString(content.to_vec()),
486 app_tag::CHARACTER_STRING => {
487 PropertyValue::CharacterString(decode_character_string(content)?)
488 }
489 app_tag::BIT_STRING => {
490 let (unused, bits) = decode_bit_string(content)?;
491 PropertyValue::BitString {
492 unused_bits: unused,
493 data: bits,
494 }
495 }
496 app_tag::ENUMERATED => PropertyValue::Enumerated(decode_unsigned(content)? as u32),
497 app_tag::DATE => PropertyValue::Date(Date::decode(content)?),
498 app_tag::TIME => PropertyValue::Time(Time::decode(content)?),
499 app_tag::OBJECT_IDENTIFIER => {
500 PropertyValue::ObjectIdentifier(ObjectIdentifier::decode(content)?)
501 }
502 other => {
503 return Err(Error::decoding(
504 offset,
505 format!("unknown application tag number {other}"),
506 ));
507 }
508 };
509
510 Ok((value, content_end))
511}
512
513pub fn encode_property_value(buf: &mut BytesMut, value: &PropertyValue) -> Result<(), Error> {
515 match value {
516 PropertyValue::Null => encode_app_null(buf),
517 PropertyValue::Boolean(v) => encode_app_boolean(buf, *v),
518 PropertyValue::Unsigned(v) => encode_app_unsigned(buf, *v),
519 PropertyValue::Signed(v) => encode_app_signed(buf, *v),
520 PropertyValue::Real(v) => encode_app_real(buf, *v),
521 PropertyValue::Double(v) => encode_app_double(buf, *v),
522 PropertyValue::OctetString(v) => encode_app_octet_string(buf, v),
523 PropertyValue::CharacterString(v) => encode_app_character_string(buf, v)?,
524 PropertyValue::BitString { unused_bits, data } => {
525 encode_app_bit_string(buf, *unused_bits, data)
526 }
527 PropertyValue::Enumerated(v) => encode_app_enumerated(buf, *v),
528 PropertyValue::Date(v) => encode_app_date(buf, v),
529 PropertyValue::Time(v) => encode_app_time(buf, v),
530 PropertyValue::ObjectIdentifier(v) => encode_app_object_id(buf, v),
531 PropertyValue::List(values) => {
532 for v in values {
533 encode_property_value(buf, v)?;
534 }
535 }
536 }
537 Ok(())
538}
539
540pub fn encode_timestamp(buf: &mut BytesMut, tag_number: u8, ts: &BACnetTimeStamp) {
551 tags::encode_opening_tag(buf, tag_number);
552 match ts {
553 BACnetTimeStamp::Time(t) => {
554 tags::encode_tag(buf, 0, TagClass::Context, 4);
555 buf.put_slice(&t.encode());
556 }
557 BACnetTimeStamp::SequenceNumber(n) => {
558 encode_ctx_unsigned(buf, 1, *n);
559 }
560 BACnetTimeStamp::DateTime { date, time } => {
561 tags::encode_opening_tag(buf, 2);
562 encode_app_date(buf, date);
563 encode_app_time(buf, time);
564 tags::encode_closing_tag(buf, 2);
565 }
566 }
567 tags::encode_closing_tag(buf, tag_number);
568}
569
570pub fn decode_timestamp(
575 data: &[u8],
576 offset: usize,
577 tag_number: u8,
578) -> Result<(BACnetTimeStamp, usize), Error> {
579 let (tag, pos) = tags::decode_tag(data, offset)?;
581 if !tag.is_opening_tag(tag_number) {
582 return Err(Error::decoding(
583 offset,
584 format!("expected opening tag {tag_number} for BACnetTimeStamp"),
585 ));
586 }
587
588 let (inner_tag, inner_pos) = tags::decode_tag(data, pos)?;
590
591 let (ts, after_inner) = if inner_tag.is_context(0) {
592 let end = inner_pos + inner_tag.length as usize;
594 if end > data.len() {
595 return Err(Error::decoding(inner_pos, "BACnetTimeStamp Time truncated"));
596 }
597 let t = Time::decode(&data[inner_pos..end])?;
598 (BACnetTimeStamp::Time(t), end)
599 } else if inner_tag.is_context(1) {
600 let end = inner_pos + inner_tag.length as usize;
602 if end > data.len() {
603 return Err(Error::decoding(
604 inner_pos,
605 "BACnetTimeStamp SequenceNumber truncated",
606 ));
607 }
608 let n = decode_unsigned(&data[inner_pos..end])?;
609 (BACnetTimeStamp::SequenceNumber(n), end)
610 } else if inner_tag.is_opening_tag(2) {
611 let (date_tag, date_pos) = tags::decode_tag(data, inner_pos)?;
614 if date_tag.class != TagClass::Application || date_tag.number != app_tag::DATE {
615 return Err(Error::decoding(
616 inner_pos,
617 "BACnetTimeStamp DateTime expected Date",
618 ));
619 }
620 let date_end = date_pos + date_tag.length as usize;
621 if date_end > data.len() {
622 return Err(Error::decoding(
623 date_pos,
624 "BACnetTimeStamp DateTime Date truncated",
625 ));
626 }
627 let date = Date::decode(&data[date_pos..date_end])?;
628
629 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 + time_tag.length as usize;
638 if time_end > data.len() {
639 return Err(Error::decoding(
640 time_pos,
641 "BACnetTimeStamp DateTime Time truncated",
642 ));
643 }
644 let time = Time::decode(&data[time_pos..time_end])?;
645
646 let (close_tag, close_pos) = tags::decode_tag(data, time_end)?;
648 if !close_tag.is_closing_tag(2) {
649 return Err(Error::decoding(
650 time_end,
651 "BACnetTimeStamp DateTime missing closing tag 2",
652 ));
653 }
654 (BACnetTimeStamp::DateTime { date, time }, close_pos)
655 } else {
656 return Err(Error::decoding(
657 pos,
658 "BACnetTimeStamp: unexpected inner choice tag",
659 ));
660 };
661
662 let (close, final_pos) = tags::decode_tag(data, after_inner)?;
664 if !close.is_closing_tag(tag_number) {
665 return Err(Error::decoding(
666 after_inner,
667 format!("expected closing tag {tag_number} for BACnetTimeStamp"),
668 ));
669 }
670
671 Ok((ts, final_pos))
672}
673
674#[cfg(test)]
679mod tests {
680 use super::*;
681 use bacnet_types::enums::ObjectType;
682
683 fn encode_to_vec<F: FnOnce(&mut BytesMut)>(f: F) -> Vec<u8> {
684 let mut buf = BytesMut::new();
685 f(&mut buf);
686 buf.to_vec()
687 }
688
689 #[test]
692 fn unsigned_encode_decode_1byte() {
693 let mut buf = BytesMut::new();
694 encode_unsigned(&mut buf, 42);
695 assert_eq!(&buf[..], &[42]);
696 assert_eq!(decode_unsigned(&buf).unwrap(), 42);
697 }
698
699 #[test]
700 fn unsigned_encode_decode_2bytes() {
701 let mut buf = BytesMut::new();
702 encode_unsigned(&mut buf, 0x1234);
703 assert_eq!(&buf[..], &[0x12, 0x34]);
704 assert_eq!(decode_unsigned(&buf).unwrap(), 0x1234);
705 }
706
707 #[test]
708 fn unsigned_encode_decode_3bytes() {
709 let mut buf = BytesMut::new();
710 encode_unsigned(&mut buf, 0x12_3456);
711 assert_eq!(&buf[..], &[0x12, 0x34, 0x56]);
712 assert_eq!(decode_unsigned(&buf).unwrap(), 0x12_3456);
713 }
714
715 #[test]
716 fn unsigned_encode_decode_4bytes() {
717 let mut buf = BytesMut::new();
718 encode_unsigned(&mut buf, 0xDEAD_BEEF);
719 assert_eq!(&buf[..], &[0xDE, 0xAD, 0xBE, 0xEF]);
720 assert_eq!(decode_unsigned(&buf).unwrap(), 0xDEAD_BEEF);
721 }
722
723 #[test]
724 fn unsigned_zero() {
725 let mut buf = BytesMut::new();
726 encode_unsigned(&mut buf, 0);
727 assert_eq!(&buf[..], &[0]);
728 assert_eq!(decode_unsigned(&buf).unwrap(), 0);
729 }
730
731 #[test]
732 fn unsigned_max_u64() {
733 let mut buf = BytesMut::new();
734 encode_unsigned(&mut buf, u64::MAX);
735 assert_eq!(buf.len(), 8);
736 assert_eq!(decode_unsigned(&buf).unwrap(), u64::MAX);
737 }
738
739 #[test]
742 fn signed_encode_decode_positive() {
743 let mut buf = BytesMut::new();
744 encode_signed(&mut buf, 42);
745 assert_eq!(&buf[..], &[42]);
746 assert_eq!(decode_signed(&buf).unwrap(), 42);
747 }
748
749 #[test]
750 fn signed_encode_decode_negative() {
751 let mut buf = BytesMut::new();
752 encode_signed(&mut buf, -1);
753 assert_eq!(&buf[..], &[0xFF]);
754 assert_eq!(decode_signed(&buf).unwrap(), -1);
755 }
756
757 #[test]
758 fn signed_encode_decode_neg128() {
759 let mut buf = BytesMut::new();
760 encode_signed(&mut buf, -128);
761 assert_eq!(&buf[..], &[0x80]);
762 assert_eq!(decode_signed(&buf).unwrap(), -128);
763 }
764
765 #[test]
766 fn signed_encode_decode_neg129() {
767 let mut buf = BytesMut::new();
768 encode_signed(&mut buf, -129);
769 assert_eq!(&buf[..], &[0xFF, 0x7F]);
770 assert_eq!(decode_signed(&buf).unwrap(), -129);
771 }
772
773 #[test]
774 fn signed_encode_decode_min() {
775 let mut buf = BytesMut::new();
776 encode_signed(&mut buf, i32::MIN);
777 assert_eq!(buf.len(), 4);
778 assert_eq!(decode_signed(&buf).unwrap(), i32::MIN);
779 }
780
781 #[test]
782 fn signed_encode_decode_max() {
783 let mut buf = BytesMut::new();
784 encode_signed(&mut buf, i32::MAX);
785 assert_eq!(buf.len(), 4);
786 assert_eq!(decode_signed(&buf).unwrap(), i32::MAX);
787 }
788
789 #[test]
792 fn real_round_trip() {
793 let mut buf = BytesMut::new();
794 encode_real(&mut buf, 72.5);
795 assert_eq!(decode_real(&buf).unwrap(), 72.5);
796 }
797
798 #[test]
799 fn double_round_trip() {
800 let mut buf = BytesMut::new();
801 encode_double(&mut buf, core::f64::consts::PI);
802 assert_eq!(decode_double(&buf).unwrap(), core::f64::consts::PI);
803 }
804
805 #[test]
808 fn character_string_round_trip() {
809 let mut buf = BytesMut::new();
810 encode_character_string(&mut buf, "hello");
811 let decoded = decode_character_string(&buf).unwrap();
812 assert_eq!(decoded, "hello");
813 }
814
815 #[test]
816 fn character_string_empty() {
817 let mut buf = BytesMut::new();
818 encode_character_string(&mut buf, "");
819 let decoded = decode_character_string(&buf).unwrap();
820 assert_eq!(decoded, "");
821 }
822
823 #[test]
826 fn bit_string_round_trip() {
827 let mut buf = BytesMut::new();
828 encode_bit_string(&mut buf, 3, &[0b1010_0000]);
829 let (unused, data) = decode_bit_string(&buf).unwrap();
830 assert_eq!(unused, 3);
831 assert_eq!(data, vec![0b1010_0000]);
832 }
833
834 #[test]
835 fn bit_string_invalid_unused() {
836 assert!(decode_bit_string(&[8]).is_err());
837 }
838
839 #[test]
842 fn app_null_round_trip() {
843 let bytes = encode_to_vec(encode_app_null);
844 let (val, offset) = decode_application_value(&bytes, 0).unwrap();
845 assert_eq!(val, PropertyValue::Null);
846 assert_eq!(offset, bytes.len());
847 }
848
849 #[test]
850 fn app_boolean_true_round_trip() {
851 let bytes = encode_to_vec(|buf| encode_app_boolean(buf, true));
852 let (val, _) = decode_application_value(&bytes, 0).unwrap();
853 assert_eq!(val, PropertyValue::Boolean(true));
854 }
855
856 #[test]
857 fn app_boolean_false_round_trip() {
858 let bytes = encode_to_vec(|buf| encode_app_boolean(buf, false));
859 let (val, _) = decode_application_value(&bytes, 0).unwrap();
860 assert_eq!(val, PropertyValue::Boolean(false));
861 }
862
863 #[test]
864 fn app_unsigned_round_trip() {
865 for &v in &[0u64, 1, 255, 256, 65535, 65536, 0xFFFF_FFFF, u64::MAX] {
866 let bytes = encode_to_vec(|buf| encode_app_unsigned(buf, v));
867 let (val, end) = decode_application_value(&bytes, 0).unwrap();
868 assert_eq!(val, PropertyValue::Unsigned(v), "failed for {v}");
869 assert_eq!(end, bytes.len());
870 }
871 }
872
873 #[test]
874 fn app_signed_round_trip() {
875 for &v in &[0i32, 1, -1, 127, -128, 128, -129, i32::MIN, i32::MAX] {
876 let bytes = encode_to_vec(|buf| encode_app_signed(buf, v));
877 let (val, end) = decode_application_value(&bytes, 0).unwrap();
878 assert_eq!(val, PropertyValue::Signed(v), "failed for {v}");
879 assert_eq!(end, bytes.len());
880 }
881 }
882
883 #[test]
884 fn app_real_round_trip() {
885 let bytes = encode_to_vec(|buf| encode_app_real(buf, 72.5));
886 let (val, end) = decode_application_value(&bytes, 0).unwrap();
887 assert_eq!(val, PropertyValue::Real(72.5));
888 assert_eq!(end, bytes.len());
889 }
890
891 #[test]
892 fn app_double_round_trip() {
893 let bytes = encode_to_vec(|buf| encode_app_double(buf, core::f64::consts::PI));
894 let (val, end) = decode_application_value(&bytes, 0).unwrap();
895 assert_eq!(val, PropertyValue::Double(core::f64::consts::PI));
896 assert_eq!(end, bytes.len());
897 }
898
899 #[test]
900 fn app_octet_string_round_trip() {
901 let data = vec![0xDE, 0xAD, 0xBE, 0xEF];
902 let bytes = encode_to_vec(|buf| encode_app_octet_string(buf, &data));
903 let (val, end) = decode_application_value(&bytes, 0).unwrap();
904 assert_eq!(val, PropertyValue::OctetString(data));
905 assert_eq!(end, bytes.len());
906 }
907
908 #[test]
909 fn app_character_string_round_trip() {
910 let bytes = encode_to_vec(|buf| encode_app_character_string(buf, "BACnet").unwrap());
911 let (val, end) = decode_application_value(&bytes, 0).unwrap();
912 assert_eq!(val, PropertyValue::CharacterString("BACnet".into()));
913 assert_eq!(end, bytes.len());
914 }
915
916 #[test]
917 fn app_enumerated_round_trip() {
918 let bytes = encode_to_vec(|buf| encode_app_enumerated(buf, 8));
919 let (val, end) = decode_application_value(&bytes, 0).unwrap();
920 assert_eq!(val, PropertyValue::Enumerated(8));
921 assert_eq!(end, bytes.len());
922 }
923
924 #[test]
925 fn app_date_round_trip() {
926 let date = Date {
927 year: 124,
928 month: 6,
929 day: 15,
930 day_of_week: 6,
931 };
932 let bytes = encode_to_vec(|buf| encode_app_date(buf, &date));
933 let (val, end) = decode_application_value(&bytes, 0).unwrap();
934 assert_eq!(val, PropertyValue::Date(date));
935 assert_eq!(end, bytes.len());
936 }
937
938 #[test]
939 fn app_time_round_trip() {
940 let time = Time {
941 hour: 14,
942 minute: 30,
943 second: 0,
944 hundredths: 0,
945 };
946 let bytes = encode_to_vec(|buf| encode_app_time(buf, &time));
947 let (val, end) = decode_application_value(&bytes, 0).unwrap();
948 assert_eq!(val, PropertyValue::Time(time));
949 assert_eq!(end, bytes.len());
950 }
951
952 #[test]
953 fn app_object_id_round_trip() {
954 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 1234).unwrap();
955 let bytes = encode_to_vec(|buf| encode_app_object_id(buf, &oid));
956 let (val, end) = decode_application_value(&bytes, 0).unwrap();
957 assert_eq!(val, PropertyValue::ObjectIdentifier(oid));
958 assert_eq!(end, bytes.len());
959 }
960
961 #[test]
962 fn app_bit_string_round_trip() {
963 let bytes = encode_to_vec(|buf| encode_app_bit_string(buf, 4, &[0xF0]));
964 let (val, end) = decode_application_value(&bytes, 0).unwrap();
965 assert_eq!(
966 val,
967 PropertyValue::BitString {
968 unused_bits: 4,
969 data: vec![0xF0],
970 }
971 );
972 assert_eq!(end, bytes.len());
973 }
974
975 #[test]
978 fn property_value_encode_decode_all_types() {
979 let oid = ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap();
980 let values = vec![
981 PropertyValue::Null,
982 PropertyValue::Boolean(true),
983 PropertyValue::Boolean(false),
984 PropertyValue::Unsigned(12345),
985 PropertyValue::Signed(-42),
986 PropertyValue::Real(72.5),
987 PropertyValue::Double(3.125),
988 PropertyValue::OctetString(vec![1, 2, 3]),
989 PropertyValue::CharacterString("test".into()),
990 PropertyValue::BitString {
991 unused_bits: 0,
992 data: vec![0xFF],
993 },
994 PropertyValue::Enumerated(8),
995 PropertyValue::Date(Date {
996 year: 124,
997 month: 1,
998 day: 1,
999 day_of_week: 1,
1000 }),
1001 PropertyValue::Time(Time {
1002 hour: 12,
1003 minute: 0,
1004 second: 0,
1005 hundredths: 0,
1006 }),
1007 PropertyValue::ObjectIdentifier(oid),
1008 ];
1009
1010 for original in &values {
1011 let bytes = encode_to_vec(|buf| encode_property_value(buf, original).unwrap());
1012 let (decoded, end) = decode_application_value(&bytes, 0).unwrap();
1013 assert_eq!(&decoded, original, "round-trip failed for {original:?}");
1014 assert_eq!(end, bytes.len(), "offset mismatch for {original:?}");
1015 }
1016 }
1017
1018 #[test]
1021 fn ctx_unsigned_encoding() {
1022 let bytes = encode_to_vec(|buf| encode_ctx_unsigned(buf, 1, 42));
1023 assert_eq!(bytes, vec![0x19, 42]);
1025 }
1026
1027 #[test]
1028 fn ctx_boolean_encoding() {
1029 let bytes = encode_to_vec(|buf| encode_ctx_boolean(buf, 0, true));
1030 assert_eq!(bytes, vec![0x09, 0x01]);
1032 }
1033
1034 #[test]
1035 fn ctx_object_id_encoding() {
1036 let oid = ObjectIdentifier::new(ObjectType::DEVICE, 1234).unwrap();
1037 let bytes = encode_to_vec(|buf| encode_ctx_object_id(buf, 2, &oid));
1038 let mut expected = vec![0x2C];
1040 expected.extend_from_slice(&oid.encode());
1041 assert_eq!(bytes, expected);
1042 }
1043
1044 #[test]
1047 fn timestamp_sequence_number_round_trip() {
1048 let ts = BACnetTimeStamp::SequenceNumber(42);
1049 let mut buf = BytesMut::new();
1050 encode_timestamp(&mut buf, 3, &ts);
1051 let (decoded, end) = decode_timestamp(&buf, 0, 3).unwrap();
1052 assert_eq!(decoded, ts);
1053 assert_eq!(end, buf.len());
1054 }
1055
1056 #[test]
1057 fn timestamp_time_round_trip() {
1058 let ts = BACnetTimeStamp::Time(Time {
1059 hour: 14,
1060 minute: 30,
1061 second: 45,
1062 hundredths: 50,
1063 });
1064 let mut buf = BytesMut::new();
1065 encode_timestamp(&mut buf, 3, &ts);
1066 let (decoded, end) = decode_timestamp(&buf, 0, 3).unwrap();
1067 assert_eq!(decoded, ts);
1068 assert_eq!(end, buf.len());
1069 }
1070
1071 #[test]
1072 fn timestamp_datetime_round_trip() {
1073 let ts = BACnetTimeStamp::DateTime {
1074 date: Date {
1075 year: 126,
1076 month: 2,
1077 day: 28,
1078 day_of_week: 6,
1079 },
1080 time: Time {
1081 hour: 10,
1082 minute: 15,
1083 second: 0,
1084 hundredths: 0,
1085 },
1086 };
1087 let mut buf = BytesMut::new();
1088 encode_timestamp(&mut buf, 5, &ts);
1089 let (decoded, end) = decode_timestamp(&buf, 0, 5).unwrap();
1090 assert_eq!(decoded, ts);
1091 assert_eq!(end, buf.len());
1092 }
1093
1094 #[test]
1095 fn ucs2_decode_ascii() {
1096 let data = [charset::UCS2, 0x00, 0x41, 0x00, 0x42];
1098 let result = decode_character_string(&data).unwrap();
1099 assert_eq!(result, "AB");
1100 }
1101
1102 #[test]
1103 fn ucs2_decode_non_ascii() {
1104 let data = [charset::UCS2, 0x00, 0xE9];
1106 let result = decode_character_string(&data).unwrap();
1107 assert_eq!(result, "é");
1108 }
1109
1110 #[test]
1111 fn unsupported_charset_errors() {
1112 for &cs in &[charset::JIS_X0201, charset::JIS_C6226, charset::UCS4] {
1113 let data = [cs, 0x41, 0x42];
1114 let result = decode_character_string(&data);
1115 assert!(result.is_err(), "charset {cs} should return an error");
1116 }
1117 }
1118}