buffer_redux/
policy.rs

1//! Types which can be used to tune the behavior of `BufReader` and `BufWriter`.
2//!
3//! Some simple policies are provided for your convenience. You may prefer to create your own
4//! types and implement the traits for them instead.
5
6use super::Buffer;
7
8/// Flag for `ReaderPolicy` methods to signal whether or not `BufReader` should read into
9/// the buffer.
10///
11/// See `do_read!()` for a shorthand.
12#[derive(Copy, Clone, Debug)]
13pub struct DoRead(pub bool);
14
15/// Shorthand for `return DoRead(bool)` or `return DoRead(true)` (empty invocation)
16#[macro_export]
17macro_rules! do_read (
18    ($val:expr) => ( return $crate::policy::DoRead($val) );
19    () => ( do_read!(true); )
20);
21
22/// Default policy for both `BufReader` and `BufWriter` that reproduces the behaviors of their
23/// `std::io` counterparts:
24///
25/// * `BufReader`: only reads when the buffer is empty, does not resize or move data.
26/// * `BufWriter`: only flushes the buffer when there is not enough room for an incoming write.
27#[derive(Debug, Default)]
28pub struct StdPolicy;
29
30/// Trait that governs `BufReader`'s behavior.
31pub trait ReaderPolicy {
32    /// Consulted before attempting to read into the buffer.
33    ///
34    /// Return `DoRead(true)` to issue a read into the buffer before reading data out of it,
35    /// or `DoRead(false)` to read from the buffer as it is, even if it's empty.
36    /// `do_read!()` is provided as a shorthand.
37    ///
38    /// If there is no room in the buffer after this method is called,
39    /// the buffer will not be read into (so if the buffer is full but you want more data
40    /// you should call `.make_room()` or reserve more space). If there *is* room, `BufReader` will
41    /// attempt to read into the buffer. If successful (`Ok(x)` where `x > 0` is returned), this
42    /// method will be consulted again for another read attempt.
43    ///
44    /// By default, this implements `std::io::BufReader`'s behavior: only read into the buffer if
45    /// it is empty.
46    ///
47    /// ### Note
48    /// If the read will ignore the buffer entirely (if the buffer is empty and the amount to be
49    /// read matches or exceeds its capacity) or if `BufReader::read_into_buf()` was called to force
50    /// a read into the buffer manually, this method will not be called.
51    fn before_read(&mut self, buffer: &mut Buffer) -> DoRead {
52        DoRead(buffer.is_empty())
53    }
54
55    /// Called after bytes are consumed from the buffer.
56    ///
57    /// Supplies the true amount consumed if the amount passed to `BufReader::consume`
58    /// was in excess.
59    ///
60    /// This is a no-op by default.
61    fn after_consume(&mut self, _buffer: &mut Buffer, _amt: usize) {}
62}
63
64/// Behavior of `std::io::BufReader`: the buffer will only be read into if it is empty.
65impl ReaderPolicy for StdPolicy {}
66
67/// A policy for [`BufReader`](::BufReader) which ensures there is at least the given number of
68/// bytes in  the buffer, failing this only if the reader is at EOF.
69///
70/// If the minimum buffer length is greater than the buffer capacity, it will be resized.
71///
72/// ### Example
73/// ```rust
74/// use buffer_redux::BufReader;
75/// use buffer_redux::policy::MinBuffered;
76/// use std::io::{BufRead, Cursor};
77///
78/// let data = (1 .. 16).collect::<Vec<u8>>();
79///
80/// // normally you should use `BufReader::new()` or give a capacity of several KiB or more
81/// let mut reader = BufReader::with_capacity(8, Cursor::new(data))
82///     // always at least 4 bytes in the buffer (or until the source is empty)
83///     .set_policy(MinBuffered(4)); // always at least 4 bytes in the buffer
84///
85/// // first buffer fill, same as `std::io::BufReader`
86/// assert_eq!(reader.fill_buf().unwrap(), &[1, 2, 3, 4, 5, 6, 7, 8]);
87/// reader.consume(3);
88///
89/// // enough data in the buffer, another read isn't done yet
90/// assert_eq!(reader.fill_buf().unwrap(), &[4, 5, 6, 7, 8]);
91/// reader.consume(4);
92///
93/// // `std::io::BufReader` would return `&[8]`
94/// assert_eq!(reader.fill_buf().unwrap(), &[8, 9, 10, 11, 12, 13, 14, 15]);
95/// reader.consume(5);
96///
97/// // no data left in the reader
98/// assert_eq!(reader.fill_buf().unwrap(), &[13, 14, 15]);
99/// ```
100#[derive(Debug)]
101pub struct MinBuffered(pub usize);
102
103impl MinBuffered {
104    /// Set the number of bytes to ensure are in the buffer.
105    pub fn set_min(&mut self, min: usize) {
106        self.0 = min;
107    }
108}
109
110impl ReaderPolicy for MinBuffered {
111    fn before_read(&mut self, buffer: &mut Buffer) -> DoRead {
112        // do nothing if we have enough data
113        if buffer.len() >= self.0 {
114            do_read!(false)
115        }
116
117        let cap = buffer.capacity();
118
119        // if there's enough room but some of it's stuck after the head
120        if buffer.usable_space() < self.0 && buffer.free_space() >= self.0 {
121            buffer.make_room();
122        } else if cap < self.0 {
123            buffer.reserve(self.0 - cap);
124        }
125
126        DoRead(true)
127    }
128}
129
130/// Flag for `WriterPolicy` methods to tell `BufWriter` how many bytes to flush to the
131/// underlying reader.
132///
133/// See `flush_amt!()` for a shorthand.
134#[derive(Copy, Clone, Debug)]
135pub struct FlushAmt(pub usize);
136
137/// Shorthand for `return FlushAmt(n)` or `return FlushAmt(0)` (empty invocation)
138#[macro_export]
139macro_rules! flush_amt (
140    ($n:expr) => ( return $crate::policy::FlushAmt($n); );
141    () => ( flush_amt!(0); )
142);
143
144/// A trait which tells `BufWriter` when to flush.
145pub trait WriterPolicy {
146    /// Return `FlushAmt(n > 0)` if the buffer should be flushed before reading into it.
147    /// If the returned amount is 0 or greater than the amount of buffered data, no flush is
148    /// performed.
149    ///
150    /// The buffer is provided, as well as `incoming` which is
151    /// the size of the buffer that will be written to the `BufWriter`.
152    ///
153    /// By default, flushes the buffer if the usable space is smaller than the incoming write.
154    fn before_write(&mut self, buf: &mut Buffer, incoming: usize) -> FlushAmt {
155        FlushAmt(if incoming > buf.usable_space() {
156            buf.len()
157        } else {
158            0
159        })
160    }
161
162    /// Return `true` if the buffer should be flushed after reading into it.
163    ///
164    /// `buf` references the updated buffer after the read.
165    ///
166    /// Default impl is a no-op.
167    fn after_write(&mut self, _buf: &Buffer) -> FlushAmt {
168        FlushAmt(0)
169    }
170}
171
172/// Default behavior of `std::io::BufWriter`: flush before a read into the buffer
173/// only if the incoming data is larger than the buffer's writable space.
174impl WriterPolicy for StdPolicy {}
175
176/// Flush the buffer if it contains at least the given number of bytes.
177#[derive(Debug, Default)]
178pub struct FlushAtLeast(pub usize);
179
180impl WriterPolicy for FlushAtLeast {
181    fn before_write(&mut self, buf: &mut Buffer, incoming: usize) -> FlushAmt {
182        ensure_capacity(buf, self.0);
183        FlushAmt(if incoming > buf.usable_space() {
184            buf.len()
185        } else {
186            0
187        })
188    }
189
190    fn after_write(&mut self, buf: &Buffer) -> FlushAmt {
191        FlushAmt(::std::cmp::max(buf.len(), self.0))
192    }
193}
194
195/// Only ever flush exactly the given number of bytes, until the writer is empty.
196#[derive(Debug, Default)]
197pub struct FlushExact(pub usize);
198
199impl WriterPolicy for FlushExact {
200    /// Flushes the buffer if there is not enough room to fit `incoming` bytes,
201    /// but only when the buffer contains at least `self.0` bytes.
202    ///
203    /// Otherwise, calls [`Buffer::make_room()`](::Buffer::make_room)
204    fn before_write(&mut self, buf: &mut Buffer, incoming: usize) -> FlushAmt {
205        ensure_capacity(buf, self.0);
206
207        // don't have enough room to fit the additional bytes but we can't flush,
208        // then make room for (at least some of) the incoming bytes.
209        if incoming > buf.usable_space() && buf.len() < self.0 {
210            buf.make_room();
211        }
212
213        FlushAmt(self.0)
214    }
215
216    /// Flushes the given amount if possible, nothing otherwise.
217    fn after_write(&mut self, _buf: &Buffer) -> FlushAmt {
218        FlushAmt(self.0)
219    }
220}
221
222/// Flush the buffer if it contains the given byte.
223///
224/// Only scans the buffer after reading. Searches from the end first.
225#[derive(Debug, Default)]
226pub struct FlushOn(pub u8);
227
228impl WriterPolicy for FlushOn {
229    fn after_write(&mut self, buf: &Buffer) -> FlushAmt {
230        // include the delimiter in the flush
231        FlushAmt(memchr::memrchr(self.0, buf.buf()).map_or(0, |n| n + 1))
232    }
233}
234
235/// Flush the buffer if it contains a newline (`\n`).
236///
237/// Equivalent to `FlushOn(b'\n')`.
238#[derive(Debug, Default)]
239pub struct FlushOnNewline;
240
241impl WriterPolicy for FlushOnNewline {
242    fn after_write(&mut self, buf: &Buffer) -> FlushAmt {
243        FlushAmt(memchr::memrchr(b'\n', buf.buf()).map_or(0, |n| n + 1))
244    }
245}
246
247fn ensure_capacity(buf: &mut Buffer, min_cap: usize) {
248    let cap = buf.capacity();
249
250    if cap < min_cap {
251        buf.reserve(min_cap - cap);
252    }
253}
254
255#[cfg(test)]
256mod test {
257    use crate::policy::*;
258    use crate::{BufReader, BufWriter};
259    use std::io::{BufRead, Cursor, Write};
260
261    #[test]
262    fn test_min_buffered() {
263        let min_buffered = 4;
264        let data = (0..20).collect::<Vec<u8>>();
265        // create a reader with 0 capacity
266        let mut reader =
267            BufReader::with_capacity(0, Cursor::new(data)).set_policy(MinBuffered(min_buffered));
268
269        // policy reserves the required space in the buffer
270        assert_eq!(reader.fill_buf().unwrap(), &[0, 1, 2, 3][..]);
271        assert_eq!(reader.capacity(), min_buffered);
272
273        // double the size now that the buffer's full
274        reader.reserve(min_buffered);
275        assert_eq!(reader.capacity(), min_buffered * 2);
276
277        // we haven't consumed anything, the reader should have the same data
278        assert_eq!(reader.fill_buf().unwrap(), &[0, 1, 2, 3]);
279        reader.consume(2);
280        // policy read more data, `std::io::BufReader` doesn't do that
281        assert_eq!(reader.fill_buf().unwrap(), &[2, 3, 4, 5, 6, 7]);
282        reader.consume(4);
283        // policy made room and read more
284        assert_eq!(reader.fill_buf().unwrap(), &[6, 7, 8, 9, 10, 11, 12, 13]);
285        reader.consume(4);
286        assert_eq!(reader.fill_buf().unwrap(), &[10, 11, 12, 13]);
287        reader.consume(2);
288        assert_eq!(
289            reader.fill_buf().unwrap(),
290            &[12, 13, 14, 15, 16, 17, 18, 19]
291        );
292        reader.consume(8);
293        assert_eq!(reader.fill_buf().unwrap(), &[])
294    }
295
296    #[test]
297    fn test_flush_at_least() {
298        let flush_min = 4;
299
300        let mut writer = BufWriter::with_capacity(0, vec![]).set_policy(FlushAtLeast(flush_min));
301        assert_eq!(writer.capacity(), 0);
302        assert_eq!(writer.write(&[1]).unwrap(), 1);
303        // policy reserved space for writing
304        assert_eq!(writer.capacity(), flush_min);
305        // one byte in buffer, we want to double our capacity
306        writer.reserve(flush_min * 2 - 1);
307        assert_eq!(writer.capacity(), flush_min * 2);
308
309        assert_eq!(writer.write(&[2, 3]).unwrap(), 2);
310        // no flush yet, only 3 bytes in buffer
311        assert_eq!(*writer.get_ref(), &[]);
312
313        assert_eq!(writer.write(&[4, 5, 6]).unwrap(), 3);
314        // flushed all
315        assert_eq!(*writer.get_ref(), &[1, 2, 3, 4, 5, 6]);
316
317        assert_eq!(writer.write(&[7, 8, 9]).unwrap(), 3);
318        // `.into_inner()` should flush always
319        assert_eq!(writer.into_inner().unwrap(), &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
320    }
321
322    #[test]
323    fn test_flush_exact() {
324        let flush_exact = 4;
325
326        let mut writer = BufWriter::with_capacity(0, vec![]).set_policy(FlushExact(flush_exact));
327        assert_eq!(writer.capacity(), 0);
328        assert_eq!(writer.write(&[1]).unwrap(), 1);
329        // policy reserved space for writing
330        assert_eq!(writer.capacity(), flush_exact);
331        // one byte in buffer, we want to double our capacity
332        writer.reserve(flush_exact * 2 - 1);
333        assert_eq!(writer.capacity(), flush_exact * 2);
334
335        assert_eq!(writer.write(&[2, 3]).unwrap(), 2);
336        // no flush yet, only 3 bytes in buffer
337        assert_eq!(*writer.get_ref(), &[]);
338
339        assert_eq!(writer.write(&[4, 5, 6]).unwrap(), 3);
340        // flushed exactly 4 bytes
341        assert_eq!(*writer.get_ref(), &[1, 2, 3, 4]);
342
343        assert_eq!(writer.write(&[7, 8, 9, 10]).unwrap(), 4);
344        // flushed another 4 bytes
345        assert_eq!(*writer.get_ref(), &[1, 2, 3, 4, 5, 6, 7, 8]);
346        // `.into_inner()` should flush always
347        assert_eq!(
348            writer.into_inner().unwrap(),
349            &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
350        );
351    }
352
353    #[test]
354    fn test_flush_on() {
355        let mut writer = BufWriter::with_capacity(8, vec![]).set_policy(FlushOn(0));
356
357        assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
358        assert_eq!(*writer.get_ref(), &[]);
359
360        assert_eq!(writer.write(&[0, 4, 5]).unwrap(), 3);
361        assert_eq!(*writer.get_ref(), &[1, 2, 3, 0]);
362
363        assert_eq!(writer.write(&[6, 7, 8, 9, 10, 11, 12]).unwrap(), 7);
364        assert_eq!(*writer.get_ref(), &[1, 2, 3, 0, 4, 5]);
365
366        assert_eq!(writer.write(&[0]).unwrap(), 1);
367        assert_eq!(
368            *writer.get_ref(),
369            &[1, 2, 3, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0]
370        );
371    }
372
373    #[test]
374    fn test_flush_on_newline() {
375        let mut writer = BufWriter::with_capacity(8, vec![]).set_policy(FlushOnNewline);
376
377        assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3);
378        assert_eq!(*writer.get_ref(), &[]);
379
380        assert_eq!(writer.write(&[b'\n', 4, 5]).unwrap(), 3);
381        assert_eq!(*writer.get_ref(), &[1, 2, 3, b'\n']);
382
383        assert_eq!(writer.write(&[6, 7, 8, 9, b'\n', 11, 12]).unwrap(), 7);
384        assert_eq!(
385            *writer.get_ref(),
386            &[1, 2, 3, b'\n', 4, 5, 6, 7, 8, 9, b'\n']
387        );
388
389        assert_eq!(writer.write(&[b'\n']).unwrap(), 1);
390        assert_eq!(
391            *writer.get_ref(),
392            &[1, 2, 3, b'\n', 4, 5, 6, 7, 8, 9, b'\n', 11, 12, b'\n']
393        );
394    }
395}