Skip to main content

bacnet_encoding/
sink.rs

1//! Append-only output for the sink-based encoders.
2//!
3//! The tag-header functions in [`crate::tags`], the bounded scalar, timestamp
4//! and string encoders in [`crate::primitives`], the bounded references in
5//! [`crate::constructed`], the APDU header writer in [`crate::apdu::header`]
6//! and the allocation-free services in bacnet-services support these sinks.
7//! Other encoder families still use their `BytesMut` buffers. The sink
8//! contract and the slice and `heapless` adapters need no allocator.
9
10use core::fmt;
11
12/// Storage that accepts a complete byte slice or leaves its output unchanged.
13///
14/// A write appends after earlier output. On failure, neither the output bytes
15/// nor the output position may change. Implementers must uphold that contract;
16/// it is not a partial-I/O interface.
17///
18/// The tag-header, bounded scalar, timestamp and reference encoders check
19/// advertised capacity, then write their complete output once. A
20/// variable-length value, such as a string with its header, is written with
21/// [`write_parts`](Self::write_parts), which is atomic for the whole value.
22/// Neither promises rollback across several encoder calls: a custom sink can
23/// refuse a later write even after a total-capacity check, leaving earlier
24/// writes in place.
25///
26/// Implemented for `&mut [u8]`, and `heapless::Vec<u8, N>` with the `heapless`
27/// feature, and with `alloc` for `alloc::vec::Vec<u8>` and `bytes::BytesMut`.
28/// Growable adapters may allocate and retain their ordinary
29/// allocation-failure behavior.
30///
31/// ```
32/// use bacnet_encoding::sink::EncodeSink;
33///
34/// let mut storage = [0xA5; 4];
35/// let mut tail = &mut storage[..];
36/// tail.write(&[1, 2])?;
37/// assert_eq!(tail.remaining_capacity(), Some(2));
38/// assert_eq!(tail.write(&[3, 4, 5]).unwrap_err().available, 2);
39/// tail.write(&[3, 4])?;
40/// assert!(tail.is_empty());
41/// assert_eq!(storage, [1, 2, 3, 4]);
42/// # Ok::<(), bacnet_encoding::sink::SinkFull>(())
43/// ```
44pub trait EncodeSink {
45    /// Remaining append space, or `None` when no fixed bound is advertised.
46    ///
47    /// Growable containers return `None`, not their current allocation's spare
48    /// capacity. A sink may still reject a write; the encoder passes through
49    /// its [`SinkFull`] without changing the error.
50    fn remaining_capacity(&self) -> Option<usize>;
51
52    /// Append all of `bytes`, or fail without changing output bytes or position.
53    fn write(&mut self, bytes: &[u8]) -> Result<(), SinkFull>;
54
55    /// Append every slice of `parts`, in order, as one value, or fail without
56    /// changing output bytes or position.
57    ///
58    /// The provided method adds up the parts' lengths, refuses a total that
59    /// overflows `usize` (with `needed` set to `usize::MAX`) or exceeds
60    /// [`remaining_capacity`](Self::remaining_capacity), and only then calls
61    /// [`write`](Self::write) once per part. That is atomic for a sink whose
62    /// `write` never refuses an append its advertised capacity covers, and
63    /// whose capacity, when it advertises none, is unbounded. Every adapter
64    /// this crate ships is such a sink.
65    ///
66    /// **A sink that can refuse despite advertising enough room**, such as
67    /// one with a quota it doesn't advertise or one backed by fallible
68    /// storage, **must override this method**, and either stage the parts or
69    /// undo the ones it wrote. Otherwise a refused later part leaves the
70    /// earlier ones written.
71    ///
72    /// ```
73    /// use bacnet_encoding::sink::EncodeSink;
74    ///
75    /// let mut storage = [0; 4];
76    /// let mut tail = &mut storage[..];
77    /// // Five octets don't fit: nothing is written.
78    /// let refused = tail.write_parts(&[&[1, 2], &[3, 4, 5]]).unwrap_err();
79    /// assert_eq!((refused.needed, refused.available), (5, 4));
80    /// assert_eq!(tail.len(), 4);
81    /// tail.write_parts(&[&[1], &[], &[2, 3]])?;
82    /// assert_eq!(storage, [1, 2, 3, 0]);
83    /// # Ok::<(), bacnet_encoding::sink::SinkFull>(())
84    /// ```
85    fn write_parts(&mut self, parts: &[&[u8]]) -> Result<(), SinkFull> {
86        let available = self.remaining_capacity();
87        let mut needed = 0usize;
88        for part in parts {
89            needed = needed.checked_add(part.len()).ok_or(SinkFull {
90                needed: usize::MAX,
91                available: available.unwrap_or(usize::MAX),
92            })?;
93        }
94        if let Some(available) = available
95            && needed > available
96        {
97            return Err(SinkFull { needed, available });
98        }
99        for part in parts {
100            self.write(part)?;
101        }
102        Ok(())
103    }
104}
105
106/// A byte append did not fit its sink.
107///
108/// This error holds no allocated message. It does not describe allocation
109/// failure in a growable container.
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
111pub struct SinkFull {
112    /// Number of bytes in the refused append.
113    pub needed: usize,
114    /// Available append space when it was refused.
115    pub available: usize,
116}
117
118impl fmt::Display for SinkFull {
119    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120        write!(
121            f,
122            "sink full: need {}, have {}",
123            self.needed, self.available
124        )
125    }
126}
127
128impl core::error::Error for SinkFull {}
129
130/// Writes at the front and advances the slice to its unused tail.
131/// A failed write leaves both the slice and its backing bytes unchanged.
132impl EncodeSink for &mut [u8] {
133    fn remaining_capacity(&self) -> Option<usize> {
134        Some(self.len())
135    }
136
137    fn write(&mut self, bytes: &[u8]) -> Result<(), SinkFull> {
138        if bytes.len() > self.len() {
139            return Err(SinkFull {
140                needed: bytes.len(),
141                available: self.len(),
142            });
143        }
144        let (head, tail) = core::mem::take(self).split_at_mut(bytes.len());
145        head.copy_from_slice(bytes);
146        *self = tail;
147        Ok(())
148    }
149}
150
151#[cfg(feature = "heapless")]
152impl<const N: usize> EncodeSink for heapless::Vec<u8, N> {
153    fn remaining_capacity(&self) -> Option<usize> {
154        Some(N - self.len())
155    }
156
157    fn write(&mut self, bytes: &[u8]) -> Result<(), SinkFull> {
158        let available = N - self.len();
159        self.extend_from_slice(bytes).map_err(|_| SinkFull {
160            needed: bytes.len(),
161            available,
162        })
163    }
164}
165
166#[cfg(feature = "alloc")]
167impl EncodeSink for alloc::vec::Vec<u8> {
168    fn remaining_capacity(&self) -> Option<usize> {
169        None
170    }
171
172    fn write(&mut self, bytes: &[u8]) -> Result<(), SinkFull> {
173        self.extend_from_slice(bytes);
174        Ok(())
175    }
176}
177
178#[cfg(feature = "alloc")]
179impl EncodeSink for bytes::BytesMut {
180    fn remaining_capacity(&self) -> Option<usize> {
181        None
182    }
183
184    fn write(&mut self, bytes: &[u8]) -> Result<(), SinkFull> {
185        self.extend_from_slice(bytes);
186        Ok(())
187    }
188}
189
190#[cfg(test)]
191mod tests;