aboutsummaryrefslogtreecommitdiff
path: root/vendor/image/src/codecs/pnm/autobreak.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
committerValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
commit1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch)
tree7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/image/src/codecs/pnm/autobreak.rs
parent5ecd8cf2cba827454317368b68571df0d13d7842 (diff)
downloadfparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz
fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/image/src/codecs/pnm/autobreak.rs')
-rw-r--r--vendor/image/src/codecs/pnm/autobreak.rs124
1 files changed, 124 insertions, 0 deletions
diff --git a/vendor/image/src/codecs/pnm/autobreak.rs b/vendor/image/src/codecs/pnm/autobreak.rs
new file mode 100644
index 0000000..cea2cd8
--- /dev/null
+++ b/vendor/image/src/codecs/pnm/autobreak.rs
@@ -0,0 +1,124 @@
+//! Insert line breaks between written buffers when they would overflow the line length.
+use std::io;
+
+// The pnm standard says to insert line breaks after 70 characters. Assumes that no line breaks
+// are actually written. We have to be careful to fully commit buffers or not commit them at all,
+// otherwise we might insert a newline in the middle of a token.
+pub(crate) struct AutoBreak<W: io::Write> {
+ wrapped: W,
+ line_capacity: usize,
+ line: Vec<u8>,
+ has_newline: bool,
+ panicked: bool, // see https://github.com/rust-lang/rust/issues/30888
+}
+
+impl<W: io::Write> AutoBreak<W> {
+ pub(crate) fn new(writer: W, line_capacity: usize) -> Self {
+ AutoBreak {
+ wrapped: writer,
+ line_capacity,
+ line: Vec::with_capacity(line_capacity + 1),
+ has_newline: false,
+ panicked: false,
+ }
+ }
+
+ fn flush_buf(&mut self) -> io::Result<()> {
+ // from BufWriter
+ let mut written = 0;
+ let len = self.line.len();
+ let mut ret = Ok(());
+ while written < len {
+ self.panicked = true;
+ let r = self.wrapped.write(&self.line[written..]);
+ self.panicked = false;
+ match r {
+ Ok(0) => {
+ ret = Err(io::Error::new(
+ io::ErrorKind::WriteZero,
+ "failed to write the buffered data",
+ ));
+ break;
+ }
+ Ok(n) => written += n,
+ Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
+ Err(e) => {
+ ret = Err(e);
+ break;
+ }
+ }
+ }
+ if written > 0 {
+ self.line.drain(..written);
+ }
+ ret
+ }
+}
+
+impl<W: io::Write> io::Write for AutoBreak<W> {
+ fn write(&mut self, buffer: &[u8]) -> io::Result<usize> {
+ if self.has_newline {
+ self.flush()?;
+ self.has_newline = false;
+ }
+
+ if !self.line.is_empty() && self.line.len() + buffer.len() > self.line_capacity {
+ self.line.push(b'\n');
+ self.has_newline = true;
+ self.flush()?;
+ self.has_newline = false;
+ }
+
+ self.line.extend_from_slice(buffer);
+ Ok(buffer.len())
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.flush_buf()?;
+ self.wrapped.flush()
+ }
+}
+
+impl<W: io::Write> Drop for AutoBreak<W> {
+ fn drop(&mut self) {
+ if !self.panicked {
+ let _r = self.flush_buf();
+ // internal writer flushed automatically by Drop
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use std::io::Write;
+
+ #[test]
+ fn test_aligned_writes() {
+ let mut output = Vec::new();
+
+ {
+ let mut writer = AutoBreak::new(&mut output, 10);
+ writer.write_all(b"0123456789").unwrap();
+ writer.write_all(b"0123456789").unwrap();
+ }
+
+ assert_eq!(output.as_slice(), b"0123456789\n0123456789");
+ }
+
+ #[test]
+ fn test_greater_writes() {
+ let mut output = Vec::new();
+
+ {
+ let mut writer = AutoBreak::new(&mut output, 10);
+ writer.write_all(b"012").unwrap();
+ writer.write_all(b"345").unwrap();
+ writer.write_all(b"0123456789").unwrap();
+ writer.write_all(b"012345678910").unwrap();
+ writer.write_all(b"_").unwrap();
+ }
+
+ assert_eq!(output.as_slice(), b"012345\n0123456789\n012345678910\n_");
+ }
+}