245
src/object.rs
Normal file
245
src/object.rs
Normal file
@@ -0,0 +1,245 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::{BTreeMap, HashMap, VecDeque};
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
pub type Str = String;
|
||||
pub type Int = i64;
|
||||
pub type Float = f64;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
Array(Vec<Value>),
|
||||
Float(Float),
|
||||
Int(Int),
|
||||
Str(String),
|
||||
Obj(ObjPtr),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjPtr {
|
||||
arena: Weak<Arena>,
|
||||
slot: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Obj {
|
||||
vtable: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Arena {
|
||||
slots: Vec<SlotRange>,
|
||||
slots_dirty: bool,
|
||||
objects: BTreeMap<usize, RefCell<Obj>>,
|
||||
max_size: Option<usize>,
|
||||
}
|
||||
|
||||
impl Arena {
|
||||
pub fn new(max_size: Option<usize>) -> Self {
|
||||
Arena {
|
||||
slots: vec![SlotRange::Open(0)],
|
||||
slots_dirty: false,
|
||||
objects: Default::default(),
|
||||
max_size,
|
||||
}
|
||||
}
|
||||
|
||||
/// Compress the slots in the arena.
|
||||
///
|
||||
/// This will:
|
||||
/// 1. Sort all slots by their starting position,
|
||||
/// 2. Merge all slots where necessary.
|
||||
pub fn compress_slots(&mut self) {
|
||||
use SlotRange::*;
|
||||
self.slots.sort_by(|a, b| match (a, b) {
|
||||
(Range(s1, _), Range(s2, _)) => s1.cmp(s2),
|
||||
(Open(o1), Open(o2)) => o1.cmp(o2),
|
||||
(Range(_, _), Open(_)) => std::cmp::Ordering::Less,
|
||||
(Open(_), Range(_, _)) => std::cmp::Ordering::Greater,
|
||||
});
|
||||
let mut slots: Vec<SlotRange> = Vec::with_capacity(self.slots.len());
|
||||
for slot in &self.slots {
|
||||
match slot {
|
||||
// Remove invalid slots
|
||||
Range(start, end) if start > end => continue,
|
||||
_ => {}
|
||||
}
|
||||
if let Some(last) = slots.last().copied() {
|
||||
if let Some(merged) = last.try_merge(slot) {
|
||||
slots.pop();
|
||||
slots.push(merged);
|
||||
} else {
|
||||
slots.push(*slot);
|
||||
}
|
||||
} else {
|
||||
slots.push(*slot);
|
||||
}
|
||||
}
|
||||
self.slots = slots;
|
||||
}
|
||||
|
||||
pub fn obj_new(self: &mut Rc<Self>, obj: Obj) -> Option<ObjPtr> {
|
||||
use SlotRange::*;
|
||||
|
||||
// Compress if necessary
|
||||
if !self.slots_dirty {
|
||||
let self_mut = Rc::get_mut(self).expect("could not get arena mutably from Rc pointer");
|
||||
self_mut.compress_slots();
|
||||
}
|
||||
if self
|
||||
.max_size
|
||||
.map(|max_size| self.objects.len() >= max_size)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
// TODO : return err instead of option
|
||||
// Could not allocate a new object because of configuration
|
||||
return None;
|
||||
}
|
||||
|
||||
// Get the next slot
|
||||
let self_mut = Rc::get_mut(self).expect("could not get arena mutably from Rc pointer");
|
||||
let slots = &mut self_mut.slots;
|
||||
let slot = match slots.first().copied().unwrap() {
|
||||
Range(start, end) => {
|
||||
if start == end {
|
||||
slots.remove(0);
|
||||
} else {
|
||||
slots[0] = Range(start + 1, end);
|
||||
}
|
||||
start
|
||||
}
|
||||
Open(index) => {
|
||||
slots[0] = Open(index + 1);
|
||||
index
|
||||
}
|
||||
};
|
||||
Some(ObjPtr {
|
||||
arena: Rc::downgrade(self),
|
||||
slot,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn free_obj(&mut self, obj_ptr: ObjPtr) {
|
||||
// Compress if necessary
|
||||
if !self.slots_dirty {
|
||||
self.compress_slots();
|
||||
}
|
||||
self.slots
|
||||
.push(SlotRange::Range(obj_ptr.slot, obj_ptr.slot));
|
||||
self.slots_dirty = true; // not my problem
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_arena_compress_slots() {
|
||||
use SlotRange::*;
|
||||
let tests = [
|
||||
(vec![Range(0, 4), Range(2, 6), Open(0)], vec![Open(0)]),
|
||||
(vec![Open(7), Range(0, 4), Range(2, 6)], vec![Open(0)]),
|
||||
(
|
||||
vec![Open(8), Range(0, 4), Range(2, 6)],
|
||||
vec![Range(0, 6), Open(8)],
|
||||
),
|
||||
(vec![Range(0, 4), Range(2, 6)], vec![Range(0, 6)]),
|
||||
(vec![Range(0, 4), Range(2, 6)], vec![Range(0, 6)]),
|
||||
(
|
||||
vec![Range(0, 1), Range(2, 2), Range(3, 4)],
|
||||
vec![Range(0, 4)],
|
||||
),
|
||||
(
|
||||
vec![Range(0, 4), Range(3, 4), Range(2, 2)],
|
||||
vec![Range(0, 4)],
|
||||
),
|
||||
(
|
||||
vec![Range(3, 6), Range(0, 4), Range(2, 2)],
|
||||
vec![Range(0, 6)],
|
||||
),
|
||||
(vec![Range(0, 6), Range(6, 6)], vec![Range(0, 6)]),
|
||||
(vec![Range(7, 6), Range(6, 6)], vec![Range(6, 6)]),
|
||||
];
|
||||
|
||||
for (slots, expected) in tests {
|
||||
let mut arena = Arena {
|
||||
slots,
|
||||
slots_dirty: true,
|
||||
objects: Default::default(),
|
||||
max_size: None,
|
||||
};
|
||||
arena.compress_slots();
|
||||
assert_eq!(arena.slots, expected);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SlotRange {
|
||||
/// A list of slots that are in a range, inclusive.
|
||||
Range(usize, usize),
|
||||
/// Everything from here and onward is open
|
||||
Open(usize),
|
||||
}
|
||||
|
||||
impl SlotRange {
|
||||
/// Try to merge this range with another range.
|
||||
///
|
||||
/// If these ranges don't overlap one another, then None is returned.
|
||||
///
|
||||
/// If a range is open, while the other is closed, the open range will take
|
||||
/// precedent over the closed range.
|
||||
///
|
||||
/// If both ranges are open, the smaller range is given precedent.
|
||||
pub fn try_merge(&self, other: &SlotRange) -> Option<SlotRange> {
|
||||
use SlotRange::*;
|
||||
match (self, other) {
|
||||
(Range(s1, e1), Range(s2, e2)) if (e1 + 1) >= *s2 => {
|
||||
let start = s1.min(s2);
|
||||
let end = e1.max(e2);
|
||||
Some(Range(*start, *end))
|
||||
}
|
||||
(Range(s, e), Open(o)) | (Open(o), Range(s, e)) => {
|
||||
if s <= o && (e + 1) >= *o {
|
||||
Some(Open(*s))
|
||||
} else if s >= o {
|
||||
Some(Open(*o))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
(Open(o1), Open(o2)) => Some(Open(*o1.min(o2))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_slot_range_merge() {
|
||||
use SlotRange::*;
|
||||
let tests = [
|
||||
(Range(0, 4), Range(0, 5), Some(Range(0, 5))),
|
||||
(Range(1, 4), Range(0, 5), Some(Range(0, 5))),
|
||||
(Range(0, 4), Range(1, 5), Some(Range(0, 5))),
|
||||
//
|
||||
(Range(0, 5), Range(0, 3), Some(Range(0, 5))),
|
||||
(Range(0, 5), Range(1, 3), Some(Range(0, 5))),
|
||||
(Range(1, 5), Range(0, 4), Some(Range(0, 5))),
|
||||
//
|
||||
(Range(0, 4), Open(4), Some(Open(0))),
|
||||
(Range(1, 4), Open(4), Some(Open(1))),
|
||||
(Range(2, 4), Open(4), Some(Open(2))),
|
||||
//
|
||||
(Range(0, 3), Range(4, 4), Some(Range(0, 4))),
|
||||
(Range(0, 4), Range(6, 6), None),
|
||||
//
|
||||
(Range(0, 4), Range(2, 2), Some(Range(0, 4))),
|
||||
];
|
||||
|
||||
for (a, b, expected) in tests {
|
||||
assert_eq!(
|
||||
a.try_merge(&b),
|
||||
expected,
|
||||
"expected merge of {:?} and {:?} to be {:?}",
|
||||
a,
|
||||
b,
|
||||
expected
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user