#[doc(hidden)]
pub mod deps {
pub use paste::paste;
pub use slab::Slab;
}
#[macro_export]
macro_rules! n_key_set {
{
$(#[$meta:meta])*
$vis:vis struct $mapname:ident $(<$($P:ident),*>)? for $V:ty
$( where [ $($constr:tt)+ ] )?
{
$($body:tt)+
}
} => {
n_key_set!{
$(#[$meta])*
$vis struct [$($($P),*)?] $mapname [$($($P),*)?] for $V
$( [ where $($constr)+ ] )?
{
$( $body )+
}
}
};
{
$(#[$meta:meta])*
$vis:vis struct [$($($G:tt)+)?] $mapname:ident [$($($P:tt)+)?] for $V:ty
$( [ where $($constr:tt)+ ])?
{
$( $(( $($flag:ident)+ ))? $key:ident : $KEY:ty $({ $($source:tt)+ })? ),+
$(,)?
}
} => {
$crate::n_key_set::deps::paste!{
$( #[$meta] )*
#[doc = concat!(
"A set of elements of type ", stringify!($V), " whose members can be accessed by multiple keys.",
"\n\nThe keys are:",
$( " * `", stringify!($key), "` (`",stringify!($KEY),"`)\n" ,
$(" (", stringify!($($flag)+), ")", )?
)+
"\
Each member has a value for *each* required key, and up to one value
for *each* optional key.
The set contains at most one member for any value of a given key.
# Requirements
Key types must have consistent `Hash` and `Eq` implementations, as
they will be used as keys in a `HashMap`.
If all keys are optional, then every element in this set
must have at least one non-None key.
An element must not change its keys over time through interior
mutability.
⚠️ If *any* of these rules is violated, the consequences are unspecified,
and could include panics or wrong answers (but not memory-unsafety).
# Limitations
This could be more efficient in space and time.
",
)]
$vis struct $mapname $(<$($G)*>)?
where $( $KEY : std::hash::Hash + Eq + Clone , )+ $($($constr)+)?
{
$([<$key _map>]: std::collections::HashMap<$KEY, usize> , )+
values: $crate::n_key_set::deps::Slab<$V>,
}
#[allow(dead_code)] impl $(<$($G)*>)? $mapname $(<$($P)*>)?
where $( $KEY : std::hash::Hash + Eq + Clone , )+ $($($constr)+)?
{
#[doc = concat!("Construct a new ", stringify!($mapname))]
$vis fn new() -> Self {
Self::with_capacity(0)
}
#[doc = concat!("Construct a new ", stringify!($mapname), " with a given capacity.")]
$vis fn with_capacity(n: usize) -> Self {
Self {
$([<$key _map>]: std::collections::HashMap::with_capacity(n),)*
values: $crate::n_key_set::deps::Slab::with_capacity(n),
}
}
$(
#[doc = concat!("Return a reference to the element whose `", stringify!($key), "` is `key`.")]
$vis fn [<by_ $key>] <BorrowAsKey_>(&self, key: &BorrowAsKey_) -> Option<&$V>
where $KEY : std::borrow::Borrow<BorrowAsKey_>,
BorrowAsKey_: std::hash::Hash + Eq + ?Sized
{
self.[<$key _map>].get(key).map(|idx| self.values.get(*idx).expect("inconsistent state"))
}
#[doc = concat!("Return a mutable reference to the element whose `", stringify!($key),
"` is `key`.")]
$vis unsafe fn [<by_ $key _mut>] <BorrowAsKey_>(
&mut self,
key: &BorrowAsKey_
) -> Option<&mut $V>
where $KEY : std::borrow::Borrow<BorrowAsKey_>,
BorrowAsKey_: std::hash::Hash + Eq + ?Sized
{
self.[<$key _map>]
.get(key)
.map(|idx| self.values.get_mut(*idx).expect("inconsistent state"))
}
#[doc = concat!("Return true if this set contains an element whose `", stringify!($key),
"` is `key`.")]
$vis fn [<contains_ $key>] <BorrowAsKey_>(&mut self, $key: &BorrowAsKey_) -> bool
where $KEY : std::borrow::Borrow<BorrowAsKey_>,
BorrowAsKey_: std::hash::Hash + Eq + ?Sized
{
self.[<$key _map>].get($key).is_some()
}
#[doc = concat!("Remove the element whose `", stringify!($key), "` is `key`")]
#[doc=stringify!($key)]
$vis fn [<remove_by_ $key>] <BorrowAsKey_>(&mut self, $key: &BorrowAsKey_) -> Option<$V>
where $KEY : std::borrow::Borrow<BorrowAsKey_>,
BorrowAsKey_: std::hash::Hash + Eq + ?Sized
{
self.[<$key _map>]
.get($key)
.copied()
.map(|old_idx| self.remove_at(old_idx).expect("inconsistent state"))
}
#[doc = concat!("Modify the element with the given value for `", stringify!($key),
" by applying `func` to it.")]
$vis fn [<modify_by_$key>] <BorrowAsKey_, F_>(
&mut self,
$key: &BorrowAsKey_,
func: F_) -> Vec<$V>
where
$KEY : std::borrow::Borrow<BorrowAsKey_>,
BorrowAsKey_: std::hash::Hash + Eq + ?Sized,
F_: FnOnce(&mut $V)
{
if let Some(idx) = self.[<$key _map>].get($key) {
self.modify_at(*idx, func)
} else {
Vec::new()
}
}
)+
$vis fn values(&self) -> impl Iterator<Item=&$V> + '_ {
self.values.iter().map(|(_, v)| v)
}
$vis fn into_values(self) -> impl Iterator<Item=$V> {
self.values.into_iter().map(|(_, v)| v)
}
$vis fn try_insert(&mut self, value: $V) -> Result<Vec<$V>, $crate::n_key_set::Error> {
if self.capacity() > 32 && self.len() < self.capacity() / 4 {
self.compact()
}
let mut replaced = Vec::new();
$(
replaced.extend(
$crate::n_key_set!( @access(value, ($($($flag)+)?) $key : $KEY $({$($source)+})?) )
.and_then(|key| self.[<remove_by_$key>](key))
);
)*
let new_idx = self.values.insert(value);
let value_ref = self.values.get(new_idx).expect("we just inserted this");
let mut some_key_found = false;
$(
$crate::n_key_set!( @access(value_ref, ($($($flag)+)?) $key : $KEY $({$($source)+})?) )
.map(|key| {
self.[<$key _map>].insert(key.to_owned(), new_idx);
some_key_found = true;
});
)*
if ! some_key_found {
self.values.remove(new_idx); return Err($crate::n_key_set::Error::NoKeys);
}
Ok(replaced)
}
$vis fn insert(&mut self, value: $V) -> Vec<$V> {
self.try_insert(value)
.expect("Tried to add a value with no key!")
}
$vis fn len(&self) -> usize {
self.values.len()
}
$vis fn is_empty(&self) -> bool {
self.values.len() == 0
}
$vis fn capacity(&self) -> usize {
self.values.capacity()
}
$vis fn retain<F>(&mut self, mut pred: F)
where F: FnMut(&$V) -> bool,
{
for idx in 0..self.values.capacity() {
if self.values.get(idx).map(&mut pred) == Some(false) {
self.remove_at(idx);
}
}
}
fn remove_at(&mut self, idx: usize) -> Option<$V> {
if let Some(removed) = self.values.try_remove(idx) {
$(
let $key = $crate::n_key_set!( @access(removed, ($($($flag)+)?) $key : $KEY $({$($source)+})?) );
if let Some($key) = $key {
let old_idx = self.[<$key _map>].remove($key);
assert_eq!(old_idx, Some(idx));
}
)*
Some(removed)
} else {
None
}
}
fn modify_at<F_>(&mut self, idx: usize, func: F_) -> Vec<$V>
where
F_: FnOnce(&mut $V)
{
let value = self.values.get_mut(idx).expect("invalid index");
$(
let [<orig_$key>] = $crate::n_key_set!( @access(value, ($($($flag)+)?) $key : $KEY $({$($source)+})?) )
.map(|elt| elt.to_owned()) ;
)+
func(value);
$(
let [<new_$key>] = $crate::n_key_set!( @access( value, ($($($flag)+)?) $key : $KEY $({$($source)+})?) ) ;
)+
let keys_changed = $(
[<orig_$key>].as_ref().map(std::borrow::Borrow::borrow) != [<new_$key>]
)||+ ;
if keys_changed {
let found_any_keys = $( [<new_$key>].is_some() )||+ ;
$(
if let Some(orig) = [<orig_ $key>] {
let removed = self.[<$key _map>].remove(&orig);
assert_eq!(removed, Some(idx));
}
)+
let removed = self.values.remove(idx);
if found_any_keys {
self.insert(removed)
} else {
vec![removed]
}
} else {
vec![]
}
}
fn compact(&mut self) {
let old_value = std::mem::replace(self, Self::with_capacity(self.len()));
for item in old_value.into_values() {
self.insert(item);
}
}
$vis fn check_invariants(&self) {
#![allow(noop_method_call)] use std::borrow::Borrow;
$(
for (k,idx) in self.[<$key _map>].iter() {
let val = self.values.get(*idx).expect("Dangling entry in hashmap.");
assert!(
Some((k).borrow()) ==
$crate::n_key_set!( @access(val, ($($($flag)+)?) $key : $KEY $({$($source)+})?) ),
"Inconsistent key between hashmap and value."
)
}
)+
for (idx, val) in self.values.iter() {
let mut found_any_key = false;
$(
if let Some(k) = $crate::n_key_set!( @access(val, ($($($flag)+)?) $key : $KEY $({$($source)+})?) ) {
found_any_key = true;
assert!(
self.[<$key _map>].get(k) == Some(&idx),
"Value not found at correct index"
)
}
stringify!($key);
)+
assert!(found_any_key, "Found a value with no keys.");
}
}
}
impl $(<$($G)*>)? Default for $mapname $(<$($P)*>)?
where $( $KEY : std::hash::Hash + Eq + Clone , )* $($($constr)+)?
{
fn default() -> Self {
$mapname::new()
}
}
impl $(<$($G)*>)? FromIterator<$V> for $mapname $(<$($P)*>)?
where $( $KEY : std::hash::Hash + Eq + Clone , )* $($($constr)+)?
{
fn from_iter<IntoIter_>(iter: IntoIter_) -> Self
where
IntoIter_: IntoIterator<Item = $V>
{
let iter = iter.into_iter();
let mut set = Self::with_capacity(iter.size_hint().0);
for value in iter {
set.insert(value);
}
set
}
}
}
};
{ @access($ex:expr, (Option) $key:ident : $t:ty ) } => {
$ex.key()
};
{ @access($ex:expr, () $key:ident : $t:ty) } => {
Some($ex.key())
};
{ @access($ex:expr, (Option) $key:ident : $t:ty { . $field:tt } ) } => {
$ex.$field.as_ref()
};
{ @access($ex:expr, () $key:ident : $t:ty { . $field:tt } ) } => {
Some(&$ex.$field)
};
{ @access($ex:expr, (Option) $key:ident : $t:ty { $func:ident () } ) } => {
$ex.$func()
};
{ @access($ex:expr, () $key:ident : $t:ty { $func:ident () } ) } => {
Some($ex.$func())
};
}
#[derive(Clone, Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("Tried to insert a value with no keys")]
NoKeys,
}
#[cfg(test)]
mod test {
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
n_key_set! {
#[derive(Clone, Debug)]
struct Tuple2Set<A,B> for (A,B) {
first: A { .0 },
second: B { .1 },
}
}
#[test]
fn basic() {
let mut set = Tuple2Set::new();
assert!(set.is_empty());
set.insert((0_u32, 99_u16));
assert_eq!(set.contains_first(&0), true);
assert_eq!(set.contains_second(&99), true);
assert_eq!(set.contains_first(&99), false);
assert_eq!(set.contains_second(&0), false);
assert_eq!(set.by_first(&0), Some(&(0, 99)));
assert_eq!(set.by_second(&99), Some(&(0, 99)));
assert_eq!(set.by_first(&99), None);
assert_eq!(set.by_second(&0), None);
assert_eq!(set.insert((12, 34)), vec![]);
assert_eq!(set.len(), 2);
assert!(set.capacity() >= 2);
assert_eq!(set.by_first(&0), Some(&(0, 99)));
assert_eq!(set.by_first(&12), Some(&(12, 34)));
assert_eq!(set.remove_by_second(&99), Some((0, 99)));
assert_eq!(set.len(), 1);
set.insert((34, 56));
set.insert((56, 78));
set.insert((78, 90));
assert_eq!(set.len(), 4);
assert_eq!(set.insert((12, 123)), vec![(12, 34)]);
let mut replaced = set.insert((12, 56));
replaced.sort();
assert_eq!(replaced, vec![(12, 123), (34, 56)]);
assert_eq!(set.len(), 3);
assert_eq!(set.is_empty(), false);
set.check_invariants();
let mut all_members: Vec<_> = set.values().collect();
all_members.sort();
assert_eq!(all_members, vec![&(12, 56), &(56, 78), &(78, 90)]);
let mut drained_members: Vec<_> = set.into_values().collect();
drained_members.sort();
assert_eq!(drained_members, vec![(12, 56), (56, 78), (78, 90)]);
}
#[test]
fn retain_and_compact() {
let mut set: Tuple2Set<String, String> = (1..=1000)
.map(|idx| (format!("A={}", idx), format!("B={}", idx)))
.collect();
assert_eq!(set.len(), 1000);
let cap_orig = set.capacity();
assert!(cap_orig >= set.len());
set.retain(|(a, _)| a.len() <= 3);
assert_eq!(set.len(), 9);
assert_eq!(set.capacity(), cap_orig);
set.check_invariants();
assert!(set
.insert(("A=0".to_string(), "B=0".to_string()))
.is_empty());
assert!(set.capacity() < cap_orig);
assert_eq!(set.len(), 10);
for idx in 0..=9 {
assert!(set.contains_first(&format!("A={}", idx)));
}
set.check_invariants();
}
#[test]
fn modify_value() {
let mut set: Tuple2Set<i32, i32> = (1..=100).map(|idx| (idx, idx * idx)).collect();
set.check_invariants();
let v = set.modify_by_first(&30, |elt| elt.1 = 256);
set.check_invariants();
assert_eq!(v.len(), 1);
assert_eq!(v[0], (16, 256));
assert_eq!(set.by_second(&256).unwrap(), &(30, 256));
assert_eq!(set.by_first(&30).unwrap(), &(30, 256));
let v = set.modify_by_first(&30, |elt| *elt = (-100, -100));
set.check_invariants();
assert_eq!(v.len(), 0);
assert_eq!(set.by_first(&30), None);
assert_eq!(set.by_second(&256), None);
assert_eq!(set.by_first(&-100).unwrap(), &(-100, -100));
assert_eq!(set.by_second(&-100).unwrap(), &(-100, -100));
set.check_invariants();
}
#[allow(dead_code)]
struct Weekday {
dow: u8,
name: &'static str,
lucky_number: Option<u16>,
}
#[allow(dead_code)]
impl Weekday {
fn dow(&self) -> &u8 {
&self.dow
}
fn name(&self) -> &str {
self.name
}
fn lucky_number(&self) -> Option<&u16> {
self.lucky_number.as_ref()
}
}
n_key_set! {
struct WeekdaySet for Weekday {
idx: u8 { dow() },
(Option) lucky: u16 { lucky_number() },
name: String { name() }
}
}
n_key_set! {
struct['a] ArrayMap['a] for (String, [&'a u32;10]) {
name: String { .0 }
}
}
n_key_set! {
struct['a, const N:usize] ArrayMap2['a, N] for (String, [&'a u32;N]) {
name: String { .0 }
}
}
}