11package internal
22
33import (
4+ "encoding/base64"
5+ "hash/fnv"
6+
47 "github.com/AutoRoute/bloom"
58 "github.com/AutoRoute/node/types"
69)
710
811type BloomReachabilityMap struct {
9- Filters []* bloom.BloomFilter
10- Conglomerate * bloom.BloomFilter
12+ Filters []* bloom.BloomFilter
13+ // Allows us to keep track of filters between nodes.
14+ filter_hashes map [string ]bool
15+ Conglomerate * bloom.BloomFilter
16+ }
17+
18+ // Generates a unique hash for a particular filter.
19+ // Args:
20+ // filter: The filter to hash.
21+ // Returns:
22+ // The FNV hash of the filter.
23+ func hashFilter (filter * bloom.BloomFilter ) string {
24+ hasher := fnv .New64 ()
25+ filter .WriteTo (hasher )
26+ return base64 .URLEncoding .EncodeToString (hasher .Sum (nil ))
1127}
1228
1329func NewBloomReachabilityMap () * BloomReachabilityMap {
@@ -16,8 +32,13 @@ func NewBloomReachabilityMap() *BloomReachabilityMap {
1632
1733 m := BloomReachabilityMap {
1834 Filters : fs ,
35+ filter_hashes : make (map [string ]bool ),
1936 Conglomerate : fs [0 ].Copy (),
2037 }
38+
39+ // Hash our initial filter to begin with.
40+ initial_hash := hashFilter (fs [0 ])
41+ m .filter_hashes [initial_hash ] = true
2142 return & m
2243}
2344
@@ -39,22 +60,69 @@ func (m *BloomReachabilityMap) Increment() {
3960 m .Filters = append (newZeroth , m .Filters ... )
4061}
4162
42- func (m * BloomReachabilityMap ) Merge (n * BloomReachabilityMap ) {
63+ // Merges two reachability maps.
64+ // Args:
65+ // n: The map to merge with this one.
66+ // Returns:
67+ // True if the map was modified, false if it wasn't. Practically, it will only
68+ // return false if it is being asked to merge a map whose filters are a subset
69+ // of this one's filters.
70+ func (m * BloomReachabilityMap ) Merge (n * BloomReachabilityMap ) bool {
71+ modified := false
72+
4373 if len (m .Filters ) < len (n .Filters ) {
74+ modified = true
4475 for k , v := range m .Filters {
76+ _ , found := m .filter_hashes [hashFilter (n .Filters [k ])]
77+ if (found ) {
78+ // This filter is not new.
79+ continue
80+ }
81+
82+ old_hash := hashFilter (v )
4583 v .Merge (n .Filters [k ])
84+ new_hash := hashFilter (v )
85+ if old_hash != new_hash {
86+ delete (m .filter_hashes , old_hash )
87+ m .filter_hashes [new_hash ] = true
88+ }
4689 }
4790 // append the remaining Filters
91+ for _ , filter := range n .Filters [len (m .Filters ):] {
92+ m .filter_hashes [hashFilter (filter )] = true
93+ }
4894 m .Filters = append (m .Filters , n .Filters [len (m .Filters ):]... )
4995 } else {
5096 for k , v := range n .Filters {
97+ // Check for an identical filter.
98+ _ , found := m .filter_hashes [hashFilter (v )]
99+ if (found ) {
100+ // This filter is not new.
101+ continue
102+ }
103+
104+ old_hash := hashFilter (m .Filters [k ])
51105 m .Filters [k ].Merge (v )
106+ new_hash := hashFilter (m .Filters [k ])
107+ if old_hash != new_hash {
108+ delete (m .filter_hashes , old_hash )
109+ m .filter_hashes [new_hash ] = true
110+ modified = true
111+ }
52112 }
53113 }
114+
115+ if ! modified {
116+ // We didn't add any new filters.
117+ return false
118+ }
119+
54120 // reconstruct the Conglomerate
55121 for _ , v := range m .Filters {
56122 m .Conglomerate .Merge (v )
57123 }
124+
125+ return true
58126}
59127
60128func (m * BloomReachabilityMap ) Copy () * BloomReachabilityMap {
0 commit comments