1use std::marker::PhantomData;
24
25use futures_util::{Sink, Stream};
26use serde::de::DeserializeOwned;
27use serde::{Deserialize, Serialize};
28use thiserror::Error;
29use tokio::io::{AsyncRead, AsyncWrite};
30use tokio_util::bytes::{Buf, BufMut, BytesMut};
31use tokio_util::codec::{Decoder, Encoder, FramedRead, FramedWrite};
32
33#[derive(Debug)]
36pub struct Codec<M> {
37 max_frame_len: usize,
38 _phantom: PhantomData<M>,
39}
40
41impl<M> Codec<M> {
42 pub fn new() -> Self {
43 Self::default()
44 }
45
46 pub fn max_frame_len(mut self, value: usize) -> Self {
47 self.max_frame_len = value;
48 self
49 }
50}
51
52impl<M> Default for Codec<M> {
53 fn default() -> Self {
54 Self {
55 max_frame_len: 1024 * 1024 * 128, _phantom: PhantomData,
57 }
58 }
59}
60
61impl<M> Encoder<M> for Codec<M>
62where
63 M: Serialize,
64{
65 type Error = CodecError;
66
67 fn encode(&mut self, item: M, dst: &mut BytesMut) -> Result<(), Self::Error> {
68 let frame_len =
70 postcard::serialize_with_flavor(&item, postcard::ser_flavors::Size::default())?;
71 if frame_len > self.max_frame_len {
72 return Err(CodecError::TooLargeMessage(frame_len, self.max_frame_len));
73 }
74
75 dst.put_u32(u32::try_from(frame_len).expect("already checked"));
80
81 dst.reserve(4 + frame_len);
83
84 let mut writer = dst.writer();
86 postcard::to_io(&item, &mut writer)?;
87
88 Ok(())
89 }
90}
91
92impl<M> Decoder for Codec<M>
93where
94 M: DeserializeOwned,
95{
96 type Item = M;
97 type Error = CodecError;
98
99 fn decode(&mut self, src: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
100 if src.len() < 4 {
102 return Ok(None);
103 }
104
105 let bytes: [u8; 4] = src[..4].try_into().expect("checked available bytes");
107 let frame_len = u32::from_be_bytes(bytes) as usize;
108 if frame_len > self.max_frame_len {
109 return Err(CodecError::TooLargeMessage(frame_len, self.max_frame_len));
110 }
111
112 if src.len() < 4 + frame_len {
114 return Ok(None);
115 }
116 let item: M = postcard::from_bytes(&src[4..4 + frame_len])?;
117
118 src.advance(4 + frame_len);
119
120 Ok(Some(item))
121 }
122}
123
124pub fn into_codec_stream<M, T>(
127 rx: T,
128) -> impl Stream<Item = Result<M, CodecError>> + Unpin + use<M, T>
129where
130 M: for<'de> Deserialize<'de> + 'static,
131 T: AsyncRead + Unpin + 'static,
132{
133 FramedRead::new(rx, Codec::<M>::new())
134}
135
136pub fn into_codec_sink<M, T>(tx: T) -> impl Sink<M, Error = CodecError>
139where
140 M: Serialize + 'static,
141 T: AsyncWrite + Unpin + 'static,
142{
143 FramedWrite::new(tx, Codec::<M>::new())
144}
145
146#[derive(Debug, Error)]
148pub enum CodecError {
149 #[error(transparent)]
150 Postcard(#[from] postcard::Error),
151
152 #[error("too large message of {0} bytes (max allowed is {1})")]
153 TooLargeMessage(usize, usize),
154
155 #[error(transparent)]
156 Io(#[from] std::io::Error),
157}
158
159#[cfg(test)]
160mod tests {
161 use futures_util::{FutureExt, SinkExt, StreamExt};
162 use p2panda_core::test_utils::TestLog;
163 use p2panda_core::{Body, Header};
164 use tokio::io::AsyncWriteExt;
165 use tokio_util::codec::{FramedRead, FramedWrite};
166
167 use super::{Codec, into_codec_sink, into_codec_stream};
168
169 #[tokio::test]
170 async fn decoding_exactly_one_frame() {
171 let (mut tx, rx) = tokio::io::duplex(64);
172 let mut stream = FramedRead::new(rx, Codec::<String>::new());
173
174 tx.write_all(&[0, 0, 0, 6]).await.unwrap();
176
177 tx.write_all(&[5]).await.unwrap();
179
180 tx.write_all("hello".as_bytes()).await.unwrap();
182
183 let message = stream.next().await;
184 assert_eq!(message.unwrap().unwrap(), "hello".to_string());
185 }
186
187 #[tokio::test]
188 async fn decoding_more_than_one_frame() {
189 let (mut tx, rx) = tokio::io::duplex(64);
190 let mut stream = FramedRead::new(rx, Codec::<String>::new());
191
192 tx.write_all(&[0, 0, 0, 6]).await.unwrap();
194
195 tx.write_all(&[5]).await.unwrap();
196 tx.write_all("hello".as_bytes()).await.unwrap();
197
198 tx.write_all(&[0, 0, 0, 10]).await.unwrap();
200
201 tx.write_all(&[9]).await.unwrap();
202 tx.write_all("aquariums".as_bytes()).await.unwrap();
203
204 let message = stream.next().await;
205 assert_eq!(message.unwrap().unwrap(), "hello".to_string());
206
207 let message = stream.next().await;
208 assert_eq!(message.unwrap().unwrap(), "aquariums".to_string());
209 }
210
211 #[tokio::test]
212 async fn decoding_incomplete_frame() {
213 let (mut tx, rx) = tokio::io::duplex(64);
214 let mut stream = FramedRead::new(rx, Codec::<String>::new());
215
216 tx.write_all(&[0, 0, 0, 6]).await.unwrap();
218
219 let message = stream.next().now_or_never();
221 assert!(message.is_none());
222
223 tx.write_all(&[5]).await.unwrap();
225 tx.write_all("h".as_bytes()).await.unwrap();
226 tx.write_all("ello".as_bytes()).await.unwrap();
227
228 let message = stream.next().await;
229 assert_eq!(message.unwrap().unwrap(), "hello".to_string());
230 }
231
232 #[tokio::test]
233 async fn decoding_too_large_message() {
234 let (mut tx, rx) = tokio::io::duplex(64);
235 let mut stream = FramedRead::new(rx, Codec::<String>::new().max_frame_len(4));
236
237 tx.write_all(&[0, 0, 0, 6]).await.unwrap();
238 tx.write_all(&[5]).await.unwrap();
239 tx.write_all("hello".as_bytes()).await.unwrap();
240
241 let message = stream.next().await;
242 assert!(message.unwrap().is_err());
243 }
244
245 #[tokio::test]
246 async fn encoding_too_large_message() {
247 let (tx, _rx) = tokio::io::duplex(64);
248 let mut sink = FramedWrite::new(tx, Codec::<String>::new().max_frame_len(4));
249 assert!(sink.send("hello".into()).await.is_err());
250 }
251
252 #[tokio::test]
253 async fn encoding() {
254 let (tx, _rx) = tokio::io::duplex(64);
255 let mut sink = FramedWrite::new(tx, Codec::<String>::new());
256 assert!(sink.feed("hello".into()).await.is_ok());
257 assert!(sink.feed("hello".into()).await.is_ok());
258 assert!(sink.feed("hello".into()).await.is_ok());
259 assert!(sink.flush().await.is_ok());
260 }
261
262 #[tokio::test]
263 async fn operations_stream() {
264 type Payload = (Header<u32>, Option<Body>);
265
266 let (tx_inner, rx_inner) = tokio::io::duplex(1024 * 100);
269
270 let mut tx = into_codec_sink::<Payload, _>(tx_inner);
271 let mut rx = into_codec_stream::<Payload, _>(rx_inner);
272
273 let log = TestLog::new();
275 for _ in 0..100 {
276 let operation = log.operation(b"boom boom boom", 32);
277 tx.send((operation.header, operation.body)).await.unwrap();
278 }
279
280 let mut i = 1;
283 loop {
284 if let Some(message) = rx.next().await {
285 if let Err(err) = message {
286 panic!("{err}");
287 }
288
289 i += 1;
290
291 if i == 100 {
292 break;
293 }
294 }
295 }
296 }
297}