1#[cfg(all(feature = "alloc", not(feature = "std")))]
27use alloc::{string::String, vec::Vec};
28
29use bacnet_types::error::{DecodeError, DecodingKind, Error};
30#[cfg(feature = "alloc")]
31use bacnet_types::primitives::PropertyValue;
32use bacnet_types::primitives::{BACnetTimeStamp, Date, Time};
33#[cfg(feature = "alloc")]
34use bytes::{BufMut, BytesMut};
35
36#[cfg(feature = "alloc")]
37use crate::constructed::tagged::misplaced_tag;
38use crate::constructed::tagged::{misplaced_kind, unclosed_kind};
39#[cfg(feature = "alloc")]
40use crate::tags::TagClass;
41use crate::tags::{self, app_tag};
42
43mod priority_value;
44mod scalar;
45mod strings;
46mod timestamp;
47mod view;
48
49#[cfg(feature = "alloc")]
50pub use priority_value::encode_priority_date_time;
51pub use priority_value::{PRIORITY_DATE_TIME_TAG, decode_priority_date_time};
52pub use strings::{
53 encode_app_bit_string_to_sink, encode_app_character_string_to_sink,
54 encode_app_octet_string_to_sink, encode_bit_string_to_sink, encode_character_string_to_sink,
55 encode_ctx_bit_string_to_sink, encode_ctx_character_string_to_sink,
56 encode_ctx_octet_string_to_sink,
57};
58pub use timestamp::{encode_timestamp_choice_to_sink, encode_timestamp_to_sink};
59pub use view::{ApplicationValueRef, CharacterStringRef, decode_application_value_ref};
60
61#[cfg(feature = "alloc")]
62pub use scalar::{
63 encode_app_boolean, encode_app_date, encode_app_double, encode_app_enumerated, encode_app_null,
64 encode_app_object_id, encode_app_real, encode_app_signed, encode_app_time, encode_app_unsigned,
65 encode_ctx_boolean, encode_ctx_date, encode_ctx_double, encode_ctx_enumerated,
66 encode_ctx_object_id, encode_ctx_real, encode_ctx_signed, encode_ctx_unsigned, encode_double,
67 encode_real, encode_signed, encode_unsigned,
68};
69pub use scalar::{
70 encode_app_boolean_to_sink, encode_app_date_to_sink, encode_app_double_to_sink,
71 encode_app_enumerated_to_sink, encode_app_null_to_sink, encode_app_object_id_to_sink,
72 encode_app_real_to_sink, encode_app_signed_to_sink, encode_app_time_to_sink,
73 encode_app_unsigned_to_sink, encode_ctx_boolean_to_sink, encode_ctx_date_to_sink,
74 encode_ctx_double_to_sink, encode_ctx_enumerated_to_sink, encode_ctx_object_id_to_sink,
75 encode_ctx_real_to_sink, encode_ctx_signed_to_sink, encode_ctx_unsigned_to_sink,
76 encode_double_to_sink, encode_real_to_sink, encode_signed_to_sink, encode_unsigned_to_sink,
77 signed_len, unsigned_len,
78};
79
80#[cold]
83fn contents_fault(kind: DecodingKind, label: &'static str, value: u64) -> Error {
84 DecodeError::new(kind, 0, label).with_value(value).into()
85}
86
87pub fn decode_unsigned(data: &[u8]) -> Result<u64, Error> {
95 if data.is_empty() || data.len() > 8 {
96 return Err(contents_fault(
97 DecodingKind::InvalidEncoding,
98 "unsigned requires 1-8 bytes, got {}",
99 data.len() as u64,
100 ));
101 }
102 let mut value: u64 = 0;
103 for &b in data {
104 value = (value << 8) | b as u64;
105 }
106 Ok(value)
107}
108
109pub fn decode_unsigned_u8(data: &[u8]) -> Result<u8, Error> {
112 let value = decode_unsigned(data)?;
113 u8::try_from(value).map_err(|_| {
114 contents_fault(
115 DecodingKind::OutOfRange,
116 "unsigned value {} exceeds u8",
117 value,
118 )
119 })
120}
121
122pub fn decode_unsigned_u16(data: &[u8]) -> Result<u16, Error> {
125 let value = decode_unsigned(data)?;
126 u16::try_from(value).map_err(|_| {
127 contents_fault(
128 DecodingKind::OutOfRange,
129 "unsigned value {} exceeds u16",
130 value,
131 )
132 })
133}
134
135pub fn decode_unsigned_u32(data: &[u8]) -> Result<u32, Error> {
138 let value = decode_unsigned(data)?;
139 u32::try_from(value).map_err(|_| {
140 contents_fault(
141 DecodingKind::OutOfRange,
142 "unsigned value {} exceeds u32",
143 value,
144 )
145 })
146}
147
148pub fn decode_signed(data: &[u8]) -> Result<i32, Error> {
152 if data.is_empty() || data.len() > 4 {
153 return Err(contents_fault(
154 DecodingKind::InvalidEncoding,
155 "signed requires 1-4 bytes, got {}",
156 data.len() as u64,
157 ));
158 }
159 let sign_extend = if data[0] & 0x80 != 0 { 0xFF } else { 0x00 };
160 let mut bytes = [sign_extend; 4];
161 bytes[4 - data.len()..].copy_from_slice(data);
162 Ok(i32::from_be_bytes(bytes))
163}
164
165#[cfg(feature = "alloc")]
166pub(crate) fn decode_signed_canonical(data: &[u8]) -> Result<i32, Error> {
167 if data.len() > 1
168 && ((data[0] == 0 && data[1] & 0x80 == 0) || (data[0] == 0xFF && data[1] & 0x80 != 0))
169 {
170 return Err(Error::decoding(0, "signed contains a redundant sign octet"));
171 }
172 decode_signed(data)
173}
174
175pub fn decode_real(data: &[u8]) -> Result<f32, Error> {
179 if data.len() != 4 {
180 return Err(contents_fault(
181 DecodingKind::InvalidEncoding,
182 "Real expects exactly 4 bytes, got {}",
183 data.len() as u64,
184 ));
185 }
186 Ok(f32::from_be_bytes([data[0], data[1], data[2], data[3]]))
187}
188
189pub fn decode_double(data: &[u8]) -> Result<f64, Error> {
193 if data.len() != 8 {
194 return Err(contents_fault(
195 DecodingKind::InvalidEncoding,
196 "Double expects exactly 8 bytes, got {}",
197 data.len() as u64,
198 ));
199 }
200 let bytes: [u8; 8] = [
201 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
202 ];
203 Ok(f64::from_be_bytes(bytes))
204}
205
206pub mod charset {
210 pub const UTF8: u8 = 0;
212 pub const IBM_MICROSOFT_DBCS: u8 = 1;
214 pub const JIS_X_0208: u8 = 2;
216 pub const UCS4: u8 = 3;
218 pub const UCS2: u8 = 4;
220 pub const ISO_8859_1: u8 = 5;
222}
223
224#[cfg(feature = "alloc")]
227pub fn encode_character_string(buf: &mut BytesMut, value: &str) {
228 encode_character_string_to_sink(buf, value).expect("the BytesMut sink cannot return SinkFull");
229}
230
231#[cfg(feature = "alloc")]
235pub fn character_string_len(value: &str) -> Result<u32, Error> {
236 u32::try_from(value.len())
237 .ok()
238 .and_then(|n| n.checked_add(1))
239 .ok_or_else(character_string_too_long)
240}
241
242#[cfg(feature = "alloc")]
244#[cold]
245fn character_string_too_long() -> Error {
246 Error::encode("CharacterString too long for BACnet encoding")
247}
248
249#[cfg(feature = "alloc")]
260pub fn decode_character_string(data: &[u8]) -> Result<String, Error> {
261 if let Some(
263 &unsupported @ (charset::IBM_MICROSOFT_DBCS | charset::JIS_X_0208 | charset::UCS4),
264 ) = data.first()
265 {
266 return Err(contents_fault(
267 DecodingKind::Unsupported,
268 "unsupported charset: {}",
269 unsupported.into(),
270 ));
271 }
272 view::check_character_string(data)?;
275 let payload = &data[1..];
276 Ok(match data[0] {
277 charset::UCS2 => payload
278 .as_chunks::<2>()
279 .0
280 .iter()
281 .filter_map(|c| char::from_u32(u16::from_be_bytes(*c).into()))
282 .collect(),
283 charset::ISO_8859_1 => payload.iter().map(|&octet| char::from(octet)).collect(),
284 _ => core::str::from_utf8(payload).unwrap_or_default().into(),
285 })
286}
287
288#[cold]
292fn invalid_utf8(error: core::str::Utf8Error) -> Error {
293 DecodeError::new(
294 DecodingKind::InvalidEncoding,
295 1,
296 "invalid UTF-8 after {} valid octets",
297 )
298 .with_value(error.valid_up_to() as u64)
299 .into()
300}
301
302#[cold]
304fn unknown_charset(charset: u8) -> Error {
305 contents_fault(
306 DecodingKind::InvalidEncoding,
307 "unknown charset: {}",
308 charset.into(),
309 )
310}
311
312#[cfg(feature = "alloc")]
317pub fn encode_bit_string(buf: &mut BytesMut, unused_bits: u8, data: &[u8]) {
318 encode_bit_string_to_sink(buf, unused_bits, data)
319 .expect("the BytesMut sink cannot return SinkFull");
320}
321
322#[cfg(feature = "alloc")]
327pub fn decode_bit_string(data: &[u8]) -> Result<(u8, Vec<u8>), Error> {
328 let (unused, bits) = decode_bit_string_ref(data)?;
329 Ok((unused, bits.to_vec()))
330}
331
332pub fn decode_bit_string_ref(data: &[u8]) -> Result<(u8, &[u8]), Error> {
336 let Some((&unused, bits)) = data.split_first() else {
337 return Err(Error::decoding(
338 0,
339 "BitString requires at least 1 byte for unused-bits count",
340 ));
341 };
342 if unused > 7 {
343 return Err(contents_fault(
344 DecodingKind::InvalidEncoding,
345 "BitString unused_bits must be 0-7, got {}",
346 unused.into(),
347 ));
348 }
349 Ok((unused, bits))
350}
351
352#[cfg(feature = "alloc")]
360pub fn encode_app_octet_string(buf: &mut BytesMut, data: &[u8]) {
361 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
362 tags::encode_tag(buf, app_tag::OCTET_STRING, TagClass::Application, data_len);
363 buf.put_slice(data);
364}
365
366#[cfg(feature = "alloc")]
370pub fn encode_app_character_string(buf: &mut BytesMut, value: &str) -> Result<(), Error> {
371 encode_app_character_string_to_sink(buf, value).map_err(|_| character_string_too_long())?;
373 Ok(())
374}
375
376#[cfg(feature = "alloc")]
380pub fn encode_app_bit_string(buf: &mut BytesMut, unused_bits: u8, data: &[u8]) {
381 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
382 let len = data_len.saturating_add(1);
383 tags::encode_tag(buf, app_tag::BIT_STRING, TagClass::Application, len);
384 encode_bit_string(buf, unused_bits, data);
385}
386
387#[cfg(feature = "alloc")]
395pub fn encode_ctx_octet_string(buf: &mut BytesMut, tag: u8, data: &[u8]) {
396 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
397 tags::encode_tag(buf, tag, TagClass::Context, data_len);
398 buf.put_slice(data);
399}
400
401#[cfg(feature = "alloc")]
405pub fn encode_ctx_character_string(buf: &mut BytesMut, tag: u8, value: &str) -> Result<(), Error> {
406 encode_ctx_character_string_to_sink(buf, tag, value)
408 .map_err(|_| character_string_too_long())?;
409 Ok(())
410}
411
412#[cfg(feature = "alloc")]
416pub fn encode_ctx_bit_string(buf: &mut BytesMut, tag: u8, unused_bits: u8, data: &[u8]) {
417 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
418 let len = data_len.saturating_add(1);
419 tags::encode_tag(buf, tag, TagClass::Context, len);
420 encode_bit_string(buf, unused_bits, data);
421}
422
423pub(crate) fn fixed_length_label(number: u8) -> Option<(&'static str, u32)> {
430 Some(match number {
431 app_tag::REAL => ("application REAL has {} contents octets, expected 4", 4),
432 app_tag::DOUBLE => ("application Double has {} contents octets, expected 8", 8),
433 app_tag::DATE => ("application Date has {} contents octets, expected 4", 4),
434 app_tag::TIME => ("application Time has {} contents octets, expected 4", 4),
435 app_tag::OBJECT_IDENTIFIER => (
436 "application BACnetObjectIdentifier has {} contents octets, expected 4",
437 4,
438 ),
439 _ => return None,
440 })
441}
442
443#[cfg(feature = "alloc")]
463pub fn decode_application_value(
464 data: &[u8],
465 offset: usize,
466) -> Result<(PropertyValue, usize), Error> {
467 let (tag, new_offset) = tags::decode_tag(data, offset)?;
468 if tag.class == TagClass::Context {
469 if tag.is_opening {
470 let (_content, after) = tags::extract_context_value(data, new_offset, tag.number)?;
471 return Ok((
472 PropertyValue::ApplicationData(data[offset..after].to_vec()),
473 after,
474 ));
475 }
476 if tag.is_closing {
477 return Err(Error::invalid_tag(offset, "unexpected closing tag"));
478 }
479 let content_end = new_offset
480 .checked_add(tag.length as usize)
481 .ok_or_else(|| Error::decoding(new_offset, "length overflow"))?;
482 if data.len() < content_end {
483 return Err(Error::buffer_too_short(content_end, data.len()));
484 }
485 return Ok((
486 PropertyValue::ApplicationData(data[offset..content_end].to_vec()),
487 content_end,
488 ));
489 }
490 let (value, end) = view::decode_application_primitive::<false>(data, offset, tag, new_offset)?;
491 let value = match value {
492 ApplicationValueRef::Null => PropertyValue::Null,
493 ApplicationValueRef::Boolean(value) => PropertyValue::Boolean(value),
494 ApplicationValueRef::Unsigned(value) => PropertyValue::Unsigned(value),
495 ApplicationValueRef::Signed(value) => PropertyValue::Signed(value),
496 ApplicationValueRef::Real(value) => PropertyValue::Real(value),
497 ApplicationValueRef::Double(value) => PropertyValue::Double(value),
498 ApplicationValueRef::OctetString(octets) => PropertyValue::OctetString(octets.to_vec()),
499 ApplicationValueRef::CharacterString(text) => {
500 PropertyValue::CharacterString(decode_character_string(text.contents())?)
501 }
502 ApplicationValueRef::BitString { unused_bits, data } => PropertyValue::BitString {
503 unused_bits,
504 data: data.to_vec(),
505 },
506 ApplicationValueRef::Enumerated(value) => PropertyValue::Enumerated(value),
507 ApplicationValueRef::Date(value) => PropertyValue::Date(value),
508 ApplicationValueRef::Time(value) => PropertyValue::Time(value),
509 ApplicationValueRef::ObjectIdentifier(value) => PropertyValue::ObjectIdentifier(value),
510 };
511 Ok((value, end))
512}
513
514#[cfg(feature = "alloc")]
522pub fn validate_application_value(data: &[u8], offset: usize) -> Result<usize, Error> {
523 let (tag, content_start) = tags::decode_tag(data, offset)?;
524 if tag.class != TagClass::Application || tag.is_opening || tag.is_closing {
525 return Err(misplaced_tag(
526 data,
527 &tag,
528 None,
529 offset,
530 "expected an application-tagged value",
531 ));
532 }
533 if tag.number != app_tag::CHARACTER_STRING && tag.number != app_tag::SIGNED {
534 return decode_application_value(data, offset).map(|(_, end)| end);
535 }
536
537 let content_end = content_start
538 .checked_add(tag.length as usize)
539 .ok_or_else(|| Error::decoding(content_start, "length overflow"))?;
540 if content_end > data.len() {
541 return Err(Error::buffer_too_short(content_end, data.len()));
542 }
543 let content = &data[content_start..content_end];
544 if tag.number == app_tag::SIGNED {
545 decode_signed_canonical(content)?;
546 return Ok(content_end);
547 }
548 view::check_character_string(content).map_err(view::rebased(content_start))?;
549 Ok(content_end)
550}
551
552#[cfg(feature = "alloc")]
555pub fn encode_property_value(buf: &mut BytesMut, value: &PropertyValue) -> Result<(), Error> {
556 match value {
557 PropertyValue::Null => encode_app_null(buf),
558 PropertyValue::Boolean(v) => encode_app_boolean(buf, *v),
559 PropertyValue::Unsigned(v) => encode_app_unsigned(buf, *v),
560 PropertyValue::Signed(v) => encode_app_signed(buf, *v),
561 PropertyValue::Real(v) => encode_app_real(buf, *v),
562 PropertyValue::Double(v) => encode_app_double(buf, *v),
563 PropertyValue::OctetString(v) => encode_app_octet_string(buf, v),
564 PropertyValue::CharacterString(v) => encode_app_character_string(buf, v)?,
565 PropertyValue::BitString { unused_bits, data } => {
566 encode_app_bit_string(buf, *unused_bits, data)
567 }
568 PropertyValue::Enumerated(v) => encode_app_enumerated(buf, *v),
569 PropertyValue::Date(v) => encode_app_date(buf, v),
570 PropertyValue::Time(v) => encode_app_time(buf, v),
571 PropertyValue::ObjectIdentifier(v) => encode_app_object_id(buf, v),
572 PropertyValue::List(values) => {
573 for v in values {
574 encode_property_value(buf, v)?;
575 }
576 }
577 PropertyValue::ApplicationData(bytes) => {
578 buf.put_slice(bytes);
581 }
582 }
583 Ok(())
584}
585
586pub const MAX_TIMESTAMP_SEQUENCE_NUMBER: u64 = 65535;
597
598#[cfg(feature = "alloc")]
616pub fn encode_timestamp_choice(buf: &mut BytesMut, ts: &BACnetTimeStamp) -> Result<(), Error> {
617 encode_timestamp_choice_to_sink(buf, ts).expect("the BytesMut sink cannot return SinkFull");
618 Ok(())
619}
620
621pub fn decode_timestamp_choice(
631 data: &[u8],
632 offset: usize,
633) -> Result<(BACnetTimeStamp, usize), Error> {
634 use crate::constructed::tagged;
635 const WHAT: &str = "BACnetTimeStamp";
636
637 let (inner_tag, inner_pos) = tags::decode_tag(data, offset)?;
638 if inner_tag.is_context(0) {
639 let (octets, end) = tagged::decode_ctx_fixed(data, offset, 0, tagged::Fixed::TIME, WHAT)?;
640 return Ok((BACnetTimeStamp::Time(Time::decode(octets)?), end));
641 }
642 if inner_tag.is_context(1) {
643 let (octets, end) = tagged::decode_ctx_primitive(data, offset, 1, WHAT)?;
644 let n = decode_unsigned(octets)?;
645 if n > MAX_TIMESTAMP_SEQUENCE_NUMBER {
646 return Err(DecodeError::new(
647 DecodingKind::OutOfRange,
648 inner_pos,
649 "BACnetTimeStamp sequence-number {} exceeds 65535",
650 )
651 .with_value(n)
652 .into());
653 }
654 let n = u16::try_from(n).expect("BACnetTimeStamp sequence range checked above");
655 return Ok((BACnetTimeStamp::SequenceNumber(n), end));
656 }
657 if inner_tag.is_opening_tag(2) {
658 const DATE_TIME: &str = "BACnetTimeStamp DateTime";
659 let (date, date_end) =
660 tagged::decode_app_fixed(data, inner_pos, app_tag::DATE, 4, DATE_TIME)?;
661 let (time, time_end) =
662 tagged::decode_app_fixed(data, date_end, app_tag::TIME, 4, DATE_TIME)?;
663 let end = tagged::expect_closing(data, time_end, 2, DATE_TIME)?;
664 let date_time = BACnetTimeStamp::DateTime {
665 date: Date::decode(date)?,
666 time: Time::decode(time)?,
667 };
668 return Ok((date_time, end));
669 }
670 Err(DecodeError::new(
671 misplaced_kind(data, offset, &inner_tag, None),
672 offset,
673 "BACnetTimeStamp: unexpected inner choice tag",
674 )
675 .into())
676}
677
678#[cfg(feature = "alloc")]
687pub fn encode_timestamp(
688 buf: &mut BytesMut,
689 tag_number: u8,
690 ts: &BACnetTimeStamp,
691) -> Result<(), Error> {
692 encode_timestamp_to_sink(buf, tag_number, ts)
693 .expect("the BytesMut sink cannot return SinkFull");
694 Ok(())
695}
696
697pub fn decode_timestamp(
702 data: &[u8],
703 offset: usize,
704 tag_number: u8,
705) -> Result<(BACnetTimeStamp, usize), Error> {
706 let (tag, pos) = tags::decode_tag(data, offset)?;
707 if !tag.is_opening_tag(tag_number) {
708 return Err(DecodeError::new(
709 misplaced_kind(data, offset, &tag, Some(tag_number)),
710 offset,
711 "expected opening tag {} for BACnetTimeStamp",
712 )
713 .with_value(tag_number.into())
714 .into());
715 }
716
717 let (ts, after_inner) = decode_timestamp_choice(data, pos)?;
718
719 let (close, final_pos) = tags::decode_tag(data, after_inner)?;
720 if !close.is_closing_tag(tag_number) {
721 return Err(DecodeError::new(
722 unclosed_kind(&close),
723 after_inner,
724 "expected closing tag {} for BACnetTimeStamp",
725 )
726 .with_value(tag_number.into())
727 .into());
728 }
729
730 Ok((ts, final_pos))
731}
732
733#[cfg(all(test, feature = "alloc"))]
738mod tests;
739
740#[cfg(all(test, feature = "alloc"))]
741mod length_first_tests;