@@ -17,9 +17,6 @@ import (
1717 "errors"
1818 "strings"
1919 "sync"
20- "unsafe"
21-
22- "github.com/nats-io/nats-server/v2/server/stree"
2320)
2421
2522// Sublist is a routing mechanism to handle subject distribution and
@@ -372,6 +369,35 @@ func (s *GenericSublist[T]) Remove(subject string, value T) error {
372369 return s .remove (subject , value , true )
373370}
374371
372+ // HasInterestStartingIn is a helper for subject tree intersection.
373+ func (s * GenericSublist [T ]) HasInterestStartingIn (subj string ) bool {
374+ s .RLock ()
375+ defer s .RUnlock ()
376+ var _tokens [64 ]string
377+ tokens := tokenizeSubjectIntoSlice (_tokens [:0 ], subj )
378+ return hasInterestStartingIn (s .root , tokens )
379+ }
380+
381+ func hasInterestStartingIn [T comparable ](l * level [T ], tokens []string ) bool {
382+ if l == nil {
383+ return false
384+ }
385+ if len (tokens ) == 0 {
386+ return true
387+ }
388+ token := tokens [0 ]
389+ if l .fwc != nil {
390+ return true
391+ }
392+ if pwc := l .pwc ; pwc != nil {
393+ return hasInterestStartingIn (pwc .next , tokens [1 :])
394+ }
395+ if n := l .nodes [token ]; n != nil {
396+ return hasInterestStartingIn (n .next , tokens [1 :])
397+ }
398+ return false
399+ }
400+
375401// pruneNode is used to prune an empty node from the tree.
376402func (l * level [T ]) pruneNode (n * node [T ], t string ) {
377403 if n == nil {
@@ -465,86 +491,15 @@ func visitLevel[T comparable](l *level[T], depth int) int {
465491 return maxDepth
466492}
467493
468- // IntersectStree will match all items in the given subject tree that
469- // have interest expressed in the given sublist. The callback will only be called
470- // once for each subject, regardless of overlapping subscriptions in the sublist.
471- func IntersectStree [T1 any , T2 comparable ](st * stree.SubjectTree [T1 ], sl * GenericSublist [T2 ], cb func (subj []byte , entry * T1 )) {
472- var _subj [255 ]byte
473- intersectStree (st , sl .root , _subj [:0 ], cb )
474- }
475-
476- func intersectStree [T1 any , T2 comparable ](st * stree.SubjectTree [T1 ], r * level [T2 ], subj []byte , cb func (subj []byte , entry * T1 )) {
477- nsubj := subj
478- if len (nsubj ) > 0 {
479- nsubj = append (subj , '.' )
480- }
481- if r .fwc != nil {
482- // We've reached a full wildcard, do a FWC match on the stree at this point
483- // and don't keep iterating downward.
484- nsubj := append (nsubj , '>' )
485- st .Match (nsubj , cb )
486- return
487- }
488- if r .pwc != nil {
489- // We've found a partial wildcard. We'll keep iterating downwards, but first
490- // check whether there's interest at this level (without triggering dupes) and
491- // match if so.
492- var done bool
493- nsubj := append (nsubj , '*' )
494- if len (r .pwc .subs ) > 0 {
495- st .Match (nsubj , cb )
496- done = true
497- }
498- if r .pwc .next .numNodes () > 0 {
499- intersectStree (st , r .pwc .next , nsubj , cb )
500- }
501- if done {
502- return
503- }
504- }
505- // Normal node with subject literals, keep iterating.
506- for t , n := range r .nodes {
507- if r .pwc != nil && r .pwc .next .numNodes () > 0 && n .next .numNodes () > 0 {
508- // A wildcard at the next level will already visit these descendents
509- // so skip so we don't callback the same subject more than once.
510- continue
511- }
512- nsubj := append (nsubj , t ... )
513- if len (n .subs ) > 0 {
514- if subjectHasWildcard (bytesToString (nsubj )) {
515- st .Match (nsubj , cb )
516- } else {
517- if e , ok := st .Find (nsubj ); ok {
518- cb (nsubj , e )
519- }
520- }
521- }
522- if n .next .numNodes () > 0 {
523- intersectStree (st , n .next , nsubj , cb )
524- }
525- }
526- }
527-
528- // Determine if a subject has any wildcard tokens.
529- func subjectHasWildcard (subject string ) bool {
530- // This one exits earlier then !subjectIsLiteral(subject)
531- for i , c := range subject {
532- if c == pwc || c == fwc {
533- if (i == 0 || subject [i - 1 ] == btsep ) &&
534- (i + 1 == len (subject ) || subject [i + 1 ] == btsep ) {
535- return true
536- }
494+ // use similar to append. meaning, the updated slice will be returned
495+ func tokenizeSubjectIntoSlice (tts []string , subject string ) []string {
496+ start := 0
497+ for i := 0 ; i < len (subject ); i ++ {
498+ if subject [i ] == btsep {
499+ tts = append (tts , subject [start :i ])
500+ start = i + 1
537501 }
538502 }
539- return false
540- }
541-
542- // Note this will avoid a copy of the data used for the string, but it will also reference the existing slice's data pointer.
543- // So this should be used sparingly when we know the encompassing byte slice's lifetime is the same.
544- func bytesToString (b []byte ) string {
545- if len (b ) == 0 {
546- return _EMPTY_
547- }
548- p := unsafe .SliceData (b )
549- return unsafe .String (p , len (b ))
503+ tts = append (tts , subject [start :])
504+ return tts
550505}
0 commit comments