Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
@@ -0,0 +1,55 @@
#![expect(missing_docs)] // https://github.com/rust-lang/rust/issues/137561
use hashbrown::Equivalent;
use hashbrown::HashMap;
use std::hash::Hash;
#[derive(Debug, Hash)]
pub struct Pair<A, B>(pub A, pub B);
impl<A, B, C, D> PartialEq<(A, B)> for Pair<C, D>
where
C: PartialEq<A>,
D: PartialEq<B>,
{
fn eq(&self, rhs: &(A, B)) -> bool {
self.0 == rhs.0 && self.1 == rhs.1
}
}
impl<A, B, X> Equivalent<X> for Pair<A, B>
where
Pair<A, B>: PartialEq<X>,
A: Hash + Eq,
B: Hash + Eq,
{
fn equivalent(&self, other: &X) -> bool {
*self == *other
}
}
#[test]
fn test_lookup() {
let s = String::from;
let mut map = HashMap::new();
map.insert((s("a"), s("b")), 1);
map.insert((s("a"), s("x")), 2);
assert!(map.contains_key(&Pair("a", "b")));
assert!(!map.contains_key(&Pair("b", "a")));
}
#[test]
fn test_string_str() {
let s = String::from;
let mut map = HashMap::new();
map.insert(s("a"), 1);
map.insert(s("b"), 2);
map.insert(s("x"), 3);
map.insert(s("y"), 4);
assert!(map.contains_key("a"));
assert!(!map.contains_key("z"));
assert_eq!(map.remove("b"), Some(2));
}
+65
View File
@@ -0,0 +1,65 @@
//! Sanity check that alternate hashers work correctly.
#![cfg(not(miri))] // FIXME: takes too long
use hashbrown::HashSet;
use std::hash::{BuildHasher, BuildHasherDefault, Hasher};
fn check<S: BuildHasher + Default>() {
let range = 0..1_000;
let mut set = HashSet::<i32, S>::default();
set.extend(range.clone());
assert!(!set.contains(&i32::MIN));
assert!(!set.contains(&(range.start - 1)));
for i in range.clone() {
assert!(set.contains(&i));
}
assert!(!set.contains(&range.end));
assert!(!set.contains(&i32::MAX));
}
/// Use hashbrown's default hasher.
#[test]
fn default() {
check::<hashbrown::DefaultHashBuilder>();
}
/// Use std's default hasher.
#[test]
fn random_state() {
check::<std::collections::hash_map::RandomState>();
}
/// Use a constant 0 hash.
#[test]
fn zero() {
#[derive(Default)]
struct ZeroHasher;
impl Hasher for ZeroHasher {
fn finish(&self) -> u64 {
0
}
fn write(&mut self, _: &[u8]) {}
}
check::<BuildHasherDefault<ZeroHasher>>();
}
/// Use a constant maximum hash.
#[test]
fn max() {
#[derive(Default)]
struct MaxHasher;
impl Hasher for MaxHasher {
fn finish(&self) -> u64 {
u64::MAX
}
fn write(&mut self, _: &[u8]) {}
}
check::<BuildHasherDefault<MaxHasher>>();
}
+158
View File
@@ -0,0 +1,158 @@
//! Repro for a caught-panic corruption path in `std::collections::HashMap`.
//!
//! The bug class is: start a multi-step internal transition, let user code
//! panic in the middle, catch the unwind, and keep using the partially updated
//! object.
//!
//! In this case the user-controlled hook is `BuildHasher::build_hasher` during
//! an in-place rehash. The table keeps its logical length after the panic, but
//! lookups can no longer find the original keys and iteration starts yielding
//! repeated garbage-like entries.
use hashbrown::HashMap;
use std::collections::BTreeSet;
use std::{
hash::{BuildHasher, Hash, Hasher},
panic::{AssertUnwindSafe, catch_unwind},
sync::Mutex,
sync::atomic::{AtomicUsize, Ordering},
};
/// One-shot panic switch used to trigger the first `build_hasher` call that
/// occurs inside `reserve(1)`.
static PANIC_COUNTER: AtomicUsize = AtomicUsize::new(0);
static TEST_LOCK: Mutex<()> = Mutex::new(());
/// A deterministic hasher that maps everything to the same bucket group.
///
/// This maximizes collisions and makes the in-place rehash path easy to reach
/// with a small, fixed workload.
#[derive(Default)]
struct ZeroHasher;
impl Hasher for ZeroHasher {
fn finish(&self) -> u64 {
0
}
fn write(&mut self, _bytes: &[u8]) {}
}
/// `BuildHasher` that panics once when armed.
///
/// Using a panicking build hook mirrors the upstream interner trigger more
/// closely than a panicking `Hash` impl.
#[derive(Clone, Default)]
struct PanicBuildHasher;
impl BuildHasher for PanicBuildHasher {
type Hasher = ZeroHasher;
fn build_hasher(&self) -> Self::Hasher {
if PANIC_COUNTER.fetch_sub(1, Ordering::SeqCst) == 0 {
panic!("panic in BuildHasher::build_hasher");
}
ZeroHasher
}
}
/// Simple integer key type so the test can verify reachability after the panic.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
struct Key(u64);
type Map = HashMap<Key, u64, PanicBuildHasher>;
/// Fill a map until `len == capacity`.
///
/// With the current toolchain this yields a map with `len == capacity == 224`
/// when constructed from `with_capacity_and_hasher(128, ...)`.
fn make_full_map() -> Map {
PANIC_COUNTER.store(!0, Ordering::SeqCst);
let mut map = HashMap::with_capacity_and_hasher(128, PanicBuildHasher);
for i in 0.. {
map.insert(Key(i), i);
if map.len() == map.capacity() {
return map;
}
}
unreachable!()
}
fn panics_silently(f: impl FnOnce()) -> bool {
let previous_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(|_| {}));
let panicked = catch_unwind(AssertUnwindSafe(f)).is_err();
std::panic::set_hook(previous_hook);
panicked
}
fn hashmap_reserve_survives_panicking_build_hasher_inner(count: usize) {
// Phase 1: fill a colliding table, then carve out the exact tombstone
// pattern that forces `reserve(1)` down the in-place rehash path.
let mut map = make_full_map();
let original_len = map.len();
assert_eq!(
(map.len(), map.capacity()),
(224, 224),
"this minimized workload is tuned for the validated std/hashbrown layout"
);
for i in 1..114 {
assert_eq!(map.remove(&Key(i)), Some(i));
}
assert_eq!(
map.len(),
111,
"setup should leave the expected tombstone pattern"
);
// Phase 2: make `BuildHasher::build_hasher` panic during the rehash, then
// keep using the recovered map.
PANIC_COUNTER.store(count, Ordering::SeqCst);
let reserve_panicked = panics_silently(|| {
map.reserve(1);
});
assert!(
reserve_panicked,
"the minimized workload should panic during the in-place rehash"
);
// Phase 3: a correct table should keep every surviving key reachable and
// should not start yielding duplicate entries.
let mut expected_visible_keys: Vec<_> = map.keys().map(|&Key(i)| i).collect();
let visible_keys: Vec<_> = (0..original_len as u64)
.filter(|&i| map.get(&Key(i)).copied() == Some(i))
.collect();
expected_visible_keys.sort();
let iter_sample: Vec<_> = map.iter().take(8).map(|(k, v)| (k.0, *v)).collect();
let distinct_entries = iter_sample.iter().copied().collect::<BTreeSet<_>>();
assert_eq!(
map.len(),
expected_visible_keys.len(),
"the table length should stay coherent"
);
assert_eq!(
visible_keys, expected_visible_keys,
"the surviving keys should stay reachable after the caught panic"
);
assert_eq!(
distinct_entries.len(),
iter_sample.len(),
"the iterator sample should not contain duplicate entries after the caught panic"
);
}
#[test]
fn hashmap_reserve_survives_panicking_build_hasher() {
let _guard = TEST_LOCK.lock().unwrap_or_else(|e| e.into_inner());
if cfg!(miri) {
for i in [0, 50, 110] {
hashmap_reserve_survives_panicking_build_hasher_inner(i);
}
} else {
for i in 0..111 {
hashmap_reserve_survives_panicking_build_hasher_inner(i);
}
}
}
+536
View File
@@ -0,0 +1,536 @@
#![expect(missing_docs)] // https://github.com/rust-lang/rust/issues/137561
#![cfg(feature = "rayon")]
use hashbrown::{HashMap, HashSet};
use rayon::iter::{
IntoParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelExtend,
ParallelIterator,
};
use std::sync::LazyLock;
macro_rules! assert_eq3 {
($e1:expr, $e2:expr, $e3:expr) => {{
assert_eq!($e1, $e2);
assert_eq!($e1, $e3);
assert_eq!($e2, $e3);
}};
}
static MAP_EMPTY: LazyLock<HashMap<char, u32>> = LazyLock::new(HashMap::new);
static MAP: LazyLock<HashMap<char, u32>> = LazyLock::new(|| {
let mut m = HashMap::new();
m.insert('b', 20);
m.insert('a', 10);
m.insert('c', 30);
m.insert('e', 50);
m.insert('f', 60);
m.insert('d', 40);
m
});
#[test]
fn map_seq_par_equivalence_iter_empty() {
let vec_seq = MAP_EMPTY.iter().collect::<Vec<_>>();
let vec_par = MAP_EMPTY.par_iter().collect::<Vec<_>>();
assert_eq3!(vec_seq, vec_par, []);
}
#[test]
fn map_seq_par_equivalence_iter() {
let mut vec_seq = MAP.iter().collect::<Vec<_>>();
let mut vec_par = MAP.par_iter().collect::<Vec<_>>();
assert_eq!(vec_seq, vec_par);
// Do not depend on the exact order of values
let expected_sorted = [
(&'a', &10),
(&'b', &20),
(&'c', &30),
(&'d', &40),
(&'e', &50),
(&'f', &60),
];
vec_seq.sort_unstable();
vec_par.sort_unstable();
assert_eq3!(vec_seq, vec_par, expected_sorted);
}
#[test]
fn map_seq_par_equivalence_keys_empty() {
let vec_seq = MAP_EMPTY.keys().collect::<Vec<&char>>();
let vec_par = MAP_EMPTY.par_keys().collect::<Vec<&char>>();
let expected: [&char; 0] = [];
assert_eq3!(vec_seq, vec_par, expected);
}
#[test]
fn map_seq_par_equivalence_keys() {
let mut vec_seq = MAP.keys().collect::<Vec<_>>();
let mut vec_par = MAP.par_keys().collect::<Vec<_>>();
assert_eq!(vec_seq, vec_par);
// Do not depend on the exact order of values
let expected_sorted = [&'a', &'b', &'c', &'d', &'e', &'f'];
vec_seq.sort_unstable();
vec_par.sort_unstable();
assert_eq3!(vec_seq, vec_par, expected_sorted);
}
#[test]
fn map_seq_par_equivalence_values_empty() {
let vec_seq = MAP_EMPTY.values().collect::<Vec<_>>();
let vec_par = MAP_EMPTY.par_values().collect::<Vec<_>>();
let expected: [&u32; 0] = [];
assert_eq3!(vec_seq, vec_par, expected);
}
#[test]
fn map_seq_par_equivalence_values() {
let mut vec_seq = MAP.values().collect::<Vec<_>>();
let mut vec_par = MAP.par_values().collect::<Vec<_>>();
assert_eq!(vec_seq, vec_par);
// Do not depend on the exact order of values
let expected_sorted = [&10, &20, &30, &40, &50, &60];
vec_seq.sort_unstable();
vec_par.sort_unstable();
assert_eq3!(vec_seq, vec_par, expected_sorted);
}
#[test]
fn map_seq_par_equivalence_iter_mut_empty() {
let mut map1 = MAP_EMPTY.clone();
let mut map2 = MAP_EMPTY.clone();
let vec_seq = map1.iter_mut().collect::<Vec<_>>();
let vec_par = map2.par_iter_mut().collect::<Vec<_>>();
assert_eq3!(vec_seq, vec_par, []);
}
#[test]
fn map_seq_par_equivalence_iter_mut() {
let mut map1 = MAP.clone();
let mut map2 = MAP.clone();
let mut vec_seq = map1.iter_mut().collect::<Vec<_>>();
let mut vec_par = map2.par_iter_mut().collect::<Vec<_>>();
assert_eq!(vec_seq, vec_par);
// Do not depend on the exact order of values
let expected_sorted = [
(&'a', &mut 10),
(&'b', &mut 20),
(&'c', &mut 30),
(&'d', &mut 40),
(&'e', &mut 50),
(&'f', &mut 60),
];
vec_seq.sort_unstable();
vec_par.sort_unstable();
assert_eq3!(vec_seq, vec_par, expected_sorted);
}
#[test]
fn map_seq_par_equivalence_values_mut_empty() {
let mut map1 = MAP_EMPTY.clone();
let mut map2 = MAP_EMPTY.clone();
let vec_seq = map1.values_mut().collect::<Vec<_>>();
let vec_par = map2.par_values_mut().collect::<Vec<_>>();
let expected: [&u32; 0] = [];
assert_eq3!(vec_seq, vec_par, expected);
}
#[test]
fn map_seq_par_equivalence_values_mut() {
let mut map1 = MAP.clone();
let mut map2 = MAP.clone();
let mut vec_seq = map1.values_mut().collect::<Vec<_>>();
let mut vec_par = map2.par_values_mut().collect::<Vec<_>>();
assert_eq!(vec_seq, vec_par);
// Do not depend on the exact order of values
let expected_sorted = [&mut 10, &mut 20, &mut 30, &mut 40, &mut 50, &mut 60];
vec_seq.sort_unstable();
vec_par.sort_unstable();
assert_eq3!(vec_seq, vec_par, expected_sorted);
}
#[test]
fn map_seq_par_equivalence_into_iter_empty() {
let vec_seq = MAP_EMPTY.clone().into_iter().collect::<Vec<_>>();
let vec_par = MAP_EMPTY.clone().into_par_iter().collect::<Vec<_>>();
assert_eq3!(vec_seq, vec_par, []);
}
#[test]
fn map_seq_par_equivalence_into_iter() {
let mut vec_seq = MAP.clone().into_iter().collect::<Vec<_>>();
let mut vec_par = MAP.clone().into_par_iter().collect::<Vec<_>>();
assert_eq!(vec_seq, vec_par);
// Do not depend on the exact order of values
let expected_sorted = [
('a', 10),
('b', 20),
('c', 30),
('d', 40),
('e', 50),
('f', 60),
];
vec_seq.sort_unstable();
vec_par.sort_unstable();
assert_eq3!(vec_seq, vec_par, expected_sorted);
}
static MAP_VEC_EMPTY: &[(char, u32)] = &[];
static MAP_VEC: &[(char, u32)] = &[
('b', 20),
('a', 10),
('c', 30),
('e', 50),
('f', 60),
('d', 40),
];
#[test]
fn map_seq_par_equivalence_collect_empty() {
let map_expected = MAP_EMPTY.clone();
let map_seq = MAP_VEC_EMPTY.iter().copied().collect::<HashMap<_, _>>();
let map_par = MAP_VEC_EMPTY.par_iter().copied().collect::<HashMap<_, _>>();
assert_eq!(map_seq, map_par);
assert_eq!(map_seq, map_expected);
assert_eq!(map_par, map_expected);
}
#[test]
fn map_seq_par_equivalence_collect() {
let map_expected = MAP.clone();
let map_seq = MAP_VEC.iter().copied().collect::<HashMap<_, _>>();
let map_par = MAP_VEC.par_iter().copied().collect::<HashMap<_, _>>();
assert_eq!(map_seq, map_par);
assert_eq!(map_seq, map_expected);
assert_eq!(map_par, map_expected);
}
static MAP_EXISTING_EMPTY: LazyLock<HashMap<char, u32>> = LazyLock::new(HashMap::new);
static MAP_EXISTING: LazyLock<HashMap<char, u32>> = LazyLock::new(|| {
let mut m = HashMap::new();
m.insert('b', 20);
m.insert('a', 10);
m
});
static MAP_EXTENSION_EMPTY: &[(char, u32)] = &[];
static MAP_EXTENSION: &[(char, u32)] = &[('c', 30), ('e', 50), ('f', 60), ('d', 40)];
#[test]
fn map_seq_par_equivalence_existing_empty_extend_empty() {
let expected = HashMap::new();
let mut map_seq = MAP_EXISTING_EMPTY.clone();
let mut map_par = MAP_EXISTING_EMPTY.clone();
map_seq.extend(MAP_EXTENSION_EMPTY.iter().copied());
map_par.par_extend(MAP_EXTENSION_EMPTY.par_iter().copied());
assert_eq3!(map_seq, map_par, expected);
}
#[test]
fn map_seq_par_equivalence_existing_empty_extend() {
let expected = MAP_EXTENSION.iter().copied().collect::<HashMap<_, _>>();
let mut map_seq = MAP_EXISTING_EMPTY.clone();
let mut map_par = MAP_EXISTING_EMPTY.clone();
map_seq.extend(MAP_EXTENSION.iter().copied());
map_par.par_extend(MAP_EXTENSION.par_iter().copied());
assert_eq3!(map_seq, map_par, expected);
}
#[test]
fn map_seq_par_equivalence_existing_extend_empty() {
let expected = MAP_EXISTING.clone();
let mut map_seq = MAP_EXISTING.clone();
let mut map_par = MAP_EXISTING.clone();
map_seq.extend(MAP_EXTENSION_EMPTY.iter().copied());
map_par.par_extend(MAP_EXTENSION_EMPTY.par_iter().copied());
assert_eq3!(map_seq, map_par, expected);
}
#[test]
fn map_seq_par_equivalence_existing_extend() {
let expected = MAP.clone();
let mut map_seq = MAP_EXISTING.clone();
let mut map_par = MAP_EXISTING.clone();
map_seq.extend(MAP_EXTENSION.iter().copied());
map_par.par_extend(MAP_EXTENSION.par_iter().copied());
assert_eq3!(map_seq, map_par, expected);
}
static SET_EMPTY: LazyLock<HashSet<char>> = LazyLock::new(HashSet::new);
static SET: LazyLock<HashSet<char>> = LazyLock::new(|| {
let mut s = HashSet::new();
s.insert('b');
s.insert('a');
s.insert('c');
s.insert('e');
s.insert('f');
s.insert('d');
s
});
#[test]
fn set_seq_par_equivalence_iter_empty() {
let vec_seq = SET_EMPTY.iter().collect::<Vec<_>>();
let vec_par = SET_EMPTY.par_iter().collect::<Vec<_>>();
let expected: [&char; 0] = [];
assert_eq3!(vec_seq, vec_par, expected);
}
#[test]
fn set_seq_par_equivalence_iter() {
let mut vec_seq = SET.iter().collect::<Vec<_>>();
let mut vec_par = SET.par_iter().collect::<Vec<_>>();
assert_eq!(vec_seq, vec_par);
// Do not depend on the exact order of values
let expected_sorted = [&'a', &'b', &'c', &'d', &'e', &'f'];
vec_seq.sort_unstable();
vec_par.sort_unstable();
assert_eq3!(vec_seq, vec_par, expected_sorted);
}
#[test]
fn set_seq_par_equivalence_into_iter_empty() {
let vec_seq = SET_EMPTY.clone().into_iter().collect::<Vec<_>>();
let vec_par = SET_EMPTY.clone().into_par_iter().collect::<Vec<_>>();
// Work around type inference failure introduced by rend dev-dependency.
let empty: [char; 0] = [];
assert_eq3!(vec_seq, vec_par, empty);
}
#[test]
fn set_seq_par_equivalence_into_iter() {
let mut vec_seq = SET.clone().into_iter().collect::<Vec<_>>();
let mut vec_par = SET.clone().into_par_iter().collect::<Vec<_>>();
assert_eq!(vec_seq, vec_par);
// Do not depend on the exact order of values
let expected_sorted = ['a', 'b', 'c', 'd', 'e', 'f'];
vec_seq.sort_unstable();
vec_par.sort_unstable();
assert_eq3!(vec_seq, vec_par, expected_sorted);
}
static SET_VEC_EMPTY: &[char] = &[];
static SET_VEC: &[char] = &['b', 'a', 'c', 'e', 'f', 'd'];
#[test]
fn set_seq_par_equivalence_collect_empty() {
let set_expected = SET_EMPTY.clone();
let set_seq = SET_VEC_EMPTY.iter().copied().collect::<HashSet<_>>();
let set_par = SET_VEC_EMPTY.par_iter().copied().collect::<HashSet<_>>();
assert_eq!(set_seq, set_par);
assert_eq!(set_seq, set_expected);
assert_eq!(set_par, set_expected);
}
#[test]
fn set_seq_par_equivalence_collect() {
let set_expected = SET.clone();
let set_seq = SET_VEC.iter().copied().collect::<HashSet<_>>();
let set_par = SET_VEC.par_iter().copied().collect::<HashSet<_>>();
assert_eq!(set_seq, set_par);
assert_eq!(set_seq, set_expected);
assert_eq!(set_par, set_expected);
}
static SET_EXISTING_EMPTY: LazyLock<HashSet<char>> = LazyLock::new(HashSet::new);
static SET_EXISTING: LazyLock<HashSet<char>> = LazyLock::new(|| {
let mut s = HashSet::new();
s.insert('b');
s.insert('a');
s
});
static SET_EXTENSION_EMPTY: &[char] = &[];
static SET_EXTENSION: &[char] = &['c', 'e', 'f', 'd'];
#[test]
fn set_seq_par_equivalence_existing_empty_extend_empty() {
let expected = HashSet::new();
let mut set_seq = SET_EXISTING_EMPTY.clone();
let mut set_par = SET_EXISTING_EMPTY.clone();
set_seq.extend(SET_EXTENSION_EMPTY.iter().copied());
set_par.par_extend(SET_EXTENSION_EMPTY.par_iter().copied());
assert_eq3!(set_seq, set_par, expected);
}
#[test]
fn set_seq_par_equivalence_existing_empty_extend() {
let expected = SET_EXTENSION.iter().copied().collect::<HashSet<_>>();
let mut set_seq = SET_EXISTING_EMPTY.clone();
let mut set_par = SET_EXISTING_EMPTY.clone();
set_seq.extend(SET_EXTENSION.iter().copied());
set_par.par_extend(SET_EXTENSION.par_iter().copied());
assert_eq3!(set_seq, set_par, expected);
}
#[test]
fn set_seq_par_equivalence_existing_extend_empty() {
let expected = SET_EXISTING.clone();
let mut set_seq = SET_EXISTING.clone();
let mut set_par = SET_EXISTING.clone();
set_seq.extend(SET_EXTENSION_EMPTY.iter().copied());
set_par.par_extend(SET_EXTENSION_EMPTY.par_iter().copied());
assert_eq3!(set_seq, set_par, expected);
}
#[test]
fn set_seq_par_equivalence_existing_extend() {
let expected = SET.clone();
let mut set_seq = SET_EXISTING.clone();
let mut set_par = SET_EXISTING.clone();
set_seq.extend(SET_EXTENSION.iter().copied());
set_par.par_extend(SET_EXTENSION.par_iter().copied());
assert_eq3!(set_seq, set_par, expected);
}
static SET_A: LazyLock<HashSet<char>> =
LazyLock::new(|| ['a', 'b', 'c', 'd'].iter().copied().collect());
static SET_B: LazyLock<HashSet<char>> =
LazyLock::new(|| ['a', 'b', 'e', 'f'].iter().copied().collect());
static SET_DIFF_AB: LazyLock<HashSet<char>> =
LazyLock::new(|| ['c', 'd'].iter().copied().collect());
static SET_DIFF_BA: LazyLock<HashSet<char>> =
LazyLock::new(|| ['e', 'f'].iter().copied().collect());
static SET_SYMM_DIFF_AB: LazyLock<HashSet<char>> =
LazyLock::new(|| ['c', 'd', 'e', 'f'].iter().copied().collect());
static SET_INTERSECTION_AB: LazyLock<HashSet<char>> =
LazyLock::new(|| ['a', 'b'].iter().copied().collect());
static SET_UNION_AB: LazyLock<HashSet<char>> =
LazyLock::new(|| ['a', 'b', 'c', 'd', 'e', 'f'].iter().copied().collect());
#[test]
fn set_seq_par_equivalence_difference() {
let diff_ab_seq = SET_A.difference(&*SET_B).copied().collect::<HashSet<_>>();
let diff_ab_par = SET_A
.par_difference(&*SET_B)
.copied()
.collect::<HashSet<_>>();
assert_eq3!(diff_ab_seq, diff_ab_par, *SET_DIFF_AB);
let diff_ba_seq = SET_B.difference(&*SET_A).copied().collect::<HashSet<_>>();
let diff_ba_par = SET_B
.par_difference(&*SET_A)
.copied()
.collect::<HashSet<_>>();
assert_eq3!(diff_ba_seq, diff_ba_par, *SET_DIFF_BA);
}
#[test]
fn set_seq_par_equivalence_symmetric_difference() {
let symm_diff_ab_seq = SET_A
.symmetric_difference(&*SET_B)
.copied()
.collect::<HashSet<_>>();
let symm_diff_ab_par = SET_A
.par_symmetric_difference(&*SET_B)
.copied()
.collect::<HashSet<_>>();
assert_eq3!(symm_diff_ab_seq, symm_diff_ab_par, *SET_SYMM_DIFF_AB);
}
#[test]
fn set_seq_par_equivalence_intersection() {
let intersection_ab_seq = SET_A.intersection(&*SET_B).copied().collect::<HashSet<_>>();
let intersection_ab_par = SET_A
.par_intersection(&*SET_B)
.copied()
.collect::<HashSet<_>>();
assert_eq3!(
intersection_ab_seq,
intersection_ab_par,
*SET_INTERSECTION_AB
);
}
#[test]
fn set_seq_par_equivalence_union() {
let union_ab_seq = SET_A.union(&*SET_B).copied().collect::<HashSet<_>>();
let union_ab_par = SET_A.par_union(&*SET_B).copied().collect::<HashSet<_>>();
assert_eq3!(union_ab_seq, union_ab_par, *SET_UNION_AB);
}
+66
View File
@@ -0,0 +1,66 @@
#![expect(missing_docs)] // https://github.com/rust-lang/rust/issues/137561
#![cfg(feature = "serde")]
use core::hash::BuildHasherDefault;
use fnv::FnvHasher;
use hashbrown::{HashMap, HashSet};
use serde_test::{Token, assert_tokens};
// We use FnvHash for this test because we rely on the ordering
type FnvHashMap<K, V> = HashMap<K, V, BuildHasherDefault<FnvHasher>>;
type FnvHashSet<T> = HashSet<T, BuildHasherDefault<FnvHasher>>;
#[test]
fn map_serde_tokens_empty() {
let map = FnvHashMap::<char, u32>::default();
assert_tokens(&map, &[Token::Map { len: Some(0) }, Token::MapEnd]);
}
#[test]
fn map_serde_tokens() {
let mut map = FnvHashMap::default();
map.insert('b', 20);
map.insert('a', 10);
map.insert('c', 30);
assert_tokens(
&map,
&[
Token::Map { len: Some(3) },
Token::Char('a'),
Token::I32(10),
Token::Char('c'),
Token::I32(30),
Token::Char('b'),
Token::I32(20),
Token::MapEnd,
],
);
}
#[test]
fn set_serde_tokens_empty() {
let set = FnvHashSet::<u32>::default();
assert_tokens(&set, &[Token::Seq { len: Some(0) }, Token::SeqEnd]);
}
#[test]
fn set_serde_tokens() {
let mut set = FnvHashSet::default();
set.insert(20);
set.insert(10);
set.insert(30);
assert_tokens(
&set,
&[
Token::Seq { len: Some(3) },
Token::I32(30),
Token::I32(20),
Token::I32(10),
Token::SeqEnd,
],
);
}
+35
View File
@@ -0,0 +1,35 @@
#![expect(missing_docs)] // https://github.com/rust-lang/rust/issues/137561
#![cfg(not(miri))] // FIXME: takes too long
use hashbrown::HashSet;
use rand::{Rng, SeedableRng, distr::Alphanumeric, rngs::SmallRng};
use std::iter;
#[test]
fn test_hashset_insert_remove() {
let mut m: HashSet<Vec<char>> = HashSet::new();
let seed = u64::from_le_bytes(*b"testseed");
let rng = &mut SmallRng::seed_from_u64(seed);
let tx: Vec<Vec<char>> = iter::repeat_with(|| {
rng.sample_iter(&Alphanumeric)
.take(32)
.map(char::from)
.collect()
})
.take(4096)
.collect();
// more readable with explicit `true` / `false`
#[expect(clippy::bool_assert_comparison)]
for _ in 0..32 {
for x in &tx {
assert_eq!(m.contains(x), false);
assert_eq!(m.insert(x.clone()), true);
}
for (i, x) in tx.iter().enumerate() {
println!("removing {i} {x:?}");
assert_eq!(m.remove(x), true);
}
}
}