diff --git a/crates/musli/src/alloc/array_buffer.rs b/crates/musli/src/alloc/array_buffer.rs new file mode 100644 index 000000000..859ec30fb --- /dev/null +++ b/crates/musli/src/alloc/array_buffer.rs @@ -0,0 +1,58 @@ +use core::mem::MaybeUninit; +use core::ops::{Deref, DerefMut}; + +use super::DEFAULT_ARRAY_BUFFER; + +/// An array that can conveniently be used as a buffer, by default this is +/// [`DEFAULT_ARRAY_BUFFER`] bytes large. +/// +/// This is aligned to 8 bytes, since that's an alignment which works with many +/// common Rust types. +/// +/// See the [module level documentation][super] for more information. +#[repr(align(8))] +pub struct ArrayBuffer { + data: [MaybeUninit; N], +} + +impl ArrayBuffer { + /// Construct a new buffer with the default size of + /// [`DEFAULT_ARRAY_BUFFER`]. + pub const fn new() -> Self { + Self::with_size() + } +} + +impl Default for ArrayBuffer { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl ArrayBuffer { + /// Construct a new buffer with a custom size. + pub const fn with_size() -> Self { + Self { + // SAFETY: This is safe to initialize, since it's just an array of + // contiguous uninitialized memory. + data: unsafe { MaybeUninit::uninit().assume_init() }, + } + } +} + +impl Deref for ArrayBuffer { + type Target = [MaybeUninit]; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl DerefMut for ArrayBuffer { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} diff --git a/crates/musli/src/alloc/mod.rs b/crates/musli/src/alloc/mod.rs index d56a0570f..1581022ce 100644 --- a/crates/musli/src/alloc/mod.rs +++ b/crates/musli/src/alloc/mod.rs @@ -3,7 +3,7 @@ //! This crate contains two types of allocators: //! * The [`System`] allocator, which uses the system allocation facilities. //! Particularly [`std::alloc::System`]. -//! * The [`Stack`] allocator, which can allocate buffers from a fixed-size +//! * The [`Slice`] allocator, which can allocate buffers from a fixed-size //! slice. //! //! The following types are also provided for convenience: @@ -41,6 +41,36 @@ //! }); //! ``` //! +//! Explicitly using a buffer on the stack with the [`Slice`] allocator: +//! +//! ``` +//! use musli::alloc::{ArrayBuffer, Slice, Vec}; +//! +//! let mut buf = ArrayBuffer::new(); +//! let alloc = Slice::new(&mut buf); +//! +//! let mut a = Vec::new_in(&alloc); +//! let mut b = Vec::new_in(&alloc); +//! +//! b.write(b"He11o"); +//! a.write(b.as_slice()); +//! +//! assert_eq!(a.as_slice(), b"He11o"); +//! assert_eq!(a.len(), 5); +//! +//! a.write(b" W0rld"); +//! +//! assert_eq!(a.as_slice(), b"He11o W0rld"); +//! assert_eq!(a.len(), 11); +//! +//! let mut c = Vec::new_in(&alloc); +//! c.write(b"!"); +//! a.write(c.as_slice()); +//! +//! assert_eq!(a.as_slice(), b"He11o W0rld!"); +//! assert_eq!(a.len(), 12); +//! ``` +//! //! [Müsli]: //! [`std::alloc::System`]: https://doc.rust-lang.org/std/alloc/struct.System.html @@ -67,7 +97,10 @@ pub use self::disabled::Disabled; mod stack; #[doc(inline)] -pub use self::stack::{Stack, StackBuffer}; +pub use self::stack::Slice; + +mod array_buffer; +pub use self::array_buffer::ArrayBuffer; mod string; pub(crate) use self::string::collect_string; @@ -80,7 +113,7 @@ pub use self::vec::Vec; /// The default stack buffer size for the default allocator provided through /// [`default!`]. -pub const DEFAULT_STACK_BUFFER: usize = 4096; +pub const DEFAULT_ARRAY_BUFFER: usize = 4096; #[macro_export] #[doc(hidden)] @@ -96,8 +129,8 @@ macro_rules! __default { /// whether the `alloc` feature is or isn't enabled. /// /// * If the `alloc` feature is enabled, this is the [`System`] allocator. -/// * If the `alloc` feature is disabled, this is the [`Stack`] allocator with -/// [`DEFAULT_STACK_BUFFER`] bytes allocated on the stack. +/// * If the `alloc` feature is disabled, this is the [`Slice`] allocator with +/// [`DEFAULT_ARRAY_BUFFER`] bytes allocated on the stack. /// /// # Examples /// @@ -146,8 +179,8 @@ macro_rules! __default_allocator_impl { macro_rules! __default_allocator_impl { (|$alloc:ident| $body:block) => {{ let mut __buf = - $crate::alloc::StackBuffer::<{ $crate::alloc::DEFAULT_STACK_BUFFER }>::new(); - let $alloc = $crate::alloc::Stack::new(&mut __buf); + $crate::alloc::ArrayBuffer::<{ $crate::alloc::DEFAULT_ARRAY_BUFFER }>::new(); + let $alloc = $crate::alloc::Slice::new(&mut __buf); $body }}; } diff --git a/crates/musli/src/alloc/stack.rs b/crates/musli/src/alloc/stack.rs index 16d5f34bb..b3c0060bf 100644 --- a/crates/musli/src/alloc/stack.rs +++ b/crates/musli/src/alloc/stack.rs @@ -10,56 +10,11 @@ use core::ptr; use super::{Allocator, RawVec}; -use super::DEFAULT_STACK_BUFFER; - // We keep max bytes to 2^31, since that ensures that addition between two // magnitutes never overflow. const MAX_BYTES: usize = i32::MAX as usize; -/// A buffer that can be used to store data on the stack. -/// -/// See the [module level documentation][super] for more information. -#[repr(align(8))] -pub struct StackBuffer { - data: [MaybeUninit; N], -} - -impl StackBuffer { - /// Construct a new buffer with the default size of - /// [`DEFAULT_STACK_BUFFER`]. - pub const fn new() -> Self { - Self { - // SAFETY: This is safe to initialize, since it's just an array of - // contiguous uninitialized memory. - data: unsafe { MaybeUninit::uninit().assume_init() }, - } - } -} - -impl Default for StackBuffer { - #[inline] - fn default() -> Self { - Self::new() - } -} - -impl Deref for StackBuffer { - type Target = [MaybeUninit]; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.data - } -} - -impl DerefMut for StackBuffer { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.data - } -} - -/// A no-std compatible fixed-memory allocator that can be used with the `musli` +/// A no-std compatible slice-based allocator that can be used with the `musli` /// crate. /// /// It is geared towards handling few allocations, but they can be arbitrarily @@ -76,7 +31,37 @@ impl DerefMut for StackBuffer { /// is fine for use with the `musli` crate, but might be a limitation for other /// use-cases. /// -/// # Design +/// ## Examples +/// +/// ``` +/// use musli::alloc::{ArrayBuffer, Slice, Vec}; +/// +/// let mut buf = ArrayBuffer::new(); +/// let alloc = Slice::new(&mut buf); +/// +/// let mut a = Vec::new_in(&alloc); +/// let mut b = Vec::new_in(&alloc); +/// +/// b.write(b"He11o"); +/// a.write(b.as_slice()); +/// +/// assert_eq!(a.as_slice(), b"He11o"); +/// assert_eq!(a.len(), 5); +/// +/// a.write(b" W0rld"); +/// +/// assert_eq!(a.as_slice(), b"He11o W0rld"); +/// assert_eq!(a.len(), 11); +/// +/// let mut c = Vec::new_in(&alloc); +/// c.write(b"!"); +/// a.write(c.as_slice()); +/// +/// assert_eq!(a.as_slice(), b"He11o W0rld!"); +/// assert_eq!(a.len(), 12); +/// ``` +/// +/// ## Design /// /// The allocator takes a buffer of contiguous memory. This is dynamically /// diviced into two parts: @@ -123,7 +108,7 @@ impl DerefMut for StackBuffer { /// # bbbbb moved to 0 /// bbbbbbxxxxaaaaaa /// ``` -pub struct Stack<'a> { +pub struct Slice<'a> { // This must be an unsafe cell, since it's mutably accessed through an // immutable pointers. We simply make sure that those accesses do not // clobber each other, which we can do since the API is restricted through @@ -133,19 +118,14 @@ pub struct Stack<'a> { _marker: PhantomData<&'a mut [MaybeUninit]>, } -impl<'a> Stack<'a> { - /// Build a new no-std allocator. - /// - /// The buffer must be aligned by 8 bytes, and should be a multiple of 8 bytes. +impl<'a> Slice<'a> { + /// Construct a new slice allocator. /// - /// See [type-level documentation][Stack] for more information. + /// See [type-level documentation][Slice] for more information. /// /// # Panics /// /// 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]) -> Self { let size = buffer.len(); assert!( @@ -188,8 +168,8 @@ impl<'a> Stack<'a> { } } -impl Allocator for Stack<'_> { - type RawVec<'this, T> = StackBuf<'this, T> where Self: 'this, T: 'this; +impl Allocator for Slice<'_> { + type RawVec<'this, T> = SliceBuf<'this, T> where Self: 'this, T: 'this; #[inline] fn new_raw_vec<'a, T>(&'a self) -> Self::RawVec<'a, T> @@ -208,7 +188,7 @@ impl Allocator for Stack<'_> { } }; - StackBuf { + SliceBuf { region, internal: &self.internal, _marker: PhantomData, @@ -216,14 +196,16 @@ impl Allocator for Stack<'_> { } } -/// A no-std allocated buffer. -pub struct StackBuf<'a, T> { +/// A slice allocated buffer. +/// +/// See [`Slice`]. +pub struct SliceBuf<'a, T> { region: Option, internal: &'a UnsafeCell, _marker: PhantomData, } -impl<'a, T> RawVec for StackBuf<'a, T> { +impl<'a, T> RawVec for SliceBuf<'a, T> { #[inline] fn resize(&mut self, len: usize, additional: usize) -> bool { if additional == 0 || size_of::() == 0 { @@ -352,7 +334,7 @@ impl<'a, T> RawVec for StackBuf<'a, T> { } } -impl Drop for StackBuf<'_, T> { +impl Drop for SliceBuf<'_, T> { fn drop(&mut self) { if let Some(region) = self.region.take() { // SAFETY: We have exclusive access to the internal state. @@ -424,7 +406,7 @@ struct Range { } impl Range { - fn new(range: std::ops::Range<*mut MaybeUninit>) -> Self { + fn new(range: core::ops::Range<*mut MaybeUninit>) -> Self { Self { start: range.start, end: range.end, @@ -538,7 +520,7 @@ impl Internal { /// Free a region. unsafe fn free_region(&mut self, region: Region) -> Header { - self.unlink(&*region); + self.unlink(®ion); region.ptr.replace(Header { range: self.full.head(), diff --git a/crates/musli/src/alloc/stack/tests.rs b/crates/musli/src/alloc/stack/tests.rs index 4c47552a1..0c5dc0d06 100644 --- a/crates/musli/src/alloc/stack/tests.rs +++ b/crates/musli/src/alloc/stack/tests.rs @@ -3,9 +3,9 @@ use std::fmt::{self, Write}; use std::string::String; use std::vec::Vec as StdVec; -use crate::alloc::{Allocator, Vec}; +use crate::alloc::{Allocator, ArrayBuffer, Vec}; -use super::{Header, HeaderId, Range, Stack, StackBuffer}; +use super::{Header, HeaderId, Range, Slice}; const A: HeaderId = unsafe { HeaderId::new_unchecked(1) }; const B: HeaderId = unsafe { HeaderId::new_unchecked(2) }; @@ -192,8 +192,8 @@ macro_rules! assert_structure { #[test] fn grow_last() { - let mut buf = StackBuffer::<4096>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let a = Vec::::new_in(&alloc); @@ -225,8 +225,8 @@ fn grow_last() { #[test] fn realloc() { - let mut buf = StackBuffer::<4096>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let mut a = Vec::::new_in(&alloc); a.write(&[1, 2, 3, 4]); @@ -301,8 +301,8 @@ fn realloc() { /// they're being written to. #[test] fn grow_empty_moved() { - let mut buf = StackBuffer::<4096>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let mut a = Vec::::new_in(&alloc); let b = Vec::::new_in(&alloc); @@ -350,8 +350,8 @@ fn grow_empty_moved() { /// merging of buffers. #[test] fn extend() { - let mut buf = StackBuffer::<4096>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let mut a = Vec::::new_in(&alloc); let mut b = Vec::::new_in(&alloc); @@ -378,8 +378,8 @@ fn extend() { /// merging of buffers. #[test] fn extend_middle() { - let mut buf = StackBuffer::<4096>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let mut a = Vec::::new_in(&alloc); let mut b = Vec::::new_in(&alloc); @@ -410,8 +410,8 @@ fn extend_middle() { /// merging of buffers. #[test] fn extend_gap() { - let mut buf = StackBuffer::<4096>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let mut a = Vec::::new_in(&alloc); let mut b = Vec::::new_in(&alloc); @@ -445,8 +445,8 @@ fn extend_gap() { /// unhappy about it. #[test] fn test_overlapping_slice_miri() { - let mut buf = StackBuffer::<4096>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let mut a = Vec::::new_in(&alloc); a.write(&[1, 2, 3, 4]); @@ -463,8 +463,8 @@ fn test_overlapping_slice_miri() { /// Test when we have a prior allocation that has been freed and we can grow into it. #[test] fn grow_into_preceeding() { - let mut buf = StackBuffer::<4096>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let mut a = Vec::::new_in(&alloc); a.write(&[0]); @@ -501,8 +501,8 @@ fn grow_into_preceeding() { /// Test when we have a prior allocation that has been freed and we can grow into it. #[test] fn flip_flop() { - let mut buf = StackBuffer::<4096>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let mut a = Vec::::new_in(&alloc); let mut b = Vec::::new_in(&alloc); @@ -579,12 +579,12 @@ fn flip_flop() { /// Test when we have a prior allocation that has been freed and we can grow into it. #[test] fn limits() { - let mut buf = StackBuffer::<8>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::<8>::with_size(); + let alloc = Slice::new(&mut buf); assert!(alloc.new_raw_vec::().region.is_none()); - let mut buf = StackBuffer::<32>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::<32>::with_size(); + let alloc = Slice::new(&mut buf); let mut a = Vec::::new_in(&alloc); assert!(a.write(&[0, 1, 2, 3, 4, 5, 6, 7])); diff --git a/crates/musli/src/alloc/tests.rs b/crates/musli/src/alloc/tests.rs index 6dd49ac91..d3865b9b2 100644 --- a/crates/musli/src/alloc/tests.rs +++ b/crates/musli/src/alloc/tests.rs @@ -75,8 +75,8 @@ fn system_basic() { #[test] fn nostd_basic() { - let mut buf = super::StackBuffer::<4096>::new(); - let alloc = super::Stack::new(&mut buf); + let mut buf = super::ArrayBuffer::<4096>::new(); + let alloc = super::Slice::new(&mut buf); basic_allocations(&alloc); } @@ -110,7 +110,7 @@ fn system_zst() { #[test] fn stack_zst() { - let mut buf = super::StackBuffer::<4096>::new(); - let alloc = super::Stack::new(&mut buf); + let mut buf = super::ArrayBuffer::<4096>::new(); + let alloc = super::Slice::new(&mut buf); zst_allocations(&alloc); } diff --git a/crates/musli/tests/trace_no_std.rs b/crates/musli/tests/trace_no_std.rs index 7093586b1..800fff1ee 100644 --- a/crates/musli/tests/trace_no_std.rs +++ b/crates/musli/tests/trace_no_std.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; -use musli::alloc::{Stack, StackBuffer}; +use musli::alloc::{ArrayBuffer, Slice}; use musli::context; use musli::{Decode, Encode}; @@ -19,8 +19,8 @@ struct Collection { #[test] fn trace_no_std() { - let mut buf = StackBuffer::<1024>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::<1024>::with_size(); + let alloc = Slice::new(&mut buf); let cx = context::with_alloc(&alloc); let mut values = HashMap::new(); diff --git a/no-std/examples/json.rs b/no-std/examples/json.rs index f4fad700b..12d1f6612 100644 --- a/no-std/examples/json.rs +++ b/no-std/examples/json.rs @@ -4,7 +4,7 @@ mod prelude; -use musli::alloc::{Stack, StackBuffer}; +use musli::alloc::{ArrayBuffer, Slice}; use musli::context; use musli::{Decode, Encode}; @@ -16,8 +16,8 @@ struct Value<'a> { #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { - let mut buf = StackBuffer::<1024>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let cx = context::with_alloc(&alloc); let encoding = musli::json::Encoding::new(); diff --git a/no-std/examples/serde.rs b/no-std/examples/serde.rs index 3e25383d7..8444d2d62 100644 --- a/no-std/examples/serde.rs +++ b/no-std/examples/serde.rs @@ -4,7 +4,7 @@ mod prelude; -use musli::alloc::{Stack, StackBuffer}; +use musli::alloc::{ArrayBuffer, Slice}; use musli::context; use musli::{Decode, Encode}; use serde::{Deserialize, Serialize}; @@ -24,8 +24,8 @@ struct Value<'a> { #[start] fn main(_argc: isize, _argv: *const *const u8) -> isize { - let mut buf = StackBuffer::<1024>::new(); - let alloc = Stack::new(&mut buf); + let mut buf = ArrayBuffer::new(); + let alloc = Slice::new(&mut buf); let cx = context::with_alloc(&alloc); let encoding = musli::json::Encoding::new();