mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-22 08:31:30 +00:00
Refactor json
This commit is contained in:
parent
dd56b2584b
commit
28b865acf0
46
common/badjson/array.go
Normal file
46
common/badjson/array.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package badjson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
|
||||||
|
"github.com/goccy/go-json"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSONArray[T any] []T
|
||||||
|
|
||||||
|
func (a JSONArray[T]) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal([]T(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *JSONArray[T]) UnmarshalJSON(content []byte) error {
|
||||||
|
decoder := json.NewDecoder(bytes.NewReader(content))
|
||||||
|
arrayStart, err := decoder.Token()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if arrayStart != json.Delim('[') {
|
||||||
|
return E.New("excepted array start, but got ", arrayStart)
|
||||||
|
}
|
||||||
|
err = a.decodeJSON(decoder)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
arrayEnd, err := decoder.Token()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else if arrayEnd != json.Delim(']') {
|
||||||
|
return E.New("excepted array end, but got ", arrayEnd)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *JSONArray[T]) decodeJSON(decoder *json.Decoder) error {
|
||||||
|
for decoder.More() {
|
||||||
|
var item T
|
||||||
|
err := decoder.Decode(&item)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
43
common/badjson/json.go
Normal file
43
common/badjson/json.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package badjson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/goccy/go-json"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
)
|
||||||
|
|
||||||
|
func decodeJSON(decoder *json.Decoder) (any, error) {
|
||||||
|
rawToken, err := decoder.Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch token := rawToken.(type) {
|
||||||
|
case json.Delim:
|
||||||
|
switch token {
|
||||||
|
case '{':
|
||||||
|
var object JSONObject
|
||||||
|
err = object.decodeJSON(decoder)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if rawToken != json.Delim('}') {
|
||||||
|
return nil, E.New("excepted object end, but got ", rawToken)
|
||||||
|
}
|
||||||
|
return object, nil
|
||||||
|
case '[':
|
||||||
|
var array JSONArray[any]
|
||||||
|
err = array.decodeJSON(decoder)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rawToken, err = decoder.Token()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if rawToken != json.Delim(']') {
|
||||||
|
return nil, E.New("excepted array end, but got ", rawToken)
|
||||||
|
}
|
||||||
|
return &array, nil
|
||||||
|
default:
|
||||||
|
return nil, E.New("excepted object or array end: ", token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rawToken, nil
|
||||||
|
}
|
78
common/badjson/object.go
Normal file
78
common/badjson/object.go
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package badjson
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goccy/go-json"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
"github.com/sagernet/sing/common/x/linkedhashmap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JSONObject struct {
|
||||||
|
linkedhashmap.Map[string, any]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JSONObject) MarshalJSON() ([]byte, error) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
buffer.WriteString("{")
|
||||||
|
items := m.Entries()
|
||||||
|
iLen := len(items)
|
||||||
|
for i, entry := range items {
|
||||||
|
keyContent, err := json.Marshal(entry.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buffer.WriteString(strings.TrimSpace(string(keyContent)))
|
||||||
|
buffer.WriteString(": ")
|
||||||
|
valueContent, err := json.Marshal(entry.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buffer.WriteString(strings.TrimSpace(string(valueContent)))
|
||||||
|
if i < iLen-1 {
|
||||||
|
buffer.WriteString(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.WriteString("}")
|
||||||
|
return buffer.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JSONObject) UnmarshalJSON(content []byte) error {
|
||||||
|
decoder := json.NewDecoder(bytes.NewReader(content))
|
||||||
|
m.Clear()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
err = m.decodeJSON(decoder)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JSONObject) decodeJSON(decoder *json.Decoder) error {
|
||||||
|
for decoder.More() {
|
||||||
|
var entryKey string
|
||||||
|
err := decoder.Decode(&entryKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var entryValue any
|
||||||
|
entryValue, err = decodeJSON(decoder)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m.Put(entryKey, entryValue)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,171 +0,0 @@
|
||||||
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]{}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
//go:build !go1.19
|
|
||||||
|
|
||||||
package constant
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
panic("sing-box requires Go 1.19 or later")
|
|
||||||
}
|
|
2
go.mod
2
go.mod
|
@ -7,7 +7,7 @@ require (
|
||||||
github.com/goccy/go-json v0.9.8
|
github.com/goccy/go-json v0.9.8
|
||||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||||
github.com/oschwald/geoip2-golang v1.7.0
|
github.com/oschwald/geoip2-golang v1.7.0
|
||||||
github.com/sagernet/sing v0.0.0-20220703051339-f128942ffe12
|
github.com/sagernet/sing v0.0.0-20220703114149-368e41b67bc4
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/spf13/cobra v1.5.0
|
github.com/spf13/cobra v1.5.0
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -20,8 +20,8 @@ github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/sagernet/sing v0.0.0-20220703051339-f128942ffe12 h1:HN3IoHyR2tpI4WwBVSDo5VJ0tKrQKqltkjlHTG9vbdo=
|
github.com/sagernet/sing v0.0.0-20220703114149-368e41b67bc4 h1:ePp3j7E71+yJfuIxDLzYkngK1AelkP2jITjkMKaHoBs=
|
||||||
github.com/sagernet/sing v0.0.0-20220703051339-f128942ffe12/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
|
github.com/sagernet/sing v0.0.0-20220703114149-368e41b67bc4/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649 h1:whNDUGOAX5GPZkSy4G3Gv9QyIgk5SXRyjkRuP7ohF8k=
|
||||||
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
|
github.com/sagernet/sing-shadowsocks v0.0.0-20220701084835-2208da1d8649/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||||
|
|
|
@ -4,15 +4,15 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
|
||||||
"github.com/goccy/go-json"
|
"github.com/goccy/go-json"
|
||||||
"github.com/sagernet/sing-box/common/linkedhashmap"
|
"github.com/sagernet/sing-box/common/badjson"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ToMap(v any) (*linkedhashmap.Map[string, any], error) {
|
func ToMap(v any) (*badjson.JSONObject, error) {
|
||||||
inputContent, err := json.Marshal(v)
|
inputContent, err := json.Marshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var content linkedhashmap.Map[string, any]
|
var content badjson.JSONObject
|
||||||
err = content.UnmarshalJSON(inputContent)
|
err = content.UnmarshalJSON(inputContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -20,8 +20,8 @@ func ToMap(v any) (*linkedhashmap.Map[string, any], error) {
|
||||||
return &content, nil
|
return &content, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MergeObjects(objects ...any) (*linkedhashmap.Map[string, any], error) {
|
func MergeObjects(objects ...any) (*badjson.JSONObject, error) {
|
||||||
var content linkedhashmap.Map[string, any]
|
var content badjson.JSONObject
|
||||||
for _, object := range objects {
|
for _, object := range objects {
|
||||||
objectMap, err := ToMap(object)
|
objectMap, err := ToMap(object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -45,12 +45,14 @@ func UnmarshallExcluded(inputContent []byte, parentObject any, object any) error
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var content linkedhashmap.Map[string, any]
|
var content badjson.JSONObject
|
||||||
err = content.UnmarshalJSON(inputContent)
|
err = content.UnmarshalJSON(inputContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
content.RemoveAll(parentContent.Keys())
|
for _, key := range parentContent.Keys() {
|
||||||
|
content.Remove(key)
|
||||||
|
}
|
||||||
inputContent, err = content.MarshalJSON()
|
inputContent, err = content.MarshalJSON()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in a new issue