use std::pin::Pin;
use std::task::{Context, Poll};
use futures_lite::io::AsyncRead as Read;
use futures_lite::{io, ready};
#[derive(Debug)]
pub(crate) struct ChunkedEncoder<R> {
reader: R,
done: bool,
}
impl<R: Read + Unpin> ChunkedEncoder<R> {
pub(crate) fn new(reader: R) -> Self {
Self {
reader,
done: false,
}
}
}
impl<R: Read + Unpin> Read for ChunkedEncoder<R> {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
if self.done {
return Poll::Ready(Ok(0));
}
let reader = &mut self.reader;
let max_bytes_to_read = max_bytes_to_read(buf.len());
let bytes = ready!(Pin::new(reader).poll_read(cx, &mut buf[..max_bytes_to_read]))?;
if bytes == 0 {
self.done = true;
}
let start = format!("{:X}\r\n", bytes);
let start_length = start.as_bytes().len();
let total = bytes + start_length + 2;
buf.copy_within(..bytes, start_length);
buf[..start_length].copy_from_slice(start.as_bytes());
buf[total - 2..total].copy_from_slice(b"\r\n");
Poll::Ready(Ok(total))
}
}
fn max_bytes_to_read(buf_len: usize) -> usize {
if buf_len < 6 {
panic!("buffers of length {} are too small for this implementation. if this is a problem for you, please open an issue", buf_len);
}
let bytes_remaining_after_two_cr_lns = (buf_len - 4) as f64;
let max_bytes_of_hex_framing = bytes_remaining_after_two_cr_lns.log2() / 4f64;
(bytes_remaining_after_two_cr_lns - max_bytes_of_hex_framing.ceil()) as usize
}
#[cfg(test)]
mod test_bytes_to_read {
#[test]
fn simple_check_of_known_values() {
let values = vec![
(6, 1), (7, 2), (20, 15), (21, 15), (22, 16), (23, 17), (260, 254), (261, 254), (262, 255), (263, 256), (4100, 4093), (4101, 4093), (4102, 4094), (4103, 4095), (4104, 4096), ];
for (input, expected) in values {
let actual = super::max_bytes_to_read(input);
assert_eq!(
actual, expected,
"\n\nexpected max_bytes_to_read({}) to be {}, but it was {}",
input, expected, actual
);
let used_bytes = expected + 4 + format!("{:X}", expected).len();
assert!(
used_bytes == input || used_bytes == input - 1,
"\n\nfor an input of {}, expected used bytes to be {} or {}, but was {}",
input,
input,
input - 1,
used_bytes
);
}
}
}