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 iterators #4

Closed
wants to merge 2 commits into from
Closed
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
6 changes: 0 additions & 6 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ on:

pull_request:
branches: [ "main" ]
Copy link

Choose a reason for hiding this comment

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

Consider re-adding the 'paths' specification to limit workflow runs to relevant changes, preventing unnecessary executions.

paths:
- src/
- tests/
- Cargo.toml
- Cargo.lock
- .github/

env:
CARGO_TERM_COLOR: always
Expand Down
103 changes: 103 additions & 0 deletions src/iter.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use std::hash::BuildHasher;

enum IterState<'a, K, V> {
Start,
Shard(
usize,
hashbrown::raw::RawIter<(K, V)>,
crate::shard::ShardReader<'a, K, V>,
),
Finished,
}

pub struct Iter<'a, K, V, S> {
willothy marked this conversation as resolved.
Show resolved Hide resolved
map: crate::ShardMap<K, V, S>,
state: IterState<'a, K, V>,
}

impl<'a, K: 'static, V: 'static, S: BuildHasher> Iter<'a, K, V, S>
where
K: Eq + std::hash::Hash + 'static,
V: 'static,
{
pub fn new(map: crate::ShardMap<K, V, S>) -> Self {
Self {
map,
state: IterState::Start,
}
}
}

impl<'a, K: Clone + 'static, V: 'static, S: BuildHasher> Iterator for Iter<'a, K, V, S>
willothy marked this conversation as resolved.
Show resolved Hide resolved
where
K: Eq + std::hash::Hash + 'static,
V: 'static,
{
type Item = (&'a K, &'a V);

fn next(&mut self) -> Option<Self::Item> {
match &mut self.state {
IterState::Start => {
let map = self.map.shard_by_idx(0);

let reader: std::sync::RwLockReadGuard<'static, hashbrown::raw::RawTable<(K, V)>> =
unsafe { std::mem::transmute(map.read_sync()) };
willothy marked this conversation as resolved.
Show resolved Hide resolved
self.state = IterState::Shard(0, unsafe { reader.iter() }, reader);

self.next()
willothy marked this conversation as resolved.
Show resolved Hide resolved
}
IterState::Shard(idx, iter, _shard) => {
match iter
.next()
.map(|bucket| unsafe { bucket.as_ref() })
.map(|bucket| (&bucket.0, &bucket.1))
{
Some(value) => Some(value),
None => {
if *idx >= self.map.num_shards() - 1 {
self.state = IterState::Finished;
return None;
}

let map = self.map.shard_by_idx(*idx + 1);

let reader: std::sync::RwLockReadGuard<
'static,
hashbrown::raw::RawTable<(K, V)>,
> = unsafe { std::mem::transmute(map.read_sync()) };

self.state = IterState::Shard(*idx + 1, unsafe { reader.iter() }, reader);

self.next()
}
}
}
IterState::Finished => None,
}
}
}

#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_iter() {
let map = crate::ShardMap::new();
map.insert("foo", "bar").await;
map.insert("baz", "qux").await;
let mut iter = map.iter().collect::<Vec<_>>();
iter.sort_by(|a, b| a.0.cmp(b.0));

assert_eq!(iter[0], (&"baz", &"qux"));
assert_eq!(iter[1], (&"foo", &"bar"));

assert_eq!(iter.len(), 2);
}

#[tokio::test]
async fn test_iter_empty() {
let map = crate::ShardMap::<u32, u32>::new();

let mut iter = map.iter();
assert_eq!(iter.next(), None);
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
//!
//! See the documentation for each data structure for more information.

pub mod iter;
pub mod mapref;
mod shard;
mod shard_map;
Expand Down
8 changes: 8 additions & 0 deletions src/shard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ where
pub async fn read<'a>(&'a self) -> ShardReader<'a, K, V> {
Read::new(self).await
}

pub fn write_sync(&self) -> ShardWriter<K, V> {
self.data.write().unwrap()
}

pub fn read_sync(&self) -> ShardReader<K, V> {
self.data.read().unwrap()
}
}

impl<K, V> std::ops::Deref for Shard<K, V> {
Expand Down
16 changes: 15 additions & 1 deletion src/shard_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,14 +185,28 @@ where
}

#[inline]
fn shard(&self, key: &K) -> (&CachePadded<Shard<K, V>>, u64) {
pub(crate) fn shard(&self, key: &K) -> (&CachePadded<Shard<K, V>>, u64) {
let hash = self.hash_u64(key);

let shard_idx = self.shard_for_hash(hash as usize);

(unsafe { self.inner.shards.get_unchecked(shard_idx) }, hash)
}

#[inline]
pub(crate) fn shard_by_idx(&self, idx: usize) -> &CachePadded<Shard<K, V>> {
unsafe { self.inner.shards.get_unchecked(idx) }
}

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

pub fn iter(&self) -> crate::iter::Iter<K, V, S> {
crate::iter::Iter::new(self.clone())
}

/// Inserts a key-value pair into the map. If the key already exists, the value is updated and
/// the old value is returned.
///
Expand Down