knave-expand 0.14.22

The reading, scoping and expansion stages of knave
Documentation
use std::cell::Cell;

pub struct DeepRecursion;

thread_local! {
    static DEPTH: Cell<usize> = const { Cell::new(0) };
}

impl DeepRecursion {
    const LEVELS_PER_CHECK: usize = 8;
    const LARGEST_LEVEL: usize = 32 * 1024;
    const INNERMOST_WORK: usize = 64 * 1024;
    const RED_ZONE: usize =
        Self::INNERMOST_WORK + (Self::LEVELS_PER_CHECK - 1) * Self::LARGEST_LEVEL;
    const GROWTH: usize = 1024 * 1024;

    pub fn guard<R>(work: impl FnOnce() -> R) -> R {
        let level = Level::enter();
        if level.is_checked() {
            stacker::maybe_grow(Self::RED_ZONE, Self::GROWTH, work)
        } else {
            work()
        }
    }
}

pub struct FreshStack {
    bytes: usize,
}

impl FreshStack {
    pub const fn of(bytes: usize) -> Self {
        Self { bytes }
    }

    pub fn run<R>(&self, work: impl FnOnce() -> R) -> R {
        stacker::grow(self.bytes, work)
    }
}

struct Level {
    depth: usize,
}

impl Level {
    fn enter() -> Self {
        let depth = DEPTH.get() + 1;
        DEPTH.set(depth);
        Self { depth }
    }

    fn is_checked(&self) -> bool {
        (self.depth - 1).is_multiple_of(DeepRecursion::LEVELS_PER_CHECK)
    }
}

impl Drop for Level {
    fn drop(&mut self) {
        DEPTH.set(self.depth - 1);
    }
}

#[cfg(test)]
mod tests {
    use super::DeepRecursion;

    struct ShortStack;

    impl ShortStack {
        const SIZE: usize = DeepRecursion::RED_ZONE / 2;
        const ONE_LEVEL: usize = 1;
        const UNCHECKED_LEVELS: usize = DeepRecursion::LEVELS_PER_CHECK - 1;

        fn run<R>(work: impl FnOnce() -> R) -> R {
            stacker::grow(Self::SIZE, work)
        }

        fn remaining_inside_unchecked_levels() -> usize {
            Self::run(|| Self::remaining_inside(Self::UNCHECKED_LEVELS))
        }

        fn remaining_inside(levels: usize) -> usize {
            if levels == 0 {
                stacker::remaining_stack().expect("the platform reports its stack limit")
            } else {
                DeepRecursion::guard(|| Self::remaining_inside(levels - 1))
            }
        }
    }

    #[test]
    fn a_checked_level_grows_a_short_stack() {
        let remaining = ShortStack::run(|| ShortStack::remaining_inside(ShortStack::ONE_LEVEL));
        assert!(remaining >= DeepRecursion::RED_ZONE);
    }

    #[test]
    fn levels_between_checks_run_on_the_stack_they_find() {
        let remaining = DeepRecursion::guard(ShortStack::remaining_inside_unchecked_levels);
        assert!(remaining < DeepRecursion::RED_ZONE);
    }

    #[test]
    fn returning_guards_give_their_levels_back() {
        let remaining = DeepRecursion::guard(|| {
            ShortStack::remaining_inside_unchecked_levels();
            ShortStack::remaining_inside_unchecked_levels()
        });
        assert!(remaining < DeepRecursion::RED_ZONE);
    }
}