1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! This crate implements the TLSF (Two-Level Segregated Fit) dynamic memory
//! allocation algorithm¹. Requires Rust 1.51.0 or later.
//!
//!  - **Allocation and deallocation operations are guaranteed to complete in
//!    constant time.** TLSF is suitable for real-time applications.
//!
//!  - **Fast and small.** You can have both. It was found to be smaller and
//!    faster² than most `no_std`-compatible allocator crates.
//!
//!  - **The memory pool is provided by an application.** Examples of potential
//!    memory pool sources include: a `static` array for global memory
//!    allocation, a memory block allocated by another memory allocator for
//!    arena allocation.
//!
//!  - **This crate supports `#![no_std]`.** It can be used in bare-metal and
//!    RTOS-based applications.
//!
//! <!-- <small> doesn't work on GitHub -->
//!
//! <sub>¹ M. Masmano, I. Ripoll, A. Crespo and J. Real, "TLSF: a new dynamic
//! memory allocator for real-time systems," *Proceedings. 16th Euromicro
//! Conference on Real-Time Systems*, 2004. ECRTS 2004., Catania, Italy, 2004,
//! pp. 79-88, doi: 10.1109/EMRTS.2004.1311009.</sub>
//!
//! <sub>² Compiled for and measured on a STM32F401 microcontroller using
//! <a href="https://github.com/yvt/farcri-rs">FarCri.rs</a>.</sub>
//!
//! # Measured Performance
//!
//! ![The result of latency measurement on STM32F401 is shown here. rlsf:
//! 260–320 cycles. buddy-alloc: 340–440 cycles. umm_malloc: 300–700 cycles.
//! dlmalloc: 450–750 cycles.
//! ](https://ipfs.io/ipfs/QmaQExwdRAT4Z2LRsyJYft1QCMCroEZBJRzpLRMu5eNrLu/time-cm4f-xf-3.svg)
//!
//! <!-- `wee_alloc` could not be measured because it ran out of memory too
//! early, probably because of <https://github.com/rustwasm/wee_alloc/issues/85>
//! `umm_malloc` does not support specifying larger alignment values. -->
//!
//! ![The result of code size measurement on WebAssembly is shown here. rlsf:
//! 1267 bytes, rlsf + pool coalescing: 1584 bytes, wee_alloc: 1910 bytes,
//! dlmalloc: 9613 bytes.
//! ](https://ipfs.io/ipfs/QmREbCr4pXZuMxtFoKU1qXkvqbuLemiydZs8iMAGuVDtAk/size-wasm-xf.svg)
//!
//! <!-- The latest version at the point of writing was used for each library's
//! measurement. The exception is `wee_alloc`, for which a fork based on commit
//! f26c431df6f was used to make it compile on the latest nightly compiler. -->
//!
//! # Limitations
//!
//!  - **It does not support concurrent access.** A whole pool must be locked
//!    for allocation and deallocation. If you use a FIFO lock to protect the
//!    pool, the worst-case execution time will be `O(num_contending_threads)`.
//!    You should consider using a thread-caching memory allocator (e.g.,
//!    TCMalloc, jemalloc) if achieving a maximal throughput in a highly
//!    concurrent environment is desired.
//!
//!  - **Free blocks cannot be returned to the underlying memory system
//!    efficiently.**
//!
//! # Examples
//!
//! ## `Tlsf`: Core API
//!
//! ```rust
//! use rlsf::Tlsf;
//! use std::{mem::MaybeUninit, alloc::Layout};
//!
//! let mut pool = [MaybeUninit::uninit(); 65536];
//!
//! // On 32-bit systems, the maximum block size is 16 << FLLEN = 65536 bytes.
//! // The worst-case fragmentation is (16 << FLLEN) / SLLEN - 2 = 4094 bytes.
//! // `'pool` represents the memory pool's lifetime (`pool` in this case).
//! let mut tlsf: Tlsf<'_, u16, u16, 12, 16> = Tlsf::INIT;
//! //                 ^^            ^^  ^^
//! //                  |             |  |
//! //                'pool           |  SLLEN
//! //                               FLLEN
//! tlsf.insert_free_block(&mut pool);
//!
//! unsafe {
//!     let mut ptr1 = tlsf.allocate(Layout::new::<u64>()).unwrap().cast::<u64>();
//!     let mut ptr2 = tlsf.allocate(Layout::new::<u64>()).unwrap().cast::<u64>();
//!     *ptr1.as_mut() = 42;
//!     *ptr2.as_mut() = 56;
//!     assert_eq!(*ptr1.as_ref(), 42);
//!     assert_eq!(*ptr2.as_ref(), 56);
//!     tlsf.deallocate(ptr1.cast(), Layout::new::<u64>().align());
//!     tlsf.deallocate(ptr2.cast(), Layout::new::<u64>().align());
//! }
//! ```
//!
//! ## `GlobalTlsf`: Global Allocator
//!
//! ```rust
//! #[cfg(all(target_arch = "wasm32", not(target_feature = "atomics")))]
//! #[global_allocator]
//! static A: rlsf::SmallGlobalTlsf = rlsf::SmallGlobalTlsf::INIT;
//!
//! let mut m = std::collections::HashMap::new();
//! m.insert(1, 2);
//! m.insert(5, 3);
//! drop(m);
//! ```
//!
//! # Details
//!
//! ## Changes from the Original Algorithm
//!
//!  - The end of each memory pool is capped by a sentinel block
//!    (a permanently occupied block) instead of a normal block with a
//!    last-block-in-pool flag. This simplifies the code a bit and improves
//!    its worst-case performance and code size.
//!
#![no_std]
#![cfg_attr(feature = "doc_cfg", feature(doc_cfg))]

// FIXME: panicking in constants is unstable
macro_rules! const_panic {
    ($($tt:tt)*) => {
        #[allow(unconditional_panic)]
        {
            let _ = 1 / 0;
            loop {}
        }
    };
}

mod flex;
mod init;
pub mod int;
mod tlsf;
mod utils;
pub use self::{
    flex::*,
    init::*,
    tlsf::{Tlsf, GRANULARITY},
};

/// Attaches `#[cfg(...)]` and `#[doc(cfg(...))]` to a given item definition
/// to conditionally compile it only when we have a `GlobalTlsf` implementation
/// for the current target.
macro_rules! if_supported_target {
    (
        $($tt:tt)*
    ) => {
        #[cfg(any(
            all(target_arch = "wasm32", not(target_feature = "atomics")),
            unix,
            doc,
        ))]
        #[cfg_attr(
            feature = "doc_cfg",
            doc(cfg(any(
                all(target_arch = "wasm32", not(target_feature = "atomics")),
                unix,
                // no `doc` here
            )))
        )]
        $($tt)*
    };
}

if_supported_target! { mod global; }
if_supported_target! { pub use self::global::*; }

#[cfg(any(test, feature = "std"))]
extern crate std;

#[cfg(test)]
mod tests;