Skip to content

Commit 9ba0591

Browse files
committed
WIP: Replace HashMap implementation with SwissTable
1 parent 423291f commit 9ba0591

File tree

9 files changed

+1638
-2023
lines changed

9 files changed

+1638
-2023
lines changed

src/libstd/collections/hash/map.rs

Lines changed: 297 additions & 873 deletions
Large diffs are not rendered by default.

src/libstd/collections/hash/mod.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,6 @@
1111
//! Unordered containers, implemented as hash-tables
1212
1313
mod bench;
14-
mod table;
14+
mod raw;
1515
pub mod map;
1616
pub mod set;
17-
18-
trait Recover<Q: ?Sized> {
19-
type Key;
20-
21-
fn get(&self, key: &Q) -> Option<&Self::Key>;
22-
fn take(&mut self, key: &Q) -> Option<Self::Key>;
23-
fn replace(&mut self, key: Self::Key) -> Option<Self::Key>;
24-
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
use super::imp::{BitMaskWord, BITMASK_MASK, BITMASK_SHIFT};
2+
use core::intrinsics;
3+
4+
/// A bit mask which contains the result of a `Match` operation on a `Group` and
5+
/// allows iterating through them.
6+
///
7+
/// The bit mask is arranged so that low-order bits represent lower memory
8+
/// addresses for group match results.
9+
#[derive(Copy, Clone)]
10+
pub struct BitMask(pub BitMaskWord);
11+
12+
impl BitMask {
13+
/// Returns a new `BitMask` with all bits inverted.
14+
#[inline]
15+
#[must_use]
16+
pub fn invert(self) -> BitMask {
17+
BitMask(self.0 ^ BITMASK_MASK)
18+
}
19+
20+
/// Returns a new `BitMask` with the lowest bit removed.
21+
#[inline]
22+
#[must_use]
23+
pub fn remove_lowest_bit(self) -> BitMask {
24+
BitMask(self.0 & (self.0 - 1))
25+
}
26+
/// Returns whether the `BitMask` has at least one set bits.
27+
#[inline]
28+
pub fn any_bit_set(self) -> bool {
29+
self.0 != 0
30+
}
31+
32+
/// Returns the first set bit in the `BitMask`, if there is one.
33+
#[inline]
34+
pub fn lowest_set_bit(self) -> Option<usize> {
35+
if self.0 == 0 {
36+
None
37+
} else {
38+
Some(self.trailing_zeros())
39+
}
40+
}
41+
42+
/// Returns the first set bit in the `BitMask`, if there is one. The
43+
/// bitmask must not be empty.
44+
#[inline]
45+
pub unsafe fn lowest_set_bit_nonzero(self) -> usize {
46+
intrinsics::cttz_nonzero(self.0) as usize >> BITMASK_SHIFT
47+
}
48+
49+
/// Returns the number of trailing zeroes in the `BitMask`.
50+
#[inline]
51+
pub fn trailing_zeros(self) -> usize {
52+
// ARM doesn't have a CTZ instruction, and instead uses RBIT + CLZ.
53+
// However older ARM versions (pre-ARMv7) don't have RBIT and need to
54+
// emulate it instead. Since we only have 1 bit set in each byte we can
55+
// use REV + CLZ instead.
56+
if cfg!(target_arch = "arm") && BITMASK_SHIFT >= 3 {
57+
self.0.swap_bytes().leading_zeros() as usize >> BITMASK_SHIFT
58+
} else {
59+
self.0.trailing_zeros() as usize >> BITMASK_SHIFT
60+
}
61+
}
62+
63+
/// Returns the number of leading zeroes in the `BitMask`.
64+
#[inline]
65+
pub fn leading_zeros(self) -> usize {
66+
self.0.leading_zeros() as usize >> BITMASK_SHIFT
67+
}
68+
}
69+
70+
impl IntoIterator for BitMask {
71+
type Item = usize;
72+
type IntoIter = BitMaskIter;
73+
74+
#[inline]
75+
fn into_iter(self) -> BitMaskIter {
76+
BitMaskIter(self)
77+
}
78+
}
79+
80+
/// Iterator over the contents of a `BitMask`, returning the indicies of set
81+
/// bits.
82+
pub struct BitMaskIter(BitMask);
83+
84+
impl Iterator for BitMaskIter {
85+
type Item = usize;
86+
87+
#[inline]
88+
fn next(&mut self) -> Option<usize> {
89+
let bit = self.0.lowest_set_bit()?;
90+
self.0 = self.0.remove_lowest_bit();
91+
Some(bit)
92+
}
93+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use super::bitmask::BitMask;
2+
use super::EMPTY;
3+
use core::{mem, ptr};
4+
5+
// Use the native word size as the group size. Using a 64-bit group size on
6+
// a 32-bit architecture will just end up being more expensive because
7+
// shifts and multiplies will need to be emulated.
8+
#[cfg(any(
9+
target_pointer_width = "64",
10+
target_arch = "aarch64",
11+
target_arch = "x86_64",
12+
))]
13+
type GroupWord = u64;
14+
#[cfg(all(
15+
target_pointer_width = "32",
16+
not(target_arch = "aarch64"),
17+
not(target_arch = "x86_64"),
18+
))]
19+
type GroupWord = u32;
20+
21+
pub type BitMaskWord = GroupWord;
22+
pub const BITMASK_SHIFT: u32 = 3;
23+
pub const BITMASK_MASK: GroupWord = 0x8080808080808080u64 as GroupWord;
24+
25+
/// Helper function to replicate a byte across a `GroupWord`.
26+
#[inline]
27+
fn repeat(byte: u8) -> GroupWord {
28+
let repeat = byte as GroupWord;
29+
let repeat = repeat | repeat.wrapping_shl(8);
30+
let repeat = repeat | repeat.wrapping_shl(16);
31+
// This last line is a no-op with a 32-bit GroupWord
32+
repeat | repeat.wrapping_shl(32)
33+
}
34+
35+
/// Abstraction over a group of control bytes which can be scanned in
36+
/// parallel.
37+
///
38+
/// This implementation uses a word-sized integer.
39+
pub struct Group(GroupWord);
40+
41+
// We perform all operations in the native endianess, and convert to
42+
// little-endian just before creating a BitMask. The can potentially
43+
// enable the compiler to eliminate unnecessary byte swaps if we are
44+
// only checking whether a BitMask is empty.
45+
impl Group {
46+
/// Number of bytes in the group.
47+
pub const WIDTH: usize = mem::size_of::<Self>();
48+
49+
/// Returns a full group of empty bytes, suitable for use as the initial
50+
/// value for an empty hash table.
51+
///
52+
/// This is guaranteed to be aligned to the group size.
53+
#[inline]
54+
pub fn static_empty() -> &'static [u8] {
55+
#[repr(C)]
56+
struct Dummy {
57+
_align: [GroupWord; 0],
58+
bytes: [u8; Group::WIDTH],
59+
};
60+
const DUMMY: Dummy = Dummy {
61+
_align: [],
62+
bytes: [EMPTY; Group::WIDTH],
63+
};
64+
&DUMMY.bytes
65+
}
66+
67+
/// Loads a group of bytes starting at the given address.
68+
#[inline]
69+
pub unsafe fn load(ptr: *const u8) -> Group {
70+
Group(ptr::read_unaligned(ptr as *const _))
71+
}
72+
73+
/// Loads a group of bytes starting at the given address, which must be
74+
/// aligned to `WIDTH`.
75+
#[inline]
76+
pub unsafe fn load_aligned(ptr: *const u8) -> Group {
77+
Group(ptr::read(ptr as *const _))
78+
}
79+
80+
/// Stores the group of bytes to the given address, which must be
81+
/// aligned to `WIDTH`.
82+
#[inline]
83+
pub unsafe fn store_aligned(&self, ptr: *mut u8) {
84+
ptr::write(ptr as *mut _, self.0);
85+
}
86+
87+
/// Returns a `BitMask` indicating all bytes in the group which *may*
88+
/// have the given value.
89+
///
90+
/// This function may return a false positive in certain cases where
91+
/// the byte in the group differs from the searched value only in its
92+
/// lowest bit. This is fine because:
93+
/// - This never happens for `EMPTY` and `DELETED`, only full entries.
94+
/// - The check for key equality will catch these.
95+
/// - This only happens if there is at least 1 true match.
96+
/// - The chance of this happening is very low (< 1% chance per byte).
97+
#[inline]
98+
pub fn match_byte(&self, byte: u8) -> BitMask {
99+
// This algorithm is derived from
100+
// http://graphics.stanford.edu/~seander/bithacks.html##ValueInWord
101+
let cmp = self.0 ^ repeat(byte);
102+
BitMask((cmp.wrapping_sub(repeat(0x01)) & !cmp & repeat(0x80)).to_le())
103+
}
104+
105+
/// Returns a `BitMask` indicating all bytes in the group which are
106+
/// `EMPTY`.
107+
#[inline]
108+
pub fn match_empty(&self) -> BitMask {
109+
BitMask((self.0 & (self.0 << 1) & repeat(0x80)).to_le())
110+
}
111+
112+
/// Returns a `BitMask` indicating all bytes in the group which are
113+
/// `EMPTY` pr `DELETED`.
114+
#[inline]
115+
pub fn match_empty_or_deleted(&self) -> BitMask {
116+
BitMask((self.0 & repeat(0x80)).to_le())
117+
}
118+
119+
/// Performs the following transformation on all bytes in the group:
120+
/// - `EMPTY => EMPTY`
121+
/// - `DELETED => EMPTY`
122+
/// - `FULL => DELETED`
123+
#[inline]
124+
pub fn convert_special_to_empty_and_full_to_deleted(&self) -> Group {
125+
Group(((self.0 & repeat(0x80)) >> 7) * 0xff)
126+
}
127+
}

0 commit comments

Comments
 (0)