Skip to content

Commit c3df917

Browse files
committed
ensure Eq and Ord use logical instead of physical value
1 parent ab9b853 commit c3df917

File tree

4 files changed

+133
-41
lines changed

4 files changed

+133
-41
lines changed

src/bst_iter.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#[cfg(target_pointer_width = "64")]
2+
use crate::BST_BITS;
3+
use crate::{BitSliceType, INLINE_SLICE_PARTS, SmolBitSet};
4+
5+
pub enum BstIter<'a> {
6+
Inline(core::array::IntoIter<BitSliceType, INLINE_SLICE_PARTS>),
7+
Heap(core::slice::Iter<'a, BitSliceType>),
8+
}
9+
10+
impl<'a> BstIter<'a> {
11+
pub fn new(sbs: &'a SmolBitSet) -> Self {
12+
if sbs.is_inline() {
13+
let data = unsafe { sbs.get_inline_data_unchecked() };
14+
#[cfg(target_pointer_width = "32")]
15+
let data = [data as BitSliceType];
16+
#[cfg(target_pointer_width = "64")]
17+
let data = [(data as BitSliceType), ((data >> BST_BITS) as BitSliceType)];
18+
return Self::Inline(data.into_iter());
19+
} else {
20+
let slice = unsafe { sbs.as_slice_unchecked() };
21+
return Self::Heap(slice.iter());
22+
}
23+
}
24+
}
25+
26+
impl Iterator for BstIter<'_> {
27+
type Item = BitSliceType;
28+
29+
fn next(&mut self) -> Option<Self::Item> {
30+
match self {
31+
Self::Inline(iter) => iter.next(),
32+
Self::Heap(iter) => iter.next().copied(),
33+
}
34+
}
35+
36+
fn size_hint(&self) -> (usize, Option<usize>) {
37+
match self {
38+
Self::Inline(iter) => iter.size_hint(),
39+
Self::Heap(iter) => iter.size_hint(),
40+
}
41+
}
42+
}
43+
44+
impl DoubleEndedIterator for BstIter<'_> {
45+
fn next_back(&mut self) -> Option<Self::Item> {
46+
match self {
47+
Self::Inline(iter) => iter.next_back(),
48+
Self::Heap(iter) => iter.next_back().copied(),
49+
}
50+
}
51+
}
52+
53+
impl ExactSizeIterator for BstIter<'_> {
54+
fn len(&self) -> usize {
55+
match self {
56+
Self::Inline(iter) => iter.len(),
57+
Self::Heap(iter) => iter.len(),
58+
}
59+
}
60+
}

src/cmp.rs

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
use crate::SmolBitSet;
2+
use crate::bst_iter::BstIter;
23

3-
use core::cmp;
4+
use core::{cmp, iter};
45

56
impl cmp::PartialEq for SmolBitSet {
67
fn eq(&self, other: &Self) -> bool {
7-
match (self.len(), other.len()) {
8-
(0, 0) => unsafe {
9-
self.get_inline_data_unchecked() == other.get_inline_data_unchecked()
10-
},
11-
(a, b) if a == b => {
12-
let a = unsafe { self.as_slice_unchecked() };
13-
let b = unsafe { other.as_slice_unchecked() };
14-
15-
a == b
16-
}
17-
_ => false,
18-
}
8+
let mut this = BstIter::new(self);
9+
let mut other = BstIter::new(other);
10+
11+
// head must all have the same bits and the tail for the longer one must be all zeroes
12+
this.by_ref().zip(other.by_ref()).all(|(t, o)| t == o)
13+
&& this.all(|x| x == 0)
14+
&& other.all(|x| x == 0)
1915
}
2016
}
2117

@@ -29,27 +25,24 @@ impl cmp::PartialOrd for SmolBitSet {
2925

3026
impl cmp::Ord for SmolBitSet {
3127
fn cmp(&self, other: &Self) -> cmp::Ordering {
32-
match (self.len(), other.len()) {
33-
(0, 0) => unsafe {
34-
self.get_inline_data_unchecked()
35-
.cmp(&other.get_inline_data_unchecked())
36-
},
37-
(0, _) => cmp::Ordering::Less,
38-
(_, 0) => cmp::Ordering::Greater,
39-
(a, b) if a == b => unsafe {
40-
let a = self.as_slice_unchecked();
41-
let b = other.as_slice_unchecked();
42-
43-
for (a, b) in a.iter().zip(b.iter()).rev() {
44-
let cmp = a.cmp(b);
45-
if cmp != cmp::Ordering::Equal {
46-
return cmp;
47-
}
28+
fn inner(long: BstIter<'_>, short: BstIter<'_>) -> cmp::Ordering {
29+
let mut final_cmp = cmp::Ordering::Equal;
30+
for (a, b) in long.zip(short.chain(iter::repeat(0))) {
31+
let cmp = a.cmp(&b);
32+
if cmp != cmp::Ordering::Equal {
33+
final_cmp = cmp;
4834
}
35+
}
36+
final_cmp
37+
}
38+
39+
let this = BstIter::new(self);
40+
let other = BstIter::new(other);
4941

50-
cmp::Ordering::Equal
51-
},
52-
(a, b) => a.cmp(&b),
42+
if this.len() > other.len() {
43+
inner(this, other)
44+
} else {
45+
inner(other, this).reverse()
5346
}
5447
}
5548
}

src/fmt.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use crate::{BST_BITS, BitSliceType, SmolBitSet};
1+
use crate::bst_iter::BstIter;
2+
use crate::{BST_BITS, SmolBitSet};
23

34
#[cfg(feature = "std")]
45
use std::fmt;
@@ -10,14 +11,7 @@ use fmt::{Binary, Debug, Display, Formatter, LowerHex, Octal, Result, UpperHex};
1011

1112
impl Debug for SmolBitSet {
1213
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
13-
let data = if self.is_inline() {
14-
let d = unsafe { self.get_inline_data_unchecked() };
15-
&[d as BitSliceType, (d >> BST_BITS) as BitSliceType]
16-
} else {
17-
unsafe { self.as_slice_unchecked() }
18-
};
19-
20-
f.debug_list().entries(data).finish()
14+
f.debug_list().entries(BstIter::new(self)).finish()
2115
}
2216
}
2317

src/lib.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ macro_rules! highest_set_bit {
6262
}
6363

6464
mod bitop;
65+
mod bst_iter;
6566
mod cmp;
6667
mod fmt;
6768
mod from;
@@ -1123,5 +1124,49 @@ mod tests {
11231124
b <<= 72;
11241125
assert!(a < b);
11251126
}
1127+
1128+
#[test]
1129+
fn eq_but_not_physical_same() {
1130+
let mut a = SmolBitSet::from(u16::MAX);
1131+
let mut b = SmolBitSet::from(0xFFFFu16);
1132+
1133+
// ensure a is larger than b in memory
1134+
a.spill(256);
1135+
1136+
assert_eq!(a, b);
1137+
assert_eq!(b, a);
1138+
1139+
a <<= 55;
1140+
assert_ne!(a, b);
1141+
assert_ne!(b, a);
1142+
1143+
b <<= 55;
1144+
assert_eq!(a, b);
1145+
assert_eq!(b, a);
1146+
}
1147+
1148+
#[test]
1149+
fn ord_but_not_physical_same() {
1150+
let mut a = SmolBitSet::from(0xBEEFu16);
1151+
let mut b = SmolBitSet::from(0x00C5_F00Du32);
1152+
1153+
// ensure a is larger than b in memory
1154+
a.spill(256);
1155+
1156+
assert!(a < b);
1157+
assert!(b > a);
1158+
1159+
a <<= 18;
1160+
assert!(a > b);
1161+
assert!(b < a);
1162+
1163+
a <<= 54;
1164+
assert!(a > b);
1165+
assert!(b < a);
1166+
1167+
b <<= 72;
1168+
assert!(a < b);
1169+
assert!(b > a);
1170+
}
11261171
}
11271172
}

0 commit comments

Comments
 (0)