use bacnet_types::error::Error;
use super::tagged::decode_ctx_primitive;
pub(super) fn unsigned_member(content: &[u8], at: usize) -> Result<Option<u64>, Error> {
if content.is_empty() {
return Err(Error::decoding(at, "Unsigned member has no content octets"));
}
let first = content.iter().position(|&octet| octet != 0);
let significant = first.map_or(&[][..], |first| &content[first..]);
if significant.len() > 8 {
return Ok(None);
}
Ok(Some(
significant
.iter()
.fold(0, |value, &octet| (value << 8) | u64::from(octet)),
))
}
pub(super) fn narrow<T: TryFrom<u64> + Default>(
value: Option<u64>,
what: &'static str,
oversized: &mut Option<&'static str>,
) -> T {
match value.map(T::try_from) {
Some(Ok(value)) => value,
_ => {
oversized.get_or_insert(what);
T::default()
}
}
}
const MAX_PADDED_OCTETS: usize = 4;
pub(super) fn unsigned_field<T: TryFrom<u64> + Default>(
data: &[u8],
offset: usize,
tag: u8,
what: &str,
too_wide: &'static str,
oversized: &mut Option<&'static str>,
) -> Result<(T, usize), Error> {
let (content, end) = decode_ctx_primitive(data, offset, tag, what)?;
if content.len() > MAX_PADDED_OCTETS && content[0] == 0 {
return Err(Error::decoding(
offset,
format!(
"{what}: [{tag}] has {} contents octets and a leading zero",
content.len()
),
));
}
let value = unsigned_member(content, end - content.len())?;
Ok((narrow(value, too_wide, oversized), end))
}