From caf580138a6d00169a9508e2806d2e1b1cd07437 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Wed, 8 Feb 2023 11:37:41 +0100 Subject: [PATCH 01/29] Implement the Allocator trait from the unstable allocator_api --- Cargo.toml | 3 +++ rust-toolchain | 1 + src/lib.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) create mode 100644 rust-toolchain diff --git a/Cargo.toml b/Cargo.toml index c267779..d7ec421 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,9 @@ license = "MIT OR Apache-2.0" name = "embedded-alloc" version = "0.5.0" +[features] +allocator_api = [] + [dependencies] critical-section = "1.0" diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..07ade69 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 27a9a7d..cbc73b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,9 @@ #![doc = include_str!("../README.md")] #![no_std] +#![cfg_attr( + feature = "allocator_api", + feature(allocator_api, nonnull_slice_from_raw_parts, alloc_layout_extra) +)] use core::alloc::{GlobalAlloc, Layout}; use core::cell::RefCell; @@ -88,3 +92,40 @@ unsafe impl GlobalAlloc for Heap { }); } } + +#[cfg(feature = "allocator_api")] +mod allocator_api { + use core::{ + alloc::{AllocError, Allocator, Layout}, + ptr::NonNull, + }; + + use crate::Heap; + + unsafe impl Allocator for Heap { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + size => critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .allocate_first_fit(layout) + .map(|allocation| NonNull::slice_from_raw_parts(allocation, size)) + .map_err(|_| AllocError) + }), + } + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + if layout.size() != 0 { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .deallocate(NonNull::new_unchecked(ptr.as_ptr()), layout) + }); + } + } + } +} From 7b9150bff3a0bff024ce55b855272f2eb8d99a90 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Mon, 13 Feb 2023 07:37:39 +0100 Subject: [PATCH 02/29] Use toml rust-toolchain format --- rust-toolchain | 1 - rust-toolchain.toml | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 rust-toolchain create mode 100644 rust-toolchain.toml diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 07ade69..0000000 --- a/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -nightly \ No newline at end of file diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" From d9fc9d8b9add641983213aaddd0b38b9d5b96fd1 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Fri, 19 May 2023 10:30:56 +0200 Subject: [PATCH 03/29] Remove nonnull_slice_from_raw_parts feature which is now stable since 1.70 --- src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cbc73b3..c92a123 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,6 @@ #![doc = include_str!("../README.md")] #![no_std] -#![cfg_attr( - feature = "allocator_api", - feature(allocator_api, nonnull_slice_from_raw_parts, alloc_layout_extra) -)] +#![cfg_attr(feature = "allocator_api", feature(allocator_api, alloc_layout_extra))] use core::alloc::{GlobalAlloc, Layout}; use core::cell::RefCell; From 5dfe6ee6b367d2df9828fbff461407b36683d416 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Fri, 20 Oct 2023 05:33:36 -0700 Subject: [PATCH 04/29] Update team in code of conduct --- CODE_OF_CONDUCT.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 3ab76c6..7a47646 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,7 +2,7 @@ ## Conduct -**Contact**: [Cortex-M team](https://github.com/rust-embedded/wg#the-cortex-m-team) +**Contact**: [Libs team](https://github.com/rust-embedded/wg#the-libs-team) * We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. * On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. @@ -10,7 +10,7 @@ * Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. * Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. * We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. -* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Cortex-M team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. +* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Libs team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. * Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. ## Moderation @@ -34,4 +34,4 @@ The enforcement policies listed above apply to all official embedded WG venues; *Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* -[team]: https://github.com/rust-embedded/wg#the-cortex-m-team +[team]: https://github.com/rust-embedded/wg#the-libs-team From 809432fdb7518cdef4f0065a690af3253e6be3a5 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Fri, 20 Oct 2023 05:45:31 -0700 Subject: [PATCH 05/29] Update CODEOWNERS --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d810925..bb51ea1 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @rust-embedded/cortex-m \ No newline at end of file +* @rust-embedded/libs From 9cc8ae3e12a6c1a0a12199d9cd7789819917ba84 Mon Sep 17 00:00:00 2001 From: Rasmus Melchior Jacobsen Date: Sat, 21 Oct 2023 11:32:14 +0200 Subject: [PATCH 06/29] Add nightly CI --- .github/workflows/ci.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d594f7..54e3645 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ on: name: Continuous integration jobs: - ci: + ci-stable: runs-on: ubuntu-latest strategy: matrix: @@ -28,3 +28,23 @@ jobs: with: command: check args: --target=${{ matrix.target }} --examples + ci-nightly: + runs-on: ubuntu-latest + strategy: + matrix: + target: + - thumbv6m-none-eabi + - thumbv7m-none-eabi + + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: nightly + target: ${{ matrix.target }} + override: true + - uses: actions-rs/cargo@v1 + with: + command: check + args: --target=${{ matrix.target }} --examples --features allocator_api \ No newline at end of file From bfe29789a9662fc5d3daed5a9cdec0da9511b1cf Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Fri, 27 Oct 2023 01:05:49 -0700 Subject: [PATCH 07/29] Update CI --- .github/workflows/ci.yml | 57 +++++++++++++++++++++++++++-------- .github/workflows/clippy.yml | 28 ----------------- .github/workflows/cron.yml | 16 +++------- .github/workflows/rustfmt.yml | 24 --------------- README.md | 4 +-- examples/global_alloc.rs | 1 + 6 files changed, 52 insertions(+), 78 deletions(-) delete mode 100644 .github/workflows/clippy.yml delete mode 100644 .github/workflows/rustfmt.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d594f7..ff2e225 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,30 +1,61 @@ on: - push: # Run CI for all branches except GitHub merge queue tmp branches - branches-ignore: - - "gh-readonly-queue/**" pull_request: # Run CI for PRs on any branch merge_group: # Run CI for the GitHub merge queue + workflow_dispatch: # Run CI when manually requested name: Continuous integration jobs: - ci: + check: runs-on: ubuntu-latest + env: {"RUSTFLAGS": "-D warnings"} strategy: matrix: target: - thumbv6m-none-eabi - thumbv7m-none-eabi + toolchain: + - stable + - nightly steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master with: - profile: minimal - toolchain: stable - target: ${{ matrix.target }} - override: true - - uses: actions-rs/cargo@v1 + targets: ${{ matrix.target }} + toolchain: ${{ matrix.toolchain }} + - run: cargo check --target=${{ matrix.target }} --examples + - if: ${{ matrix.toolchain == 'nightly' }} + run: cargo check --target=${{ matrix.target }} --examples --all-features + + clippy: + name: Clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + components: clippy + toolchain: nightly + targets: thumbv6m-none-eabi + - run: cargo clippy --all-features --examples --target=thumbv6m-none-eabi -- --deny warnings + + format: + name: Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly with: - command: check - args: --target=${{ matrix.target }} --examples + components: rustfmt + - run: cargo fmt -- --check + + rustdoc: + name: rustdoc + runs-on: ubuntu-latest + env: {"RUSTDOCFLAGS": "-D warnings"} + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - name: rustdoc + run: cargo rustdoc --all-features diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml deleted file mode 100644 index 0fe9b23..0000000 --- a/.github/workflows/clippy.yml +++ /dev/null @@ -1,28 +0,0 @@ -on: - push: # Run CI for all branches except GitHub merge queue tmp branches - branches-ignore: - - "gh-readonly-queue/**" - pull_request: # Run CI for PRs on any branch - merge_group: # Run CI for the GitHub merge queue - -name: Clippy check - -jobs: - clippy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - if: github.event_name == 'pull_request_target' - with: - ref: refs/pull/${{ github.event.number }}/head - - uses: actions/checkout@v2 - if: github.event_name != 'pull_request_target' - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: clippy - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index baaa18c..ec46eb6 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -9,18 +9,12 @@ jobs: ci-cron: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable with: - profile: minimal - toolchain: stable - target: thumbv6m-none-eabi - override: true - - uses: actions-rs/cargo@v1 - with: - command: check - args: --examples --target thumbv6m-none-eabi - - uses: imjohnbo/issue-bot@v2 + targets: thumbv6m-none-eabi + - run: cargo check --examples --target thumbv6m-none-eabi + - uses: imjohnbo/issue-bot@v3 if: failure() with: title: CI Failure diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml deleted file mode 100644 index 714f503..0000000 --- a/.github/workflows/rustfmt.yml +++ /dev/null @@ -1,24 +0,0 @@ -on: - push: # Run CI for all branches except GitHub merge queue tmp branches - branches-ignore: - - "gh-readonly-queue/**" - pull_request: # Run CI for PRs on any branch - merge_group: # Run CI for the GitHub merge queue - -name: Code formatting check - -jobs: - rustfmt: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: rustfmt - - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check diff --git a/README.md b/README.md index a7ea481..5fd93d7 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,8 @@ Please refer to the documentation of [`critical-section`](https://docs.rs/critic Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. diff --git a/examples/global_alloc.rs b/examples/global_alloc.rs index 63a16e7..812c9f1 100644 --- a/examples/global_alloc.rs +++ b/examples/global_alloc.rs @@ -24,6 +24,7 @@ fn main() -> ! { let mut xs = Vec::new(); xs.push(1); + #[allow(clippy::empty_loop)] loop { /* .. */ } } From 459f4b19106d00213905f43f033da2ea5fcb772c Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sat, 28 Oct 2023 21:39:45 -0700 Subject: [PATCH 08/29] Remove rust-toolchain.toml --- rust-toolchain.toml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 rust-toolchain.toml diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 5d56faf..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "nightly" From cd44eb29868425ca1946c44d50a7383b259dad52 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 29 Oct 2023 13:43:45 -0700 Subject: [PATCH 09/29] CHANGELOG: update for allocator_api feature --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12fd89e..0710ceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +### Added + +- Implemented [`Allocator`] for `Heap` with the `allocator_api` crate feature. + This feature requires a nightly toolchain for the unstable [`allocator_api`] + compiler feature. + +[`Allocator`]: https://doc.rust-lang.org/core/alloc/trait.Allocator.html +[`allocator_api`]: https://doc.rust-lang.org/beta/unstable-book/library-features/allocator-api.html + +### Changed + - Updated `linked_list_allocator` dependency to 0.10.5, which allows compiling with stable rust. From a6e12c0de8a30b5b497dcba9a53752f2ea955f74 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 29 Oct 2023 12:55:41 -0700 Subject: [PATCH 10/29] Add basic smoke test --- .cargo/config.toml | 4 ++ .github/workflows/ci.yml | 17 ++++++- Cargo.toml | 2 + examples/integration_test.rs | 88 ++++++++++++++++++++++++++++++++++++ memory.x | 6 +++ 5 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 .cargo/config.toml create mode 100644 examples/integration_test.rs create mode 100644 memory.x diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..80fc334 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,4 @@ +[target.thumbv7m-none-eabi] +# used to run the qemu_test.rs example with QEMU +runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" +rustflags = ["-C", "link-arg=-Tlink.x"] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff2e225..c7a0f5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,10 +24,25 @@ jobs: with: targets: ${{ matrix.target }} toolchain: ${{ matrix.toolchain }} - - run: cargo check --target=${{ matrix.target }} --examples + - run: cargo check --target=${{ matrix.target }} --example global_alloc - if: ${{ matrix.toolchain == 'nightly' }} run: cargo check --target=${{ matrix.target }} --examples --all-features + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + targets: thumbv7m-none-eabi + toolchain: nightly + - name: Install QEMU + run: | + sudo apt update + sudo apt install qemu-system-arm + - run: qemu-system-arm --version + - run: cargo run --target thumbv7m-none-eabi --example integration_test --all-features + clippy: name: Clippy runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index eeae610..aa8ebee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,3 +36,5 @@ version = "0.10.5" [dev-dependencies] cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" +cortex-m-semihosting = "0.5" +panic-semihosting = { version = "0.6", features = ["exit"] } diff --git a/examples/integration_test.rs b/examples/integration_test.rs new file mode 100644 index 0000000..a45ba87 --- /dev/null +++ b/examples/integration_test.rs @@ -0,0 +1,88 @@ +//! This is a very basic smoke test that runs in QEMU +//! Reference the QEMU section of the [Embedded Rust Book] for more information +//! +//! This only tests integration of the allocator on an embedded target. +//! Comprehensive allocator tests are located in the allocator dependency. +//! +//! After toolchain installation this test can be run with: +//! +//! ```bash +//! cargo +nightly run --target thumbv7m-none-eabi --example integration_test --all-features +//! ``` +//! +//! [Embedded Rust Book]: https://docs.rust-embedded.org/book/intro/index.html + +#![feature(allocator_api)] +#![no_main] +#![no_std] + +extern crate alloc; +extern crate panic_semihosting; + +use alloc::vec::Vec; +use core::mem::{size_of, MaybeUninit}; +use cortex_m_rt::entry; +use cortex_m_semihosting::{debug, hprintln}; +use embedded_alloc::Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +fn test_global_heap() { + assert_eq!(HEAP.used(), 0); + + let mut xs: Vec = alloc::vec![1]; + xs.push(2); + xs.extend(&[3, 4]); + + // do not optimize xs + core::hint::black_box(&mut xs); + + assert_eq!(xs.as_slice(), &[1, 2, 3, 4]); + assert_eq!(HEAP.used(), size_of::() * xs.len()); +} + +fn test_allocator_api() { + // small local heap + const HEAP_SIZE: usize = 16; + let heap_mem: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + let local_heap: Heap = Heap::empty(); + unsafe { local_heap.init(heap_mem.as_ptr() as usize, HEAP_SIZE) } + + assert_eq!(local_heap.used(), 0); + + let mut v: Vec = Vec::new_in(local_heap); + v.push(0xCAFE); + v.extend(&[0xDEAD, 0xFEED]); + + // do not optimize v + core::hint::black_box(&mut v); + + assert_eq!(v.as_slice(), &[0xCAFE, 0xDEAD, 0xFEED]); +} + +#[entry] +fn main() -> ! { + { + const HEAP_SIZE: usize = 1024; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } + } + + #[allow(clippy::type_complexity)] + let tests: &[(fn() -> (), &'static str)] = &[ + (test_global_heap, "test_global_heap"), + (test_allocator_api, "test_allocator_api"), + ]; + + for (test_fn, test_name) in tests { + hprintln!("{}: start", test_name); + test_fn(); + hprintln!("{}: pass", test_name); + } + + // exit QEMU with a success status + debug::exit(debug::EXIT_SUCCESS); + #[allow(clippy::empty_loop)] + loop {} +} diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..367c5c8 --- /dev/null +++ b/memory.x @@ -0,0 +1,6 @@ +MEMORY +{ + /* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */ + FLASH : ORIGIN = 0x00000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 64K +} From 08b99fdbdf1d5471bb12aff740538f8514a16ffe Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 29 Oct 2023 14:00:38 -0700 Subject: [PATCH 11/29] deduplicate code between Allocator and GlobalAlloc --- src/lib.rs | 44 +++++++++++++++----------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c92a123..cc9fe1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,18 +66,17 @@ impl Heap { pub fn free(&self) -> usize { critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free()) } + + fn alloc_first_fit(&self, layout: Layout) -> Result, ()> { + critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().allocate_first_fit(layout)) + } } unsafe impl GlobalAlloc for Heap { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .allocate_first_fit(layout) - .ok() - .map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) - }) + self.alloc_first_fit(layout) + .ok() + .map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { @@ -92,36 +91,23 @@ unsafe impl GlobalAlloc for Heap { #[cfg(feature = "allocator_api")] mod allocator_api { - use core::{ - alloc::{AllocError, Allocator, Layout}, - ptr::NonNull, - }; - - use crate::Heap; + use core::alloc::{AllocError, Allocator, GlobalAlloc, Layout}; + use core::ptr::NonNull; - unsafe impl Allocator for Heap { + unsafe impl Allocator for crate::Heap { fn allocate(&self, layout: Layout) -> Result, AllocError> { match layout.size() { 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), - size => critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .allocate_first_fit(layout) - .map(|allocation| NonNull::slice_from_raw_parts(allocation, size)) - .map_err(|_| AllocError) - }), + size => self + .alloc_first_fit(layout) + .map(|allocation| NonNull::slice_from_raw_parts(allocation, size)) + .map_err(|_| AllocError), } } unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { if layout.size() != 0 { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .deallocate(NonNull::new_unchecked(ptr.as_ptr()), layout) - }); + self.dealloc(ptr.as_ptr(), layout); } } } From b744b416d45b117ca885a2e530e3582fd70f9577 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 29 Oct 2023 14:48:10 -0700 Subject: [PATCH 12/29] Add allocator_api example --- .github/workflows/ci.yml | 2 +- examples/allocator_api.rs | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 examples/allocator_api.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff2e225..69a3600 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: with: targets: ${{ matrix.target }} toolchain: ${{ matrix.toolchain }} - - run: cargo check --target=${{ matrix.target }} --examples + - run: cargo check --target=${{ matrix.target }} --example global_alloc - if: ${{ matrix.toolchain == 'nightly' }} run: cargo check --target=${{ matrix.target }} --examples --all-features diff --git a/examples/allocator_api.rs b/examples/allocator_api.rs new file mode 100644 index 0000000..95ab702 --- /dev/null +++ b/examples/allocator_api.rs @@ -0,0 +1,35 @@ +#![feature(allocator_api)] +#![no_std] +#![no_main] + +extern crate alloc; + +use alloc::vec::Vec; +use core::mem::MaybeUninit; +use core::panic::PanicInfo; +use cortex_m_rt::entry; +use embedded_alloc::Heap; + +// This is not used, but as of 2023-10-29 allocator_api cannot be used without +// a global heap +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +#[entry] +fn main() -> ! { + const HEAP_SIZE: usize = 16; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + let heap: Heap = Heap::empty(); + unsafe { heap.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } + + let mut xs = Vec::new_in(heap); + xs.push(1); + + #[allow(clippy::empty_loop)] + loop { /* .. */ } +} + +#[panic_handler] +fn panic(_: &PanicInfo) -> ! { + loop {} +} From 06f1379337b5152d43ac48186bd747c1b06ca621 Mon Sep 17 00:00:00 2001 From: Jacob Trueb Date: Sun, 29 Oct 2023 22:04:31 -0500 Subject: [PATCH 13/29] Add choice of Two-Level Segregated Fit and Linked List First Fit --- Cargo.toml | 12 +- README.md | 2 +- examples/global_alloc.rs | 5 +- src/lib.rs | 322 +++++++++++++++++++++++++++------------ 4 files changed, 238 insertions(+), 103 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eeae610..0a02726 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,14 +24,20 @@ name = "embedded-alloc" version = "0.5.0" [features] +default = ["llff"] allocator_api = [] +# Use the Two-Level Segregated Fit allocator +tlsf = ["rlsf", "const-default"] + # Use the LinkedList first-fit allocator +llff = ["linked_list_allocator"] + [dependencies] critical-section = "1.0" +linked_list_allocator = { version = "0.10.5", default-features = false, optional = true } +rlsf = { version = "0.2.1", default-features = false, optional = true } +const-default = { version = "1.0.0", default-features = false, optional = true } -[dependencies.linked_list_allocator] -default-features = false -version = "0.10.5" [dev-dependencies] cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } diff --git a/README.md b/README.md index 5fd93d7..a34cb15 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Starting with Rust 1.68, this crate can be used as a global allocator on stable extern crate alloc; use cortex_m_rt::entry; -use embedded_alloc::Heap; +use embedded_alloc::LlffHeap as Heap; #[global_allocator] static HEAP: Heap = Heap::empty(); diff --git a/examples/global_alloc.rs b/examples/global_alloc.rs index 812c9f1..d41debd 100644 --- a/examples/global_alloc.rs +++ b/examples/global_alloc.rs @@ -6,7 +6,10 @@ extern crate alloc; use alloc::vec::Vec; use core::panic::PanicInfo; use cortex_m_rt::entry; -use embedded_alloc::Heap; +// Linked-List First Fit Heap allocator (feature = "llff") +use embedded_alloc::LlffHeap as Heap; +// Two-Level Segregated Fit Heap allocator (feature = "tlsf") +// use embedded_alloc::TlsfHeap as Heap; #[global_allocator] static HEAP: Heap = Heap::empty(); diff --git a/src/lib.rs b/src/lib.rs index c92a123..93ac4ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,121 +7,247 @@ use core::cell::RefCell; use core::ptr::{self, NonNull}; use critical_section::Mutex; -use linked_list_allocator::Heap as LLHeap; -pub struct Heap { - heap: Mutex>, -} +#[cfg(feature = "llff")] +pub use llff::Heap as LlffHeap; +#[cfg(feature = "tlsf")] +pub use tlsf::Heap as TlsfHeap; -impl Heap { - /// Crate a new UNINITIALIZED heap allocator - /// - /// You must initialize this heap using the - /// [`init`](Self::init) method before using the allocator. - pub const fn empty() -> Heap { - Heap { - heap: Mutex::new(RefCell::new(LLHeap::empty())), - } +#[cfg(feature = "llff")] +mod llff { + use super::*; + use linked_list_allocator::Heap as LLHeap; + + pub struct Heap { + heap: Mutex>, } - /// Initializes the heap - /// - /// This function must be called BEFORE you run any code that makes use of the - /// allocator. - /// - /// `start_addr` is the address where the heap will be located. - /// - /// `size` is the size of the heap in bytes. - /// - /// Note that: - /// - /// - The heap grows "upwards", towards larger addresses. Thus `start_addr` will - /// be the smallest address used. - /// - /// - The largest address used is `start_addr + size - 1`, so if `start_addr` is - /// `0x1000` and `size` is `0x30000` then the allocator won't use memory at - /// addresses `0x31000` and larger. - /// - /// # Safety - /// - /// Obey these or Bad Stuff will happen. - /// - /// - This function must be called exactly ONCE. - /// - `size > 0` - pub unsafe fn init(&self, start_addr: usize, size: usize) { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .init(start_addr as *mut u8, size); - }); + impl Heap { + /// Crate a new UNINITIALIZED heap allocator + /// + /// You must initialize this heap using the + /// [`init`](Self::init) method before using the allocator. + pub const fn empty() -> Heap { + Heap { + heap: Mutex::new(RefCell::new(LLHeap::empty())), + } + } + + /// Initializes the heap + /// + /// This function must be called BEFORE you run any code that makes use of the + /// allocator. + /// + /// `start_addr` is the address where the heap will be located. + /// + /// `size` is the size of the heap in bytes. + /// + /// Note that: + /// + /// - The heap grows "upwards", towards larger addresses. Thus `start_addr` will + /// be the smallest address used. + /// + /// - The largest address used is `start_addr + size - 1`, so if `start_addr` is + /// `0x1000` and `size` is `0x30000` then the allocator won't use memory at + /// addresses `0x31000` and larger. + /// + /// # Safety + /// + /// Obey these or Bad Stuff will happen. + /// + /// - This function must be called exactly ONCE. + /// - `size > 0` + pub unsafe fn init(&self, start_addr: usize, size: usize) { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .init(start_addr as *mut u8, size); + }); + } + + /// Returns an estimate of the amount of bytes in use. + pub fn used(&self) -> usize { + critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().used()) + } + + /// Returns an estimate of the amount of bytes available. + pub fn free(&self) -> usize { + critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free()) + } } - /// Returns an estimate of the amount of bytes in use. - pub fn used(&self) -> usize { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().used()) + unsafe impl GlobalAlloc for Heap { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .allocate_first_fit(layout) + .ok() + .map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) + }) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .deallocate(NonNull::new_unchecked(ptr), layout) + }); + } } - /// Returns an estimate of the amount of bytes available. - pub fn free(&self) -> usize { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free()) + #[cfg(feature = "allocator_api")] + mod allocator_api { + use super::*; + use core::{ + alloc::{AllocError, Allocator, Layout}, + ptr::NonNull, + }; + + unsafe impl Allocator for Heap { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + size => critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .allocate_first_fit(layout) + .map(|allocation| NonNull::slice_from_raw_parts(allocation, size)) + .map_err(|_| AllocError) + }), + } + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + if layout.size() != 0 { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .deallocate(NonNull::new_unchecked(ptr.as_ptr()), layout) + }); + } + } + } } } -unsafe impl GlobalAlloc for Heap { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .allocate_first_fit(layout) - .ok() - .map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) - }) - } +#[cfg(feature = "tlsf")] +mod tlsf { + use super::*; + use const_default::ConstDefault; + use rlsf::Tlsf; + + type TlsfHeap = Tlsf<'static, usize, usize, { usize::BITS as usize }, { usize::BITS as usize }>; - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .deallocate(NonNull::new_unchecked(ptr), layout) - }); + pub struct Heap { + heap: Mutex>, } -} -#[cfg(feature = "allocator_api")] -mod allocator_api { - use core::{ - alloc::{AllocError, Allocator, Layout}, - ptr::NonNull, - }; - - use crate::Heap; - - unsafe impl Allocator for Heap { - fn allocate(&self, layout: Layout) -> Result, AllocError> { - match layout.size() { - 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), - size => critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .allocate_first_fit(layout) - .map(|allocation| NonNull::slice_from_raw_parts(allocation, size)) - .map_err(|_| AllocError) - }), + impl Heap { + /// Crate a new UNINITIALIZED heap allocator + /// + /// You must initialize this heap using the + /// [`init`](Self::init) method before using the allocator. + pub const fn empty() -> Heap { + Heap { + heap: Mutex::new(RefCell::new(ConstDefault::DEFAULT)), } } - unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { - if layout.size() != 0 { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .deallocate(NonNull::new_unchecked(ptr.as_ptr()), layout) - }); + /// Initializes the heap + /// + /// This function must be called BEFORE you run any code that makes use of the + /// allocator. + /// + /// `start_addr` is the address where the heap will be located. + /// + /// `size` is the size of the heap in bytes. + /// + /// Note that: + /// + /// - The heap grows "upwards", towards larger addresses. Thus `start_addr` will + /// be the smallest address used. + /// + /// - The largest address used is `start_addr + size - 1`, so if `start_addr` is + /// `0x1000` and `size` is `0x30000` then the allocator won't use memory at + /// addresses `0x31000` and larger. + /// + /// # Safety + /// + /// Obey these or Bad Stuff will happen. + /// + /// - This function must be called exactly ONCE. + /// - `size > 0` + pub unsafe fn init(&self, start_addr: usize, size: usize) { + critical_section::with(|cs| { + let block: &[u8] = core::slice::from_raw_parts(start_addr as *const u8, size); + self.heap + .borrow(cs) + .borrow_mut() + .insert_free_block_ptr(block.into()); + }); + } + } + + unsafe impl GlobalAlloc for Heap { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .allocate(layout) + .map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) + }) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .deallocate(NonNull::new_unchecked(ptr), layout.align()) + }); + } + } + + #[cfg(feature = "allocator_api")] + mod allocator_api { + use super::*; + use core::{ + alloc::{AllocError, Allocator, Layout}, + ptr::NonNull, + }; + + unsafe impl Allocator for Heap { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + size => critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .allocate(layout) + .map_or(Err(AllocError), |allocation| { + Ok(NonNull::slice_from_raw_parts(allocation, size)) + }) + }), + } + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + if layout.size() != 0 { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .deallocate(NonNull::new_unchecked(ptr.as_ptr()), layout.align()) + }); + } } } } From 26f60411f7f37f34c1c72c290fded71fcc1c6724 Mon Sep 17 00:00:00 2001 From: Jacob Trueb Date: Tue, 31 Oct 2023 10:11:01 -0500 Subject: [PATCH 14/29] Update integration tests for choice of allocator --- .github/workflows/ci.yml | 3 +- examples/allocator_api.rs | 2 +- ...ation_test.rs => llff_integration_test.rs} | 4 +- examples/tlsf_integration_test.rs | 105 ++++++++++++++++++ 4 files changed, 110 insertions(+), 4 deletions(-) rename examples/{integration_test.rs => llff_integration_test.rs} (94%) create mode 100644 examples/tlsf_integration_test.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7a0f5e..49f0e38 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,7 +41,8 @@ jobs: sudo apt update sudo apt install qemu-system-arm - run: qemu-system-arm --version - - run: cargo run --target thumbv7m-none-eabi --example integration_test --all-features + - run: cargo run --target thumbv7m-none-eabi --example llff_integration_test --all-features + - run: cargo run --target thumbv7m-none-eabi --example tlsf_integration_test --all-features clippy: name: Clippy diff --git a/examples/allocator_api.rs b/examples/allocator_api.rs index 95ab702..39d54cf 100644 --- a/examples/allocator_api.rs +++ b/examples/allocator_api.rs @@ -8,7 +8,7 @@ use alloc::vec::Vec; use core::mem::MaybeUninit; use core::panic::PanicInfo; use cortex_m_rt::entry; -use embedded_alloc::Heap; +use embedded_alloc::LlffHeap as Heap; // This is not used, but as of 2023-10-29 allocator_api cannot be used without // a global heap diff --git a/examples/integration_test.rs b/examples/llff_integration_test.rs similarity index 94% rename from examples/integration_test.rs rename to examples/llff_integration_test.rs index a45ba87..af006be 100644 --- a/examples/integration_test.rs +++ b/examples/llff_integration_test.rs @@ -7,7 +7,7 @@ //! After toolchain installation this test can be run with: //! //! ```bash -//! cargo +nightly run --target thumbv7m-none-eabi --example integration_test --all-features +//! cargo +nightly run --target thumbv7m-none-eabi --example llff_integration_test --all-features //! ``` //! //! [Embedded Rust Book]: https://docs.rust-embedded.org/book/intro/index.html @@ -23,7 +23,7 @@ use alloc::vec::Vec; use core::mem::{size_of, MaybeUninit}; use cortex_m_rt::entry; use cortex_m_semihosting::{debug, hprintln}; -use embedded_alloc::Heap; +use embedded_alloc::LlffHeap as Heap; #[global_allocator] static HEAP: Heap = Heap::empty(); diff --git a/examples/tlsf_integration_test.rs b/examples/tlsf_integration_test.rs new file mode 100644 index 0000000..574497b --- /dev/null +++ b/examples/tlsf_integration_test.rs @@ -0,0 +1,105 @@ +//! This is a very basic smoke test that runs in QEMU +//! Reference the QEMU section of the [Embedded Rust Book] for more information +//! +//! This only tests integration of the allocator on an embedded target. +//! Comprehensive allocator tests are located in the allocator dependency. +//! +//! After toolchain installation this test can be run with: +//! +//! ```bash +//! cargo +nightly run --target thumbv7m-none-eabi --example tlsf_integration_test --all-features +//! ``` +//! +//! [Embedded Rust Book]: https://docs.rust-embedded.org/book/intro/index.html + +#![feature(allocator_api)] +#![no_main] +#![no_std] + +extern crate alloc; +extern crate panic_semihosting; + +use alloc::collections::LinkedList; +use core::mem::MaybeUninit; +use cortex_m_rt::entry; +use cortex_m_semihosting::{debug, hprintln}; +use embedded_alloc::TlsfHeap as Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); +const HEAP_SIZE: usize = 30 * 1024; + +fn test_global_heap() { + const ELEMS: usize = 250; + + let mut allocated = LinkedList::new(); + for _ in 0..ELEMS { + allocated.push_back(0); + } + for i in 0..ELEMS { + allocated.push_back(i as i32); + } + + assert_eq!(allocated.len(), 2 * ELEMS); + + for _ in 0..ELEMS { + allocated.pop_front(); + } + + for i in 0..ELEMS { + assert_eq!(allocated.pop_front().unwrap(), i as i32); + } +} + +fn test_allocator_api() { + // small local heap + const HEAP_SIZE: usize = 256; + let heap_mem: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + let local_heap: Heap = Heap::empty(); + unsafe { local_heap.init(heap_mem.as_ptr() as usize, HEAP_SIZE) } + + const ELEMS: usize = 2; + + let mut allocated = LinkedList::new_in(local_heap); + for _ in 0..ELEMS { + allocated.push_back(0); + } + for i in 0..ELEMS { + allocated.push_back(i as i32); + } + + assert_eq!(allocated.len(), 2 * ELEMS); + + for _ in 0..ELEMS { + allocated.pop_front(); + } + + for i in 0..ELEMS { + assert_eq!(allocated.pop_front().unwrap(), i as i32); + } +} + +#[entry] +fn main() -> ! { + { + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } + } + + #[allow(clippy::type_complexity)] + let tests: &[(fn() -> (), &'static str)] = &[ + (test_global_heap, "test_global_heap"), + (test_allocator_api, "test_allocator_api"), + ]; + + for (test_fn, test_name) in tests { + hprintln!("{}: start", test_name); + test_fn(); + hprintln!("{}: pass", test_name); + } + + // exit QEMU with a success status + debug::exit(debug::EXIT_SUCCESS); + #[allow(clippy::empty_loop)] + loop {} +} From dff7df232bc5422f98a98d6497e5888e4500ab63 Mon Sep 17 00:00:00 2001 From: Jacob Trueb Date: Tue, 31 Oct 2023 10:16:31 -0500 Subject: [PATCH 15/29] Remove duplicate code from allocator_api --- src/lib.rs | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d3dc59c..d17e4ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,7 @@ mod llff { critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free()) } - unsafe fn alloc(&self, layout: Layout) -> Option> { + fn alloc(&self, layout: Layout) -> Option> { critical_section::with(|cs| { self.heap .borrow(cs) @@ -119,25 +119,15 @@ mod llff { fn allocate(&self, layout: Layout) -> Result, AllocError> { match layout.size() { 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), - size => critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .allocate_first_fit(layout) - .map(|allocation| NonNull::slice_from_raw_parts(allocation, size)) - .map_err(|_| AllocError) + size => self.alloc(layout).map_or(Err(AllocError), |allocation| { + Ok(NonNull::slice_from_raw_parts(allocation, size)) }), } } unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { if layout.size() != 0 { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .deallocate(NonNull::new_unchecked(ptr.as_ptr()), layout) - }); + self.dealloc(ptr.as_ptr(), layout); } } } @@ -201,7 +191,7 @@ mod tlsf { }); } - unsafe fn alloc(&self, layout: Layout) -> Option> { + fn alloc(&self, layout: Layout) -> Option> { critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().allocate(layout)) } @@ -238,26 +228,15 @@ mod tlsf { fn allocate(&self, layout: Layout) -> Result, AllocError> { match layout.size() { 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), - size => critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .allocate(layout) - .map_or(Err(AllocError), |allocation| { - Ok(NonNull::slice_from_raw_parts(allocation, size)) - }) + size => self.alloc(layout).map_or(Err(AllocError), |allocation| { + Ok(NonNull::slice_from_raw_parts(allocation, size)) }), } } unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { if layout.size() != 0 { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .deallocate(NonNull::new_unchecked(ptr.as_ptr()), layout.align()) - }); + self.dealloc(ptr.as_ptr(), layout); } } } From 6a1ba1cc0becbe935e421d342190549f37ac869f Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sat, 4 Nov 2023 10:59:26 -0700 Subject: [PATCH 16/29] version: 0.5.0 -> 0.5.1 --- CHANGELOG.md | 5 +++-- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0710ceb..2c8bb34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## [v0.5.1] - 2023-11-04 ### Added @@ -120,7 +120,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Initial version of the allocator -[Unreleased]: https://github.com/rust-embedded/embedded-alloc/compare/v0.5.0...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-alloc/compare/v0.5.1...HEAD +[v0.5.1]: https://github.com/rust-embedded/embedded-alloc/compare/v0.5.0...v0.5.1 [v0.5.0]: https://github.com/rust-embedded/embedded-alloc/compare/v0.4.3...v0.5.0 [v0.4.3]: https://github.com/rust-embedded/embedded-alloc/compare/v0.4.2...v0.4.3 [v0.4.2]: https://github.com/rust-embedded/embedded-alloc/compare/v0.4.1...v0.4.2 diff --git a/Cargo.toml b/Cargo.toml index aa8ebee..febf07f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ keywords = [ ] license = "MIT OR Apache-2.0" name = "embedded-alloc" -version = "0.5.0" +version = "0.5.1" [features] allocator_api = [] From d2e6fa41a7b9ef4353b270236d0b6558da51c66a Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sat, 4 Nov 2023 11:15:08 -0700 Subject: [PATCH 17/29] merge cron.yml with ci.yml --- .github/workflows/ci.yml | 15 +++++++++++++++ .github/workflows/cron.yml | 26 -------------------------- 2 files changed, 15 insertions(+), 26 deletions(-) delete mode 100644 .github/workflows/cron.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7a0f5e..0085f6a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,6 +2,9 @@ on: pull_request: # Run CI for PRs on any branch merge_group: # Run CI for the GitHub merge queue workflow_dispatch: # Run CI when manually requested + schedule: + # Run every week at 8am UTC Saturday + - cron: '0 8 * * SAT' name: Continuous integration @@ -27,6 +30,18 @@ jobs: - run: cargo check --target=${{ matrix.target }} --example global_alloc - if: ${{ matrix.toolchain == 'nightly' }} run: cargo check --target=${{ matrix.target }} --examples --all-features + - uses: imjohnbo/issue-bot@v3 + if: | + failure() + && github.event_name == 'schedule' + with: + title: CI Failure + labels: ci + body: | + Scheduled CI run failed. Details: + https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} test: runs-on: ubuntu-latest diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml deleted file mode 100644 index ec46eb6..0000000 --- a/.github/workflows/cron.yml +++ /dev/null @@ -1,26 +0,0 @@ -on: - schedule: - # Run every week at 8am UTC Saturday. - - cron: '0 8 * * SAT' - -name: Cron CI - -jobs: - ci-cron: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - with: - targets: thumbv6m-none-eabi - - run: cargo check --examples --target thumbv6m-none-eabi - - uses: imjohnbo/issue-bot@v3 - if: failure() - with: - title: CI Failure - labels: ci - body: | - Scheduled CI run failed. Details: - https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From ba635851aa148f7bb4eb6680103323941c011a92 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Mon, 4 Dec 2023 13:49:22 -0600 Subject: [PATCH 18/29] chore(typo): "Crate" -> "Create" --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index cc9fe1e..999308c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ pub struct Heap { } impl Heap { - /// Crate a new UNINITIALIZED heap allocator + /// Create a new UNINITIALIZED heap allocator /// /// You must initialize this heap using the /// [`init`](Self::init) method before using the allocator. From 69187bf2da35e07d8138746f9b3a8ee45776372c Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Thu, 14 Dec 2023 12:18:00 -0800 Subject: [PATCH 19/29] Update documentation for TLSF and LLFF heaps --- CHANGELOG.md | 10 +++ Cargo.toml | 1 - README.md | 10 ++- src/lib.rs | 240 ++------------------------------------------------- src/llff.rs | 122 ++++++++++++++++++++++++++ src/tlsf.rs | 110 +++++++++++++++++++++++ 6 files changed, 256 insertions(+), 237 deletions(-) create mode 100644 src/llff.rs create mode 100644 src/tlsf.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c8bb34..6c1daaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + +### Added + +- Added a Two-Level Segregated Fit heap with the `tlsf` feature. + +### Changed + +- The `Heap` struct has been renamed to `LlffHeap` and requires the `llff` feature. + ## [v0.5.1] - 2023-11-04 ### Added diff --git a/Cargo.toml b/Cargo.toml index 247517c..086fcf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ linked_list_allocator = { version = "0.10.5", default-features = false, optional rlsf = { version = "0.2.1", default-features = false, optional = true } const-default = { version = "1.0.0", default-features = false, optional = true } - [dev-dependencies] cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" diff --git a/README.md b/README.md index a34cb15..bbe9eb9 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,17 @@ For a full usage example, see [`examples/global_alloc.rs`](https://github.com/ru For this to work, an implementation of [`critical-section`](https://github.com/rust-embedded/critical-section) must be provided. -For simple use cases you may enable the `critical-section-single-core` feature in the [cortex-m](https://github.com/rust-embedded/cortex-m) crate. +For simple use cases with Cortex-M CPUs you may enable the `critical-section-single-core` feature in the [cortex-m](https://github.com/rust-embedded/cortex-m) crate. Please refer to the documentation of [`critical-section`](https://docs.rs/critical-section) for further guidance. +## Features + +There are two heaps available to use: + +* `llff`: Provides `LlffHeap`, a Linked List First Fit heap. +* `tlsf`: Provides `TlsfHeap`, a Two-Level Segregated Fit heap. + +The best heap to use will depend on your application, see [#78](https://github.com/rust-embedded/embedded-alloc/pull/78) for more discussion. ## License diff --git a/src/lib.rs b/src/lib.rs index 33ac654..4308790 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,244 +1,14 @@ #![doc = include_str!("../README.md")] #![no_std] #![cfg_attr(feature = "allocator_api", feature(allocator_api, alloc_layout_extra))] - -use core::alloc::{GlobalAlloc, Layout}; -use core::cell::RefCell; -use core::ptr::{self, NonNull}; - -use critical_section::Mutex; +#![warn(missing_docs)] #[cfg(feature = "llff")] -pub use llff::Heap as LlffHeap; +mod llff; #[cfg(feature = "tlsf")] -pub use tlsf::Heap as TlsfHeap; +mod tlsf; #[cfg(feature = "llff")] -mod llff { - use super::*; - use linked_list_allocator::Heap as LLHeap; - - pub struct Heap { - heap: Mutex>, - } - - impl Heap { - /// Create a new UNINITIALIZED heap allocator - /// - /// You must initialize this heap using the - /// [`init`](Self::init) method before using the allocator. - pub const fn empty() -> Heap { - Heap { - heap: Mutex::new(RefCell::new(LLHeap::empty())), - } - } - - /// Initializes the heap - /// - /// This function must be called BEFORE you run any code that makes use of the - /// allocator. - /// - /// `start_addr` is the address where the heap will be located. - /// - /// `size` is the size of the heap in bytes. - /// - /// Note that: - /// - /// - The heap grows "upwards", towards larger addresses. Thus `start_addr` will - /// be the smallest address used. - /// - /// - The largest address used is `start_addr + size - 1`, so if `start_addr` is - /// `0x1000` and `size` is `0x30000` then the allocator won't use memory at - /// addresses `0x31000` and larger. - /// - /// # Safety - /// - /// Obey these or Bad Stuff will happen. - /// - /// - This function must be called exactly ONCE. - /// - `size > 0` - pub unsafe fn init(&self, start_addr: usize, size: usize) { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .init(start_addr as *mut u8, size); - }); - } - - /// Returns an estimate of the amount of bytes in use. - pub fn used(&self) -> usize { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().used()) - } - - /// Returns an estimate of the amount of bytes available. - pub fn free(&self) -> usize { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free()) - } - - fn alloc(&self, layout: Layout) -> Option> { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .allocate_first_fit(layout) - .ok() - }) - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .deallocate(NonNull::new_unchecked(ptr), layout) - }); - } - } - - unsafe impl GlobalAlloc for Heap { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - self.alloc(layout) - .map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - self.dealloc(ptr, layout); - } - } - - #[cfg(feature = "allocator_api")] - mod allocator_api { - use super::*; - use core::{ - alloc::{AllocError, Allocator, Layout}, - ptr::NonNull, - }; - - unsafe impl Allocator for Heap { - fn allocate(&self, layout: Layout) -> Result, AllocError> { - match layout.size() { - 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), - size => self.alloc(layout).map_or(Err(AllocError), |allocation| { - Ok(NonNull::slice_from_raw_parts(allocation, size)) - }), - } - } - - unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { - if layout.size() != 0 { - self.dealloc(ptr.as_ptr(), layout); - } - } - } - } -} - +pub use llff::Heap as LlffHeap; #[cfg(feature = "tlsf")] -mod tlsf { - use super::*; - use const_default::ConstDefault; - use rlsf::Tlsf; - - type TlsfHeap = Tlsf<'static, usize, usize, { usize::BITS as usize }, { usize::BITS as usize }>; - - pub struct Heap { - heap: Mutex>, - } - - impl Heap { - /// Create a new UNINITIALIZED heap allocator - /// - /// You must initialize this heap using the - /// [`init`](Self::init) method before using the allocator. - pub const fn empty() -> Heap { - Heap { - heap: Mutex::new(RefCell::new(ConstDefault::DEFAULT)), - } - } - - /// Initializes the heap - /// - /// This function must be called BEFORE you run any code that makes use of the - /// allocator. - /// - /// `start_addr` is the address where the heap will be located. - /// - /// `size` is the size of the heap in bytes. - /// - /// Note that: - /// - /// - The heap grows "upwards", towards larger addresses. Thus `start_addr` will - /// be the smallest address used. - /// - /// - The largest address used is `start_addr + size - 1`, so if `start_addr` is - /// `0x1000` and `size` is `0x30000` then the allocator won't use memory at - /// addresses `0x31000` and larger. - /// - /// # Safety - /// - /// Obey these or Bad Stuff will happen. - /// - /// - This function must be called exactly ONCE. - /// - `size > 0` - pub unsafe fn init(&self, start_addr: usize, size: usize) { - critical_section::with(|cs| { - let block: &[u8] = core::slice::from_raw_parts(start_addr as *const u8, size); - self.heap - .borrow(cs) - .borrow_mut() - .insert_free_block_ptr(block.into()); - }); - } - - fn alloc(&self, layout: Layout) -> Option> { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().allocate(layout)) - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .deallocate(NonNull::new_unchecked(ptr), layout.align()) - }) - } - } - - unsafe impl GlobalAlloc for Heap { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - self.alloc(layout) - .map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - self.dealloc(ptr, layout) - } - } - - #[cfg(feature = "allocator_api")] - mod allocator_api { - use super::*; - use core::{ - alloc::{AllocError, Allocator, Layout}, - ptr::NonNull, - }; - - unsafe impl Allocator for Heap { - fn allocate(&self, layout: Layout) -> Result, AllocError> { - match layout.size() { - 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), - size => self.alloc(layout).map_or(Err(AllocError), |allocation| { - Ok(NonNull::slice_from_raw_parts(allocation, size)) - }), - } - } - - unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { - if layout.size() != 0 { - self.dealloc(ptr.as_ptr(), layout); - } - } - } - } -} +pub use tlsf::Heap as TlsfHeap; diff --git a/src/llff.rs b/src/llff.rs new file mode 100644 index 0000000..d618a6a --- /dev/null +++ b/src/llff.rs @@ -0,0 +1,122 @@ +use core::alloc::{GlobalAlloc, Layout}; +use core::cell::RefCell; +use core::ptr::{self, NonNull}; + +use critical_section::Mutex; +use linked_list_allocator::Heap as LLHeap; + +/// A linked list first fit heap. +pub struct Heap { + heap: Mutex>, +} + +impl Heap { + /// Create a new UNINITIALIZED heap allocator + /// + /// You must initialize this heap using the + /// [`init`](Self::init) method before using the allocator. + pub const fn empty() -> Heap { + Heap { + heap: Mutex::new(RefCell::new(LLHeap::empty())), + } + } + + /// Initializes the heap + /// + /// This function must be called BEFORE you run any code that makes use of the + /// allocator. + /// + /// `start_addr` is the address where the heap will be located. + /// + /// `size` is the size of the heap in bytes. + /// + /// Note that: + /// + /// - The heap grows "upwards", towards larger addresses. Thus `start_addr` will + /// be the smallest address used. + /// + /// - The largest address used is `start_addr + size - 1`, so if `start_addr` is + /// `0x1000` and `size` is `0x30000` then the allocator won't use memory at + /// addresses `0x31000` and larger. + /// + /// # Safety + /// + /// Obey these or Bad Stuff will happen. + /// + /// - This function must be called exactly ONCE. + /// - `size > 0` + pub unsafe fn init(&self, start_addr: usize, size: usize) { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .init(start_addr as *mut u8, size); + }); + } + + /// Returns an estimate of the amount of bytes in use. + pub fn used(&self) -> usize { + critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().used()) + } + + /// Returns an estimate of the amount of bytes available. + pub fn free(&self) -> usize { + critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free()) + } + + fn alloc(&self, layout: Layout) -> Option> { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .allocate_first_fit(layout) + .ok() + }) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .deallocate(NonNull::new_unchecked(ptr), layout) + }); + } +} + +unsafe impl GlobalAlloc for Heap { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + self.alloc(layout) + .map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + self.dealloc(ptr, layout); + } +} + +#[cfg(feature = "allocator_api")] +mod allocator_api { + use super::*; + use core::{ + alloc::{AllocError, Allocator, Layout}, + ptr::NonNull, + }; + + unsafe impl Allocator for Heap { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + size => self.alloc(layout).map_or(Err(AllocError), |allocation| { + Ok(NonNull::slice_from_raw_parts(allocation, size)) + }), + } + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + if layout.size() != 0 { + self.dealloc(ptr.as_ptr(), layout); + } + } + } +} diff --git a/src/tlsf.rs b/src/tlsf.rs new file mode 100644 index 0000000..3a31cfd --- /dev/null +++ b/src/tlsf.rs @@ -0,0 +1,110 @@ +use core::alloc::{GlobalAlloc, Layout}; +use core::cell::RefCell; +use core::ptr::{self, NonNull}; + +use const_default::ConstDefault; +use critical_section::Mutex; +use rlsf::Tlsf; + +type TlsfHeap = Tlsf<'static, usize, usize, { usize::BITS as usize }, { usize::BITS as usize }>; + +/// A two-Level segregated fit heap. +pub struct Heap { + heap: Mutex>, +} + +impl Heap { + /// Create a new UNINITIALIZED heap allocator + /// + /// You must initialize this heap using the + /// [`init`](Self::init) method before using the allocator. + pub const fn empty() -> Heap { + Heap { + heap: Mutex::new(RefCell::new(ConstDefault::DEFAULT)), + } + } + + /// Initializes the heap + /// + /// This function must be called BEFORE you run any code that makes use of the + /// allocator. + /// + /// `start_addr` is the address where the heap will be located. + /// + /// `size` is the size of the heap in bytes. + /// + /// Note that: + /// + /// - The heap grows "upwards", towards larger addresses. Thus `start_addr` will + /// be the smallest address used. + /// + /// - The largest address used is `start_addr + size - 1`, so if `start_addr` is + /// `0x1000` and `size` is `0x30000` then the allocator won't use memory at + /// addresses `0x31000` and larger. + /// + /// # Safety + /// + /// Obey these or Bad Stuff will happen. + /// + /// - This function must be called exactly ONCE. + /// - `size > 0` + pub unsafe fn init(&self, start_addr: usize, size: usize) { + critical_section::with(|cs| { + let block: &[u8] = core::slice::from_raw_parts(start_addr as *const u8, size); + self.heap + .borrow(cs) + .borrow_mut() + .insert_free_block_ptr(block.into()); + }); + } + + fn alloc(&self, layout: Layout) -> Option> { + critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().allocate(layout)) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + critical_section::with(|cs| { + self.heap + .borrow(cs) + .borrow_mut() + .deallocate(NonNull::new_unchecked(ptr), layout.align()) + }) + } +} + +unsafe impl GlobalAlloc for Heap { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + self.alloc(layout) + .map_or(ptr::null_mut(), |allocation| allocation.as_ptr()) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + self.dealloc(ptr, layout) + } +} + +#[cfg(feature = "allocator_api")] +mod allocator_api { + use super::*; + use core::{ + alloc::{AllocError, Allocator, Layout}, + ptr::NonNull, + }; + + unsafe impl Allocator for Heap { + fn allocate(&self, layout: Layout) -> Result, AllocError> { + match layout.size() { + 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), + size => self.alloc(layout).map_or(Err(AllocError), |allocation| { + Ok(NonNull::slice_from_raw_parts(allocation, size)) + }), + } + } + + unsafe fn deallocate(&self, ptr: NonNull, layout: Layout) { + if layout.size() != 0 { + self.dealloc(ptr.as_ptr(), layout); + } + } + } +} From 4d6ff20957bed0c1a671b0737c60b8d849c34399 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Thu, 14 Dec 2023 15:34:07 -0800 Subject: [PATCH 20/29] edition: 2018 -> 2021 --- CHANGELOG.md | 1 + Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c1daaf..e26db31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - The `Heap` struct has been renamed to `LlffHeap` and requires the `llff` feature. +- Updated the rust edition from 2018 to 2021. ## [v0.5.1] - 2023-11-04 diff --git a/Cargo.toml b/Cargo.toml index 086fcf6..881bafe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ description = "A heap allocator for embedded systems" repository = "https://github.com/rust-embedded/embedded-alloc" documentation = "https://docs.rs/embedded-alloc" readme = "README.md" -edition = "2018" +edition = "2021" keywords = [ "allocator", From 5ca340f0d3245b7b27512342e287a592b539dad8 Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sat, 24 Feb 2024 09:16:45 -0800 Subject: [PATCH 21/29] Fix new clippy lints --- src/llff.rs | 5 +---- src/tlsf.rs | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/llff.rs b/src/llff.rs index d618a6a..a07e73c 100644 --- a/src/llff.rs +++ b/src/llff.rs @@ -98,10 +98,7 @@ unsafe impl GlobalAlloc for Heap { #[cfg(feature = "allocator_api")] mod allocator_api { use super::*; - use core::{ - alloc::{AllocError, Allocator, Layout}, - ptr::NonNull, - }; + use core::alloc::{AllocError, Allocator}; unsafe impl Allocator for Heap { fn allocate(&self, layout: Layout) -> Result, AllocError> { diff --git a/src/tlsf.rs b/src/tlsf.rs index 3a31cfd..75f5255 100644 --- a/src/tlsf.rs +++ b/src/tlsf.rs @@ -86,10 +86,7 @@ unsafe impl GlobalAlloc for Heap { #[cfg(feature = "allocator_api")] mod allocator_api { use super::*; - use core::{ - alloc::{AllocError, Allocator, Layout}, - ptr::NonNull, - }; + use core::alloc::{AllocError, Allocator}; unsafe impl Allocator for Heap { fn allocate(&self, layout: Layout) -> Result, AllocError> { From 80e76f57d9b94fb349479167f31fd3803fdb9d5c Mon Sep 17 00:00:00 2001 From: Alex Martens Date: Sun, 1 Sep 2024 09:26:09 -0700 Subject: [PATCH 22/29] version: 0.5.1 -> 0.6.0 --- CHANGELOG.md | 5 +++-- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e26db31..9f8c5a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [Unreleased] +## [v0.6.0] - 2024-09-01 ### Added @@ -131,7 +131,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Initial version of the allocator -[Unreleased]: https://github.com/rust-embedded/embedded-alloc/compare/v0.5.1...HEAD +[Unreleased]: https://github.com/rust-embedded/embedded-alloc/compare/v0.6.0...HEAD +[v0.6.0]: https://github.com/rust-embedded/embedded-alloc/compare/v0.5.1...v0.6.0 [v0.5.1]: https://github.com/rust-embedded/embedded-alloc/compare/v0.5.0...v0.5.1 [v0.5.0]: https://github.com/rust-embedded/embedded-alloc/compare/v0.4.3...v0.5.0 [v0.4.3]: https://github.com/rust-embedded/embedded-alloc/compare/v0.4.2...v0.4.3 diff --git a/Cargo.toml b/Cargo.toml index 881bafe..f84d36a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,7 @@ keywords = [ ] license = "MIT OR Apache-2.0" name = "embedded-alloc" -version = "0.5.1" +version = "0.6.0" [features] default = ["llff", "tlsf"] From 0e9babaf1f413c9be18b1c27c307319e715116af Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Sat, 21 Sep 2024 08:56:25 +0000 Subject: [PATCH 23/29] Declare required features for examples --- Cargo.toml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index f84d36a..bcf197a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,3 +43,19 @@ cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7" cortex-m-semihosting = "0.5" panic-semihosting = { version = "0.6", features = ["exit"] } + +[[example]] +name = "allocator_api" +required-features = ["allocator_api", "llff"] + +[[example]] +name = "llff_integration_test" +required-features = ["allocator_api", "llff"] + +[[example]] +name = "tlsf_integration_test" +required-features = ["allocator_api", "tlsf"] + +[[example]] +name = "global_alloc" +required-features = ["llff"] From 93b323b4723e6afc480ecb1e15e599735a059e3c Mon Sep 17 00:00:00 2001 From: Jan Niehusmann Date: Sat, 21 Sep 2024 09:10:10 +0000 Subject: [PATCH 24/29] Use addr_of_mut! instead of as_ptr() to avoid shared reference to mutable static --- README.md | 3 ++- examples/allocator_api.rs | 3 ++- examples/global_alloc.rs | 3 ++- examples/llff_integration_test.rs | 3 ++- examples/tlsf_integration_test.rs | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bbe9eb9..5eb6cde 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Starting with Rust 1.68, this crate can be used as a global allocator on stable extern crate alloc; +use core::ptr::addr_of_mut; use cortex_m_rt::entry; use embedded_alloc::LlffHeap as Heap; @@ -35,7 +36,7 @@ fn main() -> ! { use core::mem::MaybeUninit; const HEAP_SIZE: usize = 1024; static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } } // now the allocator is ready types like Box, Vec can be used. diff --git a/examples/allocator_api.rs b/examples/allocator_api.rs index 39d54cf..ab08818 100644 --- a/examples/allocator_api.rs +++ b/examples/allocator_api.rs @@ -7,6 +7,7 @@ extern crate alloc; use alloc::vec::Vec; use core::mem::MaybeUninit; use core::panic::PanicInfo; +use core::ptr::addr_of_mut; use cortex_m_rt::entry; use embedded_alloc::LlffHeap as Heap; @@ -20,7 +21,7 @@ fn main() -> ! { const HEAP_SIZE: usize = 16; static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; let heap: Heap = Heap::empty(); - unsafe { heap.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } + unsafe { heap.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } let mut xs = Vec::new_in(heap); xs.push(1); diff --git a/examples/global_alloc.rs b/examples/global_alloc.rs index d41debd..21c95f5 100644 --- a/examples/global_alloc.rs +++ b/examples/global_alloc.rs @@ -5,6 +5,7 @@ extern crate alloc; use alloc::vec::Vec; use core::panic::PanicInfo; +use core::ptr::addr_of_mut; use cortex_m_rt::entry; // Linked-List First Fit Heap allocator (feature = "llff") use embedded_alloc::LlffHeap as Heap; @@ -21,7 +22,7 @@ fn main() -> ! { use core::mem::MaybeUninit; const HEAP_SIZE: usize = 1024; static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } } let mut xs = Vec::new(); diff --git a/examples/llff_integration_test.rs b/examples/llff_integration_test.rs index af006be..c0823e5 100644 --- a/examples/llff_integration_test.rs +++ b/examples/llff_integration_test.rs @@ -21,6 +21,7 @@ extern crate panic_semihosting; use alloc::vec::Vec; use core::mem::{size_of, MaybeUninit}; +use core::ptr::addr_of_mut; use cortex_m_rt::entry; use cortex_m_semihosting::{debug, hprintln}; use embedded_alloc::LlffHeap as Heap; @@ -66,7 +67,7 @@ fn main() -> ! { { const HEAP_SIZE: usize = 1024; static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } } #[allow(clippy::type_complexity)] diff --git a/examples/tlsf_integration_test.rs b/examples/tlsf_integration_test.rs index 574497b..13e95b0 100644 --- a/examples/tlsf_integration_test.rs +++ b/examples/tlsf_integration_test.rs @@ -21,6 +21,7 @@ extern crate panic_semihosting; use alloc::collections::LinkedList; use core::mem::MaybeUninit; +use core::ptr::addr_of_mut; use cortex_m_rt::entry; use cortex_m_semihosting::{debug, hprintln}; use embedded_alloc::TlsfHeap as Heap; @@ -83,7 +84,7 @@ fn test_allocator_api() { fn main() -> ! { { static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } + unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } } #[allow(clippy::type_complexity)] From 8cf2418c743407b9eee865e1ba6d870a848d1aa3 Mon Sep 17 00:00:00 2001 From: riceman2000 Date: Mon, 27 Jan 2025 21:30:27 -0500 Subject: [PATCH 25/29] Change to &raw --- README.md | 3 +-- examples/allocator_api.rs | 3 +-- examples/global_alloc.rs | 3 +-- examples/llff_integration_test.rs | 7 +++---- examples/tlsf_integration_test.rs | 7 +++---- 5 files changed, 9 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5eb6cde..a1022f4 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,6 @@ Starting with Rust 1.68, this crate can be used as a global allocator on stable extern crate alloc; -use core::ptr::addr_of_mut; use cortex_m_rt::entry; use embedded_alloc::LlffHeap as Heap; @@ -36,7 +35,7 @@ fn main() -> ! { use core::mem::MaybeUninit; const HEAP_SIZE: usize = 1024; static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } } // now the allocator is ready types like Box, Vec can be used. diff --git a/examples/allocator_api.rs b/examples/allocator_api.rs index ab08818..10f5261 100644 --- a/examples/allocator_api.rs +++ b/examples/allocator_api.rs @@ -7,7 +7,6 @@ extern crate alloc; use alloc::vec::Vec; use core::mem::MaybeUninit; use core::panic::PanicInfo; -use core::ptr::addr_of_mut; use cortex_m_rt::entry; use embedded_alloc::LlffHeap as Heap; @@ -21,7 +20,7 @@ fn main() -> ! { const HEAP_SIZE: usize = 16; static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; let heap: Heap = Heap::empty(); - unsafe { heap.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + unsafe { heap.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } let mut xs = Vec::new_in(heap); xs.push(1); diff --git a/examples/global_alloc.rs b/examples/global_alloc.rs index 21c95f5..81705fc 100644 --- a/examples/global_alloc.rs +++ b/examples/global_alloc.rs @@ -5,7 +5,6 @@ extern crate alloc; use alloc::vec::Vec; use core::panic::PanicInfo; -use core::ptr::addr_of_mut; use cortex_m_rt::entry; // Linked-List First Fit Heap allocator (feature = "llff") use embedded_alloc::LlffHeap as Heap; @@ -22,7 +21,7 @@ fn main() -> ! { use core::mem::MaybeUninit; const HEAP_SIZE: usize = 1024; static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } } let mut xs = Vec::new(); diff --git a/examples/llff_integration_test.rs b/examples/llff_integration_test.rs index c0823e5..ce9b553 100644 --- a/examples/llff_integration_test.rs +++ b/examples/llff_integration_test.rs @@ -21,7 +21,6 @@ extern crate panic_semihosting; use alloc::vec::Vec; use core::mem::{size_of, MaybeUninit}; -use core::ptr::addr_of_mut; use cortex_m_rt::entry; use cortex_m_semihosting::{debug, hprintln}; use embedded_alloc::LlffHeap as Heap; @@ -46,9 +45,9 @@ fn test_global_heap() { fn test_allocator_api() { // small local heap const HEAP_SIZE: usize = 16; - let heap_mem: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + let mut heap_mem: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; let local_heap: Heap = Heap::empty(); - unsafe { local_heap.init(heap_mem.as_ptr() as usize, HEAP_SIZE) } + unsafe { local_heap.init(&raw mut heap_mem as usize, HEAP_SIZE) } assert_eq!(local_heap.used(), 0); @@ -67,7 +66,7 @@ fn main() -> ! { { const HEAP_SIZE: usize = 1024; static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } } #[allow(clippy::type_complexity)] diff --git a/examples/tlsf_integration_test.rs b/examples/tlsf_integration_test.rs index 13e95b0..26d36f4 100644 --- a/examples/tlsf_integration_test.rs +++ b/examples/tlsf_integration_test.rs @@ -21,7 +21,6 @@ extern crate panic_semihosting; use alloc::collections::LinkedList; use core::mem::MaybeUninit; -use core::ptr::addr_of_mut; use cortex_m_rt::entry; use cortex_m_semihosting::{debug, hprintln}; use embedded_alloc::TlsfHeap as Heap; @@ -55,9 +54,9 @@ fn test_global_heap() { fn test_allocator_api() { // small local heap const HEAP_SIZE: usize = 256; - let heap_mem: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + let mut heap_mem: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; let local_heap: Heap = Heap::empty(); - unsafe { local_heap.init(heap_mem.as_ptr() as usize, HEAP_SIZE) } + unsafe { local_heap.init(&raw mut heap_mem as usize, HEAP_SIZE) } const ELEMS: usize = 2; @@ -84,7 +83,7 @@ fn test_allocator_api() { fn main() -> ! { { static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(addr_of_mut!(HEAP_MEM) as usize, HEAP_SIZE) } + unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } } #[allow(clippy::type_complexity)] From 20d9db5e6aba2028f03c605f30efdfc3599cfdea Mon Sep 17 00:00:00 2001 From: riceman2000 Date: Mon, 3 Feb 2025 01:50:03 -0500 Subject: [PATCH 26/29] Review changes --- examples/tlsf_integration_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/tlsf_integration_test.rs b/examples/tlsf_integration_test.rs index 26d36f4..26a4353 100644 --- a/examples/tlsf_integration_test.rs +++ b/examples/tlsf_integration_test.rs @@ -56,7 +56,7 @@ fn test_allocator_api() { const HEAP_SIZE: usize = 256; let mut heap_mem: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; let local_heap: Heap = Heap::empty(); - unsafe { local_heap.init(&raw mut heap_mem as usize, HEAP_SIZE) } + unsafe { local_heap.init(heap_mem.as_mut_ptr() as usize, HEAP_SIZE) } const ELEMS: usize = 2; From eb87f1a58450e76702676575273cedce8c8c915b Mon Sep 17 00:00:00 2001 From: Jalon Wong Date: Thu, 14 Aug 2025 14:30:50 -0500 Subject: [PATCH 27/29] Add a macro to make initialization easier --- CHANGELOG.md | 6 +++++ Cargo.toml | 2 +- README.md | 4 +++ examples/global_alloc.rs | 7 ++--- examples/llff_integration_test.rs | 6 ++--- examples/tlsf_integration_test.rs | 5 ++-- src/lib.rs | 45 +++++++++++++++++++++++++++++++ 7 files changed, 62 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f8c5a6..e23d086 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + +### Added + +- Added a `init` macro to make initialization easier. + ## [v0.6.0] - 2024-09-01 ### Added diff --git a/Cargo.toml b/Cargo.toml index bcf197a..d23dfe6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ allocator_api = [] # Use the Two-Level Segregated Fit allocator tlsf = ["rlsf", "const-default"] - # Use the LinkedList first-fit allocator +# Use the LinkedList first-fit allocator llff = ["linked_list_allocator"] [dependencies] diff --git a/README.md b/README.md index a1022f4..868ae51 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,10 @@ static HEAP: Heap = Heap::empty(); #[entry] fn main() -> ! { // Initialize the allocator BEFORE you use it + unsafe { + embedded_alloc::init!(HEAP, 1024); + } + // Alternatively, you can write the code directly to meet specific requirements. { use core::mem::MaybeUninit; const HEAP_SIZE: usize = 1024; diff --git a/examples/global_alloc.rs b/examples/global_alloc.rs index 81705fc..b47f0ef 100644 --- a/examples/global_alloc.rs +++ b/examples/global_alloc.rs @@ -17,11 +17,8 @@ static HEAP: Heap = Heap::empty(); #[entry] fn main() -> ! { // Initialize the allocator BEFORE you use it - { - use core::mem::MaybeUninit; - const HEAP_SIZE: usize = 1024; - static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } + unsafe { + embedded_alloc::init!(HEAP, 1024); } let mut xs = Vec::new(); diff --git a/examples/llff_integration_test.rs b/examples/llff_integration_test.rs index ce9b553..5b7f4b2 100644 --- a/examples/llff_integration_test.rs +++ b/examples/llff_integration_test.rs @@ -63,10 +63,8 @@ fn test_allocator_api() { #[entry] fn main() -> ! { - { - const HEAP_SIZE: usize = 1024; - static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } + unsafe { + embedded_alloc::init!(HEAP, 1024); } #[allow(clippy::type_complexity)] diff --git a/examples/tlsf_integration_test.rs b/examples/tlsf_integration_test.rs index 26a4353..591b7d3 100644 --- a/examples/tlsf_integration_test.rs +++ b/examples/tlsf_integration_test.rs @@ -81,9 +81,8 @@ fn test_allocator_api() { #[entry] fn main() -> ! { - { - static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; - unsafe { HEAP.init(&raw mut HEAP_MEM as usize, HEAP_SIZE) } + unsafe { + embedded_alloc::init!(HEAP, HEAP_SIZE); } #[allow(clippy::type_complexity)] diff --git a/src/lib.rs b/src/lib.rs index 4308790..860c82b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,3 +12,48 @@ mod tlsf; pub use llff::Heap as LlffHeap; #[cfg(feature = "tlsf")] pub use tlsf::Heap as TlsfHeap; + +/// Initialize the global heap. +/// +/// This macro creates a static, uninitialized memory buffer of the specified size and +/// initializes the heap instance with that buffer. +/// +/// # Parameters +/// +/// - `$heap:ident`: The identifier of the global heap instance to initialize. +/// - `$size:expr`: An expression evaluating to a `usize` that specifies the size of the +/// static memory buffer in bytes. It must be **greater than zero**. +/// +/// # Safety +/// +/// This macro must be called first, before any operations on the heap, and **only once**. +/// It internally calls `Heap::init(...)` on the heap, +/// so `Heap::init(...)` should not be called directly if this macro is used. +/// +/// # Example +/// +/// ```rust +/// use cortex_m_rt::entry; +/// use embedded_alloc::LlffHeap as Heap; +/// +/// #[global_allocator] +/// static HEAP: Heap = Heap::empty(); +/// +/// #[entry] +/// fn main() -> ! { +/// // Initialize the allocator BEFORE you use it +/// unsafe { +/// embedded_alloc::init!(HEAP, 1024); +/// } +/// let mut xs = Vec::new(); +/// // ... +/// } +/// ``` +#[macro_export] +macro_rules! init { + ($heap:ident, $size:expr) => { + static mut HEAP_MEM: [::core::mem::MaybeUninit; $size] = + [::core::mem::MaybeUninit::uninit(); $size]; + $heap.init(&raw mut HEAP_MEM as usize, $size) + }; +} From 7d9604211e7bba7ca2bbb9c5c633b725cdfb925b Mon Sep 17 00:00:00 2001 From: Jalon Wong Date: Mon, 25 Aug 2025 13:00:13 -0500 Subject: [PATCH 28/29] Add a flag to prevent duplicate initialization --- CHANGELOG.md | 4 ++++ src/lib.rs | 7 +++++++ src/llff.rs | 38 +++++++++++++++++++++++--------------- src/tlsf.rs | 32 ++++++++++++++++++++------------ 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e23d086..a5ac15b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Added a `init` macro to make initialization easier. +### Changed + +- The `init` function will panic if it's called more than once or with `size == 0`. + ## [v0.6.0] - 2024-09-01 ### Added diff --git a/src/lib.rs b/src/lib.rs index 860c82b..0b6bd29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,13 @@ pub use tlsf::Heap as TlsfHeap; /// It internally calls `Heap::init(...)` on the heap, /// so `Heap::init(...)` should not be called directly if this macro is used. /// +/// # Panics +/// +/// This macro will panic if either of the following are true: +/// +/// - this function is called more than ONCE. +/// - `size == 0`. +/// /// # Example /// /// ```rust diff --git a/src/llff.rs b/src/llff.rs index a07e73c..aae2485 100644 --- a/src/llff.rs +++ b/src/llff.rs @@ -7,7 +7,7 @@ use linked_list_allocator::Heap as LLHeap; /// A linked list first fit heap. pub struct Heap { - heap: Mutex>, + heap: Mutex>, } impl Heap { @@ -17,7 +17,7 @@ impl Heap { /// [`init`](Self::init) method before using the allocator. pub const fn empty() -> Heap { Heap { - heap: Mutex::new(RefCell::new(LLHeap::empty())), + heap: Mutex::new(RefCell::new((LLHeap::empty(), false))), } } @@ -41,34 +41,42 @@ impl Heap { /// /// # Safety /// - /// Obey these or Bad Stuff will happen. + /// This function is safe if the following invariants hold: /// - /// - This function must be called exactly ONCE. - /// - `size > 0` + /// - `start_addr` points to valid memory. + /// - `size` is correct. + /// + /// # Panics + /// + /// This function will panic if either of the following are true: + /// + /// - this function is called more than ONCE. + /// - `size == 0`. pub unsafe fn init(&self, start_addr: usize, size: usize) { + assert!(size > 0); critical_section::with(|cs| { - self.heap - .borrow(cs) - .borrow_mut() - .init(start_addr as *mut u8, size); + let mut heap = self.heap.borrow_ref_mut(cs); + assert!(!heap.1); + heap.1 = true; + heap.0.init(start_addr as *mut u8, size); }); } /// Returns an estimate of the amount of bytes in use. pub fn used(&self) -> usize { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().used()) + critical_section::with(|cs| self.heap.borrow_ref_mut(cs).0.used()) } /// Returns an estimate of the amount of bytes available. pub fn free(&self) -> usize { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().free()) + critical_section::with(|cs| self.heap.borrow_ref_mut(cs).0.free()) } fn alloc(&self, layout: Layout) -> Option> { critical_section::with(|cs| { self.heap - .borrow(cs) - .borrow_mut() + .borrow_ref_mut(cs) + .0 .allocate_first_fit(layout) .ok() }) @@ -77,8 +85,8 @@ impl Heap { unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { critical_section::with(|cs| { self.heap - .borrow(cs) - .borrow_mut() + .borrow_ref_mut(cs) + .0 .deallocate(NonNull::new_unchecked(ptr), layout) }); } diff --git a/src/tlsf.rs b/src/tlsf.rs index 75f5255..37935d1 100644 --- a/src/tlsf.rs +++ b/src/tlsf.rs @@ -10,7 +10,7 @@ type TlsfHeap = Tlsf<'static, usize, usize, { usize::BITS as usize }, { usize::B /// A two-Level segregated fit heap. pub struct Heap { - heap: Mutex>, + heap: Mutex>, } impl Heap { @@ -20,7 +20,7 @@ impl Heap { /// [`init`](Self::init) method before using the allocator. pub const fn empty() -> Heap { Heap { - heap: Mutex::new(RefCell::new(ConstDefault::DEFAULT)), + heap: Mutex::new(RefCell::new((ConstDefault::DEFAULT, false))), } } @@ -44,29 +44,37 @@ impl Heap { /// /// # Safety /// - /// Obey these or Bad Stuff will happen. + /// This function is safe if the following invariants hold: /// - /// - This function must be called exactly ONCE. - /// - `size > 0` + /// - `start_addr` points to valid memory. + /// - `size` is correct. + /// + /// # Panics + /// + /// This function will panic if either of the following are true: + /// + /// - this function is called more than ONCE. + /// - `size == 0`. pub unsafe fn init(&self, start_addr: usize, size: usize) { + assert!(size > 0); critical_section::with(|cs| { + let mut heap = self.heap.borrow_ref_mut(cs); + assert!(!heap.1); + heap.1 = true; let block: &[u8] = core::slice::from_raw_parts(start_addr as *const u8, size); - self.heap - .borrow(cs) - .borrow_mut() - .insert_free_block_ptr(block.into()); + heap.0.insert_free_block_ptr(block.into()); }); } fn alloc(&self, layout: Layout) -> Option> { - critical_section::with(|cs| self.heap.borrow(cs).borrow_mut().allocate(layout)) + critical_section::with(|cs| self.heap.borrow_ref_mut(cs).0.allocate(layout)) } unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { critical_section::with(|cs| { self.heap - .borrow(cs) - .borrow_mut() + .borrow_ref_mut(cs) + .0 .deallocate(NonNull::new_unchecked(ptr), layout.align()) }) } From 61a268565e8878e7807b8eb4fa963d715df51528 Mon Sep 17 00:00:00 2001 From: Jalon Wong Date: Sun, 31 Aug 2025 11:50:21 -0500 Subject: [PATCH 29/29] Update CHANGELOG.md Co-authored-by: Zeeshan Ali Khan --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5ac15b..0921f25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed -- The `init` function will panic if it's called more than once or with `size == 0`. +- The `Heap::init` methods now panic if they're called more than once or with `size == 0`. ## [v0.6.0] - 2024-09-01