Git Product home page Git Product logo

sonic's Introduction

Sonic

English | 中文

A blazingly fast JSON serializing & deserializing library, accelerated by JIT (just-in-time compiling) and SIMD (single-instruction-multiple-data).

Requirement

  • Go 1.16~1.22
  • Linux / MacOS / Windows(need go1.17 above)
  • Amd64 ARCH

Features

  • Runtime object binding without code generation
  • Complete APIs for JSON value manipulation
  • Fast, fast, fast!

APIs

see go.dev

Benchmarks

For all sizes of json and all scenarios of usage, Sonic performs best.

  • Medium (13KB, 300+ key, 6 layers)
goversion: 1.17.1
goos: darwin
goarch: amd64
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
BenchmarkEncoder_Generic_Sonic-16                      32393 ns/op         402.40 MB/s       11965 B/op          4 allocs/op
BenchmarkEncoder_Generic_Sonic_Fast-16                 21668 ns/op         601.57 MB/s       10940 B/op          4 allocs/op
BenchmarkEncoder_Generic_JsonIter-16                   42168 ns/op         309.12 MB/s       14345 B/op        115 allocs/op
BenchmarkEncoder_Generic_GoJson-16                     65189 ns/op         199.96 MB/s       23261 B/op         16 allocs/op
BenchmarkEncoder_Generic_StdLib-16                    106322 ns/op         122.60 MB/s       49136 B/op        789 allocs/op
BenchmarkEncoder_Binding_Sonic-16                       6269 ns/op        2079.26 MB/s       14173 B/op          4 allocs/op
BenchmarkEncoder_Binding_Sonic_Fast-16                  5281 ns/op        2468.16 MB/s       12322 B/op          4 allocs/op
BenchmarkEncoder_Binding_JsonIter-16                   20056 ns/op         649.93 MB/s        9488 B/op          2 allocs/op
BenchmarkEncoder_Binding_GoJson-16                      8311 ns/op        1568.32 MB/s        9481 B/op          1 allocs/op
BenchmarkEncoder_Binding_StdLib-16                     16448 ns/op         792.52 MB/s        9479 B/op          1 allocs/op
BenchmarkEncoder_Parallel_Generic_Sonic-16              6681 ns/op        1950.93 MB/s       12738 B/op          4 allocs/op
BenchmarkEncoder_Parallel_Generic_Sonic_Fast-16         4179 ns/op        3118.99 MB/s       10757 B/op          4 allocs/op
BenchmarkEncoder_Parallel_Generic_JsonIter-16           9861 ns/op        1321.84 MB/s       14362 B/op        115 allocs/op
BenchmarkEncoder_Parallel_Generic_GoJson-16            18850 ns/op         691.52 MB/s       23278 B/op         16 allocs/op
BenchmarkEncoder_Parallel_Generic_StdLib-16            45902 ns/op         283.97 MB/s       49174 B/op        789 allocs/op
BenchmarkEncoder_Parallel_Binding_Sonic-16              1480 ns/op        8810.09 MB/s       13049 B/op          4 allocs/op
BenchmarkEncoder_Parallel_Binding_Sonic_Fast-16         1209 ns/op        10785.23 MB/s      11546 B/op          4 allocs/op
BenchmarkEncoder_Parallel_Binding_JsonIter-16           6170 ns/op        2112.58 MB/s        9504 B/op          2 allocs/op
BenchmarkEncoder_Parallel_Binding_GoJson-16             3321 ns/op        3925.52 MB/s        9496 B/op          1 allocs/op
BenchmarkEncoder_Parallel_Binding_StdLib-16             3739 ns/op        3486.49 MB/s        9480 B/op          1 allocs/op

BenchmarkDecoder_Generic_Sonic-16                      66812 ns/op         195.10 MB/s       57602 B/op        723 allocs/op
BenchmarkDecoder_Generic_Sonic_Fast-16                 54523 ns/op         239.07 MB/s       49786 B/op        313 allocs/op
BenchmarkDecoder_Generic_StdLib-16                    124260 ns/op         104.90 MB/s       50869 B/op        772 allocs/op
BenchmarkDecoder_Generic_JsonIter-16                   91274 ns/op         142.81 MB/s       55782 B/op       1068 allocs/op
BenchmarkDecoder_Generic_GoJson-16                     88569 ns/op         147.17 MB/s       66367 B/op        973 allocs/op
BenchmarkDecoder_Binding_Sonic-16                      32557 ns/op         400.38 MB/s       28302 B/op        137 allocs/op
BenchmarkDecoder_Binding_Sonic_Fast-16                 28649 ns/op         455.00 MB/s       24999 B/op         34 allocs/op
BenchmarkDecoder_Binding_StdLib-16                    111437 ns/op         116.97 MB/s       10576 B/op        208 allocs/op
BenchmarkDecoder_Binding_JsonIter-16                   35090 ns/op         371.48 MB/s       14673 B/op        385 allocs/op
BenchmarkDecoder_Binding_GoJson-16                     28738 ns/op         453.59 MB/s       22039 B/op         49 allocs/op
BenchmarkDecoder_Parallel_Generic_Sonic-16             12321 ns/op        1057.91 MB/s       57233 B/op        723 allocs/op
BenchmarkDecoder_Parallel_Generic_Sonic_Fast-16        10644 ns/op        1224.64 MB/s       49362 B/op        313 allocs/op
BenchmarkDecoder_Parallel_Generic_StdLib-16            57587 ns/op         226.35 MB/s       50874 B/op        772 allocs/op
BenchmarkDecoder_Parallel_Generic_JsonIter-16          38666 ns/op         337.12 MB/s       55789 B/op       1068 allocs/op
BenchmarkDecoder_Parallel_Generic_GoJson-16            30259 ns/op         430.79 MB/s       66370 B/op        974 allocs/op
BenchmarkDecoder_Parallel_Binding_Sonic-16              5965 ns/op        2185.28 MB/s       27747 B/op        137 allocs/op
BenchmarkDecoder_Parallel_Binding_Sonic_Fast-16         5170 ns/op        2521.31 MB/s       24715 B/op         34 allocs/op
BenchmarkDecoder_Parallel_Binding_StdLib-16            27582 ns/op         472.58 MB/s       10576 B/op        208 allocs/op
BenchmarkDecoder_Parallel_Binding_JsonIter-16          13571 ns/op         960.51 MB/s       14685 B/op        385 allocs/op
BenchmarkDecoder_Parallel_Binding_GoJson-16            10031 ns/op        1299.51 MB/s       22111 B/op         49 allocs/op

BenchmarkGetOne_Sonic-16                                3276 ns/op        3975.78 MB/s          24 B/op          1 allocs/op
BenchmarkGetOne_Gjson-16                                9431 ns/op        1380.81 MB/s           0 B/op          0 allocs/op
BenchmarkGetOne_Jsoniter-16                            51178 ns/op         254.46 MB/s       27936 B/op        647 allocs/op
BenchmarkGetOne_Parallel_Sonic-16                      216.7 ns/op       60098.95 MB/s          24 B/op          1 allocs/op
BenchmarkGetOne_Parallel_Gjson-16                       1076 ns/op        12098.62 MB/s          0 B/op          0 allocs/op
BenchmarkGetOne_Parallel_Jsoniter-16                   17741 ns/op         734.06 MB/s       27945 B/op        647 allocs/op
BenchmarkSetOne_Sonic-16                               9571 ns/op         1360.61 MB/s        1584 B/op         17 allocs/op
BenchmarkSetOne_Sjson-16                               36456 ns/op         357.22 MB/s       52180 B/op          9 allocs/op
BenchmarkSetOne_Jsoniter-16                            79475 ns/op         163.86 MB/s       45862 B/op        964 allocs/op
BenchmarkSetOne_Parallel_Sonic-16                      850.9 ns/op       15305.31 MB/s        1584 B/op         17 allocs/op
BenchmarkSetOne_Parallel_Sjson-16                      18194 ns/op         715.77 MB/s       52247 B/op          9 allocs/op
BenchmarkSetOne_Parallel_Jsoniter-16                   33560 ns/op         388.05 MB/s       45892 B/op        964 allocs/op
BenchmarkLoadNode/LoadAll()-16                         11384 ns/op        1143.93 MB/s        6307 B/op         25 allocs/op
BenchmarkLoadNode_Parallel/LoadAll()-16                 5493 ns/op        2370.68 MB/s        7145 B/op         25 allocs/op
BenchmarkLoadNode/Interface()-16                       17722 ns/op         734.85 MB/s       13323 B/op         88 allocs/op
BenchmarkLoadNode_Parallel/Interface()-16              10330 ns/op        1260.70 MB/s       15178 B/op         88 allocs/op
  • Small (400B, 11 keys, 3 layers) small benchmarks
  • Large (635KB, 10000+ key, 6 layers) large benchmarks

See bench.sh for benchmark codes.

How it works

See INTRODUCTION.md.

Usage

Marshal/Unmarshal

Default behaviors are mostly consistent with encoding/json, except HTML escaping form (see Escape HTML) and SortKeys feature (optional support see Sort Keys) that is NOT in conformity to RFC8259.

import "github.com/bytedance/sonic"

var data YourSchema
// Marshal
output, err := sonic.Marshal(&data)
// Unmarshal
err := sonic.Unmarshal(output, &data)

Streaming IO

Sonic supports decoding json from io.Reader or encoding objects into io.Writer, aims at handling multiple values as well as reducing memory consumption.

  • encoder
var o1 = map[string]interface{}{
    "a": "b",
}
var o2 = 1
var w = bytes.NewBuffer(nil)
var enc = sonic.ConfigDefault.NewEncoder(w)
enc.Encode(o1)
enc.Encode(o2)
fmt.Println(w.String())
// Output:
// {"a":"b"}
// 1
  • decoder
var o =  map[string]interface{}{}
var r = strings.NewReader(`{"a":"b"}{"1":"2"}`)
var dec = sonic.ConfigDefault.NewDecoder(r)
dec.Decode(&o)
dec.Decode(&o)
fmt.Printf("%+v", o)
// Output:
// map[1:2 a:b]

Use Number/Use Int64

import "github.com/bytedance/sonic/decoder"

var input = `1`
var data interface{}

// default float64
dc := decoder.NewDecoder(input)
dc.Decode(&data) // data == float64(1)
// use json.Number
dc = decoder.NewDecoder(input)
dc.UseNumber()
dc.Decode(&data) // data == json.Number("1")
// use int64
dc = decoder.NewDecoder(input)
dc.UseInt64()
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

Sort Keys

On account of the performance loss from sorting (roughly 10%), sonic doesn't enable this feature by default. If your component depends on it to work (like zstd), Use it like this:

import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/encoder"

// Binding map only
m := map[string]interface{}{}
v, err := encoder.Encode(m, encoder.SortMapKeys)

// Or ast.Node.SortKeys() before marshal
var root := sonic.Get(JSON)
err := root.SortKeys()

Escape HTML

On account of the performance loss (roughly 15%), sonic doesn't enable this feature by default. You can use encoder.EscapeHTML option to open this feature (align with encoding/json.HTMLEscape).

import "github.com/bytedance/sonic"

v := map[string]string{"&&":"<>"}
ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"}}`

Compact Format

Sonic encodes primitive objects (struct/map...) as compact-format JSON by default, except marshaling json.RawMessage or json.Marshaler: sonic ensures validating their output JSON but DONOT compacting them for performance concerns. We provide the option encoder.CompactMarshaler to add compacting process.

Print Error

If there invalid syntax in input JSON, sonic will return decoder.SyntaxError, which supports pretty-printing of error position

import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/decoder"

var data interface{}
err := sonic.UnmarshalString("[[[}]]", &data)
if err != nil {
    /* One line by default */
    println(e.Error()) // "Syntax error at index 3: invalid char\n\n\t[[[}]]\n\t...^..\n"
    /* Pretty print */
    if e, ok := err.(decoder.SyntaxError); ok {
        /*Syntax error at index 3: invalid char

            [[[}]]
            ...^..
        */
        print(e.Description())
    } else if me, ok := err.(*decoder.MismatchTypeError); ok {
        // decoder.MismatchTypeError is new to Sonic v1.6.0
        print(me.Description())
    }
}

Mismatched Types [Sonic v1.6.0]

If there a mismatch-typed value for a given key, sonic will report decoder.MismatchTypeError (if there are many, report the last one), but still skip wrong the value and keep decoding next JSON.

import "github.com/bytedance/sonic"
import "github.com/bytedance/sonic/decoder"

var data = struct{
    A int
    B int
}{}
err := UnmarshalString(`{"A":"1","B":1}`, &data)
println(err.Error())    // Mismatch type int with value string "at index 5: mismatched type with value\n\n\t{\"A\":\"1\",\"B\":1}\n\t.....^.........\n"
fmt.Printf("%+v", data) // {A:0 B:1}

Ast.Node

Sonic/ast.Node is a completely self-contained AST for JSON. It implements serialization and deserialization both and provides robust APIs for obtaining and modification of generic data.

Get/Index

Search partial JSON by given paths, which must be non-negative integer or string, or nil

import "github.com/bytedance/sonic"

input := []byte(`{"key1":[{},{"key2":{"key3":[1,2,3]}}]}`)

// no path, returns entire json
root, err := sonic.Get(input)
raw := root.Raw() // == string(input)

// multiple paths
root, err := sonic.Get(input, "key1", 1, "key2")
sub := root.Get("key3").Index(2).Int64() // == 3

Tip: since Index() uses offset to locate data, which is much faster than scanning like Get(), we suggest you use it as much as possible. And sonic also provides another API IndexOrGet() to underlying use offset as well as ensure the key is matched.

Set/Unset

Modify the json content by Set()/Unset()

import "github.com/bytedance/sonic"

// Set
exist, err := root.Set("key4", NewBool(true)) // exist == false
alias1 := root.Get("key4")
println(alias1.Valid()) // true
alias2 := root.Index(1)
println(alias1 == alias2) // true

// Unset
exist, err := root.UnsetByIndex(1) // exist == true
println(root.Get("key4").Check()) // "value not exist"

Serialize

To encode ast.Node as json, use MarshalJson() or json.Marshal() (MUST pass the node's pointer)

import (
    "encoding/json"
    "github.com/bytedance/sonic"
)

buf, err := root.MarshalJson()
println(string(buf))                // {"key1":[{},{"key2":{"key3":[1,2,3]}}]}
exp, err := json.Marshal(&root)     // WARN: use pointer
println(string(buf) == string(exp)) // true

APIs

  • validation: Check(), Error(), Valid(), Exist()
  • searching: Index(), Get(), IndexPair(), IndexOrGet(), GetByPath()
  • go-type casting: Int64(), Float64(), String(), Number(), Bool(), Map[UseNumber|UseNode](), Array[UseNumber|UseNode](), Interface[UseNumber|UseNode]()
  • go-type packing: NewRaw(), NewNumber(), NewNull(), NewBool(), NewString(), NewObject(), NewArray()
  • iteration: Values(), Properties(), ForEach(), SortKeys()
  • modification: Set(), SetByIndex(), Add()

Ast.Visitor

Sonic provides an advanced API for fully parsing JSON into non-standard types (neither struct not map[string]interface{}) without using any intermediate representation (ast.Node or interface{}). For example, you might have the following types which are like interface{} but actually not interface{}:

type UserNode interface {}

// the following types implement the UserNode interface.
type (
    UserNull    struct{}
    UserBool    struct{ Value bool }
    UserInt64   struct{ Value int64 }
    UserFloat64 struct{ Value float64 }
    UserString  struct{ Value string }
    UserObject  struct{ Value map[string]UserNode }
    UserArray   struct{ Value []UserNode }
)

Sonic provides the following API to return the preorder traversal of a JSON AST. The ast.Visitor is a SAX style interface which is used in some C++ JSON library. You should implement ast.Visitor by yourself and pass it to ast.Preorder() method. In your visitor you can make your custom types to represent JSON values. There may be an O(n) space container (such as stack) in your visitor to record the object / array hierarchy.

func Preorder(str string, visitor Visitor, opts *VisitorOptions) error

type Visitor interface {
    OnNull() error
    OnBool(v bool) error
    OnString(v string) error
    OnInt64(v int64, n json.Number) error
    OnFloat64(v float64, n json.Number) error
    OnObjectBegin(capacity int) error
    OnObjectKey(key string) error
    OnObjectEnd() error
    OnArrayBegin(capacity int) error
    OnArrayEnd() error
}

See ast/visitor.go for detailed usage. We also implement a demo visitor for UserNode in ast/visitor_test.go.

Compatibility

Sonic DOES NOT ensure to support all environments, due to the difficulty of developing high-performance codes. For developers who use sonic to build their applications in different environments, we have the following suggestions:

  • Developing on Mac M1: Make sure you have Rosetta 2 installed on your machine, and set GOARCH=amd64 when building your application. Rosetta 2 can automatically translate x86 binaries to arm64 binaries and run x86 applications on Mac M1.
  • Developing on Linux arm64: You can install qemu and use the qemu-x86_64 -cpu max command to convert x86 binaries to amr64 binaries for applications built with sonic. The qemu can achieve a similar transfer effect to Rosetta 2 on Mac M1.

For developers who want to use sonic on Linux arm64 without qemu, or those who want to handle JSON strictly consistent with encoding/json, we provide some compatible APIs as sonic.API

  • ConfigDefault: the sonic's default config (EscapeHTML=false,SortKeys=false...) to run on sonic-supporting environment. It will fall back to encoding/json with the corresponding config, and some options like SortKeys=false will be invalid.
  • ConfigStd: the std-compatible config (EscapeHTML=true,SortKeys=true...) to run on sonic-supporting environment. It will fall back to encoding/json.
  • ConfigFastest: the fastest config (NoQuoteTextMarshaler=true) to run on sonic-supporting environment. It will fall back to encoding/json with the corresponding config, and some options will be invalid.

Tips

Pretouch

Since Sonic uses golang-asm as a JIT assembler, which is NOT very suitable for runtime compiling, first-hit running of a huge schema may cause request-timeout or even process-OOM. For better stability, we advise using Pretouch() for huge-schema or compact-memory applications before Marshal()/Unmarshal().

import (
    "reflect"
    "github.com/bytedance/sonic"
    "github.com/bytedance/sonic/option"
)

func init() {
    var v HugeStruct

    // For most large types (nesting depth <= option.DefaultMaxInlineDepth)
    err := sonic.Pretouch(reflect.TypeOf(v))

    // with more CompileOption...
    err := sonic.Pretouch(reflect.TypeOf(v),
        // If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth),
        // you can set compile recursive loops in Pretouch for better stability in JIT.
        option.WithCompileRecursiveDepth(loop),
        // For a large nested struct, try to set a smaller depth to reduce compiling time.
        option.WithCompileMaxInlineDepth(depth),
    )
}

Copy string

When decoding string values without any escaped characters, sonic references them from the origin JSON buffer instead of mallocing a new buffer to copy. This helps a lot for CPU performance but may leave the whole JSON buffer in memory as long as the decoded objects are being used. In practice, we found the extra memory introduced by referring JSON buffer is usually 20% ~ 80% of decoded objects. Once an application holds these objects for a long time (for example, cache the decoded objects for reusing), its in-use memory on the server may go up. - Config.CopyString/decoder.CopyString(): We provide the option for Decode() / Unmarshal() users to choose not to reference the JSON buffer, which may cause a decline in CPU performance to some degree.

  • GetFromStringNoCopy(): For memory safety, sonic.Get() / sonic.GetFromString() now copies return JSON. If users want to get json more quickly and not care about memory usage, you can use GetFromStringNoCopy() to return a JSON directly referenced from source.

Pass string or []byte?

For alignment to encoding/json, we provide API to pass []byte as an argument, but the string-to-bytes copy is conducted at the same time considering safety, which may lose performance when the origin JSON is huge. Therefore, you can use UnmarshalString() and GetFromString() to pass a string, as long as your origin data is a string or nocopy-cast is safe for your []byte. We also provide API MarshalString() for convenient nocopy-cast of encoded JSON []byte, which is safe since sonic's output bytes is always duplicated and unique.

Accelerate encoding.TextMarshaler

To ensure data security, sonic.Encoder quotes and escapes string values from encoding.TextMarshaler interfaces by default, which may degrade performance much if most of your data is in form of them. We provide encoder.NoQuoteTextMarshaler to skip these operations, which means you MUST ensure their output string escaped and quoted following RFC8259.

Better performance for generic data

In fully-parsed scenario, Unmarshal() performs better than Get()+Node.Interface(). But if you only have a part of the schema for specific json, you can combine Get() and Unmarshal() together:

import "github.com/bytedance/sonic"

node, err := sonic.GetFromString(_TwitterJson, "statuses", 3, "user")
var user User // your partial schema...
err = sonic.UnmarshalString(node.Raw(), &user)

Even if you don't have any schema, use ast.Node as the container of generic values instead of map or interface:

import "github.com/bytedance/sonic"

root, err := sonic.GetFromString(_TwitterJson)
user := root.GetByPath("statuses", 3, "user")  // === root.Get("status").Index(3).Get("user")
err = user.Check()

// err = user.LoadAll() // only call this when you want to use 'user' concurrently...
go someFunc(user)

Why? Because ast.Node stores its children using array:

  • Array's performance is much better than Map when Inserting (Deserialize) and Scanning (Serialize) data;
  • Hashing (map[x]) is not as efficient as Indexing (array[x]), which ast.Node can conduct on both array and object;
  • Using Interface()/Map() means Sonic must parse all the underlying values, while ast.Node can parse them on demand.

CAUTION: ast.Node DOESN'T ensure concurrent security directly, due to its lazy-load design. However, you can call Node.Load()/Node.LoadAll() to achieve that, which may bring performance reduction while it still works faster than converting to map or interface{}

Ast.Node or Ast.Visitor?

For generic data, ast.Node should be enough for your needs in most cases.

However, ast.Node is designed for partially processing JSON string. It has some special designs such as lazy-load which might not be suitable for directly parsing the whole JSON string like Unmarshal(). Although ast.Node is better then map or interface{}, it's also a kind of intermediate representation after all if your final types are customized and you have to convert the above types to your custom types after parsing.

For better performance, in previous case the ast.Visitor will be the better choice. It performs JSON decoding like Unmarshal() and you can directly use your final types to represents a JSON AST without any intermediate representations.

But ast.Visitor is not a very handy API. You might need to write a lot of code to implement your visitor and carefully maintain the tree hierarchy during decoding. Please read the comments in ast/visitor.go carefully if you decide to use this API.

Community

Sonic is a subproject of CloudWeGo. We are committed to building a cloud native ecosystem.

sonic's People

Contributors

alexandermahone0 avatar ancientmodern avatar andeya avatar asterdy avatar baijinping avatar bisstocuz avatar chenzhuoyu avatar crazyharb avatar cyn6315 avatar dorapower avatar elee1766 avatar erin-liman-tiktok avatar felix021 avatar goccy avatar hitzhangjie avatar ii64 avatar jimyag avatar linuxsong avatar liuq19 avatar pengqiseven avatar purewhitewu avatar testwill avatar xiekeyi98 avatar xiezheng-xd avatar xumingyukou avatar zhongxinghong avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sonic's Issues

Behaves differently from stdlib in map encoding

The default behavior of encoding/json is to sort the map before encoding. This behavior has a significant performance impact and you should implement this feature before comparing it to other libraries. Otherwise, the result will be unfair.

Reproducible code

package main

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/bytedance/sonic"
)

func main() {
	v := map[string]int{
		"a": 1,
		"b": 2,
		"c": 3,
		"d": 4,
	}
	for i := 0; i < 100; i++ {
		expected, err := json.Marshal(v)
		if err != nil {
			panic(err)
		}
		actual, err := sonic.Marshal(v)
		if err != nil {
			panic(err)
		}
		if !bytes.Equal(expected, actual) {
			fmt.Println("failed to encode map")
			fmt.Println("expected:", string(expected))
			fmt.Println("got     :", string(actual))
			break
		}
	}
}

not parsing the UTF-16 surrogate pair after invalid unicode

code:

func TestIssue107_UnmarshalUTF16SurrogatePairAfterInvalidUnicode(t *testing.T) {
    var obj string
    err := Unmarshal([]byte(`"\uDA51\uD83D\uDE04"`), &obj)
    require.NoError(t, err)
    require.Equal(t, obj, "\ufffd" + `😄`)
}

output:

                Error:          Not equal: 
                                expected: "���"
                                actual  : "�😄"
                            
                                Diff:
                                --- Expected
                                +++ Actual
                                @@ -1 +1 @@
                                -���
                                +�😄
                Test:           TestIssue107_UnmarshalUTF16SurrogatePairAfterInvalidUnicode

runtime: goroutine stack exceeds 1000000000-byte limit

func TestGoroutineStackExceeds(t *testing.T) {
	var v interface{}
	err := Unmarshal([]byte(`{"a":`+strings.Repeat(`[`, 3000000)+strings.Repeat(`]`, 3000000)+`}`), &v)
	require.Nil(t, err)
}

=== RUN   TestGoroutineStackExceeds
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc02138c3a8 stack=[0xc02138c000, 0xc04138c000]
fatal error: stack overflow

runtime stack:
runtime.throw(0x142f564, 0xe)
	/usr/local/Cellar/go/1.15.2/libexec/src/runtime/panic.go:1116 +0x72
runtime.newstack()
	/usr/local/Cellar/go/1.15.2/libexec/src/runtime/stack.go:1060 +0x78d
runtime.morestack()
	/usr/local/Cellar/go/1.15.2/libexec/src/runtime/asm_amd64.s:449 +0x8f

goroutine 19 [running]:
runtime.mallocgc(0x100, 0x13bf520, 0x1, 0xaaaaaaaaaaaaaaaa)
	/usr/local/Cellar/go/1.15.2/libexec/src/runtime/malloc.go:903 +0xa8b fp=0xc02138c3b8 sp=0xc02138c3b0 pc=0x100fc8b
runtime.makeslice(0x13bf520, 0x0, 0x10, 0x0)
	/usr/local/Cellar/go/1.15.2/libexec/src/runtime/slice.go:98 +0x6c fp=0xc02138c3e8 sp=0xc02138c3b8 pc=0x104f82c
github.com/bytedance/sonic/decoder.decodeArray(0x100)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:134 +0xc6 fp=0xc02138c488 sp=0xc02138c3e8 pc=0x125f606
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138c528 sp=0xc02138c488 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a665300)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138c5c8 sp=0xc02138c528 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138c668 sp=0xc02138c5c8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a665200)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138c708 sp=0xc02138c668 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138c7a8 sp=0xc02138c708 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a665100)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138c848 sp=0xc02138c7a8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138c8e8 sp=0xc02138c848 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a665000)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138c988 sp=0xc02138c8e8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138ca28 sp=0xc02138c988 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664f00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138cac8 sp=0xc02138ca28 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138cb68 sp=0xc02138cac8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664e00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138cc08 sp=0xc02138cb68 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138cca8 sp=0xc02138cc08 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664d00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138cd48 sp=0xc02138cca8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138cde8 sp=0xc02138cd48 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664c00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138ce88 sp=0xc02138cde8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138cf28 sp=0xc02138ce88 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664b00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138cfc8 sp=0xc02138cf28 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138d068 sp=0xc02138cfc8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664a00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138d108 sp=0xc02138d068 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138d1a8 sp=0xc02138d108 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664900)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138d248 sp=0xc02138d1a8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138d2e8 sp=0xc02138d248 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664800)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138d388 sp=0xc02138d2e8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138d428 sp=0xc02138d388 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664700)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138d4c8 sp=0xc02138d428 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138d568 sp=0xc02138d4c8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664600)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138d608 sp=0xc02138d568 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138d6a8 sp=0xc02138d608 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664500)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138d748 sp=0xc02138d6a8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138d7e8 sp=0xc02138d748 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664400)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138d888 sp=0xc02138d7e8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138d928 sp=0xc02138d888 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664300)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138d9c8 sp=0xc02138d928 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138da68 sp=0xc02138d9c8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664200)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138db08 sp=0xc02138da68 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138dba8 sp=0xc02138db08 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664100)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138dc48 sp=0xc02138dba8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138dce8 sp=0xc02138dc48 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a664000)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138dd88 sp=0xc02138dce8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138de28 sp=0xc02138dd88 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663f00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138dec8 sp=0xc02138de28 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138df68 sp=0xc02138dec8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663e00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138e008 sp=0xc02138df68 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138e0a8 sp=0xc02138e008 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663d00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138e148 sp=0xc02138e0a8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138e1e8 sp=0xc02138e148 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663c00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138e288 sp=0xc02138e1e8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138e328 sp=0xc02138e288 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663b00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138e3c8 sp=0xc02138e328 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138e468 sp=0xc02138e3c8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663a00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138e508 sp=0xc02138e468 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138e5a8 sp=0xc02138e508 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663900)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138e648 sp=0xc02138e5a8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138e6e8 sp=0xc02138e648 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663800)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138e788 sp=0xc02138e6e8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138e828 sp=0xc02138e788 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663700)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138e8c8 sp=0xc02138e828 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138e968 sp=0xc02138e8c8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663600)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138ea08 sp=0xc02138e968 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138eaa8 sp=0xc02138ea08 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663500)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138eb48 sp=0xc02138eaa8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138ebe8 sp=0xc02138eb48 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663400)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138ec88 sp=0xc02138ebe8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138ed28 sp=0xc02138ec88 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663300)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138edc8 sp=0xc02138ed28 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138ee68 sp=0xc02138edc8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663200)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138ef08 sp=0xc02138ee68 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138efa8 sp=0xc02138ef08 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663100)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138f048 sp=0xc02138efa8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138f0e8 sp=0xc02138f048 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a663000)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138f188 sp=0xc02138f0e8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138f228 sp=0xc02138f188 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662f00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138f2c8 sp=0xc02138f228 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138f368 sp=0xc02138f2c8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662e00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138f408 sp=0xc02138f368 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138f4a8 sp=0xc02138f408 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662d00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138f548 sp=0xc02138f4a8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138f5e8 sp=0xc02138f548 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662c00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138f688 sp=0xc02138f5e8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138f728 sp=0xc02138f688 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662b00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138f7c8 sp=0xc02138f728 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138f868 sp=0xc02138f7c8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662a00)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138f908 sp=0xc02138f868 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138f9a8 sp=0xc02138f908 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662900)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138fa48 sp=0xc02138f9a8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138fae8 sp=0xc02138fa48 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662800)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138fb88 sp=0xc02138fae8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138fc28 sp=0xc02138fb88 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662700)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138fcc8 sp=0xc02138fc28 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138fd68 sp=0xc02138fcc8 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662600)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138fe08 sp=0xc02138fd68 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138fea8 sp=0xc02138fe08 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662500)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc02138ff48 sp=0xc02138fea8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc02138ffe8 sp=0xc02138ff48 pc=0x17f50a2
github.com/bytedance/sonic/decoder.decodeArray(0xc01a662400)
	/Users/wkc/Downloads/sonic/decoder/generic_amd64.s:147 +0xfa fp=0xc021390088 sp=0xc02138ffe8 pc=0x125f63a
__decode_value_17f5000()
	?:0 +0xa2 fp=0xc021390128 sp=0xc021390088 pc=0x17f50a2
...additional frames elided...
created by testing.(*T).Run
	/usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1178 +0x386

goroutine 1 [chan receive]:
testing.(*T).Run(0xc000082a80, 0x1435bea, 0x19, 0x144b578, 0x108b901)
	/usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1179 +0x3ad
testing.runTests.func1(0xc000082900)
	/usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1449 +0x78
testing.tRunner(0xc000082900, 0xc0000d5de0)
	/usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1127 +0xef
testing.runTests(0xc0001cfaa0, 0x16918e0, 0x87, 0x87, 0xc02538cc519f3ee8, 0x8bb2e696ee, 0x16d0860, 0x100f570)
	/usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1447 +0x2e8
testing.(*M).Run(0xc0000ec600, 0x0)
	/usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1357 +0x245
main.main()
	_testmain.go:323 +0x138
exit status 2
FAIL	github.com/bytedance/sonic	1.647s

return error when skip exponent with zero base

code:

type skipNumberTest struct {
}

func TestSkipExponentWithZeroBase(t *testing.T) {
    var obj skipNumberTest
    err := Unmarshal([]byte(`{"X":0e0}`), &obj)
    require.NoError(t, err)
}

output:

        	Error:      	Received unexpected error:
        	            	Syntax error at index 6: invalid char
        	Test:       	SkipExponentWithZeroBase

bug: go test panic in /encoder and /decoder

overview

The unit test panicked in /encoder and /decoder,
saying :

# github.com/goccy/go-json/internal/encoder/vm 
fatal error: runtime: out of memory

go version

go version go1.16 linux/amd64

go env

GO111MODULE=""
GOARCH="amd64"
GOBIN="/home/liwm29/gopath/bin"
GOCACHE="/home/liwm29/.cache/go-build"
GOENV="/home/liwm29/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/liwm29/gopath/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/liwm29/gopath"
GOPRIVATE=""
GOPROXY="https://goproxy.cn,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.16"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/liwm29/sonic_raw/sonic/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1917513849=/tmp/go-build -gno-record-gcc-switches"

what happened

[liwm29@VM-12-11-centos sonic]$ go test ./...
ok      github.com/bytedance/sonic      4.103s
ok      github.com/bytedance/sonic/ast  0.006s
# github.com/goccy/go-json/internal/encoder/vm
fatal error: runtime: out of memory

runtime stack:
runtime.throw(0xcac67f, 0x16)
        /usr/local/go/src/runtime/panic.go:1117 +0x72
runtime.sysMap(0xc030000000, 0xc000000, 0x11e7b90)
        /usr/local/go/src/runtime/mem_linux.go:169 +0xc6
runtime.(*mheap).sysAlloc(0x11c8bc0, 0xa000000, 0x42bcf7, 0x11c8bc8)
        /usr/local/go/src/runtime/malloc.go:729 +0x1e5
runtime.(*mheap).grow(0x11c8bc0, 0x4ef7, 0x0)
        /usr/local/go/src/runtime/mheap.go:1346 +0x85
runtime.(*mheap).allocSpan(0x11c8bc0, 0x4ef7, 0x440100, 0x7f1fa3d21688)
        /usr/local/go/src/runtime/mheap.go:1173 +0x609
runtime.(*mheap).alloc.func1()
        /usr/local/go/src/runtime/mheap.go:910 +0x59
runtime.systemstack(0x46d334)
        /usr/local/go/src/runtime/asm_amd64.s:379 +0x66
runtime.mstart()
        /usr/local/go/src/runtime/proc.go:1246

goroutine 1 [running]:
runtime.systemstack_switch()
        /usr/local/go/src/runtime/asm_amd64.s:339 fp=0xc00286cac0 sp=0xc00286cab8 pc=0x46d460
runtime.(*mheap).alloc(0x11c8bc0, 0x4ef7, 0xc002860001, 0xc00458c000)
        /usr/local/go/src/runtime/mheap.go:904 +0x85 fp=0xc00286cb10 sp=0xc00286cac0 pc=0x4279a5
runtime.(*mcache).allocLarge(0x7f1fcac23108, 0x9dee000, 0xc002be0100, 0x7f1fa3d21688)
        /usr/local/go/src/runtime/mcache.go:224 +0x97 fp=0xc00286cb68 sp=0xc00286cb10 pc=0x418017
runtime.mallocgc(0x9dee000, 0x0, 0x0, 0xc002be2000)
        /usr/local/go/src/runtime/malloc.go:1078 +0x925 fp=0xc00286cbf0 sp=0xc00286cb68 pc=0x40de65
runtime.growslice(0xc686a0, 0xc025b74000, 0x7e5800, 0x7e5800, 0x7e5801, 0x1122970, 0x1, 0xc0)
        /usr/local/go/src/runtime/slice.go:224 +0x154 fp=0xc00286cc58 sp=0xc00286cbf0 pc=0x44fcd4
cmd/compile/internal/ssa.(*debugState).appendLiveSlot(...)
        /usr/local/go/src/cmd/compile/internal/ssa/debug.go:290
cmd/compile/internal/ssa.(*debugState).liveness(0xc001ccd660, 0xc0014b33b0, 0xc0032bb030, 0xc0047a58d8)
        /usr/local/go/src/cmd/compile/internal/ssa/debug.go:512 +0x7e5 fp=0xc00286ce58 sp=0xc00286cc58 pc=0x6705a5
cmd/compile/internal/ssa.BuildFuncDebug(0xc0000fa000, 0xc0018c51e0, 0xc0041be300, 0xcc81f8, 0x6a9)
        /usr/local/go/src/cmd/compile/internal/ssa/debug.go:436 +0xa65 fp=0xc00286d0d0 sp=0xc00286ce58 pc=0x66f345
cmd/compile/internal/gc.genssa(0xc0018c51e0, 0xc004c62000)
        /usr/local/go/src/cmd/compile/internal/gc/ssa.go:6527 +0x28ac fp=0xc00286d5b0 sp=0xc00286d0d0 pc=0xb437ec
cmd/compile/internal/gc.compileSSA(0xc0000e5e40, 0x0)
        /usr/local/go/src/cmd/compile/internal/gc/pgen.go:329 +0x3a5 fp=0xc00286d690 sp=0xc00286d5b0 pc=0xad7685
cmd/compile/internal/gc.compile(0xc0000e5e40)
        /usr/local/go/src/cmd/compile/internal/gc/pgen.go:277 +0x39e fp=0xc00286d708 sp=0xc00286d690 pc=0xad711e
cmd/compile/internal/gc.funccompile(0xc0000e5e40)
        /usr/local/go/src/cmd/compile/internal/gc/pgen.go:220 +0xc5 fp=0xc00286d760 sp=0xc00286d708 pc=0xad6c65
cmd/compile/internal/gc.Main(0xcc7d20)
        /usr/local/go/src/cmd/compile/internal/gc/main.go:762 +0x3525 fp=0xc00286df10 sp=0xc00286d760 pc=0xaabbe5
main.main()
        /usr/local/go/src/cmd/compile/main.go:52 +0xb1 fp=0xc00286df88 sp=0xc00286df10 pc=0xbf90d1
runtime.main()
        /usr/local/go/src/runtime/proc.go:225 +0x256 fp=0xc00286dfe0 sp=0xc00286df88 pc=0x439d76
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1371 +0x1 fp=0xc00286dfe8 sp=0xc00286dfe0 pc=0x46f161
FAIL    github.com/bytedance/sonic/decoder [build failed]
FAIL    github.com/bytedance/sonic/encoder [build failed]
ok      github.com/bytedance/sonic/internal/caching     0.002s
?       github.com/bytedance/sonic/internal/cpu [no test files]
ok      github.com/bytedance/sonic/internal/jit 0.004s
ok      github.com/bytedance/sonic/internal/loader      0.003s
?       github.com/bytedance/sonic/internal/native      [no test files]
ok      github.com/bytedance/sonic/internal/native/avx  0.006s
ok      github.com/bytedance/sonic/internal/native/avx2 0.006s
?       github.com/bytedance/sonic/internal/native/types        [no test files]
ok      github.com/bytedance/sonic/internal/resolver    0.002s
?       github.com/bytedance/sonic/internal/rt  [no test files]
?       github.com/bytedance/sonic/unquote      [no test files]
FAIL

other

i have changed nothing, except cloning and cd /sonic and run go test ./...

如何将Node类型再转换成json string

a := {"name":"sam","info":{"age":10,"high":"x"}}
obj, _ = sonic.GetFromString(a)
obj_info := obj.GetByPath("info")
new_high := "y"
new_node, _ := sonic.GetFromString(new_high)
obj_info.Set("high", new_node)

//fmt.Println(obj)

我的使用场景是将json里面的info.high的指改了,然后现在要获取修改后的json格式的字符串,请问要怎么获取?

Large map value may get missing when deserializing and rehasing

Large map value whose width are more than 128B may cause problem when deserializing map which has more than 9 elements

  • code
func TestLargeMapValue(t *testing.T) {
    var stdw, sonicw map[string]LargeMapValue
	if err := stdjson.Unmarshal([]byte(jsonStr), &stdw); err != nil {
        t.Fatal(err)
    }
    fmt.Printf("struct  size:    %d\n", unsafe.Sizeof(LargeMapValue{}))
    fmt.Printf("map  length:    %d\n", len(stdw))
	if err := Unmarshal([]byte(jsonStr), &sonicw); err != nil {
        t.Fatal(err)
	}
	if !reflect.DeepEqual(stdw, sonicw) {
        fmt.Printf("have %d:\n    %#v\nwant %d:\n    %#v\n", len(sonicw), sonicw, len(stdw), stdw)
        t.Fail()
	}
}

type LargeMapValue struct {
	Id [129]byte 
}

var jsonStr = `{
        "1":{
        },
        "2":{
        },
        "3":{
        },
        "4":{
        },
        "5":{
        },
        "6":{
        },
        "7":{          
        },
        "8":{
        },
        "9":{
        }
    }
`
  • result
=== RUN   TestLargeMapValue
struct  size:    129
map  length:    9
have 9:
    map[string]sonic.LargeMapValue{"1":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "2":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0
x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "7":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "8":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "9":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}
want 9:
    map[string]sonic.LargeMapValue{"1":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "2":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0
x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "3":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "4":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "5":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "6":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "7":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "8":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x
0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, "9":sonic.LargeMapValue{Id:[129]uint8{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0
x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}
--- FAIL: TestLargeMapValue (0.03s)
FAIL

not return error when parsing unescaped characters that MUST be escaped as JSON RFC

output: should have unmarshal error like &json.SyntaxError{msg:"invalid character '\\b' in string literal", Offset:2}

func TestInvalideUnicode(t *testing.T) {
	var sonicv, stdv interface{}
	data := []byte("\"\b\"")
	sonicerr := Unmarshal(data, &sonicv)
	stderr := json.Unmarshal(data, &stdv)
	if stderr != nil {
		if sonicerr == nil {
			t.Errorf("should have unmarshal error like %#v\n", stderr)
		}
		return
	}
	if !reflect.DeepEqual(sonicv, stdv) {
		t.Errorf("Unmarshal %#v\n want obj:\n %#v\n get obj:\n %#v\n", data, stdv, sonicv)
	}
}

not consistent with encoding/json when marshalling struct field with "omitempty" tag

code

type Struct struct {
    F1   struct{ F2 string} `json:",omitempty"`
}
func TestMarshalStructFieldWithOmitemptyTag(t *testing.T) {
    var obj Struct

    out, err := Marshal(obj)
    require.NoError(t, err)

    want, err := json.Marshal(obj)
    require.NoError(t, err)

    require.Equal(t, string(want), string(out))
}

output

        	Error:      	Not equal:
        	            	expected: "{\"F1\":{\"F2\":\"\"}}"
        	            	actual  : "{}"

        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1 +1 @@
        	            	-{"F1":{"F2":""}}
        	            	+{}
        	Test:       	TestMarshalStructFieldWithOmitemptyTag

Not support struct field as json.Unmarshaler

  • Code
type iface interface {
    UnmarshalJSON(b []byte) error
}
type wrap struct {
    F iface `json:"F"`
}
type impl struct {
    a string
}

func (self *impl) UnmarshalJSON(b []byte) error{
    self.a = string(b)
    return nil
}

func TestStructFieldInterface(t *testing.T) {
    var p = &impl{}
    var obj = wrap{
        F: p,
    }
    var data = []byte(`{"F":"xx"}`)
    err := Unmarshal(data, &obj)
    if err != nil {
        t.Fatal(err)
    }
    if p.a != "\"xx\"" {
        t.Fatalf("%#v", p.a)
    }
}

Result:

Fail: json: cannot unmarshal  into Go value of type sonic.iface

Std:

PASS  p.a == "\"xx\""

UnmarshalInterface failed

func TestUnmarshalInterface(t *testing.T) {
	var xint Xint
	var i interface{} = &xint
	if err := Unmarshal([]byte(`{"X":1}`), &i); err != nil {
		t.Fatalf("Unmarshal: %v", err)
	}
	if xint.X != 1 {
		t.Fatalf("Did not write to xint")
	}
}
--- FAIL: TestUnmarshalInterface (0.00s)
    std_decode_test.go:1333: Did not write to xint
FAIL

go 1.17 support

internal/loader/funcdata_invalid.go:23:11: panic("Unsupported Go version. Supported versions are: 1.15, 1.16")

Decode: float64 precision incorrect

Use same json to Unmarshal(), but got different precision

  • json
    {"test":0.6667}
  • got
    map[string]interface {}{"test":0.6667000000000001}
  • exp
    map[string]interface {}{"test":0.6667}
  • testcode
func TestFloat(t *testing.T) {
	data := `{"test":0.6667}`
	var stdobj map[string]interface{}
	if err := json.Unmarshal([]byte(data), &stdobj);err != nil {
		t.Fatal(err)
	}
	var sonicobj map[string]interface{}
	if err := Unmarshal([]byte(data), &sonicobj); err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(stdobj,sonicobj) {
		t.Fatalf("exp: \n%#v, \ngot: \n%#v\n", stdobj, sonicobj)
	}
}

Crash when decoding concurrently

This crashes the program:

type testStruct struct {
	A  string `json:"a"`
}

func TestNilPointerSlice(t *testing.T) {
	Pretouch(reflect.TypeOf([]*testStruct{}))
	wg := sync.WaitGroup{}
	for i:=0; i<10000; i++ {
		go func(){
			defer wg.Add(1)
			var v = &[]*testStruct{}
			var d = `[{"a":"1"},{"a":"2"}]`
			dc := decoder.NewDecoder(d)
			if err := dc.Decode(v); err != nil {
				t.Fatal(err)
			}
		}()
	}
	wg.Wait()
}

```go test -race``` detects race condition when using sonic. But using inbuilt `encoding/json` does not.

Write at 0x00c0002c5870 by goroutine 20:
  github.com/bytedance/sonic/internal/caching.(*_ProgramMap).insert()
      /Users/probhonjon/Dev/go1.16/pkg/mod/github.com/bytedance/[email protected]/internal/caching/pcache.go:111 +0x136
  github.com/bytedance/sonic/internal/caching.(*_ProgramMap).add()
      /Users/probhonjon/Dev/go1.16/pkg/mod/github.com/bytedance/[email protected]/internal/caching/pcache.go:82 +0xc5
  github.com/bytedance/sonic/internal/caching.(*ProgramCache).Put()
      /Users/probhonjon/Dev/go1.16/pkg/mod/github.com/bytedance/[email protected]/internal/caching/pcache.go:142 +0xa9
  github.com/bytedance/sonic/decoder.findOrCompile()
      /Users/probhonjon/Dev/go1.16/pkg/mod/github.com/bytedance/[email protected]/decoder/pools.go:104 +0x431
  github.com/bytedance/sonic/decoder.decodeTypedPointer()
      /Users/probhonjon/Dev/go1.16/pkg/mod/github.com/bytedance/[email protected]/decoder/primitives.go:28 +0x44
  github.com/bytedance/sonic/decoder.(*Decoder).Decode()
      /Users/probhonjon/Dev/go1.16/pkg/mod/github.com/bytedance/[email protected]/decoder/decoder.go:63 +0x384
  github.com/bytedance/sonic.UnmarshalString()
      /Users/probhonjon/Dev/go1.16/pkg/mod/github.com/bytedance/[email protected]/sonic.go:41 +0x44f
  github.com/bytedance/sonic.Unmarshal()
      /Users/probhonjon/Dev/go1.16/pkg/mod/github.com/bytedance/[email protected]/sonic.go:36 +0x3d1

Logic: lots of goroutines call a helper function. This helper function calls sonic to extract json.

CPU没有avx2指令,启动报错

现象

程序启动时报错 SIGILL: illegal instruction

stack trace

SIGILL: illegal instruction
  189 PC=0xea2343 m=12 sigcode=2
  190 instruction bytes: 0xc4 0x43 0x3d 0x38 0x44 0x24 0xc 0x1 0xc4 0x62 0x3d 0x0 0xc1 0xc5 0x3d 0xdb
  191 
  192 goroutine 814 [running]:
  193 runtime: unexpected return pc for github.com/chenzhuoyu/base64x.___asm2asm_compiled_code__DO_NOT_CALL_THIS_SYMBOL___ called from 0xc00e5c8000
  194 stack: frame={sp:0xc00f209ae8, fp:0xc00f209af0} stack=[0xc00f208000,0xc00f20a000)
  195 000000c00f2099e8:  000000c00f209a20  000000000047f048 <sync.(*poolDequeue).popTail+200>
  196 000000c00f2099f8:  000000c00884ebd0  0000000000000000
  197 000000c00f209a08:  00000000036a8640  000000100000000d
  198 000000c00f209a18:  000000c009656a20  000000c00f209a68
  199 000000c00f209a28:  000000000047f3fa <sync.(*poolChain).popTail+90>  000000c012a63f20
  200 000000c00f209a38:  00000000036a8640  000000c009656a20
  201 000000c00f209a48:  00000000038a9301  000000c0124d8a18
  202 000000c00f209a58:  000000c012a63f20  0000000000f1aebd <github.com/bytedance/sonic/encoder.encodeQuote+893>
  203 000000c00f209a68:  000000c00e1ca895  000000c00567bf85
  204 000000c00f209a78:  0000000000000007  00000000038a9340
  205 000000c00f209a88:  000000c001f2bd40  0000000000000001
  206 000000c00f209a98:  000000c001f2bd40  030000000047e8a5
  207 000000c00f209aa8:  ffffffffffffffff  0000000000000003
  208 000000c00f209ab8:  0000000000100000  000000000047e8a5 <sync.(*Pool).pin+37>
  209 000000c00f209ac8:  0000000000000003  000000c00e1c6000
  210 000000c00f209ad8:  000000c013072328  000000c00f209b08
  211 000000c00f209ae8: <000000c00e5c8000 >000000c013042960
  212 000000c00f209af8:  0000000000000000  0000000000000000
  213 000000c00f209b08:  000000c00f209b98  00007f0accb6c156 <__json_encoder_7f0accb68000+16726>
  214 000000c00f209b18:  000000c0007090b0  00000000036a8640
  215 000000c00f209b28:  000000c009656a20  0000000000000007
  216 000000c00f209b38:  0000000000000000  0000000000000000
  217 000000c00f209b48:  0000000000000000  0000000000000000
  218 000000c00f209b58:  000000c00e1c6000  00000000000048a8
  219 000000c00f209b68:  0000000000100000  0000000000000000
  220 000000c00f209b78:  000000c009656a20  000000c00e1c6000
  221 000000c00f209b88:  000000000000489e  0000000000100000
  222 000000c00f209b98:  000000c00f209be8  0000000000f1b55f <github.com/bytedance/sonic/encoder.encodeTypedPointer+351>
  223 000000c00f209ba8:  000000c005274840  000000c002c4b068
  224 000000c00f209bb8:  000000c00e5c8000  0000000000000000
  225 000000c00f209bc8:  0000000000000000  0000000000000000
  226 000000c00f209bd8:  0000000000000000  0000000000203000
  227 000000c00f209be8:  000000c00f209c78
  228 github.com/chenzhuoyu/base64x.___asm2asm_compiled_code__DO_NOT_CALL_THIS_SYMBOL___(0xc013042960, 0x0, 0x0, 0xc00f209b98, 0x7f0accb6c156, 0xc0007090b0, 0x36a8640, 0xc009656a20, 0x7, 0x0, ...)
  229     /opt/tiger/compile_path/pkg/mod/github.com/chenzhuoyu/[email protected]/native_amd64.s:148 +0x183 fp=0xc00f209af0 sp=0xc00f209ae8 pc=0xea2343

cpuinfo

processor	: 23
vendor_id	: GenuineIntel
cpu family	: 6
model		: 62
model name	: Intel(R) Xeon(R) CPU E5-2630 v2 @ 2.60GHz
stepping	: 4
microcode	: 0x424
cpu MHz		: 2900.091
cache size	: 15360 KB
physical id	: 1
siblings	: 12
core id		: 5
cpu cores	: 6
apicid		: 43
initial apicid	: 43
fpu		: yes
fpu_exception	: yes
cpuid level	: 13
wp		: yes
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid dca sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm cpuid_fault tpr_shadow vnmi flexpriority ept vpid fsgsbase smep erms xsaveopt dtherm ida arat pln pts
bugs		: cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf
bogomips	: 5201.66
clflush size	: 64
cache_alignment	: 64
address sizes	: 46 bits physical, 48 bits virtual
power management:

not return error when unmarshalling INF-like string

Sonic does not return the error when unmarshalling an INF-like string, as 9e370.

func TestUnmarshalINF(t *testing.T) {
	var v interface{}
	data := []byte("9e370")   // INF
	sonicerr := sonic.Unmarshal(data, &v)
	stderr := json.Unmarshal(data, &v)
	if sonicerr == nil && stderr != nil {
		t.Errorf("should have unmarshal error like %#v\n", stderr)
	}
}

not return error when unmarshalling "M10"

Not return any errors when unmarshalling "M", "I", "J", "`" with a number, such as "M10", "I123", "`0"...

func TestUnmarshalMWithNumber(t *testing.T) {
	var v interface{}
	err := Unmarshal([]byte("M10"), &v) // MIJ`
	require.Error(t, err)
}

output:

=== RUN   TestUnmarshalMWithNumber
1
    /Users/bytedance/sonic-master/issue101_test.go:16:
        	Error Trace:	issue101_test.go:16
        	Error:      	An error is expected but got nil.
        	Test:       	TestUnmarshalMWithNumber
--- FAIL: TestUnmarshalMWithNumber (0.00s)
FAIL

not consistent with Golang encoding/json

Test code:

type JsonStringTag struct {
	Str    string `json:","`
	StrTag string `json:",string"`
}

func TestStringTag(t *testing.T) {
	input := `{
		"Str": "\ud800",
		"StrTag": "\"\\ud800\""
	}`
	var sonicstruct, stdstruct JsonStringTag

	stderr := json.Unmarshal([]byte(input), &stdstruct)
	sonicerr := Unmarshal([]byte(input), &sonicstruct) // sonic
	if stderr == nil && sonicerr != nil {
		t.Errorf("hava %#v want %#v", sonicerr, stderr)
	}
	if reflect.DeepEqual(sonicstruct, stdstruct) {
		t.Errorf("hava %#v want %#v", sonicstruct, stdstruct)
	}
}

Result:
sonic err: decoder.SyntaxError{Pos:43, Src:"{\n\t\t\"Str\": \"\\ud800\",\n\t\t\"StrTag\": \"\\\"\\\\ud800\\\"\"\n\t}", Code:0x1}
stdjson err: <nil>

not support unmarshalling IEEE-754 negative zero

func TestUnmarshalNegZero(t *testing.T) {
	var sonicv, stdv float64
	data := []byte("-0")
	sonicerr := Unmarshal(data, &sonicv)
	stderr := json.Unmarshal(data, &stdv)
	if sonicerr == nil && stderr != nil {
		t.Errorf("should have unmarshal error like %#v\n", stderr)
	}
	if !reflect.DeepEqual(math.Float64bits(sonicv), math.Float64bits(stdv)) {
		t.Errorf("want:\n    %#v\nhave:\n    %#v\n", stdv, sonicv)
	}
}

HTML escape support

encoding/json escapes and encodes HTML characters as the default behavior, but it seems that it is not supported yet.
When implementing this feature, I think that performance will be affected depending on the implementation, so it seems better to give priority to it.

Reproducible code

Go Playground's example is here

package main

import (
	"bytes"
	"encoding/json"
	"fmt"

	"github.com/bytedance/sonic"
)

func main() {
	s := `<html>foo &` + "\xe2\x80\xa8 \xe2\x80\xa9" + `</html>`
	expected, err := json.Marshal(s)
	if err != nil {
		panic(err)
	}
	got, err := sonic.Marshal(s)
	if err != nil {
		panic(err)
	}
	if !bytes.Equal(expected, got) {
		fmt.Println("failed to encode contains escaped characters")
		fmt.Println("expected:", string(expected))
		fmt.Println("got     :", string(got))
	} else {
		fmt.Println("successful")
	}
}

implement the Ryu algorithm to speed up float64->decimal conversion

go1.17beta1 benchmark:

name                         old time/op  new time/op  delta
AppendFloat/32Fixed8Hard-4   72.0ns ± 2%  56.0ns ± 2%  -22.28%  (p=0.000 n=10+10)
AppendFloat/32Fixed9Hard-4   74.8ns ± 0%  64.2ns ± 2%  -14.16%  (p=0.000 n=8+10)
AppendFloat/64Fixed1-4       60.4ns ± 1%  54.2ns ± 1%  -10.31%  (p=0.000 n=10+9)
AppendFloat/64Fixed2-4       66.3ns ± 1%  53.3ns ± 1%  -19.54%  (p=0.000 n=10+9)
AppendFloat/64Fixed3-4       61.0ns ± 1%  55.0ns ± 2%   -9.80%  (p=0.000 n=9+10)
AppendFloat/64Fixed4-4       66.9ns ± 0%  52.0ns ± 2%  -22.20%  (p=0.000 n=8+10)
AppendFloat/64Fixed12-4      95.5ns ± 1%  76.2ns ± 3%  -20.19%  (p=0.000 n=10+9)
AppendFloat/64Fixed16-4      1.62µs ± 0%  0.07µs ± 2%  -95.69%  (p=0.000 n=10+10)
AppendFloat/64Fixed12Hard-4  1.27µs ± 1%  0.07µs ± 1%  -94.83%  (p=0.000 n=9+9)
AppendFloat/64Fixed17Hard-4  3.68µs ± 1%  0.08µs ± 2%  -97.86%  (p=0.000 n=10+9)
AppendFloat/64Fixed18Hard-4  3.67µs ± 0%  3.72µs ± 1%   +1.44%  (p=0.000 n=9+10)

Ryu algorithm resources:
golang/go#15672
https://github.com/ulfjack/ryu
https://dl.acm.org/doi/10.1145/3360595
https://dl.acm.org/doi/10.1145/3192366.3192369
https://dl.acm.org/doi/10.1145/3360595
https://cseweb.ucsd.edu/~lerner/papers/fp-printing-popl16.pdf

not return error when unmarshalling invalid base64-encoded string to byte slice

not return an error when unmarshalling invalid base64-encoded string to a byte slice

code:

func TestUnmarshalInvalidBase64EncodedString(t *testing.T) {
    var obj []byte
    data := `"123456"`   // also `"12345"`  or `"1234;6"` 
    err := Unmarshal([]byte(data), &obj)
    require.Error(t, err)
}

output:

        	Error:      	An error is expected but got nil.
        	Test:       	TestUnmarshalInvalidBase64EncodedString
--- FAIL: TestUnmarshalInvalidBase64EncodedString (0.00s)

not return error when unmarshalling an invalid object

When there is no commas in the json object, sonic does not return the error.

func TestUnmarshalNoCommaInObject(t *testing.T) {
	var v interface{}
	err := Unmarshal([]byte("{\"y\":1 \"x\":1}"), &v)
	fmt.Printf("%#v\n", v)
	require.Error(t, err)
}

Output:

=== RUN   TestUnmarshalNoCommaInObject
map[string]interface {}{"x":1, "y":1}
    /Users/bytedance/sonic/issue103_test.go:15:
        	Error Trace:	issue103_test.go:15
        	Error:      	An error is expected but got nil.
        	Test:       	TestUnmarshalNoCommaInObject
--- FAIL: TestUnmarshalNoCommaInObject (0.00s)
FAIL

panic: encode struct of single map-typed field

  • code
type Wrap struct {
	SingleMap *SingleMap `bson:"single_map,omitempty" json:"single_map,omitempty"`
}

type SingleMap struct {
	//Any      string `json:"any"`
	M map[string]string `bson:"m" json:"m"`
}

func TestMarshal_SingleMap(t *testing.T) {
	var data = `[{
		"single_map": {
			"m": {
				"a": "b"
			}
		}
	}]`
	var obj = new([]*Wrap)
	if err := json.Unmarshal([]byte(data), obj); err != nil {
		t.Fatal(err)
	}
	fmt.Printf("%#v\n", obj)
	
	buf, err := Encode(obj, 0)
	if err != nil {
		t.Fatal(err)
	}
	fmt.Println(string(buf))
}
  • result
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1011f6b]

goroutine 6 [running]:
testing.tRunner.func1.2(0x1417e60, 0x170de30)
	/usr/local/go/src/testing/testing.go:1143 +0x332
testing.tRunner.func1(0xc000001b00)
	/usr/local/go/src/testing/testing.go:1146 +0x4b6
panic(0x1417e60, 0x170de30)
	/usr/local/go/src/runtime/panic.go:965 +0x1b9
github.com/bytedance/sonic/encoder.iteratorStart(0x1416320, 0xc000010508, 0x0, 0xc00013fcb0, 0x0, 0xc000300000)
	/Users/admin/Desktop/kitex/sonic3/encoder/mapiter.go:162 +0x16f
github.com/bytedance/sonic/encoder.encodeTypedPointer(0xc00000f500, 0x14209e0, 0xc00013fcb0, 0xc000400000, 0x0, 0xc000300000, 0xc000010508)
	/Users/admin/Desktop/kitex/sonic3/encoder/primitives.go:81 +0x447
github.com/bytedance/sonic/encoder.encodeTypedPointer(0xc00000f500, 0x13f6a40, 0xc000610008, 0xc000400000, 0x0, 0x0, 0xc00013fe60)
	/Users/admin/Desktop/kitex/sonic3/encoder/primitives.go:81 +0x447
github.com/bytedance/sonic/encoder.EncodeInto(0xc00000f500, 0x13f6a40, 0xc00000f3f8, 0x0, 0x30, 0x0)
	/Users/admin/Desktop/kitex/sonic3/encoder/encoder.go:101 +0xd4
github.com/bytedance/sonic/encoder.Encode(0x13f6a40, 0xc00000f3f8, 0x0, 0x4, 0xc00013ff50, 0x1, 0x1, 0x30)
	/Users/admin/Desktop/kitex/sonic3/encoder/encoder.go:79 +0xc5
github.com/bytedance/sonic/encoder.TestMarshal_SingleMap(0xc000001b00)
	/Users/admin/Desktop/kitex/sonic3/encoder/issue40_test.go:230 +0x19e
testing.tRunner(0xc000001b00, 0x14978b0)
	/usr/local/go/src/testing/testing.go:1193 +0xef
created by testing.(*T).Run
	/usr/local/go/src/testing/testing.go:1238 +0x2b3
exit status 2

TestRandomValidStrings race detected during execution of test

--- PASS: TestRandomData (4.60s)
=== RUN   TestRandomValidStrings
==================
WARNING: DATA RACE
Read at 0x00c05a2679c8 by goroutine 45:
  github.com/bytedance/sonic.TestRandomValidStrings()
      /Users/wkc/Downloads/sonic/search_test.go:188 +0x344
  testing.tRunner()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1127 +0x202

Previous write at 0x00c05a2679c8 by goroutine 46:
  github.com/bytedance/sonic.TestRandomValidStrings.func1()
      /Users/wkc/Downloads/sonic/search_test.go:156 +0x113

Goroutine 45 (running) created at:
  testing.(*T).Run()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1178 +0x796
  testing.runTests.func1()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1449 +0xa6
  testing.tRunner()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1127 +0x202
  testing.runTests()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1447 +0x5aa
  testing.(*M).Run()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1357 +0x4eb
  main.main()
      _testmain.go:197 +0x236

Goroutine 46 (running) created at:
  github.com/bytedance/sonic.TestRandomValidStrings()
      /Users/wkc/Downloads/sonic/search_test.go:154 +0x276
  testing.tRunner()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1127 +0x202
==================
==================
WARNING: DATA RACE
Read at 0x00c05a278004 by goroutine 45:
  encoding/hex.Encode()
      /usr/local/Cellar/go/1.15.2/libexec/src/encoding/hex/hex.go:27 +0x98
  encoding/hex.(*dumper).Write()
      /usr/local/Cellar/go/1.15.2/libexec/src/encoding/hex/hex.go:259 +0x132
  encoding/hex.Dump()
      /usr/local/Cellar/go/1.15.2/libexec/src/encoding/hex/hex.go:132 +0x171
  github.com/bytedance/sonic.TestRandomValidStrings()
      /Users/wkc/Downloads/sonic/search_test.go:188 +0x378
  testing.tRunner()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1127 +0x202

Previous write at 0x00c05a278004 by goroutine 46:
  math/rand.read()
      /usr/local/Cellar/go/1.15.2/libexec/src/math/rand/rand.go:280 +0xc4
  math/rand.(*lockedSource).read()
      /usr/local/Cellar/go/1.15.2/libexec/src/math/rand/rand.go:418 +0xaf
  math/rand.(*Rand).Read()
      /usr/local/Cellar/go/1.15.2/libexec/src/math/rand/rand.go:262 +0x9c
  math/rand.Read()
      /usr/local/Cellar/go/1.15.2/libexec/src/math/rand/rand.go:359 +0xdb
  github.com/bytedance/sonic.TestRandomValidStrings.func1()
      /Users/wkc/Downloads/sonic/search_test.go:156 +0x63

Goroutine 45 (running) created at:
  testing.(*T).Run()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1178 +0x796
  testing.runTests.func1()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1449 +0xa6
  testing.tRunner()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1127 +0x202
  testing.runTests()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1447 +0x5aa
  testing.(*M).Run()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1357 +0x4eb
  main.main()
      _testmain.go:197 +0x236

Goroutine 46 (running) created at:
  github.com/bytedance/sonic.TestRandomValidStrings()
      /Users/wkc/Downloads/sonic/search_test.go:154 +0x276
  testing.tRunner()
      /usr/local/Cellar/go/1.15.2/libexec/src/testing/testing.go:1127 +0x202
==================
00000000  24 27 ec 2b d5 91 a3 21  4b ba 4e f3 7a 09 76 4e  |$'.+...!K.N.z.vN|
00000010  f9 8c 02 5b ac af 2a 5a  ad 70 d1 63 b7 3e bf 09  |...[..*Z.p.c.>..|
00000020  bc ff ed 67 0f 72 4b 3a  0d f7 33 32 50 c2 78 df  |...g.rK:..32P.x.|
00000030  04 ea dc bd 67 b1 04                              |....g..|

    testing.go:1042: race detected during execution of test
--- FAIL: TestRandomValidStrings (10.00s)

internal\loader\loader.go:28:11: undefined: syscall.MAP_ANON

D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:28:11: undefined: syscall.MAP_ANON
D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:28:31: undefined: syscall.MAP_PRIVATE
D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:29:11: undefined: syscall.PROT_READ
D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:29:31: undefined: syscall.PROT_EXEC
D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:30:11: undefined: syscall.PROT_READ
D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:30:31: undefined: syscall.PROT_WRITE
D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:59:19: undefined: syscall.RawSyscall6
D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:59:39: undefined: syscall.SYS_MMAP
D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:67:21: undefined: syscall.RawSyscall
D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:67:40: undefined: syscall.SYS_MPROTECT
D:\go\pkg\mod\github.com\bytedance\[email protected]\internal\loader\loader.go:67:40: too many errors

Not 100% compatibility with encoding/json for prefilled arr

func TestPrefilledArr(t *testing.T) {
	a := &[...]int{1, 2}
	b := &[...]int{1, 2}
	err1 := json.Unmarshal([]byte(`[3]`), a)
	err2 := Unmarshal([]byte(`[3]`), b)
	require.Equal(t, err1, err2)
	require.Equal(t, a, b)
}
=== RUN   TestPrefilledArr
    fuzz_test.go:109: 
                Error Trace:    fuzz_test.go:109
                Error:          Not equal: 
                                expected: &[2]int{3, 0}
                                actual  : &[2]int{3, 2}
                            
                                Diff:
                                --- Expected
                                +++ Actual
                                @@ -2,3 +2,3 @@
                                  (int) 3,
                                - (int) 0
                                + (int) 2
                                 })
                Test:           TestPrefilledArr
--- FAIL: TestPrefilledArr (0.00s)
FAIL
exit status 1

Unmarshal()使用了内存分配

func Unmarshal(buf []byte, val interface{}) error {
	return sonic.UnmarshalString(*(*string)(unsafe.Pointer(&buf)), val)
}

使用*(*string)(unsafe.Pointer(&buf))而不是string(buf),以减少内存分配。

假如考虑到byte数组的安全性,我认为不需要担心,使用sonic框架后,正常调用都是通过sonic.UnmarshalString完成的,项目中很少会用到sonic.Unmarshal。很多时候用到byte数组的序列化都是因为需要替换其他框架的序列化方式。

Not return error when unmarshalling invalid numbers

Not return error when unmarshalling invalid numbers.
The definition of invalid number is as RFC 8259.

Input: 1e2e3

Sonic return error: nil

Stdjson return error: &json.SyntaxError{msg:"invalid character 'e' after top-level value", Offset:4}

func TestInvalidNumber(t *testing.T) {
    data := `1e2e3`
    var stdobj, sonicobj interface{}
    stderr := json.Unmarshal([]byte(data), &stdobj)
    sonicerr := Unmarshal([]byte(data), &sonicobj)
    if sonicerr == nil && stderr != nil {
        t.Fatalf("exp err: \n%#v, \ngot err: \n%#v\n", stderr, sonicerr)
    }
}

style: using gofmt to format your code

It is weird to see

import (
    `io/ioutil`
    `testing`

    `github.com/stretchr/testify/require`
)

where use `` instead of ""

if we want others to participate in, we should follow some basic go-style code style

just use gofmt -w . in /sonic to format all *.go files, please

return null when unmarshalling empty string to byte slice

code:

func TestEmptyByteArray(t *testing.T) {
    var obj []byte
    err := Unmarshal([]byte("\"\""), &obj)
    require.NoError(t, err)
    require.Equal(t, obj, make([]byte, 0, 0))

}

output:

        	Error:      	Not equal:
        	            	expected: []byte(nil)
        	            	actual  : []byte{}

        	            	Diff:
        	            	--- Expected
        	            	+++ Actual
        	            	@@ -1,2 +1,3 @@
        	            	-([]uint8) <nil>
        	            	+([]uint8) {
        	            	+}

        	Test:       	TestEmptyByteArray

cc: can't resolve alias type as json.Marshal

  • version
    v0.0.0-20210824052355-8c119dd72d11
  • code
type JString string

func (j *JString) UnmarshalJSON(b []byte) error {
	*j = JString(b)
	return nil
}

func (j JString) MarshalJSON() ([]byte, error) {
	return []byte(j), nil
}

type Data []*map[string]JString

var Tstring = "[{\"group_item_0\":\"652900\",\"group_item_1\":23,\"item_count\":1}]"

func TestX(t *testing.T) {
	var Tdata Data
	err := sonic.Unmarshal([]byte(Tstring), &Tdata)
	fmt.Println(err.(decoder.SyntaxError).Description())

	err = json.Unmarshal([]byte(Tstring), &Tdata)
	fmt.Println(err)
}
  • result
Syntax error at index 41: invalid char

	,"group_item_1":23,"item_count":
	................^...............

<nil>

支持对一个json的一个value的search,找出对应的path

使用场景如下:

{
"name": "test",
"members": [
{
"name": "name1"
"age": 12,
},
{
"name": "name2",
"age": 22,
}
],
"adminMembers" [
{
"name": "name3"
"age": 12,
},
{
"name": "name4",
"age": 22,
}
],
}
如上的一个json,我用这样的一个json来存储信息,一般情况下我知道path可以找出某个人的age,另外一种场景就是我现在知道一个人的name,想知道他的path,然后也就可以通过path知道这个人的信息以及其他信息了.
请问是否可以支持如上的功能?

Not 100% compatibility with encoding/json

func TestCompatibility(t *testing.T) {
	inputs := []interface{}{`"<&>"`, `"\"<&>\""`, "\b", float64(-0), float32(-0), map[string]int{"3": 3, "2": 2, "1": 1}}
	for _, input := range inputs {
		t.Run(fmt.Sprintf("case %v", input), func(t *testing.T) {
			buf1, err1 := json.Marshal(input)
			buf2, err2 := Marshal(input)
			require.Nil(t, err1)
			require.Nil(t, err2)
			require.Equal(t, string(buf1), string(buf2))
		})
	}
}
--- FAIL: TestCompatibility (0.00s)
    --- FAIL: TestCompatibility/case_"<&>" (0.00s)
        fuzz_test.go:112: 
                Error Trace:    fuzz_test.go:112
                Error:          Not equal: 
                                expected: "\"\\\"\\u003c\\u0026\\u003e\\\"\""
                                actual  : "\"\\\"<&>\\\"\""
                            
                                Diff:
                                --- Expected
                                +++ Actual
                                @@ -1 +1 @@
                                -"\"\u003c\u0026\u003e\""
                                +"\"<&>\""
                Test:           TestCompatibility/case_"<&>"
    --- FAIL: TestCompatibility/case_"\"<&>\"" (0.00s)
        fuzz_test.go:112: 
                Error Trace:    fuzz_test.go:112
                Error:          Not equal: 
                                expected: "\"\\\"\\\\\\\"\\u003c\\u0026\\u003e\\\\\\\"\\\"\""
                                actual  : "\"\\\"\\\\\\\"<&>\\\\\\\"\\\"\""
                            
                                Diff:
                                --- Expected
                                +++ Actual
                                @@ -1 +1 @@
                                -"\"\\\"\u003c\u0026\u003e\\\"\""
                                +"\"\\\"<&>\\\"\""
                Test:           TestCompatibility/case_"\"<&>\""
    --- FAIL: TestCompatibility/case_\b (0.00s)
        fuzz_test.go:112: 
                Error Trace:    fuzz_test.go:112
                Error:          Not equal: 
                                expected: "\"\\u0008\""
                                actual  : "\"\\b\""
                            
                                Diff:
                                --- Expected
                                +++ Actual
                                @@ -1 +1 @@
                                -"\u0008"
                                +"\b"
                Test:           TestCompatibility/case_\b
    --- FAIL: TestCompatibility/case_map[1:1_2:2_3:3] (0.00s)
        fuzz_test.go:112: 
                Error Trace:    fuzz_test.go:112
                Error:          Not equal: 
                                expected: "{\"1\":1,\"2\":2,\"3\":3}"
                                actual  : "{\"2\":2,\"1\":1,\"3\":3}"
                            
                                Diff:
                                --- Expected
                                +++ Actual
                                @@ -1 +1 @@
                                -{"1":1,"2":2,"3":3}
                                +{"2":2,"1":1,"3":3}
                Test:           TestCompatibility/case_map[1:1_2:2_3:3]
FAIL
exit status 1
FAIL    github.com/bytedance/sonic      0.144s

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.