2022-01-07 20:30:55 -08:00
|
|
|
// TODO - remove this at some point.
|
|
|
|
|
// I haven't gotten around to testing or working with this API yet and I don't
|
|
|
|
|
// want it to clog the warnings yet.
|
|
|
|
|
#![allow(dead_code)]
|
|
|
|
|
|
2022-01-16 14:14:50 -08:00
|
|
|
use crate::syn::ast::SpStmt;
|
2022-01-12 21:49:41 -08:00
|
|
|
use crate::vm::{error::Result, machine::Machine};
|
2022-01-12 18:00:11 -08:00
|
|
|
use crate::{scope::Scope, syn::span::Span, vm::inst::Inst};
|
2021-12-21 11:29:59 -08:00
|
|
|
use std::cell::RefCell;
|
2022-01-07 20:30:55 -08:00
|
|
|
use std::collections::{BTreeMap, HashMap};
|
2022-01-12 18:00:11 -08:00
|
|
|
use std::fmt::{self, Debug, Display};
|
2021-12-21 11:29:59 -08:00
|
|
|
use std::rc::{Rc, Weak};
|
|
|
|
|
|
|
|
|
|
pub type Str = String;
|
|
|
|
|
pub type Int = i64;
|
|
|
|
|
pub type Float = f64;
|
2022-01-07 20:30:55 -08:00
|
|
|
pub type VTable = HashMap<String, Value>;
|
2021-12-21 11:29:59 -08:00
|
|
|
|
2022-01-12 17:35:48 -08:00
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Quote
|
|
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
/// A handle to a quote pointing to an element in a `QuoteTable`.
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub struct Quote(usize);
|
|
|
|
|
|
|
|
|
|
impl Quote {
|
|
|
|
|
pub fn index(&self) -> usize {
|
|
|
|
|
self.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A table of compiled quotes, their expression trees, and their spans.
|
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
|
|
|
pub struct QuoteTable {
|
2022-01-16 14:14:50 -08:00
|
|
|
table: Vec<(Span, Scope, Vec<SpStmt>, Rc<Vec<Inst>>)>,
|
2022-01-12 17:35:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl QuoteTable {
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
Default::default()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn insert(
|
|
|
|
|
&mut self,
|
|
|
|
|
span: Span,
|
|
|
|
|
scope: Scope,
|
2022-01-16 14:14:50 -08:00
|
|
|
quote: Vec<SpStmt>,
|
2022-01-12 21:49:41 -08:00
|
|
|
compiled: Rc<Vec<Inst>>,
|
2022-01-12 17:35:48 -08:00
|
|
|
) -> Quote {
|
|
|
|
|
let next = Quote(self.table.len());
|
|
|
|
|
self.table.push((span, scope, quote, compiled));
|
|
|
|
|
next
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-16 14:14:50 -08:00
|
|
|
pub fn get(&self, quote: Quote) -> &(Span, Scope, Vec<SpStmt>, Rc<Vec<Inst>>) {
|
2022-01-12 17:35:48 -08:00
|
|
|
&self.table[quote.0]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Value
|
|
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2021-12-21 11:29:59 -08:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum Value {
|
|
|
|
|
Array(Vec<Value>),
|
|
|
|
|
Float(Float),
|
|
|
|
|
Int(Int),
|
2022-01-07 20:30:55 -08:00
|
|
|
Str(Str),
|
2022-01-12 17:35:48 -08:00
|
|
|
Quote(Quote),
|
2022-01-07 20:30:55 -08:00
|
|
|
ObjPtr(ObjPtr),
|
2022-01-12 18:00:11 -08:00
|
|
|
BuiltinFn(BuiltinFn),
|
2021-12-21 11:29:59 -08:00
|
|
|
}
|
|
|
|
|
|
2022-01-12 17:35:48 -08:00
|
|
|
impl Value {
|
|
|
|
|
pub fn name(&self) -> &str {
|
|
|
|
|
use Value::*;
|
|
|
|
|
match self {
|
|
|
|
|
Array(_) => "array",
|
|
|
|
|
Float(_) => "float",
|
|
|
|
|
Int(_) => "int",
|
|
|
|
|
Str(_) => "str",
|
|
|
|
|
Quote(_) => "quote",
|
|
|
|
|
ObjPtr(_) => "object",
|
2022-01-12 18:00:11 -08:00
|
|
|
BuiltinFn(_) => "builtin function",
|
2022-01-12 17:35:48 -08:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-12 21:49:41 -08:00
|
|
|
|
|
|
|
|
pub fn is_truthy(&self) -> bool {
|
|
|
|
|
use Value::*;
|
|
|
|
|
match self {
|
|
|
|
|
Array(a) => a.len() > 0,
|
|
|
|
|
Float(f) => *f != 0.0,
|
|
|
|
|
Int(i) => *i != 0,
|
|
|
|
|
Str(s) => s.len() > 0,
|
|
|
|
|
Quote(_) => true,
|
|
|
|
|
ObjPtr(_) => true,
|
|
|
|
|
BuiltinFn(_) => true,
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-12 17:35:48 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for Value {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
use Value::*;
|
|
|
|
|
match self {
|
|
|
|
|
Array(_) => write!(fmt, "[array]"),
|
|
|
|
|
Float(f) => write!(fmt, "{}", f),
|
|
|
|
|
Int(i) => write!(fmt, "{}", i),
|
|
|
|
|
Str(s) => write!(fmt, "{}", s),
|
|
|
|
|
Quote(q) => write!(fmt, "[quoted value #{}]", q.index()),
|
|
|
|
|
ObjPtr(o) => write!(fmt, "[object #{}]", o.slot()),
|
2022-01-12 18:00:11 -08:00
|
|
|
BuiltinFn(b) => write!(fmt, "[{:?}]", b),
|
2022-01-12 17:35:48 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-12 21:49:41 -08:00
|
|
|
/// The intent of a builtin function once the function proper returns.
|
|
|
|
|
///
|
|
|
|
|
/// When a builtin function itself returns, it may want to remain on the call
|
|
|
|
|
/// stack, or do some kind of cleanup before it's finished. So, a function may
|
|
|
|
|
/// pre-empt itself with the understanding that it will eventually return.
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
|
pub enum BuiltinExit {
|
|
|
|
|
/// Pop this builtin off the call stack, and then call the given quote.
|
|
|
|
|
Call(Quote),
|
|
|
|
|
|
|
|
|
|
/// Continue with the assumption that some other function has been pushed to
|
|
|
|
|
/// the call stack, updating the reentry for this function call to the
|
|
|
|
|
/// specified constant.
|
|
|
|
|
Resume(usize),
|
|
|
|
|
|
|
|
|
|
/// Pops this builtin off the call stack, returning like normal.
|
|
|
|
|
Return,
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-12 18:00:11 -08:00
|
|
|
#[derive(Clone)]
|
2022-01-12 21:49:41 -08:00
|
|
|
pub struct BuiltinFn(Rc<BuiltinFnPtr>);
|
|
|
|
|
|
|
|
|
|
impl BuiltinFn {
|
|
|
|
|
pub fn new(fun: Rc<BuiltinFnPtr>) -> Self {
|
|
|
|
|
BuiltinFn(fun)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn call(&self, machine: &mut Machine, reentry: usize) -> Result<BuiltinExit> {
|
|
|
|
|
(self.0)(machine, reentry)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//pub type BuiltinFnPtr = dyn Fn(&mut Machine, usize) -> Result<BuiltinExit>;
|
|
|
|
|
pub type BuiltinFnPtr = fn(&mut Machine, usize) -> Result<BuiltinExit>;
|
2022-01-12 18:00:11 -08:00
|
|
|
|
|
|
|
|
impl Debug for BuiltinFn {
|
|
|
|
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
|
write!(
|
|
|
|
|
fmt,
|
|
|
|
|
"builtin function at {:#x}",
|
|
|
|
|
(&self.0 as *const _ as usize)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-12 17:35:48 -08:00
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Obj
|
|
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2021-12-21 11:29:59 -08:00
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub struct ObjPtr {
|
2022-01-07 20:30:55 -08:00
|
|
|
arena: Weak<RefCell<Arena>>,
|
2021-12-21 11:29:59 -08:00
|
|
|
slot: usize,
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-12 17:35:48 -08:00
|
|
|
impl ObjPtr {
|
|
|
|
|
pub fn slot(&self) -> usize {
|
|
|
|
|
self.slot
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-07 20:30:55 -08:00
|
|
|
#[derive(Debug, Default)]
|
2021-12-21 11:29:59 -08:00
|
|
|
pub struct Obj {
|
2022-01-07 20:30:55 -08:00
|
|
|
vtable: VTable,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Obj {
|
|
|
|
|
pub fn new(vtable: VTable) -> Self {
|
|
|
|
|
Self { vtable }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn vtable(&self) -> &VTable {
|
|
|
|
|
&self.vtable
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn vtable_mut(&mut self) -> &mut VTable {
|
|
|
|
|
&mut self.vtable
|
|
|
|
|
}
|
2021-12-21 11:29:59 -08:00
|
|
|
}
|
|
|
|
|
|
2022-01-12 17:35:48 -08:00
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Arena
|
|
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2021-12-21 11:29:59 -08:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct Arena {
|
|
|
|
|
slots: Vec<SlotRange>,
|
|
|
|
|
slots_dirty: bool,
|
2022-01-07 20:30:55 -08:00
|
|
|
objects: BTreeMap<usize, Obj>,
|
2021-12-21 11:29:59 -08:00
|
|
|
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;
|
2022-01-07 20:30:55 -08:00
|
|
|
// Make sure to indicate that slots available are not dirty.
|
|
|
|
|
self.slots_dirty = false;
|
2021-12-21 11:29:59 -08:00
|
|
|
}
|
|
|
|
|
|
2022-01-07 20:30:55 -08:00
|
|
|
/// Creates a shared pointer for an Obj value, using the next available slot.
|
|
|
|
|
///
|
|
|
|
|
/// If no slot is available, None is returned.
|
|
|
|
|
pub fn alloc_obj(self_rc: &Rc<RefCell<Self>>, obj: Obj) -> Option<ObjPtr> {
|
2021-12-21 11:29:59 -08:00
|
|
|
use SlotRange::*;
|
|
|
|
|
|
2022-01-07 20:30:55 -08:00
|
|
|
let mut self_mut = self_rc
|
|
|
|
|
.try_borrow_mut()
|
|
|
|
|
.expect("could not get arena mutably from Rc pointer");
|
2021-12-21 11:29:59 -08:00
|
|
|
// Compress if necessary
|
2022-01-07 20:30:55 -08:00
|
|
|
if self_mut.slots_dirty {
|
2021-12-21 11:29:59 -08:00
|
|
|
self_mut.compress_slots();
|
|
|
|
|
}
|
2022-01-07 20:30:55 -08:00
|
|
|
if self_mut
|
2021-12-21 11:29:59 -08:00
|
|
|
.max_size
|
2022-01-07 20:30:55 -08:00
|
|
|
.map(|max_size| self_mut.objects.len() >= max_size)
|
2021-12-21 11:29:59 -08:00
|
|
|
.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 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
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-01-07 20:30:55 -08:00
|
|
|
let previous = self_mut.objects.insert(slot, obj);
|
|
|
|
|
assert!(
|
|
|
|
|
previous.is_none(),
|
|
|
|
|
"slot {} was allocated but is already in use",
|
|
|
|
|
slot
|
|
|
|
|
);
|
2021-12-21 11:29:59 -08:00
|
|
|
Some(ObjPtr {
|
2022-01-07 20:30:55 -08:00
|
|
|
arena: Rc::downgrade(self_rc),
|
2021-12-21 11:29:59 -08:00
|
|
|
slot,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn free_obj(&mut self, obj_ptr: ObjPtr) {
|
|
|
|
|
// Compress if necessary
|
2022-01-07 20:30:55 -08:00
|
|
|
if self.slots_dirty {
|
2021-12-21 11:29:59 -08:00
|
|
|
self.compress_slots();
|
|
|
|
|
}
|
2022-01-07 20:30:55 -08:00
|
|
|
let value = self.objects.remove(&obj_ptr.slot);
|
|
|
|
|
assert!(
|
|
|
|
|
value.is_some(),
|
|
|
|
|
"attempted to free object that was not tracked (slot {})",
|
|
|
|
|
obj_ptr.slot
|
|
|
|
|
);
|
2021-12-21 11:29:59 -08:00
|
|
|
self.slots
|
|
|
|
|
.push(SlotRange::Range(obj_ptr.slot, obj_ptr.slot));
|
|
|
|
|
self.slots_dirty = true; // not my problem
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-12 17:35:48 -08:00
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Slots
|
|
|
|
|
// /////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2021-12-21 11:29:59 -08:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
pub enum SlotRange {
|
2022-01-07 20:30:55 -08:00
|
|
|
/// A list of slots that are in a range, exclusive.
|
|
|
|
|
///
|
|
|
|
|
/// If the range start and end are equal, then it is a single opening.
|
2021-12-21 11:29:59 -08:00
|
|
|
Range(usize, usize),
|
2022-01-07 20:30:55 -08:00
|
|
|
/// Everything from here and onward is open.
|
2021-12-21 11:29:59 -08:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-01-07 20:30:55 -08:00
|
|
|
|
|
|
|
|
#[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)]),
|
|
|
|
|
(
|
|
|
|
|
vec![Range(0, 0), Range(1, 1), Range(2, 2)],
|
|
|
|
|
vec![Range(0, 2)],
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
vec![Range(0, 0), Range(1, 1), Range(2, 2), Open(3)],
|
|
|
|
|
vec![Open(0)],
|
|
|
|
|
),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_arena_obj_lifetime() {
|
|
|
|
|
let arena = Rc::new(RefCell::new(Arena::new(None)));
|
|
|
|
|
|
|
|
|
|
let p1 = Arena::alloc_obj(&arena, Obj::default()).unwrap();
|
|
|
|
|
assert_eq!(arena.borrow().slots, vec![SlotRange::Open(1)]);
|
|
|
|
|
let p2 = Arena::alloc_obj(&arena, Obj::default()).unwrap();
|
|
|
|
|
assert_eq!(arena.borrow().slots, vec![SlotRange::Open(2)]);
|
|
|
|
|
let p3 = Arena::alloc_obj(&arena, Obj::default()).unwrap();
|
|
|
|
|
assert_eq!(arena.borrow().slots, vec![SlotRange::Open(3)]);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let mut arena_mut = arena.borrow_mut();
|
|
|
|
|
arena_mut.free_obj(p2);
|
|
|
|
|
arena_mut.compress_slots();
|
|
|
|
|
assert_eq!(
|
|
|
|
|
arena_mut.slots,
|
|
|
|
|
vec![SlotRange::Range(1, 1), SlotRange::Open(3)]
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let p4 = Arena::alloc_obj(&arena, Obj::default()).unwrap();
|
|
|
|
|
assert_eq!(arena.borrow().slots, vec![SlotRange::Open(3)]);
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
let mut arena_mut = arena.borrow_mut();
|
|
|
|
|
arena_mut.free_obj(p1);
|
|
|
|
|
arena_mut.free_obj(p3);
|
|
|
|
|
arena_mut.free_obj(p4);
|
|
|
|
|
arena_mut.compress_slots();
|
|
|
|
|
assert_eq!(arena_mut.slots, vec![SlotRange::Open(0)],);
|
|
|
|
|
}
|
|
|
|
|
}
|