mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-03 20:07:38 +03:00
Add range api
This commit is contained in:
parent
eb2fad956a
commit
4f2b872a8c
5 changed files with 330 additions and 20 deletions
9
.github/workflows/debug.yml
vendored
9
.github/workflows/debug.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
||||||
uses: actions/setup-go@v2
|
uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ steps.version.outputs.go_version }}
|
go-version: ${{ steps.version.outputs.go_version }}
|
||||||
- name: Build
|
- name: Add cache to Go proxy
|
||||||
run: |
|
run: |
|
||||||
version=`git rev-parse HEAD`
|
version=`git rev-parse HEAD`
|
||||||
mkdir build
|
mkdir build
|
||||||
|
@ -38,4 +38,9 @@ jobs:
|
||||||
go mod init build
|
go mod init build
|
||||||
go get -v github.com/sagernet/sing@$version
|
go get -v github.com/sagernet/sing@$version
|
||||||
popd
|
popd
|
||||||
go build -v ./...
|
continue-on-error: true
|
||||||
|
- name: Build
|
||||||
|
run: |
|
||||||
|
make test
|
||||||
|
make lint_install
|
||||||
|
make lint
|
|
@ -3,14 +3,14 @@ linters:
|
||||||
enable:
|
enable:
|
||||||
- gofumpt
|
- gofumpt
|
||||||
- govet
|
- govet
|
||||||
- gci
|
# - gci
|
||||||
- staticcheck
|
- staticcheck
|
||||||
|
|
||||||
linters-settings:
|
linters-settings:
|
||||||
gci:
|
# gci:
|
||||||
sections:
|
# sections:
|
||||||
- standard
|
# - standard
|
||||||
- prefix(github.com/sagernet/)
|
# - prefix(github.com/sagernet/)
|
||||||
- default
|
# - default
|
||||||
staticcheck:
|
staticcheck:
|
||||||
go: '1.18'
|
go: '1.19'
|
||||||
|
|
21
Makefile
21
Makefile
|
@ -1,10 +1,11 @@
|
||||||
fmt:
|
fmt:
|
||||||
gofumpt -l -w .
|
@gofumpt -l -w .
|
||||||
gofmt -s -w .
|
@gofmt -s -w .
|
||||||
gci write -s "standard,prefix(github.com/sagernet/),default" .
|
@gci write -s "standard,prefix(github.com/sagernet/),default" .
|
||||||
|
|
||||||
lint_install:
|
fmt_install:
|
||||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
go install -v mvdan.cc/gofumpt@latest
|
||||||
|
go install -v github.com/daixiang0/gci@v0.4.0
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
GOOS=linux golangci-lint run ./...
|
GOOS=linux golangci-lint run ./...
|
||||||
|
@ -12,10 +13,8 @@ lint:
|
||||||
GOOS=darwin golangci-lint run ./...
|
GOOS=darwin golangci-lint run ./...
|
||||||
GOOS=freebsd golangci-lint run ./...
|
GOOS=freebsd golangci-lint run ./...
|
||||||
|
|
||||||
test:
|
lint_install:
|
||||||
go test -v .
|
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||||
|
|
||||||
update:
|
test:
|
||||||
git fetch
|
go test -v ./...
|
||||||
git reset FETCH_HEAD --hard
|
|
||||||
git clean -fdx
|
|
112
common/ranges/range.go
Normal file
112
common/ranges/range.go
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package ranges
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/x/constraints"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Range[N comparable] struct {
|
||||||
|
Start N
|
||||||
|
End N
|
||||||
|
}
|
||||||
|
|
||||||
|
func New[N constraints.Integer](start N, end N) Range[N] {
|
||||||
|
return Range[N]{start, end}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSingle[N constraints.Integer](index N) Range[N] {
|
||||||
|
return Range[N]{index, index}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Merge[N constraints.Integer](ranges []Range[N]) (mergedRanges []Range[N]) {
|
||||||
|
if len(ranges) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sort.Slice(ranges, func(i, j int) bool {
|
||||||
|
return ranges[i].Start < ranges[j].Start
|
||||||
|
})
|
||||||
|
mergedRanges = ranges[:1]
|
||||||
|
var rangeIndex N
|
||||||
|
for _, r := range ranges[1:] {
|
||||||
|
if r.Start > mergedRanges[rangeIndex].End+1 {
|
||||||
|
mergedRanges = append(mergedRanges, r)
|
||||||
|
rangeIndex++
|
||||||
|
} else if r.End > mergedRanges[rangeIndex].End {
|
||||||
|
mergedRanges[rangeIndex].End = r.End
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Revert[N constraints.Integer](start, end N, ranges []Range[N]) (revertedRanges []Range[N]) {
|
||||||
|
if len(ranges) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ranges = Merge(ranges)
|
||||||
|
if ranges[0].Start > start {
|
||||||
|
revertedRanges = append(revertedRanges, Range[N]{start, ranges[0].Start - 1})
|
||||||
|
}
|
||||||
|
rangeEnd := ranges[0].End
|
||||||
|
for _, r := range ranges[1:] {
|
||||||
|
if r.Start > rangeEnd+1 {
|
||||||
|
revertedRanges = append(revertedRanges, Range[N]{rangeEnd + 1, r.Start - 1})
|
||||||
|
}
|
||||||
|
rangeEnd = r.End
|
||||||
|
}
|
||||||
|
if end > rangeEnd {
|
||||||
|
revertedRanges = append(revertedRanges, Range[N]{rangeEnd + 1, end})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exclude[N constraints.Integer](ranges []Range[N], targetRanges []Range[N]) []Range[N] {
|
||||||
|
ranges = Merge(ranges)
|
||||||
|
if len(ranges) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
targetRanges = Merge(targetRanges)
|
||||||
|
if len(targetRanges) == 0 {
|
||||||
|
return ranges
|
||||||
|
}
|
||||||
|
var mergedRanges []Range[N]
|
||||||
|
rangeStart := ranges[0].Start
|
||||||
|
rangeEnd := ranges[0].End
|
||||||
|
rangeIndex := rangeStart
|
||||||
|
ranges = ranges[1:]
|
||||||
|
targetStart := targetRanges[0].Start
|
||||||
|
targetEnd := targetRanges[0].End
|
||||||
|
targetRanges = targetRanges[1:]
|
||||||
|
for {
|
||||||
|
if targetStart > rangeEnd {
|
||||||
|
if rangeIndex <= rangeEnd {
|
||||||
|
mergedRanges = append(mergedRanges, Range[N]{rangeIndex, rangeEnd})
|
||||||
|
}
|
||||||
|
if len(ranges) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
rangeStart = ranges[0].Start
|
||||||
|
rangeEnd = ranges[0].End
|
||||||
|
rangeIndex = rangeStart
|
||||||
|
ranges = ranges[1:]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if targetStart > rangeIndex {
|
||||||
|
mergedRanges = append(mergedRanges, Range[N]{rangeIndex, targetStart - 1})
|
||||||
|
rangeIndex = targetStart + 1
|
||||||
|
}
|
||||||
|
if targetEnd <= rangeEnd {
|
||||||
|
rangeIndex = targetEnd + 1
|
||||||
|
if len(targetRanges) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
targetStart = targetRanges[0].Start
|
||||||
|
targetEnd = targetRanges[0].End
|
||||||
|
targetRanges = targetRanges[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if rangeIndex <= rangeEnd {
|
||||||
|
mergedRanges = append(mergedRanges, Range[N]{rangeIndex, rangeEnd})
|
||||||
|
}
|
||||||
|
return Merge(append(mergedRanges, ranges...))
|
||||||
|
}
|
194
common/ranges/range_test.go
Normal file
194
common/ranges/range_test.go
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
package ranges
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRevertRanges(t *testing.T) {
|
||||||
|
for _, testRange := range []struct {
|
||||||
|
start, end int
|
||||||
|
ranges []Range[int]
|
||||||
|
expected []Range[int]
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 10,
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{0, 1},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{2, 10},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 10,
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{9, 10},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{0, 8},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 10,
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{0, 1},
|
||||||
|
{9, 10},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{2, 8},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 10,
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{2, 4},
|
||||||
|
{6, 8},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{0, 1},
|
||||||
|
{5, 5},
|
||||||
|
{9, 10},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 10,
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{2, 4},
|
||||||
|
{8, 9},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{0, 1},
|
||||||
|
{5, 7},
|
||||||
|
{10, 10},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
result := Revert(testRange.start, testRange.end, testRange.ranges)
|
||||||
|
if !reflect.DeepEqual(result, testRange.expected) {
|
||||||
|
t.Fatal("expected", testRange.expected, "\ngot", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeRanges(t *testing.T) {
|
||||||
|
for _, testRange := range []struct {
|
||||||
|
ranges []Range[int]
|
||||||
|
expected []Range[int]
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{0, 1},
|
||||||
|
{1, 2},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{0, 2},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{0, 3},
|
||||||
|
{5, 7},
|
||||||
|
{8, 9},
|
||||||
|
{10, 10},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{0, 3},
|
||||||
|
{5, 10},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{1, 3},
|
||||||
|
{2, 6},
|
||||||
|
{8, 10},
|
||||||
|
{15, 18},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{1, 6},
|
||||||
|
{8, 10},
|
||||||
|
{15, 18},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{1, 3},
|
||||||
|
{2, 7},
|
||||||
|
{2, 6},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{1, 7},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{1, 3},
|
||||||
|
{2, 6},
|
||||||
|
{2, 7},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{1, 7},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
result := Merge(testRange.ranges)
|
||||||
|
if !reflect.DeepEqual(result, testRange.expected) {
|
||||||
|
t.Fatal("input", testRange.ranges, "\nexpected", testRange.expected, "\ngot", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExcludeRanges(t *testing.T) {
|
||||||
|
for _, testRange := range []struct {
|
||||||
|
ranges []Range[int]
|
||||||
|
exclude []Range[int]
|
||||||
|
expected []Range[int]
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{0, 100},
|
||||||
|
},
|
||||||
|
exclude: []Range[int]{
|
||||||
|
{0, 10},
|
||||||
|
{20, 30},
|
||||||
|
{55, 55},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{11, 19},
|
||||||
|
{31, 54},
|
||||||
|
{56, 100},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ranges: []Range[int]{
|
||||||
|
{0, 100},
|
||||||
|
{200, 300},
|
||||||
|
},
|
||||||
|
exclude: []Range[int]{
|
||||||
|
{0, 10},
|
||||||
|
{20, 30},
|
||||||
|
{55, 55},
|
||||||
|
{250, 250},
|
||||||
|
{299, 299},
|
||||||
|
},
|
||||||
|
expected: []Range[int]{
|
||||||
|
{11, 19},
|
||||||
|
{31, 54},
|
||||||
|
{56, 100},
|
||||||
|
{200, 249},
|
||||||
|
{251, 298},
|
||||||
|
{300, 300},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
result := Exclude(testRange.ranges, testRange.exclude)
|
||||||
|
if !reflect.DeepEqual(result, testRange.expected) {
|
||||||
|
t.Fatal("input", testRange.ranges, testRange.exclude, "\nexpected", testRange.expected, "\ngot", result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue