Skip to content

Commit 643f569

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

File tree

4 files changed

+120
-42
lines changed

4 files changed

+120
-42
lines changed

src/bst_slice.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#[cfg(target_pointer_width = "64")]
2+
use crate::BST_BITS;
3+
use crate::{BitSliceType, INLINE_SLICE_PARTS, SmolBitSet};
4+
5+
pub enum BstSlice<'a> {
6+
Inline([BitSliceType; INLINE_SLICE_PARTS]),
7+
Heap(&'a [BitSliceType]),
8+
}
9+
10+
impl<'a> BstSlice<'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);
19+
} else {
20+
let slice = unsafe { sbs.as_slice_unchecked() };
21+
return Self::Heap(slice);
22+
}
23+
}
24+
25+
pub fn slice(&self) -> &[BitSliceType] {
26+
match self {
27+
Self::Inline(items) => items,
28+
Self::Heap(items) => *items,
29+
}
30+
}
31+
}

src/cmp.rs

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
use crate::SmolBitSet;
1+
use crate::{ BitSliceType, SmolBitSet};
2+
use crate::bst_slice::BstSlice;
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 this = BstSlice::new(self);
9+
let other = BstSlice::new(other);
10+
11+
let this = this.slice();
12+
let other = other.slice();
13+
14+
let (long, short) = if this.len() >= other.len() {
15+
(this, other)
16+
} else {
17+
(other, this)
18+
};
19+
20+
let (prefix, suffix) = long.split_at(short.len());
21+
prefix == short && suffix.iter().all(|&x| x == 0)
1922
}
2023
}
2124

@@ -29,27 +32,32 @@ impl cmp::PartialOrd for SmolBitSet {
2932

3033
impl cmp::Ord for SmolBitSet {
3134
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-
}
35+
fn inner(long: &[BitSliceType], short: &[BitSliceType]) -> cmp::Ordering {
36+
let (prefix, suffix) = long.split_at(short.len());
37+
if suffix.iter().any(|&x| x != 0) {
38+
return cmp::Ordering::Greater;
39+
}
40+
41+
for (a, b) in iter::zip(prefix, short).rev() {
42+
let cmp = a.cmp(b);
43+
if cmp != cmp::Ordering::Equal {
44+
return cmp;
4845
}
46+
}
47+
48+
cmp::Ordering::Equal
49+
}
50+
51+
let this = BstSlice::new(self);
52+
let other = BstSlice::new(other);
53+
54+
let this = this.slice();
55+
let other = other.slice();
4956

50-
cmp::Ordering::Equal
51-
},
52-
(a, b) => a.cmp(&b),
57+
if this.len() >= other.len() {
58+
inner(this, other)
59+
} else {
60+
inner(other, this).reverse()
5361
}
5462
}
5563
}

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_slice::BstSlice;
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(BstSlice::new(self).slice()).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_slice;
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)