use core::cell::UnsafeCell;
use core::ptr::NonNull;
use alloc::boxed::Box;
use alloc::vec::Vec;
use musli::{Allocator, Buf};
pub struct System {
internal: UnsafeCell<Internal>,
}
impl System {
#[inline]
pub const fn new() -> Self {
Self {
internal: UnsafeCell::new(Internal { head: None }),
}
}
}
impl Default for System {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Allocator for System {
type Buf<'this> = SystemBuf<'this> where Self: 'this;
#[inline(always)]
fn alloc(&self) -> Option<Self::Buf<'_>> {
Some(SystemBuf {
region: Internal::alloc(&self.internal),
internal: &self.internal,
})
}
}
impl Drop for System {
fn drop(&mut self) {
let internal = unsafe { &mut *self.internal.get() };
while let Some(mut head) = internal.head.take() {
unsafe {
internal.head = head.as_mut().next.take();
drop(Box::from_raw(head.as_ptr()));
}
}
}
}
pub struct SystemBuf<'a> {
region: &'a mut Region,
internal: &'a UnsafeCell<Internal>,
}
impl<'a> Buf for SystemBuf<'a> {
#[inline]
fn write(&mut self, bytes: &[u8]) -> bool {
self.region.data.extend_from_slice(bytes);
true
}
#[inline(always)]
fn len(&self) -> usize {
self.region.data.len()
}
#[inline(always)]
fn as_slice(&self) -> &[u8] {
&self.region.data
}
}
impl<'a> Drop for SystemBuf<'a> {
fn drop(&mut self) {
Internal::free(self.internal, self.region);
}
}
#[repr(C)]
struct Region {
data: Vec<u8>,
next: Option<NonNull<Region>>,
}
struct Internal {
head: Option<NonNull<Region>>,
}
impl Internal {
fn alloc<'a>(this: &UnsafeCell<Self>) -> &'a mut Region {
let internal = unsafe { &mut *this.get() };
if let Some(mut head) = internal.head.take() {
unsafe {
let head = head.as_mut();
internal.head = head.next.take();
head
}
} else {
Box::leak(Box::new(Region {
data: Vec::new(),
next: None,
}))
}
}
fn free(this: &UnsafeCell<Self>, region: &mut Region) {
unsafe {
let this = &mut *this.get();
region.data.clear();
region.next = this.head;
this.head = Some(NonNull::from(region));
}
}
}