mirror of
https://github.com/ii64/sonic.git
synced 2026-06-23 09:56:44 +08:00
feat: support UseNumber for ast (#14)
Co-authored-by: duanyi.aster <duanyi.aster@bytedance.com>
This commit is contained in:
parent
4447cc41a7
commit
accee2e689
8 changed files with 362 additions and 82 deletions
22
README.md
22
README.md
|
|
@ -1,17 +1,17 @@
|
||||||
# Sonic
|
# Sonic
|
||||||
|
|
||||||
A blazingly fast JSON serializing & deserializing library.
|
A blazingly fast JSON serializing & deserializing library, accelerated by JIT(just-in-time compiling) and SIMD(single-instruction-multi-data).
|
||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
For all sizes of json and all scenes of usage, Sonic performs almost best.
|
For all sizes of json and all scenes of usage, Sonic performs almost best.
|
||||||
- Small (400B, 11 keys, 3 levels)
|
- Small (400B, 11 keys, 3 levels)
|
||||||

|

|
||||||
- Medium (110KB, 300+ keys, 3 levels)
|
- Medium (110KB, 300+ keys, 3 levels, with many quoted-json values)
|
||||||

|

|
||||||
- Large (550KB, 10000+ key, 6 levels)
|
- Large (550KB, 10000+ key, 6 levels)
|
||||||

|

|
||||||
|
|
||||||
For a 13KB [TwitterJson](https://github.com/bytedance/sonic/blob/main/decoder/testdata_test.go#L19)(cpu i9-9880H, goarch amd64), Sonic is **1.5x** fast than [json-iterator](https://github.com/json-iterator/go) in decoding, **2.5x** fast in encoding.
|
For a 13KB [TwitterJson](https://github.com/bytedance/sonic/blob/main/decoder/testdata_test.go#L19)(cpu i9-9880H, goarch amd64), Sonic is **1.5x** faster than [json-iterator](https://github.com/json-iterator/go) in decoding, **2.5x** faster in encoding.
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
BenchmarkDecoder_Generic_Sonic-16 10000 54309 ns/op 240.01 MB/s 46149 B/op 303 allocs/op
|
BenchmarkDecoder_Generic_Sonic-16 10000 54309 ns/op 240.01 MB/s 46149 B/op 303 allocs/op
|
||||||
|
|
@ -27,7 +27,7 @@ BenchmarkEncoder_Binding_Sonic-16 10000 73
|
||||||
BenchmarkEncoder_Binding_JsonIter-16 10000 23223 ns/op 561.31 MB/s 9489 B/op 2 allocs/op
|
BenchmarkEncoder_Binding_JsonIter-16 10000 23223 ns/op 561.31 MB/s 9489 B/op 2 allocs/op
|
||||||
BenchmarkEncoder_Binding_StdLib-16 10000 19512 ns/op 668.07 MB/s 9477 B/op 1 allocs/op
|
BenchmarkEncoder_Binding_StdLib-16 10000 19512 ns/op 668.07 MB/s 9477 B/op 1 allocs/op
|
||||||
```
|
```
|
||||||
More detail see [ast/search_test.go](https://github.com/bytedance/sonic/blob/main/ast/search_test.go), [decoder/decoder_test.go](https://github.com/bytedance/sonic/blob/main/decoder/decoder_test.go), [encoder/encoder_test.go](https://github.com/bytedance/sonic/blob/main/encoder/encoder_test.go)
|
More detail see [ast/search_test.go](https://github.com/bytedance/sonic/blob/main/ast/search_test.go), [decoder/decoder_test.go](https://github.com/bytedance/sonic/blob/main/decoder/decoder_test.go), [encoder/encoder_test.go](https://github.com/bytedance/sonic/blob/main/encoder/encoder_test.go),
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
@ -83,6 +83,14 @@ dc.Decode(&data) // data == json.Number("1")
|
||||||
dc = decoder.NewDecoder(input)
|
dc = decoder.NewDecoder(input)
|
||||||
dc.UseInt64()
|
dc.UseInt64()
|
||||||
dc.Decode(&data) // data == int64(1)
|
dc.Decode(&data) // data == int64(1)
|
||||||
|
|
||||||
|
root, err := sonic.GetFromString(input)
|
||||||
|
// Get json.Number
|
||||||
|
jn := root.Number()
|
||||||
|
jm := root.InterfaceUseNumber().(json.Number) // jn == jm
|
||||||
|
// Get float64
|
||||||
|
fn := root.Float64()
|
||||||
|
fm := root.Interface().(float64) // jn == jm
|
||||||
```
|
```
|
||||||
|
|
||||||
## Tips
|
## Tips
|
||||||
|
|
@ -102,7 +110,7 @@ func init() {
|
||||||
```
|
```
|
||||||
|
|
||||||
### Pass string or []byte?
|
### Pass string or []byte?
|
||||||
For alignment to encoding/json, we provide API to pass `[]byte` as arguement, but the string-to-bytes copy is conducted at the same time considering safety, which may lose performance when origin json is huge. Therefore, you can use `UnmarshalString`, `GetFromString` to pass string, as long as your origin data is string or **nocopy-case** is safe for your []byte.
|
For alignment to encoding/json, we provide API to pass `[]byte` as arguement, but the string-to-bytes copy is conducted at the same time considering safety, which may lose performance when origin json is huge. Therefore, you can use `UnmarshalString`, `GetFromString` to pass string, as long as your origin data is string or **nocopy-cast** is safe for your []byte.
|
||||||
|
|
||||||
### Avoid repeating work
|
### Avoid repeating work
|
||||||
`Get()` overlapping pathes from the same root may cause repeating parsing. Instead of using `Get()` several times, you can use parser and searcher together like this:
|
`Get()` overlapping pathes from the same root may cause repeating parsing. Instead of using `Get()` several times, you can use parser and searcher together like this:
|
||||||
|
|
@ -115,8 +123,8 @@ b = root.GetByPath( "entities","url")
|
||||||
c = root.GetByPath( "created_at")
|
c = root.GetByPath( "created_at")
|
||||||
```
|
```
|
||||||
No need to worry about the overlaping or overparsing of a, b and c, because the inner parser of their root is lazy-loaded.
|
No need to worry about the overlaping or overparsing of a, b and c, because the inner parser of their root is lazy-loaded.
|
||||||
### Better performance for `interface{}` (or `map[string]interface{}`)
|
### Better performance for generic deserializing
|
||||||
In most cases of fully-load generic json, `Unmarshal()` performs better than `ast.Loads()`. But if you only want to search a partial json and convert it into `interface{}` (or `Map()` for `map[string]interface{}`, `Array()` for `[]interface{}`), we advise you to combine these two:
|
In most cases of fully-load generic json, `Unmarshal()` performs better than `ast.Loads()`. But if you only want to search a partial json and convert it into `interface{}` (or `map[string]interface{}`, `[]interface{}`), we advise you to combine `Get()` and `Unmarshal()`:
|
||||||
```go
|
```go
|
||||||
import "github.com/bytedance/sonic"
|
import "github.com/bytedance/sonic"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,9 @@ func TestIterator(t *testing.T) {
|
||||||
if i < int64(loop) && v.Int64() != i {
|
if i < int64(loop) && v.Int64() != i {
|
||||||
t.Fatalf("exp:%v, got:%v", i, v)
|
t.Fatalf("exp:%v, got:%v", i, v)
|
||||||
}
|
}
|
||||||
|
if i != int64(ai.Pos())-1 || i >= int64(ai.Len()) {
|
||||||
|
t.Fatal(i)
|
||||||
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -70,6 +73,9 @@ func TestIterator(t *testing.T) {
|
||||||
if i < int64(loop) &&( v.Value.Int64() != i ||v.Key != fmt.Sprintf("k%d", i)) {
|
if i < int64(loop) &&( v.Value.Int64() != i ||v.Key != fmt.Sprintf("k%d", i)) {
|
||||||
t.Fatalf("exp:%v, got:%v", i, v.Value.Interface())
|
t.Fatalf("exp:%v, got:%v", i, v.Value.Interface())
|
||||||
}
|
}
|
||||||
|
if i != int64(mi.Pos())-1 || i >= int64(mi.Len()) {
|
||||||
|
t.Fatal(i)
|
||||||
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
138
ast/node.go
138
ast/node.go
|
|
@ -19,7 +19,6 @@ package ast
|
||||||
import (
|
import (
|
||||||
`encoding/json`
|
`encoding/json`
|
||||||
`fmt`
|
`fmt`
|
||||||
`strconv`
|
|
||||||
`unsafe`
|
`unsafe`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/internal/native`
|
`github.com/bytedance/sonic/internal/native`
|
||||||
|
|
@ -29,7 +28,7 @@ import (
|
||||||
const (
|
const (
|
||||||
_MAP_THRESHOLD = 5
|
_MAP_THRESHOLD = 5
|
||||||
_CAP_BITS = 32
|
_CAP_BITS = 32
|
||||||
_LEN_MASK = 1<<_CAP_BITS - 1
|
_LEN_MASK = 1 << _CAP_BITS - 1
|
||||||
_APPEND_EXTRA_SIZE = 5
|
_APPEND_EXTRA_SIZE = 5
|
||||||
|
|
||||||
_NODE_SIZE = unsafe.Sizeof(Node{})
|
_NODE_SIZE = unsafe.Sizeof(Node{})
|
||||||
|
|
@ -38,6 +37,7 @@ const (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
V_RAW native.ValueType = 1 << 4
|
V_RAW native.ValueType = 1 << 4
|
||||||
|
V_NUMBER native.ValueType = 10
|
||||||
V_ARRAY_RAW = V_RAW | native.V_ARRAY
|
V_ARRAY_RAW = V_RAW | native.V_ARRAY
|
||||||
V_OBJECT_RAW = V_RAW | native.V_OBJECT
|
V_OBJECT_RAW = V_RAW | native.V_OBJECT
|
||||||
MASK_RAW = V_RAW - 1
|
MASK_RAW = V_RAW - 1
|
||||||
|
|
@ -85,10 +85,9 @@ func (self *Node) Bool() bool {
|
||||||
// Int64 as above.
|
// Int64 as above.
|
||||||
func (self *Node) Int64() int64 {
|
func (self *Node) Int64() int64 {
|
||||||
switch self.t {
|
switch self.t {
|
||||||
|
case V_NUMBER : return numberToInt64(self)
|
||||||
case native.V_TRUE : return 1
|
case native.V_TRUE : return 1
|
||||||
case native.V_FALSE : return 0
|
case native.V_FALSE : return 0
|
||||||
case native.V_DOUBLE : return int64(i64tof(self.v))
|
|
||||||
case native.V_INTEGER : return self.v
|
|
||||||
case V_RAW :
|
case V_RAW :
|
||||||
n := self.parseRaw()
|
n := self.parseRaw()
|
||||||
return n.Int64()
|
return n.Int64()
|
||||||
|
|
@ -99,11 +98,10 @@ func (self *Node) Int64() int64 {
|
||||||
// Number as above.
|
// Number as above.
|
||||||
func (self *Node) Number() json.Number {
|
func (self *Node) Number() json.Number {
|
||||||
switch self.t {
|
switch self.t {
|
||||||
case native.V_DOUBLE : return json.Number(strconv.FormatFloat(i64tof(self.v), 'g', -1, 64))
|
|
||||||
case native.V_INTEGER : return json.Number(strconv.FormatInt(self.v, 10))
|
|
||||||
case V_RAW :
|
case V_RAW :
|
||||||
n := self.parseRaw()
|
n := self.parseRaw()
|
||||||
return n.Number()
|
return n.Number()
|
||||||
|
case V_NUMBER : return toNumber(self)
|
||||||
default : panic("value cannot be represented as a json.Number")
|
default : panic("value cannot be represented as a json.Number")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -111,12 +109,11 @@ func (self *Node) Number() json.Number {
|
||||||
// String as above.
|
// String as above.
|
||||||
func (self *Node) String() string {
|
func (self *Node) String() string {
|
||||||
switch self.t {
|
switch self.t {
|
||||||
|
case V_NUMBER : return toNumber(self).String()
|
||||||
case native.V_NULL : return "null"
|
case native.V_NULL : return "null"
|
||||||
case native.V_TRUE : return "true"
|
case native.V_TRUE : return "true"
|
||||||
case native.V_FALSE : return "false"
|
case native.V_FALSE : return "false"
|
||||||
case native.V_STRING : return addr2str(self.p, self.v)
|
case native.V_STRING : return addr2str(self.p, self.v)
|
||||||
case native.V_DOUBLE : return strconv.FormatFloat(i64tof(self.v), 'g', -1, 64)
|
|
||||||
case native.V_INTEGER : return strconv.FormatInt(self.v, 10)
|
|
||||||
case V_RAW :
|
case V_RAW :
|
||||||
n := self.parseRaw()
|
n := self.parseRaw()
|
||||||
return n.String()
|
return n.String()
|
||||||
|
|
@ -127,10 +124,9 @@ func (self *Node) String() string {
|
||||||
// Float64 as above.
|
// Float64 as above.
|
||||||
func (self *Node) Float64() float64 {
|
func (self *Node) Float64() float64 {
|
||||||
switch self.t {
|
switch self.t {
|
||||||
|
case V_NUMBER : return numberToFloat64(self)
|
||||||
case native.V_TRUE : return 1.0
|
case native.V_TRUE : return 1.0
|
||||||
case native.V_FALSE : return 0.0
|
case native.V_FALSE : return 0.0
|
||||||
case native.V_DOUBLE : return i64tof(self.v)
|
|
||||||
case native.V_INTEGER : return float64(self.v)
|
|
||||||
case V_RAW :
|
case V_RAW :
|
||||||
n := self.parseRaw()
|
n := self.parseRaw()
|
||||||
return n.Float64()
|
return n.Float64()
|
||||||
|
|
@ -254,12 +250,14 @@ func (self *Node) Index(idx int) *Node {
|
||||||
return self.loadIndex(idx)
|
return self.loadIndex(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Values returns iterator for array's children traversal
|
||||||
func (self *Node) Values() ListIterator {
|
func (self *Node) Values() ListIterator {
|
||||||
self.must(native.V_ARRAY, "an array")
|
self.must(native.V_ARRAY, "an array")
|
||||||
self.loadAllIndex()
|
self.loadAllIndex()
|
||||||
return ListIterator{Iterator{p: self}}
|
return ListIterator{Iterator{p: self}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Values returns iterator for object's children traversal
|
||||||
func (self *Node) Properties() ObjectIterator {
|
func (self *Node) Properties() ObjectIterator {
|
||||||
self.must(native.V_OBJECT, "an object")
|
self.must(native.V_OBJECT, "an object")
|
||||||
self.loadAllKey()
|
self.loadAllKey()
|
||||||
|
|
@ -275,6 +273,13 @@ func (self *Node) Map() map[string]interface{} {
|
||||||
return self.toGenericObject()
|
return self.toGenericObject()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapUseNumber loads all keys of an object node, with numeric nodes casted to json.Number
|
||||||
|
func (self *Node) MapUseNumber() map[string]interface{} {
|
||||||
|
self.must(native.V_OBJECT, "an object")
|
||||||
|
self.loadAllKey()
|
||||||
|
return self.toGenericObjectUseNumber()
|
||||||
|
}
|
||||||
|
|
||||||
// Array loads all indexes of an array node
|
// Array loads all indexes of an array node
|
||||||
func (self *Node) Array() []interface{} {
|
func (self *Node) Array() []interface{} {
|
||||||
self.must(native.V_ARRAY, "an array")
|
self.must(native.V_ARRAY, "an array")
|
||||||
|
|
@ -282,8 +287,16 @@ func (self *Node) Array() []interface{} {
|
||||||
return self.toGenericArray()
|
return self.toGenericArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Array loads all indexes of an array node, with numeric nodes casted to json.Number
|
||||||
|
func (self *Node) ArrayUseNumber() []interface{} {
|
||||||
|
self.must(native.V_ARRAY, "an array")
|
||||||
|
self.loadAllIndex()
|
||||||
|
return self.toGenericArrayUseNumber()
|
||||||
|
}
|
||||||
|
|
||||||
// Interface loads all children under all pathes from this node,
|
// Interface loads all children under all pathes from this node,
|
||||||
// and converts itself as generic go type
|
// and converts itself as generic go type
|
||||||
|
// all numberic nodes are casted to float64
|
||||||
func (self *Node) Interface() interface{} {
|
func (self *Node) Interface() interface{} {
|
||||||
switch self.t {
|
switch self.t {
|
||||||
case native.V_EOF : panic("invalid value")
|
case native.V_EOF : panic("invalid value")
|
||||||
|
|
@ -293,8 +306,8 @@ func (self *Node) Interface() interface{} {
|
||||||
case native.V_ARRAY : return self.toGenericArray()
|
case native.V_ARRAY : return self.toGenericArray()
|
||||||
case native.V_OBJECT : return self.toGenericObject()
|
case native.V_OBJECT : return self.toGenericObject()
|
||||||
case native.V_STRING : return addr2str(self.p, self.v)
|
case native.V_STRING : return addr2str(self.p, self.v)
|
||||||
case native.V_DOUBLE : return i64tof(self.v)
|
case V_NUMBER :
|
||||||
case native.V_INTEGER : return self.v
|
return numberToFloat64(self)
|
||||||
case V_ARRAY_RAW:
|
case V_ARRAY_RAW:
|
||||||
self.loadAllIndex()
|
self.loadAllIndex()
|
||||||
return self.toGenericArray()
|
return self.toGenericArray()
|
||||||
|
|
@ -308,6 +321,32 @@ func (self *Node) Interface() interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InterfaceUseNumber works same with Interface()
|
||||||
|
// except numberic nodes are casted to json.Number
|
||||||
|
func (self *Node) InterfaceUseNumber() interface{} {
|
||||||
|
switch self.t {
|
||||||
|
case native.V_EOF : panic("invalid value")
|
||||||
|
case native.V_NULL : return nil
|
||||||
|
case native.V_TRUE : return true
|
||||||
|
case native.V_FALSE : return false
|
||||||
|
case native.V_ARRAY : return self.toGenericArrayUseNumber()
|
||||||
|
case native.V_OBJECT : return self.toGenericObjectUseNumber()
|
||||||
|
case native.V_STRING : return addr2str(self.p, self.v)
|
||||||
|
case V_NUMBER :
|
||||||
|
return toNumber(self)
|
||||||
|
case V_ARRAY_RAW:
|
||||||
|
self.loadAllIndex()
|
||||||
|
return self.toGenericArrayUseNumber()
|
||||||
|
case V_OBJECT_RAW:
|
||||||
|
self.loadAllKey()
|
||||||
|
return self.toGenericObjectUseNumber()
|
||||||
|
case V_RAW :
|
||||||
|
n := self.parseRaw()
|
||||||
|
return n.InterfaceUseNumber()
|
||||||
|
default : panic("not gonna happen")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Internal Helper Methods **/
|
/** Internal Helper Methods **/
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -652,6 +691,25 @@ func (self *Node) toGenericArray() []interface{} {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Node) toGenericArrayUseNumber() []interface{} {
|
||||||
|
nb := self.Len()
|
||||||
|
ret := make([]interface{}, nb)
|
||||||
|
if nb == 0 {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert each item */
|
||||||
|
var p = (*Node)(self.p)
|
||||||
|
ret[0] = p.InterfaceUseNumber()
|
||||||
|
for i := 1; i < nb; i++ {
|
||||||
|
p = p.unsafe_next()
|
||||||
|
ret[i] = p.InterfaceUseNumber()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* all done */
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func (self *Node) toGenericObject() map[string]interface{} {
|
func (self *Node) toGenericObject() map[string]interface{} {
|
||||||
nb := self.Len()
|
nb := self.Len()
|
||||||
ret := make(map[string]interface{}, nb)
|
ret := make(map[string]interface{}, nb)
|
||||||
|
|
@ -671,6 +729,26 @@ func (self *Node) toGenericObject() map[string]interface{} {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func (self *Node) toGenericObjectUseNumber() map[string]interface{} {
|
||||||
|
nb := self.Len()
|
||||||
|
ret := make(map[string]interface{}, nb)
|
||||||
|
if nb == 0 {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/* convert each item */
|
||||||
|
var p = (*Pair)(self.p)
|
||||||
|
ret[p.Key] = p.Value.InterfaceUseNumber()
|
||||||
|
for i := 1; i < nb; i++ {
|
||||||
|
p = p.unsafe_next()
|
||||||
|
ret[p.Key] = p.Value.InterfaceUseNumber()
|
||||||
|
}
|
||||||
|
|
||||||
|
/* all done */
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
/** Internal Factory Methods **/
|
/** Internal Factory Methods **/
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -682,13 +760,34 @@ var (
|
||||||
emptyObjectNode = Node{t: native.V_OBJECT}
|
emptyObjectNode = Node{t: native.V_OBJECT}
|
||||||
)
|
)
|
||||||
|
|
||||||
func newInt64(v int64) Node {
|
func newNumber(v string) Node {
|
||||||
return Node{
|
return Node{
|
||||||
v: v,
|
v: int64(len(v)),
|
||||||
t: native.V_INTEGER,
|
p: str2ptr(v),
|
||||||
|
t: V_NUMBER,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toNumber(node *Node) json.Number {
|
||||||
|
return json.Number(addr2str(node.p, node.v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func numberToFloat64(node *Node) float64 {
|
||||||
|
ret,err := toNumber(node).Float64()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func numberToInt64(node *Node) int64 {
|
||||||
|
ret,err := toNumber(node).Int64()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
func newBytes(v []byte) Node {
|
func newBytes(v []byte) Node {
|
||||||
return Node{
|
return Node{
|
||||||
t: native.V_STRING,
|
t: native.V_STRING,
|
||||||
|
|
@ -705,13 +804,6 @@ func newString(v string) Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFloat64(v float64) Node {
|
|
||||||
return Node{
|
|
||||||
t: native.V_DOUBLE,
|
|
||||||
v: f64toi(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func newArray(v []Node) Node {
|
func newArray(v []Node) Node {
|
||||||
return Node{
|
return Node{
|
||||||
t: native.V_ARRAY,
|
t: native.V_ARRAY,
|
||||||
|
|
@ -799,7 +891,7 @@ func newRawNode(str string) Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *Node) parseRaw() Node {
|
func (self *Node) parseRaw() Node {
|
||||||
raw := self.Raw()
|
raw := addr2str(self.p, self.v)
|
||||||
n, e := NewParser(raw).Parse()
|
n, e := NewParser(raw).Parse()
|
||||||
if e != 0 {
|
if e != 0 {
|
||||||
panic(fmt.Sprintf("%v, raw json: %s", e.Error(), raw))
|
panic(fmt.Sprintf("%v, raw json: %s", e.Error(), raw))
|
||||||
|
|
|
||||||
121
ast/node_test.go
121
ast/node_test.go
|
|
@ -17,15 +17,81 @@
|
||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"encoding/json"
|
||||||
"testing"
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
"github.com/bytedance/sonic/internal/native"
|
jsoniter "github.com/json-iterator/go"
|
||||||
jsoniter "github.com/json-iterator/go"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var parallelism = 4
|
var parallelism = 4
|
||||||
|
|
||||||
|
func TestUseNumber(t *testing.T) {
|
||||||
|
node, err := NewParser("1061346755812312312313").Parse()
|
||||||
|
if err != 0 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if node.Type() != V_NUMBER {
|
||||||
|
t.Fatalf("wrong type: %v", node.Type())
|
||||||
|
}
|
||||||
|
iv := node.InterfaceUseNumber().(json.Number)
|
||||||
|
if iv.String() != "1061346755812312312313" {
|
||||||
|
t.Fatalf("exp:%#v, got:%#v", "1061346755812312312313", iv.String())
|
||||||
|
}
|
||||||
|
ix := node.Interface().(float64)
|
||||||
|
if ix != float64(1061346755812312312313) {
|
||||||
|
t.Fatalf("exp:%#v, got:%#v", float64(1061346755812312312313), ix)
|
||||||
|
}
|
||||||
|
ij,_ := node.Number().Int64()
|
||||||
|
jj,_ := json.Number("1061346755812312312313").Int64()
|
||||||
|
if ij != jj {
|
||||||
|
t.Fatalf("exp:%#v, got:%#v", jj, ij)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMap(t *testing.T) {
|
||||||
|
node, err := NewParser(`{"a":-0, "b":1, "c":-1.2, "d":-1.2e-10}`).Parse()
|
||||||
|
if err != 0 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
m := node.Map()
|
||||||
|
assert.Equal(t, m, map[string]interface{}{
|
||||||
|
"a": float64(0),
|
||||||
|
"b": float64(1),
|
||||||
|
"c": float64(-1.2),
|
||||||
|
"d": float64(-1.2e-10),
|
||||||
|
})
|
||||||
|
m1 := node.MapUseNumber()
|
||||||
|
assert.Equal(t, m1, map[string]interface{}{
|
||||||
|
"a": json.Number("-0"),
|
||||||
|
"b": json.Number("1"),
|
||||||
|
"c": json.Number("-1.2"),
|
||||||
|
"d": json.Number("-1.2e-10"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestArray(t *testing.T) {
|
||||||
|
node, err := NewParser(`[-0, 1, -1.2, -1.2e-10]`).Parse()
|
||||||
|
if err != 0 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
m := node.Array()
|
||||||
|
assert.Equal(t, m, []interface{}{
|
||||||
|
float64(0),
|
||||||
|
float64(1),
|
||||||
|
float64(-1.2),
|
||||||
|
float64(-1.2e-10),
|
||||||
|
})
|
||||||
|
m1 := node.ArrayUseNumber()
|
||||||
|
assert.Equal(t, m1, []interface{}{
|
||||||
|
json.Number("-0"),
|
||||||
|
json.Number("1"),
|
||||||
|
json.Number("-1.2"),
|
||||||
|
json.Number("-1.2e-10"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestNodeRaw(t *testing.T) {
|
func TestNodeRaw(t *testing.T) {
|
||||||
root, derr := NewSearcher(_TwitterJson).GetByPath("search_metadata")
|
root, derr := NewSearcher(_TwitterJson).GetByPath("search_metadata")
|
||||||
if derr != nil {
|
if derr != nil {
|
||||||
|
|
@ -145,19 +211,14 @@ func TestNodeSet(t *testing.T) {
|
||||||
if derr != 0 {
|
if derr != 0 {
|
||||||
t.Fatalf("decode failed: %v", derr.Error())
|
t.Fatalf("decode failed: %v", derr.Error())
|
||||||
}
|
}
|
||||||
root.GetByPath("statuses", 3).Set("id_str", Node{
|
app,_ := NewParser("111").Parse()
|
||||||
v: int64(111),
|
root.GetByPath("statuses", 3).Set("id_str", app)
|
||||||
t: native.V_INTEGER,
|
|
||||||
})
|
|
||||||
val := root.GetByPath("statuses", 3, "id_str").Int64()
|
val := root.GetByPath("statuses", 3, "id_str").Int64()
|
||||||
if val != 111 {
|
if val != 111 {
|
||||||
t.Fatalf("exp: %+v, got: %+v", 111, val)
|
t.Fatalf("exp: %+v, got: %+v", 111, val)
|
||||||
}
|
}
|
||||||
for i := root.GetByPath("statuses", 3).Cap(); i >= 0; i-- {
|
for i := root.GetByPath("statuses", 3).Cap(); i >= 0; i-- {
|
||||||
root.GetByPath("statuses", 3).Set("id_str"+strconv.Itoa(i), Node{
|
root.GetByPath("statuses", 3).Set("id_str"+strconv.Itoa(i), app)
|
||||||
v: int64(111),
|
|
||||||
t: native.V_INTEGER,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
val = root.GetByPath("statuses", 3, "id_str0").Int64()
|
val = root.GetByPath("statuses", 3, "id_str0").Int64()
|
||||||
if val != 111 {
|
if val != 111 {
|
||||||
|
|
@ -180,10 +241,8 @@ func TestNodeSetByIndex(t *testing.T) {
|
||||||
if derr != 0 {
|
if derr != 0 {
|
||||||
t.Fatalf("decode failed: %v", derr.Error())
|
t.Fatalf("decode failed: %v", derr.Error())
|
||||||
}
|
}
|
||||||
root.GetByPath("statuses").SetByIndex(0, Node{
|
app, _ := NewParser("111").Parse()
|
||||||
v: int64(111),
|
root.GetByPath("statuses").SetByIndex(0, app)
|
||||||
t: native.V_INTEGER,
|
|
||||||
})
|
|
||||||
val := root.GetByPath("statuses", 0).Int64()
|
val := root.GetByPath("statuses", 0).Int64()
|
||||||
if val != 111 {
|
if val != 111 {
|
||||||
t.Fatalf("exp: %+v, got: %+v", 111, val)
|
t.Fatalf("exp: %+v, got: %+v", 111, val)
|
||||||
|
|
@ -205,12 +264,10 @@ func TestNodeAdd(t *testing.T) {
|
||||||
if derr != 0 {
|
if derr != 0 {
|
||||||
t.Fatalf("decode failed: %v", derr.Error())
|
t.Fatalf("decode failed: %v", derr.Error())
|
||||||
}
|
}
|
||||||
|
app, _ := NewParser("111").Parse()
|
||||||
|
|
||||||
for i := root.GetByPath("statuses").Cap(); i >= 0; i-- {
|
for i := root.GetByPath("statuses").Cap(); i >= 0; i-- {
|
||||||
root.GetByPath("statuses").Add(Node{
|
root.GetByPath("statuses").Add(app)
|
||||||
v: int64(111),
|
|
||||||
t: native.V_INTEGER,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
val := root.GetByPath("statuses", 4).Int64()
|
val := root.GetByPath("statuses", 4).Int64()
|
||||||
if val != 111 {
|
if val != 111 {
|
||||||
|
|
@ -286,11 +343,11 @@ func BenchmarkNodeGet(b *testing.B) {
|
||||||
b.Fatalf("decode failed: %v", derr.Error())
|
b.Fatalf("decode failed: %v", derr.Error())
|
||||||
}
|
}
|
||||||
node := root.Get("statuses").Index(3).Get("entities").Get("hashtags").Index(0)
|
node := root.Get("statuses").Index(3).Get("entities").Get("hashtags").Index(0)
|
||||||
node.Set("test1", newInt64(1))
|
node.Set("test1", newNumber("1"))
|
||||||
node.Set("test2", newInt64(2))
|
node.Set("test2", newNumber("2"))
|
||||||
node.Set("test3", newInt64(3))
|
node.Set("test3", newNumber("3"))
|
||||||
node.Set("test4", newInt64(4))
|
node.Set("test4", newNumber("4"))
|
||||||
node.Set("test5", newInt64(5))
|
node.Set("test5", newNumber("5"))
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
node.Get("text").Interface()
|
node.Get("text").Interface()
|
||||||
|
|
@ -303,11 +360,11 @@ func BenchmarkMapGet(b *testing.B) {
|
||||||
b.Fatalf("decode failed: %v", derr.Error())
|
b.Fatalf("decode failed: %v", derr.Error())
|
||||||
}
|
}
|
||||||
node := root.Get("statuses").Index(3).Get("entities").Get("hashtags").Index(0)
|
node := root.Get("statuses").Index(3).Get("entities").Get("hashtags").Index(0)
|
||||||
node.Set("test1", newInt64(1))
|
node.Set("test1", newNumber("1"))
|
||||||
node.Set("test2", newInt64(2))
|
node.Set("test2", newNumber("2"))
|
||||||
node.Set("test3", newInt64(3))
|
node.Set("test3", newNumber("3"))
|
||||||
node.Set("test4", newInt64(4))
|
node.Set("test4", newNumber("4"))
|
||||||
node.Set("test5", newInt64(5))
|
node.Set("test5", newNumber("5"))
|
||||||
m := node.Map()
|
m := node.Map()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
|
@ -324,7 +381,7 @@ func BenchmarkNodeSet(b *testing.B) {
|
||||||
b.SetParallelism(parallelism)
|
b.SetParallelism(parallelism)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
node.Set("test1", newInt64(1))
|
node.Set("test1", newNumber("1"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -352,7 +409,7 @@ func BenchmarkNodeAdd(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
root, _ := NewParser(data).Parse()
|
root, _ := NewParser(data).Parse()
|
||||||
node := root.Get("statuses")
|
node := root.Get("statuses")
|
||||||
node.Add(newObject([]Pair{{"test", newInt64(1)}}))
|
node.Add(newObject([]Pair{{"test", newNumber("1")}}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -257,19 +257,19 @@ func (self *Parser) Parse() (Node, native.ParsingError) {
|
||||||
case native.V_NULL : return nullNode, 0
|
case native.V_NULL : return nullNode, 0
|
||||||
case native.V_TRUE : return trueNode, 0
|
case native.V_TRUE : return trueNode, 0
|
||||||
case native.V_FALSE : return falseNode, 0
|
case native.V_FALSE : return falseNode, 0
|
||||||
case native.V_ARRAY:
|
|
||||||
if self.noLazy {
|
|
||||||
return self.decodeArray(make([]Node, 0, _DEFAULT_NODE_CAP))
|
|
||||||
}
|
|
||||||
return newRawArray(self, make([]Node, 0, _DEFAULT_NODE_CAP)), 0
|
|
||||||
case native.V_OBJECT:
|
|
||||||
if self.noLazy {
|
|
||||||
return self.decodeObject(make([]Pair, 0, _DEFAULT_NODE_CAP))
|
|
||||||
}
|
|
||||||
return newRawObject(self, make([]Pair, 0, _DEFAULT_NODE_CAP)), 0
|
|
||||||
case native.V_STRING : return self.decodeString(val.Iv, val.Ep)
|
case native.V_STRING : return self.decodeString(val.Iv, val.Ep)
|
||||||
case native.V_DOUBLE : return newFloat64(val.Dv), 0
|
case native.V_ARRAY:
|
||||||
case native.V_INTEGER : return newInt64(val.Iv), 0
|
if self.noLazy {
|
||||||
|
return self.decodeArray(make([]Node, 0, _DEFAULT_NODE_CAP))
|
||||||
|
}
|
||||||
|
return newRawArray(self, make([]Node, 0, _DEFAULT_NODE_CAP)), 0
|
||||||
|
case native.V_OBJECT:
|
||||||
|
if self.noLazy {
|
||||||
|
return self.decodeObject(make([]Pair, 0, _DEFAULT_NODE_CAP))
|
||||||
|
}
|
||||||
|
return newRawObject(self, make([]Pair, 0, _DEFAULT_NODE_CAP)), 0
|
||||||
|
case native.V_DOUBLE : return newNumber(self.s[val.Ep:self.p]), 0
|
||||||
|
case native.V_INTEGER : return newNumber(self.s[val.Ep:self.p]), 0
|
||||||
default : return Node{}, native.ParsingError(-val.Vt)
|
default : return Node{}, native.ParsingError(-val.Vt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -287,6 +287,7 @@ func (self *Parser) skip() (int, native.ParsingError) {
|
||||||
|
|
||||||
/** Parser Factory **/
|
/** Parser Factory **/
|
||||||
|
|
||||||
|
// Loads parse all json into interface{}
|
||||||
func Loads(src string) (int, interface{}, native.ParsingError) {
|
func Loads(src string) (int, interface{}, native.ParsingError) {
|
||||||
ps := &Parser{s: src}
|
ps := &Parser{s: src}
|
||||||
np, err := ps.Parse()
|
np, err := ps.Parse()
|
||||||
|
|
@ -299,6 +300,19 @@ func Loads(src string) (int, interface{}, native.ParsingError) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loads parse all json into interface{}, with numeric nodes casted to json.Number
|
||||||
|
func LoadsUseNumber(src string) (int, interface{}, native.ParsingError) {
|
||||||
|
ps := &Parser{s: src}
|
||||||
|
np, err := ps.Parse()
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if err != 0 {
|
||||||
|
return 0, nil, err
|
||||||
|
} else {
|
||||||
|
return ps.Pos(), np.InterfaceUseNumber(), 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewParser(src string) *Parser {
|
func NewParser(src string) *Parser {
|
||||||
return &Parser{s: src}
|
return &Parser{s: src}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,6 @@ package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
`encoding/json`
|
`encoding/json`
|
||||||
`fmt`
|
|
||||||
`reflect`
|
|
||||||
`testing`
|
`testing`
|
||||||
|
|
||||||
jsoniter `github.com/json-iterator/go`
|
jsoniter `github.com/json-iterator/go`
|
||||||
|
|
@ -30,20 +28,53 @@ import (
|
||||||
func runDecoderTest(t *testing.T, src string, expect interface{}) {
|
func runDecoderTest(t *testing.T, src string, expect interface{}) {
|
||||||
vv, err := NewParser(src).Parse()
|
vv, err := NewParser(src).Parse()
|
||||||
if err != 0 { panic(err) }
|
if err != 0 { panic(err) }
|
||||||
fmt.Printf("%s -> %s :: %v\n", src, reflect.TypeOf(vv), vv)
|
|
||||||
assert.Equal(t, expect, vv.Interface())
|
assert.Equal(t, expect, vv.Interface())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runDecoderTestUseNumber(t *testing.T, src string, expect interface{}) {
|
||||||
|
vv, err := NewParser(src).Parse()
|
||||||
|
if err != 0 { panic(err) }
|
||||||
|
vvv := vv.InterfaceUseNumber()
|
||||||
|
switch vvv.(type) {
|
||||||
|
case json.Number:
|
||||||
|
assert.Equal(t, expect, n2f64(vvv.(json.Number)))
|
||||||
|
case []interface{}:
|
||||||
|
x := vvv.([]interface{})
|
||||||
|
for i, e := range x {
|
||||||
|
if ev,ok := e.(json.Number);ok {
|
||||||
|
x[i] = n2f64(ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.Equal(t, expect, x)
|
||||||
|
case map[string]interface{}:
|
||||||
|
x := vvv.(map[string]interface{})
|
||||||
|
for k,v := range x {
|
||||||
|
if ev, ok := v.(json.Number); ok {
|
||||||
|
x[k] = n2f64(ev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.Equal(t, expect, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func n2f64(i json.Number) float64{
|
||||||
|
x, err := i.Float64()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
func TestParser_Basic(t *testing.T) {
|
func TestParser_Basic(t *testing.T) {
|
||||||
runDecoderTest(t, `null`, nil)
|
runDecoderTest(t, `null`, nil)
|
||||||
runDecoderTest(t, `true`, true)
|
runDecoderTest(t, `true`, true)
|
||||||
runDecoderTest(t, `false`, false)
|
runDecoderTest(t, `false`, false)
|
||||||
runDecoderTest(t, `"hello, world \\ \/ \b \f \n \r \t \u666f 测试中文"`, "hello, world \\ / \b \f \n \r \t \u666f 测试中文")
|
runDecoderTest(t, `"hello, world \\ \/ \b \f \n \r \t \u666f 测试中文"`, "hello, world \\ / \b \f \n \r \t \u666f 测试中文")
|
||||||
runDecoderTest(t, `"\ud83d\ude00"`, "😀")
|
runDecoderTest(t, `"\ud83d\ude00"`, "😀")
|
||||||
runDecoderTest(t, `0`, int64(0))
|
runDecoderTest(t, `0`, float64(0))
|
||||||
runDecoderTest(t, `-0`, int64(0))
|
runDecoderTest(t, `-0`, float64(0))
|
||||||
runDecoderTest(t, `123456`, int64(123456))
|
runDecoderTest(t, `123456`, float64(123456))
|
||||||
runDecoderTest(t, `-12345`, int64(-12345))
|
runDecoderTest(t, `-12345`, float64(-12345))
|
||||||
runDecoderTest(t, `0.2`, 0.2)
|
runDecoderTest(t, `0.2`, 0.2)
|
||||||
runDecoderTest(t, `1.2`, 1.2)
|
runDecoderTest(t, `1.2`, 1.2)
|
||||||
runDecoderTest(t, `-0.2`, -0.2)
|
runDecoderTest(t, `-0.2`, -0.2)
|
||||||
|
|
@ -78,6 +109,62 @@ func TestParser_Basic(t *testing.T) {
|
||||||
runDecoderTest(t, `{}`, map[string]interface{}{})
|
runDecoderTest(t, `{}`, map[string]interface{}{})
|
||||||
runDecoderTest(t, `["asd", "123", true, false, null, 2.4, 1.2e15]`, []interface{}{"asd", "123", true, false, nil, 2.4, 1.2e15})
|
runDecoderTest(t, `["asd", "123", true, false, null, 2.4, 1.2e15]`, []interface{}{"asd", "123", true, false, nil, 2.4, 1.2e15})
|
||||||
runDecoderTest(t, `{"asdf": "qwer", "zxcv": true}`, map[string]interface{}{"asdf": "qwer", "zxcv": true})
|
runDecoderTest(t, `{"asdf": "qwer", "zxcv": true}`, map[string]interface{}{"asdf": "qwer", "zxcv": true})
|
||||||
|
runDecoderTest(t, `{"a": "123", "b": true, "c": false, "d": null, "e": 2.4, "f": 1.2e15, "g": 1}`, map[string]interface{}{"a":"123", "b":true, "c":false, "d":nil, "e":float64(2.4), "f":float64(1.2e15), "g":float64(1)})
|
||||||
|
|
||||||
|
runDecoderTestUseNumber(t, `null`, nil)
|
||||||
|
runDecoderTestUseNumber(t, `true`, true)
|
||||||
|
runDecoderTestUseNumber(t, `false`, false)
|
||||||
|
runDecoderTestUseNumber(t, `"hello, world \\ \/ \b \f \n \r \t \u666f 测试中文"`, "hello, world \\ / \b \f \n \r \t \u666f 测试中文")
|
||||||
|
runDecoderTestUseNumber(t, `"\ud83d\ude00"`, "😀")
|
||||||
|
runDecoderTestUseNumber(t, `0`, float64(0))
|
||||||
|
runDecoderTestUseNumber(t, `-0`, float64(0))
|
||||||
|
runDecoderTestUseNumber(t, `123456`, float64(123456))
|
||||||
|
runDecoderTestUseNumber(t, `-12345`, float64(-12345))
|
||||||
|
runDecoderTestUseNumber(t, `0.2`, float64(0.2))
|
||||||
|
runDecoderTestUseNumber(t, `1.2`, float64(1.2))
|
||||||
|
runDecoderTestUseNumber(t, `-0.2`, float64(-0.2))
|
||||||
|
runDecoderTestUseNumber(t, `-1.2`, float64(-1.2))
|
||||||
|
runDecoderTestUseNumber(t, `0e12`, float64(0e12))
|
||||||
|
runDecoderTestUseNumber(t, `0e+12`, float64(0e+12))
|
||||||
|
runDecoderTestUseNumber(t, `0e-12`, float64(0e-12))
|
||||||
|
runDecoderTestUseNumber(t, `-0e12`, float64(-0e12))
|
||||||
|
runDecoderTestUseNumber(t, `-0e+12`, float64(-0e+12))
|
||||||
|
runDecoderTestUseNumber(t, `-0e-12`, float64(-0e-12))
|
||||||
|
runDecoderTestUseNumber(t, `2e12`, float64(2e12))
|
||||||
|
runDecoderTestUseNumber(t, `2E12`, float64(2e12))
|
||||||
|
runDecoderTestUseNumber(t, `2e+12`, float64(2e+12))
|
||||||
|
runDecoderTestUseNumber(t, `2e-12`, float64(2e-12))
|
||||||
|
runDecoderTestUseNumber(t, `-2e12`, float64(-2e12))
|
||||||
|
runDecoderTestUseNumber(t, `-2e+12`, float64(-2e+12))
|
||||||
|
runDecoderTestUseNumber(t, `-2e-12`, float64(-2e-12))
|
||||||
|
runDecoderTestUseNumber(t, `0.2e12`, float64(0.2e12))
|
||||||
|
runDecoderTestUseNumber(t, `0.2e+12`, float64(0.2e+12))
|
||||||
|
runDecoderTestUseNumber(t, `0.2e-12`, float64(0.2e-12))
|
||||||
|
runDecoderTestUseNumber(t, `-0.2e12`, float64(-0.2e12))
|
||||||
|
runDecoderTestUseNumber(t, `-0.2e+12`, float64(-0.2e+12))
|
||||||
|
runDecoderTestUseNumber(t, `-0.2e-12`, float64(-0.2e-12))
|
||||||
|
runDecoderTestUseNumber(t, `1.2e12`, float64(1.2e12))
|
||||||
|
runDecoderTestUseNumber(t, `1.2e+12`, float64(1.2e+12))
|
||||||
|
runDecoderTestUseNumber(t, `1.2e-12`, float64(1.2e-12))
|
||||||
|
runDecoderTestUseNumber(t, `-1.2e12`, float64(-1.2e12))
|
||||||
|
runDecoderTestUseNumber(t, `-1.2e+12`, float64(-1.2e+12))
|
||||||
|
runDecoderTestUseNumber(t, `-1.2e-12`, float64(-1.2e-12))
|
||||||
|
runDecoderTestUseNumber(t, `-1.2E-12`, float64(-1.2e-12))
|
||||||
|
runDecoderTestUseNumber(t, `["asd", "123", true, false, null, 2.4, 1.2e15, 1]`, []interface{}{"asd", "123", true, false, nil, float64(2.4), float64(1.2e15), float64(1)})
|
||||||
|
runDecoderTestUseNumber(t, `{"a": "123", "b": true, "c": false, "d": null, "e": 2.4, "f": 1.2e15, "g": 1}`, map[string]interface{}{"a":"123", "b":true, "c":false, "d":nil, "e":float64(2.4), "f":float64(1.2e15), "g":float64(1)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLoads(t *testing.T) {
|
||||||
|
_,i,e := Loads(`{"a": "123", "b": true, "c": false, "d": null, "e": 2.4, "f": 1.2e15, "g": 1}`)
|
||||||
|
if e != 0 {
|
||||||
|
t.Fatal(e)
|
||||||
|
}
|
||||||
|
assert.Equal(t, map[string]interface{}{"a": "123", "b": true, "c": false, "d": nil, "e": float64(2.4), "f": float64(1.2e15), "g": float64(1)}, i)
|
||||||
|
_,i,e = LoadsUseNumber(`{"a": "123", "b": true, "c": false, "d": null, "e": 2.4, "f": 1.2e15, "g": 1}`)
|
||||||
|
if e != 0 {
|
||||||
|
t.Fatal(e)
|
||||||
|
}
|
||||||
|
assert.Equal(t, map[string]interface{}{"a": "123", "b": true, "c": false, "d": nil, "e": json.Number("2.4"), "f": json.Number("1.2e15"), "g": json.Number("1")}, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkParser_StdLib(b *testing.B) {
|
func BenchmarkParser_StdLib(b *testing.B) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import (
|
||||||
|
|
||||||
type Searcher struct {
|
type Searcher struct {
|
||||||
parser Parser
|
parser Parser
|
||||||
// cache map[string]*Node
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSearcher(str string) *Searcher {
|
func NewSearcher(str string) *Searcher {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import (
|
||||||
`testing`
|
`testing`
|
||||||
|
|
||||||
jsoniter `github.com/json-iterator/go`
|
jsoniter `github.com/json-iterator/go`
|
||||||
|
`github.com/stretchr/testify/assert`
|
||||||
`github.com/tidwall/gjson`
|
`github.com/tidwall/gjson`
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -85,6 +86,22 @@ func TestSearcher_GetByPathErr(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadIndex(t *testing.T) {
|
||||||
|
node, err := NewSearcher(`{"a":[-0, 1, -1.2, -1.2e-10]}`).GetByPath("a")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
a := node.Index(3).Float64()
|
||||||
|
assert.Equal(t, float64(-1.2e-10), a)
|
||||||
|
m := node.Array()
|
||||||
|
assert.Equal(t, m, []interface{}{
|
||||||
|
float64(0),
|
||||||
|
float64(1),
|
||||||
|
float64(-1.2),
|
||||||
|
float64(-1.2e-10),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkSearchOne_Gjson(b *testing.B) {
|
func BenchmarkSearchOne_Gjson(b *testing.B) {
|
||||||
b.SetBytes(int64(len(_TwitterJson)))
|
b.SetBytes(int64(len(_TwitterJson)))
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue