diff --git a/whisper/whisperv5/filter.go b/whisper/whisperv5/filter.go index 7e6c87a47c..b38ef3bce1 100644 --- a/whisper/whisperv5/filter.go +++ b/whisper/whisperv5/filter.go @@ -104,11 +104,14 @@ func (fs *Filters) Get(id string) *Filter { func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { var msg *ReceivedMessage + matchedTopics := fs.topicMatcher.take() + defer fs.topicMatcher.resolve(matchedTopics) fs.mutex.RLock() defer fs.mutex.RUnlock() - for watcherID := range fs.topicMatcher.matchedTopics(env.Topic) { + fs.topicMatcher.matchedTopics(env.Topic, &matchedTopics) + for _, watcherID := range matchedTopics { watcher, ok := fs.watchers[watcherID] if !ok { log.Trace(fmt.Sprintf("msg [%x], filter [%s]: filter not exists", env.Hash(), watcherID)) @@ -224,16 +227,30 @@ func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 } +type topicMatcher struct { + mapper map[string]map[string]struct{} + mx sync.RWMutex + pool sync.Pool +} + func newTopicMatcher() *topicMatcher { tm := new(topicMatcher) tm.mapper = make(map[string]map[string]struct{}) tm.mapper[ALL_TOPICS] = make(map[string]struct{}) + tm.pool.New = func() interface{} { + return []string{} + } return tm } -type topicMatcher struct { - mapper map[string]map[string]struct{} - mx sync.RWMutex +func (fs *topicMatcher) take() []string { + return fs.pool.Get().([]string) +} +func (fs *topicMatcher) resolve(s []string) { + if cap(s) > 1000 { + return + } + fs.pool.Put(s[:0]) } func (fs *topicMatcher) addFilterToTopicsMapping(watcher *Filter, id string) { @@ -259,8 +276,6 @@ func (fs *topicMatcher) removeTopicFromTopicMapping(id string) { } func (fs *topicMatcher) prepareTopicsMapping(watcher *Filter) map[string]struct{} { - fs.mx.RLock() - defer fs.mx.RUnlock() topics := make(map[string]struct{}, len(watcher.Topics)) if len(watcher.Topics) == 0 { @@ -275,18 +290,15 @@ func (fs *topicMatcher) prepareTopicsMapping(watcher *Filter) map[string]struct{ return topics } -func (fs *topicMatcher) matchedTopics(topic TopicType) map[string]struct{} { +func (fs *topicMatcher) matchedTopics(topic TopicType, matched *[]string) { fs.mx.RLock() defer fs.mx.RUnlock() - m := make(map[string]struct{}, len(fs.mapper[ALL_TOPICS])+len(fs.mapper[topic.String()])) - for i := range fs.mapper[ALL_TOPICS] { - m[i] = struct{}{} + *matched = append(*matched, i) } for i := range fs.mapper[topic.String()] { - m[i] = struct{}{} + *matched = append(*matched, i) } - return m } diff --git a/whisper/whisperv5/filter_test.go b/whisper/whisperv5/filter_test.go index e0db720dbb..3e2a4f54df 100644 --- a/whisper/whisperv5/filter_test.go +++ b/whisper/whisperv5/filter_test.go @@ -489,33 +489,6 @@ func TestMatchMessageAsym(t *testing.T) { t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed) } - fs := generateFilters() - filterID, err := fs.Install(f) - if err != nil { - t.Fatalf("failed filter install with seed %d: %s.", seed, err) - } - - m := fs.topicMatcher.matchedTopics(env.Topic) - _, matchedTopic := m[filterID] - - if !matchedTopic { - t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) - } - - // topic mismatch - if !fs.Uninstall(filterID) { - t.Fatal("failed to uninstall filter") - } - f.Topics[index][0]++ - filterID, err = fs.Install(f) - m = fs.topicMatcher.matchedTopics(env.Topic) - _, matchedTopic = m[filterID] - - if matchedTopic { - t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) - } - f.Topics[index][0]-- - // key mismatch prev := *f.KeyAsym.PublicKey.X zero := *big.NewInt(0) @@ -761,10 +734,10 @@ func TestVariableTopics(t *testing.T) { env.Topic = BytesToTopic(f.Topics[i]) //test match - m := fs.topicMatcher.matchedTopics(env.Topic) - _, ok := m[filterID] + matched := []string{} + fs.topicMatcher.matchedTopics(env.Topic, &matched) match = f.MatchEnvelope(env) - if !(match && ok) { + if !(match && hasFilterID(matched, filterID)) { t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i) } @@ -773,9 +746,10 @@ func TestVariableTopics(t *testing.T) { //false positive test match = f.MatchEnvelope(env) - m = fs.topicMatcher.matchedTopics(env.Topic) - _, ok = m[filterID] - if match && ok { + + matched = matched[:0] + fs.topicMatcher.matchedTopics(env.Topic, &matched) + if match && hasFilterID(matched, filterID) { t.Fatalf("MatchEnvelope symmetric with seed %d, step %d: false positive.", seed, i) } } @@ -812,8 +786,9 @@ func TestTopicsMapping(t *testing.T) { if err != nil { t.Fatal(err) } - m := fs.topicMatcher.matchedTopics(env.Topic) - if _, matchTopic := m[filterID]; !matchTopic { + matched := []string{} + fs.topicMatcher.matchedTopics(env.Topic, &matched) + if !hasFilterID(matched, filterID) { t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i) } @@ -821,8 +796,9 @@ func TestTopicsMapping(t *testing.T) { if !fs.Uninstall(filterID) { t.Fatal("Failed to uninstall filter") } - m = fs.topicMatcher.matchedTopics(env.Topic) - if _, matchTopic := m[filterID]; matchTopic { + matched = matched[:0] + fs.topicMatcher.matchedTopics(env.Topic, &matched) + if hasFilterID(matched, filterID) { t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i) } @@ -832,8 +808,9 @@ func TestTopicsMapping(t *testing.T) { if err != nil { t.Fatal(err) } - m = fs.topicMatcher.matchedTopics(env.Topic) - if _, matchTopic := m[filterID]; matchTopic { + matched = matched[:0] + fs.topicMatcher.matchedTopics(env.Topic, &matched) + if hasFilterID(matched, filterID) { t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i) } if !fs.Uninstall(filterID) { @@ -862,8 +839,9 @@ func TestTopicsMapping_MatchAllTopics_Success(t *testing.T) { topic := TopicType{} mrand.Read(topic[:]) - m := fs.topicMatcher.matchedTopics(topic) - if _, matchTopic := m[filterID]; !matchTopic { + matched := []string{} + fs.topicMatcher.matchedTopics(topic, &matched) + if !hasFilterID(matched, filterID) { t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed) } if _, ok := fs.topicMatcher.mapper[ALL_TOPICS][filterID]; !ok { @@ -874,8 +852,9 @@ func TestTopicsMapping_MatchAllTopics_Success(t *testing.T) { if !fs.Uninstall(filterID) { t.Fatal("Failed to uninstall filter") } - m = fs.topicMatcher.matchedTopics(topic) - if _, matchTopic := m[filterID]; matchTopic { + matched = matched[:0] + fs.topicMatcher.matchedTopics(topic, &matched) + if hasFilterID(matched, filterID) { t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed) } @@ -883,3 +862,12 @@ func TestTopicsMapping_MatchAllTopics_Success(t *testing.T) { t.Fatal("watcher mapping incorrect") } } + +func hasFilterID(matched []string, filterID string) bool { + for i := range matched { + if matched[i] == filterID { + return true + } + } + return false +}