Skip to content

Commit 0aa5c1b

Browse files
committed
Hash tracker
1 parent c21ee7d commit 0aa5c1b

File tree

1 file changed

+52
-14
lines changed

1 file changed

+52
-14
lines changed

src/main.rs

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
use std::cell::{Cell, RefCell};
2+
use std::fmt::{self, Debug, Formatter};
3+
use std::hash::Hash;
4+
use std::marker::PhantomData;
5+
use std::num::NonZeroU128;
6+
7+
use siphasher::sip128::{Hasher128, SipHasher};
28

39
// TODO
410
// - Nested tracked call
@@ -39,7 +45,7 @@ fn describe(image: TrackedImage) -> &'static str {
3945

4046
thread_local! {
4147
static NR: Cell<usize> = Cell::new(0);
42-
static CACHE: RefCell<Vec<(ImageConstraint, &'static str)>> =
48+
static CACHE: RefCell<Vec<(ImageTracker, &'static str)>> =
4349
RefCell::new(vec![]);
4450
}
4551

@@ -48,15 +54,18 @@ fn describe(image: TrackedImage) -> &'static str {
4854
cache
4955
.borrow()
5056
.iter()
51-
.find(|(ct, _)| ct.valid(image.inner))
57+
.find(|(tracker, _)| tracker.valid(image.inner))
5258
.map(|&(_, output)| output)
5359
});
5460

5561
let output = output.unwrap_or_else(|| {
56-
let ct = ImageConstraint::default();
57-
let image = TrackedImage { inner: image.inner, tracker: Some(&ct) };
62+
let tracker = ImageTracker::default();
63+
let image = TrackedImage {
64+
inner: image.inner,
65+
tracker: Some(&tracker),
66+
};
5867
let output = inner(image);
59-
CACHE.with(|cache| cache.borrow_mut().push((ct, output)));
68+
CACHE.with(|cache| cache.borrow_mut().push((tracker, output)));
6069
hit = false;
6170
output
6271
});
@@ -75,40 +84,69 @@ fn describe(image: TrackedImage) -> &'static str {
7584
#[derive(Copy, Clone)]
7685
struct TrackedImage<'a> {
7786
inner: &'a Image,
78-
tracker: Option<&'a ImageConstraint>,
87+
tracker: Option<&'a ImageTracker>,
7988
}
8089

8190
impl<'a> TrackedImage<'a> {
8291
fn width(&self) -> u32 {
8392
let output = self.inner.width();
8493
if let Some(tracker) = &self.tracker {
85-
tracker.width.set(Some(output));
94+
tracker.width.track(&output);
8695
}
8796
output
8897
}
8998

9099
fn height(&self) -> u32 {
91100
let output = self.inner.height();
92101
if let Some(tracker) = &self.tracker {
93-
tracker.height.set(Some(output));
102+
tracker.height.track(&output);
94103
}
95104
output
96105
}
97106
}
98107

99108
#[derive(Debug, Default)]
100-
struct ImageConstraint {
101-
width: Cell<Option<u32>>,
102-
height: Cell<Option<u32>>,
109+
struct ImageTracker {
110+
width: HashTracker<u32>,
111+
height: HashTracker<u32>,
103112
}
104113

105-
impl ImageConstraint {
114+
impl ImageTracker {
106115
fn valid(&self, image: &Image) -> bool {
107-
self.width.get().map_or(true, |v| v == image.width())
108-
&& self.height.get().map_or(true, |v| v == image.height())
116+
self.width.valid(&image.width()) && self.height.valid(&image.height())
117+
}
118+
}
119+
120+
#[derive(Default)]
121+
struct HashTracker<T: Hash>(Cell<Option<NonZeroU128>>, PhantomData<T>);
122+
123+
impl<T: Hash> HashTracker<T> {
124+
fn valid(&self, value: &T) -> bool {
125+
self.0.get().map_or(true, |v| v == siphash(value))
126+
}
127+
128+
fn track(&self, value: &T) {
129+
self.0.set(Some(siphash(value)));
130+
}
131+
}
132+
133+
impl<T: Hash> Debug for HashTracker<T> {
134+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
135+
write!(f, "HashTracker({:?})", self.0)
109136
}
110137
}
111138

139+
/// Produce a non zero 128-bit hash of the value.
140+
fn siphash<T: Hash>(value: &T) -> NonZeroU128 {
141+
let mut state = SipHasher::new();
142+
value.hash(&mut state);
143+
state
144+
.finish128()
145+
.as_u128()
146+
.try_into()
147+
.unwrap_or(NonZeroU128::new(u128::MAX).unwrap())
148+
}
149+
112150
/// A raster image.
113151
struct Image {
114152
width: u32,

0 commit comments

Comments
 (0)