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  }