From 54fdcb798b287757b4ed1d51db8e00f961f3056a Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Thu, 15 Aug 2024 20:48:48 +0200 Subject: [PATCH 1/6] Add `std` -> `bitcoin-io` bridge When we've removed the blanket impl we forgot to replace it with a bridge that would make the usage with `std` types easy. This change implements the missing bridge forwarding all the methods and traits. --- io/src/bridge.rs | 129 +++++++++++++++++++++++++++++++++++++++++++++++ io/src/lib.rs | 23 +++++++++ 2 files changed, 152 insertions(+) create mode 100644 io/src/bridge.rs diff --git a/io/src/bridge.rs b/io/src/bridge.rs new file mode 100644 index 0000000000..45b0b8cb7e --- /dev/null +++ b/io/src/bridge.rs @@ -0,0 +1,129 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +/// A bridging wrapper providing the IO traits for types that already implement `std` IO traits. +#[repr(transparent)] +pub struct FromStd(T); + +impl FromStd { + /// Wraps an IO type. + #[inline] + pub const fn new(inner: T) -> Self { Self(inner) } + + /// Returns the wrapped value. + #[inline] + pub fn into_inner(self) -> T { + self.0 + } + + /// Returns a reference to the wrapped value. + #[inline] + pub fn inner(&self) -> &T { + &self.0 + } + + /// Returns a mutable reference to the wrapped value. + #[inline] + pub fn inner_mut(&mut self) -> &mut T { + &mut self.0 + } + + /// Wraps a mutable reference to IO type. + #[inline] + pub fn new_mut(inner: &mut T) -> &mut Self { + // SAFETY: the type is repr(transparent) and the lifetimes match + unsafe { &mut *(inner as *mut _ as *mut Self) } + } + + /// Wraps a boxed IO type. + #[cfg(feature = "alloc")] + #[inline] + pub fn new_boxed(inner: Box) -> Box { + // SAFETY: the type is repr(transparent) and the pointer is created from Box + unsafe { Box::from_raw(Box::into_raw(inner) as *mut Self) } + } +} + +impl super::Read for FromStd { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> super::Result { + self.0.read(buf).map_err(Into::into) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> super::Result<()> { + self.0.read_exact(buf).map_err(Into::into) + } +} + +impl super::BufRead for FromStd { + #[inline] + fn fill_buf(&mut self) -> super::Result<&[u8]> { + self.0.fill_buf().map_err(Into::into) + } + + #[inline] + fn consume(&mut self, amount: usize) { + self.0.consume(amount) + } +} + +impl super::Write for FromStd { + #[inline] + fn write(&mut self, buf: &[u8]) -> super::Result { + self.0.write(buf).map_err(Into::into) + } + + #[inline] + fn flush(&mut self) -> super::Result<()> { + self.0.flush().map_err(Into::into) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> super::Result<()> { + self.0.write_all(buf).map_err(Into::into) + } +} + +// We also impl std traits so that mixing the calls is not annoying. + +impl std::io::Read for FromStd { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.0.read(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> { + self.0.read_exact(buf) + } +} + +impl std::io::BufRead for FromStd { + #[inline] + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.0.fill_buf() + } + + #[inline] + fn consume(&mut self, amount: usize) { + self.0.consume(amount) + } +} + +impl std::io::Write for FromStd { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.0.write(buf) + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + self.0.flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { + self.0.write_all(buf) + } +} diff --git a/io/src/lib.rs b/io/src/lib.rs index e7878625e2..2bf1bfd47e 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -23,6 +23,11 @@ extern crate alloc; mod error; mod macros; +#[cfg(feature = "std")] +mod bridge; + +#[cfg(feature = "std")] +pub use bridge::FromStd; #[cfg(all(not(feature = "std"), feature = "alloc"))] use alloc::vec::Vec; @@ -320,6 +325,24 @@ impl std::io::Write for Sink { #[inline] pub fn sink() -> Sink { Sink } +/// Wraps a `std` IO type to implement the traits from this crate. +/// +/// All methods are passed through converting the errors. +#[cfg(feature = "std")] +#[inline] +pub const fn from_std(std_io: T) -> FromStd { + FromStd::new(std_io) +} + +/// Wraps a mutable reference to `std` IO type to implement the traits from this crate. +/// +/// All methods are passed through converting the errors. +#[cfg(feature = "std")] +#[inline] +pub fn from_std_mut(std_io: &mut T) -> &mut FromStd { + FromStd::new_mut(std_io) +} + #[cfg(test)] mod tests { #[cfg(all(not(feature = "std"), feature = "alloc"))] From fc7e213f216ca91b40164a37611c0cab5633803a Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Thu, 15 Aug 2024 21:23:51 +0200 Subject: [PATCH 2/6] Add `bitcoin-io` -> `std` bridge This adds a reverse of the previous commit however it does not add convenience methods since user should generally prefer the macro instead. I had a clever idea to add a convenience conversion trait but that is blocked by Rust stabilizing overlapping *marker* trait impls. --- io/src/bridge.rs | 127 +++++++++++++++++++++++++++++++++++++++++++++++ io/src/lib.rs | 2 +- 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/io/src/bridge.rs b/io/src/bridge.rs index 45b0b8cb7e..cba47e6bc8 100644 --- a/io/src/bridge.rs +++ b/io/src/bridge.rs @@ -127,3 +127,130 @@ impl std::io::Write for FromStd { self.0.write_all(buf) } } + +/// A bridging wrapper providing the std traits for types that already implement our traits. +#[repr(transparent)] +pub struct ToStd(T); + +impl ToStd { + /// Wraps an IO type. + #[inline] + pub const fn new(inner: T) -> Self { Self(inner) } + + /// Returns the wrapped value. + #[inline] + pub fn into_inner(self) -> T { + self.0 + } + + /// Returns a reference to the wrapped value. + #[inline] + pub fn inner(&self) -> &T { + &self.0 + } + + /// Returns a mutable reference to the wrapped value. + #[inline] + pub fn inner_mut(&mut self) -> &mut T { + &mut self.0 + } + + /// Wraps a mutable reference to IO type. + #[inline] + pub fn new_mut(inner: &mut T) -> &mut Self { + // SAFETY: the type is repr(transparent) and the lifetimes match + unsafe { &mut *(inner as *mut _ as *mut Self) } + } + + /// Wraps a boxed IO type. + #[cfg(feature = "alloc")] + #[inline] + pub fn new_boxed(inner: Box) -> Box { + // SAFETY: the type is repr(transparent) and the pointer is created from Box + unsafe { Box::from_raw(Box::into_raw(inner) as *mut Self) } + } +} + +impl std::io::Read for ToStd { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + self.0.read(buf).map_err(Into::into) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> { + self.0.read_exact(buf).map_err(Into::into) + } +} + +impl std::io::BufRead for ToStd { + #[inline] + fn fill_buf(&mut self) -> std::io::Result<&[u8]> { + self.0.fill_buf().map_err(Into::into) + } + + #[inline] + fn consume(&mut self, amount: usize) { + self.0.consume(amount) + } +} + +impl std::io::Write for ToStd { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { + self.0.write(buf).map_err(Into::into) + } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { + self.0.flush().map_err(Into::into) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { + self.0.write_all(buf).map_err(Into::into) + } +} + +// We also impl our traits so that mixing the calls is not annoying. + +impl super::Read for ToStd { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> super::Result { + self.0.read(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> super::Result<()> { + self.0.read_exact(buf) + } +} + +impl super::BufRead for ToStd { + #[inline] + fn fill_buf(&mut self) -> super::Result<&[u8]> { + self.0.fill_buf() + } + + #[inline] + fn consume(&mut self, amount: usize) { + self.0.consume(amount) + } +} + +impl super::Write for ToStd { + #[inline] + fn write(&mut self, buf: &[u8]) -> super::Result { + self.0.write(buf) + } + + #[inline] + fn flush(&mut self) -> super::Result<()> { + self.0.flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> super::Result<()> { + self.0.write_all(buf) + } +} diff --git a/io/src/lib.rs b/io/src/lib.rs index 2bf1bfd47e..581d732897 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -27,7 +27,7 @@ mod macros; mod bridge; #[cfg(feature = "std")] -pub use bridge::FromStd; +pub use bridge::{FromStd, ToStd}; #[cfg(all(not(feature = "std"), feature = "alloc"))] use alloc::vec::Vec; From 94768d3f7094297e807f14e553e5b981469ca223 Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Thu, 15 Aug 2024 21:43:01 +0200 Subject: [PATCH 3/6] Add `set_position` method to `Cursor` Without the ability to seek, the `Cursor` type is only useful for wrapping owned buffers. In `std` the equivalent type also allows seeking. We currently do not have the `Seek` trait so this simply adds a method to set the position. Further, this adds a method to access inner type so seeking from the end can be implemented (to compute the position relative to the end). Closes #3174 --- io/src/lib.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/io/src/lib.rs b/io/src/lib.rs index 581d732897..8de9f28d99 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -202,11 +202,28 @@ impl> Cursor { #[inline] pub fn position(&self) -> u64 { self.pos } + /// Sets the internal position. + /// + /// This method allows seeking within the wrapped memory by setting the position. + /// + /// Note that setting a position that is larger than the buffer length will cause reads to + /// return no bytes (EOF). + #[inline] + pub fn set_position(&mut self, position: u64) { + self.pos = position; + } + /// Returns the inner buffer. /// /// This is the whole wrapped buffer, including the bytes already read. #[inline] pub fn into_inner(self) -> T { self.inner } + + /// Returns a reference to the inner buffer. + /// + /// This is the whole wrapped buffer, including the bytes already read. + #[inline] + pub fn inner(&self) -> &T { &self.inner } } impl> Read for Cursor { From 505ecd8a2eae1af98bd314c520f2d5616f59c825 Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Fri, 16 Aug 2024 09:43:31 +0200 Subject: [PATCH 4/6] Move `std` impl from `lib.rs` to `bridge.rs` This reduces the number of compilation conditions. --- io/src/bridge.rs | 32 ++++++++++++++++++++++++++++++++ io/src/lib.rs | 36 ------------------------------------ 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/io/src/bridge.rs b/io/src/bridge.rs index cba47e6bc8..4ff12fed16 100644 --- a/io/src/bridge.rs +++ b/io/src/bridge.rs @@ -254,3 +254,35 @@ impl super::Write for ToStd { self.0.write_all(buf) } } + +impl super::Read for std::io::BufReader { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> super::Result { Ok(std::io::Read::read(self, buf)?) } +} + +impl super::BufRead for std::io::BufReader { + #[inline] + fn fill_buf(&mut self) -> super::Result<&[u8]> { Ok(std::io::BufRead::fill_buf(self)?) } + + #[inline] + fn consume(&mut self, amount: usize) { std::io::BufRead::consume(self, amount) } +} + +impl std::io::Write for super::Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> std::io::Result { Ok(buf.len()) } + + #[inline] + fn write_all(&mut self, _: &[u8]) -> std::io::Result<()> { Ok(()) } + + #[inline] + fn flush(&mut self) -> std::io::Result<()> { Ok(()) } +} + +impl super::Write for std::io::BufWriter { + #[inline] + fn write(&mut self, buf: &[u8]) -> super::Result { Ok(std::io::Write::write(self, buf)?) } + + #[inline] + fn flush(&mut self) -> super::Result<()> { Ok(std::io::Write::flush(self)?) } +} diff --git a/io/src/lib.rs b/io/src/lib.rs index 8de9f28d99..1fc5dd53e2 100644 --- a/io/src/lib.rs +++ b/io/src/lib.rs @@ -163,12 +163,6 @@ impl Read for &[u8] { } } -#[cfg(feature = "std")] -impl Read for std::io::BufReader { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> Result { Ok(std::io::Read::read(self, buf)?) } -} - impl BufRead for &[u8] { #[inline] fn fill_buf(&mut self) -> Result<&[u8]> { Ok(self) } @@ -178,15 +172,6 @@ impl BufRead for &[u8] { fn consume(&mut self, amount: usize) { *self = &self[amount..] } } -#[cfg(feature = "std")] -impl BufRead for std::io::BufReader { - #[inline] - fn fill_buf(&mut self) -> Result<&[u8]> { Ok(std::io::BufRead::fill_buf(self)?) } - - #[inline] - fn consume(&mut self, amount: usize) { std::io::BufRead::consume(self, amount) } -} - /// Wraps an in memory reader providing the `position` function. pub struct Cursor { inner: T, @@ -301,15 +286,6 @@ impl<'a> Write for &'a mut [u8] { fn flush(&mut self) -> Result<()> { Ok(()) } } -#[cfg(feature = "std")] -impl Write for std::io::BufWriter { - #[inline] - fn write(&mut self, buf: &[u8]) -> Result { Ok(std::io::Write::write(self, buf)?) } - - #[inline] - fn flush(&mut self) -> Result<()> { Ok(std::io::Write::flush(self)?) } -} - /// A sink to which all writes succeed. See [`std::io::Sink`] for more info. /// /// Created using `io::sink()`. @@ -326,18 +302,6 @@ impl Write for Sink { fn flush(&mut self) -> Result<()> { Ok(()) } } -#[cfg(feature = "std")] -impl std::io::Write for Sink { - #[inline] - fn write(&mut self, buf: &[u8]) -> std::io::Result { Ok(buf.len()) } - - #[inline] - fn write_all(&mut self, _: &[u8]) -> std::io::Result<()> { Ok(()) } - - #[inline] - fn flush(&mut self) -> std::io::Result<()> { Ok(()) } -} - /// Returns a sink to which all writes succeed. See [`std::io::sink`] for more info. #[inline] pub fn sink() -> Sink { Sink } From 5e30c9f1906bd801742d6e50a89ce62a6de9cdbe Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Fri, 16 Aug 2024 10:09:04 +0200 Subject: [PATCH 5/6] Use macro to implement our traits for `std` types We want to implement the traits for more types which would be tedious without a macro and it'd risk forgetting to forward some method since they may be speialized. This also adds previously-forgotten specializations. It also relaxes the implicit `Sized` bound on `BufReader` and `BufWriter` in Rust versions above 1.72. --- io/Cargo.toml | 2 +- io/build.rs | 37 +++++++++++++++++++++ io/src/bridge.rs | 84 ++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 io/build.rs diff --git a/io/Cargo.toml b/io/Cargo.toml index 292e5022c4..852e465c5d 100644 --- a/io/Cargo.toml +++ b/io/Cargo.toml @@ -23,4 +23,4 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [lints.rust] -unexpected_cfgs = { level = "deny" } +unexpected_cfgs = { level = "deny", check-cfg = ['cfg(rust_v_1_72)'] } diff --git a/io/build.rs b/io/build.rs new file mode 100644 index 0000000000..2b741ca0d4 --- /dev/null +++ b/io/build.rs @@ -0,0 +1,37 @@ +fn main() { + let rustc = std::env::var_os("RUSTC"); + let rustc = rustc.as_ref().map(std::path::Path::new).unwrap_or_else(|| "rustc".as_ref()); + let output = std::process::Command::new(rustc) + .arg("--version") + .output() + .unwrap_or_else(|error| panic!("failed to run `{:?} --version`: {:?}", rustc, error)); + assert!(output.status.success(), "{:?} -- version returned non-zero exit code", rustc); + let stdout = String::from_utf8(output.stdout).expect("rustc produced non-UTF-8 output"); + let version_prefix = "rustc "; + if !stdout.starts_with(version_prefix) { + panic!("unexpected rustc output: {}", stdout); + } + + let version = &stdout[version_prefix.len()..]; + let end = version.find(&[' ', '-'] as &[_]).unwrap_or(version.len()); + let version = &version[..end]; + let mut version_components = version.split('.'); + let major = version_components.next().unwrap(); + assert_eq!(major, "1", "unexpected Rust major version"); + let minor = version_components + .next() + .unwrap_or("0") + .parse::() + .expect("invalid Rust minor version"); + + let msrv = std::env::var("CARGO_PKG_RUST_VERSION").unwrap(); + let mut msrv = msrv.split("."); + let msrv_major = msrv.next().unwrap(); + assert_eq!(msrv_major, "1", "unexpected Rust major version"); + let msrv_minor = msrv.next().unwrap().parse::().unwrap(); + + // print cfg for all interesting versions less than or equal to minor + for version in msrv_minor..=minor { + println!("cargo:rustc-cfg=rust_v_1_{}", version); + } +} diff --git a/io/src/bridge.rs b/io/src/bridge.rs index 4ff12fed16..f54530b408 100644 --- a/io/src/bridge.rs +++ b/io/src/bridge.rs @@ -255,17 +255,73 @@ impl super::Write for ToStd { } } -impl super::Read for std::io::BufReader { - #[inline] - fn read(&mut self, buf: &mut [u8]) -> super::Result { Ok(std::io::Read::read(self, buf)?) } +macro_rules! impl_our { + (impl$(<$($gen:ident $(: $gent:path)?),*>)? Read for $std_type:ty $(where $($where:tt)*)?) => { + impl$(<$($gen$(: $gent)?),*>)? super::Read for $std_type $(where $($where)*)? { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> super::Result { + std::io::Read::read(self, buf).map_err(Into::into) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> super::Result<()> { + std::io::Read::read_exact(self, buf).map_err(Into::into) + } + } + }; + + (impl$(<$($gen:ident $(: $gent:path)?),*>)? BufRead for $std_type:ty $(where $($where:tt)*)?) => { + impl$(<$($gen$(: $gent)?),*>)? super::BufRead for $std_type $(where $($where)*)? { + #[inline] + fn fill_buf(&mut self) -> super::Result<&[u8]> { + std::io::BufRead::fill_buf(self).map_err(Into::into) + } + + #[inline] + fn consume(&mut self, amount: usize) { + std::io::BufRead::consume(self, amount) + } + } + }; + + (impl$(<$($gen:ident $(: $gent:path)?),*>)? Write for $std_type:ty $(where $($where:tt)*)?) => { + impl$(<$($gen$(: $gent)?),*>)? super::Write for $std_type $(where $($where)*)? { + #[inline] + fn write(&mut self, buf: &[u8]) -> super::Result { + std::io::Write::write(self, buf).map_err(Into::into) + } + + #[inline] + fn flush(&mut self) -> super::Result<()> { + std::io::Write::flush(self).map_err(Into::into) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> super::Result<()> { + std::io::Write::write_all(self, buf).map_err(Into::into) + } + } + }; } -impl super::BufRead for std::io::BufReader { - #[inline] - fn fill_buf(&mut self) -> super::Result<&[u8]> { Ok(std::io::BufRead::fill_buf(self)?) } +#[cfg(rust_v_1_72)] +impl_our! { + impl Read for std::io::BufReader where R: ?Sized +} - #[inline] - fn consume(&mut self, amount: usize) { std::io::BufRead::consume(self, amount) } +#[cfg(not(rust_v_1_72))] +impl_our! { + impl Read for std::io::BufReader +} + +#[cfg(rust_v_1_72)] +impl_our! { + impl BufRead for std::io::BufReader where R: ?Sized +} + +#[cfg(not(rust_v_1_72))] +impl_our! { + impl BufRead for std::io::BufReader } impl std::io::Write for super::Sink { @@ -279,10 +335,12 @@ impl std::io::Write for super::Sink { fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } -impl super::Write for std::io::BufWriter { - #[inline] - fn write(&mut self, buf: &[u8]) -> super::Result { Ok(std::io::Write::write(self, buf)?) } +#[cfg(rust_v_1_72)] +impl_our! { + impl Write for std::io::BufWriter where W: ?Sized +} - #[inline] - fn flush(&mut self) -> super::Result<()> { Ok(std::io::Write::flush(self)?) } +#[cfg(not(rust_v_1_72))] +impl_our! { + impl Write for std::io::BufWriter } From 56b19d0601557509e6deb33d1b1e10208146f70b Mon Sep 17 00:00:00 2001 From: Martin Habovstiak Date: Fri, 16 Aug 2024 11:59:43 +0200 Subject: [PATCH 6/6] Add missing IO impls for `std` types Previously we've only implemented `bitcoin-io` traits for `BufReader` and `BufWriter` with the reasoning that people should most likely use those and implementing for other types is too much work. However since then there were requests to implement them so we do in this commit. --- io/Cargo.toml | 2 +- io/src/bridge.rs | 195 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/io/Cargo.toml b/io/Cargo.toml index 852e465c5d..f6747bb3bc 100644 --- a/io/Cargo.toml +++ b/io/Cargo.toml @@ -23,4 +23,4 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [lints.rust] -unexpected_cfgs = { level = "deny", check-cfg = ['cfg(rust_v_1_72)'] } +unexpected_cfgs = { level = "deny", check-cfg = ['cfg(rust_v_1_72)', 'cfg(rust_v_1_73)', 'cfg(rust_v_1_75)', 'cfg(rust_v_1_78)'] } diff --git a/io/src/bridge.rs b/io/src/bridge.rs index f54530b408..85e999167b 100644 --- a/io/src/bridge.rs +++ b/io/src/bridge.rs @@ -344,3 +344,198 @@ impl_our! { impl_our! { impl Write for std::io::BufWriter } + +#[cfg(rust_v_1_72)] +impl_our! { + impl Write for std::io::LineWriter where W: ?Sized +} + +#[cfg(not(rust_v_1_72))] +impl_our! { + impl Write for std::io::LineWriter +} + +impl_our! { + impl Read for std::io::Take +} + +impl_our! { + impl BufRead for std::io::Take +} + +impl_our! { + impl Read for std::io::Chain +} + +impl_our! { + impl BufRead for std::io::Chain +} + +impl_our! { + impl> Read for std::io::Cursor +} + +impl_our! { + impl> BufRead for std::io::Cursor +} + +impl_our! { + impl Write for std::io::Cursor> +} + +impl_our! { + impl Write for std::io::Cursor<&'_ mut std::vec::Vec> +} + +impl_our! { + impl Write for std::io::Cursor> +} + +impl_our! { + impl Read for std::io::Empty +} + +impl_our! { + impl BufRead for std::io::Empty +} + +#[cfg(rust_v_1_73)] +impl_our! { + impl Write for std::io::Empty +} + +// No idea why &Empty impls Write but not Read + BufRead +#[cfg(rust_v_1_73)] +impl_our! { + impl Write for &'_ std::io::Empty +} + +impl_our! { + impl Read for std::io::Repeat +} + +impl_our! { + impl Read for std::io::Stdin +} + +#[cfg(rust_v_1_78)] +impl_our! { + impl Read for &'_ std::io::Stdin +} + +impl_our! { + impl Write for std::io::Stdout +} + +impl_our! { + impl Write for &'_ std::io::Stdout +} + +impl_our! { + impl Write for std::io::Stderr +} + +impl_our! { + impl Write for &'_ std::io::Stderr +} + +impl_our! { + impl Read for std::io::StdinLock<'_> +} + +impl_our! { + impl BufRead for std::io::StdinLock<'_> +} + +impl_our! { + impl Read for std::fs::File +} + +impl_our! { + impl Write for std::fs::File +} + +impl_our! { + impl Read for &'_ std::fs::File +} + +impl_our! { + impl Write for &'_ std::fs::File +} + +#[cfg(rust_v_1_73)] +impl_our! { + impl Read for std::sync::Arc +} + +#[cfg(rust_v_1_73)] +impl_our! { + impl Write for std::sync::Arc +} + +impl_our! { + impl Read for std::net::TcpStream +} + +impl_our! { + impl Write for std::net::TcpStream +} + +impl_our! { + impl Read for &'_ std::net::TcpStream +} + +impl_our! { + impl Write for &'_ std::net::TcpStream +} + +#[cfg(target_family = "unix")] +impl_our! { + impl Read for std::os::unix::net::UnixStream +} + +#[cfg(target_family = "unix")] +impl_our! { + impl Write for std::os::unix::net::UnixStream +} + +#[cfg(target_family = "unix")] +impl_our! { + impl Read for &'_ std::os::unix::net::UnixStream +} + +#[cfg(target_family = "unix")] +impl_our! { + impl Write for &'_ std::os::unix::net::UnixStream +} + +impl_our! { + impl Read for std::process::ChildStderr +} + +impl_our! { + impl Read for std::process::ChildStdout +} + +impl_our! { + impl Write for std::process::ChildStdin +} + +// No ide why other &ChildStd* are not implemented +impl_our! { + impl Write for &'_ std::process::ChildStdin +} + +#[cfg(rust_v_1_75)] +impl_our! { + impl Read for std::collections::VecDeque +} + +#[cfg(rust_v_1_75)] +impl_our! { + impl BufRead for std::collections::VecDeque +} + +impl_our! { + impl Write for std::collections::VecDeque +}