github.com/metacubex/mihomo@v1.18.5/common/utils/ranges.go (about) 1 package utils 2 3 import ( 4 "errors" 5 "fmt" 6 "strconv" 7 "strings" 8 9 "golang.org/x/exp/constraints" 10 ) 11 12 type IntRanges[T constraints.Integer] []Range[T] 13 14 var errIntRanges = errors.New("intRanges error") 15 16 func newIntRanges[T constraints.Integer](expected string, parseFn func(string) (T, error)) (IntRanges[T], error) { 17 // example: 200 or 200/302 or 200-400 or 200/204/401-429/501-503 18 expected = strings.TrimSpace(expected) 19 if len(expected) == 0 || expected == "*" { 20 return nil, nil 21 } 22 23 // support: 200,302 or 200,204,401-429,501-503 24 expected = strings.ReplaceAll(expected, ",", "/") 25 list := strings.Split(expected, "/") 26 if len(list) > 28 { 27 return nil, fmt.Errorf("%w, too many ranges to use, maximum support 28 ranges", errIntRanges) 28 } 29 30 return newIntRangesFromList[T](list, parseFn) 31 } 32 33 func newIntRangesFromList[T constraints.Integer](list []string, parseFn func(string) (T, error)) (IntRanges[T], error) { 34 var ranges IntRanges[T] 35 for _, s := range list { 36 if s == "" { 37 continue 38 } 39 40 status := strings.Split(s, "-") 41 statusLen := len(status) 42 if statusLen > 2 { 43 return nil, errIntRanges 44 } 45 46 start, err := parseFn(strings.Trim(status[0], "[ ]")) 47 if err != nil { 48 return nil, errIntRanges 49 } 50 51 switch statusLen { 52 case 1: // Port range 53 ranges = append(ranges, NewRange(T(start), T(start))) 54 case 2: // Single port 55 end, err := parseFn(strings.Trim(status[1], "[ ]")) 56 if err != nil { 57 return nil, errIntRanges 58 } 59 60 ranges = append(ranges, NewRange(T(start), T(end))) 61 } 62 } 63 64 return ranges, nil 65 } 66 67 func parseUnsigned[T constraints.Unsigned](s string) (T, error) { 68 if val, err := strconv.ParseUint(s, 10, 64); err == nil { 69 return T(val), nil 70 } else { 71 return 0, err 72 } 73 } 74 75 func NewUnsignedRanges[T constraints.Unsigned](expected string) (IntRanges[T], error) { 76 return newIntRanges(expected, parseUnsigned[T]) 77 } 78 79 func NewUnsignedRangesFromList[T constraints.Unsigned](list []string) (IntRanges[T], error) { 80 return newIntRangesFromList(list, parseUnsigned[T]) 81 } 82 83 func parseSigned[T constraints.Signed](s string) (T, error) { 84 if val, err := strconv.ParseInt(s, 10, 64); err == nil { 85 return T(val), nil 86 } else { 87 return 0, err 88 } 89 } 90 91 func NewSignedRanges[T constraints.Signed](expected string) (IntRanges[T], error) { 92 return newIntRanges(expected, parseSigned[T]) 93 } 94 95 func NewSignedRangesFromList[T constraints.Signed](list []string) (IntRanges[T], error) { 96 return newIntRangesFromList(list, parseSigned[T]) 97 } 98 99 func (ranges IntRanges[T]) Check(status T) bool { 100 if len(ranges) == 0 { 101 return true 102 } 103 104 for _, segment := range ranges { 105 if segment.Contains(status) { 106 return true 107 } 108 } 109 110 return false 111 } 112 113 func (ranges IntRanges[T]) String() string { 114 if len(ranges) == 0 { 115 return "*" 116 } 117 118 terms := make([]string, len(ranges)) 119 for i, r := range ranges { 120 start := r.Start() 121 end := r.End() 122 123 var term string 124 if start == end { 125 term = strconv.Itoa(int(start)) 126 } else { 127 term = strconv.Itoa(int(start)) + "-" + strconv.Itoa(int(end)) 128 } 129 130 terms[i] = term 131 } 132 133 return strings.Join(terms, "/") 134 } 135 136 func (ranges IntRanges[T]) Range(f func(t T) bool) { 137 if len(ranges) == 0 { 138 return 139 } 140 141 for _, r := range ranges { 142 for i := r.Start(); i <= r.End(); i++ { 143 if !f(i) { 144 return 145 } 146 } 147 } 148 }