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

Implement Iter, IterMut, and Drain #1

Merged
merged 11 commits into from
Jul 24, 2023
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
6 changes: 1 addition & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[Also available on crates.io.](https://crates.io/crates/wrapped_slab)

Very simple Rust library useful when you want stronger type guarantees than Slab's `usize` keys. Generates `TSlab(Slab<T>)` that accepts `TKey` instead of `usize`. `TVacantEntry(VacantEntry<T>)` is also generated along the same lines.
Very simple Rust library useful when you want stronger type guarantees than Slab's `usize` keys. Generates `TSlab(Slab<T>)` that accepts `TKey` instead of `usize`. `TVacantEntry(VacantEntry<T>)` is also generated along the same lines. This should be a drop-in replacement for `Slab<T>`, provided all the keys are changed from `usize` to `TKey`.

## Example

Expand All @@ -23,7 +23,3 @@ fn main() {
// See wrapped_slab/tests/ for more examples
}
```

## Warning

This is a very early version, however the aim is to just translate the Slab API 1-1. Currently the only things missing are Iter, IterMut, drain, and retain.
5 changes: 5 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
Version 0.2.0 (2023-07-24)
==========================

- Implement Iter, IterMut, and Drain.

Version 0.1.1 (2023-07-19)
==========================

Expand Down
6 changes: 3 additions & 3 deletions wrapped_slab/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wrapped_slab"
version = "0.1.1"
version = "0.2.0"
edition = "2021"
authors = ["Louis Wyborn <louiswyborn@gmail.com>"]
rust-version = "1.62"
Expand All @@ -14,7 +14,7 @@ readme = "README.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
wrapped_slab_derive = {version="0.1.0", path="../wrapped_slab_derive"}
wrapped_slab_derive = { version = "0.2.0", path = "../wrapped_slab_derive" }

# Used as the backing store of all the elements.
slab = { version = "0.4" }
slab = { version = "0.4" }
6 changes: 6 additions & 0 deletions wrapped_slab/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
pub use wrapped_slab_derive::WrappedSlab;

#[derive(Clone)]
pub enum Entry<T> {
Vacant(usize),
Occupied(T),
}

#[doc(hidden)]
pub use slab;
84 changes: 68 additions & 16 deletions wrapped_slab/tests/basic.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use wrapped_slab::WrappedSlab;

#[derive(WrappedSlab)]
#[derive(WrappedSlab, PartialEq, Debug)]
struct TestUnitStruct(String);

#[test]
Expand All @@ -18,15 +18,9 @@ fn test_unit_struct() {
assert_eq!(val.0, "testing");

assert_eq!(slab.len(), 0);

let next_entry: TestUnitStructVacantEntry = slab.vacant_entry();
let next_key: TestUnitStructKey = next_entry.key();
let next_entry_ref: &mut TestUnitStruct =
next_entry.insert(TestUnitStruct(format!("{next_key:?}")));
assert_eq!(next_entry_ref.0, format!("{next_key:?}"))
}

#[derive(WrappedSlab)]
#[derive(WrappedSlab, PartialEq, Debug)]
struct TestStruct {
field1: String,
}
Expand All @@ -48,18 +42,12 @@ fn test_struct() {
assert_eq!(val.field1, "testing");

assert_eq!(slab.len(), 0);

let next_entry: TestStructVacantEntry = slab.vacant_entry();
let next_key: TestStructKey = next_entry.key();
let next_entry_ref: &mut TestStruct = next_entry.insert(TestStruct {
field1: format!("{next_key:?}"),
});
assert_eq!(next_entry_ref.field1, format!("{next_key:?}"))
}

#[derive(WrappedSlab, PartialEq, Debug)]
enum TestEnum {
VariantOne(String),
VariantTwo,
}

#[test]
Expand All @@ -77,6 +65,12 @@ fn test_enum() {
assert_eq!(val, TestEnum::VariantOne("testing".into()));

assert_eq!(slab.len(), 0);
}

#[test]
fn test_vacant_entry() {
let mut slab = TestEnumSlab::default();
slab.insert(TestEnum::VariantOne("testing".into()));

let next_entry: TestEnumVacantEntry = slab.vacant_entry();
let next_key: TestEnumKey = next_entry.key();
Expand All @@ -85,5 +79,63 @@ fn test_enum() {
assert_eq!(
next_entry_ref,
&mut TestEnum::VariantOne(format!("{next_key:?}"))
)
);
}

#[test]
fn test_iter() {
let mut slab = TestEnumSlab::default();
slab.insert(TestEnum::VariantOne("testing".into()));

let mut iter = slab.iter_mut();
let (idx, s) = iter.next().unwrap();
assert_eq!(idx, TestEnumKey(0));
assert_eq!(s, &mut TestEnum::VariantOne("testing".to_string()));
*s = TestEnum::VariantTwo;
assert_eq!(iter.next(), None);

let mut iter = slab.iter();
let (idx, s) = iter.next().unwrap();
assert_eq!(idx, TestEnumKey(0));
assert_eq!(s, &TestEnum::VariantTwo);
assert_eq!(iter.next(), None);

let mut iter = slab.into_iter();
let (idx, s) = iter.next().unwrap();
assert_eq!(idx, TestEnumKey(0));
assert_eq!(s, TestEnum::VariantTwo);
assert_eq!(iter.next(), None);
}

#[test]
fn test_drain() {
let mut slab = TestEnumSlab::with_capacity(32);
slab.reserve(64);
assert_eq!(slab.capacity(), 64);

slab.insert(TestEnum::VariantTwo);

let mut drain = slab.drain();
assert_eq!(drain.len(), 1);
let s = drain.next().unwrap();
assert_eq!(s, TestEnum::VariantTwo);
assert_eq!(drain.len(), 0);
drop(drain);

assert_eq!(slab.len(), 0);
}

#[test]
fn test_index() {
let mut slab = TestEnumSlab::with_capacity(32);
slab.reserve(64);
assert_eq!(slab.capacity(), 64);

slab.insert(TestEnum::VariantTwo);

let s = &slab[TestEnumKey(0)];
assert_eq!(s, &TestEnum::VariantTwo);

let s = &mut slab[TestEnumKey(0)];
assert_eq!(s, &mut TestEnum::VariantTwo);
}
6 changes: 3 additions & 3 deletions wrapped_slab_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wrapped_slab_derive"
version = "0.1.1"
version = "0.2.0"
edition = "2021"
authors = ["Louis Wyborn <louiswyborn@gmail.com>"]
rust-version = "1.62"
Expand All @@ -21,10 +21,10 @@ syn = { version = "1.0", features = ["full"] }
quote = { version = "1.0" }

# Necessary for syn and quote
proc-macro2 = { version = "1.0"}
proc-macro2 = { version = "1.0" }

# Better error handling in proc macros, avoids using panic!
proc-macro-error = { version = "1.0" }

[lib]
proc-macro = true
proc-macro = true
144 changes: 144 additions & 0 deletions wrapped_slab_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@ pub fn wrapped_slab_derive(input: proc_macro::TokenStream) -> proc_macro::TokenS
let slab_name = format_ident!("{element_name}Slab");
let vacant_entry_name = format_ident!("{element_name}VacantEntry");
let key_name = format_ident!("{element_name}Key");
let iter_name = format_ident!("{element_name}Iter");
let iter_mut_name = format_ident!("{element_name}IterMut");
let into_iter_name = format_ident!("{element_name}IntoIter");

let expanded = quote! {
#[derive(Default)]
#element_vis struct #slab_name(wrapped_slab::slab::Slab<#element_name>);

#[derive(Debug)]
#element_vis struct #vacant_entry_name<'a>(wrapped_slab::slab::VacantEntry<'a, #element_name>);

#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
Expand All @@ -35,6 +39,134 @@ pub fn wrapped_slab_derive(input: proc_macro::TokenStream) -> proc_macro::TokenS
}
}

#[derive(Debug)]
#element_vis struct #iter_name<'a>(wrapped_slab::slab::Iter<'a, #element_name>);

#[derive(Debug)]
#element_vis struct #iter_mut_name<'a>(wrapped_slab::slab::IterMut<'a, #element_name>);

#[derive(Debug)]
#element_vis struct #into_iter_name(wrapped_slab::slab::IntoIter<#element_name>);

impl<'a> Iterator for #iter_name<'a> {
type Item = (#key_name, &'a #element_name);

fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(key, val)| (#key_name(key), val))
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}

impl DoubleEndedIterator for #iter_name<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|(key, val)| (#key_name(key), val))
}
}

impl ExactSizeIterator for #iter_name<'_> {
fn len(&self) -> usize {
self.0.len()
}
}

impl core::iter::FusedIterator for #iter_name<'_> {}

impl<'a> Iterator for #iter_mut_name<'a> {
type Item = (#key_name, &'a mut #element_name);

fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(key, val)| (#key_name(key), val))
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}

impl DoubleEndedIterator for #iter_mut_name<'_> {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|(key, val)| (#key_name(key), val))
}
}

impl ExactSizeIterator for #iter_mut_name<'_> {
fn len(&self) -> usize {
self.0.len()
}
}

impl core::iter::FusedIterator for #iter_mut_name<'_> {}

impl Iterator for #into_iter_name {
type Item = (#key_name, #element_name);

fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(key, val)| (#key_name(key), val))
}

fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}

impl DoubleEndedIterator for #into_iter_name {
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|(key, val)| (#key_name(key), val))
}
}

impl ExactSizeIterator for #into_iter_name {
fn len(&self) -> usize {
self.0.len()
}
}

impl core::iter::FusedIterator for #into_iter_name {}

impl<'a> IntoIterator for &'a #slab_name {
type Item = (#key_name, &'a #element_name);
type IntoIter = #iter_name<'a>;

fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}

impl<'a> IntoIterator for &'a mut #slab_name {
type Item = (#key_name, &'a mut #element_name);
type IntoIter = #iter_mut_name<'a>;

fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}

impl IntoIterator for #slab_name {
type Item = (#key_name, #element_name);
type IntoIter = #into_iter_name;

fn into_iter(self) -> Self::IntoIter {
#into_iter_name(self.0.into_iter())
}
}

impl std::ops::Index<#key_name> for #slab_name {
type Output = #element_name;

fn index(&self, key: #key_name) -> &#element_name {
self.0.index(key.0)
}
}

impl std::ops::IndexMut<#key_name> for #slab_name {
fn index_mut(&mut self, key: #key_name) -> &mut #element_name {
self.0.index_mut(key.0)
}
}

impl #slab_name {
pub const fn new() -> Self {
Self(wrapped_slab::slab::Slab::new())
Expand Down Expand Up @@ -123,6 +255,18 @@ pub fn wrapped_slab_derive(input: proc_macro::TokenStream) -> proc_macro::TokenS
pub fn contains(&self, key: #key_name) -> bool {
self.0.contains(key.0)
}

pub fn iter(&self) -> #iter_name<'_> {
#iter_name(self.0.iter())
}

pub fn iter_mut(&mut self) -> #iter_mut_name<'_> {
#iter_mut_name(self.0.iter_mut())
}

pub fn drain(&mut self) -> wrapped_slab::slab::Drain<'_, #element_name> {
self.0.drain()
}
}
};

Expand Down