1#[cfg(not(feature = "std"))]
7use alloc::{string::String, vec::Vec};
8
9use bacnet_types::error::{DecodingKind, Error};
10use bacnet_types::primitives::{BACnetTimeStamp, Date, ObjectIdentifier, PropertyValue, Time};
11use bytes::{BufMut, BytesMut};
12
13use crate::constructed::tagged::{misplaced_tag, unclosed_kind};
14use crate::tags::{self, app_tag, TagClass};
15
16pub fn encode_unsigned(buf: &mut BytesMut, value: u64) {
24 if value <= 0xFF {
25 buf.put_u8(value as u8);
26 } else if value <= 0xFFFF {
27 buf.put_u16(value as u16);
28 } else if value <= 0xFF_FFFF {
29 buf.put_u8((value >> 16) as u8);
30 buf.put_u16(value as u16);
31 } else if value <= 0xFFFF_FFFF {
32 buf.put_u32(value as u32);
33 } else if value <= 0xFF_FFFF_FFFF {
34 buf.put_u8((value >> 32) as u8);
35 buf.put_u32(value as u32);
36 } else if value <= 0xFFFF_FFFF_FFFF {
37 buf.put_u16((value >> 32) as u16);
38 buf.put_u32(value as u32);
39 } else if value <= 0xFF_FFFF_FFFF_FFFF {
40 buf.put_u8((value >> 48) as u8);
41 buf.put_u16((value >> 32) as u16);
42 buf.put_u32(value as u32);
43 } else {
44 buf.put_u64(value);
45 }
46}
47
48pub fn unsigned_len(value: u64) -> u32 {
50 if value <= 0xFF {
51 1
52 } else if value <= 0xFFFF {
53 2
54 } else if value <= 0xFF_FFFF {
55 3
56 } else if value <= 0xFFFF_FFFF {
57 4
58 } else if value <= 0xFF_FFFF_FFFF {
59 5
60 } else if value <= 0xFFFF_FFFF_FFFF {
61 6
62 } else if value <= 0xFF_FFFF_FFFF_FFFF {
63 7
64 } else {
65 8
66 }
67}
68
69pub fn decode_unsigned(data: &[u8]) -> Result<u64, Error> {
71 if data.is_empty() || data.len() > 8 {
72 return Err(Error::decoding(
73 0,
74 format!("unsigned requires 1-8 bytes, got {}", data.len()),
75 ));
76 }
77 let mut value: u64 = 0;
78 for &b in data {
79 value = (value << 8) | b as u64;
80 }
81 Ok(value)
82}
83
84pub fn decode_unsigned_u8(data: &[u8]) -> Result<u8, Error> {
87 let value = decode_unsigned(data)?;
88 u8::try_from(value)
89 .map_err(|_| Error::out_of_range(0, format!("unsigned value {value} exceeds u8")))
90}
91
92pub fn decode_unsigned_u16(data: &[u8]) -> Result<u16, Error> {
95 let value = decode_unsigned(data)?;
96 u16::try_from(value)
97 .map_err(|_| Error::out_of_range(0, format!("unsigned value {value} exceeds u16")))
98}
99
100pub fn decode_unsigned_u32(data: &[u8]) -> Result<u32, Error> {
103 let value = decode_unsigned(data)?;
104 u32::try_from(value)
105 .map_err(|_| Error::out_of_range(0, format!("unsigned value {value} exceeds u32")))
106}
107
108pub fn encode_signed(buf: &mut BytesMut, value: i32) {
112 let n = signed_len(value);
113 let bytes = value.to_be_bytes();
114 buf.put_slice(&bytes[4 - n as usize..]);
115}
116
117pub fn signed_len(value: i32) -> u32 {
119 if (-128..=127).contains(&value) {
120 1
121 } else if (-32768..=32767).contains(&value) {
122 2
123 } else if (-8_388_608..=8_388_607).contains(&value) {
124 3
125 } else {
126 4
127 }
128}
129
130pub fn decode_signed(data: &[u8]) -> Result<i32, Error> {
132 if data.is_empty() || data.len() > 4 {
133 return Err(Error::decoding(
134 0,
135 format!("signed requires 1-4 bytes, got {}", data.len()),
136 ));
137 }
138 let sign_extend = if data[0] & 0x80 != 0 { 0xFF } else { 0x00 };
139 let mut bytes = [sign_extend; 4];
140 bytes[4 - data.len()..].copy_from_slice(data);
141 Ok(i32::from_be_bytes(bytes))
142}
143
144pub(crate) fn decode_signed_canonical(data: &[u8]) -> Result<i32, Error> {
145 if data.len() > 1
146 && ((data[0] == 0 && data[1] & 0x80 == 0) || (data[0] == 0xFF && data[1] & 0x80 != 0))
147 {
148 return Err(Error::decoding(0, "signed contains a redundant sign octet"));
149 }
150 decode_signed(data)
151}
152
153pub fn encode_real(buf: &mut BytesMut, value: f32) {
157 buf.put_f32(value);
158}
159
160pub fn decode_real(data: &[u8]) -> Result<f32, Error> {
162 if data.len() != 4 {
163 return Err(Error::decoding(
164 0,
165 format!("Real expects exactly 4 bytes, got {}", data.len()),
166 ));
167 }
168 Ok(f32::from_be_bytes([data[0], data[1], data[2], data[3]]))
169}
170
171pub fn encode_double(buf: &mut BytesMut, value: f64) {
175 buf.put_f64(value);
176}
177
178pub fn decode_double(data: &[u8]) -> Result<f64, Error> {
180 if data.len() != 8 {
181 return Err(Error::decoding(
182 0,
183 format!("Double expects exactly 8 bytes, got {}", data.len()),
184 ));
185 }
186 let bytes: [u8; 8] = [
187 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
188 ];
189 Ok(f64::from_be_bytes(bytes))
190}
191
192pub mod charset {
196 pub const UTF8: u8 = 0;
198 pub const IBM_MICROSOFT_DBCS: u8 = 1;
200 pub const JIS_X_0208: u8 = 2;
202 pub const UCS4: u8 = 3;
204 pub const UCS2: u8 = 4;
206 pub const ISO_8859_1: u8 = 5;
208}
209
210pub fn encode_character_string(buf: &mut BytesMut, value: &str) {
212 buf.put_u8(charset::UTF8);
213 buf.put_slice(value.as_bytes());
214}
215
216pub fn character_string_len(value: &str) -> Result<u32, Error> {
218 u32::try_from(value.len())
219 .ok()
220 .and_then(|n| n.checked_add(1))
221 .ok_or_else(|| Error::Encoding("CharacterString too long for BACnet encoding".into()))
222}
223
224pub fn decode_character_string(data: &[u8]) -> Result<String, Error> {
233 if data.is_empty() {
234 return Err(Error::decoding(
235 0,
236 "CharacterString requires at least 1 byte for charset",
237 ));
238 }
239 let charset_id = data[0];
240 let payload = &data[1..];
241 match charset_id {
242 charset::UTF8 => String::from_utf8(payload.to_vec())
243 .map_err(|e| Error::decoding(1, format!("invalid UTF-8: {e}"))),
244 charset::UCS2 => {
245 if !payload.len().is_multiple_of(2) {
246 return Err(Error::decoding(1, "UCS-2 data must have even length"));
247 }
248 let mut s = String::new();
249 for (i, chunk) in payload.as_chunks::<2>().0.iter().enumerate() {
250 let code_point = u16::from_be_bytes(*chunk);
251 if let Some(c) = char::from_u32(code_point as u32) {
252 s.push(c);
253 } else {
254 return Err(Error::decoding(1 + i * 2, "invalid UCS-2 code point"));
255 }
256 }
257 Ok(s)
258 }
259 charset::ISO_8859_1 => Ok(payload.iter().map(|&b| b as char).collect()),
260 charset::IBM_MICROSOFT_DBCS | charset::JIS_X_0208 | charset::UCS4 => {
262 Err(Error::decoding_kind(
263 DecodingKind::Unsupported,
264 0,
265 format!("unsupported charset: {charset_id}"),
266 ))
267 }
268 other => Err(Error::decoding(0, format!("unknown charset: {other}"))),
269 }
270}
271
272pub fn encode_bit_string(buf: &mut BytesMut, unused_bits: u8, data: &[u8]) {
276 buf.put_u8(unused_bits);
277 buf.put_slice(data);
278}
279
280pub fn decode_bit_string(data: &[u8]) -> Result<(u8, Vec<u8>), Error> {
284 if data.is_empty() {
285 return Err(Error::decoding(
286 0,
287 "BitString requires at least 1 byte for unused-bits count",
288 ));
289 }
290 let unused = data[0];
291 if unused > 7 {
292 return Err(Error::decoding(
293 0,
294 format!("BitString unused_bits must be 0-7, got {unused}"),
295 ));
296 }
297 Ok((unused, data[1..].to_vec()))
298}
299
300pub fn encode_app_null(buf: &mut BytesMut) {
306 tags::encode_tag(buf, app_tag::NULL, TagClass::Application, 0);
307}
308
309pub fn encode_app_boolean(buf: &mut BytesMut, value: bool) {
313 tags::encode_tag(
314 buf,
315 app_tag::BOOLEAN,
316 TagClass::Application,
317 if value { 1 } else { 0 },
318 );
319}
320
321pub fn encode_app_unsigned(buf: &mut BytesMut, value: u64) {
323 let len = unsigned_len(value);
324 tags::encode_tag(buf, app_tag::UNSIGNED, TagClass::Application, len);
325 encode_unsigned(buf, value);
326}
327
328pub fn encode_app_signed(buf: &mut BytesMut, value: i32) {
330 let len = signed_len(value);
331 tags::encode_tag(buf, app_tag::SIGNED, TagClass::Application, len);
332 encode_signed(buf, value);
333}
334
335pub fn encode_app_real(buf: &mut BytesMut, value: f32) {
337 tags::encode_tag(buf, app_tag::REAL, TagClass::Application, 4);
338 encode_real(buf, value);
339}
340
341pub fn encode_app_double(buf: &mut BytesMut, value: f64) {
343 tags::encode_tag(buf, app_tag::DOUBLE, TagClass::Application, 8);
344 encode_double(buf, value);
345}
346
347pub fn encode_app_octet_string(buf: &mut BytesMut, data: &[u8]) {
349 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
350 tags::encode_tag(buf, app_tag::OCTET_STRING, TagClass::Application, data_len);
351 buf.put_slice(data);
352}
353
354pub fn encode_app_character_string(buf: &mut BytesMut, value: &str) -> Result<(), Error> {
356 let len = character_string_len(value)?;
357 tags::encode_tag(buf, app_tag::CHARACTER_STRING, TagClass::Application, len);
358 encode_character_string(buf, value);
359 Ok(())
360}
361
362pub fn encode_app_bit_string(buf: &mut BytesMut, unused_bits: u8, data: &[u8]) {
364 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
365 let len = data_len.saturating_add(1);
366 tags::encode_tag(buf, app_tag::BIT_STRING, TagClass::Application, len);
367 encode_bit_string(buf, unused_bits, data);
368}
369
370pub fn encode_app_enumerated(buf: &mut BytesMut, value: u32) {
372 let len = unsigned_len(value as u64);
373 tags::encode_tag(buf, app_tag::ENUMERATED, TagClass::Application, len);
374 encode_unsigned(buf, value as u64);
375}
376
377pub fn encode_app_date(buf: &mut BytesMut, date: &Date) {
379 tags::encode_tag(buf, app_tag::DATE, TagClass::Application, 4);
380 buf.put_slice(&date.encode());
381}
382
383pub fn encode_app_time(buf: &mut BytesMut, time: &Time) {
385 tags::encode_tag(buf, app_tag::TIME, TagClass::Application, 4);
386 buf.put_slice(&time.encode());
387}
388
389pub fn encode_app_object_id(buf: &mut BytesMut, oid: &ObjectIdentifier) {
391 tags::encode_tag(buf, app_tag::OBJECT_IDENTIFIER, TagClass::Application, 4);
392 buf.put_slice(&oid.encode());
393}
394
395pub fn encode_ctx_unsigned(buf: &mut BytesMut, tag: u8, value: u64) {
401 let len = unsigned_len(value);
402 tags::encode_tag(buf, tag, TagClass::Context, len);
403 encode_unsigned(buf, value);
404}
405
406pub fn encode_ctx_signed(buf: &mut BytesMut, tag: u8, value: i32) {
408 let len = signed_len(value);
409 tags::encode_tag(buf, tag, TagClass::Context, len);
410 encode_signed(buf, value);
411}
412
413pub fn encode_ctx_real(buf: &mut BytesMut, tag: u8, value: f32) {
415 tags::encode_tag(buf, tag, TagClass::Context, 4);
416 encode_real(buf, value);
417}
418
419pub fn encode_ctx_double(buf: &mut BytesMut, tag: u8, value: f64) {
421 tags::encode_tag(buf, tag, TagClass::Context, 8);
422 encode_double(buf, value);
423}
424
425pub fn encode_ctx_enumerated(buf: &mut BytesMut, tag: u8, value: u32) {
427 let len = unsigned_len(value as u64);
428 tags::encode_tag(buf, tag, TagClass::Context, len);
429 encode_unsigned(buf, value as u64);
430}
431
432pub fn encode_ctx_boolean(buf: &mut BytesMut, tag: u8, value: bool) {
436 tags::encode_tag(buf, tag, TagClass::Context, 1);
437 buf.put_u8(if value { 1 } else { 0 });
438}
439
440pub fn encode_ctx_object_id(buf: &mut BytesMut, tag: u8, oid: &ObjectIdentifier) {
442 tags::encode_tag(buf, tag, TagClass::Context, 4);
443 buf.put_slice(&oid.encode());
444}
445
446pub fn encode_ctx_octet_string(buf: &mut BytesMut, tag: u8, data: &[u8]) {
448 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
449 tags::encode_tag(buf, tag, TagClass::Context, data_len);
450 buf.put_slice(data);
451}
452
453pub fn encode_ctx_character_string(buf: &mut BytesMut, tag: u8, value: &str) -> Result<(), Error> {
455 let len = character_string_len(value)?;
456 tags::encode_tag(buf, tag, TagClass::Context, len);
457 encode_character_string(buf, value);
458 Ok(())
459}
460
461pub fn encode_ctx_date(buf: &mut BytesMut, tag: u8, date: &Date) {
463 tags::encode_tag(buf, tag, TagClass::Context, 4);
464 buf.put_slice(&date.encode());
465}
466
467pub fn encode_ctx_bit_string(buf: &mut BytesMut, tag: u8, unused_bits: u8, data: &[u8]) {
469 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
470 let len = data_len.saturating_add(1);
471 tags::encode_tag(buf, tag, TagClass::Context, len);
472 encode_bit_string(buf, unused_bits, data);
473}
474
475pub fn decode_application_value(
497 data: &[u8],
498 offset: usize,
499) -> Result<(PropertyValue, usize), Error> {
500 let (tag, new_offset) = tags::decode_tag(data, offset)?;
501 if tag.class == TagClass::Context {
502 if tag.is_opening {
503 let (_content, after) = tags::extract_context_value(data, new_offset, tag.number)?;
504 return Ok((
505 PropertyValue::ApplicationData(data[offset..after].to_vec()),
506 after,
507 ));
508 }
509 if tag.is_closing {
510 return Err(Error::invalid_tag(offset, "unexpected closing tag"));
511 }
512 let content_end = new_offset
513 .checked_add(tag.length as usize)
514 .ok_or_else(|| Error::decoding(new_offset, "length overflow"))?;
515 if data.len() < content_end {
516 return Err(Error::buffer_too_short(content_end, data.len()));
517 }
518 return Ok((
519 PropertyValue::ApplicationData(data[offset..content_end].to_vec()),
520 content_end,
521 ));
522 }
523 if tag.is_opening || tag.is_closing {
524 return Err(Error::invalid_tag(offset, "unexpected opening/closing tag"));
525 }
526
527 if tag.number == app_tag::NULL && tag.length != 0 {
528 return Err(Error::decoding(
529 offset,
530 "application NULL must have no contents",
531 ));
532 }
533 if tag.number == app_tag::BOOLEAN && tag.length > 1 {
534 return Err(Error::decoding(
535 offset,
536 "application BOOLEAN L/V/T must be 0 or 1",
537 ));
538 }
539
540 let content_start = new_offset;
541 let content_len = tag.length as usize;
542 let content_end = content_start
543 .checked_add(content_len)
544 .ok_or_else(|| Error::decoding(content_start, "length overflow"))?;
545
546 if tag.number == app_tag::BOOLEAN {
547 return Ok((PropertyValue::Boolean(tag.length != 0), content_start));
548 }
549
550 let fixed = match tag.number {
553 app_tag::REAL => Some(("REAL", 4)),
554 app_tag::DOUBLE => Some(("Double", 8)),
555 app_tag::DATE => Some(("Date", 4)),
556 app_tag::TIME => Some(("Time", 4)),
557 app_tag::OBJECT_IDENTIFIER => Some(("BACnetObjectIdentifier", 4)),
558 _ => None,
559 };
560 if let Some((kind, octets)) = fixed {
561 if tag.length != octets {
562 return Err(Error::decoding(
563 offset,
564 format!(
565 "application {kind} has {} contents octets, expected {octets}",
566 tag.length
567 ),
568 ));
569 }
570 }
571
572 if data.len() < content_end {
573 return Err(Error::buffer_too_short(content_end, data.len()));
574 }
575
576 let content = &data[content_start..content_end];
577
578 let value = match tag.number {
579 app_tag::NULL => PropertyValue::Null,
580 app_tag::UNSIGNED => PropertyValue::Unsigned(decode_unsigned(content)?),
581 app_tag::SIGNED => PropertyValue::Signed(decode_signed(content)?),
582 app_tag::REAL => PropertyValue::Real(decode_real(content)?),
583 app_tag::DOUBLE => PropertyValue::Double(decode_double(content)?),
584 app_tag::OCTET_STRING => PropertyValue::OctetString(content.to_vec()),
585 app_tag::CHARACTER_STRING => {
586 PropertyValue::CharacterString(decode_character_string(content)?)
587 }
588 app_tag::BIT_STRING => {
589 let (unused, bits) = decode_bit_string(content)?;
590 PropertyValue::BitString {
591 unused_bits: unused,
592 data: bits,
593 }
594 }
595 app_tag::ENUMERATED => {
596 let value = u32::try_from(decode_unsigned(content)?)
597 .map_err(|_| Error::out_of_range(content_start, "ENUMERATED exceeds u32"))?;
598 PropertyValue::Enumerated(value)
599 }
600 app_tag::DATE => PropertyValue::Date(Date::decode(content)?),
601 app_tag::TIME => PropertyValue::Time(Time::decode(content)?),
602 app_tag::OBJECT_IDENTIFIER => {
603 PropertyValue::ObjectIdentifier(ObjectIdentifier::decode(content)?)
604 }
605 other => {
606 return Err(Error::invalid_tag(
607 offset,
608 format!("unknown application tag number {other}"),
609 ));
610 }
611 };
612
613 Ok((value, content_end))
614}
615
616pub fn validate_application_value(data: &[u8], offset: usize) -> Result<usize, Error> {
624 let (tag, content_start) = tags::decode_tag(data, offset)?;
625 if tag.class != TagClass::Application || tag.is_opening || tag.is_closing {
626 return Err(misplaced_tag(
627 data,
628 &tag,
629 None,
630 offset,
631 "expected an application-tagged value",
632 ));
633 }
634 if tag.number != app_tag::CHARACTER_STRING && tag.number != app_tag::SIGNED {
635 return decode_application_value(data, offset).map(|(_, end)| end);
636 }
637
638 let content_end = content_start
639 .checked_add(tag.length as usize)
640 .ok_or_else(|| Error::decoding(content_start, "length overflow"))?;
641 if content_end > data.len() {
642 return Err(Error::buffer_too_short(content_end, data.len()));
643 }
644 let content = &data[content_start..content_end];
645 if tag.number == app_tag::SIGNED {
646 decode_signed_canonical(content)?;
647 return Ok(content_end);
648 }
649 let Some((&charset_id, payload)) = content.split_first() else {
650 return Err(Error::decoding(
651 content_start,
652 "CharacterString requires a character-set octet",
653 ));
654 };
655 match charset_id {
656 charset::UTF8 | charset::UCS2 | charset::ISO_8859_1 => {
657 decode_character_string(content)?;
658 }
659 charset::IBM_MICROSOFT_DBCS if payload.len() < 2 => {
660 return Err(Error::decoding(
661 content_start + 1,
662 "DBCS CharacterString requires a two-octet code page",
663 ));
664 }
665 charset::IBM_MICROSOFT_DBCS | charset::JIS_X_0208 => {}
666 charset::UCS4 => {
667 if !payload.len().is_multiple_of(4) {
668 return Err(Error::decoding(
669 content_start + 1,
670 "UCS-4 CharacterString length must be a multiple of four",
671 ));
672 }
673 for (index, encoded) in payload.as_chunks::<4>().0.iter().enumerate() {
674 let code_point = u32::from_be_bytes(*encoded);
675 if char::from_u32(code_point).is_none() {
676 return Err(Error::decoding(
677 content_start + 1 + index * 4,
678 "invalid UCS-4 code point",
679 ));
680 }
681 }
682 }
683 _ => {
684 return Err(Error::decoding(
685 content_start,
686 format!("reserved character set: {charset_id}"),
687 ));
688 }
689 }
690 Ok(content_end)
691}
692
693pub fn encode_property_value(buf: &mut BytesMut, value: &PropertyValue) -> Result<(), Error> {
695 match value {
696 PropertyValue::Null => encode_app_null(buf),
697 PropertyValue::Boolean(v) => encode_app_boolean(buf, *v),
698 PropertyValue::Unsigned(v) => encode_app_unsigned(buf, *v),
699 PropertyValue::Signed(v) => encode_app_signed(buf, *v),
700 PropertyValue::Real(v) => encode_app_real(buf, *v),
701 PropertyValue::Double(v) => encode_app_double(buf, *v),
702 PropertyValue::OctetString(v) => encode_app_octet_string(buf, v),
703 PropertyValue::CharacterString(v) => encode_app_character_string(buf, v)?,
704 PropertyValue::BitString { unused_bits, data } => {
705 encode_app_bit_string(buf, *unused_bits, data)
706 }
707 PropertyValue::Enumerated(v) => encode_app_enumerated(buf, *v),
708 PropertyValue::Date(v) => encode_app_date(buf, v),
709 PropertyValue::Time(v) => encode_app_time(buf, v),
710 PropertyValue::ObjectIdentifier(v) => encode_app_object_id(buf, v),
711 PropertyValue::List(values) => {
712 for v in values {
713 encode_property_value(buf, v)?;
714 }
715 }
716 PropertyValue::ApplicationData(bytes) => {
717 buf.put_slice(bytes);
720 }
721 }
722 Ok(())
723}
724
725pub const MAX_TIMESTAMP_SEQUENCE_NUMBER: u64 = 65535;
736
737pub fn encode_timestamp_choice(buf: &mut BytesMut, ts: &BACnetTimeStamp) -> Result<(), Error> {
754 match ts {
755 BACnetTimeStamp::Time(t) => {
756 tags::encode_tag(buf, 0, TagClass::Context, 4);
757 buf.put_slice(&t.encode());
758 }
759 BACnetTimeStamp::SequenceNumber(n) => {
760 encode_ctx_unsigned(buf, 1, u64::from(*n));
761 }
762 BACnetTimeStamp::DateTime { date, time } => {
763 tags::encode_opening_tag(buf, 2);
764 encode_app_date(buf, date);
765 encode_app_time(buf, time);
766 tags::encode_closing_tag(buf, 2);
767 }
768 }
769 Ok(())
770}
771
772pub fn decode_timestamp_choice(
782 data: &[u8],
783 offset: usize,
784) -> Result<(BACnetTimeStamp, usize), Error> {
785 use crate::constructed::tagged;
786 const WHAT: &str = "BACnetTimeStamp";
787
788 let (inner_tag, inner_pos) = tags::decode_tag(data, offset)?;
789 if inner_tag.is_context(0) {
790 let (octets, end) = tagged::decode_ctx_fixed(data, offset, 0, 4, "Time", WHAT)?;
791 return Ok((BACnetTimeStamp::Time(Time::decode(octets)?), end));
792 }
793 if inner_tag.is_context(1) {
794 let (octets, end) = tagged::decode_ctx_primitive(data, offset, 1, WHAT)?;
795 let n = decode_unsigned(octets)?;
796 if n > MAX_TIMESTAMP_SEQUENCE_NUMBER {
797 return Err(Error::out_of_range(
798 inner_pos,
799 format!("BACnetTimeStamp sequence-number {n} exceeds 65535"),
800 ));
801 }
802 let n = u16::try_from(n).expect("BACnetTimeStamp sequence range checked above");
803 return Ok((BACnetTimeStamp::SequenceNumber(n), end));
804 }
805 if inner_tag.is_opening_tag(2) {
806 const DATE_TIME: &str = "BACnetTimeStamp DateTime";
807 let (date, date_end) =
808 tagged::decode_app_fixed(data, inner_pos, app_tag::DATE, 4, DATE_TIME)?;
809 let (time, time_end) =
810 tagged::decode_app_fixed(data, date_end, app_tag::TIME, 4, DATE_TIME)?;
811 let end = tagged::expect_closing(data, time_end, 2, DATE_TIME)?;
812 let date_time = BACnetTimeStamp::DateTime {
813 date: Date::decode(date)?,
814 time: Time::decode(time)?,
815 };
816 return Ok((date_time, end));
817 }
818 Err(misplaced_tag(
819 data,
820 &inner_tag,
821 None,
822 offset,
823 "BACnetTimeStamp: unexpected inner choice tag",
824 ))
825}
826
827pub fn encode_timestamp(
833 buf: &mut BytesMut,
834 tag_number: u8,
835 ts: &BACnetTimeStamp,
836) -> Result<(), Error> {
837 tags::encode_opening_tag(buf, tag_number);
838 encode_timestamp_choice(buf, ts)?;
839 tags::encode_closing_tag(buf, tag_number);
840 Ok(())
841}
842
843pub fn decode_timestamp(
848 data: &[u8],
849 offset: usize,
850 tag_number: u8,
851) -> Result<(BACnetTimeStamp, usize), Error> {
852 let (tag, pos) = tags::decode_tag(data, offset)?;
853 if !tag.is_opening_tag(tag_number) {
854 return Err(misplaced_tag(
855 data,
856 &tag,
857 Some(tag_number),
858 offset,
859 format!("expected opening tag {tag_number} for BACnetTimeStamp"),
860 ));
861 }
862
863 let (ts, after_inner) = decode_timestamp_choice(data, pos)?;
864
865 let (close, final_pos) = tags::decode_tag(data, after_inner)?;
866 if !close.is_closing_tag(tag_number) {
867 return Err(Error::decoding_kind(
868 unclosed_kind(&close),
869 after_inner,
870 format!("expected closing tag {tag_number} for BACnetTimeStamp"),
871 ));
872 }
873
874 Ok((ts, final_pos))
875}
876
877#[cfg(test)]
882mod tests;
883
884#[cfg(test)]
885mod length_first_tests;