vector/internal_telemetry/allocations/allocator/
stack.rs

1use super::token::AllocationGroupId;
2
3/// An allocation group stack.
4///
5/// As allocation groups are entered and exited, they naturally end up looking a lot like a stack itself: the active
6/// allocation group gets added to the stack when entered, and if another allocation group is entered before the
7/// previous is exited, the newer group is added to the stack above the previous one, and so on and so forth.
8///
9/// This implementation is uses an array to represent the stack to avoid thread local destructor registration issues.
10#[derive(Copy, Clone)]
11pub(crate) struct GroupStack<const N: usize> {
12    current_top: usize,
13    slots: [AllocationGroupId; N],
14}
15
16impl<const N: usize> GroupStack<N> {
17    /// Creates an empty [`GroupStack`].
18    pub const fn new() -> Self {
19        Self {
20            current_top: 0,
21            slots: [AllocationGroupId::ROOT; N],
22        }
23    }
24
25    /// Gets the currently active allocation group.
26    ///
27    /// If the stack is empty, then the root allocation group is the defacto active allocation group, and is returned as such.
28    pub const fn current(&self) -> AllocationGroupId {
29        self.slots[self.current_top]
30    }
31
32    /// Pushes an allocation group on to the stack, marking it as the active allocation group.
33    pub fn push(&mut self, group: AllocationGroupId) {
34        self.current_top += 1;
35        if self.current_top >= self.slots.len() {
36            panic!(
37                "tried to push new allocation group to the current stack, but hit the limit of {N} entries"
38            );
39        }
40        self.slots[self.current_top] = group;
41    }
42
43    /// Pops the previous allocation group that was on the stack.
44    pub fn pop(&mut self) {
45        if self.current_top == 0 {
46            panic!("tried to pop current allocation group from the stack but the stack is empty");
47        }
48        self.current_top -= 1;
49    }
50}