-
Notifications
You must be signed in to change notification settings - Fork 288
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Further sequester Group
/Tag
code
#568
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
cfg_if! { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just copied directly from the |
||
// Use the SSE2 implementation if possible: it allows us to scan 16 buckets | ||
// at once instead of 8. We don't bother with AVX since it would require | ||
// runtime dispatch and wouldn't gain us much anyways: the probability of | ||
// finding a match drops off drastically after the first few buckets. | ||
// | ||
// I attempted an implementation on ARM using NEON instructions, but it | ||
// turns out that most NEON instructions have multi-cycle latency, which in | ||
// the end outweighs any gains over the generic implementation. | ||
if #[cfg(all( | ||
target_feature = "sse2", | ||
any(target_arch = "x86", target_arch = "x86_64"), | ||
not(miri), | ||
))] { | ||
mod sse2; | ||
use sse2 as imp; | ||
} else if #[cfg(all( | ||
target_arch = "aarch64", | ||
target_feature = "neon", | ||
// NEON intrinsics are currently broken on big-endian targets. | ||
// See https://github.com/rust-lang/stdarch/issues/1484. | ||
target_endian = "little", | ||
not(miri), | ||
))] { | ||
mod neon; | ||
use neon as imp; | ||
} else { | ||
mod generic; | ||
use generic as imp; | ||
} | ||
} | ||
pub(crate) use self::imp::Group; | ||
pub(super) use self::imp::{ | ||
BitMaskWord, NonZeroBitMaskWord, BITMASK_ITER_MASK, BITMASK_MASK, BITMASK_STRIDE, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
mod bitmask; | ||
mod group; | ||
mod tag; | ||
|
||
use self::bitmask::BitMask; | ||
pub(crate) use self::{ | ||
bitmask::BitMaskIter, | ||
group::Group, | ||
tag::{Tag, TagSliceExt}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
use core::{fmt, mem}; | ||
|
||
/// Single tag in a control group. | ||
#[derive(Copy, Clone, PartialEq, Eq)] | ||
#[repr(transparent)] | ||
pub(crate) struct Tag(pub(super) u8); | ||
impl Tag { | ||
/// Control tag value for an empty bucket. | ||
pub(crate) const EMPTY: Tag = Tag(0b1111_1111); | ||
|
||
/// Control tag value for a deleted bucket. | ||
pub(crate) const DELETED: Tag = Tag(0b1000_0000); | ||
|
||
/// Checks whether a control tag represents a full bucket (top bit is clear). | ||
#[inline] | ||
pub(crate) const fn is_full(self) -> bool { | ||
self.0 & 0x80 == 0 | ||
} | ||
|
||
/// Checks whether a control tag represents a special value (top bit is set). | ||
#[inline] | ||
pub(crate) const fn is_special(self) -> bool { | ||
self.0 & 0x80 != 0 | ||
} | ||
|
||
/// Checks whether a special control value is EMPTY (just check 1 bit). | ||
#[inline] | ||
pub(crate) const fn special_is_empty(self) -> bool { | ||
debug_assert!(self.is_special()); | ||
self.0 & 0x01 != 0 | ||
} | ||
|
||
/// Creates a control tag representing a full bucket with the given hash. | ||
#[inline] | ||
#[allow(clippy::cast_possible_truncation)] | ||
pub(crate) const fn full(hash: u64) -> Tag { | ||
// Constant for function that grabs the top 7 bits of the hash. | ||
const MIN_HASH_LEN: usize = if mem::size_of::<usize>() < mem::size_of::<u64>() { | ||
mem::size_of::<usize>() | ||
} else { | ||
mem::size_of::<u64>() | ||
}; | ||
|
||
// Grab the top 7 bits of the hash. While the hash is normally a full 64-bit | ||
// value, some hash functions (such as FxHash) produce a usize result | ||
// instead, which means that the top 32 bits are 0 on 32-bit platforms. | ||
// So we use MIN_HASH_LEN constant to handle this. | ||
let top7 = hash >> (MIN_HASH_LEN * 8 - 7); | ||
Tag((top7 & 0x7f) as u8) // truncation | ||
} | ||
} | ||
impl fmt::Debug for Tag { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
if self.is_special() { | ||
if self.special_is_empty() { | ||
f.pad("EMPTY") | ||
} else { | ||
f.pad("DELETED") | ||
} | ||
} else { | ||
f.debug_tuple("full").field(&(self.0 & 0x7F)).finish() | ||
} | ||
} | ||
} | ||
Comment on lines
+52
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added this to replace the derived debug to help me debug the code I'm going to add in a future PR, so it's a bit easier to read. I figure that if we care about the additional compile time added by this one debug impl so much we can There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't a problem for compilation time since it's not a generic function. |
||
|
||
/// Extension trait for slices of tags. | ||
pub(crate) trait TagSliceExt { | ||
/// Fills the control with the given tag. | ||
fn fill_tag(&mut self, tag: Tag); | ||
|
||
/// Clears out the control. | ||
#[inline] | ||
fn fill_empty(&mut self) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs |
||
self.fill_tag(Tag::EMPTY) | ||
} | ||
} | ||
impl TagSliceExt for [Tag] { | ||
#[inline] | ||
fn fill_tag(&mut self, tag: Tag) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs |
||
// SAFETY: We have access to the entire slice, so, we can write to the entire slice. | ||
unsafe { self.as_mut_ptr().write_bytes(tag.0, self.len()) } | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iterators in general shouldn't implement
Copy
, for better or worse.