use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::hash::Hash;
use std::rc::Rc;
type ScopeRef<K, V> = Rc<ScopeData<K, V>>;
type Slot<K, V> = (ScopeRef<K, V>, usize);
struct Entry<K, V> {
key: K,
value: V,
shadowed: Option<Slot<K, V>>,
}
struct ScopeData<K, V> {
entries: RefCell<Vec<Entry<K, V>>>,
parent: Option<ScopeRef<K, V>>,
depth: u32,
active: Cell<bool>,
}
impl<K, V> Drop for ScopeData<K, V> {
fn drop(&mut self) {
debug_assert!(!self.active.get(), "Cannot destroy an active scope");
}
}
pub struct ScopePtr<K, V>(Option<ScopeRef<K, V>>);
impl<K, V> ScopePtr<K, V> {
fn new(scope: ScopeRef<K, V>) -> Self {
ScopePtr(Some(scope))
}
pub fn is_null(&self) -> bool {
self.0.is_none()
}
pub fn reset(&mut self) {
self.0 = None;
}
fn get(&self) -> Option<&ScopeRef<K, V>> {
self.0.as_ref()
}
}
impl<K, V> Clone for ScopePtr<K, V> {
fn clone(&self) -> Self {
ScopePtr(self.0.clone())
}
}
impl<K, V> Default for ScopePtr<K, V> {
fn default() -> Self {
ScopePtr(None)
}
}
impl<K, V> PartialEq for ScopePtr<K, V> {
fn eq(&self, other: &Self) -> bool {
match (&self.0, &other.0) {
(Some(a), Some(b)) => Rc::ptr_eq(a, b),
(None, None) => true,
_ => false,
}
}
}
pub struct Scope<'m, K: Eq + Hash + Copy, V: Clone> {
base: &'m PersistentScopedMap<K, V>,
scope: ScopeRef<K, V>,
}
impl<'m, K: Eq + Hash + Copy, V: Clone> Scope<'m, K, V> {
pub fn new(base: &'m PersistentScopedMap<K, V>) -> Self {
let parent = base.scope.borrow().clone();
let depth = parent.as_ref().map_or(0, |p| p.depth + 1);
let scope = Rc::new(ScopeData {
entries: RefCell::new(Vec::new()),
parent,
depth,
active: Cell::new(true),
});
*base.scope.borrow_mut() = Some(scope.clone());
Scope { base, scope }
}
pub fn depth(&self) -> u32 {
self.scope.depth
}
pub fn ptr(&self) -> ScopePtr<K, V> {
ScopePtr::new(self.scope.clone())
}
}
impl<'m, K: Eq + Hash + Copy, V: Clone> Drop for Scope<'m, K, V> {
fn drop(&mut self) {
self.base.pop_scope(&self.scope);
}
}
pub struct PersistentScopedMap<K, V> {
map: RefCell<HashMap<K, Slot<K, V>>>,
scope: RefCell<Option<ScopeRef<K, V>>>,
}
impl<K: Eq + Hash + Copy, V: Clone> Default for PersistentScopedMap<K, V> {
fn default() -> Self {
Self::new()
}
}
impl<K, V> Drop for PersistentScopedMap<K, V> {
fn drop(&mut self) {
debug_assert!(
self.scope.borrow().is_none(),
"Scopes remain when destructing PersistentScopedMap"
);
debug_assert!(
self.map.borrow().is_empty(),
"Elements remaining in map without scope!"
);
}
}
impl<K: Eq + Hash + Copy, V: Clone> PersistentScopedMap<K, V> {
pub fn new() -> Self {
PersistentScopedMap {
map: RefCell::new(HashMap::new()),
scope: RefCell::new(None),
}
}
pub fn current_scope(&self) -> ScopePtr<K, V> {
ScopePtr(self.scope.borrow().clone())
}
fn require_current(&self) -> ScopeRef<K, V> {
self.scope
.borrow()
.clone()
.expect("PersistentScopedMap has no current scope")
}
fn require_scope(ptr: &ScopePtr<K, V>) -> ScopeRef<K, V> {
ptr.get()
.cloned()
.expect("PersistentScopedMapScopePtr must not be null")
}
fn push_entry(&self, scope: &ScopeRef<K, V>, key: K, idx: usize) {
let prev = self.map.borrow_mut().insert(key, (scope.clone(), idx));
if let Some((ref prev_scope, _)) = prev {
debug_assert!(
prev_scope.depth < scope.depth,
"Can't insert values under existing names"
);
}
scope.entries.borrow_mut()[idx].shadowed = prev;
}
fn insert_new_node(
&self,
scope: &ScopeRef<K, V>,
key: K,
value: V,
) -> usize {
let idx = {
let mut entries = scope.entries.borrow_mut();
entries.push(Entry {
key,
value,
shadowed: None,
});
entries.len() - 1
};
self.push_entry(scope, key, idx);
idx
}
fn pop_entry(&self, scope: &ScopeRef<K, V>, key: K, idx: usize) {
let mut map = self.map.borrow_mut();
let current = map
.get(&key)
.cloned()
.expect("Asked to pop an empty scope value");
debug_assert!(
Rc::ptr_eq(¤t.0, scope) && current.1 == idx,
"Unexpected innermost value for key"
);
let shadowed = scope.entries.borrow()[idx].shadowed.clone();
match shadowed {
Some(s) => {
map.insert(key, s);
}
None => {
map.remove(&key);
}
}
}
fn pop_scope(&self, scope: &ScopeRef<K, V>) {
debug_assert!(
scope.active.get(),
"Attempting to pop an inactive scope"
);
{
let current = self.scope.borrow();
debug_assert!(
matches!(current.as_ref(), Some(c) if Rc::ptr_eq(c, scope)),
"Attempting to pop not current scope"
);
}
let len = scope.entries.borrow().len();
for idx in 0..len {
let key = scope.entries.borrow()[idx].key;
self.pop_entry(scope, key, idx);
}
scope.active.set(false);
*self.scope.borrow_mut() = scope.parent.clone();
}
fn push_child_scope(&self, scope: &ScopeRef<K, V>) {
debug_assert!(
!scope.active.get(),
"Attempting to push an active scope"
);
{
let current = self.scope.borrow();
let is_child_of_current = match (&scope.parent, current.as_ref()) {
(Some(p), Some(c)) => Rc::ptr_eq(p, c),
(None, None) => true,
_ => false,
};
debug_assert!(
is_child_of_current,
"Attempting to push a scope that isn't a child of the \
current one"
);
}
let len = scope.entries.borrow().len();
for idx in 0..len {
let key = scope.entries.borrow()[idx].key;
self.push_entry(scope, key, idx);
}
scope.active.set(true);
*self.scope.borrow_mut() = Some(scope.clone());
}
fn try_emplace_into_scope_impl(
&self,
scope: &ScopeRef<K, V>,
key: K,
value: V,
) -> (usize, bool) {
debug_assert!(
self.require_current().active.get(),
"Attempting to modify an inactive scope"
);
let existing = self.map.borrow().get(&key).cloned();
if let Some((ref existing_scope, existing_idx)) = existing {
if existing_scope.depth == scope.depth {
return (existing_idx, false);
}
}
let idx = self.insert_new_node(scope, key, value);
(idx, true)
}
pub fn try_emplace_into_scope(
&self,
scope: &ScopePtr<K, V>,
key: K,
value: V,
) -> bool {
let scope_rc = Self::require_scope(scope);
self.try_emplace_into_scope_impl(&scope_rc, key, value).1
}
pub fn try_emplace(&self, key: K, value: V) -> bool {
let scope_rc = self.require_current();
self.try_emplace_into_scope_impl(&scope_rc, key, value).1
}
pub fn put_in_scope(&self, scope: &ScopePtr<K, V>, key: K, value: V) {
let scope_rc = Self::require_scope(scope);
let (idx, inserted) = self.try_emplace_into_scope_impl(
&scope_rc,
key,
value.clone(),
);
if !inserted {
scope_rc.entries.borrow_mut()[idx].value = value;
}
}
pub fn put(&self, key: K, value: V) {
let current = self.current_scope();
self.put_in_scope(¤t, key, value);
}
pub fn count(&self, key: &K) -> u32 {
u32::from(self.map.borrow().contains_key(key))
}
pub fn lookup(&self, key: &K) -> Option<V> {
self.find(key)
}
pub fn find(&self, key: &K) -> Option<V> {
let map = self.map.borrow();
let (scope, idx) = map.get(key)?;
let value = scope.entries.borrow()[*idx].value.clone();
Some(value)
}
pub fn find_with_depth(&self, key: &K) -> Option<(V, u32)> {
let map = self.map.borrow();
let (scope, idx) = map.get(key)?;
let value = scope.entries.borrow()[*idx].value.clone();
Some((value, scope.depth))
}
pub fn find_in_current_scope(&self, key: &K) -> Option<V> {
let map = self.map.borrow();
let (scope, idx) = map.get(key)?;
let current_depth = self.require_current().depth;
if scope.depth != current_depth {
return None;
}
let value = scope.entries.borrow()[*idx].value.clone();
Some(value)
}
pub fn activate_scope(&self, new_scope_ptr: &ScopePtr<K, V>) {
let new_scope = Self::require_scope(new_scope_ptr);
let mut activate_list: Vec<ScopeRef<K, V>> = Vec::new();
let mut active_parent = Some(new_scope);
while let Some(candidate) = active_parent {
if candidate.active.get() {
active_parent = Some(candidate);
break;
}
active_parent = candidate.parent.clone();
activate_list.push(candidate);
}
loop {
let current = self.scope.borrow().clone();
let reached = match (¤t, &active_parent) {
(Some(c), Some(a)) => Rc::ptr_eq(c, a),
(None, None) => true,
_ => false,
};
if reached {
break;
}
let current = current.expect("ran out of scopes to pop");
self.pop_scope(¤t);
}
for scope in activate_list.into_iter().rev() {
self.push_child_scope(&scope);
}
}
pub fn flatten(&self) -> HashMap<K, V> {
let map = self.map.borrow();
let mut result = HashMap::with_capacity(map.len());
for (key, (scope, idx)) in map.iter() {
result.insert(*key, scope.entries.borrow()[*idx].value.clone());
}
result
}
pub fn keys_by_scope(&self) -> Vec<Vec<K>> {
let current = self.require_current();
let size = (current.depth + 1) as usize;
let mut result: Vec<Vec<K>> = vec![Vec::new(); size];
for (key, (scope, _)) in self.map.borrow().iter() {
debug_assert!(scope.depth <= current.depth, "Node at bad depth");
result[size - scope.depth as usize - 1].push(*key);
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn smoke_test() {
let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
let scope = Scope::new(&table);
table.try_emplace("foo", "bar");
assert_eq!(table.lookup(&"foo"), Some("bar"));
drop(scope);
}
#[test]
fn nesting() {
let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
let outer = Scope::new(&table);
table.try_emplace("key", "outer");
assert_eq!(table.lookup(&"key"), Some("outer"));
{
let _inner = Scope::new(&table);
table.try_emplace("key", "inner");
assert_eq!(table.lookup(&"key"), Some("inner"));
}
assert_eq!(table.lookup(&"key"), Some("outer"));
drop(outer);
}
#[test]
fn overwrite() {
let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
let outer = Scope::new(&table);
table.put("key", "foo");
assert_eq!(table.lookup(&"key"), Some("foo"));
table.put("key", "outer");
assert_eq!(table.lookup(&"key"), Some("outer"));
{
let _inner = Scope::new(&table);
table.put("key", "foo");
assert_eq!(table.lookup(&"key"), Some("foo"));
table.put("key", "inner");
assert_eq!(table.lookup(&"key"), Some("inner"));
}
assert_eq!(table.lookup(&"key"), Some("outer"));
drop(outer);
}
#[test]
fn flatten_test() {
let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
let outer = Scope::new(&table);
table.try_emplace("out", "outer");
{
let _inner = Scope::new(&table);
table.try_emplace("in", "inner");
let map = table.flatten();
assert_eq!(map.len(), 2);
assert_eq!(map.get("out"), Some(&"outer"));
assert_eq!(map.get("in"), Some(&"inner"));
}
drop(outer);
}
#[test]
fn get_keys_by_scope() {
let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
let outer = Scope::new(&table);
table.try_emplace("out", "outer");
table.try_emplace("in", "trash");
{
let _inner = Scope::new(&table);
table.try_emplace("in", "inner");
let scopes = table.keys_by_scope();
assert_eq!(scopes.len(), 2);
assert_eq!(scopes[0].len(), 1);
assert_eq!(scopes[1].len(), 1);
assert_eq!(scopes[0][0], "in");
assert_eq!(scopes[1][0], "out");
}
drop(outer);
}
#[test]
fn put_test() {
let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
let outer = Scope::new(&table);
table.try_emplace("foo", "true");
{
let _inner = Scope::new(&table);
assert_eq!(table.lookup(&"foo"), Some("true"));
table.try_emplace("foo", "false");
assert_eq!(table.lookup(&"foo"), Some("false"));
table.try_emplace("foo", "true");
assert_eq!(table.lookup(&"foo"), Some("false"));
table.put("foo", "false");
assert_eq!(table.lookup(&"foo"), Some("false"));
}
assert_eq!(table.lookup(&"foo"), Some("true"));
drop(outer);
}
#[test]
fn find_in_current_scope() {
let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
let outer = Scope::new(&table);
table.try_emplace("foo", "true");
{
let _inner = Scope::new(&table);
table.try_emplace("bar", "true");
assert_eq!(table.find_in_current_scope(&"foo"), None);
assert_eq!(table.find_in_current_scope(&"bar"), Some("true"));
}
assert_eq!(table.find_in_current_scope(&"foo"), Some("true"));
drop(outer);
}
#[test]
fn activate() {
let mut ptr: ScopePtr<&str, &str>;
let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
{
let a = Scope::new(&table);
table.try_emplace("A", "a");
table.try_emplace("key", "keyA");
let b = Scope::new(&table);
table.try_emplace("B", "b");
table.try_emplace("key", "keyB");
{
let c = Scope::new(&table);
table.try_emplace("C", "c");
table.try_emplace("key", "keyC");
let d = Scope::new(&table);
table.try_emplace("D", "d");
table.try_emplace("key", "keyD");
ptr = d.ptr();
{
let map = table.flatten();
assert_eq!(map.len(), 5);
assert_eq!(map.get("A"), Some(&"a"));
assert_eq!(map.get("B"), Some(&"b"));
assert_eq!(map.get("C"), Some(&"c"));
assert_eq!(map.get("D"), Some(&"d"));
assert_eq!(map.get("key"), Some(&"keyD"));
}
drop(d);
drop(c);
}
{
let map = table.flatten();
assert_eq!(map.len(), 3);
assert_eq!(map.get("A"), Some(&"a"));
assert_eq!(map.get("B"), Some(&"b"));
assert_eq!(map.get("key"), Some(&"keyB"));
}
let e = Scope::new(&table);
table.try_emplace("E", "e");
table.try_emplace("key", "keyE");
let f = Scope::new(&table);
table.try_emplace("F", "f");
table.try_emplace("key", "keyF");
let g = Scope::new(&table);
table.try_emplace("G", "g");
table.try_emplace("key", "keyG");
{
let map = table.flatten();
assert_eq!(map.len(), 6);
assert_eq!(map.get("A"), Some(&"a"));
assert_eq!(map.get("B"), Some(&"b"));
assert_eq!(map.get("E"), Some(&"e"));
assert_eq!(map.get("F"), Some(&"f"));
assert_eq!(map.get("G"), Some(&"g"));
assert_eq!(map.get("key"), Some(&"keyG"));
}
table.activate_scope(&e.ptr());
{
let map = table.flatten();
assert_eq!(map.len(), 4);
assert_eq!(map.get("A"), Some(&"a"));
assert_eq!(map.get("B"), Some(&"b"));
assert_eq!(map.get("E"), Some(&"e"));
assert_eq!(map.get("key"), Some(&"keyE"));
}
table.activate_scope(&f.ptr());
{
let map = table.flatten();
assert_eq!(map.len(), 5);
assert_eq!(map.get("A"), Some(&"a"));
assert_eq!(map.get("B"), Some(&"b"));
assert_eq!(map.get("E"), Some(&"e"));
assert_eq!(map.get("F"), Some(&"f"));
assert_eq!(map.get("key"), Some(&"keyF"));
}
table.activate_scope(&g.ptr());
{
let map = table.flatten();
assert_eq!(map.len(), 6);
assert_eq!(map.get("A"), Some(&"a"));
assert_eq!(map.get("B"), Some(&"b"));
assert_eq!(map.get("E"), Some(&"e"));
assert_eq!(map.get("F"), Some(&"f"));
assert_eq!(map.get("G"), Some(&"g"));
assert_eq!(map.get("key"), Some(&"keyG"));
}
table.activate_scope(&ptr);
{
let map = table.flatten();
assert_eq!(map.len(), 5);
assert_eq!(map.get("A"), Some(&"a"));
assert_eq!(map.get("B"), Some(&"b"));
assert_eq!(map.get("C"), Some(&"c"));
assert_eq!(map.get("D"), Some(&"d"));
assert_eq!(map.get("key"), Some(&"keyD"));
}
table.activate_scope(&g.ptr());
{
let map = table.flatten();
assert_eq!(map.len(), 6);
assert_eq!(map.get("A"), Some(&"a"));
assert_eq!(map.get("B"), Some(&"b"));
assert_eq!(map.get("E"), Some(&"e"));
assert_eq!(map.get("F"), Some(&"f"));
assert_eq!(map.get("G"), Some(&"g"));
assert_eq!(map.get("key"), Some(&"keyG"));
}
drop(g);
drop(f);
drop(e);
drop(b);
drop(a);
}
ptr.reset();
}
#[test]
fn count_and_find() {
let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
let outer = Scope::new(&table);
assert_eq!(table.count(&"foo"), 0);
table.try_emplace("foo", "true");
assert_eq!(table.count(&"foo"), 1);
assert_eq!(table.find(&"foo"), Some("true"));
assert_eq!(table.find_with_depth(&"foo"), Some(("true", 0)));
{
let _inner = Scope::new(&table);
table.try_emplace("foo", "inner");
assert_eq!(table.find_with_depth(&"foo"), Some(("inner", 1)));
}
assert_eq!(table.find_with_depth(&"foo"), Some(("true", 0)));
drop(outer);
}
#[test]
fn drop_last_ptr_of_popped_scope_does_not_disturb_siblings() {
let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
let outer = Scope::new(&table);
table.try_emplace("A", "a");
let mut b_ptr: ScopePtr<&str, &str>;
{
let b = Scope::new(&table);
table.try_emplace("B", "b");
b_ptr = b.ptr();
}
assert_eq!(table.lookup(&"B"), None);
b_ptr.reset();
let c_ptr;
{
let c = Scope::new(&table);
table.try_emplace("C", "c");
c_ptr = c.ptr();
}
table.activate_scope(&c_ptr);
assert_eq!(table.lookup(&"A"), Some("a"));
assert_eq!(table.lookup(&"C"), Some("c"));
assert_eq!(table.lookup(&"B"), None);
table.activate_scope(&outer.ptr());
drop(outer);
}
}