Skip to main content

rm2k/
sink.rs

1//! Where serialized bytes go.
2//!
3//! [`Sink`] abstracts the write destination so the same generated `write` method can serve a
4//! fixed stack buffer on an MCU, a `Vec<u8>`, or a file - no allocator or `std` required unless
5//! the destination itself needs one.
6//!
7//! The associated `Error` type is the point of the design. `Vec<u8>` cannot fail, so its impl
8//! uses [`Infallible`](core::convert::Infallible) - which makes the whole `alloc` write path
9//! statically infallible, and lets callers of an infallible write discard the result without
10//! pretending to handle an error that cannot occur.
11
12use crate::error::WriteError;
13
14/// A byte destination.
15pub trait Sink {
16    /// How a write can fail. [`Infallible`](core::convert::Infallible) when it cannot.
17    type Error;
18
19    /// Append `bytes`.
20    ///
21    /// # Errors
22    ///
23    /// Implementation-defined; see [`Sink::Error`].
24    fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error>;
25}
26
27/// Writes into a fixed caller-supplied buffer. The `no_std` sink.
28///
29/// Pair it with the `lcf_size` method the schema macro generates to size the buffer exactly.
30#[derive(Debug)]
31pub struct SliceSink<'a> {
32    buf: &'a mut [u8],
33    len: usize,
34}
35
36impl<'a> SliceSink<'a> {
37    /// Wrap a buffer.
38    #[must_use]
39    pub fn new(buf: &'a mut [u8]) -> Self {
40        Self { buf, len: 0 }
41    }
42
43    /// Bytes written so far.
44    #[must_use]
45    pub const fn len(&self) -> usize {
46        self.len
47    }
48
49    /// Whether nothing has been written.
50    #[must_use]
51    pub const fn is_empty(&self) -> bool {
52        self.len == 0
53    }
54
55    /// The written prefix.
56    #[must_use]
57    pub fn as_slice(&self) -> &[u8] {
58        &self.buf[..self.len]
59    }
60
61    /// Consume the sink and return the written prefix.
62    #[must_use]
63    pub fn into_slice(self) -> &'a [u8] {
64        &self.buf[..self.len]
65    }
66}
67
68impl Sink for SliceSink<'_> {
69    type Error = WriteError;
70
71    #[inline]
72    fn write(&mut self, bytes: &[u8]) -> Result<(), WriteError> {
73        let end = self.len.checked_add(bytes.len()).ok_or(WriteError::BufferFull)?;
74        let dst = self.buf.get_mut(self.len..end).ok_or(WriteError::BufferFull)?;
75        dst.copy_from_slice(bytes);
76        self.len = end;
77        Ok(())
78    }
79}
80
81/// Counts bytes without storing them.
82///
83/// A cross-check for the generated `lcf_size` methods: writing into a `CountSink` must produce
84/// exactly what `lcf_size` predicted, or the two-pass writer would emit wrong chunk lengths.
85/// Cheap enough to assert on in tests for every fixture.
86#[derive(Clone, Copy, Debug, Default)]
87pub struct CountSink {
88    len: u64,
89}
90
91impl CountSink {
92    /// A fresh counter.
93    #[must_use]
94    pub const fn new() -> Self {
95        Self { len: 0 }
96    }
97
98    /// Bytes that would have been written.
99    #[must_use]
100    pub const fn len(&self) -> u64 {
101        self.len
102    }
103
104    /// Whether nothing would have been written.
105    #[must_use]
106    pub const fn is_empty(&self) -> bool {
107        self.len == 0
108    }
109}
110
111impl Sink for CountSink {
112    type Error = core::convert::Infallible;
113
114    #[inline]
115    fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
116        self.len += bytes.len() as u64;
117        Ok(())
118    }
119}
120
121#[cfg(feature = "alloc")]
122impl Sink for alloc::vec::Vec<u8> {
123    type Error = core::convert::Infallible;
124
125    #[inline]
126    fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
127        self.extend_from_slice(bytes);
128        Ok(())
129    }
130}
131
132/// Adapts any [`std::io::Write`] into a [`Sink`].
133#[cfg(feature = "std")]
134#[derive(Debug)]
135pub struct IoSink<W>(pub W);
136
137#[cfg(feature = "std")]
138impl<W: std::io::Write> Sink for IoSink<W> {
139    type Error = std::io::Error;
140
141    #[inline]
142    fn write(&mut self, bytes: &[u8]) -> Result<(), Self::Error> {
143        self.0.write_all(bytes)
144    }
145}