Skip to content

Commit 670c598

Browse files
committed
Optimize Occurrence representation
1 parent dc5cab9 commit 670c598

2 files changed

Lines changed: 48 additions & 128 deletions

File tree

src/parsing.rs

Lines changed: 46 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,33 @@
11
use std::collections::{HashMap, HashSet};
2-
use std::iter::FromIterator;
32

43
pub type CharSet = HashSet<char>;
54
pub type Occurrences = HashMap<char, Vec<Occurrence>>;
65

7-
#[derive(Clone, Copy, Debug)]
6+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
7+
#[repr(align(8))]
88
pub struct Occurrence {
99
pub target_idx: u32,
10-
pub is_start: bool,
11-
pub char: char,
10+
data: u32,
1211
}
1312

14-
impl Eq for Occurrence {}
13+
impl Occurrence {
14+
#[inline]
15+
pub fn new(target_idx: u32, is_start: bool, char: char) -> Self {
16+
assert_eq!(char as u32 & (1 << 31), 0);
17+
Self {
18+
target_idx,
19+
data: ((is_start as u32) << 31) | char as u32,
20+
}
21+
}
1522

16-
impl PartialEq for Occurrence {
17-
fn eq(&self, other: &Occurrence) -> bool {
18-
self.target_idx == other.target_idx
19-
&& self.char == other.char
20-
&& self.is_start == other.is_start
23+
#[inline]
24+
pub fn is_start(self) -> bool {
25+
(self.data >> 31) != 0
26+
}
27+
28+
#[inline]
29+
pub fn char(self) -> char {
30+
unsafe { char::from_u32_unchecked(self.data & !(1 << 31)) }
2131
}
2232
}
2333

@@ -54,11 +64,7 @@ pub fn build_occurrences(query: &QueryChars, string: &str, case_insensitive: boo
5464
occurrences
5565
.entry(key_char)
5666
.or_insert(Vec::new())
57-
.push(Occurrence {
58-
char: original_c,
59-
target_idx: i as u32,
60-
is_start,
61-
});
67+
.push(Occurrence::new(i as u32, is_start, original_c));
6268
}
6369

6470
continue;
@@ -76,11 +82,7 @@ pub fn build_occurrences(query: &QueryChars, string: &str, case_insensitive: boo
7682
occurrences
7783
.entry(key_char)
7884
.or_insert(Vec::new())
79-
.push(Occurrence {
80-
char: original_c,
81-
target_idx: i as u32,
82-
is_start,
83-
});
85+
.push(Occurrence::new(i as u32, is_start, original_c));
8486
}
8587

8688
prev_is_start = is_start;
@@ -141,6 +143,19 @@ mod tests {
141143

142144
use super::{Occurrence, QueryChar, build_occurrences, condense, is_word_sep, process_query};
143145

146+
#[test]
147+
fn occurrence_repr() {
148+
for c in char::MIN..=char::MAX {
149+
let o = Occurrence::new(0, false, c);
150+
assert!(!o.is_start());
151+
assert_eq!(o.char(), c);
152+
153+
let o = Occurrence::new(0, true, c);
154+
assert!(o.is_start());
155+
assert_eq!(o.char(), c);
156+
}
157+
}
158+
144159
#[test]
145160
fn word_seps() {
146161
let seps: Vec<char> = vec![
@@ -204,52 +219,6 @@ mod tests {
204219
);
205220
}
206221

207-
#[test]
208-
fn occurrence_eq() {
209-
let a = Occurrence {
210-
char: 'c',
211-
target_idx: 0,
212-
is_start: true,
213-
};
214-
215-
assert_eq!(
216-
a,
217-
Occurrence {
218-
char: 'c',
219-
target_idx: 0,
220-
is_start: true
221-
}
222-
);
223-
assert_ne!(
224-
a,
225-
Occurrence {
226-
char: 'c',
227-
target_idx: 0,
228-
is_start: false
229-
},
230-
"is_start differs but eq"
231-
);
232-
assert_ne!(
233-
a,
234-
Occurrence {
235-
char: 'c',
236-
target_idx: 1,
237-
is_start: true
238-
},
239-
"target_idx differs but eq"
240-
);
241-
242-
assert_ne!(
243-
a,
244-
Occurrence {
245-
char: 'b',
246-
target_idx: 0,
247-
is_start: true
248-
},
249-
"char differs but eq"
250-
);
251-
}
252-
253222
#[test]
254223
fn occurrences() {
255224
let t = "SoccerCartoonController";
@@ -260,40 +229,17 @@ mod tests {
260229

261230
let s = occs.remove(&'s').expect("Missing s occurrences");
262231

263-
assert_eq!(
264-
s,
265-
vec![Occurrence {
266-
char: 'S',
267-
target_idx: 0,
268-
is_start: true,
269-
}]
270-
);
232+
assert_eq!(s, vec![Occurrence::new(0, true, 'S')]);
271233

272234
let c = occs.remove(&'c').expect("Missing c occurrences");
273235

274236
assert_eq!(
275237
c,
276238
vec![
277-
Occurrence {
278-
char: 'c',
279-
target_idx: 2,
280-
is_start: false,
281-
},
282-
Occurrence {
283-
char: 'c',
284-
target_idx: 3,
285-
is_start: false,
286-
},
287-
Occurrence {
288-
char: 'C',
289-
target_idx: 6,
290-
is_start: true,
291-
},
292-
Occurrence {
293-
char: 'C',
294-
target_idx: 13,
295-
is_start: true,
296-
},
239+
Occurrence::new(2, false, 'c'),
240+
Occurrence::new(3, false, 'c'),
241+
Occurrence::new(6, true, 'C'),
242+
Occurrence::new(13, true, 'C'),
297243
]
298244
);
299245
}
@@ -311,16 +257,8 @@ mod tests {
311257
assert_eq!(
312258
s,
313259
vec![
314-
Occurrence {
315-
char: 'S',
316-
target_idx: 0,
317-
is_start: true,
318-
},
319-
Occurrence {
320-
char: 's',
321-
target_idx: 3,
322-
is_start: false,
323-
}
260+
Occurrence::new(0, true, 'S'),
261+
Occurrence::new(3, false, 's')
324262
]
325263
);
326264

@@ -329,26 +267,10 @@ mod tests {
329267
assert_eq!(
330268
c,
331269
vec![
332-
Occurrence {
333-
char: 'c',
334-
target_idx: 1,
335-
is_start: false,
336-
},
337-
Occurrence {
338-
char: 'c',
339-
target_idx: 2,
340-
is_start: false,
341-
},
342-
Occurrence {
343-
char: 'C',
344-
target_idx: 4,
345-
is_start: true,
346-
},
347-
Occurrence {
348-
char: 'C',
349-
target_idx: 8,
350-
is_start: true,
351-
},
270+
Occurrence::new(1, false, 'c'),
271+
Occurrence::new(2, false, 'c'),
272+
Occurrence::new(4, true, 'C'),
273+
Occurrence::new(8, true, 'C'),
352274
]
353275
);
354276
}

src/search.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
use std::rc::Rc;
2-
31
use bumpalo::{Bump, vec};
42
use hashbrown::HashMap;
53

@@ -150,7 +148,7 @@ impl<'a, 'bump> FuzzySearcher<'a, 'bump> {
150148
if self.case_insensitive {
151149
self.query
152150
.get(query_idx as usize)
153-
.map_or(0, |c| (c.original == occurrence.char) as isize)
151+
.map_or(0, |c| (c.original == occurrence.char()) as isize)
154152
* self.scoring.bonus_match_case
155153
} else {
156154
0
@@ -240,7 +238,7 @@ impl<'a, 'bump> FuzzySearcher<'a, 'bump> {
240238

241239
fn match_calc_score(&self, query_idx: u32, occurrence: &Occurrence, consecutive: u32) -> isize {
242240
consecutive as isize * self.scoring.bonus_consecutive
243-
+ occurrence.is_start as isize * self.scoring.bonus_word_start
241+
+ occurrence.is_start() as isize * self.scoring.bonus_word_start
244242
+ self.case_bonus(query_idx - 1, occurrence)
245243
}
246244
}

0 commit comments

Comments
 (0)