Refactor json

This commit is contained in:
世界 2022-07-03 19:43:27 +08:00
parent dd56b2584b
commit 28b865acf0
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
8 changed files with 179 additions and 188 deletions

46
common/badjson/array.go Normal file
View 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
View 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
View 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
}

View file

@ -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]{}
}

View file

@ -1,7 +0,0 @@
//go:build !go1.19
package constant
func init() {
panic("sing-box requires Go 1.19 or later")
}

2
go.mod
View file

@ -7,7 +7,7 @@ require (
github.com/goccy/go-json v0.9.8
github.com/logrusorgru/aurora v2.0.3+incompatible
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/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.5.0

4
go.sum
View file

@ -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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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-20220703051339-f128942ffe12/go.mod h1:3ZmoGNg/nNJTyHAZFNRSPaXpNIwpDvyIiAUd0KIWV5c=
github.com/sagernet/sing v0.0.0-20220703114149-368e41b67bc4 h1:ePp3j7E71+yJfuIxDLzYkngK1AelkP2jITjkMKaHoBs=
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/go.mod h1:MuyT+9fEPjvauAv0fSE0a6Q+l0Tv2ZrAafTkYfnxBFw=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=

View file

@ -4,15 +4,15 @@ import (
"bytes"
"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)
if err != nil {
return nil, err
}
var content linkedhashmap.Map[string, any]
var content badjson.JSONObject
err = content.UnmarshalJSON(inputContent)
if err != nil {
return nil, err
@ -20,8 +20,8 @@ func ToMap(v any) (*linkedhashmap.Map[string, any], error) {
return &content, nil
}
func MergeObjects(objects ...any) (*linkedhashmap.Map[string, any], error) {
var content linkedhashmap.Map[string, any]
func MergeObjects(objects ...any) (*badjson.JSONObject, error) {
var content badjson.JSONObject
for _, object := range objects {
objectMap, err := ToMap(object)
if err != nil {
@ -45,12 +45,14 @@ func UnmarshallExcluded(inputContent []byte, parentObject any, object any) error
if err != nil {
return err
}
var content linkedhashmap.Map[string, any]
var content badjson.JSONObject
err = content.UnmarshalJSON(inputContent)
if err != nil {
return err
}
content.RemoveAll(parentContent.Keys())
for _, key := range parentContent.Keys() {
content.Remove(key)
}
inputContent, err = content.MarshalJSON()
if err != nil {
return err