Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
# Disabled due to issues with cross-rs
#x86_64-pc-windows-gnu,
]
channel: [1.80.0, nightly]
channel: [1.84.0, nightly]
include:
- os: macos-latest
target: aarch64-apple-darwin
Expand All @@ -64,10 +64,10 @@ jobs:
channel: nightly
- os: macos-latest
target: aarch64-apple-darwin
channel: 1.80.0
channel: 1.84.0
- os: windows-latest
target: x86_64-pc-windows-msvc
channel: 1.80.0
channel: 1.84.0
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
channel: beta
Expand Down
41 changes: 32 additions & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,29 @@ keywords = ["hash", "no_std", "hashmap", "swisstable"]
categories = ["data-structures", "no-std"]
exclude = [".github", "/ci/*"]
edition = "2021"
rust-version = "1.65.0"
rust-version = "1.84.0"

[lints.rust]
missing_docs = "warn"
unreachable_pub = "warn"
unsafe_op_in_unsafe_fn = "warn"

# rust_2018_idioms
bare_trait_objects = "warn"
elided_lifetimes_in_paths = "warn"
ellipsis_inclusive_range_patterns = "warn"
explicit_outlives_requirements = "warn"
unused_extern_crates = "warn"
Comment on lines +20 to +25
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Rather than enable the lint group here, I decided to just manually add these lints to the list since it avoids lint group priority issues. Since this is 2018 idioms, I doubt that the group will gain any new additions any time soon.


[lints.clippy]
doc_markdown = "allow"
manual_map = "allow"
missing_errors_doc = "allow"
missing_safety_doc = "allow"
module_name_repetitions = "allow"
must_use_candidate = "allow"
option_if_let_else = "allow"
redundant_else = "allow"

[dependencies]
# For the default hasher
Expand All @@ -26,7 +48,7 @@ alloc = { version = "1.0.0", optional = true, package = "rustc-std-workspace-all

# Support for allocators that use allocator-api2
allocator-api2 = { version = "0.2.9", optional = true, default-features = false, features = [
"alloc",
"alloc",
] }

# Equivalent trait which can be shared with other hash table implementations.
Expand All @@ -50,7 +72,13 @@ bumpalo = { version = "3.13.0", features = ["allocator-api2"] }
libc = "0.2.155"

[features]
default = ["default-hasher", "inline-more", "allocator-api2", "equivalent", "raw-entry"]
default = [
"default-hasher",
"inline-more",
"allocator-api2",
"equivalent",
"raw-entry",
]

# Enables use of nightly features. This is only guaranteed to work on the latest
# version of nightly Rust.
Expand All @@ -60,12 +88,7 @@ nightly = ["foldhash?/nightly", "bumpalo/allocator_api"]
rustc-internal-api = []

# Internal feature used when building as part of the standard library.
rustc-dep-of-std = [
"nightly",
"core",
"alloc",
"rustc-internal-api",
]
rustc-dep-of-std = ["nightly", "core", "alloc", "rustc-internal-api"]

# Enables serde support.
serde = ["dep:serde_core", "dep:serde"]
Expand Down
8 changes: 4 additions & 4 deletions benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This benchmark suite contains some benchmarks along a set of dimensions:
// Hasher: std default (SipHash) and crate default (foldhash).
// Int key distribution: low bit heavy, top bit heavy, and random.
// Task: basic functionality: insert, insert_erase, lookup, lookup_fail, iter
//! This benchmark suite contains some benchmarks along a set of dimensions:
//! * Hasher: std default (SipHash) and crate default (foldhash).
//! * Int key distribution: low bit heavy, top bit heavy, and random.
//! * Task: basic functionality: insert, insert_erase, lookup, lookup_fail, iter
#![feature(test)]

extern crate test;
Expand Down
1 change: 1 addition & 0 deletions benches/with_capacity.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![expect(missing_docs)] // https://github.com/rust-lang/rust/issues/137561
#![feature(test)]

extern crate test;
Expand Down
2 changes: 1 addition & 1 deletion ci/tools.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ if retry rustup component add rustfmt ; then
fi

if retry rustup component add clippy ; then
cargo clippy --all --tests --features serde,rayon -- -D clippy::all
cargo clippy --all --tests --features serde,rayon -- -D warnings
Copy link
Contributor Author

@clarfonthey clarfonthey Dec 23, 2025

Choose a reason for hiding this comment

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

-D clippy::all has a higher priority than the various lints in Cargo.toml, so, this ends up ignoring our lint preferences to allow some lints, but -D warnings does what we want here.

fi

if command -v shellcheck ; then
Expand Down
2 changes: 1 addition & 1 deletion clippy.toml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
doc-valid-idents = [ "CppCon", "SwissTable", "SipHash", "HashDoS" ]
doc-valid-idents = ["CppCon", "SwissTable", "SipHash", "HashDoS"]
14 changes: 2 additions & 12 deletions src/control/bitmask.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use super::group::{
BitMaskWord, NonZeroBitMaskWord, BITMASK_ITER_MASK, BITMASK_MASK, BITMASK_STRIDE,
};
use super::group::{BitMaskWord, NonZeroBitMaskWord, BITMASK_ITER_MASK, BITMASK_STRIDE};

/// A bit mask which contains the result of a `Match` operation on a `Group` and
/// allows iterating through them.
Expand All @@ -21,16 +19,8 @@ use super::group::{
#[derive(Copy, Clone)]
pub(crate) struct BitMask(pub(crate) BitMaskWord);

#[allow(clippy::use_self)]
#[expect(clippy::use_self)]
impl BitMask {
/// Returns a new `BitMask` with all bits inverted.
#[inline]
#[must_use]
#[allow(dead_code)]
pub(crate) fn invert(self) -> Self {
BitMask(self.0 ^ BITMASK_MASK)
}

/// Returns a new `BitMask` with the lowest bit removed.
#[inline]
#[must_use]
Expand Down
22 changes: 12 additions & 10 deletions src/control/group/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ pub(crate) type BitMaskWord = GroupWord;
pub(crate) type NonZeroBitMaskWord = NonZeroGroupWord;
pub(crate) const BITMASK_STRIDE: usize = 8;
// We only care about the highest bit of each tag for the mask.
#[allow(clippy::cast_possible_truncation, clippy::unnecessary_cast)]
pub(crate) const BITMASK_MASK: BitMaskWord = u64::from_ne_bytes([Tag::DELETED.0; 8]) as GroupWord;
#[expect(clippy::cast_possible_truncation, clippy::unnecessary_cast)]
const BITMASK_MASK: BitMaskWord = u64::from_ne_bytes([Tag::DELETED.0; 8]) as GroupWord;
pub(crate) const BITMASK_ITER_MASK: BitMaskWord = !0;

/// Helper function to replicate a tag across a `GroupWord`.
Expand All @@ -45,7 +45,7 @@ pub(crate) struct Group(GroupWord);
// 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.
#[allow(clippy::use_self)]
#[expect(clippy::use_self)]
impl Group {
/// Number of bytes in the group.
pub(crate) const WIDTH: usize = mem::size_of::<Self>();
Expand All @@ -70,27 +70,29 @@ impl Group {

/// Loads a group of tags starting at the given address.
#[inline]
#[allow(clippy::cast_ptr_alignment)] // unaligned load
#[expect(clippy::cast_ptr_alignment)] // unaligned load
pub(crate) unsafe fn load(ptr: *const Tag) -> Self {
Group(ptr::read_unaligned(ptr.cast()))
unsafe { Group(ptr::read_unaligned(ptr.cast())) }
}

/// Loads a group of tags starting at the given address, which must be
/// aligned to `mem::align_of::<Group>()`.
#[inline]
#[allow(clippy::cast_ptr_alignment)]
#[expect(clippy::cast_ptr_alignment)]
pub(crate) unsafe fn load_aligned(ptr: *const Tag) -> Self {
debug_assert_eq!(ptr.align_offset(mem::align_of::<Self>()), 0);
Group(ptr::read(ptr.cast()))
unsafe { Group(ptr::read(ptr.cast())) }
}

/// Stores the group of tags to the given address, which must be
/// aligned to `mem::align_of::<Group>()`.
#[inline]
#[allow(clippy::cast_ptr_alignment)]
#[expect(clippy::cast_ptr_alignment)]
pub(crate) unsafe fn store_aligned(self, ptr: *mut Tag) {
debug_assert_eq!(ptr.align_offset(mem::align_of::<Self>()), 0);
ptr::write(ptr.cast(), self.0);
unsafe {
ptr::write(ptr.cast(), self.0);
}
}

/// Returns a `BitMask` indicating all tags in the group which *may*
Expand Down Expand Up @@ -132,7 +134,7 @@ impl Group {
/// Returns a `BitMask` indicating all tags in the group which are full.
#[inline]
pub(crate) fn match_full(self) -> BitMask {
self.match_empty_or_deleted().invert()
BitMask(self.match_empty_or_deleted().0 ^ BITMASK_MASK)
}

/// Performs the following transformation on all tags in the group:
Expand Down
19 changes: 10 additions & 9 deletions src/control/group/lsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use core::arch::loongarch64::*;
pub(crate) type BitMaskWord = u16;
pub(crate) type NonZeroBitMaskWord = NonZeroU16;
pub(crate) const BITMASK_STRIDE: usize = 1;
pub(crate) const BITMASK_MASK: BitMaskWord = 0xffff;
pub(crate) const BITMASK_ITER_MASK: BitMaskWord = !0;

/// Abstraction over a group of control tags which can be scanned in
Expand All @@ -18,7 +17,7 @@ pub(crate) const BITMASK_ITER_MASK: BitMaskWord = !0;
pub(crate) struct Group(m128i);

// FIXME: https://github.com/rust-lang/rust-clippy/issues/3859
#[allow(clippy::use_self)]
#[expect(clippy::use_self)]
impl Group {
/// Number of bytes in the group.
pub(crate) const WIDTH: usize = mem::size_of::<Self>();
Expand All @@ -28,7 +27,7 @@ impl Group {
///
/// This is guaranteed to be aligned to the group size.
#[inline]
#[allow(clippy::items_after_statements)]
#[expect(clippy::items_after_statements)]
pub(crate) const fn static_empty() -> &'static [Tag; Group::WIDTH] {
#[repr(C)]
struct AlignedTags {
Expand All @@ -44,27 +43,29 @@ impl Group {

/// Loads a group of tags starting at the given address.
#[inline]
#[allow(clippy::cast_ptr_alignment)] // unaligned load
#[expect(clippy::cast_ptr_alignment)] // unaligned load
pub(crate) unsafe fn load(ptr: *const Tag) -> Self {
Group(lsx_vld::<0>(ptr.cast()))
unsafe { Group(lsx_vld::<0>(ptr.cast())) }
}

/// Loads a group of tags starting at the given address, which must be
/// aligned to `mem::align_of::<Group>()`.
#[inline]
#[allow(clippy::cast_ptr_alignment)]
#[expect(clippy::cast_ptr_alignment)]
pub(crate) unsafe fn load_aligned(ptr: *const Tag) -> Self {
debug_assert_eq!(ptr.align_offset(mem::align_of::<Self>()), 0);
Group(lsx_vld::<0>(ptr.cast()))
unsafe { Group(lsx_vld::<0>(ptr.cast())) }
}

/// Stores the group of tags to the given address, which must be
/// aligned to `mem::align_of::<Group>()`.
#[inline]
#[allow(clippy::cast_ptr_alignment)]
#[expect(clippy::cast_ptr_alignment)]
pub(crate) unsafe fn store_aligned(self, ptr: *mut Tag) {
debug_assert_eq!(ptr.align_offset(mem::align_of::<Self>()), 0);
lsx_vst::<0>(self.0, ptr.cast());
unsafe {
lsx_vst::<0>(self.0, ptr.cast());
}
}

/// Returns a `BitMask` indicating all tags in the group which have
Expand Down
4 changes: 1 addition & 3 deletions src/control/group/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,4 @@ cfg_if! {
}
}
pub(crate) use self::imp::Group;
pub(super) use self::imp::{
BitMaskWord, NonZeroBitMaskWord, BITMASK_ITER_MASK, BITMASK_MASK, BITMASK_STRIDE,
};
pub(super) use self::imp::{BitMaskWord, NonZeroBitMaskWord, BITMASK_ITER_MASK, BITMASK_STRIDE};
17 changes: 9 additions & 8 deletions src/control/group/neon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use core::num::NonZeroU64;
pub(crate) type BitMaskWord = u64;
pub(crate) type NonZeroBitMaskWord = NonZeroU64;
pub(crate) const BITMASK_STRIDE: usize = 8;
pub(crate) const BITMASK_MASK: BitMaskWord = !0;
pub(crate) const BITMASK_ITER_MASK: BitMaskWord = 0x8080_8080_8080_8080;

/// Abstraction over a group of control tags which can be scanned in
Expand All @@ -16,7 +15,7 @@ pub(crate) const BITMASK_ITER_MASK: BitMaskWord = 0x8080_8080_8080_8080;
#[derive(Copy, Clone)]
pub(crate) struct Group(neon::uint8x8_t);

#[allow(clippy::use_self)]
#[expect(clippy::use_self)]
impl Group {
/// Number of bytes in the group.
pub(crate) const WIDTH: usize = mem::size_of::<Self>();
Expand All @@ -41,27 +40,29 @@ impl Group {

/// Loads a group of tags starting at the given address.
#[inline]
#[allow(clippy::cast_ptr_alignment)] // unaligned load
#[expect(clippy::cast_ptr_alignment)] // unaligned load
pub(crate) unsafe fn load(ptr: *const Tag) -> Self {
Group(neon::vld1_u8(ptr.cast()))
unsafe { Group(neon::vld1_u8(ptr.cast())) }
}

/// Loads a group of tags starting at the given address, which must be
/// aligned to `mem::align_of::<Group>()`.
#[inline]
#[allow(clippy::cast_ptr_alignment)]
#[expect(clippy::cast_ptr_alignment)]
pub(crate) unsafe fn load_aligned(ptr: *const Tag) -> Self {
debug_assert_eq!(ptr.align_offset(mem::align_of::<Self>()), 0);
Group(neon::vld1_u8(ptr.cast()))
unsafe { Group(neon::vld1_u8(ptr.cast())) }
}

/// Stores the group of tags to the given address, which must be
/// aligned to `mem::align_of::<Group>()`.
#[inline]
#[allow(clippy::cast_ptr_alignment)]
#[expect(clippy::cast_ptr_alignment)]
pub(crate) unsafe fn store_aligned(self, ptr: *mut Tag) {
debug_assert_eq!(ptr.align_offset(mem::align_of::<Self>()), 0);
neon::vst1_u8(ptr.cast(), self.0);
unsafe {
neon::vst1_u8(ptr.cast(), self.0);
}
}

/// Returns a `BitMask` indicating all tags in the group which *may*
Expand Down
Loading