|
16 | 16 |
|
17 | 17 | use ethcore_bytes::Bytes; |
18 | 18 | use std::net::SocketAddr; |
19 | | -use std::collections::{HashSet, HashMap, BTreeMap, VecDeque}; |
| 19 | +use std::collections::{HashSet, HashMap, VecDeque}; |
20 | 20 | use std::mem; |
21 | 21 | use std::default::Default; |
22 | 22 | use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; |
@@ -303,35 +303,52 @@ impl Discovery { |
303 | 303 | } |
304 | 304 |
|
305 | 305 | fn nearest_node_entries(&self, target: &NodeId) -> Vec<NodeEntry> { |
306 | | - let mut found: BTreeMap<H256, Vec<&NodeEntry>> = BTreeMap::new(); |
307 | | - let mut count = 0; |
308 | 306 | let target_hash = keccak(target); |
| 307 | + let target_distance = self.id_hash ^ target_hash; |
| 308 | + |
| 309 | + let mut ret = Vec::<NodeEntry>::with_capacity(BUCKET_SIZE); |
| 310 | + |
| 311 | + // Sort bucket entries by distance to target and append to end of result vector. |
| 312 | + let append_bucket = |results: &mut Vec<NodeEntry>, bucket: &NodeBucket| -> bool { |
| 313 | + let mut sorted_entries: Vec<&BucketEntry> = bucket.nodes.iter().collect(); |
| 314 | + sorted_entries.sort_unstable_by_key(|entry| entry.id_hash ^ target_hash); |
| 315 | + |
| 316 | + let remaining_capacity = results.capacity() - results.len(); |
| 317 | + let to_append = if remaining_capacity < sorted_entries.len() { |
| 318 | + &sorted_entries[0..remaining_capacity] |
| 319 | + } else { |
| 320 | + &sorted_entries |
| 321 | + }; |
| 322 | + for entry in to_append.iter() { |
| 323 | + results.push(entry.address.clone()); |
| 324 | + } |
| 325 | + results.len() == results.capacity() |
| 326 | + }; |
309 | 327 |
|
310 | | - // Sort nodes by distance to target. |
311 | | - for bucket in &self.node_buckets { |
312 | | - for node in &bucket.nodes { |
313 | | - let dist = target_hash ^ node.id_hash; |
314 | | - found.entry(dist).or_insert_with(Vec::new).push(&node.address); |
315 | | - if count == BUCKET_SIZE { |
316 | | - // delete the most distant element |
317 | | - let remove = { |
318 | | - let (key, last) = found.iter_mut().next_back().expect("Last element is always Some when count > 0"); |
319 | | - last.pop(); |
320 | | - if last.is_empty() { Some(key.clone()) } else { None } |
321 | | - }; |
322 | | - if let Some(remove) = remove { |
323 | | - found.remove(&remove); |
324 | | - } |
325 | | - } |
326 | | - else { |
327 | | - count += 1; |
| 328 | + // This algorithm leverages the structure of the routing table to efficiently find the |
| 329 | + // nearest entries to a target hash. First, we compute the XOR distance from this node to |
| 330 | + // the target. One a first pass, we iterate from the MSB of the distance, stopping at any |
| 331 | + // buckets where the distance bit is set, and skipping the buckets where it is unset. These |
| 332 | + // must be in order the nearest to the target. On a second pass, we traverse from LSB to |
| 333 | + // MSB, appending the buckets skipped on the first pass. The reason this works is that all |
| 334 | + // entries in bucket i have a common prefix of length exactly 32 - i - 1 with the ID of this |
| 335 | + // node. |
| 336 | + |
| 337 | + for i in 0..ADDRESS_BITS { |
| 338 | + if ((target_distance[i / 8] << (i % 8)) & 0x80) != 0 { |
| 339 | + let bucket = &self.node_buckets[ADDRESS_BITS - i - 1]; |
| 340 | + if !bucket.nodes.is_empty() && append_bucket(&mut ret, bucket) { |
| 341 | + return ret; |
328 | 342 | } |
329 | 343 | } |
330 | 344 | } |
331 | | - |
332 | | - let mut ret:Vec<NodeEntry> = Vec::new(); |
333 | | - for nodes in found.values() { |
334 | | - ret.extend(nodes.iter().map(|&n| n.clone())); |
| 345 | + for i in (0..ADDRESS_BITS).rev() { |
| 346 | + if ((target_distance[i / 8] << (i % 8)) & 0x80) == 0 { |
| 347 | + let bucket = &self.node_buckets[ADDRESS_BITS - i - 1]; |
| 348 | + if !bucket.nodes.is_empty() && append_bucket(&mut ret, bucket) { |
| 349 | + return ret; |
| 350 | + } |
| 351 | + } |
335 | 352 | } |
336 | 353 | ret |
337 | 354 | } |
|
0 commit comments