Skip to main content

mail_builder/
writer.rs

1use std::io::{self, Write};
2
3/// Byte sink used by every serializer in this crate.
4///
5/// `Vec<u8>` is the primary implementation; [`IoWriter`] adapts any
6/// [`std::io::Write`] with an internal buffer.
7pub trait Writer {
8    fn write(&mut self, bytes: &[u8]);
9
10    #[inline]
11    fn write_byte(&mut self, byte: u8) {
12        self.write(&[byte]);
13    }
14
15    #[inline]
16    fn reserve(&mut self, _additional: usize) {}
17
18    /// Hands the sink a scratch region of `len` bytes, keeps the first
19    /// `fill(region)` bytes of it and discards the rest.
20    fn write_with(&mut self, len: usize, fill: impl FnOnce(&mut [u8]) -> usize) {
21        let mut buffer = vec![0; len];
22        let written = fill(&mut buffer).min(len);
23        self.write(buffer.get(..written).unwrap_or_default());
24    }
25
26    /// Hands `append` a buffer to append at most `len` bytes to, passes what
27    /// it appended on to the sink and returns what `append` returned.
28    ///
29    /// Sinks backed by a `Vec<u8>` hand over that vector, so the encoders
30    /// write their output in place.
31    fn append_with<T>(&mut self, len: usize, append: impl FnOnce(&mut Vec<u8>) -> T) -> T {
32        let mut buffer = Vec::with_capacity(len);
33        let result = append(&mut buffer);
34        self.write(&buffer);
35        result
36    }
37
38    /// Largest `len` worth passing to [`Writer::append_with`] at once;
39    /// longer output is appended in pieces of at most this size.
40    #[inline]
41    fn append_limit(&self) -> usize {
42        IO_BUFFER_MAX
43    }
44}
45
46impl Writer for Vec<u8> {
47    #[inline(always)]
48    fn write(&mut self, bytes: &[u8]) {
49        self.extend_from_slice(bytes);
50    }
51
52    #[inline(always)]
53    fn write_byte(&mut self, byte: u8) {
54        self.push(byte);
55    }
56
57    #[inline]
58    fn reserve(&mut self, additional: usize) {
59        Vec::reserve(self, additional);
60    }
61
62    #[inline]
63    fn write_with(&mut self, len: usize, fill: impl FnOnce(&mut [u8]) -> usize) {
64        let start = self.len();
65        self.resize(start + len, 0);
66        let written = fill(self.get_mut(start..).unwrap_or_default()).min(len);
67        self.truncate(start + written);
68    }
69
70    #[inline(always)]
71    fn append_with<T>(&mut self, _len: usize, append: impl FnOnce(&mut Vec<u8>) -> T) -> T {
72        append(self)
73    }
74
75    #[inline(always)]
76    fn append_limit(&self) -> usize {
77        usize::MAX
78    }
79}
80
81impl<W: Writer + ?Sized> Writer for &mut W {
82    #[inline(always)]
83    fn write(&mut self, bytes: &[u8]) {
84        (**self).write(bytes);
85    }
86
87    #[inline(always)]
88    fn write_byte(&mut self, byte: u8) {
89        (**self).write_byte(byte);
90    }
91
92    #[inline]
93    fn reserve(&mut self, additional: usize) {
94        (**self).reserve(additional);
95    }
96
97    #[inline]
98    fn write_with(&mut self, len: usize, fill: impl FnOnce(&mut [u8]) -> usize) {
99        (**self).write_with(len, fill);
100    }
101
102    #[inline(always)]
103    fn append_with<T>(&mut self, len: usize, append: impl FnOnce(&mut Vec<u8>) -> T) -> T {
104        (**self).append_with(len, append)
105    }
106
107    #[inline(always)]
108    fn append_limit(&self) -> usize {
109        (**self).append_limit()
110    }
111}
112
113pub(crate) const IO_BUFFER_MAX: usize = 64 * 1024;
114pub(crate) const IO_BUFFER_MIN: usize = 4 * 1024;
115
116/// Buffered adapter that lets any [`std::io::Write`] act as a [`Writer`].
117///
118/// Errors are sticky: the first failure stops all further output and is
119/// returned by [`IoWriter::finish`]. Buffered bytes are only written by
120/// [`IoWriter::finish`] or [`IoWriter::into_result`]; dropping the adapter
121/// discards them.
122pub struct IoWriter<W: Write> {
123    inner: W,
124    buffer: Vec<u8>,
125    error: Option<io::Error>,
126}
127
128impl<W: Write> IoWriter<W> {
129    pub fn new(inner: W) -> Self {
130        Self::with_capacity(IO_BUFFER_MAX, inner)
131    }
132
133    pub fn with_capacity(capacity: usize, inner: W) -> Self {
134        IoWriter {
135            inner,
136            buffer: Vec::with_capacity(capacity.max(1)),
137            error: None,
138        }
139    }
140
141    fn write_inner(&mut self, bytes: &[u8]) {
142        if self.error.is_none()
143            && let Err(err) = self.inner.write_all(bytes)
144        {
145            self.error = Some(err);
146        }
147    }
148
149    fn flush_buffer(&mut self) {
150        if !self.buffer.is_empty() {
151            let buffer = std::mem::take(&mut self.buffer);
152            self.write_inner(&buffer);
153            self.buffer = buffer;
154            self.buffer.clear();
155        }
156    }
157
158    /// Flushes the buffer and returns the wrapped writer, or the first
159    /// error that occurred.
160    pub fn finish(mut self) -> io::Result<W> {
161        self.flush_buffer();
162        match self.error.take() {
163            Some(err) => Err(err),
164            None => Ok(self.inner),
165        }
166    }
167
168    pub fn into_result(self) -> io::Result<()> {
169        self.finish().map(|_| ())
170    }
171}
172
173impl<W: Write> Writer for IoWriter<W> {
174    #[inline]
175    fn write(&mut self, bytes: &[u8]) {
176        if bytes.len() > self.buffer.capacity() - self.buffer.len() {
177            self.flush_buffer();
178            if bytes.len() >= self.buffer.capacity() {
179                self.write_inner(bytes);
180                return;
181            }
182        }
183        self.buffer.extend_from_slice(bytes);
184    }
185
186    #[inline]
187    fn write_byte(&mut self, byte: u8) {
188        if self.buffer.len() == self.buffer.capacity() {
189            self.flush_buffer();
190        }
191        self.buffer.push(byte);
192    }
193
194    #[inline]
195    fn write_with(&mut self, len: usize, fill: impl FnOnce(&mut [u8]) -> usize) {
196        if len > self.buffer.capacity() - self.buffer.len() {
197            self.flush_buffer();
198        }
199        let start = self.buffer.len();
200        self.buffer.resize(start + len, 0);
201        let written = fill(self.buffer.get_mut(start..).unwrap_or_default()).min(len);
202        self.buffer.truncate(start + written);
203    }
204
205    #[inline]
206    fn append_with<T>(&mut self, len: usize, append: impl FnOnce(&mut Vec<u8>) -> T) -> T {
207        let capacity = self.buffer.capacity();
208        if len > capacity - self.buffer.len() {
209            self.flush_buffer();
210        }
211        let result = append(&mut self.buffer);
212        if self.buffer.capacity() > capacity {
213            self.flush_buffer();
214            self.buffer.shrink_to(capacity);
215        }
216        result
217    }
218
219    #[inline]
220    fn append_limit(&self) -> usize {
221        self.buffer.capacity()
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228
229    struct FailAfter(usize);
230
231    impl Write for FailAfter {
232        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
233            if self.0 == 0 {
234                Err(io::Error::other("full"))
235            } else {
236                self.0 = self.0.saturating_sub(buf.len());
237                Ok(buf.len())
238            }
239        }
240
241        fn flush(&mut self) -> io::Result<()> {
242            Ok(())
243        }
244    }
245
246    #[derive(Default)]
247    struct Chunks {
248        writes: Vec<Vec<u8>>,
249    }
250
251    impl Write for Chunks {
252        fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
253            self.writes.push(buf.to_vec());
254            Ok(buf.len())
255        }
256
257        fn flush(&mut self) -> io::Result<()> {
258            Ok(())
259        }
260    }
261
262    impl Chunks {
263        fn joined(&self) -> Vec<u8> {
264            self.writes.iter().flatten().copied().collect()
265        }
266    }
267
268    #[test]
269    fn io_writer_buffers_and_flushes_in_order() {
270        let mut writer = IoWriter::with_capacity(8, Vec::new());
271        writer.write(b"abc");
272        writer.write_byte(b'd');
273        writer.write(b"efghij");
274        writer.write_with(4, |region| {
275            region.copy_from_slice(b"KLMN");
276            2
277        });
278        writer.write(b"0123456789abcdef");
279        assert_eq!(writer.finish().unwrap(), b"abcdefghijKL0123456789abcdef");
280    }
281
282    #[test]
283    fn io_writer_reports_the_first_error() {
284        let mut writer = IoWriter::with_capacity(4, FailAfter(6));
285        writer.write(b"abcdef");
286        writer.write(b"ghij");
287        writer.write(b"klmn");
288        assert!(writer.into_result().is_err());
289    }
290
291    #[test]
292    fn io_writer_keeps_writing_after_the_first_error() {
293        let mut writer = IoWriter::with_capacity(4, FailAfter(0));
294        for _ in 0..100 {
295            writer.write(b"abcdefgh");
296            writer.write_byte(b'x');
297        }
298        let error = writer.into_result().expect_err("error expected");
299        assert_eq!(error.to_string(), "full");
300    }
301
302    #[test]
303    fn io_writer_passes_large_writes_through() {
304        let mut writer = IoWriter::with_capacity(8, Chunks::default());
305        writer.write(b"ab");
306        writer.write(b"0123456789");
307        writer.write(b"cd");
308        let chunks = writer.finish().unwrap();
309        assert_eq!(chunks.writes.len(), 3);
310        assert_eq!(chunks.writes.first().map(Vec::as_slice), Some(b"ab".as_ref()));
311        assert_eq!(chunks.writes.get(1).map(Vec::as_slice), Some(b"0123456789".as_ref()));
312        assert_eq!(chunks.joined(), b"ab0123456789cd");
313    }
314
315    #[test]
316    fn io_writer_byte_writes_never_exceed_the_buffer() {
317        let mut writer = IoWriter::with_capacity(4, Chunks::default());
318        for byte in b"abcdefghij" {
319            writer.write_byte(*byte);
320        }
321        let chunks = writer.finish().unwrap();
322        assert!(chunks.writes.iter().all(|chunk| chunk.len() <= 4), "{:?}", chunks.writes);
323        assert_eq!(chunks.joined(), b"abcdefghij");
324    }
325
326    #[test]
327    fn io_writer_write_with_grows_beyond_the_buffer() {
328        let mut writer = IoWriter::with_capacity(4, Chunks::default());
329        writer.write(b"ab");
330        writer.write_with(16, |region| {
331            for (slot, byte) in region.iter_mut().zip(b"0123456789".iter()) {
332                *slot = *byte;
333            }
334            10
335        });
336        writer.write(b"cd");
337        let chunks = writer.finish().unwrap();
338        assert_eq!(chunks.joined(), b"ab0123456789cd");
339    }
340
341    #[test]
342    fn io_writer_write_with_keeps_only_the_filled_prefix() {
343        let mut writer = IoWriter::with_capacity(64, Vec::new());
344        writer.write_with(8, |region| {
345            assert_eq!(region.len(), 8);
346            assert!(region.iter().all(|byte| *byte == 0));
347            region.iter_mut().for_each(|slot| *slot = b'z');
348            3
349        });
350        writer.write_with(8, |_| 0);
351        writer.write(b"!");
352        assert_eq!(writer.finish().unwrap(), b"zzz!");
353    }
354
355    #[test]
356    fn io_writer_write_with_caps_the_reported_length() {
357        let mut writer = IoWriter::with_capacity(64, Vec::new());
358        writer.write_with(4, |region| {
359            region.copy_from_slice(b"abcd");
360            usize::MAX
361        });
362        assert_eq!(writer.finish().unwrap(), b"abcd");
363    }
364
365    #[test]
366    fn vec_write_with_keeps_only_the_filled_prefix() {
367        let mut out = b"x".to_vec();
368        out.write_with(6, |region| {
369            region[..3].copy_from_slice(b"abc");
370            3
371        });
372        assert_eq!(out, b"xabc");
373    }
374
375    #[test]
376    fn vec_write_with_zeroes_the_region() {
377        let mut out = Vec::new();
378        out.write_with(8, |region| {
379            region.iter_mut().for_each(|slot| *slot = b'a');
380            8
381        });
382        out.truncate(4);
383        out.write_with(8, |region| {
384            assert!(region.iter().all(|byte| *byte == 0));
385            0
386        });
387        assert_eq!(out, b"aaaa");
388    }
389
390    #[test]
391    fn default_write_with_matches_the_vec_implementation() {
392        struct Plain(Vec<u8>);
393        impl Writer for Plain {
394            fn write(&mut self, bytes: &[u8]) {
395                self.0.extend_from_slice(bytes);
396            }
397        }
398
399        let mut plain = Plain(Vec::new());
400        plain.write_byte(b'a');
401        plain.write_with(6, |region| {
402            region.iter_mut().for_each(|slot| *slot = b'b');
403            4
404        });
405        assert_eq!(plain.0, b"abbbb");
406    }
407}