Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More allocation work #159

Merged
merged 8 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/musli-core/src/buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl std::error::Error for Error {}
/// [`Context`]: crate::Context
pub trait Buf {
/// An item in the buffer.
type Item: 'static;
type Item;

/// Resize the buffer.
fn resize(&mut self, len: usize, additional: usize) -> bool;
Expand Down
4 changes: 2 additions & 2 deletions crates/musli-core/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub trait Context {
type Buf<'this, T>: Buf<Item = T>
where
Self: 'this,
T: 'static;
T: 'this;
/// An allocated buffer containing a valid string.
type BufString<'this>: AsRef<str>
where
Expand Down Expand Up @@ -77,7 +77,7 @@ pub trait Context {
/// some reason, typically this indicates that we've run out of memory.
///
/// The buffer will be deallocated once the returned handle is dropped.
fn alloc<T>(&self) -> Option<Self::Buf<'_, T>>;
fn alloc<T>(&self) -> Self::Buf<'_, T>;

/// Collect and allocate a string from a [`Display`] implementation.
///
Expand Down
5 changes: 1 addition & 4 deletions crates/musli-core/src/impls/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -622,10 +622,7 @@ where
use crate::Buf;

encoder.encode_variant_fn(|variant| {
let Some(mut buf) = cx.alloc::<u8>() else {
return Err(cx.message("Failed to allocate buffer"));
};

let mut buf = cx.alloc::<u8>();
let mut len = 0;

for w in self.encode_wide() {
Expand Down
12 changes: 6 additions & 6 deletions crates/musli/src/allocator/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,27 @@ pub trait Allocator {
type Buf<'this, T>: Buf<Item = T>
where
Self: 'this,
T: 'static;
T: 'this;

/// Allocate an empty, uninitialized buffer with an alignment matching that
/// of `T`.
///
/// Calling this method returns `None` if the allocation failed.
fn alloc<T>(&self) -> Option<Self::Buf<'_, T>>
fn alloc<'a, T>(&'a self) -> Self::Buf<'a, T>
where
T: 'static;
T: 'a;
}

impl<A> Allocator for &A
where
A: ?Sized + Allocator,
{
type Buf<'this, T> = A::Buf<'this, T> where Self: 'this, T: 'static;
type Buf<'this, T> = A::Buf<'this, T> where Self: 'this, T: 'this;

#[inline(always)]
fn alloc<T>(&self) -> Option<Self::Buf<'_, T>>
fn alloc<'a, T>(&'a self) -> Self::Buf<'a, T>
where
T: 'static,
T: 'a,
{
(*self).alloc::<T>()
}
Expand Down
19 changes: 8 additions & 11 deletions crates/musli/src/allocator/disabled.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ pub struct EmptyBuf<T> {
_marker: PhantomData<T>,
}

impl<T> Buf for EmptyBuf<T>
where
T: 'static,
{
impl<T> Buf for EmptyBuf<T> {
type Item = T;

#[inline]
Expand All @@ -21,12 +18,12 @@ where

#[inline]
fn as_ptr(&self) -> *const Self::Item {
ptr::null()
ptr::NonNull::dangling().as_ptr()
}

#[inline]
fn as_mut_ptr(&mut self) -> *mut Self::Item {
ptr::null_mut()
ptr::NonNull::dangling().as_ptr()
}

#[inline]
Expand Down Expand Up @@ -60,15 +57,15 @@ impl Default for Disabled {
}

impl Allocator for Disabled {
type Buf<'this, T> = EmptyBuf<T> where T: 'static;
type Buf<'this, T> = EmptyBuf<T> where T: 'this;

#[inline(always)]
fn alloc<T>(&self) -> Option<Self::Buf<'_, T>>
fn alloc<'a, T>(&'a self) -> Self::Buf<'a, T>
where
T: 'static,
T: 'a,
{
Some(EmptyBuf {
EmptyBuf {
_marker: PhantomData,
})
}
}
}
16 changes: 8 additions & 8 deletions crates/musli/src/allocator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
//! use musli::buf::BufVec;
//!
//! musli::allocator::default!(|alloc| {
//! let mut a = BufVec::new_in(alloc).expect("allocation a failed");
//! let mut b = BufVec::new_in(alloc).expect("allocation b failed");
//! let mut a = BufVec::new_in(alloc);
//! let mut b = BufVec::new_in(alloc);
//!
//! b.write(b"He11o");
//! a.write(b.as_slice());
Expand All @@ -29,7 +29,7 @@
//! assert_eq!(a.as_slice(), b"He11o W0rld");
//! assert_eq!(a.len(), 11);
//!
//! let mut c = BufVec::new_in(alloc).expect("allocation c failed");
//! let mut c = BufVec::new_in(alloc);
//! c.write(b"!");
//! a.write(c.as_slice());
//!
Expand All @@ -52,7 +52,7 @@ mod system;
pub use self::system::System;

/// The static system allocator instance.
#[cfg(all(feature = "alloc", not(loom)))]
#[cfg(all(not(loom), feature = "alloc"))]
pub static SYSTEM: System = System::new();

mod disabled;
Expand Down Expand Up @@ -94,8 +94,8 @@ macro_rules! __default {
/// use musli::buf::BufVec;
///
/// musli::allocator::default!(|alloc| {
/// let mut a = BufVec::new_in(alloc).expect("allocation a failed");
/// let mut b = BufVec::new_in(alloc).expect("allocation b failed");
/// let mut a = BufVec::new_in(alloc);
/// let mut b = BufVec::new_in(alloc);
///
/// b.write(b"He11o");
/// a.write(b.as_slice());
Expand All @@ -108,7 +108,7 @@ macro_rules! __default {
/// assert_eq!(a.as_slice(), b"He11o W0rld");
/// assert_eq!(a.len(), 11);
///
/// let mut c = BufVec::new_in(alloc).expect("allocation c failed");
/// let mut c = BufVec::new_in(alloc);
/// c.write(b"!");
/// a.write(c.as_slice());
///
Expand All @@ -119,7 +119,7 @@ macro_rules! __default {
#[doc(inline)]
pub use __default as default;

#[cfg(all(feature = "alloc", not(loom)))]
#[cfg(all(not(loom), feature = "alloc"))]
#[macro_export]
#[doc(hidden)]
macro_rules! __default_allocator_impl {
Expand Down
74 changes: 50 additions & 24 deletions crates/musli/src/allocator/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,16 @@ impl<'a> Stack<'a> {
///
/// # Panics
///
/// This method panics if called with a buffer larger than 2**31 or is
/// provided a buffer which is not aligned by 8.
/// This panics if called with a buffer larger than `2^31` bytes.
///
/// An easy way to align a buffer is to use [`StackBuffer`] when
/// constructing it.
pub fn new(buffer: &'a mut [MaybeUninit<u8>]) -> Self {
let size = buffer.len();
assert!(
size <= MAX_BYTES,
"Buffer of {size} bytes is larger than the maximum {MAX_BYTES}"
);

let mut data = buffer.as_mut_ptr_range();

Expand Down Expand Up @@ -214,44 +217,53 @@ impl<'a> Stack<'a> {
}

impl Allocator for Stack<'_> {
type Buf<'this, T> = StackBuf<'this, T> where Self: 'this, T: 'static;
type Buf<'this, T> = StackBuf<'this, T> where Self: 'this, T: 'this;

#[inline]
fn alloc<T>(&self) -> Option<Self::Buf<'_, T>>
fn alloc<'a, T>(&'a self) -> Self::Buf<'a, T>
where
T: 'static,
T: 'a,
{
// SAFETY: We have exclusive access to the internal state, and it's only
// held for the duration of this call.
let region = unsafe { (*self.internal.get()).alloc(0, align_of::<T>())? };
let region = if size_of::<T>() == 0 {
None
} else {
unsafe {
(*self.internal.get())
.alloc(0, align_of::<T>())
.map(|r| r.id)
}
};

Some(StackBuf {
region: region.id,
StackBuf {
region,
internal: &self.internal,
_marker: PhantomData,
})
}
}
}

/// A no-std allocated buffer.
pub struct StackBuf<'a, T> {
region: HeaderId,
region: Option<HeaderId>,
internal: &'a UnsafeCell<Internal>,
_marker: PhantomData<T>,
}

impl<'a, T> Buf for StackBuf<'a, T>
where
T: 'static,
{
impl<'a, T> Buf for StackBuf<'a, T> {
type Item = T;

#[inline]
fn resize(&mut self, len: usize, additional: usize) -> bool {
if additional == 0 {
if additional == 0 || size_of::<T>() == 0 {
return true;
}

let Some(region_id) = self.region else {
return false;
};

let Some(len) = len.checked_mul(size_of::<T>()) else {
return false;
};
Expand All @@ -273,36 +285,44 @@ where
unsafe {
let i = &mut *self.internal.get();

let region = i.region(self.region);
let region = i.region(region_id);

// Region can already fit in the requested bytes.
if region.capacity() >= requested {
return true;
};

let Some(region) = i.realloc(self.region, len, requested, align_of::<T>()) else {
let Some(region) = i.realloc(region_id, len, requested, align_of::<T>()) else {
return false;
};

self.region = region.id;
self.region = Some(region.id);
true
}
}

#[inline]
fn as_ptr(&self) -> *const Self::Item {
let Some(region) = self.region else {
return ptr::NonNull::dangling().as_ptr();
};

unsafe {
let i = &*self.internal.get();
let this = i.header(self.region);
let this = i.header(region);
this.start.cast_const().cast()
}
}

#[inline]
fn as_mut_ptr(&mut self) -> *mut Self::Item {
let Some(region) = self.region else {
return ptr::NonNull::dangling().as_ptr();
};

unsafe {
let i = &*self.internal.get();
let this = i.header(self.region);
let this = i.header(region);
this.start.cast()
}
}
Expand All @@ -312,6 +332,10 @@ where
where
B: Buf<Item = T>,
{
let Some(region) = self.region else {
return Err(buf);
};

let this_len = this_len * size_of::<T>();
let other_len = other_len * size_of::<T>();

Expand All @@ -321,7 +345,7 @@ where

unsafe {
let i = &mut *self.internal.get();
let mut this = i.region(self.region);
let mut this = i.region(region);

debug_assert!(this.capacity() >= this_len);

Expand Down Expand Up @@ -360,9 +384,11 @@ where

impl<T> Drop for StackBuf<'_, T> {
fn drop(&mut self) {
// SAFETY: We have exclusive access to the internal state.
unsafe {
(*self.internal.get()).free(self.region);
if let Some(region) = self.region.take() {
// SAFETY: We have exclusive access to the internal state.
unsafe {
(*self.internal.get()).free(region);
}
}
}
}
Expand Down
Loading