use std::ptr::NonNull;
use crate::{
block::{drop_node, BlockNode, BLOCK_CAP},
Buffer,
};
pub struct BufferMut {
pub(crate) head: Option<NonNull<BlockNode>>,
pub(crate) tail: Option<NonNull<BlockNode>>,
pub(crate) read_offset: usize,
pub(crate) write_offset: usize,
pub(crate) cap: usize,
pub(crate) len: usize,
}
impl Drop for BufferMut {
#[inline]
fn drop(&mut self) {
let mut maybe_node = self.head;
while let Some(mut node) = maybe_node {
maybe_node = unsafe { node.as_mut() }.next.take();
unsafe { drop_node(node) };
}
}
}
impl BufferMut {
#[inline]
pub fn with_capacity(capacity: usize) -> Self {
let mut allocated = 0;
let mut head = None;
while allocated < capacity {
let mut node = BlockNode::alloc();
allocated += BLOCK_CAP;
let node_mut = unsafe { node.as_mut() };
node_mut.next = head;
head = Some(node);
}
Self {
head,
tail: head,
read_offset: 0,
write_offset: 0,
cap: allocated,
len: 0,
}
}
#[inline]
pub const fn new() -> Self {
Self {
head: None,
tail: None,
read_offset: 0,
write_offset: 0,
cap: 0,
len: 0,
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn capacity(&self) -> usize {
self.cap
}
#[inline]
pub fn clear(&mut self) {
self.len = 0;
self.tail = self.head;
self.write_offset = self.read_offset;
}
#[inline]
pub unsafe fn set_len(&mut self, len: usize) {
debug_assert!(len <= self.cap, "set_len out of bounds");
self.len = len;
let mut target_offset = self.read_offset + len;
let mut new_tail = self.head;
while target_offset > BLOCK_CAP {
target_offset -= BLOCK_CAP;
new_tail = new_tail.unwrap().as_mut().next;
}
if target_offset == BLOCK_CAP && new_tail.is_some() {
if let Some(next) = new_tail.unwrap_unchecked().as_mut().next {
target_offset = 0;
new_tail = Some(next);
}
}
self.tail = new_tail;
self.write_offset = target_offset;
}
#[inline]
pub fn reserve(&mut self, additional: usize) {
let want = self.len() + additional;
while self.cap < want {
self.reserve_block();
}
}
#[inline]
pub fn advance(&mut self, cnt: usize) {
assert!(
cnt <= self.len(),
"cannot advance past `remaining`: {:?} <= {:?}",
cnt,
self.len(),
);
self.len -= cnt;
self.cap -= cnt;
let mut offset = self.read_offset + cnt;
while offset > BLOCK_CAP {
offset -= BLOCK_CAP;
let mut to_drop = self.head.unwrap();
self.head = unsafe { to_drop.as_mut() }.next;
unsafe { drop_node(to_drop) };
}
if offset == BLOCK_CAP && self.head.is_some() {
if let Some(next) = unsafe { self.head.unwrap_unchecked().as_mut() }.next {
self.head = Some(next);
offset = 0;
}
}
self.read_offset = offset;
}
#[inline]
pub unsafe fn advance_mut(&mut self, cnt: usize) {
let new_len = self.len() + cnt;
assert!(
new_len <= self.cap,
"new_len = {}; capacity = {}",
new_len,
self.cap
);
self.len = new_len;
let mut offset = self.write_offset + cnt;
while offset > BLOCK_CAP {
offset -= BLOCK_CAP;
self.tail = self.tail.unwrap().as_ref().next;
}
self.write_offset = offset;
}
#[inline]
pub fn freeze(mut self) -> Buffer {
if self.len == 0 {
let mut head = self.head;
while let Some(node) = head {
head = unsafe { node.as_ref() }.next;
unsafe { drop_node(node) };
}
return Buffer {
head: None,
read_offset: 0,
len: 0,
};
}
if self.read_offset == BLOCK_CAP {
self.read_offset = 0;
let mut h = self.head.unwrap();
self.head = unsafe { h.as_mut() }.next;
unsafe { drop_node(h) };
}
let mut node = self.head.unwrap();
let mut cnt = 0;
let target = self.len + self.read_offset;
loop {
cnt += BLOCK_CAP;
if cnt >= target {
break;
}
node = unsafe { node.as_mut() }.next.unwrap();
}
let mut to_drop = unsafe { node.as_mut() }.next.take();
while let Some(node) = to_drop {
to_drop = unsafe { node.as_ref() }.next;
unsafe { drop_node(node) };
}
Buffer {
head: self.head,
read_offset: self.read_offset,
len: self.len,
}
}
pub(crate) fn reserve_block(&mut self) {
let mut node = BlockNode::alloc();
if let Some(mut tail) = self.tail {
unsafe { node.as_mut() }.next = unsafe { tail.as_mut() }.next.take();
unsafe { tail.as_mut() }.next = Some(node);
} else {
if self.head.is_none() {
self.head = Some(node);
}
self.tail = Some(node);
self.read_offset = 0;
self.write_offset = 0;
}
self.cap += BLOCK_CAP;
if self.write_offset == BLOCK_CAP {
self.write_offset = 0;
self.tail = unsafe { self.tail.unwrap().as_ref() }.next;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new() {
let mut b = BufferMut::new();
assert_eq!(b.len(), 0);
assert!(b.is_empty());
assert_eq!(b.capacity(), 0);
b.reserve(8);
unsafe { b.set_len(4) };
assert_eq!(b.len(), 4, "length should be 4");
assert_eq!(b.capacity(), BLOCK_CAP, "one block should be allocated");
assert_eq!(b.write_offset, 4);
}
#[test]
fn set_len() {
let b = BufferMut::with_capacity(BLOCK_CAP * 3);
assert_eq!(b.len(), 0);
assert_eq!(b.capacity(), BLOCK_CAP * 3);
let mut b = BufferMut::with_capacity(BLOCK_CAP * 3 + 1);
assert_eq!(b.len(), 0);
assert_eq!(b.capacity(), BLOCK_CAP * 4);
unsafe { b.set_len(BLOCK_CAP) };
assert_eq!(b.len(), BLOCK_CAP, "length should equals to BLOCK_CAP");
assert_eq!(b.capacity(), BLOCK_CAP * 4);
assert_eq!(b.write_offset, 0);
unsafe { b.set_len(1) };
assert_eq!(b.len(), 1, "length should equals to 1");
assert_eq!(b.capacity(), BLOCK_CAP * 4);
assert_eq!(b.write_offset, 1);
assert_eq!(b.head, b.tail);
}
#[test]
fn clear() {
let mut b = BufferMut::with_capacity(BLOCK_CAP + 1);
unsafe { b.set_len(BLOCK_CAP) };
b.clear();
assert!(b.is_empty());
assert_eq!(b.capacity(), BLOCK_CAP * 2);
}
#[test]
#[should_panic]
fn advance_panic() {
let mut b = BufferMut::new();
b.advance(4);
}
#[test]
fn advance() {
let mut b = BufferMut::with_capacity(BLOCK_CAP);
unsafe { b.set_len(8) };
b.advance(4);
assert_eq!(b.capacity(), BLOCK_CAP - 4);
assert_eq!(b.len(), 4);
unsafe { b.set_len(2) };
assert_eq!(b.capacity(), BLOCK_CAP - 4);
assert_eq!(b.len(), 2);
}
}