Browse Source

feat: add priority for config loaders

AkinoKaede 2 years ago
parent
commit
d1705f8869
6 changed files with 200 additions and 9 deletions
  1. 2 0
      common/pq/pq.go
  2. 182 0
      common/pq/priorityqueue.go
  3. 9 3
      config.go
  4. 2 2
      go.mod
  5. 4 4
      go.sum
  6. 1 0
      main/formats/formats.go

+ 2 - 0
common/pq/pq.go

@@ -0,0 +1,2 @@
+// package pq provides a implementation of a priority queue.
+package pq

+ 182 - 0
common/pq/priorityqueue.go

@@ -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,
+	}
+}

+ 9 - 3
config.go

@@ -14,6 +14,7 @@ import (
 	"github.com/v2fly/v2ray-core/v5/common"
 	"github.com/v2fly/v2ray-core/v5/common"
 	"github.com/v2fly/v2ray-core/v5/common/buf"
 	"github.com/v2fly/v2ray-core/v5/common/buf"
 	"github.com/v2fly/v2ray-core/v5/common/cmdarg"
 	"github.com/v2fly/v2ray-core/v5/common/cmdarg"
+	"github.com/v2fly/v2ray-core/v5/common/pq"
 )
 )
 
 
 const (
 const (
@@ -35,6 +36,7 @@ const (
 type ConfigFormat struct {
 type ConfigFormat struct {
 	Name      []string
 	Name      []string
 	Extension []string
 	Extension []string
+	Priority  int
 	Loader    ConfigLoader
 	Loader    ConfigLoader
 }
 }
 
 
@@ -42,7 +44,7 @@ type ConfigFormat struct {
 type ConfigLoader func(input interface{}) (*Config, error)
 type ConfigLoader func(input interface{}) (*Config, error)
 
 
 var (
 var (
-	configLoaders      = make([]*ConfigFormat, 0)
+	configLoaders      = pq.NewMaxPriorityQueue[*ConfigFormat, int]()
 	configLoaderByName = make(map[string]*ConfigFormat)
 	configLoaderByName = make(map[string]*ConfigFormat)
 	configLoaderByExt  = make(map[string]*ConfigFormat)
 	configLoaderByExt  = make(map[string]*ConfigFormat)
 )
 )
@@ -63,7 +65,7 @@ func RegisterConfigLoader(format *ConfigFormat) error {
 		}
 		}
 		configLoaderByExt[lext] = format
 		configLoaderByExt[lext] = format
 	}
 	}
-	configLoaders = append(configLoaders, format)
+	configLoaders.Push(format, format.Priority)
 	return nil
 	return nil
 }
 }
 
 
@@ -167,7 +169,11 @@ func loadSingleConfigAutoFormatFromFile(file string) (*Config, error) {
 func loadSingleConfigByTryingAllLoaders(input interface{}) (*Config, error) {
 func loadSingleConfigByTryingAllLoaders(input interface{}) (*Config, error) {
 	var errorReasons strings.Builder
 	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 {
 		if f.Name[0] == FormatAuto {
 			continue
 			continue
 		}
 		}

+ 2 - 2
go.mod

@@ -27,6 +27,7 @@ require (
 	go.starlark.net v0.0.0-20230612165344-9532f5667272
 	go.starlark.net v0.0.0-20230612165344-9532f5667272
 	go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
 	go4.org/netipx v0.0.0-20230303233057-f1b76eb4bb35
 	golang.org/x/crypto v0.10.0
 	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/net v0.11.0
 	golang.org/x/sync v0.2.0
 	golang.org/x/sync v0.2.0
 	golang.org/x/sys v0.9.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/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
 	github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 // indirect
 	github.com/secure-io/siv-go v0.0.0-20180922214919-5ff40651e2c4 // indirect
 	github.com/xtaci/smux v1.5.24 // 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/text v0.10.0 // indirect
 	golang.org/x/tools v0.9.3 // indirect
 	golang.org/x/tools v0.9.3 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc // indirect

+ 4 - 4
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-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-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-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-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/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=
 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.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.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.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-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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

+ 1 - 0
main/formats/formats.go

@@ -31,6 +31,7 @@ func makeMergeLoader(formatName string) (*core.ConfigFormat, error) {
 	return &core.ConfigFormat{
 	return &core.ConfigFormat{
 		Name:      []string{formatName},
 		Name:      []string{formatName},
 		Extension: extensions,
 		Extension: extensions,
+		Priority:  -2,
 		Loader:    makeLoaderFunc(formatName),
 		Loader:    makeLoaderFunc(formatName),
 	}, nil
 	}, nil
 }
 }