#[derive(Debug)]
pub struct Map {
size: usize,
map: Vec<u8>,
}
#[derive(Debug, Eq, PartialEq, Clone)]
#[repr(u8)]
pub enum State {
Free,
Allocated,
Used,
}
impl State {
pub fn bits(&self) -> u8 {
match self {
State::Free => 0b00,
State::Allocated => 0b01,
State::Used => 0b10,
}
}
}
impl From<u8> for State {
fn from(val: u8) -> State {
match val {
0b00 => State::Free,
0b01 => State::Allocated,
0b10 => State::Used,
_ => panic!("invalid gc state"),
}
}
}
impl Map {
pub fn new(size: usize) -> Map {
Map {
size,
map: vec![0; size],
}
}
pub fn get(&self, index: usize) -> Option<State> {
self.map
.get(index / 4)
.map(|it| (it >> ((index % 4) * 2)) & 0b11)
.map(|it| it.into())
}
pub fn set(&mut self, index: usize, state: State) {
match self.map.get_mut(index / 4) {
Some(byte) => {
let mask: u8 = 0b11 << ((index % 4) * 2);
let flag: u8 = state.bits() << ((index % 4) * 2);
*byte &= !mask;
*byte |= flag;
}
None => panic!("invalid gc index"),
}
}
pub fn mark(&mut self, index: usize) {
self.set(index, State::Used);
}
pub fn is_marked(&self, index: usize) -> bool {
matches!(self.get(index), Some(State::Used))
}
pub fn capacity(&self) -> usize {
self.size
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn initial_state_is_free() {
let gcmap = Map::new(8);
assert_eq!(gcmap.size, 8);
for it in 0..32 {
assert_eq!(gcmap.get(it), Some(State::Free));
}
assert_eq!(gcmap.get(32), None);
}
#[test]
fn mutation() {
let mut gcmap = Map::new(8);
gcmap.set(0, State::Used);
gcmap.set(1, State::Allocated);
gcmap.set(15, State::Used);
assert_eq!(gcmap.get(0), Some(State::Used));
assert_eq!(gcmap.get(15), Some(State::Used));
assert_eq!(gcmap.get(1), Some(State::Allocated));
assert_eq!(gcmap.get(2), Some(State::Free));
}
}