mirror of
https://github.com/v2fly/v2ray-core.git
synced 2025-06-26 21:29:58 +00:00
feat: add priority for config loaders
This commit is contained in:
parent
580d33a7e3
commit
d1705f8869
2
common/pq/pq.go
Normal file
2
common/pq/pq.go
Normal file
@ -0,0 +1,2 @@
|
||||
// package pq provides a implementation of a priority queue.
|
||||
package pq
|
182
common/pq/priorityqueue.go
Normal file
182
common/pq/priorityqueue.go
Normal file
@ -0,0 +1,182 @@
|
||||
// modified from github.com/oleiade/lane
|
||||
|
||||
package pq
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
// PriorityQueue is a heap-based priority-queue data structure implementation.
|
||||
//
|
||||
// It can either be min (ascending) or max (descending)
|
||||
// oriented/ordered. Its type parameters `T` and `P`, respectively
|
||||
// specify the underlying value type and the underlying priority type.
|
||||
type PriorityQueue[T any, P constraints.Ordered] struct {
|
||||
items []*priorityQueueItem[T, P]
|
||||
itemCount uint
|
||||
comparator func(lhs, rhs P) bool
|
||||
}
|
||||
|
||||
// NewPriorityQueue instantiates a new PriorityQueue with the provided comparison heuristic.
|
||||
// The package defines the `Max` and `Min` heuristic to define a max-oriented or
|
||||
// min-oriented heuristics, respectively.
|
||||
func NewPriorityQueue[T any, P constraints.Ordered](heuristic func(lhs, rhs P) bool) *PriorityQueue[T, P] {
|
||||
items := make([]*priorityQueueItem[T, P], 1)
|
||||
items[0] = nil
|
||||
|
||||
return &PriorityQueue[T, P]{
|
||||
items: items,
|
||||
itemCount: 0,
|
||||
comparator: heuristic,
|
||||
}
|
||||
}
|
||||
|
||||
// NewMaxPriorityQueue instantiates a new maximum oriented PriorityQueue.
|
||||
func NewMaxPriorityQueue[T any, P constraints.Ordered]() *PriorityQueue[T, P] {
|
||||
return NewPriorityQueue[T](Maximum[P])
|
||||
}
|
||||
|
||||
// NewMinPriorityQueue instantiates a new minimum oriented PriorityQueue.
|
||||
func NewMinPriorityQueue[T any, P constraints.Ordered]() *PriorityQueue[T, P] {
|
||||
return NewPriorityQueue[T](Minimum[P])
|
||||
}
|
||||
|
||||
// Maximum returns whether `rhs` is greater than `lhs`.
|
||||
//
|
||||
// Use it as a comparison heuristic during a PriorityQueue's
|
||||
// instantiation.
|
||||
func Maximum[T constraints.Ordered](lhs, rhs T) bool {
|
||||
return lhs < rhs
|
||||
}
|
||||
|
||||
// Minimum returns whether `rhs` is less than `lhs`.
|
||||
//
|
||||
// Use it as a comparison heuristic during a PriorityQueue's
|
||||
// instantiation.
|
||||
func Minimum[T constraints.Ordered](lhs, rhs T) bool {
|
||||
return lhs > rhs
|
||||
}
|
||||
|
||||
// Push inserts the value in the PriorityQueue with the provided priority
|
||||
// in at most *O(log n)* time complexity.
|
||||
func (pq *PriorityQueue[T, P]) Push(value T, priority P) {
|
||||
item := newPriorityQueueItem(value, priority)
|
||||
|
||||
pq.items = append(pq.items, item)
|
||||
pq.itemCount++
|
||||
pq.swim(pq.size())
|
||||
}
|
||||
|
||||
// Pop and return the highest or lowest priority item (depending on the
|
||||
// comparison heuristic of your PriorityQueue) from the PriorityQueue in
|
||||
// at most *O(log n)* complexity.
|
||||
func (pq *PriorityQueue[T, P]) Pop() (value T, priority P, ok bool) {
|
||||
|
||||
if pq.size() < 1 {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
|
||||
max := pq.items[1]
|
||||
pq.exch(1, pq.size())
|
||||
pq.items = pq.items[0:pq.size()]
|
||||
pq.itemCount--
|
||||
pq.sink(1)
|
||||
|
||||
value = max.value
|
||||
priority = max.priority
|
||||
ok = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Head returns the highest or lowest priority item (depending on
|
||||
// the comparison heuristic of your PriorityQueue) from the PriorityQueue
|
||||
// in *O(1)* complexity.
|
||||
func (pq *PriorityQueue[T, P]) Head() (value T, priority P, ok bool) {
|
||||
|
||||
if pq.size() < 1 {
|
||||
ok = false
|
||||
return
|
||||
}
|
||||
|
||||
value = pq.items[1].value
|
||||
priority = pq.items[1].priority
|
||||
ok = true
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Size returns the number of elements present in the PriorityQueue.
|
||||
func (pq *PriorityQueue[T, P]) Size() uint {
|
||||
|
||||
return pq.size()
|
||||
}
|
||||
|
||||
// Empty returns whether the PriorityQueue is empty.
|
||||
func (pq *PriorityQueue[T, P]) Empty() bool {
|
||||
|
||||
return pq.size() == 0
|
||||
}
|
||||
|
||||
// Clone returns a copy of the PriorityQueue.
|
||||
func (pq *PriorityQueue[T, P]) Clone() *PriorityQueue[T, P] {
|
||||
items := make([]*priorityQueueItem[T, P], len(pq.items))
|
||||
copy(items, pq.items)
|
||||
|
||||
return &PriorityQueue[T, P]{
|
||||
items: items,
|
||||
itemCount: pq.itemCount,
|
||||
comparator: pq.comparator,
|
||||
}
|
||||
}
|
||||
|
||||
func (pq *PriorityQueue[T, P]) swim(k uint) {
|
||||
for k > 1 && pq.less(k/2, k) {
|
||||
pq.exch(k/2, k)
|
||||
k /= 2
|
||||
}
|
||||
}
|
||||
|
||||
func (pq *PriorityQueue[T, P]) sink(k uint) {
|
||||
for 2*k <= pq.size() {
|
||||
j := 2 * k
|
||||
|
||||
if j < pq.size() && pq.less(j, j+1) {
|
||||
j++
|
||||
}
|
||||
|
||||
if !pq.less(k, j) {
|
||||
break
|
||||
}
|
||||
|
||||
pq.exch(k, j)
|
||||
k = j
|
||||
}
|
||||
}
|
||||
|
||||
func (pq *PriorityQueue[T, P]) size() uint {
|
||||
return pq.itemCount
|
||||
}
|
||||
|
||||
func (pq *PriorityQueue[T, P]) less(lhs, rhs uint) bool {
|
||||
return pq.comparator(pq.items[lhs].priority, pq.items[rhs].priority)
|
||||
}
|
||||
|
||||
func (pq *PriorityQueue[T, P]) exch(lhs, rhs uint) {
|
||||
pq.items[lhs], pq.items[rhs] = pq.items[rhs], pq.items[lhs]
|
||||
}
|
||||
|
||||
// priorityQueueItem is the underlying PriorityQueue item container.
|
||||
type priorityQueueItem[T any, P constraints.Ordered] struct {
|
||||
value T
|
||||
priority P
|
||||
}
|
||||
|
||||
// newPriorityQueue instantiates a new priorityQueueItem.
|
||||
func newPriorityQueueItem[T any, P constraints.Ordered](value T, priority P) *priorityQueueItem[T, P] {
|
||||
return &priorityQueueItem[T, P]{
|
||||
value: value,
|
||||
priority: priority,
|
||||
}
|
||||
}
|
12
config.go
12
config.go
@ -14,6 +14,7 @@ import (
|
||||
"github.com/v2fly/v2ray-core/v5/common"
|
||||
"github.com/v2fly/v2ray-core/v5/common/buf"
|
||||
"github.com/v2fly/v2ray-core/v5/common/cmdarg"
|
||||
"github.com/v2fly/v2ray-core/v5/common/pq"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -35,6 +36,7 @@ const (
|
||||
type ConfigFormat struct {
|
||||
Name []string
|
||||
Extension []string
|
||||
Priority int
|
||||
Loader ConfigLoader
|
||||
}
|
||||
|
||||
@ -42,7 +44,7 @@ type ConfigFormat struct {
|
||||
type ConfigLoader func(input interface{}) (*Config, error)
|
||||
|
||||
var (
|
||||
configLoaders = make([]*ConfigFormat, 0)
|
||||
configLoaders = pq.NewMaxPriorityQueue[*ConfigFormat, int]()
|
||||
configLoaderByName = make(map[string]*ConfigFormat)
|
||||
configLoaderByExt = make(map[string]*ConfigFormat)
|
||||
)
|
||||
@ -63,7 +65,7 @@ func RegisterConfigLoader(format *ConfigFormat) error {
|
||||
}
|
||||
configLoaderByExt[lext] = format
|
||||
}
|
||||
configLoaders = append(configLoaders, format)
|
||||
configLoaders.Push(format, format.Priority)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -167,7 +169,11 @@ func loadSingleConfigAutoFormatFromFile(file string) (*Config, error) {
|
||||
func loadSingleConfigByTryingAllLoaders(input interface{}) (*Config, error) {
|
||||
var errorReasons strings.Builder
|
||||
|
||||
for _, f := range configLoaders {
|
||||
queue := configLoaders.Clone()
|
||||
size := queue.Size()
|
||||
|
||||
for i := 0; i < int(size); i++ {
|
||||
f, _, _ := queue.Pop()
|
||||
if f.Name[0] == FormatAuto {
|
||||
continue
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@ -27,6 +27,7 @@ require (
|
||||
go.starlark.net v0.0.0-20230612165344-9532f5667272
|
||||
go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
|
||||
golang.org/x/crypto v0.10.0
|
||||
golang.org/x/exp v0.0.0-20230725012225-302865e7556b
|
||||
golang.org/x/net v0.11.0
|
||||
golang.org/x/sync v0.2.0
|
||||
golang.org/x/sys v0.9.0
|
||||
@ -70,8 +71,7 @@ require (
|
||||
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
|
||||
github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 // indirect
|
||||
github.com/xtaci/smux v1.5.24 // indirect
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/mod v0.11.0 // indirect
|
||||
golang.org/x/text v0.10.0 // indirect
|
||||
golang.org/x/tools v0.9.3 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
|
||||
|
8
go.sum
8
go.sum
@ -362,8 +362,8 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/exp v0.0.0-20230725012225-302865e7556b h1:tK7yjGqVRzYdXsBcfD2MLhFAhHfDgGLm2rY1ub7FA9k=
|
||||
golang.org/x/exp v0.0.0-20230725012225-302865e7556b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@ -381,8 +381,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
|
||||
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -31,6 +31,7 @@ func makeMergeLoader(formatName string) (*core.ConfigFormat, error) {
|
||||
return &core.ConfigFormat{
|
||||
Name: []string{formatName},
|
||||
Extension: extensions,
|
||||
Priority: -2,
|
||||
Loader: makeLoaderFunc(formatName),
|
||||
}, nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user