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 }