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

feat: add CursorMut #25

Merged
merged 13 commits into from
Apr 22, 2024
251 changes: 240 additions & 11 deletions src/linked_hash_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,42 @@ where
}
}
}

/// Returns the `CursorMut` over the front node.
///
/// Note: The `CursorMut` is pointing to the _guard_ node in an empty `LinkedHashMap` and
/// will always return `None` as its current element, regardless of any move in any
/// direction.
pub fn cursor_front_mut(&mut self) -> CursorMut<K, V, S> {
unsafe { ensure_guard_node(&mut self.values) };
let mut c = CursorMut {
cur: self.values.as_ptr(),
hash_builder: &self.hash_builder,
free: &mut self.free,
values: &mut self.values,
table: &mut self.table,
};
c.move_next();
c
}

/// Returns the `CursorMut` over the back node.
///
/// Note: The `CursorMut` is pointing to the _guard_ node in an empty `LinkedHashMap` and
/// will always return `None` as its current element, regardless of any move in any
/// direction.
pub fn cursor_back_mut(&mut self) -> CursorMut<K, V, S> {
unsafe { ensure_guard_node(&mut self.values) };
let mut c = CursorMut {
cur: self.values.as_ptr(),
hash_builder: &self.hash_builder,
free: &mut self.free,
values: &mut self.values,
table: &mut self.table,
};
c.move_prev();
c
}
Copy link
Owner

Choose a reason for hiding this comment

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

We can still have a cursor_mut method that's private that returns the guard node, to save duplicated code.

}

impl<K, V, S> LinkedHashMap<K, V, S>
Expand Down Expand Up @@ -680,7 +716,7 @@ where
}

pub enum Entry<'a, K, V, S> {
Occupied(OccupiedEntry<'a, K, V>),
Occupied(OccupiedEntry<'a, K, V, S>),
Vacant(VacantEntry<'a, K, V, S>),
}

Expand Down Expand Up @@ -755,12 +791,12 @@ impl<'a, K, V, S> Entry<'a, K, V, S> {
}
}

pub struct OccupiedEntry<'a, K, V> {
pub struct OccupiedEntry<'a, K, V, S> {
key: K,
raw_entry: RawOccupiedEntryMut<'a, K, V>,
raw_entry: RawOccupiedEntryMut<'a, K, V, S>,
}

impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for OccupiedEntry<'_, K, V> {
impl<K: fmt::Debug, V: fmt::Debug, S> fmt::Debug for OccupiedEntry<'_, K, V, S> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OccupiedEntry")
Expand All @@ -770,7 +806,7 @@ impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for OccupiedEntry<'_, K, V> {
}
}

impl<'a, K, V> OccupiedEntry<'a, K, V> {
impl<'a, K, V, S> OccupiedEntry<'a, K, V, S> {
#[inline]
pub fn key(&self) -> &K {
self.raw_entry.key()
Expand Down Expand Up @@ -829,6 +865,16 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> {
self.replace_entry(value)
}

/// Returns a `CursorMut` over the current entry.
#[inline]
pub fn cursor_mut(self) -> CursorMut<'a, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
self.raw_entry.cursor_mut()
}

/// Replaces the entry's key with the key provided to `LinkedHashMap::entry`, and replaces the
/// entry's value with the given `value` parameter.
///
Expand Down Expand Up @@ -984,6 +1030,7 @@ where

match entry {
Ok(occupied) => RawEntryMut::Occupied(RawOccupiedEntryMut {
hash_builder: &self.map.hash_builder,
free: &mut self.map.free,
values: &mut self.map.values,
entry: occupied,
Expand Down Expand Up @@ -1015,7 +1062,7 @@ where
}

pub enum RawEntryMut<'a, K, V, S> {
Occupied(RawOccupiedEntryMut<'a, K, V>),
Occupied(RawOccupiedEntryMut<'a, K, V, S>),
Vacant(RawVacantEntryMut<'a, K, V, S>),
}

Expand Down Expand Up @@ -1076,13 +1123,14 @@ impl<'a, K, V, S> RawEntryMut<'a, K, V, S> {
}
}

pub struct RawOccupiedEntryMut<'a, K, V> {
pub struct RawOccupiedEntryMut<'a, K, V, S> {
hash_builder: &'a S,
free: &'a mut Option<NonNull<Node<K, V>>>,
values: &'a mut Option<NonNull<Node<K, V>>>,
entry: hash_table::OccupiedEntry<'a, NonNull<Node<K, V>>>,
}

impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> {
impl<'a, K, V, S> RawOccupiedEntryMut<'a, K, V, S> {
#[inline]
pub fn key(&self) -> &K {
self.get_key_value().0
Expand Down Expand Up @@ -1184,6 +1232,22 @@ impl<'a, K, V> RawOccupiedEntryMut<'a, K, V> {
let node = self.entry.remove().0;
unsafe { remove_node(self.free, node) }
}

/// Returns a `CursorMut` over the current entry.
#[inline]
pub fn cursor_mut(self) -> CursorMut<'a, K, V, S>
where
K: Eq + Hash,
S: BuildHasher,
{
CursorMut {
cur: self.entry.get().as_ptr(),
hash_builder: self.hash_builder,
free: self.free,
values: self.values,
table: self.entry.into_table(),
}
}
}

pub struct RawVacantEntryMut<'a, K, V, S> {
Expand Down Expand Up @@ -1260,7 +1324,7 @@ impl<K: fmt::Debug, V: fmt::Debug, S> fmt::Debug for RawEntryMut<'_, K, V, S> {
}
}

impl<K: fmt::Debug, V: fmt::Debug> fmt::Debug for RawOccupiedEntryMut<'_, K, V> {
impl<K: fmt::Debug, V: fmt::Debug, S> fmt::Debug for RawOccupiedEntryMut<'_, K, V, S> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RawOccupiedEntryMut")
Expand All @@ -1284,17 +1348,19 @@ impl<K, V, S> fmt::Debug for RawEntryBuilder<'_, K, V, S> {
}
}

unsafe impl<'a, K, V> Send for RawOccupiedEntryMut<'a, K, V>
unsafe impl<'a, K, V, S> Send for RawOccupiedEntryMut<'a, K, V, S>
where
K: Send,
V: Send,
S: Send,
{
}

unsafe impl<'a, K, V> Sync for RawOccupiedEntryMut<'a, K, V>
unsafe impl<'a, K, V, S> Sync for RawOccupiedEntryMut<'a, K, V, S>
where
K: Sync,
V: Sync,
S: Sync,
{
}

Expand Down Expand Up @@ -1344,6 +1410,28 @@ pub struct Drain<'a, K, V> {
marker: PhantomData<(K, V, &'a LinkedHashMap<K, V>)>,
}

/// The `CursorMut` struct and its implementation provide the basic mutable Cursor API for Linked
/// lists as proposed in
/// [here](https://github.com/rust-lang/rfcs/blob/master/text/2570-linked-list-cursors.md), with
/// several exceptions:
///
/// - It behaves similarly to Rust's Iterators, returning `None` when the end of the list is
/// reached. A _guard_ node is positioned between the head and tail of the linked list to
/// facilitate this. If the cursor is over this guard node, `None` is returned, signaling the end
/// or start of the list. From this position, the cursor can move in either direction as the
/// linked list is circular, with the guard node connecting the two ends.
/// - The current implementation does not include an `index` method, as it does not track the index
/// of its elements. It operates by providing elements as key-value tuples, allowing the value to be
/// modified via a mutable reference while the key could not be changed.
///
Copy link
Owner

Choose a reason for hiding this comment

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

Grammar fix: "could not" -> "cannot"

Or, just something a little simpler: "It provides access to each map entry as a tuple of (&K, &mut V)."

pub struct CursorMut<'a, K, V, S> {
cur: *mut Node<K, V>,
hash_builder: &'a S,
free: &'a mut Option<NonNull<Node<K, V>>>,
values: &'a mut Option<NonNull<Node<K, V>>>,
table: &'a mut hashbrown::HashTable<NonNull<Node<K, V>>>,
}

impl<K, V> IterMut<'_, K, V> {
#[inline]
pub(crate) fn iter(&self) -> Iter<'_, K, V> {
Expand Down Expand Up @@ -1677,6 +1765,147 @@ impl<'a, K, V> Drop for Drain<'a, K, V> {
}
}

impl<'a, K, V, S> CursorMut<'a, K, V, S> {
/// Returns an `Option` of the current element in the list, provided it is not the
/// _guard_ node, and `None` overwise.
#[inline]
pub fn current(&mut self) -> Option<(&K, &mut V)> {
unsafe {
let at = NonNull::new_unchecked(self.cur);
self.peek(at)
}
}

/// Retrieves the next element in the list (moving towards the end).
#[inline]
pub fn peek_next(&mut self) -> Option<(&K, &mut V)> {
unsafe {
let at = (*self.cur).links.value.next;
self.peek(at)
}
}

/// Retrieves the previous element in the list (moving towards the front).
#[inline]
pub fn peek_prev(&mut self) -> Option<(&K, &mut V)> {
unsafe {
let at = (*self.cur).links.value.prev;
self.peek(at)
}
}

// Retrieves the element without advancing current position to it.
#[inline]
fn peek(&mut self, at: NonNull<Node<K, V>>) -> Option<(&K, &mut V)> {
if let Some(values) = self.values {
unsafe {
let node = at.as_ptr();
if node == values.as_ptr() {
None
} else {
let entry = (*node).entry_mut();
Some((&entry.0, &mut entry.1))
}
}
} else {
None
}
}

/// Updates the pointer to the current element to the next element in the
/// list (that is, moving towards the end).
#[inline]
pub fn move_next(&mut self) {
let at = unsafe { (*self.cur).links.value.next };
self.muv(at);
}

/// Updates the pointer to the current element to the previous element in the
/// list (that is, moving towards the front).
#[inline]
pub fn move_prev(&mut self) {
let at = unsafe { (*self.cur).links.value.prev };
self.muv(at);
}

// Updates the pointer to the current element to the one returned by the at closure function.
#[inline]
fn muv(&mut self, at: NonNull<Node<K, V>>) {
self.cur = at.as_ptr();
}

/// Inserts the provided key and value before the current element. It checks if an entry
/// with the given key exists and, if so, replaces its value with the provided `key`
/// parameter. The key is not updated; this matters for types that can be `==` without
/// being identical.
///
/// If the entry doesn't exist, it creates a new one. If a value has been updated, the
/// function returns the *old* value wrapped with `Some` and `None` otherwise.
#[inline]
pub fn insert_before(&mut self, key: K, value: V) -> Option<V>
where
K: Eq + Hash,
S: BuildHasher,
{
let before = unsafe { NonNull::new_unchecked(self.cur) };
self.insert(key, value, before)
}

/// Inserts the provided key and value after the current element. It checks if an entry
/// with the given key exists and, if so, replaces its value with the provided `key`
/// parameter. The key is not updated; this matters for types that can be `==` without
/// being identical.
///
/// If the entry doesn't exist, it creates a new one. If a value has been updated, the
/// function returns the *old* value wrapped with `Some` and `None` otherwise.
#[inline]
pub fn insert_after(&mut self, key: K, value: V) -> Option<V>
where
K: Eq + Hash,
S: BuildHasher,
{
let before = unsafe { (*self.cur).links.value.next };
self.insert(key, value, before)
}

// Inserts an element immediately before the given `before` node.
#[inline]
fn insert(&mut self, key: K, value: V, before: NonNull<Node<K, V>>) -> Option<V>
where
K: Eq + Hash,
S: BuildHasher,
{
unsafe {
let hash = hash_key(self.hash_builder, &key);
let i_entry = self
.table
.find_entry(hash, |o| (*o).as_ref().key_ref().eq(&key));

match i_entry {
Ok(occupied) => {
let mut node = *occupied.into_mut();
let pv = mem::replace(&mut node.as_mut().entry_mut().1, value);
if node != before {
detach_node(node);
attach_before(node, before);
}
Some(pv)
}
Err(_) => {
let mut new_node = allocate_node(self.free);
new_node.as_mut().put_entry((key, value));
attach_before(new_node, before);
let hash_builder = self.hash_builder;
self.table.insert_unique(hash, new_node, move |k| {
hash_key(hash_builder, (*k).as_ref().key_ref())
});
None
}
}
}
}
}

pub struct Keys<'a, K, V> {
inner: Iter<'a, K, V>,
}
Expand Down
Loading