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