summaryrefslogtreecommitdiffstats
path: root/vendor/anstream/src/strip.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/anstream/src/strip.rs')
-rw-r--r--vendor/anstream/src/strip.rs196
1 files changed, 196 insertions, 0 deletions
diff --git a/vendor/anstream/src/strip.rs b/vendor/anstream/src/strip.rs
new file mode 100644
index 000000000..0dd017497
--- /dev/null
+++ b/vendor/anstream/src/strip.rs
@@ -0,0 +1,196 @@
+use crate::adapter::StripBytes;
+use crate::Lockable;
+use crate::RawStream;
+
+/// Only pass printable data to the inner `Write`
+#[derive(Debug)]
+pub struct StripStream<S> {
+ raw: S,
+ state: StripBytes,
+}
+
+impl<S> StripStream<S>
+where
+ S: RawStream,
+{
+ /// Only pass printable data to the inner `Write`
+ #[inline]
+ pub fn new(raw: S) -> Self {
+ Self {
+ raw,
+ state: Default::default(),
+ }
+ }
+
+ /// Get the wrapped [`RawStream`]
+ #[inline]
+ pub fn into_inner(self) -> S {
+ self.raw
+ }
+
+ #[inline]
+ #[cfg(feature = "auto")]
+ pub fn is_terminal(&self) -> bool {
+ self.raw.is_terminal()
+ }
+}
+
+#[cfg(feature = "auto")]
+impl<S> is_terminal::IsTerminal for StripStream<S>
+where
+ S: RawStream,
+{
+ #[inline]
+ fn is_terminal(&self) -> bool {
+ self.is_terminal()
+ }
+}
+
+impl<S> std::io::Write for StripStream<S>
+where
+ S: RawStream,
+{
+ #[inline]
+ fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+ write(&mut self.raw, &mut self.state, buf)
+ }
+
+ #[inline]
+ fn flush(&mut self) -> std::io::Result<()> {
+ self.raw.flush()
+ }
+
+ // Provide explicit implementations of trait methods
+ // - To reduce bookkeeping
+ // - Avoid acquiring / releasing locks in a loop
+
+ #[inline]
+ fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
+ write_all(&mut self.raw, &mut self.state, buf)
+ }
+
+ // Not bothering with `write_fmt` as it just calls `write_all`
+}
+
+fn write(
+ raw: &mut dyn std::io::Write,
+ state: &mut StripBytes,
+ buf: &[u8],
+) -> std::io::Result<usize> {
+ let initial_state = state.clone();
+
+ for printable in state.strip_next(buf) {
+ let possible = printable.len();
+ let written = raw.write(printable)?;
+ if possible != written {
+ let divergence = &printable[written..];
+ let offset = offset_to(buf, divergence);
+ let consumed = &buf[offset..];
+ *state = initial_state;
+ state.strip_next(consumed).last();
+ return Ok(offset);
+ }
+ }
+ Ok(buf.len())
+}
+
+fn write_all(
+ raw: &mut dyn std::io::Write,
+ state: &mut StripBytes,
+ buf: &[u8],
+) -> std::io::Result<()> {
+ for printable in state.strip_next(buf) {
+ raw.write_all(printable)?;
+ }
+ Ok(())
+}
+
+#[inline]
+fn offset_to(total: &[u8], subslice: &[u8]) -> usize {
+ let total = total.as_ptr();
+ let subslice = subslice.as_ptr();
+
+ debug_assert!(
+ total <= subslice,
+ "`Offset::offset_to` only accepts slices of `self`"
+ );
+ subslice as usize - total as usize
+}
+
+impl<S> Lockable for StripStream<S>
+where
+ S: Lockable,
+{
+ type Locked = StripStream<<S as Lockable>::Locked>;
+
+ #[inline]
+ fn lock(self) -> Self::Locked {
+ Self::Locked {
+ raw: self.raw.lock(),
+ state: self.state,
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use proptest::prelude::*;
+ use std::io::Write as _;
+
+ proptest! {
+ #[test]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn write_all_no_escapes(s in "\\PC*") {
+ let buffer = crate::Buffer::new();
+ let mut stream = StripStream::new(buffer);
+ stream.write_all(s.as_bytes()).unwrap();
+ let buffer = stream.into_inner();
+ let actual = std::str::from_utf8(buffer.as_ref()).unwrap();
+ assert_eq!(s, actual);
+ }
+
+ #[test]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn write_byte_no_escapes(s in "\\PC*") {
+ let buffer = crate::Buffer::new();
+ let mut stream = StripStream::new(buffer);
+ for byte in s.as_bytes() {
+ stream.write_all(&[*byte]).unwrap();
+ }
+ let buffer = stream.into_inner();
+ let actual = std::str::from_utf8(buffer.as_ref()).unwrap();
+ assert_eq!(s, actual);
+ }
+
+ #[test]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn write_all_random(s in any::<Vec<u8>>()) {
+ let buffer = crate::Buffer::new();
+ let mut stream = StripStream::new(buffer);
+ stream.write_all(s.as_slice()).unwrap();
+ let buffer = stream.into_inner();
+ if let Ok(actual) = std::str::from_utf8(buffer.as_ref()) {
+ for char in actual.chars() {
+ assert!(!char.is_ascii() || !char.is_control() || char.is_ascii_whitespace(), "{:?} -> {:?}: {:?}", String::from_utf8_lossy(&s), actual, char);
+ }
+ }
+ }
+
+ #[test]
+ #[cfg_attr(miri, ignore)] // See https://github.com/AltSysrq/proptest/issues/253
+ fn write_byte_random(s in any::<Vec<u8>>()) {
+ let buffer = crate::Buffer::new();
+ let mut stream = StripStream::new(buffer);
+ for byte in s.as_slice() {
+ stream.write_all(&[*byte]).unwrap();
+ }
+ let buffer = stream.into_inner();
+ if let Ok(actual) = std::str::from_utf8(buffer.as_ref()) {
+ for char in actual.chars() {
+ assert!(!char.is_ascii() || !char.is_control() || char.is_ascii_whitespace(), "{:?} -> {:?}: {:?}", String::from_utf8_lossy(&s), actual, char);
+ }
+ }
+ }
+ }
+}