Add range api

This commit is contained in:
世界 2022-08-14 22:58:30 +08:00
parent eb2fad956a
commit 4f2b872a8c
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
5 changed files with 330 additions and 20 deletions

View file

@ -30,7 +30,7 @@ jobs:
uses: actions/setup-go@v2
with:
go-version: ${{ steps.version.outputs.go_version }}
- name: Build
- name: Add cache to Go proxy
run: |
version=`git rev-parse HEAD`
mkdir build
@ -38,4 +38,9 @@ jobs:
go mod init build
go get -v github.com/sagernet/sing@$version
popd
go build -v ./...
continue-on-error: true
- name: Build
run: |
make test
make lint_install
make lint

View file

@ -3,14 +3,14 @@ linters:
enable:
- gofumpt
- govet
- gci
# - gci
- staticcheck
linters-settings:
gci:
sections:
- standard
- prefix(github.com/sagernet/)
- default
# gci:
# sections:
# - standard
# - prefix(github.com/sagernet/)
# - default
staticcheck:
go: '1.18'
go: '1.19'

View file

@ -1,10 +1,11 @@
fmt:
gofumpt -l -w .
gofmt -s -w .
gci write -s "standard,prefix(github.com/sagernet/),default" .
@gofumpt -l -w .
@gofmt -s -w .
@gci write -s "standard,prefix(github.com/sagernet/),default" .
lint_install:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
fmt_install:
go install -v mvdan.cc/gofumpt@latest
go install -v github.com/daixiang0/gci@v0.4.0
lint:
GOOS=linux golangci-lint run ./...
@ -12,10 +13,8 @@ lint:
GOOS=darwin golangci-lint run ./...
GOOS=freebsd golangci-lint run ./...
test:
go test -v .
lint_install:
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
update:
git fetch
git reset FETCH_HEAD --hard
git clean -fdx
test:
go test -v ./...

112
common/ranges/range.go Normal file
View 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
View 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)
}
}
}