Skip to content

Commit 068c856

Browse files
authored
Merge pull request #2040 from alecchendev/2023-02-indexed-map-btreeset-to-vec
Replace `BTreeSet` in `IndexedMap` with sorted `Vec`
2 parents 558b2f2 + 62a88f9 commit 068c856

File tree

3 files changed

+77
-33
lines changed

3 files changed

+77
-33
lines changed

fuzz/src/indexedmap.rs

+39-15
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,27 @@ use hashbrown::HashSet;
1313

1414
use crate::utils::test_logger;
1515

16-
fn check_eq(btree: &BTreeMap<u8, u8>, indexed: &IndexedMap<u8, u8>) {
16+
use std::ops::{RangeBounds, Bound};
17+
18+
struct ExclLowerInclUpper(u8, u8);
19+
impl RangeBounds<u8> for ExclLowerInclUpper {
20+
fn start_bound(&self) -> Bound<&u8> { Bound::Excluded(&self.0) }
21+
fn end_bound(&self) -> Bound<&u8> { Bound::Included(&self.1) }
22+
}
23+
struct ExclLowerExclUpper(u8, u8);
24+
impl RangeBounds<u8> for ExclLowerExclUpper {
25+
fn start_bound(&self) -> Bound<&u8> { Bound::Excluded(&self.0) }
26+
fn end_bound(&self) -> Bound<&u8> { Bound::Excluded(&self.1) }
27+
}
28+
29+
fn check_eq(btree: &BTreeMap<u8, u8>, mut indexed: IndexedMap<u8, u8>) {
1730
assert_eq!(btree.len(), indexed.len());
1831
assert_eq!(btree.is_empty(), indexed.is_empty());
1932

2033
let mut btree_clone = btree.clone();
2134
assert!(btree_clone == *btree);
2235
let mut indexed_clone = indexed.clone();
23-
assert!(indexed_clone == *indexed);
36+
assert!(indexed_clone == indexed);
2437

2538
for k in 0..=255 {
2639
assert_eq!(btree.contains_key(&k), indexed.contains_key(&k));
@@ -43,16 +56,27 @@ fn check_eq(btree: &BTreeMap<u8, u8>, indexed: &IndexedMap<u8, u8>) {
4356
}
4457

4558
const STRIDE: u8 = 16;
46-
for k in 0..=255/STRIDE {
47-
let lower_bound = k * STRIDE;
48-
let upper_bound = lower_bound + (STRIDE - 1);
49-
let mut btree_iter = btree.range(lower_bound..=upper_bound);
50-
let mut indexed_iter = indexed.range(lower_bound..=upper_bound);
51-
loop {
52-
let b_v = btree_iter.next();
53-
let i_v = indexed_iter.next();
54-
assert_eq!(b_v, i_v);
55-
if b_v.is_none() { break; }
59+
for range_type in 0..4 {
60+
for k in 0..=255/STRIDE {
61+
let lower_bound = k * STRIDE;
62+
let upper_bound = lower_bound + (STRIDE - 1);
63+
macro_rules! range { ($map: expr) => {
64+
match range_type {
65+
0 => $map.range(lower_bound..upper_bound),
66+
1 => $map.range(lower_bound..=upper_bound),
67+
2 => $map.range(ExclLowerInclUpper(lower_bound, upper_bound)),
68+
3 => $map.range(ExclLowerExclUpper(lower_bound, upper_bound)),
69+
_ => unreachable!(),
70+
}
71+
} }
72+
let mut btree_iter = range!(btree);
73+
let mut indexed_iter = range!(indexed);
74+
loop {
75+
let b_v = btree_iter.next();
76+
let i_v = indexed_iter.next();
77+
assert_eq!(b_v, i_v);
78+
if b_v.is_none() { break; }
79+
}
5680
}
5781
}
5882

@@ -91,15 +115,15 @@ pub fn do_test(data: &[u8]) {
91115
let prev_value_i = indexed.insert(tuple[0], tuple[1]);
92116
assert_eq!(prev_value_b, prev_value_i);
93117
}
94-
check_eq(&btree, &indexed);
118+
check_eq(&btree, indexed.clone());
95119

96120
// Now, modify the maps in all the ways we have to do so, checking that the maps remain
97121
// equivalent as we go.
98122
for (k, v) in indexed.unordered_iter_mut() {
99123
*v = *k;
100124
*btree.get_mut(k).unwrap() = *k;
101125
}
102-
check_eq(&btree, &indexed);
126+
check_eq(&btree, indexed.clone());
103127

104128
for k in 0..=255 {
105129
match btree.entry(k) {
@@ -124,7 +148,7 @@ pub fn do_test(data: &[u8]) {
124148
},
125149
}
126150
}
127-
check_eq(&btree, &indexed);
151+
check_eq(&btree, indexed);
128152
}
129153

130154
pub fn indexedmap_test<Out: test_logger::Output>(data: &[u8], _out: Out) {

lightning/src/routing/gossip.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ where U::Target: UtxoLookup, L::Target: Logger
390390
}
391391

392392
fn get_next_channel_announcement(&self, starting_point: u64) -> Option<(ChannelAnnouncement, Option<ChannelUpdate>, Option<ChannelUpdate>)> {
393-
let channels = self.network_graph.channels.read().unwrap();
393+
let mut channels = self.network_graph.channels.write().unwrap();
394394
for (_, ref chan) in channels.range(starting_point..) {
395395
if chan.announcement_message.is_some() {
396396
let chan_announcement = chan.announcement_message.clone().unwrap();
@@ -412,7 +412,7 @@ where U::Target: UtxoLookup, L::Target: Logger
412412
}
413413

414414
fn get_next_node_announcement(&self, starting_point: Option<&NodeId>) -> Option<NodeAnnouncement> {
415-
let nodes = self.network_graph.nodes.read().unwrap();
415+
let mut nodes = self.network_graph.nodes.write().unwrap();
416416
let iter = if let Some(node_id) = starting_point {
417417
nodes.range((Bound::Excluded(node_id), Bound::Unbounded))
418418
} else {
@@ -572,7 +572,7 @@ where U::Target: UtxoLookup, L::Target: Logger
572572
// (has at least one update). A peer may still want to know the channel
573573
// exists even if its not yet routable.
574574
let mut batches: Vec<Vec<u64>> = vec![Vec::with_capacity(MAX_SCIDS_PER_REPLY)];
575-
let channels = self.network_graph.channels.read().unwrap();
575+
let mut channels = self.network_graph.channels.write().unwrap();
576576
for (_, ref chan) in channels.range(inclusive_start_scid.unwrap()..exclusive_end_scid.unwrap()) {
577577
if let Some(chan_announcement) = &chan.announcement_message {
578578
// Construct a new batch if last one is full

lightning/src/util/indexed_map.rs

+35-15
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
//! This module has a map which can be iterated in a deterministic order. See the [`IndexedMap`].
22
33
use crate::prelude::{HashMap, hash_map};
4-
use alloc::collections::{BTreeSet, btree_set};
4+
use alloc::vec::Vec;
5+
use alloc::slice::Iter;
56
use core::hash::Hash;
67
use core::cmp::Ord;
7-
use core::ops::RangeBounds;
8+
use core::ops::{Bound, RangeBounds};
89

910
/// A map which can be iterated in a deterministic order.
1011
///
@@ -21,19 +22,18 @@ use core::ops::RangeBounds;
2122
/// keys in the order defined by [`Ord`].
2223
///
2324
/// [`BTreeMap`]: alloc::collections::BTreeMap
24-
#[derive(Clone, Debug, PartialEq, Eq)]
25+
#[derive(Clone, Debug, Eq)]
2526
pub struct IndexedMap<K: Hash + Ord, V> {
2627
map: HashMap<K, V>,
27-
// TODO: Explore swapping this for a sorted vec (that is only sorted on first range() call)
28-
keys: BTreeSet<K>,
28+
keys: Vec<K>,
2929
}
3030

3131
impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
3232
/// Constructs a new, empty map
3333
pub fn new() -> Self {
3434
Self {
3535
map: HashMap::new(),
36-
keys: BTreeSet::new(),
36+
keys: Vec::new(),
3737
}
3838
}
3939

@@ -58,7 +58,8 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
5858
pub fn remove(&mut self, key: &K) -> Option<V> {
5959
let ret = self.map.remove(key);
6060
if let Some(_) = ret {
61-
assert!(self.keys.remove(key), "map and keys must be consistent");
61+
let idx = self.keys.iter().position(|k| k == key).expect("map and keys must be consistent");
62+
self.keys.remove(idx);
6263
}
6364
ret
6465
}
@@ -68,7 +69,7 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
6869
pub fn insert(&mut self, key: K, value: V) -> Option<V> {
6970
let ret = self.map.insert(key.clone(), value);
7071
if ret.is_none() {
71-
assert!(self.keys.insert(key), "map and keys must be consistent");
72+
self.keys.push(key);
7273
}
7374
ret
7475
}
@@ -109,9 +110,21 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
109110
}
110111

111112
/// Returns an iterator which iterates over the `key`/`value` pairs in a given range.
112-
pub fn range<R: RangeBounds<K>>(&self, range: R) -> Range<K, V> {
113+
pub fn range<R: RangeBounds<K>>(&mut self, range: R) -> Range<K, V> {
114+
self.keys.sort_unstable();
115+
let start = match range.start_bound() {
116+
Bound::Unbounded => 0,
117+
Bound::Included(key) => self.keys.binary_search(key).unwrap_or_else(|index| index),
118+
Bound::Excluded(key) => self.keys.binary_search(key).and_then(|index| Ok(index + 1)).unwrap_or_else(|index| index),
119+
};
120+
let end = match range.end_bound() {
121+
Bound::Unbounded => self.keys.len(),
122+
Bound::Included(key) => self.keys.binary_search(key).and_then(|index| Ok(index + 1)).unwrap_or_else(|index| index),
123+
Bound::Excluded(key) => self.keys.binary_search(key).unwrap_or_else(|index| index),
124+
};
125+
113126
Range {
114-
inner_range: self.keys.range(range),
127+
inner_range: self.keys[start..end].iter(),
115128
map: &self.map,
116129
}
117130
}
@@ -127,9 +140,15 @@ impl<K: Clone + Hash + Ord, V> IndexedMap<K, V> {
127140
}
128141
}
129142

143+
impl<K: Hash + Ord + PartialEq, V: PartialEq> PartialEq for IndexedMap<K, V> {
144+
fn eq(&self, other: &Self) -> bool {
145+
self.map == other.map
146+
}
147+
}
148+
130149
/// An iterator over a range of values in an [`IndexedMap`]
131150
pub struct Range<'a, K: Hash + Ord, V> {
132-
inner_range: btree_set::Range<'a, K>,
151+
inner_range: Iter<'a, K>,
133152
map: &'a HashMap<K, V>,
134153
}
135154
impl<'a, K: Hash + Ord, V: 'a> Iterator for Range<'a, K, V> {
@@ -148,7 +167,7 @@ pub struct VacantEntry<'a, K: Hash + Ord, V> {
148167
#[cfg(not(feature = "hashbrown"))]
149168
underlying_entry: hash_map::VacantEntry<'a, K, V>,
150169
key: K,
151-
keys: &'a mut BTreeSet<K>,
170+
keys: &'a mut Vec<K>,
152171
}
153172

154173
/// An [`Entry`] for an existing key-value pair
@@ -157,7 +176,7 @@ pub struct OccupiedEntry<'a, K: Hash + Ord, V> {
157176
underlying_entry: hash_map::OccupiedEntry<'a, K, V, hash_map::DefaultHashBuilder>,
158177
#[cfg(not(feature = "hashbrown"))]
159178
underlying_entry: hash_map::OccupiedEntry<'a, K, V>,
160-
keys: &'a mut BTreeSet<K>,
179+
keys: &'a mut Vec<K>,
161180
}
162181

163182
/// A mutable reference to a position in the map. This can be used to reference, add, or update the
@@ -172,7 +191,7 @@ pub enum Entry<'a, K: Hash + Ord, V> {
172191
impl<'a, K: Hash + Ord, V> VacantEntry<'a, K, V> {
173192
/// Insert a value into the position described by this entry.
174193
pub fn insert(self, value: V) -> &'a mut V {
175-
assert!(self.keys.insert(self.key), "map and keys must be consistent");
194+
self.keys.push(self.key);
176195
self.underlying_entry.insert(value)
177196
}
178197
}
@@ -181,7 +200,8 @@ impl<'a, K: Hash + Ord, V> OccupiedEntry<'a, K, V> {
181200
/// Remove the value at the position described by this entry.
182201
pub fn remove_entry(self) -> (K, V) {
183202
let res = self.underlying_entry.remove_entry();
184-
assert!(self.keys.remove(&res.0), "map and keys must be consistent");
203+
let idx = self.keys.iter().position(|k| k == &res.0).expect("map and keys must be consistent");
204+
self.keys.remove(idx);
185205
res
186206
}
187207

0 commit comments

Comments
 (0)