mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-06-08 04:04:55 +03:00
Ordered json output & Disallow unknown fields
This commit is contained in:
47
common/domain/matcher.go
Normal file
47
common/domain/matcher.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package domain
|
||||
|
||||
import "unicode/utf8"
|
||||
|
||||
type Matcher struct {
|
||||
set *succinctSet
|
||||
}
|
||||
|
||||
func NewMatcher(domains []string, domainSuffix []string) *Matcher {
|
||||
var domainList []string
|
||||
for _, domain := range domains {
|
||||
domainList = append(domainList, reverseDomain(domain))
|
||||
}
|
||||
for _, domain := range domainSuffix {
|
||||
domainList = append(domainList, reverseDomainSuffix(domain))
|
||||
}
|
||||
return &Matcher{
|
||||
newSuccinctSet(domainList),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Matcher) Match(domain string) bool {
|
||||
return m.set.Has(reverseDomain(domain))
|
||||
}
|
||||
|
||||
func reverseDomain(domain string) string {
|
||||
l := len(domain)
|
||||
b := make([]byte, l)
|
||||
for i := 0; i < l; {
|
||||
r, n := utf8.DecodeRuneInString(domain[i:])
|
||||
i += n
|
||||
utf8.EncodeRune(b[l-i:], r)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func reverseDomainSuffix(domain string) string {
|
||||
l := len(domain)
|
||||
b := make([]byte, l+1)
|
||||
for i := 0; i < l; {
|
||||
r, n := utf8.DecodeRuneInString(domain[i:])
|
||||
i += n
|
||||
utf8.EncodeRune(b[l-i:], r)
|
||||
}
|
||||
b[l] = prefixLabel
|
||||
return string(b)
|
||||
}
|
||||
18
common/domain/matcher_test.go
Normal file
18
common/domain/matcher_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMatch(t *testing.T) {
|
||||
r := require.New(t)
|
||||
matcher := NewMatcher([]string{"domain.com"}, []string{"suffix.com", ".suffix.org"})
|
||||
r.True(matcher.Match("domain.com"))
|
||||
r.False(matcher.Match("my.domain.com"))
|
||||
r.True(matcher.Match("suffix.com"))
|
||||
r.True(matcher.Match("my.suffix.com"))
|
||||
r.False(matcher.Match("suffix.org"))
|
||||
r.True(matcher.Match("my.suffix.org"))
|
||||
}
|
||||
232
common/domain/set.go
Normal file
232
common/domain/set.go
Normal file
@@ -0,0 +1,232 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
const prefixLabel = '\r'
|
||||
|
||||
// mod from https://github.com/openacid/succinct
|
||||
|
||||
type succinctSet struct {
|
||||
leaves, labelBitmap []uint64
|
||||
labels []byte
|
||||
ranks, selects []int32
|
||||
}
|
||||
|
||||
func newSuccinctSet(keys []string) *succinctSet {
|
||||
ss := &succinctSet{}
|
||||
lIdx := 0
|
||||
type qElt struct{ s, e, col int }
|
||||
queue := []qElt{{0, len(keys), 0}}
|
||||
for i := 0; i < len(queue); i++ {
|
||||
elt := queue[i]
|
||||
if elt.col == len(keys[elt.s]) {
|
||||
// a leaf node
|
||||
elt.s++
|
||||
setBit(&ss.leaves, i, 1)
|
||||
}
|
||||
for j := elt.s; j < elt.e; {
|
||||
frm := j
|
||||
for ; j < elt.e && keys[j][elt.col] == keys[frm][elt.col]; j++ {
|
||||
}
|
||||
queue = append(queue, qElt{frm, j, elt.col + 1})
|
||||
ss.labels = append(ss.labels, keys[frm][elt.col])
|
||||
setBit(&ss.labelBitmap, lIdx, 0)
|
||||
lIdx++
|
||||
}
|
||||
|
||||
setBit(&ss.labelBitmap, lIdx, 1)
|
||||
lIdx++
|
||||
}
|
||||
ss.init()
|
||||
return ss
|
||||
}
|
||||
|
||||
func (ss *succinctSet) Has(key string) bool {
|
||||
var nodeId, bmIdx int
|
||||
for i := 0; i < len(key); i++ {
|
||||
currentChar := key[i]
|
||||
for ; ; bmIdx++ {
|
||||
if getBit(ss.labelBitmap, bmIdx) != 0 {
|
||||
return false
|
||||
}
|
||||
nextLabel := ss.labels[bmIdx-nodeId]
|
||||
if nextLabel == prefixLabel {
|
||||
return true
|
||||
}
|
||||
if nextLabel == currentChar {
|
||||
break
|
||||
}
|
||||
}
|
||||
nodeId = countZeros(ss.labelBitmap, ss.ranks, bmIdx+1)
|
||||
bmIdx = selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nodeId-1) + 1
|
||||
}
|
||||
if getBit(ss.leaves, nodeId) != 0 {
|
||||
return true
|
||||
}
|
||||
for ; ; bmIdx++ {
|
||||
if getBit(ss.labelBitmap, bmIdx) != 0 {
|
||||
return false
|
||||
}
|
||||
if ss.labels[bmIdx-nodeId] == prefixLabel {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setBit(bm *[]uint64, i int, v int) {
|
||||
for i>>6 >= len(*bm) {
|
||||
*bm = append(*bm, 0)
|
||||
}
|
||||
(*bm)[i>>6] |= uint64(v) << uint(i&63)
|
||||
}
|
||||
|
||||
func getBit(bm []uint64, i int) uint64 {
|
||||
return bm[i>>6] & (1 << uint(i&63))
|
||||
}
|
||||
|
||||
func (ss *succinctSet) init() {
|
||||
ss.selects, ss.ranks = indexSelect32R64(ss.labelBitmap)
|
||||
}
|
||||
|
||||
func countZeros(bm []uint64, ranks []int32, i int) int {
|
||||
a, _ := rank64(bm, ranks, int32(i))
|
||||
return i - int(a)
|
||||
}
|
||||
|
||||
func selectIthOne(bm []uint64, ranks, selects []int32, i int) int {
|
||||
a, _ := select32R64(bm, selects, ranks, int32(i))
|
||||
return int(a)
|
||||
}
|
||||
|
||||
func rank64(words []uint64, rindex []int32, i int32) (int32, int32) {
|
||||
wordI := i >> 6
|
||||
j := uint32(i & 63)
|
||||
n := rindex[wordI]
|
||||
w := words[wordI]
|
||||
c1 := n + int32(bits.OnesCount64(w&mask[j]))
|
||||
return c1, int32(w>>uint(j)) & 1
|
||||
}
|
||||
|
||||
func indexRank64(words []uint64, opts ...bool) []int32 {
|
||||
trailing := false
|
||||
if len(opts) > 0 {
|
||||
trailing = opts[0]
|
||||
}
|
||||
l := len(words)
|
||||
if trailing {
|
||||
l++
|
||||
}
|
||||
idx := make([]int32, l)
|
||||
n := int32(0)
|
||||
for i := 0; i < len(words); i++ {
|
||||
idx[i] = n
|
||||
n += int32(bits.OnesCount64(words[i]))
|
||||
}
|
||||
if trailing {
|
||||
idx[len(words)] = n
|
||||
}
|
||||
return idx
|
||||
}
|
||||
|
||||
func select32R64(words []uint64, selectIndex, rankIndex []int32, i int32) (int32, int32) {
|
||||
a := int32(0)
|
||||
l := int32(len(words))
|
||||
wordI := selectIndex[i>>5] >> 6
|
||||
for ; rankIndex[wordI+1] <= i; wordI++ {
|
||||
}
|
||||
w := words[wordI]
|
||||
ww := w
|
||||
base := wordI << 6
|
||||
findIth := int(i - rankIndex[wordI])
|
||||
offset := int32(0)
|
||||
ones := bits.OnesCount32(uint32(ww))
|
||||
if ones <= findIth {
|
||||
findIth -= ones
|
||||
offset |= 32
|
||||
ww >>= 32
|
||||
}
|
||||
ones = bits.OnesCount16(uint16(ww))
|
||||
if ones <= findIth {
|
||||
findIth -= ones
|
||||
offset |= 16
|
||||
ww >>= 16
|
||||
}
|
||||
ones = bits.OnesCount8(uint8(ww))
|
||||
if ones <= findIth {
|
||||
a = int32(select8Lookup[(ww>>5)&(0x7f8)|uint64(findIth-ones)]) + offset + 8
|
||||
} else {
|
||||
a = int32(select8Lookup[(ww&0xff)<<3|uint64(findIth)]) + offset
|
||||
}
|
||||
a += base
|
||||
w &= rMaskUpto[a&63]
|
||||
if w != 0 {
|
||||
return a, base + int32(bits.TrailingZeros64(w))
|
||||
}
|
||||
wordI++
|
||||
for ; wordI < l; wordI++ {
|
||||
w = words[wordI]
|
||||
if w != 0 {
|
||||
return a, wordI<<6 + int32(bits.TrailingZeros64(w))
|
||||
}
|
||||
}
|
||||
return a, l << 6
|
||||
}
|
||||
|
||||
func indexSelect32R64(words []uint64) ([]int32, []int32) {
|
||||
l := len(words) << 6
|
||||
sidx := make([]int32, 0, len(words))
|
||||
|
||||
ith := -1
|
||||
for i := 0; i < l; i++ {
|
||||
if words[i>>6]&(1<<uint(i&63)) != 0 {
|
||||
ith++
|
||||
if ith&31 == 0 {
|
||||
sidx = append(sidx, int32(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// clone to reduce cap to len
|
||||
sidx = append(sidx[:0:0], sidx...)
|
||||
return sidx, indexRank64(words, true)
|
||||
}
|
||||
|
||||
func init() {
|
||||
initMasks()
|
||||
initSelectLookup()
|
||||
}
|
||||
|
||||
var (
|
||||
mask [65]uint64
|
||||
rMaskUpto [64]uint64
|
||||
)
|
||||
|
||||
func initMasks() {
|
||||
for i := 0; i < 65; i++ {
|
||||
mask[i] = (1 << uint(i)) - 1
|
||||
}
|
||||
|
||||
var maskUpto [64]uint64
|
||||
for i := 0; i < 64; i++ {
|
||||
maskUpto[i] = (1 << uint(i+1)) - 1
|
||||
rMaskUpto[i] = ^maskUpto[i]
|
||||
}
|
||||
}
|
||||
|
||||
var select8Lookup [256 * 8]uint8
|
||||
|
||||
func initSelectLookup() {
|
||||
for i := 0; i < 256; i++ {
|
||||
w := uint8(i)
|
||||
for j := 0; j < 8; j++ {
|
||||
// x-th 1 in w
|
||||
// if x-th 1 is not found, it is 8
|
||||
x := bits.TrailingZeros8(w)
|
||||
w &= w - 1
|
||||
|
||||
select8Lookup[i*8+j] = uint8(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
171
common/linkedhashmap/map.go
Normal file
171
common/linkedhashmap/map.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package linkedhashmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
"github.com/sagernet/sing/common/x/list"
|
||||
)
|
||||
|
||||
type Map[K comparable, V any] struct {
|
||||
raw list.List[mapEntry[K, V]]
|
||||
rawMap map[K]*list.Element[mapEntry[K, V]]
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) init() {
|
||||
if m.rawMap == nil {
|
||||
m.rawMap = make(map[K]*list.Element[mapEntry[K, V]])
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) MarshalJSON() ([]byte, error) {
|
||||
buffer := new(bytes.Buffer)
|
||||
buffer.WriteString("{")
|
||||
for item := m.raw.Front(); item != nil; {
|
||||
entry := item.Value
|
||||
err := json.NewEncoder(buffer).Encode(entry.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buffer.WriteString(": ")
|
||||
err = json.NewEncoder(buffer).Encode(entry.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item = item.Next()
|
||||
if item != nil {
|
||||
buffer.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buffer.WriteString("}")
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) UnmarshalJSON(content []byte) error {
|
||||
decoder := json.NewDecoder(bytes.NewReader(content))
|
||||
m.Clear()
|
||||
m.init()
|
||||
objectStart, err := decoder.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if objectStart != json.Delim('{') {
|
||||
return E.New("expected json object start, but starts with ", objectStart)
|
||||
}
|
||||
for decoder.More() {
|
||||
var entryKey K
|
||||
err = decoder.Decode(&entryKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var entryValue V
|
||||
err = decoder.Decode(&entryValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.rawMap[entryKey] = m.raw.PushBack(mapEntry[K, V]{Key: entryKey, Value: entryValue})
|
||||
}
|
||||
objectEnd, err := decoder.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
} else if objectEnd != json.Delim('}') {
|
||||
return E.New("expected json object end, but ends with ", objectEnd)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type mapEntry[K comparable, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) Size() int {
|
||||
return m.raw.Size()
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) IsEmpty() bool {
|
||||
return m.raw.IsEmpty()
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) ContainsKey(key K) bool {
|
||||
m.init()
|
||||
_, loaded := m.rawMap[key]
|
||||
return loaded
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) Get(key K) (V, bool) {
|
||||
m.init()
|
||||
value, loaded := m.rawMap[key]
|
||||
return value.Value.Value, loaded
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) Put(key K, value V) V {
|
||||
m.init()
|
||||
entry, loaded := m.rawMap[key]
|
||||
if loaded {
|
||||
oldValue := entry.Value.Value
|
||||
entry.Value.Value = value
|
||||
return oldValue
|
||||
}
|
||||
entry = m.raw.PushBack(mapEntry[K, V]{Key: key, Value: value})
|
||||
m.rawMap[key] = entry
|
||||
return common.DefaultValue[V]()
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) PutAll(other *Map[K, V]) {
|
||||
for item := other.raw.Front(); item != nil; item = item.Next() {
|
||||
m.Put(item.Value.Key, item.Value.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) Remove(key K) bool {
|
||||
m.init()
|
||||
entry, loaded := m.rawMap[key]
|
||||
if !loaded {
|
||||
return false
|
||||
}
|
||||
m.raw.Remove(entry)
|
||||
delete(m.rawMap, key)
|
||||
return true
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) RemoveAll(keys []K) {
|
||||
m.init()
|
||||
for _, key := range keys {
|
||||
entry, loaded := m.rawMap[key]
|
||||
if !loaded {
|
||||
continue
|
||||
}
|
||||
m.raw.Remove(entry)
|
||||
delete(m.rawMap, key)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) AsMap() map[K]V {
|
||||
result := make(map[K]V, m.raw.Len())
|
||||
for item := m.raw.Front(); item != nil; item = item.Next() {
|
||||
result[item.Value.Key] = item.Value.Value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) Keys() []K {
|
||||
result := make([]K, 0, m.raw.Len())
|
||||
for item := m.raw.Front(); item != nil; item = item.Next() {
|
||||
result = append(result, item.Value.Key)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) Values() []V {
|
||||
result := make([]V, 0, m.raw.Len())
|
||||
for item := m.raw.Front(); item != nil; item = item.Next() {
|
||||
result = append(result, item.Value.Value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *Map[K, V]) Clear() {
|
||||
*m = Map[K, V]{}
|
||||
}
|
||||
Reference in New Issue
Block a user