github.com/nats-io/nats-server/v2@v2.11.0-preview.2/server/sublist.go (about) 1 // Copyright 2016-2024 The NATS Authors 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package server 15 16 import ( 17 "bytes" 18 "errors" 19 "strings" 20 "sync" 21 "sync/atomic" 22 "unicode/utf8" 23 ) 24 25 // Sublist is a routing mechanism to handle subject distribution and 26 // provides a facility to match subjects from published messages to 27 // interested subscribers. Subscribers can have wildcard subjects to 28 // match multiple published subjects. 29 30 // Common byte variables for wildcards and token separator. 31 const ( 32 pwc = '*' 33 pwcs = "*" 34 fwc = '>' 35 fwcs = ">" 36 tsep = "." 37 btsep = '.' 38 ) 39 40 // Sublist related errors 41 var ( 42 ErrInvalidSubject = errors.New("sublist: invalid subject") 43 ErrNotFound = errors.New("sublist: no matches found") 44 ErrNilChan = errors.New("sublist: nil channel") 45 ErrAlreadyRegistered = errors.New("sublist: notification already registered") 46 ) 47 48 const ( 49 // cacheMax is used to bound limit the frontend cache 50 slCacheMax = 1024 51 // If we run a sweeper we will drain to this count. 52 slCacheSweep = 256 53 // plistMin is our lower bounds to create a fast plist for Match. 54 plistMin = 256 55 ) 56 57 // SublistResult is a result structure better optimized for queue subs. 58 type SublistResult struct { 59 psubs []*subscription 60 qsubs [][]*subscription // don't make this a map, too expensive to iterate 61 } 62 63 // A Sublist stores and efficiently retrieves subscriptions. 64 type Sublist struct { 65 sync.RWMutex 66 genid uint64 67 matches uint64 68 cacheHits uint64 69 inserts uint64 70 removes uint64 71 root *level 72 cache map[string]*SublistResult 73 ccSweep int32 74 notify *notifyMaps 75 count uint32 76 } 77 78 // notifyMaps holds maps of arrays of channels for notifications 79 // on a change of interest. 80 type notifyMaps struct { 81 insert map[string][]chan<- bool 82 remove map[string][]chan<- bool 83 } 84 85 // A node contains subscriptions and a pointer to the next level. 86 type node struct { 87 next *level 88 psubs map[*subscription]struct{} 89 qsubs map[string]map[*subscription]struct{} 90 plist []*subscription 91 } 92 93 // A level represents a group of nodes and special pointers to 94 // wildcard nodes. 95 type level struct { 96 nodes map[string]*node 97 pwc, fwc *node 98 } 99 100 // Create a new default node. 101 func newNode() *node { 102 return &node{psubs: make(map[*subscription]struct{})} 103 } 104 105 // Create a new default level. 106 func newLevel() *level { 107 return &level{nodes: make(map[string]*node)} 108 } 109 110 // In general caching is recommended however in some extreme cases where 111 // interest changes are high, suppressing the cache can help. 112 // https://github.com/nats-io/nats-server/issues/941 113 // FIXME(dlc) - should be more dynamic at some point based on cache thrashing. 114 115 // NewSublist will create a default sublist with caching enabled per the flag. 116 func NewSublist(enableCache bool) *Sublist { 117 if enableCache { 118 return &Sublist{root: newLevel(), cache: make(map[string]*SublistResult)} 119 } 120 return &Sublist{root: newLevel()} 121 } 122 123 // NewSublistWithCache will create a default sublist with caching enabled. 124 func NewSublistWithCache() *Sublist { 125 return NewSublist(true) 126 } 127 128 // NewSublistNoCache will create a default sublist with caching disabled. 129 func NewSublistNoCache() *Sublist { 130 return NewSublist(false) 131 } 132 133 // CacheEnabled returns whether or not caching is enabled for this sublist. 134 func (s *Sublist) CacheEnabled() bool { 135 s.RLock() 136 enabled := s.cache != nil 137 s.RUnlock() 138 return enabled 139 } 140 141 // RegisterNotification will register for notifications when interest for the given 142 // subject changes. The subject must be a literal publish type subject. 143 // The notification is true for when the first interest for a subject is inserted, 144 // and false when all interest in the subject is removed. Note that this interest 145 // needs to be exact and that wildcards will not trigger the notifications. The sublist 146 // will not block when trying to send the notification. Its up to the caller to make 147 // sure the channel send will not block. 148 func (s *Sublist) RegisterNotification(subject string, notify chan<- bool) error { 149 return s.registerNotification(subject, _EMPTY_, notify) 150 } 151 152 func (s *Sublist) RegisterQueueNotification(subject, queue string, notify chan<- bool) error { 153 return s.registerNotification(subject, queue, notify) 154 } 155 156 func (s *Sublist) registerNotification(subject, queue string, notify chan<- bool) error { 157 if subjectHasWildcard(subject) { 158 return ErrInvalidSubject 159 } 160 if notify == nil { 161 return ErrNilChan 162 } 163 164 var hasInterest bool 165 r := s.Match(subject) 166 167 if len(r.psubs)+len(r.qsubs) > 0 { 168 if queue == _EMPTY_ { 169 for _, sub := range r.psubs { 170 if string(sub.subject) == subject { 171 hasInterest = true 172 break 173 } 174 } 175 } else { 176 for _, qsub := range r.qsubs { 177 qs := qsub[0] 178 if string(qs.subject) == subject && string(qs.queue) == queue { 179 hasInterest = true 180 break 181 } 182 } 183 } 184 } 185 186 key := keyFromSubjectAndQueue(subject, queue) 187 var err error 188 189 s.Lock() 190 if s.notify == nil { 191 s.notify = ¬ifyMaps{ 192 insert: make(map[string][]chan<- bool), 193 remove: make(map[string][]chan<- bool), 194 } 195 } 196 // Check which list to add us to. 197 if hasInterest { 198 err = s.addRemoveNotify(key, notify) 199 } else { 200 err = s.addInsertNotify(key, notify) 201 } 202 s.Unlock() 203 204 if err == nil { 205 sendNotification(notify, hasInterest) 206 } 207 return err 208 } 209 210 // Lock should be held. 211 func chkAndRemove(key string, notify chan<- bool, ms map[string][]chan<- bool) bool { 212 chs := ms[key] 213 for i, ch := range chs { 214 if ch == notify { 215 chs[i] = chs[len(chs)-1] 216 chs = chs[:len(chs)-1] 217 if len(chs) == 0 { 218 delete(ms, key) 219 } 220 return true 221 } 222 } 223 return false 224 } 225 226 func (s *Sublist) ClearNotification(subject string, notify chan<- bool) bool { 227 return s.clearNotification(subject, _EMPTY_, notify) 228 } 229 230 func (s *Sublist) ClearQueueNotification(subject, queue string, notify chan<- bool) bool { 231 return s.clearNotification(subject, queue, notify) 232 } 233 234 func (s *Sublist) clearNotification(subject, queue string, notify chan<- bool) bool { 235 s.Lock() 236 if s.notify == nil { 237 s.Unlock() 238 return false 239 } 240 key := keyFromSubjectAndQueue(subject, queue) 241 // Check both, start with remove. 242 didRemove := chkAndRemove(key, notify, s.notify.remove) 243 didRemove = didRemove || chkAndRemove(key, notify, s.notify.insert) 244 // Check if everything is gone 245 if len(s.notify.remove)+len(s.notify.insert) == 0 { 246 s.notify = nil 247 } 248 s.Unlock() 249 return didRemove 250 } 251 252 func sendNotification(ch chan<- bool, hasInterest bool) { 253 select { 254 case ch <- hasInterest: 255 default: 256 } 257 } 258 259 // Add a new channel for notification in insert map. 260 // Write lock should be held. 261 func (s *Sublist) addInsertNotify(subject string, notify chan<- bool) error { 262 return s.addNotify(s.notify.insert, subject, notify) 263 } 264 265 // Add a new channel for notification in removal map. 266 // Write lock should be held. 267 func (s *Sublist) addRemoveNotify(subject string, notify chan<- bool) error { 268 return s.addNotify(s.notify.remove, subject, notify) 269 } 270 271 // Add a new channel for notification. 272 // Write lock should be held. 273 func (s *Sublist) addNotify(m map[string][]chan<- bool, subject string, notify chan<- bool) error { 274 chs := m[subject] 275 if len(chs) > 0 { 276 // Check to see if this chan is already registered. 277 for _, ch := range chs { 278 if ch == notify { 279 return ErrAlreadyRegistered 280 } 281 } 282 } 283 284 m[subject] = append(chs, notify) 285 return nil 286 } 287 288 // To generate a key from subject and queue. We just add spc. 289 func keyFromSubjectAndQueue(subject, queue string) string { 290 if len(queue) == 0 { 291 return subject 292 } 293 var sb strings.Builder 294 sb.WriteString(subject) 295 sb.WriteString(" ") 296 sb.WriteString(queue) 297 return sb.String() 298 } 299 300 // chkForInsertNotification will check to see if we need to notify on this subject. 301 // Write lock should be held. 302 func (s *Sublist) chkForInsertNotification(subject, queue string) { 303 key := keyFromSubjectAndQueue(subject, queue) 304 305 // All notify subjects are also literal so just do a hash lookup here. 306 if chs := s.notify.insert[key]; len(chs) > 0 { 307 for _, ch := range chs { 308 sendNotification(ch, true) 309 } 310 // Move from the insert map to the remove map. 311 s.notify.remove[key] = append(s.notify.remove[key], chs...) 312 delete(s.notify.insert, key) 313 } 314 } 315 316 // chkForRemoveNotification will check to see if we need to notify on this subject. 317 // Write lock should be held. 318 func (s *Sublist) chkForRemoveNotification(subject, queue string) { 319 key := keyFromSubjectAndQueue(subject, queue) 320 if chs := s.notify.remove[key]; len(chs) > 0 { 321 // We need to always check that we have no interest anymore. 322 var hasInterest bool 323 r := s.matchNoLock(subject) 324 325 if len(r.psubs)+len(r.qsubs) > 0 { 326 if queue == _EMPTY_ { 327 for _, sub := range r.psubs { 328 if string(sub.subject) == subject { 329 hasInterest = true 330 break 331 } 332 } 333 } else { 334 for _, qsub := range r.qsubs { 335 qs := qsub[0] 336 if string(qs.subject) == subject && string(qs.queue) == queue { 337 hasInterest = true 338 break 339 } 340 } 341 } 342 } 343 if !hasInterest { 344 for _, ch := range chs { 345 sendNotification(ch, false) 346 } 347 // Move from the remove map to the insert map. 348 s.notify.insert[key] = append(s.notify.insert[key], chs...) 349 delete(s.notify.remove, key) 350 } 351 } 352 } 353 354 // Insert adds a subscription into the sublist 355 func (s *Sublist) Insert(sub *subscription) error { 356 // copy the subject since we hold this and this might be part of a large byte slice. 357 subject := string(sub.subject) 358 tsa := [32]string{} 359 tokens := tsa[:0] 360 start := 0 361 for i := 0; i < len(subject); i++ { 362 if subject[i] == btsep { 363 tokens = append(tokens, subject[start:i]) 364 start = i + 1 365 } 366 } 367 tokens = append(tokens, subject[start:]) 368 369 s.Lock() 370 371 var sfwc, haswc, isnew bool 372 var n *node 373 l := s.root 374 375 for _, t := range tokens { 376 lt := len(t) 377 if lt == 0 || sfwc { 378 s.Unlock() 379 return ErrInvalidSubject 380 } 381 382 if lt > 1 { 383 n = l.nodes[t] 384 } else { 385 switch t[0] { 386 case pwc: 387 n = l.pwc 388 haswc = true 389 case fwc: 390 n = l.fwc 391 haswc, sfwc = true, true 392 default: 393 n = l.nodes[t] 394 } 395 } 396 if n == nil { 397 n = newNode() 398 if lt > 1 { 399 l.nodes[t] = n 400 } else { 401 switch t[0] { 402 case pwc: 403 l.pwc = n 404 case fwc: 405 l.fwc = n 406 default: 407 l.nodes[t] = n 408 } 409 } 410 } 411 if n.next == nil { 412 n.next = newLevel() 413 } 414 l = n.next 415 } 416 if sub.queue == nil { 417 n.psubs[sub] = struct{}{} 418 isnew = len(n.psubs) == 1 419 if n.plist != nil { 420 n.plist = append(n.plist, sub) 421 } else if len(n.psubs) > plistMin { 422 n.plist = make([]*subscription, 0, len(n.psubs)) 423 // Populate 424 for psub := range n.psubs { 425 n.plist = append(n.plist, psub) 426 } 427 } 428 } else { 429 if n.qsubs == nil { 430 n.qsubs = make(map[string]map[*subscription]struct{}) 431 } 432 qname := string(sub.queue) 433 // This is a queue subscription 434 subs, ok := n.qsubs[qname] 435 if !ok { 436 subs = make(map[*subscription]struct{}) 437 n.qsubs[qname] = subs 438 isnew = true 439 } 440 subs[sub] = struct{}{} 441 } 442 443 s.count++ 444 s.inserts++ 445 446 s.addToCache(subject, sub) 447 atomic.AddUint64(&s.genid, 1) 448 449 if s.notify != nil && isnew && !haswc && len(s.notify.insert) > 0 { 450 s.chkForInsertNotification(subject, string(sub.queue)) 451 } 452 s.Unlock() 453 454 return nil 455 } 456 457 // Deep copy 458 func copyResult(r *SublistResult) *SublistResult { 459 nr := &SublistResult{} 460 nr.psubs = append([]*subscription(nil), r.psubs...) 461 for _, qr := range r.qsubs { 462 nqr := append([]*subscription(nil), qr...) 463 nr.qsubs = append(nr.qsubs, nqr) 464 } 465 return nr 466 } 467 468 // Adds a new sub to an existing result. 469 func (r *SublistResult) addSubToResult(sub *subscription) *SublistResult { 470 // Copy since others may have a reference. 471 nr := copyResult(r) 472 if sub.queue == nil { 473 nr.psubs = append(nr.psubs, sub) 474 } else { 475 if i := findQSlot(sub.queue, nr.qsubs); i >= 0 { 476 nr.qsubs[i] = append(nr.qsubs[i], sub) 477 } else { 478 nr.qsubs = append(nr.qsubs, []*subscription{sub}) 479 } 480 } 481 return nr 482 } 483 484 // addToCache will add the new entry to the existing cache 485 // entries if needed. Assumes write lock is held. 486 // Assumes write lock is held. 487 func (s *Sublist) addToCache(subject string, sub *subscription) { 488 if s.cache == nil { 489 return 490 } 491 // If literal we can direct match. 492 if subjectIsLiteral(subject) { 493 if r := s.cache[subject]; r != nil { 494 s.cache[subject] = r.addSubToResult(sub) 495 } 496 return 497 } 498 for key, r := range s.cache { 499 if matchLiteral(key, subject) { 500 s.cache[key] = r.addSubToResult(sub) 501 } 502 } 503 } 504 505 // removeFromCache will remove the sub from any active cache entries. 506 // Assumes write lock is held. 507 func (s *Sublist) removeFromCache(subject string) { 508 if s.cache == nil { 509 return 510 } 511 // If literal we can direct match. 512 if subjectIsLiteral(subject) { 513 delete(s.cache, subject) 514 return 515 } 516 // Wildcard here. 517 for key := range s.cache { 518 if matchLiteral(key, subject) { 519 delete(s.cache, key) 520 } 521 } 522 } 523 524 // a place holder for an empty result. 525 var emptyResult = &SublistResult{} 526 527 // Match will match all entries to the literal subject. 528 // It will return a set of results for both normal and queue subscribers. 529 func (s *Sublist) Match(subject string) *SublistResult { 530 return s.match(subject, true, nil) 531 } 532 533 // MatchWithResult will match all entries to the literal subject, reusing the 534 // supplied SublistResult to reduce allocations on hot paths. 535 // It will return a set of results for both normal and queue subscribers. 536 func (s *Sublist) MatchWithResult(subject string, result *SublistResult) *SublistResult { 537 return s.match(subject, true, result) 538 } 539 540 func (s *Sublist) matchNoLock(subject string) *SublistResult { 541 return s.match(subject, false, nil) 542 } 543 544 func (s *Sublist) match(subject string, doLock bool, result *SublistResult) *SublistResult { 545 atomic.AddUint64(&s.matches, 1) 546 547 // Check cache first. 548 if doLock { 549 s.RLock() 550 } 551 // Writing to the cache is only allowed if not supplying our 552 // own SublistResult, i.e. via call to MatchWithResult. 553 cacheEnabled := result == nil && s.cache != nil 554 r, ok := s.cache[subject] 555 if doLock { 556 s.RUnlock() 557 } 558 if ok { 559 atomic.AddUint64(&s.cacheHits, 1) 560 return r 561 } 562 563 tsa := [32]string{} 564 tokens := tsa[:0] 565 start := 0 566 for i := 0; i < len(subject); i++ { 567 if subject[i] == btsep { 568 if i-start == 0 { 569 return emptyResult 570 } 571 tokens = append(tokens, subject[start:i]) 572 start = i + 1 573 } 574 } 575 if start >= len(subject) { 576 return emptyResult 577 } 578 tokens = append(tokens, subject[start:]) 579 580 // FIXME(dlc) - Make shared pool between sublist and client readLoop? 581 if result == nil { 582 result = &SublistResult{} 583 } 584 result.psubs = result.psubs[:0] 585 result.qsubs = result.qsubs[:0] 586 587 // Get result from the main structure and place into the shared cache. 588 // Hold the read lock to avoid race between match and store. 589 var n int 590 591 if doLock { 592 if cacheEnabled { 593 s.Lock() 594 } else { 595 s.RLock() 596 } 597 } 598 599 matchLevel(s.root, tokens, result) 600 // Check for empty result. 601 if len(result.psubs) == 0 && len(result.qsubs) == 0 { 602 result = emptyResult 603 } 604 if cacheEnabled { 605 s.cache[subject] = result 606 n = len(s.cache) 607 } 608 if doLock { 609 if cacheEnabled { 610 s.Unlock() 611 } else { 612 s.RUnlock() 613 } 614 } 615 616 // Reduce the cache count if we have exceeded our set maximum. 617 if cacheEnabled && n > slCacheMax && atomic.CompareAndSwapInt32(&s.ccSweep, 0, 1) { 618 go s.reduceCacheCount() 619 } 620 621 return result 622 } 623 624 // Remove entries in the cache until we are under the maximum. 625 // TODO(dlc) this could be smarter now that its not inline. 626 func (s *Sublist) reduceCacheCount() { 627 defer atomic.StoreInt32(&s.ccSweep, 0) 628 // If we are over the cache limit randomly drop until under the limit. 629 s.Lock() 630 for key := range s.cache { 631 delete(s.cache, key) 632 if len(s.cache) <= slCacheSweep { 633 break 634 } 635 } 636 s.Unlock() 637 } 638 639 // Helper function for auto-expanding remote qsubs. 640 func isRemoteQSub(sub *subscription) bool { 641 return sub != nil && sub.queue != nil && sub.client != nil && (sub.client.kind == ROUTER || sub.client.kind == LEAF) 642 } 643 644 // UpdateRemoteQSub should be called when we update the weight of an existing 645 // remote queue sub. 646 func (s *Sublist) UpdateRemoteQSub(sub *subscription) { 647 // We could search to make sure we find it, but probably not worth 648 // it unless we are thrashing the cache. Just remove from our L2 and update 649 // the genid so L1 will be flushed. 650 s.Lock() 651 s.removeFromCache(string(sub.subject)) 652 atomic.AddUint64(&s.genid, 1) 653 s.Unlock() 654 } 655 656 // This will add in a node's results to the total results. 657 func addNodeToResults(n *node, results *SublistResult) { 658 // Normal subscriptions 659 if n.plist != nil { 660 results.psubs = append(results.psubs, n.plist...) 661 } else { 662 for psub := range n.psubs { 663 results.psubs = append(results.psubs, psub) 664 } 665 } 666 // Queue subscriptions 667 for qname, qr := range n.qsubs { 668 if len(qr) == 0 { 669 continue 670 } 671 // Need to find matching list in results 672 var i int 673 if i = findQSlot([]byte(qname), results.qsubs); i < 0 { 674 i = len(results.qsubs) 675 nqsub := make([]*subscription, 0, len(qr)) 676 results.qsubs = append(results.qsubs, nqsub) 677 } 678 for sub := range qr { 679 if isRemoteQSub(sub) { 680 ns := atomic.LoadInt32(&sub.qw) 681 // Shadow these subscriptions 682 for n := 0; n < int(ns); n++ { 683 results.qsubs[i] = append(results.qsubs[i], sub) 684 } 685 } else { 686 results.qsubs[i] = append(results.qsubs[i], sub) 687 } 688 } 689 } 690 } 691 692 // We do not use a map here since we want iteration to be past when 693 // processing publishes in L1 on client. So we need to walk sequentially 694 // for now. Keep an eye on this in case we start getting large number of 695 // different queue subscribers for the same subject. 696 func findQSlot(queue []byte, qsl [][]*subscription) int { 697 if queue == nil { 698 return -1 699 } 700 for i, qr := range qsl { 701 if len(qr) > 0 && bytes.Equal(queue, qr[0].queue) { 702 return i 703 } 704 } 705 return -1 706 } 707 708 // matchLevel is used to recursively descend into the trie. 709 func matchLevel(l *level, toks []string, results *SublistResult) { 710 var pwc, n *node 711 for i, t := range toks { 712 if l == nil { 713 return 714 } 715 if l.fwc != nil { 716 addNodeToResults(l.fwc, results) 717 } 718 if pwc = l.pwc; pwc != nil { 719 matchLevel(pwc.next, toks[i+1:], results) 720 } 721 n = l.nodes[t] 722 if n != nil { 723 l = n.next 724 } else { 725 l = nil 726 } 727 } 728 if n != nil { 729 addNodeToResults(n, results) 730 } 731 if pwc != nil { 732 addNodeToResults(pwc, results) 733 } 734 } 735 736 // lnt is used to track descent into levels for a removal for pruning. 737 type lnt struct { 738 l *level 739 n *node 740 t string 741 } 742 743 // Raw low level remove, can do batches with lock held outside. 744 func (s *Sublist) remove(sub *subscription, shouldLock bool, doCacheUpdates bool) error { 745 subject := string(sub.subject) 746 tsa := [32]string{} 747 tokens := tsa[:0] 748 start := 0 749 for i := 0; i < len(subject); i++ { 750 if subject[i] == btsep { 751 tokens = append(tokens, subject[start:i]) 752 start = i + 1 753 } 754 } 755 tokens = append(tokens, subject[start:]) 756 757 if shouldLock { 758 s.Lock() 759 defer s.Unlock() 760 } 761 762 var sfwc, haswc bool 763 var n *node 764 l := s.root 765 766 // Track levels for pruning 767 var lnts [32]lnt 768 levels := lnts[:0] 769 770 for _, t := range tokens { 771 lt := len(t) 772 if lt == 0 || sfwc { 773 return ErrInvalidSubject 774 } 775 if l == nil { 776 return ErrNotFound 777 } 778 if lt > 1 { 779 n = l.nodes[t] 780 } else { 781 switch t[0] { 782 case pwc: 783 n = l.pwc 784 haswc = true 785 case fwc: 786 n = l.fwc 787 haswc, sfwc = true, true 788 default: 789 n = l.nodes[t] 790 } 791 } 792 if n != nil { 793 levels = append(levels, lnt{l, n, t}) 794 l = n.next 795 } else { 796 l = nil 797 } 798 } 799 removed, last := s.removeFromNode(n, sub) 800 if !removed { 801 return ErrNotFound 802 } 803 804 s.count-- 805 s.removes++ 806 807 for i := len(levels) - 1; i >= 0; i-- { 808 l, n, t := levels[i].l, levels[i].n, levels[i].t 809 if n.isEmpty() { 810 l.pruneNode(n, t) 811 } 812 } 813 if doCacheUpdates { 814 s.removeFromCache(subject) 815 atomic.AddUint64(&s.genid, 1) 816 } 817 818 if s.notify != nil && last && !haswc && len(s.notify.remove) > 0 { 819 s.chkForRemoveNotification(subject, string(sub.queue)) 820 } 821 822 return nil 823 } 824 825 // Remove will remove a subscription. 826 func (s *Sublist) Remove(sub *subscription) error { 827 return s.remove(sub, true, true) 828 } 829 830 // RemoveBatch will remove a list of subscriptions. 831 func (s *Sublist) RemoveBatch(subs []*subscription) error { 832 if len(subs) == 0 { 833 return nil 834 } 835 836 s.Lock() 837 defer s.Unlock() 838 839 // TODO(dlc) - We could try to be smarter here for a client going away but the account 840 // has a large number of subscriptions compared to this client. Quick and dirty testing 841 // though said just disabling all the time best for now. 842 843 // Turn off our cache if enabled. 844 wasEnabled := s.cache != nil 845 s.cache = nil 846 // We will try to remove all subscriptions but will report the first that caused 847 // an error. In other words, we don't bail out at the first error which would 848 // possibly leave a bunch of subscriptions that could have been removed. 849 var err error 850 for _, sub := range subs { 851 if lerr := s.remove(sub, false, false); lerr != nil && err == nil { 852 err = lerr 853 } 854 } 855 // Turn caching back on here. 856 atomic.AddUint64(&s.genid, 1) 857 if wasEnabled { 858 s.cache = make(map[string]*SublistResult) 859 } 860 return err 861 } 862 863 // pruneNode is used to prune an empty node from the tree. 864 func (l *level) pruneNode(n *node, t string) { 865 if n == nil { 866 return 867 } 868 if n == l.fwc { 869 l.fwc = nil 870 } else if n == l.pwc { 871 l.pwc = nil 872 } else { 873 delete(l.nodes, t) 874 } 875 } 876 877 // isEmpty will test if the node has any entries. Used 878 // in pruning. 879 func (n *node) isEmpty() bool { 880 if len(n.psubs) == 0 && len(n.qsubs) == 0 { 881 if n.next == nil || n.next.numNodes() == 0 { 882 return true 883 } 884 } 885 return false 886 } 887 888 // Return the number of nodes for the given level. 889 func (l *level) numNodes() int { 890 num := len(l.nodes) 891 if l.pwc != nil { 892 num++ 893 } 894 if l.fwc != nil { 895 num++ 896 } 897 return num 898 } 899 900 // Remove the sub for the given node. 901 func (s *Sublist) removeFromNode(n *node, sub *subscription) (found, last bool) { 902 if n == nil { 903 return false, true 904 } 905 if sub.queue == nil { 906 _, found = n.psubs[sub] 907 delete(n.psubs, sub) 908 if found && n.plist != nil { 909 // This will brute force remove the plist to perform 910 // correct behavior. Will get re-populated on a call 911 // to Match as needed. 912 n.plist = nil 913 } 914 return found, len(n.psubs) == 0 915 } 916 917 // We have a queue group subscription here 918 qsub := n.qsubs[string(sub.queue)] 919 _, found = qsub[sub] 920 delete(qsub, sub) 921 if len(qsub) == 0 { 922 // This is the last queue subscription interest when len(qsub) == 0, not 923 // when n.qsubs is empty. 924 last = true 925 delete(n.qsubs, string(sub.queue)) 926 } 927 return found, last 928 } 929 930 // Count returns the number of subscriptions. 931 func (s *Sublist) Count() uint32 { 932 s.RLock() 933 defer s.RUnlock() 934 return s.count 935 } 936 937 // CacheCount returns the number of result sets in the cache. 938 func (s *Sublist) CacheCount() int { 939 s.RLock() 940 cc := len(s.cache) 941 s.RUnlock() 942 return cc 943 } 944 945 // SublistStats are public stats for the sublist 946 type SublistStats struct { 947 NumSubs uint32 `json:"num_subscriptions"` 948 NumCache uint32 `json:"num_cache"` 949 NumInserts uint64 `json:"num_inserts"` 950 NumRemoves uint64 `json:"num_removes"` 951 NumMatches uint64 `json:"num_matches"` 952 CacheHitRate float64 `json:"cache_hit_rate"` 953 MaxFanout uint32 `json:"max_fanout"` 954 AvgFanout float64 `json:"avg_fanout"` 955 totFanout int 956 cacheCnt int 957 cacheHits uint64 958 } 959 960 func (s *SublistStats) add(stat *SublistStats) { 961 s.NumSubs += stat.NumSubs 962 s.NumCache += stat.NumCache 963 s.NumInserts += stat.NumInserts 964 s.NumRemoves += stat.NumRemoves 965 s.NumMatches += stat.NumMatches 966 s.cacheHits += stat.cacheHits 967 if s.MaxFanout < stat.MaxFanout { 968 s.MaxFanout = stat.MaxFanout 969 } 970 971 // ignore slStats.AvgFanout, collect the values 972 // it's based on instead 973 s.totFanout += stat.totFanout 974 s.cacheCnt += stat.cacheCnt 975 if s.totFanout > 0 { 976 s.AvgFanout = float64(s.totFanout) / float64(s.cacheCnt) 977 } 978 if s.NumMatches > 0 { 979 s.CacheHitRate = float64(s.cacheHits) / float64(s.NumMatches) 980 } 981 } 982 983 // Stats will return a stats structure for the current state. 984 func (s *Sublist) Stats() *SublistStats { 985 st := &SublistStats{} 986 987 s.RLock() 988 cache := s.cache 989 cc := len(s.cache) 990 st.NumSubs = s.count 991 st.NumInserts = s.inserts 992 st.NumRemoves = s.removes 993 s.RUnlock() 994 995 st.NumCache = uint32(cc) 996 st.NumMatches = atomic.LoadUint64(&s.matches) 997 st.cacheHits = atomic.LoadUint64(&s.cacheHits) 998 if st.NumMatches > 0 { 999 st.CacheHitRate = float64(st.cacheHits) / float64(st.NumMatches) 1000 } 1001 1002 // whip through cache for fanout stats, this can be off if cache is full and doing evictions. 1003 // If this is called frequently, which it should not be, this could hurt performance. 1004 if cache != nil { 1005 tot, max, clen := 0, 0, 0 1006 s.RLock() 1007 for _, r := range s.cache { 1008 clen++ 1009 l := len(r.psubs) + len(r.qsubs) 1010 tot += l 1011 if l > max { 1012 max = l 1013 } 1014 } 1015 s.RUnlock() 1016 st.totFanout = tot 1017 st.cacheCnt = clen 1018 st.MaxFanout = uint32(max) 1019 if tot > 0 { 1020 st.AvgFanout = float64(tot) / float64(clen) 1021 } 1022 } 1023 return st 1024 } 1025 1026 // numLevels will return the maximum number of levels 1027 // contained in the Sublist tree. 1028 func (s *Sublist) numLevels() int { 1029 return visitLevel(s.root, 0) 1030 } 1031 1032 // visitLevel is used to descend the Sublist tree structure 1033 // recursively. 1034 func visitLevel(l *level, depth int) int { 1035 if l == nil || l.numNodes() == 0 { 1036 return depth 1037 } 1038 1039 depth++ 1040 maxDepth := depth 1041 1042 for _, n := range l.nodes { 1043 if n == nil { 1044 continue 1045 } 1046 newDepth := visitLevel(n.next, depth) 1047 if newDepth > maxDepth { 1048 maxDepth = newDepth 1049 } 1050 } 1051 if l.pwc != nil { 1052 pwcDepth := visitLevel(l.pwc.next, depth) 1053 if pwcDepth > maxDepth { 1054 maxDepth = pwcDepth 1055 } 1056 } 1057 if l.fwc != nil { 1058 fwcDepth := visitLevel(l.fwc.next, depth) 1059 if fwcDepth > maxDepth { 1060 maxDepth = fwcDepth 1061 } 1062 } 1063 return maxDepth 1064 } 1065 1066 // Determine if a subject has any wildcard tokens. 1067 func subjectHasWildcard(subject string) bool { 1068 // This one exits earlier then !subjectIsLiteral(subject) 1069 for i, c := range subject { 1070 if c == pwc || c == fwc { 1071 if (i == 0 || subject[i-1] == btsep) && 1072 (i+1 == len(subject) || subject[i+1] == btsep) { 1073 return true 1074 } 1075 } 1076 } 1077 return false 1078 } 1079 1080 // Determine if the subject has any wildcards. Fast version, does not check for 1081 // valid subject. Used in caching layer. 1082 func subjectIsLiteral(subject string) bool { 1083 for i, c := range subject { 1084 if c == pwc || c == fwc { 1085 if (i == 0 || subject[i-1] == btsep) && 1086 (i+1 == len(subject) || subject[i+1] == btsep) { 1087 return false 1088 } 1089 } 1090 } 1091 return true 1092 } 1093 1094 // IsValidPublishSubject returns true if a subject is valid and a literal, false otherwise 1095 func IsValidPublishSubject(subject string) bool { 1096 return IsValidSubject(subject) && subjectIsLiteral(subject) 1097 } 1098 1099 // IsValidSubject returns true if a subject is valid, false otherwise 1100 func IsValidSubject(subject string) bool { 1101 return isValidSubject(subject, false) 1102 } 1103 1104 func isValidSubject(subject string, checkRunes bool) bool { 1105 if subject == _EMPTY_ { 1106 return false 1107 } 1108 if checkRunes { 1109 // Since casting to a string will always produce valid UTF-8, we need to look for replacement runes. 1110 // This signals something is off or corrupt. 1111 for _, r := range subject { 1112 if r == utf8.RuneError { 1113 return false 1114 } 1115 } 1116 } 1117 sfwc := false 1118 tokens := strings.Split(subject, tsep) 1119 for _, t := range tokens { 1120 length := len(t) 1121 if length == 0 || sfwc { 1122 return false 1123 } 1124 if length > 1 { 1125 if strings.ContainsAny(t, "\t\n\f\r ") { 1126 return false 1127 } 1128 continue 1129 } 1130 switch t[0] { 1131 case fwc: 1132 sfwc = true 1133 case ' ', '\t', '\n', '\r', '\f': 1134 return false 1135 } 1136 } 1137 return true 1138 } 1139 1140 // IsValidLiteralSubject returns true if a subject is valid and literal (no wildcards), false otherwise 1141 func IsValidLiteralSubject(subject string) bool { 1142 return isValidLiteralSubject(strings.Split(subject, tsep)) 1143 } 1144 1145 // isValidLiteralSubject returns true if the tokens are valid and literal (no wildcards), false otherwise 1146 func isValidLiteralSubject(tokens []string) bool { 1147 for _, t := range tokens { 1148 if len(t) == 0 { 1149 return false 1150 } 1151 if len(t) > 1 { 1152 continue 1153 } 1154 switch t[0] { 1155 case pwc, fwc: 1156 return false 1157 } 1158 } 1159 return true 1160 } 1161 1162 // ValidateMappingDestination returns nil error if the subject is a valid subject mapping destination subject 1163 func ValidateMappingDestination(subject string) error { 1164 if subject == _EMPTY_ { 1165 return nil 1166 } 1167 subjectTokens := strings.Split(subject, tsep) 1168 sfwc := false 1169 for _, t := range subjectTokens { 1170 length := len(t) 1171 if length == 0 || sfwc { 1172 return &mappingDestinationErr{t, ErrInvalidMappingDestinationSubject} 1173 } 1174 1175 if length > 4 && t[0] == '{' && t[1] == '{' && t[length-2] == '}' && t[length-1] == '}' { 1176 if !partitionMappingFunctionRegEx.MatchString(t) && 1177 !wildcardMappingFunctionRegEx.MatchString(t) && 1178 !splitFromLeftMappingFunctionRegEx.MatchString(t) && 1179 !splitFromRightMappingFunctionRegEx.MatchString(t) && 1180 !sliceFromLeftMappingFunctionRegEx.MatchString(t) && 1181 !sliceFromRightMappingFunctionRegEx.MatchString(t) && 1182 !splitMappingFunctionRegEx.MatchString(t) { 1183 return &mappingDestinationErr{t, ErrUnknownMappingDestinationFunction} 1184 } else { 1185 continue 1186 } 1187 } 1188 1189 if length == 1 && t[0] == fwc { 1190 sfwc = true 1191 } else if strings.ContainsAny(t, "\t\n\f\r ") { 1192 return ErrInvalidMappingDestinationSubject 1193 } 1194 } 1195 return nil 1196 } 1197 1198 // Will check tokens and report back if the have any partial or full wildcards. 1199 func analyzeTokens(tokens []string) (hasPWC, hasFWC bool) { 1200 for _, t := range tokens { 1201 if lt := len(t); lt == 0 || lt > 1 { 1202 continue 1203 } 1204 switch t[0] { 1205 case pwc: 1206 hasPWC = true 1207 case fwc: 1208 hasFWC = true 1209 } 1210 } 1211 return 1212 } 1213 1214 // Check on a token basis if they could match. 1215 func tokensCanMatch(t1, t2 string) bool { 1216 if len(t1) == 0 || len(t2) == 0 { 1217 return false 1218 } 1219 t1c, t2c := t1[0], t2[0] 1220 if t1c == pwc || t2c == pwc || t1c == fwc || t2c == fwc { 1221 return true 1222 } 1223 return t1 == t2 1224 } 1225 1226 // SubjectsCollide will determine if two subjects could both match a single literal subject. 1227 func SubjectsCollide(subj1, subj2 string) bool { 1228 if subj1 == subj2 { 1229 return true 1230 } 1231 toks1 := strings.Split(subj1, tsep) 1232 toks2 := strings.Split(subj2, tsep) 1233 pwc1, fwc1 := analyzeTokens(toks1) 1234 pwc2, fwc2 := analyzeTokens(toks2) 1235 // if both literal just string compare. 1236 l1, l2 := !(pwc1 || fwc1), !(pwc2 || fwc2) 1237 if l1 && l2 { 1238 return subj1 == subj2 1239 } 1240 // So one or both have wildcards. If one is literal than we can do subset matching. 1241 if l1 && !l2 { 1242 return isSubsetMatch(toks1, subj2) 1243 } else if l2 && !l1 { 1244 return isSubsetMatch(toks2, subj1) 1245 } 1246 // Both have wildcards. 1247 // If they only have partials then the lengths must match. 1248 if !fwc1 && !fwc2 && len(toks1) != len(toks2) { 1249 return false 1250 } 1251 if lt1, lt2 := len(toks1), len(toks2); lt1 != lt2 { 1252 // If the shorter one only has partials then these will not collide. 1253 if lt1 < lt2 && !fwc1 || lt2 < lt1 && !fwc2 { 1254 return false 1255 } 1256 } 1257 1258 stop := len(toks1) 1259 if len(toks2) < stop { 1260 stop = len(toks2) 1261 } 1262 1263 // We look for reasons to say no. 1264 for i := 0; i < stop; i++ { 1265 t1, t2 := toks1[i], toks2[i] 1266 if !tokensCanMatch(t1, t2) { 1267 return false 1268 } 1269 } 1270 1271 return true 1272 } 1273 1274 // Returns number of tokens in the subject. 1275 func numTokens(subject string) int { 1276 var numTokens int 1277 if len(subject) == 0 { 1278 return 0 1279 } 1280 for i := 0; i < len(subject); i++ { 1281 if subject[i] == btsep { 1282 numTokens++ 1283 } 1284 } 1285 return numTokens + 1 1286 } 1287 1288 // Fast way to return an indexed token. 1289 // This is one based, so first token is TokenAt(subject, 1) 1290 func tokenAt(subject string, index uint8) string { 1291 ti, start := uint8(1), 0 1292 for i := 0; i < len(subject); i++ { 1293 if subject[i] == btsep { 1294 if ti == index { 1295 return subject[start:i] 1296 } 1297 start = i + 1 1298 ti++ 1299 } 1300 } 1301 if ti == index { 1302 return subject[start:] 1303 } 1304 return _EMPTY_ 1305 } 1306 1307 // use similar to append. meaning, the updated slice will be returned 1308 func tokenizeSubjectIntoSlice(tts []string, subject string) []string { 1309 start := 0 1310 for i := 0; i < len(subject); i++ { 1311 if subject[i] == btsep { 1312 tts = append(tts, subject[start:i]) 1313 start = i + 1 1314 } 1315 } 1316 tts = append(tts, subject[start:]) 1317 return tts 1318 } 1319 1320 // Calls into the function isSubsetMatch() 1321 func subjectIsSubsetMatch(subject, test string) bool { 1322 tsa := [32]string{} 1323 tts := tokenizeSubjectIntoSlice(tsa[:0], subject) 1324 return isSubsetMatch(tts, test) 1325 } 1326 1327 // This will test a subject as an array of tokens against a test subject 1328 // Calls into the function isSubsetMatchTokenized 1329 func isSubsetMatch(tokens []string, test string) bool { 1330 tsa := [32]string{} 1331 tts := tokenizeSubjectIntoSlice(tsa[:0], test) 1332 return isSubsetMatchTokenized(tokens, tts) 1333 } 1334 1335 // This will test a subject as an array of tokens against a test subject (also encoded as array of tokens) 1336 // and determine if the tokens are matched. Both test subject and tokens 1337 // may contain wildcards. So foo.* is a subset match of [">", "*.*", "foo.*"], 1338 // but not of foo.bar, etc. 1339 func isSubsetMatchTokenized(tokens, test []string) bool { 1340 // Walk the target tokens 1341 for i, t2 := range test { 1342 if i >= len(tokens) { 1343 return false 1344 } 1345 l := len(t2) 1346 if l == 0 { 1347 return false 1348 } 1349 if t2[0] == fwc && l == 1 { 1350 return true 1351 } 1352 t1 := tokens[i] 1353 1354 l = len(t1) 1355 if l == 0 || t1[0] == fwc && l == 1 { 1356 return false 1357 } 1358 1359 if t1[0] == pwc && len(t1) == 1 { 1360 m := t2[0] == pwc && len(t2) == 1 1361 if !m { 1362 return false 1363 } 1364 if i >= len(test) { 1365 return true 1366 } 1367 continue 1368 } 1369 if t2[0] != pwc && strings.Compare(t1, t2) != 0 { 1370 return false 1371 } 1372 } 1373 return len(tokens) == len(test) 1374 } 1375 1376 // matchLiteral is used to test literal subjects, those that do not have any 1377 // wildcards, with a target subject. This is used in the cache layer. 1378 func matchLiteral(literal, subject string) bool { 1379 li := 0 1380 ll := len(literal) 1381 ls := len(subject) 1382 for i := 0; i < ls; i++ { 1383 if li >= ll { 1384 return false 1385 } 1386 // This function has been optimized for speed. 1387 // For instance, do not set b:=subject[i] here since 1388 // we may bump `i` in this loop to avoid `continue` or 1389 // skipping common test in a particular test. 1390 // Run Benchmark_SublistMatchLiteral before making any change. 1391 switch subject[i] { 1392 case pwc: 1393 // NOTE: This is not testing validity of a subject, instead ensures 1394 // that wildcards are treated as such if they follow some basic rules, 1395 // namely that they are a token on their own. 1396 if i == 0 || subject[i-1] == btsep { 1397 if i == ls-1 { 1398 // There is no more token in the subject after this wildcard. 1399 // Skip token in literal and expect to not find a separator. 1400 for { 1401 // End of literal, this is a match. 1402 if li >= ll { 1403 return true 1404 } 1405 // Presence of separator, this can't be a match. 1406 if literal[li] == btsep { 1407 return false 1408 } 1409 li++ 1410 } 1411 } else if subject[i+1] == btsep { 1412 // There is another token in the subject after this wildcard. 1413 // Skip token in literal and expect to get a separator. 1414 for { 1415 // We found the end of the literal before finding a separator, 1416 // this can't be a match. 1417 if li >= ll { 1418 return false 1419 } 1420 if literal[li] == btsep { 1421 break 1422 } 1423 li++ 1424 } 1425 // Bump `i` since we know there is a `.` following, we are 1426 // safe. The common test below is going to check `.` with `.` 1427 // which is good. A `continue` here is too costly. 1428 i++ 1429 } 1430 } 1431 case fwc: 1432 // For `>` to be a wildcard, it means being the only or last character 1433 // in the string preceded by a `.` 1434 if (i == 0 || subject[i-1] == btsep) && i == ls-1 { 1435 return true 1436 } 1437 } 1438 if subject[i] != literal[li] { 1439 return false 1440 } 1441 li++ 1442 } 1443 // Make sure we have processed all of the literal's chars.. 1444 return li >= ll 1445 } 1446 1447 func addLocalSub(sub *subscription, subs *[]*subscription, includeLeafHubs bool) { 1448 if sub != nil && sub.client != nil { 1449 kind := sub.client.kind 1450 if kind == CLIENT || kind == SYSTEM || kind == JETSTREAM || kind == ACCOUNT || 1451 (includeLeafHubs && sub.client.isHubLeafNode() /* implied kind==LEAF */) { 1452 *subs = append(*subs, sub) 1453 } 1454 } 1455 } 1456 1457 func (s *Sublist) addNodeToSubs(n *node, subs *[]*subscription, includeLeafHubs bool) { 1458 // Normal subscriptions 1459 if n.plist != nil { 1460 for _, sub := range n.plist { 1461 addLocalSub(sub, subs, includeLeafHubs) 1462 } 1463 } else { 1464 for sub := range n.psubs { 1465 addLocalSub(sub, subs, includeLeafHubs) 1466 } 1467 } 1468 // Queue subscriptions 1469 for _, qr := range n.qsubs { 1470 for sub := range qr { 1471 addLocalSub(sub, subs, includeLeafHubs) 1472 } 1473 } 1474 } 1475 1476 func (s *Sublist) collectLocalSubs(l *level, subs *[]*subscription, includeLeafHubs bool) { 1477 for _, n := range l.nodes { 1478 s.addNodeToSubs(n, subs, includeLeafHubs) 1479 s.collectLocalSubs(n.next, subs, includeLeafHubs) 1480 } 1481 if l.pwc != nil { 1482 s.addNodeToSubs(l.pwc, subs, includeLeafHubs) 1483 s.collectLocalSubs(l.pwc.next, subs, includeLeafHubs) 1484 } 1485 if l.fwc != nil { 1486 s.addNodeToSubs(l.fwc, subs, includeLeafHubs) 1487 s.collectLocalSubs(l.fwc.next, subs, includeLeafHubs) 1488 } 1489 } 1490 1491 // Return all local client subscriptions. Use the supplied slice. 1492 func (s *Sublist) localSubs(subs *[]*subscription, includeLeafHubs bool) { 1493 s.RLock() 1494 s.collectLocalSubs(s.root, subs, includeLeafHubs) 1495 s.RUnlock() 1496 } 1497 1498 // All is used to collect all subscriptions. 1499 func (s *Sublist) All(subs *[]*subscription) { 1500 s.RLock() 1501 s.collectAllSubs(s.root, subs) 1502 s.RUnlock() 1503 } 1504 1505 func (s *Sublist) addAllNodeToSubs(n *node, subs *[]*subscription) { 1506 // Normal subscriptions 1507 if n.plist != nil { 1508 *subs = append(*subs, n.plist...) 1509 } else { 1510 for sub := range n.psubs { 1511 *subs = append(*subs, sub) 1512 } 1513 } 1514 // Queue subscriptions 1515 for _, qr := range n.qsubs { 1516 for sub := range qr { 1517 *subs = append(*subs, sub) 1518 } 1519 } 1520 } 1521 1522 func (s *Sublist) collectAllSubs(l *level, subs *[]*subscription) { 1523 for _, n := range l.nodes { 1524 s.addAllNodeToSubs(n, subs) 1525 s.collectAllSubs(n.next, subs) 1526 } 1527 if l.pwc != nil { 1528 s.addAllNodeToSubs(l.pwc, subs) 1529 s.collectAllSubs(l.pwc.next, subs) 1530 } 1531 if l.fwc != nil { 1532 s.addAllNodeToSubs(l.fwc, subs) 1533 s.collectAllSubs(l.fwc.next, subs) 1534 } 1535 } 1536 1537 // For a given subject (which may contain wildcards), this call returns all 1538 // subscriptions that would match that subject. For instance, suppose that 1539 // the sublist contains: foo.bar, foo.bar.baz and foo.baz, ReverseMatch("foo.*") 1540 // would return foo.bar and foo.baz. 1541 // This is used in situations where the sublist is likely to contain only 1542 // literals and one wants to get all the subjects that would have been a match 1543 // to a subscription on `subject`. 1544 func (s *Sublist) ReverseMatch(subject string) *SublistResult { 1545 tsa := [32]string{} 1546 tokens := tsa[:0] 1547 start := 0 1548 for i := 0; i < len(subject); i++ { 1549 if subject[i] == btsep { 1550 tokens = append(tokens, subject[start:i]) 1551 start = i + 1 1552 } 1553 } 1554 tokens = append(tokens, subject[start:]) 1555 1556 result := &SublistResult{} 1557 1558 s.RLock() 1559 reverseMatchLevel(s.root, tokens, nil, result) 1560 // Check for empty result. 1561 if len(result.psubs) == 0 && len(result.qsubs) == 0 { 1562 result = emptyResult 1563 } 1564 s.RUnlock() 1565 1566 return result 1567 } 1568 1569 func reverseMatchLevel(l *level, toks []string, n *node, results *SublistResult) { 1570 if l == nil { 1571 return 1572 } 1573 for i, t := range toks { 1574 if len(t) == 1 { 1575 if t[0] == fwc { 1576 getAllNodes(l, results) 1577 return 1578 } else if t[0] == pwc { 1579 for _, n := range l.nodes { 1580 reverseMatchLevel(n.next, toks[i+1:], n, results) 1581 } 1582 if l.pwc != nil { 1583 reverseMatchLevel(l.pwc.next, toks[i+1:], n, results) 1584 } 1585 if l.fwc != nil { 1586 getAllNodes(l, results) 1587 } 1588 return 1589 } 1590 } 1591 // If the sub tree has a fwc at this position, match as well. 1592 if l.fwc != nil { 1593 getAllNodes(l, results) 1594 return 1595 } else if l.pwc != nil { 1596 reverseMatchLevel(l.pwc.next, toks[i+1:], n, results) 1597 } 1598 n = l.nodes[t] 1599 if n == nil { 1600 break 1601 } 1602 l = n.next 1603 } 1604 if n != nil { 1605 addNodeToResults(n, results) 1606 } 1607 } 1608 1609 func getAllNodes(l *level, results *SublistResult) { 1610 if l == nil { 1611 return 1612 } 1613 if l.pwc != nil { 1614 addNodeToResults(l.pwc, results) 1615 } 1616 if l.fwc != nil { 1617 addNodeToResults(l.fwc, results) 1618 } 1619 for _, n := range l.nodes { 1620 addNodeToResults(n, results) 1621 getAllNodes(n.next, results) 1622 } 1623 }