1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
#![feature(async_await)] //! `tobytcp` is a library used when sending messages over a buffer, typically an async `TcpStream`. //! //! It uses length-prefixing to allow the receiver to differentiate different messages //! //! # Examples //! Here is a tiny example of what it looks like to use `TobyTcp`'s built-in `send` and `receive` fns. Also look at the `/examples` directory //! and unit tests in the source code for concrete uses of this library. //! //! ```no_run //! #![feature(async_await)] //! # use romio::TcpStream; //! # use tobytcp::{send, receive}; //! //! # async fn toby() -> Result<u64, std::io::Error> { // For some reason when I do Result<(), std::io::Error> it complains a ton.. //! # let mut stream = TcpStream::connect(&"127.0.0.1:7070".parse().unwrap()).await?; //! let mut buf = vec![1, 2, 3]; //! send(&mut buf, &mut stream).await?; //! //! // Pretend we're connected to an echo server.. //! let received = receive(&mut stream).await?; //! //! assert_eq!(buf, received); //! # Ok(8) //! # }; //! ``` //! /// Includes methods for generating the `tobytcp` prefix, or attempting to decode the length of /// the encoded data i.e. payload, from a buffer. pub mod protocol; use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; use std::io; use std::marker::Unpin; /// Writes the data, encoded as `tobytcp`, to the `Write`. Returns the total number of bytes /// written, equal to `data.len() + 8`. See the `examples/` dir in the source code, or the tests /// of this fn in the source code for some examples of this being used. /// /// Note: Do *not* perform any IO on the `Write` outside of calling `send` or `receive`! It can corrupt the tobytcp stream pub async fn send<'w, W>(data: &'w [u8], write: &'w mut W) -> Result<usize, io::Error> where W: AsyncWrite + Unpin, { let prefix = protocol::tobytcp_prefix(data.len()); write.write_all(&prefix).await?; write.write_all(data).await?; Ok(data.len() + 8) } /// Wait for data, which was encoded as `tobytcp`, to be received from this `Read`. Returns the data or any error. /// See the `examples/` dir in the source code, or the tests of this fn in the source code for some examples of this being used. /// /// Note: Do *not* perform any IO on the `Read` outside of calling `send` or `receive`! It can corrupt the tobytcp stream pub async fn receive<R>(read: &mut R) -> Result<Vec<u8>, io::Error> where R: AsyncRead + Unpin, { let mut len_buf: [u8; 8] = [0; 8]; read.read_exact(&mut len_buf).await?; let len = protocol::tobytcp_len(len_buf); let mut buf = vec![0; len as usize]; read.read_exact(&mut buf).await?; Ok(buf) } #[cfg(test)] mod tests { use super::*; #[runtime::test] async fn simple_test() { let to_send: Vec<u8> = vec![13, 58, 2, 4]; let mut output = Vec::new(); let size: usize = 12; assert_eq!(size, send(&to_send, &mut output).await.unwrap()); // 'manually' check the buffer let mut len_bytes: [u8; 8] = [0; 8]; len_bytes.clone_from_slice(&output[0..8]); assert_eq!(4, u64::from_be_bytes(len_bytes)); assert_eq!(to_send[0..], output[8..12]); // hacky.. let x = output.clone(); let mut y = x.as_slice(); let received = receive(&mut y).await.unwrap(); assert_eq!(received, to_send); } // Tests that if we buffer up a bunch of data that we can separate them, very important! #[runtime::test] async fn many_sends_then_receive_test() { let mut output = Vec::new(); let num = 20; for i in 0..num { let to_send: Vec<u8> = vec![i, i, i, i]; send(&to_send, &mut output).await.unwrap(); } // hacky.. let x = output.clone(); let mut y = x.as_slice(); for i in 0..num { let received = receive(&mut y).await.unwrap(); assert_eq!(vec![i, i, i, i], received); } } }