87 lines
2.0 KiB
Go
87 lines
2.0 KiB
Go
package main
|
|
|
|
//TODO FULLY IMPLEMENT SUBSCRIPTIONS INSTEAD OF JUST THE TOPIC FILTERS
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
|
|
"badat.dev/maeqtt/v2/mqtt/packets"
|
|
)
|
|
|
|
var Subscriptions SubscriptionTreeNode = *NewSubscriptionTreeNode()
|
|
|
|
type Subscription chan packets.PublishPacket
|
|
|
|
type SubscriptionTreeNode struct {
|
|
subscriptions []Subscription
|
|
children map[string]*SubscriptionTreeNode
|
|
nodeLock sync.RWMutex
|
|
}
|
|
func NewSubscriptionTreeNode() *SubscriptionTreeNode {
|
|
s := SubscriptionTreeNode{}
|
|
s.children = make(map[string]*SubscriptionTreeNode)
|
|
return &s
|
|
}
|
|
|
|
func (s *SubscriptionTreeNode) findNode(fields []string) *SubscriptionTreeNode {
|
|
if len(fields) == 0 {
|
|
return s
|
|
}
|
|
|
|
field := fields[0]
|
|
|
|
s.nodeLock.RLock()
|
|
_, exists := s.children[field]
|
|
// Insert a value into the map if one doesn't exist yet
|
|
if !exists {
|
|
// Can't upgrade a read lock so we need to unlock and
|
|
// check again, this time with a write lock
|
|
s.nodeLock.RUnlock()
|
|
s.nodeLock.Lock()
|
|
|
|
_, exists = s.children[field]
|
|
if !exists {
|
|
s.children[field] = NewSubscriptionTreeNode()
|
|
}
|
|
s.nodeLock.Unlock()
|
|
s.nodeLock.RLock()
|
|
}
|
|
|
|
child, _ := s.children[field]
|
|
s.nodeLock.RUnlock()
|
|
return child.findNode(fields[1:])
|
|
}
|
|
|
|
func (s *SubscriptionTreeNode) Subscribe(topic packets.Topic, sub Subscription) {
|
|
node := s.findNode(topic.Fields)
|
|
node.nodeLock.Lock()
|
|
node.subscriptions = append(node.subscriptions, sub)
|
|
node.nodeLock.Unlock()
|
|
}
|
|
|
|
func (s *SubscriptionTreeNode) GetSubscriptions(topic string) ([]Subscription, sync.Locker) {
|
|
fields := strings.Split(topic,"/")
|
|
|
|
child := s.findNode(fields)
|
|
locker := child.nodeLock.RLocker()
|
|
locker.Lock()
|
|
return child.subscriptions, locker
|
|
}
|
|
|
|
func (s *SubscriptionTreeNode) findMatchingRec(topic []string) ([]Subscription, sync.Locker) {
|
|
locker := s.nodeLock.RLocker()
|
|
s.nodeLock.RLock()
|
|
if len(topic) == 0 {
|
|
return s.subscriptions,locker
|
|
}
|
|
defer s.nodeLock.RUnlock()
|
|
|
|
child, exists := s.children[topic[0]]
|
|
if exists {
|
|
return child.findMatchingRec(topic[1:])
|
|
} else {
|
|
return []Subscription{},locker
|
|
}
|
|
}
|