xalloc/tlsf.rs
1//
2// Copyright 2017 yvt, all rights reserved.
3//
4// Licensed under the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>. This file may
6// not be copied, modified,or distributed except
7// according to those terms.
8//
9//! A dynamic external memory allocator based on the TLSF (Two-Level Segregated Fit)
10//! algorithm[^1].
11//!
12//! [^1]: Masmano, Miguel, et al. "TLSF: A new dynamic memory allocator for real-time systems."
13//! Real-Time Systems, 2004. ECRTS 2004. Proceedings. 16th Euromicro Conference on. IEEE, 2004.
14//!
15//! ## Type parameters
16//!
17//! - `T` is an integer type used to represent region sizes. You usually use
18//! `u32` or `u64` for this.
19//! - `A` is a memory arena type used to allocate internal block structures.
20//!
21//! ## A Caveat
22//!
23//! This TLSF allocator implements a Good-Fit strategy. In order to achieve the
24//! O(1) execution time, only the first element of each free space list is examined.
25//! As a result, allocations are not guaranteed to succeed even if there
26//! is an enough free space if the following condition is met:
27//!
28//! - There is no free space that is larger than the requested size by a certain
29//! amount.
30//! - There is a free space that is almost as large as the requested size.
31//!
32//! Or more strictly:
33//!
34//! - Let `S`, `mapping` the number of bytes to allocate and the mapping
35//! function that calculates the indexes into the TLSF data structure given
36//! the size of a block, respectively. There exists no free space with a size
37//! `s` where `mapping(s) != mapping(S) && s > S`.
38//! - There exists a free space with a size `s` where
39//! `mapping(s) == mapping(S) && s < S`.
40//!
41//! ## Memory Overhead
42//!
43//! A TLSF allocator requires the following internal storage to operate (some
44//! details are excluded):
45//!
46//! - A variable storing the size of the heap.
47//! - One first-level list that consists of pointers to second-level lists and
48//! a bit field of type `T` where each bit indicates whether a free block is
49//! available in the corresponding second-level list or not.
50//! - `FLI` second-level lists each of which consists of `1 << SLI` pointers to
51//! free blocks and a bit field of `SLI`-bit wide where each bit indicates
52//! whether the corresponding entry of the free block is valid or not.
53//!
54//! When the heap size `size` is a power of two and larger than `1 << SLI`,
55//! `FLI` can be written as `log2(size) + 1 - SLI`. `SLI` is hard-coded to `4`
56//! in this implementation. Using these, the baseline memory consumption can be
57//! calculated by the formula `2 * T + 3 * PS + FLI * (3 * PS + SLI * P + SLI / 8)`
58//! (where `PS = size_of::<usize>()`).
59//!
60//! The following table shows the estimated baseline memory consumption of
61//! [`SysTlsf`] for common configurations.
62//!
63//! | `size_of::<usize>()` | `T` | `size` | memory consumption (bytes) |
64//! | -------------------- | ----- | ----------------- | -------------------------- |
65//! | `8` (64-bit system) | `u32` | `16` | 186 |
66//! | | `u32` | `1 << 10` (1KiB) | 1,110 |
67//! | | `u32` | `1 << 24` (16MiB) | 3,266 |
68//! | | `u32` | `1 << 30` (1GiB) | 4,190 |
69//! | | `u64` | `16` | 194 |
70//! | | `u64` | `1 << 10` (1KiB) | 1,118 |
71//! | | `u64` | `1 << 24` (16MiB) | 3,274 |
72//! | | `u64` | `1 << 30` (1GiB) | 4,198 |
73//! | | `u64` | `1 << 36` (64GiB) | 5,122 |
74//! | `4` (32-bit system) | `u32` | `16` | 98 |
75//! | | `u32` | `1 << 10` (1KiB) | 566 |
76//! | | `u32` | `1 << 24` (16MiB) | 1,658 |
77//! | | `u32` | `1 << 30` (1GiB) | 2,126 |
78//!
79//! [`SysTlsf`]: type.SysTlsf.html
80//!
81//! Note that this does not include the overhead incurred by the system memory
82//! allocator.
83//!
84//! Furthermore, each allocated/free region (represented by `TlsfBlock`)
85//! consumes a certain amount of memory. The exact size of `TlsfBlock` might
86//! differ among compiler versions due to structure layout optimizations, but
87//! we can know the lower bound:
88//!
89//! ```
90//! use xalloc::tlsf::TlsfBlock;
91//! use std::mem::size_of;
92//! assert!(size_of::<TlsfBlock<u32, u32>>() >= 25);
93//! assert!(size_of::<TlsfBlock<u32, u64>>() >= 41);
94//! assert!(size_of::<TlsfBlock<u64, u64>>() >= 49);
95//! ```
96//!
97//! ## Performance
98//!
99//! The allocation throughput is mostly equivalent to that of jemalloc.
100use num::{One, Zero};
101use std::fmt;
102use unreachable::{unreachable, UncheckedOptionExt};
103
104use arena::{SafeArena, UnsafeArena, UnsafeArenaWithMembershipCheck};
105use int::{BinaryInteger, BinaryUInteger};
106
107type TlsfL2Bitmap = u16;
108const LOG2_L2_SIZE: u32 = 4; // must be <= log2(sizeof(TlsfL2Bitmap)*8)
109const L2_SIZE: u32 = 1 << LOG2_L2_SIZE;
110
111/// TLSF-based external memory allocator.
112///
113/// See [the module-level documentation] for more.
114///
115/// [the module-level documentation]: index.html
116///
117/// ## Type parameters
118///
119/// - `T` is an integer type used to represent region sizes. You usually use
120/// `u32` or `u64` for this.
121/// - `A` is a memory arena type used to allocate internal block structures.
122///
123#[derive(Debug)]
124pub struct Tlsf<T, A, P>
125where
126 A: UnsafeArena<TlsfBlock<T, P>, Ptr = P>,
127 P: Clone + Default + PartialEq + Eq + fmt::Debug,
128{
129 size: T,
130 l1: TlsfL1<T, P>,
131 blocks: A,
132}
133
134use arena;
135
136/// [`Tlsf`] that uses [`CheckedArena`] for rigorous memory safety check.
137///
138/// It is really slow. Use [`SysTlsf`] in a production code.
139///
140/// [`CheckedArena`]: crate::arena::CheckedArena
141///
142/// ## Type parameter
143///
144/// - `T` is an integer type used to represent region sizes. You usually use
145/// `u32` or `u64` for this.
146///
147pub type SafeTlsf<T> =
148 Tlsf<T, arena::CheckedArena<TlsfBlock<T, arena::checked::Ptr>>, arena::checked::Ptr>;
149
150/// Type alias of [`TlsfRegion`] for [`SafeTlsf`].
151pub type SafeTlsfRegion = TlsfRegion<arena::checked::Ptr>;
152
153impl<T: BinaryUInteger> SafeTlsf<T> {
154 /// Construct a `SafeTlsf`.
155 pub fn new(size: T) -> Self {
156 Tlsf::with_arena(size, arena::CheckedArena::new())
157 }
158}
159
160/// `Tlsf` that uses the system allocator for the internal storage allocation.
161///
162/// ## Type parameter
163///
164/// - `T` is an integer type used to represent region sizes. You usually use
165/// `u32` or `u64` for this.
166///
167pub type SysTlsf<T> = Tlsf<
168 T,
169 arena::PooledArena<TlsfBlock<T, arena::sys::Ptr>, arena::SysAllocator, arena::sys::Ptr>,
170 arena::sys::Ptr,
171>;
172
173/// Type alias of [`TlsfRegion`] for [`SysTlsf`].
174pub type SysTlsfRegion = TlsfRegion<arena::sys::Ptr>;
175
176impl<T: BinaryUInteger> SysTlsf<T> {
177 /// Construct a `SysTlsf`.
178 pub fn new(size: T) -> Self {
179 Tlsf::with_arena(size, arena::PooledArena::new(arena::SysAllocator))
180 }
181
182 /// Construct a `SysTlsf` with a specific capacity.
183 pub fn with_capacity(size: T, capacity: usize) -> Self {
184 Tlsf::with_arena(
185 size,
186 arena::PooledArena::with_capacity(arena::SysAllocator, capacity),
187 )
188 }
189}
190
191/// A handle type to a region allocated in a [`Tlsf`].
192///
193/// `TlsfRegion` returned by a `Tlsf` only can be used with the
194/// same `Tlsf`.
195#[derive(Debug, PartialEq, Eq, Hash)]
196pub struct TlsfRegion<P>(P);
197
198/// Internal data structure used by [`Tlsf`] that represents a free/occpied
199/// memory block.
200#[derive(Debug)]
201pub struct TlsfBlock<T, P> {
202 /// Points the previous (in terms of the external memory address) block.
203 prev: Option<P>,
204
205 /// Points the next (in terms of the external memory address) block.
206 next: Option<P>,
207
208 /// The external memory address.
209 address: T,
210
211 /// The size of the block in the external memory space.
212 size: T,
213 state: TlsfBlockState<P>,
214}
215
216#[derive(Debug, PartialEq, Eq)]
217enum TlsfBlockState<P> {
218 Free {
219 /// The previous free block in the same free space list.
220 prev_free: Option<P>,
221
222 /// The next free block in the same free space list.
223 next_free: Option<P>,
224 },
225 Used,
226}
227
228impl<P> TlsfBlockState<P> {
229 fn is_used(&self) -> bool {
230 match self {
231 TlsfBlockState::Used => true,
232 _ => false,
233 }
234 }
235}
236
237/// First level table.
238#[derive(Debug)]
239struct TlsfL1<T, P> {
240 /// Array of second level tables.
241 ///
242 /// - `l1[0]` contains segregated lists for free spaces smaller
243 /// than `L2_SIZE`.
244 /// `l1[0].l2[L] contains the segregated list for free spaces whose sizes
245 /// are equal to `L`.
246 /// - `l1[K]` contains segregated lists for free spaces whose sizes are
247 /// in the range `L2_SIZE << (K - 1) .. L2_Size << K`.
248 /// `l1[K].l2[L] contains the segregated list for free spaces whose sizes
249 /// are in the range
250 /// `(L2_SIZE << (K - 1)) + (1 << (K - 1)) * L .. (L2_Size << (K - 1)) + (1 << (K - 1)) * (L + 1)`
251 ///
252 l1: Vec<TlsfL2<P>>,
253
254 /// Each bit indices whether the corresponding element of
255 /// `l1` has at least one free space or not.
256 ///
257 /// The following invariant holds:
258 ///
259 /// - `(bitmap.extract_u32(i..(i+1)) != 0) == (i1[i].bitmap != 0)`
260 //
261 /// The number of L2 tables is proportional to the number of digits of the pool
262 /// size, so using `T` here would be a good choice.
263 bitmap: T,
264
265 /// Points the free block that fills entire the available space
266 /// (used only if the pool size is a power of two and no
267 /// segregated list entry is available for it)
268 entire: Option<P>,
269}
270
271/// Second level table.
272#[derive(Debug, Clone)]
273struct TlsfL2<P> {
274 /// Each bit indicates whether the corresponding element of
275 /// `l2` is valid or not.
276 bitmap: TlsfL2Bitmap,
277
278 /// Each element represents the first block in a free space list.
279 ///
280 /// Points blocks stored in `Tlsf::blocks`. The validity of each
281 /// element is indicated by the corresponding bit of `bitmap`.
282 l2: [P; L2_SIZE as usize],
283}
284
285impl<T, P, A> Tlsf<T, A, P>
286where
287 T: BinaryUInteger,
288 A: UnsafeArena<TlsfBlock<T, P>, Ptr = P>,
289 P: Clone + Default + PartialEq + Eq + fmt::Debug,
290{
291 /// Construct a `Tlsf`.
292 pub fn with_arena(size: T, arena: A) -> Self {
293 let mut sa = Tlsf {
294 l1: TlsfL1::new(&size),
295 size,
296 blocks: arena,
297 };
298
299 // Create the initial free block
300 let block = TlsfBlock {
301 prev: None,
302 next: None,
303 address: Zero::zero(),
304 size: sa.size.clone(),
305 state: TlsfBlockState::Used, // don't care
306 };
307 let block_ptr = sa.blocks.insert(block);
308 unsafe {
309 sa.l1.link(&mut sa.blocks, block_ptr);
310 }
311
312 sa
313 }
314
315 /// Get a reference to the underlying memory arena.
316 pub fn arena(&self) -> &A {
317 &self.blocks
318 }
319
320 /// Get a mutable reference to the underlying memory arena.
321 pub fn arena_mut(&mut self) -> &mut A {
322 &mut self.blocks
323 }
324
325 /// Allocate a region of the size `size` with a given alignment requirement.
326 ///
327 /// Returns a handle of the allocated region and its offset if the
328 /// allocation succeeds. Returns `None` otherwise.
329 ///
330 /// - `align` must be a power of two.
331 /// - `size` must not be zero.
332 #[cfg_attr(feature = "cargo-clippy", allow(clippy::needless_pass_by_value))]
333 pub fn alloc_aligned(&mut self, size: T, align: T) -> Option<(TlsfRegion<P>, T)> {
334 assert!(align.is_power_of_two());
335 self.allocate_aligned_log2(size, align.trailing_zeros())
336 }
337
338 /// Allocate a region of the size `size`.
339 ///
340 /// Returns a handle of the allocated region and its offset if the
341 /// allocation succeeds. Returns `None` otherwise.
342 ///
343 /// `size` must not be zero.
344 pub fn alloc(&mut self, size: T) -> Option<(TlsfRegion<P>, T)> {
345 self.allocate_aligned_log2(size, 0)
346 }
347
348 fn allocate_aligned_log2(&mut self, size: T, align_bits: u32) -> Option<(TlsfRegion<P>, T)> {
349 if size > self.size {
350 return None;
351 }
352 assert_ne!(size, Zero::zero());
353
354 let suitable = unsafe { self.l1.search_suitable(&mut self.blocks, &size, align_bits) };
355 suitable.map(|(position, free_block_ptr, pad)| unsafe {
356 let (mut prev, mut next, free_block_address, free_block_size) = {
357 let block = self.blocks.get_unchecked(&free_block_ptr);
358 (
359 block.prev.clone(),
360 block.next.clone(),
361 block.address.clone(),
362 block.size.clone(),
363 )
364 };
365 let data_end = pad.clone() + size.clone();
366
367 // For exception safety...
368 let mut reserve = 0;
369 if pad != Zero::zero() {
370 reserve += 1;
371 }
372 if data_end != free_block_size {
373 reserve += 1;
374 }
375 self.blocks.reserve(reserve);
376
377 self.l1
378 .unlink_head(&mut self.blocks, free_block_ptr.clone(), position);
379 self.blocks.remove_unchecked(&free_block_ptr);
380
381 if pad != Zero::zero() {
382 let block = TlsfBlock {
383 prev: prev.clone(),
384 next: None, // linked later
385 address: free_block_address.clone(),
386 size: pad.clone(),
387 state: TlsfBlockState::Used, // don't care
388 };
389 let block_ptr = self.blocks.insert(block);
390 self.l1.link(&mut self.blocks, block_ptr.clone());
391 if let Some(ref old_prev) = prev {
392 self.blocks.get_unchecked_mut(old_prev).next = Some(block_ptr.clone());
393 }
394 prev = Some(block_ptr);
395 }
396
397 if data_end != free_block_size {
398 let block = TlsfBlock {
399 prev: None, // linked later
400 next: next.clone(),
401 address: free_block_address.clone() + data_end.clone(),
402 size: free_block_size.clone() - data_end.clone(),
403 state: TlsfBlockState::Used, // don't care
404 };
405 let block_ptr = self.blocks.insert(block);
406 self.l1.link(&mut self.blocks, block_ptr.clone());
407 if let Some(ref old_next) = next {
408 self.blocks.get_unchecked_mut(old_next).prev = Some(block_ptr.clone());
409 }
410 next = Some(block_ptr);
411 }
412
413 let main_ptr = {
414 let block = TlsfBlock {
415 prev: prev.clone(),
416 next: next.clone(),
417 address: free_block_address.clone() + pad.clone(),
418 size,
419 state: TlsfBlockState::Used, // care!
420 };
421 self.blocks.insert(block)
422 };
423
424 // Connect neighboring blocks to this
425 let address = self.blocks.get_unchecked(&main_ptr).address.clone();
426
427 if let Some(ptr) = prev {
428 self.blocks.get_unchecked_mut(&ptr).next = Some(main_ptr.clone());
429 }
430 if let Some(ptr) = next {
431 self.blocks.get_unchecked_mut(&ptr).prev = Some(main_ptr.clone());
432 }
433
434 (TlsfRegion(main_ptr), address)
435 })
436 }
437
438 /// Deallocate the specified region, without checking the origin of the
439 /// `TlsfRegion`.
440 ///
441 /// This might result in an undefined behavior if `r` originates from
442 /// a different instance of `Tlsf`.
443 pub unsafe fn dealloc_unchecked(&mut self, r: TlsfRegion<P>) {
444 let block_ptr = r.0;
445
446 let (prev_ptr, next_ptr) = {
447 let block = self.blocks.get_unchecked(&block_ptr);
448 if let TlsfBlockState::Used = block.state {
449 } else {
450 // It's impossible for the application to obtain a
451 // `TlsfRegion` for a free block. `TlsfRegion` isn't even
452 // `Clone` nor `Copy`.
453 unreachable();
454 }
455 (block.prev.clone(), block.next.clone())
456 };
457
458 // Try to merge neighboring free blocks
459 let prev_info = if let Some(ref ptr) = prev_ptr {
460 let block = self.blocks.get_unchecked(ptr);
461 if let TlsfBlockState::Free { .. } = block.state {
462 Some((block.prev.clone(), block.size.clone()))
463 } else {
464 None
465 }
466 } else {
467 None
468 };
469 let next_info = if let Some(ref ptr) = next_ptr {
470 let block = self.blocks.get_unchecked(ptr);
471 if let TlsfBlockState::Free { .. } = block.state {
472 Some((block.next.clone(), block.size.clone()))
473 } else {
474 None
475 }
476 } else {
477 None
478 };
479 {
480 let block = self.blocks.get_unchecked_mut(&block_ptr);
481 if let Some((ref new_prev_ptr, ref prev_size)) = prev_info {
482 block.prev = new_prev_ptr.clone();
483 block.size += prev_size.clone();
484 block.address -= prev_size.clone();
485 }
486 if let Some((ref new_next_ptr, ref next_size)) = next_info {
487 block.next = new_next_ptr.clone();
488 block.size += next_size.clone();
489 }
490 }
491
492 if prev_info.is_some() {
493 self.l1
494 .unlink(&mut self.blocks, prev_ptr.clone().unchecked_unwrap());
495 self.blocks.remove_unchecked(&prev_ptr.unchecked_unwrap());
496 }
497 if next_info.is_some() {
498 self.l1
499 .unlink(&mut self.blocks, next_ptr.clone().unchecked_unwrap());
500 self.blocks.remove_unchecked(&next_ptr.unchecked_unwrap());
501 }
502
503 if let Some((Some(new_prev_ptr), _)) = prev_info {
504 let block = self.blocks.get_unchecked_mut(&new_prev_ptr);
505 block.next = Some(block_ptr.clone());
506 }
507 if let Some((Some(new_next_ptr), _)) = next_info {
508 let block = self.blocks.get_unchecked_mut(&new_next_ptr);
509 block.prev = Some(block_ptr.clone());
510 }
511
512 self.l1.link(&mut self.blocks, block_ptr);
513 }
514
515 #[doc(hidden)]
516 pub unsafe fn test_integrity(&mut self, root_ptr: &TlsfRegion<P>)
517 where
518 P: fmt::Debug + PartialEq,
519 {
520 // Find the physically first block
521 let mut first_ptr = root_ptr.0.clone();
522 while self.blocks.get_unchecked(&first_ptr).prev.is_some() {
523 first_ptr = self.blocks.get_unchecked(&first_ptr).prev.clone().unwrap();
524 }
525
526 let dump = || {
527 use std::fmt::Write;
528 let mut s = String::new();
529 let mut cur_ptr = first_ptr.clone();
530 loop {
531 let cur = self.blocks.get_unchecked(&cur_ptr);
532 let next_ptr = cur.next.clone();
533 writeln!(
534 &mut s,
535 "{:?} - [{:?}, {:?}] - {:?}",
536 cur.prev, cur_ptr, cur.state, cur.next
537 )
538 .unwrap();
539 if let Some(next_ptr) = next_ptr {
540 cur_ptr = next_ptr;
541 } else {
542 break;
543 }
544 }
545 s
546 };
547
548 // scan every block and check the physical connections
549 let mut cur_ptr = first_ptr.clone();
550 let mut addr = Zero::zero();
551 loop {
552 let cur = self.blocks.get_unchecked(&cur_ptr);
553 assert_eq!(
554 cur.address,
555 addr,
556 "[{:?}].prev ({:?}) should be {:?}. Dump: \n{}",
557 cur_ptr,
558 &cur.address,
559 &addr,
560 dump()
561 );
562 addr += cur.size.clone();
563
564 let next_ptr = cur.next.clone();
565 if let Some(next_ptr) = next_ptr {
566 let next = self.blocks.get_unchecked(&next_ptr);
567 assert_eq!(
568 next.prev,
569 Some(cur_ptr.clone()),
570 "[{:?}].prev ({:?}) should be {:?}. Dump: \n{}",
571 next_ptr,
572 next.prev,
573 cur_ptr,
574 dump()
575 );
576 assert!(
577 next.state.is_used() || cur.state.is_used(),
578 "[{:?}].state and [{:?}].state must not be Free at the same time. Dump: \n{}",
579 next_ptr,
580 cur_ptr,
581 dump()
582 );
583 cur_ptr = next_ptr;
584 } else {
585 break;
586 }
587 }
588 assert_eq!(
589 self.size,
590 addr,
591 "self.size ({:?}) should be {:?}. Dump: \n{}",
592 &self.size,
593 &addr,
594 dump()
595 );
596 }
597}
598
599impl<T, P, A> Tlsf<T, A, P>
600where
601 T: BinaryUInteger,
602 A: UnsafeArena<TlsfBlock<T, P>, Ptr = P> + UnsafeArenaWithMembershipCheck<TlsfBlock<T, P>>,
603 P: Clone + Default + PartialEq + Eq + fmt::Debug,
604{
605 /// Deallocate the specified region.
606 ///
607 /// Returns `Err(r)` if `r` does not originate from the same instance of `Tlsf`.
608 pub fn dealloc(&mut self, r: TlsfRegion<P>) -> Result<(), TlsfRegion<P>> {
609 unsafe {
610 if self.blocks.contains_unchecked(&r.0) {
611 self.dealloc_unchecked(r);
612 Ok(())
613 } else {
614 Err(r)
615 }
616 }
617 }
618}
619
620impl<T, P, A> Tlsf<T, A, P>
621where
622 T: BinaryUInteger,
623 A: UnsafeArena<TlsfBlock<T, P>, Ptr = P> + SafeArena<TlsfBlock<T, P>>,
624 P: Clone + Default + PartialEq + Eq + fmt::Debug,
625{
626 /// Deallocate the specified region.
627 ///
628 /// `r` must originate from the same instance of `Tlsf`. Otherwise, `Tlsf`
629 /// enters an inconsistent state and possibly panics, but does not cause an
630 /// undefined behavior.
631 pub fn dealloc_relaxed(&mut self, r: TlsfRegion<P>) {
632 unsafe { self.dealloc_unchecked(r) }
633 }
634}
635
636impl<T: BinaryUInteger, P> TlsfBlock<T, P> {
637 /// Return whether the requested region can fit in this space (assuming it
638 /// is free).
639 ///
640 /// The returned value is the size of padding required to meet the
641 /// alignment requirement. `None` if it cannot fit.
642 fn can_fit(&self, size: &T, align_bits: u32) -> Option<T> {
643 if align_bits == 0 {
644 if size <= &self.size {
645 Some(Zero::zero())
646 } else {
647 None
648 }
649 } else {
650 let start = self.address.clone().checked_ceil_fix(align_bits);
651 let end_block = self.address.clone() + self.size.clone();
652 if let Some(start) = start {
653 if start < end_block && size <= &(end_block.clone() - start.clone()) {
654 Some(start - self.address.clone())
655 } else {
656 None
657 }
658 } else {
659 start
660 }
661 }
662 }
663}
664
665impl<T: BinaryUInteger, P: Clone + Default + PartialEq + Eq + fmt::Debug> TlsfL1<T, P> {
666 /// Constructs `TlsfL1`.
667 fn new(size: &T) -> Self {
668 assert!(size > &Zero::zero());
669
670 let size_m1 = size.clone() - One::one();
671 let num_l2s = T::max_digits().saturating_sub(LOG2_L2_SIZE + size_m1.leading_zeros()) + 1;
672
673 Self {
674 l1: vec![
675 TlsfL2 {
676 bitmap: Zero::zero(),
677 l2: [
678 // L2_SIZE elements
679 P::default(),
680 P::default(),
681 P::default(),
682 P::default(),
683 P::default(),
684 P::default(),
685 P::default(),
686 P::default(),
687 P::default(),
688 P::default(),
689 P::default(),
690 P::default(),
691 P::default(),
692 P::default(),
693 P::default(),
694 P::default(),
695 ],
696 };
697 num_l2s as usize
698 ],
699 bitmap: Zero::zero(),
700 entire: None,
701 }
702 }
703
704 /// Compute the first and second level table index for a given size of free
705 /// space.
706 #[inline]
707 fn map_size(&self, size: &T) -> (u32, u32) {
708 // Equivalent to:
709 // `let l1_index = T::max_digits().saturating_sub(LOG2_L2_SIZE + size.leading_zeros());`
710 let l1_index = T::max_digits()
711 - LOG2_L2_SIZE
712 - (size.clone() | T::ones(0..LOG2_L2_SIZE)).leading_zeros();
713
714 // Branch-less equivalent of:
715 // `let min_bit_index = l1_index.saturating_sub(1);`
716 let min_bit_index = l1_index - if l1_index == 0 { 0 } else { 1 };
717
718 let l2_index = (size.clone() >> min_bit_index).extract_u32(0..LOG2_L2_SIZE);
719
720 (l1_index, l2_index)
721 }
722
723 /// Search a free block at least as large as `size` with the alignment
724 /// requirement `1 << align_bits`.
725 ///
726 /// The result can be one of the following:
727 ///
728 /// - `None`: No suitable block was found.
729 /// - `Some((position, block_ptr, pad)): A suitable block was found. `position` is either of:
730 /// - `Some((l1, l2))`: `block_ptr` is the head of the free space list at the position `(l1, l2)`.
731 /// - `None`: `block_ptr` is `self.entire`.
732 ///
733 /// `size` must be less than or equal to the size of the heap.
734 #[cfg_attr(feature = "cargo-clippy", allow(clippy::type_complexity))]
735 unsafe fn search_suitable<A: UnsafeArena<TlsfBlock<T, P>, Ptr = P>>(
736 &self,
737 blocks: &mut A,
738 size: &T,
739 align_bits: u32,
740 ) -> Option<(Option<(u32, u32)>, P, T)> {
741 if let Some(ref entire) = self.entire {
742 return Some((None, entire.clone(), Zero::zero()));
743 }
744
745 let (l1_first, l2_first) = self.map_size(size);
746 if self.bitmap.get_bit(l1_first) {
747 if l1_first as usize >= self.l1.len() {
748 unreachable();
749 }
750 let l2t: &TlsfL2<P> = &self.l1[l1_first as usize];
751 if l2t.bitmap.get_bit(l2_first) {
752 // Found a free block in the same bucket.
753 let block_ptr = l2t.l2[l2_first as usize].clone();
754 let block = blocks.get_unchecked(&block_ptr);
755 if let Some(pad) = block.can_fit(size, align_bits) {
756 return Some((Some((l1_first, l2_first)), block_ptr, pad));
757 }
758 }
759
760 // Search the same second level table.
761 let l2 = l2t.bitmap.bit_scan_forward(l2_first + 1);
762 if l2 < L2_SIZE {
763 // Found one
764 let block_ptr = l2t.l2[l2 as usize].clone();
765 let can_fit = if align_bits == 0 {
766 Some(Zero::zero())
767 } else {
768 blocks.get_unchecked(&block_ptr).can_fit(size, align_bits)
769 };
770 if let Some(pad) = can_fit {
771 if align_bits == 0 {
772 debug_assert!(blocks
773 .get_unchecked(&block_ptr)
774 .can_fit(size, align_bits)
775 .is_some());
776 }
777 return Some((Some((l1_first, l2)), block_ptr, pad));
778 }
779 }
780 }
781
782 let mut l1_first = self.bitmap.bit_scan_forward(l1_first + 1);
783 let mut l2_first = if l1_first == T::max_digits() {
784 return None;
785 } else {
786 if l1_first as usize >= self.l1.len() {
787 unreachable();
788 }
789 let l2t: &TlsfL2<P> = &self.l1[l1_first as usize];
790 let l2 = l2t.bitmap.bit_scan_forward(0);
791 debug_assert_ne!(l2, TlsfL2Bitmap::max_digits());
792 let block_ptr = l2t.l2[l2 as usize].clone();
793 let can_fit = if align_bits == 0 {
794 Some(Zero::zero())
795 } else {
796 blocks.get_unchecked(&block_ptr).can_fit(size, align_bits)
797 };
798 if let Some(pad) = can_fit {
799 if align_bits == 0 {
800 debug_assert!(blocks
801 .get_unchecked(&block_ptr)
802 .can_fit(size, align_bits)
803 .is_some());
804 }
805 return Some((Some((l1_first, l2)), block_ptr, pad));
806 }
807 l2
808 };
809
810 // For aligned allocations, there are cases where no free space that can
811 // satisfy the alignment requirement even if the size requirement is met.
812 // We need to check more free lists.
813 //
814 // The code below should be unreachable for allocations without an
815 // alignment requirement.
816 debug_assert_ne!(align_bits, 0);
817
818 // FIXME: add explanation
819 let worst_size = size.ref_saturating_add(T::ones(0..align_bits));
820 let (l1_worst, l2_worst) = self.map_size(&worst_size);
821 while (l1_first, l2_first) < (l1_worst, l2_worst) {
822 // Determine the next search start position
823 l2_first += 1;
824 if l2_first >= TlsfL2Bitmap::max_digits() {
825 l1_first = self.bitmap.bit_scan_forward(l1_first + 1);
826 if l1_first == T::max_digits() {
827 return None;
828 }
829 l2_first = 0;
830 }
831
832 let l2t: &TlsfL2<P> = &self.l1[l1_first as usize];
833 let l2 = l2t.bitmap.bit_scan_forward(l2_first);
834 if l2 == TlsfL2Bitmap::max_digits() {
835 l2_first = l2;
836 continue;
837 }
838 let block_ptr = l2t.l2[l2 as usize].clone();
839 if let Some(pad) = blocks.get_unchecked(&block_ptr).can_fit(size, align_bits) {
840 return Some((Some((l1_first, l2)), block_ptr, pad));
841 } else {
842 l2_first = l2;
843 }
844 }
845
846 None
847 }
848
849 /// Remove the given block from the free space list.
850 #[inline]
851 unsafe fn unlink<A: UnsafeArena<TlsfBlock<T, P>, Ptr = P>>(
852 &mut self,
853 blocks: &mut A,
854 block_ptr: P,
855 ) {
856 let (l1, l2) = self.map_size(&blocks.get_unchecked(&block_ptr).size);
857 if l1 as usize >= self.l1.len() {
858 self.entire = None;
859 } else {
860 {
861 debug_assert!(self.bitmap.get_bit(l1));
862 debug_assert!(
863 self.l1[l1 as usize].bitmap.get_bit(l2),
864 "L2 bitmap 0b{:b} has not bit {} set.",
865 &self.l1[l1 as usize].bitmap,
866 l2
867 );
868 if self.l1[l1 as usize].l2[l2 as usize] == block_ptr {
869 return self.unlink_head(blocks, block_ptr, Some((l1, l2)));
870 }
871 }
872
873 // Retrieve the neighboring blocks (in the free space list)
874 let (prev_ptr, o_next_ptr) = {
875 let block = blocks.get_unchecked(&block_ptr);
876 if let TlsfBlockState::Free {
877 prev_free: Some(ref prev_free),
878 ref next_free,
879 } = block.state
880 {
881 (prev_free.clone(), next_free.clone())
882 } else {
883 unreachable();
884 }
885 };
886
887 // Unlink the current block
888 if let Some(ref next_ptr) = o_next_ptr {
889 let next_block = blocks.get_unchecked_mut(next_ptr);
890 if let TlsfBlockState::Free {
891 ref mut prev_free, ..
892 } = next_block.state
893 {
894 debug_assert_eq!(*prev_free, Some(block_ptr.clone()));
895 *prev_free = Some(prev_ptr.clone());
896 } else {
897 unreachable();
898 }
899 }
900
901 {
902 let prev_block = blocks.get_unchecked_mut(&prev_ptr);
903 if let TlsfBlockState::Free {
904 ref mut next_free, ..
905 } = prev_block.state
906 {
907 debug_assert_eq!(*next_free, Some(block_ptr.clone()));
908 *next_free = o_next_ptr;
909 } else {
910 unreachable();
911 }
912 }
913 }
914 }
915
916 /// Remove the given block from the free space list.
917 ///
918 /// `block_ptr` must be the head of the free space list specified by `position`.
919 /// `block_ptr` returned by `search_suitable` always satisfies this condition,
920 /// supposing no intervening modification was done.
921 #[inline]
922 unsafe fn unlink_head<A: UnsafeArena<TlsfBlock<T, P>, Ptr = P>>(
923 &mut self,
924 blocks: &mut A,
925 block_ptr: P,
926 position: Option<(u32, u32)>,
927 ) {
928 if let Some((l1, l2)) = position {
929 let l2t: &mut TlsfL2<P> = &mut self.l1[l1 as usize];
930
931 debug_assert!(self.bitmap.get_bit(l1));
932 debug_assert!(
933 l2t.bitmap.get_bit(l2),
934 "L2 bitmap 0b{:b} has not bit {} set.",
935 &l2t.bitmap,
936 l2
937 );
938 debug_assert_eq!(block_ptr, l2t.l2[l2 as usize]);
939
940 let next_block_ptr = {
941 let block = blocks.get_unchecked(&block_ptr);
942 if let TlsfBlockState::Free { ref next_free, .. } = block.state {
943 next_free.clone()
944 } else {
945 unreachable();
946 }
947 };
948
949 if let Some(next_block_ptr) = next_block_ptr {
950 let next_block = blocks.get_unchecked_mut(&next_block_ptr);
951 if let TlsfBlockState::Free {
952 ref mut prev_free, ..
953 } = next_block.state
954 {
955 debug_assert_eq!(*prev_free, Some(block_ptr));
956 *prev_free = None;
957 } else {
958 unreachable();
959 }
960
961 l2t.l2[l2 as usize] = next_block_ptr;
962 } else {
963 l2t.bitmap.clear_bit(l2);
964 if l2t.bitmap == Zero::zero() {
965 self.bitmap.clear_bit(l1);
966 }
967
968 // don't care about the value of `l2t.l2[l2 as usize]`
969 }
970 } else {
971 debug_assert_eq!(Some(block_ptr), self.entire);
972 self.entire = None;
973 }
974 }
975
976 /// Insert the given block to a free space list.
977 ///
978 /// `block_ptr` must point a valid `TlsfBlock` in `blocks`.
979 /// The given block's `TlsfBlock::state` will be overwritten with a new
980 /// `TlsfBlockState::Free` value.
981 #[inline]
982 unsafe fn link<A>(&mut self, blocks: &mut A, block_ptr: P)
983 where
984 A: UnsafeArena<TlsfBlock<T, P>, Ptr = P>,
985 {
986 let (l1, l2) = self.map_size(&blocks.get_unchecked(&block_ptr).size);
987 if l1 as usize >= self.l1.len() {
988 self.entire = Some(block_ptr);
989 } else {
990 let l2t: &mut TlsfL2<P> = &mut self.l1[l1 as usize];
991
992 // Update bitmaps
993 let head_valid = l2t.bitmap.get_bit(l2);
994 l2t.bitmap.set_bit(l2);
995 self.bitmap.set_bit(l1);
996
997 // Link the given block to the list
998 let head = &mut l2t.l2[l2 as usize];
999
1000 {
1001 let block = blocks.get_unchecked_mut(&block_ptr);
1002 block.state = TlsfBlockState::Free {
1003 prev_free: None,
1004 next_free: if head_valid { Some(head.clone()) } else { None },
1005 };
1006 }
1007 if head_valid {
1008 let next_block = blocks.get_unchecked_mut(head);
1009 if let TlsfBlockState::Free {
1010 ref mut prev_free, ..
1011 } = next_block.state
1012 {
1013 debug_assert!(prev_free.is_none());
1014 *prev_free = Some(block_ptr.clone());
1015 } else {
1016 unreachable();
1017 }
1018 }
1019
1020 *head = block_ptr;
1021 }
1022 }
1023}
1024
1025#[test]
1026fn num_l2s() {
1027 for i in 1..L2_SIZE {
1028 let l1 = TlsfL1::<_, u32>::new(&(i as u32));
1029 assert_eq!(l1.l1.len(), 1);
1030 }
1031 for k in 0..4 {
1032 let i = L2_SIZE << k;
1033 let l1 = TlsfL1::<_, u32>::new(&i);
1034 assert_eq!(l1.l1.len(), k + 1);
1035 }
1036}