linked-buffer 0.0.2

Yet another linked buffer implemention.
use std::ptr::NonNull;

use crate::block::{drop_node, BlockNode, BLOCK_CAP};

/// BufferMut freeze to be Buffer.
/// Buffer can be cloned with very little cost.
pub struct Buffer {
    // the first block pointer
    pub(crate) head: Option<NonNull<BlockNode>>,
    // read offset
    pub(crate) read_offset: usize,
    // whole length
    pub(crate) len: usize,
}

impl Clone for Buffer {
    fn clone(&self) -> Self {
        let mut node = self.head;
        while let Some(mut n) = node {
            unsafe { n.as_mut().block.as_mut() }.ref_count += 1;
            node = unsafe { n.as_ref() }.next;
        }

        Self {
            head: self.head,
            read_offset: self.read_offset,
            len: self.len,
        }
    }
}

impl Buffer {
    /// Returns the number of bytes contained in this `BufferMut`.
    #[inline]
    pub fn len(&self) -> usize {
        self.len
    }

    /// Returns true if the `BufferMut` has a length of 0.
    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    /// Advance read offset.
    /// It's like BufferMut's impl. TODO: maybe merge them?
    #[inline]
    pub fn advance(&mut self, cnt: usize) {
        assert!(
            cnt <= self.len(),
            "cannot advance past `remaining`: {:?} <= {:?}",
            cnt,
            self.len(),
        );
        self.len -= cnt;

        let mut offset = self.read_offset + cnt;
        while offset > BLOCK_CAP {
            offset -= BLOCK_CAP;

            // we have to move to next block, it must exists since we checked the cnt.
            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() {
            // move to next if the next exists
            // head is Some, we checked it already
            if let Some(next) = unsafe { self.head.unwrap_unchecked().as_mut() }.next {
                self.head = Some(next);
                self.read_offset = 0;
            }
        }
        self.read_offset = offset;
    }
}