Skip to content

Commit

Permalink
clean up and prep 0.1.0 release (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
dataphract authored Aug 22, 2022
1 parent 3908def commit c4b25cb
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 89 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "acid_alloc"
authors = ["dataphract <[email protected]>"]
version = "0.1.0"
edition = "2021"
description = "Bare metal-friendly allocators"
description = "Bare-metal allocators"
readme = "README.md"
repository = "https://github.com/dataphract/acid_alloc"
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -32,3 +32,4 @@ sptr = { version = "0.2.3", optional = true }

[dev-dependencies]
quickcheck = "1.0.3"
version-sync = "0.9.2"
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# `acid_alloc`

## Bare metal-friendly allocators.
[![CI](https://github.com/dataphract/acid_alloc/actions/workflows/ci.yaml/badge.svg)](https://github.com/dataphract/acid_alloc/actions)
[![crates-io](https://img.shields.io/crates/v/acid_alloc.svg)](https://crates.io/crates/acid_alloc)
[![api-docs](https://docs.rs/acid_alloc/badge.svg)](https://docs.rs/acid_alloc)

This crate provides allocators that can function without the backing of another
allocator. This makes them suitable for use on bare metal or with OS allocation
facilities like `mmap(2)`/`brk(2)`.
## Bare-metal allocators.

This crate provides allocators that are suitable for use on bare metal or with
OS allocation facilities like `mmap(2)`/`brk(2)`.

The following allocator types are available:

Expand Down Expand Up @@ -46,7 +49,7 @@ available when enabling feature flags:
<td>Yes</td>
<td>
Exposes constructors for allocators backed by implementors of the
unstable<code>Allocator</code> trait, and enables the internal use of
unstable <code>Allocator</code> trait, and enables the internal use of
nightly-only Rust features. Obviates <code>sptr</code>.
</td>
</tr>
Expand All @@ -69,18 +72,17 @@ standard-library APIs whose implementations are reproduced verbatim here. These
features are listed below along with their authors and/or maintainers:

- `alloc_layout_extra`, by [Amanieu d'Antras]
- `int_log`, by [yoshuawuyts]
- `strict_provenance` for `NonNull<T>`, by [Aria Beingessner (Gankra)]
- `int_log`, by [Yoshua Wuyts]
- `strict_provenance`, by [Aria Beingessner (Gankra)]

This crate also depends on [`sptr`] (also authored by Gankra) to reproduce
strict provenance for normal pointers on stable Rust.

_If I've misattributed any of this work, or a contributor to these features is
missing, please open an issue!_

[library api team]: https://www.rust-lang.org/governance/teams/library#Library%20API%20team
[amanieu d'antras]: https://github.com/Amanieu
[yoshuawuyts]: https://github.com/yoshuawuyts
[yoshua wuyts]: https://github.com/yoshuawuyts
[aria beingessner (gankra)]: https://github.com/Gankra

## License
Expand Down
65 changes: 32 additions & 33 deletions src/buddy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ use core::{
iter::{self, Peekable},
mem::ManuallyDrop,
ops::Range,
ptr,
};

use crate::core::{
Expand Down Expand Up @@ -391,6 +390,13 @@ impl<const BLK_SIZE: usize, const LEVELS: usize> Buddy<BLK_SIZE, LEVELS, Raw> {
/// - `metadata` must be a pointer to a region that satisfies the [`Layout`] returned by
/// [`Self::metadata_layout(num_blocks)`], and it must be valid for reads and writes for the
/// entire size indicated by that `Layout`.
/// - The regions pointed to by `region` and `metadata` must not overlap.
/// - No references to the memory at `region` or `metadata` may exist when this function is
/// called.
/// - As long as the returned `Buddy` exists:
/// - No accesses may be made to the memory at `region` except by way of methods on the
/// returned `Buddy`.
/// - No accesses may be made to the memory at `metadata`.
///
/// # Errors
///
Expand All @@ -413,13 +419,23 @@ impl<const BLK_SIZE: usize, const LEVELS: usize> Buddy<BLK_SIZE, LEVELS, Raw> {

/// Constructs a new `Buddy` from raw pointers without populating it.
///
/// The returned `Buddy` will be unable to allocate until address ranges are made available to
/// it using [`Buddy::add_region`]. See [unpopulated initialization] for details.
///
/// [unpopulated initialization]: crate::buddy::Buddy#unpopulated-initialization
///
/// # Safety
///
/// The caller must uphold the following invariants:
/// - `region` must be a pointer to a region that satisfies the [`Layout`] returned by
/// [`Self::region_layout(num_blocks)`].
/// - `metadata` must be a pointer to a region that satisfies the [`Layout`] returned by
/// [`Self::metadata_layout(num_blocks)`], and it must be valid for reads and writes for the
/// entire size indicated by that `Layout`.
/// - The regions pointed to by `region` and `metadata` must not overlap.
/// - No references to the memory at `metadata` may exist when this function is called.
/// - As long as the returned `Buddy` exists, no accesses may be made to the memory at
/// `metadata`.
///
/// [`Self::region_layout(num_blocks)`]: Self::region_layout
/// [`Self::metadata_layout(num_blocks)`]: Self::metadata_layout
Expand All @@ -437,10 +453,23 @@ impl<const BLK_SIZE: usize, const LEVELS: usize> Buddy<BLK_SIZE, LEVELS, Raw> {

/// Populates a region not already managed by this allocator.
///
/// # Panics
///
/// This method panics if any of the following are true:
/// - Either bound of `addr_range` falls outside the allocator region.
/// - Either bound of `addr_range` is not aligned to the value returned by
/// [`Self::min_block_size()`][0].
///
/// [0]: Buddy::min_block_size
///
/// # Safety
///
/// `range` must be a range of addresses not already managed by this
/// allocator.
/// The caller must uphold the following invariants:
/// - `range` must not overlap any range of addresses already managed by this allocator.
/// - No references to the memory indicated by `addr_range` may exist when this function is
/// called.
/// - As long as `self` exists, no accesses may be made to the memory indicated by `addr_range`
/// except by way of methods on `self`.
pub unsafe fn add_region(&mut self, addr_range: Range<NonZeroUsize>) {
unsafe { self.raw.add_region(addr_range) };
}
Expand Down Expand Up @@ -756,19 +785,6 @@ impl<const BLK_SIZE: usize, const LEVELS: usize, A: BackingAllocator> Buddy<BLK_
pub unsafe fn deallocate(&mut self, ptr: NonNull<u8>) {
unsafe { self.raw.deallocate(ptr) }
}

/// Returns a pointer to the managed region.
///
/// # Safety
///
/// Calling this function cannot cause undefined behavior. However:
/// - Reading from the returned pointer, including upgrading it to a reference, is undefined
/// behavior if there are any outstanding allocations.
/// - Writing to the returned pointer, including upgrading it to a mutable reference, is
/// _always_ undefined behavior.
pub fn region(&mut self) -> NonNull<[u8]> {
self.raw.region()
}
}

impl<const BLK_SIZE: usize, const LEVELS: usize, A: BackingAllocator> fmt::Debug
Expand Down Expand Up @@ -1211,23 +1227,6 @@ impl<const BLK_SIZE: usize, const LEVELS: usize> RawBuddy<BLK_SIZE, LEVELS> {

assert!(block.is_none(), "top level coalesced a block");
}

/// Returns a pointer to the managed region.
///
/// # Safety
///
/// Calling this function cannot cause undefined behavior. However:
/// - Reading from the returned pointer, including upgrading it to a reference, is undefined
/// behavior if there are any outstanding allocations.
/// - Writing to the returned pointer, including upgrading it to a mutable reference, is
/// _always_ undefined behavior.
pub fn region(&mut self) -> NonNull<[u8]> {
NonNull::new(ptr::slice_from_raw_parts_mut(
self.base.ptr().as_ptr(),
self.num_blocks.get() * BLK_SIZE,
))
.unwrap()
}
}

struct AlignedRanges<I>
Expand Down
66 changes: 33 additions & 33 deletions src/bump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,11 @@
//!
//! #### Fragmentation
//!
//! Because bump allocators can allocate blocks of any size, they suffer minimal
//! internal fragmentation. External fragmentation correlates linearly with the
//! amount of deallocated data until all blocks are deallocated or the allocator
//! is manually reset.
//! Because bump allocators can allocate blocks of any size, they suffer minimal internal
//! fragmentation. External fragmentation becomes significant when many deallocations occur
//! without deallocating all outstanding allocations.
use core::{fmt, ptr};
use core::fmt;

use crate::{
core::{
Expand All @@ -42,6 +41,10 @@ use crate::core::ptr::NonNullStrict;
use crate::Global;

/// A bump allocator.
///
/// For a general discussion of bump allocation, see the [module-level documentation].
///
/// [module-level documentation]: crate::bump
pub struct Bump<A: BackingAllocator> {
base: BasePtr,
low_mark: NonZeroUsize,
Expand All @@ -55,7 +58,12 @@ impl Bump<Raw> {
///
/// # Safety
///
/// `region` must be valid for reads and writes for `size` bytes.
/// The caller must uphold the following invariants:
/// - `region` must be a pointer to a region that fits `layout`, and it must be valid for reads
/// and writes for the entire size indicated by `layout`.
/// - No references to the memory at `region` may exist when this function is called.
/// - As long as the returned `Bump` exists, no accesses may be made to the memory at `region`
/// except by way of methods on the returned `Bump`.
pub unsafe fn new_raw(
region: NonNull<u8>,
layout: Layout,
Expand All @@ -69,6 +77,8 @@ impl Bump<Raw> {
/// unallocated memory, while the second manages all of `self`'s allocated
/// memory; all prior allocations from `self` are backed by the second
/// element.
// TODO: this API is not finalized. It should not be considered part of the stable public API.
#[doc(hidden)]
pub fn split(self) -> (Bump<Raw>, Bump<Raw>) {
let base_addr = self.base.addr();
let lower_size = self.low_mark.get().checked_sub(base_addr.get()).unwrap();
Expand Down Expand Up @@ -97,16 +107,18 @@ impl Bump<Raw> {
}

#[cfg(all(any(feature = "alloc", test), not(feature = "unstable")))]
#[cfg_attr(docs_rs, doc(cfg(all(feature = "alloc"))))]
impl Bump<Global> {
/// Attempts to construct a new `Bump` backed by the global allocator.
///
/// In particular, the memory managed by this `Bump` is allocated from the
/// global allocator according to `layout`.
/// The memory managed by this `Bump` is allocated from the global allocator according to
/// `layout`.
///
/// # Errors
///
/// Returns an error if sufficient memory could not be allocated from the
/// global allocator.
/// Returns an error if any of the following are true:
/// - `layout.size()` is zero.
/// - Sufficient memory could not be allocated from the global allocator.
pub fn try_new(layout: Layout) -> Result<Bump<Global>, AllocInitError> {
if layout.size() == 0 {
return Err(AllocInitError::InvalidConfig);
Expand All @@ -128,35 +140,35 @@ impl Bump<Global> {
}

#[cfg(all(any(feature = "alloc", test), feature = "unstable"))]
#[cfg_attr(docs_rs, doc(cfg(all(feature = "alloc"))))]
impl Bump<Global> {
/// Attempts to construct a new `Bump` backed by the global allocator.
///
/// In particular, the memory managed by this `Bump` is allocated from the
/// global allocator according to `layout`.
/// The memory managed by this `Bump` is allocated from the global allocator according to
/// `layout`.
///
/// # Errors
///
/// Returns an error if sufficient memory could not be allocated from the
/// global allocator.
/// Returns an error if sufficient memory could not be allocated from the global allocator.
pub fn try_new(layout: Layout) -> Result<Bump<Global>, AllocInitError> {
Self::try_new_in(layout, Global)
}
}

#[cfg(feature = "unstable")]
#[cfg_attr(docs_rs, doc(cfg(feature = "unstable")))]
impl<A> Bump<A>
where
A: Allocator,
{
/// Attempts to construct a new `Bump` backed by `backing_allocator`.
///
/// In particular, the memory managed by this `Bump` is allocated from
/// `backing_allocator` according to `layout`.
/// The memory managed by this `Bump` is allocated from `backing_allocator` according to
/// `layout`.
///
/// # Errors
///
/// Returns an error if sufficient memory could not be allocated from
/// `backing_allocator`.
/// Returns an error if sufficient memory could not be allocated from `backing_allocator`.
pub fn try_new_in(layout: Layout, backing_allocator: A) -> Result<Bump<A>, AllocInitError> {
unsafe {
let region_ptr = backing_allocator
Expand Down Expand Up @@ -238,26 +250,14 @@ where
/// # Safety
///
/// The caller must uphold the following invariants:
/// - No references to data allocated by this `Bump` may exist when the method is called.
/// - Any pointers to data previously allocated by this allocator may no longer be dereferenced
/// or passed to [`Bump::deallocate()`].
/// - No references to memory allocated by this `Bump` may exist when the method is called.
/// - Any pointers to memory previously allocated by this allocator may no longer be
/// dereferenced or passed to [`Bump::deallocate()`].
///
/// [`Bump::deallocate()`]: Bump::deallocate
pub unsafe fn reset(&mut self) {
self.low_mark = self.base.limit();
}

/// Returns a pointer to the managed region.
///
/// It is undefined behavior to dereference the returned pointer or upgrade
/// it to a reference if there are any outstanding allocations.
pub fn region(&mut self) -> NonNull<[u8]> {
NonNull::new(ptr::slice_from_raw_parts_mut(
self.base.ptr().as_ptr(),
self.layout.size(),
))
.unwrap()
}
}

impl<A> Drop for Bump<A>
Expand Down
11 changes: 8 additions & 3 deletions src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ pub(crate) mod alloc {

use crate::layout_error;

/// Indicates an allocation failure due to resource exhaustion or an unsupported
/// set of arguments.
/// Indicates that an allocation failed.
///
/// This may occur due to resource exhaustion or an unsupported set of arguments.
///
/// When the `unstable` feature is enabled, this type is a re-export of
/// [`core::alloc::AllocError`].
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct AllocError;

Expand Down Expand Up @@ -155,7 +159,8 @@ pub(crate) mod ptr {
where
T: Sized,
{
// SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed to be non-zero.
// SAFETY: The result of `ptr::from::with_addr` is non-null because `addr` is guaranteed
// to be non-zero.
unsafe { NonNull::new_unchecked(self.as_ptr().with_addr(addr.get()) as *mut _) }
}

Expand Down
9 changes: 8 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
//! <td>Yes</td>
//! <td>
//! Exposes constructors for allocators backed by implementors of the
//! unstable<code>Allocator</code> trait, and enables the internal use of
//! unstable <code>Allocator</code> trait, and enables the internal use of
//! nightly-only Rust features. Obviates <code>sptr</code>.
//! </td>
//! </tr>
Expand All @@ -60,6 +60,7 @@
//!
//! [`sptr`]: https://crates.io/crates/sptr
#![doc(html_root_url = "https://docs.rs/acid_alloc/0.1.0")]
#![no_std]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
Expand Down Expand Up @@ -183,6 +184,9 @@ requires_sptr_or_unstable! {
}

/// A marker type indicating that an allocator is backed by raw pointers.
///
/// Allocators using this type will not deallocate owned memory on drop. The memory can be
/// reclaimed using the appropriate `.into_raw_parts()` method.
#[derive(Clone, Debug)]
pub struct Raw;
impl Sealed for Raw {}
Expand All @@ -192,6 +196,9 @@ requires_sptr_or_unstable! {

#[cfg(all(any(feature = "alloc", test), not(feature = "unstable")))]
/// The global memory allocator.
///
/// When both the `alloc` and `unstable` features are enabled, this type is a re-export of
/// [`alloc::alloc::Global`].
#[derive(Clone, Debug)]
pub struct Global;

Expand Down
Loading

0 comments on commit c4b25cb

Please sign in to comment.