#![allow(dead_code)]
use std::borrow::{Borrow, BorrowMut};
use std::ptr;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::io::{Read, Write, Result};
pub struct MBuf {
data: *mut u8,
len: usize,
capacity: usize,
ref_count: *mut AtomicUsize,
}
impl MBuf {
pub fn with_capacity(capacity: usize) -> Self {
let layout = std::alloc::Layout::from_size_align(capacity, 1).unwrap();
let data = unsafe { std::alloc::alloc(layout) };
let ref_count = Box::into_raw(Box::new(AtomicUsize::new(1)));
Self {
data,
len: 0,
capacity,
ref_count,
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn capacity(&self) -> usize {
self.capacity
}
pub fn append(&mut self, data: &[u8]) -> usize {
let current_len = self.len;
let needed = current_len + data.len();
if needed > self.capacity {
self.resize(needed);
}
let to_copy = data.len();
unsafe {
ptr::copy_nonoverlapping(
data.as_ptr(),
self.data.add(current_len),
to_copy
);
}
self.len = current_len + to_copy;
to_copy
}
pub fn is_unique(&self) -> bool {
unsafe { (*self.ref_count).load(Ordering::Acquire) == 1 }
}
pub fn resize(&mut self, new_capacity: usize) {
let current_len = self.len;
let new_capacity = std::cmp::max(new_capacity, self.capacity + self.capacity / 2);
let new_layout = std::alloc::Layout::from_size_align(new_capacity, 1).unwrap();
let new_data = unsafe { std::alloc::alloc(new_layout) };
unsafe {
ptr::copy_nonoverlapping(
self.data,
new_data,
current_len
);
if (*self.ref_count).fetch_sub(1, Ordering::AcqRel) == 1 {
let old_layout = std::alloc::Layout::from_size_align(self.capacity, 1).unwrap();
std::alloc::dealloc(self.data, old_layout);
drop(Box::from_raw(self.ref_count));
}
}
self.data = new_data;
self.capacity = new_capacity;
self.ref_count = Box::into_raw(Box::new(AtomicUsize::new(1)));
}
pub fn clear(&mut self) {
self.len = 0;
}
}
impl Clone for MBuf {
fn clone(&self) -> Self {
unsafe {
(*self.ref_count).fetch_add(1, Ordering::Relaxed);
}
Self {
data: self.data,
len: self.len,
capacity: self.capacity,
ref_count: self.ref_count,
}
}
}
impl Drop for MBuf {
fn drop(&mut self) {
unsafe {
if (*self.ref_count).fetch_sub(1, Ordering::AcqRel) == 1 {
let layout = std::alloc::Layout::from_size_align(self.capacity, 1).unwrap();
std::alloc::dealloc(self.data, layout);
drop(Box::from_raw(self.ref_count));
}
}
}
}
impl Deref for MBuf {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.data, self.len) }
}
}
impl DerefMut for MBuf {
fn deref_mut(&mut self) -> &mut [u8] {
let len = self.len;
unsafe { std::slice::from_raw_parts_mut(self.data, len) }
}
}
impl AsRef<[u8]> for MBuf {
fn as_ref(&self) -> &[u8] {
&*self
}
}
impl AsMut<[u8]> for MBuf {
fn as_mut(&mut self) -> &mut [u8] {
&mut *self
}
}
impl Borrow<[u8]> for MBuf {
fn borrow(&self) -> &[u8] {
&*self
}
}
impl BorrowMut<[u8]> for MBuf {
fn borrow_mut(&mut self) -> &mut [u8] {
&mut *self
}
}
unsafe impl Send for MBuf {}
unsafe impl Sync for MBuf {}
pub struct Cursor<'a> {
buf: &'a MBuf,
pos: usize,
}
impl<'a> Cursor<'a> {
pub fn new(buf: &'a MBuf) -> Self {
Self { buf, pos: 0 }
}
pub fn position(&self) -> usize {
self.pos
}
pub fn next(&mut self) -> Option<u8> {
if self.pos >= self.buf.len() {
return None;
}
let byte = unsafe { *self.buf.data.add(self.pos) };
self.pos += 1;
Some(byte)
}
pub fn next_slice(&mut self, size: usize) -> Option<&'a [u8]> {
if self.pos + size > self.buf.len() {
return None;
}
let slice = unsafe { std::slice::from_raw_parts(self.buf.data.add(self.pos), size) };
self.pos += size;
Some(slice)
}
pub fn reset(&mut self) {
self.pos = 0;
}
}
impl Read for MBuf {
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let len = self.len;
let to_read = std::cmp::min(buf.len(), len);
unsafe {
ptr::copy_nonoverlapping(
self.data,
buf.as_mut_ptr(),
to_read
);
ptr::copy(
self.data.add(to_read),
self.data,
len - to_read
);
}
self.len = len - to_read;
Ok(to_read)
}
}
impl Write for MBuf {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
let written = self.append(buf);
Ok(written)
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mbuf_basic() {
let mut buf = MBuf::with_capacity(1024);
assert_eq!(buf.len(), 0);
assert_eq!(buf.capacity(), 1024);
let data = b"hello world";
let copied = buf.append(data);
assert_eq!(copied, data.len());
assert_eq!(buf.len(), data.len());
let slice: &[u8] = &buf;
assert_eq!(slice, data);
}
#[test]
fn test_cursor() {
let mut buf = MBuf::with_capacity(1024);
let data = b"hello world";
buf.append(data);
let mut cursor = Cursor::new(&buf);
assert_eq!(cursor.next(), Some(b'h'));
assert_eq!(cursor.next_slice(4), Some(b"ello".as_ref()));
assert_eq!(cursor.position(), 5);
cursor.reset();
assert_eq!(cursor.position(), 0);
assert_eq!(cursor.next_slice(data.len()), Some(data.as_ref()));
}
#[test]
fn test_read() {
let mut buf = MBuf::with_capacity(1024);
let data = b"test data";
buf.append(data);
let mut read_buf = [0u8; 9];
let read_len = buf.read(&mut read_buf).unwrap();
assert_eq!(read_len, data.len());
assert_eq!(&read_buf, data);
assert_eq!(buf.len(), 0);
}
#[test]
fn test_write() {
let mut buf = MBuf::with_capacity(1024);
let data = b"write test";
let written = buf.write(data).unwrap();
assert_eq!(written, data.len());
assert_eq!(buf.len(), data.len());
assert_eq!(&*buf, data);
}
#[test]
fn test_fetch_sub() {
let mm = AtomicUsize::new(1);
let q = mm.fetch_sub(1, Ordering::Relaxed);
println!("q: {}", q);
let q = mm.fetch_sub(1, Ordering::Relaxed);
println!("q: {}", q);
}
}