mirror of
https://github.com/ii64/sonic.git
synced 2026-06-24 02:16:45 +08:00
feat: add compatible API (#233)
* feat: add compatible API * test: add api compatibility test * fmt * test: adjust CI * fix: no trailing '\n' for std `Marshal()` * doc: update README.md * test: add windows CI * doc: update README.md * doc: update README.md * fmt: add comments and refator
This commit is contained in:
parent
8917a0a7d9
commit
ced28302a9
17 changed files with 3608 additions and 2970 deletions
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
|
|
@ -55,7 +55,7 @@ jobs:
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
name: Push Check Linux
|
name: Push Check All
|
||||||
|
|
||||||
on: push
|
on: push
|
||||||
|
|
||||||
|
|
@ -7,7 +7,8 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
go-version: [1.15.x, 1.16.x, 1.17.x]
|
go-version: [1.15.x, 1.16.x, 1.17.x]
|
||||||
runs-on: self-hosted
|
os: [self-hosted]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
|
@ -24,4 +25,4 @@ jobs:
|
||||||
${{ runner.os }}-go-
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
- name: Unit Test
|
- name: Unit Test
|
||||||
run: GOMAXPROCS=4 go test -v -gcflags=-d=checkptr=0 ./...
|
run: go test -v -gcflags=-d=checkptr=0 ./...
|
||||||
27
.github/workflows/push-check-arm.yml
vendored
Normal file
27
.github/workflows/push-check-arm.yml
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
name: Push Check ARM
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
go-version: [1.15.x, 1.18.x]
|
||||||
|
runs-on: [self-hosted, arm]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v2
|
||||||
|
with:
|
||||||
|
go-version: ${{ matrix.go-version }}
|
||||||
|
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/go/pkg/mod
|
||||||
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
|
- name: Compatibility Test
|
||||||
|
run: go test -v -gcflags=-d=checkptr=0 .
|
||||||
|
|
@ -24,4 +24,4 @@ jobs:
|
||||||
run: GOMAXPROCS=4 go test -v -gcflags=-d=checkptr=0 -race ./...
|
run: GOMAXPROCS=4 go test -v -gcflags=-d=checkptr=0 -race ./...
|
||||||
|
|
||||||
- name: Generic Test
|
- name: Generic Test
|
||||||
run: go test -v -gcflags=-d=checkptr=0 -race ./generic_test
|
run: GOMAXPROCS=4 go test -v -gcflags=-d=checkptr=0 -race ./generic_test
|
||||||
43
.github/workflows/push-check-windows.yml
vendored
43
.github/workflows/push-check-windows.yml
vendored
|
|
@ -1,26 +1,27 @@
|
||||||
# name: Push Check Go Windows
|
name: Push Check Windows
|
||||||
|
|
||||||
# on: push
|
on: push
|
||||||
|
|
||||||
# jobs:
|
jobs:
|
||||||
# build:
|
build:
|
||||||
# strategy:
|
strategy:
|
||||||
# matrix:
|
matrix:
|
||||||
# go-version: [1.15.x, 1.16.x, 1.17.x, 1.18.x]
|
go-version: [1.15.x, 1.18.x]
|
||||||
# os: [windows-latest]
|
runs-on: windows-latest
|
||||||
# runs-on: ${{ matrix.os }}
|
steps:
|
||||||
# steps:
|
- uses: actions/checkout@v2
|
||||||
# - uses: actions/checkout@v2
|
|
||||||
|
|
||||||
# - name: Set up Go
|
- name: Set up Go
|
||||||
# uses: actions/setup-go@v3
|
uses: actions/setup-go@v2
|
||||||
# with:
|
with:
|
||||||
# go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
|
|
||||||
# - name: Unit Test
|
- uses: actions/cache@v2
|
||||||
# run: go test -v -gcflags -d=checkptr=0 -covermode atomic -coverprofile coverage.out ./...
|
with:
|
||||||
# env:
|
path: ~/go/pkg/mod
|
||||||
# GOMAXPROCS: 4
|
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-go-
|
||||||
|
|
||||||
# - name: Generic Test
|
- name: Compatibility Test
|
||||||
# run: go test -v -gcflags -d=checkptr=0 -covermode atomic ./generic_test
|
run: go test -v -gcflags=-d=checkptr=0 .
|
||||||
|
|
|
||||||
11
README.md
11
README.md
|
|
@ -4,7 +4,7 @@ A blazingly fast JSON serializing & deserializing library, accelerated by JI
|
||||||
|
|
||||||
## Requirement
|
## Requirement
|
||||||
- Go 1.15/1.16/1.17/1.18
|
- Go 1.15/1.16/1.17/1.18
|
||||||
- Linux/darwin OS
|
- Linux/MacOS/Windows
|
||||||
- Amd64 CPU with AVX instruction set
|
- Amd64 CPU with AVX instruction set
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
@ -260,6 +260,12 @@ println(string(buf) == string(exp)) // true
|
||||||
- iteration: `Values()`, `Properties()`, `ForEach()`, `SortKeys()`
|
- iteration: `Values()`, `Properties()`, `ForEach()`, `SortKeys()`
|
||||||
- modification: `Set()`, `SetByIndex()`, `Add()`
|
- modification: `Set()`, `SetByIndex()`, `Add()`
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
Sonic **DOSE 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 (ex: developing on M1 Mac but running on linux server), 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 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 whill 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 corresponding config , and some options will be invalid.
|
||||||
|
|
||||||
## Tips
|
## Tips
|
||||||
|
|
||||||
### Pretouch
|
### Pretouch
|
||||||
|
|
@ -317,3 +323,6 @@ Why? Because `ast.Node` stores its children using `array`:
|
||||||
- Using `Interface()`/`Map()` means Sonic must parse all the underlying values, while `ast.Node` can parse them **on demand**.
|
- 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, your 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{}`
|
**CAUTION:** `ast.Node` **DOESN'T** ensure concurrent security directly, due to its **lazy-load** design. However, your 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{}`
|
||||||
|
|
||||||
|
## Community
|
||||||
|
Sonic is a subproject of [CloudWeGo](https://www.cloudwego.io/). We are committed to building a cloud native ecosystem.
|
||||||
127
api.go
Normal file
127
api.go
Normal file
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonic
|
||||||
|
|
||||||
|
import (
|
||||||
|
`io`
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is a combination of sonic/encoder.Options and sonic/decoder.Options
|
||||||
|
type Config struct {
|
||||||
|
EscapeHTML bool
|
||||||
|
SortMapKeys bool
|
||||||
|
CompactMarshaler bool
|
||||||
|
NoQuoteTextMarshaler bool
|
||||||
|
UseInt64 bool
|
||||||
|
UseNumber bool
|
||||||
|
UseUnicodeErrors bool
|
||||||
|
DisallowUnknownFields bool
|
||||||
|
CopyString bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ConfigDefault is the default config of APIs, aiming at efficiency and safty.
|
||||||
|
ConfigDefault = Config{}.Froze()
|
||||||
|
|
||||||
|
// ConfigStd is the standard config of APIs, aiming at being compatible with encoding/json.
|
||||||
|
ConfigStd = Config{
|
||||||
|
EscapeHTML : true,
|
||||||
|
SortMapKeys: true,
|
||||||
|
CompactMarshaler: true,
|
||||||
|
CopyString : true,
|
||||||
|
}.Froze()
|
||||||
|
|
||||||
|
// ConfigFastest is the fastest config of APIs, aiming at speed.
|
||||||
|
ConfigFastest = Config{
|
||||||
|
NoQuoteTextMarshaler: true,
|
||||||
|
}.Froze()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// API a binding of specific config.
|
||||||
|
// This interface is inspired by github.com/json-iterator/go,
|
||||||
|
// and has same behaviors under equavilent config.
|
||||||
|
type API interface {
|
||||||
|
// MarshalToString returns the JSON encoding string of v
|
||||||
|
MarshalToString(v interface{}) (string, error)
|
||||||
|
// Marshal returns the JSON encoding bytes of v.
|
||||||
|
Marshal(v interface{}) ([]byte, error)
|
||||||
|
// MarshalIndent returns the JSON encoding bytes with indent and prefix.
|
||||||
|
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
|
||||||
|
// UnmarshalFromString parses the JSON-encoded bytes and stores the result in the value pointed to by v.
|
||||||
|
UnmarshalFromString(str string, v interface{}) error
|
||||||
|
// Unmarshal parses the JSON-encoded string and stores the result in the value pointed to by v.
|
||||||
|
Unmarshal(data []byte, v interface{}) error
|
||||||
|
// NewEncoder create a Encoder holding writer
|
||||||
|
NewEncoder(writer io.Writer) Encoder
|
||||||
|
// NewDecoder create a Decoder holding reader
|
||||||
|
NewDecoder(reader io.Reader) Decoder
|
||||||
|
// Valid validates the JSON-encoded bytes and reportes if it is valid
|
||||||
|
Valid(data []byte) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encoder encodes JSON into io.Writer
|
||||||
|
type Encoder interface {
|
||||||
|
// Encode writes the JSON encoding of v to the stream, followed by a newline character.
|
||||||
|
Encode(val interface{}) error
|
||||||
|
// SetEscapeHTML specifies whether problematic HTML characters
|
||||||
|
// should be escaped inside JSON quoted strings.
|
||||||
|
// The default behavior NOT ESCAPE
|
||||||
|
SetEscapeHTML(on bool)
|
||||||
|
// SetIndent instructs the encoder to format each subsequent encoded value
|
||||||
|
// as if indented by the package-level function Indent(dst, src, prefix, indent).
|
||||||
|
// Calling SetIndent("", "") disables indentation
|
||||||
|
SetIndent(prefix, indent string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoder decodes JSON from io.Read
|
||||||
|
type Decoder interface {
|
||||||
|
// Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
|
||||||
|
Decode(val interface{}) error
|
||||||
|
// Buffered returns a reader of the data remaining in the Decoder's buffer.
|
||||||
|
// The reader is valid until the next call to Decode.
|
||||||
|
Buffered() io.Reader
|
||||||
|
// DisallowUnknownFields causes the Decoder to return an error when the destination is a struct
|
||||||
|
// and the input contains object keys which do not match any non-ignored, exported fields in the destination.
|
||||||
|
DisallowUnknownFields()
|
||||||
|
// More reports whether there is another element in the current array or object being parsed.
|
||||||
|
More() bool
|
||||||
|
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a Number instead of as a float64.
|
||||||
|
UseNumber()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal returns the JSON encoding bytes of v.
|
||||||
|
func Marshal(val interface{}) ([]byte, error) {
|
||||||
|
return ConfigDefault.Marshal(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalString returns the JSON encoding string of v.
|
||||||
|
func MarshalString(val interface{}) (string, error) {
|
||||||
|
return ConfigDefault.MarshalToString(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
|
||||||
|
// NOTICE: This API copies given buffer by default,
|
||||||
|
// if you want to pass JSON more efficiently, use UnmarshalString instead.
|
||||||
|
func Unmarshal(buf []byte, val interface{}) error {
|
||||||
|
return ConfigDefault.Unmarshal(buf, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalString is like Unmarshal, except buf is a string.
|
||||||
|
func UnmarshalString(buf string, val interface{}) error {
|
||||||
|
return ConfigDefault.UnmarshalFromString(buf, val)
|
||||||
|
}
|
||||||
262
api_test.go
Normal file
262
api_test.go
Normal file
|
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonic
|
||||||
|
|
||||||
|
import (
|
||||||
|
`bytes`
|
||||||
|
`encoding/json`
|
||||||
|
`testing`
|
||||||
|
|
||||||
|
jsoniter `github.com/json-iterator/go`
|
||||||
|
`github.com/stretchr/testify/require`
|
||||||
|
)
|
||||||
|
|
||||||
|
var jt = jsoniter.Config{
|
||||||
|
ValidateJsonRawMessage: true,
|
||||||
|
}.Froze()
|
||||||
|
|
||||||
|
func TestCompatMarshalDefault(t *testing.T){
|
||||||
|
var obj = map[string]interface{}{
|
||||||
|
"c": json.RawMessage("[\"<&>\"]"),
|
||||||
|
}
|
||||||
|
sout, serr := ConfigDefault.Marshal(obj)
|
||||||
|
jout, jerr := jt.Marshal(obj)
|
||||||
|
require.Equal(t, jerr, serr)
|
||||||
|
require.Equal(t, string(jout), string(sout))
|
||||||
|
|
||||||
|
// obj = map[string]interface{}{
|
||||||
|
// "a": json.RawMessage(" [} "),
|
||||||
|
// }
|
||||||
|
// sout, serr = ConfigDefault.Marshal(obj)
|
||||||
|
// jout, jerr = json.Marshal(obj)
|
||||||
|
// require.NotNil(t, jerr)
|
||||||
|
// require.NotNil(t, serr)
|
||||||
|
// require.Equal(t, string(jout), string(sout))
|
||||||
|
|
||||||
|
obj = map[string]interface{}{
|
||||||
|
"a": json.RawMessage("1"),
|
||||||
|
}
|
||||||
|
sout, serr = ConfigDefault.MarshalIndent(obj, "", " ")
|
||||||
|
jout, jerr = jt.MarshalIndent(obj, "", " ")
|
||||||
|
require.Equal(t, jerr, serr)
|
||||||
|
require.Equal(t, string(jout), string(sout))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompatMarshalStd(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
var obj = map[string]interface{}{
|
||||||
|
"c": json.RawMessage(" [ \"<&>\" ] "),
|
||||||
|
"b": json.RawMessage(" [ ] "),
|
||||||
|
}
|
||||||
|
sout, serr := ConfigStd.Marshal(obj)
|
||||||
|
jout, jerr := json.Marshal(obj)
|
||||||
|
require.Equal(t, jerr, serr)
|
||||||
|
require.Equal(t, string(jout), string(sout))
|
||||||
|
|
||||||
|
obj = map[string]interface{}{
|
||||||
|
"a": json.RawMessage(" [} "),
|
||||||
|
}
|
||||||
|
sout, serr = ConfigStd.Marshal(obj)
|
||||||
|
jout, jerr = json.Marshal(obj)
|
||||||
|
require.NotNil(t, jerr)
|
||||||
|
require.NotNil(t, serr)
|
||||||
|
require.Equal(t, string(jout), string(sout))
|
||||||
|
|
||||||
|
obj = map[string]interface{}{
|
||||||
|
"a": json.RawMessage("1"),
|
||||||
|
}
|
||||||
|
sout, serr = ConfigStd.MarshalIndent(obj, "xxxx", " ")
|
||||||
|
jout, jerr = json.MarshalIndent(obj, "xxxx", " ")
|
||||||
|
require.Equal(t, jerr, serr)
|
||||||
|
require.Equal(t, string(jout), string(sout))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompatUnmarshalDefault(t *testing.T) {
|
||||||
|
var sobj = map[string]interface{}{}
|
||||||
|
var jobj = map[string]interface{}{}
|
||||||
|
var data = []byte(`{"a":-0}`)
|
||||||
|
var str = string(data)
|
||||||
|
serr := ConfigDefault.UnmarshalFromString(str, &sobj)
|
||||||
|
jerr := jt.UnmarshalFromString(str, &jobj)
|
||||||
|
require.Equal(t, jerr, serr)
|
||||||
|
require.Equal(t, jobj, sobj)
|
||||||
|
|
||||||
|
x := struct{A json.Number}{}
|
||||||
|
y := struct{A json.Number}{}
|
||||||
|
data = []byte(`{"A":"1", "B":-1}`)
|
||||||
|
serr = ConfigDefault.Unmarshal(data, &x)
|
||||||
|
jerr = jt.Unmarshal(data, &y)
|
||||||
|
require.Equal(t, jerr, serr)
|
||||||
|
require.Equal(t, y, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompatUnmarshalStd(t *testing.T) {
|
||||||
|
var sobj = map[string]interface{}{}
|
||||||
|
var jobj = map[string]interface{}{}
|
||||||
|
var data = []byte(`{"a":1.00000001E-10}`)
|
||||||
|
var str = string(data)
|
||||||
|
serr := ConfigStd.UnmarshalFromString(str, &sobj)
|
||||||
|
jerr := json.Unmarshal(data, &jobj)
|
||||||
|
require.Equal(t, jerr, serr)
|
||||||
|
require.Equal(t, jobj, sobj)
|
||||||
|
data[2] = '0'
|
||||||
|
require.Equal(t, jobj, sobj)
|
||||||
|
|
||||||
|
sobj = map[string]interface{}{}
|
||||||
|
jobj = map[string]interface{}{}
|
||||||
|
data = []byte(`{"a":1}`)
|
||||||
|
cfg := Config{
|
||||||
|
UseNumber: true,
|
||||||
|
}.Froze()
|
||||||
|
serr = cfg.Unmarshal(data, &sobj)
|
||||||
|
dec := json.NewDecoder(bytes.NewBuffer(data))
|
||||||
|
dec.UseNumber()
|
||||||
|
jerr = dec.Decode(&jobj)
|
||||||
|
require.Equal(t, jerr, serr)
|
||||||
|
require.Equal(t, jobj, sobj)
|
||||||
|
|
||||||
|
x := struct{A json.Number}{}
|
||||||
|
y := struct{A json.Number}{}
|
||||||
|
data = []byte(`{"A":"1", "B":-1}`)
|
||||||
|
cfg = Config{
|
||||||
|
DisallowUnknownFields: true,
|
||||||
|
}.Froze()
|
||||||
|
serr = cfg.Unmarshal(data, &x)
|
||||||
|
dec = json.NewDecoder(bytes.NewBuffer(data))
|
||||||
|
dec.UseNumber()
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
jerr = dec.Decode(&y)
|
||||||
|
require.Equal(t, jerr, serr)
|
||||||
|
require.Equal(t, y, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompatEncoderDefault(t *testing.T) {
|
||||||
|
var o = map[string]interface{}{
|
||||||
|
"a": "<>",
|
||||||
|
// "b": json.RawMessage(" [ ] "),
|
||||||
|
}
|
||||||
|
var w1 = bytes.NewBuffer(nil)
|
||||||
|
var w2 = bytes.NewBuffer(nil)
|
||||||
|
var enc1 = jt.NewEncoder(w1)
|
||||||
|
var enc2 = ConfigDefault.NewEncoder(w2)
|
||||||
|
|
||||||
|
require.Nil(t, enc1.Encode(o))
|
||||||
|
require.Nil(t, enc2.Encode(o))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
|
||||||
|
enc1.SetEscapeHTML(true)
|
||||||
|
enc2.SetEscapeHTML(true)
|
||||||
|
enc1.SetIndent("", " ")
|
||||||
|
enc2.SetIndent("", " ")
|
||||||
|
require.Nil(t, enc1.Encode(o))
|
||||||
|
require.Nil(t, enc2.Encode(o))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
|
||||||
|
enc1.SetEscapeHTML(false)
|
||||||
|
enc2.SetEscapeHTML(false)
|
||||||
|
enc1.SetIndent("", "")
|
||||||
|
enc2.SetIndent("", "")
|
||||||
|
require.Nil(t, enc1.Encode(o))
|
||||||
|
require.Nil(t, enc2.Encode(o))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompatEncoderStd(t *testing.T) {
|
||||||
|
var o = map[string]interface{}{
|
||||||
|
"a": "<>",
|
||||||
|
"b": json.RawMessage(" [ ] "),
|
||||||
|
}
|
||||||
|
var w1 = bytes.NewBuffer(nil)
|
||||||
|
var w2 = bytes.NewBuffer(nil)
|
||||||
|
var enc1 = json.NewEncoder(w1)
|
||||||
|
var enc2 = ConfigStd.NewEncoder(w2)
|
||||||
|
|
||||||
|
require.Nil(t, enc1.Encode(o))
|
||||||
|
require.Nil(t, enc2.Encode(o))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
|
||||||
|
enc1.SetEscapeHTML(true)
|
||||||
|
enc2.SetEscapeHTML(true)
|
||||||
|
enc1.SetIndent("\n", " ")
|
||||||
|
enc2.SetIndent("\n", " ")
|
||||||
|
require.Nil(t, enc1.Encode(o))
|
||||||
|
require.Nil(t, enc2.Encode(o))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
|
||||||
|
enc1.SetEscapeHTML(false)
|
||||||
|
enc2.SetEscapeHTML(false)
|
||||||
|
enc1.SetIndent("", "")
|
||||||
|
enc2.SetIndent("", "")
|
||||||
|
require.Nil(t, enc1.Encode(o))
|
||||||
|
require.Nil(t, enc2.Encode(o))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompatDecoderStd(t *testing.T) {
|
||||||
|
var o1 = map[string]interface{}{}
|
||||||
|
var o2 = map[string]interface{}{}
|
||||||
|
var s = `{"a":"b"} {"1":"2"} a {}`
|
||||||
|
var w1 = bytes.NewBuffer([]byte(s))
|
||||||
|
var w2 = bytes.NewBuffer([]byte(s))
|
||||||
|
var enc1 = json.NewDecoder(w1)
|
||||||
|
var enc2 = ConfigStd.NewDecoder(w2)
|
||||||
|
|
||||||
|
require.Equal(t, enc1.More(), enc2.More())
|
||||||
|
require.Nil(t, enc1.Decode(&o1))
|
||||||
|
require.Nil(t, enc2.Decode(&o2))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
|
||||||
|
require.Equal(t, enc1.More(), enc2.More())
|
||||||
|
require.Nil(t, enc1.Decode(&o1))
|
||||||
|
require.Nil(t, enc2.Decode(&o2))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
|
||||||
|
require.Equal(t, enc1.More(), enc2.More())
|
||||||
|
require.NotNil(t, enc1.Decode(&o1))
|
||||||
|
require.NotNil(t, enc2.Decode(&o2))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompatDecoderDefault(t *testing.T) {
|
||||||
|
var o1 = map[string]interface{}{}
|
||||||
|
var o2 = map[string]interface{}{}
|
||||||
|
var s = `{"a":"b"} {"1":"2"} a {}`
|
||||||
|
var w1 = bytes.NewBuffer([]byte(s))
|
||||||
|
var w2 = bytes.NewBuffer([]byte(s))
|
||||||
|
var enc1 = jt.NewDecoder(w1)
|
||||||
|
var enc2 = ConfigDefault.NewDecoder(w2)
|
||||||
|
|
||||||
|
require.Equal(t, enc1.More(), enc2.More())
|
||||||
|
require.Nil(t, enc1.Decode(&o1))
|
||||||
|
require.Nil(t, enc2.Decode(&o2))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
|
||||||
|
require.Equal(t, enc1.More(), enc2.More())
|
||||||
|
require.Nil(t, enc1.Decode(&o1))
|
||||||
|
require.Nil(t, enc2.Decode(&o2))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
|
||||||
|
require.Equal(t, enc1.More(), enc2.More())
|
||||||
|
require.NotNil(t, enc1.Decode(&o1))
|
||||||
|
require.NotNil(t, enc2.Decode(&o2))
|
||||||
|
require.Equal(t, w1.String(), w2.String())
|
||||||
|
|
||||||
|
// require.Equal(t, enc1.More(), enc2.More())
|
||||||
|
// require.NotNil(t, enc1.Decode(&o1))
|
||||||
|
// require.NotNil(t, enc2.Decode(&o2))
|
||||||
|
// require.Equal(t, w1.String(), w2.String())
|
||||||
|
}
|
||||||
117
compat.go
Normal file
117
compat.go
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
// +build !amd64
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2021 ByteDance Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sonic
|
||||||
|
|
||||||
|
import (
|
||||||
|
`bytes`
|
||||||
|
`encoding/json`
|
||||||
|
`io`
|
||||||
|
)
|
||||||
|
|
||||||
|
type frozenConfig struct {
|
||||||
|
Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// Froze convert the Config to API
|
||||||
|
func (cfg Config) Froze() API {
|
||||||
|
api := &frozenConfig{Config: cfg}
|
||||||
|
return api
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *frozenConfig) marshalOptions(val interface{}, prefix, indent string) ([]byte, error) {
|
||||||
|
w := bytes.NewBuffer([]byte{})
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.SetEscapeHTML(cfg.EscapeHTML)
|
||||||
|
enc.SetIndent(prefix, indent)
|
||||||
|
err := enc.Encode(val)
|
||||||
|
out := w.Bytes()
|
||||||
|
|
||||||
|
// json.Encoder always appends '\n' after encoding,
|
||||||
|
// which is not same with json.Marshal()
|
||||||
|
if len(out) > 0 && out[len(out)-1] == '\n' {
|
||||||
|
out = out[:len(out)-1]
|
||||||
|
}
|
||||||
|
return out, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) Marshal(val interface{}) ([]byte, error) {
|
||||||
|
if !cfg.EscapeHTML {
|
||||||
|
return cfg.marshalOptions(val, "", "")
|
||||||
|
}
|
||||||
|
return json.Marshal(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalToString is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) MarshalToString(val interface{}) (string, error) {
|
||||||
|
out, err := cfg.Marshal(val)
|
||||||
|
return string(out), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalIndent is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) MarshalIndent(val interface{}, prefix, indent string) ([]byte, error) {
|
||||||
|
if !cfg.EscapeHTML {
|
||||||
|
return cfg.marshalOptions(val, prefix, indent)
|
||||||
|
}
|
||||||
|
return json.MarshalIndent(val, prefix, indent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalFromString is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) UnmarshalFromString(buf string, val interface{}) error {
|
||||||
|
r := bytes.NewBufferString(buf)
|
||||||
|
dec := json.NewDecoder(r)
|
||||||
|
if cfg.UseNumber {
|
||||||
|
dec.UseNumber()
|
||||||
|
}
|
||||||
|
if cfg.DisallowUnknownFields {
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
}
|
||||||
|
return dec.Decode(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) Unmarshal(buf []byte, val interface{}) error {
|
||||||
|
return cfg.UnmarshalFromString(string(buf), val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) NewEncoder(writer io.Writer) Encoder {
|
||||||
|
enc := json.NewEncoder(writer)
|
||||||
|
if !cfg.EscapeHTML {
|
||||||
|
enc.SetEscapeHTML(cfg.EscapeHTML)
|
||||||
|
}
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) NewDecoder(reader io.Reader) Decoder {
|
||||||
|
dec := json.NewDecoder(reader)
|
||||||
|
if cfg.UseNumber {
|
||||||
|
dec.UseNumber()
|
||||||
|
}
|
||||||
|
if cfg.DisallowUnknownFields {
|
||||||
|
dec.DisallowUnknownFields()
|
||||||
|
}
|
||||||
|
return dec
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) Valid(data []byte) bool {
|
||||||
|
return json.Valid(data)
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
*
|
*
|
||||||
|
|
@ -17,12 +19,12 @@
|
||||||
package sonic
|
package sonic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
`encoding/json`
|
||||||
"reflect"
|
`reflect`
|
||||||
"strings"
|
`strings`
|
||||||
"testing"
|
`testing`
|
||||||
|
|
||||||
"github.com/bytedance/sonic/decoder"
|
`github.com/bytedance/sonic/decoder`
|
||||||
)
|
)
|
||||||
|
|
||||||
type atofTest struct {
|
type atofTest struct {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,24 @@ const (
|
||||||
_F_allow_control = 31
|
_F_allow_control = 31
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Options uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
OptionUseInt64 Options = 1 << _F_use_int64
|
||||||
|
OptionUseNumber Options = 1 << _F_use_number
|
||||||
|
OptionUseUnicodeErrors Options = 1 << _F_disable_urc
|
||||||
|
OptionDisableUnknown Options = 1 << _F_disable_unknown
|
||||||
|
OptionCopyString Options = 1 << _F_copy_string
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *Decoder) SetOptions(opts Options) {
|
||||||
|
if (opts & 1<<_F_use_number != 0) && (opts & 1<<_F_use_int64 != 0) {
|
||||||
|
panic("can't set OptionUseInt64 and OptionUseNumber both!")
|
||||||
|
}
|
||||||
|
self.f = uint64(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Decoder is the decoder context object
|
// Decoder is the decoder context object
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
i int
|
i int
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
*
|
*
|
||||||
|
|
@ -160,7 +162,7 @@ func TestRoundtripStringTag(t *testing.T) {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
// Indent with a tab prefix to make the multi-line string
|
// Indent with a tab prefix to make the multi-line string
|
||||||
// literals in the table nicer to read.
|
// literals in the table nicer to read.
|
||||||
got, err := encoder.EncodeIndented(&test.in, "\t\t\t", "\t", 0)
|
got, err := encoder.EncodeIndented(&test.in, " ", " ", 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
128
sonic.go
128
sonic.go
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
*
|
*
|
||||||
|
|
@ -18,6 +20,7 @@
|
||||||
package sonic
|
package sonic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
`io`
|
||||||
`reflect`
|
`reflect`
|
||||||
|
|
||||||
`github.com/bytedance/sonic/ast`
|
`github.com/bytedance/sonic/ast`
|
||||||
|
|
@ -28,36 +31,7 @@ import (
|
||||||
`github.com/bytedance/sonic/internal/rt`
|
`github.com/bytedance/sonic/internal/rt`
|
||||||
)
|
)
|
||||||
|
|
||||||
// Marshal returns the JSON encoding bytes of v.
|
func checkTrailings(buf string, pos int) error {
|
||||||
func Marshal(val interface{}) ([]byte, error) {
|
|
||||||
return encoder.Encode(val, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalString returns the JSON encoding string of v.
|
|
||||||
func MarshalString(val interface{}) (string, error) {
|
|
||||||
buf, err := encoder.Encode(val, 0)
|
|
||||||
return rt.Mem2Str(buf), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmarshal parses the JSON-encoded data and stores the result in the value
|
|
||||||
// pointed to by v.
|
|
||||||
// NOTICE: This API copies given buffer by default,
|
|
||||||
// if you want to pass JSON more efficiently, use UnmarshalString instead.
|
|
||||||
func Unmarshal(buf []byte, val interface{}) error {
|
|
||||||
return UnmarshalString(string(buf), val)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalString is like Unmarshal, except buf is a string.
|
|
||||||
func UnmarshalString(buf string, val interface{}) error {
|
|
||||||
dec := decoder.NewDecoder(buf)
|
|
||||||
err := dec.Decode(val)
|
|
||||||
pos := dec.Pos()
|
|
||||||
|
|
||||||
/* check for errors */
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
/* skip all the trailing spaces */
|
/* skip all the trailing spaces */
|
||||||
if pos != len(buf) {
|
if pos != len(buf) {
|
||||||
for pos < len(buf) && (types.SPACE_MASK & (1 << buf[pos])) != 0 {
|
for pos < len(buf) && (types.SPACE_MASK & (1 << buf[pos])) != 0 {
|
||||||
|
|
@ -73,11 +47,103 @@ func UnmarshalString(buf string, val interface{}) error {
|
||||||
/* junk after JSON value */
|
/* junk after JSON value */
|
||||||
return decoder.SyntaxError {
|
return decoder.SyntaxError {
|
||||||
Src : buf,
|
Src : buf,
|
||||||
Pos : dec.Pos(),
|
Pos : pos,
|
||||||
Code : types.ERR_INVALID_CHAR,
|
Code : types.ERR_INVALID_CHAR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type frozenConfig struct {
|
||||||
|
Config
|
||||||
|
encoderOpts encoder.Options
|
||||||
|
decoderOpts decoder.Options
|
||||||
|
}
|
||||||
|
|
||||||
|
// Froze convert the Config to API
|
||||||
|
func (cfg Config) Froze() API {
|
||||||
|
api := &frozenConfig{Config: cfg}
|
||||||
|
if cfg.EscapeHTML {
|
||||||
|
api.encoderOpts |= encoder.EscapeHTML
|
||||||
|
}
|
||||||
|
if cfg.SortMapKeys {
|
||||||
|
api.encoderOpts |= encoder.SortMapKeys
|
||||||
|
}
|
||||||
|
if cfg.CompactMarshaler {
|
||||||
|
api.encoderOpts |= encoder.CompactMarshaler
|
||||||
|
}
|
||||||
|
if cfg.NoQuoteTextMarshaler {
|
||||||
|
api.encoderOpts |= encoder.NoQuoteTextMarshaler
|
||||||
|
}
|
||||||
|
if cfg.UseInt64 {
|
||||||
|
api.decoderOpts |= decoder.OptionUseInt64
|
||||||
|
}
|
||||||
|
if cfg.UseNumber {
|
||||||
|
api.decoderOpts |= decoder.OptionUseNumber
|
||||||
|
}
|
||||||
|
if cfg.DisallowUnknownFields {
|
||||||
|
api.decoderOpts |= decoder.OptionDisableUnknown
|
||||||
|
}
|
||||||
|
if cfg.CopyString {
|
||||||
|
api.decoderOpts |= decoder.OptionCopyString
|
||||||
|
}
|
||||||
|
return api
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) Marshal(val interface{}) ([]byte, error) {
|
||||||
|
return encoder.Encode(val, cfg.encoderOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalToString is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) MarshalToString(val interface{}) (string, error) {
|
||||||
|
buf, err := encoder.Encode(val, cfg.encoderOpts)
|
||||||
|
return rt.Mem2Str(buf), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalIndent is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) MarshalIndent(val interface{}, prefix, indent string) ([]byte, error) {
|
||||||
|
return encoder.EncodeIndented(val, prefix, indent, cfg.encoderOpts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalFromString is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) UnmarshalFromString(buf string, val interface{}) error {
|
||||||
|
dec := decoder.NewDecoder(buf)
|
||||||
|
dec.SetOptions(cfg.decoderOpts)
|
||||||
|
|
||||||
|
err := dec.Decode(val)
|
||||||
|
pos := dec.Pos()
|
||||||
|
|
||||||
|
/* check for errors */
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return checkTrailings(buf, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) Unmarshal(buf []byte, val interface{}) error {
|
||||||
|
return cfg.UnmarshalFromString(string(buf), val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEncoder is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) NewEncoder(writer io.Writer) Encoder {
|
||||||
|
enc := encoder.NewStreamEncoder(writer)
|
||||||
|
enc.Opts = cfg.encoderOpts
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDecoder is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) NewDecoder(reader io.Reader) Decoder {
|
||||||
|
dec := decoder.NewStreamDecoder(reader)
|
||||||
|
dec.SetOptions(cfg.decoderOpts)
|
||||||
|
return dec
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid is implemented by sonic
|
||||||
|
func (cfg *frozenConfig) Valid(data []byte) bool {
|
||||||
|
ok, _ := encoder.Valid(data)
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
|
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
|
||||||
// order to reduce the first-hit latency.
|
// order to reduce the first-hit latency.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// +build amd64
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2021 ByteDance Inc.
|
* Copyright 2021 ByteDance Inc.
|
||||||
*
|
*
|
||||||
|
|
@ -17,11 +19,11 @@
|
||||||
package sonic
|
package sonic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
`encoding/json`
|
||||||
"reflect"
|
`reflect`
|
||||||
"testing"
|
`testing`
|
||||||
|
|
||||||
"github.com/bytedance/sonic/decoder"
|
`github.com/bytedance/sonic/decoder`
|
||||||
)
|
)
|
||||||
|
|
||||||
type useInt64Test struct {
|
type useInt64Test struct {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue