tlsf 1.1.0

An implementation of the Two-Level Segregated Fit (TLSF) allocator with optimized memory footprint
Documentation
use crate::block::{Anchor, FreeBlock};
use crate::header::Header;
use crate::mapping::Indices;

impl<const FLL: usize> Header<FLL> {
    pub(super) unsafe fn unlink<'a>(&mut self, anchor: Anchor<'a>, block: &FreeBlock<'a>) {
        let Indices { fl, sl } = Self::mapping_insert(block.usable_size());

        #[cfg(any(fuzzing, test))]
        debug_assert!(self.get_free_list(fl, sl).is_some());

        match (block.get_prev_free(), block.get_next_free()) {
            (None, None) => {
                #[cfg(all(test, not(miri)))]
                cov_mark::hit!(unlink_last);

                self.set_free_list(fl, sl, None);

                self.clear_sl_bit(fl, sl);

                if self.is_sl_empty(fl) {
                    self.clear_fl_bit(fl);
                }
            }

            (None, Some(next_offset)) => {
                #[cfg(all(test, not(miri)))]
                cov_mark::hit!(unlink_head);

                let next_block = anchor.get_free_block(next_offset);
                next_block.clear_prev_free();

                self.set_free_list(fl, sl, Some(next_offset));
            }

            (Some(prev_offset), None) => {
                #[cfg(all(test, not(miri)))]
                cov_mark::hit!(unlink_tail);

                let prev_block = anchor.get_free_block(prev_offset);
                prev_block.clear_next_free();
            }

            (Some(prev_offset), Some(next_offset)) => {
                #[cfg(all(test, not(miri)))]
                cov_mark::hit!(unlink_middle);

                let prev = anchor.get_free_block(prev_offset);
                let next = anchor.get_free_block(next_offset);

                prev.set_next_free(next_offset);
                next.set_prev_free(prev_offset);
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use core::mem::MaybeUninit;

    use super::*;
    use crate::helpers::FreeBlocks;

    #[test]
    fn last_block_in_free_list() {
        let mut header = Header::<1>::new();
        let mut memory = [MaybeUninit::uninit(); 3];
        let mut free_blocks = FreeBlocks::new(&mut memory);
        let anchor = free_blocks.anchor;
        unsafe {
            let first = free_blocks.next(4, true);
            header.push(anchor, first.clone());

            assert!(header.is_sl_bit_set(0, 1));
            assert!(header.is_fl_bit_set(0));

            {
                #[cfg(not(miri))]
                cov_mark::check!(unlink_last);

                header.unlink(anchor, &first);
            }

            assert!(!header.is_sl_bit_set(0, 1));
            assert!(!header.is_fl_bit_set(0));
            assert!(header.linked_free_blocks(anchor).is_empty());
        }
    }

    #[test]
    fn free_list_head() {
        let mut header = Header::<1>::new();
        let mut memory = [MaybeUninit::uninit(); 5];
        let mut free_blocks = FreeBlocks::new(&mut memory);
        let anchor = free_blocks.anchor;
        unsafe {
            let tail = free_blocks.next(4, false);
            header.push(anchor, tail.clone());

            let head = free_blocks.next(4, true);
            header.push(anchor, head.clone());

            assert!(header.is_sl_bit_set(0, 1));
            assert!(header.is_fl_bit_set(0));

            {
                #[cfg(not(miri))]
                cov_mark::check!(unlink_head);

                header.unlink(anchor, &head);
            }

            assert!(header.is_sl_bit_set(0, 1));
            assert!(header.is_fl_bit_set(0));
            let [remaining] = header.linked_free_blocks(anchor).try_into().unwrap();
            assert_eq!(anchor.offset_of(&tail), anchor.offset_of(&remaining));
        }
    }

    #[test]
    fn free_list_tail() {
        let mut header = Header::<1>::new();
        let mut memory = [MaybeUninit::uninit(); 5];
        let mut free_blocks = FreeBlocks::new(&mut memory);
        let anchor = free_blocks.anchor;
        unsafe {
            let tail = free_blocks.next(4, false);
            header.push(anchor, tail.clone());

            let head = free_blocks.next(4, true);
            header.push(anchor, head.clone());

            assert!(header.is_sl_bit_set(0, 1));
            assert!(header.is_fl_bit_set(0));

            {
                #[cfg(not(miri))]
                cov_mark::check!(unlink_tail);

                header.unlink(anchor, &tail);
            }

            assert!(header.is_sl_bit_set(0, 1));
            assert!(header.is_fl_bit_set(0));
            let [remaining] = header.linked_free_blocks(anchor).try_into().unwrap();
            assert_eq!(anchor.offset_of(&head), anchor.offset_of(&remaining));
        }
    }

    #[test]
    fn from_middle_of_free_list() {
        let mut header = Header::<1>::new();
        let mut memory = [MaybeUninit::uninit(); 7];

        let mut free_blocks = FreeBlocks::new(&mut memory);
        let anchor = free_blocks.anchor;
        unsafe {
            let tail = free_blocks.next(4, false);
            header.push(anchor, tail.clone());

            let middle = free_blocks.next(4, false);
            header.push(anchor, middle.clone());

            let head = free_blocks.next(4, true);
            header.push(anchor, head.clone());

            assert!(header.is_sl_bit_set(0, 1));
            assert!(header.is_fl_bit_set(0));

            {
                #[cfg(not(miri))]
                cov_mark::check!(unlink_middle);

                header.unlink(anchor, &middle);
            }

            assert!(header.is_sl_bit_set(0, 1));
            assert!(header.is_fl_bit_set(0));
            let [a, b] = header.linked_free_blocks(anchor).try_into().unwrap();
            assert_eq!(anchor.offset_of(&head), anchor.offset_of(&a));
            assert_eq!(anchor.offset_of(&tail), anchor.offset_of(&b));
        }
    }
}