/src/image/src/codecs/pnm/autobreak.rs
Line | Count | Source |
1 | | //! Insert line breaks between written buffers when they would overflow the line length. |
2 | | use std::io; |
3 | | |
4 | | // The pnm standard says to insert line breaks after 70 characters. Assumes that no line breaks |
5 | | // are actually written. We have to be careful to fully commit buffers or not commit them at all, |
6 | | // otherwise we might insert a newline in the middle of a token. |
7 | | pub(crate) struct AutoBreak<W: io::Write> { |
8 | | wrapped: W, |
9 | | line_capacity: usize, |
10 | | line: Vec<u8>, |
11 | | has_newline: bool, |
12 | | panicked: bool, // see https://github.com/rust-lang/rust/issues/30888 |
13 | | } |
14 | | |
15 | | impl<W: io::Write> AutoBreak<W> { |
16 | 0 | pub(crate) fn new(writer: W, line_capacity: usize) -> io::Result<Self> { |
17 | 0 | let mut line = Vec::new(); |
18 | 0 | line.try_reserve_exact(line_capacity + 1)?; |
19 | 0 | Ok(AutoBreak { |
20 | 0 | wrapped: writer, |
21 | 0 | line_capacity, |
22 | 0 | line, |
23 | 0 | has_newline: false, |
24 | 0 | panicked: false, |
25 | 0 | }) |
26 | 0 | } |
27 | | |
28 | 0 | fn flush_buf(&mut self) -> io::Result<()> { |
29 | | // from BufWriter |
30 | 0 | let mut written = 0; |
31 | 0 | let len = self.line.len(); |
32 | 0 | let mut ret = Ok(()); |
33 | 0 | while written < len { |
34 | 0 | self.panicked = true; |
35 | 0 | let r = self.wrapped.write(&self.line[written..]); |
36 | 0 | self.panicked = false; |
37 | 0 | match r { |
38 | | Ok(0) => { |
39 | 0 | ret = Err(io::Error::new( |
40 | 0 | io::ErrorKind::WriteZero, |
41 | 0 | "failed to write the buffered data", |
42 | 0 | )); |
43 | 0 | break; |
44 | | } |
45 | 0 | Ok(n) => written += n, |
46 | 0 | Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} |
47 | 0 | Err(e) => { |
48 | 0 | ret = Err(e); |
49 | 0 | break; |
50 | | } |
51 | | } |
52 | | } |
53 | 0 | if written > 0 { |
54 | 0 | self.line.drain(..written); |
55 | 0 | } |
56 | 0 | ret |
57 | 0 | } |
58 | | } |
59 | | |
60 | | impl<W: io::Write> io::Write for AutoBreak<W> { |
61 | 0 | fn write(&mut self, buffer: &[u8]) -> io::Result<usize> { |
62 | 0 | if self.has_newline { |
63 | 0 | self.flush()?; |
64 | 0 | self.has_newline = false; |
65 | 0 | } |
66 | | |
67 | 0 | if !self.line.is_empty() && self.line.len() + buffer.len() > self.line_capacity { |
68 | 0 | self.line.push(b'\n'); |
69 | 0 | self.has_newline = true; |
70 | 0 | self.flush()?; |
71 | 0 | self.has_newline = false; |
72 | 0 | } |
73 | | |
74 | 0 | self.line.extend_from_slice(buffer); |
75 | 0 | Ok(buffer.len()) |
76 | 0 | } |
77 | | |
78 | 0 | fn flush(&mut self) -> io::Result<()> { |
79 | 0 | self.flush_buf()?; |
80 | 0 | self.wrapped.flush() |
81 | 0 | } |
82 | | } |
83 | | |
84 | | impl<W: io::Write> Drop for AutoBreak<W> { |
85 | 0 | fn drop(&mut self) { |
86 | 0 | if !self.panicked { |
87 | 0 | let _r = self.flush_buf(); |
88 | 0 | // internal writer flushed automatically by Drop |
89 | 0 | } |
90 | 0 | } |
91 | | } |
92 | | |
93 | | #[cfg(test)] |
94 | | mod tests { |
95 | | use super::*; |
96 | | use std::io::Write; |
97 | | |
98 | | #[test] |
99 | | fn test_aligned_writes() { |
100 | | let mut output = Vec::new(); |
101 | | |
102 | | { |
103 | | let mut writer = AutoBreak::new(&mut output, 10).unwrap(); |
104 | | writer.write_all(b"0123456789").unwrap(); |
105 | | writer.write_all(b"0123456789").unwrap(); |
106 | | } |
107 | | |
108 | | assert_eq!(output.as_slice(), b"0123456789\n0123456789"); |
109 | | } |
110 | | |
111 | | #[test] |
112 | | fn test_greater_writes() { |
113 | | let mut output = Vec::new(); |
114 | | |
115 | | { |
116 | | let mut writer = AutoBreak::new(&mut output, 10).unwrap(); |
117 | | writer.write_all(b"012").unwrap(); |
118 | | writer.write_all(b"345").unwrap(); |
119 | | writer.write_all(b"0123456789").unwrap(); |
120 | | writer.write_all(b"012345678910").unwrap(); |
121 | | writer.write_all(b"_").unwrap(); |
122 | | } |
123 | | |
124 | | assert_eq!(output.as_slice(), b"012345\n0123456789\n012345678910\n_"); |
125 | | } |
126 | | } |