- internal/jsjson: JSON.stringify-compatible encoder and order-preserving decoder, tested against fixtures produced by Node itself - internal/ctrl: awg-ctrl in Go (params, vpn:// keys, users, peers, sync, 3.1 upgrade, CAKE shaping, metrics, monitor job, Express-compatible HTTP layer) - internal/iauth, internal/logx: internal Ed25519 tokens and logging - tools/diffbench: Docker bench with stubbed awg/ip/tc/nft that runs one scenario against the TS and Go implementations for comparison
87 lines
2.0 KiB
Go
87 lines
2.0 KiB
Go
package jsjson
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
)
|
|
|
|
// Decode разбирает JSON с сохранением порядка ключей: объекты — Object, массивы —
|
|
// []any, числа — float64, как JSON.parse. Нужен, чтобы JSON.stringify(JSON.parse(x))
|
|
// воспроизводился точно (например, сохранённый профиль сервера с ключами awg).
|
|
func Decode(data []byte) (any, error) {
|
|
d := json.NewDecoder(bytes.NewReader(data))
|
|
d.UseNumber()
|
|
v, err := decodeValue(d)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if _, err := d.Token(); err != io.EOF {
|
|
return nil, errors.New("jsjson: trailing data")
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
func decodeValue(d *json.Decoder) (any, error) {
|
|
t, err := d.Token()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch x := t.(type) {
|
|
case json.Delim:
|
|
switch x {
|
|
case '{':
|
|
obj := Object{}
|
|
for d.More() {
|
|
kt, err := d.Token()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
key, ok := kt.(string)
|
|
if !ok {
|
|
return nil, errors.New("jsjson: object key is not a string")
|
|
}
|
|
val, err := decodeValue(d)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// повторный ключ в JS перезаписывает значение, оставаясь на первом месте
|
|
replaced := false
|
|
for i := range obj {
|
|
if obj[i].Key == key {
|
|
obj[i].Val = val
|
|
replaced = true
|
|
break
|
|
}
|
|
}
|
|
if !replaced {
|
|
obj = append(obj, KV{key, val})
|
|
}
|
|
}
|
|
if _, err := d.Token(); err != nil {
|
|
return nil, err
|
|
}
|
|
return obj, nil
|
|
case '[':
|
|
arr := []any{}
|
|
for d.More() {
|
|
val, err := decodeValue(d)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
arr = append(arr, val)
|
|
}
|
|
if _, err := d.Token(); err != nil {
|
|
return nil, err
|
|
}
|
|
return arr, nil
|
|
}
|
|
return nil, errors.New("jsjson: unexpected delimiter")
|
|
case json.Number:
|
|
return x.Float64()
|
|
default:
|
|
return x, nil // string, bool, nil
|
|
}
|
|
}
|