Skip to content
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 exit-stack/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ readme = "Readme.md"
categories = ["embedded", "memory-management", "no-std"]

[dependencies]
static-alloc = { path = "../static-alloc", version = "0.3.0-prerelease" }
static-alloc = { path = "../static-alloc", version = "0.4.0" }

[dev-dependencies]
pin-utils = "0.1"
18 changes: 12 additions & 6 deletions static-alloc/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[package]
name = "static-alloc"
version = "0.3.3"
version = "0.4.0"
description = "A bump allocator on static memory for the alloc-traits crate"
edition = "2024"
documentation = "https://docs.rs/static-alloc"
categories = ["embedded", "memory-management", "no-std"]
rust-version = "1.85"
rust-version = "1.100"

authors.workspace = true
license.workspace = true
Expand All @@ -16,14 +16,20 @@ repository.workspace = true
all-features = true

[dependencies]
alloc-traits = { path = "../alloc-traits", version = "0.1.0" }
# Soft deprecated: currently some functionality is built on top of abstractions
# in this crate but we're no longer recommending the main surface since the
# `Allocator` trait was stabilized in `core` in Rust 1.100.
alloc-traits = { path = "../alloc-traits", version = "0.1.0", optional = true }
portable-atomic = { version = "1", optional = true, default-features = false }
allocator-api2 = { version = "0.4", optional = true, default-features = false }

[dev-dependencies]
allocator-api2 = { version = "0.4", features = ["std"] }

[features]
# Required to build for now but intent to deprecate. Once we remove the hard
# dependency on this we will recommend using no-default-features and remove
# this from the default list in the next major release.
default = ["alloc-traits"]
# Provides interop for `alloc::{boxed::Box, vec::Vec}`.
alloc = []
# Enables the `unsync::Chain` module. Note that this is explicitly outside the
# SemVer stability guarantees!
Expand Down Expand Up @@ -60,4 +66,4 @@ required-features = ["nightly_chain"]
[[test]]
name = "allocator-api2"
path = "tests/allocator-api2.rs"
required-features = ["allocator-api2"]
required-features = ["alloc"]
22 changes: 22 additions & 0 deletions static-alloc/Changes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
# v0.4.0

Structural:
- The `allocator_api2` feature is removed as its functionality is now part of
`core`. We implement the `core::alloc::Allocator` trait as part of the
unconditional public API. For historical reasons the module with adapters
will still be named `allocator_api2` for a while.
- The `alloc-traits` dependency is now marked as optional, but still required
for successful compilation. The crate is partly superseded by Rust 1.100 and
its functionality will be brought into this crate in a way that is not
exposed in the public API.

Feature changes:
- Added `From` impls for `LeakBox` to convert an `Box` from the standard
`alloc` crate when they are allocated via handles from this crate.
- Added `From` impls for `LeakBox` to convert a `Vec` from the standard `alloc`
crate when they are allocated via handles from this crate into a boxed slice.
This will never reallocate and forget about the extraneous capacity. Only the
length is captured.
- Added `AllocTime` as a wrapper around the `alloc-traits` type of the same
name to disentangle this public dependency surface.

# v0.3.3

Expect this to be the last version before requiring rust-version 1.100 (the
Expand Down
10 changes: 5 additions & 5 deletions static-alloc/src/allocator_api2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,11 @@ use crate::{
unsync,
};

use allocator_api2::alloc::{AllocError, Allocator, Layout};
use core::{cell, ptr::NonNull};
use core::{
alloc::{AllocError, Allocator, Layout},
cell,
ptr::NonNull,
};

unsafe impl<T> Allocator for Bump<T> {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
Expand Down Expand Up @@ -213,7 +216,6 @@ impl<A> WithAllocationInfo<A> {
/// ```
/// use static_alloc::allocator_api2::WithAllocationInfo;
/// use static_alloc::bump::Bump;
/// use allocator_api2::vec::Vec;
///
/// let arena = Bump::<[usize; 4]>::uninit();
/// let hdl = WithAllocationInfo::new(&arena);
Expand All @@ -236,7 +238,6 @@ impl<A> WithAllocationInfo<A> {
/// ```
/// use static_alloc::allocator_api2::WithAllocationInfo;
/// use static_alloc::bump::Bump;
/// use allocator_api2::vec::Vec;
///
/// let arena = Bump::<[usize; 4]>::uninit();
/// let hdl = WithAllocationInfo::new(&arena);
Expand All @@ -261,7 +262,6 @@ impl<A> WithAllocationInfo<A> {
/// ```
/// use static_alloc::allocator_api2::WithAllocationInfo;
/// use static_alloc::bump::Bump;
/// use allocator_api2::vec::Vec;
///
/// let arena = Bump::<[usize; 4]>::uninit();
/// let hdl = WithAllocationInfo::new(&arena);
Expand Down
17 changes: 11 additions & 6 deletions static-alloc/src/bump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "polyfill")]
use portable_atomic::{AtomicUsize, Ordering};

use crate::leaked::LeakBox;
use alloc_traits::{AllocTime, LocalAlloc, NonZeroLayout};
use crate::{AllocTime, leaked::LeakBox};
use alloc_traits::{LocalAlloc, NonZeroLayout};

/// Allocator drawing from an inner, statically sized memory resource.
///
Expand Down Expand Up @@ -763,7 +763,12 @@ impl<T> Bump<T> {

/// Move a value into an owned allocation.
///
/// For safely initializing a value _after_ a successful allocation, see [`LeakBox::write`].
/// For safely initializing a value _after_ a successful allocation, allocate `MaybeUninit<T>`
/// instead and see [`LeakBox::write`]. This method only works with sized values as unsized type
/// can not be encapsulated into `MaybeUninit`. Instead, allocate an appropriately sized region
/// with [`Self::get_layout`], cast the pointer to imbue it with the correct metadata and
/// manually initialize the allocation. Then call [`LeakBox::from_raw`] once its safety
/// requirements are fulfilled.
///
/// [`LeakBox::write`]: ../leaked/struct.LeakBox.html#method.write
///
Expand Down Expand Up @@ -1882,7 +1887,7 @@ unsafe impl GlobalAlloc for BumpView<'_> {
// Safety: `ptr` is currently allocated via this allocator, i.e. non-null.
ptr: unsafe { NonNull::new_unchecked(ptr) },
layout: current,
lifetime: AllocTime::default(),
lifetime: alloc_traits::AllocTime::default(),
};

// Safety: satisfies our own invariants.
Expand Down Expand Up @@ -1912,7 +1917,7 @@ unsafe impl<'alloc, T> LocalAlloc<'alloc> for Bump<T> {
Some(alloc_traits::Allocation {
ptr: raw_alloc.ptr,
layout,
lifetime: AllocTime::default(),
lifetime: alloc_traits::AllocTime::default(),
})
}

Expand Down Expand Up @@ -1965,7 +1970,7 @@ unsafe impl<'alloc> LocalAlloc<'alloc> for BumpView<'alloc> {
Some(alloc_traits::Allocation {
ptr: raw_alloc.ptr,
layout,
lifetime: AllocTime::default(),
lifetime: alloc_traits::AllocTime::default(),
})
}

Expand Down
108 changes: 104 additions & 4 deletions static-alloc/src/leaked.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//!
//! FIXME(breaking): Naming. `leaking` implies the `Drop` of the value as well but we do the
//! precise opposite.
use alloc_traits::AllocTime;
use crate::AllocTime;
use core::pin::Pin;

use core::{
Expand Down Expand Up @@ -105,15 +105,17 @@ impl<T> Alloca<T> {
/// Represents an allocation within a Bump.
///
/// This is an owning pointer comparable to `Box`. It drops the contained value when it is dropped
/// itself. The difference is that no deallocation logic is ever executed.
/// itself. The difference is that no deallocation logic is ever executed. This is very similar to
/// `alloc::boxed::Box<T, &'ctx BumpSlice>` but the allocator type can be completely erased. It can
/// be converted from such boxes when the `alloc` feature is enabled.
///
/// FIXME(non-breaking): the name is rather confusing. Maybe it should be `BumpBox` or `RefBox`?
/// FIXME(non-breaking): the name can be rather confusing. Maybe it should be `BumpBox` or `RefBox`?
/// Not `StackBox` because the value's location in memory is not the defining feature.
///
/// # Usage
///
/// This box can be used to manage one valid instance constructed within the memory provided by a
/// `MaybeUninit` instance.
/// `MaybeUninit` instance. This only works with values of sized types.
///
/// ```
/// use core::mem::MaybeUninit;
Expand Down Expand Up @@ -545,6 +547,104 @@ impl<'ctx, T> From<&'ctx mut [MaybeUninit<T>]> for LeakBox<'ctx, [MaybeUninit<T>
}
}

#[cfg(feature = "alloc")]
impl<'ctx, T: ?Sized> From<alloc::boxed::Box<T, &'ctx crate::bump::BumpSlice>>
for LeakBox<'ctx, T>
{
fn from(boxed: alloc::boxed::Box<T, &'ctx crate::bump::BumpSlice>) -> Self {
// SAFETY:
// * The Box type guarantees this is initialized and not aliased.
let (ptr, _allocator) = alloc::boxed::Box::into_raw_with_allocator(boxed);
unsafe { LeakBox::from_raw(ptr) }
}
}

#[cfg(feature = "alloc")]
impl<'ctx, T: ?Sized, U> From<alloc::boxed::Box<T, &'ctx crate::bump::Bump<U>>>
for LeakBox<'ctx, T>
{
fn from(boxed: alloc::boxed::Box<T, &'ctx crate::bump::Bump<U>>) -> Self {
// SAFETY:
// * The Box type guarantees this is initialized and not aliased.
let (ptr, _allocator) = alloc::boxed::Box::into_raw_with_allocator(boxed);
unsafe { LeakBox::from_raw(ptr) }
}
}

#[cfg(feature = "alloc")]
impl<'ctx, T: ?Sized> From<alloc::boxed::Box<T, &'ctx crate::unsync::BumpSlice>>
for LeakBox<'ctx, T>
{
fn from(boxed: alloc::boxed::Box<T, &'ctx crate::unsync::BumpSlice>) -> Self {
// SAFETY:
// * The Box type guarantees this is initialized and not aliased.
let (ptr, _allocator) = alloc::boxed::Box::into_raw_with_allocator(boxed);
unsafe { LeakBox::from_raw(ptr) }
}
}

#[cfg(feature = "alloc")]
impl<'ctx, T: ?Sized, U> From<alloc::boxed::Box<T, &'ctx crate::unsync::Bump<U>>>
for LeakBox<'ctx, T>
{
fn from(boxed: alloc::boxed::Box<T, &'ctx crate::unsync::Bump<U>>) -> Self {
// SAFETY:
// * The Box type guarantees this is initialized and not aliased.
let (ptr, _allocator) = alloc::boxed::Box::into_raw_with_allocator(boxed);
unsafe { LeakBox::from_raw(ptr) }
}
}

#[cfg(feature = "alloc")]
impl<'ctx, T> From<alloc::vec::Vec<T, &'ctx crate::bump::BumpSlice>> for LeakBox<'ctx, [T]> {
fn from(boxed: alloc::vec::Vec<T, &'ctx crate::bump::BumpSlice>) -> Self {
let (ptr, len, _cap, _allocator) = alloc::vec::Vec::into_raw_parts_with_allocator(boxed);
let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
// SAFETY:
// * The Vec guarantees that all elements within its length are initialized and not aliased.
unsafe { LeakBox::from_raw(slice) }
}
}

#[cfg(feature = "alloc")]
impl<'ctx, T, U> From<alloc::vec::Vec<T, &'ctx crate::bump::Bump<U>>>
for LeakBox<'ctx, [T]>
{
fn from(boxed: alloc::vec::Vec<T, &'ctx crate::bump::Bump<U>>) -> Self {
let (ptr, len, _cap, _allocator) = alloc::vec::Vec::into_raw_parts_with_allocator(boxed);
let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
// SAFETY:
// * The Vec guarantees that all elements within its length are initialized and not aliased.
unsafe { LeakBox::from_raw(slice) }
}
}

#[cfg(feature = "alloc")]
impl<'ctx, T> From<alloc::vec::Vec<T, &'ctx crate::unsync::BumpSlice>>
for LeakBox<'ctx, [T]>
{
fn from(boxed: alloc::vec::Vec<T, &'ctx crate::unsync::BumpSlice>) -> Self {
let (ptr, len, _cap, _allocator) = alloc::vec::Vec::into_raw_parts_with_allocator(boxed);
let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
// SAFETY:
// * The Vec guarantees that all elements within its length are initialized and not aliased.
unsafe { LeakBox::from_raw(slice) }
}
}

#[cfg(feature = "alloc")]
impl<'ctx, T, U> From<alloc::vec::Vec<T, &'ctx crate::unsync::Bump<U>>>
for LeakBox<'ctx, [T]>
{
fn from(boxed: alloc::vec::Vec<T, &'ctx crate::unsync::Bump<U>>) -> Self {
let (ptr, len, _cap, _allocator) = alloc::vec::Vec::into_raw_parts_with_allocator(boxed);
let slice = core::ptr::slice_from_raw_parts_mut(ptr, len);
// SAFETY:
// * The Vec guarantees that all elements within its length are initialized and not aliased.
unsafe { LeakBox::from_raw(slice) }
}
}

impl<T: ?Sized> AsRef<T> for LeakBox<'_, T> {
fn as_ref(&self) -> &T {
self
Expand Down
17 changes: 16 additions & 1 deletion static-alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,13 @@
#![no_std]
#![deny(missing_docs)]

#[cfg(not(feature = "alloc-traits"))]
compile_error!("The `alloc-traits` feature is required for this crate to work");

#[cfg(feature = "alloc")]
extern crate alloc;

/// Specialized types for `allocator-api2` (the planned std allocator).
#[cfg(feature = "allocator-api2")]
pub mod allocator_api2;

pub mod bump;
Expand All @@ -74,6 +76,19 @@ macro_rules! insert_as_doc {
}
}

/// Denotes the borrow of an allocator, the maximum validity of an [`Allocation`](`bump::Allocation`).
///
/// This is a zero-sized type with no runtime semantics. It exists so that the borrow relationship
/// can be denoted in the type system (where we otherwise have raw pointers with no lifetime) and
/// you can consequently enforce it to be verified by the borrow checker. When your use of the
/// allocation does not maintain a borrow from the allocator in the same scope, you need to maintain
/// the correctness yourself. You can use [`AllocTime::default()`] to recreate that assertion to
/// APIs that require it.
///
/// See also [`Allocation`](`bump::Allocation`) and [`LeakBox`](`leaked::LeakBox`).
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct AllocTime<'lt>(alloc_traits::AllocTime<'lt>);

/// A module containing extended documentation and examples.
#[cfg(doc)]
pub mod doc {
Expand Down
9 changes: 5 additions & 4 deletions static-alloc/src/unsync/bump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ use core::{
ptr::{self, NonNull},
};

use alloc_traits::AllocTime;

use crate::bump::{Allocation, Failure, Level};
use crate::leaked::LeakBox;
use crate::{
AllocTime,
bump::{Allocation, Failure, Level},
leaked::LeakBox,
};

/// A bump allocator whose storage capacity and alignment is given by `T`.
///
Expand Down
Loading
Loading