#![warn(missing_docs)]
#![warn(
clippy::undocumented_unsafe_blocks,
clippy::missing_safety_doc,
clippy::multiple_unsafe_ops_per_block
)]
#![warn(clippy::cast_lossless)]
use std::{
alloc::{alloc, dealloc, Layout, LayoutError},
cell::{Cell, UnsafeCell},
marker::PhantomData,
mem::{align_of, needs_drop, size_of},
ops::{Deref, DerefMut},
ptr::{drop_in_place, NonNull},
rc::Rc,
};
struct Metadata {
count: u64,
beg: NonNull<u8>,
layout: Layout,
}
impl Metadata {
unsafe fn decrement_and_drop(mut sself: NonNull<Self>) {
sself.as_mut().count -= 1;
if sself.as_ref().count == 0 {
dealloc(sself.as_ref().beg.as_ptr(), sself.as_ref().layout)
}
}
}
pub struct Bump {
metadata: NonNull<Metadata>,
first_free: Cell<NonNull<u8>>,
}
pub struct BumpMember<T> {
metadata: NonNull<Metadata>,
data: NonNull<T>,
}
impl<T> Deref for BumpMember<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { self.data.as_ref() }
}
}
impl<T> DerefMut for BumpMember<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { self.data.as_mut() }
}
}
struct BumpRcEntry<T> {
count: usize,
value: T,
}
enum NeedsDrop<T> {
Yes(NonNull<BumpRcEntry<T>>),
No(NonNull<T>),
}
impl<T> NeedsDrop<T> {
fn from_rc_data(rc_data: NonNull<u8>) -> NeedsDrop<T> {
if needs_drop::<T>() {
NeedsDrop::Yes(rc_data.cast())
} else {
NeedsDrop::No(rc_data.cast())
}
}
}
pub struct RcBumpMember<T> {
metadata: NonNull<Metadata>,
rc_data: NonNull<u8>,
_marker: PhantomData<T>,
}
impl<T> RcBumpMember<T> {
fn rc_data(&self) -> NeedsDrop<T> {
NeedsDrop::from_rc_data(self.rc_data)
}
}
fn inner_bump_layout(capacity: usize, align: usize) -> Result<(Layout, usize), LayoutError> {
Layout::from_size_align(capacity, align)?.extend(Layout::new::<Metadata>())
}
struct RawBumpMember<T> {
metadata: NonNull<Metadata>,
data: NonNull<T>,
}
impl Bump {
pub fn new(capacity: usize, align: usize) -> Self {
if capacity == 0 {
panic!("Trying to create a Bump with null capacity")
}
let (layout, metadata_offset) = inner_bump_layout(capacity, align).unwrap();
let inner_ptr = unsafe { alloc(layout) };
if inner_ptr.is_null() {
panic!("Memory allocation failed")
}
let metadata_ptr = {
let metadata_ptr = unsafe { inner_ptr.add(metadata_offset) };
let metadata_ptr = metadata_ptr.cast::<Metadata>();
unsafe { NonNull::new_unchecked(metadata_ptr) }
};
let first_free = unsafe { NonNull::new_unchecked(inner_ptr) };
let metadata = Metadata {
count: 1,
beg: first_free,
layout,
};
unsafe { metadata_ptr.as_ptr().write(metadata) }
Bump {
metadata: metadata_ptr,
first_free: first_free.into(),
}
}
fn can_fit<T>(&self) -> Option<(*mut T, *mut u8)> {
let first_free: *mut u8 = self.first_free.get().as_ptr();
let align_offset: usize = first_free.align_offset(align_of::<T>());
let tentative_start: usize = (first_free as usize).checked_add(align_offset)?;
let tentative_end: usize = tentative_start.checked_add(size_of::<T>())?;
if tentative_end <= self.metadata.as_ptr() as usize {
let beg = unsafe { first_free.add(align_offset) };
let end = unsafe { beg.add(size_of::<T>()) };
Some((beg.cast(), end))
} else {
None
}
}
fn try_alloc_inner<T>(&self, value: T) -> Result<RawBumpMember<T>, T> {
let (start, end): (*mut T, *mut u8) = match self.can_fit::<T>() {
Some(res) => res,
None => return Err(value),
};
unsafe { start.write(value) };
let start = unsafe { NonNull::new_unchecked(start) };
unsafe { (*self.metadata.as_ptr()).count += 1 }
let new_end: NonNull<u8> = unsafe { NonNull::new_unchecked(end) };
self.first_free.set(new_end);
let res = RawBumpMember {
metadata: self.metadata,
data: start,
};
Ok(res)
}
pub fn try_alloc<T>(&self, value: T) -> Result<BumpMember<T>, T> {
let RawBumpMember { metadata, data } = self.try_alloc_inner(value)?;
Ok(BumpMember { metadata, data })
}
pub fn try_alloc_rc<T>(&self, value: T) -> Result<RcBumpMember<T>, T> {
if needs_drop::<T>() {
let RawBumpMember { metadata, data } = self
.try_alloc_inner(BumpRcEntry { count: 1, value })
.map_err(|srce| srce.value)?;
Ok(RcBumpMember {
metadata,
rc_data: data.cast(),
_marker: PhantomData,
})
} else {
let RawBumpMember { metadata, data } = self.try_alloc_inner(value)?;
Ok(RcBumpMember {
metadata,
rc_data: data.cast(),
_marker: PhantomData,
})
}
}
}
impl Drop for Bump {
fn drop(&mut self) {
unsafe { Metadata::decrement_and_drop(self.metadata) };
}
}
impl<T> Drop for BumpMember<T> {
fn drop(&mut self) {
unsafe {
drop_in_place(self.data.as_ptr());
}
unsafe {
Metadata::decrement_and_drop(self.metadata);
}
}
}
impl<T> Deref for RcBumpMember<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self.rc_data() {
NeedsDrop::Yes(rc_entry) => unsafe { &rc_entry.as_ref().value },
NeedsDrop::No(value) => unsafe { value.as_ref() },
}
}
}
impl<T> Drop for RcBumpMember<T> {
fn drop(&mut self) {
match self.rc_data() {
NeedsDrop::Yes(mut rc_entry) => {
unsafe { rc_entry.as_mut().count -= 1 };
if unsafe { rc_entry.as_ref().count == 0 } {
#[allow(clippy::multiple_unsafe_ops_per_block)]
unsafe {
drop_in_place(&mut (*rc_entry.as_ptr()).value)
};
unsafe { Metadata::decrement_and_drop(self.metadata) };
}
}
NeedsDrop::No(_) => unsafe { Metadata::decrement_and_drop(self.metadata) },
}
}
}
impl<T> Clone for RcBumpMember<T> {
fn clone(&self) -> Self {
match self.rc_data() {
NeedsDrop::Yes(mut rc_data) => unsafe { rc_data.as_mut().count += 1 },
NeedsDrop::No(_) => unsafe { (*self.metadata.as_ptr()).count += 1 },
}
Self {
metadata: self.metadata,
rc_data: self.rc_data,
_marker: PhantomData,
}
}
}
pub struct Paving {
capacity: usize,
align: usize,
current_bump: UnsafeCell<Bump>,
}
impl Paving {
pub fn new(capacity: usize, align: usize) -> Self {
let first_bump = Bump::new(capacity, align);
Self {
capacity,
align,
current_bump: first_bump.into(),
}
}
pub fn try_alloc<T>(&self, value: T) -> Result<BumpMember<T>, T> {
if size_of::<T>() * 2 > self.capacity {
return Err(value);
}
match unsafe { (*self.current_bump.get()).try_alloc(value) } {
Ok(sm) => Ok(sm),
Err(value) => {
unsafe { *self.current_bump.get() = Bump::new(self.capacity, self.align) };
let res = unsafe { (*self.current_bump.get()).try_alloc(value) };
debug_assert!(res.is_ok());
res
}
}
}
pub fn try_alloc_rc<T>(&self, value: T) -> Result<RcBumpMember<T>, T> {
if size_of::<T>() * 2 > self.capacity {
return Err(value);
}
match unsafe { (*self.current_bump.get()).try_alloc_rc(value) } {
Ok(sm) => Ok(sm),
Err(value) => {
unsafe { *self.current_bump.get() = Bump::new(self.capacity, self.align) };
let res = unsafe { (*self.current_bump.get()).try_alloc_rc(value) };
debug_assert!(res.is_ok());
res
}
}
}
}
pub enum OwnedMixedPavingMember<T> {
BumpMember(BumpMember<T>),
Box(Box<T>),
}
impl<T> DerefMut for OwnedMixedPavingMember<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
OwnedMixedPavingMember::BumpMember(sm) => sm,
OwnedMixedPavingMember::Box(b) => b,
}
}
}
impl<T> Deref for OwnedMixedPavingMember<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
OwnedMixedPavingMember::BumpMember(sm) => sm,
OwnedMixedPavingMember::Box(b) => b,
}
}
}
pub enum SharedMixedPavingMember<T> {
RcBumpMember(RcBumpMember<T>),
Rc(Rc<T>),
}
impl<T> Deref for SharedMixedPavingMember<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
SharedMixedPavingMember::RcBumpMember(sm) => sm,
SharedMixedPavingMember::Rc(rc) => rc,
}
}
}
pub struct MixedPaving(Paving);
impl MixedPaving {
pub fn new(capacity: usize, align: usize) -> Self {
Self(Paving::new(capacity, align))
}
pub fn alloc<T>(&self, value: T) -> OwnedMixedPavingMember<T> {
match self.0.try_alloc(value) {
Ok(sm) => OwnedMixedPavingMember::BumpMember(sm),
Err(val) => OwnedMixedPavingMember::Box(Box::new(val)),
}
}
pub fn alloc_rc<T>(&self, value: T) -> SharedMixedPavingMember<T> {
match self.0.try_alloc_rc(value) {
Ok(sm) => SharedMixedPavingMember::RcBumpMember(sm),
Err(val) => SharedMixedPavingMember::Rc(Rc::new(val)),
}
}
}
#[cfg(test)]
mod test {
use std::mem::{align_of, size_of};
use crate::{Bump, Paving};
#[test]
fn test_creation_bump() {
{
let mut bump_member1;
let bump_member2;
{
let bump = Bump::new(2 * size_of::<u64>(), align_of::<u64>());
bump_member1 = bump.try_alloc(123_u64).unwrap();
bump_member2 = bump.try_alloc(456_u64).unwrap();
}
assert_eq!(*bump_member2, 456);
assert_eq!(*bump_member1, 123);
*bump_member1 += 1;
assert_eq!(*bump_member1, 124);
}
}
#[test]
fn test_creation_paving() {
{
let bump_member1;
let bump_member2;
{
let bump = Paving::new(2 * size_of::<u64>(), align_of::<u64>());
bump_member1 = bump.try_alloc(123_u64).unwrap();
bump.try_alloc(0_u64).unwrap();
bump.try_alloc(0_u64).unwrap();
bump_member2 = bump.try_alloc(456_u64).unwrap();
}
assert_eq!(*bump_member1, 123);
assert_eq!(*bump_member2, 456);
}
}
}