Skip to content

Commit 89a205b

Browse files
committed
add a VecMap data structure
1 parent e9de08a commit 89a205b

File tree

3 files changed

+114
-0
lines changed

3 files changed

+114
-0
lines changed

src/librustc_data_structures/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ pub mod tiny_list;
9595
pub mod thin_vec;
9696
pub mod transitive_relation;
9797
pub use ena::unify;
98+
pub mod vec_map;
9899
pub mod vec_linked_list;
99100
pub mod work_queue;
100101
pub mod fingerprint;
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#[cfg(test)]
2+
mod test;
3+
4+
/// A (multi-)map based on a sorted vector. This uses binary search to
5+
/// find the starting index for a given element and can be a fairly
6+
/// efficient data structure, particularly for small-ish sets of data.
7+
///
8+
/// To use, you supply the starting vector along with a "key fn" that
9+
/// extracts the key from each element.
10+
pub struct VecMap<E, KeyFn> {
11+
data: Vec<E>,
12+
key_fn: KeyFn,
13+
}
14+
15+
impl<E, K, KeyFn> VecMap<E, KeyFn>
16+
where
17+
KeyFn: Fn(&E) -> K,
18+
K: Ord + std::fmt::Debug,
19+
{
20+
pub fn new(
21+
mut data: Vec<E>,
22+
key_fn: KeyFn,
23+
) -> Self {
24+
data.sort_by_key(&key_fn);
25+
Self { data, key_fn }
26+
}
27+
28+
/// Extract the first index for the given key using binary search.
29+
/// Returns `None` if there is no such index.
30+
fn get_range(&self, key: &K) -> Option<(usize, usize)> {
31+
match self.data.binary_search_by_key(key, &self.key_fn) {
32+
Ok(mid) => {
33+
// We get back *some* element with the given key -- so
34+
// search backwards to find the *first* one.
35+
//
36+
// (It'd be more efficient to use a "galloping" search
37+
// here, but it's not really worth it for small-ish
38+
// amounts of data.)
39+
let mut start = mid;
40+
while start > 0 {
41+
if (self.key_fn)(&self.data[start - 1]) == *key {
42+
start -= 1;
43+
} else {
44+
break;
45+
}
46+
}
47+
48+
// Now search forward to find the *last* one.
49+
//
50+
// (It'd be more efficient to use a "galloping" search
51+
// here, but it's not really worth it for small-ish
52+
// amounts of data.)
53+
let mut end = mid + 1;
54+
let max = self.data.len();
55+
while end < max {
56+
if (self.key_fn)(&self.data[end]) == *key {
57+
end += 1;
58+
} else {
59+
break;
60+
}
61+
}
62+
63+
Some((start, end))
64+
}
65+
Err(_) => None,
66+
}
67+
}
68+
69+
/// Gets the (first) value associated with a given key.
70+
pub fn get_first(&self, key: &K) -> Option<&E> {
71+
let (start, _) = self.get_range(key)?;
72+
Some(&self.data[start])
73+
}
74+
75+
/// Gets a slice of values associated with the given key.
76+
pub fn get_all(&self, key: &K) -> &[E] {
77+
let (start, end) = self.get_range(key).unwrap_or((0, 0));
78+
&self.data[start..end]
79+
}
80+
81+
/// Gets a slice of values associated with the given key.
82+
pub fn get_iter<'k>(&'k self, key: &'k K) -> impl Iterator<Item = &'k E> {
83+
self.get_all(key).iter()
84+
}
85+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use super::*;
2+
3+
type Element = (usize, &'static str);
4+
5+
fn test_map() -> VecMap<Element, impl Fn(&Element) -> usize> {
6+
let data = vec![(3, "three-a"), (0, "zero"), (3, "three-b"), (22, "twenty-two")];
7+
VecMap::new(data, |&(key, _)| key)
8+
}
9+
10+
#[test]
11+
fn get_first() {
12+
let map = test_map();
13+
assert_eq!(map.get_first(&0), Some(&(0, "zero")));
14+
assert_eq!(map.get_first(&1), None);
15+
assert_eq!(map.get_first(&3), Some(&(3, "three-a")));
16+
assert_eq!(map.get_first(&22), Some(&(22, "twenty-two")));
17+
assert_eq!(map.get_first(&23), None);
18+
}
19+
20+
#[test]
21+
fn get_all() {
22+
let map = test_map();
23+
assert_eq!(map.get_all(&0), &[(0, "zero")]);
24+
assert_eq!(map.get_all(&1), &[]);
25+
assert_eq!(map.get_all(&3), &[(3, "three-a"), (3, "three-b")]);
26+
assert_eq!(map.get_all(&22), &[(22, "twenty-two")]);
27+
assert_eq!(map.get_all(&23), &[]);
28+
}

0 commit comments

Comments
 (0)