#[cfg(feature = "std")]
use std::error::Error;
#[cfg(not(feature = "std"))]
use core::fmt;
#[cfg(feature = "std")]
use std::fmt;
#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
use crate::object::ObjectIdentifier;
#[cfg(feature = "std")]
pub type Result<T> = std::result::Result<T, EncodingError>;
#[cfg(not(feature = "std"))]
pub type Result<T> = core::result::Result<T, EncodingError>;
#[derive(Debug, Clone)]
pub enum EncodingError {
BufferOverflow,
BufferUnderflow,
InvalidTag,
InvalidLength,
UnexpectedEndOfData,
InvalidFormat(String),
ValueOutOfRange,
}
impl fmt::Display for EncodingError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
EncodingError::BufferOverflow => write!(f, "Buffer overflow during encoding"),
EncodingError::BufferUnderflow => write!(f, "Buffer underflow during decoding"),
EncodingError::InvalidTag => write!(f, "Invalid tag number encountered"),
EncodingError::InvalidLength => write!(f, "Invalid length value"),
EncodingError::UnexpectedEndOfData => write!(f, "Unexpected end of data"),
EncodingError::InvalidFormat(msg) => write!(f, "Invalid format: {}", msg),
EncodingError::ValueOutOfRange => write!(f, "Value out of valid range"),
}
}
}
#[cfg(feature = "std")]
impl Error for EncodingError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum ApplicationTag {
Null = 0,
Boolean = 1,
UnsignedInt = 2,
SignedInt = 3,
Real = 4,
Double = 5,
OctetString = 6,
CharacterString = 7,
BitString = 8,
Enumerated = 9,
Date = 10,
Time = 11,
ObjectIdentifier = 12,
Reserved13 = 13,
Reserved14 = 14,
Reserved15 = 15,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BACnetTag {
Application(ApplicationTag),
Context(u8),
}
pub fn decode_tag(data: &[u8]) -> Result<(BACnetTag, usize, usize)> {
if data.is_empty() {
return Err(EncodingError::InvalidTag);
}
let tag_byte = data[0];
let tag_type = tag_byte & 0x08;
let mut length = (tag_byte & 0x07) as usize;
let mut consumed = 1;
let tag = if tag_type == 0 {
BACnetTag::Application(ApplicationTag::try_from(tag_byte >> 4)?)
} else {
BACnetTag::Context(tag_byte >> 4)
};
if length == 5 {
if data.len() < 2 {
return Err(EncodingError::BufferUnderflow);
}
let len_byte = data[1];
consumed += 1;
match len_byte {
0..=253 => {
length = len_byte as usize;
}
254 => {
if data.len() < 4 {
return Err(EncodingError::BufferUnderflow);
}
length = u16::from_be_bytes([data[2], data[3]]) as usize;
consumed += 2;
}
255 => {
if data.len() < 6 {
return Err(EncodingError::BufferUnderflow);
}
length = u32::from_be_bytes([data[2], data[3], data[4], data[5]]) as usize;
consumed += 4;
}
}
}
Ok((tag, length, consumed))
}
pub fn encode_application_tag(buffer: &mut Vec<u8>, tag: ApplicationTag, length: usize) {
let tag_byte = if length < 5 {
(tag as u8) << 4 | (length as u8)
} else {
(tag as u8) << 4 | 5
};
buffer.push(tag_byte);
if length >= 5 {
if length < 254 {
buffer.push(length as u8);
} else if length < 65536 {
buffer.push(254);
buffer.extend_from_slice(&(length as u16).to_be_bytes());
} else {
buffer.push(255);
buffer.extend_from_slice(&(length as u32).to_be_bytes());
}
}
}
pub fn decode_application_tag(data: &[u8]) -> Result<(ApplicationTag, usize, usize)> {
if data.is_empty() {
return Err(EncodingError::InvalidTag);
}
let tag_byte = data[0];
let tag = ApplicationTag::try_from(tag_byte >> 4)?;
let mut length = (tag_byte & 0x0F) as usize;
let mut consumed = 1;
if length == 5 {
if data.len() < 2 {
return Err(EncodingError::BufferUnderflow);
}
let len_byte = data[1];
consumed += 1;
match len_byte {
0..=253 => {
length = len_byte as usize;
}
254 => {
if data.len() < 4 {
return Err(EncodingError::BufferUnderflow);
}
length = u16::from_be_bytes([data[2], data[3]]) as usize;
consumed += 2;
}
255 => {
if data.len() < 6 {
return Err(EncodingError::BufferUnderflow);
}
length = u32::from_be_bytes([data[2], data[3], data[4], data[5]]) as usize;
consumed += 4;
}
}
}
Ok((tag, length, consumed))
}
pub fn encode_boolean(buffer: &mut Vec<u8>, value: bool) -> Result<()> {
encode_application_tag(buffer, ApplicationTag::Boolean, if value { 1 } else { 0 });
Ok(())
}
pub fn decode_boolean(data: &[u8]) -> Result<(bool, usize)> {
let (tag, length, consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::Boolean {
return Err(EncodingError::InvalidTag);
}
let value = match length {
0 => false,
1 => true,
_ => return Err(EncodingError::InvalidLength),
};
Ok((value, consumed))
}
pub fn encode_unsigned(buffer: &mut Vec<u8>, value: u32) -> Result<()> {
let bytes = if value == 0 {
vec![0]
} else if value <= 0xFF {
vec![value as u8]
} else if value <= 0xFFFF {
(value as u16).to_be_bytes().to_vec()
} else if value <= 0xFFFFFF {
let bytes = value.to_be_bytes();
bytes[1..].to_vec()
} else {
value.to_be_bytes().to_vec()
};
encode_application_tag(buffer, ApplicationTag::UnsignedInt, bytes.len());
buffer.extend_from_slice(&bytes);
Ok(())
}
pub fn encode_unsigned64(buffer: &mut Vec<u8>, value: u64) {
let bytes = if value == 0 {
vec![0]
} else if value <= 0xFF {
vec![value as u8]
} else if value <= 0xFFFF {
(value as u16).to_be_bytes().to_vec()
} else if value <= 0xFFFFFF {
let bytes = value.to_be_bytes();
bytes[1..].to_vec()
} else if value <= 0xFFFFFFFF {
(value as u32).to_be_bytes().to_vec()
} else {
value.to_be_bytes().to_vec()
};
encode_application_tag(buffer, ApplicationTag::UnsignedInt, bytes.len());
buffer.extend_from_slice(&bytes);
}
pub fn decode_unsigned(data: &[u8]) -> Result<(u32, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::UnsignedInt {
return Err(EncodingError::InvalidTag);
}
if data.len() < consumed + length {
return Err(EncodingError::BufferUnderflow);
}
let value = match length {
1 => data[consumed] as u32,
2 => u16::from_be_bytes([data[consumed], data[consumed + 1]]) as u32,
3 => {
let bytes = [0, data[consumed], data[consumed + 1], data[consumed + 2]];
u32::from_be_bytes(bytes)
}
4 => u32::from_be_bytes([
data[consumed],
data[consumed + 1],
data[consumed + 2],
data[consumed + 3],
]),
_ => return Err(EncodingError::InvalidLength),
};
consumed += length;
Ok((value, consumed))
}
pub fn decode_unsigned64(data: &[u8]) -> Result<(u64, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::UnsignedInt {
return Err(EncodingError::InvalidTag);
}
if data.len() < consumed + length {
return Err(EncodingError::BufferUnderflow);
}
let unused = 8 - length;
let mut value = [0; 8];
value[unused..].copy_from_slice(&data[consumed..consumed + length]);
let value = u64::from_be_bytes(value);
consumed += length;
Ok((value, consumed))
}
pub fn encode_signed(buffer: &mut Vec<u8>, value: i32) -> Result<()> {
let bytes = if (-128..=127).contains(&value) {
vec![value as u8]
} else if (-32768..=32767).contains(&value) {
(value as i16).to_be_bytes().to_vec()
} else if (-8388608..=8388607).contains(&value) {
let bytes = value.to_be_bytes();
bytes[1..].to_vec()
} else {
value.to_be_bytes().to_vec()
};
encode_application_tag(buffer, ApplicationTag::SignedInt, bytes.len());
buffer.extend_from_slice(&bytes);
Ok(())
}
pub fn encode_signed64(buffer: &mut Vec<u8>, value: i64) {
let bytes = if (-128..=127).contains(&value) {
vec![value as u8]
} else if (-32768..=32767).contains(&value) {
(value as i16).to_be_bytes().to_vec()
} else if (-8388608..=8388607).contains(&value) {
let bytes = value.to_be_bytes();
bytes[1..].to_vec()
} else if (i32::MIN as i64..=i32::MAX as i64).contains(&value) {
(value as i32).to_be_bytes().to_vec()
} else {
value.to_be_bytes().to_vec()
};
encode_application_tag(buffer, ApplicationTag::SignedInt, bytes.len());
buffer.extend_from_slice(&bytes);
}
pub fn decode_signed(data: &[u8]) -> Result<(i32, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::SignedInt {
return Err(EncodingError::InvalidTag);
}
if data.len() < consumed + length {
return Err(EncodingError::BufferUnderflow);
}
let value = match length {
1 => data[consumed] as i8 as i32,
2 => i16::from_be_bytes([data[consumed], data[consumed + 1]]) as i32,
3 => {
let sign_extend = if data[consumed] & 0x80 != 0 {
0xFF
} else {
0x00
};
let bytes = [
sign_extend,
data[consumed],
data[consumed + 1],
data[consumed + 2],
];
i32::from_be_bytes(bytes)
}
4 => i32::from_be_bytes([
data[consumed],
data[consumed + 1],
data[consumed + 2],
data[consumed + 3],
]),
_ => return Err(EncodingError::InvalidLength),
};
consumed += length;
Ok((value, consumed))
}
pub fn decode_signed64(data: &[u8]) -> Result<(i64, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::SignedInt {
return Err(EncodingError::InvalidTag);
}
if data.len() < consumed + length {
return Err(EncodingError::BufferUnderflow);
}
let unused = 8 - length;
let mut value = [0; 8];
value[unused..].copy_from_slice(&data[consumed..consumed + length]);
let value = match length {
1 => data[consumed] as i8 as i64,
2 => i16::from_be_bytes([data[consumed], data[consumed + 1]]) as i64,
v if (3..8).contains(&v) => {
let sign_extend = if data[consumed] & 0x80 != 0 {
0xFF
} else {
0x00
};
let mut value = [sign_extend; 8];
value[unused..].copy_from_slice(&data[consumed..consumed + length]);
i64::from_be_bytes(value)
}
8 => {
let mut value = [0; 8];
value.copy_from_slice(&data[consumed..consumed + length]);
i64::from_be_bytes(value)
}
_ => return Err(EncodingError::InvalidLength),
};
consumed += length;
Ok((value, consumed))
}
pub fn encode_real(buffer: &mut Vec<u8>, value: f32) -> Result<()> {
encode_application_tag(buffer, ApplicationTag::Real, 4);
buffer.extend_from_slice(&value.to_be_bytes());
Ok(())
}
pub fn decode_real(data: &[u8]) -> Result<(f32, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::Real {
return Err(EncodingError::InvalidTag);
}
if length != 4 {
return Err(EncodingError::InvalidLength);
}
if data.len() < consumed + 4 {
return Err(EncodingError::BufferUnderflow);
}
let value = f32::from_be_bytes([
data[consumed],
data[consumed + 1],
data[consumed + 2],
data[consumed + 3],
]);
consumed += 4;
Ok((value, consumed))
}
pub fn encode_octet_string(buffer: &mut Vec<u8>, value: &[u8]) -> Result<()> {
encode_application_tag(buffer, ApplicationTag::OctetString, value.len());
buffer.extend_from_slice(value);
Ok(())
}
pub fn decode_octet_string(data: &[u8]) -> Result<(Vec<u8>, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::OctetString {
return Err(EncodingError::InvalidTag);
}
if data.len() < consumed + length {
return Err(EncodingError::BufferUnderflow);
}
let value = data[consumed..consumed + length].to_vec();
consumed += length;
Ok((value, consumed))
}
pub fn encode_character_string(buffer: &mut Vec<u8>, value: &str) -> Result<()> {
let string_bytes = value.as_bytes();
encode_application_tag(
buffer,
ApplicationTag::CharacterString,
string_bytes.len() + 1,
);
buffer.push(0); buffer.extend_from_slice(string_bytes);
Ok(())
}
pub fn decode_character_string(data: &[u8]) -> Result<(String, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::CharacterString {
return Err(EncodingError::InvalidTag);
}
if data.len() < consumed + length || length == 0 {
return Err(EncodingError::BufferUnderflow);
}
let _encoding = data[consumed];
consumed += 1;
let string_data = &data[consumed..consumed + length - 1];
let value = String::from_utf8(string_data.to_vec())
.map_err(|_| EncodingError::InvalidFormat("Invalid UTF-8 string".to_string()))?;
consumed += length - 1;
Ok((value, consumed))
}
pub fn encode_enumerated(buffer: &mut Vec<u8>, value: u32) {
let bytes = if value <= 0xFF {
vec![value as u8]
} else if value <= 0xFFFF {
(value as u16).to_be_bytes().to_vec()
} else if value <= 0xFFFFFF {
let bytes = value.to_be_bytes();
bytes[1..].to_vec()
} else {
value.to_be_bytes().to_vec()
};
encode_application_tag(buffer, ApplicationTag::Enumerated, bytes.len());
buffer.extend_from_slice(&bytes);
}
pub fn decode_enumerated(data: &[u8]) -> Result<(u32, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::Enumerated {
return Err(EncodingError::InvalidTag);
}
if data.len() < consumed + length {
return Err(EncodingError::BufferUnderflow);
}
let value = match length {
1 => data[consumed] as u32,
2 => u16::from_be_bytes([data[consumed], data[consumed + 1]]) as u32,
3 => {
let bytes = [0, data[consumed], data[consumed + 1], data[consumed + 2]];
u32::from_be_bytes(bytes)
}
4 => u32::from_be_bytes([
data[consumed],
data[consumed + 1],
data[consumed + 2],
data[consumed + 3],
]),
_ => return Err(EncodingError::InvalidLength),
};
consumed += length;
Ok((value, consumed))
}
pub fn encode_date(buffer: &mut Vec<u8>, year: u16, month: u8, day: u8, weekday: u8) -> Result<()> {
encode_application_tag(buffer, ApplicationTag::Date, 4);
buffer.push(((year - 1900) % 256) as u8);
buffer.push(month);
buffer.push(day);
buffer.push(weekday);
Ok(())
}
pub fn decode_date(data: &[u8]) -> Result<((u16, u8, u8, u8), usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::Date {
return Err(EncodingError::InvalidTag);
}
if length != 4 || data.len() < consumed + 4 {
return Err(EncodingError::InvalidLength);
}
let year = if data[consumed] == 255 {
255
} else {
1900 + data[consumed] as u16
};
let month = data[consumed + 1];
let day = data[consumed + 2];
let weekday = data[consumed + 3];
consumed += 4;
Ok(((year, month, day, weekday), consumed))
}
pub fn encode_time(
buffer: &mut Vec<u8>,
hour: u8,
minute: u8,
second: u8,
hundredths: u8,
) -> Result<()> {
encode_application_tag(buffer, ApplicationTag::Time, 4);
buffer.push(hour);
buffer.push(minute);
buffer.push(second);
buffer.push(hundredths);
Ok(())
}
pub fn decode_time(data: &[u8]) -> Result<((u8, u8, u8, u8), usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::Time {
return Err(EncodingError::InvalidTag);
}
if length != 4 || data.len() < consumed + 4 {
return Err(EncodingError::InvalidLength);
}
let hour = data[consumed];
let minute = data[consumed + 1];
let second = data[consumed + 2];
let hundredths = data[consumed + 3];
consumed += 4;
Ok(((hour, minute, second, hundredths), consumed))
}
pub fn encode_object_identifier(buffer: &mut Vec<u8>, object_id: ObjectIdentifier) -> Result<()> {
let object_id: u32 = object_id
.try_into()
.map_err(|_| EncodingError::ValueOutOfRange)?;
encode_application_tag(buffer, ApplicationTag::ObjectIdentifier, 4);
buffer.extend_from_slice(&object_id.to_be_bytes());
Ok(())
}
pub fn decode_object_identifier(data: &[u8]) -> Result<(ObjectIdentifier, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::ObjectIdentifier {
return Err(EncodingError::InvalidTag);
}
if length != 4 || data.len() < consumed + 4 {
return Err(EncodingError::InvalidLength);
}
let object_id = u32::from_be_bytes([
data[consumed],
data[consumed + 1],
data[consumed + 2],
data[consumed + 3],
]);
let object_type = object_id >> 22;
let instance = object_id & 0x3FFFFF;
let object_id = ObjectIdentifier::new(object_type.into(), instance);
consumed += 4;
Ok((object_id, consumed))
}
pub fn encode_double(buffer: &mut Vec<u8>, value: f64) -> Result<()> {
encode_application_tag(buffer, ApplicationTag::Double, 8);
buffer.extend_from_slice(&value.to_be_bytes());
Ok(())
}
pub fn decode_double(data: &[u8]) -> Result<(f64, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::Double {
return Err(EncodingError::InvalidTag);
}
if length != 8 || data.len() < consumed + 8 {
return Err(EncodingError::InvalidLength);
}
let value = f64::from_be_bytes([
data[consumed],
data[consumed + 1],
data[consumed + 2],
data[consumed + 3],
data[consumed + 4],
data[consumed + 5],
data[consumed + 6],
data[consumed + 7],
]);
consumed += 8;
Ok((value, consumed))
}
pub fn encode_context_tag(buffer: &mut Vec<u8>, tag_number: u8, length: usize) -> Result<()> {
if tag_number > 14 {
return Err(EncodingError::ValueOutOfRange);
}
let tag_byte = if length < 5 {
0x08 | (tag_number << 4) | (length as u8)
} else {
0x08 | (tag_number << 4) | 5
};
buffer.push(tag_byte);
if length >= 5 {
if length < 254 {
buffer.push(length as u8);
} else if length < 65536 {
buffer.push(254);
buffer.extend_from_slice(&(length as u16).to_be_bytes());
} else {
buffer.push(255);
buffer.extend_from_slice(&(length as u32).to_be_bytes());
}
}
Ok(())
}
pub fn encode_context_unsigned(value: u32, tag_number: u8) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
let bytes = if value == 0 {
vec![0]
} else if value <= 0xFF {
vec![value as u8]
} else if value <= 0xFFFF {
(value as u16).to_be_bytes().to_vec()
} else if value <= 0xFFFFFF {
let bytes = value.to_be_bytes();
bytes[1..].to_vec()
} else {
value.to_be_bytes().to_vec()
};
encode_context_tag(&mut buffer, tag_number, bytes.len())?;
buffer.extend_from_slice(&bytes);
Ok(buffer)
}
pub fn decode_context_tag(data: &[u8]) -> Result<(u8, usize, usize)> {
if data.is_empty() {
return Err(EncodingError::InvalidTag);
}
let tag_byte = data[0];
if (tag_byte & 0x08) == 0 {
return Err(EncodingError::InvalidTag);
}
let tag_number = (tag_byte >> 4) & 0x0F;
let mut length = (tag_byte & 0x07) as usize;
let mut consumed = 1;
if length == 5 {
if data.len() < 2 {
return Err(EncodingError::BufferUnderflow);
}
let len_byte = data[1];
consumed += 1;
match len_byte {
0..=253 => {
length = len_byte as usize;
}
254 => {
if data.len() < 4 {
return Err(EncodingError::BufferUnderflow);
}
length = u16::from_be_bytes([data[2], data[3]]) as usize;
consumed += 2;
}
255 => {
if data.len() < 6 {
return Err(EncodingError::BufferUnderflow);
}
length = u32::from_be_bytes([data[2], data[3], data[4], data[5]]) as usize;
consumed += 4;
}
}
}
Ok((tag_number, length, consumed))
}
pub fn decode_context_unsigned(data: &[u8], expected_tag: u8) -> Result<(u32, usize)> {
let (tag_number, length, tag_consumed) = decode_context_tag(data)?;
if tag_number != expected_tag {
return Err(EncodingError::InvalidTag);
}
if data.len() < tag_consumed + length {
return Err(EncodingError::BufferUnderflow);
}
let value = match length {
0 => 0,
1 => data[tag_consumed] as u32,
2 => u16::from_be_bytes([data[tag_consumed], data[tag_consumed + 1]]) as u32,
3 => {
let bytes = [
0,
data[tag_consumed],
data[tag_consumed + 1],
data[tag_consumed + 2],
];
u32::from_be_bytes(bytes)
}
4 => u32::from_be_bytes([
data[tag_consumed],
data[tag_consumed + 1],
data[tag_consumed + 2],
data[tag_consumed + 3],
]),
_ => return Err(EncodingError::InvalidLength),
};
Ok((value, tag_consumed + length))
}
pub fn encode_context_enumerated(value: u32, tag_number: u8) -> Result<Vec<u8>> {
encode_context_unsigned(value, tag_number)
}
pub fn decode_context_enumerated(data: &[u8], expected_tag: u8) -> Result<(u32, usize)> {
decode_context_unsigned(data, expected_tag)
}
pub fn encode_context_object_id(object_id: ObjectIdentifier, tag_number: u8) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
let object_id: u32 = object_id.try_into()?;
encode_context_tag(&mut buffer, tag_number, 4)?;
buffer.extend_from_slice(&object_id.to_be_bytes());
Ok(buffer)
}
pub fn decode_context_object_id(
data: &[u8],
expected_tag: u8,
) -> Result<(ObjectIdentifier, usize)> {
let (tag_number, length, tag_consumed) = decode_context_tag(data)?;
if tag_number != expected_tag {
return Err(EncodingError::InvalidTag);
}
if length != 4 {
return Err(EncodingError::InvalidLength);
}
if data.len() < tag_consumed + 4 {
return Err(EncodingError::BufferUnderflow);
}
let object_id = u32::from_be_bytes([
data[tag_consumed],
data[tag_consumed + 1],
data[tag_consumed + 2],
data[tag_consumed + 3],
]);
Ok((object_id.into(), tag_consumed + 4))
}
impl TryFrom<u8> for ApplicationTag {
type Error = EncodingError;
fn try_from(value: u8) -> Result<Self> {
match value {
0 => Ok(ApplicationTag::Null),
1 => Ok(ApplicationTag::Boolean),
2 => Ok(ApplicationTag::UnsignedInt),
3 => Ok(ApplicationTag::SignedInt),
4 => Ok(ApplicationTag::Real),
5 => Ok(ApplicationTag::Double),
6 => Ok(ApplicationTag::OctetString),
7 => Ok(ApplicationTag::CharacterString),
8 => Ok(ApplicationTag::BitString),
9 => Ok(ApplicationTag::Enumerated),
10 => Ok(ApplicationTag::Date),
11 => Ok(ApplicationTag::Time),
12 => Ok(ApplicationTag::ObjectIdentifier),
_ => Err(EncodingError::InvalidTag),
}
}
}
pub mod advanced {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::{collections::BTreeMap, vec::Vec};
#[derive(Debug)]
pub struct BufferManager {
#[cfg(feature = "std")]
encode_buffers: Vec<Vec<u8>>,
#[cfg(not(feature = "std"))]
encode_buffers: alloc::vec::Vec<alloc::vec::Vec<u8>>,
max_buffer_size: usize,
pub stats: BufferStats,
}
#[derive(Debug, Default)]
pub struct BufferStats {
pub total_allocations: u64,
pub buffer_reuses: u64,
pub max_buffer_size_used: usize,
pub total_bytes_encoded: u64,
pub total_bytes_decoded: u64,
}
impl BufferManager {
pub fn new(max_buffer_size: usize) -> Self {
Self {
encode_buffers: Vec::with_capacity(8),
max_buffer_size,
stats: BufferStats::default(),
}
}
pub fn get_encode_buffer(&mut self) -> Vec<u8> {
if let Some(mut buffer) = self.encode_buffers.pop() {
buffer.clear();
self.stats.buffer_reuses += 1;
buffer
} else {
self.stats.total_allocations += 1;
Vec::with_capacity(256)
}
}
pub fn return_buffer(&mut self, buffer: Vec<u8>) {
self.stats.total_bytes_encoded += buffer.len() as u64;
if buffer.capacity() <= self.max_buffer_size && self.encode_buffers.len() < 16 {
self.encode_buffers.push(buffer);
}
}
pub fn update_decode_stats(&mut self, bytes_decoded: usize) {
self.stats.total_bytes_decoded += bytes_decoded as u64;
}
}
pub mod context {
use super::*;
pub fn encode_context_tag(
buffer: &mut Vec<u8>,
tag_number: u8,
length: usize,
) -> Result<()> {
if tag_number > 14 {
return Err(EncodingError::ValueOutOfRange);
}
let tag_byte = if length < 5 {
0x08 | (tag_number << 4) | (length as u8)
} else {
0x08 | (tag_number << 4) | 5
};
buffer.push(tag_byte);
if length >= 5 {
if length < 254 {
buffer.push(length as u8);
} else if length < 65536 {
buffer.push(254);
buffer.extend_from_slice(&(length as u16).to_be_bytes());
} else {
buffer.push(255);
buffer.extend_from_slice(&(length as u32).to_be_bytes());
}
}
Ok(())
}
pub fn decode_context_tag(data: &[u8]) -> Result<(u8, usize, usize)> {
if data.is_empty() {
return Err(EncodingError::InvalidTag);
}
let tag_byte = data[0];
if (tag_byte & 0x08) == 0 {
return Err(EncodingError::InvalidTag);
}
let tag_number = (tag_byte >> 4) & 0x0F;
let mut length = (tag_byte & 0x07) as usize;
let mut consumed = 1;
if length == 5 {
if data.len() < 2 {
return Err(EncodingError::BufferUnderflow);
}
let len_byte = data[1];
consumed += 1;
match len_byte {
0..=253 => {
length = len_byte as usize;
}
254 => {
if data.len() < 4 {
return Err(EncodingError::BufferUnderflow);
}
length = u16::from_be_bytes([data[2], data[3]]) as usize;
consumed += 2;
}
255 => {
if data.len() < 6 {
return Err(EncodingError::BufferUnderflow);
}
length = u32::from_be_bytes([data[2], data[3], data[4], data[5]]) as usize;
consumed += 4;
}
}
}
Ok((tag_number, length, consumed))
}
pub fn encode_opening_tag(buffer: &mut Vec<u8>, tag_number: u8) -> Result<()> {
if tag_number > 14 {
return Err(EncodingError::ValueOutOfRange);
}
buffer.push(0x0E | (tag_number << 4));
Ok(())
}
pub fn encode_closing_tag(buffer: &mut Vec<u8>, tag_number: u8) -> Result<()> {
if tag_number > 14 {
return Err(EncodingError::ValueOutOfRange);
}
buffer.push(0x0F | (tag_number << 4));
Ok(())
}
}
pub mod bitstring {
use super::*;
#[allow(clippy::manual_is_multiple_of)]
pub fn encode_bit_string(buffer: &mut Vec<u8>, bits: &[bool]) -> Result<()> {
let byte_count = bits.len().div_ceil(8);
let unused_bits = if bits.len() % 8 == 0 {
0
} else {
8 - (bits.len() % 8)
};
encode_application_tag(buffer, ApplicationTag::BitString, byte_count + 1);
buffer.push(unused_bits as u8);
let mut current_byte = 0u8;
let mut bit_pos = 0;
for &bit in bits {
if bit {
current_byte |= 1 << (7 - bit_pos);
}
bit_pos += 1;
if bit_pos == 8 {
buffer.push(current_byte);
current_byte = 0;
bit_pos = 0;
}
}
if bit_pos > 0 {
buffer.push(current_byte);
}
Ok(())
}
pub fn decode_bit_string(data: &[u8]) -> Result<(Vec<bool>, usize)> {
let (tag, length, mut consumed) = decode_application_tag(data)?;
if tag != ApplicationTag::BitString {
return Err(EncodingError::InvalidTag);
}
if length == 0 || data.len() < consumed + length {
return Err(EncodingError::BufferUnderflow);
}
let unused_bits = data[consumed] as usize;
consumed += 1;
if unused_bits > 7 {
return Err(EncodingError::InvalidFormat(
"Invalid unused bits count".to_string(),
));
}
let mut bits = Vec::new();
let byte_count = length - 1;
for i in 0..byte_count {
let byte_val = data[consumed + i];
let bits_in_byte = if i == byte_count - 1 {
8 - unused_bits
} else {
8
};
for bit_pos in 0..bits_in_byte {
bits.push((byte_val & (1 << (7 - bit_pos))) != 0);
}
}
consumed += byte_count;
Ok((bits, consumed))
}
}
pub mod perf {
use super::*;
pub struct FastEncoder {
buffer: Vec<u8>,
}
impl FastEncoder {
pub fn new(capacity: usize) -> Self {
Self {
buffer: Vec::with_capacity(capacity),
}
}
pub fn data(&self) -> &[u8] {
&self.buffer
}
pub fn clear(&mut self) {
self.buffer.clear();
}
pub fn encode_unsigned_fast(&mut self, value: u32) -> Result<()> {
match value {
0 => {
self.buffer.extend_from_slice(&[0x21, 0x00]);
}
1..=255 => {
self.buffer.extend_from_slice(&[0x21, value as u8]);
}
256..=65535 => {
let bytes = (value as u16).to_be_bytes();
self.buffer.extend_from_slice(&[0x22]);
self.buffer.extend_from_slice(&bytes);
}
65536..=16777215 => {
let bytes = value.to_be_bytes();
self.buffer.extend_from_slice(&[0x23]);
self.buffer.extend_from_slice(&bytes[1..]);
}
_ => {
let bytes = value.to_be_bytes();
self.buffer.extend_from_slice(&[0x24]);
self.buffer.extend_from_slice(&bytes);
}
}
Ok(())
}
pub fn encode_boolean_fast(&mut self, value: bool) -> Result<()> {
self.buffer.push(if value { 0x11 } else { 0x10 });
Ok(())
}
pub fn encode_real_fast(&mut self, value: f32) -> Result<()> {
self.buffer.push(0x44);
self.buffer.extend_from_slice(&value.to_be_bytes());
Ok(())
}
}
}
pub mod validation {
use super::*;
pub struct DataValidator {
max_tag_depth: usize,
max_string_length: usize,
}
impl DataValidator {
pub fn new(max_tag_depth: usize, max_string_length: usize) -> Self {
Self {
max_tag_depth,
max_string_length,
}
}
pub fn validate(&self, data: &[u8]) -> Result<()> {
self.validate_recursive(data, 0)
}
fn validate_recursive(&self, data: &[u8], depth: usize) -> Result<()> {
if depth > self.max_tag_depth {
return Err(EncodingError::InvalidFormat(
"Maximum tag depth exceeded".to_string(),
));
}
let mut pos = 0;
while pos < data.len() {
let (tag, length, consumed) = decode_application_tag(&data[pos..])?;
pos += consumed;
match tag {
ApplicationTag::CharacterString => {
if length > self.max_string_length {
return Err(EncodingError::InvalidFormat(
"String too long".to_string(),
));
}
}
ApplicationTag::OctetString => {
if length > self.max_string_length * 2 {
return Err(EncodingError::InvalidFormat(
"Octet string too long".to_string(),
));
}
}
_ => {}
}
pos += length;
}
Ok(())
}
}
}
}
pub struct EncodingStream {
buffer: Vec<u8>,
position: usize,
max_size: usize,
}
impl EncodingStream {
pub fn new(max_size: usize) -> Self {
Self {
buffer: Vec::with_capacity(max_size),
position: 0,
max_size,
}
}
pub fn encode_tagged<T: EncodableValue>(
&mut self,
tag: ApplicationTag,
value: T,
) -> Result<()> {
if self.buffer.len() >= self.max_size {
return Err(EncodingError::BufferOverflow);
}
value.encode_to(tag, &mut self.buffer)
}
pub fn encode_context<T: EncodableValue>(&mut self, tag_number: u8, value: T) -> Result<()> {
if self.buffer.len() >= self.max_size {
return Err(EncodingError::BufferOverflow);
}
value.encode_context_to(tag_number, &mut self.buffer)
}
pub fn data(&self) -> &[u8] {
&self.buffer
}
pub fn into_buffer(self) -> Vec<u8> {
self.buffer
}
pub fn clear(&mut self) {
self.buffer.clear();
self.position = 0;
}
}
pub trait EncodableValue {
fn encode_to(&self, tag: ApplicationTag, buffer: &mut Vec<u8>) -> Result<()>;
fn encode_context_to(&self, tag_number: u8, buffer: &mut Vec<u8>) -> Result<()>;
}
impl EncodableValue for bool {
fn encode_to(&self, _tag: ApplicationTag, buffer: &mut Vec<u8>) -> Result<()> {
encode_boolean(buffer, *self)
}
fn encode_context_to(&self, tag_number: u8, buffer: &mut Vec<u8>) -> Result<()> {
advanced::context::encode_context_tag(buffer, tag_number, if *self { 1 } else { 0 })
}
}
impl EncodableValue for u32 {
fn encode_to(&self, _tag: ApplicationTag, buffer: &mut Vec<u8>) -> Result<()> {
encode_unsigned(buffer, *self)
}
fn encode_context_to(&self, tag_number: u8, buffer: &mut Vec<u8>) -> Result<()> {
let temp_buffer = Vec::new();
let mut temp = temp_buffer;
encode_unsigned(&mut temp, *self)?;
advanced::context::encode_context_tag(buffer, tag_number, temp.len() - 1)?;
buffer.extend_from_slice(&temp[1..]);
Ok(())
}
}
impl EncodableValue for i32 {
fn encode_to(&self, _tag: ApplicationTag, buffer: &mut Vec<u8>) -> Result<()> {
encode_signed(buffer, *self)
}
fn encode_context_to(&self, tag_number: u8, buffer: &mut Vec<u8>) -> Result<()> {
let temp_buffer = Vec::new();
let mut temp = temp_buffer;
encode_signed(&mut temp, *self)?;
advanced::context::encode_context_tag(buffer, tag_number, temp.len() - 1)?;
buffer.extend_from_slice(&temp[1..]);
Ok(())
}
}
impl EncodableValue for f32 {
fn encode_to(&self, _tag: ApplicationTag, buffer: &mut Vec<u8>) -> Result<()> {
encode_real(buffer, *self)
}
fn encode_context_to(&self, tag_number: u8, buffer: &mut Vec<u8>) -> Result<()> {
advanced::context::encode_context_tag(buffer, tag_number, 4)?;
buffer.extend_from_slice(&self.to_be_bytes());
Ok(())
}
}
impl EncodableValue for f64 {
fn encode_to(&self, _tag: ApplicationTag, buffer: &mut Vec<u8>) -> Result<()> {
encode_double(buffer, *self)
}
fn encode_context_to(&self, tag_number: u8, buffer: &mut Vec<u8>) -> Result<()> {
advanced::context::encode_context_tag(buffer, tag_number, 8)?;
buffer.extend_from_slice(&self.to_be_bytes());
Ok(())
}
}
impl EncodableValue for &str {
fn encode_to(&self, _tag: ApplicationTag, buffer: &mut Vec<u8>) -> Result<()> {
encode_character_string(buffer, self)
}
fn encode_context_to(&self, tag_number: u8, buffer: &mut Vec<u8>) -> Result<()> {
advanced::context::encode_context_tag(buffer, tag_number, self.len() + 1)?;
buffer.push(0); buffer.extend_from_slice(self.as_bytes());
Ok(())
}
}
pub struct DecodingStream<'a> {
data: &'a [u8],
position: usize,
}
impl<'a> DecodingStream<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data, position: 0 }
}
pub fn has_data(&self) -> bool {
self.position < self.data.len()
}
pub fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.position)
}
pub fn peek_tag(&self) -> Result<ApplicationTag> {
if self.position >= self.data.len() {
return Err(EncodingError::UnexpectedEndOfData);
}
let tag_byte = self.data[self.position];
let tag = ApplicationTag::try_from(tag_byte >> 4)?;
Ok(tag)
}
pub fn decode_boolean(&mut self) -> Result<bool> {
let (value, consumed) = decode_boolean(&self.data[self.position..])?;
self.position += consumed;
Ok(value)
}
pub fn decode_unsigned(&mut self) -> Result<u32> {
let (value, consumed) = decode_unsigned(&self.data[self.position..])?;
self.position += consumed;
Ok(value)
}
pub fn decode_signed(&mut self) -> Result<i32> {
let (value, consumed) = decode_signed(&self.data[self.position..])?;
self.position += consumed;
Ok(value)
}
pub fn decode_real(&mut self) -> Result<f32> {
let (value, consumed) = decode_real(&self.data[self.position..])?;
self.position += consumed;
Ok(value)
}
pub fn decode_double(&mut self) -> Result<f64> {
let (value, consumed) = decode_double(&self.data[self.position..])?;
self.position += consumed;
Ok(value)
}
pub fn decode_character_string(&mut self) -> Result<String> {
let (value, consumed) = decode_character_string(&self.data[self.position..])?;
self.position += consumed;
Ok(value)
}
pub fn decode_octet_string(&mut self) -> Result<Vec<u8>> {
let (value, consumed) = decode_octet_string(&self.data[self.position..])?;
self.position += consumed;
Ok(value)
}
pub fn decode_enumerated(&mut self) -> Result<u32> {
let (value, consumed) = decode_enumerated(&self.data[self.position..])?;
self.position += consumed;
Ok(value)
}
pub fn decode_date(&mut self) -> Result<(u16, u8, u8, u8)> {
let (value, consumed) = decode_date(&self.data[self.position..])?;
self.position += consumed;
Ok(value)
}
pub fn decode_time(&mut self) -> Result<(u8, u8, u8, u8)> {
let (value, consumed) = decode_time(&self.data[self.position..])?;
self.position += consumed;
Ok(value)
}
pub fn decode_object_identifier(&mut self) -> Result<ObjectIdentifier> {
let (identifier, consumed) = decode_object_identifier(&self.data[self.position..])?;
self.position += consumed;
Ok(identifier)
}
pub fn skip_value(&mut self) -> Result<()> {
let (_tag, length, consumed) = decode_application_tag(&self.data[self.position..])?;
self.position += consumed + length;
Ok(())
}
pub fn position(&self) -> usize {
self.position
}
pub fn set_position(&mut self, position: usize) -> Result<()> {
if position > self.data.len() {
return Err(EncodingError::ValueOutOfRange);
}
self.position = position;
Ok(())
}
}
#[derive(Default)]
pub struct PropertyArrayEncoder {
buffer: Vec<u8>,
count: usize,
}
impl PropertyArrayEncoder {
pub fn new() -> Self {
Self::default()
}
pub fn add_property<T: EncodableValue>(&mut self, property_id: u32, value: T) -> Result<()> {
advanced::context::encode_context_tag(&mut self.buffer, 0, 4)?;
self.buffer.extend_from_slice(&property_id.to_be_bytes());
advanced::context::encode_opening_tag(&mut self.buffer, 1)?;
value.encode_to(ApplicationTag::Null, &mut self.buffer)?;
advanced::context::encode_closing_tag(&mut self.buffer, 1)?;
self.count += 1;
Ok(())
}
pub fn data(&self) -> &[u8] {
&self.buffer
}
pub fn count(&self) -> usize {
self.count
}
pub fn clear(&mut self) {
self.buffer.clear();
self.count = 0;
}
}
#[derive(Default)]
pub struct ErrorEncoder {
buffer: Vec<u8>,
}
impl ErrorEncoder {
pub fn new() -> Self {
Self::default()
}
pub fn encode_error(&mut self, error_class: u32, error_code: u32) -> Result<()> {
advanced::context::encode_context_tag(
&mut self.buffer,
0,
if error_class <= 0xFF {
1
} else if error_class <= 0xFFFF {
2
} else {
4
},
)?;
encode_enumerated(&mut self.buffer, error_class);
advanced::context::encode_context_tag(
&mut self.buffer,
1,
if error_code <= 0xFF {
1
} else if error_code <= 0xFFFF {
2
} else {
4
},
)?;
encode_enumerated(&mut self.buffer, error_code);
Ok(())
}
pub fn data(&self) -> &[u8] {
&self.buffer
}
pub fn clear(&mut self) {
self.buffer.clear();
}
}
#[derive(Debug, Default)]
pub struct EncodingAnalyzer {
pub stats: EncodingStatistics,
benchmarks: Vec<EncodingBenchmark>,
error_patterns: Vec<ErrorPattern>,
}
#[derive(Debug, Default)]
pub struct EncodingStatistics {
pub total_encodings: u64,
pub total_decodings: u64,
pub bytes_encoded: u64,
pub bytes_decoded: u64,
pub encoding_errors: u64,
pub decoding_errors: u64,
pub avg_encode_time_us: f64,
pub avg_decode_time_us: f64,
}
#[derive(Debug, Clone)]
struct EncodingBenchmark {
_data_type: &'static str,
_size: usize,
_encode_time_us: u64,
_decode_time_us: u64,
#[cfg(feature = "std")]
_timestamp: std::time::Instant,
}
#[derive(Debug, Clone)]
struct ErrorPattern {
error_type: EncodingError,
count: u32,
#[cfg(feature = "std")]
last_seen: std::time::Instant,
}
impl EncodingAnalyzer {
pub fn new() -> Self {
Self::default()
}
pub fn record_encoding(&mut self, data_type: &'static str, bytes: usize, duration_us: u64) {
self.stats.total_encodings += 1;
self.stats.bytes_encoded += bytes as u64;
let total_time = self.stats.avg_encode_time_us * (self.stats.total_encodings - 1) as f64;
self.stats.avg_encode_time_us =
(total_time + duration_us as f64) / self.stats.total_encodings as f64;
self.benchmarks.push(EncodingBenchmark {
_data_type: data_type,
_size: bytes,
_encode_time_us: duration_us,
_decode_time_us: 0,
#[cfg(feature = "std")]
_timestamp: std::time::Instant::now(),
});
if self.benchmarks.len() > 1000 {
self.benchmarks.remove(0);
}
}
pub fn record_decoding(&mut self, _data_type: &'static str, bytes: usize, duration_us: u64) {
self.stats.total_decodings += 1;
self.stats.bytes_decoded += bytes as u64;
let total_time = self.stats.avg_decode_time_us * (self.stats.total_decodings - 1) as f64;
self.stats.avg_decode_time_us =
(total_time + duration_us as f64) / self.stats.total_decodings as f64;
}
pub fn record_error(&mut self, error: EncodingError) {
self.stats.encoding_errors += 1;
if let Some(pattern) = self
.error_patterns
.iter_mut()
.find(|p| std::mem::discriminant(&p.error_type) == std::mem::discriminant(&error))
{
pattern.count += 1;
#[cfg(feature = "std")]
{
pattern.last_seen = std::time::Instant::now();
}
} else {
self.error_patterns.push(ErrorPattern {
error_type: error,
count: 1,
#[cfg(feature = "std")]
last_seen: std::time::Instant::now(),
});
}
}
pub fn get_encoding_throughput(&self) -> f64 {
if self.stats.avg_encode_time_us > 0.0 {
(self.stats.bytes_encoded as f64 / self.stats.total_encodings as f64)
/ (self.stats.avg_encode_time_us / 1_000_000.0)
} else {
0.0
}
}
pub fn get_decoding_throughput(&self) -> f64 {
if self.stats.avg_decode_time_us > 0.0 {
(self.stats.bytes_decoded as f64 / self.stats.total_decodings as f64)
/ (self.stats.avg_decode_time_us / 1_000_000.0)
} else {
0.0
}
}
pub fn get_top_errors(&self, limit: usize) -> Vec<(&EncodingError, u32)> {
let mut errors: Vec<_> = self
.error_patterns
.iter()
.map(|p| (&p.error_type, p.count))
.collect();
errors.sort_by(|a, b| b.1.cmp(&a.1));
errors.truncate(limit);
errors
}
pub fn reset(&mut self) {
self.stats = EncodingStatistics::default();
self.benchmarks.clear();
self.error_patterns.clear();
}
}
#[derive(Debug)]
pub struct EncodingCache {
cache: Vec<CacheEntry>,
max_size: usize,
pub hits: u64,
pub misses: u64,
}
#[derive(Debug, Clone)]
struct CacheEntry {
hash: u64,
encoded: Vec<u8>,
access_count: u32,
#[cfg(feature = "std")]
last_access: std::time::Instant,
}
impl EncodingCache {
pub fn new(max_size: usize) -> Self {
Self {
cache: Vec::with_capacity(max_size),
max_size,
hits: 0,
misses: 0,
}
}
pub fn get(&mut self, hash: u64) -> Option<Vec<u8>> {
if let Some(entry) = self.cache.iter_mut().find(|e| e.hash == hash) {
entry.access_count += 1;
#[cfg(feature = "std")]
{
entry.last_access = std::time::Instant::now();
}
self.hits += 1;
Some(entry.encoded.clone())
} else {
self.misses += 1;
None
}
}
pub fn put(&mut self, hash: u64, encoded: Vec<u8>) {
if self.cache.iter().any(|e| e.hash == hash) {
return;
}
if self.cache.len() >= self.max_size {
self.cache.sort_by_key(|e| e.access_count);
self.cache.remove(0);
}
self.cache.push(CacheEntry {
hash,
encoded,
access_count: 1,
#[cfg(feature = "std")]
last_access: std::time::Instant::now(),
});
}
pub fn clear(&mut self) {
self.cache.clear();
self.hits = 0;
self.misses = 0;
}
pub fn hit_ratio(&self) -> f64 {
let total = self.hits + self.misses;
if total > 0 {
self.hits as f64 / total as f64
} else {
0.0
}
}
}
#[derive(Debug, Clone)]
pub struct EncodingConfig {
pub use_compression: bool,
pub compression_threshold: usize,
pub enable_caching: bool,
pub cache_size: usize,
pub enable_performance_tracking: bool,
pub validation_level: ValidationLevel,
pub max_string_length: usize,
pub max_array_size: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValidationLevel {
None,
Basic,
Strict,
Paranoid,
}
impl Default for EncodingConfig {
fn default() -> Self {
Self {
use_compression: false,
compression_threshold: 1024,
enable_caching: true,
cache_size: 1000,
enable_performance_tracking: true,
validation_level: ValidationLevel::Basic,
max_string_length: 4096,
max_array_size: 1000,
}
}
}
#[derive(Debug)]
pub struct EncodingManager {
_config: EncodingConfig,
analyzer: Option<EncodingAnalyzer>,
cache: Option<EncodingCache>,
buffer_manager: advanced::BufferManager,
}
impl EncodingManager {
pub fn new(config: EncodingConfig) -> Self {
let analyzer = if config.enable_performance_tracking {
Some(EncodingAnalyzer::new())
} else {
None
};
let cache = if config.enable_caching {
Some(EncodingCache::new(config.cache_size))
} else {
None
};
Self {
_config: config,
analyzer,
cache,
buffer_manager: advanced::BufferManager::new(8192),
}
}
pub fn encode<T: EncodableValue>(&mut self, value: T, tag: ApplicationTag) -> Result<Vec<u8>> {
#[cfg(feature = "std")]
let start_time = std::time::Instant::now();
let mut buffer = self.buffer_manager.get_encode_buffer();
let result = value.encode_to(tag, &mut buffer);
#[cfg(feature = "std")]
let duration = start_time.elapsed();
match result {
Ok(_) => {
if let Some(ref mut analyzer) = self.analyzer {
#[cfg(feature = "std")]
analyzer.record_encoding("generic", buffer.len(), duration.as_micros() as u64);
#[cfg(not(feature = "std"))]
analyzer.record_encoding("generic", buffer.len(), 0);
}
let result_buffer = buffer.clone();
self.buffer_manager.return_buffer(buffer);
Ok(result_buffer)
}
Err(e) => {
if let Some(ref mut analyzer) = self.analyzer {
analyzer.record_error(e.clone());
}
self.buffer_manager.return_buffer(buffer);
Err(e)
}
}
}
pub fn decode<T>(
&mut self,
data: &[u8],
decoder: impl Fn(&[u8]) -> Result<(T, usize)>,
) -> Result<T> {
#[cfg(feature = "std")]
let start_time = std::time::Instant::now();
let result = decoder(data);
#[cfg(feature = "std")]
let duration = start_time.elapsed();
match result {
Ok((value, consumed)) => {
if let Some(ref mut analyzer) = self.analyzer {
#[cfg(feature = "std")]
analyzer.record_decoding("generic", consumed, duration.as_micros() as u64);
#[cfg(not(feature = "std"))]
analyzer.record_decoding("generic", consumed, 0);
}
Ok(value)
}
Err(e) => {
if let Some(ref mut analyzer) = self.analyzer {
analyzer.record_error(e.clone());
}
Err(e)
}
}
}
pub fn get_stats(&self) -> Option<&EncodingStatistics> {
self.analyzer.as_ref().map(|a| &a.stats)
}
pub fn get_cache_stats(&self) -> Option<(u64, u64, f64)> {
self.cache
.as_ref()
.map(|c| (c.hits, c.misses, c.hit_ratio()))
}
pub fn reset_stats(&mut self) {
if let Some(ref mut analyzer) = self.analyzer {
analyzer.reset();
}
if let Some(ref mut cache) = self.cache {
cache.clear();
}
}
}
impl Default for EncodingManager {
fn default() -> Self {
Self::new(EncodingConfig::default())
}
}
#[cfg(test)]
mod tests {
use crate::ObjectType;
use super::*;
#[test]
fn test_encode_decode_boolean() {
let mut buffer = Vec::new();
encode_boolean(&mut buffer, true).unwrap();
let (value, consumed) = decode_boolean(&buffer).unwrap();
assert!(value);
assert_eq!(consumed, 1);
buffer.clear();
encode_boolean(&mut buffer, false).unwrap();
let (value, consumed) = decode_boolean(&buffer).unwrap();
assert!(!value);
assert_eq!(consumed, 1);
}
#[test]
fn test_encode_decode_unsigned() {
let mut buffer = Vec::new();
let test_values = [0, 255, 65535, 16777215, 4294967295];
for &test_value in &test_values {
buffer.clear();
encode_unsigned(&mut buffer, test_value).unwrap();
let (value, _) = decode_unsigned(&buffer).unwrap();
assert_eq!(value, test_value);
}
}
#[test]
fn test_encode_decode_signed() {
let mut buffer = Vec::new();
let test_values = [-128, -1, 0, 1, 127, -32768, 32767, -8388608, 8388607];
for &test_value in &test_values {
buffer.clear();
encode_signed(&mut buffer, test_value).unwrap();
let (value, _) = decode_signed(&buffer).unwrap();
assert_eq!(value, test_value);
}
}
#[test]
fn test_encode_decode_real() {
let mut buffer = Vec::new();
let test_values = [
0.0,
1.0,
-1.0,
std::f32::consts::PI,
-273.15,
f32::MAX,
f32::MIN,
];
for &test_value in &test_values {
buffer.clear();
encode_real(&mut buffer, test_value).unwrap();
let (value, _) = decode_real(&buffer).unwrap();
assert_eq!(value, test_value);
}
}
#[test]
fn test_encode_decode_character_string() {
let mut buffer = Vec::new();
let test_strings = ["Hello", "BACnet", "Temperature Sensor", ""];
for &test_string in &test_strings {
buffer.clear();
encode_character_string(&mut buffer, test_string).unwrap();
let (value, _) = decode_character_string(&buffer).unwrap();
assert_eq!(value, test_string);
}
}
#[test]
fn test_encode_decode_octet_string() {
let mut buffer = Vec::new();
let test_data = vec![0x01, 0x02, 0x03, 0xFF, 0x00];
encode_octet_string(&mut buffer, &test_data).unwrap();
let (decoded, _) = decode_octet_string(&buffer).unwrap();
assert_eq!(decoded, test_data);
}
#[test]
fn test_encode_decode_enumerated() {
let mut buffer = Vec::new();
let test_values = [0, 1, 255, 256, 65535, 65536, 16777215];
for &test_value in &test_values {
buffer.clear();
encode_enumerated(&mut buffer, test_value);
let (value, _) = decode_enumerated(&buffer).unwrap();
assert_eq!(value, test_value);
}
}
#[test]
fn test_encode_decode_date() {
let mut buffer = Vec::new();
encode_date(&mut buffer, 2024, 3, 15, 5).unwrap(); let ((year, month, day, weekday), _) = decode_date(&buffer).unwrap();
assert_eq!(year, 2024);
assert_eq!(month, 3);
assert_eq!(day, 15);
assert_eq!(weekday, 5);
}
#[test]
fn test_encode_decode_time() {
let mut buffer = Vec::new();
encode_time(&mut buffer, 14, 30, 45, 50).unwrap(); let ((hour, minute, second, hundredths), _) = decode_time(&buffer).unwrap();
assert_eq!(hour, 14);
assert_eq!(minute, 30);
assert_eq!(second, 45);
assert_eq!(hundredths, 50);
}
#[test]
fn test_encode_decode_object_identifier() {
let mut buffer = Vec::new();
let object_id = ObjectIdentifier::new(ObjectType::AnalogValue, 12345);
encode_object_identifier(&mut buffer, object_id).unwrap(); let (object_id, _) = decode_object_identifier(&buffer).unwrap();
assert_eq!(object_id.object_type, ObjectType::AnalogValue);
assert_eq!(object_id.instance, 12345);
}
#[test]
fn test_encode_decode_double() {
let mut buffer = Vec::new();
let test_values = [
0.0,
1.0,
-1.0,
std::f64::consts::PI,
-273.15,
f64::MAX,
f64::MIN,
];
for &test_value in &test_values {
buffer.clear();
encode_double(&mut buffer, test_value).unwrap();
let (value, _) = decode_double(&buffer).unwrap();
assert_eq!(value, test_value);
}
}
#[test]
fn test_buffer_manager() {
use advanced::BufferManager;
let mut manager = BufferManager::new(1024);
let buffer1 = manager.get_encode_buffer();
let buffer2 = manager.get_encode_buffer();
assert_eq!(manager.stats.total_allocations, 2);
assert_eq!(manager.stats.buffer_reuses, 0);
manager.return_buffer(buffer1);
let buffer3 = manager.get_encode_buffer();
assert_eq!(manager.stats.total_allocations, 2);
assert_eq!(manager.stats.buffer_reuses, 1);
manager.return_buffer(buffer2);
manager.return_buffer(buffer3);
}
#[test]
fn test_context_specific_encoding() {
use advanced::context::*;
let mut buffer = Vec::new();
encode_context_tag(&mut buffer, 5, 10).unwrap();
let (tag_number, length, consumed) = decode_context_tag(&buffer).unwrap();
assert_eq!(tag_number, 5);
assert_eq!(length, 10);
assert_eq!(consumed, 2);
}
#[test]
fn test_opening_closing_tags() {
use advanced::context::*;
let mut buffer = Vec::new();
encode_opening_tag(&mut buffer, 3).unwrap();
encode_closing_tag(&mut buffer, 3).unwrap();
assert_eq!(buffer, vec![0x3E, 0x3F]);
}
#[test]
fn test_bit_string_encoding() {
use advanced::bitstring::*;
let mut buffer = Vec::new();
let bits = vec![true, false, true, true, false, false, true, false, true];
encode_bit_string(&mut buffer, &bits).unwrap();
let (decoded_bits, _) = decode_bit_string(&buffer).unwrap();
assert_eq!(decoded_bits, bits);
}
#[test]
fn test_fast_encoder() {
use advanced::perf::FastEncoder;
let mut encoder = FastEncoder::new(256);
encoder.encode_unsigned_fast(42).unwrap();
encoder.encode_boolean_fast(true).unwrap();
encoder.encode_real_fast(std::f32::consts::PI).unwrap();
let data = encoder.data();
assert!(!data.is_empty());
encoder.clear();
assert_eq!(encoder.data().len(), 0);
}
#[test]
fn test_data_validator() {
use advanced::validation::DataValidator;
let validator = DataValidator::new(10, 1000);
let mut buffer = Vec::new();
encode_unsigned(&mut buffer, 42).unwrap();
encode_character_string(&mut buffer, "Hello").unwrap();
assert!(validator.validate(&buffer).is_ok());
}
#[test]
fn test_encode_decode_performance() {
let mut buffer = Vec::new();
let iterations = 1000;
for i in 0..iterations {
buffer.clear();
encode_unsigned(&mut buffer, i).unwrap();
let (value, _) = decode_unsigned(&buffer).unwrap();
assert_eq!(value, i);
}
}
#[test]
fn test_encode_decode_i64() {
let mut buffer = Vec::new();
let test_values = [
0,
1,
-1,
-330,
i32::MAX as i64,
i32::MIN as i64,
i32::MAX as i64 + 10,
i32::MIN as i64 - 10,
i64::MAX,
i64::MIN,
i64::MAX as i32 as i64,
i64::MIN as i32 as i64,
];
for &test_value in &test_values {
buffer.clear();
encode_signed64(&mut buffer, test_value);
let (value, _) = decode_signed64(&buffer).unwrap();
assert_eq!(value, test_value);
}
}
#[test]
fn test_encode_decode_u64() {
let mut buffer = Vec::new();
let test_values = [
0,
1,
255,
330,
u32::MAX as u64,
u32::MIN as u64,
u32::MAX as u64 + 10,
u64::MAX,
u64::MIN,
u64::MAX as u32 as u64,
u64::MIN as u32 as u64,
];
for &test_value in &test_values {
buffer.clear();
encode_unsigned64(&mut buffer, test_value);
let (value, _) = decode_unsigned64(&buffer).unwrap();
assert_eq!(value, test_value);
}
}
#[test]
fn test_decode_bacnet_tag() {
let data = [0x21];
let (tag, length, consumed) = decode_tag(&data).unwrap();
assert_eq!(tag, BACnetTag::Application(ApplicationTag::UnsignedInt));
assert_eq!(length, 1);
assert_eq!(consumed, 1);
let data = [0x1E];
let (tag, length, consumed) = decode_tag(&data).unwrap();
assert_eq!(tag, BACnetTag::Context(1));
assert_eq!(length, 6);
assert_eq!(consumed, 1);
let data = [0x0C];
let (tag, length, consumed) = decode_tag(&data).unwrap();
assert_eq!(tag, BACnetTag::Context(0));
assert_eq!(length, 4);
assert_eq!(consumed, 1);
let data = [0x35, 0x08];
let (tag, length, consumed) = decode_tag(&data).unwrap();
assert_eq!(tag, BACnetTag::Application(ApplicationTag::SignedInt));
assert_eq!(length, 8);
assert_eq!(consumed, 2);
}
}