Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more constructors and entry-APIs for la-arena #12931

Merged
merged 2 commits into from
Aug 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 11 additions & 1 deletion lib/la-arena/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::{
};

mod map;
pub use map::ArenaMap;
pub use map::{ArenaMap, Entry, OccupiedEntry, VacantEntry};

/// The raw index of a value in an arena.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand Down Expand Up @@ -208,6 +208,16 @@ impl<T> Arena<T> {
Arena { data: Vec::new() }
}

/// Create a new empty arena with specific capacity.
///
/// ```
/// let arena: la_arena::Arena<i32> = la_arena::Arena::with_capacity(42);
/// assert!(arena.is_empty());
/// ```
pub fn with_capacity(capacity: usize) -> Arena<T> {
Arena { data: Vec::with_capacity(capacity) }
}

/// Empties the arena, removing all contained values.
///
/// ```
Expand Down
119 changes: 118 additions & 1 deletion lib/la-arena/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ pub struct ArenaMap<IDX, V> {
}

impl<T, V> ArenaMap<Idx<T>, V> {
/// Creates a new empty map.
pub const fn new() -> Self {
Self { v: Vec::new(), _ty: PhantomData }
}

/// Create a new empty map with specific capacity.
pub fn with_capacity(capacity: usize) -> Self {
Self { v: Vec::with_capacity(capacity), _ty: PhantomData }
}

/// Inserts a value associated with a given arena index into the map.
pub fn insert(&mut self, idx: Idx<T>, t: V) {
let idx = Self::to_idx(idx);
Expand Down Expand Up @@ -46,6 +56,16 @@ impl<T, V> ArenaMap<Idx<T>, V> {
self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
}

/// Gets the given key's corresponding entry in the map for in-place manipulation.
pub fn entry(&mut self, idx: Idx<T>) -> Entry<'_, Idx<T>, V> {
let idx = Self::to_idx(idx);
self.v.resize_with((idx + 1).max(self.v.len()), || None);
match &mut self.v[idx] {
slot @ Some(_) => Entry::Occupied(OccupiedEntry { slot, _ty: PhantomData }),
slot @ None => Entry::Vacant(VacantEntry { slot, _ty: PhantomData }),
}
}

fn to_idx(idx: Idx<T>) -> usize {
u32::from(idx.into_raw()) as usize
}
Expand All @@ -70,6 +90,103 @@ impl<T, V> std::ops::IndexMut<Idx<V>> for ArenaMap<Idx<V>, T> {

impl<T, V> Default for ArenaMap<Idx<V>, T> {
fn default() -> Self {
ArenaMap { v: Vec::new(), _ty: PhantomData }
Self::new()
}
}

/// A view into a single entry in a map, which may either be vacant or occupied.
///
/// This `enum` is constructed from the [`entry`] method on [`ArenaMap`].
///
/// [`entry`]: ArenaMap::entry
pub enum Entry<'a, IDX, V> {
/// A vacant entry.
Vacant(VacantEntry<'a, IDX, V>),
/// An occupied entry.
Occupied(OccupiedEntry<'a, IDX, V>),
}

impl<'a, IDX, V> Entry<'a, IDX, V> {
/// Ensures a value is in the entry by inserting the default if empty, and returns a mutable reference to
/// the value in the entry.
pub fn or_insert(self, default: V) -> &'a mut V {
match self {
Self::Vacant(ent) => ent.insert(default),
Self::Occupied(ent) => ent.into_mut(),
}
}

/// Ensures a value is in the entry by inserting the result of the default function if empty, and returns
/// a mutable reference to the value in the entry.
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
match self {
Self::Vacant(ent) => ent.insert(default()),
Self::Occupied(ent) => ent.into_mut(),
}
}

/// Provides in-place mutable access to an occupied entry before any potential inserts into the map.
pub fn and_modify<F: FnOnce(&mut V)>(mut self, f: F) -> Self {
if let Self::Occupied(ent) = &mut self {
f(ent.get_mut());
}
self
}
}

impl<'a, IDX, V> Entry<'a, IDX, V>
where
V: Default,
{
/// Ensures a value is in the entry by inserting the default value if empty, and returns a mutable reference
/// to the value in the entry.
pub fn or_default(self) -> &'a mut V {
self.or_insert_with(Default::default)
}
}

/// A view into an vacant entry in a [`ArenaMap`]. It is part of the [`Entry`] enum.
pub struct VacantEntry<'a, IDX, V> {
slot: &'a mut Option<V>,
_ty: PhantomData<IDX>,
}

impl<'a, IDX, V> VacantEntry<'a, IDX, V> {
/// Sets the value of the entry with the `VacantEntry`’s key, and returns a mutable reference to it.
pub fn insert(self, value: V) -> &'a mut V {
self.slot.insert(value)
}
}

/// A view into an occupied entry in a [`ArenaMap`]. It is part of the [`Entry`] enum.
pub struct OccupiedEntry<'a, IDX, V> {
slot: &'a mut Option<V>,
_ty: PhantomData<IDX>,
}

impl<'a, IDX, V> OccupiedEntry<'a, IDX, V> {
/// Gets a reference to the value in the entry.
pub fn get(&self) -> &V {
self.slot.as_ref().expect("Occupied")
}

/// Gets a mutable reference to the value in the entry.
pub fn get_mut(&mut self) -> &mut V {
self.slot.as_mut().expect("Occupied")
}

/// Converts the entry into a mutable reference to its value.
pub fn into_mut(self) -> &'a mut V {
self.slot.as_mut().expect("Occupied")
}

/// Sets the value of the entry with the `OccupiedEntry`’s key, and returns the entry’s old value.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, we already have a couple of smart quotes in the codebase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that's copied from std. Should I convert them to '?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to, I guess, since we already have others.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, we already have a couple of smart quotes in the codebase.

Hehe that was probably me, since I wrote the la-arena docs and I always use smart quotes out of habit :)

pub fn insert(&mut self, value: V) -> V {
self.slot.replace(value).expect("Occupied")
}

/// Takes the value of the entry out of the map, and returns it.
pub fn remove(self) -> V {
self.slot.take().expect("Occupied")
}
}