#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use super::tagged::{contents, decode_ctx_constructed, expect_end};
use super::validate_tlv_sequence;
use crate::{primitives, tags};
use bacnet_types::bitstring::LogStatus;
use bacnet_types::enums::{ErrorClass, ErrorCode};
use bacnet_types::error::{DecodeError, DecodingKind, Error, RangeError};
use bacnet_types::primitives::{Date, Time};
use bytes::BytesMut;
pub(super) fn encode_timestamp(buf: &mut BytesMut, date: &Date, time: &Time) {
tags::encode_opening_tag(buf, 0);
primitives::encode_app_date(buf, date);
primitives::encode_app_time(buf, time);
tags::encode_closing_tag(buf, 0);
}
pub(super) fn decode_timestamp(
data: &[u8],
offset: usize,
what: &'static str,
) -> Result<(Date, Time, usize), Error> {
let (body, end) = decode_ctx_constructed(data, offset, 0, what)?;
let (date, next) = application(body, 0, tags::app_tag::DATE, 4, offset, what)?;
let (time, last) = application(body, next, tags::app_tag::TIME, 4, offset, what)?;
expect_end(body, last, offset, what)?;
Ok((Date::decode(date)?, Time::decode(time)?, end))
}
pub(super) fn encode_log_status(buf: &mut BytesMut, tag: u8, status: LogStatus) {
primitives::encode_ctx_bit_string(buf, tag, 5, &[status.to_bacnet()]);
}
pub(super) fn decode_log_status(
contents: &[u8],
offset: usize,
record: &'static str,
) -> Result<LogStatus, Error> {
match contents {
[5, bits] if bits & 0x1f == 0 => Ok(LogStatus::from_bacnet(&[*bits])),
_ => Err(DecodeError::new(
DecodingKind::InvalidEncoding,
offset,
"log-status must be a canonical three-bit BitString",
)
.within(record)
.into()),
}
}
pub(super) fn encode_ctx_integer(buf: &mut BytesMut, tag: u8, value: i64) {
let octets = value.to_be_bytes();
let start = (0..7)
.find(|&i| {
let redundant = (octets[i] == 0x00 && octets[i + 1] & 0x80 == 0)
|| (octets[i] == 0xFF && octets[i + 1] & 0x80 != 0);
!redundant
})
.unwrap_or(7);
tags::encode_tag(buf, tag, tags::TagClass::Context, (8 - start) as u32);
buf.extend_from_slice(&octets[start..]);
}
pub(super) fn decode_integer(contents: &[u8], offset: usize) -> Result<i64, Error> {
if contents.is_empty() || contents.len() > 8 {
return Err(DecodeError::new(
DecodingKind::InvalidEncoding,
offset,
"integer-value needs 1 to 8 octets, got {}",
)
.with_value(contents.len() as u64)
.into());
}
let fill = if contents[0] & 0x80 != 0 { 0xFF } else { 0x00 };
let mut octets = [fill; 8];
octets[8 - contents.len()..].copy_from_slice(contents);
Ok(i64::from_be_bytes(octets))
}
pub(super) fn encode_failure(
buf: &mut BytesMut,
tag: u8,
error_class: ErrorClass,
error_code: ErrorCode,
) {
tags::encode_opening_tag(buf, tag);
primitives::encode_app_enumerated(buf, error_class.to_raw().into());
primitives::encode_app_enumerated(buf, error_code.to_raw().into());
tags::encode_closing_tag(buf, tag);
}
pub(super) fn decode_failure(
body: &[u8],
offset: usize,
what: &'static str,
) -> Result<(ErrorClass, ErrorCode), Error> {
let (class, next) = application(body, 0, tags::app_tag::ENUMERATED, 0, offset, what)?;
let (code, last) = application(body, next, tags::app_tag::ENUMERATED, 0, offset, what)?;
expect_end(body, last, offset, what)?;
let sixteen = |raw: &[u8], message: &'static str| -> Result<u16, Error> {
let value = primitives::decode_unsigned_u32(raw)?;
u16::try_from(value).map_err(|_| {
DecodeError::new(DecodingKind::OutOfRange, offset, message)
.within(what)
.with_value(value.into())
.into()
})
};
Ok((
ErrorClass::from_raw(sixteen(class, "error class {} exceeds u16")?),
ErrorCode::from_raw(sixteen(code, "error code {} exceeds u16")?),
))
}
pub(super) fn check_bit_string(
unused_bits: u8,
data: &[u8],
label: &'static str,
) -> Result<(), Error> {
if unused_bits > 7 || (data.is_empty() && unused_bits != 0) {
return Err(RangeError::new(label)
.with_values(unused_bits.into(), data.len() as u64)
.into());
}
Ok(())
}
pub(super) fn encode_any_value(buf: &mut BytesMut, tag: u8, bytes: &[u8]) -> Result<(), Error> {
validate_tlv_sequence(bytes, "any-value")
.map_err(Error::into_encode_refusal)?;
tags::encode_opening_tag(buf, tag);
buf.extend_from_slice(bytes);
tags::encode_closing_tag(buf, tag);
Ok(())
}
pub(super) fn decode_any_value(body: &[u8]) -> Result<Vec<u8>, Error> {
validate_tlv_sequence(body, "any-value")?;
Ok(body.to_vec())
}
fn application<'a>(
data: &'a [u8],
pos: usize,
number: u8,
length: u32,
offset: usize,
what: &'static str,
) -> Result<(&'a [u8], usize), Error> {
let (tag, start) = tags::decode_tag(data, pos)?;
if tag.class != tags::TagClass::Application
|| tag.number != number
|| (length != 0 && tag.length != length)
{
return Err(DecodeError::new(
DecodingKind::InvalidEncoding,
offset,
"expected application tag {}",
)
.within(what)
.with_value(number.into())
.into());
}
contents(data, start, tag.length)
}