slvm/heap/
storage.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
use crate::bits::{
    is_live, is_marked, is_mutable, is_traced, FLAG_MARK, FLAG_MUT, FLAG_STICKY, FLAG_TRACED,
};
use crate::{clear_bit, is_bit_set, set_bit};

#[derive(Debug)]
pub(super) struct Storage<T: Clone> {
    flags: Vec<u8>,
    vals: Vec<T>,
    capacity: usize,
    live_objects: usize,
    sticky_objects: usize,
    grow_factor: f64,
}

impl<T: Clone> Storage<T> {
    pub fn with_capacity(capacity: usize) -> Self {
        Self {
            flags: Vec::with_capacity(capacity),
            // Keep one extra slot to do sway on replace.
            vals: Vec::with_capacity(capacity + 1),
            capacity,
            live_objects: 0,
            sticky_objects: 0,
            grow_factor: 2.0,
        }
    }

    pub fn get(&self, idx: usize) -> Option<&T> {
        self.vals.get(idx)
    }

    pub fn get_mut(&mut self, idx: usize) -> Option<&mut T> {
        self.vals.get_mut(idx)
    }

    pub fn set_grow_factor(&mut self, grow_factor: f64) {
        self.grow_factor = grow_factor;
    }

    pub fn capacity(&self) -> usize {
        self.capacity
    }

    pub fn live_objects(&self) -> usize {
        self.live_objects
    }

    pub fn alloc(&mut self, obj: T, flags: u8) -> u32 {
        if self.live_objects >= self.capacity {
            let new_min = (self.live_objects as f64 * self.grow_factor) as usize;
            if new_min > self.capacity {
                self.capacity = new_min;
                self.flags.reserve(new_min - self.flags.len());
                self.vals.reserve((new_min - self.vals.len()) + 1);
            }
        }
        if self.vals.len() < self.capacity {
            let idx = self.vals.len();
            self.vals.push(obj);
            self.flags.push(flags | FLAG_MARK);
            self.live_objects += 1;
            idx as u32
        } else {
            for (idx, flag) in self.flags.iter_mut().enumerate() {
                if !is_live(*flag) {
                    self.live_objects += 1;
                    *flag = flags | FLAG_MARK;
                    self.vals.push(obj);
                    self.vals.swap_remove(idx);
                    return idx as u32;
                }
            }
            panic!("Failed to allocate to heap- no free objects and no capacity!");
        }
    }

    pub fn clear_marks(&mut self) {
        self.live_objects = 0;
        self.live_objects = 0;
        for flag in self.flags.iter_mut() {
            clear_bit!(*flag, FLAG_MARK);
            clear_bit!(*flag, FLAG_TRACED);
            // if it is sticky mark it
            if is_bit_set!(*flag, FLAG_STICKY) {
                self.live_objects += 1;
                set_bit!(*flag, FLAG_MARK);
            }
        }
    }

    /// Is the object at index still alive after GC.
    pub fn is_live(&self, idx: usize) -> bool {
        if let Some(flag) = self.flags.get(idx) {
            is_live(*flag)
        } else {
            false
        }
    }

    /// Is the object at index mutable.
    pub fn is_mutable(&self, idx: usize) -> bool {
        if let Some(flag) = self.flags.get(idx) {
            is_mutable(*flag)
        } else {
            false
        }
    }

    /// Mark the object at index immutable.
    pub fn immutable(&mut self, idx: usize) {
        if let Some(flag) = self.flags.get_mut(idx) {
            clear_bit!(*flag, FLAG_MUT);
        } else {
            panic!("Invalid object handle in immutable!")
        }
    }

    pub fn mark(&mut self, idx: usize) {
        if let Some(flag) = self.flags.get_mut(idx) {
            if !is_marked(*flag) {
                self.live_objects += 1;
                set_bit!(*flag, FLAG_MARK);
            }
        } else {
            panic!("Invalid object handle in mark!")
        }
    }

    pub fn is_traced_and_set(&mut self, idx: usize) -> bool {
        if let Some(flag) = self.flags.get_mut(idx) {
            let ret = is_traced(*flag);
            set_bit!(*flag, FLAG_TRACED);
            ret
        } else {
            panic!("Invalid object handle in traced!")
        }
    }

    pub fn sticky(&mut self, idx: usize) {
        if let Some(flag) = self.flags.get_mut(idx) {
            if !is_bit_set!(*flag, FLAG_STICKY) {
                self.sticky_objects += 1;
                set_bit!(*flag, FLAG_STICKY);
            }
        } else {
            panic!("Invalid object handle in sticky!")
        }
    }

    pub fn unsticky(&mut self, idx: usize) {
        if let Some(flag) = self.flags.get_mut(idx) {
            if is_bit_set!(*flag, FLAG_STICKY) {
                self.sticky_objects -= 1;
                clear_bit!(*flag, FLAG_STICKY);
            }
        } else {
            panic!("Invalid object handle in unsticky!")
        }
    }

    /// For any dead, live bit not set, objects in heap set them to val.
    pub fn set_all_dead(&mut self, val: T) {
        for (cur, flag) in self.flags.iter().enumerate() {
            if !is_live(*flag) {
                self.vals.push(val.clone());
                self.vals.swap_remove(cur);
            }
        }
    }

    pub fn trace_all_live<FN: FnMut(&T)>(&mut self, mut trace: FN) {
        for (flag, value) in self.flags.iter_mut().zip(self.vals.iter()) {
            if is_live(*flag) {
                set_bit!(*flag, FLAG_TRACED);
                trace(value);
            }
        }
    }
}

impl<T: Clone> Default for Storage<T> {
    fn default() -> Self {
        Self::with_capacity(512)
    }
}