rw-stream-sink-0.4.0/.cargo_vcs_info.json0000644000000001610000000000100137160ustar { "git": { "sha1": "42566869b2ab3d1903d164c84da3308e7e890b8b" }, "path_in_vcs": "misc/rw-stream-sink" }rw-stream-sink-0.4.0/CHANGELOG.md000064400000000000000000000005201046102023000143160ustar 00000000000000## 0.4.0 - Raise MSRV to 1.65. See [PR 3715]. [PR 3715]: https://github.com/libp2p/rust-libp2p/pull/3715 ## 0.3.0 - Move from https://github.com/paritytech/rw-stream-sink/ to https://github.com/libp2p/rust-libp2p. See [Issue 2504]. - Update to Rust edition 2021. [Issue 2504]: https://github.com/libp2p/rust-libp2p/issues/2504 rw-stream-sink-0.4.0/Cargo.toml0000644000000022130000000000100117140ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.65.0" name = "rw-stream-sink" version = "0.4.0" authors = ["Parity Technologies "] description = "Adaptator between Stream/Sink and AsyncRead/AsyncWrite" keywords = ["networking"] categories = [ "network-programming", "asynchronous", ] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" [package.metadata.docs.rs] all-features = true rustc-args = [ "--cfg", "docsrs", ] rustdoc-args = [ "--cfg", "docsrs", ] [dependencies.futures] version = "0.3.28" [dependencies.pin-project] version = "1.1.0" [dependencies.static_assertions] version = "1" [dev-dependencies.async-std] version = "1.0" rw-stream-sink-0.4.0/Cargo.toml.orig000064400000000000000000000013641046102023000154030ustar 00000000000000[package] name = "rw-stream-sink" edition = "2021" description = "Adaptator between Stream/Sink and AsyncRead/AsyncWrite" rust-version = { workspace = true } version = "0.4.0" authors = ["Parity Technologies "] license = "MIT" repository = "https://github.com/libp2p/rust-libp2p" keywords = ["networking"] categories = ["network-programming", "asynchronous"] [dependencies] futures = "0.3.28" pin-project = "1.1.0" static_assertions = "1" [dev-dependencies] async-std = "1.0" # Passing arguments to the docsrs builder in order to properly document cfg's. # More information: https://docs.rs/about/builds#cross-compiling [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] rustc-args = ["--cfg", "docsrs"] rw-stream-sink-0.4.0/src/lib.rs000064400000000000000000000165711046102023000144250ustar 00000000000000// Copyright 2017-2020 Parity Technologies (UK) Ltd. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. //! This crate provides the [`RwStreamSink`] type. It wraps around a [`Stream`] //! and [`Sink`] that produces and accepts byte arrays, and implements //! [`AsyncRead`] and [`AsyncWrite`]. //! //! Each call to [`AsyncWrite::poll_write`] will send one packet to the sink. //! Calls to [`AsyncRead::poll_read`] will read from the stream's incoming packets. #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] use futures::{prelude::*, ready}; use std::{ io::{self, Read}, mem, pin::Pin, task::{Context, Poll}, }; static_assertions::const_assert!(mem::size_of::() <= mem::size_of::()); /// Wraps a [`Stream`] and [`Sink`] whose items are buffers. /// Implements [`AsyncRead`] and [`AsyncWrite`]. #[pin_project::pin_project] pub struct RwStreamSink { #[pin] inner: S, current_item: Option::Ok>>, } impl RwStreamSink { /// Wraps around `inner`. pub fn new(inner: S) -> Self { RwStreamSink { inner, current_item: None, } } } impl AsyncRead for RwStreamSink where S: TryStream, ::Ok: AsRef<[u8]>, { fn poll_read( self: Pin<&mut Self>, cx: &mut Context, buf: &mut [u8], ) -> Poll> { let mut this = self.project(); // Grab the item to copy from. let item_to_copy = loop { if let Some(ref mut i) = this.current_item { if i.position() < i.get_ref().as_ref().len() as u64 { break i; } } *this.current_item = Some(match ready!(this.inner.as_mut().try_poll_next(cx)) { Some(Ok(i)) => std::io::Cursor::new(i), Some(Err(e)) => return Poll::Ready(Err(e)), None => return Poll::Ready(Ok(0)), // EOF }); }; // Copy it! Poll::Ready(Ok(item_to_copy.read(buf)?)) } } impl AsyncWrite for RwStreamSink where S: TryStream + Sink<::Ok, Error = io::Error>, ::Ok: for<'r> From<&'r [u8]>, { fn poll_write(self: Pin<&mut Self>, cx: &mut Context, buf: &[u8]) -> Poll> { let mut this = self.project(); ready!(this.inner.as_mut().poll_ready(cx)?); let n = buf.len(); if let Err(e) = this.inner.start_send(buf.into()) { return Poll::Ready(Err(e)); } Poll::Ready(Ok(n)) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let this = self.project(); this.inner.poll_flush(cx) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let this = self.project(); this.inner.poll_close(cx) } } #[cfg(test)] mod tests { use super::RwStreamSink; use async_std::task; use futures::{channel::mpsc, prelude::*, stream}; use std::{ pin::Pin, task::{Context, Poll}, }; // This struct merges a stream and a sink and is quite useful for tests. struct Wrapper(St, Si); impl Stream for Wrapper where St: Stream + Unpin, Si: Unpin, { type Item = St::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { self.0.poll_next_unpin(cx) } } impl Sink for Wrapper where St: Unpin, Si: Sink + Unpin, { type Error = Si::Error; fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { Pin::new(&mut self.1).poll_ready(cx) } fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { Pin::new(&mut self.1).start_send(item) } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { Pin::new(&mut self.1).poll_flush(cx) } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { Pin::new(&mut self.1).poll_close(cx) } } #[test] fn basic_reading() { let (tx1, _) = mpsc::channel::>(10); let (mut tx2, rx2) = mpsc::channel(10); let mut wrapper = RwStreamSink::new(Wrapper(rx2.map(Ok), tx1)); task::block_on(async move { tx2.send(Vec::from("hel")).await.unwrap(); tx2.send(Vec::from("lo wor")).await.unwrap(); tx2.send(Vec::from("ld")).await.unwrap(); tx2.close().await.unwrap(); let mut data = Vec::new(); wrapper.read_to_end(&mut data).await.unwrap(); assert_eq!(data, b"hello world"); }) } #[test] fn skip_empty_stream_items() { let data: Vec<&[u8]> = vec![b"", b"foo", b"", b"bar", b"", b"baz", b""]; let mut rws = RwStreamSink::new(stream::iter(data).map(Ok)); let mut buf = [0; 9]; task::block_on(async move { assert_eq!(3, rws.read(&mut buf).await.unwrap()); assert_eq!(3, rws.read(&mut buf[3..]).await.unwrap()); assert_eq!(3, rws.read(&mut buf[6..]).await.unwrap()); assert_eq!(0, rws.read(&mut buf).await.unwrap()); assert_eq!(b"foobarbaz", &buf[..]) }) } #[test] fn partial_read() { let data: Vec<&[u8]> = vec![b"hell", b"o world"]; let mut rws = RwStreamSink::new(stream::iter(data).map(Ok)); let mut buf = [0; 3]; task::block_on(async move { assert_eq!(3, rws.read(&mut buf).await.unwrap()); assert_eq!(b"hel", &buf[..3]); assert_eq!(0, rws.read(&mut buf[..0]).await.unwrap()); assert_eq!(1, rws.read(&mut buf).await.unwrap()); assert_eq!(b"l", &buf[..1]); assert_eq!(3, rws.read(&mut buf).await.unwrap()); assert_eq!(b"o w", &buf[..3]); assert_eq!(0, rws.read(&mut buf[..0]).await.unwrap()); assert_eq!(3, rws.read(&mut buf).await.unwrap()); assert_eq!(b"orl", &buf[..3]); assert_eq!(1, rws.read(&mut buf).await.unwrap()); assert_eq!(b"d", &buf[..1]); assert_eq!(0, rws.read(&mut buf).await.unwrap()); }) } }