Skip to main content

Module persistent_scoped_map

Module persistent_scoped_map 

Source
Expand description

Port of hermes::PersistentScopedMap (include/hermes/ADT/PersistentScopedMap.h): a scoped hash table similar to hermes::ScopedHashTable, but scopes can be retained and reactivated after they have been popped from the table.

The type ScopePtr, which in C++ is an intrusive reference-counting smart pointer, is used to retain ownership of a scope. The pointer can be used to reactivate the scope in the table using PersistentScopedMap::activate_scope.

Scopes can also be re-activated even if they are currently active but are not the current scope. Note however that if there are active scopes in the stack, in the end we must restore the state — the top-most scope in the stack must be active.

Example (mirrors the C++ doc comment):

use hermes_support::persistent_scoped_map::{PersistentScopedMap, Scope, ScopePtr};

let table: PersistentScopedMap<&str, &str> = PersistentScopedMap::new();
let mut ptr: ScopePtr<&str, &str> = ScopePtr::default();
let a = Scope::new(&table);
let b = Scope::new(&table);
// At this point, A and B are active scopes in the table.
// A(active)->B(active)
// We can reactivate A:
table.activate_scope(&a.ptr());
// Now the state is the same as it was when A was active initially.
table.activate_scope(&b.ptr());
// The state has been restored to "normal".
{
    let c = Scope::new(&table);
    // A(active)->B(active)->C(active)
    // Save C for later.
    ptr = c.ptr();
}
// A(active)->B(active)
let d = Scope::new(&table);
// A(active)->B(active)->D(active).
// Activate C again.
table.activate_scope(&ptr);
// A(active)->B(active)->C(active)
// Restore normal state.
table.activate_scope(&d.ptr());
ptr.reset();
drop(d);
drop(b);
drop(a);

§Deviations from the C++ implementation

support is #![forbid(unsafe_code)], so this port cannot reproduce the C++ web of raw pointers and an intrusive, manually-maintained reference count (PersistentScopedMapScopeData::addRef/decRef). Instead:

  • A scope’s data (ScopeData<K, V>, the port of detail::PersistentScopedMapScopeData) is held behind an Rc. ScopePtr is Option<ScopeRef<K, V>>; Rc’s own strong count replaces the manual refCount_, and Clone/Drop on Rc replace addRef/decRef — there is nothing left to implement by hand.
  • The C++ Node intrusive linked lists (nextInScope_ links every node created in a scope; nextShadowed_ links a node to the same-key node it shadows in an ancestor scope) become, per scope, a Vec<Entry<K, V>> in insertion order (ScopeData::entries) plus a shadowed: Option<Slot<K, V>> field on Entry (Slot<K, V> = (Rc<ScopeData<K, V>>, usize)) that identifies the shadowed entry by (scope, index) instead of by raw pointer. The map from key to innermost definition (C++ map_: DenseMap<K, Node *>) becomes map: RefCell<HashMap<K, Slot<K, V>>>.
  • Because each key is inserted at most once per scope (try_emplace refuses a second insertion into the same scope), the order in which a scope’s own entries are popped/pushed does not affect observable behavior. This port walks entries forward (insertion order); the C++ walks head_/nextInScope_, which is reverse insertion order. Only the internal traversal order differs — not behavior.
  • lookup, find, find_with_depth, and find_in_current_scope return an owned Option<V> (hence the V: Clone bound) rather than C++’s V* / default-constructed V (for lookup). Interior mutability (RefCell) means we cannot hand back a reference into the map that outlives the call, so callers get a clone instead. count still returns u32 (0 or 1), matching DenseMap::count’s semantics for a unique key.
  • All PersistentScopedMap methods take &self: the map’s mutable state lives in RefCells so it can be shared behind a plain reference, which is what a scope-retaining API needs (a Scope borrows the map for its whole lifetime while ScopePtrs to older scopes may outlive it).

Every method below keeps the corresponding C++ comment (adapted) and the same assertions, expressed as debug_assert!/assert!.

Structs§

PersistentScopedMap
Scoped hash table similar to hermes::ScopedHashTable, but scopes can be retained and reactivated after they have been popped from the table. See the module documentation for the full example and for the deviations from the C++ implementation.
Scope
RAII for creating and popping a scope. Port of PersistentScopedMapScope.
ScopePtr
Smart pointer retaining ownership of a scope so it can be reactivated after it has been popped from the table. Port of PersistentScopedMapScopePtr; Rc’s automatic reference counting replaces the manual addRef/decRef pair.