Skip to main content

bacnet_npdu/
encode.rs

1//! Encoding an [`Npdu`] into the caller's buffer: [`Npdu::encoded_len`]
2//! and [`Npdu::encode_into`].
3
4use core::fmt;
5
6use crate::view::{DESTINATION, EXPECTING_REPLY, NETWORK_MESSAGE, SOURCE};
7use crate::{Address, GLOBAL_NETWORK, MAX_MAC_LEN, Npdu, VERSION};
8
9/// Why [`Npdu::encode_into`] refused an NPDU. Nothing was written.
10///
11/// It is `#[non_exhaustive]`, so a new refusal isn't a breaking change: a
12/// `match` needs a wildcard arm.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14#[non_exhaustive]
15pub enum EncodeError {
16    /// The buffer is shorter than the NPDU.
17    NoRoom {
18        /// The NPDU's length, [`Npdu::encoded_len`].
19        needed: usize,
20        /// The buffer's length.
21        available: usize,
22    },
23    /// A DNET of 0, or an SNET of 0 or 0xFFFF, which no decoder accepts.
24    ReservedNetwork,
25    /// An empty SADR.
26    EmptySource,
27    /// A DADR or SADR longer than [`MAX_MAC_LEN`].
28    AddressTooLong,
29}
30
31impl fmt::Display for EncodeError {
32    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33        match self {
34            Self::NoRoom { needed, available } => {
35                write!(f, "an NPDU of {needed} octets doesn't fit in {available}")
36            }
37            Self::ReservedNetwork => f.write_str("NPDU names a reserved network number"),
38            Self::EmptySource => f.write_str("NPDU source SLEN=0 is invalid"),
39            Self::AddressTooLong => write!(f, "NPDU address exceeds the {MAX_MAC_LEN}-octet limit"),
40        }
41    }
42}
43
44impl core::error::Error for EncodeError {}
45
46impl Npdu<'_> {
47    /// The octets [`encode_into`](Self::encode_into) produces: the NPCI the
48    /// fields call for, then the payload.
49    #[must_use]
50    pub fn encoded_len(&self) -> usize {
51        let address = |address: Option<Address<'_>>| address.map_or(0, |a| 3 + a.mac.len());
52        let message = match self.message_type {
53            Some(kind) if kind.is_proprietary() => 3,
54            Some(_) => 1,
55            None => 0,
56        };
57        2 + address(self.destination)
58            + address(self.source)
59            + usize::from(self.destination.is_some())
60            + message
61            + self.payload.len()
62    }
63
64    /// Encode the NPDU at the start of `out` and return its length.
65    ///
66    /// Nothing is written unless all of it fits and its fields are ones
67    /// [`Npdu::decode`] accepts. The hop count is written only with a
68    /// destination, and the vendor ID only with a proprietary message type
69    /// (0 when it is `None`).
70    pub fn encode_into(&self, out: &mut [u8]) -> Result<usize, EncodeError> {
71        if let Some(destination) = self.destination {
72            if destination.network == 0 {
73                return Err(EncodeError::ReservedNetwork);
74            }
75            if destination.mac.len() > MAX_MAC_LEN {
76                return Err(EncodeError::AddressTooLong);
77            }
78        }
79        if let Some(source) = self.source {
80            if source.network == 0 || source.network == GLOBAL_NETWORK {
81                return Err(EncodeError::ReservedNetwork);
82            }
83            if source.mac.is_empty() {
84                return Err(EncodeError::EmptySource);
85            }
86            if source.mac.len() > MAX_MAC_LEN {
87                return Err(EncodeError::AddressTooLong);
88            }
89        }
90        let needed = self.encoded_len();
91        let available = out.len();
92        let out = out
93            .get_mut(..needed)
94            .ok_or(EncodeError::NoRoom { needed, available })?;
95
96        let mut control = self.priority.to_raw();
97        if self.message_type.is_some() {
98            control |= NETWORK_MESSAGE;
99        }
100        if self.destination.is_some() {
101            control |= DESTINATION;
102        }
103        if self.source.is_some() {
104            control |= SOURCE;
105        }
106        if self.expecting_reply {
107            control |= EXPECTING_REPLY;
108        }
109        let mut cursor = Cursor { out, at: 0 };
110        cursor.put(&[VERSION, control]);
111        if let Some(destination) = self.destination {
112            cursor.put_address(destination);
113        }
114        if let Some(source) = self.source {
115            cursor.put_address(source);
116        }
117        if self.destination.is_some() {
118            cursor.put(&[self.hop_count]);
119        }
120        if let Some(kind) = self.message_type {
121            cursor.put(&[kind.to_raw()]);
122            if kind.is_proprietary() {
123                cursor.put(&self.vendor_id.unwrap_or(0).to_be_bytes());
124            }
125        }
126        cursor.put(self.payload);
127        debug_assert_eq!(cursor.at, needed);
128        Ok(needed)
129    }
130}
131
132/// Appends to a buffer already sized to what goes in it.
133struct Cursor<'o> {
134    out: &'o mut [u8],
135    at: usize,
136}
137
138impl Cursor<'_> {
139    fn put(&mut self, octets: &[u8]) {
140        self.out[self.at..self.at + octets.len()].copy_from_slice(octets);
141        self.at += octets.len();
142    }
143
144    fn put_address(&mut self, address: Address<'_>) {
145        self.put(&address.network.to_be_bytes());
146        // At most MAX_MAC_LEN, checked before anything is written.
147        self.put(&[address.mac.len() as u8]);
148        self.put(address.mac);
149    }
150}