sillad_hex/
lib.rs

1use std::pin::Pin;
2use futures_util::{AsyncRead, AsyncWrite};
3use pin_project::pin_project;
4use std::task::{Context, Poll};
5use async_trait::async_trait;
6use sillad::{dialer::Dialer, listener::Listener, Pipe};
7
8#[pin_project]
9pub struct HexPipe<P: Pipe> {
10    #[pin]
11    inner: P,
12    read_buf: Vec<u8>,
13    leftover: Vec<u8>,
14}
15
16impl<P: Pipe> HexPipe<P> {
17    pub fn new(inner: P) -> Self {
18        Self { inner, read_buf: Vec::new(), leftover: Vec::new() }
19    }
20}
21
22impl<P: Pipe> AsyncRead for HexPipe<P> {
23    fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<std::io::Result<usize>> {
24        let mut this = self.project();
25        if !this.read_buf.is_empty() {
26            let n = buf.len().min(this.read_buf.len());
27            buf[..n].copy_from_slice(&this.read_buf[..n]);
28            this.read_buf.drain(..n);
29            return Poll::Ready(Ok(n));
30        }
31        let mut tmp = [0u8; 4096];
32        match Pin::new(&mut this.inner).poll_read(cx, &mut tmp) {
33            Poll::Ready(Ok(0)) => {
34                if this.leftover.is_empty() {
35                    Poll::Ready(Ok(0))
36                } else {
37                    Poll::Ready(Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, "incomplete hex")))
38                }
39            }
40            Poll::Ready(Ok(n)) => {
41                this.leftover.extend_from_slice(&tmp[..n]);
42                let decode_len = this.leftover.len() / 2 * 2;
43                let decoded = match hex::decode(&this.leftover[..decode_len]) {
44                    Ok(v) => v,
45                    Err(_) => return Poll::Ready(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "bad hex"))),
46                };
47                this.read_buf.extend_from_slice(&decoded);
48                this.leftover.drain(..decode_len);
49                let n = buf.len().min(this.read_buf.len());
50                buf[..n].copy_from_slice(&this.read_buf[..n]);
51                this.read_buf.drain(..n);
52                Poll::Ready(Ok(n))
53            }
54            Poll::Ready(Err(e)) => Poll::Ready(Err(e)),
55            Poll::Pending => Poll::Pending,
56        }
57    }
58}
59
60impl<P: Pipe> AsyncWrite for HexPipe<P> {
61    fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<std::io::Result<usize>> {
62        let mut this = self.project();
63        let encoded = hex::encode(buf);
64        this.inner.as_mut().poll_write(cx, encoded.as_bytes()).map_ok(|_| buf.len())
65    }
66
67    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
68        let mut this = self.project();
69        this.inner.as_mut().poll_flush(cx)
70    }
71
72    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
73        let mut this = self.project();
74        this.inner.as_mut().poll_close(cx)
75    }
76}
77
78impl<P: Pipe> Pipe for HexPipe<P> {
79    fn shared_secret(&self) -> Option<&[u8]> { self.inner.shared_secret() }
80    fn protocol(&self) -> &str { "hex" }
81    fn remote_addr(&self) -> Option<&str> { self.inner.remote_addr() }
82}
83
84pub struct HexDialer<D: Dialer> { pub inner: D }
85
86#[async_trait]
87impl<D: Dialer> Dialer for HexDialer<D> {
88    type P = HexPipe<D::P>;
89    async fn dial(&self) -> std::io::Result<Self::P> { self.inner.dial().await.map(HexPipe::new) }
90}
91
92pub struct HexListener<L: Listener> { pub inner: L }
93
94#[async_trait]
95impl<L: Listener> Listener for HexListener<L> {
96    type P = HexPipe<L::P>;
97    async fn accept(&mut self) -> std::io::Result<Self::P> { self.inner.accept().await.map(HexPipe::new) }
98}