mirror of
https://github.com/openharmony/third_party_rust_hashbrown.git
synced 2026-07-01 21:04:01 -04:00
Merge branch 'master' into update-deps
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- trying.tmp
|
||||
- staging.tmp
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
miri:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- env:
|
||||
TARGET: x86_64-unknown-linux-gnu
|
||||
run: sh ci/miri.sh
|
||||
|
||||
rustfmt_clippy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- env:
|
||||
TARGET: i586-unknown-linux-gnu
|
||||
run: sh ci/tools.sh
|
||||
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.channel }}
|
||||
override: true
|
||||
- env:
|
||||
TARGET: ${{ matrix.target }}
|
||||
CHANNEL: ${{ matrix.channel }}
|
||||
CROSS: ${{ matrix.target != 'x86_64-unknown-linux-gnu' && '1' || '0' }}
|
||||
NO_STD: ${{ matrix.target == 'thumbv6m-none-eabi' && '1' || '0' }}
|
||||
run: sh ci/run.sh
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
target: [
|
||||
x86_64-unknown-linux-gnu,
|
||||
i686-unknown-linux-gnu,
|
||||
i586-unknown-linux-gnu,
|
||||
armv7-unknown-linux-gnueabihf,
|
||||
aarch64-unknown-linux-gnu,
|
||||
thumbv6m-none-eabi,
|
||||
x86_64-pc-windows-gnu,
|
||||
]
|
||||
channel: [1.49.0, nightly]
|
||||
include:
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
channel: nightly
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
channel: nightly
|
||||
- os: macos-latest
|
||||
target: x86_64-apple-darwin
|
||||
channel: 1.49.0
|
||||
- os: windows-latest
|
||||
target: x86_64-pc-windows-msvc
|
||||
channel: 1.49.0
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
channel: beta
|
||||
- os: ubuntu-latest
|
||||
target: x86_64-unknown-linux-gnu
|
||||
channel: stable
|
||||
|
||||
# These jobs doesn't actually test anything, but they're only used to tell
|
||||
# bors the build completed, as there is no practical way to detect when a
|
||||
# workflow is successful listening to webhooks only.
|
||||
#
|
||||
# ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
|
||||
|
||||
end-success:
|
||||
name: bors build finished
|
||||
if: github.event.pusher.name == 'bors' && success()
|
||||
runs-on: ubuntu-latest
|
||||
needs: [miri, rustfmt_clippy, test]
|
||||
|
||||
steps:
|
||||
- name: Mark the job as successful
|
||||
run: exit 0
|
||||
|
||||
end-failure:
|
||||
name: bors build finished
|
||||
if: github.event.pusher.name == 'bors' && (failure() || cancelled())
|
||||
runs-on: ubuntu-latest
|
||||
needs: [miri, rustfmt_clippy, test]
|
||||
|
||||
steps:
|
||||
- name: Mark the job as a failure
|
||||
run: exit 1
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
language: rust
|
||||
rust: nightly
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- name: "miri"
|
||||
env: TARGET=x86_64-unknown-linux-gnu
|
||||
script: sh ci/miri.sh
|
||||
- name: "rustfmt/clippy"
|
||||
env: TARGET=i586-unknown-linux-gnu
|
||||
script: sh ci/tools.sh
|
||||
- name: "docs"
|
||||
env: TARGET=x86_64-unknown-linux-gnu
|
||||
script:
|
||||
- cargo -vv doc --features nightly,serde,rayon,raw
|
||||
- echo '<meta http-equiv=refresh content=0;url=hashbrown/index.html>' > target/doc/index.html
|
||||
deploy:
|
||||
provider: pages
|
||||
skip-cleanup: true
|
||||
github-token: $GITHUB_TOKEN
|
||||
local-dir: target/doc
|
||||
keep-history: false
|
||||
on:
|
||||
branch: master
|
||||
|
||||
# Tier 1 targets:
|
||||
- name: "x86_64-unknown-linux-gnu"
|
||||
env: TARGET=x86_64-unknown-linux-gnu
|
||||
- name: "x86_64-unknown-linux-gnu (beta)"
|
||||
rust: beta
|
||||
env: TARGET=x86_64-unknown-linux-gnu
|
||||
- name: "x86_64-unknown-linux-gnu (stable)"
|
||||
rust: stable
|
||||
env: TARGET=x86_64-unknown-linux-gnu
|
||||
- name: "x86_64-unknown-linux-gnu (Rust 1.49.0)"
|
||||
rust: 1.49.0
|
||||
env: TARGET=x86_64-unknown-linux-gnu
|
||||
- name: "i686-unknown-linux-gnu"
|
||||
env: TARGET=i686-unknown-linux-gnu CROSS=1
|
||||
- name: "x86_64-apple-darwin"
|
||||
env: TARGET=x86_64-apple-darwin
|
||||
os: osx
|
||||
osx_image: xcode10
|
||||
- name: "x86_64-pc-windows-msvc"
|
||||
env: TARGET=x86_64-pc-windows-msvc
|
||||
os: windows
|
||||
- name: "x86_64-pc-windows-gnu"
|
||||
env: TARGET=x86_64-pc-windows-gnu CROSS=1
|
||||
# This target is not supported by cross
|
||||
#- name: "i686-pc-windows-gnu"
|
||||
# env: TARGET=i686-pc-windows-gnu CROSS=1
|
||||
|
||||
# Tier 2/3 targets:
|
||||
- name: "i586-unknown-linux-gnu (no SSE2)"
|
||||
env: TARGET=i586-unknown-linux-gnu CROSS=1
|
||||
- name: "armv7-unknown-linux-gnueabihf"
|
||||
env: TARGET=armv7-unknown-linux-gnueabihf CROSS=1
|
||||
- name: "aarch64-unknown-linux-gnu"
|
||||
env: TARGET=aarch64-unknown-linux-gnu CROSS=1
|
||||
|
||||
# Ensure that we successfully build without libstd
|
||||
- name: "thumbv6m-none-eabi"
|
||||
env: TARGET=thumbv6m-none-eabi NO_STD=1
|
||||
script:
|
||||
# cross doesn't seem to work with thumb targets...
|
||||
- rustup target install $TARGET
|
||||
- sh ci/run.sh
|
||||
|
||||
install: travis_retry rustup target add "${TARGET}"
|
||||
script: sh ci/run.sh
|
||||
|
||||
branches:
|
||||
# Don't build these branches
|
||||
except:
|
||||
# Used by bors
|
||||
- trying.tmp
|
||||
- staging.tmp
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ repository = "https://github.com/rust-lang/hashbrown"
|
||||
readme = "README.md"
|
||||
keywords = ["hash", "no_std", "hashmap", "swisstable"]
|
||||
categories = ["data-structures", "no-std"]
|
||||
exclude = [".travis.yml", "bors.toml", "/ci/*"]
|
||||
exclude = [".github", "bors.toml", "/ci/*"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
hashbrown
|
||||
=========
|
||||
|
||||
[](https://travis-ci.com/rust-lang/hashbrown)
|
||||
[](https://github.com/rust-lang/hashbrown/actions)
|
||||
[](https://crates.io/crates/hashbrown)
|
||||
[](https://docs.rs/hashbrown)
|
||||
[](https://github.com/rust-lang/hashbrown)
|
||||
@@ -114,8 +114,8 @@ this pre-generates seeds at compile time and embeds them as constants. See [aHas
|
||||
|
||||
Licensed under either of:
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ impl Iterator for RandomKeys {
|
||||
type Item = usize;
|
||||
fn next(&mut self) -> Option<usize> {
|
||||
// Add 1 then multiply by some 32 bit prime.
|
||||
self.state = self.state.wrapping_add(1).wrapping_mul(3787392781);
|
||||
self.state = self.state.wrapping_add(1).wrapping_mul(3_787_392_781);
|
||||
Some(self.state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ else
|
||||
FEATURES="rustc-internal-api,serde,rayon,raw,bumpalo"
|
||||
OP="test"
|
||||
fi
|
||||
if [ "${TRAVIS_RUST_VERSION}" = "nightly" ]; then
|
||||
if [ "${CHANNEL}" = "nightly" ]; then
|
||||
FEATURES="${FEATURES},nightly"
|
||||
export RUSTFLAGS="$RUSTFLAGS -D warnings"
|
||||
fi
|
||||
@@ -36,7 +36,7 @@ fi
|
||||
"${CARGO}" -vv ${OP} --target="${TARGET}" --release
|
||||
"${CARGO}" -vv ${OP} --target="${TARGET}" --release --features "${FEATURES}"
|
||||
|
||||
if [ "${TRAVIS_RUST_VERSION}" = "nightly" ] && [ "${NO_STD}" != 1 ]; then
|
||||
if [ "${CHANNEL}" = "nightly" ] && [ "${NO_STD}" != 1 ]; then
|
||||
# Run benchmark on native targets, build them on non-native ones:
|
||||
NO_RUN=""
|
||||
if [ "${CROSS}" = "1" ]; then
|
||||
|
||||
+3
-7
@@ -30,13 +30,9 @@ if retry rustup component add rustfmt ; then
|
||||
fi
|
||||
|
||||
if retry rustup component add clippy ; then
|
||||
cargo clippy --all -- -D clippy::pedantic
|
||||
fi
|
||||
|
||||
if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
|
||||
if retry rustup component add clippy ; then
|
||||
cargo clippy --all --target=i586-unknown-linux-gnu -- -D clippy::all -D clippy::pedantic
|
||||
fi
|
||||
cargo clippy --all --tests --features serde,rayon,bumpalo -- -D clippy::all -D clippy::pedantic
|
||||
cargo clippy --all --tests --features raw -- -D clippy::all -D clippy::pedantic \
|
||||
-A clippy::missing_safety_doc -A clippy::missing_errors_doc
|
||||
fi
|
||||
|
||||
if command -v shellcheck ; then
|
||||
|
||||
@@ -4,6 +4,7 @@ use alloc::vec::Vec;
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
|
||||
/// Helper for collecting parallel iterators to an intermediary
|
||||
#[allow(clippy::linkedlist)] // yes, we need linked list here for efficient appending!
|
||||
pub(super) fn collect<I: IntoParallelIterator>(iter: I) -> (LinkedList<Vec<I::Item>>, usize) {
|
||||
let list = iter
|
||||
.into_par_iter()
|
||||
|
||||
@@ -512,7 +512,7 @@ mod test_par_map {
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
self.k.hash(state)
|
||||
self.k.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -679,7 +679,7 @@ mod test_par_map {
|
||||
fn test_values_mut() {
|
||||
let vec = vec![(1, 1), (2, 2), (3, 3)];
|
||||
let mut map: HashMap<_, _> = vec.into_par_iter().collect();
|
||||
map.par_values_mut().for_each(|value| *value = (*value) * 2);
|
||||
map.par_values_mut().for_each(|value| *value *= 2);
|
||||
let values: Vec<_> = map.par_values().cloned().collect();
|
||||
assert_eq!(values.len(), 3);
|
||||
assert!(values.contains(&2));
|
||||
|
||||
@@ -134,7 +134,7 @@ impl<T: Send, A: Allocator + Clone> ParallelIterator for RawParDrain<'_, T, A> {
|
||||
C: UnindexedConsumer<Self::Item>,
|
||||
{
|
||||
let _guard = guard(self.table, |table| unsafe {
|
||||
table.as_mut().clear_no_drop()
|
||||
table.as_mut().clear_no_drop();
|
||||
});
|
||||
let iter = unsafe { self.table.as_ref().iter().iter };
|
||||
mem::forget(self);
|
||||
@@ -146,7 +146,9 @@ impl<T: Send, A: Allocator + Clone> ParallelIterator for RawParDrain<'_, T, A> {
|
||||
impl<T, A: Allocator + Clone> Drop for RawParDrain<'_, T, A> {
|
||||
fn drop(&mut self) {
|
||||
// If drive_unindexed is not called then simply clear the table.
|
||||
unsafe { self.table.as_mut().clear() }
|
||||
unsafe {
|
||||
self.table.as_mut().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +177,7 @@ impl<T: Send> UnindexedProducer for ParDrainProducer<T> {
|
||||
{
|
||||
// Make sure to modify the iterator in-place so that any remaining
|
||||
// elements are processed in our Drop impl.
|
||||
while let Some(item) = self.iter.next() {
|
||||
for item in &mut self.iter {
|
||||
folder = folder.consume(unsafe { item.read() });
|
||||
if folder.full() {
|
||||
return folder;
|
||||
@@ -193,7 +195,7 @@ impl<T> Drop for ParDrainProducer<T> {
|
||||
fn drop(&mut self) {
|
||||
// Drop all remaining elements
|
||||
if mem::needs_drop::<T>() {
|
||||
while let Some(item) = self.iter.next() {
|
||||
for item in &mut self.iter {
|
||||
unsafe {
|
||||
item.drop();
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ mod set {
|
||||
deserializer.deserialize_seq(visitor)
|
||||
}
|
||||
|
||||
#[allow(clippy::missing_errors_doc)]
|
||||
fn deserialize_in_place<D>(deserializer: D, place: &mut Self) -> Result<(), D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
|
||||
+2
-2
@@ -57,8 +57,8 @@ macro_rules! cfg_if {
|
||||
// default fn syntax for specialization changes in the future.
|
||||
#[cfg(feature = "nightly")]
|
||||
macro_rules! default_fn {
|
||||
($($tt:tt)*) => {
|
||||
default $($tt)*
|
||||
(#[$($a:tt)*] $($tt:tt)*) => {
|
||||
#[$($a)*] default $($tt)*
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
|
||||
+51
-63
@@ -8,8 +8,6 @@ use core::hash::{BuildHasher, Hash};
|
||||
use core::iter::{FromIterator, FusedIterator};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
#[cfg(feature = "nightly")]
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ops::Index;
|
||||
|
||||
/// Default hasher for `HashMap`.
|
||||
@@ -1149,16 +1147,7 @@ where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
let mut pairs = self.get_each_inner_mut(ks);
|
||||
// TODO use `MaybeUninit::uninit_array` here instead once that's stable.
|
||||
let mut out: [MaybeUninit<Result<&'_ mut V, UnavailableMutError>>; N] =
|
||||
unsafe { MaybeUninit::uninit().assume_init() };
|
||||
for i in 0..N {
|
||||
out[i] = MaybeUninit::new(
|
||||
mem::replace(&mut pairs[i], Err(UnavailableMutError::Absent)).map(|(_, v)| v),
|
||||
);
|
||||
}
|
||||
unsafe { MaybeUninit::array_assume_init(out) }
|
||||
self.get_each_inner_mut(ks).map(|res| res.map(|(_, v)| v))
|
||||
}
|
||||
|
||||
/// Attempts to get mutable references to `N` values in the map at once, with immutable
|
||||
@@ -1208,17 +1197,8 @@ where
|
||||
K: Borrow<Q>,
|
||||
Q: Hash + Eq,
|
||||
{
|
||||
let mut pairs = self.get_each_inner_mut(ks);
|
||||
// TODO use `MaybeUninit::uninit_array` here instead once that's stable.
|
||||
let mut out: [MaybeUninit<Result<(&'_ K, &'_ mut V), UnavailableMutError>>; N] =
|
||||
unsafe { MaybeUninit::uninit().assume_init() };
|
||||
for i in 0..N {
|
||||
out[i] = MaybeUninit::new(
|
||||
mem::replace(&mut pairs[i], Err(UnavailableMutError::Absent))
|
||||
.map(|(k, v)| (&*k, v)),
|
||||
);
|
||||
}
|
||||
unsafe { MaybeUninit::array_assume_init(out) }
|
||||
self.get_each_inner_mut(ks)
|
||||
.map(|res| res.map(|(k, v)| (&*k, v)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
@@ -1692,7 +1672,7 @@ pub(super) struct ConsumeAllOnDrop<'a, T: Iterator>(pub &'a mut T);
|
||||
impl<T: Iterator> Drop for ConsumeAllOnDrop<'_, T> {
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
fn drop(&mut self) {
|
||||
self.0.for_each(drop)
|
||||
self.0.for_each(drop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1729,7 +1709,7 @@ impl<K, V, A: Allocator + Clone> DrainFilterInner<'_, K, V, A> {
|
||||
F: FnMut(&K, &mut V) -> bool,
|
||||
{
|
||||
unsafe {
|
||||
while let Some(item) = self.iter.next() {
|
||||
for item in &mut self.iter {
|
||||
let &mut (ref key, ref mut value) = item.as_mut();
|
||||
if f(key, value) {
|
||||
return Some(self.table.remove(item));
|
||||
@@ -3507,7 +3487,6 @@ mod test_map {
|
||||
use super::DefaultHashBuilder;
|
||||
use super::Entry::{Occupied, Vacant};
|
||||
use super::{HashMap, RawEntryMut};
|
||||
use crate::TryReserveError::*;
|
||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||
use std::borrow::ToOwned;
|
||||
use std::cell::RefCell;
|
||||
@@ -3576,6 +3555,7 @@ mod test_map {
|
||||
assert_eq!(m.len(), 1);
|
||||
assert!(m.insert(2, 4).is_none());
|
||||
assert_eq!(m.len(), 2);
|
||||
#[allow(clippy::redundant_clone)]
|
||||
let m2 = m.clone();
|
||||
assert_eq!(*m2.get(&1).unwrap(), 2);
|
||||
assert_eq!(*m2.get(&2).unwrap(), 4);
|
||||
@@ -3729,6 +3709,7 @@ mod test_map {
|
||||
}
|
||||
});
|
||||
|
||||
#[allow(clippy::let_underscore_drop)] // kind-of a false positive
|
||||
for _ in half.by_ref() {}
|
||||
|
||||
DROP_VECTOR.with(|v| {
|
||||
@@ -3940,7 +3921,7 @@ mod test_map {
|
||||
fn test_keys() {
|
||||
let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
|
||||
let map: HashMap<_, _> = vec.into_iter().collect();
|
||||
let keys: Vec<_> = map.keys().cloned().collect();
|
||||
let keys: Vec<_> = map.keys().copied().collect();
|
||||
assert_eq!(keys.len(), 3);
|
||||
assert!(keys.contains(&1));
|
||||
assert!(keys.contains(&2));
|
||||
@@ -3951,7 +3932,7 @@ mod test_map {
|
||||
fn test_values() {
|
||||
let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
|
||||
let map: HashMap<_, _> = vec.into_iter().collect();
|
||||
let values: Vec<_> = map.values().cloned().collect();
|
||||
let values: Vec<_> = map.values().copied().collect();
|
||||
assert_eq!(values.len(), 3);
|
||||
assert!(values.contains(&'a'));
|
||||
assert!(values.contains(&'b'));
|
||||
@@ -3963,9 +3944,9 @@ mod test_map {
|
||||
let vec = vec![(1, 1), (2, 2), (3, 3)];
|
||||
let mut map: HashMap<_, _> = vec.into_iter().collect();
|
||||
for value in map.values_mut() {
|
||||
*value = (*value) * 2
|
||||
*value *= 2;
|
||||
}
|
||||
let values: Vec<_> = map.values().cloned().collect();
|
||||
let values: Vec<_> = map.values().copied().collect();
|
||||
assert_eq!(values.len(), 3);
|
||||
assert!(values.contains(&2));
|
||||
assert!(values.contains(&4));
|
||||
@@ -4130,7 +4111,7 @@ mod test_map {
|
||||
fn test_from_iter() {
|
||||
let xs = [(1, 1), (2, 2), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
|
||||
|
||||
let map: HashMap<_, _> = xs.iter().cloned().collect();
|
||||
let map: HashMap<_, _> = xs.iter().copied().collect();
|
||||
|
||||
for &(k, v) in &xs {
|
||||
assert_eq!(map.get(&k), Some(&v));
|
||||
@@ -4143,7 +4124,7 @@ mod test_map {
|
||||
fn test_size_hint() {
|
||||
let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
|
||||
|
||||
let map: HashMap<_, _> = xs.iter().cloned().collect();
|
||||
let map: HashMap<_, _> = xs.iter().copied().collect();
|
||||
|
||||
let mut iter = map.iter();
|
||||
|
||||
@@ -4156,7 +4137,7 @@ mod test_map {
|
||||
fn test_iter_len() {
|
||||
let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
|
||||
|
||||
let map: HashMap<_, _> = xs.iter().cloned().collect();
|
||||
let map: HashMap<_, _> = xs.iter().copied().collect();
|
||||
|
||||
let mut iter = map.iter();
|
||||
|
||||
@@ -4169,7 +4150,7 @@ mod test_map {
|
||||
fn test_mut_size_hint() {
|
||||
let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
|
||||
|
||||
let mut map: HashMap<_, _> = xs.iter().cloned().collect();
|
||||
let mut map: HashMap<_, _> = xs.iter().copied().collect();
|
||||
|
||||
let mut iter = map.iter_mut();
|
||||
|
||||
@@ -4182,7 +4163,7 @@ mod test_map {
|
||||
fn test_iter_mut_len() {
|
||||
let xs = [(1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6)];
|
||||
|
||||
let mut map: HashMap<_, _> = xs.iter().cloned().collect();
|
||||
let mut map: HashMap<_, _> = xs.iter().copied().collect();
|
||||
|
||||
let mut iter = map.iter_mut();
|
||||
|
||||
@@ -4211,6 +4192,7 @@ mod test_map {
|
||||
map.insert(2, 1);
|
||||
map.insert(3, 4);
|
||||
|
||||
#[allow(clippy::no_effect)] // false positive lint
|
||||
map[&4];
|
||||
}
|
||||
|
||||
@@ -4218,7 +4200,7 @@ mod test_map {
|
||||
fn test_entry() {
|
||||
let xs = [(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
|
||||
|
||||
let mut map: HashMap<_, _> = xs.iter().cloned().collect();
|
||||
let mut map: HashMap<_, _> = xs.iter().copied().collect();
|
||||
|
||||
// Existing key (insert)
|
||||
match map.entry(1) {
|
||||
@@ -4350,11 +4332,11 @@ mod test_map {
|
||||
let key = "hello there";
|
||||
let value = "value goes here";
|
||||
assert!(a.is_empty());
|
||||
a.insert(key.clone(), value.clone());
|
||||
a.insert(key, value);
|
||||
assert_eq!(a.len(), 1);
|
||||
assert_eq!(a[key], value);
|
||||
|
||||
match a.entry(key.clone()) {
|
||||
match a.entry(key) {
|
||||
Vacant(_) => panic!(),
|
||||
Occupied(e) => assert_eq!(key, *e.key()),
|
||||
}
|
||||
@@ -4369,11 +4351,11 @@ mod test_map {
|
||||
let value = "value goes here";
|
||||
|
||||
assert!(a.is_empty());
|
||||
match a.entry(key.clone()) {
|
||||
match a.entry(key) {
|
||||
Occupied(_) => panic!(),
|
||||
Vacant(e) => {
|
||||
assert_eq!(key, *e.key());
|
||||
e.insert(value.clone());
|
||||
e.insert(value);
|
||||
}
|
||||
}
|
||||
assert_eq!(a.len(), 1);
|
||||
@@ -4641,21 +4623,27 @@ mod test_map {
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)] // FIXME: no OOM signalling (https://github.com/rust-lang/miri/issues/613)
|
||||
fn test_try_reserve() {
|
||||
let mut empty_bytes: HashMap<u8, u8> = HashMap::new();
|
||||
use crate::TryReserveError::{AllocError, CapacityOverflow};
|
||||
|
||||
const MAX_USIZE: usize = usize::MAX;
|
||||
|
||||
let mut empty_bytes: HashMap<u8, u8> = HashMap::new();
|
||||
|
||||
if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) {
|
||||
} else {
|
||||
panic!("usize::MAX should trigger an overflow!");
|
||||
}
|
||||
|
||||
if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE / 8) {
|
||||
if let Err(AllocError { .. }) = empty_bytes.try_reserve(MAX_USIZE / 16) {
|
||||
} else {
|
||||
// This may succeed if there is enough free memory. Attempt to
|
||||
// allocate a second hashmap to ensure the allocation will fail.
|
||||
// allocate a few more hashmaps to ensure the allocation will fail.
|
||||
let mut empty_bytes2: HashMap<u8, u8> = HashMap::new();
|
||||
if let Err(AllocError { .. }) = empty_bytes2.try_reserve(MAX_USIZE / 8) {
|
||||
let _ = empty_bytes2.try_reserve(MAX_USIZE / 16);
|
||||
let mut empty_bytes3: HashMap<u8, u8> = HashMap::new();
|
||||
let _ = empty_bytes3.try_reserve(MAX_USIZE / 16);
|
||||
let mut empty_bytes4: HashMap<u8, u8> = HashMap::new();
|
||||
if let Err(AllocError { .. }) = empty_bytes4.try_reserve(MAX_USIZE / 16) {
|
||||
} else {
|
||||
panic!("usize::MAX / 8 should trigger an OOM!");
|
||||
}
|
||||
@@ -4666,9 +4654,9 @@ mod test_map {
|
||||
fn test_raw_entry() {
|
||||
use super::RawEntryMut::{Occupied, Vacant};
|
||||
|
||||
let xs = [(1i32, 10i32), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
|
||||
let xs = [(1_i32, 10_i32), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
|
||||
|
||||
let mut map: HashMap<_, _> = xs.iter().cloned().collect();
|
||||
let mut map: HashMap<_, _> = xs.iter().copied().collect();
|
||||
|
||||
let compute_hash = |map: &HashMap<i32, i32>, k: i32| -> u64 {
|
||||
super::make_insert_hash::<i32, _>(map.hasher(), &k)
|
||||
@@ -4741,7 +4729,7 @@ mod test_map {
|
||||
// Ensure all lookup methods produce equivalent results.
|
||||
for k in 0..12 {
|
||||
let hash = compute_hash(&map, k);
|
||||
let v = map.get(&k).cloned();
|
||||
let v = map.get(&k).copied();
|
||||
let kv = v.as_ref().map(|v| (&k, v));
|
||||
|
||||
assert_eq!(map.raw_entry().from_key(&k), kv);
|
||||
@@ -4820,13 +4808,13 @@ mod test_map {
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
for n in 0..N {
|
||||
let mut m = HashMap::new();
|
||||
let mut map = HashMap::new();
|
||||
for i in 0..n {
|
||||
assert!(m.insert(i, 2 * i).is_none());
|
||||
assert!(map.insert(i, 2 * i).is_none());
|
||||
}
|
||||
let hasher = m.hasher().clone();
|
||||
let hash_builder = map.hasher().clone();
|
||||
|
||||
let mut it = unsafe { m.table.iter() };
|
||||
let mut it = unsafe { map.table.iter() };
|
||||
assert_eq!(it.len(), n);
|
||||
|
||||
let mut i = 0;
|
||||
@@ -4835,23 +4823,23 @@ mod test_map {
|
||||
loop {
|
||||
// occasionally remove some elements
|
||||
if i < n && rng.gen_bool(0.1) {
|
||||
let mut hsh = hasher.build_hasher();
|
||||
i.hash(&mut hsh);
|
||||
let hash = hsh.finish();
|
||||
let mut hasher = hash_builder.build_hasher();
|
||||
i.hash(&mut hasher);
|
||||
let hash_value = hasher.finish();
|
||||
|
||||
unsafe {
|
||||
let e = m.table.find(hash, |q| q.0.eq(&i));
|
||||
let e = map.table.find(hash_value, |q| q.0.eq(&i));
|
||||
if let Some(e) = e {
|
||||
it.reflect_remove(&e);
|
||||
let t = m.table.remove(e);
|
||||
let t = map.table.remove(e);
|
||||
removed.push(t);
|
||||
left -= 1;
|
||||
} else {
|
||||
assert!(removed.contains(&(i, 2 * i)), "{} not in {:?}", i, removed);
|
||||
let e = m.table.insert(
|
||||
hash,
|
||||
let e = map.table.insert(
|
||||
hash_value,
|
||||
(i, 2 * i),
|
||||
super::make_hasher::<usize, _, usize, _>(&hasher),
|
||||
super::make_hasher::<usize, _, usize, _>(&hash_builder),
|
||||
);
|
||||
it.reflect_insert(&e);
|
||||
if let Some(p) = removed.iter().position(|e| e == &(i, 2 * i)) {
|
||||
@@ -4869,14 +4857,14 @@ mod test_map {
|
||||
assert!(i < n);
|
||||
let t = unsafe { e.unwrap().as_ref() };
|
||||
assert!(!removed.contains(t));
|
||||
let (k, v) = t;
|
||||
assert_eq!(*v, 2 * k);
|
||||
let (key, value) = t;
|
||||
assert_eq!(*value, 2 * key);
|
||||
i += 1;
|
||||
}
|
||||
assert!(i <= n);
|
||||
|
||||
// just for safety:
|
||||
assert_eq!(m.table.len(), left);
|
||||
assert_eq!(map.table.len(), left);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4898,7 +4886,7 @@ mod test_map {
|
||||
const EMPTY_MAP: HashMap<u32, std::string::String, MyHasher> =
|
||||
HashMap::with_hasher(MyHasher);
|
||||
|
||||
let mut map = EMPTY_MAP.clone();
|
||||
let mut map = EMPTY_MAP;
|
||||
map.insert(17, "seventeen".to_owned());
|
||||
assert_eq!("seventeen", map[&17]);
|
||||
}
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ mod inner {
|
||||
}
|
||||
#[inline]
|
||||
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
|
||||
dealloc(ptr.as_ptr(), layout)
|
||||
dealloc(ptr.as_ptr(), layout);
|
||||
}
|
||||
}
|
||||
impl Default for Global {
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ impl IntoIterator for BitMask {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator over the contents of a `BitMask`, returning the indicies of set
|
||||
/// Iterator over the contents of a `BitMask`, returning the indices of set
|
||||
/// bits.
|
||||
pub struct BitMaskIter(BitMask);
|
||||
|
||||
|
||||
+5
-2
@@ -9,12 +9,14 @@ use core::{mem, ptr};
|
||||
target_pointer_width = "64",
|
||||
target_arch = "aarch64",
|
||||
target_arch = "x86_64",
|
||||
target_arch = "wasm32",
|
||||
))]
|
||||
type GroupWord = u64;
|
||||
#[cfg(all(
|
||||
target_pointer_width = "32",
|
||||
not(target_arch = "aarch64"),
|
||||
not(target_arch = "x86_64"),
|
||||
not(target_arch = "wasm32"),
|
||||
))]
|
||||
type GroupWord = u32;
|
||||
|
||||
@@ -37,7 +39,7 @@ fn repeat(byte: u8) -> GroupWord {
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Group(GroupWord);
|
||||
|
||||
// We perform all operations in the native endianess, and convert to
|
||||
// We perform all operations in the native endianness, and convert to
|
||||
// little-endian just before creating a BitMask. The can potentially
|
||||
// enable the compiler to eliminate unnecessary byte swaps if we are
|
||||
// only checking whether a BitMask is empty.
|
||||
@@ -50,6 +52,7 @@ impl Group {
|
||||
/// value for an empty hash table.
|
||||
///
|
||||
/// This is guaranteed to be aligned to the group size.
|
||||
#[inline]
|
||||
pub const fn static_empty() -> &'static [u8; Group::WIDTH] {
|
||||
#[repr(C)]
|
||||
struct AlignedBytes {
|
||||
@@ -103,7 +106,7 @@ impl Group {
|
||||
#[inline]
|
||||
pub fn match_byte(self, byte: u8) -> BitMask {
|
||||
// This algorithm is derived from
|
||||
// http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html##ValueInWord
|
||||
let cmp = self.0 ^ repeat(byte);
|
||||
BitMask((cmp.wrapping_sub(repeat(0x01)) & !cmp & repeat(0x80)).to_le())
|
||||
}
|
||||
|
||||
+321
-177
@@ -3,7 +3,6 @@ use crate::scopeguard::guard;
|
||||
use crate::TryReserveError;
|
||||
#[cfg(feature = "nightly")]
|
||||
use crate::UnavailableMutError;
|
||||
use core::hint;
|
||||
use core::iter::FusedIterator;
|
||||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
@@ -11,6 +10,7 @@ use core::mem::ManuallyDrop;
|
||||
#[cfg(feature = "nightly")]
|
||||
use core::mem::MaybeUninit;
|
||||
use core::ptr::NonNull;
|
||||
use core::{hint, ptr};
|
||||
|
||||
cfg_if! {
|
||||
// Use the SSE2 implementation if possible: it allows us to scan 16 buckets
|
||||
@@ -59,7 +59,7 @@ fn cold() {}
|
||||
#[inline]
|
||||
fn likely(b: bool) -> bool {
|
||||
if !b {
|
||||
cold()
|
||||
cold();
|
||||
}
|
||||
b
|
||||
}
|
||||
@@ -67,18 +67,18 @@ fn likely(b: bool) -> bool {
|
||||
#[inline]
|
||||
fn unlikely(b: bool) -> bool {
|
||||
if b {
|
||||
cold()
|
||||
cold();
|
||||
}
|
||||
b
|
||||
}
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
unsafe fn offset_from<T>(to: *const T, from: *const T) -> usize {
|
||||
to.offset_from(from) as usize
|
||||
}
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
unsafe fn offset_from<T>(to: *const T, from: *const T) -> usize {
|
||||
(to as usize - from as usize) / mem::size_of::<T>()
|
||||
}
|
||||
@@ -211,7 +211,7 @@ fn capacity_to_buckets(cap: usize) -> Option<usize> {
|
||||
|
||||
// Any overflows will have been caught by the checked_mul. Also, any
|
||||
// rounding errors from the division above will be cleaned up by
|
||||
// next_power_of_two (which can't overflow because of the previous divison).
|
||||
// next_power_of_two (which can't overflow because of the previous division).
|
||||
Some(adjusted_cap.next_power_of_two())
|
||||
}
|
||||
|
||||
@@ -292,14 +292,14 @@ pub struct Bucket<T> {
|
||||
unsafe impl<T> Send for Bucket<T> {}
|
||||
|
||||
impl<T> Clone for Bucket<T> {
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Self { ptr: self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Bucket<T> {
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
unsafe fn from_base_index(base: NonNull<T>, index: usize) -> Self {
|
||||
let ptr = if mem::size_of::<T>() == 0 {
|
||||
// won't overflow because index must be less than length
|
||||
@@ -311,7 +311,7 @@ impl<T> Bucket<T> {
|
||||
ptr: NonNull::new_unchecked(ptr),
|
||||
}
|
||||
}
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
unsafe fn to_base_index(&self, base: NonNull<T>) -> usize {
|
||||
if mem::size_of::<T>() == 0 {
|
||||
self.ptr.as_ptr() as usize - 1
|
||||
@@ -319,7 +319,7 @@ impl<T> Bucket<T> {
|
||||
offset_from(base.as_ptr(), self.ptr.as_ptr())
|
||||
}
|
||||
}
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub fn as_ptr(&self) -> *mut T {
|
||||
if mem::size_of::<T>() == 0 {
|
||||
// Just return an arbitrary ZST pointer which is properly aligned
|
||||
@@ -328,7 +328,7 @@ impl<T> Bucket<T> {
|
||||
unsafe { self.ptr.as_ptr().sub(1) }
|
||||
}
|
||||
}
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
unsafe fn next_n(&self, offset: usize) -> Self {
|
||||
let ptr = if mem::size_of::<T>() == 0 {
|
||||
(self.ptr.as_ptr() as usize + offset) as *mut T
|
||||
@@ -343,23 +343,24 @@ impl<T> Bucket<T> {
|
||||
pub unsafe fn drop(&self) {
|
||||
self.as_ptr().drop_in_place();
|
||||
}
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub unsafe fn read(&self) -> T {
|
||||
self.as_ptr().read()
|
||||
}
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub unsafe fn write(&self, val: T) {
|
||||
self.as_ptr().write(val);
|
||||
}
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub unsafe fn as_ref<'a>(&self) -> &'a T {
|
||||
&*self.as_ptr()
|
||||
}
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub unsafe fn as_mut<'a>(&self) -> &'a mut T {
|
||||
&mut *self.as_ptr()
|
||||
}
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[cfg(feature = "raw")]
|
||||
#[inline]
|
||||
pub unsafe fn copy_from_nonoverlapping(&self, other: &Self) {
|
||||
self.as_ptr().copy_from_nonoverlapping(other.as_ptr(), 1);
|
||||
}
|
||||
@@ -398,7 +399,7 @@ impl<T> RawTable<T, Global> {
|
||||
/// In effect this returns a table with exactly 1 bucket. However we can
|
||||
/// leave the data pointer dangling since that bucket is never written to
|
||||
/// due to our load factor forcing us to always have at least 1 free bucket.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
table: RawTableInner::new_in(Global),
|
||||
@@ -427,7 +428,7 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
/// In effect this returns a table with exactly 1 bucket. However we can
|
||||
/// leave the data pointer dangling since that bucket is never written to
|
||||
/// due to our load factor forcing us to always have at least 1 free bucket.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub fn new_in(alloc: A) -> Self {
|
||||
Self {
|
||||
table: RawTableInner::new_in(alloc),
|
||||
@@ -501,30 +502,30 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
/// Deallocates the table without dropping any entries.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
unsafe fn free_buckets(&mut self) {
|
||||
self.table.free_buckets(TableLayout::new::<T>())
|
||||
self.table.free_buckets(TableLayout::new::<T>());
|
||||
}
|
||||
|
||||
/// Returns pointer to one past last element of data table.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub unsafe fn data_end(&self) -> NonNull<T> {
|
||||
NonNull::new_unchecked(self.table.ctrl.as_ptr().cast())
|
||||
}
|
||||
|
||||
/// Returns pointer to start of data table.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
#[cfg(feature = "nightly")]
|
||||
pub unsafe fn data_start(&self) -> *mut T {
|
||||
self.data_end().as_ptr().wrapping_sub(self.buckets())
|
||||
}
|
||||
|
||||
/// Returns the index of a bucket from a `Bucket`.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub unsafe fn bucket_index(&self, bucket: &Bucket<T>) -> usize {
|
||||
bucket.to_base_index(self.data_end())
|
||||
}
|
||||
|
||||
/// Returns a pointer to an element in the table.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub unsafe fn bucket(&self, index: usize) -> Bucket<T> {
|
||||
debug_assert_ne!(self.table.bucket_mask, 0);
|
||||
debug_assert!(index < self.buckets());
|
||||
@@ -536,7 +537,7 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
#[deprecated(since = "0.8.1", note = "use erase or remove instead")]
|
||||
pub unsafe fn erase_no_drop(&mut self, item: &Bucket<T>) {
|
||||
let index = self.bucket_index(item);
|
||||
self.table.erase(index)
|
||||
self.table.erase(index);
|
||||
}
|
||||
|
||||
/// Erases an element from the table, dropping it in place.
|
||||
@@ -556,7 +557,9 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
pub fn erase_entry(&mut self, hash: u64, eq: impl FnMut(&T) -> bool) -> bool {
|
||||
// Avoid `Option::map` because it bloats LLVM IR.
|
||||
if let Some(bucket) = self.find(hash, eq) {
|
||||
unsafe { self.erase(bucket) };
|
||||
unsafe {
|
||||
self.erase(bucket);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
@@ -585,7 +588,7 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
/// Marks all table buckets as empty without dropping their contents.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
pub fn clear_no_drop(&mut self) {
|
||||
self.table.clear_no_drop()
|
||||
self.table.clear_no_drop();
|
||||
}
|
||||
|
||||
/// Removes all elements from the table without freeing the backing memory.
|
||||
@@ -599,7 +602,7 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
}
|
||||
|
||||
unsafe fn drop_elements(&mut self) {
|
||||
if mem::needs_drop::<T>() && self.len() != 0 {
|
||||
if mem::needs_drop::<T>() && !self.is_empty() {
|
||||
for item in self.iter() {
|
||||
item.drop();
|
||||
}
|
||||
@@ -630,7 +633,7 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
if min_buckets < self.buckets() {
|
||||
// Fast path if the table is empty
|
||||
if self.table.items == 0 {
|
||||
*self = Self::with_capacity_in(min_size, self.table.alloc.clone())
|
||||
*self = Self::with_capacity_in(min_size, self.table.alloc.clone());
|
||||
} else {
|
||||
// Avoid `Result::unwrap_or_else` because it bloats LLVM IR.
|
||||
if self
|
||||
@@ -682,102 +685,18 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
hasher: impl Fn(&T) -> u64,
|
||||
fallibility: Fallibility,
|
||||
) -> Result<(), TryReserveError> {
|
||||
// Avoid `Option::ok_or_else` because it bloats LLVM IR.
|
||||
let new_items = match self.table.items.checked_add(additional) {
|
||||
Some(new_items) => new_items,
|
||||
None => return Err(fallibility.capacity_overflow()),
|
||||
};
|
||||
let full_capacity = bucket_mask_to_capacity(self.table.bucket_mask);
|
||||
if new_items <= full_capacity / 2 {
|
||||
// Rehash in-place without re-allocating if we have plenty of spare
|
||||
// capacity that is locked up due to DELETED entries.
|
||||
self.rehash_in_place(hasher);
|
||||
Ok(())
|
||||
} else {
|
||||
// Otherwise, conservatively resize to at least the next size up
|
||||
// to avoid churning deletes into frequent rehashes.
|
||||
self.resize(
|
||||
usize::max(new_items, full_capacity + 1),
|
||||
hasher,
|
||||
fallibility,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Rehashes the contents of the table in place (i.e. without changing the
|
||||
/// allocation).
|
||||
///
|
||||
/// If `hasher` panics then some the table's contents may be lost.
|
||||
fn rehash_in_place(&mut self, hasher: impl Fn(&T) -> u64) {
|
||||
unsafe {
|
||||
// If the hash function panics then properly clean up any elements
|
||||
// that we haven't rehashed yet. We unfortunately can't preserve the
|
||||
// element since we lost their hash and have no way of recovering it
|
||||
// without risking another panic.
|
||||
self.table.prepare_rehash_in_place();
|
||||
|
||||
let mut guard = guard(&mut self.table, move |self_| {
|
||||
self.table.reserve_rehash_inner(
|
||||
additional,
|
||||
&|table, index| hasher(table.bucket::<T>(index).as_ref()),
|
||||
fallibility,
|
||||
TableLayout::new::<T>(),
|
||||
if mem::needs_drop::<T>() {
|
||||
for i in 0..self_.buckets() {
|
||||
if *self_.ctrl(i) == DELETED {
|
||||
self_.set_ctrl(i, EMPTY);
|
||||
self_.bucket::<T>(i).drop();
|
||||
self_.items -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
self_.growth_left = bucket_mask_to_capacity(self_.bucket_mask) - self_.items;
|
||||
});
|
||||
|
||||
// At this point, DELETED elements are elements that we haven't
|
||||
// rehashed yet. Find them and re-insert them at their ideal
|
||||
// position.
|
||||
'outer: for i in 0..guard.buckets() {
|
||||
if *guard.ctrl(i) != DELETED {
|
||||
continue;
|
||||
}
|
||||
|
||||
'inner: loop {
|
||||
// Hash the current item
|
||||
let item = guard.bucket(i);
|
||||
let hash = hasher(item.as_ref());
|
||||
|
||||
// Search for a suitable place to put it
|
||||
let new_i = guard.find_insert_slot(hash);
|
||||
|
||||
// Probing works by scanning through all of the control
|
||||
// bytes in groups, which may not be aligned to the group
|
||||
// size. If both the new and old position fall within the
|
||||
// same unaligned group, then there is no benefit in moving
|
||||
// it and we can just continue to the next item.
|
||||
if likely(guard.is_in_same_group(i, new_i, hash)) {
|
||||
guard.set_ctrl_h2(i, hash);
|
||||
continue 'outer;
|
||||
}
|
||||
|
||||
// We are moving the current item to a new position. Write
|
||||
// our H2 to the control byte of the new position.
|
||||
let prev_ctrl = guard.replace_ctrl_h2(new_i, hash);
|
||||
if prev_ctrl == EMPTY {
|
||||
guard.set_ctrl(i, EMPTY);
|
||||
// If the target slot is empty, simply move the current
|
||||
// element into the new slot and clear the old control
|
||||
// byte.
|
||||
guard.bucket(new_i).copy_from_nonoverlapping(&item);
|
||||
continue 'outer;
|
||||
} else {
|
||||
// If the target slot is occupied, swap the two elements
|
||||
// and then continue processing the element that we just
|
||||
// swapped into the old slot.
|
||||
debug_assert_eq!(prev_ctrl, DELETED);
|
||||
mem::swap(guard.bucket(new_i).as_mut(), item.as_mut());
|
||||
continue 'inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard.growth_left = bucket_mask_to_capacity(guard.bucket_mask) - guard.items;
|
||||
mem::forget(guard);
|
||||
Some(mem::transmute(ptr::drop_in_place::<T> as unsafe fn(*mut T)))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -790,30 +709,12 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
fallibility: Fallibility,
|
||||
) -> Result<(), TryReserveError> {
|
||||
unsafe {
|
||||
let mut new_table =
|
||||
self.table
|
||||
.prepare_resize(TableLayout::new::<T>(), capacity, fallibility)?;
|
||||
|
||||
// Copy all elements to the new table.
|
||||
for item in self.iter() {
|
||||
// This may panic.
|
||||
let hash = hasher(item.as_ref());
|
||||
|
||||
// We can use a simpler version of insert() here since:
|
||||
// - there are no DELETED entries.
|
||||
// - we know there is enough space in the table.
|
||||
// - all elements are unique.
|
||||
let (index, _) = new_table.prepare_insert_slot(hash);
|
||||
new_table.bucket(index).copy_from_nonoverlapping(&item);
|
||||
}
|
||||
|
||||
// We successfully copied all elements without panicking. Now replace
|
||||
// self with the new table. The old table will have its memory freed but
|
||||
// the items will not be dropped (since they have been moved into the
|
||||
// new table).
|
||||
mem::swap(&mut self.table, &mut new_table);
|
||||
|
||||
Ok(())
|
||||
self.table.resize_inner(
|
||||
capacity,
|
||||
&|table, index| hasher(table.bucket::<T>(index).as_ref()),
|
||||
fallibility,
|
||||
TableLayout::new::<T>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -921,14 +822,14 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
/// Searches for an element in the table.
|
||||
#[inline]
|
||||
pub fn find(&self, hash: u64, mut eq: impl FnMut(&T) -> bool) -> Option<Bucket<T>> {
|
||||
unsafe {
|
||||
for bucket in self.iter_hash(hash) {
|
||||
let elm = bucket.as_ref();
|
||||
if likely(eq(elm)) {
|
||||
return Some(bucket);
|
||||
}
|
||||
}
|
||||
None
|
||||
let result = self.table.find_inner(hash, &mut |index| unsafe {
|
||||
eq(self.bucket(index).as_ref())
|
||||
});
|
||||
|
||||
// Avoid `Option::map` because it bloats LLVM IR.
|
||||
match result {
|
||||
Some(index) => Some(unsafe { self.bucket(index) }),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,19 +915,25 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
///
|
||||
/// This number is a lower bound; the table might be able to hold
|
||||
/// more, but is guaranteed to be able to hold at least this many.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.table.items + self.table.growth_left
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the table.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.table.items
|
||||
}
|
||||
|
||||
/// Returns `true` if the table contains no elements.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Returns the number of buckets in the table.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub fn buckets(&self) -> usize {
|
||||
self.table.bucket_mask + 1
|
||||
}
|
||||
@@ -1035,7 +942,7 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
/// the caller to ensure that the `RawTable` outlives the `RawIter`.
|
||||
/// Because we cannot make the `next` method unsafe on the `RawIter`
|
||||
/// struct, we have to make the `iter` method unsafe.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
pub unsafe fn iter(&self) -> RawIter<T> {
|
||||
let data = Bucket::from_base_index(self.data_end(), 0);
|
||||
RawIter {
|
||||
@@ -1046,12 +953,15 @@ impl<T, A: Allocator + Clone> RawTable<T, A> {
|
||||
|
||||
/// Returns an iterator over occupied buckets that could match a given hash.
|
||||
///
|
||||
/// In rare cases, the iterator may return a bucket with a different hash.
|
||||
/// `RawTable` only stores 7 bits of the hash value, so this iterator may
|
||||
/// return items that have a hash value different than the one provided. You
|
||||
/// should always validate the returned values before using them.
|
||||
///
|
||||
/// It is up to the caller to ensure that the `RawTable` outlives the
|
||||
/// `RawIterHash`. Because we cannot make the `next` method unsafe on the
|
||||
/// `RawIterHash` struct, we have to make the `iter_hash` method unsafe.
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[cfg(feature = "raw")]
|
||||
pub unsafe fn iter_hash(&self, hash: u64) -> RawIterHash<'_, T, A> {
|
||||
RawIterHash::new(self, hash)
|
||||
}
|
||||
@@ -1129,7 +1039,7 @@ unsafe impl<T, A: Allocator + Clone> Send for RawTable<T, A> where T: Send {}
|
||||
unsafe impl<T, A: Allocator + Clone> Sync for RawTable<T, A> where T: Sync {}
|
||||
|
||||
impl<A> RawTableInner<A> {
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
const fn new_in(alloc: A) -> Self {
|
||||
Self {
|
||||
// Be careful to cast the entire slice to a raw pointer.
|
||||
@@ -1158,6 +1068,15 @@ impl<A: Allocator + Clone> RawTableInner<A> {
|
||||
None => return Err(fallibility.capacity_overflow()),
|
||||
};
|
||||
|
||||
// We need an additional check to ensure that the allocation doesn't
|
||||
// exceed `isize::MAX`. We can skip this check on 64-bit systems since
|
||||
// such allocations will never succeed anyways.
|
||||
//
|
||||
// This mirrors what Vec does in the standard library.
|
||||
if mem::size_of::<usize>() < 8 && layout.size() > isize::MAX as usize {
|
||||
return Err(fallibility.capacity_overflow());
|
||||
}
|
||||
|
||||
let ptr: NonNull<u8> = match do_alloc(&alloc, layout) {
|
||||
Ok(block) => block.cast(),
|
||||
Err(_) => return Err(fallibility.alloc_err(layout)),
|
||||
@@ -1225,7 +1144,7 @@ impl<A: Allocator + Clone> RawTableInner<A> {
|
||||
// EMPTY entries. These will unfortunately trigger a
|
||||
// match, but once masked may point to a full bucket that
|
||||
// is already occupied. We detect this situation here and
|
||||
// perform a second scan starting at the begining of the
|
||||
// perform a second scan starting at the beginning of the
|
||||
// table. This second scan is guaranteed to find an empty
|
||||
// slot (due to the load factor) before hitting the trailing
|
||||
// control bytes (containing EMPTY).
|
||||
@@ -1244,6 +1163,32 @@ impl<A: Allocator + Clone> RawTableInner<A> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Searches for an element in the table. This uses dynamic dispatch to reduce the amount of
|
||||
/// code generated, but it is eliminated by LLVM optimizations.
|
||||
#[inline]
|
||||
fn find_inner(&self, hash: u64, eq: &mut dyn FnMut(usize) -> bool) -> Option<usize> {
|
||||
let h2_hash = h2(hash);
|
||||
let mut probe_seq = self.probe_seq(hash);
|
||||
|
||||
loop {
|
||||
let group = unsafe { Group::load(self.ctrl(probe_seq.pos)) };
|
||||
|
||||
for bit in group.match_byte(h2_hash) {
|
||||
let index = (probe_seq.pos + bit) & self.bucket_mask;
|
||||
|
||||
if likely(eq(index)) {
|
||||
return Some(index);
|
||||
}
|
||||
}
|
||||
|
||||
if likely(group.match_empty().any_bit_set()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
probe_seq.move_next(self.bucket_mask);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_mut)]
|
||||
#[inline]
|
||||
unsafe fn prepare_rehash_in_place(&mut self) {
|
||||
@@ -1267,14 +1212,22 @@ impl<A: Allocator + Clone> RawTableInner<A> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
unsafe fn bucket<T>(&self, index: usize) -> Bucket<T> {
|
||||
debug_assert_ne!(self.bucket_mask, 0);
|
||||
debug_assert!(index < self.buckets());
|
||||
Bucket::from_base_index(self.data_end(), index)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
unsafe fn bucket_ptr(&self, index: usize, size_of: usize) -> *mut u8 {
|
||||
debug_assert_ne!(self.bucket_mask, 0);
|
||||
debug_assert!(index < self.buckets());
|
||||
let base: *mut u8 = self.data_end().as_ptr();
|
||||
base.sub((index + 1) * size_of)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn data_end<T>(&self) -> NonNull<T> {
|
||||
NonNull::new_unchecked(self.ctrl.as_ptr().cast())
|
||||
}
|
||||
@@ -1326,7 +1279,7 @@ impl<A: Allocator + Clone> RawTableInner<A> {
|
||||
/// the end of the array.
|
||||
#[inline]
|
||||
unsafe fn set_ctrl_h2(&self, index: usize, hash: u64) {
|
||||
self.set_ctrl(index, h2(hash))
|
||||
self.set_ctrl(index, h2(hash));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1419,6 +1372,179 @@ impl<A: Allocator + Clone> RawTableInner<A> {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Reserves or rehashes to make room for `additional` more elements.
|
||||
///
|
||||
/// This uses dynamic dispatch to reduce the amount of
|
||||
/// code generated, but it is eliminated by LLVM optimizations when inlined.
|
||||
#[allow(clippy::inline_always)]
|
||||
#[inline(always)]
|
||||
unsafe fn reserve_rehash_inner(
|
||||
&mut self,
|
||||
additional: usize,
|
||||
hasher: &dyn Fn(&mut Self, usize) -> u64,
|
||||
fallibility: Fallibility,
|
||||
layout: TableLayout,
|
||||
drop: Option<fn(*mut u8)>,
|
||||
) -> Result<(), TryReserveError> {
|
||||
// Avoid `Option::ok_or_else` because it bloats LLVM IR.
|
||||
let new_items = match self.items.checked_add(additional) {
|
||||
Some(new_items) => new_items,
|
||||
None => return Err(fallibility.capacity_overflow()),
|
||||
};
|
||||
let full_capacity = bucket_mask_to_capacity(self.bucket_mask);
|
||||
if new_items <= full_capacity / 2 {
|
||||
// Rehash in-place without re-allocating if we have plenty of spare
|
||||
// capacity that is locked up due to DELETED entries.
|
||||
self.rehash_in_place(hasher, layout.size, drop);
|
||||
Ok(())
|
||||
} else {
|
||||
// Otherwise, conservatively resize to at least the next size up
|
||||
// to avoid churning deletes into frequent rehashes.
|
||||
self.resize_inner(
|
||||
usize::max(new_items, full_capacity + 1),
|
||||
hasher,
|
||||
fallibility,
|
||||
layout,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a new table of a different size and moves the contents of the
|
||||
/// current table into it.
|
||||
///
|
||||
/// This uses dynamic dispatch to reduce the amount of
|
||||
/// code generated, but it is eliminated by LLVM optimizations when inlined.
|
||||
#[allow(clippy::inline_always)]
|
||||
#[inline(always)]
|
||||
unsafe fn resize_inner(
|
||||
&mut self,
|
||||
capacity: usize,
|
||||
hasher: &dyn Fn(&mut Self, usize) -> u64,
|
||||
fallibility: Fallibility,
|
||||
layout: TableLayout,
|
||||
) -> Result<(), TryReserveError> {
|
||||
let mut new_table = self.prepare_resize(layout, capacity, fallibility)?;
|
||||
|
||||
// Copy all elements to the new table.
|
||||
for i in 0..self.buckets() {
|
||||
if !is_full(*self.ctrl(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This may panic.
|
||||
let hash = hasher(self, i);
|
||||
|
||||
// We can use a simpler version of insert() here since:
|
||||
// - there are no DELETED entries.
|
||||
// - we know there is enough space in the table.
|
||||
// - all elements are unique.
|
||||
let (index, _) = new_table.prepare_insert_slot(hash);
|
||||
|
||||
ptr::copy_nonoverlapping(
|
||||
self.bucket_ptr(i, layout.size),
|
||||
new_table.bucket_ptr(index, layout.size),
|
||||
layout.size,
|
||||
);
|
||||
}
|
||||
|
||||
// We successfully copied all elements without panicking. Now replace
|
||||
// self with the new table. The old table will have its memory freed but
|
||||
// the items will not be dropped (since they have been moved into the
|
||||
// new table).
|
||||
mem::swap(self, &mut new_table);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Rehashes the contents of the table in place (i.e. without changing the
|
||||
/// allocation).
|
||||
///
|
||||
/// If `hasher` panics then some the table's contents may be lost.
|
||||
///
|
||||
/// This uses dynamic dispatch to reduce the amount of
|
||||
/// code generated, but it is eliminated by LLVM optimizations when inlined.
|
||||
#[allow(clippy::inline_always)]
|
||||
#[cfg_attr(feature = "inline-more", inline(always))]
|
||||
#[cfg_attr(not(feature = "inline-more"), inline)]
|
||||
unsafe fn rehash_in_place(
|
||||
&mut self,
|
||||
hasher: &dyn Fn(&mut Self, usize) -> u64,
|
||||
size_of: usize,
|
||||
drop: Option<fn(*mut u8)>,
|
||||
) {
|
||||
// If the hash function panics then properly clean up any elements
|
||||
// that we haven't rehashed yet. We unfortunately can't preserve the
|
||||
// element since we lost their hash and have no way of recovering it
|
||||
// without risking another panic.
|
||||
self.prepare_rehash_in_place();
|
||||
|
||||
let mut guard = guard(self, move |self_| {
|
||||
if let Some(drop) = drop {
|
||||
for i in 0..self_.buckets() {
|
||||
if *self_.ctrl(i) == DELETED {
|
||||
self_.set_ctrl(i, EMPTY);
|
||||
drop(self_.bucket_ptr(i, size_of));
|
||||
self_.items -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
self_.growth_left = bucket_mask_to_capacity(self_.bucket_mask) - self_.items;
|
||||
});
|
||||
|
||||
// At this point, DELETED elements are elements that we haven't
|
||||
// rehashed yet. Find them and re-insert them at their ideal
|
||||
// position.
|
||||
'outer: for i in 0..guard.buckets() {
|
||||
if *guard.ctrl(i) != DELETED {
|
||||
continue;
|
||||
}
|
||||
|
||||
let i_p = guard.bucket_ptr(i, size_of);
|
||||
|
||||
'inner: loop {
|
||||
// Hash the current item
|
||||
let hash = hasher(*guard, i);
|
||||
|
||||
// Search for a suitable place to put it
|
||||
let new_i = guard.find_insert_slot(hash);
|
||||
let new_i_p = guard.bucket_ptr(new_i, size_of);
|
||||
|
||||
// Probing works by scanning through all of the control
|
||||
// bytes in groups, which may not be aligned to the group
|
||||
// size. If both the new and old position fall within the
|
||||
// same unaligned group, then there is no benefit in moving
|
||||
// it and we can just continue to the next item.
|
||||
if likely(guard.is_in_same_group(i, new_i, hash)) {
|
||||
guard.set_ctrl_h2(i, hash);
|
||||
continue 'outer;
|
||||
}
|
||||
|
||||
// We are moving the current item to a new position. Write
|
||||
// our H2 to the control byte of the new position.
|
||||
let prev_ctrl = guard.replace_ctrl_h2(new_i, hash);
|
||||
if prev_ctrl == EMPTY {
|
||||
guard.set_ctrl(i, EMPTY);
|
||||
// If the target slot is empty, simply move the current
|
||||
// element into the new slot and clear the old control
|
||||
// byte.
|
||||
ptr::copy_nonoverlapping(i_p, new_i_p, size_of);
|
||||
continue 'outer;
|
||||
} else {
|
||||
// If the target slot is occupied, swap the two elements
|
||||
// and then continue processing the element that we just
|
||||
// swapped into the old slot.
|
||||
debug_assert_eq!(prev_ctrl, DELETED);
|
||||
ptr::swap_nonoverlapping(i_p, new_i_p, size_of);
|
||||
continue 'inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard.growth_left = bucket_mask_to_capacity(guard.bucket_mask) - guard.items;
|
||||
|
||||
mem::forget(guard);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn free_buckets(&mut self, table_layout: TableLayout) {
|
||||
// Avoid `Option::unwrap_or_else` because it bloats LLVM IR.
|
||||
@@ -1458,7 +1584,7 @@ impl<A: Allocator + Clone> RawTableInner<A> {
|
||||
//
|
||||
// Note that in this context `leading_zeros` refers to the bytes at the
|
||||
// end of a group, while `trailing_zeros` refers to the bytes at the
|
||||
// begining of a group.
|
||||
// beginning of a group.
|
||||
let ctrl = if empty_before.leading_zeros() + empty_after.trailing_zeros() >= Group::WIDTH {
|
||||
DELETED
|
||||
} else {
|
||||
@@ -1528,7 +1654,7 @@ impl<T: Clone, A: Allocator + Clone> Clone for RawTable<T, A> {
|
||||
|
||||
self.clone_from_spec(source, |self_| {
|
||||
// We need to leave the table in an empty state.
|
||||
self_.clear_no_drop()
|
||||
self_.clear_no_drop();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1540,8 +1666,8 @@ trait RawTableClone {
|
||||
unsafe fn clone_from_spec(&mut self, source: &Self, on_panic: impl FnMut(&mut Self));
|
||||
}
|
||||
impl<T: Clone, A: Allocator + Clone> RawTableClone for RawTable<T, A> {
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
default_fn! {
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
unsafe fn clone_from_spec(&mut self, source: &Self, on_panic: impl FnMut(&mut Self)) {
|
||||
self.clone_from_impl(source, on_panic);
|
||||
}
|
||||
@@ -1578,7 +1704,7 @@ impl<T: Clone, A: Allocator + Clone> RawTable<T, A> {
|
||||
// to make sure we drop only the elements that have been
|
||||
// cloned so far.
|
||||
let mut guard = guard((0, &mut *self), |(index, self_)| {
|
||||
if mem::needs_drop::<T>() && self_.len() != 0 {
|
||||
if mem::needs_drop::<T>() && !self_.is_empty() {
|
||||
for i in 0..=*index {
|
||||
if is_full(*self_.table.ctrl(i)) {
|
||||
self_.bucket(i).drop();
|
||||
@@ -1654,7 +1780,7 @@ impl<T: Clone, A: Allocator + Clone> RawTable<T, A> {
|
||||
}
|
||||
|
||||
impl<T, A: Allocator + Clone + Default> Default for RawTable<T, A> {
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::new_in(Default::default())
|
||||
}
|
||||
@@ -1828,7 +1954,7 @@ impl<T> Iterator for RawIterRange<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
// We don't have an item count, so just guess based on the range size.
|
||||
(
|
||||
@@ -1875,7 +2001,7 @@ impl<T> RawIter<T> {
|
||||
/// For the iterator to remain valid, this method must be called once
|
||||
/// for each insert before `next` is called again.
|
||||
///
|
||||
/// This method does not guarantee that an insertion of a bucket witha greater
|
||||
/// This method does not guarantee that an insertion of a bucket with a greater
|
||||
/// index than the last one yielded will be reflected in the iterator.
|
||||
///
|
||||
/// This method should be called _after_ the given insert is made.
|
||||
@@ -1927,7 +2053,7 @@ impl<T> RawIter<T> {
|
||||
// If it did, we're done.
|
||||
// - Otherwise, update the iterator cached group so that it won't
|
||||
// yield a to-be-removed bucket, or _will_ yield a to-be-added bucket.
|
||||
// We'll also need ot update the item count accordingly.
|
||||
// We'll also need to update the item count accordingly.
|
||||
if let Some(index) = self.iter.current_group.lowest_set_bit() {
|
||||
let next_bucket = self.iter.data.next_n(index);
|
||||
if b.as_ptr() > next_bucket.as_ptr() {
|
||||
@@ -2010,7 +2136,7 @@ impl<T> Iterator for RawIter<T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(self.items, Some(self.items))
|
||||
}
|
||||
@@ -2076,7 +2202,7 @@ impl<T, A: Allocator + Clone> Iterator for RawIntoIter<T, A> {
|
||||
unsafe { Some(self.iter.next()?.read()) }
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
@@ -2140,7 +2266,7 @@ impl<T, A: Allocator + Clone> Iterator for RawDrain<'_, T, A> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
@@ -2151,7 +2277,9 @@ impl<T, A: Allocator + Clone> FusedIterator for RawDrain<'_, T, A> {}
|
||||
|
||||
/// Iterator over occupied buckets that could match a given hash.
|
||||
///
|
||||
/// In rare cases, the iterator may return a bucket with a different hash.
|
||||
/// `RawTable` only stores 7 bits of the hash value, so this iterator may return
|
||||
/// items that have a hash value different than the one provided. You should
|
||||
/// always validate the returned values before using them.
|
||||
pub struct RawIterHash<'a, T, A: Allocator + Clone = Global> {
|
||||
inner: RawIterHashInner<'a, A>,
|
||||
_marker: PhantomData<T>,
|
||||
@@ -2174,6 +2302,7 @@ struct RawIterHashInner<'a, A: Allocator + Clone> {
|
||||
|
||||
impl<'a, T, A: Allocator + Clone> RawIterHash<'a, T, A> {
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[cfg(feature = "raw")]
|
||||
fn new(table: &'a RawTable<T, A>, hash: u64) -> Self {
|
||||
RawIterHash {
|
||||
inner: RawIterHashInner::new(&table.table, hash),
|
||||
@@ -2183,6 +2312,7 @@ impl<'a, T, A: Allocator + Clone> RawIterHash<'a, T, A> {
|
||||
}
|
||||
impl<'a, A: Allocator + Clone> RawIterHashInner<'a, A> {
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
#[cfg(feature = "raw")]
|
||||
fn new(table: &'a RawTableInner<A>, hash: u64) -> Self {
|
||||
unsafe {
|
||||
let h2_hash = h2(hash);
|
||||
@@ -2239,6 +2369,20 @@ impl<'a, A: Allocator + Clone> Iterator for RawIterHashInner<'a, A> {
|
||||
mod test_map {
|
||||
use super::*;
|
||||
|
||||
fn rehash_in_place<T>(table: &mut RawTable<T>, hasher: impl Fn(&T) -> u64) {
|
||||
unsafe {
|
||||
table.table.rehash_in_place(
|
||||
&|table, index| hasher(table.bucket::<T>(index).as_ref()),
|
||||
mem::size_of::<T>(),
|
||||
if mem::needs_drop::<T>() {
|
||||
Some(mem::transmute(ptr::drop_in_place::<T> as unsafe fn(*mut T)))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rehash() {
|
||||
let mut table = RawTable::new();
|
||||
@@ -2254,7 +2398,7 @@ mod test_map {
|
||||
assert!(table.find(i + 100, |x| *x == i + 100).is_none());
|
||||
}
|
||||
|
||||
table.rehash_in_place(hasher);
|
||||
rehash_in_place(&mut table, hasher);
|
||||
|
||||
for i in 0..100 {
|
||||
unsafe {
|
||||
|
||||
@@ -28,6 +28,7 @@ impl Group {
|
||||
/// value for an empty hash table.
|
||||
///
|
||||
/// This is guaranteed to be aligned to the group size.
|
||||
#[inline]
|
||||
#[allow(clippy::items_after_statements)]
|
||||
pub const fn static_empty() -> &'static [u8; Group::WIDTH] {
|
||||
#[repr(C)]
|
||||
|
||||
+1
-1
@@ -44,6 +44,6 @@ where
|
||||
{
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
(self.dropfn)(&mut self.value)
|
||||
(self.dropfn)(&mut self.value);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -378,7 +378,7 @@ impl<T, S, A: Allocator + Clone> HashSet<T, S, A> {
|
||||
/// ```
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
pub fn clear(&mut self) {
|
||||
self.map.clear()
|
||||
self.map.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,7 +559,7 @@ where
|
||||
/// ```
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
pub fn reserve(&mut self, additional: usize) {
|
||||
self.map.reserve(additional)
|
||||
self.map.reserve(additional);
|
||||
}
|
||||
|
||||
/// Tries to reserve capacity for at least `additional` more elements to be inserted
|
||||
@@ -601,7 +601,7 @@ where
|
||||
/// ```
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
pub fn shrink_to_fit(&mut self) {
|
||||
self.map.shrink_to_fit()
|
||||
self.map.shrink_to_fit();
|
||||
}
|
||||
|
||||
/// Shrinks the capacity of the set with a lower limit. It will drop
|
||||
@@ -627,7 +627,7 @@ where
|
||||
/// ```
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
pub fn shrink_to(&mut self, min_capacity: usize) {
|
||||
self.map.shrink_to(min_capacity)
|
||||
self.map.shrink_to(min_capacity);
|
||||
}
|
||||
|
||||
/// Visits the values representing the difference,
|
||||
@@ -1168,7 +1168,7 @@ where
|
||||
{
|
||||
#[cfg_attr(feature = "inline-more", inline)]
|
||||
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
|
||||
self.extend(iter.into_iter().cloned());
|
||||
self.extend(iter.into_iter().copied());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -1969,7 +1969,7 @@ mod test_set {
|
||||
let expected = [3, 5, 11, 77];
|
||||
for x in a.intersection(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
i += 1;
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
@@ -1992,7 +1992,7 @@ mod test_set {
|
||||
let expected = [1, 5, 11];
|
||||
for x in a.difference(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
i += 1;
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
@@ -2018,7 +2018,7 @@ mod test_set {
|
||||
let expected = [-2, 1, 5, 11, 14, 22];
|
||||
for x in a.symmetric_difference(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
i += 1;
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
@@ -2048,7 +2048,7 @@ mod test_set {
|
||||
let expected = [-2, 1, 3, 5, 9, 11, 13, 16, 19, 24];
|
||||
for x in a.union(&b) {
|
||||
assert!(expected.contains(x));
|
||||
i += 1
|
||||
i += 1;
|
||||
}
|
||||
assert_eq!(i, expected.len());
|
||||
}
|
||||
@@ -2074,7 +2074,7 @@ mod test_set {
|
||||
fn test_from_iter() {
|
||||
let xs = [1, 2, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
|
||||
let set: HashSet<_> = xs.iter().cloned().collect();
|
||||
let set: HashSet<_> = xs.iter().copied().collect();
|
||||
|
||||
for x in &xs {
|
||||
assert!(set.contains(x));
|
||||
@@ -2236,7 +2236,7 @@ mod test_set {
|
||||
#[test]
|
||||
fn test_retain() {
|
||||
let xs = [1, 2, 3, 4, 5, 6];
|
||||
let mut set: HashSet<i32> = xs.iter().cloned().collect();
|
||||
let mut set: HashSet<i32> = xs.iter().copied().collect();
|
||||
set.retain(|&k| k % 2 == 0);
|
||||
assert_eq!(set.len(), 3);
|
||||
assert!(set.contains(&2));
|
||||
@@ -2278,7 +2278,7 @@ mod test_set {
|
||||
|
||||
const EMPTY_SET: HashSet<u32, MyHasher> = HashSet::with_hasher(MyHasher);
|
||||
|
||||
let mut set = EMPTY_SET.clone();
|
||||
let mut set = EMPTY_SET;
|
||||
set.insert(19);
|
||||
assert!(set.contains(&19));
|
||||
}
|
||||
|
||||
+35
-35
@@ -269,20 +269,20 @@ fn map_seq_par_equivalence_existing_empty_extend_empty() {
|
||||
let mut map_seq = MAP_EXISTING_EMPTY.clone();
|
||||
let mut map_par = MAP_EXISTING_EMPTY.clone();
|
||||
|
||||
map_seq.extend(MAP_EXTENSION_EMPTY.iter().cloned());
|
||||
map_par.par_extend(MAP_EXTENSION_EMPTY.par_iter().cloned());
|
||||
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().cloned().collect::<HashMap<_, _>>();
|
||||
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().cloned());
|
||||
map_par.par_extend(MAP_EXTENSION.par_iter().cloned());
|
||||
map_seq.extend(MAP_EXTENSION.iter().copied());
|
||||
map_par.par_extend(MAP_EXTENSION.par_iter().copied());
|
||||
|
||||
assert_eq3!(map_seq, map_par, expected);
|
||||
}
|
||||
@@ -293,8 +293,8 @@ fn map_seq_par_equivalence_existing_extend_empty() {
|
||||
let mut map_seq = MAP_EXISTING.clone();
|
||||
let mut map_par = MAP_EXISTING.clone();
|
||||
|
||||
map_seq.extend(MAP_EXTENSION_EMPTY.iter().cloned());
|
||||
map_par.par_extend(MAP_EXTENSION_EMPTY.par_iter().cloned());
|
||||
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);
|
||||
}
|
||||
@@ -305,8 +305,8 @@ fn map_seq_par_equivalence_existing_extend() {
|
||||
let mut map_seq = MAP_EXISTING.clone();
|
||||
let mut map_par = MAP_EXISTING.clone();
|
||||
|
||||
map_seq.extend(MAP_EXTENSION.iter().cloned());
|
||||
map_par.par_extend(MAP_EXTENSION.par_iter().cloned());
|
||||
map_seq.extend(MAP_EXTENSION.iter().copied());
|
||||
map_par.par_extend(MAP_EXTENSION.par_iter().copied());
|
||||
|
||||
assert_eq3!(map_seq, map_par, expected);
|
||||
}
|
||||
@@ -423,20 +423,20 @@ fn set_seq_par_equivalence_existing_empty_extend_empty() {
|
||||
let mut set_seq = SET_EXISTING_EMPTY.clone();
|
||||
let mut set_par = SET_EXISTING_EMPTY.clone();
|
||||
|
||||
set_seq.extend(SET_EXTENSION_EMPTY.iter().cloned());
|
||||
set_par.par_extend(SET_EXTENSION_EMPTY.par_iter().cloned());
|
||||
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().cloned().collect::<HashSet<_>>();
|
||||
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().cloned());
|
||||
set_par.par_extend(SET_EXTENSION.par_iter().cloned());
|
||||
set_seq.extend(SET_EXTENSION.iter().copied());
|
||||
set_par.par_extend(SET_EXTENSION.par_iter().copied());
|
||||
|
||||
assert_eq3!(set_seq, set_par, expected);
|
||||
}
|
||||
@@ -447,8 +447,8 @@ fn set_seq_par_equivalence_existing_extend_empty() {
|
||||
let mut set_seq = SET_EXISTING.clone();
|
||||
let mut set_par = SET_EXISTING.clone();
|
||||
|
||||
set_seq.extend(SET_EXTENSION_EMPTY.iter().cloned());
|
||||
set_par.par_extend(SET_EXTENSION_EMPTY.par_iter().cloned());
|
||||
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);
|
||||
}
|
||||
@@ -459,37 +459,37 @@ fn set_seq_par_equivalence_existing_extend() {
|
||||
let mut set_seq = SET_EXISTING.clone();
|
||||
let mut set_par = SET_EXISTING.clone();
|
||||
|
||||
set_seq.extend(SET_EXTENSION.iter().cloned());
|
||||
set_par.par_extend(SET_EXTENSION.par_iter().cloned());
|
||||
set_seq.extend(SET_EXTENSION.iter().copied());
|
||||
set_par.par_extend(SET_EXTENSION.par_iter().copied());
|
||||
|
||||
assert_eq3!(set_seq, set_par, expected);
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref SET_A: HashSet<char> = ['a', 'b', 'c', 'd'].iter().cloned().collect();
|
||||
static ref SET_B: HashSet<char> = ['a', 'b', 'e', 'f'].iter().cloned().collect();
|
||||
static ref SET_DIFF_AB: HashSet<char> = ['c', 'd'].iter().cloned().collect();
|
||||
static ref SET_DIFF_BA: HashSet<char> = ['e', 'f'].iter().cloned().collect();
|
||||
static ref SET_SYMM_DIFF_AB: HashSet<char> = ['c', 'd', 'e', 'f'].iter().cloned().collect();
|
||||
static ref SET_INTERSECTION_AB: HashSet<char> = ['a', 'b'].iter().cloned().collect();
|
||||
static ref SET_A: HashSet<char> = ['a', 'b', 'c', 'd'].iter().copied().collect();
|
||||
static ref SET_B: HashSet<char> = ['a', 'b', 'e', 'f'].iter().copied().collect();
|
||||
static ref SET_DIFF_AB: HashSet<char> = ['c', 'd'].iter().copied().collect();
|
||||
static ref SET_DIFF_BA: HashSet<char> = ['e', 'f'].iter().copied().collect();
|
||||
static ref SET_SYMM_DIFF_AB: HashSet<char> = ['c', 'd', 'e', 'f'].iter().copied().collect();
|
||||
static ref SET_INTERSECTION_AB: HashSet<char> = ['a', 'b'].iter().copied().collect();
|
||||
static ref SET_UNION_AB: HashSet<char> =
|
||||
['a', 'b', 'c', 'd', 'e', 'f'].iter().cloned().collect();
|
||||
['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).cloned().collect::<HashSet<_>>();
|
||||
let diff_ab_seq = SET_A.difference(&*SET_B).copied().collect::<HashSet<_>>();
|
||||
let diff_ab_par = SET_A
|
||||
.par_difference(&*SET_B)
|
||||
.cloned()
|
||||
.copied()
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
assert_eq3!(diff_ab_seq, diff_ab_par, *SET_DIFF_AB);
|
||||
|
||||
let diff_ba_seq = SET_B.difference(&*SET_A).cloned().collect::<HashSet<_>>();
|
||||
let diff_ba_seq = SET_B.difference(&*SET_A).copied().collect::<HashSet<_>>();
|
||||
let diff_ba_par = SET_B
|
||||
.par_difference(&*SET_A)
|
||||
.cloned()
|
||||
.copied()
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
assert_eq3!(diff_ba_seq, diff_ba_par, *SET_DIFF_BA);
|
||||
@@ -499,11 +499,11 @@ fn set_seq_par_equivalence_difference() {
|
||||
fn set_seq_par_equivalence_symmetric_difference() {
|
||||
let symm_diff_ab_seq = SET_A
|
||||
.symmetric_difference(&*SET_B)
|
||||
.cloned()
|
||||
.copied()
|
||||
.collect::<HashSet<_>>();
|
||||
let symm_diff_ab_par = SET_A
|
||||
.par_symmetric_difference(&*SET_B)
|
||||
.cloned()
|
||||
.copied()
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
assert_eq3!(symm_diff_ab_seq, symm_diff_ab_par, *SET_SYMM_DIFF_AB);
|
||||
@@ -511,10 +511,10 @@ fn set_seq_par_equivalence_symmetric_difference() {
|
||||
|
||||
#[test]
|
||||
fn set_seq_par_equivalence_intersection() {
|
||||
let intersection_ab_seq = SET_A.intersection(&*SET_B).cloned().collect::<HashSet<_>>();
|
||||
let intersection_ab_seq = SET_A.intersection(&*SET_B).copied().collect::<HashSet<_>>();
|
||||
let intersection_ab_par = SET_A
|
||||
.par_intersection(&*SET_B)
|
||||
.cloned()
|
||||
.copied()
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
assert_eq3!(
|
||||
@@ -526,8 +526,8 @@ fn set_seq_par_equivalence_intersection() {
|
||||
|
||||
#[test]
|
||||
fn set_seq_par_equivalence_union() {
|
||||
let union_ab_seq = SET_A.union(&*SET_B).cloned().collect::<HashSet<_>>();
|
||||
let union_ab_par = SET_A.par_union(&*SET_B).cloned().collect::<HashSet<_>>();
|
||||
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);
|
||||
}
|
||||
|
||||
+17
-16
@@ -2,35 +2,36 @@
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use rand::{distributions::Alphanumeric, rngs::SmallRng, Rng, SeedableRng};
|
||||
use std::iter;
|
||||
|
||||
#[test]
|
||||
fn test_hashset_insert_remove() {
|
||||
let mut m: HashSet<Vec<char>> = HashSet::new();
|
||||
//let num: u32 = 4096;
|
||||
//let tx: Vec<Vec<u8>> = (0..num).map(|i| (i..(16 + i)).collect()).collect();
|
||||
let seed: [u8; 32] = [
|
||||
130, 220, 246, 217, 111, 124, 221, 189, 190, 234, 121, 93, 67, 95, 100, 43, // again
|
||||
130, 220, 246, 217, 111, 124, 221, 189, 190, 234, 121, 93, 67, 95, 100, 43,
|
||||
];
|
||||
|
||||
let rng = &mut SmallRng::from_seed(seed);
|
||||
let tx: Vec<Vec<char>> = (0..4096)
|
||||
.map(|_| {
|
||||
rng.sample_iter(&Alphanumeric)
|
||||
.take(32)
|
||||
.map(char::from)
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
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`
|
||||
#[allow(clippy::bool_assert_comparison)]
|
||||
for _ in 0..32 {
|
||||
for i in 0..4096 {
|
||||
assert_eq!(m.contains(&tx[i].clone()), false);
|
||||
assert_eq!(m.insert(tx[i].clone()), true);
|
||||
for x in &tx {
|
||||
assert_eq!(m.contains(x), false);
|
||||
assert_eq!(m.insert(x.clone()), true);
|
||||
}
|
||||
for i in 0..4096 {
|
||||
println!("removing {} {:?}", i, tx[i]);
|
||||
assert_eq!(m.remove(&tx[i]), true);
|
||||
for (i, x) in tx.iter().enumerate() {
|
||||
println!("removing {} {:?}", i, x);
|
||||
assert_eq!(m.remove(x), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user