use std::io;
use compio_buf::{
IoBuf, IoBufMut, Slice,
bytes::{Buf, BufMut},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Frame {
prefix: usize,
payload: usize,
suffix: usize,
}
impl Frame {
pub fn new(prefix: usize, payload: usize, suffix: usize) -> Self {
Self {
prefix,
payload,
suffix,
}
}
pub fn len(&self) -> usize {
self.prefix + self.payload + self.suffix
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn slice<B: IoBuf>(&self, buf: B) -> Slice<B> {
buf.slice(self.prefix..self.prefix + self.payload)
}
}
pub trait Framer<B: IoBufMut> {
fn enclose(&mut self, buf: &mut B);
fn extract(&mut self, buf: &Slice<B>) -> io::Result<Option<Frame>>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LengthDelimited {
length_field_len: usize,
length_field_is_big_endian: bool,
}
impl Default for LengthDelimited {
fn default() -> Self {
Self {
length_field_len: 4,
length_field_is_big_endian: true,
}
}
}
impl LengthDelimited {
pub fn new() -> Self {
Self::default()
}
pub fn length_field_len(&self) -> usize {
self.length_field_len
}
pub fn set_length_field_len(mut self, len_field_len: usize) -> Self {
self.length_field_len = len_field_len;
self
}
pub fn length_field_is_big_endian(&self) -> bool {
self.length_field_is_big_endian
}
pub fn set_length_field_is_big_endian(mut self, big_endian: bool) -> Self {
self.length_field_is_big_endian = big_endian;
self
}
}
impl<B: IoBufMut> Framer<B> for LengthDelimited {
fn enclose(&mut self, buf: &mut B) {
let len = (*buf).buf_len();
buf.reserve(self.length_field_len).expect("Reserve failed");
buf.copy_within(0..len, self.length_field_len); unsafe { buf.advance_to(len + self.length_field_len) };
let slice = buf.as_mut_slice();
if self.length_field_is_big_endian {
(&mut slice[0..self.length_field_len]).put_uint(len as _, self.length_field_len);
} else {
(&mut slice[0..self.length_field_len]).put_uint_le(len as _, self.length_field_len);
}
}
fn extract(&mut self, buf: &Slice<B>) -> io::Result<Option<Frame>> {
if buf.len() < self.length_field_len {
return Ok(None);
}
let mut buf = buf.as_init();
let len = if self.length_field_is_big_endian {
buf.get_uint(self.length_field_len)
} else {
buf.get_uint_le(self.length_field_len)
} as usize;
if buf.len() < len {
return Ok(None);
}
Ok(Some(Frame::new(self.length_field_len, len, 0)))
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CharDelimited<const C: char> {
char_buf: [u8; 4],
}
impl<const C: char> CharDelimited<C> {
pub fn new() -> Self {
Self { char_buf: [0; 4] }
}
fn as_any_delimited(&mut self) -> AnyDelimited<'_> {
let bytes = C.encode_utf8(&mut self.char_buf).as_bytes();
AnyDelimited::new(bytes)
}
}
impl<B: IoBufMut, const C: char> Framer<B> for CharDelimited<C> {
fn enclose(&mut self, buf: &mut B) {
self.as_any_delimited().enclose(buf);
}
fn extract(&mut self, buf: &Slice<B>) -> io::Result<Option<Frame>> {
self.as_any_delimited().extract(buf)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AnyDelimited<'a> {
bytes: &'a [u8],
}
impl<'a> AnyDelimited<'a> {
pub fn new(bytes: &'a [u8]) -> Self {
Self { bytes }
}
}
impl<B: IoBufMut> Framer<B> for AnyDelimited<'_> {
fn extract(&mut self, buf: &Slice<B>) -> io::Result<Option<Frame>> {
if buf.is_empty() {
return Ok(None);
}
if let Some(pos) = buf
.windows(self.bytes.len())
.position(|window| window == self.bytes)
{
Ok(Some(Frame::new(0, pos, self.bytes.len())))
} else {
Ok(None)
}
}
fn enclose(&mut self, buf: &mut B) {
buf.extend_from_slice(self.bytes)
.expect("Failed to append delimiter");
}
}
pub type LineDelimited = CharDelimited<'\n'>;
#[cfg(test)]
mod tests {
use compio_buf::{IntoInner, IoBufMut};
use super::*;
#[test]
fn test_length_delimited() {
let mut framer = LengthDelimited::new();
let mut buf = Vec::from(b"hello");
framer.enclose(&mut buf);
assert_eq!(&buf.as_slice()[..9], b"\x00\x00\x00\x05hello");
let buf = buf.slice(..);
let frame = framer.extract(&buf).unwrap().unwrap();
let buf = buf.into_inner();
assert_eq!(frame, Frame::new(4, 5, 0));
let payload = frame.slice(buf);
assert_eq!(payload.as_init(), b"hello");
}
#[test]
fn test_char_delimited() {
let mut framer = CharDelimited::<'ℝ'>::new();
let mut buf = Vec::new();
IoBufMut::extend_from_slice(&mut buf, b"hello").unwrap();
framer.enclose(&mut buf);
assert_eq!(buf.as_slice(), "helloℝ".as_init());
let buf = buf.slice(..);
let frame = framer.extract(&buf).unwrap().unwrap();
assert_eq!(frame, Frame::new(0, 5, 3));
let payload = frame.slice(buf);
assert_eq!(payload.as_init(), b"hello");
}
}