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 scalar;
44mod strings;
45mod timestamp;
46mod view;
47
48pub use strings::{
49 encode_app_bit_string_to_sink, encode_app_character_string_to_sink,
50 encode_app_octet_string_to_sink, encode_bit_string_to_sink, encode_character_string_to_sink,
51 encode_ctx_bit_string_to_sink, encode_ctx_character_string_to_sink,
52 encode_ctx_octet_string_to_sink,
53};
54pub use timestamp::{encode_timestamp_choice_to_sink, encode_timestamp_to_sink};
55pub use view::{decode_application_value_ref, ApplicationValueRef, CharacterStringRef};
56
57#[cfg(feature = "alloc")]
58pub use scalar::{
59 encode_app_boolean, encode_app_date, encode_app_double, encode_app_enumerated, encode_app_null,
60 encode_app_object_id, encode_app_real, encode_app_signed, encode_app_time, encode_app_unsigned,
61 encode_ctx_boolean, encode_ctx_date, encode_ctx_double, encode_ctx_enumerated,
62 encode_ctx_object_id, encode_ctx_real, encode_ctx_signed, encode_ctx_unsigned, encode_double,
63 encode_real, encode_signed, encode_unsigned,
64};
65pub use scalar::{
66 encode_app_boolean_to_sink, encode_app_date_to_sink, encode_app_double_to_sink,
67 encode_app_enumerated_to_sink, encode_app_null_to_sink, encode_app_object_id_to_sink,
68 encode_app_real_to_sink, encode_app_signed_to_sink, encode_app_time_to_sink,
69 encode_app_unsigned_to_sink, encode_ctx_boolean_to_sink, encode_ctx_date_to_sink,
70 encode_ctx_double_to_sink, encode_ctx_enumerated_to_sink, encode_ctx_object_id_to_sink,
71 encode_ctx_real_to_sink, encode_ctx_signed_to_sink, encode_ctx_unsigned_to_sink,
72 encode_double_to_sink, encode_real_to_sink, encode_signed_to_sink, encode_unsigned_to_sink,
73 signed_len, unsigned_len,
74};
75
76#[cold]
79fn contents_fault(kind: DecodingKind, label: &'static str, value: u64) -> Error {
80 DecodeError::new(kind, 0, label).with_value(value).into()
81}
82
83pub fn decode_unsigned(data: &[u8]) -> Result<u64, Error> {
91 if data.is_empty() || data.len() > 8 {
92 return Err(contents_fault(
93 DecodingKind::InvalidEncoding,
94 "unsigned requires 1-8 bytes, got {}",
95 data.len() as u64,
96 ));
97 }
98 let mut value: u64 = 0;
99 for &b in data {
100 value = (value << 8) | b as u64;
101 }
102 Ok(value)
103}
104
105pub fn decode_unsigned_u8(data: &[u8]) -> Result<u8, Error> {
108 let value = decode_unsigned(data)?;
109 u8::try_from(value).map_err(|_| {
110 contents_fault(
111 DecodingKind::OutOfRange,
112 "unsigned value {} exceeds u8",
113 value,
114 )
115 })
116}
117
118pub fn decode_unsigned_u16(data: &[u8]) -> Result<u16, Error> {
121 let value = decode_unsigned(data)?;
122 u16::try_from(value).map_err(|_| {
123 contents_fault(
124 DecodingKind::OutOfRange,
125 "unsigned value {} exceeds u16",
126 value,
127 )
128 })
129}
130
131pub fn decode_unsigned_u32(data: &[u8]) -> Result<u32, Error> {
134 let value = decode_unsigned(data)?;
135 u32::try_from(value).map_err(|_| {
136 contents_fault(
137 DecodingKind::OutOfRange,
138 "unsigned value {} exceeds u32",
139 value,
140 )
141 })
142}
143
144pub fn decode_signed(data: &[u8]) -> Result<i32, Error> {
148 if data.is_empty() || data.len() > 4 {
149 return Err(contents_fault(
150 DecodingKind::InvalidEncoding,
151 "signed requires 1-4 bytes, got {}",
152 data.len() as u64,
153 ));
154 }
155 let sign_extend = if data[0] & 0x80 != 0 { 0xFF } else { 0x00 };
156 let mut bytes = [sign_extend; 4];
157 bytes[4 - data.len()..].copy_from_slice(data);
158 Ok(i32::from_be_bytes(bytes))
159}
160
161#[cfg(feature = "alloc")]
162pub(crate) fn decode_signed_canonical(data: &[u8]) -> Result<i32, Error> {
163 if data.len() > 1
164 && ((data[0] == 0 && data[1] & 0x80 == 0) || (data[0] == 0xFF && data[1] & 0x80 != 0))
165 {
166 return Err(Error::decoding(0, "signed contains a redundant sign octet"));
167 }
168 decode_signed(data)
169}
170
171pub fn decode_real(data: &[u8]) -> Result<f32, Error> {
175 if data.len() != 4 {
176 return Err(contents_fault(
177 DecodingKind::InvalidEncoding,
178 "Real expects exactly 4 bytes, got {}",
179 data.len() as u64,
180 ));
181 }
182 Ok(f32::from_be_bytes([data[0], data[1], data[2], data[3]]))
183}
184
185pub fn decode_double(data: &[u8]) -> Result<f64, Error> {
189 if data.len() != 8 {
190 return Err(contents_fault(
191 DecodingKind::InvalidEncoding,
192 "Double expects exactly 8 bytes, got {}",
193 data.len() as u64,
194 ));
195 }
196 let bytes: [u8; 8] = [
197 data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
198 ];
199 Ok(f64::from_be_bytes(bytes))
200}
201
202pub mod charset {
206 pub const UTF8: u8 = 0;
208 pub const IBM_MICROSOFT_DBCS: u8 = 1;
210 pub const JIS_X_0208: u8 = 2;
212 pub const UCS4: u8 = 3;
214 pub const UCS2: u8 = 4;
216 pub const ISO_8859_1: u8 = 5;
218}
219
220#[cfg(feature = "alloc")]
223pub fn encode_character_string(buf: &mut BytesMut, value: &str) {
224 encode_character_string_to_sink(buf, value).expect("the BytesMut sink cannot return SinkFull");
225}
226
227#[cfg(feature = "alloc")]
231pub fn character_string_len(value: &str) -> Result<u32, Error> {
232 u32::try_from(value.len())
233 .ok()
234 .and_then(|n| n.checked_add(1))
235 .ok_or_else(character_string_too_long)
236}
237
238#[cfg(feature = "alloc")]
240#[cold]
241fn character_string_too_long() -> Error {
242 Error::encode("CharacterString too long for BACnet encoding")
243}
244
245#[cfg(feature = "alloc")]
256pub fn decode_character_string(data: &[u8]) -> Result<String, Error> {
257 if let Some(
259 &unsupported @ (charset::IBM_MICROSOFT_DBCS | charset::JIS_X_0208 | charset::UCS4),
260 ) = data.first()
261 {
262 return Err(contents_fault(
263 DecodingKind::Unsupported,
264 "unsupported charset: {}",
265 unsupported.into(),
266 ));
267 }
268 view::check_character_string(data)?;
271 let payload = &data[1..];
272 Ok(match data[0] {
273 charset::UCS2 => payload
274 .as_chunks::<2>()
275 .0
276 .iter()
277 .filter_map(|c| char::from_u32(u16::from_be_bytes(*c).into()))
278 .collect(),
279 charset::ISO_8859_1 => payload.iter().map(|&octet| char::from(octet)).collect(),
280 _ => core::str::from_utf8(payload).unwrap_or_default().into(),
281 })
282}
283
284#[cold]
288fn invalid_utf8(error: core::str::Utf8Error) -> Error {
289 DecodeError::new(
290 DecodingKind::InvalidEncoding,
291 1,
292 "invalid UTF-8 after {} valid octets",
293 )
294 .with_value(error.valid_up_to() as u64)
295 .into()
296}
297
298#[cold]
300fn unknown_charset(charset: u8) -> Error {
301 contents_fault(
302 DecodingKind::InvalidEncoding,
303 "unknown charset: {}",
304 charset.into(),
305 )
306}
307
308#[cfg(feature = "alloc")]
313pub fn encode_bit_string(buf: &mut BytesMut, unused_bits: u8, data: &[u8]) {
314 encode_bit_string_to_sink(buf, unused_bits, data)
315 .expect("the BytesMut sink cannot return SinkFull");
316}
317
318#[cfg(feature = "alloc")]
323pub fn decode_bit_string(data: &[u8]) -> Result<(u8, Vec<u8>), Error> {
324 let (unused, bits) = decode_bit_string_ref(data)?;
325 Ok((unused, bits.to_vec()))
326}
327
328pub fn decode_bit_string_ref(data: &[u8]) -> Result<(u8, &[u8]), Error> {
332 let Some((&unused, bits)) = data.split_first() else {
333 return Err(Error::decoding(
334 0,
335 "BitString requires at least 1 byte for unused-bits count",
336 ));
337 };
338 if unused > 7 {
339 return Err(contents_fault(
340 DecodingKind::InvalidEncoding,
341 "BitString unused_bits must be 0-7, got {}",
342 unused.into(),
343 ));
344 }
345 Ok((unused, bits))
346}
347
348#[cfg(feature = "alloc")]
356pub fn encode_app_octet_string(buf: &mut BytesMut, data: &[u8]) {
357 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
358 tags::encode_tag(buf, app_tag::OCTET_STRING, TagClass::Application, data_len);
359 buf.put_slice(data);
360}
361
362#[cfg(feature = "alloc")]
366pub fn encode_app_character_string(buf: &mut BytesMut, value: &str) -> Result<(), Error> {
367 encode_app_character_string_to_sink(buf, value).map_err(|_| character_string_too_long())?;
369 Ok(())
370}
371
372#[cfg(feature = "alloc")]
376pub fn encode_app_bit_string(buf: &mut BytesMut, unused_bits: u8, data: &[u8]) {
377 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
378 let len = data_len.saturating_add(1);
379 tags::encode_tag(buf, app_tag::BIT_STRING, TagClass::Application, len);
380 encode_bit_string(buf, unused_bits, data);
381}
382
383#[cfg(feature = "alloc")]
391pub fn encode_ctx_octet_string(buf: &mut BytesMut, tag: u8, data: &[u8]) {
392 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
393 tags::encode_tag(buf, tag, TagClass::Context, data_len);
394 buf.put_slice(data);
395}
396
397#[cfg(feature = "alloc")]
401pub fn encode_ctx_character_string(buf: &mut BytesMut, tag: u8, value: &str) -> Result<(), Error> {
402 encode_ctx_character_string_to_sink(buf, tag, value)
404 .map_err(|_| character_string_too_long())?;
405 Ok(())
406}
407
408#[cfg(feature = "alloc")]
412pub fn encode_ctx_bit_string(buf: &mut BytesMut, tag: u8, unused_bits: u8, data: &[u8]) {
413 let data_len = u32::try_from(data.len()).unwrap_or(u32::MAX);
414 let len = data_len.saturating_add(1);
415 tags::encode_tag(buf, tag, TagClass::Context, len);
416 encode_bit_string(buf, unused_bits, data);
417}
418
419pub(crate) fn fixed_length_label(number: u8) -> Option<(&'static str, u32)> {
426 Some(match number {
427 app_tag::REAL => ("application REAL has {} contents octets, expected 4", 4),
428 app_tag::DOUBLE => ("application Double has {} contents octets, expected 8", 8),
429 app_tag::DATE => ("application Date has {} contents octets, expected 4", 4),
430 app_tag::TIME => ("application Time has {} contents octets, expected 4", 4),
431 app_tag::OBJECT_IDENTIFIER => (
432 "application BACnetObjectIdentifier has {} contents octets, expected 4",
433 4,
434 ),
435 _ => return None,
436 })
437}
438
439#[cfg(feature = "alloc")]
459pub fn decode_application_value(
460 data: &[u8],
461 offset: usize,
462) -> Result<(PropertyValue, usize), Error> {
463 let (tag, new_offset) = tags::decode_tag(data, offset)?;
464 if tag.class == TagClass::Context {
465 if tag.is_opening {
466 let (_content, after) = tags::extract_context_value(data, new_offset, tag.number)?;
467 return Ok((
468 PropertyValue::ApplicationData(data[offset..after].to_vec()),
469 after,
470 ));
471 }
472 if tag.is_closing {
473 return Err(Error::invalid_tag(offset, "unexpected closing tag"));
474 }
475 let content_end = new_offset
476 .checked_add(tag.length as usize)
477 .ok_or_else(|| Error::decoding(new_offset, "length overflow"))?;
478 if data.len() < content_end {
479 return Err(Error::buffer_too_short(content_end, data.len()));
480 }
481 return Ok((
482 PropertyValue::ApplicationData(data[offset..content_end].to_vec()),
483 content_end,
484 ));
485 }
486 let (value, end) = view::decode_application_primitive::<false>(data, offset, tag, new_offset)?;
487 let value = match value {
488 ApplicationValueRef::Null => PropertyValue::Null,
489 ApplicationValueRef::Boolean(value) => PropertyValue::Boolean(value),
490 ApplicationValueRef::Unsigned(value) => PropertyValue::Unsigned(value),
491 ApplicationValueRef::Signed(value) => PropertyValue::Signed(value),
492 ApplicationValueRef::Real(value) => PropertyValue::Real(value),
493 ApplicationValueRef::Double(value) => PropertyValue::Double(value),
494 ApplicationValueRef::OctetString(octets) => PropertyValue::OctetString(octets.to_vec()),
495 ApplicationValueRef::CharacterString(text) => {
496 PropertyValue::CharacterString(decode_character_string(text.contents())?)
497 }
498 ApplicationValueRef::BitString { unused_bits, data } => PropertyValue::BitString {
499 unused_bits,
500 data: data.to_vec(),
501 },
502 ApplicationValueRef::Enumerated(value) => PropertyValue::Enumerated(value),
503 ApplicationValueRef::Date(value) => PropertyValue::Date(value),
504 ApplicationValueRef::Time(value) => PropertyValue::Time(value),
505 ApplicationValueRef::ObjectIdentifier(value) => PropertyValue::ObjectIdentifier(value),
506 };
507 Ok((value, end))
508}
509
510#[cfg(feature = "alloc")]
518pub fn validate_application_value(data: &[u8], offset: usize) -> Result<usize, Error> {
519 let (tag, content_start) = tags::decode_tag(data, offset)?;
520 if tag.class != TagClass::Application || tag.is_opening || tag.is_closing {
521 return Err(misplaced_tag(
522 data,
523 &tag,
524 None,
525 offset,
526 "expected an application-tagged value",
527 ));
528 }
529 if tag.number != app_tag::CHARACTER_STRING && tag.number != app_tag::SIGNED {
530 return decode_application_value(data, offset).map(|(_, end)| end);
531 }
532
533 let content_end = content_start
534 .checked_add(tag.length as usize)
535 .ok_or_else(|| Error::decoding(content_start, "length overflow"))?;
536 if content_end > data.len() {
537 return Err(Error::buffer_too_short(content_end, data.len()));
538 }
539 let content = &data[content_start..content_end];
540 if tag.number == app_tag::SIGNED {
541 decode_signed_canonical(content)?;
542 return Ok(content_end);
543 }
544 view::check_character_string(content).map_err(view::rebased(content_start))?;
545 Ok(content_end)
546}
547
548#[cfg(feature = "alloc")]
551pub fn encode_property_value(buf: &mut BytesMut, value: &PropertyValue) -> Result<(), Error> {
552 match value {
553 PropertyValue::Null => encode_app_null(buf),
554 PropertyValue::Boolean(v) => encode_app_boolean(buf, *v),
555 PropertyValue::Unsigned(v) => encode_app_unsigned(buf, *v),
556 PropertyValue::Signed(v) => encode_app_signed(buf, *v),
557 PropertyValue::Real(v) => encode_app_real(buf, *v),
558 PropertyValue::Double(v) => encode_app_double(buf, *v),
559 PropertyValue::OctetString(v) => encode_app_octet_string(buf, v),
560 PropertyValue::CharacterString(v) => encode_app_character_string(buf, v)?,
561 PropertyValue::BitString { unused_bits, data } => {
562 encode_app_bit_string(buf, *unused_bits, data)
563 }
564 PropertyValue::Enumerated(v) => encode_app_enumerated(buf, *v),
565 PropertyValue::Date(v) => encode_app_date(buf, v),
566 PropertyValue::Time(v) => encode_app_time(buf, v),
567 PropertyValue::ObjectIdentifier(v) => encode_app_object_id(buf, v),
568 PropertyValue::List(values) => {
569 for v in values {
570 encode_property_value(buf, v)?;
571 }
572 }
573 PropertyValue::ApplicationData(bytes) => {
574 buf.put_slice(bytes);
577 }
578 }
579 Ok(())
580}
581
582pub const MAX_TIMESTAMP_SEQUENCE_NUMBER: u64 = 65535;
593
594#[cfg(feature = "alloc")]
612pub fn encode_timestamp_choice(buf: &mut BytesMut, ts: &BACnetTimeStamp) -> Result<(), Error> {
613 encode_timestamp_choice_to_sink(buf, ts).expect("the BytesMut sink cannot return SinkFull");
614 Ok(())
615}
616
617pub fn decode_timestamp_choice(
627 data: &[u8],
628 offset: usize,
629) -> Result<(BACnetTimeStamp, usize), Error> {
630 use crate::constructed::tagged;
631 const WHAT: &str = "BACnetTimeStamp";
632
633 let (inner_tag, inner_pos) = tags::decode_tag(data, offset)?;
634 if inner_tag.is_context(0) {
635 let (octets, end) = tagged::decode_ctx_fixed(data, offset, 0, tagged::Fixed::TIME, WHAT)?;
636 return Ok((BACnetTimeStamp::Time(Time::decode(octets)?), end));
637 }
638 if inner_tag.is_context(1) {
639 let (octets, end) = tagged::decode_ctx_primitive(data, offset, 1, WHAT)?;
640 let n = decode_unsigned(octets)?;
641 if n > MAX_TIMESTAMP_SEQUENCE_NUMBER {
642 return Err(DecodeError::new(
643 DecodingKind::OutOfRange,
644 inner_pos,
645 "BACnetTimeStamp sequence-number {} exceeds 65535",
646 )
647 .with_value(n)
648 .into());
649 }
650 let n = u16::try_from(n).expect("BACnetTimeStamp sequence range checked above");
651 return Ok((BACnetTimeStamp::SequenceNumber(n), end));
652 }
653 if inner_tag.is_opening_tag(2) {
654 const DATE_TIME: &str = "BACnetTimeStamp DateTime";
655 let (date, date_end) =
656 tagged::decode_app_fixed(data, inner_pos, app_tag::DATE, 4, DATE_TIME)?;
657 let (time, time_end) =
658 tagged::decode_app_fixed(data, date_end, app_tag::TIME, 4, DATE_TIME)?;
659 let end = tagged::expect_closing(data, time_end, 2, DATE_TIME)?;
660 let date_time = BACnetTimeStamp::DateTime {
661 date: Date::decode(date)?,
662 time: Time::decode(time)?,
663 };
664 return Ok((date_time, end));
665 }
666 Err(DecodeError::new(
667 misplaced_kind(data, offset, &inner_tag, None),
668 offset,
669 "BACnetTimeStamp: unexpected inner choice tag",
670 )
671 .into())
672}
673
674#[cfg(feature = "alloc")]
683pub fn encode_timestamp(
684 buf: &mut BytesMut,
685 tag_number: u8,
686 ts: &BACnetTimeStamp,
687) -> Result<(), Error> {
688 encode_timestamp_to_sink(buf, tag_number, ts)
689 .expect("the BytesMut sink cannot return SinkFull");
690 Ok(())
691}
692
693pub fn decode_timestamp(
698 data: &[u8],
699 offset: usize,
700 tag_number: u8,
701) -> Result<(BACnetTimeStamp, usize), Error> {
702 let (tag, pos) = tags::decode_tag(data, offset)?;
703 if !tag.is_opening_tag(tag_number) {
704 return Err(DecodeError::new(
705 misplaced_kind(data, offset, &tag, Some(tag_number)),
706 offset,
707 "expected opening tag {} for BACnetTimeStamp",
708 )
709 .with_value(tag_number.into())
710 .into());
711 }
712
713 let (ts, after_inner) = decode_timestamp_choice(data, pos)?;
714
715 let (close, final_pos) = tags::decode_tag(data, after_inner)?;
716 if !close.is_closing_tag(tag_number) {
717 return Err(DecodeError::new(
718 unclosed_kind(&close),
719 after_inner,
720 "expected closing tag {} for BACnetTimeStamp",
721 )
722 .with_value(tag_number.into())
723 .into());
724 }
725
726 Ok((ts, final_pos))
727}
728
729#[cfg(all(test, feature = "alloc"))]
734mod tests;
735
736#[cfg(all(test, feature = "alloc"))]
737mod length_first_tests;