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 } }