github.com/v2fly/v2ray-core/v5@v5.16.2-0.20240507031116-8191faa6e095/common/strmatcher/matchergroup_substr.go (about)

     1  package strmatcher
     2  
     3  import (
     4  	"sort"
     5  	"strings"
     6  )
     7  
     8  // SubstrMatcherGroup is implementation of MatcherGroup,
     9  // It is simply implmeneted to comply with the priority specification of Substr matchers.
    10  type SubstrMatcherGroup struct {
    11  	patterns []string
    12  	values   []uint32
    13  }
    14  
    15  // AddSubstrMatcher implements MatcherGroupForSubstr.AddSubstrMatcher.
    16  func (g *SubstrMatcherGroup) AddSubstrMatcher(matcher SubstrMatcher, value uint32) {
    17  	g.patterns = append(g.patterns, matcher.Pattern())
    18  	g.values = append(g.values, value)
    19  }
    20  
    21  // Match implements MatcherGroup.Match.
    22  func (g *SubstrMatcherGroup) Match(input string) []uint32 {
    23  	var result []uint32
    24  	for i, pattern := range g.patterns {
    25  		for j := strings.LastIndex(input, pattern); j != -1; j = strings.LastIndex(input[:j], pattern) {
    26  			result = append(result, uint32(j)<<16|uint32(i)&0xffff) // uint32: position (higher 16 bit) | patternIdx (lower 16 bit)
    27  		}
    28  	}
    29  	// sort.Slice will trigger allocation no matter what input is. See https://github.com/golang/go/issues/17332
    30  	// We optimize the sorting by length to prevent memory allocation as possible.
    31  	switch len(result) {
    32  	case 0:
    33  		return nil
    34  	case 1:
    35  		// No need to sort
    36  	case 2:
    37  		// Do a simple swap if unsorted
    38  		if result[0] > result[1] {
    39  			result[0], result[1] = result[1], result[0]
    40  		}
    41  	default:
    42  		// Sort the match results in dictionary order, so that:
    43  		//   1. Pattern matched at smaller position (meaning matched further) takes precedence.
    44  		//   2. When patterns matched at same position, pattern with smaller index (meaning inserted early) takes precedence.
    45  		sort.Slice(result, func(i, j int) bool { return result[i] < result[j] })
    46  	}
    47  	for i, entry := range result {
    48  		result[i] = g.values[entry&0xffff] // Get pattern value from its index (the lower 16 bit)
    49  	}
    50  	return result
    51  }
    52  
    53  // MatchAny implements MatcherGroup.MatchAny.
    54  func (g *SubstrMatcherGroup) MatchAny(input string) bool {
    55  	for _, pattern := range g.patterns {
    56  		if strings.Contains(input, pattern) {
    57  			return true
    58  		}
    59  	}
    60  	return false
    61  }