diff options
Diffstat (limited to 'vendor/gix-packetline-blocking/src/write')
-rw-r--r-- | vendor/gix-packetline-blocking/src/write/async_io.rs | 97 | ||||
-rw-r--r-- | vendor/gix-packetline-blocking/src/write/blocking_io.rs | 71 | ||||
-rw-r--r-- | vendor/gix-packetline-blocking/src/write/mod.rs | 21 |
3 files changed, 189 insertions, 0 deletions
diff --git a/vendor/gix-packetline-blocking/src/write/async_io.rs b/vendor/gix-packetline-blocking/src/write/async_io.rs new file mode 100644 index 000000000..19eaac16c --- /dev/null +++ b/vendor/gix-packetline-blocking/src/write/async_io.rs @@ -0,0 +1,97 @@ +use std::{ + io, + pin::Pin, + task::{Context, Poll}, +}; + +use futures_io::AsyncWrite; + +use crate::{encode, MAX_DATA_LEN, U16_HEX_BYTES}; + +pin_project_lite::pin_project! { + /// An implementor of [`Write`][io::Write] which passes all input to an inner `Write` in packet line data encoding, + /// one line per `write(…)` call or as many lines as it takes if the data doesn't fit into the maximum allowed line length. + pub struct Writer<T> { + #[pin] + inner: encode::LineWriter<'static, T>, + state: State, + } +} + +enum State { + Idle, + WriteData(usize), +} + +impl<T: AsyncWrite + Unpin> Writer<T> { + /// Create a new instance from the given `write` + pub fn new(write: T) -> Self { + Writer { + inner: encode::LineWriter::new(write, &[], &[]), + state: State::Idle, + } + } + + /// Return the inner writer, consuming self. + pub fn into_inner(self) -> T { + self.inner.into_inner() + } + + /// Return a mutable reference to the inner writer, useful if packet lines should be serialized directly. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner.writer + } +} + +/// Non-IO methods +impl<T> Writer<T> { + /// If called, each call to [`write()`][io::Write::write()] will write bytes as is. + pub fn enable_binary_mode(&mut self) { + self.inner.suffix = &[]; + } + /// If called, each call to [`write()`][io::Write::write()] will write the input as text, appending a trailing newline + /// if needed before writing. + pub fn enable_text_mode(&mut self) { + self.inner.suffix = &[b'\n']; + } +} + +impl<T: AsyncWrite + Unpin> AsyncWrite for Writer<T> { + fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> { + let mut this = self.project(); + loop { + match this.state { + State::Idle => { + if buf.is_empty() { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::Other, + "empty packet lines are not permitted as '0004' is invalid", + ))); + } + *this.state = State::WriteData(0) + } + State::WriteData(written) => { + while *written != buf.len() { + let data = &buf[*written..*written + (buf.len() - *written).min(MAX_DATA_LEN)]; + let n = futures_lite::ready!(this.inner.as_mut().poll_write(cx, data))?; + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *written += n; + *written -= U16_HEX_BYTES + this.inner.suffix.len(); + } + *this.state = State::Idle; + return Poll::Ready(Ok(buf.len())); + } + } + } + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { + self.project().inner.poll_flush(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { + self.project().inner.poll_close(cx) + } +} diff --git a/vendor/gix-packetline-blocking/src/write/blocking_io.rs b/vendor/gix-packetline-blocking/src/write/blocking_io.rs new file mode 100644 index 000000000..d537d7ddf --- /dev/null +++ b/vendor/gix-packetline-blocking/src/write/blocking_io.rs @@ -0,0 +1,71 @@ +use std::io; + +use crate::{MAX_DATA_LEN, U16_HEX_BYTES}; + +/// An implementor of [`Write`][io::Write] which passes all input to an inner `Write` in packet line data encoding, +/// one line per `write(…)` call or as many lines as it takes if the data doesn't fit into the maximum allowed line length. +pub struct Writer<T> { + /// the `Write` implementation to which to propagate packet lines + inner: T, + binary: bool, +} + +impl<T: io::Write> Writer<T> { + /// Create a new instance from the given `write` + pub fn new(write: T) -> Self { + Writer { + inner: write, + binary: true, + } + } +} + +/// Non-IO methods +impl<T> Writer<T> { + /// If called, each call to [`write()`][io::Write::write()] will write bytes as is. + pub fn enable_binary_mode(&mut self) { + self.binary = true; + } + /// If called, each call to [`write()`][io::Write::write()] will write the input as text, appending a trailing newline + /// if needed before writing. + pub fn enable_text_mode(&mut self) { + self.binary = false; + } + /// Return the inner writer, consuming self. + pub fn into_inner(self) -> T { + self.inner + } + /// Return a mutable reference to the inner writer, useful if packet lines should be serialized directly. + pub fn inner_mut(&mut self) -> &mut T { + &mut self.inner + } +} + +impl<T: io::Write> io::Write for Writer<T> { + fn write(&mut self, mut buf: &[u8]) -> io::Result<usize> { + if buf.is_empty() { + return Err(io::Error::new( + io::ErrorKind::Other, + "empty packet lines are not permitted as '0004' is invalid", + )); + } + + let mut written = 0; + while !buf.is_empty() { + let (data, rest) = buf.split_at(buf.len().min(MAX_DATA_LEN)); + written += if self.binary { + crate::encode::data_to_write(data, &mut self.inner) + } else { + crate::encode::text_to_write(data, &mut self.inner) + }?; + // subtract header (and trailing NL) because write-all can't handle writing more than it passes in + written -= U16_HEX_BYTES + usize::from(!self.binary); + buf = rest; + } + Ok(written) + } + + fn flush(&mut self) -> io::Result<()> { + self.inner.flush() + } +} diff --git a/vendor/gix-packetline-blocking/src/write/mod.rs b/vendor/gix-packetline-blocking/src/write/mod.rs new file mode 100644 index 000000000..f40a6bdae --- /dev/null +++ b/vendor/gix-packetline-blocking/src/write/mod.rs @@ -0,0 +1,21 @@ +use crate::Writer; + +#[cfg(all(not(feature = "blocking-io"), feature = "async-io"))] +pub(crate) mod async_io; + +#[cfg(feature = "blocking-io")] +pub(crate) mod blocking_io; + +/// Common methods +impl<T> Writer<T> { + /// As [`enable_text_mode()`][Writer::enable_text_mode()], but suitable for chaining. + pub fn text_mode(mut self) -> Self { + self.enable_text_mode(); + self + } + /// As [`enable_binary_mode()`][Writer::enable_binary_mode()], but suitable for chaining. + pub fn binary_mode(mut self) -> Self { + self.enable_binary_mode(); + self + } +} |