Skip to content

Commit e44ee2d

Browse files
committed
Merge branch 'master' of https://github.com/rust-lang/hashbrown into or-default-entry
2 parents 5b9c757 + e514afe commit e44ee2d

File tree

15 files changed

+362
-64
lines changed

15 files changed

+362
-64
lines changed

.github/workflows/release-plz.yml

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Release-plz
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
8+
jobs:
9+
10+
# Release unpublished packages.
11+
release-plz-release:
12+
name: Release-plz release
13+
runs-on: ubuntu-latest
14+
if: ${{ github.repository_owner == 'rust-lang' }}
15+
permissions:
16+
contents: write
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
- name: Install Rust toolchain
23+
uses: dtolnay/rust-toolchain@stable
24+
- name: Run release-plz
25+
uses: release-plz/[email protected]
26+
with:
27+
command: release
28+
env:
29+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
31+
32+
# Create a PR with the new versions and changelog, preparing the next release.
33+
release-plz-pr:
34+
name: Release-plz PR
35+
runs-on: ubuntu-latest
36+
if: ${{ github.repository_owner == 'rust-lang' }}
37+
permissions:
38+
contents: write
39+
pull-requests: write
40+
concurrency:
41+
group: release-plz-${{ github.ref }}
42+
cancel-in-progress: false
43+
steps:
44+
- name: Checkout repository
45+
uses: actions/checkout@v4
46+
with:
47+
fetch-depth: 0
48+
- name: Install Rust toolchain
49+
uses: dtolnay/rust-toolchain@stable
50+
- name: Run release-plz
51+
uses: release-plz/[email protected]
52+
with:
53+
command: release-pr
54+
env:
55+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
56+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

CHANGELOG.md

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,33 @@
1-
# Change Log
1+
# Changelog
22

33
All notable changes to this project will be documented in this file.
44

5-
The format is based on [Keep a Changelog](https://keepachangelog.com/)
6-
and this project adheres to [Semantic Versioning](https://semver.org/).
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
99

10+
## [0.15.4](https://github.com/rust-lang/hashbrown/compare/v0.15.3...v0.15.4) - 2025-06-05
11+
12+
### Changed
13+
14+
- Removed optional dependency on compiler-builtins. This only affects building as part of `std`.
15+
16+
## [0.15.3](https://github.com/rust-lang/hashbrown/compare/v0.15.2...v0.15.3) - 2025-04-29
17+
18+
### Added
19+
20+
- SIMD implementation for LoongArch (#592, requires nightly)
21+
22+
### Changed
23+
24+
- Optimized insertion path by avoiding an unnecessary `match_empty` (#607)
25+
- Increased minimum table size for small types (#615)
26+
- Dropped FnMut trait bounds from `ExtractIf` data structures (#616)
27+
- Relaxed constraint in `hash_map::EntryRef` insertion methods `K: From<&Q>` to &Q: `Into<K>` (#611)
28+
- Added allocator template argument for `rustc_iter` (#605)
29+
- The `allocator-api2/nightly` feature is no longer enabled by `hashbrown/nightly` (#606)
30+
1031
## [v0.15.2] - 2024-11-14
1132

1233
### Added

Cargo.toml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hashbrown"
3-
version = "0.15.2"
3+
version = "0.15.4"
44
authors = ["Amanieu d'Antras <[email protected]>"]
55
description = "A Rust port of Google's SwissTable hash map"
66
license = "MIT OR Apache-2.0"
@@ -22,7 +22,6 @@ serde = { version = "1.0.25", default-features = false, optional = true }
2222

2323
# When built as part of libstd
2424
core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" }
25-
compiler_builtins = { version = "0.1.2", optional = true }
2625
alloc = { version = "1.0.0", optional = true, package = "rustc-std-workspace-alloc" }
2726

2827
# Support for allocators that use allocator-api2
@@ -35,7 +34,7 @@ equivalent = { version = "1.0", optional = true, default-features = false }
3534

3635
[dev-dependencies]
3736
lazy_static = "1.4"
38-
rand = { version = "0.8.3", features = ["small_rng"] }
37+
rand = { version = "0.9.0", features = ["small_rng"] }
3938
rayon = "1.2"
4039
fnv = "1.0.7"
4140
serde_test = "1.0"
@@ -47,7 +46,7 @@ default = ["default-hasher", "inline-more", "allocator-api2", "equivalent", "raw
4746

4847
# Enables use of nightly features. This is only guaranteed to work on the latest
4948
# version of nightly Rust.
50-
nightly = ["allocator-api2?/nightly", "bumpalo/allocator_api"]
49+
nightly = ["bumpalo/allocator_api"]
5150

5251
# Enables the RustcEntry API used to provide the standard library's Entry API.
5352
rustc-internal-api = []
@@ -56,10 +55,8 @@ rustc-internal-api = []
5655
rustc-dep-of-std = [
5756
"nightly",
5857
"core",
59-
"compiler_builtins",
6058
"alloc",
6159
"rustc-internal-api",
62-
"raw-entry",
6360
]
6461

6562
# Enables the deprecated RawEntry API.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ hashbrown
44
[![Build Status](https://github.com/rust-lang/hashbrown/actions/workflows/rust.yml/badge.svg)](https://github.com/rust-lang/hashbrown/actions)
55
[![Crates.io](https://img.shields.io/crates/v/hashbrown.svg)](https://crates.io/crates/hashbrown)
66
[![Documentation](https://docs.rs/hashbrown/badge.svg)](https://docs.rs/hashbrown)
7-
[![Rust](https://img.shields.io/badge/rust-1.63.0%2B-blue.svg?maxAge=3600)](https://github.com/rust-lang/hashbrown)
7+
[![Rust](https://img.shields.io/badge/rust-1.65.0%2B-blue.svg?maxAge=3600)](https://github.com/rust-lang/hashbrown)
88

99
This crate is a Rust port of Google's high-performance [SwissTable] hash
1010
map, adapted to make it a drop-in replacement for Rust's standard `HashMap`

benches/bench.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ impl Iterator for RandomKeys {
4545

4646
// Just an arbitrary side effect to make the maps not shortcircuit to the non-dropping path
4747
// when dropping maps/entries (most real world usages likely have drop in the key or value)
48-
lazy_static::lazy_static! {
49-
static ref SIDE_EFFECT: AtomicUsize = AtomicUsize::new(0);
50-
}
48+
static SIDE_EFFECT: AtomicUsize = AtomicUsize::new(0);
5149

5250
#[derive(Clone)]
5351
struct DropType(usize);

src/control/group/lsx.rs

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
use super::super::{BitMask, Tag};
2+
use core::mem;
3+
use core::num::NonZeroU16;
4+
5+
use core::arch::loongarch64::*;
6+
use mem::transmute;
7+
8+
pub(crate) type BitMaskWord = u16;
9+
pub(crate) type NonZeroBitMaskWord = NonZeroU16;
10+
pub(crate) const BITMASK_STRIDE: usize = 1;
11+
pub(crate) const BITMASK_MASK: BitMaskWord = 0xffff;
12+
pub(crate) const BITMASK_ITER_MASK: BitMaskWord = !0;
13+
14+
/// Abstraction over a group of control tags which can be scanned in
15+
/// parallel.
16+
///
17+
/// This implementation uses a 128-bit LSX value.
18+
#[derive(Copy, Clone)]
19+
pub(crate) struct Group(v16i8);
20+
21+
// FIXME: https://github.com/rust-lang/rust-clippy/issues/3859
22+
#[allow(clippy::use_self)]
23+
impl Group {
24+
/// Number of bytes in the group.
25+
pub(crate) const WIDTH: usize = mem::size_of::<Self>();
26+
27+
/// Returns a full group of empty tags, suitable for use as the initial
28+
/// value for an empty hash table.
29+
///
30+
/// This is guaranteed to be aligned to the group size.
31+
#[inline]
32+
#[allow(clippy::items_after_statements)]
33+
pub(crate) const fn static_empty() -> &'static [Tag; Group::WIDTH] {
34+
#[repr(C)]
35+
struct AlignedTags {
36+
_align: [Group; 0],
37+
tags: [Tag; Group::WIDTH],
38+
}
39+
const ALIGNED_TAGS: AlignedTags = AlignedTags {
40+
_align: [],
41+
tags: [Tag::EMPTY; Group::WIDTH],
42+
};
43+
&ALIGNED_TAGS.tags
44+
}
45+
46+
/// Loads a group of tags starting at the given address.
47+
#[inline]
48+
#[allow(clippy::cast_ptr_alignment)] // unaligned load
49+
pub(crate) unsafe fn load(ptr: *const Tag) -> Self {
50+
Group(lsx_vld::<0>(ptr.cast()))
51+
}
52+
53+
/// Loads a group of tags starting at the given address, which must be
54+
/// aligned to `mem::align_of::<Group>()`.
55+
#[inline]
56+
#[allow(clippy::cast_ptr_alignment)]
57+
pub(crate) unsafe fn load_aligned(ptr: *const Tag) -> Self {
58+
debug_assert_eq!(ptr.align_offset(mem::align_of::<Self>()), 0);
59+
Group(lsx_vld::<0>(ptr.cast()))
60+
}
61+
62+
/// Stores the group of tags to the given address, which must be
63+
/// aligned to `mem::align_of::<Group>()`.
64+
#[inline]
65+
#[allow(clippy::cast_ptr_alignment)]
66+
pub(crate) unsafe fn store_aligned(self, ptr: *mut Tag) {
67+
debug_assert_eq!(ptr.align_offset(mem::align_of::<Self>()), 0);
68+
lsx_vst::<0>(self.0, ptr.cast());
69+
}
70+
71+
/// Returns a `BitMask` indicating all tags in the group which have
72+
/// the given value.
73+
#[inline]
74+
pub(crate) fn match_tag(self, tag: Tag) -> BitMask {
75+
#[allow(clippy::missing_transmute_annotations)]
76+
unsafe {
77+
let cmp = lsx_vseq_b(self.0, lsx_vreplgr2vr_b(tag.0 as i32));
78+
BitMask(lsx_vpickve2gr_hu::<0>(transmute(lsx_vmskltz_b(cmp))) as u16)
79+
}
80+
}
81+
82+
/// Returns a `BitMask` indicating all tags in the group which are
83+
/// `EMPTY`.
84+
#[inline]
85+
pub(crate) fn match_empty(self) -> BitMask {
86+
#[allow(clippy::missing_transmute_annotations)]
87+
unsafe {
88+
let cmp = lsx_vseqi_b::<{ Tag::EMPTY.0 as i8 as i32 }>(self.0);
89+
BitMask(lsx_vpickve2gr_hu::<0>(transmute(lsx_vmskltz_b(cmp))) as u16)
90+
}
91+
}
92+
93+
/// Returns a `BitMask` indicating all tags in the group which are
94+
/// `EMPTY` or `DELETED`.
95+
#[inline]
96+
pub(crate) fn match_empty_or_deleted(self) -> BitMask {
97+
#[allow(clippy::missing_transmute_annotations)]
98+
unsafe {
99+
// A tag is EMPTY or DELETED iff the high bit is set
100+
BitMask(lsx_vpickve2gr_hu::<0>(transmute(lsx_vmskltz_b(self.0))) as u16)
101+
}
102+
}
103+
104+
/// Returns a `BitMask` indicating all tags in the group which are full.
105+
#[inline]
106+
pub(crate) fn match_full(&self) -> BitMask {
107+
#[allow(clippy::missing_transmute_annotations)]
108+
unsafe {
109+
// A tag is EMPTY or DELETED iff the high bit is set
110+
BitMask(lsx_vpickve2gr_hu::<0>(transmute(lsx_vmskgez_b(self.0))) as u16)
111+
}
112+
}
113+
114+
/// Performs the following transformation on all tags in the group:
115+
/// - `EMPTY => EMPTY`
116+
/// - `DELETED => EMPTY`
117+
/// - `FULL => DELETED`
118+
#[inline]
119+
pub(crate) fn convert_special_to_empty_and_full_to_deleted(self) -> Self {
120+
// Map high_bit = 1 (EMPTY or DELETED) to 1111_1111
121+
// and high_bit = 0 (FULL) to 1000_0000
122+
//
123+
// Here's this logic expanded to concrete values:
124+
// let special = 0 > tag = 1111_1111 (true) or 0000_0000 (false)
125+
// 1111_1111 | 1000_0000 = 1111_1111
126+
// 0000_0000 | 1000_0000 = 1000_0000
127+
#[allow(clippy::missing_transmute_annotations)]
128+
unsafe {
129+
let zero = lsx_vreplgr2vr_b(0);
130+
let special = lsx_vslt_b(self.0, zero);
131+
Group(transmute(lsx_vor_v(
132+
transmute(special),
133+
transmute(lsx_vreplgr2vr_b(Tag::DELETED.0 as i32)),
134+
)))
135+
}
136+
}
137+
}

src/control/group/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ cfg_if! {
2424
))] {
2525
mod neon;
2626
use neon as imp;
27+
} else if #[cfg(all(
28+
feature = "nightly",
29+
target_arch = "loongarch64",
30+
target_feature = "lsx",
31+
not(miri),
32+
))] {
33+
mod lsx;
34+
use lsx as imp;
2735
} else {
2836
mod generic;
2937
use generic as imp;

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@
4242
feature = "nightly",
4343
allow(clippy::incompatible_msrv, internal_features)
4444
)]
45+
#![cfg_attr(
46+
all(feature = "nightly", target_arch = "loongarch64"),
47+
feature(stdarch_loongarch)
48+
)]
4549

4650
/// Default hasher for [`HashMap`] and [`HashSet`].
4751
#[cfg(feature = "default-hasher")]

0 commit comments

Comments
 (0)