mihomo/common/utils/ranges.go
2024-03-12 15:14:56 +08:00

149 lines
3.2 KiB
Go

package utils
import (
"errors"
"fmt"
"strconv"
"strings"
"golang.org/x/exp/constraints"
)
type IntRanges[T constraints.Integer] []Range[T]
var errIntRanges = errors.New("intRanges error")
func newIntRanges[T constraints.Integer](expected string, parseFn func(string) (T, error)) (IntRanges[T], error) {
// example: 200 or 200/302 or 200-400 or 200/204/401-429/501-503
expected = strings.TrimSpace(expected)
if len(expected) == 0 || expected == "*" {
return nil, nil
}
// support: 200,302 or 200,204,401-429,501-503
expected = strings.ReplaceAll(expected, ",", "/")
list := strings.Split(expected, "/")
if len(list) > 28 {
return nil, fmt.Errorf("%w, too many ranges to use, maximum support 28 ranges", errIntRanges)
}
return newIntRangesFromList[T](list, parseFn)
}
func newIntRangesFromList[T constraints.Integer](list []string, parseFn func(string) (T, error)) (IntRanges[T], error) {
var ranges IntRanges[T]
for _, s := range list {
if s == "" {
continue
}
status := strings.Split(s, "-")
statusLen := len(status)
if statusLen > 2 {
return nil, errIntRanges
}
start, err := parseFn(strings.Trim(status[0], "[ ]"))
if err != nil {
return nil, errIntRanges
}
switch statusLen {
case 1: // Port range
ranges = append(ranges, NewRange(T(start), T(start)))
case 2: // Single port
end, err := parseFn(strings.Trim(status[1], "[ ]"))
if err != nil {
return nil, errIntRanges
}
ranges = append(ranges, NewRange(T(start), T(end)))
}
}
return ranges, nil
}
func parseUnsigned[T constraints.Unsigned](s string) (T, error) {
if val, err := strconv.ParseUint(s, 10, 64); err == nil {
return T(val), nil
} else {
return 0, err
}
}
func NewUnsignedRanges[T constraints.Unsigned](expected string) (IntRanges[T], error) {
return newIntRanges(expected, parseUnsigned[T])
}
func NewUnsignedRangesFromList[T constraints.Unsigned](list []string) (IntRanges[T], error) {
return newIntRangesFromList(list, parseUnsigned[T])
}
func parseSigned[T constraints.Signed](s string) (T, error) {
if val, err := strconv.ParseInt(s, 10, 64); err == nil {
return T(val), nil
} else {
return 0, err
}
}
func NewSignedRanges[T constraints.Signed](expected string) (IntRanges[T], error) {
return newIntRanges(expected, parseSigned[T])
}
func NewSignedRangesFromList[T constraints.Signed](list []string) (IntRanges[T], error) {
return newIntRangesFromList(list, parseSigned[T])
}
func (ranges IntRanges[T]) Check(status T) bool {
if len(ranges) == 0 {
return true
}
for _, segment := range ranges {
if segment.Contains(status) {
return true
}
}
return false
}
func (ranges IntRanges[T]) String() string {
if len(ranges) == 0 {
return "*"
}
terms := make([]string, len(ranges))
for i, r := range ranges {
start := r.Start()
end := r.End()
var term string
if start == end {
term = strconv.Itoa(int(start))
} else {
term = strconv.Itoa(int(start)) + "-" + strconv.Itoa(int(end))
}
terms[i] = term
}
return strings.Join(terms, "/")
}
func (ranges IntRanges[T]) Range(f func(t T) bool) {
if len(ranges) == 0 {
return
}
for _, r := range ranges {
for i := r.Start(); i <= r.End(); i++ {
if !f(i) {
return
}
}
}
}