Skip to content

Commit

Permalink
Merge pull request #25 from olebedev/oleg/add-cursors
Browse files Browse the repository at this point in the history
feat: add `CursorMut`
  • Loading branch information
kyren authored Apr 22, 2024
2 parents 83c137e + f4b9a7f commit 24b87fe
Show file tree
Hide file tree
Showing 2 changed files with 482 additions and 11 deletions.
248 changes: 237 additions & 11 deletions src/linked_hash_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,40 @@ where
}
}
}

// Returns the `CursorMut` over the _guard_ node.
fn cursor_mut(&mut self) -> CursorMut<K, V, S> {
unsafe { ensure_guard_node(&mut self.values) };
CursorMut {
cur: self.values.as_ptr(),
hash_builder: &self.hash_builder,
free: &mut self.free,
values: &mut self.values,
table: &mut self.table,
}
}

/// 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> {
let mut c = self.cursor_mut();
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> {
let mut c = self.cursor_mut();
c.move_prev();
c
}
}

impl<K, V, S> LinkedHashMap<K, V, S>
Expand Down Expand Up @@ -680,7 +714,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 +789,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 +804,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 +863,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 +1028,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 +1060,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 +1121,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 +1230,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 +1322,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 +1346,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 +1408,27 @@ 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 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 +1762,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

0 comments on commit 24b87fe

Please sign in to comment.