use std::{
borrow::Cow,
cell::UnsafeCell,
fmt::{Debug, Formatter},
iter::Enumerate,
ops::{Bound, Deref, RangeBounds},
rc::Rc,
str::Utf8Error,
};
use fluke_maybe_uring::{
buf::{IoBuf, IoBufMut},
io::ReadOwned,
};
use nom::{
Compare, CompareResult, FindSubstring, InputIter, InputLength, InputTake, InputTakeAtPosition,
Needed, Slice,
};
use crate::{Buf, BufMut, BUF_SIZE};
pub struct RollMut {
storage: StorageMut,
len: u32,
}
enum StorageMut {
Buf(BufMut),
Box(BoxStorage),
}
impl Debug for StorageMut {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::Buf(bm) => f
.debug_struct("Buf")
.field("index", &bm.index)
.field("off", &bm.off)
.field("len", &bm.len)
.finish(),
Self::Box(bs) => f
.debug_struct("Box")
.field("buf", &bs.buf)
.field("off", &bs.off)
.finish(),
}
}
}
impl StorageMut {
#[inline(always)]
fn cap(&self) -> usize {
match self {
StorageMut::Buf(_) => BUF_SIZE as usize,
StorageMut::Box(b) => b.cap(),
}
}
#[inline(always)]
fn len(&self) -> usize {
match self {
StorageMut::Buf(b) => b.len(),
StorageMut::Box(b) => b.len(),
}
}
unsafe fn as_mut_ptr(&mut self) -> *mut u8 {
match self {
StorageMut::Buf(b) => b.as_mut_ptr(),
StorageMut::Box(b) => b.as_mut_ptr(),
}
}
}
#[derive(Clone)]
struct BoxStorage {
buf: Rc<UnsafeCell<Box<[u8]>>>,
off: u32,
}
impl BoxStorage {
#[inline(always)]
fn len(&self) -> usize {
let buf = self.buf.get();
let len = unsafe { (*buf).len() };
len - self.off as usize
}
fn as_ptr(&self) -> *const u8 {
let buf = self.buf.get();
unsafe { (*buf).as_ptr().add(self.off as usize) }
}
unsafe fn as_mut_ptr(&self) -> *mut u8 {
let buf = self.buf.get();
(*buf).as_mut_ptr().add(self.off as usize)
}
fn slice(&self, len: u32) -> &[u8] {
let buf = self.buf.get();
unsafe { &(*buf)[self.off as usize..][..len as usize] }
}
fn slice_mut(&mut self, len: u32) -> &mut [u8] {
let buf = self.buf.get();
unsafe { &mut (*buf)[self.off as usize..][..len as usize] }
}
fn cap(&self) -> usize {
let buf = self.buf.get();
unsafe { (*buf).len() }
}
}
#[derive(Debug, thiserror::Error)]
#[error("slice does not fit into this RollMut")]
pub struct DoesNotFit;
impl RollMut {
pub fn alloc() -> eyre::Result<Self> {
Ok(Self {
storage: StorageMut::Buf(BufMut::alloc()?),
len: 0,
})
}
pub fn grow(&mut self) {
let old_cap = self.storage.cap();
let new_cap = old_cap * 2;
let b = vec![0; new_cap].into_boxed_slice();
let mut bs = BoxStorage {
buf: Rc::new(UnsafeCell::new(b)),
off: 0,
};
let dst_slice = bs.slice_mut(self.len() as u32);
dst_slice.copy_from_slice(&self[..]);
let next_storage = StorageMut::Box(bs);
self.storage = next_storage;
}
pub fn realloc(&mut self) -> eyre::Result<()> {
assert!(self.len() != self.storage_size());
let next_storage = match &self.storage {
StorageMut::Buf(_) => {
let mut next_b = BufMut::alloc()?;
next_b[..self.len()].copy_from_slice(&self[..]);
StorageMut::Buf(next_b)
}
StorageMut::Box(b) => {
if self.len() > BUF_SIZE as usize {
let mut next_b = vec![0; b.cap()].into_boxed_slice();
next_b[..self.len()].copy_from_slice(&self[..]);
let next_b = BoxStorage {
buf: Rc::new(UnsafeCell::new(next_b)),
off: 0,
};
StorageMut::Box(next_b)
} else {
let mut next_b = BufMut::alloc()?;
next_b[..self.len()].copy_from_slice(&self[..]);
StorageMut::Buf(next_b)
}
}
};
self.storage = next_storage;
Ok(())
}
pub fn reserve(&mut self) -> eyre::Result<()> {
if self.len() < self.cap() {
return Ok(());
}
if self.len() < self.storage_size() {
self.realloc()?
} else {
self.grow()
}
Ok(())
}
#[inline(always)]
pub fn len(&self) -> usize {
self.len as usize
}
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline(always)]
pub fn cap(&self) -> usize {
self.storage.len() - self.len as usize
}
pub fn storage_size(&self) -> usize {
self.storage.cap()
}
pub async fn read_into(
self,
limit: usize,
r: &mut impl ReadOwned,
) -> (std::io::Result<usize>, Self) {
let read_cap = std::cmp::min(limit, self.cap());
assert!(read_cap > 0, "refusing to do empty read");
let read_off = self.len;
tracing::trace!(%read_off, %read_cap, storage = ?self.storage, len = %self.len, "read_into in progress...");
let read_into = ReadInto {
buf: self,
off: read_off,
cap: read_cap.try_into().unwrap(),
init: 0,
};
let (res, mut read_into) = r.read(read_into).await;
tracing::trace!(init = %read_into.init, "read_into done!");
read_into.buf.len += read_into.init;
(res, read_into.buf)
}
pub fn put(&mut self, s: impl AsRef<[u8]>) -> Result<(), DoesNotFit> {
let s = s.as_ref();
let len = s.len();
if len > self.cap() {
return Err(DoesNotFit);
}
unsafe {
let ptr = self.storage.as_mut_ptr().add(self.len as usize);
std::ptr::copy_nonoverlapping(s.as_ptr(), ptr, len);
}
let u32_len: u32 = len.try_into().unwrap();
self.len += u32_len;
Ok(())
}
pub fn put_with<T, E>(
&mut self,
len: usize,
f: impl FnOnce(&mut [u8]) -> Result<T, E>,
) -> Result<T, E> {
assert!(len <= self.cap());
let u32_len: u32 = len.try_into().unwrap();
let slice = unsafe {
std::slice::from_raw_parts_mut(self.storage.as_mut_ptr().add(self.len as usize), len)
};
let res = f(slice);
if res.is_ok() {
self.len += u32_len;
}
res
}
pub fn put_to_roll(
&mut self,
len: usize,
f: impl FnOnce(&mut [u8]) -> eyre::Result<()>,
) -> eyre::Result<Roll> {
assert_eq!(self.len(), 0);
if self.cap() < len {
self.reserve()?;
}
self.put_with(len, f)?;
let roll = self.take_all();
debug_assert_eq!(roll.len(), len);
Ok(roll)
}
pub fn filled(&self) -> Roll {
match &self.storage {
StorageMut::Buf(b) => b.freeze_slice(0..self.len()).into(),
StorageMut::Box(b) => RollBox {
b: b.clone(),
len: self.len,
}
.into(),
}
}
pub fn skip(&mut self, n: usize) {
let u32_n: u32 = n.try_into().unwrap();
assert!(u32_n <= self.len);
match &mut self.storage {
StorageMut::Buf(b) => b.skip(n),
StorageMut::Box(b) => b.off += u32_n,
}
self.len -= u32_n;
}
pub fn take_at_most(&mut self, n: usize) -> Option<Roll> {
assert!(n != 0, "refusing to do empty take_at_most");
if self.len == 0 {
return None;
}
let n = std::cmp::min(n, self.len as usize);
let roll = self.filled().slice(..n);
self.skip(n);
Some(roll)
}
pub fn take_all(&mut self) -> Roll {
let roll = self.filled();
assert!(
!roll.is_empty(),
"take_all is pointless if the filled part is empty, check len first"
);
self.skip(roll.len());
roll
}
pub fn keep(&mut self, roll: Roll) {
match (&mut self.storage, &roll.inner) {
(StorageMut::Buf(ours), RollInner::Buf(theirs)) => {
assert_eq!(ours.index, theirs.index, "roll must be from same buffer");
assert!(theirs.off >= ours.off, "roll must start within buffer");
let skipped = theirs.off - ours.off;
tracing::trace!(our_index = %ours.index, their_index = %theirs.index, our_off = %ours.off, their_off = %theirs.off, %skipped, "RollMut::keep");
self.len -= skipped as u32;
ours.len -= skipped;
ours.off = theirs.off;
}
(StorageMut::Box(ours), RollInner::Box(theirs)) => {
assert_eq!(
ours.buf.get(),
theirs.b.buf.get(),
"roll must be from same buffer"
);
assert!(theirs.b.off >= ours.off, "roll must start within buffer");
let skipped = theirs.b.off - ours.off;
self.len -= skipped;
ours.off = theirs.b.off;
}
_ => {
panic!("roll must be from same buffer");
}
}
}
}
impl std::io::Write for RollMut {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let n = buf.len();
if self.cap() < n {
self.reserve()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
}
self.put(buf)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
Ok(n)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl Deref for RollMut {
type Target = [u8];
#[inline(always)]
fn deref(&self) -> &Self::Target {
match &self.storage {
StorageMut::Buf(b) => &b[..self.len as usize],
StorageMut::Box(b) => b.slice(self.len),
}
}
}
struct ReadInto {
buf: RollMut,
off: u32,
cap: u32,
init: u32,
}
unsafe impl IoBuf for ReadInto {
#[inline(always)]
fn stable_ptr(&self) -> *const u8 {
unreachable!("ReadInto should never be used as a write buffer")
}
#[inline(always)]
fn bytes_init(&self) -> usize {
unreachable!("ReadInto should never be used as a write buffer")
}
#[inline(always)]
fn bytes_total(&self) -> usize {
self.cap as _
}
}
unsafe impl IoBufMut for ReadInto {
#[inline(always)]
fn stable_mut_ptr(&mut self) -> *mut u8 {
unsafe { self.buf.storage.as_mut_ptr().add(self.off as usize) }
}
#[inline(always)]
unsafe fn set_init(&mut self, pos: usize) {
self.init = pos.try_into().expect("reads should be < 4GiB");
}
}
#[derive(Clone)]
pub struct Roll {
inner: RollInner,
}
impl Debug for Roll {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self[..], f)
}
}
unsafe impl IoBuf for Roll {
#[inline(always)]
fn stable_ptr(&self) -> *const u8 {
match &self.inner {
RollInner::Buf(b) => b.stable_ptr(),
RollInner::Box(b) => b.b.as_ptr(),
RollInner::Empty => [].as_ptr(),
}
}
#[inline(always)]
fn bytes_init(&self) -> usize {
self.len()
}
#[inline(always)]
fn bytes_total(&self) -> usize {
self.len()
}
}
impl<T> PartialEq<T> for Roll
where
T: AsRef<[u8]>,
{
fn eq(&self, other: &T) -> bool {
&self[..] == other.as_ref()
}
}
impl Eq for Roll {}
impl From<RollInner> for Roll {
fn from(inner: RollInner) -> Self {
Self { inner }
}
}
impl From<RollBox> for Roll {
fn from(b: RollBox) -> Self {
RollInner::Box(b).into()
}
}
impl From<Buf> for Roll {
fn from(b: Buf) -> Self {
RollInner::Buf(b).into()
}
}
#[derive(Clone)]
enum RollInner {
Buf(Buf),
Box(RollBox),
Empty,
}
#[derive(Clone)]
struct RollBox {
b: BoxStorage,
len: u32,
}
impl RollBox {
#[inline(always)]
fn split_at(self, at: usize) -> (Self, Self) {
let at: u32 = at.try_into().unwrap();
assert!(at <= self.len);
let left = Self {
b: self.b.clone(),
len: at,
};
let mut right = Self {
b: self.b,
len: self.len - at,
};
right.b.off += at;
(left, right)
}
#[inline(always)]
fn len(&self) -> usize {
self.len as usize
}
fn slice(mut self, range: impl RangeBounds<usize>) -> Self {
let mut new_start = 0;
let mut new_end = self.len();
match range.start_bound() {
Bound::Included(&n) => new_start = n,
Bound::Excluded(&n) => new_start = n + 1,
Bound::Unbounded => {}
}
match range.end_bound() {
Bound::Included(&n) => new_end = n + 1,
Bound::Excluded(&n) => new_end = n,
Bound::Unbounded => {}
}
assert!(new_start <= new_end);
assert!(new_end <= self.len());
self.b.off += new_start as u32;
self.len = (new_end - new_start) as u32;
self
}
}
impl AsRef<[u8]> for RollBox {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
self.b.slice(self.len)
}
}
impl AsRef<[u8]> for Roll {
#[inline(always)]
fn as_ref(&self) -> &[u8] {
match &self.inner {
RollInner::Buf(b) => b.as_ref(),
RollInner::Box(b) => b.as_ref(),
RollInner::Empty => &[],
}
}
}
impl Deref for Roll {
type Target = [u8];
#[inline(always)]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl RollInner {
#[inline(always)]
fn split_at(self, at: usize) -> (Self, Self) {
match self {
RollInner::Buf(b) => {
let (left, right) = b.split_at(at);
(RollInner::Buf(left), RollInner::Buf(right))
}
RollInner::Box(b) => {
let (left, right) = b.split_at(at);
(RollInner::Box(left), RollInner::Box(right))
}
RollInner::Empty => {
assert_eq!(at, 0);
(RollInner::Empty, RollInner::Empty)
}
}
}
}
impl Roll {
pub fn empty() -> Self {
RollInner::Empty.into()
}
#[inline(always)]
pub fn len(&self) -> usize {
match &self.inner {
RollInner::Buf(b) => b.len(),
RollInner::Box(b) => b.len(),
RollInner::Empty => 0,
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn split_at(self, at: usize) -> (Roll, Roll) {
let (left, right) = self.inner.split_at(at);
(left.into(), right.into())
}
pub fn slice(self, range: impl RangeBounds<usize>) -> Self {
match self.inner {
RollInner::Buf(b) => b.slice(range).into(),
RollInner::Box(b) => b.slice(range).into(),
RollInner::Empty => panic!("cannot slice empty roll"),
}
}
pub fn iter(&self) -> RollIter {
RollIter {
roll: self.clone(),
pos: 0,
}
}
pub fn to_string_lossy(&self) -> Cow<'_, str> {
String::from_utf8_lossy(self)
}
pub fn to_string(self) -> Result<RollStr, Utf8Error> {
_ = std::str::from_utf8(&self)?;
Ok(RollStr { roll: self })
}
pub unsafe fn to_string_unchecked(self) -> RollStr {
RollStr { roll: self }
}
}
impl InputIter for Roll {
type Item = u8;
type Iter = Enumerate<Self::IterElem>;
type IterElem = RollIter;
#[inline]
fn iter_indices(&self) -> Self::Iter {
self.iter_elements().enumerate()
}
#[inline]
fn iter_elements(&self) -> Self::IterElem {
self.iter()
}
#[inline]
fn position<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Item) -> bool,
{
self.iter().position(predicate)
}
#[inline]
fn slice_index(&self, count: usize) -> Result<usize, Needed> {
if self.len() >= count {
Ok(count)
} else {
Err(Needed::new(count - self.len()))
}
}
}
pub struct RollIter {
roll: Roll,
pos: usize,
}
impl Iterator for RollIter {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
if self.pos >= self.roll.len() {
return None;
}
let c = self.roll[self.pos];
self.pos += 1;
Some(c)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.roll.len() - self.pos;
(remaining, Some(remaining))
}
}
impl InputTake for Roll {
#[inline]
fn take(&self, count: usize) -> Self {
self.clone().slice(..count)
}
#[inline]
fn take_split(&self, count: usize) -> (Self, Self) {
let (prefix, suffix) = self.clone().split_at(count);
(suffix, prefix)
}
}
impl InputTakeAtPosition for Roll {
type Item = u8;
fn split_at_position<P, E: nom::error::ParseError<Self>>(
&self,
predicate: P,
) -> nom::IResult<Self, Self, E>
where
P: Fn(Self::Item) -> bool,
{
match self.iter().position(predicate) {
Some(i) => Ok(self.clone().take_split(i)),
None => Err(nom::Err::Incomplete(nom::Needed::new(1))),
}
}
fn split_at_position1<P, E: nom::error::ParseError<Self>>(
&self,
predicate: P,
e: nom::error::ErrorKind,
) -> nom::IResult<Self, Self, E>
where
P: Fn(Self::Item) -> bool,
{
match self.iter().position(predicate) {
Some(0) => Err(nom::Err::Error(E::from_error_kind(self.clone(), e))),
Some(i) => Ok(self.take_split(i)),
None => Err(nom::Err::Incomplete(nom::Needed::new(1))),
}
}
fn split_at_position_complete<P, E: nom::error::ParseError<Self>>(
&self,
predicate: P,
) -> nom::IResult<Self, Self, E>
where
P: Fn(Self::Item) -> bool,
{
match self.iter().position(predicate) {
Some(i) => Ok(self.take_split(i)),
None => Ok(self.take_split(self.input_len())),
}
}
fn split_at_position1_complete<P, E: nom::error::ParseError<Self>>(
&self,
predicate: P,
e: nom::error::ErrorKind,
) -> nom::IResult<Self, Self, E>
where
P: Fn(Self::Item) -> bool,
{
match self.iter().position(predicate) {
Some(0) => Err(nom::Err::Error(E::from_error_kind(self.clone(), e))),
Some(i) => Ok(self.take_split(i)),
None => {
if self.is_empty() {
Err(nom::Err::Error(E::from_error_kind(self.clone(), e)))
} else {
Ok(self.take_split(self.input_len()))
}
}
}
}
}
impl FindSubstring<&[u8]> for Roll {
fn find_substring(&self, substr: &[u8]) -> Option<usize> {
if substr.len() > self.len() {
return None;
}
let (&substr_first, substr_rest) = match substr.split_first() {
Some(split) => split,
None => return Some(0),
};
if substr_rest.is_empty() {
return memchr::memchr(substr_first, self);
}
let mut offset = 0;
let haystack = &self[..self.len() - substr_rest.len()];
while let Some(position) = memchr::memchr(substr_first, &haystack[offset..]) {
offset += position;
let next_offset = offset + 1;
if &self[next_offset..][..substr_rest.len()] == substr_rest {
return Some(offset);
}
offset = next_offset;
}
None
}
}
impl Compare<&[u8]> for Roll {
#[inline(always)]
fn compare(&self, t: &[u8]) -> CompareResult {
let pos = self.iter().zip(t.iter()).position(|(a, b)| a != *b);
match pos {
Some(_) => CompareResult::Error,
None => {
if self.len() >= t.len() {
CompareResult::Ok
} else {
CompareResult::Incomplete
}
}
}
}
#[inline(always)]
fn compare_no_case(&self, t: &[u8]) -> CompareResult {
if self
.iter()
.zip(t)
.any(|(a, b)| lowercase_byte(a) != lowercase_byte(*b))
{
CompareResult::Error
} else if self.len() < t.len() {
CompareResult::Incomplete
} else {
CompareResult::Ok
}
}
}
fn lowercase_byte(c: u8) -> u8 {
match c {
b'A'..=b'Z' => c - b'A' + b'a',
_ => c,
}
}
impl InputLength for Roll {
#[inline]
fn input_len(&self) -> usize {
self.len()
}
}
impl<S> Slice<S> for Roll
where
S: RangeBounds<usize>,
{
fn slice(&self, range: S) -> Self {
Roll::slice(self.clone(), range)
}
}
#[derive(Clone)]
pub struct RollStr {
roll: Roll,
}
impl Debug for RollStr {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(&self[..], f)
}
}
impl Deref for RollStr {
type Target = str;
fn deref(&self) -> &Self::Target {
unsafe { std::str::from_utf8_unchecked(&self.roll) }
}
}
impl RollStr {
pub fn into_inner(self) -> Roll {
self.roll
}
}
#[cfg(test)]
mod tests {
use nom::IResult;
use tracing::trace;
use crate::{Roll, RollMut, BUF_SIZE};
#[test]
fn test_roll_put() {
fn test_roll_put_inner(mut rm: RollMut) {
let initial_size = rm.cap();
rm.put(b"hello").unwrap();
assert_eq!(rm.cap(), initial_size - 5);
let filled = rm.filled();
assert_eq!(&filled[..], b"hello");
rm.skip(5);
assert_eq!(rm.len(), 0);
assert_eq!(rm.cap(), initial_size - 5);
}
let rm = RollMut::alloc().unwrap();
assert_eq!(rm.cap(), BUF_SIZE as usize);
test_roll_put_inner(rm);
let mut rm = RollMut::alloc().unwrap();
rm.grow();
test_roll_put_inner(rm);
let mut rm = RollMut::alloc().unwrap();
rm.grow();
rm.grow();
test_roll_put_inner(rm);
}
#[test]
fn test_roll_put_does_not_fit() {
let mut rm = RollMut::alloc().unwrap();
rm.put(" ".repeat(rm.cap())).unwrap();
let err = rm.put("drop").unwrap_err();
assert!(format!("{err:?}").contains("DoesNotFit"));
assert!(format!("{err}").contains("does not fit"));
}
#[test]
fn test_roll_realloc() {
fn test_roll_realloc_inner(mut rm: RollMut) {
let init_cap = rm.cap();
rm.put("hello").unwrap();
rm.take_all();
assert_eq!(rm.cap(), init_cap - 5);
rm.realloc().unwrap();
assert_eq!(rm.cap(), BUF_SIZE as usize);
}
let rm = RollMut::alloc().unwrap();
test_roll_realloc_inner(rm);
let mut rm = RollMut::alloc().unwrap();
rm.grow();
test_roll_realloc_inner(rm);
}
#[test]
fn test_roll_realloc_big() {
let mut rm = RollMut::alloc().unwrap();
rm.grow();
let put = "x".repeat(rm.cap() * 2 / 3);
rm.put(&put).unwrap();
rm.realloc().unwrap();
assert_eq!(rm.storage_size(), BUF_SIZE as usize * 2);
assert_eq!(rm.len(), put.len());
assert_eq!(&rm[..], put.as_bytes());
}
#[test]
fn test_roll_reserve() {
let mut rm = RollMut::alloc().unwrap();
assert_eq!(rm.cap(), BUF_SIZE as usize);
assert_eq!(rm.len(), 0);
rm.reserve().unwrap();
assert_eq!(rm.cap(), BUF_SIZE as usize);
assert_eq!(rm.len(), 0);
rm.put("hello").unwrap();
rm.take_all();
assert_eq!(rm.cap(), BUF_SIZE as usize - 5);
assert_eq!(rm.len(), 0);
rm.reserve().unwrap();
assert_eq!(rm.cap(), BUF_SIZE as usize - 5);
assert_eq!(rm.len(), 0);
let old_cap = rm.cap();
rm.put(b" ".repeat(old_cap)).unwrap();
assert_eq!(rm.cap(), 0);
assert_eq!(rm.len(), old_cap);
rm.reserve().unwrap();
assert_eq!(rm.cap(), 5);
assert_eq!(rm.len(), old_cap);
rm.put("hello").unwrap();
rm.reserve().unwrap();
assert_eq!(rm.cap(), BUF_SIZE as usize);
assert_eq!(rm.len(), BUF_SIZE as usize);
}
#[test]
fn test_roll_put_then_grow() {
let mut rm = RollMut::alloc().unwrap();
assert_eq!(rm.cap(), BUF_SIZE as usize);
let input = b"I am pretty long";
rm.put(input).unwrap();
assert_eq!(rm.len(), input.len());
assert_eq!(&rm[..], input);
assert_eq!(rm.cap(), BUF_SIZE as usize - input.len());
rm.grow();
assert_eq!(rm.cap(), 2 * (BUF_SIZE as usize) - input.len());
assert_eq!(&rm[..], input);
rm.skip(5);
assert_eq!(&rm[..], b"pretty long");
}
#[test]
#[cfg(not(feature = "miri"))]
fn test_roll_readfrom_start() {
use fluke_maybe_uring::io::ChanRead;
fluke_maybe_uring::start(async move {
let mut rm = RollMut::alloc().unwrap();
let (send, mut read) = ChanRead::new();
fluke_maybe_uring::spawn(async move {
send.send("123456").await.unwrap();
});
let mut res;
(res, rm) = rm.read_into(3, &mut read).await;
res.unwrap();
assert_eq!(rm.len(), 3);
assert_eq!(rm.filled().as_ref(), b"123");
(res, rm) = rm.read_into(3, &mut read).await;
res.unwrap();
assert_eq!(rm.len(), 6);
assert_eq!(rm.filled().as_ref(), b"123456");
});
}
#[test]
fn test_roll_keep() {
fn test_roll_keep_inner(mut rm: RollMut) {
rm.put(b"helloworld").unwrap();
assert_eq!(&rm[..], b"helloworld");
{
let roll = rm.filled().slice(3..=5);
assert_eq!(roll, b"low");
}
{
let roll = rm.filled().slice(..5);
assert_eq!(roll, b"hello");
}
let roll = rm.filled().slice(5..);
assert_eq!(roll, b"world");
rm.keep(roll);
assert_eq!(&rm[..], b"world");
}
let rm = RollMut::alloc().unwrap();
test_roll_keep_inner(rm);
let mut rm = RollMut::alloc().unwrap();
rm.grow();
test_roll_keep_inner(rm);
}
#[test]
#[should_panic(expected = "roll must be from same buffer")]
fn test_roll_keep_different_buf() {
let mut rm1 = RollMut::alloc().unwrap();
rm1.put("hello").unwrap();
let mut rm2 = RollMut::alloc().unwrap();
rm2.put("hello").unwrap();
let roll2 = rm2.take_all();
rm1.keep(roll2);
}
#[test]
#[should_panic(expected = "roll must be from same buffer")]
fn test_roll_keep_different_box() {
let mut rm1 = RollMut::alloc().unwrap();
rm1.grow();
rm1.put("hello").unwrap();
let mut rm2 = RollMut::alloc().unwrap();
rm2.grow();
rm2.put("hello").unwrap();
let roll2 = rm2.take_all();
rm1.keep(roll2);
}
#[test]
#[should_panic(expected = "roll must be from same buffer")]
fn test_roll_keep_different_type() {
let mut rm1 = RollMut::alloc().unwrap();
rm1.grow();
rm1.put("hello").unwrap();
let mut rm2 = RollMut::alloc().unwrap();
rm2.put("hello").unwrap();
let roll2 = rm2.take_all();
rm1.keep(roll2);
}
#[test]
#[should_panic(expected = "roll must start within buffer")]
fn test_roll_keep_before_buf() {
let mut rm1 = RollMut::alloc().unwrap();
rm1.put("hello").unwrap();
let roll = rm1.filled();
rm1.skip(5);
rm1.keep(roll);
}
#[test]
#[should_panic(expected = "roll must start within buffer")]
fn test_roll_keep_before_box() {
let mut rm1 = RollMut::alloc().unwrap();
rm1.grow();
rm1.put("hello").unwrap();
let roll = rm1.filled();
rm1.skip(5);
rm1.keep(roll);
}
#[test]
fn test_roll_iter() {
let mut rm = RollMut::alloc().unwrap();
rm.put(b"hello").unwrap();
let roll = rm.filled();
let v = roll.iter().collect::<Vec<_>>();
assert_eq!(v, b"hello");
assert_eq!(roll.to_string_lossy(), "hello");
}
#[test]
#[cfg(not(feature = "miri"))]
fn test_roll_iobuf() {
use fluke_maybe_uring::{
io::{IntoHalves, ReadOwned, WriteOwned},
net::{TcpListener, TcpStream},
};
async fn test_roll_iobuf_inner(mut rm: RollMut) -> eyre::Result<()> {
rm.put(b"hello").unwrap();
let roll = rm.take_all();
let ln = TcpListener::bind("127.0.0.1:0".parse()?).await?;
let local_addr = ln.local_addr()?;
let send_fut = async move {
let stream = TcpStream::connect(local_addr).await?;
let (_stream_r, mut stream_w) = IntoHalves::into_halves(stream);
stream_w.write_all(roll).await?;
Ok::<_, eyre::Report>(())
};
let recv_fut = async move {
let (stream, addr) = ln.accept().await?;
let (mut stream_r, _stream_w) = IntoHalves::into_halves(stream);
println!("Accepted connection from {addr}");
let mut buf = Vec::with_capacity(1024);
let res;
(res, buf) = stream_r.read(buf).await;
res?;
assert_eq!(buf, b"hello");
Ok::<_, eyre::Report>(())
};
tokio::try_join!(send_fut, recv_fut)?;
Ok(())
}
fluke_maybe_uring::start(async move {
let rm = RollMut::alloc().unwrap();
test_roll_iobuf_inner(rm).await.unwrap();
let mut rm = RollMut::alloc().unwrap();
rm.grow();
test_roll_iobuf_inner(rm).await.unwrap();
});
}
#[test]
fn test_roll_take_at_most() {
let mut rm = RollMut::alloc().unwrap();
rm.put(b"hello").unwrap();
let roll = rm.take_at_most(4).unwrap();
assert_eq!(roll, b"hell");
let mut rm = RollMut::alloc().unwrap();
rm.put(b"hello").unwrap();
let roll = rm.take_at_most(12).unwrap();
assert_eq!(roll, b"hello");
let mut rm = RollMut::alloc().unwrap();
assert!(rm.take_at_most(12).is_none());
}
#[test]
fn test_roll_take_all() {
let mut rm = RollMut::alloc().unwrap();
rm.put(b"hello").unwrap();
let roll = rm.take_all();
assert_eq!(roll, b"hello");
}
#[test]
#[should_panic(expected = "take_all is pointless if the filled part is empty")]
fn test_roll_take_all_empty() {
let mut rm = RollMut::alloc().unwrap();
rm.take_all();
}
#[test]
#[should_panic(expected = "refusing to do empty take_at_most")]
fn test_roll_take_at_most_panic() {
let mut rm = RollMut::alloc().unwrap();
rm.take_at_most(0);
}
#[test]
fn test_roll_nom_sample() {
fn parse(i: Roll) -> IResult<Roll, Roll> {
nom::bytes::streaming::tag(&b"HTTP/1.1 200 OK"[..])(i)
}
let mut buf = RollMut::alloc().unwrap();
let input = b"HTTP/1.1 200 OK".repeat(1000);
let mut pending = &input[..];
loop {
if buf.cap() == 0 {
trace!("buf had zero cap, growing");
buf.grow()
}
let (rest, version) = match parse(buf.filled()) {
Ok(t) => t,
Err(e) => {
if e.is_incomplete() {
{
if pending.is_empty() {
println!("ran out of input");
break;
}
let n = std::cmp::min(buf.cap(), pending.len());
buf.put(&pending[..n]).unwrap();
pending = &pending[n..];
println!("advanced by {n}, {} remaining", pending.len());
}
continue;
}
panic!("parsing error: {e}");
}
};
assert_eq!(version, b"HTTP/1.1 200 OK");
buf.keep(rest);
}
}
#[test]
fn test_roll_io_write() {
let mut rm = RollMut::alloc().unwrap();
std::io::Write::write_all(&mut rm, b"hello").unwrap();
let roll = rm.take_all();
assert_eq!(std::str::from_utf8(&roll).unwrap(), "hello");
}
}