github.com/bytedance/sonic@v1.11.7-0.20240517092252-d2edb31b167b/README.md (about)

     1  # Sonic
     2  
     3  English | [中文](README_ZH_CN.md)
     4  
     5  A blazingly fast JSON serializing & deserializing library, accelerated by JIT (just-in-time compiling) and SIMD (single-instruction-multiple-data).
     6  
     7  ## Requirement
     8  
     9  - Go 1.16~1.22
    10  - Linux / MacOS / Windows(need go1.17 above)
    11  - Amd64 ARCH
    12  
    13  ## Features
    14  
    15  - Runtime object binding without code generation
    16  - Complete APIs for JSON value manipulation
    17  - Fast, fast, fast!
    18  
    19  ## APIs
    20  
    21  see [go.dev](https://pkg.go.dev/github.com/bytedance/sonic)
    22  
    23  ## Benchmarks
    24  
    25  For **all sizes** of json and **all scenarios** of usage, **Sonic performs best**.
    26  
    27  - [Medium](https://github.com/bytedance/sonic/blob/main/decoder/testdata_test.go#L19) (13KB, 300+ key, 6 layers)
    28  
    29  ```powershell
    30  goversion: 1.17.1
    31  goos: darwin
    32  goarch: amd64
    33  cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
    34  BenchmarkEncoder_Generic_Sonic-16                      32393 ns/op         402.40 MB/s       11965 B/op          4 allocs/op
    35  BenchmarkEncoder_Generic_Sonic_Fast-16                 21668 ns/op         601.57 MB/s       10940 B/op          4 allocs/op
    36  BenchmarkEncoder_Generic_JsonIter-16                   42168 ns/op         309.12 MB/s       14345 B/op        115 allocs/op
    37  BenchmarkEncoder_Generic_GoJson-16                     65189 ns/op         199.96 MB/s       23261 B/op         16 allocs/op
    38  BenchmarkEncoder_Generic_StdLib-16                    106322 ns/op         122.60 MB/s       49136 B/op        789 allocs/op
    39  BenchmarkEncoder_Binding_Sonic-16                       6269 ns/op        2079.26 MB/s       14173 B/op          4 allocs/op
    40  BenchmarkEncoder_Binding_Sonic_Fast-16                  5281 ns/op        2468.16 MB/s       12322 B/op          4 allocs/op
    41  BenchmarkEncoder_Binding_JsonIter-16                   20056 ns/op         649.93 MB/s        9488 B/op          2 allocs/op
    42  BenchmarkEncoder_Binding_GoJson-16                      8311 ns/op        1568.32 MB/s        9481 B/op          1 allocs/op
    43  BenchmarkEncoder_Binding_StdLib-16                     16448 ns/op         792.52 MB/s        9479 B/op          1 allocs/op
    44  BenchmarkEncoder_Parallel_Generic_Sonic-16              6681 ns/op        1950.93 MB/s       12738 B/op          4 allocs/op
    45  BenchmarkEncoder_Parallel_Generic_Sonic_Fast-16         4179 ns/op        3118.99 MB/s       10757 B/op          4 allocs/op
    46  BenchmarkEncoder_Parallel_Generic_JsonIter-16           9861 ns/op        1321.84 MB/s       14362 B/op        115 allocs/op
    47  BenchmarkEncoder_Parallel_Generic_GoJson-16            18850 ns/op         691.52 MB/s       23278 B/op         16 allocs/op
    48  BenchmarkEncoder_Parallel_Generic_StdLib-16            45902 ns/op         283.97 MB/s       49174 B/op        789 allocs/op
    49  BenchmarkEncoder_Parallel_Binding_Sonic-16              1480 ns/op        8810.09 MB/s       13049 B/op          4 allocs/op
    50  BenchmarkEncoder_Parallel_Binding_Sonic_Fast-16         1209 ns/op        10785.23 MB/s      11546 B/op          4 allocs/op
    51  BenchmarkEncoder_Parallel_Binding_JsonIter-16           6170 ns/op        2112.58 MB/s        9504 B/op          2 allocs/op
    52  BenchmarkEncoder_Parallel_Binding_GoJson-16             3321 ns/op        3925.52 MB/s        9496 B/op          1 allocs/op
    53  BenchmarkEncoder_Parallel_Binding_StdLib-16             3739 ns/op        3486.49 MB/s        9480 B/op          1 allocs/op
    54  
    55  BenchmarkDecoder_Generic_Sonic-16                      66812 ns/op         195.10 MB/s       57602 B/op        723 allocs/op
    56  BenchmarkDecoder_Generic_Sonic_Fast-16                 54523 ns/op         239.07 MB/s       49786 B/op        313 allocs/op
    57  BenchmarkDecoder_Generic_StdLib-16                    124260 ns/op         104.90 MB/s       50869 B/op        772 allocs/op
    58  BenchmarkDecoder_Generic_JsonIter-16                   91274 ns/op         142.81 MB/s       55782 B/op       1068 allocs/op
    59  BenchmarkDecoder_Generic_GoJson-16                     88569 ns/op         147.17 MB/s       66367 B/op        973 allocs/op
    60  BenchmarkDecoder_Binding_Sonic-16                      32557 ns/op         400.38 MB/s       28302 B/op        137 allocs/op
    61  BenchmarkDecoder_Binding_Sonic_Fast-16                 28649 ns/op         455.00 MB/s       24999 B/op         34 allocs/op
    62  BenchmarkDecoder_Binding_StdLib-16                    111437 ns/op         116.97 MB/s       10576 B/op        208 allocs/op
    63  BenchmarkDecoder_Binding_JsonIter-16                   35090 ns/op         371.48 MB/s       14673 B/op        385 allocs/op
    64  BenchmarkDecoder_Binding_GoJson-16                     28738 ns/op         453.59 MB/s       22039 B/op         49 allocs/op
    65  BenchmarkDecoder_Parallel_Generic_Sonic-16             12321 ns/op        1057.91 MB/s       57233 B/op        723 allocs/op
    66  BenchmarkDecoder_Parallel_Generic_Sonic_Fast-16        10644 ns/op        1224.64 MB/s       49362 B/op        313 allocs/op
    67  BenchmarkDecoder_Parallel_Generic_StdLib-16            57587 ns/op         226.35 MB/s       50874 B/op        772 allocs/op
    68  BenchmarkDecoder_Parallel_Generic_JsonIter-16          38666 ns/op         337.12 MB/s       55789 B/op       1068 allocs/op
    69  BenchmarkDecoder_Parallel_Generic_GoJson-16            30259 ns/op         430.79 MB/s       66370 B/op        974 allocs/op
    70  BenchmarkDecoder_Parallel_Binding_Sonic-16              5965 ns/op        2185.28 MB/s       27747 B/op        137 allocs/op
    71  BenchmarkDecoder_Parallel_Binding_Sonic_Fast-16         5170 ns/op        2521.31 MB/s       24715 B/op         34 allocs/op
    72  BenchmarkDecoder_Parallel_Binding_StdLib-16            27582 ns/op         472.58 MB/s       10576 B/op        208 allocs/op
    73  BenchmarkDecoder_Parallel_Binding_JsonIter-16          13571 ns/op         960.51 MB/s       14685 B/op        385 allocs/op
    74  BenchmarkDecoder_Parallel_Binding_GoJson-16            10031 ns/op        1299.51 MB/s       22111 B/op         49 allocs/op
    75  
    76  BenchmarkGetOne_Sonic-16                                3276 ns/op        3975.78 MB/s          24 B/op          1 allocs/op
    77  BenchmarkGetOne_Gjson-16                                9431 ns/op        1380.81 MB/s           0 B/op          0 allocs/op
    78  BenchmarkGetOne_Jsoniter-16                            51178 ns/op         254.46 MB/s       27936 B/op        647 allocs/op
    79  BenchmarkGetOne_Parallel_Sonic-16                      216.7 ns/op       60098.95 MB/s          24 B/op          1 allocs/op
    80  BenchmarkGetOne_Parallel_Gjson-16                       1076 ns/op        12098.62 MB/s          0 B/op          0 allocs/op
    81  BenchmarkGetOne_Parallel_Jsoniter-16                   17741 ns/op         734.06 MB/s       27945 B/op        647 allocs/op
    82  BenchmarkSetOne_Sonic-16                               9571 ns/op         1360.61 MB/s        1584 B/op         17 allocs/op
    83  BenchmarkSetOne_Sjson-16                               36456 ns/op         357.22 MB/s       52180 B/op          9 allocs/op
    84  BenchmarkSetOne_Jsoniter-16                            79475 ns/op         163.86 MB/s       45862 B/op        964 allocs/op
    85  BenchmarkSetOne_Parallel_Sonic-16                      850.9 ns/op       15305.31 MB/s        1584 B/op         17 allocs/op
    86  BenchmarkSetOne_Parallel_Sjson-16                      18194 ns/op         715.77 MB/s       52247 B/op          9 allocs/op
    87  BenchmarkSetOne_Parallel_Jsoniter-16                   33560 ns/op         388.05 MB/s       45892 B/op        964 allocs/op
    88  BenchmarkLoadNode/LoadAll()-16                         11384 ns/op        1143.93 MB/s        6307 B/op         25 allocs/op
    89  BenchmarkLoadNode_Parallel/LoadAll()-16                 5493 ns/op        2370.68 MB/s        7145 B/op         25 allocs/op
    90  BenchmarkLoadNode/Interface()-16                       17722 ns/op         734.85 MB/s       13323 B/op         88 allocs/op
    91  BenchmarkLoadNode_Parallel/Interface()-16              10330 ns/op        1260.70 MB/s       15178 B/op         88 allocs/op
    92  ```
    93  
    94  - [Small](https://github.com/bytedance/sonic/blob/main/testdata/small.go) (400B, 11 keys, 3 layers)
    95  ![small benchmarks](./docs/imgs/bench-small.png)
    96  - [Large](https://github.com/bytedance/sonic/blob/main/testdata/twitter.json) (635KB, 10000+ key, 6 layers)
    97  ![large benchmarks](./docs/imgs/bench-large.png)
    98  
    99  See [bench.sh](https://github.com/bytedance/sonic/blob/main/scripts/bench.sh) for benchmark codes.
   100  
   101  ## How it works
   102  
   103  See [INTRODUCTION.md](./docs/INTRODUCTION.md).
   104  
   105  ## Usage
   106  
   107  ### Marshal/Unmarshal
   108  
   109  Default behaviors are mostly consistent with `encoding/json`, except HTML escaping form (see [Escape HTML](https://github.com/bytedance/sonic/blob/main/README.md#escape-html)) and `SortKeys` feature (optional support see [Sort Keys](https://github.com/bytedance/sonic/blob/main/README.md#sort-keys)) that is **NOT** in conformity to [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259).
   110  
   111   ```go
   112  import "github.com/bytedance/sonic"
   113  
   114  var data YourSchema
   115  // Marshal
   116  output, err := sonic.Marshal(&data)
   117  // Unmarshal
   118  err := sonic.Unmarshal(output, &data)
   119   ```
   120  
   121  ### Streaming IO
   122  
   123  Sonic supports decoding json from `io.Reader` or encoding objects into `io.Writer`, aims at handling multiple values as well as reducing memory consumption.
   124  
   125  - encoder
   126  
   127  ```go
   128  var o1 = map[string]interface{}{
   129      "a": "b",
   130  }
   131  var o2 = 1
   132  var w = bytes.NewBuffer(nil)
   133  var enc = sonic.ConfigDefault.NewEncoder(w)
   134  enc.Encode(o1)
   135  enc.Encode(o2)
   136  fmt.Println(w.String())
   137  // Output:
   138  // {"a":"b"}
   139  // 1
   140  ```
   141  
   142  - decoder
   143  
   144  ```go
   145  var o =  map[string]interface{}{}
   146  var r = strings.NewReader(`{"a":"b"}{"1":"2"}`)
   147  var dec = sonic.ConfigDefault.NewDecoder(r)
   148  dec.Decode(&o)
   149  dec.Decode(&o)
   150  fmt.Printf("%+v", o)
   151  // Output:
   152  // map[1:2 a:b]
   153  ```
   154  
   155  ### Use Number/Use Int64
   156  
   157   ```go
   158  import "github.com/bytedance/sonic/decoder"
   159  
   160  var input = `1`
   161  var data interface{}
   162  
   163  // default float64
   164  dc := decoder.NewDecoder(input)
   165  dc.Decode(&data) // data == float64(1)
   166  // use json.Number
   167  dc = decoder.NewDecoder(input)
   168  dc.UseNumber()
   169  dc.Decode(&data) // data == json.Number("1")
   170  // use int64
   171  dc = decoder.NewDecoder(input)
   172  dc.UseInt64()
   173  dc.Decode(&data) // data == int64(1)
   174  
   175  root, err := sonic.GetFromString(input)
   176  // Get json.Number
   177  jn := root.Number()
   178  jm := root.InterfaceUseNumber().(json.Number) // jn == jm
   179  // Get float64
   180  fn := root.Float64()
   181  fm := root.Interface().(float64) // jn == jm
   182   ```
   183  
   184  ### Sort Keys
   185  
   186  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](https://github.com/facebook/zstd)), Use it like this:
   187  
   188  ```go
   189  import "github.com/bytedance/sonic"
   190  import "github.com/bytedance/sonic/encoder"
   191  
   192  // Binding map only
   193  m := map[string]interface{}{}
   194  v, err := encoder.Encode(m, encoder.SortMapKeys)
   195  
   196  // Or ast.Node.SortKeys() before marshal
   197  var root := sonic.Get(JSON)
   198  err := root.SortKeys()
   199  ```
   200  
   201  ### Escape HTML
   202  
   203  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`).
   204  
   205  ```go
   206  import "github.com/bytedance/sonic"
   207  
   208  v := map[string]string{"&&":"<>"}
   209  ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"}}`
   210  ```
   211  
   212  ### Compact Format
   213  
   214  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.
   215  
   216  ### Print Error
   217  
   218  If there invalid syntax in input JSON, sonic will return `decoder.SyntaxError`, which supports pretty-printing of error position
   219  
   220  ```go
   221  import "github.com/bytedance/sonic"
   222  import "github.com/bytedance/sonic/decoder"
   223  
   224  var data interface{}
   225  err := sonic.UnmarshalString("[[[}]]", &data)
   226  if err != nil {
   227      /* One line by default */
   228      println(e.Error()) // "Syntax error at index 3: invalid char\n\n\t[[[}]]\n\t...^..\n"
   229      /* Pretty print */
   230      if e, ok := err.(decoder.SyntaxError); ok {
   231          /*Syntax error at index 3: invalid char
   232  
   233              [[[}]]
   234              ...^..
   235          */
   236          print(e.Description())
   237      } else if me, ok := err.(*decoder.MismatchTypeError); ok {
   238          // decoder.MismatchTypeError is new to Sonic v1.6.0
   239          print(me.Description())
   240      }
   241  }
   242  ```
   243  
   244  #### Mismatched Types [Sonic v1.6.0]
   245  
   246  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.
   247  
   248  ```go
   249  import "github.com/bytedance/sonic"
   250  import "github.com/bytedance/sonic/decoder"
   251  
   252  var data = struct{
   253      A int
   254      B int
   255  }{}
   256  err := UnmarshalString(`{"A":"1","B":1}`, &data)
   257  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"
   258  fmt.Printf("%+v", data) // {A:0 B:1}
   259  ```
   260  
   261  ### Ast.Node
   262  
   263  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.
   264  
   265  #### Get/Index
   266  
   267  Search partial JSON by given paths, which must be non-negative integer or string, or nil
   268  
   269  ```go
   270  import "github.com/bytedance/sonic"
   271  
   272  input := []byte(`{"key1":[{},{"key2":{"key3":[1,2,3]}}]}`)
   273  
   274  // no path, returns entire json
   275  root, err := sonic.Get(input)
   276  raw := root.Raw() // == string(input)
   277  
   278  // multiple paths
   279  root, err := sonic.Get(input, "key1", 1, "key2")
   280  sub := root.Get("key3").Index(2).Int64() // == 3
   281  ```
   282  
   283  **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.
   284  
   285  #### Set/Unset
   286  
   287  Modify the json content by Set()/Unset()
   288  
   289  ```go
   290  import "github.com/bytedance/sonic"
   291  
   292  // Set
   293  exist, err := root.Set("key4", NewBool(true)) // exist == false
   294  alias1 := root.Get("key4")
   295  println(alias1.Valid()) // true
   296  alias2 := root.Index(1)
   297  println(alias1 == alias2) // true
   298  
   299  // Unset
   300  exist, err := root.UnsetByIndex(1) // exist == true
   301  println(root.Get("key4").Check()) // "value not exist"
   302  ```
   303  
   304  #### Serialize
   305  
   306  To encode `ast.Node` as json, use `MarshalJson()` or `json.Marshal()` (MUST pass the node's pointer)
   307  
   308  ```go
   309  import (
   310      "encoding/json"
   311      "github.com/bytedance/sonic"
   312  )
   313  
   314  buf, err := root.MarshalJson()
   315  println(string(buf))                // {"key1":[{},{"key2":{"key3":[1,2,3]}}]}
   316  exp, err := json.Marshal(&root)     // WARN: use pointer
   317  println(string(buf) == string(exp)) // true
   318  ```
   319  
   320  #### APIs
   321  
   322  - validation: `Check()`, `Error()`, `Valid()`, `Exist()`
   323  - searching: `Index()`, `Get()`, `IndexPair()`, `IndexOrGet()`, `GetByPath()`
   324  - go-type casting: `Int64()`, `Float64()`, `String()`, `Number()`, `Bool()`, `Map[UseNumber|UseNode]()`, `Array[UseNumber|UseNode]()`, `Interface[UseNumber|UseNode]()`
   325  - go-type packing: `NewRaw()`, `NewNumber()`, `NewNull()`, `NewBool()`, `NewString()`, `NewObject()`, `NewArray()`
   326  - iteration: `Values()`, `Properties()`, `ForEach()`, `SortKeys()`
   327  - modification: `Set()`, `SetByIndex()`, `Add()`
   328  
   329  ### Ast.Visitor
   330  
   331  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{}`:
   332  
   333  ```go
   334  type UserNode interface {}
   335  
   336  // the following types implement the UserNode interface.
   337  type (
   338      UserNull    struct{}
   339      UserBool    struct{ Value bool }
   340      UserInt64   struct{ Value int64 }
   341      UserFloat64 struct{ Value float64 }
   342      UserString  struct{ Value string }
   343      UserObject  struct{ Value map[string]UserNode }
   344      UserArray   struct{ Value []UserNode }
   345  )
   346  ```
   347  
   348  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.
   349  
   350  ```go
   351  func Preorder(str string, visitor Visitor, opts *VisitorOptions) error
   352  
   353  type Visitor interface {
   354      OnNull() error
   355      OnBool(v bool) error
   356      OnString(v string) error
   357      OnInt64(v int64, n json.Number) error
   358      OnFloat64(v float64, n json.Number) error
   359      OnObjectBegin(capacity int) error
   360      OnObjectKey(key string) error
   361      OnObjectEnd() error
   362      OnArrayBegin(capacity int) error
   363      OnArrayEnd() error
   364  }
   365  ```
   366  
   367  See [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) for detailed usage. We also implement a demo visitor for `UserNode` in [ast/visitor_test.go](https://github.com/bytedance/sonic/blob/main/ast/visitor_test.go).
   368  
   369  ## Compatibility
   370  
   371  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:
   372  
   373  - 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.
   374  - 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.
   375  
   376  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`
   377  
   378  - `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.
   379  - `ConfigStd`: the std-compatible config (`EscapeHTML=true`,`SortKeys=true`...) to run on sonic-supporting environment. It will fall back to `encoding/json`.
   380  - `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.
   381  
   382  ## Tips
   383  
   384  ### Pretouch
   385  
   386  Since Sonic uses [golang-asm](https://github.com/twitchyliquid64/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()`.
   387  
   388  ```go
   389  import (
   390      "reflect"
   391      "github.com/bytedance/sonic"
   392      "github.com/bytedance/sonic/option"
   393  )
   394  
   395  func init() {
   396      var v HugeStruct
   397  
   398      // For most large types (nesting depth <= option.DefaultMaxInlineDepth)
   399      err := sonic.Pretouch(reflect.TypeOf(v))
   400  
   401      // with more CompileOption...
   402      err := sonic.Pretouch(reflect.TypeOf(v),
   403          // If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth),
   404          // you can set compile recursive loops in Pretouch for better stability in JIT.
   405          option.WithCompileRecursiveDepth(loop),
   406          // For a large nested struct, try to set a smaller depth to reduce compiling time.
   407          option.WithCompileMaxInlineDepth(depth),
   408      )
   409  }
   410  ```
   411  
   412  ### Copy string
   413  
   414  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.
   415  
   416  - `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.
   417  
   418  ### Pass string or []byte?
   419  
   420  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.
   421  
   422  ### Accelerate `encoding.TextMarshaler`
   423  
   424  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](https://datatracker.ietf.org/doc/html/rfc8259).
   425  
   426  ### Better performance for generic data
   427  
   428  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:
   429  
   430  ```go
   431  import "github.com/bytedance/sonic"
   432  
   433  node, err := sonic.GetFromString(_TwitterJson, "statuses", 3, "user")
   434  var user User // your partial schema...
   435  err = sonic.UnmarshalString(node.Raw(), &user)
   436  ```
   437  
   438  Even if you don't have any schema, use `ast.Node` as the container of generic values instead of `map` or `interface`:
   439  
   440  ```go
   441  import "github.com/bytedance/sonic"
   442  
   443  root, err := sonic.GetFromString(_TwitterJson)
   444  user := root.GetByPath("statuses", 3, "user")  // === root.Get("status").Index(3).Get("user")
   445  err = user.Check()
   446  
   447  // err = user.LoadAll() // only call this when you want to use 'user' concurrently...
   448  go someFunc(user)
   449  ```
   450  
   451  Why? Because `ast.Node` stores its children using `array`:
   452  
   453  - `Array`'s performance is **much better** than `Map` when Inserting (Deserialize) and Scanning (Serialize) data;
   454  - **Hashing** (`map[x]`) is not as efficient as **Indexing** (`array[x]`), which `ast.Node` can conduct on **both array and object**;
   455  - Using `Interface()`/`Map()` means Sonic must parse all the underlying values, while `ast.Node` can parse them **on demand**.
   456  
   457  **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{}`
   458  
   459  ### Ast.Node or Ast.Visitor?
   460  
   461  For generic data, `ast.Node` should be enough for your needs in most cases.
   462  
   463  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.
   464  
   465  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.
   466  
   467  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](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) carefully if you decide to use this API.
   468  
   469  ## Community
   470  
   471  Sonic is a subproject of [CloudWeGo](https://www.cloudwego.io/). We are committed to building a cloud native ecosystem.