ctf_pwn/io/util/cache/
read_until_regex.rs

1use crate::io::AsyncCacheRead;
2use pin_project_lite::pin_project;
3use regex::bytes::*;
4use std::future::Future;
5use std::io;
6use std::io::ErrorKind;
7use std::marker::PhantomPinned;
8use std::pin::Pin;
9use std::task::{ready, Context, Poll};
10use tokio::io::ReadBuf;
11
12pin_project! {
13    /// The delimiter is included in the resulting vector.
14    #[derive(Debug)]
15    #[must_use = "futures do nothing unless you `.await` or poll them"]
16    pub struct ReadUntilRegex<'a, R: ?Sized> {
17        reader: &'a mut R,
18        regex: Regex,
19        buf: &'a mut Vec<u8>,
20        internal_buf: Vec<u8>,
21        // The number of bytes appended to buf. This can be less than buf.len() if
22        // the buffer was not empty when the operation was started.
23        read: usize,
24        // Make this future `!Unpin` for compatibility with async trait methods.
25        #[pin]
26        _pin: PhantomPinned,
27    }
28}
29
30pub(crate) fn read_until_regex<'a, R>(
31    reader: &'a mut R,
32    pattern: &str,
33    buf: &'a mut Vec<u8>,
34) -> Result<ReadUntilRegex<'a, R>, regex::Error>
35where
36    R: AsyncCacheRead + ?Sized + Unpin,
37{
38    let regex = Regex::new(pattern)?;
39    Ok(ReadUntilRegex {
40        reader,
41        regex,
42        buf,
43        internal_buf: Vec::new(),
44        read: 0,
45        _pin: PhantomPinned,
46    })
47}
48
49fn eof() -> io::Error {
50    io::Error::new(ErrorKind::UnexpectedEof, "early eof")
51}
52
53pub(super) fn read_until_regex_internal<R: AsyncCacheRead + ?Sized>(
54    mut reader: Pin<&mut R>,
55    cx: &mut Context<'_>,
56    regex: &mut Regex,
57    buf: &mut Vec<u8>,
58    internal_buf: &mut Vec<u8>,
59    read: &mut usize,
60) -> Poll<io::Result<(usize, usize)>> {
61    let mut read_buf = [0u8; 4096];
62    let mut data = ReadBuf::new(&mut read_buf);
63    loop {
64        data.clear();
65        ready!(reader.as_mut().poll_read(cx, &mut data))?;
66        let read_len = data.filled().len();
67        if read_len == 0 {
68            return Err(eof()).into();
69        }
70        *read += read_len;
71        internal_buf.extend_from_slice(data.filled());
72
73        match regex.find(&internal_buf) {
74            Some(m) => {
75                let drain_index = m.end();
76                buf.extend_from_slice(&internal_buf[..drain_index]);
77                let restore_data = &internal_buf[drain_index..];
78                reader.restore(restore_data);
79                *read -= restore_data.len();
80                return Poll::Ready(Ok((buf.len(), m.len())));
81            }
82            None => {}
83        }
84    }
85}
86
87impl<R: AsyncCacheRead + ?Sized + Unpin> Future for ReadUntilRegex<'_, R> {
88    type Output = io::Result<(usize, usize)>;
89
90    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
91        let me = self.project();
92        read_until_regex_internal(
93            Pin::new(*me.reader),
94            cx,
95            me.regex,
96            me.buf,
97            me.internal_buf,
98            me.read,
99        )
100    }
101}