update dependencies
CI / build (map[arch:arm64 deps:gcc-aarch64-linux-gnu libc6-dev-arm64-cross goarch:arm64 os:linux prefix:aarch64-linux-gnu]) (push) Has been skipped
CI / build (map[arch:armhf deps:gcc-arm-linux-gnueabihf libc6-dev-armhf-cross goarch:arm os:linux prefix:arm-linux-gnueabihf]) (push) Has been skipped
CI / build-static (push) Failing after 3m12s
CI / build (map[arch:amd64 deps:gcc goarch:amd64 os:linux prefix:]) (push) Has been skipped

This commit is contained in:
kaedwen committed 2024-10-18 18:02:01 +02:00
1 parent 78146d98f7
commit 898413d149
754 files changed
+165174 -133195

No files matched your search

+3 -2
View File
@@ -76,12 +76,13 @@ var ErrHelp = errors.New("help requested by user")
// ErrVersion indicates that the builtin --version was provided
var ErrVersion = errors.New("version requested by user")
// for monkey patching in example code
// for monkey patching in example and test code
var mustParseExit = os.Exit
var mustParseOut io.Writer = os.Stdout
// MustParse processes command line arguments and exits upon failure
func MustParse(dest ...interface{}) *Parser {
return mustParse(Config{Exit: mustParseExit}, dest...)
return mustParse(Config{Exit: mustParseExit, Out: mustParseOut}, dest...)
}
// mustParse is a helper that facilitates testing
+4 -1
View File
@@ -49,4 +49,7 @@ ast/bench.sh
!testdata/*.json.gz
fuzz/testdata
*__debug_bin
*__debug_bin*
*pprof
*coverage.txt
tools/venv/*
+36 -12
View File
@@ -6,9 +6,9 @@ A blazingly fast JSON serializing & deserializing library, accelerated by JI
## Requirement
- Go 1.16~1.22
- Linux / MacOS / Windows(need go1.17 above)
- Amd64 ARCH
- Go: 1.17~1.23
- OS: Linux / MacOS / Windows
- CPU: AMD64 / ARM64(need go1.20 above)
## Features
@@ -282,6 +282,22 @@ 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.
#### SearchOption
`Searcher` provides some options for user to meet different needs:
```go
opts := ast.SearchOption{ CopyReturn: true ... }
val, err := sonic.GetWithOptions(JSON, opts, "key")
```
- CopyReturn
Indicate the searcher to copy the result JSON string instead of refer from the input. This can help to reduce memory usage if you cache the results
- ConcurentRead
Since `ast.Node` use `Lazy-Load` design, it doesn't support Concurrently-Read by default. If you want to read it concurrently, please specify it.
- ValidateJSON
Indicate the searcher to validate the entire JSON. This option is enabled by default, which slow down the search speed a little.
#### Set/Unset
Modify the json content by Set()/Unset()
@@ -368,16 +384,12 @@ See [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.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:
For developers who want to use sonic to meet diffirent scenarios, we provide some integrated configs as `sonic.API`
- 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.
- `ConfigDefault`: the sonic's default config (`EscapeHTML=false`,`SortKeys=false`...) to run sonic fast meanwhile ensure security.
- `ConfigStd`: the std-compatible config (`EscapeHTML=true`,`SortKeys=true`...)
- `ConfigFastest`: the fastest config (`NoQuoteTextMarshaler=true`) to run on sonic as fast as possible.
Sonic **DOES NOT** ensure to support all environments, due to the difficulty of developing high-performance codes. On non-sonic-supporting environment, the implementation will fall back to `encoding/json`. Thus beflow configs will all equal to `ConfigStd`.
## Tips
@@ -466,6 +478,18 @@ For better performance, in previous case the `ast.Visitor` will be the better ch
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.
### Buffer Size
Sonic use memory pool in many places like `encoder.Encode`, `ast.Node.MarshalJSON` to improve performace, which may produce more memory usage (in-use) when server's load is high. See [issue 614](https://github.com/bytedance/sonic/issues/614). Therefore, we introduce some options to let user control the behavior of memory pool. See [option](https://pkg.go.dev/github.com/bytedance/sonic@v1.11.9/option#pkg-variables) package.
### Faster JSON skip
For security, sonic use [FSM](native/skip_one.c) algorithm to validate JSON when decoding raw JSON or encoding `json.Marshaler`, which is much slower (1~10x) than [SIMD-searching-pair](native/skip_one_fast.c) algorithm. If user has many redundant JSON value and DO NOT NEED to strictly validate JSON correctness, you can enable below options:
- `Config.NoValidateSkipJSON`: for faster skipping JSON when decoding, such as unknown fields, json.Unmarshaler(json.RawMessage), mismatched values, and redundant array elements
- `Config.NoValidateJSONMarshaler`: avoid validating JSON when encoding `json.Marshaler`
- `SearchOption.ValidateJSON`: indicates if validate located JSON value when `Get`
## Community
Sonic is a subproject of [CloudWeGo](https://www.cloudwego.io/). We are committed to building a cloud native ecosystem.
+37 -13
View File
@@ -6,9 +6,9 @@
## 依赖
- Go 1.16~1.22
- Linux / MacOS / Windows(需要 Go1.17 以上)
- Amd64 架构
- Go: 1.17~1.23
- OS: Linux / MacOS / Windows
- CPU: AMD64 / ARM64(需要 Go1.20 以上)
## 接口
@@ -260,7 +260,7 @@ fmt.Printf("%+v", data) // {A:0 B:1}
### `Ast.Node`
Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列化和反序列化,并提供了获取和修改通用数据的鲁棒的 API。
Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列化和反序列化,并提供了获取和修改JSON数据的鲁棒的 API。
#### 查找/索引
@@ -282,6 +282,22 @@ sub := root.Get("key3").Index(2).Int64() // == 3
**注意**:由于 `Index()` 使用偏移量来定位数据,比使用扫描的 `Get()` 要快的多,建议尽可能的使用 `Index` 。 Sonic 也提供了另一个 API, `IndexOrGet()` ,以偏移量为基础并且也确保键的匹配。
#### 查找选项
`ast.Searcher`提供了一些选项,以满足用户的不同需求:
```
opts:= ast.SearchOption{CopyReturn: true…}
Val, err:= sonic。gettwithoptions (JSON, opts, "key")
```
- CopyReturn
指示搜索器复制结果JSON字符串,而不是从输入引用。如果用户缓存结果,这有助于减少内存使用
- ConcurentRead
因为`ast.Node`使用`Lazy-Load`设计,默认不支持并发读取。如果您想同时读取,请指定它。
- ValidateJSON
指示搜索器来验证整个JSON。默认情况下启用该选项, 但是对于查找速度有一定影响。
#### 修改
使用 `Set()` / `Unset()` 修改 json 的内容
@@ -368,16 +384,12 @@ type Visitor interface {
## 兼容性
由于开发高性能代码的困难性, Sonic **不**保证对所有环境的支持。对于在不同环境中使用 Sonic 构建应用程序的开发者,我们有以下建议:
对于想要使用sonic来满足不同场景的开发人员,我们提供了一些集成配置:
- 在 **Mac M1** 上开发:确保在您的计算机上安装了 Rosetta 2,并在构建时设置 `GOARCH=amd64` 。 Rosetta 2 可以自动将 x86 二进制文件转换为 arm64 二进制文件,并在 Mac M1 上运行 x86 应用程序。
- 在 **Linux arm64** 上开发:您可以安装 qemu 并使用 `qemu-x86_64 -cpu max` 命令来将 x86 二进制文件转换为 arm64 二进制文件。qemu可以实现与Mac M1上的Rosetta 2类似的转换效果。
对于希望在不使用 qemu 下使用 sonic 的开发者,或者希望处理 JSON 时与 `encoding/JSON` 严格保持一致的开发者,我们在 `sonic.API` 中提供了一些兼容性 API
- `ConfigDefault`: 在支持 sonic 的环境下 sonic 的默认配置(`EscapeHTML=false`,`SortKeys=false`等)。行为与具有相应配置的 `encoding/json` 一致,一些选项,如 `SortKeys=false` 将无效。
- `ConfigStd`: 在支持 sonic 的环境下与标准库兼容的配置(`EscapeHTML=true`,`SortKeys=true`等)。行为与 `encoding/json` 一致。
- `ConfigFastest`: 在支持 sonic 的环境下运行最快的配置(`NoQuoteTextMarshaler=true`)。行为与具有相应配置的 `encoding/json` 一致,某些选项将无效。
- `ConfigDefault`: sonic的默认配置 (`EscapeHTML=false`, `SortKeys=false`…) 保证性能同时兼顾安全性。
- `ConfigStd`: 与 `encoding/json` 保证完全兼容的配置
- `ConfigFastest`: 最快的配置(`NoQuoteTextMarshaler=true...`) 保证性能最优但是会缺少一些安全性检查(validate UTF8 等)
Sonic **不**确保支持所有环境,由于开发高性能代码的困难。在不支持声音的环境中,实现将回落到 `encoding/json`。因此上述配置将全部等于`ConfigStd`。
## 注意事项
@@ -464,6 +476,18 @@ go someFunc(user)
但是,`ast.Visitor` 并不是一个很易用的 API。你可能需要写大量的代码去实现自己的 `ast.Visitor`,并且需要在解析过程中仔细维护树的层级。如果你决定要使用这个 API,请先仔细阅读 [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) 中的注释。
### 缓冲区大小
Sonic在许多地方使用内存池,如`encoder.Encode`, `ast.Node.MarshalJSON`等来提高性能,这可能会在服务器负载高时产生更多的内存使用(in-use)。参见[issue 614](https://github.com/bytedance/sonic/issues/614)。因此,我们引入了一些选项来让用户配置内存池的行为。参见[option](https://pkg.go.dev/github.com/bytedance/sonic@v1.11.9/option#pkg-variables)包。
### 更快的 JSON Skip
为了安全起见,在跳过原始JSON 时,sonic decoder 默认使用[FSM](native/skip_one.c)算法扫描来跳过同时校验 JSON。它相比[SIMD-searching-pair](native/skip_one_fast.c)算法跳过要慢得多(1~10倍)。如果用户有很多冗余的JSON值,并且不需要严格验证JSON的正确性,你可以启用以下选项:
- `Config.NoValidateSkipJSON`: 用于在解码时更快地跳过JSON,例如未知字段,`json.RawMessage`,不匹配的值和冗余的数组元素等
- `Config.NoValidateJSONMarshaler`: 编码JSON时避免验证JSON。封送拆收器
- `SearchOption.ValidateJSON`: 指示当`Get`时是否验证定位的JSON值
## 社区
Sonic 是 [CloudWeGo](https://www.cloudwego.io/) 下的一个子项目。我们致力于构建云原生生态系统。
+33
View File
@@ -23,6 +23,16 @@ import (
`github.com/bytedance/sonic/internal/rt`
)
const (
// UseStdJSON indicates you are using fallback implementation (encoding/json)
UseStdJSON = iota
// UseSonicJSON indicates you are using real sonic implementation
UseSonicJSON
)
// APIKind is the kind of API, 0 is std json, 1 is sonic.
const APIKind = apiKind
// Config is a combination of sonic/encoder.Options and sonic/decoder.Options
type Config struct {
// EscapeHTML indicates encoder to escape all HTML characters
@@ -74,9 +84,16 @@ type Config struct {
// NoValidateJSONMarshaler indicates that the encoder should not validate the output string
// after encoding the JSONMarshaler to JSON.
NoValidateJSONMarshaler bool
// NoValidateJSONSkip indicates the decoder should not validate the JSON value when skipping it,
// such as unknown-fields, mismatched-type, redundant elements..
NoValidateJSONSkip bool
// NoEncoderNewline indicates that the encoder should not add a newline after every message
NoEncoderNewline bool
// Encode Infinity or Nan float into `null`, instead of returning an error.
EncodeNullForInfOrNan bool
}
var (
@@ -96,6 +113,7 @@ var (
ConfigFastest = Config{
NoQuoteTextMarshaler: true,
NoValidateJSONMarshaler: true,
NoValidateJSONSkip: true,
}.Froze()
)
@@ -157,6 +175,13 @@ func Marshal(val interface{}) ([]byte, error) {
return ConfigDefault.Marshal(val)
}
// MarshalIndent is like Marshal but applies Indent to format the output.
// Each JSON element in the output will begin on a new line beginning with prefix
// followed by one or more copies of indent according to the indentation nesting.
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
return ConfigDefault.MarshalIndent(v, prefix, indent)
}
// MarshalString returns the JSON encoding string of v.
func MarshalString(val interface{}) (string, error) {
return ConfigDefault.MarshalToString(val)
@@ -189,6 +214,14 @@ func Get(src []byte, path ...interface{}) (ast.Node, error) {
return GetCopyFromString(rt.Mem2Str(src), path...)
}
//GetWithOptions searches and locates the given path from src json,
// with specific options of ast.Searcher
func GetWithOptions(src []byte, opts ast.SearchOptions, path ...interface{}) (ast.Node, error) {
s := ast.NewSearcher(rt.Mem2Str(src))
s.SearchOptions = opts
return s.GetByPath(path...)
}
// GetFromString is same with Get except src is string.
//
// WARNING: The returned JSON is **Referenced** from the input.
+2 -2
View File
@@ -1,5 +1,5 @@
//go:build (amd64 && go1.16 && !go1.23) || (arm64 && go1.20 && !go1.23)
// +build amd64,go1.16,!go1.23 arm64,go1.20,!go1.23
//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24)
// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24
/*
* Copyright 2022 ByteDance Inc.
+2 -2
View File
@@ -1,4 +1,4 @@
// +build !amd64,!arm64 go1.23 !go1.16 arm64,!go1.20
// +build !amd64,!arm64 go1.24 !go1.17 arm64,!go1.20
/*
* Copyright 2022 ByteDance Inc.
@@ -27,7 +27,7 @@ import (
)
func init() {
println("WARNING:(ast) sonic only supports Go1.16~1.22, but your environment is not suitable")
println("WARNING:(ast) sonic only supports go1.17~1.23, but your environment is not suitable")
}
func quote(buf *[]byte, val string) {
+75 -14
View File
@@ -17,8 +17,10 @@
package ast
import (
`sort`
`unsafe`
"sort"
"unsafe"
"github.com/bytedance/sonic/internal/caching"
)
type nodeChunk [_DEFAULT_NODE_CAP]Node
@@ -90,18 +92,11 @@ func (self *linkedNodes) Pop() {
self.size--
}
func (self *linkedPairs) Pop() {
if self == nil || self.size == 0 {
return
}
self.Set(self.size-1, Pair{})
self.size--
}
func (self *linkedNodes) Push(v Node) {
self.Set(self.size, v)
}
func (self *linkedNodes) Set(i int, v Node) {
if i < _DEFAULT_NODE_CAP {
self.head[i] = v
@@ -195,11 +190,22 @@ func (self *linkedNodes) FromSlice(con []Node) {
type pairChunk [_DEFAULT_NODE_CAP]Pair
type linkedPairs struct {
index map[uint64]int
head pairChunk
tail []*pairChunk
size int
}
func (self *linkedPairs) BuildIndex() {
if self.index == nil {
self.index = make(map[uint64]int, self.size)
}
for i:=0; i<self.size; i++ {
p := self.At(i)
self.index[p.hash] = i
}
}
func (self *linkedPairs) Cap() int {
if self == nil {
return 0
@@ -233,7 +239,31 @@ func (self *linkedPairs) Push(v Pair) {
self.Set(self.size, v)
}
func (self *linkedPairs) Pop() {
if self == nil || self.size == 0 {
return
}
self.Unset(self.size-1)
self.size--
}
func (self *linkedPairs) Unset(i int) {
if self.index != nil {
p := self.At(i)
delete(self.index, p.hash)
}
self.set(i, Pair{})
}
func (self *linkedPairs) Set(i int, v Pair) {
if self.index != nil {
h := v.hash
self.index[h] = i
}
self.set(i, v)
}
func (self *linkedPairs) set(i int, v Pair) {
if i < _DEFAULT_NODE_CAP {
self.head[i] = v
if self.size <= i {
@@ -276,6 +306,21 @@ func (self *linkedPairs) growTailLength(l int) {
// linear search
func (self *linkedPairs) Get(key string) (*Pair, int) {
if self.index != nil {
// fast-path
i, ok := self.index[caching.StrHash(key)]
if ok {
n := self.At(i)
if n.Key == key {
return n, i
}
// hash conflicts
goto linear_search
} else {
return nil, -1
}
}
linear_search:
for i:=0; i<self.size; i++ {
if n := self.At(i); n.Key == key {
return n, i
@@ -313,15 +358,27 @@ func (self *linkedPairs) ToMap(con map[string]Node) {
}
}
func (self *linkedPairs) copyPairs(to []Pair, from []Pair, l int) {
copy(to, from)
if self.index != nil {
for i:=0; i<l; i++ {
// NOTICE: in case of user not pass hash, just cal it
h := caching.StrHash(from[i].Key)
from[i].hash = h
self.index[h] = i
}
}
}
func (self *linkedPairs) FromSlice(con []Pair) {
self.size = len(con)
i := self.size-1
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
if a < 0 {
copy(self.head[:b+1], con)
self.copyPairs(self.head[:b+1], con, b+1)
return
} else {
copy(self.head[:], con)
self.copyPairs(self.head[:], con, len(self.head))
con = con[_DEFAULT_NODE_CAP:]
}
@@ -333,12 +390,12 @@ func (self *linkedPairs) FromSlice(con []Pair) {
for i:=0; i<a; i++ {
self.tail[i] = new(pairChunk)
copy(self.tail[i][:], con)
self.copyPairs(self.tail[i][:], con, len(self.tail[i]))
con = con[_DEFAULT_NODE_CAP:]
}
self.tail[a] = new(pairChunk)
copy(self.tail[a][:b+1], con)
self.copyPairs(self.tail[a][:b+1], con, b+1)
}
func (self *linkedPairs) Less(i, j int) bool {
@@ -347,6 +404,10 @@ func (self *linkedPairs) Less(i, j int) bool {
func (self *linkedPairs) Swap(i, j int) {
a, b := self.At(i), self.At(j)
if self.index != nil {
self.index[a.hash] = j
self.index[b.hash] = i
}
*a, *b = *b, *a
}
+4 -2
View File
@@ -29,8 +29,10 @@ import (
// Hack: this is used for both checking space and cause firendly compile errors in 32-bit arch.
const _Sonic_Not_Support_32Bit_Arch__Checking_32Bit_Arch_Here = (1 << ' ') | (1 << '\t') | (1 << '\r') | (1 << '\n')
var bytesNull = []byte("null")
const (
bytesNull = "null"
strNull = "null"
bytesTrue = "true"
bytesFalse = "false"
bytesObject = "{}"
@@ -64,7 +66,7 @@ func decodeNull(src string, pos int) (ret int) {
if ret > len(src) {
return -int(types.ERR_EOF)
}
if src[pos:ret] == bytesNull {
if src[pos:ret] == strNull {
return ret
} else {
return -int(types.ERR_INVALID_CHAR)
+35 -20
View File
@@ -17,12 +17,11 @@
package ast
import (
`sync`
`unicode/utf8`
)
"sync"
"unicode/utf8"
const (
_MaxBuffer = 1024 // 1KB buffer size
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/option"
)
func quoteString(e *[]byte, s string) {
@@ -30,7 +29,7 @@ func quoteString(e *[]byte, s string) {
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
if safeSet[b] {
if rt.SafeSet[b] {
i++
continue
}
@@ -54,8 +53,8 @@ func quoteString(e *[]byte, s string) {
// user-controlled strings are rendered into JSON
// and served to some browsers.
*e = append(*e, `u00`...)
*e = append(*e, hex[b>>4])
*e = append(*e, hex[b&0xF])
*e = append(*e, rt.Hex[b>>4])
*e = append(*e, rt.Hex[b&0xF])
}
i++
start = i
@@ -76,7 +75,7 @@ func quoteString(e *[]byte, s string) {
*e = append(*e, s[start:i]...)
}
*e = append(*e, `\u202`...)
*e = append(*e, hex[c&0xF])
*e = append(*e, rt.Hex[c&0xF])
i += size
start = i
continue
@@ -92,16 +91,24 @@ func quoteString(e *[]byte, s string) {
var bytesPool = sync.Pool{}
func (self *Node) MarshalJSON() ([]byte, error) {
if self == nil {
return bytesNull, nil
}
buf := newBuffer()
err := self.encode(buf)
if err != nil {
freeBuffer(buf)
return nil, err
}
ret := make([]byte, len(*buf))
copy(ret, *buf)
freeBuffer(buf)
var ret []byte
if !rt.CanSizeResue(cap(*buf)) {
ret = *buf
} else {
ret = make([]byte, len(*buf))
copy(ret, *buf)
freeBuffer(buf)
}
return ret, err
}
@@ -109,21 +116,24 @@ func newBuffer() *[]byte {
if ret := bytesPool.Get(); ret != nil {
return ret.(*[]byte)
} else {
buf := make([]byte, 0, _MaxBuffer)
buf := make([]byte, 0, option.DefaultAstBufferSize)
return &buf
}
}
func freeBuffer(buf *[]byte) {
if !rt.CanSizeResue(cap(*buf)) {
return
}
*buf = (*buf)[:0]
bytesPool.Put(buf)
}
func (self *Node) encode(buf *[]byte) error {
if self.IsRaw() {
if self.isRaw() {
return self.encodeRaw(buf)
}
switch self.Type() {
switch int(self.itype()) {
case V_NONE : return ErrNotExist
case V_ERROR : return self.Check()
case V_NULL : return self.encodeNull(buf)
@@ -139,16 +149,21 @@ func (self *Node) encode(buf *[]byte) error {
}
func (self *Node) encodeRaw(buf *[]byte) error {
raw, err := self.Raw()
if err != nil {
return err
lock := self.rlock()
if !self.isRaw() {
self.runlock()
return self.encode(buf)
}
raw := self.toString()
if lock {
self.runlock()
}
*buf = append(*buf, raw...)
return nil
}
func (self *Node) encodeNull(buf *[]byte) error {
*buf = append(*buf, bytesNull...)
*buf = append(*buf, strNull...)
return nil
}
+5 -1
View File
@@ -17,6 +17,10 @@ func newError(err types.ParsingError, msg string) *Node {
}
}
func newErrorPair(err SyntaxError) *Pair {
return &Pair{0, "", *newSyntaxError(err)}
}
// Error returns error message if the node is invalid
func (self Node) Error() string {
if self.t != V_ERROR {
@@ -79,7 +83,7 @@ func (self SyntaxError) description() string {
/* check for empty source */
if self.Src == "" {
return fmt.Sprintf("no sources available: %#v", self)
return fmt.Sprintf("no sources available, the input json is empty: %#v", self)
}
/* prevent slicing before the beginning */
+17 -4
View File
@@ -17,19 +17,29 @@
package ast
import (
`fmt`
"fmt"
`github.com/bytedance/sonic/internal/native/types`
"github.com/bytedance/sonic/internal/caching"
"github.com/bytedance/sonic/internal/native/types"
)
type Pair struct {
hash uint64
Key string
Value Node
}
func NewPair(key string, val Node) Pair {
return Pair{
hash: caching.StrHash(key),
Key: key,
Value: val,
}
}
// Values returns iterator for array's children traversal
func (self *Node) Values() (ListIterator, error) {
if err := self.should(types.V_ARRAY, "an array"); err != nil {
if err := self.should(types.V_ARRAY); err != nil {
return ListIterator{}, err
}
return self.values(), nil
@@ -41,7 +51,7 @@ func (self *Node) values() ListIterator {
// Properties returns iterator for object's children traversal
func (self *Node) Properties() (ObjectIterator, error) {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
if err := self.should(types.V_OBJECT); err != nil {
return ObjectIterator{}, err
}
return self.properties(), nil
@@ -168,6 +178,9 @@ type Scanner func(path Sequence, node *Node) bool
//
// NOTICE: A unsetted node WON'T trigger sc, but its index still counts into Path.Index
func (self *Node) ForEach(sc Scanner) error {
if err := self.checkRaw(); err != nil {
return err
}
switch self.itype() {
case types.V_ARRAY:
iter, err := self.Values()
+161 -142
View File
@@ -17,13 +17,15 @@
package ast
import (
`encoding/json`
`fmt`
`strconv`
`unsafe`
`github.com/bytedance/sonic/internal/native/types`
`github.com/bytedance/sonic/internal/rt`
"encoding/json"
"fmt"
"strconv"
"sync"
"sync/atomic"
"unsafe"
"github.com/bytedance/sonic/internal/native/types"
"github.com/bytedance/sonic/internal/rt"
)
const (
@@ -36,7 +38,7 @@ const (
_V_ARRAY_LAZY = _V_LAZY | types.V_ARRAY
_V_OBJECT_LAZY = _V_LAZY | types.V_OBJECT
_MASK_LAZY = _V_LAZY - 1
_MASK_RAW = _V_RAW - 1
_MASK_RAW = _V_RAW - 1
)
const (
@@ -56,6 +58,7 @@ type Node struct {
t types.ValueType
l uint
p unsafe.Pointer
m *sync.RWMutex
}
// UnmarshalJSON is just an adapter to json.Unmarshaler.
@@ -79,17 +82,39 @@ func (self *Node) UnmarshalJSON(data []byte) (err error) {
// V_STRING = 7 (json value string)
// V_NUMBER = 33 (json value number )
// V_ANY = 34 (golang interface{})
//
// Deprecated: not concurrent safe. Use TypeSafe instead
func (self Node) Type() int {
return int(self.t & _MASK_LAZY & _MASK_RAW)
}
func (self Node) itype() types.ValueType {
// Type concurrently-safe returns json type represented by the node
// It will be one of belows:
// V_NONE = 0 (empty node, key not exists)
// V_ERROR = 1 (error node)
// V_NULL = 2 (json value `null`, key exists)
// V_TRUE = 3 (json value `true`)
// V_FALSE = 4 (json value `false`)
// V_ARRAY = 5 (json value array)
// V_OBJECT = 6 (json value object)
// V_STRING = 7 (json value string)
// V_NUMBER = 33 (json value number )
// V_ANY = 34 (golang interface{})
func (self *Node) TypeSafe() int {
return int(self.loadt() & _MASK_LAZY & _MASK_RAW)
}
func (self *Node) itype() types.ValueType {
return self.t & _MASK_LAZY & _MASK_RAW
}
// Exists returns false only if the self is nil or empty node V_NONE
func (self *Node) Exists() bool {
return self.Valid() && self.t != _V_NONE
if self == nil {
return false
}
t := self.loadt()
return t != V_ERROR && t != _V_NONE
}
// Valid reports if self is NOT V_ERROR or nil
@@ -97,7 +122,7 @@ func (self *Node) Valid() bool {
if self == nil {
return false
}
return self.t != V_ERROR
return self.loadt() != V_ERROR
}
// Check checks if the node itself is valid, and return:
@@ -106,24 +131,31 @@ func (self *Node) Valid() bool {
func (self *Node) Check() error {
if self == nil {
return ErrNotExist
} else if self.t != V_ERROR {
} else if self.loadt() != V_ERROR {
return nil
} else {
return self
}
}
// IsRaw returns true if node's underlying value is raw json
// isRaw returns true if node's underlying value is raw json
//
// Deprecated: not concurent safe
func (self Node) IsRaw() bool {
return self.t&_V_RAW != 0
return self.t & _V_RAW != 0
}
// IsRaw returns true if node's underlying value is raw json
func (self *Node) isRaw() bool {
return self.loadt() & _V_RAW != 0
}
func (self *Node) isLazy() bool {
return self != nil && self.t&_V_LAZY != 0
return self != nil && self.t & _V_LAZY != 0
}
func (self *Node) isAny() bool {
return self != nil && self.t == _V_ANY
return self != nil && self.loadt() == _V_ANY
}
/** Simple Value Methods **/
@@ -133,18 +165,26 @@ func (self *Node) Raw() (string, error) {
if self == nil {
return "", ErrNotExist
}
if !self.IsRaw() {
lock := self.rlock()
if !self.isRaw() {
if lock {
self.runlock()
}
buf, err := self.MarshalJSON()
return rt.Mem2Str(buf), err
}
return self.toString(), nil
ret := self.toString()
if lock {
self.runlock()
}
return ret, nil
}
func (self *Node) checkRaw() error {
if err := self.Check(); err != nil {
return err
}
if self.IsRaw() {
if self.isRaw() {
self.parseRaw(false)
}
return self.Check()
@@ -504,7 +544,7 @@ func (self *Node) Len() (int, error) {
}
}
func (self Node) len() int {
func (self *Node) len() int {
return int(self.l)
}
@@ -527,7 +567,7 @@ func (self *Node) Cap() (int, error) {
//
// If self is V_NONE or V_NULL, it becomes V_OBJECT and sets the node at the key.
func (self *Node) Set(key string, node Node) (bool, error) {
if err := self.Check(); err != nil {
if err := self.checkRaw(); err != nil {
return false, err
}
if err := node.Check(); err != nil {
@@ -535,7 +575,7 @@ func (self *Node) Set(key string, node Node) (bool, error) {
}
if self.t == _V_NONE || self.t == types.V_NULL {
*self = NewObject([]Pair{{key, node}})
*self = NewObject([]Pair{NewPair(key, node)})
return false, nil
} else if self.itype() != types.V_OBJECT {
return false, ErrUnsupportType
@@ -549,7 +589,7 @@ func (self *Node) Set(key string, node Node) (bool, error) {
*self = newObject(new(linkedPairs))
}
s := (*linkedPairs)(self.p)
s.Push(Pair{key, node})
s.Push(NewPair(key, node))
self.l++
return false, nil
@@ -568,7 +608,7 @@ func (self *Node) SetAny(key string, val interface{}) (bool, error) {
// Unset REMOVE (soft) the node of given key under object parent, and reports if the key has existed.
func (self *Node) Unset(key string) (bool, error) {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
if err := self.should(types.V_OBJECT); err != nil {
return false, err
}
// NOTICE: must get acurate length before deduct
@@ -589,7 +629,7 @@ func (self *Node) Unset(key string) (bool, error) {
//
// The index must be within self's children.
func (self *Node) SetByIndex(index int, node Node) (bool, error) {
if err := self.Check(); err != nil {
if err := self.checkRaw(); err != nil {
return false, err
}
if err := node.Check(); err != nil {
@@ -669,7 +709,7 @@ func (self *Node) UnsetByIndex(index int) (bool, error) {
//
// If self is V_NONE or V_NULL, it becomes V_ARRAY and sets the node at index 0.
func (self *Node) Add(node Node) error {
if err := self.Check(); err != nil {
if err := self.checkRaw(); err != nil {
return err
}
@@ -677,7 +717,7 @@ func (self *Node) Add(node Node) error {
*self = NewArray([]Node{node})
return nil
}
if err := self.should(types.V_ARRAY, "an array"); err != nil {
if err := self.should(types.V_ARRAY); err != nil {
return err
}
@@ -740,7 +780,7 @@ func (self *Node) Pop() error {
//
// WARN: this will change address of elements, which is a dangerous action.
func (self *Node) Move(dst, src int) error {
if err := self.should(types.V_ARRAY, "an array"); err != nil {
if err := self.should(types.V_ARRAY); err != nil {
return err
}
@@ -812,7 +852,7 @@ func (self *Node) GetByPath(path ...interface{}) *Node {
// Get loads given key of an object node on demands
func (self *Node) Get(key string) *Node {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
if err := self.should(types.V_OBJECT); err != nil {
return unwrapError(err)
}
n, _ := self.skipKey(key)
@@ -845,14 +885,14 @@ func (self *Node) Index(idx int) *Node {
// IndexPair indexies pair at given idx,
// node type MUST be either V_OBJECT
func (self *Node) IndexPair(idx int) *Pair {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
if err := self.should(types.V_OBJECT); err != nil {
return nil
}
return self.skipIndexPair(idx)
}
func (self *Node) indexOrGet(idx int, key string) (*Node, int) {
if err := self.should(types.V_OBJECT, "an object"); err != nil {
if err := self.should(types.V_OBJECT); err != nil {
return unwrapError(err), idx
}
@@ -889,10 +929,10 @@ func (self *Node) Map() (map[string]interface{}, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_OBJECT, "an object"); err != nil {
if err := self.should(types.V_OBJECT); err != nil {
return nil, err
}
if err := self.loadAllKey(); err != nil {
if err := self.loadAllKey(false); err != nil {
return nil, err
}
return self.toGenericObject()
@@ -908,10 +948,10 @@ func (self *Node) MapUseNumber() (map[string]interface{}, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_OBJECT, "an object"); err != nil {
if err := self.should(types.V_OBJECT); err != nil {
return nil, err
}
if err := self.loadAllKey(); err != nil {
if err := self.loadAllKey(false); err != nil {
return nil, err
}
return self.toGenericObjectUseNumber()
@@ -928,7 +968,7 @@ func (self *Node) MapUseNode() (map[string]Node, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_OBJECT, "an object"); err != nil {
if err := self.should(types.V_OBJECT); err != nil {
return nil, err
}
if err := self.skipAllKey(); err != nil {
@@ -1034,10 +1074,10 @@ func (self *Node) Array() ([]interface{}, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_ARRAY, "an array"); err != nil {
if err := self.should(types.V_ARRAY); err != nil {
return nil, err
}
if err := self.loadAllIndex(); err != nil {
if err := self.loadAllIndex(false); err != nil {
return nil, err
}
return self.toGenericArray()
@@ -1053,10 +1093,10 @@ func (self *Node) ArrayUseNumber() ([]interface{}, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_ARRAY, "an array"); err != nil {
if err := self.should(types.V_ARRAY); err != nil {
return nil, err
}
if err := self.loadAllIndex(); err != nil {
if err := self.loadAllIndex(false); err != nil {
return nil, err
}
return self.toGenericArrayUseNumber()
@@ -1073,7 +1113,7 @@ func (self *Node) ArrayUseNode() ([]Node, error) {
return nil, ErrUnsupportType
}
}
if err := self.should(types.V_ARRAY, "an array"); err != nil {
if err := self.should(types.V_ARRAY); err != nil {
return nil, err
}
if err := self.skipAllIndex(); err != nil {
@@ -1129,12 +1169,12 @@ func (self *Node) Interface() (interface{}, error) {
}
return v, nil
case _V_ARRAY_LAZY :
if err := self.loadAllIndex(); err != nil {
if err := self.loadAllIndex(false); err != nil {
return nil, err
}
return self.toGenericArray()
case _V_OBJECT_LAZY :
if err := self.loadAllKey(); err != nil {
if err := self.loadAllKey(false); err != nil {
return nil, err
}
return self.toGenericObject()
@@ -1168,12 +1208,12 @@ func (self *Node) InterfaceUseNumber() (interface{}, error) {
case types.V_STRING : return self.toString(), nil
case _V_NUMBER : return self.toNumber(), nil
case _V_ARRAY_LAZY :
if err := self.loadAllIndex(); err != nil {
if err := self.loadAllIndex(false); err != nil {
return nil, err
}
return self.toGenericArrayUseNumber()
case _V_OBJECT_LAZY :
if err := self.loadAllKey(); err != nil {
if err := self.loadAllKey(false); err != nil {
return nil, err
}
return self.toGenericObjectUseNumber()
@@ -1205,70 +1245,30 @@ func (self *Node) InterfaceUseNode() (interface{}, error) {
}
}
// LoadAll loads all the node's children and children's children as parsed.
// After calling it, the node can be safely used on concurrency
// LoadAll loads the node's children
// and ensure all its children can be READ concurrently (include its children's children)
func (self *Node) LoadAll() error {
if self.IsRaw() {
self.parseRaw(true)
return self.Check()
}
switch self.itype() {
case types.V_ARRAY:
e := self.len()
if err := self.loadAllIndex(); err != nil {
return err
}
for i := 0; i < e; i++ {
n := self.nodeAt(i)
if n.IsRaw() {
n.parseRaw(true)
}
if err := n.Check(); err != nil {
return err
}
}
return nil
case types.V_OBJECT:
e := self.len()
if err := self.loadAllKey(); err != nil {
return err
}
for i := 0; i < e; i++ {
n := self.pairAt(i)
if n.Value.IsRaw() {
n.Value.parseRaw(true)
}
if err := n.Value.Check(); err != nil {
return err
}
}
return nil
default:
return self.Check()
}
return self.Load()
}
// Load loads the node's children as parsed.
// After calling it, only the node itself can be used on concurrency (not include its children)
// and ensure all its children can be READ concurrently (include its children's children)
func (self *Node) Load() error {
if err := self.checkRaw(); err != nil {
return err
}
switch self.t {
case _V_ARRAY_LAZY:
return self.skipAllIndex()
case _V_OBJECT_LAZY:
return self.skipAllKey()
default:
return self.Check()
case _V_ARRAY_LAZY: self.loadAllIndex(true)
case _V_OBJECT_LAZY: self.loadAllKey(true)
case V_ERROR: return self
case V_NONE: return nil
}
if self.m == nil {
self.m = new(sync.RWMutex)
}
return self.checkRaw()
}
/**---------------------------------- Internal Helper Methods ----------------------------------**/
func (self *Node) should(t types.ValueType, s string) error {
func (self *Node) should(t types.ValueType) error {
if err := self.checkRaw(); err != nil {
return err
}
@@ -1439,13 +1439,17 @@ func (self *Node) skipIndexPair(index int) *Pair {
return nil
}
func (self *Node) loadAllIndex() error {
func (self *Node) loadAllIndex(loadOnce bool) error {
if !self.isLazy() {
return nil
}
var err types.ParsingError
parser, stack := self.getParserAndArrayStack()
parser.noLazy = true
if !loadOnce {
parser.noLazy = true
} else {
parser.loadOnce = true
}
*self, err = parser.decodeArray(&stack.v)
if err != 0 {
return parser.ExportError(err)
@@ -1453,14 +1457,19 @@ func (self *Node) loadAllIndex() error {
return nil
}
func (self *Node) loadAllKey() error {
func (self *Node) loadAllKey(loadOnce bool) error {
if !self.isLazy() {
return nil
}
var err types.ParsingError
parser, stack := self.getParserAndObjectStack()
parser.noLazy = true
*self, err = parser.decodeObject(&stack.v)
if !loadOnce {
parser.noLazy = true
*self, err = parser.decodeObject(&stack.v)
} else {
parser.loadOnce = true
*self, err = parser.decodeObject(&stack.v)
}
if err != 0 {
return parser.ExportError(err)
}
@@ -1629,7 +1638,23 @@ func NewRaw(json string) Node {
if it == _V_NONE {
return Node{}
}
return newRawNode(parser.s[start:parser.p], it)
return newRawNode(parser.s[start:parser.p], it, false)
}
// NewRawConcurrentRead creates a node of raw json, which can be READ
// (GetByPath/Get/Index/GetOrIndex/Int64/Bool/Float64/String/Number/Interface/Array/Map/Raw/MarshalJSON) concurrently.
// If the input json is invalid, NewRaw returns a error Node.
func NewRawConcurrentRead(json string) Node {
parser := NewParserObj(json)
start, err := parser.skip()
if err != 0 {
return *newError(err, err.Message())
}
it := switchRawType(parser.s[start])
if it == _V_NONE {
return Node{}
}
return newRawNode(parser.s[start:parser.p], it, true)
}
// NewAny creates a node of type V_ANY if any's type isn't Node or *Node,
@@ -1653,7 +1678,7 @@ func NewBytes(src []byte) Node {
if len(src) == 0 {
panic("empty src bytes")
}
out := encodeBase64(src)
out := rt.EncodeBase64(src)
return NewString(out)
}
@@ -1689,15 +1714,15 @@ func NewNumber(v string) Node {
}
}
func (node Node) toNumber() json.Number {
func (node *Node) toNumber() json.Number {
return json.Number(rt.StrFrom(node.p, int64(node.l)))
}
func (self Node) toString() string {
func (self *Node) toString() string {
return rt.StrFrom(self.p, int64(self.l))
}
func (node Node) toFloat64() (float64, error) {
func (node *Node) toFloat64() (float64, error) {
ret, err := node.toNumber().Float64()
if err != nil {
return 0, err
@@ -1705,7 +1730,7 @@ func (node Node) toFloat64() (float64, error) {
return ret, nil
}
func (node Node) toInt64() (int64, error) {
func (node *Node) toInt64() (int64, error) {
ret,err := node.toNumber().Int64()
if err != nil {
return 0, err
@@ -1741,6 +1766,8 @@ func NewArray(v []Node) Node {
return newArray(s)
}
const _Threshold_Index = 16
func newArray(v *linkedNodes) Node {
return Node{
t: types.V_ARRAY,
@@ -1764,6 +1791,9 @@ func NewObject(v []Pair) Node {
}
func newObject(v *linkedPairs) Node {
if v.size > _Threshold_Index {
v.BuildIndex()
}
return Node{
t: types.V_OBJECT,
l: uint(v.Len()),
@@ -1772,53 +1802,42 @@ func newObject(v *linkedPairs) Node {
}
func (self *Node) setObject(v *linkedPairs) {
if v.size > _Threshold_Index {
v.BuildIndex()
}
self.t = types.V_OBJECT
self.l = uint(v.Len())
self.p = unsafe.Pointer(v)
}
func newRawNode(str string, typ types.ValueType) Node {
return Node{
t: _V_RAW | typ,
p: rt.StrPtr(str),
l: uint(len(str)),
}
}
func (self *Node) parseRaw(full bool) {
lock := self.lock()
defer self.unlock()
if !self.isRaw() {
return
}
raw := self.toString()
parser := NewParserObj(raw)
var e types.ParsingError
if full {
parser.noLazy = true
parser.skipValue = false
*self, e = parser.Parse()
} else if lock {
var n Node
parser.noLazy = true
parser.loadOnce = true
n, e = parser.Parse()
self.assign(n)
} else {
*self, e = parser.Parse()
}
var e types.ParsingError
*self, e = parser.Parse()
if e != 0 {
*self = *newSyntaxError(parser.syntaxError(e))
}
}
var typeJumpTable = [256]types.ValueType{
'"' : types.V_STRING,
'-' : _V_NUMBER,
'0' : _V_NUMBER,
'1' : _V_NUMBER,
'2' : _V_NUMBER,
'3' : _V_NUMBER,
'4' : _V_NUMBER,
'5' : _V_NUMBER,
'6' : _V_NUMBER,
'7' : _V_NUMBER,
'8' : _V_NUMBER,
'9' : _V_NUMBER,
'[' : types.V_ARRAY,
'f' : types.V_FALSE,
'n' : types.V_NULL,
't' : types.V_TRUE,
'{' : types.V_OBJECT,
}
func switchRawType(c byte) types.ValueType {
return typeJumpTable[c]
func (self *Node) assign(n Node) {
self.l = n.l
self.p = n.p
atomic.StoreInt64(&self.t, n.t)
}
+120 -18
View File
@@ -17,14 +17,16 @@
package ast
import (
`fmt`
"fmt"
"sync"
"sync/atomic"
`github.com/bytedance/sonic/internal/native/types`
`github.com/bytedance/sonic/internal/rt`
"github.com/bytedance/sonic/internal/native/types"
"github.com/bytedance/sonic/internal/rt"
)
const (
_DEFAULT_NODE_CAP int = 8
_DEFAULT_NODE_CAP int = 16
_APPEND_GROW_SHIFT = 1
)
@@ -45,6 +47,7 @@ type Parser struct {
p int
s string
noLazy bool
loadOnce bool
skipValue bool
dbuf *byte
}
@@ -152,7 +155,7 @@ func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) {
if t == _V_NONE {
return Node{}, types.ERR_INVALID_CHAR
}
val = newRawNode(self.s[start:self.p], t)
val = newRawNode(self.s[start:self.p], t, false)
}else{
/* decode the value */
if val, err = self.Parse(); err != 0 {
@@ -238,7 +241,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
if t == _V_NONE {
return Node{}, types.ERR_INVALID_CHAR
}
val = newRawNode(self.s[start:self.p], t)
val = newRawNode(self.s[start:self.p], t, false)
} else {
/* decode the value */
if val, err = self.Parse(); err != 0 {
@@ -248,7 +251,7 @@ func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
/* add the value to result */
// FIXME: ret's address may change here, thus previous referred node in ret may be invalid !!
ret.Push(Pair{Key: key, Value: val})
ret.Push(NewPair(key, val))
self.p = self.lspace(self.p)
/* check for EOF */
@@ -295,6 +298,10 @@ func (self *Parser) Pos() int {
return self.p
}
// Parse returns a ast.Node representing the parser's JSON.
// NOTICE: the specific parsing lazy dependens parser's option
// It only parse first layer and first child for Object or Array be default
func (self *Parser) Parse() (Node, types.ParsingError) {
switch val := self.decodeValue(); val.Vt {
case types.V_EOF : return Node{}, types.ERR_EOF
@@ -303,22 +310,48 @@ func (self *Parser) Parse() (Node, types.ParsingError) {
case types.V_FALSE : return falseNode, 0
case types.V_STRING : return self.decodeString(val.Iv, val.Ep)
case types.V_ARRAY:
s := self.p - 1;
if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == ']' {
self.p = p + 1
return Node{t: types.V_ARRAY}, 0
}
if self.noLazy {
if self.loadOnce {
self.noLazy = false
}
return self.decodeArray(new(linkedNodes))
}
// NOTICE: loadOnce always keep raw json for object or array
if self.loadOnce {
self.p = s
s, e := self.skipFast()
if e != 0 {
return Node{}, e
}
return newRawNode(self.s[s:self.p], types.V_ARRAY, true), 0
}
return newLazyArray(self), 0
case types.V_OBJECT:
s := self.p - 1;
if p := skipBlank(self.s, self.p); p >= self.p && self.s[p] == '}' {
self.p = p + 1
return Node{t: types.V_OBJECT}, 0
}
// NOTICE: loadOnce always keep raw json for object or array
if self.noLazy {
if self.loadOnce {
self.noLazy = false
}
return self.decodeObject(new(linkedPairs))
}
if self.loadOnce {
self.p = s
s, e := self.skipFast()
if e != 0 {
return Node{}, e
}
return newRawNode(self.s[s:self.p], types.V_OBJECT, true), 0
}
return newLazyObject(self), 0
case types.V_DOUBLE : return NewNumber(self.s[val.Ep:self.p]), 0
case types.V_INTEGER : return NewNumber(self.s[val.Ep:self.p]), 0
@@ -475,7 +508,7 @@ func (self *Node) skipNextNode() *Node {
if t == _V_NONE {
return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))
}
val = newRawNode(parser.s[start:parser.p], t)
val = newRawNode(parser.s[start:parser.p], t, false)
}
/* add the value to result */
@@ -514,7 +547,7 @@ func (self *Node) skipNextPair() (*Pair) {
/* check for EOF */
if parser.p = parser.lspace(sp); parser.p >= ns {
return &Pair{"", *newSyntaxError(parser.syntaxError(types.ERR_EOF))}
return newErrorPair(parser.syntaxError(types.ERR_EOF))
}
/* check for empty object */
@@ -531,7 +564,7 @@ func (self *Node) skipNextPair() (*Pair) {
/* decode the key */
if njs = parser.decodeValue(); njs.Vt != types.V_STRING {
return &Pair{"", *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
}
/* extract the key */
@@ -541,34 +574,34 @@ func (self *Node) skipNextPair() (*Pair) {
/* check for escape sequence */
if njs.Ep != -1 {
if key, err = unquote(key); err != 0 {
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
return newErrorPair(parser.syntaxError(err))
}
}
/* expect a ':' delimiter */
if err = parser.delim(); err != 0 {
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
return newErrorPair(parser.syntaxError(err))
}
/* skip the value */
if start, err := parser.skipFast(); err != 0 {
return &Pair{key, *newSyntaxError(parser.syntaxError(err))}
return newErrorPair(parser.syntaxError(err))
} else {
t := switchRawType(parser.s[start])
if t == _V_NONE {
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
}
val = newRawNode(parser.s[start:parser.p], t)
val = newRawNode(parser.s[start:parser.p], t, false)
}
/* add the value to result */
ret.Push(Pair{Key: key, Value: val})
ret.Push(NewPair(key, val))
self.l++
parser.p = parser.lspace(parser.p)
/* check for EOF */
if parser.p >= ns {
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_EOF))}
return newErrorPair(parser.syntaxError(types.ERR_EOF))
}
/* check for the next character */
@@ -581,7 +614,7 @@ func (self *Node) skipNextPair() (*Pair) {
self.setObject(ret)
return ret.At(ret.Len()-1)
default:
return &Pair{key, *newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))}
return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
}
}
@@ -662,3 +695,72 @@ func backward(src string, i int) int {
for ; i>=0 && isSpace(src[i]); i-- {}
return i
}
func newRawNode(str string, typ types.ValueType, lock bool) Node {
ret := Node{
t: typ | _V_RAW,
p: rt.StrPtr(str),
l: uint(len(str)),
}
if lock {
ret.m = new(sync.RWMutex)
}
return ret
}
var typeJumpTable = [256]types.ValueType{
'"' : types.V_STRING,
'-' : _V_NUMBER,
'0' : _V_NUMBER,
'1' : _V_NUMBER,
'2' : _V_NUMBER,
'3' : _V_NUMBER,
'4' : _V_NUMBER,
'5' : _V_NUMBER,
'6' : _V_NUMBER,
'7' : _V_NUMBER,
'8' : _V_NUMBER,
'9' : _V_NUMBER,
'[' : types.V_ARRAY,
'f' : types.V_FALSE,
'n' : types.V_NULL,
't' : types.V_TRUE,
'{' : types.V_OBJECT,
}
func switchRawType(c byte) types.ValueType {
return typeJumpTable[c]
}
func (self *Node) loadt() types.ValueType {
return (types.ValueType)(atomic.LoadInt64(&self.t))
}
func (self *Node) lock() bool {
if m := self.m; m != nil {
m.Lock()
return true
}
return false
}
func (self *Node) unlock() {
if m := self.m; m != nil {
m.Unlock()
}
}
func (self *Node) rlock() bool {
if m := self.m; m != nil {
m.RLock()
return true
}
return false
}
func (self *Node) runlock() {
if m := self.m; m != nil {
m.RUnlock()
}
}
+25 -6
View File
@@ -21,8 +21,23 @@ import (
`github.com/bytedance/sonic/internal/native/types`
)
// SearchOptions controls Searcher's behavior
type SearchOptions struct {
// ValidateJSON indicates the searcher to validate the entire JSON
ValidateJSON bool
// CopyReturn indicates the searcher to copy the result JSON instead of refer from the input
// This can help to reduce memory usage if you cache the results
CopyReturn bool
// ConcurrentRead indicates the searcher to return a concurrently-READ-safe node,
// including: GetByPath/Get/Index/GetOrIndex/Int64/Bool/Float64/String/Number/Interface/Array/Map/Raw/MarshalJSON
ConcurrentRead bool
}
type Searcher struct {
parser Parser
SearchOptions
}
func NewSearcher(str string) *Searcher {
@@ -31,12 +46,16 @@ func NewSearcher(str string) *Searcher {
s: str,
noLazy: false,
},
SearchOptions: SearchOptions{
ValidateJSON: true,
},
}
}
// GetByPathCopy search in depth from top json and returns a **Copied** json node at the path location
func (self *Searcher) GetByPathCopy(path ...interface{}) (Node, error) {
return self.getByPath(true, true, path...)
self.CopyReturn = true
return self.getByPath(path...)
}
// GetByPathNoCopy search in depth from top json and returns a **Referenced** json node at the path location
@@ -44,15 +63,15 @@ func (self *Searcher) GetByPathCopy(path ...interface{}) (Node, error) {
// WARN: this search directly refer partial json from top json, which has faster speed,
// may consumes more memory.
func (self *Searcher) GetByPath(path ...interface{}) (Node, error) {
return self.getByPath(false, true, path...)
return self.getByPath(path...)
}
func (self *Searcher) getByPath(copystring bool, validate bool, path ...interface{}) (Node, error) {
func (self *Searcher) getByPath(path ...interface{}) (Node, error) {
var err types.ParsingError
var start int
self.parser.p = 0
start, err = self.parser.getByPath(validate, path...)
start, err = self.parser.getByPath(self.ValidateJSON, path...)
if err != 0 {
// for compatibility with old version
if err == types.ERR_NOT_FOUND {
@@ -71,12 +90,12 @@ func (self *Searcher) getByPath(copystring bool, validate bool, path ...interfac
// copy string to reducing memory usage
var raw string
if copystring {
if self.CopyReturn {
raw = rt.Mem2Str([]byte(self.parser.s[start:self.parser.p]))
} else {
raw = self.parser.s[start:self.parser.p]
}
return newRawNode(raw, t), nil
return newRawNode(raw, t, self.ConcurrentRead), nil
}
// GetByPath searches a path and returns relaction and types of target
+102 -9
View File
@@ -17,10 +17,10 @@
package ast
import (
`unsafe`
`unicode/utf8`
"unicode/utf8"
"unsafe"
`github.com/bytedance/sonic/internal/rt`
"github.com/bytedance/sonic/internal/rt"
)
//go:noescape
@@ -37,13 +37,106 @@ func mem2ptr(s []byte) unsafe.Pointer {
return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr
}
var (
//go:linkname safeSet encoding/json.safeSet
safeSet [utf8.RuneSelf]bool
var safeSet = [utf8.RuneSelf]bool{
' ': true,
'!': true,
'"': false,
'#': true,
'$': true,
'%': true,
'&': true,
'\'': true,
'(': true,
')': true,
'*': true,
'+': true,
',': true,
'-': true,
'.': true,
'/': true,
'0': true,
'1': true,
'2': true,
'3': true,
'4': true,
'5': true,
'6': true,
'7': true,
'8': true,
'9': true,
':': true,
';': true,
'<': true,
'=': true,
'>': true,
'?': true,
'@': true,
'A': true,
'B': true,
'C': true,
'D': true,
'E': true,
'F': true,
'G': true,
'H': true,
'I': true,
'J': true,
'K': true,
'L': true,
'M': true,
'N': true,
'O': true,
'P': true,
'Q': true,
'R': true,
'S': true,
'T': true,
'U': true,
'V': true,
'W': true,
'X': true,
'Y': true,
'Z': true,
'[': true,
'\\': false,
']': true,
'^': true,
'_': true,
'`': true,
'a': true,
'b': true,
'c': true,
'd': true,
'e': true,
'f': true,
'g': true,
'h': true,
'i': true,
'j': true,
'k': true,
'l': true,
'm': true,
'n': true,
'o': true,
'p': true,
'q': true,
'r': true,
's': true,
't': true,
'u': true,
'v': true,
'w': true,
'x': true,
'y': true,
'z': true,
'{': true,
'|': true,
'}': true,
'~': true,
'\u007f': true,
}
//go:linkname hex encoding/json.hex
hex string
)
var hex = "0123456789abcdef"
//go:linkname unquoteBytes encoding/json.unquoteBytes
func unquoteBytes(s []byte) (t []byte, ok bool)
+2 -1
View File
@@ -251,6 +251,7 @@ func (self *traverser) decodeObject() error {
/* check for empty object */
if self.parser.s[self.parser.p] == '}' {
self.parser.p++
return self.visitor.OnObjectEnd()
}
@@ -326,6 +327,6 @@ func (self *traverser) decodeString(iv int64, ep int) error {
return self.visitor.OnString(out)
}
// If visitor return this error on `OnObjectBegin()` or `OnArrayBegin()`,
// If visitor return this error on `OnObjectBegin()` or `OnArrayBegin()`,
// the transverer will skip entiry object or array
var VisitOPSkip = errors.New("")
+3 -1
View File
@@ -1,4 +1,4 @@
// +build !amd64 !go1.16 go1.23
// +build !amd64,!arm64 go1.24 !go1.17 arm64,!go1.20
/*
* Copyright 2021 ByteDance Inc.
@@ -27,6 +27,8 @@ import (
`github.com/bytedance/sonic/option`
)
const apiKind = UseStdJSON
type frozenConfig struct {
Config
}
+4 -2
View File
@@ -1,4 +1,4 @@
// +build !amd64 !go1.16 go1.23
// +build !amd64,!arm64 go1.24 !go1.17 arm64,!go1.20
/*
* Copyright 2023 ByteDance Inc.
@@ -30,7 +30,7 @@ import (
)
func init() {
println("WARNING: sonic only supports Go1.16~1.22 && CPU amd64, but your environment is not suitable")
println("WARNING: sonic/decoder only supports (Go1.17~1.23 && CPU amd64) or (go1.20~1.23 && CPU arm64), but your environment is not suitable")
}
const (
@@ -42,6 +42,7 @@ const (
_F_use_number = types.B_USE_NUMBER
_F_validate_string = types.B_VALIDATE_STRING
_F_allow_control = types.B_ALLOW_CONTROL
_F_no_validate_json = types.B_NO_VALIDATE_JSON
)
type Options uint64
@@ -53,6 +54,7 @@ const (
OptionDisableUnknown Options = 1 << _F_disable_unknown
OptionCopyString Options = 1 << _F_copy_string
OptionValidateString Options = 1 << _F_validate_string
OptionNoValidateJSON Options = 1 << _F_no_validate_json
)
func (self *Decoder) SetOptions(opts Options) {
@@ -1,4 +1,6 @@
// +build amd64,go1.16,!go1.23
//go:build (amd64 && go1.17 && !go1.24) || (arm64 && go1.20 && !go1.24)
// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24
/*
* Copyright 2023 ByteDance Inc.
@@ -19,50 +21,51 @@
package decoder
import (
`github.com/bytedance/sonic/internal/decoder`
`github.com/bytedance/sonic/internal/decoder/api`
)
// Decoder is the decoder context object
type Decoder = decoder.Decoder
type Decoder = api.Decoder
// SyntaxError represents json syntax error
type SyntaxError = decoder.SyntaxError
type SyntaxError = api.SyntaxError
// MismatchTypeError represents dismatching between json and object
type MismatchTypeError = decoder.MismatchTypeError
type MismatchTypeError = api.MismatchTypeError
// Options for decode.
type Options = decoder.Options
type Options = api.Options
const (
OptionUseInt64 Options = decoder.OptionUseInt64
OptionUseNumber Options = decoder.OptionUseNumber
OptionUseUnicodeErrors Options = decoder.OptionUseUnicodeErrors
OptionDisableUnknown Options = decoder.OptionDisableUnknown
OptionCopyString Options = decoder.OptionCopyString
OptionValidateString Options = decoder.OptionValidateString
OptionUseInt64 Options = api.OptionUseInt64
OptionUseNumber Options = api.OptionUseNumber
OptionUseUnicodeErrors Options = api.OptionUseUnicodeErrors
OptionDisableUnknown Options = api.OptionDisableUnknown
OptionCopyString Options = api.OptionCopyString
OptionValidateString Options = api.OptionValidateString
OptionNoValidateJSON Options = api.OptionNoValidateJSON
)
// StreamDecoder is the decoder context object for streaming input.
type StreamDecoder = decoder.StreamDecoder
type StreamDecoder = api.StreamDecoder
var (
// NewDecoder creates a new decoder instance.
NewDecoder = decoder.NewDecoder
NewDecoder = api.NewDecoder
// NewStreamDecoder adapts to encoding/json.NewDecoder API.
//
// NewStreamDecoder returns a new decoder that reads from r.
NewStreamDecoder = decoder.NewStreamDecoder
NewStreamDecoder = api.NewStreamDecoder
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
// order to reduce the first-hit latency.
//
// Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
// a compile option to set the depth of recursive compile for the nested struct type.
Pretouch = decoder.Pretouch
Pretouch = api.Pretouch
// Skip skips only one json value, and returns first non-blank character position and its ending position if it is valid.
// Otherwise, returns negative error code using start and invalid character position using end
Skip = decoder.Skip
Skip = api.Skip
)
+2 -2
View File
@@ -1,4 +1,4 @@
// +build !amd64 !go1.16 go1.23
// +build !amd64,!arm64 go1.24 !go1.17 arm64,!go1.20
/*
* Copyright 2023 ByteDance Inc.
@@ -28,7 +28,7 @@ import (
)
func init() {
println("WARNING:(encoder) sonic only supports Go1.16~1.22 && CPU amd64, but your environment is not suitable")
println("WARNING:(encoder) sonic only supports (Go1.17~1.23 && CPU amd64) or (G01.20~1.23 && CPU arm64) , but your environment is not suitable")
}
// EnableFallback indicates if encoder use fallback
@@ -1,4 +1,4 @@
// +build amd64,go1.16,!go1.23
// +build amd64,go1.17,!go1.24 arm64,go1.20,!go1.24
/*
* Copyright 2023 ByteDance Inc.
@@ -70,6 +70,9 @@ const (
// CompatibleWithStd is used to be compatible with std encoder.
CompatibleWithStd Options = encoder.CompatibleWithStd
// Encode Infinity or Nan float into `null`, instead of returning an error.
EncodeNullForInfOrNan Options = encoder.EncodeNullForInfOrNan
)
+1
View File
@@ -0,0 +1 @@
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+46
View File
@@ -0,0 +1,46 @@
// +build amd64,go1.16
/**
* Copyright 2023 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 base64
import (
"github.com/cloudwego/base64x"
)
func DecodeBase64(src string) ([]byte, error) {
return base64x.StdEncoding.DecodeString(src)
}
func EncodeBase64(buf []byte, src []byte) []byte {
if len(src) == 0 {
return append(buf, '"', '"')
}
buf = append(buf, '"')
need := base64x.StdEncoding.EncodedLen(len(src))
if cap(buf) - len(buf) < need {
tmp := make([]byte, len(buf), len(buf) + need*2)
copy(tmp, buf)
buf = tmp
}
base64x.StdEncoding.Encode(buf[len(buf):cap(buf)], src)
buf = buf[:len(buf) + need]
buf = append(buf, '"')
return buf
}
+44
View File
@@ -0,0 +1,44 @@
// +build !amd64 !go1.16
/*
* Copyright 2022 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 base64
import (
"encoding/base64"
)
func EncodeBase64(buf []byte, src []byte) []byte {
if len(src) == 0 {
return append(buf, '"', '"')
}
buf = append(buf, '"')
need := base64.StdEncoding.EncodedLen(len(src))
if cap(buf) - len(buf) < need {
tmp := make([]byte, len(buf), len(buf) + need*2)
copy(tmp, buf)
buf = tmp
}
base64.StdEncoding.Encode(buf[len(buf):cap(buf)], src)
buf = buf[:len(buf) + need]
buf = append(buf, '"')
return buf
}
func DecodeBase64(src string) ([]byte, error) {
return base64.StdEncoding.DecodeString(src)
}
+2 -2
View File
@@ -24,7 +24,6 @@ import (
)
var (
HasAVX = cpuid.CPU.Has(cpuid.AVX)
HasAVX2 = cpuid.CPU.Has(cpuid.AVX2)
HasSSE = cpuid.CPU.Has(cpuid.SSE)
)
@@ -33,7 +32,8 @@ func init() {
switch v := os.Getenv("SONIC_MODE"); v {
case "" : break
case "auto" : break
case "noavx" : HasAVX = false; fallthrough
case "noavx" : HasAVX2 = false
// will also disable avx, act as `noavx`, we remain it to make sure forward compatibility
case "noavx2" : HasAVX2 = false
default : panic(fmt.Sprintf("invalid mode: '%s', should be one of 'auto', 'noavx', 'noavx2'", v))
}
@@ -14,51 +14,52 @@
* limitations under the License.
*/
package decoder
package api
import (
`unsafe`
`encoding/json`
`reflect`
`runtime`
`github.com/bytedance/sonic/internal/native`
`github.com/bytedance/sonic/internal/native/types`
`github.com/bytedance/sonic/internal/decoder/consts`
`github.com/bytedance/sonic/internal/decoder/errors`
`github.com/bytedance/sonic/internal/rt`
`github.com/bytedance/sonic/option`
`github.com/bytedance/sonic/utf8`
)
const (
_F_use_int64 = 0
_F_disable_urc = 2
_F_disable_unknown = 3
_F_copy_string = 4
_F_allow_control = consts.F_allow_control
_F_copy_string = consts.F_copy_string
_F_disable_unknown = consts.F_disable_unknown
_F_disable_urc = consts.F_disable_urc
_F_use_int64 = consts.F_use_int64
_F_use_number = consts.F_use_number
_F_validate_string = consts.F_validate_string
_F_use_number = types.B_USE_NUMBER
_F_validate_string = types.B_VALIDATE_STRING
_F_allow_control = types.B_ALLOW_CONTROL
_MaxStack = consts.MaxStack
OptionUseInt64 = consts.OptionUseInt64
OptionUseNumber = consts.OptionUseNumber
OptionUseUnicodeErrors = consts.OptionUseUnicodeErrors
OptionDisableUnknown = consts.OptionDisableUnknown
OptionCopyString = consts.OptionCopyString
OptionValidateString = consts.OptionValidateString
OptionNoValidateJSON = consts.OptionNoValidateJSON
)
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
OptionValidateString Options = 1 << _F_validate_string
type (
Options = consts.Options
MismatchTypeError = errors.MismatchTypeError
SyntaxError = errors.SyntaxError
)
func (self *Decoder) SetOptions(opts Options) {
if (opts & OptionUseNumber != 0) && (opts & OptionUseInt64 != 0) {
if (opts & consts.OptionUseNumber != 0) && (opts & consts.OptionUseInt64 != 0) {
panic("can't set OptionUseInt64 and OptionUseNumber both!")
}
self.f = uint64(opts)
}
// Decoder is the decoder context object
type Decoder struct {
i int
@@ -109,44 +110,7 @@ func (self *Decoder) CheckTrailings() error {
// Decode parses the JSON-encoded data from current position and stores the result
// in the value pointed to by val.
func (self *Decoder) Decode(val interface{}) error {
/* validate json if needed */
if (self.f & (1 << _F_validate_string)) != 0 && !utf8.ValidateString(self.s){
dbuf := utf8.CorrectWith(nil, rt.Str2Mem(self.s), "\ufffd")
self.s = rt.Mem2Str(dbuf)
}
vv := rt.UnpackEface(val)
vp := vv.Value
/* check for nil type */
if vv.Type == nil {
return &json.InvalidUnmarshalError{}
}
/* must be a non-nil pointer */
if vp == nil || vv.Type.Kind() != reflect.Ptr {
return &json.InvalidUnmarshalError{Type: vv.Type.Pack()}
}
etp := rt.PtrElem(vv.Type)
/* check the defined pointer type for issue 379 */
if vv.Type.IsNamed() {
newp := vp
etp = vv.Type
vp = unsafe.Pointer(&newp)
}
/* create a new stack, and call the decoder */
sb := newStack()
nb, err := decodeTypedPointer(self.s, self.i, etp, vp, sb, self.f)
/* return the stack back */
self.i = nb
freeStack(sb)
/* avoid GC ahead */
runtime.KeepAlive(vv)
return err
return decodeImpl(&self.s, &self.i, self.f, val)
}
// UseInt64 indicates the Decoder to unmarshal an integer into an interface{} as an
@@ -194,53 +158,7 @@ func (self *Decoder) ValidateString() {
// Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
// a compile option to set the depth of recursive compile for the nested struct type.
func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
cfg := option.DefaultCompileOptions()
for _, opt := range opts {
opt(&cfg)
}
return pretouchRec(map[reflect.Type]bool{vt:true}, cfg)
}
func pretouchType(_vt reflect.Type, opts option.CompileOptions) (map[reflect.Type]bool, error) {
/* compile function */
compiler := newCompiler().apply(opts)
decoder := func(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
if pp, err := compiler.compile(_vt); err != nil {
return nil, err
} else {
as := newAssembler(pp)
as.name = _vt.String()
return as.Load(), nil
}
}
/* find or compile */
vt := rt.UnpackType(_vt)
if val := programCache.Get(vt); val != nil {
return nil, nil
} else if _, err := programCache.Compute(vt, decoder); err == nil {
return compiler.rec, nil
} else {
return nil, err
}
}
func pretouchRec(vtm map[reflect.Type]bool, opts option.CompileOptions) error {
if opts.RecursiveDepth < 0 || len(vtm) == 0 {
return nil
}
next := make(map[reflect.Type]bool)
for vt := range(vtm) {
sub, err := pretouchType(vt, opts)
if err != nil {
return err
}
for svt := range(sub) {
next[svt] = true
}
}
opts.RecursiveDepth -= 1
return pretouchRec(next, opts)
return pretouchImpl(vt, opts...)
}
// Skip skips only one json value, and returns first non-blank character position and its ending position if it is valid.
@@ -0,0 +1,38 @@
//go:build go1.17 && !go1.24
// +build go1.17,!go1.24
/*
* 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 api
import (
"github.com/bytedance/sonic/internal/envs"
"github.com/bytedance/sonic/internal/decoder/jitdec"
"github.com/bytedance/sonic/internal/decoder/optdec"
)
var (
pretouchImpl = jitdec.Pretouch
decodeImpl = jitdec.Decode
)
func init() {
if envs.UseOptDec {
pretouchImpl = optdec.Pretouch
decodeImpl = optdec.Decode
}
}
@@ -1,6 +1,4 @@
// Code generated by Makefile, DO NOT EDIT.
// Code generated by Makefile, DO NOT EDIT.
// +build go1.17,!go1.24
/*
* Copyright 2021 ByteDance Inc.
@@ -18,19 +16,23 @@
* limitations under the License.
*/
package avx
package api
import (
`unsafe`
`github.com/bytedance/sonic/internal/rt`
`github.com/bytedance/sonic/internal/decoder/optdec`
`github.com/bytedance/sonic/internal/envs`
)
var F_u64toa func(out unsafe.Pointer, val uint64) (ret int)
var (
pretouchImpl = optdec.Pretouch
decodeImpl = optdec.Decode
)
var S_u64toa uintptr
//go:nosplit
func u64toa(out *byte, val uint64) (ret int) {
return F_u64toa(rt.NoEscape(unsafe.Pointer(out)), val)
func init() {
// whe in aarch64. we enable all optimize
envs.EnableOptDec()
envs.EnableFastMap()
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package decoder
package api
import (
`bytes`
@@ -47,6 +47,12 @@ var bufPool = sync.Pool{
},
}
func freeBytes(buf []byte) {
if rt.CanSizeResue(cap(buf)) {
bufPool.Put(buf[:0])
}
}
// NewStreamDecoder adapts to encoding/json.NewDecoder API.
//
// NewStreamDecoder returns a new decoder that reads from r.
@@ -61,25 +67,16 @@ func NewStreamDecoder(r io.Reader) *StreamDecoder {
func (self *StreamDecoder) Decode(val interface{}) (err error) {
// read more data into buf
if self.More() {
// println(string(self.buf))
var s = self.scanp
try_skip:
var e = len(self.buf)
// println("s:", s, "e:", e, "scanned:",self.scanned, "scanp:",self.scanp, self.buf)
var src = rt.Mem2Str(self.buf[s:e])
// if len(src) > 5 {
// println(src[:5], src[len(src)-5:])
// } else {
// println(src)
// }
// try skip
var x = 0;
if y := native.SkipOneFast(&src, &x); y < 0 {
if self.readMore() {
// println("more")
goto try_skip
} else {
// println("no more")
err = SyntaxError{e, self.s, types.ParsingError(-s), ""}
self.setErr(err)
return
@@ -89,7 +86,6 @@ func (self *StreamDecoder) Decode(val interface{}) (err error) {
e = x + s
}
// println("decode: ", s, e)
// must copy string here for safety
self.Decoder.Reset(string(self.buf[s:e]))
err = self.Decoder.Decode(val)
@@ -101,13 +97,11 @@ func (self *StreamDecoder) Decode(val interface{}) (err error) {
self.scanp = e
_, empty := self.scan()
if empty {
// println("recycle")
// no remain valid bytes, thus we just recycle buffer
mem := self.buf
self.buf = nil
bufPool.Put(mem[:0])
freeBytes(mem)
} else {
// println("keep")
// remain undecoded bytes, move them onto head
n := copy(self.buf, self.buf[self.scanp:])
self.buf = self.buf[:n]
@@ -123,7 +117,6 @@ func (self *StreamDecoder) Decode(val interface{}) (err error) {
// InputOffset returns the input stream byte offset of the current decoder position.
// The offset gives the location of the end of the most recently returned token and the beginning of the next token.
func (self *StreamDecoder) InputOffset() int64 {
// println("input offset",self.scanned, self.scanp)
return self.scanned + int64(self.scanp)
}
@@ -178,7 +171,7 @@ func (self *StreamDecoder) setErr(err error) {
self.err = err
mem := self.buf[:0]
self.buf = nil
bufPool.Put(mem)
freeBytes(mem)
}
func (self *StreamDecoder) peek() (byte, error) {
@@ -237,12 +230,10 @@ func realloc(buf *[]byte) bool {
l := uint(len(*buf))
c := uint(cap(*buf))
if c == 0 {
// println("use pool!")
*buf = bufPool.Get().([]byte)
return true
}
if c - l <= c >> minLeftBufferShift {
// println("realloc!")
e := l+(l>>minLeftBufferShift)
if e <= c {
e = c*2
@@ -1,130 +0,0 @@
// +build go1.16,!go1.17
// Copyright 2023 CloudWeGo Authors
//
// 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 decoder
import (
`strconv`
_ `unsafe`
`github.com/bytedance/sonic/internal/jit`
`github.com/bytedance/sonic/internal/rt`
`github.com/twitchyliquid64/golang-asm/obj`
`github.com/twitchyliquid64/golang-asm/obj/x86`
)
var _runtime_writeBarrier uintptr = rt.GcwbAddr()
//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
func gcWriteBarrierAX()
var (
_V_writeBarrier = jit.Imm(int64(_runtime_writeBarrier))
_F_gcWriteBarrierAX = jit.Func(gcWriteBarrierAX)
)
func (self *_Assembler) WritePtrAX(i int, rec obj.Addr, saveDI bool) {
self.Emit("MOVQ", _V_writeBarrier, _R10)
self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
if saveDI {
self.save(_DI)
}
self.Emit("LEAQ", rec, _DI)
self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10) // MOVQ ${fn}, AX
self.Rjmp("CALL", _R10)
if saveDI {
self.load(_DI)
}
self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Emit("MOVQ", _AX, rec)
self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
}
func (self *_Assembler) WriteRecNotAX(i int, ptr obj.Addr, rec obj.Addr, saveDI bool, saveAX bool) {
if rec.Reg == x86.REG_AX || rec.Index == x86.REG_AX {
panic("rec contains AX!")
}
self.Emit("MOVQ", _V_writeBarrier, _R10)
self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
if saveAX {
self.Emit("XCHGQ", ptr, _AX)
} else {
self.Emit("MOVQ", ptr, _AX)
}
if saveDI {
self.save(_DI)
}
self.Emit("LEAQ", rec, _DI)
self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10) // MOVQ ${fn}, AX
self.Rjmp("CALL", _R10)
if saveDI {
self.load(_DI)
}
if saveAX {
self.Emit("XCHGQ", ptr, _AX)
}
self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Emit("MOVQ", ptr, rec)
self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
}
func (self *_ValueDecoder) WritePtrAX(i int, rec obj.Addr, saveDI bool) {
self.Emit("MOVQ", _V_writeBarrier, _R10)
self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
if saveDI {
self.save(_DI)
}
self.Emit("LEAQ", rec, _DI)
self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10) // MOVQ ${fn}, AX
self.Rjmp("CALL", _R10)
if saveDI {
self.load(_DI)
}
self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Emit("MOVQ", _AX, rec)
self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
}
func (self *_ValueDecoder) WriteRecNotAX(i int, ptr obj.Addr, rec obj.Addr, saveDI bool) {
if rec.Reg == x86.REG_AX || rec.Index == x86.REG_AX {
panic("rec contains AX!")
}
self.Emit("MOVQ", _V_writeBarrier, _R10)
self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Emit("MOVQ", ptr, _AX)
if saveDI {
self.save(_DI)
}
self.Emit("LEAQ", rec, _DI)
self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10) // MOVQ ${fn}, AX
self.Rjmp("CALL", _R10)
if saveDI {
self.load(_DI)
}
self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Emit("MOVQ", ptr, rec)
self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
}
File diff suppressed because it is too large. Load diff
+36
View File
@@ -0,0 +1,36 @@
package consts
import (
`github.com/bytedance/sonic/internal/native/types`
)
const (
F_use_int64 = 0
F_disable_urc = 2
F_disable_unknown = 3
F_copy_string = 4
F_use_number = types.B_USE_NUMBER
F_validate_string = types.B_VALIDATE_STRING
F_allow_control = types.B_ALLOW_CONTROL
F_no_validate_json = types.B_NO_VALIDATE_JSON
)
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
OptionValidateString Options = 1 << F_validate_string
OptionNoValidateJSON Options = 1 << F_no_validate_json
)
const (
MaxStack = 4096
)
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package decoder
package errors
import (
`encoding/json`
@@ -46,7 +46,7 @@ func (self SyntaxError) Description() string {
func (self SyntaxError) description() string {
/* check for empty source */
if self.Src == "" {
return fmt.Sprintf("no sources available: %#v", self)
return fmt.Sprintf("no sources available, the input json is empty: %#v", self)
}
p, x, q, y := calcBounds(len(self.Src), self.Pos)
@@ -112,12 +112,12 @@ func clamp_zero(v int) int {
/** JIT Error Helpers **/
var stackOverflow = &json.UnsupportedValueError {
var StackOverflow = &json.UnsupportedValueError {
Str : "Value nesting too deep",
Value : reflect.ValueOf("..."),
}
func error_wrap(src string, pos int, code types.ParsingError) error {
func ErrorWrap(src string, pos int, code types.ParsingError) error {
return *error_wrap_heap(src, pos, code)
}
@@ -130,7 +130,7 @@ func error_wrap_heap(src string, pos int, code types.ParsingError) *SyntaxError
}
}
func error_type(vt *rt.GoType) error {
func ErrorType(vt *rt.GoType) error {
return &json.UnmarshalTypeError{Type: vt.Pack()}
}
@@ -171,7 +171,7 @@ func (self MismatchTypeError) Description() string {
return fmt.Sprintf("Mismatch type %s with value %s %s", self.Type.String(), swithchJSONType(self.Src, self.Pos), se.description())
}
func error_mismatch(src string, pos int, vt *rt.GoType) error {
func ErrorMismatch(src string, pos int, vt *rt.GoType) error {
return &MismatchTypeError {
Pos : pos,
Src : src,
@@ -179,11 +179,11 @@ func error_mismatch(src string, pos int, vt *rt.GoType) error {
}
}
func error_field(name string) error {
func ErrorField(name string) error {
return errors.New("json: unknown field " + strconv.Quote(name))
}
func error_value(value string, vtype reflect.Type) error {
func ErrorValue(value string, vtype reflect.Type) error {
return &json.UnmarshalTypeError {
Type : vtype,
Value : value,
@@ -1,733 +0,0 @@
// +build go1.16,!go1.17
/*
* 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 decoder
import (
`encoding/json`
`fmt`
`reflect`
`github.com/bytedance/sonic/internal/jit`
`github.com/bytedance/sonic/internal/native`
`github.com/bytedance/sonic/internal/native/types`
`github.com/twitchyliquid64/golang-asm/obj`
)
/** Crucial Registers:
*
* ST(BX) : ro, decoder stack
* DF(R10) : ro, decoder flags
* EP(R11) : wo, error pointer
* IP(R12) : ro, input pointer
* IL(R13) : ro, input length
* IC(R14) : rw, input cursor
* VP(R15) : ro, value pointer (to an interface{})
*/
const (
_VD_args = 8 // 8 bytes for passing arguments to this functions
_VD_fargs = 64 // 64 bytes for passing arguments to other Go functions
_VD_saves = 40 // 40 bytes for saving the registers before CALL instructions
_VD_locals = 88 // 88 bytes for local variables
)
const (
_VD_offs = _VD_fargs + _VD_saves + _VD_locals
_VD_size = _VD_offs + 8 // 8 bytes for the parent frame pointer
)
var (
_VAR_ss = _VAR_ss_Vt
_VAR_df = jit.Ptr(_SP, _VD_fargs + _VD_saves)
)
var (
_VAR_ss_Vt = jit.Ptr(_SP, _VD_fargs + _VD_saves + 8)
_VAR_ss_Dv = jit.Ptr(_SP, _VD_fargs + _VD_saves + 16)
_VAR_ss_Iv = jit.Ptr(_SP, _VD_fargs + _VD_saves + 24)
_VAR_ss_Ep = jit.Ptr(_SP, _VD_fargs + _VD_saves + 32)
_VAR_ss_Db = jit.Ptr(_SP, _VD_fargs + _VD_saves + 40)
_VAR_ss_Dc = jit.Ptr(_SP, _VD_fargs + _VD_saves + 48)
)
var (
_VAR_cs_LR = jit.Ptr(_SP, _VD_fargs + _VD_saves + 56)
_VAR_cs_p = jit.Ptr(_SP, _VD_fargs + _VD_saves + 64)
_VAR_cs_n = jit.Ptr(_SP, _VD_fargs + _VD_saves + 72)
_VAR_cs_d = jit.Ptr(_SP, _VD_fargs + _VD_saves + 80)
)
type _ValueDecoder struct {
jit.BaseAssembler
}
func (self *_ValueDecoder) build() uintptr {
self.Init(self.compile)
return *(*uintptr)(self.Load("decode_value", _VD_size, _VD_args, argPtrs_generic, localPtrs_generic))
}
/** Function Calling Helpers **/
func (self *_ValueDecoder) save(r ...obj.Addr) {
for i, v := range r {
if i > _VD_saves / 8 - 1 {
panic("too many registers to save")
} else {
self.Emit("MOVQ", v, jit.Ptr(_SP, _VD_fargs + int64(i) * 8))
}
}
}
func (self *_ValueDecoder) load(r ...obj.Addr) {
for i, v := range r {
if i > _VD_saves / 8 - 1 {
panic("too many registers to load")
} else {
self.Emit("MOVQ", jit.Ptr(_SP, _VD_fargs + int64(i) * 8), v)
}
}
}
func (self *_ValueDecoder) call(fn obj.Addr) {
self.Emit("MOVQ", fn, _AX) // MOVQ ${fn}, AX
self.Rjmp("CALL", _AX) // CALL AX
}
func (self *_ValueDecoder) call_go(fn obj.Addr) {
self.save(_REG_go...) // SAVE $REG_go
self.call(fn) // CALL ${fn}
self.load(_REG_go...) // LOAD $REG_go
}
/** Decoder Assembler **/
const (
_S_val = iota + 1
_S_arr
_S_arr_0
_S_obj
_S_obj_0
_S_obj_delim
_S_obj_sep
)
const (
_S_omask_key = (1 << _S_obj_0) | (1 << _S_obj_sep)
_S_omask_end = (1 << _S_obj_0) | (1 << _S_obj)
_S_vmask = (1 << _S_val) | (1 << _S_arr_0)
)
const (
_A_init_len = 1
_A_init_cap = 16
)
const (
_ST_Sp = 0
_ST_Vt = _PtrBytes
_ST_Vp = _PtrBytes * (types.MAX_RECURSE + 1)
)
var (
_V_true = jit.Imm(int64(pbool(true)))
_V_false = jit.Imm(int64(pbool(false)))
_F_value = jit.Imm(int64(native.S_value))
)
var (
_V_max = jit.Imm(int64(types.V_MAX))
_E_eof = jit.Imm(int64(types.ERR_EOF))
_E_invalid = jit.Imm(int64(types.ERR_INVALID_CHAR))
_E_recurse = jit.Imm(int64(types.ERR_RECURSE_EXCEED_MAX))
)
var (
_F_convTslice = jit.Func(convTslice)
_F_convTstring = jit.Func(convTstring)
_F_invalid_vtype = jit.Func(invalid_vtype)
)
var (
_T_map = jit.Type(reflect.TypeOf((map[string]interface{})(nil)))
_T_bool = jit.Type(reflect.TypeOf(false))
_T_int64 = jit.Type(reflect.TypeOf(int64(0)))
_T_eface = jit.Type(reflect.TypeOf((*interface{})(nil)).Elem())
_T_slice = jit.Type(reflect.TypeOf(([]interface{})(nil)))
_T_string = jit.Type(reflect.TypeOf(""))
_T_number = jit.Type(reflect.TypeOf(json.Number("")))
_T_float64 = jit.Type(reflect.TypeOf(float64(0)))
)
var _R_tab = map[int]string {
'[': "_decode_V_ARRAY",
'{': "_decode_V_OBJECT",
':': "_decode_V_KEY_SEP",
',': "_decode_V_ELEM_SEP",
']': "_decode_V_ARRAY_END",
'}': "_decode_V_OBJECT_END",
}
func (self *_ValueDecoder) compile() {
self.Emit("SUBQ", jit.Imm(_VD_size), _SP) // SUBQ $_VD_size, SP
self.Emit("MOVQ", _BP, jit.Ptr(_SP, _VD_offs)) // MOVQ BP, _VD_offs(SP)
self.Emit("LEAQ", jit.Ptr(_SP, _VD_offs), _BP) // LEAQ _VD_offs(SP), BP
/* initialize the state machine */
self.Emit("XORL", _CX, _CX) // XORL CX, CX
self.Emit("MOVQ", _DF, _VAR_df) // MOVQ DF, df
/* initialize digital buffer first */
self.Emit("MOVQ", jit.Imm(_MaxDigitNums), _VAR_ss_Dc) // MOVQ $_MaxDigitNums, ss.Dcap
self.Emit("LEAQ", jit.Ptr(_ST, _DbufOffset), _AX) // LEAQ _DbufOffset(ST), AX
self.Emit("MOVQ", _AX, _VAR_ss_Db) // MOVQ AX, ss.Dbuf
/* add ST offset */
self.Emit("ADDQ", jit.Imm(_FsmOffset), _ST) // ADDQ _FsmOffset, _ST
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
self.WriteRecNotAX(0, _VP, jit.Ptr(_ST, _ST_Vp), false) // MOVQ VP, ST.Vp[0]
self.Emit("MOVQ", jit.Imm(_S_val), jit.Ptr(_ST, _ST_Vt)) // MOVQ _S_val, ST.Vt[0]
self.Sjmp("JMP" , "_next") // JMP _next
/* set the value from previous round */
self.Link("_set_value") // _set_value:
self.Emit("MOVL" , jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX
self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ" , jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX
self.Sjmp("JNC" , "_vtype_error") // JNC _vtype_error
self.Emit("XORL" , _SI, _SI) // XORL SI, SI
self.Emit("SUBQ" , jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp
self.Emit("XCHGQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // XCHGQ ST.Vp[CX], SI
self.Emit("MOVQ" , _R8, jit.Ptr(_SI, 0)) // MOVQ R8, (SI)
self.WriteRecNotAX(1, _R9, jit.Ptr(_SI, 8), false) // MOVQ R9, 8(SI)
/* check for value stack */
self.Link("_next") // _next:
self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _AX) // MOVQ ST.Sp, AX
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
self.Sjmp("JS" , "_return") // JS _return
/* fast path: test up to 4 characters manually */
self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL
self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF
self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX
self.Emit("MOVQ" , jit.Imm(_BM_space), _DX) // MOVQ _BM_space, DX
self.Emit("CMPQ" , _AX, jit.Imm(' ')) // CMPQ AX, $' '
self.Sjmp("JA" , "_decode_fast") // JA _decode_fast
self.Emit("BTQ" , _AX, _DX) // BTQ _AX, _DX
self.Sjmp("JNC" , "_decode_fast") // JNC _decode_fast
self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC
/* at least 1 to 3 spaces */
for i := 0; i < 3; i++ {
self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL
self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF
self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX
self.Emit("CMPQ" , _AX, jit.Imm(' ')) // CMPQ AX, $' '
self.Sjmp("JA" , "_decode_fast") // JA _decode_fast
self.Emit("BTQ" , _AX, _DX) // BTQ _AX, _DX
self.Sjmp("JNC" , "_decode_fast") // JNC _decode_fast
self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC
}
/* at least 4 spaces */
self.Emit("CMPQ" , _IC, _IL) // CMPQ IC, IL
self.Sjmp("JAE" , "_decode_V_EOF") // JAE _decode_V_EOF
self.Emit("MOVBQZX", jit.Sib(_IP, _IC, 1, 0), _AX) // MOVBQZX (IP)(IC), AX
/* fast path: use lookup table to select decoder */
self.Link("_decode_fast") // _decode_fast:
self.Byte(0x48, 0x8d, 0x3d) // LEAQ ?(PC), DI
self.Sref("_decode_tab", 4) // .... &_decode_tab
self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, 0), _AX) // MOVLQSX (DI)(AX*4), AX
self.Emit("TESTQ" , _AX, _AX) // TESTQ AX, AX
self.Sjmp("JZ" , "_decode_native") // JZ _decode_native
self.Emit("ADDQ" , jit.Imm(1), _IC) // ADDQ $1, IC
self.Emit("ADDQ" , _DI, _AX) // ADDQ DI, AX
self.Rjmp("JMP" , _AX) // JMP AX
/* decode with native decoder */
self.Link("_decode_native") // _decode_native:
self.Emit("MOVQ", _IP, _DI) // MOVQ IP, DI
self.Emit("MOVQ", _IL, _SI) // MOVQ IL, SI
self.Emit("MOVQ", _IC, _DX) // MOVQ IC, DX
self.Emit("LEAQ", _VAR_ss, _CX) // LEAQ ss, CX
self.Emit("MOVQ", _VAR_df, _R8) // MOVQ $df, R8
self.Emit("BTSQ", jit.Imm(_F_allow_control), _R8) // ANDQ $1<<_F_allow_control, R8
self.call(_F_value) // CALL value
self.Emit("MOVQ", _AX, _IC) // MOVQ AX, IC
/* check for errors */
self.Emit("MOVQ" , _VAR_ss_Vt, _AX) // MOVQ ss.Vt, AX
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
self.Sjmp("JS" , "_parsing_error")
self.Sjmp("JZ" , "_invalid_vtype") // JZ _invalid_vtype
self.Emit("CMPQ" , _AX, _V_max) // CMPQ AX, _V_max
self.Sjmp("JA" , "_invalid_vtype") // JA _invalid_vtype
/* jump table selector */
self.Byte(0x48, 0x8d, 0x3d) // LEAQ ?(PC), DI
self.Sref("_switch_table", 4) // .... &_switch_table
self.Emit("MOVLQSX", jit.Sib(_DI, _AX, 4, -4), _AX) // MOVLQSX -4(DI)(AX*4), AX
self.Emit("ADDQ" , _DI, _AX) // ADDQ DI, AX
self.Rjmp("JMP" , _AX) // JMP AX
/** V_EOF **/
self.Link("_decode_V_EOF") // _decode_V_EOF:
self.Emit("MOVL", _E_eof, _EP) // MOVL _E_eof, EP
self.Sjmp("JMP" , "_error") // JMP _error
/** V_NULL **/
self.Link("_decode_V_NULL") // _decode_V_NULL:
self.Emit("XORL", _R8, _R8) // XORL R8, R8
self.Emit("XORL", _R9, _R9) // XORL R9, R9
self.Emit("LEAQ", jit.Ptr(_IC, -4), _DI) // LEAQ -4(IC), DI
self.Sjmp("JMP" , "_set_value") // JMP _set_value
/** V_TRUE **/
self.Link("_decode_V_TRUE") // _decode_V_TRUE:
self.Emit("MOVQ", _T_bool, _R8) // MOVQ _T_bool, R8
// TODO: maybe modified by users?
self.Emit("MOVQ", _V_true, _R9) // MOVQ _V_true, R9
self.Emit("LEAQ", jit.Ptr(_IC, -4), _DI) // LEAQ -4(IC), DI
self.Sjmp("JMP" , "_set_value") // JMP _set_value
/** V_FALSE **/
self.Link("_decode_V_FALSE") // _decode_V_FALSE:
self.Emit("MOVQ", _T_bool, _R8) // MOVQ _T_bool, R8
self.Emit("MOVQ", _V_false, _R9) // MOVQ _V_false, R9
self.Emit("LEAQ", jit.Ptr(_IC, -5), _DI) // LEAQ -5(IC), DI
self.Sjmp("JMP" , "_set_value") // JMP _set_value
/** V_ARRAY **/
self.Link("_decode_V_ARRAY") // _decode_V_ARRAY
self.Emit("MOVL", jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX
self.Sjmp("JNC" , "_invalid_char") // JNC _invalid_char
/* create a new array */
self.Emit("MOVQ", _T_eface, _AX) // MOVQ _T_eface, AX
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
self.Emit("MOVQ", jit.Imm(_A_init_len), jit.Ptr(_SP, 8)) // MOVQ _A_init_len, 8(SP)
self.Emit("MOVQ", jit.Imm(_A_init_cap), jit.Ptr(_SP, 16)) // MOVQ _A_init_cap, 16(SP)
self.call_go(_F_makeslice) // CALL_GO runtime.makeslice
self.Emit("MOVQ", jit.Ptr(_SP, 24), _DX) // MOVQ 24(SP), DX
/* pack into an interface */
self.Emit("MOVQ", _DX, jit.Ptr(_SP, 0)) // MOVQ DX, (SP)
self.Emit("MOVQ", jit.Imm(_A_init_len), jit.Ptr(_SP, 8)) // MOVQ _A_init_len, 8(SP)
self.Emit("MOVQ", jit.Imm(_A_init_cap), jit.Ptr(_SP, 16)) // MOVQ _A_init_cap, 16(SP)
self.call_go(_F_convTslice) // CALL_GO runtime.convTslice
self.Emit("MOVQ", jit.Ptr(_SP, 24), _R8) // MOVQ 24(SP), R8
/* replace current state with an array */
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
self.Emit("MOVQ", jit.Imm(_S_arr), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_arr, ST.Vt[CX]
self.Emit("MOVQ", _T_slice, _AX) // MOVQ _T_slice, AX
self.Emit("MOVQ", _AX, jit.Ptr(_SI, 0)) // MOVQ AX, (SI)
self.WriteRecNotAX(2, _R8, jit.Ptr(_SI, 8), false) // MOVQ R8, 8(SI)
/* add a new slot for the first element */
self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX
self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE}
self.Sjmp("JAE" , "_stack_overflow") // JA _stack_overflow
self.Emit("MOVQ", jit.Ptr(_R8, 0), _AX) // MOVQ (R8), AX
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
self.WritePtrAX(3, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ AX, ST.Vp[CX]
self.Emit("MOVQ", jit.Imm(_S_arr_0), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_arr_0, ST.Vt[CX]
self.Sjmp("JMP" , "_next") // JMP _next
/** V_OBJECT **/
self.Link("_decode_V_OBJECT") // _decode_V_OBJECT:
self.Emit("MOVL", jit.Imm(_S_vmask), _DX) // MOVL _S_vmask, DX
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
self.Emit("BTQ" , _AX, _DX) // BTQ AX, DX
self.Sjmp("JNC" , "_invalid_char") // JNC _invalid_char
self.call_go(_F_makemap_small) // CALL_GO runtime.makemap_small
self.Emit("MOVQ", jit.Ptr(_SP, 0), _AX) // MOVQ (SP), AX
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ", jit.Imm(_S_obj_0), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_obj, ST.Vt[CX]
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
self.Emit("MOVQ", _T_map, _DX) // MOVQ _T_map, DX
self.Emit("MOVQ", _DX, jit.Ptr(_SI, 0)) // MOVQ DX, (SI)
self.WritePtrAX(4, jit.Ptr(_SI, 8), false) // MOVQ AX, 8(SI)
self.Sjmp("JMP" , "_next") // JMP _next
/** V_STRING **/
self.Link("_decode_V_STRING") // _decode_V_STRING:
self.Emit("MOVQ", _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX
self.Emit("MOVQ", _IC, _AX) // MOVQ IC, AX
self.Emit("SUBQ", _CX, _AX) // SUBQ CX, AX
/* check for escapes */
self.Emit("CMPQ", _VAR_ss_Ep, jit.Imm(-1)) // CMPQ ss.Ep, $-1
self.Sjmp("JNE" , "_unquote") // JNE _unquote
self.Emit("SUBQ", jit.Imm(1), _AX) // SUBQ $1, AX
self.Emit("LEAQ", jit.Sib(_IP, _CX, 1, 0), _R8) // LEAQ (IP)(CX), R8
self.Byte(0x48, 0x8d, 0x3d) // LEAQ (PC), DI
self.Sref("_copy_string_end", 4)
self.Emit("BTQ", jit.Imm(_F_copy_string), _VAR_df)
self.Sjmp("JC", "copy_string")
self.Link("_copy_string_end")
self.Emit("XORL", _DX, _DX) // XORL DX, DX
/* strings with no escape sequences */
self.Link("_noescape") // _noescape:
self.Emit("MOVL", jit.Imm(_S_omask_key), _DI) // MOVL _S_omask, DI
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _SI) // MOVQ ST.Vt[CX], SI
self.Emit("BTQ" , _SI, _DI) // BTQ SI, DI
self.Sjmp("JC" , "_object_key") // JC _object_key
/* check for pre-packed strings, avoid 1 allocation */
self.Emit("TESTQ", _DX, _DX) // TESTQ DX, DX
self.Sjmp("JNZ" , "_packed_str") // JNZ _packed_str
self.Emit("MOVQ" , _R8, jit.Ptr(_SP, 0)) // MOVQ R8, (SP)
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 8)) // MOVQ AX, 8(SP)
self.call_go(_F_convTstring) // CALL_GO runtime.convTstring
self.Emit("MOVQ" , jit.Ptr(_SP, 16), _R9) // MOVQ 16(SP), R9
/* packed string already in R9 */
self.Link("_packed_str") // _packed_str:
self.Emit("MOVQ", _T_string, _R8) // MOVQ _T_string, R8
self.Emit("MOVQ", _VAR_ss_Iv, _DI) // MOVQ ss.Iv, DI
self.Emit("SUBQ", jit.Imm(1), _DI) // SUBQ $1, DI
self.Sjmp("JMP" , "_set_value") // JMP _set_value
/* the string is an object key, get the map */
self.Link("_object_key")
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
/* add a new delimiter */
self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX
self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE}
self.Sjmp("JAE" , "_stack_overflow") // JA _stack_overflow
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
self.Emit("MOVQ", jit.Imm(_S_obj_delim), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_obj_delim, ST.Vt[CX]
/* add a new slot int the map */
self.Emit("MOVQ", _T_map, _DX) // MOVQ _T_map, DX
self.Emit("MOVQ", _DX, jit.Ptr(_SP, 0)) // MOVQ DX, (SP)
self.Emit("MOVQ", _SI, jit.Ptr(_SP, 8)) // MOVQ SI, 8(SP)
self.Emit("MOVQ", _R8, jit.Ptr(_SP, 16)) // MOVQ R9, 16(SP)
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 24)) // MOVQ AX, 24(SP)
self.call_go(_F_mapassign_faststr) // CALL_GO runtime.mapassign_faststr
self.Emit("MOVQ", jit.Ptr(_SP, 32), _AX) // MOVQ 32(SP), AX
/* add to the pointer stack */
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.WritePtrAX(6, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ AX, ST.Vp[CX]
self.Sjmp("JMP" , "_next") // JMP _next
/* allocate memory to store the string header and unquoted result */
self.Link("_unquote") // _unquote:
self.Emit("ADDQ", jit.Imm(15), _AX) // ADDQ $15, AX
self.Emit("MOVQ", _T_byte, _CX) // MOVQ _T_byte, CX
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
self.Emit("MOVQ", _CX, jit.Ptr(_SP, 8)) // MOVQ CX, 8(SP)
self.Emit("MOVB", jit.Imm(0), jit.Ptr(_SP, 16)) // MOVB $0, 16(SP)
self.call_go(_F_mallocgc) // CALL_GO runtime.mallocgc
self.Emit("MOVQ", jit.Ptr(_SP, 24), _R9) // MOVQ 24(SP), R9
/* prepare the unquoting parameters */
self.Emit("MOVQ" , _VAR_ss_Iv, _CX) // MOVQ ss.Iv, CX
self.Emit("LEAQ" , jit.Sib(_IP, _CX, 1, 0), _DI) // LEAQ (IP)(CX), DI
self.Emit("NEGQ" , _CX) // NEGQ CX
self.Emit("LEAQ" , jit.Sib(_IC, _CX, 1, -1), _SI) // LEAQ -1(IC)(CX), SI
self.Emit("LEAQ" , jit.Ptr(_R9, 16), _DX) // LEAQ 16(R8), DX
self.Emit("LEAQ" , _VAR_ss_Ep, _CX) // LEAQ ss.Ep, CX
self.Emit("XORL" , _R8, _R8) // XORL R8, R8
self.Emit("BTQ" , jit.Imm(_F_disable_urc), _VAR_df) // BTQ ${_F_disable_urc}, fv
self.Emit("SETCC", _R8) // SETCC R8
self.Emit("SHLQ" , jit.Imm(types.B_UNICODE_REPLACE), _R8) // SHLQ ${types.B_UNICODE_REPLACE}, R8
/* unquote the string, with R9 been preserved */
self.save(_R9) // SAVE R9
self.call(_F_unquote) // CALL unquote
self.load(_R9) // LOAD R9
/* check for errors */
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
self.Sjmp("JS" , "_unquote_error") // JS _unquote_error
self.Emit("MOVL" , jit.Imm(1), _DX) // MOVL $1, DX
self.Emit("LEAQ" , jit.Ptr(_R9, 16), _R8) // ADDQ $16, R8
self.Emit("MOVQ" , _R8, jit.Ptr(_R9, 0)) // MOVQ R8, (R9)
self.Emit("MOVQ" , _AX, jit.Ptr(_R9, 8)) // MOVQ AX, 8(R9)
self.Sjmp("JMP" , "_noescape") // JMP _noescape
/** V_DOUBLE **/
self.Link("_decode_V_DOUBLE") // _decode_V_DOUBLE:
self.Emit("BTQ" , jit.Imm(_F_use_number), _VAR_df) // BTQ _F_use_number, df
self.Sjmp("JC" , "_use_number") // JC _use_number
self.Emit("MOVSD", _VAR_ss_Dv, _X0) // MOVSD ss.Dv, X0
self.Sjmp("JMP" , "_use_float64") // JMP _use_float64
/** V_INTEGER **/
self.Link("_decode_V_INTEGER") // _decode_V_INTEGER:
self.Emit("BTQ" , jit.Imm(_F_use_number), _VAR_df) // BTQ _F_use_number, df
self.Sjmp("JC" , "_use_number") // JC _use_number
self.Emit("BTQ" , jit.Imm(_F_use_int64), _VAR_df) // BTQ _F_use_int64, df
self.Sjmp("JC" , "_use_int64") // JC _use_int64
self.Emit("MOVQ" , _VAR_ss_Iv, _AX) // MOVQ ss.Iv, AX
self.Emit("CVTSQ2SD", _AX, _X0) // CVTSQ2SD AX, X0
/* represent numbers as `float64` */
self.Link("_use_float64") // _use_float64:
self.Emit("MOVSD", _X0, jit.Ptr(_SP, 0)) // MOVSD X0, (SP)
self.call_go(_F_convT64) // CALL_GO runtime.convT64
self.Emit("MOVQ" , _T_float64, _R8) // MOVQ _T_float64, R8
self.Emit("MOVQ" , jit.Ptr(_SP, 8), _R9) // MOVQ 8(SP), R9
self.Emit("MOVQ" , _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI
self.Sjmp("JMP" , "_set_value") // JMP _set_value
/* represent numbers as `json.Number` */
self.Link("_use_number") // _use_number
self.Emit("MOVQ", _VAR_ss_Ep, _AX) // MOVQ ss.Ep, AX
self.Emit("LEAQ", jit.Sib(_IP, _AX, 1, 0), _SI) // LEAQ (IP)(AX), SI
self.Emit("MOVQ", _IC, _CX) // MOVQ IC, CX
self.Emit("SUBQ", _AX, _CX) // SUBQ AX, CX
self.Emit("MOVQ", _SI, jit.Ptr(_SP, 0)) // MOVQ SI, (SP)
self.Emit("MOVQ", _CX, jit.Ptr(_SP, 8)) // MOVQ CX, 8(SP)
self.call_go(_F_convTstring) // CALL_GO runtime.convTstring
self.Emit("MOVQ", _T_number, _R8) // MOVQ _T_number, R8
self.Emit("MOVQ", jit.Ptr(_SP, 16), _R9) // MOVQ 16(SP), R9
self.Emit("MOVQ", _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI
self.Sjmp("JMP" , "_set_value") // JMP _set_value
/* represent numbers as `int64` */
self.Link("_use_int64") // _use_int64:
self.Emit("MOVQ", _VAR_ss_Iv, _AX) // MOVQ ss.Iv, AX
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
self.call_go(_F_convT64) // CALL_GO runtime.convT64
self.Emit("MOVQ", _T_int64, _R8) // MOVQ _T_int64, R8
self.Emit("MOVQ", jit.Ptr(_SP, 8), _R9) // MOVQ 8(SP), R9
self.Emit("MOVQ", _VAR_ss_Ep, _DI) // MOVQ ss.Ep, DI
self.Sjmp("JMP" , "_set_value") // JMP _set_value
/** V_KEY_SEP **/
self.Link("_decode_V_KEY_SEP") // _decode_V_KEY_SEP:
// self.Byte(0xcc)
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
self.Emit("CMPQ", _AX, jit.Imm(_S_obj_delim)) // CMPQ AX, _S_obj_delim
self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char
self.Emit("MOVQ", jit.Imm(_S_val), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_val, ST.Vt[CX]
self.Emit("MOVQ", jit.Imm(_S_obj), jit.Sib(_ST, _CX, 8, _ST_Vt - 8)) // MOVQ _S_obj, ST.Vt[CX - 1]
self.Sjmp("JMP" , "_next") // JMP _next
/** V_ELEM_SEP **/
self.Link("_decode_V_ELEM_SEP") // _decode_V_ELEM_SEP:
self.Emit("MOVQ" , jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ" , jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
self.Emit("CMPQ" , _AX, jit.Imm(_S_arr)) // CMPQ _AX, _S_arr
self.Sjmp("JE" , "_array_sep") // JZ _next
self.Emit("CMPQ" , _AX, jit.Imm(_S_obj)) // CMPQ _AX, _S_arr
self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char
self.Emit("MOVQ" , jit.Imm(_S_obj_sep), jit.Sib(_ST, _CX, 8, _ST_Vt))
self.Sjmp("JMP" , "_next") // JMP _next
/* arrays */
self.Link("_array_sep")
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
self.Emit("MOVQ", jit.Ptr(_SI, 8), _DX) // MOVQ 8(SI), DX
self.Emit("CMPQ", _DX, jit.Ptr(_SI, 16)) // CMPQ DX, 16(SI)
self.Sjmp("JAE" , "_array_more") // JAE _array_more
/* add a slot for the new element */
self.Link("_array_append") // _array_append:
self.Emit("ADDQ", jit.Imm(1), jit.Ptr(_SI, 8)) // ADDQ $1, 8(SI)
self.Emit("MOVQ", jit.Ptr(_SI, 0), _SI) // MOVQ (SI), SI
self.Emit("ADDQ", jit.Imm(1), _CX) // ADDQ $1, CX
self.Emit("CMPQ", _CX, jit.Imm(types.MAX_RECURSE)) // CMPQ CX, ${types.MAX_RECURSE}
self.Sjmp("JAE" , "_stack_overflow")
self.Emit("SHLQ", jit.Imm(1), _DX) // SHLQ $1, DX
self.Emit("LEAQ", jit.Sib(_SI, _DX, 8, 0), _SI) // LEAQ (SI)(DX*8), SI
self.Emit("MOVQ", _CX, jit.Ptr(_ST, _ST_Sp)) // MOVQ CX, ST.Sp
self.WriteRecNotAX(7 , _SI, jit.Sib(_ST, _CX, 8, _ST_Vp), false) // MOVQ SI, ST.Vp[CX]
self.Emit("MOVQ", jit.Imm(_S_val), jit.Sib(_ST, _CX, 8, _ST_Vt)) // MOVQ _S_val, ST.Vt[CX}
self.Sjmp("JMP" , "_next") // JMP _next
/** V_ARRAY_END **/
self.Link("_decode_V_ARRAY_END") // _decode_V_ARRAY_END:
self.Emit("XORL", _DX, _DX) // XORL DX, DX
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
self.Emit("CMPQ", _AX, jit.Imm(_S_arr_0)) // CMPQ AX, _S_arr_0
self.Sjmp("JE" , "_first_item") // JE _first_item
self.Emit("CMPQ", _AX, jit.Imm(_S_arr)) // CMPQ AX, _S_arr
self.Sjmp("JNE" , "_invalid_char") // JNE _invalid_char
self.Emit("SUBQ", jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp
self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ DX, ST.Vp[CX]
self.Sjmp("JMP" , "_next") // JMP _next
/* first element of an array */
self.Link("_first_item") // _first_item:
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("SUBQ", jit.Imm(2), jit.Ptr(_ST, _ST_Sp)) // SUBQ $2, ST.Sp
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp - 8), _SI) // MOVQ ST.Vp[CX - 1], SI
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp - 8)) // MOVQ DX, ST.Vp[CX - 1]
self.Emit("MOVQ", _DX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ DX, ST.Vp[CX]
self.Emit("MOVQ", _DX, jit.Ptr(_SI, 8)) // MOVQ DX, 8(SI)
self.Sjmp("JMP" , "_next") // JMP _next
/** V_OBJECT_END **/
self.Link("_decode_V_OBJECT_END") // _decode_V_OBJECT_END:
self.Emit("MOVL", jit.Imm(_S_omask_end), _DX) // MOVL _S_omask, DI
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vt), _AX) // MOVQ ST.Vt[CX], AX
self.Emit("BTQ" , _AX, _DX)
self.Sjmp("JNC" , "_invalid_char") // JNE _invalid_char
self.Emit("XORL", _AX, _AX) // XORL AX, AX
self.Emit("SUBQ", jit.Imm(1), jit.Ptr(_ST, _ST_Sp)) // SUBQ $1, ST.Sp
self.Emit("MOVQ", _AX, jit.Sib(_ST, _CX, 8, _ST_Vp)) // MOVQ AX, ST.Vp[CX]
self.Sjmp("JMP" , "_next") // JMP _next
/* return from decoder */
self.Link("_return") // _return:
self.Emit("XORL", _EP, _EP) // XORL EP, EP
self.Emit("MOVQ", _EP, jit.Ptr(_ST, _ST_Vp)) // MOVQ EP, ST.Vp[0]
self.Link("_epilogue") // _epilogue:
self.Emit("SUBQ", jit.Imm(_FsmOffset), _ST) // SUBQ _FsmOffset, _ST
self.Emit("MOVQ", jit.Ptr(_SP, _VD_offs), _BP) // MOVQ _VD_offs(SP), BP
self.Emit("ADDQ", jit.Imm(_VD_size), _SP) // ADDQ $_VD_size, SP
self.Emit("RET") // RET
/* array expand */
self.Link("_array_more") // _array_more:
self.Emit("MOVQ" , _T_eface, _AX) // MOVQ _T_eface, AX
self.Emit("MOVOU", jit.Ptr(_SI, 0), _X0) // MOVOU (SI), X0
self.Emit("MOVQ" , jit.Ptr(_SI, 16), _DX) // MOVQ 16(SI), DX
self.Emit("MOVQ" , _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
self.Emit("MOVOU", _X0, jit.Ptr(_SP, 8)) // MOVOU X0, 8(SP)
self.Emit("MOVQ" , _DX, jit.Ptr(_SP, 24)) // MOVQ DX, 24(SP)
self.Emit("SHLQ" , jit.Imm(1), _DX) // SHLQ $1, DX
self.Emit("MOVQ" , _DX, jit.Ptr(_SP, 32)) // MOVQ DX, 32(SP)
self.call_go(_F_growslice) // CALL_GO runtime.growslice
self.Emit("MOVQ" , jit.Ptr(_SP, 40), _DI) // MOVOU 40(SP), DI
self.Emit("MOVQ" , jit.Ptr(_SP, 48), _DX) // MOVOU 48(SP), DX
self.Emit("MOVQ" , jit.Ptr(_SP, 56), _AX) // MOVQ 56(SP), AX
/* update the slice */
self.Emit("MOVQ", jit.Ptr(_ST, _ST_Sp), _CX) // MOVQ ST.Sp, CX
self.Emit("MOVQ", jit.Sib(_ST, _CX, 8, _ST_Vp), _SI) // MOVQ ST.Vp[CX], SI
self.Emit("MOVQ", jit.Ptr(_SI, 8), _SI) // MOVQ 8(SI), SI
self.Emit("MOVQ", _DX, jit.Ptr(_SI, 8)) // MOVQ DX, 8(SI)
self.Emit("MOVQ", _AX, jit.Ptr(_SI, 16)) // MOVQ AX, 16(AX)
self.WriteRecNotAX(8 , _DI, jit.Ptr(_SI, 0), false) // MOVQ R10, (SI)
self.Sjmp("JMP" , "_array_append") // JMP _array_append
/* copy string */
self.Link("copy_string") // pointer: R8, length: AX, return addr: DI
// self.Byte(0xcc)
self.Emit("MOVQ", _R8, _VAR_cs_p)
self.Emit("MOVQ", _AX, _VAR_cs_n)
self.Emit("MOVQ", _DI, _VAR_cs_LR)
self.Emit("MOVQ", _T_byte, _R8)
self.Emit("MOVQ", _R8, jit.Ptr(_SP, 0))
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 8))
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))
self.call_go(_F_makeslice)
self.Emit("MOVQ", jit.Ptr(_SP, 24), _R8)
self.Emit("MOVQ", _R8, _VAR_cs_d)
self.Emit("MOVQ", _R8, jit.Ptr(_SP, 0))
self.Emit("MOVQ", _VAR_cs_p, _R8)
self.Emit("MOVQ", _R8, jit.Ptr(_SP, 8))
self.Emit("MOVQ", _VAR_cs_n, _AX)
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 16))
self.call_go(_F_memmove)
self.Emit("MOVQ", _VAR_cs_d, _R8)
self.Emit("MOVQ", _VAR_cs_n, _AX)
self.Emit("MOVQ", _VAR_cs_LR, _DI)
// self.Byte(0xcc)
self.Rjmp("JMP", _DI)
/* error handlers */
self.Link("_stack_overflow")
self.Emit("MOVL" , _E_recurse, _EP) // MOVQ _E_recurse, EP
self.Sjmp("JMP" , "_error") // JMP _error
self.Link("_vtype_error") // _vtype_error:
self.Emit("MOVQ" , _DI, _IC) // MOVQ DI, IC
self.Emit("MOVL" , _E_invalid, _EP) // MOVL _E_invalid, EP
self.Sjmp("JMP" , "_error") // JMP _error
self.Link("_invalid_char") // _invalid_char:
self.Emit("SUBQ" , jit.Imm(1), _IC) // SUBQ $1, IC
self.Emit("MOVL" , _E_invalid, _EP) // MOVL _E_invalid, EP
self.Sjmp("JMP" , "_error") // JMP _error
self.Link("_unquote_error") // _unquote_error:
self.Emit("MOVQ" , _VAR_ss_Iv, _IC) // MOVQ ss.Iv, IC
self.Emit("SUBQ" , jit.Imm(1), _IC) // SUBQ $1, IC
self.Link("_parsing_error") // _parsing_error:
self.Emit("NEGQ" , _AX) // NEGQ AX
self.Emit("MOVQ" , _AX, _EP) // MOVQ AX, EP
self.Link("_error") // _error:
self.Emit("PXOR" , _X0, _X0) // PXOR X0, X0
self.Emit("MOVOU", _X0, jit.Ptr(_VP, 0)) // MOVOU X0, (VP)
self.Sjmp("JMP" , "_epilogue") // JMP _epilogue
/* invalid value type, never returns */
self.Link("_invalid_vtype")
self.Emit("MOVQ", _AX, jit.Ptr(_SP, 0)) // MOVQ AX, (SP)
self.call(_F_invalid_vtype) // CALL invalid_type
self.Emit("UD2") // UD2
/* switch jump table */
self.Link("_switch_table") // _switch_table:
self.Sref("_decode_V_EOF", 0) // SREF &_decode_V_EOF, $0
self.Sref("_decode_V_NULL", -4) // SREF &_decode_V_NULL, $-4
self.Sref("_decode_V_TRUE", -8) // SREF &_decode_V_TRUE, $-8
self.Sref("_decode_V_FALSE", -12) // SREF &_decode_V_FALSE, $-12
self.Sref("_decode_V_ARRAY", -16) // SREF &_decode_V_ARRAY, $-16
self.Sref("_decode_V_OBJECT", -20) // SREF &_decode_V_OBJECT, $-20
self.Sref("_decode_V_STRING", -24) // SREF &_decode_V_STRING, $-24
self.Sref("_decode_V_DOUBLE", -28) // SREF &_decode_V_DOUBLE, $-28
self.Sref("_decode_V_INTEGER", -32) // SREF &_decode_V_INTEGER, $-32
self.Sref("_decode_V_KEY_SEP", -36) // SREF &_decode_V_KEY_SEP, $-36
self.Sref("_decode_V_ELEM_SEP", -40) // SREF &_decode_V_ELEM_SEP, $-40
self.Sref("_decode_V_ARRAY_END", -44) // SREF &_decode_V_ARRAY_END, $-44
self.Sref("_decode_V_OBJECT_END", -48) // SREF &_decode_V_OBJECT_END, $-48
/* fast character lookup table */
self.Link("_decode_tab") // _decode_tab:
self.Sref("_decode_V_EOF", 0) // SREF &_decode_V_EOF, $0
/* generate rest of the tabs */
for i := 1; i < 256; i++ {
if to, ok := _R_tab[i]; ok {
self.Sref(to, -int64(i) * 4)
} else {
self.Byte(0x00, 0x00, 0x00, 0x00)
}
}
}
/** Generic Decoder **/
var (
_subr_decode_value = new(_ValueDecoder).build()
)
//go:nosplit
func invalid_vtype(vt types.ValueType) {
throw(fmt.Sprintf("invalid value type: %d", vt))
}
@@ -1,37 +0,0 @@
// +build go1.16,!go1.17
//
// 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.
//
#include "go_asm.h"
#include "funcdata.h"
#include "textflag.h"
TEXT ·decodeValueStub(SB), NOSPLIT, $0 - 72
NO_LOCAL_POINTERS
PXOR X0, X0
MOVOU X0, rv+48(FP)
MOVQ st+0(FP), BX
MOVQ sp+8(FP), R12
MOVQ sn+16(FP), R13
MOVQ ic+24(FP), R14
MOVQ vp+32(FP), R15
MOVQ df+40(FP), R10
MOVQ ·_subr_decode_value(SB), AX
CALL AX
MOVQ R14, rp+48(FP)
MOVQ R11, ex+56(FP)
RET
@@ -14,7 +14,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package decoder
package jitdec
import (
`strconv`
@@ -1,4 +1,4 @@
// +build go1.21,!go1.23
// +build go1.21,!go1.24
// Copyright 2023 CloudWeGo Authors
//
@@ -14,7 +14,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package decoder
package jitdec
import (
`strconv`
@@ -1,4 +1,5 @@
// +build go1.17,!go1.23
//go:build go1.17 && !go1.24
// +build go1.17,!go1.24
/*
* Copyright 2021 ByteDance Inc.
@@ -16,21 +17,22 @@
* limitations under the License.
*/
package decoder
package jitdec
import (
`encoding/json`
`fmt`
`math`
`reflect`
`unsafe`
"encoding/json"
"fmt"
"math"
"reflect"
"strings"
"unsafe"
`github.com/bytedance/sonic/internal/caching`
`github.com/bytedance/sonic/internal/jit`
`github.com/bytedance/sonic/internal/native`
`github.com/bytedance/sonic/internal/native/types`
`github.com/bytedance/sonic/internal/rt`
`github.com/twitchyliquid64/golang-asm/obj`
"github.com/bytedance/sonic/internal/caching"
"github.com/bytedance/sonic/internal/jit"
"github.com/bytedance/sonic/internal/native"
"github.com/bytedance/sonic/internal/native/types"
"github.com/bytedance/sonic/internal/rt"
"github.com/twitchyliquid64/golang-asm/obj"
)
/** Register Allocations
@@ -292,7 +294,6 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
_OP_array_clear_p : (*_Assembler)._asm_OP_array_clear_p,
_OP_slice_init : (*_Assembler)._asm_OP_slice_init,
_OP_slice_append : (*_Assembler)._asm_OP_slice_append,
_OP_object_skip : (*_Assembler)._asm_OP_object_skip,
_OP_object_next : (*_Assembler)._asm_OP_object_next,
_OP_struct_field : (*_Assembler)._asm_OP_struct_field,
_OP_unmarshal : (*_Assembler)._asm_OP_unmarshal,
@@ -312,6 +313,7 @@ var _OpFuncTab = [256]func(*_Assembler, *_Instr) {
_OP_check_char_0 : (*_Assembler)._asm_OP_check_char_0,
_OP_dismatch_err : (*_Assembler)._asm_OP_dismatch_err,
_OP_go_skip : (*_Assembler)._asm_OP_go_skip,
_OP_skip_emtpy : (*_Assembler)._asm_OP_skip_empty,
_OP_add : (*_Assembler)._asm_OP_add,
_OP_check_empty : (*_Assembler)._asm_OP_check_empty,
_OP_debug : (*_Assembler)._asm_OP_debug,
@@ -385,7 +387,7 @@ func (self *_Assembler) prologue() {
var (
_REG_go = []obj.Addr { _ST, _VP, _IP, _IL, _IC }
_REG_rt = []obj.Addr { _ST, _VP, _IP, _IL, _IC, _IL }
_REG_rt = []obj.Addr { _ST, _VP, _IP, _IL, _IC }
)
func (self *_Assembler) save(r ...obj.Addr) {
@@ -492,9 +494,9 @@ func (self *_Assembler) type_error() {
func (self *_Assembler) mismatch_error() {
self.Link(_LB_mismatch_error) // _type_error:
self.Emit("MOVQ", _VAR_et, _ET) // MOVQ _VAR_et, ET
self.Emit("MOVQ", _VAR_ic, _EP) // MOVQ _VAR_ic, EP
self.Emit("MOVQ", _I_json_MismatchTypeError, _CX) // MOVQ _I_json_MismatchType, CX
self.Emit("CMPQ", _ET, _CX) // CMPQ ET, CX
self.Emit("MOVQ", jit.Ptr(_ST, _EpOffset), _EP) // MOVQ stack.Ep, EP
self.Sjmp("JE" , _LB_error) // JE _LB_error
self.Emit("MOVQ", _ARG_sp, _AX)
self.Emit("MOVQ", _ARG_sl, _BX)
@@ -600,6 +602,28 @@ func (self *_Assembler) _asm_OP_go_skip(p *_Instr) {
self.Sjmp("JMP" , _LB_skip_one) // JMP _skip_one
}
var _F_IndexByte = jit.Func(strings.IndexByte)
func (self *_Assembler) _asm_OP_skip_empty(p *_Instr) {
// self.Byte(0xcc)
self.call_sf(_F_skip_one) // CALL_SF skip_one
// self.Byte(0xcc)
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
self.Sjmp("JS" , _LB_parsing_error_v) // JS _parse_error_v
self.Emit("BTQ", jit.Imm(_F_disable_unknown), _ARG_fv)
self.Xjmp("JNC", p.vi())
self.Emit("LEAQ", jit.Sib(_IC, _AX, 1, 0), _BX)
self.Emit("MOVQ", _BX, _ARG_sv_n)
self.Emit("LEAQ", jit.Sib(_IP, _AX, 1, 0), _AX)
self.Emit("MOVQ", _AX, _ARG_sv_p)
self.Emit("MOVQ", jit.Imm(':'), _CX)
self.call_go(_F_IndexByte)
// self.Byte(0xcc)
self.Emit("TESTQ", _AX, _AX)
// disallow unknown field
self.Sjmp("JNS", _LB_field_error)
}
func (self *_Assembler) skip_one() {
self.Link(_LB_skip_one) // _skip:
self.Emit("MOVQ", _VAR_ic, _IC) // MOVQ _VAR_ic, IC
@@ -972,11 +996,13 @@ var (
var (
_F_decodeJsonUnmarshaler obj.Addr
_F_decodeJsonUnmarshalerQuoted obj.Addr
_F_decodeTextUnmarshaler obj.Addr
)
func init() {
_F_decodeJsonUnmarshaler = jit.Func(decodeJsonUnmarshaler)
_F_decodeJsonUnmarshalerQuoted = jit.Func(decodeJsonUnmarshalerQuoted)
_F_decodeTextUnmarshaler = jit.Func(decodeTextUnmarshaler)
}
@@ -1057,18 +1083,18 @@ func (self *_Assembler) mapassign_utext(t reflect.Type, addressable bool) {
var (
_F_skip_one = jit.Imm(int64(native.S_skip_one))
_F_skip_array = jit.Imm(int64(native.S_skip_array))
_F_skip_object = jit.Imm(int64(native.S_skip_object))
_F_skip_number = jit.Imm(int64(native.S_skip_number))
)
func (self *_Assembler) unmarshal_json(t reflect.Type, deref bool) {
func (self *_Assembler) unmarshal_json(t reflect.Type, deref bool, f obj.Addr) {
self.call_sf(_F_skip_one) // CALL_SF skip_one
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
self.Sjmp("JS" , _LB_parsing_error_v) // JS _parse_error_v
self.Emit("MOVQ", _IC, _VAR_ic) // store for mismatche error skip
self.slice_from_r(_AX, 0) // SLICE_R AX, $0
self.Emit("MOVQ" , _DI, _ARG_sv_p) // MOVQ DI, sv.p
self.Emit("MOVQ" , _SI, _ARG_sv_n) // MOVQ SI, sv.n
self.unmarshal_func(t, _F_decodeJsonUnmarshaler, deref) // UNMARSHAL json, ${t}, ${deref}
self.unmarshal_func(t, f, deref) // UNMARSHAL json, ${t}, ${deref}
}
func (self *_Assembler) unmarshal_text(t reflect.Type, deref bool) {
@@ -1103,7 +1129,15 @@ func (self *_Assembler) unmarshal_func(t reflect.Type, fn obj.Addr, deref bool)
self.Emit("MOVQ" , _ARG_sv_n, _DI) // MOVQ sv.n, DI
self.call_go(fn) // CALL_GO ${fn}
self.Emit("TESTQ", _ET, _ET) // TESTQ ET, ET
self.Sjmp("JNZ" , _LB_error) // JNZ _error
self.Sjmp("JZ" , "_unmarshal_func_end_{n}") // JNZ _error
self.Emit("MOVQ", _I_json_MismatchTypeError, _CX) // MOVQ ET, VAR.et
self.Emit("CMPQ", _ET, _CX) // check if MismatchedError
self.Sjmp("JNE" , _LB_error)
self.Emit("MOVQ", jit.Type(t), _CX) // store current type
self.Emit("MOVQ", _CX, _VAR_et) // store current type
self.Emit("MOVQ", _VAR_ic, _IC) // recover the pos
self.Emit("XORL", _ET, _ET)
self.Link("_unmarshal_func_end_{n}")
}
/** Dynamic Decoding Routine **/
@@ -1136,8 +1170,8 @@ func (self *_Assembler) decode_dynamic(vt obj.Addr, vp obj.Addr) {
self.Emit("MOVQ", _I_json_MismatchTypeError, _CX) // MOVQ _I_json_MismatchTypeError, CX
self.Emit("CMPQ", _ET, _CX) // CMPQ ET, CX
self.Sjmp("JNE", _LB_error) // JNE LB_error
self.Emit("MOVQ", _EP, _VAR_ic) // MOVQ EP, VAR_ic
self.Emit("MOVQ", _ET, _VAR_et) // MOVQ ET, VAR_et
self.WriteRecNotAX(14, _EP, jit.Ptr(_ST, _EpOffset), false, false) // MOVQ EP, stack.Ep
self.Link("_decode_dynamic_end_{n}")
}
@@ -1698,12 +1732,6 @@ func (self *_Assembler) _asm_OP_slice_append(p *_Instr) {
self.Link("_append_slice_end_{n}")
}
func (self *_Assembler) _asm_OP_object_skip(_ *_Instr) {
self.call_sf(_F_skip_object) // CALL_SF skip_object
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
self.Sjmp("JS" , _LB_parsing_error_v) // JS _parse_error_v
}
func (self *_Assembler) _asm_OP_object_next(_ *_Instr) {
self.call_sf(_F_skip_one) // CALL_SF skip_one
self.Emit("TESTQ", _AX, _AX) // TESTQ AX, AX
@@ -1774,11 +1802,19 @@ func (self *_Assembler) _asm_OP_struct_field(p *_Instr) {
}
func (self *_Assembler) _asm_OP_unmarshal(p *_Instr) {
self.unmarshal_json(p.vt(), true)
if iv := p.i64(); iv != 0 {
self.unmarshal_json(p.vt(), true, _F_decodeJsonUnmarshalerQuoted)
} else {
self.unmarshal_json(p.vt(), true, _F_decodeJsonUnmarshaler)
}
}
func (self *_Assembler) _asm_OP_unmarshal_p(p *_Instr) {
self.unmarshal_json(p.vt(), false)
if iv := p.i64(); iv != 0 {
self.unmarshal_json(p.vt(), false, _F_decodeJsonUnmarshalerQuoted)
} else {
self.unmarshal_json(p.vt(), false, _F_decodeJsonUnmarshaler)
}
}
func (self *_Assembler) _asm_OP_unmarshal_text(p *_Instr) {
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package decoder
package jitdec
import (
`encoding/json`
@@ -77,7 +77,6 @@ const (
_OP_array_clear_p
_OP_slice_init
_OP_slice_append
_OP_object_skip
_OP_object_next
_OP_struct_field
_OP_unmarshal
@@ -97,6 +96,7 @@ const (
_OP_check_char_0
_OP_dismatch_err
_OP_go_skip
_OP_skip_emtpy
_OP_add
_OP_check_empty
_OP_debug
@@ -155,7 +155,6 @@ var _OpNames = [256]string {
_OP_array_skip : "array_skip",
_OP_slice_init : "slice_init",
_OP_slice_append : "slice_append",
_OP_object_skip : "object_skip",
_OP_object_next : "object_next",
_OP_struct_field : "struct_field",
_OP_unmarshal : "unmarshal",
@@ -271,6 +270,13 @@ func newInsVt(op _Op, vt reflect.Type) _Instr {
}
}
func newInsVtI(op _Op, vt reflect.Type, iv int) _Instr {
return _Instr {
u: packOp(op) | rt.PackInt(iv),
p: unsafe.Pointer(rt.UnpackType(vt)),
}
}
func newInsVf(op _Op, vf *caching.FieldMap) _Instr {
return _Instr {
u: packOp(op),
@@ -452,6 +458,10 @@ func (self *_Program) rtt(op _Op, vt reflect.Type) {
*self = append(*self, newInsVt(op, vt))
}
func (self *_Program) rtti(op _Op, vt reflect.Type, iv int) {
*self = append(*self, newInsVtI(op, vt, iv))
}
func (self *_Program) fmv(op _Op, vf *caching.FieldMap) {
*self = append(*self, newInsVf(op, vf))
}
@@ -527,35 +537,54 @@ func (self *_Compiler) compile(vt reflect.Type) (ret _Program, err error) {
return
}
func (self *_Compiler) checkMarshaler(p *_Program, vt reflect.Type) bool {
const (
checkMarshalerFlags_quoted = 1
)
func (self *_Compiler) checkMarshaler(p *_Program, vt reflect.Type, flags int, exec bool) bool {
pt := reflect.PtrTo(vt)
/* check for `json.Unmarshaler` with pointer receiver */
if pt.Implements(jsonUnmarshalerType) {
p.rtt(_OP_unmarshal_p, pt)
if exec {
p.add(_OP_lspace)
p.rtti(_OP_unmarshal_p, pt, flags)
}
return true
}
/* check for `json.Unmarshaler` */
if vt.Implements(jsonUnmarshalerType) {
p.add(_OP_lspace)
self.compileUnmarshalJson(p, vt)
if exec {
p.add(_OP_lspace)
self.compileUnmarshalJson(p, vt, flags)
}
return true
}
if flags == checkMarshalerFlags_quoted {
// text marshaler shouldn't be supported for quoted string
return false
}
/* check for `encoding.TextMarshaler` with pointer receiver */
if pt.Implements(encodingTextUnmarshalerType) {
p.add(_OP_lspace)
self.compileUnmarshalTextPtr(p, pt)
if exec {
p.add(_OP_lspace)
self.compileUnmarshalTextPtr(p, pt, flags)
}
return true
}
/* check for `encoding.TextUnmarshaler` */
if vt.Implements(encodingTextUnmarshalerType) {
p.add(_OP_lspace)
self.compileUnmarshalText(p, vt)
if exec {
p.add(_OP_lspace)
self.compileUnmarshalText(p, vt, flags)
}
return true
}
return false
}
@@ -567,7 +596,7 @@ func (self *_Compiler) compileOne(p *_Program, sp int, vt reflect.Type) {
return
}
if self.checkMarshaler(p, vt) {
if self.checkMarshaler(p, vt, 0, true) {
return
}
@@ -690,7 +719,7 @@ func (self *_Compiler) compilePtr(p *_Program, sp int, et reflect.Type) {
/* dereference all the way down */
for et.Kind() == reflect.Ptr {
if self.checkMarshaler(p, et) {
if self.checkMarshaler(p, et, 0, true) {
return
}
et = et.Elem()
@@ -872,7 +901,24 @@ func (self *_Compiler) compileStructBody(p *_Program, sp int, vt reflect.Type) {
n := p.pc()
p.add(_OP_is_null)
skip := self.checkIfSkip(p, vt, '{')
j := p.pc()
p.chr(_OP_check_char_0, '{')
p.rtt(_OP_dismatch_err, vt)
/* special case for empty object */
if len(fv) == 0 {
p.pin(j)
s := p.pc()
p.add(_OP_skip_emtpy)
p.pin(s)
p.pin(n)
return
}
skip := p.pc()
p.add(_OP_go_skip)
p.pin(j)
p.int(_OP_add, 1)
p.add(_OP_save)
p.add(_OP_lspace)
@@ -890,11 +936,6 @@ func (self *_Compiler) compileStructBody(p *_Program, sp int, vt reflect.Type) {
p.chr(_OP_check_char, '}')
p.chr(_OP_match_char, ',')
/* special case of an empty struct */
if len(fv) == 0 {
p.add(_OP_object_skip)
goto end_of_object
}
/* match the remaining fields */
p.add(_OP_lspace)
@@ -930,7 +971,6 @@ func (self *_Compiler) compileStructBody(p *_Program, sp int, vt reflect.Type) {
p.int(_OP_goto, y0)
}
end_of_object:
p.pin(x)
p.pin(y1)
p.add(_OP_drop)
@@ -938,7 +978,22 @@ end_of_object:
p.pin(skip)
}
func (self *_Compiler) compileStructFieldStrUnmarshal(p *_Program, vt reflect.Type) {
p.add(_OP_lspace)
n0 := p.pc()
p.add(_OP_is_null)
self.checkMarshaler(p, vt, checkMarshalerFlags_quoted, true)
p.pin(n0)
}
func (self *_Compiler) compileStructFieldStr(p *_Program, sp int, vt reflect.Type) {
// according to std, json.Unmarshaler should be called before stringize
// see https://github.com/bytedance/sonic/issues/670
if self.checkMarshaler(p, vt, checkMarshalerFlags_quoted, false) {
self.compileStructFieldStrUnmarshal(p, vt)
return
}
n1 := -1
ft := vt
sv := false
@@ -1106,7 +1161,7 @@ func (self *_Compiler) compileUnmarshalEnd(p *_Program, vt reflect.Type, i int)
p.pin(j)
}
func (self *_Compiler) compileUnmarshalJson(p *_Program, vt reflect.Type) {
func (self *_Compiler) compileUnmarshalJson(p *_Program, vt reflect.Type, flags int) {
i := p.pc()
v := _OP_unmarshal
p.add(_OP_is_null)
@@ -1117,11 +1172,11 @@ func (self *_Compiler) compileUnmarshalJson(p *_Program, vt reflect.Type) {
}
/* call the unmarshaler */
p.rtt(v, vt)
p.rtti(v, vt, flags)
self.compileUnmarshalEnd(p, vt, i)
}
func (self *_Compiler) compileUnmarshalText(p *_Program, vt reflect.Type) {
func (self *_Compiler) compileUnmarshalText(p *_Program, vt reflect.Type, iv int) {
i := p.pc()
v := _OP_unmarshal_text
p.add(_OP_is_null)
@@ -1134,15 +1189,15 @@ func (self *_Compiler) compileUnmarshalText(p *_Program, vt reflect.Type) {
}
/* call the unmarshaler */
p.rtt(v, vt)
p.rtti(v, vt, iv)
self.compileUnmarshalEnd(p, vt, i)
}
func (self *_Compiler) compileUnmarshalTextPtr(p *_Program, vt reflect.Type) {
func (self *_Compiler) compileUnmarshalTextPtr(p *_Program, vt reflect.Type, iv int) {
i := p.pc()
p.add(_OP_is_null)
p.chr(_OP_match_char, '"')
p.rtt(_OP_unmarshal_text_p, vt)
p.rtti(_OP_unmarshal_text_p, vt, iv)
p.pin(i)
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package decoder
package jitdec
import (
`os`
+140
View File
@@ -0,0 +1,140 @@
package jitdec
import (
`unsafe`
`encoding/json`
`reflect`
`runtime`
`github.com/bytedance/sonic/internal/decoder/consts`
`github.com/bytedance/sonic/internal/decoder/errors`
`github.com/bytedance/sonic/internal/rt`
`github.com/bytedance/sonic/utf8`
`github.com/bytedance/sonic/option`
)
type (
MismatchTypeError = errors.MismatchTypeError
SyntaxError = errors.SyntaxError
)
const (
_F_allow_control = consts.F_allow_control
_F_copy_string = consts.F_copy_string
_F_disable_unknown = consts.F_disable_unknown
_F_disable_urc = consts.F_disable_urc
_F_use_int64 = consts.F_use_int64
_F_use_number = consts.F_use_number
_F_no_validate_json = consts.F_no_validate_json
_F_validate_string = consts.F_validate_string
)
var (
error_wrap = errors.ErrorWrap
error_type = errors.ErrorType
error_field = errors.ErrorField
error_value = errors.ErrorValue
error_mismatch = errors.ErrorMismatch
stackOverflow = errors.StackOverflow
)
// Decode parses the JSON-encoded data from current position and stores the result
// in the value pointed to by val.
func Decode(s *string, i *int, f uint64, val interface{}) error {
/* validate json if needed */
if (f & (1 << _F_validate_string)) != 0 && !utf8.ValidateString(*s){
dbuf := utf8.CorrectWith(nil, rt.Str2Mem(*s), "\ufffd")
*s = rt.Mem2Str(dbuf)
}
vv := rt.UnpackEface(val)
vp := vv.Value
/* check for nil type */
if vv.Type == nil {
return &json.InvalidUnmarshalError{}
}
/* must be a non-nil pointer */
if vp == nil || vv.Type.Kind() != reflect.Ptr {
return &json.InvalidUnmarshalError{Type: vv.Type.Pack()}
}
etp := rt.PtrElem(vv.Type)
/* check the defined pointer type for issue 379 */
if vv.Type.IsNamed() {
newp := vp
etp = vv.Type
vp = unsafe.Pointer(&newp)
}
/* create a new stack, and call the decoder */
sb := newStack()
nb, err := decodeTypedPointer(*s, *i, etp, vp, sb, f)
/* return the stack back */
*i = nb
freeStack(sb)
/* avoid GC ahead */
runtime.KeepAlive(vv)
return err
}
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
// order to reduce the first-hit latency.
//
// Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
// a compile option to set the depth of recursive compile for the nested struct type.
func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
cfg := option.DefaultCompileOptions()
for _, opt := range opts {
opt(&cfg)
}
return pretouchRec(map[reflect.Type]bool{vt:true}, cfg)
}
func pretouchType(_vt reflect.Type, opts option.CompileOptions) (map[reflect.Type]bool, error) {
/* compile function */
compiler := newCompiler().apply(opts)
decoder := func(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
if pp, err := compiler.compile(_vt); err != nil {
return nil, err
} else {
as := newAssembler(pp)
as.name = _vt.String()
return as.Load(), nil
}
}
/* find or compile */
vt := rt.UnpackType(_vt)
if val := programCache.Get(vt); val != nil {
return nil, nil
} else if _, err := programCache.Compute(vt, decoder); err == nil {
return compiler.rec, nil
} else {
return nil, err
}
}
func pretouchRec(vtm map[reflect.Type]bool, opts option.CompileOptions) error {
if opts.RecursiveDepth < 0 || len(vtm) == 0 {
return nil
}
next := make(map[reflect.Type]bool)
for vt := range(vtm) {
sub, err := pretouchType(vt, opts)
if err != nil {
return err
}
for svt := range(sub) {
next[svt] = true
}
}
opts.RecursiveDepth -= 1
return pretouchRec(next, opts)
}
@@ -1,4 +1,4 @@
// +build go1.17,!go1.23
// +build go1.17,!go1.24
/*
* Copyright 2021 ByteDance Inc.
@@ -16,7 +16,7 @@
* limitations under the License.
*/
package decoder
package jitdec
import (
`encoding/json`
@@ -1,4 +1,4 @@
// +build go1.17,!go1.23
// +build go1.17,!go1.24
//
// Copyright 2021 ByteDance Inc.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package decoder
package jitdec
import (
`sync`
@@ -36,6 +36,7 @@ const (
_PtrBytes = _PTR_SIZE / 8
_FsmOffset = (_MaxStack + 1) * _PtrBytes
_DbufOffset = _FsmOffset + int64(unsafe.Sizeof(types.StateMachine{})) + types.MAX_RECURSE * _PtrBytes
_EpOffset = _DbufOffset + _MaxDigitNums
_StackSize = unsafe.Sizeof(_Stack{})
)
@@ -53,6 +54,7 @@ type _Stack struct {
mm types.StateMachine
vp [types.MAX_RECURSE]unsafe.Pointer
dp [_MaxDigitNums]byte
ep unsafe.Pointer
}
type _Decoder func(
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package decoder
package jitdec
import (
`encoding`
@@ -39,6 +39,13 @@ func decodeJsonUnmarshaler(vv interface{}, s string) error {
return vv.(json.Unmarshaler).UnmarshalJSON(rt.Str2Mem(s))
}
func decodeJsonUnmarshalerQuoted(vv interface{}, s string) error {
if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
return &MismatchTypeError{}
}
return vv.(json.Unmarshaler).UnmarshalJSON(rt.Str2Mem(s[1:len(s)-1]))
}
func decodeTextUnmarshaler(vv interface{}, s string) error {
return vv.(encoding.TextUnmarshaler).UnmarshalText(rt.Str2Mem(s))
}
@@ -1,4 +1,4 @@
// +build go1.16,!go1.20
// +build go1.17,!go1.20
/*
* Copyright 2021 ByteDance Inc.
@@ -16,7 +16,7 @@
* limitations under the License.
*/
package decoder
package jitdec
import (
`unsafe`
@@ -16,7 +16,7 @@
* limitations under the License.
*/
package decoder
package jitdec
import (
`unsafe`
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package decoder
package jitdec
import (
`encoding`
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package decoder
package jitdec
import (
`unsafe`
@@ -0,0 +1,174 @@
package optdec
import (
"fmt"
"reflect"
caching "github.com/bytedance/sonic/internal/optcaching"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/internal/resolver"
)
const (
_MAX_FIELDS = 50 // cutoff at 50 fields struct
)
func (c *compiler) compileIntStringOption(vt reflect.Type) decFunc {
switch vt.Size() {
case 4:
switch vt.Kind() {
case reflect.Uint:
fallthrough
case reflect.Uintptr:
return &u32StringDecoder{}
case reflect.Int:
return &i32StringDecoder{}
}
case 8:
switch vt.Kind() {
case reflect.Uint:
fallthrough
case reflect.Uintptr:
return &u64StringDecoder{}
case reflect.Int:
return &i64StringDecoder{}
}
default:
panic("not supported pointer size: " + fmt.Sprint(vt.Size()))
}
panic("unreachable")
}
func isInteger(vt reflect.Type) bool {
switch vt.Kind() {
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr, reflect.Int: return true
default: return false
}
}
func (c *compiler) assertStringOptTypes(vt reflect.Type) {
if c.depth > _CompileMaxDepth {
panic(*stackOverflow)
}
c.depth += 1
defer func () {
c.depth -= 1
}()
if isInteger(vt) {
return
}
switch vt.Kind() {
case reflect.String, reflect.Bool, reflect.Float32, reflect.Float64:
return
case reflect.Ptr: c.assertStringOptTypes(vt.Elem())
default:
panicForInvalidStrType(vt)
}
}
func (c *compiler) compileFieldStringOption(vt reflect.Type) decFunc {
c.assertStringOptTypes(vt)
unmDec := c.tryCompilePtrUnmarshaler(vt, true)
if unmDec != nil {
return unmDec
}
switch vt.Kind() {
case reflect.String:
if vt == jsonNumberType {
return &numberStringDecoder{}
}
return &strStringDecoder{}
case reflect.Bool:
return &boolStringDecoder{}
case reflect.Int8:
return &i8StringDecoder{}
case reflect.Int16:
return &i16StringDecoder{}
case reflect.Int32:
return &i32StringDecoder{}
case reflect.Int64:
return &i64StringDecoder{}
case reflect.Uint8:
return &u8StringDecoder{}
case reflect.Uint16:
return &u16StringDecoder{}
case reflect.Uint32:
return &u32StringDecoder{}
case reflect.Uint64:
return &u64StringDecoder{}
case reflect.Float32:
return &f32StringDecoder{}
case reflect.Float64:
return &f64StringDecoder{}
case reflect.Uint:
fallthrough
case reflect.Uintptr:
fallthrough
case reflect.Int:
return c.compileIntStringOption(vt)
case reflect.Ptr:
return &ptrStrDecoder{
typ: rt.UnpackType(vt.Elem()),
deref: c.compileFieldStringOption(vt.Elem()),
}
default:
panicForInvalidStrType(vt)
return nil
}
}
func (c *compiler) compileStruct(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
if c.namedPtr {
c.namedPtr = false
return c.compileStructBody(vt)
}
if c.depth >= c.opts.MaxInlineDepth + 1 || (c.counts > 0 && vt.NumField() >= _MAX_FIELDS) {
return &recuriveDecoder{
typ: rt.UnpackType(vt),
}
} else {
return c.compileStructBody(vt)
}
}
func (c *compiler) compileStructBody(vt reflect.Type) decFunc {
fv := resolver.ResolveStruct(vt)
entries := make([]fieldEntry, 0, len(fv))
for _, f := range fv {
var dec decFunc
/* dealt with field tag options */
if f.Opts&resolver.F_stringize != 0 {
dec = c.compileFieldStringOption(f.Type)
} else {
dec = c.compile(f.Type)
}
/* deal with embedded pointer fields */
if f.Path[0].Kind == resolver.F_deref {
dec = &embeddedFieldPtrDecoder{
field: f,
fieldDec: dec,
fieldName: f.Name,
}
}
entries = append(entries, fieldEntry{
FieldMeta: f,
fieldDec: dec,
})
}
return &structDecoder{
fieldMap: caching.NewFieldLookup(fv),
fields: entries,
structName: vt.Name(),
typ: vt,
}
}
+449
View File
@@ -0,0 +1,449 @@
package optdec
import (
"encoding/json"
"fmt"
"reflect"
"github.com/bytedance/sonic/option"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/internal/caching"
)
var (
programCache = caching.CreateProgramCache()
)
func findOrCompile(vt *rt.GoType) (decFunc, error) {
makeDecoder := func(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
ret, err := newCompiler().compileType(vt.Pack())
return ret, err
}
if val := programCache.Get(vt); val != nil {
return val.(decFunc), nil
} else if ret, err := programCache.Compute(vt, makeDecoder); err == nil {
return ret.(decFunc), nil
} else {
return nil, err
}
}
type compiler struct {
visited map[reflect.Type]bool
depth int
counts int
opts option.CompileOptions
namedPtr bool
}
func newCompiler() *compiler {
return &compiler{
visited: make(map[reflect.Type]bool),
opts: option.DefaultCompileOptions(),
}
}
func (self *compiler) apply(opts option.CompileOptions) *compiler {
self.opts = opts
return self
}
const _CompileMaxDepth = 4096
func (c *compiler) enter(vt reflect.Type) {
c.visited[vt] = true
c.depth += 1
if c.depth > _CompileMaxDepth {
panic(*stackOverflow)
}
}
func (c *compiler) exit(vt reflect.Type) {
c.visited[vt] = false
c.depth -= 1
}
func (c *compiler) compileInt(vt reflect.Type) decFunc {
switch vt.Size() {
case 4:
switch vt.Kind() {
case reflect.Uint:
fallthrough
case reflect.Uintptr:
return &u32Decoder{}
case reflect.Int:
return &i32Decoder{}
}
case 8:
switch vt.Kind() {
case reflect.Uint:
fallthrough
case reflect.Uintptr:
return &u64Decoder{}
case reflect.Int:
return &i64Decoder{}
}
default:
panic("not supported pointer size: " + fmt.Sprint(vt.Size()))
}
panic("unreachable")
}
func (c *compiler) rescue(ep *error) {
if val := recover(); val != nil {
if err, ok := val.(error); ok {
*ep = err
} else {
panic(val)
}
}
}
func (c *compiler) compileType(vt reflect.Type) (rt decFunc, err error) {
defer c.rescue(&err)
rt = c.compile(vt)
return rt, err
}
func (c *compiler) compile(vt reflect.Type) decFunc {
if c.visited[vt] {
return &recuriveDecoder{
typ: rt.UnpackType(vt),
}
}
dec := c.tryCompilePtrUnmarshaler(vt, false)
if dec != nil {
return dec
}
return c.compileBasic(vt)
}
func (c *compiler) compileBasic(vt reflect.Type) decFunc {
defer func() {
c.counts += 1
}()
switch vt.Kind() {
case reflect.Bool:
return &boolDecoder{}
case reflect.Int8:
return &i8Decoder{}
case reflect.Int16:
return &i16Decoder{}
case reflect.Int32:
return &i32Decoder{}
case reflect.Int64:
return &i64Decoder{}
case reflect.Uint8:
return &u8Decoder{}
case reflect.Uint16:
return &u16Decoder{}
case reflect.Uint32:
return &u32Decoder{}
case reflect.Uint64:
return &u64Decoder{}
case reflect.Float32:
return &f32Decoder{}
case reflect.Float64:
return &f64Decoder{}
case reflect.Uint:
fallthrough
case reflect.Uintptr:
fallthrough
case reflect.Int:
return c.compileInt(vt)
case reflect.String:
return c.compileString(vt)
case reflect.Array:
return c.compileArray(vt)
case reflect.Interface:
return c.compileInterface(vt)
case reflect.Map:
return c.compileMap(vt)
case reflect.Ptr:
return c.compilePtr(vt)
case reflect.Slice:
return c.compileSlice(vt)
case reflect.Struct:
return c.compileStruct(vt)
default:
panic(&json.UnmarshalTypeError{Type: vt})
}
}
func (c *compiler) compilePtr(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
// specail logic for Named Ptr, issue 379
if reflect.PtrTo(vt.Elem()) != vt {
c.namedPtr = true
return &ptrDecoder{
typ: rt.UnpackType(vt.Elem()),
deref: c.compileBasic(vt.Elem()),
}
}
return &ptrDecoder{
typ: rt.UnpackType(vt.Elem()),
deref: c.compile(vt.Elem()),
}
}
func (c *compiler) compileArray(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
return &arrayDecoder{
len: vt.Len(),
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
func (c *compiler) compileString(vt reflect.Type) decFunc {
if vt == jsonNumberType {
return &numberDecoder{}
}
return &stringDecoder{}
}
func (c *compiler) tryCompileSliceUnmarshaler(vt reflect.Type) decFunc {
pt := reflect.PtrTo(vt.Elem())
if pt.Implements(jsonUnmarshalerType) {
return &sliceDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
if pt.Implements(encodingTextUnmarshalerType) {
return &sliceDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
return nil
}
func (c *compiler) compileSlice(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
// Some common slice, use a decoder, to avoid function calls
et := rt.UnpackType(vt.Elem())
/* first checking `[]byte` */
if et.Kind() == reflect.Uint8 /* []byte */ {
return c.compileSliceBytes(vt)
}
dec := c.tryCompileSliceUnmarshaler(vt)
if dec != nil {
return dec
}
if vt == reflect.TypeOf([]interface{}{}) {
return &sliceEfaceDecoder{}
}
if et.IsInt32() {
return &sliceI32Decoder{}
}
if et.IsInt64() {
return &sliceI64Decoder{}
}
if et.IsUint32() {
return &sliceU32Decoder{}
}
if et.IsUint64() {
return &sliceU64Decoder{}
}
if et.Kind() == reflect.String {
return &sliceStringDecoder{}
}
return &sliceDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
func (c *compiler) compileSliceBytes(vt reflect.Type) decFunc {
ep := reflect.PtrTo(vt.Elem())
if ep.Implements(jsonUnmarshalerType) {
return &sliceBytesUnmarshalerDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
if ep.Implements(encodingTextUnmarshalerType) {
return &sliceBytesUnmarshalerDecoder{
elemType: rt.UnpackType(vt.Elem()),
elemDec: c.compile(vt.Elem()),
typ: vt,
}
}
return &sliceBytesDecoder{}
}
func (c *compiler) compileInterface(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
if vt.NumMethod() == 0 {
return &efaceDecoder{}
}
if vt.Implements(jsonUnmarshalerType) {
return &unmarshalJSONDecoder{
typ: rt.UnpackType(vt),
}
}
if vt.Implements(encodingTextUnmarshalerType) {
return &unmarshalTextDecoder{
typ: rt.UnpackType(vt),
}
}
return &ifaceDecoder{
typ: rt.UnpackType(vt),
}
}
func (c *compiler) compileMap(vt reflect.Type) decFunc {
c.enter(vt)
defer c.exit(vt)
// check the key unmarshaler at first
decKey := tryCompileKeyUnmarshaler(vt)
if decKey != nil {
return &mapDecoder{
mapType: rt.MapType(rt.UnpackType(vt)),
keyDec: decKey,
elemDec: c.compile(vt.Elem()),
}
}
// Most common map, use a decoder, to avoid function calls
if vt == reflect.TypeOf(map[string]interface{}{}) {
return &mapEfaceDecoder{}
} else if vt == reflect.TypeOf(map[string]string{}) {
return &mapStringDecoder{}
}
// Some common integer map later
mt := rt.MapType(rt.UnpackType(vt))
if mt.Key.Kind() == reflect.String {
return &mapStrKeyDecoder{
mapType: mt,
assign: rt.GetMapStrAssign(vt),
elemDec: c.compile(vt.Elem()),
}
}
if mt.Key.IsInt64() {
return &mapI64KeyDecoder{
mapType: mt,
elemDec: c.compile(vt.Elem()),
assign: rt.GetMap64Assign(vt),
}
}
if mt.Key.IsInt32() {
return &mapI32KeyDecoder{
mapType: mt,
elemDec: c.compile(vt.Elem()),
assign: rt.GetMap32Assign(vt),
}
}
if mt.Key.IsUint64() {
return &mapU64KeyDecoder{
mapType: mt,
elemDec: c.compile(vt.Elem()),
assign: rt.GetMap64Assign(vt),
}
}
if mt.Key.IsUint32() {
return &mapU32KeyDecoder{
mapType: mt,
elemDec: c.compile(vt.Elem()),
assign: rt.GetMap32Assign(vt),
}
}
// Generic map
return &mapDecoder{
mapType: mt,
keyDec: c.compileMapKey(vt),
elemDec: c.compile(vt.Elem()),
}
}
func tryCompileKeyUnmarshaler(vt reflect.Type) decKey {
pt := reflect.PtrTo(vt.Key())
/* check for `encoding.TextUnmarshaler` with pointer receiver */
if pt.Implements(encodingTextUnmarshalerType) {
return decodeKeyTextUnmarshaler
}
/* not support map key with `json.Unmarshaler` */
return nil
}
func (c *compiler) compileMapKey(vt reflect.Type) decKey {
switch vt.Key().Kind() {
case reflect.Int8:
return decodeKeyI8
case reflect.Int16:
return decodeKeyI16
case reflect.Uint8:
return decodeKeyU8
case reflect.Uint16:
return decodeKeyU16
default:
panic(&json.UnmarshalTypeError{Type: vt})
}
}
// maybe vt is a named type, and not a pointer receiver, see issue 379
func (c *compiler) tryCompilePtrUnmarshaler(vt reflect.Type, strOpt bool) decFunc {
pt := reflect.PtrTo(vt)
/* check for `json.Unmarshaler` with pointer receiver */
if pt.Implements(jsonUnmarshalerType) {
return &unmarshalJSONDecoder{
typ: rt.UnpackType(pt),
strOpt: strOpt,
}
}
/* check for `encoding.TextMarshaler` with pointer receiver */
if pt.Implements(encodingTextUnmarshalerType) {
/* TextUnmarshal not support ,strig tag */
if strOpt {
panicForInvalidStrType(vt)
}
return &unmarshalTextDecoder{
typ: rt.UnpackType(pt),
}
}
return nil
}
func panicForInvalidStrType(vt reflect.Type) {
panic(error_type(rt.UnpackType(vt)))
}
+60
View File
@@ -0,0 +1,60 @@
package optdec
import "math"
/*
Copied from sonic-rs
// JSON Value Type
const NULL: u64 = 0;
const BOOL: u64 = 2;
const FALSE: u64 = BOOL;
const TRUE: u64 = (1 << 3) | BOOL;
const NUMBER: u64 = 3;
const UINT: u64 = NUMBER;
const SINT: u64 = (1 << 3) | NUMBER;
const REAL: u64 = (2 << 3) | NUMBER;
const RAWNUMBER: u64 = (3 << 3) | NUMBER;
const STRING: u64 = 4;
const STRING_COMMON: u64 = STRING;
const STRING_HASESCAPED: u64 = (1 << 3) | STRING;
const OBJECT: u64 = 6;
const ARRAY: u64 = 7;
/// JSON Type Mask
const POS_MASK: u64 = (!0) << 32;
const POS_BITS: u64 = 32;
const TYPE_MASK: u64 = 0xFF;
const TYPE_BITS: u64 = 8;
*/
const (
// BasicType: 3 bits
KNull = 0 // xxxxx000
KBool = 2 // xxxxx010
KNumber = 3 // xxxxx011
KString = 4 // xxxxx100
KRaw = 5 // xxxxx101
KObject = 6 // xxxxx110
KArray = 7 // xxxxx111
// SubType: 2 bits
KFalse = (0 << 3) | KBool // xxx00_010, 2
KTrue = (1 << 3) | KBool // xxx01_010, 10
KUint = (0 << 3) | KNumber // xxx00_011, 3
KSint = (1 << 3) | KNumber // xxx01_011, 11
KReal = (2 << 3) | KNumber // xxx10_011, 19
KRawNumber = (3 << 3) | KNumber // xxx11_011, 27
KStringCommon = KString // xxx00_100, 4
KStringEscaped = (1 << 3) | KString // xxx01_100, 12
)
const (
PosMask = math.MaxUint64 << 32
PosBits = 32
TypeMask = 0xFF
TypeBits = 8
ConLenMask = uint64(math.MaxUint32)
ConLenBits = 32
)
+3
View File
@@ -0,0 +1,3 @@
package optdec
type context = Context
+160
View File
@@ -0,0 +1,160 @@
package optdec
import (
"reflect"
"unsafe"
"encoding/json"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/option"
"github.com/bytedance/sonic/internal/decoder/errors"
"github.com/bytedance/sonic/internal/decoder/consts"
)
type (
MismatchTypeError = errors.MismatchTypeError
SyntaxError = errors.SyntaxError
)
const (
_F_allow_control = consts.F_allow_control
_F_copy_string = consts.F_copy_string
_F_disable_unknown = consts.F_disable_unknown
_F_disable_urc = consts.F_disable_urc
_F_use_int64 = consts.F_use_int64
_F_use_number = consts.F_use_number
_F_validate_string = consts.F_validate_string
)
type Options = consts.Options
const (
OptionUseInt64 = consts.OptionUseInt64
OptionUseNumber = consts.OptionUseNumber
OptionUseUnicodeErrors = consts.OptionUseUnicodeErrors
OptionDisableUnknown = consts.OptionDisableUnknown
OptionCopyString = consts.OptionCopyString
OptionValidateString = consts.OptionValidateString
)
func Decode(s *string, i *int, f uint64, val interface{}) error {
vv := rt.UnpackEface(val)
vp := vv.Value
/* check for nil type */
if vv.Type == nil {
return &json.InvalidUnmarshalError{}
}
/* must be a non-nil pointer */
if vp == nil || vv.Type.Kind() != reflect.Ptr {
return &json.InvalidUnmarshalError{Type: vv.Type.Pack()}
}
etp := rt.PtrElem(vv.Type)
/* check the defined pointer type for issue 379 */
if vv.Type.IsNamed() {
newp := vp
etp = vv.Type
vp = unsafe.Pointer(&newp)
}
dec, err := findOrCompile(etp)
if err != nil {
return err
}
/* parse into document */
ctx, err := NewContext(*s, *i, uint64(f), etp)
defer ctx.Delete()
if ctx.Parser.Utf8Inv {
*s = ctx.Parser.Json
}
if err != nil {
goto fix_error;
}
err = dec.FromDom(vp, ctx.Root(), &ctx)
fix_error:
err = fix_error(*s, *i, err)
// update position at last
*i += ctx.Parser.Pos()
return err
}
func fix_error(json string, pos int, err error) error {
if e, ok := err.(SyntaxError); ok {
return SyntaxError{
Pos: int(e.Pos) + pos,
Src: json,
Msg: e.Msg,
}
}
if e, ok := err.(MismatchTypeError); ok {
return &MismatchTypeError {
Pos: int(e.Pos) + pos,
Src: json,
Type: e.Type,
}
}
return err
}
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
// order to reduce the first-hit latency.
//
// Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is
// a compile option to set the depth of recursive compile for the nested struct type.
func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
cfg := option.DefaultCompileOptions()
for _, opt := range opts {
opt(&cfg)
}
return pretouchRec(map[reflect.Type]bool{vt:true}, cfg)
}
func pretouchType(_vt reflect.Type, opts option.CompileOptions) (map[reflect.Type]bool, error) {
/* compile function */
compiler := newCompiler().apply(opts)
decoder := func(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
if f, err := compiler.compileType(_vt); err != nil {
return nil, err
} else {
return f, nil
}
}
/* find or compile */
vt := rt.UnpackType(_vt)
if val := programCache.Get(vt); val != nil {
return nil, nil
} else if _, err := programCache.Compute(vt, decoder); err == nil {
return compiler.visited, nil
} else {
return nil, err
}
}
func pretouchRec(vtm map[reflect.Type]bool, opts option.CompileOptions) error {
if opts.RecursiveDepth < 0 || len(vtm) == 0 {
return nil
}
next := make(map[reflect.Type]bool)
for vt := range(vtm) {
sub, err := pretouchType(vt, opts)
if err != nil {
return err
}
for svt := range(sub) {
next[svt] = true
}
}
opts.RecursiveDepth -= 1
return pretouchRec(next, opts)
}
+73
View File
@@ -0,0 +1,73 @@
/*
* 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 optdec
import (
"encoding/json"
"errors"
"reflect"
"strconv"
"github.com/bytedance/sonic/internal/rt"
)
/** JIT Error Helpers **/
var stackOverflow = &json.UnsupportedValueError{
Str: "Value nesting too deep",
Value: reflect.ValueOf("..."),
}
func error_type(vt *rt.GoType) error {
return &json.UnmarshalTypeError{Type: vt.Pack()}
}
func error_mismatch(node Node, ctx *context, typ reflect.Type) error {
return MismatchTypeError{
Pos: node.Position(),
Src: ctx.Parser.Json,
Type: typ,
}
}
func newUnmatched(pos int, vt *rt.GoType) error {
return MismatchTypeError{
Pos: pos,
Src: "",
Type: vt.Pack(),
}
}
func error_field(name string) error {
return errors.New("json: unknown field " + strconv.Quote(name))
}
func error_value(value string, vtype reflect.Type) error {
return &json.UnmarshalTypeError{
Type: vtype,
Value: value,
}
}
func error_syntax(pos int, src string, msg string) error {
return SyntaxError{
Pos: pos,
Src: src,
Msg: msg,
}
}
+281
View File
@@ -0,0 +1,281 @@
package optdec
import (
"encoding/json"
"math"
"unsafe"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/internal/resolver"
)
type decFunc interface {
FromDom(vp unsafe.Pointer, node Node, ctx *context) error
}
type ptrDecoder struct {
typ *rt.GoType
deref decFunc
}
// Pointer Value is allocated in the Caller
func (d *ptrDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*unsafe.Pointer)(vp) = nil
return nil
}
if *(*unsafe.Pointer)(vp) == nil {
*(*unsafe.Pointer)(vp) = rt.Mallocgc(d.typ.Size, d.typ, true)
}
return d.deref.FromDom(*(*unsafe.Pointer)(vp), node, ctx)
}
type embeddedFieldPtrDecoder struct {
field resolver.FieldMeta
fieldDec decFunc
fieldName string
}
// Pointer Value is allocated in the Caller
func (d *embeddedFieldPtrDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
// seek into the pointer
vp = unsafe.Pointer(uintptr(vp) - uintptr(d.field.Path[0].Size))
for _, f := range d.field.Path {
deref := rt.UnpackType(f.Type)
vp = unsafe.Pointer(uintptr(vp) + f.Size)
if f.Kind == resolver.F_deref {
if *(*unsafe.Pointer)(vp) == nil {
*(*unsafe.Pointer)(vp) = rt.Mallocgc(deref.Size, deref, true)
}
vp = *(*unsafe.Pointer)(vp)
}
}
return d.fieldDec.FromDom(vp, node, ctx)
}
type i8Decoder struct{}
func (d *i8Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsI64(ctx)
if !ok || ret > math.MaxInt8 || ret < math.MinInt8 {
return error_mismatch(node, ctx, int8Type)
}
*(*int8)(vp) = int8(ret)
return nil
}
type i16Decoder struct{}
func (d *i16Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsI64(ctx)
if !ok || ret > math.MaxInt16 || ret < math.MinInt16 {
return error_mismatch(node, ctx, int16Type)
}
*(*int16)(vp) = int16(ret)
return nil
}
type i32Decoder struct{}
func (d *i32Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsI64(ctx)
if !ok || ret > math.MaxInt32 || ret < math.MinInt32 {
return error_mismatch(node, ctx, int32Type)
}
*(*int32)(vp) = int32(ret)
return nil
}
type i64Decoder struct{}
func (d *i64Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsI64(ctx)
if !ok {
return error_mismatch(node, ctx, int64Type)
}
*(*int64)(vp) = int64(ret)
return nil
}
type u8Decoder struct{}
func (d *u8Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsU64(ctx)
if !ok || ret > math.MaxUint8 {
err := error_mismatch(node, ctx, uint8Type)
return err
}
*(*uint8)(vp) = uint8(ret)
return nil
}
type u16Decoder struct{}
func (d *u16Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsU64(ctx)
if !ok || ret > math.MaxUint16 {
return error_mismatch(node, ctx, uint16Type)
}
*(*uint16)(vp) = uint16(ret)
return nil
}
type u32Decoder struct{}
func (d *u32Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsU64(ctx)
if !ok || ret > math.MaxUint32 {
return error_mismatch(node, ctx, uint32Type)
}
*(*uint32)(vp) = uint32(ret)
return nil
}
type u64Decoder struct{}
func (d *u64Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsU64(ctx)
if !ok {
return error_mismatch(node, ctx, uint64Type)
}
*(*uint64)(vp) = uint64(ret)
return nil
}
type f32Decoder struct{}
func (d *f32Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsF64(ctx)
if !ok || ret > math.MaxFloat32 || ret < -math.MaxFloat32 {
return error_mismatch(node, ctx, float32Type)
}
*(*float32)(vp) = float32(ret)
return nil
}
type f64Decoder struct{}
func (d *f64Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsF64(ctx)
if !ok {
return error_mismatch(node, ctx, float64Type)
}
*(*float64)(vp) = float64(ret)
return nil
}
type boolDecoder struct {
}
func (d *boolDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsBool()
if !ok {
return error_mismatch(node, ctx, boolType)
}
*(*bool)(vp) = bool(ret)
return nil
}
type stringDecoder struct {
}
func (d *stringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
ret, ok := node.AsStr(ctx)
if !ok {
return error_mismatch(node, ctx, stringType)
}
*(*string)(vp) = ret
return nil
}
type numberDecoder struct {
}
func (d *numberDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
num, ok := node.AsNumber(ctx)
if !ok {
return error_mismatch(node, ctx, jsonNumberType)
}
*(*json.Number)(vp) = num
return nil
}
type recuriveDecoder struct {
typ *rt.GoType
}
func (d *recuriveDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
dec, err := findOrCompile(d.typ)
if err != nil {
return err
}
return dec.FromDom(vp, node, ctx)
}
+101
View File
@@ -0,0 +1,101 @@
package optdec
import (
"encoding/json"
"strconv"
"github.com/bytedance/sonic/internal/native"
"github.com/bytedance/sonic/internal/native/types"
)
func SkipNumberFast(json string, start int) (int, error) {
// find the number ending, we pasred in sonic-cpp, it alway valid
pos := start
for pos < len(json) && json[pos] != ']' && json[pos] != '}' && json[pos] != ',' {
if json[pos] >= '0' && json[pos] <= '9' || json[pos] == '.' || json[pos] == '-' || json[pos] == '+' || json[pos] == 'e' || json[pos] == 'E' {
pos += 1
} else {
return pos, error_syntax(pos, json, "invalid number")
}
}
return pos, nil
}
func ValidNumberFast(json string) error {
// find the number ending, we pasred in sonic-cpp, it alway valid
pos := 0
for pos < len(json) && json[pos] != ']' && json[pos] != '}' && json[pos] != ',' {
if json[pos] >= '0' && json[pos] <= '9' || json[pos] == '.' || json[pos] == '-' || json[pos] == '+' || json[pos] == 'e' || json[pos] == 'E' {
pos += 1
} else {
return error_syntax(pos, json, "invalid number")
}
}
if pos == 0 {
return error_syntax(pos, json, "invalid number")
}
return nil
}
func SkipOneFast2(json string, pos *int) (int, error) {
// find the number ending, we pasred in sonic-cpp, it alway valid
start := native.SkipOneFast(&json, pos)
if start < 0 {
return -1, error_syntax(*pos, json, types.ParsingError(-start).Error())
}
return start, nil
}
func SkipOneFast(json string, pos int) (string, error) {
// find the number ending, we pasred in sonic-cpp, it alway valid
start := native.SkipOneFast(&json, &pos)
if start < 0 {
// TODO: details error code
return "", error_syntax(pos, json, types.ParsingError(-start).Error())
}
return json[start:pos], nil
}
func ParseI64(raw string) (int64, error) {
i64, err := strconv.ParseInt(raw, 10, 64)
if err != nil {
return 0, err
}
return i64, nil
}
func ParseBool(raw string) (bool, error) {
var b bool
err := json.Unmarshal([]byte(raw), &b)
if err != nil {
return false, err
}
return b, nil
}
func ParseU64(raw string) (uint64, error) {
u64, err := strconv.ParseUint(raw, 10, 64)
if err != nil {
return 0, err
}
return u64, nil
}
func ParseF64(raw string) (float64, error) {
f64, err := strconv.ParseFloat(raw, 64)
if err != nil {
return 0, err
}
return f64, nil
}
func Unquote(raw string) (string, error) {
var u string
err := json.Unmarshal([]byte(raw), &u)
if err != nil {
return "", err
}
return u, nil
}
+169
View File
@@ -0,0 +1,169 @@
package optdec
import (
"encoding"
"encoding/json"
"unsafe"
"reflect"
"github.com/bytedance/sonic/internal/rt"
)
type efaceDecoder struct {
}
func (d *efaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*interface{})(vp) = interface{}(nil)
return nil
}
eface := *(*rt.GoEface)(vp)
// not pointer type, or nil pointer, or *interface{}
if eface.Value == nil || eface.Type.Kind() != reflect.Ptr || rt.PtrElem(eface.Type) == anyType {
ret, err := node.AsEface(ctx)
if err != nil {
return err
}
*(*interface{})(vp) = ret
return nil
}
etp := rt.PtrElem(eface.Type)
vp = eface.Value
/* check the defined pointer type for issue 379 */
if eface.Type.IsNamed() {
newp := vp
etp = eface.Type
vp = unsafe.Pointer(&newp)
}
dec, err := findOrCompile(etp)
if err != nil {
return err
}
return dec.FromDom(vp, node, ctx)
}
type ifaceDecoder struct {
typ *rt.GoType
}
func (d *ifaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*unsafe.Pointer)(vp) = nil
return nil
}
iface := *(*rt.GoIface)(vp)
if iface.Itab == nil {
return error_type(d.typ)
}
vt := iface.Itab.Vt
// not pointer type, or nil pointer, or *interface{}
if vp == nil || vt.Kind() != reflect.Ptr || rt.PtrElem(vt) == anyType {
ret, err := node.AsEface(ctx)
if err != nil {
return err
}
*(*interface{})(vp) = ret
return nil
}
etp := rt.PtrElem(vt)
vp = iface.Value
/* check the defined pointer type for issue 379 */
if vt.IsNamed() {
newp := vp
etp = vt
vp = unsafe.Pointer(&newp)
}
dec, err := findOrCompile(etp)
if err != nil {
return err
}
return dec.FromDom(vp, node, ctx)
}
type unmarshalTextDecoder struct {
typ *rt.GoType
}
func (d *unmarshalTextDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*unsafe.Pointer)(vp) = nil
return nil
}
txt, ok := node.AsStringText(ctx)
if !ok {
return error_mismatch(node, ctx, d.typ.Pack())
}
v := *(*interface{})(unsafe.Pointer(&rt.GoEface{
Type: d.typ,
Value: vp,
}))
// fast path
if u, ok := v.(encoding.TextUnmarshaler); ok {
return u.UnmarshalText(txt)
}
// slow path
rv := reflect.ValueOf(v)
if u, ok := rv.Interface().(encoding.TextUnmarshaler); ok {
return u.UnmarshalText(txt)
}
return error_type(d.typ)
}
type unmarshalJSONDecoder struct {
typ *rt.GoType
strOpt bool
}
func (d *unmarshalJSONDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
v := *(*interface{})(unsafe.Pointer(&rt.GoEface{
Type: d.typ,
Value: vp,
}))
var input []byte
if d.strOpt && node.IsNull() {
input = []byte("null")
} else if d.strOpt {
s, ok := node.AsStringText(ctx)
if !ok {
return error_mismatch(node, ctx, d.typ.Pack())
}
input = s
} else {
input = []byte(node.AsRaw(ctx))
}
// fast path
if u, ok := v.(json.Unmarshaler); ok {
return u.UnmarshalJSON((input))
}
// slow path
rv := reflect.ValueOf(v)
if u, ok := rv.Interface().(json.Unmarshaler); ok {
return u.UnmarshalJSON(input)
}
return error_type(d.typ)
}
+430
View File
@@ -0,0 +1,430 @@
package optdec
import (
"encoding"
"encoding/json"
"math"
"reflect"
"unsafe"
"github.com/bytedance/sonic/internal/rt"
)
/** Decoder for most common map types: map[string]interface{}, map[string]string **/
type mapEfaceDecoder struct {
}
func (d *mapEfaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*map[string]interface{})(vp) = nil
return nil
}
return node.AsMapEface(ctx, vp)
}
type mapStringDecoder struct {
}
func (d *mapStringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*map[string]string)(vp) = nil
return nil
}
return node.AsMapString(ctx, vp)
}
/** Decoder for map with string key **/
type mapStrKeyDecoder struct {
mapType *rt.GoMapType
elemDec decFunc
assign rt.MapStrAssign
typ reflect.Type
}
func (d *mapStrKeyDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*unsafe.Pointer)(vp) = nil
return nil
}
obj, ok := node.AsObj()
if !ok {
return error_mismatch(node, ctx, d.mapType.Pack())
}
// allocate map
m := *(*unsafe.Pointer)(vp)
if m == nil {
m = rt.Makemap(&d.mapType.GoType, obj.Len())
}
var gerr error
next := obj.Children()
for i := 0; i < obj.Len(); i++ {
keyn := NewNode(next)
key, _ := keyn.AsStr(ctx)
valn := NewNode(PtrOffset(next, 1))
valp := d.assign(d.mapType, m, key)
err := d.elemDec.FromDom(valp, valn, ctx)
if gerr == nil && err != nil {
gerr = err
}
next = valn.Next()
}
*(*unsafe.Pointer)(vp) = m
return gerr
}
/** Decoder for map with int32 or int64 key **/
type mapI32KeyDecoder struct {
mapType *rt.GoMapType
elemDec decFunc
assign rt.Map32Assign
}
func (d *mapI32KeyDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*unsafe.Pointer)(vp) = nil
return nil
}
obj, ok := node.AsObj()
if !ok {
return error_mismatch(node, ctx, d.mapType.Pack())
}
// allocate map
m := *(*unsafe.Pointer)(vp)
if m == nil {
m = rt.Makemap(&d.mapType.GoType, obj.Len())
}
next := obj.Children()
var gerr error
for i := 0; i < obj.Len(); i++ {
keyn := NewNode(next)
k, ok := keyn.ParseI64(ctx)
if !ok || k > math.MaxInt32 || k < math.MinInt32 {
if gerr == nil {
gerr = error_mismatch(keyn, ctx, d.mapType.Pack())
}
valn := NewNode(PtrOffset(next, 1))
next = valn.Next()
continue
}
key := int32(k)
ku32 := *(*uint32)(unsafe.Pointer(&key))
valn := NewNode(PtrOffset(next, 1))
valp := d.assign(d.mapType, m, ku32)
err := d.elemDec.FromDom(valp, valn, ctx)
if gerr == nil && err != nil {
gerr = err
}
next = valn.Next()
}
*(*unsafe.Pointer)(vp) = m
return gerr
}
type mapI64KeyDecoder struct {
mapType *rt.GoMapType
elemDec decFunc
assign rt.Map64Assign
}
func (d *mapI64KeyDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*unsafe.Pointer)(vp) = nil
return nil
}
obj, ok := node.AsObj()
if !ok {
return error_mismatch(node, ctx, d.mapType.Pack())
}
// allocate map
m := *(*unsafe.Pointer)(vp)
if m == nil {
m = rt.Makemap(&d.mapType.GoType, obj.Len())
}
var gerr error
next := obj.Children()
for i := 0; i < obj.Len(); i++ {
keyn := NewNode(next)
key, ok := keyn.ParseI64(ctx)
if !ok {
if gerr == nil {
gerr = error_mismatch(keyn, ctx, d.mapType.Pack())
}
valn := NewNode(PtrOffset(next, 1))
next = valn.Next()
continue
}
ku64 := *(*uint64)(unsafe.Pointer(&key))
valn := NewNode(PtrOffset(next, 1))
valp := d.assign(d.mapType, m, ku64)
err := d.elemDec.FromDom(valp, valn, ctx)
if gerr == nil && err != nil {
gerr = err
}
next = valn.Next()
}
*(*unsafe.Pointer)(vp) = m
return gerr
}
/** Decoder for map with unt32 or uint64 key **/
type mapU32KeyDecoder struct {
mapType *rt.GoMapType
elemDec decFunc
assign rt.Map32Assign
}
func (d *mapU32KeyDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*unsafe.Pointer)(vp) = nil
return nil
}
obj, ok := node.AsObj()
if !ok {
return error_mismatch(node, ctx, d.mapType.Pack())
}
// allocate map
m := *(*unsafe.Pointer)(vp)
if m == nil {
m = rt.Makemap(&d.mapType.GoType, obj.Len())
}
var gerr error
next := obj.Children()
for i := 0; i < obj.Len(); i++ {
keyn := NewNode(next)
k, ok := keyn.ParseU64(ctx)
if !ok || k > math.MaxUint32 {
if gerr == nil {
gerr = error_mismatch(keyn, ctx, d.mapType.Pack())
}
valn := NewNode(PtrOffset(next, 1))
next = valn.Next()
continue
}
key := uint32(k)
valn := NewNode(PtrOffset(next, 1))
valp := d.assign(d.mapType, m, key)
err := d.elemDec.FromDom(valp, valn, ctx)
if gerr == nil && err != nil {
gerr = err
}
next = valn.Next()
}
*(*unsafe.Pointer)(vp) = m
return gerr
}
type mapU64KeyDecoder struct {
mapType *rt.GoMapType
elemDec decFunc
assign rt.Map64Assign
}
func (d *mapU64KeyDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*unsafe.Pointer)(vp) = nil
return nil
}
obj, ok := node.AsObj()
if !ok {
return error_mismatch(node, ctx, d.mapType.Pack())
}
// allocate map
m := *(*unsafe.Pointer)(vp)
if m == nil {
m = rt.Makemap(&d.mapType.GoType, obj.Len())
}
var gerr error
next := obj.Children()
for i := 0; i < obj.Len(); i++ {
keyn := NewNode(next)
key, ok := keyn.ParseU64(ctx)
if !ok {
if gerr == nil {
gerr = error_mismatch(keyn, ctx, d.mapType.Pack())
}
valn := NewNode(PtrOffset(next, 1))
next = valn.Next()
continue
}
valn := NewNode(PtrOffset(next, 1))
valp := d.assign(d.mapType, m, key)
err := d.elemDec.FromDom(valp, valn, ctx)
if gerr == nil && err != nil {
gerr = err
}
next = valn.Next()
}
*(*unsafe.Pointer)(vp) = m
return gerr
}
/** Decoder for generic cases */
type decKey func(dec *mapDecoder, raw string, ctx *context) (interface{}, error)
func decodeKeyU8(dec *mapDecoder, raw string, ctx *context) (interface{}, error) {
key, err := Unquote(raw)
if err != nil {
return nil, err
}
ret, err := ParseU64(key)
if err != nil {
return nil, err
}
if ret > math.MaxUint8 {
return nil, error_value(key, dec.mapType.Key.Pack())
}
return uint8(ret), nil
}
func decodeKeyU16(dec *mapDecoder, raw string, ctx *context) (interface{}, error) {
key, err := Unquote(raw)
if err != nil {
return nil, err
}
ret, err := ParseU64(key)
if err != nil {
return nil, err
}
if ret > math.MaxUint16 {
return nil, error_value(key, dec.mapType.Key.Pack())
}
return uint16(ret), nil
}
func decodeKeyI8(dec *mapDecoder, raw string, ctx *context) (interface{}, error) {
key, err := Unquote(raw)
if err != nil {
return nil, err
}
ret, err := ParseI64(key)
if err != nil {
return nil, err
}
if ret > math.MaxInt8 || ret < math.MinInt8 {
return nil, error_value(key, dec.mapType.Key.Pack())
}
return int8(ret), nil
}
func decodeKeyI16(dec *mapDecoder, raw string, ctx *context) (interface{}, error) {
key, err := Unquote(raw)
if err != nil {
return nil, err
}
ret, err := ParseI64(key)
if err != nil {
return nil, err
}
if ret > math.MaxInt16 || ret < math.MinInt16 {
return nil, error_value(key, dec.mapType.Key.Pack())
}
return int16(ret), nil
}
func decodeKeyJSONUnmarshaler(dec *mapDecoder, raw string, _ *context) (interface{}, error) {
ret := reflect.New(dec.mapType.Key.Pack()).Interface()
err := ret.(json.Unmarshaler).UnmarshalJSON([]byte(raw))
if err != nil {
return nil, err
}
return ret, nil
}
func decodeKeyTextUnmarshaler(dec *mapDecoder, raw string, ctx *context) (interface{}, error) {
key, err := Unquote(raw)
if err != nil {
return nil, err
}
ret := reflect.New(dec.mapType.Key.Pack()).Interface()
err = ret.(encoding.TextUnmarshaler).UnmarshalText([]byte(key))
if err != nil {
return nil, err
}
return ret, nil
}
type mapDecoder struct {
mapType *rt.GoMapType
keyDec decKey
elemDec decFunc
}
func (d *mapDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*unsafe.Pointer)(vp) = nil
return nil
}
obj, ok := node.AsObj()
if !ok {
return error_mismatch(node, ctx, d.mapType.Pack())
}
// allocate map
m := *(*unsafe.Pointer)(vp)
if m == nil {
m = rt.Makemap(&d.mapType.GoType, obj.Len())
}
next := obj.Children()
var gerr error
for i := 0; i < obj.Len(); i++ {
keyn := NewNode(next)
raw := keyn.AsRaw(ctx)
key, err := d.keyDec(d, raw, ctx)
if err != nil {
if gerr == nil {
gerr = error_mismatch(keyn, ctx, d.mapType.Pack())
}
valn := NewNode(PtrOffset(next, 1))
next = valn.Next()
continue
}
valn := NewNode(PtrOffset(next, 1))
keyp := rt.UnpackEface(key).Value
valp := rt.Mapassign(d.mapType, m, keyp)
err = d.elemDec.FromDom(valp, valn, ctx)
if gerr == nil && err != nil {
gerr = err
}
next = valn.Next()
}
*(*unsafe.Pointer)(vp) = m
return gerr
}
+269
View File
@@ -0,0 +1,269 @@
package optdec
import (
"fmt"
"reflect"
"unsafe"
"sync"
"github.com/bytedance/sonic/internal/native"
"github.com/bytedance/sonic/internal/native/types"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/utf8"
)
type ErrorCode int
const (
SONIC_OK = 0;
SONIC_CONTROL_CHAR = 1;
SONIC_INVALID_ESCAPED = 2;
SONIC_INVALID_NUM = 3;
SONIC_FLOAT_INF = 4;
SONIC_EOF = 5;
SONIC_INVALID_CHAR = 6;
SONIC_EXPECT_KEY = 7;
SONIC_EXPECT_COLON = 8;
SONIC_EXPECT_OBJ_COMMA_OR_END = 9;
SONIC_EXPECT_ARR_COMMA_OR_END = 10;
SONIC_VISIT_FAILED = 11;
SONIC_INVALID_ESCAPED_UTF = 12;
SONIC_INVALID_LITERAL = 13;
SONIC_STACK_OVERFLOW = 14;
)
var ParsingErrors = []string{
SONIC_OK : "ok",
SONIC_CONTROL_CHAR : "control chars in string",
SONIC_INVALID_ESCAPED : "invalid escaped chars in string",
SONIC_INVALID_NUM : "invalid number",
SONIC_FLOAT_INF : "float infinity",
SONIC_EOF : "eof",
SONIC_INVALID_CHAR : "invalid chars",
SONIC_EXPECT_KEY : "expect a json key",
SONIC_EXPECT_COLON : "expect a `:`",
SONIC_EXPECT_OBJ_COMMA_OR_END : "expect a `,` or `}`",
SONIC_EXPECT_ARR_COMMA_OR_END : "expect a `,` or `]`",
SONIC_VISIT_FAILED : "failed in json visitor",
SONIC_INVALID_ESCAPED_UTF : "invalid escaped unicodes",
SONIC_INVALID_LITERAL : "invalid literal(true/false/null)",
SONIC_STACK_OVERFLOW : "json is exceeded max depth 4096, cause stack overflow",
}
func (code ErrorCode) Error() string {
return ParsingErrors[code]
}
type node struct {
typ uint64
val uint64
}
// should consitent with native/parser.c
type _nospaceBlock struct {
_ [8]byte
_ [8]byte
}
// should consitent with native/parser.c
type nodeBuf struct {
ncur uintptr
parent int64
depth uint64
nstart uintptr
nend uintptr
stat jsonStat
}
func (self *nodeBuf) init(nodes []node) {
self.ncur = uintptr(unsafe.Pointer(&nodes[0]))
self.nstart = self.ncur
self.nend = self.ncur + uintptr(cap(nodes)) * unsafe.Sizeof(node{})
self.parent = -1
}
// should consitent with native/parser.c
type Parser struct {
Json string
padded []byte
nodes []node
dbuf []byte
backup []node
options uint64
// JSON cursor
start uintptr
cur uintptr
end uintptr
_nbk _nospaceBlock
// node buffer cursor
nbuf nodeBuf
Utf8Inv bool
isEface bool
}
// only when parse non-empty object/array are needed.
type jsonStat struct {
object uint32
array uint32
str uint32
number uint32
array_elems uint32
object_keys uint32
max_depth uint32
}
var (
defaultJsonPaddedCap uintptr = 1 << 20 // 1 Mb
defaultNodesCap uintptr = (1 << 20) / unsafe.Sizeof(node{}) // 1 Mb
)
var parsePool sync.Pool = sync.Pool {
New: func () interface{} {
return &Parser{
options: 0,
padded: make([]byte, 0, defaultJsonPaddedCap),
nodes: make([]node, defaultNodesCap, defaultNodesCap),
dbuf: make([]byte, types.MaxDigitNums, types.MaxDigitNums),
}
},
}
var padding string = "x\"x\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
func newParser(data string, pos int, opt uint64) *Parser {
p := parsePool.Get().(*Parser)
/* validate json if needed */
if (opt & (1 << _F_validate_string)) != 0 && !utf8.ValidateString(data){
dbuf := utf8.CorrectWith(nil, rt.Str2Mem(data[pos:]), "\ufffd")
dbuf = append(dbuf, padding...)
p.Json = rt.Mem2Str(dbuf[:len(dbuf) - len(padding)])
p.Utf8Inv = true
p.start = uintptr((*rt.GoString)(unsafe.Pointer(&p.Json)).Ptr)
} else {
p.Json = data
// TODO: prevent too large JSON
p.padded = append(p.padded, data[pos:]...)
p.padded = append(p.padded, padding...)
p.start = uintptr((*rt.GoSlice)(unsafe.Pointer(&p.padded)).Ptr)
}
p.cur = p.start
p.end = p.cur + uintptr(len(p.Json))
p.options = opt
p.nbuf.init(p.nodes)
return p
}
func (p *Parser) Pos() int {
return int(p.cur - p.start)
}
func (p *Parser) JsonBytes() []byte {
if p.Utf8Inv {
return (rt.Str2Mem(p.Json))
} else {
return p.padded
}
}
var nodeType = rt.UnpackType(reflect.TypeOf(node{}))
//go:inline
func calMaxNodeCap(jsonSize int) int {
return jsonSize / 2 + 2
}
func (p *Parser) parse() ErrorCode {
// when decode into struct, we should decode number as possible
old := p.options
if !p.isEface {
p.options &^= 1 << _F_use_number
}
// fast path with limited node buffer
err := ErrorCode(native.ParseWithPadding(unsafe.Pointer(p)))
if err != SONIC_VISIT_FAILED {
p.options = old
return err
}
// check OoB here
offset := p.nbuf.ncur - p.nbuf.nstart
curLen := offset / unsafe.Sizeof(node{})
if curLen != uintptr(len(p.nodes)) {
panic(fmt.Sprintf("current len: %d, real len: %d cap: %d", curLen, len(p.nodes), cap(p.nodes)))
}
// node buf is not enough, continue parse
// the maxCap is always meet all valid JSON
maxCap := calMaxNodeCap(len(p.Json))
slice := rt.GoSlice{
Ptr: rt.Mallocgc(uintptr(maxCap) * nodeType.Size, nodeType, false),
Len: maxCap,
Cap: maxCap,
}
rt.Memmove(unsafe.Pointer(slice.Ptr), unsafe.Pointer(&p.nodes[0]), offset)
p.backup = p.nodes
p.nodes = *(*[]node)(unsafe.Pointer(&slice))
// update node cursor
p.nbuf.nstart = uintptr(unsafe.Pointer(&p.nodes[0]))
p.nbuf.nend = p.nbuf.nstart + uintptr(cap(p.nodes)) * unsafe.Sizeof(node{})
p.nbuf.ncur = p.nbuf.nstart + offset
// continue parse json
err = ErrorCode(native.ParseWithPadding(unsafe.Pointer(p)))
p.options = old
return err
}
func (p *Parser) reset() {
p.options = 0
p.padded = p.padded[:0]
// nodes is too large here, we will not reset it and use small backup nodes buffer
if p.backup != nil {
p.nodes = p.backup
p.backup = nil
}
p.start = 0
p.cur = 0
p.end = 0
p.Json = ""
p.nbuf = nodeBuf{}
p._nbk = _nospaceBlock{}
p.Utf8Inv = false
p.isEface = false
}
func (p *Parser) free() {
p.reset()
parsePool.Put(p)
}
//go:noinline
func (p *Parser) fixError(code ErrorCode) error {
if code == SONIC_OK {
return nil
}
if p.Pos() == 0 {
code = SONIC_EOF;
}
pos := p.Pos() - 1
return error_syntax(pos, p.Json, ParsingErrors[code])
}
func Parse(data string, opt uint64) error {
p := newParser(data, 0, opt)
err := p.parse()
p.free()
return err
}
File diff suppressed because it is too large. Load diff
+224
View File
@@ -0,0 +1,224 @@
package optdec
import (
"reflect"
"unsafe"
"github.com/bytedance/sonic/internal/rt"
)
type sliceDecoder struct {
elemType *rt.GoType
elemDec decFunc
typ reflect.Type
}
var (
emptyPtr = &struct{}{}
)
func (d *sliceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*rt.GoSlice)(vp) = rt.GoSlice{}
return nil
}
arr, ok := node.AsArr()
if !ok {
return error_mismatch(node, ctx, d.typ)
}
slice := rt.MakeSlice(vp, d.elemType, arr.Len())
elems := slice.Ptr
next := arr.Children()
var gerr error
for i := 0; i < arr.Len(); i++ {
val := NewNode(next)
elem := unsafe.Pointer(uintptr(elems) + uintptr(i)*d.elemType.Size)
err := d.elemDec.FromDom(elem, val, ctx)
if gerr == nil && err != nil {
gerr = err
}
next = val.Next()
}
*(*rt.GoSlice)(vp) = *slice
return gerr
}
type arrayDecoder struct {
len int
elemType *rt.GoType
elemDec decFunc
typ reflect.Type
}
//go:nocheckptr
func (d *arrayDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
arr, ok := node.AsArr()
if !ok {
return error_mismatch(node, ctx, d.typ)
}
next := arr.Children()
i := 0
var gerr error
for ; i < d.len && i < arr.Len(); i++ {
elem := unsafe.Pointer(uintptr(vp) + uintptr(i)*d.elemType.Size)
val := NewNode(next)
err := d.elemDec.FromDom(elem, val, ctx)
if gerr == nil && err != nil {
gerr = err
}
next = val.Next()
}
/* zero rest of array */
ptr := unsafe.Pointer(uintptr(vp) + uintptr(i)*d.elemType.Size)
n := uintptr(d.len-i) * d.elemType.Size
rt.ClearMemory(d.elemType, ptr, n)
return gerr
}
type sliceEfaceDecoder struct {
}
func (d *sliceEfaceDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*rt.GoSlice)(vp) = rt.GoSlice{}
return nil
}
return node.AsSliceEface(ctx, vp)
}
type sliceI32Decoder struct {
}
func (d *sliceI32Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*rt.GoSlice)(vp) = rt.GoSlice{}
return nil
}
return node.AsSliceI32(ctx, vp)
}
type sliceI64Decoder struct {
}
func (d *sliceI64Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*rt.GoSlice)(vp) = rt.GoSlice{}
return nil
}
return node.AsSliceI64(ctx, vp)
}
type sliceU32Decoder struct {
}
func (d *sliceU32Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*rt.GoSlice)(vp) = rt.GoSlice{}
return nil
}
return node.AsSliceU32(ctx, vp)
}
type sliceU64Decoder struct {
}
func (d *sliceU64Decoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*rt.GoSlice)(vp) = rt.GoSlice{}
return nil
}
return node.AsSliceU64(ctx, vp)
}
type sliceStringDecoder struct {
}
func (d *sliceStringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*rt.GoSlice)(vp) = rt.GoSlice{}
return nil
}
return node.AsSliceString(ctx, vp)
}
type sliceBytesDecoder struct {
}
func (d *sliceBytesDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*rt.GoSlice)(vp) = rt.GoSlice{}
return nil
}
s, err := node.AsSliceBytes(ctx)
if err != nil {
return err
}
*(*[]byte)(vp) = s
return nil
}
type sliceBytesUnmarshalerDecoder struct {
elemType *rt.GoType
elemDec decFunc
typ reflect.Type
}
func (d *sliceBytesUnmarshalerDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*rt.GoSlice)(vp) = rt.GoSlice{}
return nil
}
/* parse JSON string into `[]byte` */
if node.IsStr() {
slice, err := node.AsSliceBytes(ctx)
if err != nil {
return err
}
*(*[]byte)(vp) = slice
return nil
}
/* parse JSON array into `[]byte` */
arr, ok := node.AsArr()
if !ok {
return error_mismatch(node, ctx, d.typ)
}
slice := rt.MakeSlice(vp, d.elemType, arr.Len())
elems := slice.Ptr
var gerr error
next := arr.Children()
for i := 0; i < arr.Len(); i++ {
child := NewNode(next)
elem := unsafe.Pointer(uintptr(elems) + uintptr(i)*d.elemType.Size)
err := d.elemDec.FromDom(elem, child, ctx)
if gerr == nil && err != nil {
gerr = err
}
next = child.Next()
}
*(*rt.GoSlice)(vp) = *slice
return gerr
}
+360
View File
@@ -0,0 +1,360 @@
package optdec
import (
"encoding/json"
"math"
"unsafe"
"github.com/bytedance/sonic/internal/rt"
)
type ptrStrDecoder struct {
typ *rt.GoType
deref decFunc
}
// Pointer Value is allocated in the Caller
func (d *ptrStrDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
*(*unsafe.Pointer)(vp) = nil
return nil
}
s, ok := node.AsStrRef(ctx)
if !ok {
return error_mismatch(node, ctx, stringType)
}
if s == "null" {
*(*unsafe.Pointer)(vp) = nil
return nil
}
if *(*unsafe.Pointer)(vp) == nil {
*(*unsafe.Pointer)(vp) = rt.Mallocgc(d.typ.Size, d.typ, true)
}
return d.deref.FromDom(*(*unsafe.Pointer)(vp), node, ctx)
}
type boolStringDecoder struct {
}
func (d *boolStringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
s, ok := node.AsStrRef(ctx)
if !ok {
return error_mismatch(node, ctx, stringType)
}
if s == "null" {
return nil
}
b, err := ParseBool(s)
if err != nil {
return error_mismatch(node, ctx, boolType)
}
*(*bool)(vp) = b
return nil
}
func parseI64(node Node, ctx *context) (int64, error, bool) {
if node.IsNull() {
return 0, nil, true
}
s, ok := node.AsStrRef(ctx)
if !ok {
return 0, error_mismatch(node, ctx, stringType), false
}
if s == "null" {
return 0, nil, true
}
ret, err := ParseI64(s)
return ret, err, false
}
type i8StringDecoder struct{}
func (d *i8StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
ret, err, null := parseI64(node, ctx)
if null {
return nil
}
if err != nil {
return err
}
if ret > math.MaxInt8 || ret < math.MinInt8 {
return error_mismatch(node, ctx, int8Type)
}
*(*int8)(vp) = int8(ret)
return nil
}
type i16StringDecoder struct{}
func (d *i16StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
ret, err, null := parseI64(node, ctx)
if null {
return nil
}
if err != nil {
return err
}
if ret > math.MaxInt16 || ret < math.MinInt16 {
return error_mismatch(node, ctx, int16Type)
}
*(*int16)(vp) = int16(ret)
return nil
}
type i32StringDecoder struct{}
func (d *i32StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
ret, err, null := parseI64(node, ctx)
if null {
return nil
}
if err != nil {
return err
}
if ret > math.MaxInt32 || ret < math.MinInt32 {
return error_mismatch(node, ctx, int32Type)
}
*(*int32)(vp) = int32(ret)
return nil
}
type i64StringDecoder struct{}
func (d *i64StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
ret, err, null := parseI64(node, ctx)
if null {
return nil
}
if err != nil {
return err
}
*(*int64)(vp) = int64(ret)
return nil
}
func parseU64(node Node, ctx *context) (uint64, error, bool) {
if node.IsNull() {
return 0, nil, true
}
s, ok := node.AsStrRef(ctx)
if !ok {
return 0, error_mismatch(node, ctx, stringType), false
}
if s == "null" {
return 0, nil, true
}
ret, err := ParseU64(s)
return ret, err, false
}
type u8StringDecoder struct{}
func (d *u8StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
ret, err, null := parseU64(node, ctx)
if null {
return nil
}
if err != nil {
return err
}
if ret > math.MaxUint8 {
return error_mismatch(node, ctx, uint8Type)
}
*(*uint8)(vp) = uint8(ret)
return nil
}
type u16StringDecoder struct{}
func (d *u16StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
ret, err, null := parseU64(node, ctx)
if null {
return nil
}
if err != nil {
return err
}
if ret > math.MaxUint16 {
return error_mismatch(node, ctx, uint16Type)
}
*(*uint16)(vp) = uint16(ret)
return nil
}
type u32StringDecoder struct{}
func (d *u32StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
ret, err, null := parseU64(node, ctx)
if null {
return nil
}
if err != nil {
return err
}
if ret > math.MaxUint32 {
return error_mismatch(node, ctx, uint32Type)
}
*(*uint32)(vp) = uint32(ret)
return nil
}
type u64StringDecoder struct{}
func (d *u64StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
ret, err, null := parseU64(node, ctx)
if null {
return nil
}
if err != nil {
return err
}
*(*uint64)(vp) = uint64(ret)
return nil
}
type f32StringDecoder struct{}
func (d *f32StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
s, ok := node.AsStrRef(ctx)
if !ok {
return error_mismatch(node, ctx, stringType)
}
if s == "null" {
return nil
}
ret, err := ParseF64(s)
if err != nil || ret > math.MaxFloat32 || ret < -math.MaxFloat32 {
return error_mismatch(node, ctx, float32Type)
}
*(*float32)(vp) = float32(ret)
return nil
}
type f64StringDecoder struct{}
func (d *f64StringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
s, ok := node.AsStrRef(ctx)
if !ok {
return error_mismatch(node, ctx, stringType)
}
if s == "null" {
return nil
}
ret, err := ParseF64(s)
if err != nil {
return error_mismatch(node, ctx, float64Type)
}
*(*float64)(vp) = float64(ret)
return nil
}
/* parse string field with string options */
type strStringDecoder struct{}
func (d *strStringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
s, ok := node.AsStrRef(ctx)
if !ok {
return error_mismatch(node, ctx, stringType)
}
if s == "null" {
return nil
}
s, err := Unquote(s)
if err != nil {
return error_mismatch(node, ctx, stringType)
}
*(*string)(vp) = s
return nil
}
type numberStringDecoder struct{}
func (d *numberStringDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
s, ok := node.AsStrRef(ctx)
if !ok {
return error_mismatch(node, ctx, stringType)
}
if s == "null" {
return nil
}
num, ok := node.ParseNumber(ctx)
if !ok {
return error_mismatch(node, ctx, jsonNumberType)
}
end, err := SkipNumberFast(s, 0)
// has error or trailing chars
if err != nil || end != len(s) {
return error_mismatch(node, ctx, jsonNumberType)
}
*(*json.Number)(vp) = json.Number(num)
return nil
}
+61
View File
@@ -0,0 +1,61 @@
package optdec
import (
"reflect"
"unsafe"
caching "github.com/bytedance/sonic/internal/optcaching"
"github.com/bytedance/sonic/internal/resolver"
)
type fieldEntry struct {
resolver.FieldMeta
fieldDec decFunc
}
type structDecoder struct {
fieldMap caching.FieldLookup
fields []fieldEntry
structName string
typ reflect.Type
}
func (d *structDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
if node.IsNull() {
return nil
}
var gerr error
obj, ok := node.AsObj()
if !ok {
return error_mismatch(node, ctx, d.typ)
}
next := obj.Children()
for i := 0; i < obj.Len(); i++ {
key, _ := NewNode(next).AsStrRef(ctx)
val := NewNode(PtrOffset(next, 1))
next = val.Next()
// find field idx
idx := d.fieldMap.Get(key)
if idx == -1 {
if Options(ctx.Options())&OptionDisableUnknown != 0 {
return error_field(key)
}
continue
}
offset := d.fields[idx].Path[0].Size
elem := unsafe.Pointer(uintptr(vp) + offset)
err := d.fields[idx].fieldDec.FromDom(elem, val, ctx)
// deal with mismatch type errors
if gerr == nil && err != nil {
// TODO: better error info
gerr = err
}
}
return gerr
}
+60
View File
@@ -0,0 +1,60 @@
/*
* 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 optdec
import (
"encoding"
"encoding/base64"
"encoding/json"
"reflect"
"unsafe"
"github.com/bytedance/sonic/internal/rt"
)
var (
boolType = reflect.TypeOf(bool(false))
byteType = reflect.TypeOf(byte(0))
intType = reflect.TypeOf(int(0))
int8Type = reflect.TypeOf(int8(0))
int16Type = reflect.TypeOf(int16(0))
int32Type = reflect.TypeOf(int32(0))
int64Type = reflect.TypeOf(int64(0))
uintType = reflect.TypeOf(uint(0))
uint8Type = reflect.TypeOf(uint8(0))
uint16Type = reflect.TypeOf(uint16(0))
uint32Type = reflect.TypeOf(uint32(0))
uint64Type = reflect.TypeOf(uint64(0))
float32Type = reflect.TypeOf(float32(0))
float64Type = reflect.TypeOf(float64(0))
stringType = reflect.TypeOf("")
bytesType = reflect.TypeOf([]byte(nil))
jsonNumberType = reflect.TypeOf(json.Number(""))
base64CorruptInputError = reflect.TypeOf(base64.CorruptInputError(0))
anyType = rt.UnpackType(reflect.TypeOf((*interface{})(nil)).Elem())
)
var (
errorType = reflect.TypeOf((*error)(nil)).Elem()
jsonUnmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
encodingTextUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
)
func rtype(t reflect.Type) (*rt.GoItab, *rt.GoType) {
p := (*rt.GoIface)(unsafe.Pointer(&t))
return p.Itab, (*rt.GoType)(p.Value)
}
@@ -14,15 +14,16 @@
* limitations under the License.
*/
package encoder
package alg
import (
"encoding"
"reflect"
"strconv"
"sync"
"unsafe"
"github.com/bytedance/sonic/internal/native"
"github.com/bytedance/sonic/internal/encoder/vars"
"github.com/bytedance/sonic/internal/rt"
)
@@ -32,8 +33,8 @@ type _MapPair struct {
m [32]byte
}
type _MapIterator struct {
it rt.GoMapIterator // must be the first field
type MapIterator struct {
It rt.GoMapIterator // must be the first field
kv rt.GoSlice // slice of _MapPair
ki int
}
@@ -44,43 +45,43 @@ var (
)
func init() {
if unsafe.Offsetof(_MapIterator{}.it) != 0 {
if unsafe.Offsetof(MapIterator{}.It) != 0 {
panic("_MapIterator.it is not the first field")
}
}
func newIterator() *_MapIterator {
func newIterator() *MapIterator {
if v := iteratorPool.Get(); v == nil {
return new(_MapIterator)
return new(MapIterator)
} else {
return resetIterator(v.(*_MapIterator))
return resetIterator(v.(*MapIterator))
}
}
func resetIterator(p *_MapIterator) *_MapIterator {
func resetIterator(p *MapIterator) *MapIterator {
p.ki = 0
p.it = rt.GoMapIterator{}
p.It = rt.GoMapIterator{}
p.kv.Len = 0
return p
}
func (self *_MapIterator) at(i int) *_MapPair {
func (self *MapIterator) at(i int) *_MapPair {
return (*_MapPair)(unsafe.Pointer(uintptr(self.kv.Ptr) + uintptr(i) * unsafe.Sizeof(_MapPair{})))
}
func (self *_MapIterator) add() (p *_MapPair) {
func (self *MapIterator) add() (p *_MapPair) {
p = self.at(self.kv.Len)
self.kv.Len++
return
}
func (self *_MapIterator) data() (p []_MapPair) {
func (self *MapIterator) data() (p []_MapPair) {
*(*rt.GoSlice)(unsafe.Pointer(&p)) = self.kv
return
}
func (self *_MapIterator) append(t *rt.GoType, k unsafe.Pointer, v unsafe.Pointer) (err error) {
func (self *MapIterator) append(t *rt.GoType, k unsafe.Pointer, v unsafe.Pointer) (err error) {
p := self.add()
p.v = v
@@ -94,26 +95,26 @@ func (self *_MapIterator) append(t *rt.GoType, k unsafe.Pointer, v unsafe.Pointe
return nil
}
func (self *_MapIterator) appendGeneric(p *_MapPair, t *rt.GoType, v reflect.Kind, k unsafe.Pointer) error {
func (self *MapIterator) appendGeneric(p *_MapPair, t *rt.GoType, v reflect.Kind, k unsafe.Pointer) error {
switch v {
case reflect.Int : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int)(k)))]) ; return nil
case reflect.Int8 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int8)(k)))]) ; return nil
case reflect.Int16 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int16)(k)))]) ; return nil
case reflect.Int32 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], int64(*(*int32)(k)))]) ; return nil
case reflect.Int64 : p.k = rt.Mem2Str(p.m[:native.I64toa(&p.m[0], *(*int64)(k))]) ; return nil
case reflect.Uint : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint)(k)))]) ; return nil
case reflect.Uint8 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint8)(k)))]) ; return nil
case reflect.Uint16 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint16)(k)))]) ; return nil
case reflect.Uint32 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uint32)(k)))]) ; return nil
case reflect.Uint64 : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], *(*uint64)(k))]) ; return nil
case reflect.Uintptr : p.k = rt.Mem2Str(p.m[:native.U64toa(&p.m[0], uint64(*(*uintptr)(k)))]) ; return nil
case reflect.Int : p.k = rt.Mem2Str(strconv.AppendInt(p.m[:0], int64(*(*int)(k)), 10)) ; return nil
case reflect.Int8 : p.k = rt.Mem2Str(strconv.AppendInt(p.m[:0], int64(*(*int8)(k)), 10)) ; return nil
case reflect.Int16 : p.k = rt.Mem2Str(strconv.AppendInt(p.m[:0], int64(*(*int16)(k)), 10)) ; return nil
case reflect.Int32 : p.k = rt.Mem2Str(strconv.AppendInt(p.m[:0], int64(*(*int32)(k)), 10)) ; return nil
case reflect.Int64 : p.k = rt.Mem2Str(strconv.AppendInt(p.m[:0], int64(*(*int64)(k)), 10)) ; return nil
case reflect.Uint : p.k = rt.Mem2Str(strconv.AppendUint(p.m[:0], uint64(*(*uint)(k)), 10)) ; return nil
case reflect.Uint8 : p.k = rt.Mem2Str(strconv.AppendUint(p.m[:0], uint64(*(*uint8)(k)), 10)) ; return nil
case reflect.Uint16 : p.k = rt.Mem2Str(strconv.AppendUint(p.m[:0], uint64(*(*uint16)(k)), 10)) ; return nil
case reflect.Uint32 : p.k = rt.Mem2Str(strconv.AppendUint(p.m[:0], uint64(*(*uint32)(k)), 10)) ; return nil
case reflect.Uint64 : p.k = rt.Mem2Str(strconv.AppendUint(p.m[:0], uint64(*(*uint64)(k)), 10)) ; return nil
case reflect.Uintptr : p.k = rt.Mem2Str(strconv.AppendUint(p.m[:0], uint64(*(*uintptr)(k)), 10)) ; return nil
case reflect.Interface : return self.appendInterface(p, t, k)
case reflect.Struct, reflect.Ptr : return self.appendConcrete(p, t, k)
default : panic("unexpected map key type")
}
}
func (self *_MapIterator) appendConcrete(p *_MapPair, t *rt.GoType, k unsafe.Pointer) (err error) {
func (self *MapIterator) appendConcrete(p *_MapPair, t *rt.GoType, k unsafe.Pointer) (err error) {
// compiler has already checked that the type implements the encoding.MarshalText interface
if !t.Indirect() {
k = *(*unsafe.Pointer)(k)
@@ -127,7 +128,7 @@ func (self *_MapIterator) appendConcrete(p *_MapPair, t *rt.GoType, k unsafe.Poi
return
}
func (self *_MapIterator) appendInterface(p *_MapPair, t *rt.GoType, k unsafe.Pointer) (err error) {
func (self *MapIterator) appendInterface(p *_MapPair, t *rt.GoType, k unsafe.Pointer) (err error) {
if len(rt.IfaceType(t).Methods) == 0 {
panic("unexpected map key type")
} else if p.k, err = asText(k); err == nil {
@@ -137,17 +138,17 @@ func (self *_MapIterator) appendInterface(p *_MapPair, t *rt.GoType, k unsafe.Po
}
}
func iteratorStop(p *_MapIterator) {
func IteratorStop(p *MapIterator) {
iteratorPool.Put(p)
}
func iteratorNext(p *_MapIterator) {
func IteratorNext(p *MapIterator) {
i := p.ki
t := &p.it
t := &p.It
/* check for unordered iteration */
if i < 0 {
mapiternext(t)
rt.Mapiternext(t)
return
}
@@ -164,12 +165,12 @@ func iteratorNext(p *_MapIterator) {
p.ki++
}
func iteratorStart(t *rt.GoMapType, m *rt.GoMap, fv uint64) (*_MapIterator, error) {
func IteratorStart(t *rt.GoMapType, m *rt.GoMap, fv uint64) (*MapIterator, error) {
it := newIterator()
mapiterinit(t, m, &it.it)
rt.Mapiterinit(t, m, &it.It)
/* check for key-sorting, empty map don't need sorting */
if m.Count == 0 || (fv & uint64(SortMapKeys)) == 0 {
if m.Count == 0 || (fv & (1<<BitSortMapKeys)) == 0 {
it.ki = -1
return it, nil
}
@@ -180,9 +181,9 @@ func iteratorStart(t *rt.GoMapType, m *rt.GoMap, fv uint64) (*_MapIterator, erro
}
/* dump all the key-value pairs */
for ; it.it.K != nil; mapiternext(&it.it) {
if err := it.append(t.Key, it.it.K, it.it.V); err != nil {
iteratorStop(it)
for ; it.It.K != nil; rt.Mapiternext(&it.It) {
if err := it.append(t.Key, it.It.K, it.It.V); err != nil {
IteratorStop(it)
return nil, err
}
}
@@ -193,7 +194,13 @@ func iteratorStart(t *rt.GoMapType, m *rt.GoMap, fv uint64) (*_MapIterator, erro
}
/* load the first pair into iterator */
it.it.V = it.at(0).v
it.it.K = unsafe.Pointer(&it.at(0).k)
it.It.V = it.at(0).v
it.It.K = unsafe.Pointer(&it.at(0).k)
return it, nil
}
func asText(v unsafe.Pointer) (string, error) {
text := rt.AssertI2I(rt.UnpackType(vars.EncodingTextMarshalerType), *(*rt.GoIface)(v))
r, e := (*(*encoding.TextMarshaler)(unsafe.Pointer(&text))).MarshalText()
return rt.Mem2Str(r), e
}
+31
View File
@@ -0,0 +1,31 @@
/**
* Copyright 2024 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 alg
const (
BitSortMapKeys = iota
BitEscapeHTML
BitCompactMarshaler
BitNoQuoteTextMarshaler
BitNoNullSliceOrMap
BitValidateString
BitNoValidateJSONMarshaler
BitNoEncoderNewline
BitEncodeNullForInfOrNan
BitPointerValue = 63
)
+95
View File
@@ -0,0 +1,95 @@
/**
* Copyright 2024 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 alg
import (
"encoding"
"encoding/json"
"github.com/bytedance/sonic/internal/encoder/vars"
"github.com/bytedance/sonic/internal/rt"
)
func Compact(p *[]byte, v []byte) error {
buf := vars.NewBuffer()
err := json.Compact(buf, v)
/* check for errors */
if err != nil {
return err
}
/* add to result */
v = buf.Bytes()
*p = append(*p, v...)
/* return the buffer into pool */
vars.FreeBuffer(buf)
return nil
}
func EncodeNil(rb *[]byte) error {
*rb = append(*rb, 'n', 'u', 'l', 'l')
return nil
}
// func Make_EncodeTypedPointer(computor func(*rt.GoType, ...interface{}) (interface{}, error)) func(*[]byte, *rt.GoType, *unsafe.Pointer, *vars.Stack, uint64) error {
// return func(buf *[]byte, vt *rt.GoType, vp *unsafe.Pointer, sb *vars.Stack, fv uint64) error {
// if vt == nil {
// return EncodeNil(buf)
// } else if fn, err := vars.FindOrCompile(vt, (fv&(1<<BitPointerValue)) != 0, computor); err != nil {
// return err
// } else if vt.Indirect() {
// err := fn(buf, *vp, sb, fv)
// return err
// } else {
// err := fn(buf, unsafe.Pointer(vp), sb, fv)
// return err
// }
// }
// }
func EncodeJsonMarshaler(buf *[]byte, val json.Marshaler, opt uint64) error {
if ret, err := val.MarshalJSON(); err != nil {
return err
} else {
if opt&(1<<BitCompactMarshaler) != 0 {
return Compact(buf, ret)
}
if opt&(1<<BitNoValidateJSONMarshaler) == 0 {
if ok, s := Valid(ret); !ok {
return vars.Error_marshaler(ret, s)
}
}
*buf = append(*buf, ret...)
return nil
}
}
func EncodeTextMarshaler(buf *[]byte, val encoding.TextMarshaler, opt uint64) error {
if ret, err := val.MarshalText(); err != nil {
return err
} else {
if opt&(1<<BitNoQuoteTextMarshaler) != 0 {
*buf = append(*buf, ret...)
return nil
}
*buf = Quote(*buf, rt.Mem2Str(ret), false)
return nil
}
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package encoder
package alg
// Algorithm 3-way Radix Quicksort, d means the radix.
// Reference: https://algs4.cs.princeton.edu/51radix/Quick3string.java.html
+198
View File
@@ -0,0 +1,198 @@
//go:build (amd64 && go1.16 && !go1.24) || (arm64 && go1.20 && !go1.24)
// +build amd64,go1.16,!go1.24 arm64,go1.20,!go1.24
/**
* Copyright 2024 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 alg
import (
"runtime"
"unsafe"
"github.com/bytedance/sonic/internal/native"
"github.com/bytedance/sonic/internal/native/types"
"github.com/bytedance/sonic/internal/rt"
)
// Valid validates json and returns first non-blank character position,
// if it is only one valid json value.
// Otherwise returns invalid character position using start.
//
// Note: it does not check for the invalid UTF-8 characters.
func Valid(data []byte) (ok bool, start int) {
n := len(data)
if n == 0 {
return false, -1
}
s := rt.Mem2Str(data)
p := 0
m := types.NewStateMachine()
ret := native.ValidateOne(&s, &p, m, 0)
types.FreeStateMachine(m)
if ret < 0 {
return false, p-1
}
/* check for trailing spaces */
for ;p < n; p++ {
if (types.SPACE_MASK & (1 << data[p])) == 0 {
return false, p
}
}
return true, ret
}
var typeByte = rt.UnpackEface(byte(0)).Type
//go:nocheckptr
func Quote(buf []byte, val string, double bool) []byte {
if len(val) == 0 {
if double {
return append(buf, `"\"\""`...)
}
return append(buf, `""`...)
}
if double {
buf = append(buf, `"\"`...)
} else {
buf = append(buf, `"`...)
}
sp := rt.IndexChar(val, 0)
nb := len(val)
b := (*rt.GoSlice)(unsafe.Pointer(&buf))
// input buffer
for nb > 0 {
// output buffer
dp := unsafe.Pointer(uintptr(b.Ptr) + uintptr(b.Len))
dn := b.Cap - b.Len
// call native.Quote, dn is byte count it outputs
opts := uint64(0)
if double {
opts = types.F_DOUBLE_UNQUOTE
}
ret := native.Quote(sp, nb, dp, &dn, opts)
// update *buf length
b.Len += dn
// no need more output
if ret >= 0 {
break
}
// double buf size
*b = rt.GrowSlice(typeByte, *b, b.Cap*2)
// ret is the complement of consumed input
ret = ^ret
// update input buffer
nb -= ret
sp = unsafe.Pointer(uintptr(sp) + uintptr(ret))
}
runtime.KeepAlive(buf)
runtime.KeepAlive(sp)
if double {
buf = append(buf, `\""`...)
} else {
buf = append(buf, `"`...)
}
return buf
}
func HtmlEscape(dst []byte, src []byte) []byte {
var sidx int
dst = append(dst, src[:0]...) // avoid check nil dst
sbuf := (*rt.GoSlice)(unsafe.Pointer(&src))
dbuf := (*rt.GoSlice)(unsafe.Pointer(&dst))
/* grow dst if it is shorter */
if cap(dst)-len(dst) < len(src)+types.BufPaddingSize {
cap := len(src)*3/2 + types.BufPaddingSize
*dbuf = rt.GrowSlice(typeByte, *dbuf, cap)
}
for sidx < sbuf.Len {
sp := rt.Add(sbuf.Ptr, uintptr(sidx))
dp := rt.Add(dbuf.Ptr, uintptr(dbuf.Len))
sn := sbuf.Len - sidx
dn := dbuf.Cap - dbuf.Len
nb := native.HTMLEscape(sp, sn, dp, &dn)
/* check for errors */
if dbuf.Len += dn; nb >= 0 {
break
}
/* not enough space, grow the slice and try again */
sidx += ^nb
*dbuf = rt.GrowSlice(typeByte, *dbuf, dbuf.Cap*2)
}
return dst
}
func F64toa(buf []byte, v float64) ([]byte) {
if v == 0 {
return append(buf, '0')
}
buf = rt.GuardSlice2(buf, 64)
ret := native.F64toa((*byte)(rt.IndexByte(buf, len(buf))), v)
if ret > 0 {
return buf[:len(buf)+ret]
} else {
return buf
}
}
func F32toa(buf []byte, v float32) ([]byte) {
if v == 0 {
return append(buf, '0')
}
buf = rt.GuardSlice2(buf, 64)
ret := native.F32toa((*byte)(rt.IndexByte(buf, len(buf))), v)
if ret > 0 {
return buf[:len(buf)+ret]
} else {
return buf
}
}
func I64toa(buf []byte, v int64) ([]byte) {
buf = rt.GuardSlice2(buf, 32)
ret := native.I64toa((*byte)(rt.IndexByte(buf, len(buf))), v)
if ret > 0 {
return buf[:len(buf)+ret]
} else {
return buf
}
}
func U64toa(buf []byte, v uint64) ([]byte) {
buf = rt.GuardSlice2(buf, 32)
ret := native.U64toa((*byte)(rt.IndexByte(buf, len(buf))), v)
if ret > 0 {
return buf[:len(buf)+ret]
} else {
return buf
}
}
+148
View File
@@ -0,0 +1,148 @@
// +build !amd64,!arm64 go1.24 !go1.16 arm64,!go1.20
/**
* Copyright 2024 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 alg
import (
_ "unsafe"
"unicode/utf8"
"strconv"
"bytes"
"encoding/json"
"github.com/bytedance/sonic/internal/rt"
)
// Valid validates json and returns first non-blank character position,
// if it is only one valid json value.
// Otherwise returns invalid character position using start.
//
// Note: it does not check for the invalid UTF-8 characters.
func Valid(data []byte) (ok bool, start int) {
ok = json.Valid(data)
return ok, 0
}
var typeByte = rt.UnpackEface(byte(0)).Type
func Quote(e []byte, s string, double bool) []byte {
if len(s) == 0 {
if double {
return append(e, `"\"\""`...)
}
return append(e, `""`...)
}
b := e
ss := len(e)
e = append(e, '"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
if rt.SafeSet[b] {
i++
continue
}
if start < i {
e = append(e, s[start:i]...)
}
e = append(e, '\\')
switch b {
case '\\', '"':
e = append(e, b)
case '\n':
e = append(e, 'n')
case '\r':
e = append(e, 'r')
case '\t':
e = append(e, 't')
default:
// This encodes bytes < 0x20 except for \t, \n and \r.
// If escapeHTML is set, it also escapes <, >, and &
// because they can lead to security holes when
// user-controlled strings are rendered into JSON
// and served to some browsers.
e = append(e, `u00`...)
e = append(e, rt.Hex[b>>4])
e = append(e, rt.Hex[b&0xF])
}
i++
start = i
continue
}
c, size := utf8.DecodeRuneInString(s[i:])
// if correct && c == utf8.RuneError && size == 1 {
// if start < i {
// e = append(e, s[start:i]...)
// }
// e = append(e, `\ufffd`...)
// i += size
// start = i
// continue
// }
if c == '\u2028' || c == '\u2029' {
if start < i {
e = append(e, s[start:i]...)
}
e = append(e, `\u202`...)
e = append(e, rt.Hex[c&0xF])
i += size
start = i
continue
}
i += size
}
if start < len(s) {
e = append(e, s[start:]...)
}
e = append(e, '"')
if double {
return strconv.AppendQuote(b, string(e[ss:]))
} else {
return e
}
}
func HtmlEscape(dst []byte, src []byte) []byte {
buf := bytes.NewBuffer(dst)
json.HTMLEscape(buf, src)
return buf.Bytes()
}
func F64toa(buf []byte, v float64) ([]byte) {
bs := bytes.NewBuffer(buf)
_ = json.NewEncoder(bs).Encode(v)
return bs.Bytes()
}
func F32toa(buf []byte, v float32) ([]byte) {
bs := bytes.NewBuffer(buf)
_ = json.NewEncoder(bs).Encode(v)
return bs.Bytes()
}
func I64toa(buf []byte, v int64) ([]byte) {
return strconv.AppendInt(buf, int64(v), 10)
}
func U64toa(buf []byte, v uint64) ([]byte) {
return strconv.AppendUint(buf, v, 10)
}
@@ -1,51 +0,0 @@
// +build go1.16,!go1.17
// Copyright 2023 CloudWeGo Authors
//
// 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 encoder
import (
`strconv`
`github.com/bytedance/sonic/internal/jit`
`github.com/twitchyliquid64/golang-asm/obj`
`github.com/twitchyliquid64/golang-asm/obj/x86`
)
var (
_V_writeBarrier = jit.Imm(int64(_runtime_writeBarrier))
_F_gcWriteBarrierAX = jit.Func(gcWriteBarrierAX)
)
func (self *_Assembler) WritePtr(i int, ptr obj.Addr, rec obj.Addr) {
if rec.Reg == x86.REG_AX || rec.Index == x86.REG_AX {
panic("rec contains AX!")
}
self.Emit("MOVQ", _V_writeBarrier, _R10)
self.Emit("CMPL", jit.Ptr(_R10, 0), jit.Imm(0))
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Emit("MOVQ", ptr, _AX)
self.xsave(_DI)
self.Emit("LEAQ", rec, _DI)
self.Emit("MOVQ", _F_gcWriteBarrierAX, _R10) // MOVQ ${fn}, AX
self.Rjmp("CALL", _R10)
self.xload(_DI)
self.Sjmp("JMP", "_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
self.Emit("MOVQ", ptr, rec)
self.Link("_end_writeBarrier" + strconv.Itoa(i) + "_{n}")
}
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
-205
View File
@@ -1,205 +0,0 @@
// +build go1.17,!go1.23
/*
* 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 encoder
import (
`fmt`
`os`
`runtime`
`strings`
`unsafe`
`github.com/bytedance/sonic/internal/jit`
`github.com/twitchyliquid64/golang-asm/obj`
)
const _FP_debug = 128
var (
debugSyncGC = os.Getenv("SONIC_SYNC_GC") != ""
debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
debugCheckPtr = os.Getenv("SONIC_CHECK_POINTER") != ""
)
var (
_Instr_End = newInsOp(_OP_is_nil)
_F_gc = jit.Func(gc)
_F_println = jit.Func(println_wrapper)
_F_print = jit.Func(print)
)
func (self *_Assembler) dsave(r ...obj.Addr) {
for i, v := range r {
if i > _FP_debug / 8 - 1 {
panic("too many registers to save")
} else {
self.Emit("MOVQ", v, jit.Ptr(_SP, _FP_fargs + _FP_saves + _FP_locals + int64(i) * 8))
}
}
}
func (self *_Assembler) dload(r ...obj.Addr) {
for i, v := range r {
if i > _FP_debug / 8 - 1 {
panic("too many registers to load")
} else {
self.Emit("MOVQ", jit.Ptr(_SP, _FP_fargs + _FP_saves + _FP_locals + int64(i) * 8), v)
}
}
}
func println_wrapper(i int, op1 int, op2 int){
println(i, " Intrs ", op1, _OpNames[op1], "next: ", op2, _OpNames[op2])
}
func print(i int){
println(i)
}
func gc() {
if !debugSyncGC {
return
}
runtime.GC()
// debug.FreeOSMemory()
}
func (self *_Assembler) dcall(fn obj.Addr) {
self.Emit("MOVQ", fn, _R10) // MOVQ ${fn}, R10
self.Rjmp("CALL", _R10) // CALL R10
}
func (self *_Assembler) debug_gc() {
if !debugSyncGC {
return
}
self.dsave(_REG_debug...)
self.dcall(_F_gc)
self.dload(_REG_debug...)
}
func (self *_Assembler) debug_instr(i int, v *_Instr) {
if debugSyncGC {
if i+1 == len(self.p) {
self.print_gc(i, v, &_Instr_End)
} else {
next := &(self.p[i+1])
self.print_gc(i, v, next)
name := _OpNames[next.op()]
if strings.Contains(name, "save") {
return
}
}
// self.debug_gc()
}
}
//go:noescape
//go:linkname checkptrBase runtime.checkptrBase
func checkptrBase(p unsafe.Pointer) uintptr
//go:noescape
//go:linkname findObject runtime.findObject
func findObject(p, refBase, refOff uintptr) (base uintptr, s unsafe.Pointer, objIndex uintptr)
var (
_F_checkptr = jit.Func(checkptr)
_F_printptr = jit.Func(printptr)
)
var (
_R10 = jit.Reg("R10")
)
var _REG_debug = []obj.Addr {
jit.Reg("AX"),
jit.Reg("BX"),
jit.Reg("CX"),
jit.Reg("DX"),
jit.Reg("DI"),
jit.Reg("SI"),
jit.Reg("BP"),
jit.Reg("SP"),
jit.Reg("R8"),
jit.Reg("R9"),
jit.Reg("R10"),
jit.Reg("R11"),
jit.Reg("R12"),
jit.Reg("R13"),
jit.Reg("R14"),
jit.Reg("R15"),
}
func checkptr(ptr uintptr) {
if ptr == 0 {
return
}
fmt.Printf("pointer: %x\n", ptr)
f := checkptrBase(unsafe.Pointer(uintptr(ptr)))
if f == 0 {
fmt.Printf("! unknown-based pointer: %x\n", ptr)
} else if f == 1 {
fmt.Printf("! stack pointer: %x\n", ptr)
} else {
fmt.Printf("base: %x\n", f)
}
findobj(ptr)
}
func findobj(ptr uintptr) {
base, s, objIndex := findObject(ptr, 0, 0)
if s != nil && base == 0 {
fmt.Printf("! invalid pointer: %x\n", ptr)
}
fmt.Printf("objIndex: %d\n", objIndex)
}
func (self *_Assembler) check_ptr(ptr obj.Addr, lea bool) {
if !debugCheckPtr {
return
}
self.dsave(_REG_debug...)
if lea {
self.Emit("LEAQ", ptr, _R10)
} else {
self.Emit("MOVQ", ptr, _R10)
}
self.Emit("MOVQ", _R10, jit.Ptr(_SP, 0))
self.dcall(_F_checkptr)
self.dload(_REG_debug...)
}
func printptr(i int, ptr uintptr) {
fmt.Printf("[%d] ptr: %x\n", i, ptr)
}
func (self *_Assembler) print_ptr(i int, ptr obj.Addr, lea bool) {
self.dsave(_REG_debug...)
if lea {
self.Emit("LEAQ", ptr, _R10)
} else {
self.Emit("MOVQ", ptr, _R10)
}
self.Emit("MOVQ", jit.Imm(int64(i)), _AX)
self.Emit("MOVQ", _R10, _BX)
self.dcall(_F_printptr)
self.dload(_REG_debug...)
}
+24
View File
@@ -0,0 +1,24 @@
//go:build !race
// +build !race
/*
* 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 encoder
func encodeIntoCheckRace(buf *[]byte, val interface{}, opts Options) error {
return encodeInto(buf, val, opts)
}
+54
View File
@@ -0,0 +1,54 @@
//go:build race
// +build race
/*
* 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 encoder
import (
`encoding/json`
`github.com/bytedance/sonic/internal/rt`
)
func helpDetectDataRace(val interface{}) {
var out []byte
defer func() {
if v := recover(); v != nil {
// NOTICE: help user to locate where panic occurs
println("panic when encoding on: ", truncate(out))
panic(v)
}
}()
out, _ = json.Marshal(val)
}
func encodeIntoCheckRace(buf *[]byte, val interface{}, opts Options) error {
err := encodeInto(buf, val, opts)
/* put last to make the panic from sonic will always be caught at first */
helpDetectDataRace(val)
return err
}
func truncate(json []byte) string {
if len(json) <= 256 {
return rt.Mem2Str(json)
} else {
return rt.Mem2Str(json[len(json)-256:])
}
}
+66 -103
View File
@@ -17,72 +17,62 @@
package encoder
import (
`bytes`
`encoding/json`
`reflect`
`runtime`
`unsafe`
"bytes"
"encoding/json"
"reflect"
"runtime"
"unsafe"
`github.com/bytedance/sonic/internal/native`
`github.com/bytedance/sonic/internal/native/types`
`github.com/bytedance/sonic/internal/rt`
`github.com/bytedance/sonic/utf8`
`github.com/bytedance/sonic/option`
"github.com/bytedance/sonic/utf8"
"github.com/bytedance/sonic/internal/encoder/alg"
"github.com/bytedance/sonic/internal/encoder/vars"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/option"
)
// Options is a set of encoding options.
type Options uint64
const (
bitSortMapKeys = iota
bitEscapeHTML
bitCompactMarshaler
bitNoQuoteTextMarshaler
bitNoNullSliceOrMap
bitValidateString
bitNoValidateJSONMarshaler
bitNoEncoderNewline
// used for recursive compile
bitPointerValue = 63
)
const (
// SortMapKeys indicates that the keys of a map needs to be sorted
// before serializing into JSON.
// WARNING: This hurts performance A LOT, USE WITH CARE.
SortMapKeys Options = 1 << bitSortMapKeys
SortMapKeys Options = 1 << alg.BitSortMapKeys
// EscapeHTML indicates encoder to escape all HTML characters
// after serializing into JSON (see https://pkg.go.dev/encoding/json#HTMLEscape).
// WARNING: This hurts performance A LOT, USE WITH CARE.
EscapeHTML Options = 1 << bitEscapeHTML
EscapeHTML Options = 1 << alg.BitEscapeHTML
// CompactMarshaler indicates that the output JSON from json.Marshaler
// is always compact and needs no validation
CompactMarshaler Options = 1 << bitCompactMarshaler
CompactMarshaler Options = 1 << alg.BitCompactMarshaler
// NoQuoteTextMarshaler indicates that the output text from encoding.TextMarshaler
// is always escaped string and needs no quoting
NoQuoteTextMarshaler Options = 1 << bitNoQuoteTextMarshaler
NoQuoteTextMarshaler Options = 1 << alg.BitNoQuoteTextMarshaler
// NoNullSliceOrMap indicates all empty Array or Object are encoded as '[]' or '{}',
// instead of 'null'
NoNullSliceOrMap Options = 1 << bitNoNullSliceOrMap
// instead of 'null'.
// NOTE: The priority of this option is lower than json tag `omitempty`.
NoNullSliceOrMap Options = 1 << alg.BitNoNullSliceOrMap
// ValidateString indicates that encoder should validate the input string
// before encoding it into JSON.
ValidateString Options = 1 << bitValidateString
ValidateString Options = 1 << alg.BitValidateString
// NoValidateJSONMarshaler indicates that the encoder should not validate the output string
// after encoding the JSONMarshaler to JSON.
NoValidateJSONMarshaler Options = 1 << bitNoValidateJSONMarshaler
NoValidateJSONMarshaler Options = 1 << alg.BitNoValidateJSONMarshaler
// NoEncoderNewline indicates that the encoder should not add a newline after every message
NoEncoderNewline Options = 1 << bitNoEncoderNewline
NoEncoderNewline Options = 1 << alg.BitNoEncoderNewline
// CompatibleWithStd is used to be compatible with std encoder.
CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler
// Encode Infinity or Nan float into `null`, instead of returning an error.
EncodeNullForInfOrNan Options = 1 << alg.BitEncodeNullForInfOrNan
)
// Encoder represents a specific set of encoder configurations.
@@ -171,53 +161,45 @@ func (enc *Encoder) SetIndent(prefix, indent string) {
// Quote returns the JSON-quoted version of s.
func Quote(s string) string {
var n int
var p []byte
/* check for empty string */
if s == "" {
return `""`
}
/* allocate space for result */
n = len(s) + 2
p = make([]byte, 0, n)
/* call the encoder */
_ = encodeString(&p, s)
return rt.Mem2Str(p)
buf := make([]byte, 0, len(s)+2)
buf = alg.Quote(buf, s, false)
return rt.Mem2Str(buf)
}
// Encode returns the JSON encoding of val, encoded with opts.
func Encode(val interface{}, opts Options) ([]byte, error) {
var ret []byte
buf := newBytes()
err := encodeInto(&buf, val, opts)
buf := vars.NewBytes()
err := encodeIntoCheckRace(buf, val, opts)
/* check for errors */
if err != nil {
freeBytes(buf)
vars.FreeBytes(buf)
return nil, err
}
/* htmlescape or correct UTF-8 if opts enable */
old := buf
buf = encodeFinish(old, opts)
pbuf := ((*rt.GoSlice)(unsafe.Pointer(&buf))).Ptr
pold := ((*rt.GoSlice)(unsafe.Pointer(&old))).Ptr
*buf = encodeFinish(*old, opts)
pbuf := ((*rt.GoSlice)(unsafe.Pointer(buf))).Ptr
pold := ((*rt.GoSlice)(unsafe.Pointer(old))).Ptr
/* return when allocated a new buffer */
if pbuf != pold {
freeBytes(old)
return buf, nil
vars.FreeBytes(old)
return *buf, nil
}
/* make a copy of the result */
ret = make([]byte, len(buf))
copy(ret, buf)
freeBytes(buf)
if rt.CanSizeResue(cap(*buf)) {
ret = make([]byte, len(*buf))
copy(ret, *buf)
vars.FreeBytes(buf)
} else {
ret = *buf
}
/* return the buffer into pool */
return ret, nil
}
@@ -225,7 +207,7 @@ func Encode(val interface{}, opts Options) ([]byte, error) {
// EncodeInto is like Encode but uses a user-supplied buffer instead of allocating
// a new one.
func EncodeInto(buf *[]byte, val interface{}, opts Options) error {
err := encodeInto(buf, val, opts)
err := encodeIntoCheckRace(buf, val, opts)
if err != nil {
return err
}
@@ -234,15 +216,15 @@ func EncodeInto(buf *[]byte, val interface{}, opts Options) error {
}
func encodeInto(buf *[]byte, val interface{}, opts Options) error {
stk := newStack()
stk := vars.NewStack()
efv := rt.UnpackEface(val)
err := encodeTypedPointer(buf, efv.Type, &efv.Value, stk, uint64(opts))
/* return the stack into pool */
if err != nil {
resetStack(stk)
vars.ResetStack(stk)
}
freeStack(stk)
vars.FreeStack(stk)
/* avoid GC ahead */
runtime.KeepAlive(buf)
@@ -254,13 +236,12 @@ func encodeFinish(buf []byte, opts Options) []byte {
if opts & EscapeHTML != 0 {
buf = HTMLEscape(nil, buf)
}
if opts & ValidateString != 0 && !utf8.Validate(buf) {
if (opts & ValidateString != 0) && !utf8.Validate(buf) {
buf = utf8.CorrectWith(nil, buf, `\ufffd`)
}
return buf
}
var typeByte = rt.UnpackType(reflect.TypeOf(byte(0)))
// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
@@ -269,7 +250,7 @@ var typeByte = rt.UnpackType(reflect.TypeOf(byte(0)))
// escaping within <script> tags, so an alternative JSON encoding must
// be used.
func HTMLEscape(dst []byte, src []byte) []byte {
return htmlEscape(dst, src)
return alg.HtmlEscape(dst, src)
}
// EncodeIndented is like Encode but applies Indent to format the output.
@@ -277,37 +258,40 @@ func HTMLEscape(dst []byte, src []byte) []byte {
// followed by one or more copies of indent according to the indentation nesting.
func EncodeIndented(val interface{}, prefix string, indent string, opts Options) ([]byte, error) {
var err error
var out []byte
var buf *bytes.Buffer
/* encode into the buffer */
out = newBytes()
err = EncodeInto(&out, val, opts)
out := vars.NewBytes()
err = EncodeInto(out, val, opts)
/* check for errors */
if err != nil {
freeBytes(out)
vars.FreeBytes(out)
return nil, err
}
/* indent the JSON */
buf = newBuffer()
err = json.Indent(buf, out, prefix, indent)
buf = vars.NewBuffer()
err = json.Indent(buf, *out, prefix, indent)
vars.FreeBytes(out)
/* check for errors */
if err != nil {
freeBytes(out)
freeBuffer(buf)
vars.FreeBuffer(buf)
return nil, err
}
/* copy to the result buffer */
ret := make([]byte, buf.Len())
copy(ret, buf.Bytes())
/* return the buffers into pool */
freeBytes(out)
freeBuffer(buf)
var ret []byte
if rt.CanSizeResue(cap(buf.Bytes())) {
ret = make([]byte, buf.Len())
copy(ret, buf.Bytes())
/* return the buffers into pool */
vars.FreeBuffer(buf)
} else {
ret = buf.Bytes()
}
return ret, nil
}
@@ -330,26 +314,5 @@ func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
//
// Note: it does not check for the invalid UTF-8 characters.
func Valid(data []byte) (ok bool, start int) {
n := len(data)
if n == 0 {
return false, -1
}
s := rt.Mem2Str(data)
p := 0
m := types.NewStateMachine()
ret := native.ValidateOne(&s, &p, m, types.F_VALIDATE_STRING)
types.FreeStateMachine(m)
if ret < 0 {
return false, p-1
}
/* check for trailing spaces */
for ;p < n; p++ {
if (types.SPACE_MASK & (1 << data[p])) == 0 {
return false, p
}
}
return true, ret
return alg.Valid(data)
}
+473
View File
@@ -0,0 +1,473 @@
/*
* 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 ir
import (
"fmt"
"reflect"
"strconv"
"strings"
"unsafe"
"github.com/bytedance/sonic/internal/encoder/vars"
"github.com/bytedance/sonic/internal/rt"
)
type Op uint8
const (
OP_null Op = iota + 1
OP_empty_arr
OP_empty_obj
OP_bool
OP_i8
OP_i16
OP_i32
OP_i64
OP_u8
OP_u16
OP_u32
OP_u64
OP_f32
OP_f64
OP_str
OP_bin
OP_quote
OP_number
OP_eface
OP_iface
OP_byte
OP_text
OP_deref
OP_index
OP_load
OP_save
OP_drop
OP_drop_2
OP_recurse
OP_is_nil
OP_is_nil_p1
OP_is_zero_1
OP_is_zero_2
OP_is_zero_4
OP_is_zero_8
OP_is_zero_map
OP_goto
OP_map_iter
OP_map_stop
OP_map_check_key
OP_map_write_key
OP_map_value_next
OP_slice_len
OP_slice_next
OP_marshal
OP_marshal_p
OP_marshal_text
OP_marshal_text_p
OP_cond_set
OP_cond_testc
)
const (
_INT_SIZE = 32 << (^uint(0) >> 63)
_PTR_SIZE = 32 << (^uintptr(0) >> 63)
_PTR_BYTE = unsafe.Sizeof(uintptr(0))
)
const OpSize = unsafe.Sizeof(NewInsOp(0))
var OpNames = [256]string{
OP_null: "null",
OP_empty_arr: "empty_arr",
OP_empty_obj: "empty_obj",
OP_bool: "bool",
OP_i8: "i8",
OP_i16: "i16",
OP_i32: "i32",
OP_i64: "i64",
OP_u8: "u8",
OP_u16: "u16",
OP_u32: "u32",
OP_u64: "u64",
OP_f32: "f32",
OP_f64: "f64",
OP_str: "str",
OP_bin: "bin",
OP_quote: "quote",
OP_number: "number",
OP_eface: "eface",
OP_iface: "iface",
OP_byte: "byte",
OP_text: "text",
OP_deref: "deref",
OP_index: "index",
OP_load: "load",
OP_save: "save",
OP_drop: "drop",
OP_drop_2: "drop_2",
OP_recurse: "recurse",
OP_is_nil: "is_nil",
OP_is_nil_p1: "is_nil_p1",
OP_is_zero_1: "is_zero_1",
OP_is_zero_2: "is_zero_2",
OP_is_zero_4: "is_zero_4",
OP_is_zero_8: "is_zero_8",
OP_is_zero_map: "is_zero_map",
OP_goto: "goto",
OP_map_iter: "map_iter",
OP_map_stop: "map_stop",
OP_map_check_key: "map_check_key",
OP_map_write_key: "map_write_key",
OP_map_value_next: "map_value_next",
OP_slice_len: "slice_len",
OP_slice_next: "slice_next",
OP_marshal: "marshal",
OP_marshal_p: "marshal_p",
OP_marshal_text: "marshal_text",
OP_marshal_text_p: "marshal_text_p",
OP_cond_set: "cond_set",
OP_cond_testc: "cond_testc",
}
func (self Op) String() string {
if ret := OpNames[self]; ret != "" {
return ret
} else {
return "<invalid>"
}
}
func OP_int() Op {
switch _INT_SIZE {
case 32:
return OP_i32
case 64:
return OP_i64
default:
panic("unsupported int size")
}
}
func OP_uint() Op {
switch _INT_SIZE {
case 32:
return OP_u32
case 64:
return OP_u64
default:
panic("unsupported uint size")
}
}
func OP_uintptr() Op {
switch _PTR_SIZE {
case 32:
return OP_u32
case 64:
return OP_u64
default:
panic("unsupported pointer size")
}
}
func OP_is_zero_ints() Op {
switch _INT_SIZE {
case 32:
return OP_is_zero_4
case 64:
return OP_is_zero_8
default:
panic("unsupported integer size")
}
}
type Instr struct {
o Op
u int // union {op: 8, _: 8, vi: 48}, vi maybe int or len(str)
p unsafe.Pointer // maybe GoString.Ptr, or *GoType
}
func NewInsOp(op Op) Instr {
return Instr{o: op}
}
func NewInsVi(op Op, vi int) Instr {
return Instr{o: op, u: vi}
}
func NewInsVs(op Op, vs string) Instr {
return Instr{
o: op,
u: len(vs),
p: (*rt.GoString)(unsafe.Pointer(&vs)).Ptr,
}
}
func NewInsVt(op Op, vt reflect.Type) Instr {
return Instr{
o: op,
p: unsafe.Pointer(rt.UnpackType(vt)),
}
}
type typAndTab struct {
vt *rt.GoType
itab *rt.GoItab
}
func NewInsVtab(op Op, vt reflect.Type, itab *rt.GoItab) Instr {
return Instr{
o: op,
p: unsafe.Pointer(&typAndTab{
vt: rt.UnpackType(vt),
itab: itab,
}),
}
}
func NewInsVp(op Op, vt reflect.Type, pv bool) Instr {
i := 0
if pv {
i = 1
}
return Instr{
o: op,
u: i,
p: unsafe.Pointer(rt.UnpackType(vt)),
}
}
func (self Instr) Op() Op {
return Op(self.o)
}
func (self Instr) Vi() int {
return self.u
}
func (self Instr) Vf() uint8 {
return (*rt.GoType)(self.p).KindFlags
}
func (self Instr) Vs() (v string) {
(*rt.GoString)(unsafe.Pointer(&v)).Ptr = self.p
(*rt.GoString)(unsafe.Pointer(&v)).Len = self.Vi()
return
}
func (self Instr) Vk() reflect.Kind {
return (*rt.GoType)(self.p).Kind()
}
func (self Instr) Vt() reflect.Type {
return (*rt.GoType)(self.p).Pack()
}
func (self Instr) Vr() *rt.GoType {
return (*rt.GoType)(self.p)
}
func (self Instr) Vp() (vt reflect.Type, pv bool) {
return (*rt.GoType)(self.p).Pack(), self.u == 1
}
func (self Instr) Vtab() (vt *rt.GoType, itab *rt.GoItab) {
tt := (*typAndTab)(self.p)
return tt.vt, tt.itab
}
func (self Instr) Vp2() (vt *rt.GoType, pv bool) {
return (*rt.GoType)(self.p), self.u == 1
}
func (self Instr) I64() int64 {
return int64(self.Vi())
}
func (self Instr) Byte() byte {
return byte(self.Vi())
}
func (self Instr) Vlen() int {
return int((*rt.GoType)(self.p).Size)
}
func (self Instr) isBranch() bool {
switch self.Op() {
case OP_goto:
fallthrough
case OP_is_nil:
fallthrough
case OP_is_nil_p1:
fallthrough
case OP_is_zero_1:
fallthrough
case OP_is_zero_2:
fallthrough
case OP_is_zero_4:
fallthrough
case OP_is_zero_8:
fallthrough
case OP_map_check_key:
fallthrough
case OP_map_write_key:
fallthrough
case OP_slice_next:
fallthrough
case OP_cond_testc:
return true
default:
return false
}
}
func (self Instr) Disassemble() string {
switch self.Op() {
case OP_byte:
return fmt.Sprintf("%-18s%s", self.Op().String(), strconv.QuoteRune(rune(self.Vi())))
case OP_text:
return fmt.Sprintf("%-18s%s", self.Op().String(), strconv.Quote(self.Vs()))
case OP_index:
return fmt.Sprintf("%-18s%d", self.Op().String(), self.Vi())
case OP_recurse:
fallthrough
case OP_map_iter:
return fmt.Sprintf("%-18s%s", self.Op().String(), self.Vt())
case OP_marshal:
fallthrough
case OP_marshal_p:
fallthrough
case OP_marshal_text:
fallthrough
case OP_marshal_text_p:
vt, _ := self.Vtab()
return fmt.Sprintf("%-18s%s", self.Op().String(), vt.Pack())
case OP_goto:
fallthrough
case OP_is_nil:
fallthrough
case OP_is_nil_p1:
fallthrough
case OP_is_zero_1:
fallthrough
case OP_is_zero_2:
fallthrough
case OP_is_zero_4:
fallthrough
case OP_is_zero_8:
fallthrough
case OP_is_zero_map:
fallthrough
case OP_cond_testc:
fallthrough
case OP_map_check_key:
fallthrough
case OP_map_write_key:
return fmt.Sprintf("%-18sL_%d", self.Op().String(), self.Vi())
case OP_slice_next:
return fmt.Sprintf("%-18sL_%d, %s", self.Op().String(), self.Vi(), self.Vt())
default:
return fmt.Sprintf("%#v", self)
}
}
type (
Program []Instr
)
func (self Program) PC() int {
return len(self)
}
func (self Program) Tag(n int) {
if n >= vars.MaxStack {
panic("type nesting too deep")
}
}
func (self Program) Pin(i int) {
v := &self[i]
v.u = self.PC()
}
func (self Program) Rel(v []int) {
for _, i := range v {
self.Pin(i)
}
}
func (self *Program) Add(op Op) {
*self = append(*self, NewInsOp(op))
}
func (self *Program) Key(op Op) {
*self = append(*self,
NewInsVi(OP_byte, '"'),
NewInsOp(op),
NewInsVi(OP_byte, '"'),
)
}
func (self *Program) Int(op Op, vi int) {
*self = append(*self, NewInsVi(op, vi))
}
func (self *Program) Str(op Op, vs string) {
*self = append(*self, NewInsVs(op, vs))
}
func (self *Program) Rtt(op Op, vt reflect.Type) {
*self = append(*self, NewInsVt(op, vt))
}
func (self *Program) Vp(op Op, vt reflect.Type, pv bool) {
*self = append(*self, NewInsVp(op, vt, pv))
}
func (self *Program) Vtab(op Op, vt reflect.Type, itab *rt.GoItab) {
*self = append(*self, NewInsVtab(op, vt, itab))
}
func (self Program) Disassemble() string {
nb := len(self)
tab := make([]bool, nb+1)
ret := make([]string, 0, nb+1)
/* prescan to get all the labels */
for _, ins := range self {
if ins.isBranch() {
tab[ins.Vi()] = true
}
}
/* disassemble each instruction */
for i, ins := range self {
if !tab[i] {
ret = append(ret, "\t"+ins.Disassemble())
} else {
ret = append(ret, fmt.Sprintf("L_%d:\n\t%s", i, ins.Disassemble()))
}
}
/* add the last label, if needed */
if tab[nb] {
ret = append(ret, fmt.Sprintf("L_%d:", nb))
}
/* add an "end" indicator, and join all the strings */
return strings.Join(append(ret, "\tend"), "\n")
}
-193
View File
@@ -1,193 +0,0 @@
/*
* 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 encoder
import (
`bytes`
`sync`
`unsafe`
`errors`
`reflect`
`github.com/bytedance/sonic/internal/caching`
`github.com/bytedance/sonic/option`
`github.com/bytedance/sonic/internal/rt`
)
const (
_MaxStack = 4096 // 4k states
_StackSize = unsafe.Sizeof(_Stack{})
)
var (
bytesPool = sync.Pool{}
stackPool = sync.Pool{}
bufferPool = sync.Pool{}
programCache = caching.CreateProgramCache()
)
type _State struct {
x int
f uint64
p unsafe.Pointer
q unsafe.Pointer
}
type _Stack struct {
sp uint64
sb [_MaxStack]_State
}
type _Encoder func(
rb *[]byte,
vp unsafe.Pointer,
sb *_Stack,
fv uint64,
) error
var _KeepAlive struct {
rb *[]byte
vp unsafe.Pointer
sb *_Stack
fv uint64
err error
frame [_FP_offs]byte
}
var errCallShadow = errors.New("DON'T CALL THIS!")
// Faker func of _Encoder, used to export its stackmap as _Encoder's
func _Encoder_Shadow(rb *[]byte, vp unsafe.Pointer, sb *_Stack, fv uint64) (err error) {
// align to assembler_amd64.go: _FP_offs
var frame [_FP_offs]byte
// must keep all args and frames noticeable to GC
_KeepAlive.rb = rb
_KeepAlive.vp = vp
_KeepAlive.sb = sb
_KeepAlive.fv = fv
_KeepAlive.err = err
_KeepAlive.frame = frame
return errCallShadow
}
func newBytes() []byte {
if ret := bytesPool.Get(); ret != nil {
return ret.([]byte)
} else {
return make([]byte, 0, option.DefaultEncoderBufferSize)
}
}
func newStack() *_Stack {
if ret := stackPool.Get(); ret == nil {
return new(_Stack)
} else {
return ret.(*_Stack)
}
}
func resetStack(p *_Stack) {
memclrNoHeapPointers(unsafe.Pointer(p), _StackSize)
}
func newBuffer() *bytes.Buffer {
if ret := bufferPool.Get(); ret != nil {
return ret.(*bytes.Buffer)
} else {
return bytes.NewBuffer(make([]byte, 0, option.DefaultEncoderBufferSize))
}
}
func freeBytes(p []byte) {
p = p[:0]
bytesPool.Put(p)
}
func freeStack(p *_Stack) {
p.sp = 0
stackPool.Put(p)
}
func freeBuffer(p *bytes.Buffer) {
p.Reset()
bufferPool.Put(p)
}
func makeEncoder(vt *rt.GoType, ex ...interface{}) (interface{}, error) {
if pp, err := newCompiler().compile(vt.Pack(), ex[0].(bool)); err != nil {
return nil, err
} else {
as := newAssembler(pp)
as.name = vt.String()
return as.Load(), nil
}
}
func findOrCompile(vt *rt.GoType, pv bool) (_Encoder, error) {
if val := programCache.Get(vt); val != nil {
return val.(_Encoder), nil
} else if ret, err := programCache.Compute(vt, makeEncoder, pv); err == nil {
return ret.(_Encoder), nil
} else {
return nil, err
}
}
func pretouchType(_vt reflect.Type, opts option.CompileOptions, v uint8) (map[reflect.Type]uint8, error) {
/* compile function */
compiler := newCompiler().apply(opts)
encoder := func(vt *rt.GoType, ex ...interface{}) (interface{}, error) {
if pp, err := compiler.compile(_vt, ex[0].(bool)); err != nil {
return nil, err
} else {
as := newAssembler(pp)
as.name = vt.String()
return as.Load(), nil
}
}
/* find or compile */
vt := rt.UnpackType(_vt)
if val := programCache.Get(vt); val != nil {
return nil, nil
} else if _, err := programCache.Compute(vt, encoder, v == 1); err == nil {
return compiler.rec, nil
} else {
return nil, err
}
}
func pretouchRec(vtm map[reflect.Type]uint8, opts option.CompileOptions) error {
if opts.RecursiveDepth < 0 || len(vtm) == 0 {
return nil
}
next := make(map[reflect.Type]uint8)
for vt, v := range vtm {
sub, err := pretouchType(vt, opts, v)
if err != nil {
return err
}
for svt, v := range sub {
next[svt] = v
}
}
opts.RecursiveDepth -= 1
return pretouchRec(next, opts)
}
+97
View File
@@ -0,0 +1,97 @@
/*
* 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 encoder
import (
"errors"
"reflect"
"unsafe"
"github.com/bytedance/sonic/internal/encoder/vars"
"github.com/bytedance/sonic/internal/encoder/x86"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/option"
)
func ForceUseJit() {
x86.SetCompiler(makeEncoderX86)
pretouchType = pretouchTypeX86
encodeTypedPointer = x86.EncodeTypedPointer
vars.UseVM = false
}
func init() {
if vars.UseVM {
ForceUseVM()
} else {
ForceUseJit()
}
}
var _KeepAlive struct {
rb *[]byte
vp unsafe.Pointer
sb *vars.Stack
fv uint64
err error
frame [x86.FP_offs]byte
}
var errCallShadow = errors.New("DON'T CALL THIS!")
// Faker func of _Encoder, used to export its stackmap as _Encoder's
func _Encoder_Shadow(rb *[]byte, vp unsafe.Pointer, sb *vars.Stack, fv uint64) (err error) {
// align to assembler_amd64.go: x86.FP_offs
var frame [x86.FP_offs]byte
// must keep all args and frames noticeable to GC
_KeepAlive.rb = rb
_KeepAlive.vp = vp
_KeepAlive.sb = sb
_KeepAlive.fv = fv
_KeepAlive.err = err
_KeepAlive.frame = frame
return errCallShadow
}
func makeEncoderX86(vt *rt.GoType, ex ...interface{}) (interface{}, error) {
pp, err := NewCompiler().Compile(vt.Pack(), ex[0].(bool))
if err != nil {
return nil, err
}
as := x86.NewAssembler(pp)
as.Name = vt.String()
return as.Load(), nil
}
func pretouchTypeX86(_vt reflect.Type, opts option.CompileOptions, v uint8) (map[reflect.Type]uint8, error) {
/* compile function */
compiler := NewCompiler().apply(opts)
/* find or compile */
vt := rt.UnpackType(_vt)
if val := vars.GetProgram(vt); val != nil {
return nil, nil
} else if _, err := vars.ComputeProgram(vt, makeEncoderX86, v == 1); err == nil {
return compiler.rec, nil
} else {
return nil, err
}
}
+24
View File
@@ -0,0 +1,24 @@
//go:build !amd64
// +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 encoder
func init() {
ForceUseVM()
}
-167
View File
@@ -1,167 +0,0 @@
/*
* 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 encoder
import (
`encoding`
`encoding/json`
`unsafe`
`github.com/bytedance/sonic/internal/jit`
`github.com/bytedance/sonic/internal/native`
`github.com/bytedance/sonic/internal/native/types`
`github.com/bytedance/sonic/internal/rt`
)
/** Encoder Primitives **/
func encodeNil(rb *[]byte) error {
*rb = append(*rb, 'n', 'u', 'l', 'l')
return nil
}
func encodeString(buf *[]byte, val string) error {
var sidx int
var pbuf *rt.GoSlice
var pstr *rt.GoString
/* opening quote */
*buf = append(*buf, '"')
pbuf = (*rt.GoSlice)(unsafe.Pointer(buf))
pstr = (*rt.GoString)(unsafe.Pointer(&val))
/* encode with native library */
for sidx < pstr.Len {
sn := pstr.Len - sidx
dn := pbuf.Cap - pbuf.Len
sp := padd(pstr.Ptr, sidx)
dp := padd(pbuf.Ptr, pbuf.Len)
nb := native.Quote(sp, sn, dp, &dn, 0)
/* check for errors */
if pbuf.Len += dn; nb >= 0 {
break
}
/* not enough space, grow the slice and try again */
sidx += ^nb
*pbuf = rt.GrowSlice(rt.UnpackType(byteType), *pbuf, pbuf.Cap * 2)
}
/* closing quote */
*buf = append(*buf, '"')
return nil
}
func encodeTypedPointer(buf *[]byte, vt *rt.GoType, vp *unsafe.Pointer, sb *_Stack, fv uint64) error {
if vt == nil {
return encodeNil(buf)
} else if fn, err := findOrCompile(vt, (fv&(1<<bitPointerValue)) != 0); err != nil {
return err
} else if vt.Indirect() {
rt.MoreStack(_FP_size + native.MaxFrameSize)
err := fn(buf, *vp, sb, fv)
return err
} else {
rt.MoreStack(_FP_size + native.MaxFrameSize)
err := fn(buf, unsafe.Pointer(vp), sb, fv)
return err
}
}
func encodeJsonMarshaler(buf *[]byte, val json.Marshaler, opt Options) error {
if ret, err := val.MarshalJSON(); err != nil {
return err
} else {
if opt & CompactMarshaler != 0 {
return compact(buf, ret)
}
if opt & NoValidateJSONMarshaler == 0 {
if ok, s := Valid(ret); !ok {
return error_marshaler(ret, s)
}
}
*buf = append(*buf, ret...)
return nil
}
}
func encodeTextMarshaler(buf *[]byte, val encoding.TextMarshaler, opt Options) error {
if ret, err := val.MarshalText(); err != nil {
return err
} else {
if opt & NoQuoteTextMarshaler != 0 {
*buf = append(*buf, ret...)
return nil
}
return encodeString(buf, rt.Mem2Str(ret) )
}
}
func htmlEscape(dst []byte, src []byte) []byte {
var sidx int
dst = append(dst, src[:0]...) // avoid check nil dst
sbuf := (*rt.GoSlice)(unsafe.Pointer(&src))
dbuf := (*rt.GoSlice)(unsafe.Pointer(&dst))
/* grow dst if it is shorter */
if cap(dst) - len(dst) < len(src) + types.BufPaddingSize {
cap := len(src) * 3 / 2 + types.BufPaddingSize
*dbuf = rt.GrowSlice(typeByte, *dbuf, cap)
}
for sidx < sbuf.Len {
sp := padd(sbuf.Ptr, sidx)
dp := padd(dbuf.Ptr, dbuf.Len)
sn := sbuf.Len - sidx
dn := dbuf.Cap - dbuf.Len
nb := native.HTMLEscape(sp, sn, dp, &dn)
/* check for errors */
if dbuf.Len += dn; nb >= 0 {
break
}
/* not enough space, grow the slice and try again */
sidx += ^nb
*dbuf = rt.GrowSlice(typeByte, *dbuf, dbuf.Cap * 2)
}
return dst
}
var (
argPtrs = []bool { true, true, true, false }
localPtrs = []bool{}
)
var (
_F_assertI2I = jit.Func(rt.AssertI2I2)
)
func asText(v unsafe.Pointer) (string, error) {
text := rt.AssertI2I2(_T_encoding_TextMarshaler, *(*rt.GoIface)(v))
r, e := (*(*encoding.TextMarshaler)(unsafe.Pointer(&text))).MarshalText()
return rt.Mem2Str(r), e
}
func asJson(v unsafe.Pointer) (string, error) {
text := rt.AssertI2I2(_T_json_Marshaler, *(*rt.GoIface)(v))
r, e := (*(*json.Marshaler)(unsafe.Pointer(&text))).MarshalJSON()
return rt.Mem2Str(r), e
}
+15 -13
View File
@@ -17,8 +17,10 @@
package encoder
import (
`encoding/json`
`io`
"encoding/json"
"io"
"github.com/bytedance/sonic/internal/encoder/vars"
)
// StreamEncoder uses io.Writer as input.
@@ -36,21 +38,20 @@ func NewStreamEncoder(w io.Writer) *StreamEncoder {
// Encode encodes interface{} as JSON to io.Writer
func (enc *StreamEncoder) Encode(val interface{}) (err error) {
buf := newBytes()
out := buf
out := vars.NewBytes()
/* encode into the buffer */
err = EncodeInto(&out, val, enc.Opts)
err = EncodeInto(out, val, enc.Opts)
if err != nil {
goto free_bytes
}
if enc.indent != "" || enc.prefix != "" {
/* indent the JSON */
buf := newBuffer()
err = json.Indent(buf, out, enc.prefix, enc.indent)
buf := vars.NewBuffer()
err = json.Indent(buf, *out, enc.prefix, enc.indent)
if err != nil {
freeBuffer(buf)
vars.FreeBuffer(buf)
goto free_bytes
}
@@ -62,16 +63,17 @@ func (enc *StreamEncoder) Encode(val interface{}) (err error) {
/* copy into io.Writer */
_, err = io.Copy(enc.w, buf)
if err != nil {
freeBuffer(buf)
vars.FreeBuffer(buf)
goto free_bytes
}
} else {
/* copy into io.Writer */
var n int
for len(out) > 0 {
n, err = enc.w.Write(out)
out = out[n:]
buf := *out
for len(buf) > 0 {
n, err = enc.w.Write(buf)
buf = buf[n:]
if err != nil {
goto free_bytes
}
@@ -84,6 +86,6 @@ func (enc *StreamEncoder) Encode(val interface{}) (err error) {
}
free_bytes:
freeBytes(buf)
vars.FreeBytes(out)
return err
}
-57
View File
@@ -1,57 +0,0 @@
// +build go1.16,!go1.17
/*
* 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 encoder
import (
`unsafe`
_ `github.com/cloudwego/base64x`
`github.com/bytedance/sonic/internal/rt`
)
//go:linkname _subr__b64encode github.com/cloudwego/base64x._subr__b64encode
var _subr__b64encode uintptr
//go:noescape
//go:linkname memmove runtime.memmove
//goland:noinspection GoUnusedParameter
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
//go:linkname mapiternext runtime.mapiternext
//goland:noinspection GoUnusedParameter
func mapiternext(it *rt.GoMapIterator)
//go:linkname mapiterinit runtime.mapiterinit
//goland:noinspection GoUnusedParameter
func mapiterinit(t *rt.GoMapType, m *rt.GoMap, it *rt.GoMapIterator)
//go:linkname isValidNumber encoding/json.isValidNumber
//goland:noinspection GoUnusedParameter
func isValidNumber(s string) bool
//go:noescape
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
//goland:noinspection GoUnusedParameter
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
var _runtime_writeBarrier uintptr = rt.GcwbAddr()
//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
func gcWriteBarrierAX()
-58
View File
@@ -1,58 +0,0 @@
// +build go1.17,!go1.20
/*
* 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 encoder
import (
`unsafe`
_ `github.com/cloudwego/base64x`
`github.com/bytedance/sonic/internal/rt`
)
//go:linkname _subr__b64encode github.com/cloudwego/base64x._subr__b64encode
var _subr__b64encode uintptr
//go:noescape
//go:linkname memmove runtime.memmove
//goland:noinspection GoUnusedParameter
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
//go:linkname mapiternext runtime.mapiternext
//goland:noinspection GoUnusedParameter
func mapiternext(it *rt.GoMapIterator)
//go:linkname mapiterinit runtime.mapiterinit
//goland:noinspection GoUnusedParameter
func mapiterinit(t *rt.GoMapType, m *rt.GoMap, it *rt.GoMapIterator)
//go:linkname isValidNumber encoding/json.isValidNumber
//goland:noinspection GoUnusedParameter
func isValidNumber(s string) bool
//go:noescape
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
//goland:noinspection GoUnusedParameter
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
//go:linkname _runtime_writeBarrier runtime.writeBarrier
var _runtime_writeBarrier uintptr
//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
func gcWriteBarrierAX()
-58
View File
@@ -1,58 +0,0 @@
// +build go1.20,!go1.21
/*
* 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 encoder
import (
`unsafe`
_ `github.com/cloudwego/base64x`
`github.com/bytedance/sonic/internal/rt`
)
//go:linkname _subr__b64encode github.com/cloudwego/base64x._subr__b64encode
var _subr__b64encode uintptr
//go:noescape
//go:linkname memmove runtime.memmove
//goland:noinspection GoUnusedParameter
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
//go:linkname mapiternext runtime.mapiternext
//goland:noinspection GoUnusedParameter
func mapiternext(it *rt.GoMapIterator)
//go:linkname mapiterinit runtime.mapiterinit
//goland:noinspection GoUnusedParameter
func mapiterinit(t *rt.GoMapType, m *rt.GoMap, it *rt.GoMapIterator)
//go:linkname isValidNumber encoding/json.isValidNumber
//goland:noinspection GoUnusedParameter
func isValidNumber(s string) bool
//go:noescape
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
//goland:noinspection GoUnusedParameter
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
//go:linkname _runtime_writeBarrier runtime.writeBarrier
var _runtime_writeBarrier uintptr
//go:linkname gcWriteBarrierAX runtime.gcWriteBarrier
func gcWriteBarrierAX()
-58
View File
@@ -1,58 +0,0 @@
// +build go1.21
/*
* 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 encoder
import (
`unsafe`
_ `github.com/cloudwego/base64x`
`github.com/bytedance/sonic/internal/rt`
)
//go:linkname _subr__b64encode github.com/cloudwego/base64x._subr__b64encode
var _subr__b64encode uintptr
//go:noescape
//go:linkname memmove runtime.memmove
//goland:noinspection GoUnusedParameter
func memmove(to unsafe.Pointer, from unsafe.Pointer, n uintptr)
//go:linkname mapiternext runtime.mapiternext
//goland:noinspection GoUnusedParameter
func mapiternext(it *rt.GoMapIterator)
//go:linkname mapiterinit runtime.mapiterinit
//goland:noinspection GoUnusedParameter
func mapiterinit(t *rt.GoMapType, m *rt.GoMap, it *rt.GoMapIterator)
//go:linkname isValidNumber encoding/json.isValidNumber
//goland:noinspection GoUnusedParameter
func isValidNumber(s string) bool
//go:noescape
//go:linkname memclrNoHeapPointers runtime.memclrNoHeapPointers
//goland:noinspection GoUnusedParameter
func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
//go:linkname _runtime_writeBarrier runtime.writeBarrier
var _runtime_writeBarrier uintptr
//go:linkname gcWriteBarrier2 runtime.gcWriteBarrier2
func gcWriteBarrier2()
-52
View File
@@ -1,52 +0,0 @@
/*
* 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 encoder
import (
`encoding/json`
`unsafe`
`github.com/bytedance/sonic/loader`
)
//go:nosplit
func padd(p unsafe.Pointer, v int) unsafe.Pointer {
return unsafe.Pointer(uintptr(p) + uintptr(v))
}
//go:nosplit
func ptoenc(p loader.Function) _Encoder {
return *(*_Encoder)(unsafe.Pointer(&p))
}
func compact(p *[]byte, v []byte) error {
buf := newBuffer()
err := json.Compact(buf, v)
/* check for errors */
if err != nil {
return err
}
/* add to result */
v = buf.Bytes()
*p = append(*p, v...)
/* return the buffer into pool */
freeBuffer(buf)
return nil
}
+48
View File
@@ -0,0 +1,48 @@
/*
* 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 vars
import (
"unsafe"
"github.com/bytedance/sonic/internal/rt"
)
type Encoder func(
rb *[]byte,
vp unsafe.Pointer,
sb *Stack,
fv uint64,
) error
func FindOrCompile(vt *rt.GoType, pv bool, compiler func(*rt.GoType, ... interface{}) (interface{}, error)) (interface{}, error) {
if val := programCache.Get(vt); val != nil {
return val, nil
} else if ret, err := programCache.Compute(vt, compiler, pv); err == nil {
return ret, nil
} else {
return nil, err
}
}
func GetProgram(vt *rt.GoType) (interface{}) {
return programCache.Get(vt)
}
func ComputeProgram(vt *rt.GoType, compute func(*rt.GoType, ... interface{}) (interface{}, error), pv bool) (interface{}, error) {
return programCache.Compute(vt, compute, pv)
}
+42
View File
@@ -0,0 +1,42 @@
/**
* Copyright 2024 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 vars
import (
"os"
"unsafe"
)
const (
MaxStack = 4096 // 4k states
StackSize = unsafe.Sizeof(Stack{})
StateSize = int64(unsafe.Sizeof(State{}))
StackLimit = MaxStack * StateSize
)
const (
MAX_ILBUF = 100000 // cutoff at 100k of IL instructions
MAX_FIELDS = 50 // cutoff at 50 fields struct
)
var (
DebugSyncGC = os.Getenv("SONIC_SYNC_GC") != ""
DebugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
DebugCheckPtr = os.Getenv("SONIC_CHECK_POINTER") != ""
)
var UseVM = os.Getenv("SONIC_ENCODER_USE_VM") != ""
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package encoder
package vars
import (
`encoding/json`
@@ -26,39 +26,39 @@ import (
`github.com/bytedance/sonic/internal/rt`
)
var _ERR_too_deep = &json.UnsupportedValueError {
var ERR_too_deep = &json.UnsupportedValueError {
Str : "Value nesting too deep",
Value : reflect.ValueOf("..."),
}
var _ERR_nan_or_infinite = &json.UnsupportedValueError {
var ERR_nan_or_infinite = &json.UnsupportedValueError {
Str : "NaN or ±Infinite",
Value : reflect.ValueOf("NaN or ±Infinite"),
}
func error_type(vtype reflect.Type) error {
func Error_type(vtype reflect.Type) error {
return &json.UnsupportedTypeError{Type: vtype}
}
func error_number(number json.Number) error {
func Error_number(number json.Number) error {
return &json.UnsupportedValueError {
Str : "invalid number literal: " + strconv.Quote(string(number)),
Value : reflect.ValueOf(number),
}
}
func error_marshaler(ret []byte, pos int) error {
func Error_marshaler(ret []byte, pos int) error {
return fmt.Errorf("invalid Marshaler output json syntax at %d: %q", pos, ret)
}
const (
panicNilPointerOfNonEmptyString int = 1 + iota
PanicNilPointerOfNonEmptyString int = 1 + iota
)
func goPanic(code int, val unsafe.Pointer) {
func GoPanic(code int, val unsafe.Pointer) {
switch(code){
case panicNilPointerOfNonEmptyString:
panic(fmt.Sprintf("val: %#v has nil pointer while its length is not zero!", (*rt.GoString)(val)))
case PanicNilPointerOfNonEmptyString:
panic(fmt.Sprintf("val: %#v has nil pointer while its length is not zero!\nThis is a nil pointer exception (NPE) problem. There might be a data race issue. It is recommended to execute the tests related to the code with the `-race` compile flag to detect the problem.", (*rt.GoString)(val)))
default:
panic("encoder error!")
}
+146
View File
@@ -0,0 +1,146 @@
/**
* Copyright 2024 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 vars
import (
"bytes"
"sync"
"unsafe"
"github.com/bytedance/sonic/internal/caching"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/option"
)
type State struct {
x int
f uint64
p unsafe.Pointer
q unsafe.Pointer
}
type Stack struct {
sp uintptr
sb [MaxStack]State
}
var (
bytesPool = sync.Pool{}
stackPool = sync.Pool{
New: func() interface{} {
return &Stack{}
},
}
bufferPool = sync.Pool{}
programCache = caching.CreateProgramCache()
)
func NewBytes() *[]byte {
if ret := bytesPool.Get(); ret != nil {
return ret.(*[]byte)
} else {
ret := make([]byte, 0, option.DefaultEncoderBufferSize)
return &ret
}
}
func NewStack() *Stack {
ret := stackPool.Get().(*Stack)
ret.sp = 0
return ret
}
func ResetStack(p *Stack) {
rt.MemclrNoHeapPointers(unsafe.Pointer(p), StackSize)
}
func (s *Stack) Top() *State {
return (*State)(rt.Add(unsafe.Pointer(&s.sb[0]), s.sp))
}
func (s *Stack) Cur() *State {
return (*State)(rt.Add(unsafe.Pointer(&s.sb[0]), s.sp - uintptr(StateSize)))
}
const _MaxStackSP = uintptr(MaxStack * StateSize)
func (s *Stack) Push(v State) bool {
if uintptr(s.sp) >= _MaxStackSP {
return false
}
st := s.Top()
*st = v
s.sp += uintptr(StateSize)
return true
}
func (s *Stack) Pop() State {
s.sp -= uintptr(StateSize)
st := s.Top()
ret := *st
*st = State{}
return ret
}
func (s *Stack) Load() (int, uint64, unsafe.Pointer, unsafe.Pointer) {
st := s.Cur()
return st.x, st.f, st.p, st.q
}
func (s *Stack) Save(x int, f uint64, p unsafe.Pointer, q unsafe.Pointer) bool {
return s.Push(State{x: x, f:f, p: p, q: q})
}
func (s *Stack) Drop() (int, uint64, unsafe.Pointer, unsafe.Pointer) {
st := s.Pop()
return st.x, st.f, st.p, st.q
}
func NewBuffer() *bytes.Buffer {
if ret := bufferPool.Get(); ret != nil {
return ret.(*bytes.Buffer)
} else {
return bytes.NewBuffer(make([]byte, 0, option.DefaultEncoderBufferSize))
}
}
func FreeBytes(p *[]byte) {
if rt.CanSizeResue(cap(*p)) {
(*p) = (*p)[:0]
bytesPool.Put(p)
}
}
func FreeStack(p *Stack) {
p.sp = 0
stackPool.Put(p)
}
func FreeBuffer(p *bytes.Buffer) {
if rt.CanSizeResue(cap(p.Bytes())) {
p.Reset()
bufferPool.Put(p)
}
}
var (
ArgPtrs = []bool{true, true, true, false}
LocalPtrs = []bool{}
ArgPtrs_generic = []bool{true}
LocalPtrs_generic = []bool{}
)
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package encoder
package vars
import (
`encoding`
@@ -23,19 +23,19 @@ import (
)
var (
byteType = reflect.TypeOf(byte(0))
jsonNumberType = reflect.TypeOf(json.Number(""))
jsonUnsupportedValueType = reflect.TypeOf(new(json.UnsupportedValueError))
ByteType = reflect.TypeOf(byte(0))
JsonNumberType = reflect.TypeOf(json.Number(""))
JsonUnsupportedValueType = reflect.TypeOf(new(json.UnsupportedValueError))
)
var (
errorType = reflect.TypeOf((*error)(nil)).Elem()
jsonMarshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
encodingTextMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
ErrorType = reflect.TypeOf((*error)(nil)).Elem()
JsonMarshalerType = reflect.TypeOf((*json.Marshaler)(nil)).Elem()
EncodingTextMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
)
func isSimpleByte(vt reflect.Type) bool {
if vt.Kind() != byteType.Kind() {
func IsSimpleByte(vt reflect.Type) bool {
if vt.Kind() != ByteType.Kind() {
return false
} else {
return !isEitherMarshaler(vt) && !isEitherMarshaler(reflect.PtrTo(vt))
@@ -43,5 +43,5 @@ func isSimpleByte(vt reflect.Type) bool {
}
func isEitherMarshaler(vt reflect.Type) bool {
return vt.Implements(jsonMarshalerType) || vt.Implements(encodingTextMarshalerType)
return vt.Implements(JsonMarshalerType) || vt.Implements(EncodingTextMarshalerType)
}
+45
View File
@@ -0,0 +1,45 @@
/**
* Copyright 2024 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 vm
import (
"unsafe"
_ "unsafe"
"github.com/bytedance/sonic/internal/encoder/alg"
"github.com/bytedance/sonic/internal/encoder/ir"
"github.com/bytedance/sonic/internal/encoder/vars"
"github.com/bytedance/sonic/internal/rt"
)
func EncodeTypedPointer(buf *[]byte, vt *rt.GoType, vp *unsafe.Pointer, sb *vars.Stack, fv uint64) error {
if vt == nil {
return alg.EncodeNil(buf)
} else if pp, err := vars.FindOrCompile(vt, (fv&(1<<alg.BitPointerValue)) != 0, compiler); err != nil {
return err
} else if vt.Indirect() {
return Execute(buf, *vp, sb, fv, pp.(*ir.Program))
} else {
return Execute(buf, unsafe.Pointer(vp), sb, fv, pp.(*ir.Program))
}
}
var compiler func(*rt.GoType, ... interface{}) (interface{}, error)
func SetCompiler(c func(*rt.GoType, ... interface{}) (interface{}, error)) {
compiler = c
}
+374
View File
@@ -0,0 +1,374 @@
// Copyright 2024 CloudWeGo Authors
//
// 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 vm
import (
"encoding"
"encoding/json"
"fmt"
"math"
"reflect"
"unsafe"
"github.com/bytedance/sonic/internal/encoder/alg"
"github.com/bytedance/sonic/internal/encoder/ir"
"github.com/bytedance/sonic/internal/encoder/vars"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/internal/base64"
)
const (
_S_cond = iota
_S_init
)
var (
_T_json_Marshaler = rt.UnpackType(vars.JsonMarshalerType)
_T_encoding_TextMarshaler = rt.UnpackType(vars.EncodingTextMarshalerType)
)
func print_instr(buf []byte, pc int, op ir.Op, ins *ir.Instr, p unsafe.Pointer) {
if len(buf) > 20 {
fmt.Println(string(buf[len(buf)-20:]))
} else {
fmt.Println(string(buf))
}
fmt.Printf("pc %04d, op %v, ins %#v, ptr: %x\n", pc, op, ins.Disassemble(), p)
}
func Execute(b *[]byte, p unsafe.Pointer, s *vars.Stack, flags uint64, prog *ir.Program) (error) {
pl := len(*prog)
if pl <= 0 {
return nil
}
var buf = *b
var x int
var q unsafe.Pointer
var f uint64
var pro = &(*prog)[0]
for pc := 0; pc < pl; {
ins := (*ir.Instr)(rt.Add(unsafe.Pointer(pro), ir.OpSize*uintptr(pc)))
pc++
op := ins.Op()
switch op {
case ir.OP_goto:
pc = ins.Vi()
continue
case ir.OP_byte:
v := ins.Byte()
buf = append(buf, v)
case ir.OP_text:
v := ins.Vs()
buf = append(buf, v...)
case ir.OP_deref:
p = *(*unsafe.Pointer)(p)
case ir.OP_index:
p = rt.Add(p, uintptr(ins.I64()))
case ir.OP_load:
// NOTICE: load CANNOT change f!
x, _, p, q = s.Load()
case ir.OP_save:
if !s.Save(x, f, p, q) {
return vars.ERR_too_deep
}
case ir.OP_drop:
x, f, p, q = s.Drop()
case ir.OP_drop_2:
s.Drop()
x, f, p, q = s.Drop()
case ir.OP_recurse:
vt, pv := ins.Vp2()
f := flags
if pv {
f |= (1 << alg.BitPointerValue)
}
*b = buf
if vt.Indirect() {
if err := EncodeTypedPointer(b, vt, (*unsafe.Pointer)(rt.NoEscape(unsafe.Pointer(&p))), s, f); err != nil {
return err
}
} else {
vp := (*unsafe.Pointer)(p)
if err := EncodeTypedPointer(b, vt, vp, s, f); err != nil {
return err
}
}
buf = *b
case ir.OP_is_nil:
if is_nil(p) {
pc = ins.Vi()
continue
}
case ir.OP_is_nil_p1:
if (*rt.GoEface)(p).Value == nil {
pc = ins.Vi()
continue
}
case ir.OP_null:
buf = append(buf, 'n', 'u', 'l', 'l')
case ir.OP_str:
v := *(*string)(p)
buf = alg.Quote(buf, v, false)
case ir.OP_bool:
if *(*bool)(p) {
buf = append(buf, 't', 'r', 'u', 'e')
} else {
buf = append(buf, 'f', 'a', 'l', 's', 'e')
}
case ir.OP_i8:
v := *(*int8)(p)
buf = alg.I64toa(buf, int64(v))
case ir.OP_i16:
v := *(*int16)(p)
buf = alg.I64toa(buf, int64(v))
case ir.OP_i32:
v := *(*int32)(p)
buf = alg.I64toa(buf, int64(v))
case ir.OP_i64:
v := *(*int64)(p)
buf = alg.I64toa(buf, int64(v))
case ir.OP_u8:
v := *(*uint8)(p)
buf = alg.U64toa(buf, uint64(v))
case ir.OP_u16:
v := *(*uint16)(p)
buf = alg.U64toa(buf, uint64(v))
case ir.OP_u32:
v := *(*uint32)(p)
buf = alg.U64toa(buf, uint64(v))
case ir.OP_u64:
v := *(*uint64)(p)
buf = alg.U64toa(buf, uint64(v))
case ir.OP_f32:
v := *(*float32)(p)
if math.IsNaN(float64(v)) || math.IsInf(float64(v), 0) {
if flags&(1<<alg.BitEncodeNullForInfOrNan) != 0 {
buf = append(buf, 'n', 'u', 'l', 'l')
continue
}
return vars.ERR_nan_or_infinite
}
buf = alg.F32toa(buf, v)
case ir.OP_f64:
v := *(*float64)(p)
if math.IsNaN(v) || math.IsInf(v, 0) {
if flags&(1<<alg.BitEncodeNullForInfOrNan) != 0 {
buf = append(buf, 'n', 'u', 'l', 'l')
continue
}
return vars.ERR_nan_or_infinite
}
buf = alg.F64toa(buf, v)
case ir.OP_bin:
v := *(*[]byte)(p)
buf = base64.EncodeBase64(buf, v)
case ir.OP_quote:
v := *(*string)(p)
buf = alg.Quote(buf, v, true)
case ir.OP_number:
v := *(*json.Number)(p)
if v == "" {
buf = append(buf, '0')
} else if !rt.IsValidNumber(string(v)) {
return vars.Error_number(v)
} else {
buf = append(buf, v...)
}
case ir.OP_eface:
*b = buf
if err := EncodeTypedPointer(b, *(**rt.GoType)(p), (*unsafe.Pointer)(rt.Add(p, 8)), s, flags); err != nil {
return err
}
buf = *b
case ir.OP_iface:
*b = buf
if err := EncodeTypedPointer(b, (*(**rt.GoItab)(p)).Vt, (*unsafe.Pointer)(rt.Add(p, 8)), s, flags); err != nil {
return err
}
buf = *b
case ir.OP_is_zero_map:
v := *(**rt.GoMap)(p)
if v == nil || v.Count == 0 {
pc = ins.Vi()
continue
}
case ir.OP_map_iter:
v := *(**rt.GoMap)(p)
vt := ins.Vr()
it, err := alg.IteratorStart(rt.MapType(vt), v, flags)
if err != nil {
return err
}
q = unsafe.Pointer(it)
case ir.OP_map_stop:
it := (*alg.MapIterator)(q)
alg.IteratorStop(it)
q = nil
case ir.OP_map_value_next:
it := (*alg.MapIterator)(q)
p = it.It.V
alg.IteratorNext(it)
case ir.OP_map_check_key:
it := (*alg.MapIterator)(q)
if it.It.K == nil {
pc = ins.Vi()
continue
}
p = it.It.K
case ir.OP_marshal_text:
vt, itab := ins.Vtab()
var it rt.GoIface
switch vt.Kind() {
case reflect.Interface :
if is_nil(p) {
buf = append(buf, 'n', 'u', 'l', 'l')
continue
}
it = rt.AssertI2I(_T_encoding_TextMarshaler, *(*rt.GoIface)(p))
case reflect.Ptr, reflect.Map : it = convT2I(p, true, itab)
default : it = convT2I(p, !vt.Indirect(), itab)
}
if err := alg.EncodeTextMarshaler(&buf, *(*encoding.TextMarshaler)(unsafe.Pointer(&it)), (flags)); err != nil {
return err
}
case ir.OP_marshal_text_p:
_, itab := ins.Vtab()
it := convT2I(p, false, itab)
if err := alg.EncodeTextMarshaler(&buf, *(*encoding.TextMarshaler)(unsafe.Pointer(&it)), (flags)); err != nil {
return err
}
case ir.OP_map_write_key:
if has_opts(flags, alg.BitSortMapKeys) {
v := *(*string)(p)
buf = alg.Quote(buf, v, false)
pc = ins.Vi()
continue
}
case ir.OP_slice_len:
v := (*rt.GoSlice)(p)
x = v.Len
p = v.Ptr
//TODO: why?
f |= 1<<_S_init
case ir.OP_slice_next:
if x == 0 {
pc = ins.Vi()
continue
}
x--
if has_opts(f, _S_init) {
f &= ^uint64(1 << _S_init)
} else {
p = rt.Add(p, uintptr(ins.Vlen()))
}
case ir.OP_cond_set:
f |= 1<<_S_cond
case ir.OP_cond_testc:
if has_opts(f, _S_cond) {
f &= ^uint64(1 << _S_cond)
pc = ins.Vi()
continue
}
case ir.OP_is_zero_1:
if *(*uint8)(p) == 0 {
pc = ins.Vi()
continue
}
case ir.OP_is_zero_2:
if *(*uint16)(p) == 0 {
pc = ins.Vi()
continue
}
case ir.OP_is_zero_4:
if *(*uint32)(p) == 0 {
pc = ins.Vi()
continue
}
case ir.OP_is_zero_8:
if *(*uint64)(p) == 0 {
pc = ins.Vi()
continue
}
case ir.OP_empty_arr:
if has_opts(flags, alg.BitNoNullSliceOrMap) {
buf = append(buf, '[', ']')
} else {
buf = append(buf, 'n', 'u', 'l', 'l')
}
case ir.OP_empty_obj:
if has_opts(flags, alg.BitNoNullSliceOrMap) {
buf = append(buf, '{', '}')
} else {
buf = append(buf, 'n', 'u', 'l', 'l')
}
case ir.OP_marshal:
vt, itab := ins.Vtab()
var it rt.GoIface
switch vt.Kind() {
case reflect.Interface :
if is_nil(p) {
buf = append(buf, 'n', 'u', 'l', 'l')
continue
}
it = rt.AssertI2I(_T_json_Marshaler, *(*rt.GoIface)(p))
case reflect.Ptr, reflect.Map : it = convT2I(p, true, itab)
default : it = convT2I(p, !vt.Indirect(), itab)
}
if err := alg.EncodeJsonMarshaler(&buf, *(*json.Marshaler)(unsafe.Pointer(&it)), (flags)); err != nil {
return err
}
case ir.OP_marshal_p:
_, itab := ins.Vtab()
it := convT2I(p, false, itab)
if err := alg.EncodeJsonMarshaler(&buf, *(*json.Marshaler)(unsafe.Pointer(&it)), (flags)); err != nil {
return err
}
default:
panic(fmt.Sprintf("not implement %s at %d", ins.Op().String(), pc))
}
}
*b = buf
return nil
}
// func to_buf(w unsafe.Pointer, l int, c int) []byte {
// return rt.BytesFrom(unsafe.Pointer(uintptr(w)-uintptr(l)), l, c)
// }
// func from_buf(buf []byte) (unsafe.Pointer, int, int) {
// return rt.IndexByte(buf, len(buf)), len(buf), cap(buf)
// }
func has_opts(opts uint64, bit int) bool {
return opts & (1<<bit) != 0
}
func is_nil(p unsafe.Pointer) bool {
return *(*unsafe.Pointer)(p) == nil
}
func convT2I(ptr unsafe.Pointer, deref bool, itab *rt.GoItab) (rt.GoIface) {
if deref {
ptr = *(*unsafe.Pointer)(ptr)
}
return rt.GoIface{
Itab: itab,
Value: ptr,
}
}
@@ -1,3 +1,4 @@
//go:build go1.17 && !go1.21
// +build go1.17,!go1.21
// Copyright 2023 CloudWeGo Authors
@@ -14,24 +15,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package encoder
package x86
import (
`strconv`
`unsafe`
"strconv"
"unsafe"
`github.com/bytedance/sonic/internal/jit`
`github.com/twitchyliquid64/golang-asm/obj`
`github.com/twitchyliquid64/golang-asm/obj/x86`
"github.com/bytedance/sonic/internal/jit"
"github.com/bytedance/sonic/internal/rt"
"github.com/twitchyliquid64/golang-asm/obj"
"github.com/twitchyliquid64/golang-asm/obj/x86"
)
var (
_V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&_runtime_writeBarrier))))
_V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&rt.RuntimeWriteBarrier))))
_F_gcWriteBarrierAX = jit.Func(gcWriteBarrierAX)
_F_gcWriteBarrierAX = jit.Func(rt.GcWriteBarrierAX)
)
func (self *_Assembler) WritePtr(i int, ptr obj.Addr, rec obj.Addr) {
func (self *Assembler) WritePtr(i int, ptr obj.Addr, rec obj.Addr) {
if rec.Reg == x86.REG_AX || rec.Index == x86.REG_AX {
panic("rec contains AX!")
}
Loaded 100 of 754 files, more files were not shown because too many files have changed in this diff. Show more