Skip to content
This repository was archived by the owner on Sep 9, 2025. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add interning arena
  • Loading branch information
Hendrik van Antwerpen
Hendrik van Antwerpen committed Mar 20, 2023
commit 5861201a1f26c4f34ef63d2f452f6cc995e30651
64 changes: 63 additions & 1 deletion stack-graphs/src/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
//! [`StackGraph`]: ../graph/struct.StackGraph.html

use std::cell::Cell;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::fmt::Debug;
use std::hash::Hash;
use std::hash::Hasher;
Expand Down Expand Up @@ -195,7 +197,7 @@ impl<T> Arena<T> {
pub fn get(&self, handle: Handle<T>) -> &T {
unsafe { std::mem::transmute(&self.items[handle.as_usize()]) }
}
///

/// Dereferences a handle to an instance owned by this arena, returning a mutable reference to
/// it.
pub fn get_mut(&mut self, handle: Handle<T>) -> &mut T {
Expand All @@ -222,6 +224,66 @@ impl<T> Arena<T> {
}
}

/// An interning arena allocation. Equal handles means equal elements.
pub struct InterningArena<T>
where
T: Clone + Eq + Hash,
{
arena: Arena<T>,
index: HashMap<T, Handle<T>>,
}

impl<T> InterningArena<T>
where
T: Clone + Eq + Hash,
{
/// Creates a new interning arena.
pub fn new() -> Self {
Self {
arena: Arena::new(),
index: HashMap::new(),
}
}

/// Adds a new instance to this arena, returning a stable handle to it.
///
/// Note that we deduplicate instances of `T`. If you add two instances that
/// have the same content, you will get identical handles for each one.
pub fn add(&mut self, item: T) -> Handle<T> {
match self.index.entry(item) {
Entry::Occupied(entry) => *entry.get(),
Entry::Vacant(entry) => {
let handle = self.arena.add(entry.key().clone());
entry.insert(handle);
handle
}
}
}

/// Dereferences a handle to an instance owned by this arena, returning a reference to it.
pub fn get(&self, handle: Handle<T>) -> &T {
self.arena.get(handle)
}

/// Dereferences a handle to an instance owned by this arena, returning a mutable reference to
/// it.
pub fn get_mut(&mut self, handle: Handle<T>) -> &mut T {
self.arena.get_mut(handle)
}

/// Returns an iterator of all of the handles in this arena. (Note that this iterator does not
/// retain a reference to the arena!)
pub fn iter_handles(&self) -> impl Iterator<Item = Handle<T>> {
self.arena.iter_handles()
}

/// Returns the number of instances stored in this arena.
#[inline(always)]
pub fn len(&self) -> usize {
self.arena.len()
}
}

//-------------------------------------------------------------------------------------------------
// Supplemental arenas

Expand Down
15 changes: 15 additions & 0 deletions stack-graphs/tests/it/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use stack_graphs::arena::Arena;
use stack_graphs::arena::Deque;
use stack_graphs::arena::DequeArena;
use stack_graphs::arena::InterningArena;
use stack_graphs::arena::List;
use stack_graphs::arena::ListArena;
use stack_graphs::arena::ReversibleList;
Expand Down Expand Up @@ -229,3 +230,17 @@ fn can_compare_deques() {
deque10.ensure_backwards(&mut arena);
assert_eq!(deque1.cmp(&mut arena, deque10), Ordering::Less);
}

#[test]
fn can_allocate_in_interning_arena() {
let mut arena = InterningArena::new();
let hello1 = arena.add("hello".to_string());
let hello2 = arena.add("hello".to_string());
let there = arena.add("there".to_string());
assert_eq!(hello1, hello2);
assert_ne!(hello1, there);
assert_ne!(hello2, there);
assert_eq!(arena.get(hello1), arena.get(hello2));
assert_ne!(arena.get(hello1), arena.get(there));
assert_ne!(arena.get(hello2), arena.get(there));
}