Skip to content

Commit

Permalink
Implement HashSet using hashbrown::raw
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed Aug 25, 2023
1 parent 752cd30 commit 9f618c3
Show file tree
Hide file tree
Showing 9 changed files with 550 additions and 375 deletions.
12 changes: 4 additions & 8 deletions crates/rune-macros/src/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -717,19 +717,17 @@ where
impl #from_value for #ty {
fn from_value(value: Value) -> #vm_result<Self> {
let value = #vm_try!(#path(value));
let value = #vm_try!(value.take());
let value = #vm_try!(#shared::take(value));
#vm_result::Ok(value)
}
}

impl #unsafe_to_ref for #ty {
type Guard = #raw_ref;

unsafe fn unsafe_to_ref<'a>(
value: #value,
) -> #vm_result<(&'a Self, Self::Guard)> {
unsafe fn unsafe_to_ref<'a>(value: #value) -> #vm_result<(&'a Self, Self::Guard)> {
let value = #vm_try!(#path(value));
let value = #vm_try!(value.into_ref());
let value = #vm_try!(#shared::into_ref(value));
let (value, guard) = #ref_::into_raw(value);
#vm_result::Ok((value.as_ref(), guard))
}
Expand All @@ -738,9 +736,7 @@ where
impl #unsafe_to_mut for #ty {
type Guard = #raw_mut;

unsafe fn unsafe_to_mut<'a>(
value: #value,
) -> #vm_result<(&'a mut Self, Self::Guard)> {
unsafe fn unsafe_to_mut<'a>(value: #value) -> #vm_result<(&'a mut Self, Self::Guard)> {
let value = #vm_try!(#path(value));
let value = #vm_try!(#shared::into_mut(value));
let (mut value, guard) = #mut_::into_raw(value);
Expand Down
3 changes: 3 additions & 0 deletions crates/rune/src/hashbrown.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod table;
pub(crate) use self::table::{IterRef, Table};
pub(crate) use ::hashbrown::raw::RawIter;
271 changes: 271 additions & 0 deletions crates/rune/src/hashbrown/table.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
use core::hash::BuildHasher;
use core::iter;
use core::marker::PhantomData;
use core::mem;
use core::ptr;

use hashbrown::raw::{RawIter, RawTable};
use std::collections::hash_map::{DefaultHasher, RandomState};

use crate::runtime::{Hasher, ProtocolCaller, RawRef, Ref, Value, VmError, VmResult};

#[derive(Clone)]
pub(crate) struct Table<V> {
table: RawTable<(Value, V)>,
state: RandomState,
}

impl<V> Table<V> {
#[inline(always)]
pub(crate) fn new() -> Self {
Self {
table: RawTable::new(),
state: RandomState::new(),
}
}

#[inline(always)]
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self {
table: RawTable::with_capacity(capacity),
state: RandomState::new(),
}
}

#[inline(always)]
pub(crate) fn len(&self) -> usize {
self.table.len()
}

#[inline(always)]
pub(crate) fn capacity(&self) -> usize {
self.table.capacity()
}

#[inline(always)]
pub(crate) fn is_empty(&self) -> bool {
self.table.is_empty()
}

#[inline(always)]
pub(crate) fn insert_with<P>(
&mut self,
key: Value,
value: V,
caller: &mut P,
) -> VmResult<Option<V>>
where
P: ?Sized + ProtocolCaller,
{
let hash = vm_try!(hash(&self.state, &key, caller));

let result =
match self
.table
.find_or_find_insert_slot2(caller, hash, eq(&key), hasher(&self.state))
{
Ok(result) => result,
Err(error) => return VmResult::Err(error),
};

let existing = match result {
Ok(bucket) => Some(mem::replace(unsafe { &mut bucket.as_mut().1 }, value)),
Err(slot) => {
unsafe {
self.table.insert_in_slot(hash, slot, (key, value));
}
None
}
};

VmResult::Ok(existing)
}

pub(crate) fn get<P>(&self, key: &Value, caller: &mut P) -> VmResult<Option<&(Value, V)>>
where
P: ?Sized + ProtocolCaller,
{
if self.table.is_empty() {
return VmResult::Ok(None);
}

let hash = vm_try!(hash(&self.state, key, caller));
VmResult::Ok(vm_try!(self.table.get2(caller, hash, eq(key))))
}

#[inline(always)]
pub(crate) fn remove_with<P>(&mut self, key: &Value, caller: &mut P) -> VmResult<Option<V>>
where
P: ?Sized + ProtocolCaller,
{
let hash = vm_try!(hash(&self.state, key, caller));

match self.table.remove_entry2(caller, hash, eq(key)) {
Ok(value) => VmResult::Ok(value.map(|(_, value)| value)),
Err(error) => VmResult::Err(error),
}
}

#[inline(always)]
pub(crate) fn clear(&mut self) {
self.table.clear()
}

pub(crate) fn iter(&self) -> Iter<'_, V> {
// SAFETY: lifetime is held by returned iterator.
let iter = unsafe { self.table.iter() };

Iter {
iter,
_marker: PhantomData,
}
}

#[inline(always)]
pub(crate) fn iter_ref(this: Ref<Self>) -> IterRef<V> {
let (this, _guard) = Ref::into_raw(this);
// SAFETY: Table will be alive and a reference to it held for as long as
// `RawRef` is alive.
let iter = unsafe { this.as_ref().table.iter() };
IterRef { iter, _guard }
}

#[inline(always)]
pub(crate) unsafe fn iter_ref_raw(this: ptr::NonNull<Table<V>>) -> RawIter<(Value, V)> {
this.as_ref().table.iter()
}

#[inline(always)]
pub(crate) fn keys_ref(this: Ref<Self>) -> KeysRef<V> {
let (this, _guard) = Ref::into_raw(this);
// SAFETY: Table will be alive and a reference to it held for as long as
// `RawRef` is alive.
let iter = unsafe { this.as_ref().table.iter() };
KeysRef { iter, _guard }
}

#[inline(always)]
pub(crate) fn values_ref(this: Ref<Self>) -> ValuesRef<V> {
let (this, _guard) = Ref::into_raw(this);
// SAFETY: Table will be alive and a reference to it held for as long as
// `RawRef` is alive.
let iter = unsafe { this.as_ref().table.iter() };
ValuesRef { iter, _guard }
}
}

pub(crate) struct Iter<'a, V> {
iter: RawIter<(Value, V)>,
_marker: PhantomData<&'a V>,
}

impl<'a, V> iter::Iterator for Iter<'a, V> {
type Item = &'a (Value, V);

#[inline]
fn next(&mut self) -> Option<Self::Item> {
// SAFETY: we're still holding onto the `RawRef` guard.
unsafe { Some(self.iter.next()?.as_ref().clone()) }
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}

pub(crate) struct IterRef<V> {
iter: RawIter<(Value, V)>,
_guard: RawRef,
}

impl<V> iter::Iterator for IterRef<V>
where
V: Clone,
{
type Item = (Value, V);

#[inline]
fn next(&mut self) -> Option<Self::Item> {
// SAFETY: we're still holding onto the `RawRef` guard.
unsafe { Some(self.iter.next()?.as_ref().clone()) }
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}

pub(crate) struct KeysRef<V> {
iter: RawIter<(Value, V)>,
_guard: RawRef,
}

impl<V> iter::Iterator for KeysRef<V> {
type Item = Value;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
// SAFETY: we're still holding onto the `RawRef` guard.
unsafe { Some(self.iter.next()?.as_ref().0.clone()) }
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}

pub(crate) struct ValuesRef<V> {
iter: RawIter<(Value, V)>,
_guard: RawRef,
}

impl<V> iter::Iterator for ValuesRef<V>
where
V: Clone,
{
type Item = V;

#[inline]
fn next(&mut self) -> Option<Self::Item> {
// SAFETY: we're still holding onto the `RawRef` guard.
unsafe { Some(self.iter.next()?.as_ref().1.clone()) }
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}

/// Convenience function to hash a value.
fn hash<S>(state: &S, value: &Value, caller: &mut impl ProtocolCaller) -> VmResult<u64>
where
S: BuildHasher<Hasher = DefaultHasher>,
{
let mut hasher = Hasher::new_with(state);
vm_try!(value.hash_with(&mut hasher, caller));
VmResult::Ok(hasher.finish())
}

/// Construct a hasher for a value in the table.
fn hasher<P, V, S>(state: &S) -> impl Fn(&mut P, &(Value, V)) -> Result<u64, VmError> + '_
where
P: ?Sized + ProtocolCaller,
S: BuildHasher<Hasher = DefaultHasher>,
{
move |caller, (key, _): &(Value, V)| hash(state, key, caller).into_result()
}

/// Construct an equality function for a value in the table that will compare an
/// entry with the current key.
fn eq<P, V>(key: &Value) -> impl Fn(&mut P, &(Value, V)) -> Result<bool, VmError> + '_
where
P: ?Sized + ProtocolCaller,
{
move |caller: &mut P, (other, _): &(Value, V)| -> Result<bool, VmError> {
key.eq_with(other, caller).into_result()
}
}
3 changes: 3 additions & 0 deletions crates/rune/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,9 @@ cfg_workspace! {
pub mod workspace;
}

#[cfg(feature = "std")]
mod hashbrown;

// Macros used internally and re-exported.
pub(crate) use rune_macros::__internal_impl_any;

Expand Down
Loading

0 comments on commit 9f618c3

Please sign in to comment.