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;