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}