get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/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, sub *subscription) { 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) 531 } 532 533 func (s *Sublist) matchNoLock(subject string) *SublistResult { 534 return s.match(subject, false) 535 } 536 537 func (s *Sublist) match(subject string, doLock bool) *SublistResult { 538 atomic.AddUint64(&s.matches, 1) 539 540 // Check cache first. 541 if doLock { 542 s.RLock() 543 } 544 cacheEnabled := s.cache != nil 545 r, ok := s.cache[subject] 546 if doLock { 547 s.RUnlock() 548 } 549 if ok { 550 atomic.AddUint64(&s.cacheHits, 1) 551 return r 552 } 553 554 tsa := [32]string{} 555 tokens := tsa[:0] 556 start := 0 557 for i := 0; i < len(subject); i++ { 558 if subject[i] == btsep { 559 if i-start == 0 { 560 return emptyResult 561 } 562 tokens = append(tokens, subject[start:i]) 563 start = i + 1 564 } 565 } 566 if start >= len(subject) { 567 return emptyResult 568 } 569 tokens = append(tokens, subject[start:]) 570 571 // FIXME(dlc) - Make shared pool between sublist and client readLoop? 572 result := &SublistResult{} 573 574 // Get result from the main structure and place into the shared cache. 575 // Hold the read lock to avoid race between match and store. 576 var n int 577 578 if doLock { 579 if cacheEnabled { 580 s.Lock() 581 } else { 582 s.RLock() 583 } 584 } 585 586 matchLevel(s.root, tokens, result) 587 // Check for empty result. 588 if len(result.psubs) == 0 && len(result.qsubs) == 0 { 589 result = emptyResult 590 } 591 if cacheEnabled { 592 s.cache[subject] = result 593 n = len(s.cache) 594 } 595 if doLock { 596 if cacheEnabled { 597 s.Unlock() 598 } else { 599 s.RUnlock() 600 } 601 } 602 603 // Reduce the cache count if we have exceeded our set maximum. 604 if cacheEnabled && n > slCacheMax && atomic.CompareAndSwapInt32(&s.ccSweep, 0, 1) { 605 go s.reduceCacheCount() 606 } 607 608 return result 609 } 610 611 // Remove entries in the cache until we are under the maximum. 612 // TODO(dlc) this could be smarter now that its not inline. 613 func (s *Sublist) reduceCacheCount() { 614 defer atomic.StoreInt32(&s.ccSweep, 0) 615 // If we are over the cache limit randomly drop until under the limit. 616 s.Lock() 617 for key := range s.cache { 618 delete(s.cache, key) 619 if len(s.cache) <= slCacheSweep { 620 break 621 } 622 } 623 s.Unlock() 624 } 625 626 // Helper function for auto-expanding remote qsubs. 627 func isRemoteQSub(sub *subscription) bool { 628 return sub != nil && sub.queue != nil && sub.client != nil && (sub.client.kind == ROUTER || sub.client.kind == LEAF) 629 } 630 631 // UpdateRemoteQSub should be called when we update the weight of an existing 632 // remote queue sub. 633 func (s *Sublist) UpdateRemoteQSub(sub *subscription) { 634 // We could search to make sure we find it, but probably not worth 635 // it unless we are thrashing the cache. Just remove from our L2 and update 636 // the genid so L1 will be flushed. 637 s.Lock() 638 s.removeFromCache(string(sub.subject), sub) 639 atomic.AddUint64(&s.genid, 1) 640 s.Unlock() 641 } 642 643 // This will add in a node's results to the total results. 644 func addNodeToResults(n *node, results *SublistResult) { 645 // Normal subscriptions 646 if n.plist != nil { 647 results.psubs = append(results.psubs, n.plist...) 648 } else { 649 for psub := range n.psubs { 650 results.psubs = append(results.psubs, psub) 651 } 652 } 653 // Queue subscriptions 654 for qname, qr := range n.qsubs { 655 if len(qr) == 0 { 656 continue 657 } 658 // Need to find matching list in results 659 var i int 660 if i = findQSlot([]byte(qname), results.qsubs); i < 0 { 661 i = len(results.qsubs) 662 nqsub := make([]*subscription, 0, len(qr)) 663 results.qsubs = append(results.qsubs, nqsub) 664 } 665 for sub := range qr { 666 if isRemoteQSub(sub) { 667 ns := atomic.LoadInt32(&sub.qw) 668 // Shadow these subscriptions 669 for n := 0; n < int(ns); n++ { 670 results.qsubs[i] = append(results.qsubs[i], sub) 671 } 672 } else { 673 results.qsubs[i] = append(results.qsubs[i], sub) 674 } 675 } 676 } 677 } 678 679 // We do not use a map here since we want iteration to be past when 680 // processing publishes in L1 on client. So we need to walk sequentially 681 // for now. Keep an eye on this in case we start getting large number of 682 // different queue subscribers for the same subject. 683 func findQSlot(queue []byte, qsl [][]*subscription) int { 684 if queue == nil { 685 return -1 686 } 687 for i, qr := range qsl { 688 if len(qr) > 0 && bytes.Equal(queue, qr[0].queue) { 689 return i 690 } 691 } 692 return -1 693 } 694 695 // matchLevel is used to recursively descend into the trie. 696 func matchLevel(l *level, toks []string, results *SublistResult) { 697 var pwc, n *node 698 for i, t := range toks { 699 if l == nil { 700 return 701 } 702 if l.fwc != nil { 703 addNodeToResults(l.fwc, results) 704 } 705 if pwc = l.pwc; pwc != nil { 706 matchLevel(pwc.next, toks[i+1:], results) 707 } 708 n = l.nodes[t] 709 if n != nil { 710 l = n.next 711 } else { 712 l = nil 713 } 714 } 715 if n != nil { 716 addNodeToResults(n, results) 717 } 718 if pwc != nil { 719 addNodeToResults(pwc, results) 720 } 721 } 722 723 // lnt is used to track descent into levels for a removal for pruning. 724 type lnt struct { 725 l *level 726 n *node 727 t string 728 } 729 730 // Raw low level remove, can do batches with lock held outside. 731 func (s *Sublist) remove(sub *subscription, shouldLock bool, doCacheUpdates bool) error { 732 subject := string(sub.subject) 733 tsa := [32]string{} 734 tokens := tsa[:0] 735 start := 0 736 for i := 0; i < len(subject); i++ { 737 if subject[i] == btsep { 738 tokens = append(tokens, subject[start:i]) 739 start = i + 1 740 } 741 } 742 tokens = append(tokens, subject[start:]) 743 744 if shouldLock { 745 s.Lock() 746 defer s.Unlock() 747 } 748 749 var sfwc, haswc bool 750 var n *node 751 l := s.root 752 753 // Track levels for pruning 754 var lnts [32]lnt 755 levels := lnts[:0] 756 757 for _, t := range tokens { 758 lt := len(t) 759 if lt == 0 || sfwc { 760 return ErrInvalidSubject 761 } 762 if l == nil { 763 return ErrNotFound 764 } 765 if lt > 1 { 766 n = l.nodes[t] 767 } else { 768 switch t[0] { 769 case pwc: 770 n = l.pwc 771 haswc = true 772 case fwc: 773 n = l.fwc 774 haswc, sfwc = true, true 775 default: 776 n = l.nodes[t] 777 } 778 } 779 if n != nil { 780 levels = append(levels, lnt{l, n, t}) 781 l = n.next 782 } else { 783 l = nil 784 } 785 } 786 removed, last := s.removeFromNode(n, sub) 787 if !removed { 788 return ErrNotFound 789 } 790 791 s.count-- 792 s.removes++ 793 794 for i := len(levels) - 1; i >= 0; i-- { 795 l, n, t := levels[i].l, levels[i].n, levels[i].t 796 if n.isEmpty() { 797 l.pruneNode(n, t) 798 } 799 } 800 if doCacheUpdates { 801 s.removeFromCache(subject, sub) 802 atomic.AddUint64(&s.genid, 1) 803 } 804 805 if s.notify != nil && last && !haswc && len(s.notify.remove) > 0 { 806 s.chkForRemoveNotification(subject, string(sub.queue)) 807 } 808 809 return nil 810 } 811 812 // Remove will remove a subscription. 813 func (s *Sublist) Remove(sub *subscription) error { 814 return s.remove(sub, true, true) 815 } 816 817 // RemoveBatch will remove a list of subscriptions. 818 func (s *Sublist) RemoveBatch(subs []*subscription) error { 819 if len(subs) == 0 { 820 return nil 821 } 822 823 s.Lock() 824 defer s.Unlock() 825 826 // TODO(dlc) - We could try to be smarter here for a client going away but the account 827 // has a large number of subscriptions compared to this client. Quick and dirty testing 828 // though said just disabling all the time best for now. 829 830 // Turn off our cache if enabled. 831 wasEnabled := s.cache != nil 832 s.cache = nil 833 // We will try to remove all subscriptions but will report the first that caused 834 // an error. In other words, we don't bail out at the first error which would 835 // possibly leave a bunch of subscriptions that could have been removed. 836 var err error 837 for _, sub := range subs { 838 if lerr := s.remove(sub, false, false); lerr != nil && err == nil { 839 err = lerr 840 } 841 } 842 // Turn caching back on here. 843 atomic.AddUint64(&s.genid, 1) 844 if wasEnabled { 845 s.cache = make(map[string]*SublistResult) 846 } 847 return err 848 } 849 850 // pruneNode is used to prune an empty node from the tree. 851 func (l *level) pruneNode(n *node, t string) { 852 if n == nil { 853 return 854 } 855 if n == l.fwc { 856 l.fwc = nil 857 } else if n == l.pwc { 858 l.pwc = nil 859 } else { 860 delete(l.nodes, t) 861 } 862 } 863 864 // isEmpty will test if the node has any entries. Used 865 // in pruning. 866 func (n *node) isEmpty() bool { 867 if len(n.psubs) == 0 && len(n.qsubs) == 0 { 868 if n.next == nil || n.next.numNodes() == 0 { 869 return true 870 } 871 } 872 return false 873 } 874 875 // Return the number of nodes for the given level. 876 func (l *level) numNodes() int { 877 num := len(l.nodes) 878 if l.pwc != nil { 879 num++ 880 } 881 if l.fwc != nil { 882 num++ 883 } 884 return num 885 } 886 887 // Remove the sub for the given node. 888 func (s *Sublist) removeFromNode(n *node, sub *subscription) (found, last bool) { 889 if n == nil { 890 return false, true 891 } 892 if sub.queue == nil { 893 _, found = n.psubs[sub] 894 delete(n.psubs, sub) 895 if found && n.plist != nil { 896 // This will brute force remove the plist to perform 897 // correct behavior. Will get re-populated on a call 898 // to Match as needed. 899 n.plist = nil 900 } 901 return found, len(n.psubs) == 0 902 } 903 904 // We have a queue group subscription here 905 qsub := n.qsubs[string(sub.queue)] 906 _, found = qsub[sub] 907 delete(qsub, sub) 908 if len(qsub) == 0 { 909 // This is the last queue subscription interest when len(qsub) == 0, not 910 // when n.qsubs is empty. 911 last = true 912 delete(n.qsubs, string(sub.queue)) 913 } 914 return found, last 915 } 916 917 // Count returns the number of subscriptions. 918 func (s *Sublist) Count() uint32 { 919 s.RLock() 920 defer s.RUnlock() 921 return s.count 922 } 923 924 // CacheCount returns the number of result sets in the cache. 925 func (s *Sublist) CacheCount() int { 926 s.RLock() 927 cc := len(s.cache) 928 s.RUnlock() 929 return cc 930 } 931 932 // SublistStats are public stats for the sublist 933 type SublistStats struct { 934 NumSubs uint32 `json:"num_subscriptions"` 935 NumCache uint32 `json:"num_cache"` 936 NumInserts uint64 `json:"num_inserts"` 937 NumRemoves uint64 `json:"num_removes"` 938 NumMatches uint64 `json:"num_matches"` 939 CacheHitRate float64 `json:"cache_hit_rate"` 940 MaxFanout uint32 `json:"max_fanout"` 941 AvgFanout float64 `json:"avg_fanout"` 942 totFanout int 943 cacheCnt int 944 cacheHits uint64 945 } 946 947 func (s *SublistStats) add(stat *SublistStats) { 948 s.NumSubs += stat.NumSubs 949 s.NumCache += stat.NumCache 950 s.NumInserts += stat.NumInserts 951 s.NumRemoves += stat.NumRemoves 952 s.NumMatches += stat.NumMatches 953 s.cacheHits += stat.cacheHits 954 if s.MaxFanout < stat.MaxFanout { 955 s.MaxFanout = stat.MaxFanout 956 } 957 958 // ignore slStats.AvgFanout, collect the values 959 // it's based on instead 960 s.totFanout += stat.totFanout 961 s.cacheCnt += stat.cacheCnt 962 if s.totFanout > 0 { 963 s.AvgFanout = float64(s.totFanout) / float64(s.cacheCnt) 964 } 965 if s.NumMatches > 0 { 966 s.CacheHitRate = float64(s.cacheHits) / float64(s.NumMatches) 967 } 968 } 969 970 // Stats will return a stats structure for the current state. 971 func (s *Sublist) Stats() *SublistStats { 972 st := &SublistStats{} 973 974 s.RLock() 975 cache := s.cache 976 cc := len(s.cache) 977 st.NumSubs = s.count 978 st.NumInserts = s.inserts 979 st.NumRemoves = s.removes 980 s.RUnlock() 981 982 st.NumCache = uint32(cc) 983 st.NumMatches = atomic.LoadUint64(&s.matches) 984 st.cacheHits = atomic.LoadUint64(&s.cacheHits) 985 if st.NumMatches > 0 { 986 st.CacheHitRate = float64(st.cacheHits) / float64(st.NumMatches) 987 } 988 989 // whip through cache for fanout stats, this can be off if cache is full and doing evictions. 990 // If this is called frequently, which it should not be, this could hurt performance. 991 if cache != nil { 992 tot, max, clen := 0, 0, 0 993 s.RLock() 994 for _, r := range s.cache { 995 clen++ 996 l := len(r.psubs) + len(r.qsubs) 997 tot += l 998 if l > max { 999 max = l 1000 } 1001 } 1002 s.RUnlock() 1003 st.totFanout = tot 1004 st.cacheCnt = clen 1005 st.MaxFanout = uint32(max) 1006 if tot > 0 { 1007 st.AvgFanout = float64(tot) / float64(clen) 1008 } 1009 } 1010 return st 1011 } 1012 1013 // numLevels will return the maximum number of levels 1014 // contained in the Sublist tree. 1015 func (s *Sublist) numLevels() int { 1016 return visitLevel(s.root, 0) 1017 } 1018 1019 // visitLevel is used to descend the Sublist tree structure 1020 // recursively. 1021 func visitLevel(l *level, depth int) int { 1022 if l == nil || l.numNodes() == 0 { 1023 return depth 1024 } 1025 1026 depth++ 1027 maxDepth := depth 1028 1029 for _, n := range l.nodes { 1030 if n == nil { 1031 continue 1032 } 1033 newDepth := visitLevel(n.next, depth) 1034 if newDepth > maxDepth { 1035 maxDepth = newDepth 1036 } 1037 } 1038 if l.pwc != nil { 1039 pwcDepth := visitLevel(l.pwc.next, depth) 1040 if pwcDepth > maxDepth { 1041 maxDepth = pwcDepth 1042 } 1043 } 1044 if l.fwc != nil { 1045 fwcDepth := visitLevel(l.fwc.next, depth) 1046 if fwcDepth > maxDepth { 1047 maxDepth = fwcDepth 1048 } 1049 } 1050 return maxDepth 1051 } 1052 1053 // Determine if a subject has any wildcard tokens. 1054 func subjectHasWildcard(subject string) bool { 1055 // This one exits earlier then !subjectIsLiteral(subject) 1056 for i, c := range subject { 1057 if c == pwc || c == fwc { 1058 if (i == 0 || subject[i-1] == btsep) && 1059 (i+1 == len(subject) || subject[i+1] == btsep) { 1060 return true 1061 } 1062 } 1063 } 1064 return false 1065 } 1066 1067 // Determine if the subject has any wildcards. Fast version, does not check for 1068 // valid subject. Used in caching layer. 1069 func subjectIsLiteral(subject string) bool { 1070 for i, c := range subject { 1071 if c == pwc || c == fwc { 1072 if (i == 0 || subject[i-1] == btsep) && 1073 (i+1 == len(subject) || subject[i+1] == btsep) { 1074 return false 1075 } 1076 } 1077 } 1078 return true 1079 } 1080 1081 // IsValidPublishSubject returns true if a subject is valid and a literal, false otherwise 1082 func IsValidPublishSubject(subject string) bool { 1083 return IsValidSubject(subject) && subjectIsLiteral(subject) 1084 } 1085 1086 // IsValidSubject returns true if a subject is valid, false otherwise 1087 func IsValidSubject(subject string) bool { 1088 return isValidSubject(subject, false) 1089 } 1090 1091 func isValidSubject(subject string, checkRunes bool) bool { 1092 if subject == _EMPTY_ { 1093 return false 1094 } 1095 if checkRunes { 1096 // Since casting to a string will always produce valid UTF-8, we need to look for replacement runes. 1097 // This signals something is off or corrupt. 1098 for _, r := range subject { 1099 if r == utf8.RuneError { 1100 return false 1101 } 1102 } 1103 } 1104 sfwc := false 1105 tokens := strings.Split(subject, tsep) 1106 for _, t := range tokens { 1107 length := len(t) 1108 if length == 0 || sfwc { 1109 return false 1110 } 1111 if length > 1 { 1112 if strings.ContainsAny(t, "\t\n\f\r ") { 1113 return false 1114 } 1115 continue 1116 } 1117 switch t[0] { 1118 case fwc: 1119 sfwc = true 1120 case ' ', '\t', '\n', '\r', '\f': 1121 return false 1122 } 1123 } 1124 return true 1125 } 1126 1127 // IsValidLiteralSubject returns true if a subject is valid and literal (no wildcards), false otherwise 1128 func IsValidLiteralSubject(subject string) bool { 1129 return isValidLiteralSubject(strings.Split(subject, tsep)) 1130 } 1131 1132 // isValidLiteralSubject returns true if the tokens are valid and literal (no wildcards), false otherwise 1133 func isValidLiteralSubject(tokens []string) bool { 1134 for _, t := range tokens { 1135 if len(t) == 0 { 1136 return false 1137 } 1138 if len(t) > 1 { 1139 continue 1140 } 1141 switch t[0] { 1142 case pwc, fwc: 1143 return false 1144 } 1145 } 1146 return true 1147 } 1148 1149 // ValidateMappingDestination returns nil error if the subject is a valid subject mapping destination subject 1150 func ValidateMappingDestination(subject string) error { 1151 if subject == _EMPTY_ { 1152 return nil 1153 } 1154 subjectTokens := strings.Split(subject, tsep) 1155 sfwc := false 1156 for _, t := range subjectTokens { 1157 length := len(t) 1158 if length == 0 || sfwc { 1159 return &mappingDestinationErr{t, ErrInvalidMappingDestinationSubject} 1160 } 1161 1162 if length > 4 && t[0] == '{' && t[1] == '{' && t[length-2] == '}' && t[length-1] == '}' { 1163 if !partitionMappingFunctionRegEx.MatchString(t) && 1164 !wildcardMappingFunctionRegEx.MatchString(t) && 1165 !splitFromLeftMappingFunctionRegEx.MatchString(t) && 1166 !splitFromRightMappingFunctionRegEx.MatchString(t) && 1167 !sliceFromLeftMappingFunctionRegEx.MatchString(t) && 1168 !sliceFromRightMappingFunctionRegEx.MatchString(t) && 1169 !splitMappingFunctionRegEx.MatchString(t) { 1170 return &mappingDestinationErr{t, ErrUnknownMappingDestinationFunction} 1171 } else { 1172 continue 1173 } 1174 } 1175 1176 if length == 1 && t[0] == fwc { 1177 sfwc = true 1178 } else if strings.ContainsAny(t, "\t\n\f\r ") { 1179 return ErrInvalidMappingDestinationSubject 1180 } 1181 } 1182 return nil 1183 } 1184 1185 // Will check tokens and report back if the have any partial or full wildcards. 1186 func analyzeTokens(tokens []string) (hasPWC, hasFWC bool) { 1187 for _, t := range tokens { 1188 if lt := len(t); lt == 0 || lt > 1 { 1189 continue 1190 } 1191 switch t[0] { 1192 case pwc: 1193 hasPWC = true 1194 case fwc: 1195 hasFWC = true 1196 } 1197 } 1198 return 1199 } 1200 1201 // Check on a token basis if they could match. 1202 func tokensCanMatch(t1, t2 string) bool { 1203 if len(t1) == 0 || len(t2) == 0 { 1204 return false 1205 } 1206 t1c, t2c := t1[0], t2[0] 1207 if t1c == pwc || t2c == pwc || t1c == fwc || t2c == fwc { 1208 return true 1209 } 1210 return t1 == t2 1211 } 1212 1213 // SubjectsCollide will determine if two subjects could both match a single literal subject. 1214 func SubjectsCollide(subj1, subj2 string) bool { 1215 if subj1 == subj2 { 1216 return true 1217 } 1218 toks1 := strings.Split(subj1, tsep) 1219 toks2 := strings.Split(subj2, tsep) 1220 pwc1, fwc1 := analyzeTokens(toks1) 1221 pwc2, fwc2 := analyzeTokens(toks2) 1222 // if both literal just string compare. 1223 l1, l2 := !(pwc1 || fwc1), !(pwc2 || fwc2) 1224 if l1 && l2 { 1225 return subj1 == subj2 1226 } 1227 // So one or both have wildcards. If one is literal than we can do subset matching. 1228 if l1 && !l2 { 1229 return isSubsetMatch(toks1, subj2) 1230 } else if l2 && !l1 { 1231 return isSubsetMatch(toks2, subj1) 1232 } 1233 // Both have wildcards. 1234 // If they only have partials then the lengths must match. 1235 if !fwc1 && !fwc2 && len(toks1) != len(toks2) { 1236 return false 1237 } 1238 if lt1, lt2 := len(toks1), len(toks2); lt1 != lt2 { 1239 // If the shorter one only has partials then these will not collide. 1240 if lt1 < lt2 && !fwc1 || lt2 < lt1 && !fwc2 { 1241 return false 1242 } 1243 } 1244 1245 stop := len(toks1) 1246 if len(toks2) < stop { 1247 stop = len(toks2) 1248 } 1249 1250 // We look for reasons to say no. 1251 for i := 0; i < stop; i++ { 1252 t1, t2 := toks1[i], toks2[i] 1253 if !tokensCanMatch(t1, t2) { 1254 return false 1255 } 1256 } 1257 1258 return true 1259 } 1260 1261 // Returns number of tokens in the subject. 1262 func numTokens(subject string) int { 1263 var numTokens int 1264 if len(subject) == 0 { 1265 return 0 1266 } 1267 for i := 0; i < len(subject); i++ { 1268 if subject[i] == btsep { 1269 numTokens++ 1270 } 1271 } 1272 return numTokens + 1 1273 } 1274 1275 // Fast way to return an indexed token. 1276 // This is one based, so first token is TokenAt(subject, 1) 1277 func tokenAt(subject string, index uint8) string { 1278 ti, start := uint8(1), 0 1279 for i := 0; i < len(subject); i++ { 1280 if subject[i] == btsep { 1281 if ti == index { 1282 return subject[start:i] 1283 } 1284 start = i + 1 1285 ti++ 1286 } 1287 } 1288 if ti == index { 1289 return subject[start:] 1290 } 1291 return _EMPTY_ 1292 } 1293 1294 // use similar to append. meaning, the updated slice will be returned 1295 func tokenizeSubjectIntoSlice(tts []string, subject string) []string { 1296 start := 0 1297 for i := 0; i < len(subject); i++ { 1298 if subject[i] == btsep { 1299 tts = append(tts, subject[start:i]) 1300 start = i + 1 1301 } 1302 } 1303 tts = append(tts, subject[start:]) 1304 return tts 1305 } 1306 1307 // Calls into the function isSubsetMatch() 1308 func subjectIsSubsetMatch(subject, test string) bool { 1309 tsa := [32]string{} 1310 tts := tokenizeSubjectIntoSlice(tsa[:0], subject) 1311 return isSubsetMatch(tts, test) 1312 } 1313 1314 // This will test a subject as an array of tokens against a test subject 1315 // Calls into the function isSubsetMatchTokenized 1316 func isSubsetMatch(tokens []string, test string) bool { 1317 tsa := [32]string{} 1318 tts := tokenizeSubjectIntoSlice(tsa[:0], test) 1319 return isSubsetMatchTokenized(tokens, tts) 1320 } 1321 1322 // This will test a subject as an array of tokens against a test subject (also encoded as array of tokens) 1323 // and determine if the tokens are matched. Both test subject and tokens 1324 // may contain wildcards. So foo.* is a subset match of [">", "*.*", "foo.*"], 1325 // but not of foo.bar, etc. 1326 func isSubsetMatchTokenized(tokens, test []string) bool { 1327 // Walk the target tokens 1328 for i, t2 := range test { 1329 if i >= len(tokens) { 1330 return false 1331 } 1332 l := len(t2) 1333 if l == 0 { 1334 return false 1335 } 1336 if t2[0] == fwc && l == 1 { 1337 return true 1338 } 1339 t1 := tokens[i] 1340 1341 l = len(t1) 1342 if l == 0 || t1[0] == fwc && l == 1 { 1343 return false 1344 } 1345 1346 if t1[0] == pwc && len(t1) == 1 { 1347 m := t2[0] == pwc && len(t2) == 1 1348 if !m { 1349 return false 1350 } 1351 if i >= len(test) { 1352 return true 1353 } 1354 continue 1355 } 1356 if t2[0] != pwc && strings.Compare(t1, t2) != 0 { 1357 return false 1358 } 1359 } 1360 return len(tokens) == len(test) 1361 } 1362 1363 // matchLiteral is used to test literal subjects, those that do not have any 1364 // wildcards, with a target subject. This is used in the cache layer. 1365 func matchLiteral(literal, subject string) bool { 1366 li := 0 1367 ll := len(literal) 1368 ls := len(subject) 1369 for i := 0; i < ls; i++ { 1370 if li >= ll { 1371 return false 1372 } 1373 // This function has been optimized for speed. 1374 // For instance, do not set b:=subject[i] here since 1375 // we may bump `i` in this loop to avoid `continue` or 1376 // skipping common test in a particular test. 1377 // Run Benchmark_SublistMatchLiteral before making any change. 1378 switch subject[i] { 1379 case pwc: 1380 // NOTE: This is not testing validity of a subject, instead ensures 1381 // that wildcards are treated as such if they follow some basic rules, 1382 // namely that they are a token on their own. 1383 if i == 0 || subject[i-1] == btsep { 1384 if i == ls-1 { 1385 // There is no more token in the subject after this wildcard. 1386 // Skip token in literal and expect to not find a separator. 1387 for { 1388 // End of literal, this is a match. 1389 if li >= ll { 1390 return true 1391 } 1392 // Presence of separator, this can't be a match. 1393 if literal[li] == btsep { 1394 return false 1395 } 1396 li++ 1397 } 1398 } else if subject[i+1] == btsep { 1399 // There is another token in the subject after this wildcard. 1400 // Skip token in literal and expect to get a separator. 1401 for { 1402 // We found the end of the literal before finding a separator, 1403 // this can't be a match. 1404 if li >= ll { 1405 return false 1406 } 1407 if literal[li] == btsep { 1408 break 1409 } 1410 li++ 1411 } 1412 // Bump `i` since we know there is a `.` following, we are 1413 // safe. The common test below is going to check `.` with `.` 1414 // which is good. A `continue` here is too costly. 1415 i++ 1416 } 1417 } 1418 case fwc: 1419 // For `>` to be a wildcard, it means being the only or last character 1420 // in the string preceded by a `.` 1421 if (i == 0 || subject[i-1] == btsep) && i == ls-1 { 1422 return true 1423 } 1424 } 1425 if subject[i] != literal[li] { 1426 return false 1427 } 1428 li++ 1429 } 1430 // Make sure we have processed all of the literal's chars.. 1431 return li >= ll 1432 } 1433 1434 func addLocalSub(sub *subscription, subs *[]*subscription, includeLeafHubs bool) { 1435 if sub != nil && sub.client != nil { 1436 kind := sub.client.kind 1437 if kind == CLIENT || kind == SYSTEM || kind == JETSTREAM || kind == ACCOUNT || 1438 (includeLeafHubs && sub.client.isHubLeafNode() /* implied kind==LEAF */) { 1439 *subs = append(*subs, sub) 1440 } 1441 } 1442 } 1443 1444 func (s *Sublist) addNodeToSubs(n *node, subs *[]*subscription, includeLeafHubs bool) { 1445 // Normal subscriptions 1446 if n.plist != nil { 1447 for _, sub := range n.plist { 1448 addLocalSub(sub, subs, includeLeafHubs) 1449 } 1450 } else { 1451 for sub := range n.psubs { 1452 addLocalSub(sub, subs, includeLeafHubs) 1453 } 1454 } 1455 // Queue subscriptions 1456 for _, qr := range n.qsubs { 1457 for sub := range qr { 1458 addLocalSub(sub, subs, includeLeafHubs) 1459 } 1460 } 1461 } 1462 1463 func (s *Sublist) collectLocalSubs(l *level, subs *[]*subscription, includeLeafHubs bool) { 1464 for _, n := range l.nodes { 1465 s.addNodeToSubs(n, subs, includeLeafHubs) 1466 s.collectLocalSubs(n.next, subs, includeLeafHubs) 1467 } 1468 if l.pwc != nil { 1469 s.addNodeToSubs(l.pwc, subs, includeLeafHubs) 1470 s.collectLocalSubs(l.pwc.next, subs, includeLeafHubs) 1471 } 1472 if l.fwc != nil { 1473 s.addNodeToSubs(l.fwc, subs, includeLeafHubs) 1474 s.collectLocalSubs(l.fwc.next, subs, includeLeafHubs) 1475 } 1476 } 1477 1478 // Return all local client subscriptions. Use the supplied slice. 1479 func (s *Sublist) localSubs(subs *[]*subscription, includeLeafHubs bool) { 1480 s.RLock() 1481 s.collectLocalSubs(s.root, subs, includeLeafHubs) 1482 s.RUnlock() 1483 } 1484 1485 // All is used to collect all subscriptions. 1486 func (s *Sublist) All(subs *[]*subscription) { 1487 s.RLock() 1488 s.collectAllSubs(s.root, subs) 1489 s.RUnlock() 1490 } 1491 1492 func (s *Sublist) addAllNodeToSubs(n *node, subs *[]*subscription) { 1493 // Normal subscriptions 1494 if n.plist != nil { 1495 *subs = append(*subs, n.plist...) 1496 } else { 1497 for sub := range n.psubs { 1498 *subs = append(*subs, sub) 1499 } 1500 } 1501 // Queue subscriptions 1502 for _, qr := range n.qsubs { 1503 for sub := range qr { 1504 *subs = append(*subs, sub) 1505 } 1506 } 1507 } 1508 1509 func (s *Sublist) collectAllSubs(l *level, subs *[]*subscription) { 1510 for _, n := range l.nodes { 1511 s.addAllNodeToSubs(n, subs) 1512 s.collectAllSubs(n.next, subs) 1513 } 1514 if l.pwc != nil { 1515 s.addAllNodeToSubs(l.pwc, subs) 1516 s.collectAllSubs(l.pwc.next, subs) 1517 } 1518 if l.fwc != nil { 1519 s.addAllNodeToSubs(l.fwc, subs) 1520 s.collectAllSubs(l.fwc.next, subs) 1521 } 1522 } 1523 1524 // For a given subject (which may contain wildcards), this call returns all 1525 // subscriptions that would match that subject. For instance, suppose that 1526 // the sublist contains: foo.bar, foo.bar.baz and foo.baz, ReverseMatch("foo.*") 1527 // would return foo.bar and foo.baz. 1528 // This is used in situations where the sublist is likely to contain only 1529 // literals and one wants to get all the subjects that would have been a match 1530 // to a subscription on `subject`. 1531 func (s *Sublist) ReverseMatch(subject string) *SublistResult { 1532 tsa := [32]string{} 1533 tokens := tsa[:0] 1534 start := 0 1535 for i := 0; i < len(subject); i++ { 1536 if subject[i] == btsep { 1537 tokens = append(tokens, subject[start:i]) 1538 start = i + 1 1539 } 1540 } 1541 tokens = append(tokens, subject[start:]) 1542 1543 result := &SublistResult{} 1544 1545 s.RLock() 1546 reverseMatchLevel(s.root, tokens, nil, result) 1547 // Check for empty result. 1548 if len(result.psubs) == 0 && len(result.qsubs) == 0 { 1549 result = emptyResult 1550 } 1551 s.RUnlock() 1552 1553 return result 1554 } 1555 1556 func reverseMatchLevel(l *level, toks []string, n *node, results *SublistResult) { 1557 if l == nil { 1558 return 1559 } 1560 for i, t := range toks { 1561 if len(t) == 1 { 1562 if t[0] == fwc { 1563 getAllNodes(l, results) 1564 return 1565 } else if t[0] == pwc { 1566 for _, n := range l.nodes { 1567 reverseMatchLevel(n.next, toks[i+1:], n, results) 1568 } 1569 if l.pwc != nil { 1570 reverseMatchLevel(l.pwc.next, toks[i+1:], n, results) 1571 } 1572 if l.fwc != nil { 1573 getAllNodes(l, results) 1574 } 1575 return 1576 } 1577 } 1578 // If the sub tree has a fwc at this position, match as well. 1579 if l.fwc != nil { 1580 getAllNodes(l, results) 1581 return 1582 } else if l.pwc != nil { 1583 reverseMatchLevel(l.pwc.next, toks[i+1:], n, results) 1584 } 1585 n = l.nodes[t] 1586 if n == nil { 1587 break 1588 } 1589 l = n.next 1590 } 1591 if n != nil { 1592 addNodeToResults(n, results) 1593 } 1594 } 1595 1596 func getAllNodes(l *level, results *SublistResult) { 1597 if l == nil { 1598 return 1599 } 1600 if l.pwc != nil { 1601 addNodeToResults(l.pwc, results) 1602 } 1603 if l.fwc != nil { 1604 addNodeToResults(l.fwc, results) 1605 } 1606 for _, n := range l.nodes { 1607 addNodeToResults(n, results) 1608 getAllNodes(n.next, results) 1609 } 1610 }