mirror of
https://github.com/gin-gonic/gin.git
synced 2025-12-11 19:47:00 +08:00
Fix SSE specification compliance - add space after colon in all SSE fields
Co-authored-by: appleboy <21979+appleboy@users.noreply.github.com>
This commit is contained in:
parent
c9d36c65b0
commit
d936a45bfd
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,3 @@
|
||||
vendor/*
|
||||
!vendor/vendor.json
|
||||
coverage.out
|
||||
count.out
|
||||
test
|
||||
|
||||
@ -1362,7 +1362,55 @@ func TestContextRenderSSE(t *testing.T) {
|
||||
"bar": "foo",
|
||||
})
|
||||
|
||||
assert.Equal(t, strings.ReplaceAll(w.Body.String(), " ", ""), strings.ReplaceAll("event:float\ndata:1.5\n\nid:123\ndata:text\n\nevent:chat\ndata:{\"bar\":\"foo\",\"foo\":\"bar\"}\n\n", " ", ""))
|
||||
assert.Equal(t, "event: float\ndata: 1.5\n\nid: 123\ndata: text\n\nevent: chat\ndata: {\"bar\":\"foo\",\"foo\":\"bar\"}\n\n", w.Body.String())
|
||||
}
|
||||
|
||||
// TestContextRenderSSEWithAllFields tests SSE rendering with all fields (id, event, retry, data)
|
||||
// and validates that spaces are present after colons per SSE specification
|
||||
func TestContextRenderSSEWithAllFields(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
|
||||
// Test with all fields including retry
|
||||
c.Render(-1, sse.Event{
|
||||
Id: "msg-1",
|
||||
Event: "update",
|
||||
Retry: 5000,
|
||||
Data: "hello",
|
||||
})
|
||||
|
||||
expected := "id: msg-1\nevent: update\nretry: 5000\ndata: hello\n\n"
|
||||
assert.Equal(t, expected, w.Body.String())
|
||||
|
||||
// Verify spaces are present after each colon
|
||||
assert.Contains(t, w.Body.String(), "id: ")
|
||||
assert.Contains(t, w.Body.String(), "event: ")
|
||||
assert.Contains(t, w.Body.String(), "retry: ")
|
||||
assert.Contains(t, w.Body.String(), "data: ")
|
||||
}
|
||||
|
||||
// TestContextRenderSSEMultiline tests SSE rendering with multiline data
|
||||
func TestContextRenderSSEMultiline(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := CreateTestContext(w)
|
||||
|
||||
c.Render(-1, sse.Event{
|
||||
Event: "message",
|
||||
Data: "line1\nline2\nline3",
|
||||
})
|
||||
|
||||
expected := "event: message\ndata: line1\ndata: line2\ndata: line3\n\n"
|
||||
assert.Equal(t, expected, w.Body.String())
|
||||
|
||||
// Verify each line has "data: " prefix with space
|
||||
lines := strings.Split(w.Body.String(), "\n")
|
||||
dataLines := 0
|
||||
for _, line := range lines {
|
||||
if strings.HasPrefix(line, "data: ") {
|
||||
dataLines++
|
||||
}
|
||||
}
|
||||
assert.Equal(t, 3, dataLines, "Should have 3 data lines with space after colon")
|
||||
}
|
||||
|
||||
func TestContextRenderFile(t *testing.T) {
|
||||
|
||||
201
vendor/github.com/bytedance/gopkg/LICENSE
generated
vendored
Normal file
201
vendor/github.com/bytedance/gopkg/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
43
vendor/github.com/bytedance/gopkg/lang/dirtmake/bytes.go
generated
vendored
Normal file
43
vendor/github.com/bytedance/gopkg/lang/dirtmake/bytes.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
// 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 dirtmake
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type slice struct {
|
||||
data unsafe.Pointer
|
||||
len int
|
||||
cap int
|
||||
}
|
||||
|
||||
//go:linkname mallocgc runtime.mallocgc
|
||||
func mallocgc(size uintptr, typ unsafe.Pointer, needzero bool) unsafe.Pointer
|
||||
|
||||
// Bytes allocates a byte slice but does not clean up the memory it references.
|
||||
// Throw a fatal error instead of panic if cap is greater than runtime.maxAlloc.
|
||||
// NOTE: MUST set any byte element before it's read.
|
||||
func Bytes(len, cap int) (b []byte) {
|
||||
if len < 0 || len > cap {
|
||||
panic("dirtmake.Bytes: len out of range")
|
||||
}
|
||||
p := mallocgc(uintptr(cap), nil, false)
|
||||
sh := (*slice)(unsafe.Pointer(&b))
|
||||
sh.data = p
|
||||
sh.len = len
|
||||
sh.cap = cap
|
||||
return
|
||||
}
|
||||
5
vendor/github.com/bytedance/sonic/.codespellrc
generated
vendored
Normal file
5
vendor/github.com/bytedance/sonic/.codespellrc
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
[codespell]
|
||||
# ignore test files, go project names, binary files via `skip` and special var/regex via `ignore-words`
|
||||
skip = fuzz,*_test.tmpl,testdata,*_test.go,go.mod,go.sum,*.gz
|
||||
ignore-words = .github/workflows/.ignore_words
|
||||
check-filenames = true
|
||||
55
vendor/github.com/bytedance/sonic/.gitignore
generated
vendored
Normal file
55
vendor/github.com/bytedance/sonic/.gitignore
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
*.o
|
||||
*.swp
|
||||
*.swm
|
||||
*.swn
|
||||
*.a
|
||||
*.so
|
||||
_obj
|
||||
_test
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
_testmain.go
|
||||
*.exe
|
||||
*.exe~
|
||||
*.test
|
||||
*.prof
|
||||
*.rar
|
||||
*.zip
|
||||
*.gz
|
||||
*.psd
|
||||
*.bmd
|
||||
*.cfg
|
||||
*.pptx
|
||||
*.log
|
||||
*nohup.out
|
||||
*settings.pyc
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.DS_Store
|
||||
/.idea/
|
||||
/.vscode/
|
||||
/output/
|
||||
/vendor/
|
||||
/Gopkg.lock
|
||||
/Gopkg.toml
|
||||
coverage.html
|
||||
coverage.out
|
||||
coverage.xml
|
||||
junit.xml
|
||||
*.profile
|
||||
*.svg
|
||||
*.out
|
||||
ast/test.out
|
||||
ast/bench.sh
|
||||
|
||||
!testdata/**/*.json.gz
|
||||
fuzz/testdata
|
||||
*__debug_bin*
|
||||
*pprof
|
||||
*coverage.txt
|
||||
tools/venv/*
|
||||
9
vendor/github.com/bytedance/sonic/.gitmodules
generated
vendored
Normal file
9
vendor/github.com/bytedance/sonic/.gitmodules
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
[submodule "cloudwego"]
|
||||
path = tools/asm2asm
|
||||
url = https://github.com/cloudwego/asm2asm.git
|
||||
[submodule "tools/simde"]
|
||||
path = tools/simde
|
||||
url = https://github.com/simd-everywhere/simde.git
|
||||
[submodule "fuzz/go-fuzz-corpus"]
|
||||
path = fuzz/go-fuzz-corpus
|
||||
url = https://github.com/dvyukov/go-fuzz-corpus.git
|
||||
24
vendor/github.com/bytedance/sonic/.licenserc.yaml
generated
vendored
Normal file
24
vendor/github.com/bytedance/sonic/.licenserc.yaml
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
header:
|
||||
license:
|
||||
spdx-id: Apache-2.0
|
||||
copyright-owner: ByteDance Inc.
|
||||
|
||||
paths:
|
||||
- '**/*.go'
|
||||
- '**/*.s'
|
||||
|
||||
paths-ignore:
|
||||
- 'ast/asm.s' # empty file
|
||||
- 'decoder/asm.s' # empty file
|
||||
- 'encoder/asm.s' # empty file
|
||||
- 'internal/caching/asm.s' # empty file
|
||||
- 'internal/jit/asm.s' # empty file
|
||||
- 'internal/native/avx/native_amd64.s' # auto-generated by asm2asm
|
||||
- 'internal/native/avx/native_subr_amd64.go' # auto-generated by asm2asm
|
||||
- 'internal/native/avx2/native_amd64.s' # auto-generated by asm2asm
|
||||
- 'internal/native/avx2/native_subr_amd64.go' # auto-generated by asm2asm
|
||||
- 'internal/resolver/asm.s' # empty file
|
||||
- 'internal/rt/asm.s' # empty file
|
||||
- 'internal/loader/asm.s' # empty file
|
||||
|
||||
comment: on-failure
|
||||
128
vendor/github.com/bytedance/sonic/CODE_OF_CONDUCT.md
generated
vendored
Normal file
128
vendor/github.com/bytedance/sonic/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
wudi.daniel@bytedance.com.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
63
vendor/github.com/bytedance/sonic/CONTRIBUTING.md
generated
vendored
Normal file
63
vendor/github.com/bytedance/sonic/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
# How to Contribute
|
||||
|
||||
## Your First Pull Request
|
||||
We use GitHub for our codebase. You can start by reading [How To Pull Request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests).
|
||||
|
||||
## Without Semantic Versioning
|
||||
We keep the stable code in branch `main` like `golang.org/x`. Development base on branch `develop`. We promise the **Forward Compatibility** by adding new package directory with suffix `v2/v3` when code has break changes.
|
||||
|
||||
## Branch Organization
|
||||
We use [git-flow](https://nvie.com/posts/a-successful-git-branching-model/) as our branch organization, as known as [FDD](https://en.wikipedia.org/wiki/Feature-driven_development)
|
||||
|
||||
|
||||
## Bugs
|
||||
### 1. How to Find Known Issues
|
||||
We are using [Github Issues](https://github.com/bytedance/sonic/issues) for our public bugs. We keep a close eye on this and try to make it clear when we have an internal fix in progress. Before filing a new task, try to make sure your problem doesn’t already exist.
|
||||
|
||||
### 2. Reporting New Issues
|
||||
Providing a reduced test code is a recommended way for reporting issues. Then can be placed in:
|
||||
- Just in issues
|
||||
- [Golang Playground](https://play.golang.org/)
|
||||
|
||||
### 3. Security Bugs
|
||||
Please do not report the safe disclosure of bugs to public issues. Contact us by [Support Email](mailto:sonic@bytedance.com)
|
||||
|
||||
## How to Get in Touch
|
||||
- [Email](mailto:wudi.daniel@bytedance.com)
|
||||
|
||||
## Submit a Pull Request
|
||||
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
1. Search [GitHub](https://github.com/bytedance/sonic/pulls) for an open or closed PR that relates to your submission. You don't want to duplicate existing efforts.
|
||||
2. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add. Discussing the design upfront helps to ensure that we're ready to accept your work.
|
||||
3. [Fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the bytedance/sonic repo.
|
||||
4. In your forked repository, make your changes in a new git branch:
|
||||
```
|
||||
git checkout -b bugfix/security_bug develop
|
||||
```
|
||||
5. Create your patch, including appropriate test cases.
|
||||
6. Follow our [Style Guides](#code-style-guides).
|
||||
7. Commit your changes using a descriptive commit message that follows [AngularJS Git Commit Message Conventions](https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit).
|
||||
Adherence to these conventions is necessary because release notes will be automatically generated from these messages.
|
||||
8. Push your branch to GitHub:
|
||||
```
|
||||
git push origin bugfix/security_bug
|
||||
```
|
||||
9. In GitHub, send a pull request to `sonic:main`
|
||||
|
||||
Note: you must use one of `optimize/feature/bugfix/doc/ci/test/refactor` following a slash(`/`) as the branch prefix.
|
||||
|
||||
Your pr title and commit message should follow https://www.conventionalcommits.org/.
|
||||
|
||||
## Contribution Prerequisites
|
||||
- Our development environment keeps up with [Go Official](https://golang.org/project/).
|
||||
- You need fully checking with lint tools before submit your pull request. [gofmt](https://golang.org/pkg/cmd/gofmt/) & [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||
- You are familiar with [Github](https://github.com)
|
||||
- Maybe you need familiar with [Actions](https://github.com/features/actions)(our default workflow tool).
|
||||
|
||||
## Code Style Guides
|
||||
See [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments).
|
||||
|
||||
Good resources:
|
||||
- [Effective Go](https://golang.org/doc/effective_go)
|
||||
- [Pingcap General advice](https://pingcap.github.io/style-guide/general.html)
|
||||
- [Uber Go Style Guide](https://github.com/uber-go/guide/blob/master/style.md)
|
||||
0
vendor/github.com/bytedance/sonic/CREDITS
generated
vendored
Normal file
0
vendor/github.com/bytedance/sonic/CREDITS
generated
vendored
Normal file
201
vendor/github.com/bytedance/sonic/LICENSE
generated
vendored
Normal file
201
vendor/github.com/bytedance/sonic/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
501
vendor/github.com/bytedance/sonic/README.md
generated
vendored
Normal file
501
vendor/github.com/bytedance/sonic/README.md
generated
vendored
Normal file
@ -0,0 +1,501 @@
|
||||
# Sonic
|
||||
|
||||
English | [中文](README_ZH_CN.md)
|
||||
|
||||
A blazingly fast JSON serializing & deserializing library, accelerated by JIT (just-in-time compiling) and SIMD (single-instruction-multiple-data).
|
||||
|
||||
## Requirement
|
||||
|
||||
- Go: 1.18~1.25
|
||||
- Notice: Go1.24.0 is not supported due to the [issue](https://github.com/golang/go/issues/71672), please use higher go version or add build tag `--ldflags="-checklinkname=0"`
|
||||
- OS: Linux / MacOS / Windows
|
||||
- CPU: AMD64 / (ARM64, need go1.20 above)
|
||||
|
||||
## Features
|
||||
|
||||
- Runtime object binding without code generation
|
||||
- Complete APIs for JSON value manipulation
|
||||
- Fast, fast, fast!
|
||||
|
||||
## APIs
|
||||
|
||||
see [go.dev](https://pkg.go.dev/github.com/bytedance/sonic)
|
||||
|
||||
## Benchmarks
|
||||
|
||||
For **all sizes** of json and **all scenarios** of usage, **Sonic performs best**.
|
||||
|
||||
- [Medium](https://github.com/bytedance/sonic/blob/main/decoder/testdata_test.go#L19) (13KB, 300+ key, 6 layers)
|
||||
|
||||
```powershell
|
||||
goversion: 1.17.1
|
||||
goos: darwin
|
||||
goarch: amd64
|
||||
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
|
||||
BenchmarkEncoder_Generic_Sonic-16 32393 ns/op 402.40 MB/s 11965 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Generic_Sonic_Fast-16 21668 ns/op 601.57 MB/s 10940 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Generic_JsonIter-16 42168 ns/op 309.12 MB/s 14345 B/op 115 allocs/op
|
||||
BenchmarkEncoder_Generic_GoJson-16 65189 ns/op 199.96 MB/s 23261 B/op 16 allocs/op
|
||||
BenchmarkEncoder_Generic_StdLib-16 106322 ns/op 122.60 MB/s 49136 B/op 789 allocs/op
|
||||
BenchmarkEncoder_Binding_Sonic-16 6269 ns/op 2079.26 MB/s 14173 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Binding_Sonic_Fast-16 5281 ns/op 2468.16 MB/s 12322 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Binding_JsonIter-16 20056 ns/op 649.93 MB/s 9488 B/op 2 allocs/op
|
||||
BenchmarkEncoder_Binding_GoJson-16 8311 ns/op 1568.32 MB/s 9481 B/op 1 allocs/op
|
||||
BenchmarkEncoder_Binding_StdLib-16 16448 ns/op 792.52 MB/s 9479 B/op 1 allocs/op
|
||||
BenchmarkEncoder_Parallel_Generic_Sonic-16 6681 ns/op 1950.93 MB/s 12738 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Parallel_Generic_Sonic_Fast-16 4179 ns/op 3118.99 MB/s 10757 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Parallel_Generic_JsonIter-16 9861 ns/op 1321.84 MB/s 14362 B/op 115 allocs/op
|
||||
BenchmarkEncoder_Parallel_Generic_GoJson-16 18850 ns/op 691.52 MB/s 23278 B/op 16 allocs/op
|
||||
BenchmarkEncoder_Parallel_Generic_StdLib-16 45902 ns/op 283.97 MB/s 49174 B/op 789 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_Sonic-16 1480 ns/op 8810.09 MB/s 13049 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_Sonic_Fast-16 1209 ns/op 10785.23 MB/s 11546 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_JsonIter-16 6170 ns/op 2112.58 MB/s 9504 B/op 2 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_GoJson-16 3321 ns/op 3925.52 MB/s 9496 B/op 1 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_StdLib-16 3739 ns/op 3486.49 MB/s 9480 B/op 1 allocs/op
|
||||
|
||||
BenchmarkDecoder_Generic_Sonic-16 66812 ns/op 195.10 MB/s 57602 B/op 723 allocs/op
|
||||
BenchmarkDecoder_Generic_Sonic_Fast-16 54523 ns/op 239.07 MB/s 49786 B/op 313 allocs/op
|
||||
BenchmarkDecoder_Generic_StdLib-16 124260 ns/op 104.90 MB/s 50869 B/op 772 allocs/op
|
||||
BenchmarkDecoder_Generic_JsonIter-16 91274 ns/op 142.81 MB/s 55782 B/op 1068 allocs/op
|
||||
BenchmarkDecoder_Generic_GoJson-16 88569 ns/op 147.17 MB/s 66367 B/op 973 allocs/op
|
||||
BenchmarkDecoder_Binding_Sonic-16 32557 ns/op 400.38 MB/s 28302 B/op 137 allocs/op
|
||||
BenchmarkDecoder_Binding_Sonic_Fast-16 28649 ns/op 455.00 MB/s 24999 B/op 34 allocs/op
|
||||
BenchmarkDecoder_Binding_StdLib-16 111437 ns/op 116.97 MB/s 10576 B/op 208 allocs/op
|
||||
BenchmarkDecoder_Binding_JsonIter-16 35090 ns/op 371.48 MB/s 14673 B/op 385 allocs/op
|
||||
BenchmarkDecoder_Binding_GoJson-16 28738 ns/op 453.59 MB/s 22039 B/op 49 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_Sonic-16 12321 ns/op 1057.91 MB/s 57233 B/op 723 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_Sonic_Fast-16 10644 ns/op 1224.64 MB/s 49362 B/op 313 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_StdLib-16 57587 ns/op 226.35 MB/s 50874 B/op 772 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_JsonIter-16 38666 ns/op 337.12 MB/s 55789 B/op 1068 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_GoJson-16 30259 ns/op 430.79 MB/s 66370 B/op 974 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_Sonic-16 5965 ns/op 2185.28 MB/s 27747 B/op 137 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_Sonic_Fast-16 5170 ns/op 2521.31 MB/s 24715 B/op 34 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_StdLib-16 27582 ns/op 472.58 MB/s 10576 B/op 208 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_JsonIter-16 13571 ns/op 960.51 MB/s 14685 B/op 385 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_GoJson-16 10031 ns/op 1299.51 MB/s 22111 B/op 49 allocs/op
|
||||
|
||||
BenchmarkGetOne_Sonic-16 3276 ns/op 3975.78 MB/s 24 B/op 1 allocs/op
|
||||
BenchmarkGetOne_Gjson-16 9431 ns/op 1380.81 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkGetOne_Jsoniter-16 51178 ns/op 254.46 MB/s 27936 B/op 647 allocs/op
|
||||
BenchmarkGetOne_Parallel_Sonic-16 216.7 ns/op 60098.95 MB/s 24 B/op 1 allocs/op
|
||||
BenchmarkGetOne_Parallel_Gjson-16 1076 ns/op 12098.62 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkGetOne_Parallel_Jsoniter-16 17741 ns/op 734.06 MB/s 27945 B/op 647 allocs/op
|
||||
BenchmarkSetOne_Sonic-16 9571 ns/op 1360.61 MB/s 1584 B/op 17 allocs/op
|
||||
BenchmarkSetOne_Sjson-16 36456 ns/op 357.22 MB/s 52180 B/op 9 allocs/op
|
||||
BenchmarkSetOne_Jsoniter-16 79475 ns/op 163.86 MB/s 45862 B/op 964 allocs/op
|
||||
BenchmarkSetOne_Parallel_Sonic-16 850.9 ns/op 15305.31 MB/s 1584 B/op 17 allocs/op
|
||||
BenchmarkSetOne_Parallel_Sjson-16 18194 ns/op 715.77 MB/s 52247 B/op 9 allocs/op
|
||||
BenchmarkSetOne_Parallel_Jsoniter-16 33560 ns/op 388.05 MB/s 45892 B/op 964 allocs/op
|
||||
BenchmarkLoadNode/LoadAll()-16 11384 ns/op 1143.93 MB/s 6307 B/op 25 allocs/op
|
||||
BenchmarkLoadNode_Parallel/LoadAll()-16 5493 ns/op 2370.68 MB/s 7145 B/op 25 allocs/op
|
||||
BenchmarkLoadNode/Interface()-16 17722 ns/op 734.85 MB/s 13323 B/op 88 allocs/op
|
||||
BenchmarkLoadNode_Parallel/Interface()-16 10330 ns/op 1260.70 MB/s 15178 B/op 88 allocs/op
|
||||
```
|
||||
|
||||
- [Small](https://github.com/bytedance/sonic/blob/main/testdata/small.go) (400B, 11 keys, 3 layers)
|
||||

|
||||
- [Large](https://github.com/bytedance/sonic/blob/main/testdata/twitter.json) (635KB, 10000+ key, 6 layers)
|
||||

|
||||
|
||||
See [bench.sh](https://github.com/bytedance/sonic/blob/main/scripts/bench.sh) for benchmark codes.
|
||||
|
||||
## How it works
|
||||
|
||||
See [INTRODUCTION.md](./docs/INTRODUCTION.md).
|
||||
|
||||
## Usage
|
||||
|
||||
### Marshal/Unmarshal
|
||||
|
||||
Default behaviors are mostly consistent with `encoding/json`, except HTML escaping form (see [Escape HTML](https://github.com/bytedance/sonic/blob/main/README.md#escape-html)) and `SortKeys` feature (optional support see [Sort Keys](https://github.com/bytedance/sonic/blob/main/README.md#sort-keys)) that is **NOT** in conformity to [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259).
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
var data YourSchema
|
||||
// Marshal
|
||||
output, err := sonic.Marshal(&data)
|
||||
// Unmarshal
|
||||
err := sonic.Unmarshal(output, &data)
|
||||
```
|
||||
|
||||
### Streaming IO
|
||||
|
||||
Sonic supports decoding json from `io.Reader` or encoding objects into `io.Writer`, aims at handling multiple values as well as reducing memory consumption.
|
||||
|
||||
- encoder
|
||||
|
||||
```go
|
||||
var o1 = map[string]interface{}{
|
||||
"a": "b",
|
||||
}
|
||||
var o2 = 1
|
||||
var w = bytes.NewBuffer(nil)
|
||||
var enc = sonic.ConfigDefault.NewEncoder(w)
|
||||
enc.Encode(o1)
|
||||
enc.Encode(o2)
|
||||
fmt.Println(w.String())
|
||||
// Output:
|
||||
// {"a":"b"}
|
||||
// 1
|
||||
```
|
||||
|
||||
- decoder
|
||||
|
||||
```go
|
||||
var o = map[string]interface{}{}
|
||||
var r = strings.NewReader(`{"a":"b"}{"1":"2"}`)
|
||||
var dec = sonic.ConfigDefault.NewDecoder(r)
|
||||
dec.Decode(&o)
|
||||
dec.Decode(&o)
|
||||
fmt.Printf("%+v", o)
|
||||
// Output:
|
||||
// map[1:2 a:b]
|
||||
```
|
||||
|
||||
### Use Number/Use Int64
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
|
||||
var input = `1`
|
||||
var data interface{}
|
||||
|
||||
// default float64
|
||||
dc := decoder.NewDecoder(input)
|
||||
dc.Decode(&data) // data == float64(1)
|
||||
// use json.Number
|
||||
dc = decoder.NewDecoder(input)
|
||||
dc.UseNumber()
|
||||
dc.Decode(&data) // data == json.Number("1")
|
||||
// use int64
|
||||
dc = decoder.NewDecoder(input)
|
||||
dc.UseInt64()
|
||||
dc.Decode(&data) // data == int64(1)
|
||||
|
||||
root, err := sonic.GetFromString(input)
|
||||
// Get json.Number
|
||||
jn := root.Number()
|
||||
jm := root.InterfaceUseNumber().(json.Number) // jn == jm
|
||||
// Get float64
|
||||
fn := root.Float64()
|
||||
fm := root.Interface().(float64) // jn == jm
|
||||
```
|
||||
|
||||
### Sort Keys
|
||||
|
||||
On account of the performance loss from sorting (roughly 10%), sonic doesn't enable this feature by default. If your component depends on it to work (like [zstd](https://github.com/facebook/zstd)), Use it like this:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/encoder"
|
||||
|
||||
// Binding map only
|
||||
m := map[string]interface{}{}
|
||||
v, err := encoder.Encode(m, encoder.SortMapKeys)
|
||||
|
||||
// Or ast.Node.SortKeys() before marshal
|
||||
var root := sonic.Get(JSON)
|
||||
err := root.SortKeys()
|
||||
```
|
||||
|
||||
### Escape HTML
|
||||
|
||||
On account of the performance loss (roughly 15%), sonic doesn't enable this feature by default. You can use `encoder.EscapeHTML` option to open this feature (align with `encoding/json.HTMLEscape`).
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
v := map[string]string{"&&":"<>"}
|
||||
ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"}}`
|
||||
```
|
||||
|
||||
### Compact Format
|
||||
|
||||
Sonic encodes primitive objects (struct/map...) as compact-format JSON by default, except marshaling `json.RawMessage` or `json.Marshaler`: sonic ensures validating their output JSON but **DO NOT** compacting them for performance concerns. We provide the option `encoder.CompactMarshaler` to add compacting process.
|
||||
|
||||
### Print Error
|
||||
|
||||
If there invalid syntax in input JSON, sonic will return `decoder.SyntaxError`, which supports pretty-printing of error position
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
|
||||
var data interface{}
|
||||
err := sonic.UnmarshalString("[[[}]]", &data)
|
||||
if err != nil {
|
||||
/* One line by default */
|
||||
println(e.Error()) // "Syntax error at index 3: invalid char\n\n\t[[[}]]\n\t...^..\n"
|
||||
/* Pretty print */
|
||||
if e, ok := err.(decoder.SyntaxError); ok {
|
||||
/*Syntax error at index 3: invalid char
|
||||
|
||||
[[[}]]
|
||||
...^..
|
||||
*/
|
||||
print(e.Description())
|
||||
} else if me, ok := err.(*decoder.MismatchTypeError); ok {
|
||||
// decoder.MismatchTypeError is new to Sonic v1.6.0
|
||||
print(me.Description())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Mismatched Types [Sonic v1.6.0]
|
||||
|
||||
If there a **mismatch-typed** value for a given key, sonic will report `decoder.MismatchTypeError` (if there are many, report the last one), but still skip wrong the value and keep decoding next JSON.
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
|
||||
var data = struct{
|
||||
A int
|
||||
B int
|
||||
}{}
|
||||
err := UnmarshalString(`{"A":"1","B":1}`, &data)
|
||||
println(err.Error()) // Mismatch type int with value string "at index 5: mismatched type with value\n\n\t{\"A\":\"1\",\"B\":1}\n\t.....^.........\n"
|
||||
fmt.Printf("%+v", data) // {A:0 B:1}
|
||||
```
|
||||
|
||||
### Ast.Node
|
||||
|
||||
Sonic/ast.Node is a completely self-contained AST for JSON. It implements serialization and deserialization both and provides robust APIs for obtaining and modification of generic data.
|
||||
|
||||
#### Get/Index
|
||||
|
||||
Search partial JSON by given paths, which must be non-negative integer or string, or nil
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
input := []byte(`{"key1":[{},{"key2":{"key3":[1,2,3]}}]}`)
|
||||
|
||||
// no path, returns entire json
|
||||
root, err := sonic.Get(input)
|
||||
raw := root.Raw() // == string(input)
|
||||
|
||||
// multiple paths
|
||||
root, err := sonic.Get(input, "key1", 1, "key2")
|
||||
sub := root.Get("key3").Index(2).Int64() // == 3
|
||||
```
|
||||
|
||||
**Tip**: since `Index()` uses offset to locate data, which is much faster than scanning like `Get()`, we suggest you use it as much as possible. And sonic also provides another API `IndexOrGet()` to underlying use offset as well as ensure the key is matched.
|
||||
|
||||
#### 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()
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
// Set
|
||||
exist, err := root.Set("key4", NewBool(true)) // exist == false
|
||||
alias1 := root.Get("key4")
|
||||
println(alias1.Valid()) // true
|
||||
alias2 := root.Index(1)
|
||||
println(alias1 == alias2) // true
|
||||
|
||||
// Unset
|
||||
exist, err := root.UnsetByIndex(1) // exist == true
|
||||
println(root.Get("key4").Check()) // "value not exist"
|
||||
```
|
||||
|
||||
#### Serialize
|
||||
|
||||
To encode `ast.Node` as json, use `MarshalJson()` or `json.Marshal()` (MUST pass the node's pointer)
|
||||
|
||||
```go
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/bytedance/sonic"
|
||||
)
|
||||
|
||||
buf, err := root.MarshalJson()
|
||||
println(string(buf)) // {"key1":[{},{"key2":{"key3":[1,2,3]}}]}
|
||||
exp, err := json.Marshal(&root) // WARN: use pointer
|
||||
println(string(buf) == string(exp)) // true
|
||||
```
|
||||
|
||||
#### APIs
|
||||
|
||||
- validation: `Check()`, `Error()`, `Valid()`, `Exist()`
|
||||
- searching: `Index()`, `Get()`, `IndexPair()`, `IndexOrGet()`, `GetByPath()`
|
||||
- go-type casting: `Int64()`, `Float64()`, `String()`, `Number()`, `Bool()`, `Map[UseNumber|UseNode]()`, `Array[UseNumber|UseNode]()`, `Interface[UseNumber|UseNode]()`
|
||||
- go-type packing: `NewRaw()`, `NewNumber()`, `NewNull()`, `NewBool()`, `NewString()`, `NewObject()`, `NewArray()`
|
||||
- iteration: `Values()`, `Properties()`, `ForEach()`, `SortKeys()`
|
||||
- modification: `Set()`, `SetByIndex()`, `Add()`
|
||||
|
||||
### Ast.Visitor
|
||||
|
||||
Sonic provides an advanced API for fully parsing JSON into non-standard types (neither `struct` not `map[string]interface{}`) without using any intermediate representation (`ast.Node` or `interface{}`). For example, you might have the following types which are like `interface{}` but actually not `interface{}`:
|
||||
|
||||
```go
|
||||
type UserNode interface {}
|
||||
|
||||
// the following types implement the UserNode interface.
|
||||
type (
|
||||
UserNull struct{}
|
||||
UserBool struct{ Value bool }
|
||||
UserInt64 struct{ Value int64 }
|
||||
UserFloat64 struct{ Value float64 }
|
||||
UserString struct{ Value string }
|
||||
UserObject struct{ Value map[string]UserNode }
|
||||
UserArray struct{ Value []UserNode }
|
||||
)
|
||||
```
|
||||
|
||||
Sonic provides the following API to return **the preorder traversal of a JSON AST**. The `ast.Visitor` is a SAX style interface which is used in some C++ JSON library. You should implement `ast.Visitor` by yourself and pass it to `ast.Preorder()` method. In your visitor you can make your custom types to represent JSON values. There may be an O(n) space container (such as stack) in your visitor to record the object / array hierarchy.
|
||||
|
||||
```go
|
||||
func Preorder(str string, visitor Visitor, opts *VisitorOptions) error
|
||||
|
||||
type Visitor interface {
|
||||
OnNull() error
|
||||
OnBool(v bool) error
|
||||
OnString(v string) error
|
||||
OnInt64(v int64, n json.Number) error
|
||||
OnFloat64(v float64, n json.Number) error
|
||||
OnObjectBegin(capacity int) error
|
||||
OnObjectKey(key string) error
|
||||
OnObjectEnd() error
|
||||
OnArrayBegin(capacity int) error
|
||||
OnArrayEnd() error
|
||||
}
|
||||
```
|
||||
|
||||
See [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go) for detailed usage. We also implement a demo visitor for `UserNode` in [ast/visitor_test.go](https://github.com/bytedance/sonic/blob/main/ast/visitor_test.go).
|
||||
|
||||
## Compatibility
|
||||
|
||||
For developers who want to use sonic to meet different scenarios, we provide some integrated configs as `sonic.API`
|
||||
|
||||
- `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 below configs will all equal to `ConfigStd`.
|
||||
|
||||
## Tips
|
||||
|
||||
### Pretouch
|
||||
|
||||
Since Sonic uses [golang-asm](https://github.com/twitchyliquid64/golang-asm) as a JIT assembler, which is NOT very suitable for runtime compiling, first-hit running of a huge schema may cause request-timeout or even process-OOM. For better stability, we advise **using `Pretouch()` for huge-schema or compact-memory applications** before `Marshal()/Unmarshal()`.
|
||||
|
||||
```go
|
||||
import (
|
||||
"reflect"
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/bytedance/sonic/option"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var v HugeStruct
|
||||
|
||||
// For most large types (nesting depth <= option.DefaultMaxInlineDepth)
|
||||
err := sonic.Pretouch(reflect.TypeOf(v))
|
||||
|
||||
// with more CompileOption...
|
||||
err := sonic.Pretouch(reflect.TypeOf(v),
|
||||
// If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth),
|
||||
// you can set compile recursive loops in Pretouch for better stability in JIT.
|
||||
option.WithCompileRecursiveDepth(loop),
|
||||
// For a large nested struct, try to set a smaller depth to reduce compiling time.
|
||||
option.WithCompileMaxInlineDepth(depth),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### Copy string
|
||||
|
||||
When decoding **string values without any escaped characters**, sonic references them from the origin JSON buffer instead of mallocing a new buffer to copy. This helps a lot for CPU performance but may leave the whole JSON buffer in memory as long as the decoded objects are being used. In practice, we found the extra memory introduced by referring JSON buffer is usually 20% ~ 80% of decoded objects. Once an application holds these objects for a long time (for example, cache the decoded objects for reusing), its in-use memory on the server may go up. - `Config.CopyString`/`decoder.CopyString()`: We provide the option for `Decode()` / `Unmarshal()` users to choose not to reference the JSON buffer, which may cause a decline in CPU performance to some degree.
|
||||
|
||||
- `GetFromStringNoCopy()`: For memory safety, `sonic.Get()` / `sonic.GetFromString()` now copies return JSON. If users want to get json more quickly and not care about memory usage, you can use `GetFromStringNoCopy()` to return a JSON directly referenced from source.
|
||||
|
||||
### Pass string or []byte?
|
||||
|
||||
For alignment to `encoding/json`, we provide API to pass `[]byte` as an argument, but the string-to-bytes copy is conducted at the same time considering safety, which may lose performance when the origin JSON is huge. Therefore, you can use `UnmarshalString()` and `GetFromString()` to pass a string, as long as your origin data is a string or **nocopy-cast** is safe for your []byte. We also provide API `MarshalString()` for convenient **nocopy-cast** of encoded JSON []byte, which is safe since sonic's output bytes is always duplicated and unique.
|
||||
|
||||
### Accelerate `encoding.TextMarshaler`
|
||||
|
||||
To ensure data security, sonic.Encoder quotes and escapes string values from `encoding.TextMarshaler` interfaces by default, which may degrade performance much if most of your data is in form of them. We provide `encoder.NoQuoteTextMarshaler` to skip these operations, which means you **MUST** ensure their output string escaped and quoted following [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259).
|
||||
|
||||
### Better performance for generic data
|
||||
|
||||
In **fully-parsed** scenario, `Unmarshal()` performs better than `Get()`+`Node.Interface()`. But if you only have a part of the schema for specific json, you can combine `Get()` and `Unmarshal()` together:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
node, err := sonic.GetFromString(_TwitterJson, "statuses", 3, "user")
|
||||
var user User // your partial schema...
|
||||
err = sonic.UnmarshalString(node.Raw(), &user)
|
||||
```
|
||||
|
||||
Even if you don't have any schema, use `ast.Node` as the container of generic values instead of `map` or `interface`:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
root, err := sonic.GetFromString(_TwitterJson)
|
||||
user := root.GetByPath("statuses", 3, "user") // === root.Get("status").Index(3).Get("user")
|
||||
err = user.Check()
|
||||
|
||||
// err = user.LoadAll() // only call this when you want to use 'user' concurrently...
|
||||
go someFunc(user)
|
||||
```
|
||||
|
||||
Why? Because `ast.Node` stores its children using `array`:
|
||||
|
||||
- `Array`'s performance is **much better** than `Map` when Inserting (Deserialize) and Scanning (Serialize) data;
|
||||
- **Hashing** (`map[x]`) is not as efficient as **Indexing** (`array[x]`), which `ast.Node` can conduct on **both array and object**;
|
||||
- Using `Interface()`/`Map()` means Sonic must parse all the underlying values, while `ast.Node` can parse them **on demand**.
|
||||
|
||||
**CAUTION:** `ast.Node` **DOESN'T** ensure concurrent security directly, due to its **lazy-load** design. However, you can call `Node.Load()`/`Node.LoadAll()` to achieve that, which may bring performance reduction while it still works faster than converting to `map` or `interface{}`
|
||||
|
||||
### Ast.Node or Ast.Visitor?
|
||||
|
||||
For generic data, `ast.Node` should be enough for your needs in most cases.
|
||||
|
||||
However, `ast.Node` is designed for partially processing JSON string. It has some special designs such as lazy-load which might not be suitable for directly parsing the whole JSON string like `Unmarshal()`. Although `ast.Node` is better then `map` or `interface{}`, it's also a kind of intermediate representation after all if your final types are customized and you have to convert the above types to your custom types after parsing.
|
||||
|
||||
For better performance, in previous case the `ast.Visitor` will be the better choice. It performs JSON decoding like `Unmarshal()` and you can directly use your final types to represents a JSON AST without any intermediate representations.
|
||||
|
||||
But `ast.Visitor` is not a very handy API. You might need to write a lot of code to implement your visitor and carefully maintain the tree hierarchy during decoding. Please read the comments in [ast/visitor.go](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 performance, 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`
|
||||
|
||||
## JSON-Path Support (GJSON)
|
||||
|
||||
[tidwall/gjson](https://github.com/tidwall/gjson) has provided a comprehensive and popular JSON-Path API, and
|
||||
a lot of older codes heavily relies on it. Therefore, we provides a wrapper library, which combines gjson's API with sonic's SIMD algorithm to boost up the performance. See [cloudwego/gjson](https://github.com/cloudwego/gjson).
|
||||
|
||||
## Community
|
||||
|
||||
Sonic is a subproject of [CloudWeGo](https://www.cloudwego.io/). We are committed to building a cloud native ecosystem.
|
||||
494
vendor/github.com/bytedance/sonic/README_ZH_CN.md
generated
vendored
Normal file
494
vendor/github.com/bytedance/sonic/README_ZH_CN.md
generated
vendored
Normal file
@ -0,0 +1,494 @@
|
||||
# Sonic
|
||||
|
||||
[English](README.md) | 中文
|
||||
|
||||
一个速度奇快的 JSON 序列化/反序列化库,由 JIT (即时编译)和 SIMD (单指令流多数据流)加速。
|
||||
|
||||
## 依赖
|
||||
|
||||
- Go: 1.18~1.25
|
||||
- 注意:Go1.24.0 由于 [issue](https://github.com/golang/go/issues/71672) 不可用,请升级到更高 Go 版本,或添加编译选项 `--ldflags="-checklinkname=0"`
|
||||
- OS: Linux / MacOS / Windows
|
||||
- CPU: AMD64 / (ARM64, 需要 Go1.20 以上)
|
||||
|
||||
## 接口
|
||||
|
||||
详见 [go.dev](https://pkg.go.dev/github.com/bytedance/sonic)
|
||||
|
||||
## 特色
|
||||
|
||||
- 运行时对象绑定,无需代码生成
|
||||
- 完备的 JSON 操作 API
|
||||
- 快,更快,还要更快!
|
||||
|
||||
## 基准测试
|
||||
|
||||
对于**所有大小**的 json 和**所有使用场景**, **Sonic 表现均为最佳**。
|
||||
|
||||
- [中型](https://github.com/bytedance/sonic/blob/main/decoder/testdata_test.go#L19) (13kB, 300+ 键, 6 层)
|
||||
|
||||
```powershell
|
||||
goversion: 1.17.1
|
||||
goos: darwin
|
||||
goarch: amd64
|
||||
cpu: Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
|
||||
BenchmarkEncoder_Generic_Sonic-16 32393 ns/op 402.40 MB/s 11965 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Generic_Sonic_Fast-16 21668 ns/op 601.57 MB/s 10940 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Generic_JsonIter-16 42168 ns/op 309.12 MB/s 14345 B/op 115 allocs/op
|
||||
BenchmarkEncoder_Generic_GoJson-16 65189 ns/op 199.96 MB/s 23261 B/op 16 allocs/op
|
||||
BenchmarkEncoder_Generic_StdLib-16 106322 ns/op 122.60 MB/s 49136 B/op 789 allocs/op
|
||||
BenchmarkEncoder_Binding_Sonic-16 6269 ns/op 2079.26 MB/s 14173 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Binding_Sonic_Fast-16 5281 ns/op 2468.16 MB/s 12322 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Binding_JsonIter-16 20056 ns/op 649.93 MB/s 9488 B/op 2 allocs/op
|
||||
BenchmarkEncoder_Binding_GoJson-16 8311 ns/op 1568.32 MB/s 9481 B/op 1 allocs/op
|
||||
BenchmarkEncoder_Binding_StdLib-16 16448 ns/op 792.52 MB/s 9479 B/op 1 allocs/op
|
||||
BenchmarkEncoder_Parallel_Generic_Sonic-16 6681 ns/op 1950.93 MB/s 12738 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Parallel_Generic_Sonic_Fast-16 4179 ns/op 3118.99 MB/s 10757 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Parallel_Generic_JsonIter-16 9861 ns/op 1321.84 MB/s 14362 B/op 115 allocs/op
|
||||
BenchmarkEncoder_Parallel_Generic_GoJson-16 18850 ns/op 691.52 MB/s 23278 B/op 16 allocs/op
|
||||
BenchmarkEncoder_Parallel_Generic_StdLib-16 45902 ns/op 283.97 MB/s 49174 B/op 789 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_Sonic-16 1480 ns/op 8810.09 MB/s 13049 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_Sonic_Fast-16 1209 ns/op 10785.23 MB/s 11546 B/op 4 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_JsonIter-16 6170 ns/op 2112.58 MB/s 9504 B/op 2 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_GoJson-16 3321 ns/op 3925.52 MB/s 9496 B/op 1 allocs/op
|
||||
BenchmarkEncoder_Parallel_Binding_StdLib-16 3739 ns/op 3486.49 MB/s 9480 B/op 1 allocs/op
|
||||
|
||||
BenchmarkDecoder_Generic_Sonic-16 66812 ns/op 195.10 MB/s 57602 B/op 723 allocs/op
|
||||
BenchmarkDecoder_Generic_Sonic_Fast-16 54523 ns/op 239.07 MB/s 49786 B/op 313 allocs/op
|
||||
BenchmarkDecoder_Generic_StdLib-16 124260 ns/op 104.90 MB/s 50869 B/op 772 allocs/op
|
||||
BenchmarkDecoder_Generic_JsonIter-16 91274 ns/op 142.81 MB/s 55782 B/op 1068 allocs/op
|
||||
BenchmarkDecoder_Generic_GoJson-16 88569 ns/op 147.17 MB/s 66367 B/op 973 allocs/op
|
||||
BenchmarkDecoder_Binding_Sonic-16 32557 ns/op 400.38 MB/s 28302 B/op 137 allocs/op
|
||||
BenchmarkDecoder_Binding_Sonic_Fast-16 28649 ns/op 455.00 MB/s 24999 B/op 34 allocs/op
|
||||
BenchmarkDecoder_Binding_StdLib-16 111437 ns/op 116.97 MB/s 10576 B/op 208 allocs/op
|
||||
BenchmarkDecoder_Binding_JsonIter-16 35090 ns/op 371.48 MB/s 14673 B/op 385 allocs/op
|
||||
BenchmarkDecoder_Binding_GoJson-16 28738 ns/op 453.59 MB/s 22039 B/op 49 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_Sonic-16 12321 ns/op 1057.91 MB/s 57233 B/op 723 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_Sonic_Fast-16 10644 ns/op 1224.64 MB/s 49362 B/op 313 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_StdLib-16 57587 ns/op 226.35 MB/s 50874 B/op 772 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_JsonIter-16 38666 ns/op 337.12 MB/s 55789 B/op 1068 allocs/op
|
||||
BenchmarkDecoder_Parallel_Generic_GoJson-16 30259 ns/op 430.79 MB/s 66370 B/op 974 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_Sonic-16 5965 ns/op 2185.28 MB/s 27747 B/op 137 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_Sonic_Fast-16 5170 ns/op 2521.31 MB/s 24715 B/op 34 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_StdLib-16 27582 ns/op 472.58 MB/s 10576 B/op 208 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_JsonIter-16 13571 ns/op 960.51 MB/s 14685 B/op 385 allocs/op
|
||||
BenchmarkDecoder_Parallel_Binding_GoJson-16 10031 ns/op 1299.51 MB/s 22111 B/op 49 allocs/op
|
||||
|
||||
BenchmarkGetOne_Sonic-16 3276 ns/op 3975.78 MB/s 24 B/op 1 allocs/op
|
||||
BenchmarkGetOne_Gjson-16 9431 ns/op 1380.81 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkGetOne_Jsoniter-16 51178 ns/op 254.46 MB/s 27936 B/op 647 allocs/op
|
||||
BenchmarkGetOne_Parallel_Sonic-16 216.7 ns/op 60098.95 MB/s 24 B/op 1 allocs/op
|
||||
BenchmarkGetOne_Parallel_Gjson-16 1076 ns/op 12098.62 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkGetOne_Parallel_Jsoniter-16 17741 ns/op 734.06 MB/s 27945 B/op 647 allocs/op
|
||||
BenchmarkSetOne_Sonic-16 9571 ns/op 1360.61 MB/s 1584 B/op 17 allocs/op
|
||||
BenchmarkSetOne_Sjson-16 36456 ns/op 357.22 MB/s 52180 B/op 9 allocs/op
|
||||
BenchmarkSetOne_Jsoniter-16 79475 ns/op 163.86 MB/s 45862 B/op 964 allocs/op
|
||||
BenchmarkSetOne_Parallel_Sonic-16 850.9 ns/op 15305.31 MB/s 1584 B/op 17 allocs/op
|
||||
BenchmarkSetOne_Parallel_Sjson-16 18194 ns/op 715.77 MB/s 52247 B/op 9 allocs/op
|
||||
BenchmarkSetOne_Parallel_Jsoniter-16 33560 ns/op 388.05 MB/s 45892 B/op 964 allocs/op
|
||||
BenchmarkLoadNode/LoadAll()-16 11384 ns/op 1143.93 MB/s 6307 B/op 25 allocs/op
|
||||
BenchmarkLoadNode_Parallel/LoadAll()-16 5493 ns/op 2370.68 MB/s 7145 B/op 25 allocs/op
|
||||
BenchmarkLoadNode/Interface()-16 17722 ns/op 734.85 MB/s 13323 B/op 88 allocs/op
|
||||
BenchmarkLoadNode_Parallel/Interface()-16 10330 ns/op 1260.70 MB/s 15178 B/op 88 allocs/op
|
||||
```
|
||||
|
||||
- [小型](https://github.com/bytedance/sonic/blob/main/testdata/small.go) (400B, 11 个键, 3 层)
|
||||

|
||||
- [大型](https://github.com/bytedance/sonic/blob/main/testdata/twitter.json) (635kB, 10000+ 个键, 6 层)
|
||||

|
||||
|
||||
要查看基准测试代码,请参阅 [bench.sh](https://github.com/bytedance/sonic/blob/main/scripts/bench.sh) 。
|
||||
|
||||
## 工作原理
|
||||
|
||||
请参阅 [INTRODUCTION_ZH_CN.md](./docs/INTRODUCTION_ZH_CN.md).
|
||||
|
||||
## 使用方式
|
||||
|
||||
### 序列化/反序列化
|
||||
|
||||
默认的行为基本上与 `encoding/json` 相一致,除了 HTML 转义形式(参见 [Escape HTML](https://github.com/bytedance/sonic/blob/main/README.md#escape-html)) 和 `SortKeys` 功能(参见 [Sort Keys](https://github.com/bytedance/sonic/blob/main/README.md#sort-keys))**没有**遵循 [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259) 。
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
var data YourSchema
|
||||
// Marshal
|
||||
output, err := sonic.Marshal(&data)
|
||||
// Unmarshal
|
||||
err := sonic.Unmarshal(output, &data)
|
||||
```
|
||||
|
||||
### 流式输入输出
|
||||
|
||||
Sonic 支持解码 `io.Reader` 中输入的 json,或将对象编码为 json 后输出至 `io.Writer`,以处理多个值并减少内存消耗。
|
||||
|
||||
- 编码器
|
||||
|
||||
```go
|
||||
var o1 = map[string]interface{}{
|
||||
"a": "b",
|
||||
}
|
||||
var o2 = 1
|
||||
var w = bytes.NewBuffer(nil)
|
||||
var enc = sonic.ConfigDefault.NewEncoder(w)
|
||||
enc.Encode(o1)
|
||||
enc.Encode(o2)
|
||||
fmt.Println(w.String())
|
||||
// Output:
|
||||
// {"a":"b"}
|
||||
// 1
|
||||
```
|
||||
|
||||
- 解码器
|
||||
|
||||
```go
|
||||
var o = map[string]interface{}{}
|
||||
var r = strings.NewReader(`{"a":"b"}{"1":"2"}`)
|
||||
var dec = sonic.ConfigDefault.NewDecoder(r)
|
||||
dec.Decode(&o)
|
||||
dec.Decode(&o)
|
||||
fmt.Printf("%+v", o)
|
||||
// Output:
|
||||
// map[1:2 a:b]
|
||||
```
|
||||
|
||||
### 使用 `Number` / `int64`
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
|
||||
var input = `1`
|
||||
var data interface{}
|
||||
|
||||
// default float64
|
||||
dc := decoder.NewDecoder(input)
|
||||
dc.Decode(&data) // data == float64(1)
|
||||
// use json.Number
|
||||
dc = decoder.NewDecoder(input)
|
||||
dc.UseNumber()
|
||||
dc.Decode(&data) // data == json.Number("1")
|
||||
// use int64
|
||||
dc = decoder.NewDecoder(input)
|
||||
dc.UseInt64()
|
||||
dc.Decode(&data) // data == int64(1)
|
||||
|
||||
root, err := sonic.GetFromString(input)
|
||||
// Get json.Number
|
||||
jn := root.Number()
|
||||
jm := root.InterfaceUseNumber().(json.Number) // jn == jm
|
||||
// Get float64
|
||||
fn := root.Float64()
|
||||
fm := root.Interface().(float64) // jn == jm
|
||||
```
|
||||
|
||||
### 对键排序
|
||||
|
||||
考虑到排序带来的性能损失(约 10% ), sonic 默认不会启用这个功能。如果你的组件依赖这个行为(如 [zstd](https://github.com/facebook/zstd)) ,可以仿照下面的例子:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/encoder"
|
||||
|
||||
// Binding map only
|
||||
m := map[string]interface{}{}
|
||||
v, err := encoder.Encode(m, encoder.SortMapKeys)
|
||||
|
||||
// Or ast.Node.SortKeys() before marshal
|
||||
var root := sonic.Get(JSON)
|
||||
err := root.SortKeys()
|
||||
```
|
||||
|
||||
### HTML 转义
|
||||
|
||||
考虑到性能损失(约15%), sonic 默认不会启用这个功能。你可以使用 `encoder.EscapeHTML` 选项来开启(与 `encoding/json.HTMLEscape` 行为一致)。
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
v := map[string]string{"&&":"<>"}
|
||||
ret, err := Encode(v, EscapeHTML) // ret == `{"\u0026\u0026":{"X":"\u003c\u003e"}}`
|
||||
```
|
||||
|
||||
### 紧凑格式
|
||||
|
||||
Sonic 默认将基本类型( `struct` , `map` 等)编码为紧凑格式的 JSON ,除非使用 `json.RawMessage` or `json.Marshaler` 进行编码: sonic 确保输出的 JSON 合法,但出于性能考虑,**不会**加工成紧凑格式。我们提供选项 `encoder.CompactMarshaler` 来添加此过程,
|
||||
|
||||
### 打印错误
|
||||
|
||||
如果输入的 JSON 存在无效的语法,sonic 将返回 `decoder.SyntaxError`,该错误支持错误位置的美化输出。
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
|
||||
var data interface{}
|
||||
err := sonic.UnmarshalString("[[[}]]", &data)
|
||||
if err != nil {
|
||||
/* One line by default */
|
||||
println(e.Error()) // "Syntax error at index 3: invalid char\n\n\t[[[}]]\n\t...^..\n"
|
||||
/* Pretty print */
|
||||
if e, ok := err.(decoder.SyntaxError); ok {
|
||||
/*Syntax error at index 3: invalid char
|
||||
|
||||
[[[}]]
|
||||
...^..
|
||||
*/
|
||||
print(e.Description())
|
||||
} else if me, ok := err.(*decoder.MismatchTypeError); ok {
|
||||
// decoder.MismatchTypeError is new to Sonic v1.6.0
|
||||
print(me.Description())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 类型不匹配 [Sonic v1.6.0]
|
||||
|
||||
如果给定键中存在**类型不匹配**的值, sonic 会抛出 `decoder.MismatchTypeError` (如果有多个,只会报告最后一个),但仍会跳过错误的值并解码下一个 JSON 。
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
import "github.com/bytedance/sonic/decoder"
|
||||
|
||||
var data = struct{
|
||||
A int
|
||||
B int
|
||||
}{}
|
||||
err := UnmarshalString(`{"A":"1","B":1}`, &data)
|
||||
println(err.Error()) // Mismatch type int with value string "at index 5: mismatched type with value\n\n\t{\"A\":\"1\",\"B\":1}\n\t.....^.........\n"
|
||||
fmt.Printf("%+v", data) // {A:0 B:1}
|
||||
```
|
||||
|
||||
### `Ast.Node`
|
||||
|
||||
Sonic/ast.Node 是完全独立的 JSON 抽象语法树库。它实现了序列化和反序列化,并提供了获取和修改JSON数据的鲁棒的 API。
|
||||
|
||||
#### 查找/索引
|
||||
|
||||
通过给定的路径搜索 JSON 片段,路径必须为非负整数,字符串或 `nil` 。
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
input := []byte(`{"key1":[{},{"key2":{"key3":[1,2,3]}}]}`)
|
||||
|
||||
// no path, returns entire json
|
||||
root, err := sonic.Get(input)
|
||||
raw := root.Raw() // == string(input)
|
||||
|
||||
// multiple paths
|
||||
root, err := sonic.Get(input, "key1", 1, "key2")
|
||||
sub := root.Get("key3").Index(2).Int64() // == 3
|
||||
```
|
||||
|
||||
**注意**:由于 `Index()` 使用偏移量来定位数据,比使用扫描的 `Get()` 要快的多,建议尽可能的使用 `Index` 。 Sonic 也提供了另一个 API, `IndexOrGet()` ,以偏移量为基础并且也确保键的匹配。
|
||||
|
||||
#### 查找选项
|
||||
|
||||
`ast.Searcher`提供了一些选项,以满足用户的不同需求:
|
||||
|
||||
```go
|
||||
opts := ast.SearchOption{CopyReturn: true…}
|
||||
val, err := sonic.GetWithOptions(JSON, opts, "key")
|
||||
```
|
||||
|
||||
- CopyReturn
|
||||
指示搜索器复制结果JSON字符串,而不是从输入引用。如果用户缓存结果,这有助于减少内存使用
|
||||
- ConcurentRead
|
||||
因为`ast.Node`使用`Lazy-Load`设计,默认不支持并发读取。如果您想同时读取,请指定它。
|
||||
- ValidateJSON
|
||||
指示搜索器来验证整个JSON。默认情况下启用该选项, 但是对于查找速度有一定影响。
|
||||
|
||||
#### 修改
|
||||
|
||||
使用 `Set()` / `Unset()` 修改 json 的内容
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
// Set
|
||||
exist, err := root.Set("key4", NewBool(true)) // exist == false
|
||||
alias1 := root.Get("key4")
|
||||
println(alias1.Valid()) // true
|
||||
alias2 := root.Index(1)
|
||||
println(alias1 == alias2) // true
|
||||
|
||||
// Unset
|
||||
exist, err := root.UnsetByIndex(1) // exist == true
|
||||
println(root.Get("key4").Check()) // "value not exist"
|
||||
```
|
||||
|
||||
#### 序列化
|
||||
|
||||
要将 `ast.Node` 编码为 json ,使用 `MarshalJson()` 或者 `json.Marshal()` (必须传递指向节点的指针)
|
||||
|
||||
```go
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/bytedance/sonic"
|
||||
)
|
||||
|
||||
buf, err := root.MarshalJson()
|
||||
println(string(buf)) // {"key1":[{},{"key2":{"key3":[1,2,3]}}]}
|
||||
exp, err := json.Marshal(&root) // WARN: use pointer
|
||||
println(string(buf) == string(exp)) // true
|
||||
```
|
||||
|
||||
#### APIs
|
||||
|
||||
- 合法性检查: `Check()`, `Error()`, `Valid()`, `Exist()`
|
||||
- 索引: `Index()`, `Get()`, `IndexPair()`, `IndexOrGet()`, `GetByPath()`
|
||||
- 转换至 go 内置类型: `Int64()`, `Float64()`, `String()`, `Number()`, `Bool()`, `Map[UseNumber|UseNode]()`, `Array[UseNumber|UseNode]()`, `Interface[UseNumber|UseNode]()`
|
||||
- go 类型打包: `NewRaw()`, `NewNumber()`, `NewNull()`, `NewBool()`, `NewString()`, `NewObject()`, `NewArray()`
|
||||
- 迭代: `Values()`, `Properties()`, `ForEach()`, `SortKeys()`
|
||||
- 修改: `Set()`, `SetByIndex()`, `Add()`
|
||||
|
||||
### `Ast.Visitor`
|
||||
|
||||
Sonic 提供了一个高级的 API 用于直接全量解析 JSON 到非标准容器里 (既不是 `struct` 也不是 `map[string]interface{}`) 且不需要借助任何中间表示 (`ast.Node` 或 `interface{}`)。举个例子,你可能定义了下述的类型,它们看起来像 `interface{}`,但实际上并不是:
|
||||
|
||||
```go
|
||||
type UserNode interface {}
|
||||
|
||||
// the following types implement the UserNode interface.
|
||||
type (
|
||||
UserNull struct{}
|
||||
UserBool struct{ Value bool }
|
||||
UserInt64 struct{ Value int64 }
|
||||
UserFloat64 struct{ Value float64 }
|
||||
UserString struct{ Value string }
|
||||
UserObject struct{ Value map[string]UserNode }
|
||||
UserArray struct{ Value []UserNode }
|
||||
)
|
||||
```
|
||||
|
||||
Sonic 提供了下述的 API 来返回 **“对 JSON AST 的前序遍历”**。`ast.Visitor` 是一个 SAX 风格的接口,这在某些 C++ 的 JSON 解析库中被使用到。你需要自己实现一个 `ast.Visitor`,将它传递给 `ast.Preorder()` 方法。在你的实现中你可以使用自定义的类型来表示 JSON 的值。在你的 `ast.Visitor` 中,可能需要有一个 O(n) 空间复杂度的容器(比如说栈)来记录 object / array 的层级。
|
||||
|
||||
```go
|
||||
func Preorder(str string, visitor Visitor, opts *VisitorOptions) error
|
||||
|
||||
type Visitor interface {
|
||||
OnNull() error
|
||||
OnBool(v bool) error
|
||||
OnString(v string) error
|
||||
OnInt64(v int64, n json.Number) error
|
||||
OnFloat64(v float64, n json.Number) error
|
||||
OnObjectBegin(capacity int) error
|
||||
OnObjectKey(key string) error
|
||||
OnObjectEnd() error
|
||||
OnArrayBegin(capacity int) error
|
||||
OnArrayEnd() error
|
||||
}
|
||||
```
|
||||
|
||||
详细用法参看 [ast/visitor.go](https://github.com/bytedance/sonic/blob/main/ast/visitor.go),我们还为 `UserNode` 实现了一个示例 `ast.Visitor`,你可以在 [ast/visitor_test.go](https://github.com/bytedance/sonic/blob/main/ast/visitor_test.go) 中找到它。
|
||||
|
||||
## 兼容性
|
||||
|
||||
对于想要使用sonic来满足不同场景的开发人员,我们提供了一些集成配置:
|
||||
|
||||
- `ConfigDefault`: sonic的默认配置 (`EscapeHTML=false`, `SortKeys=false`…) 保证性能同时兼顾安全性。
|
||||
- `ConfigStd`: 与 `encoding/json` 保证完全兼容的配置
|
||||
- `ConfigFastest`: 最快的配置(`NoQuoteTextMarshaler=true...`) 保证性能最优但是会缺少一些安全性检查(validate UTF8 等)
|
||||
Sonic **不**确保支持所有环境,由于开发高性能代码的困难。在不支持sonic的环境中,实现将回落到 `encoding/json`。因此上述配置将全部等于`ConfigStd`。
|
||||
|
||||
## 注意事项
|
||||
|
||||
### 预热
|
||||
|
||||
由于 Sonic 使用 [golang-asm](https://github.com/twitchyliquid64/golang-asm) 作为 JIT 汇编器,这个库并不适用于运行时编译,第一次运行一个大型模式可能会导致请求超时甚至进程内存溢出。为了更好地稳定性,我们建议在运行大型模式或在内存有限的应用中,在使用 `Marshal()/Unmarshal()` 前运行 `Pretouch()`。
|
||||
|
||||
```go
|
||||
import (
|
||||
"reflect"
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/bytedance/sonic/option"
|
||||
)
|
||||
|
||||
func init() {
|
||||
var v HugeStruct
|
||||
|
||||
// For most large types (nesting depth <= option.DefaultMaxInlineDepth)
|
||||
err := sonic.Pretouch(reflect.TypeOf(v))
|
||||
|
||||
// with more CompileOption...
|
||||
err := sonic.Pretouch(reflect.TypeOf(v),
|
||||
// If the type is too deep nesting (nesting depth > option.DefaultMaxInlineDepth),
|
||||
// you can set compile recursive loops in Pretouch for better stability in JIT.
|
||||
option.WithCompileRecursiveDepth(loop),
|
||||
// For a large nested struct, try to set a smaller depth to reduce compiling time.
|
||||
option.WithCompileMaxInlineDepth(depth),
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
### 拷贝字符串
|
||||
|
||||
当解码 **没有转义字符的字符串**时, sonic 会从原始的 JSON 缓冲区内引用而不是复制到新的一个缓冲区中。这对 CPU 的性能方面很有帮助,但是可能因此在解码后对象仍在使用的时候将整个 JSON 缓冲区保留在内存中。实践中我们发现,通过引用 JSON 缓冲区引入的额外内存通常是解码后对象的 20% 至 80% ,一旦应用长期保留这些对象(如缓存以备重用),服务器所使用的内存可能会增加。我们提供了选项 `decoder.CopyString()` 供用户选择,不引用 JSON 缓冲区。这可能在一定程度上降低 CPU 性能。
|
||||
|
||||
### 传递字符串还是字节数组?
|
||||
|
||||
为了和 `encoding/json` 保持一致,我们提供了传递 `[]byte` 作为参数的 API ,但考虑到安全性,字符串到字节的复制是同时进行的,这在原始 JSON 非常大时可能会导致性能损失。因此,你可以使用 `UnmarshalString()` 和 `GetFromString()` 来传递字符串,只要你的原始数据是字符串,或**零拷贝类型转换**对于你的字节数组是安全的。我们也提供了 `MarshalString()` 的 API ,以便对编码的 JSON 字节数组进行**零拷贝类型转换**,因为 sonic 输出的字节始终是重复并且唯一的,所以这样是安全的。
|
||||
|
||||
### 加速 `encoding.TextMarshaler`
|
||||
|
||||
为了保证数据安全性, `sonic.Encoder` 默认会对来自 `encoding.TextMarshaler` 接口的字符串进行引用和转义,如果大部分数据都是这种形式那可能会导致很大的性能损失。我们提供了 `encoder.NoQuoteTextMarshaler` 选项来跳过这些操作,但你**必须**保证他们的输出字符串依照 [RFC8259](https://datatracker.ietf.org/doc/html/rfc8259) 进行了转义和引用。
|
||||
|
||||
### 泛型的性能优化
|
||||
|
||||
在 **完全解析**的场景下, `Unmarshal()` 表现得比 `Get()`+`Node.Interface()` 更好。但是如果你只有特定 JSON 的部分模式,你可以将 `Get()` 和 `Unmarshal()` 结合使用:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
node, err := sonic.GetFromString(_TwitterJson, "statuses", 3, "user")
|
||||
var user User // your partial schema...
|
||||
err = sonic.UnmarshalString(node.Raw(), &user)
|
||||
```
|
||||
|
||||
甚至如果你没有任何模式,可以用 `ast.Node` 代替 `map` 或 `interface` 作为泛型的容器:
|
||||
|
||||
```go
|
||||
import "github.com/bytedance/sonic"
|
||||
|
||||
root, err := sonic.GetFromString(_TwitterJson)
|
||||
user := root.GetByPath("statuses", 3, "user") // === root.Get("status").Index(3).Get("user")
|
||||
err = user.Check()
|
||||
|
||||
// err = user.LoadAll() // only call this when you want to use 'user' concurrently...
|
||||
go someFunc(user)
|
||||
```
|
||||
|
||||
为什么?因为 `ast.Node` 使用 `array` 来存储其子节点:
|
||||
|
||||
- 在插入(反序列化)和扫描(序列化)数据时,`Array` 的性能比 `Map` **好得多**;
|
||||
- **哈希**(`map[x]`)的效率不如**索引**(`array[x]`)高效,而 `ast.Node` 可以在数组和对象上使用索引;
|
||||
- 使用 `Interface()` / `Map()` 意味着 sonic 必须解析所有的底层值,而 `ast.Node` 可以**按需解析**它们。
|
||||
|
||||
**注意**:由于 `ast.Node` 的惰性加载设计,其**不能**直接保证并发安全性,但你可以调用 `Node.Load()` / `Node.LoadAll()` 来实现并发安全。尽管可能会带来性能损失,但仍比转换成 `map` 或 `interface{}` 更为高效。
|
||||
|
||||
### 使用 `ast.Node` 还是 `ast.Visitor`?
|
||||
|
||||
对于泛型数据的解析,`ast.Node` 在大多数场景上应该能够满足你的需求。
|
||||
|
||||
然而,`ast.Node` 是一种针对部分解析 JSON 而设计的泛型容器,它包含一些特殊设计,比如惰性加载,如果你希望像 `Unmarshal()` 那样直接解析整个 JSON,这些设计可能并不合适。尽管 `ast.Node` 相较于 `map` 或 `interface{}` 来说是更好的一种泛型容器,但它毕竟也是一种中间表示,如果你的最终类型是自定义的,你还得在解析完成后将上述类型转化成你自定义的类型。
|
||||
|
||||
在上述场景中,如果想要有更极致的性能,`ast.Visitor` 会是更好的选择。它采用和 `Unmarshal()` 类似的形式解析 JSON,并且你可以直接使用你的最终类型去表示 JSON AST,而不需要经过额外的任何中间表示。
|
||||
|
||||
但是,`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/) 下的一个子项目。我们致力于构建云原生生态系统。
|
||||
249
vendor/github.com/bytedance/sonic/api.go
generated
vendored
Normal file
249
vendor/github.com/bytedance/sonic/api.go
generated
vendored
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright 2021 ByteDance Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package sonic
|
||||
|
||||
import (
|
||||
`io`
|
||||
|
||||
`github.com/bytedance/sonic/ast`
|
||||
`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
|
||||
// after serializing into JSON (see https://pkg.go.dev/encoding/json#HTMLEscape).
|
||||
// WARNING: This hurts performance A LOT, USE WITH CARE.
|
||||
EscapeHTML bool
|
||||
|
||||
// SortMapKeys indicates encoder that the keys of a map needs to be sorted
|
||||
// before serializing into JSON.
|
||||
// WARNING: This hurts performance A LOT, USE WITH CARE.
|
||||
SortMapKeys bool
|
||||
|
||||
// CompactMarshaler indicates encoder that the output JSON from json.Marshaler
|
||||
// is always compact and needs no validation
|
||||
CompactMarshaler bool
|
||||
|
||||
// NoQuoteTextMarshaler indicates encoder that the output text from encoding.TextMarshaler
|
||||
// is always escaped string and needs no quoting
|
||||
NoQuoteTextMarshaler bool
|
||||
|
||||
// NoNullSliceOrMap indicates encoder that all empty Array or Object are encoded as '[]' or '{}',
|
||||
// instead of 'null'
|
||||
NoNullSliceOrMap bool
|
||||
|
||||
// UseInt64 indicates decoder to unmarshal an integer into an interface{} as an
|
||||
// int64 instead of as a float64.
|
||||
UseInt64 bool
|
||||
|
||||
// UseNumber indicates decoder to unmarshal a number into an interface{} as a
|
||||
// json.Number instead of as a float64.
|
||||
UseNumber bool
|
||||
|
||||
// UseUnicodeErrors indicates decoder to return an error when encounter invalid
|
||||
// UTF-8 escape sequences.
|
||||
UseUnicodeErrors bool
|
||||
|
||||
// DisallowUnknownFields indicates decoder to return an error when the destination
|
||||
// is a struct and the input contains object keys which do not match any
|
||||
// non-ignored, exported fields in the destination.
|
||||
DisallowUnknownFields bool
|
||||
|
||||
// CopyString indicates decoder to decode string values by copying instead of referring.
|
||||
CopyString bool
|
||||
|
||||
// ValidateString indicates decoder and encoder to validate string values: decoder will return errors
|
||||
// when unescaped control chars(\u0000-\u001f) in the string value of JSON.
|
||||
ValidateString bool
|
||||
|
||||
// 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
|
||||
|
||||
// CaseSensitive indicates that the decoder should not ignore the case of object keys.
|
||||
CaseSensitive bool
|
||||
}
|
||||
|
||||
var (
|
||||
// ConfigDefault is the default config of APIs, aiming at efficiency and safety.
|
||||
ConfigDefault = Config{}.Froze()
|
||||
|
||||
// ConfigStd is the standard config of APIs, aiming at being compatible with encoding/json.
|
||||
ConfigStd = Config{
|
||||
EscapeHTML : true,
|
||||
SortMapKeys: true,
|
||||
CompactMarshaler: true,
|
||||
CopyString : true,
|
||||
ValidateString : true,
|
||||
}.Froze()
|
||||
|
||||
// ConfigFastest is the fastest config of APIs, aiming at speed.
|
||||
ConfigFastest = Config{
|
||||
NoValidateJSONMarshaler: true,
|
||||
NoValidateJSONSkip: true,
|
||||
}.Froze()
|
||||
)
|
||||
|
||||
|
||||
// API is a binding of specific config.
|
||||
// This interface is inspired by github.com/json-iterator/go,
|
||||
// and has same behaviors under equivalent config.
|
||||
type API interface {
|
||||
// MarshalToString returns the JSON encoding string of v
|
||||
MarshalToString(v interface{}) (string, error)
|
||||
// Marshal returns the JSON encoding bytes of v.
|
||||
Marshal(v interface{}) ([]byte, error)
|
||||
// MarshalIndent returns the JSON encoding bytes with indent and prefix.
|
||||
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
|
||||
// UnmarshalFromString parses the JSON-encoded bytes and stores the result in the value pointed to by v.
|
||||
UnmarshalFromString(str string, v interface{}) error
|
||||
// Unmarshal parses the JSON-encoded string and stores the result in the value pointed to by v.
|
||||
Unmarshal(data []byte, v interface{}) error
|
||||
// NewEncoder create a Encoder holding writer
|
||||
NewEncoder(writer io.Writer) Encoder
|
||||
// NewDecoder create a Decoder holding reader
|
||||
NewDecoder(reader io.Reader) Decoder
|
||||
// Valid validates the JSON-encoded bytes and reports if it is valid
|
||||
Valid(data []byte) bool
|
||||
}
|
||||
|
||||
// Encoder encodes JSON into io.Writer
|
||||
type Encoder interface {
|
||||
// Encode writes the JSON encoding of v to the stream, followed by a newline character.
|
||||
Encode(val interface{}) error
|
||||
// SetEscapeHTML specifies whether problematic HTML characters
|
||||
// should be escaped inside JSON quoted strings.
|
||||
// The default behavior NOT ESCAPE
|
||||
SetEscapeHTML(on bool)
|
||||
// SetIndent instructs the encoder to format each subsequent encoded value
|
||||
// as if indented by the package-level function Indent(dst, src, prefix, indent).
|
||||
// Calling SetIndent("", "") disables indentation
|
||||
SetIndent(prefix, indent string)
|
||||
}
|
||||
|
||||
// Decoder decodes JSON from io.Read
|
||||
type Decoder interface {
|
||||
// Decode reads the next JSON-encoded value from its input and stores it in the value pointed to by v.
|
||||
Decode(val interface{}) error
|
||||
// Buffered returns a reader of the data remaining in the Decoder's buffer.
|
||||
// The reader is valid until the next call to Decode.
|
||||
Buffered() io.Reader
|
||||
// DisallowUnknownFields causes the Decoder to return an error when the destination is a struct
|
||||
// and the input contains object keys which do not match any non-ignored, exported fields in the destination.
|
||||
DisallowUnknownFields()
|
||||
// More reports whether there is another element in the current array or object being parsed.
|
||||
More() bool
|
||||
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a Number instead of as a float64.
|
||||
UseNumber()
|
||||
}
|
||||
|
||||
// Marshal returns the JSON encoding bytes of v.
|
||||
func Marshal(val interface{}) ([]byte, error) {
|
||||
return ConfigDefault.Marshal(val)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
|
||||
// NOTICE: This API copies given buffer by default,
|
||||
// if you want to pass JSON more efficiently, use UnmarshalString instead.
|
||||
func Unmarshal(buf []byte, val interface{}) error {
|
||||
return ConfigDefault.Unmarshal(buf, val)
|
||||
}
|
||||
|
||||
// UnmarshalString is like Unmarshal, except buf is a string.
|
||||
func UnmarshalString(buf string, val interface{}) error {
|
||||
return ConfigDefault.UnmarshalFromString(buf, val)
|
||||
}
|
||||
|
||||
// Get searches and locates the given path from src json,
|
||||
// and returns a ast.Node representing the partially json.
|
||||
//
|
||||
// Each path arg must be integer or string:
|
||||
// - Integer is target index(>=0), means searching current node as array.
|
||||
// - String is target key, means searching current node as object.
|
||||
//
|
||||
//
|
||||
// Notice: It expects the src json is **Well-formed** and **Immutable** when calling,
|
||||
// otherwise it may return unexpected result.
|
||||
// Considering memory safety, the returned JSON is **Copied** from the input
|
||||
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.
|
||||
// Caching or long-time holding the returned node may cause OOM.
|
||||
// If your src is big, consider use GetFromStringCopy().
|
||||
func GetFromString(src string, path ...interface{}) (ast.Node, error) {
|
||||
return ast.NewSearcher(src).GetByPath(path...)
|
||||
}
|
||||
|
||||
// GetCopyFromString is same with Get except src is string
|
||||
func GetCopyFromString(src string, path ...interface{}) (ast.Node, error) {
|
||||
return ast.NewSearcher(src).GetByPathCopy(path...)
|
||||
}
|
||||
|
||||
// Valid reports whether data is a valid JSON encoding.
|
||||
func Valid(data []byte) bool {
|
||||
return ConfigDefault.Valid(data)
|
||||
}
|
||||
|
||||
// Valid reports whether data is a valid JSON encoding.
|
||||
func ValidString(data string) bool {
|
||||
return ConfigDefault.Valid(rt.Str2Mem(data))
|
||||
}
|
||||
94
vendor/github.com/bytedance/sonic/ast/api.go
generated
vendored
Normal file
94
vendor/github.com/bytedance/sonic/ast/api.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
//go:build (amd64 && go1.17 && !go1.26) || (arm64 && go1.20 && !go1.26)
|
||||
// +build amd64,go1.17,!go1.26 arm64,go1.20,!go1.26
|
||||
|
||||
/*
|
||||
* 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 ast
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bytedance/sonic/encoder"
|
||||
"github.com/bytedance/sonic/internal/encoder/alg"
|
||||
"github.com/bytedance/sonic/internal/native"
|
||||
"github.com/bytedance/sonic/internal/native/types"
|
||||
"github.com/bytedance/sonic/internal/rt"
|
||||
"github.com/bytedance/sonic/utf8"
|
||||
)
|
||||
|
||||
var typeByte = rt.UnpackEface(byte(0)).Type
|
||||
|
||||
func quote(buf *[]byte, val string) {
|
||||
*buf = alg.Quote(*buf, val, false)
|
||||
}
|
||||
|
||||
func (self *Parser) decodeValue() (val types.JsonState) {
|
||||
sv := (*rt.GoString)(unsafe.Pointer(&self.s))
|
||||
flag := types.F_USE_NUMBER
|
||||
if self.dbuf != nil {
|
||||
flag = 0
|
||||
val.Dbuf = self.dbuf
|
||||
val.Dcap = types.MaxDigitNums
|
||||
}
|
||||
self.p = native.Value(sv.Ptr, sv.Len, self.p, &val, uint64(flag))
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Parser) skip() (int, types.ParsingError) {
|
||||
fsm := types.NewStateMachine()
|
||||
start := native.SkipOne(&self.s, &self.p, fsm, 0)
|
||||
types.FreeStateMachine(fsm)
|
||||
|
||||
if start < 0 {
|
||||
return self.p, types.ParsingError(-start)
|
||||
}
|
||||
return start, 0
|
||||
}
|
||||
|
||||
func (self *Node) encodeInterface(buf *[]byte) error {
|
||||
//WARN: NOT compatible with json.Encoder
|
||||
return encoder.EncodeInto(buf, self.packAny(), encoder.NoEncoderNewline)
|
||||
}
|
||||
|
||||
func (self *Parser) skipFast() (int, types.ParsingError) {
|
||||
start := native.SkipOneFast(&self.s, &self.p)
|
||||
if start < 0 {
|
||||
return self.p, types.ParsingError(-start)
|
||||
}
|
||||
return start, 0
|
||||
}
|
||||
|
||||
func (self *Parser) getByPath(validate bool, path ...interface{}) (int, types.ParsingError) {
|
||||
var fsm *types.StateMachine
|
||||
if validate {
|
||||
fsm = types.NewStateMachine()
|
||||
}
|
||||
start := native.GetByPath(&self.s, &self.p, &path, fsm)
|
||||
if validate {
|
||||
types.FreeStateMachine(fsm)
|
||||
}
|
||||
runtime.KeepAlive(path)
|
||||
if start < 0 {
|
||||
return self.p, types.ParsingError(-start)
|
||||
}
|
||||
return start, 0
|
||||
}
|
||||
|
||||
func validate_utf8(str string) bool {
|
||||
return utf8.ValidateString(str)
|
||||
}
|
||||
103
vendor/github.com/bytedance/sonic/ast/api_compat.go
generated
vendored
Normal file
103
vendor/github.com/bytedance/sonic/ast/api_compat.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
// +build !amd64,!arm64 go1.26 !go1.17 arm64,!go1.20
|
||||
|
||||
/*
|
||||
* 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 ast
|
||||
|
||||
import (
|
||||
`encoding/json`
|
||||
`unicode/utf8`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/compat`
|
||||
)
|
||||
|
||||
func init() {
|
||||
compat.Warn("sonic/ast")
|
||||
}
|
||||
|
||||
func quote(buf *[]byte, val string) {
|
||||
quoteString(buf, val)
|
||||
}
|
||||
|
||||
func (self *Parser) decodeValue() (val types.JsonState) {
|
||||
e, v := decodeValue(self.s, self.p, self.dbuf == nil)
|
||||
if e < 0 {
|
||||
return v
|
||||
}
|
||||
self.p = e
|
||||
return v
|
||||
}
|
||||
|
||||
func (self *Parser) skip() (int, types.ParsingError) {
|
||||
e, s := skipValue(self.s, self.p)
|
||||
if e < 0 {
|
||||
return self.p, types.ParsingError(-e)
|
||||
}
|
||||
self.p = e
|
||||
return s, 0
|
||||
}
|
||||
|
||||
func (self *Parser) skipFast() (int, types.ParsingError) {
|
||||
e, s := skipValueFast(self.s, self.p)
|
||||
if e < 0 {
|
||||
return self.p, types.ParsingError(-e)
|
||||
}
|
||||
self.p = e
|
||||
return s, 0
|
||||
}
|
||||
|
||||
func (self *Node) encodeInterface(buf *[]byte) error {
|
||||
out, err := json.Marshal(self.packAny())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*buf = append(*buf, out...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Parser) getByPath(validate bool, path ...interface{}) (int, types.ParsingError) {
|
||||
for _, p := range path {
|
||||
if idx, ok := p.(int); ok && idx >= 0 {
|
||||
if err := self.searchIndex(idx); err != 0 {
|
||||
return self.p, err
|
||||
}
|
||||
} else if key, ok := p.(string); ok {
|
||||
if err := self.searchKey(key); err != 0 {
|
||||
return self.p, err
|
||||
}
|
||||
} else {
|
||||
panic("path must be either int(>=0) or string")
|
||||
}
|
||||
}
|
||||
|
||||
var start int
|
||||
var e types.ParsingError
|
||||
if validate {
|
||||
start, e = self.skip()
|
||||
} else {
|
||||
start, e = self.skipFast()
|
||||
}
|
||||
if e != 0 {
|
||||
return self.p, e
|
||||
}
|
||||
return start, 0
|
||||
}
|
||||
|
||||
func validate_utf8(str string) bool {
|
||||
return utf8.ValidString(str)
|
||||
}
|
||||
0
vendor/github.com/bytedance/sonic/ast/asm.s
generated
vendored
Normal file
0
vendor/github.com/bytedance/sonic/ast/asm.s
generated
vendored
Normal file
470
vendor/github.com/bytedance/sonic/ast/buffer.go
generated
vendored
Normal file
470
vendor/github.com/bytedance/sonic/ast/buffer.go
generated
vendored
Normal file
@ -0,0 +1,470 @@
|
||||
/**
|
||||
* 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 ast
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bytedance/sonic/internal/caching"
|
||||
)
|
||||
|
||||
type nodeChunk [_DEFAULT_NODE_CAP]Node
|
||||
|
||||
type linkedNodes struct {
|
||||
head nodeChunk
|
||||
tail []*nodeChunk
|
||||
size int
|
||||
}
|
||||
|
||||
func (self *linkedNodes) Cap() int {
|
||||
if self == nil {
|
||||
return 0
|
||||
}
|
||||
return (len(self.tail)+1)*_DEFAULT_NODE_CAP
|
||||
}
|
||||
|
||||
func (self *linkedNodes) Len() int {
|
||||
if self == nil {
|
||||
return 0
|
||||
}
|
||||
return self.size
|
||||
}
|
||||
|
||||
func (self *linkedNodes) At(i int) (*Node) {
|
||||
if self == nil {
|
||||
return nil
|
||||
}
|
||||
if i >= 0 && i<self.size && i < _DEFAULT_NODE_CAP {
|
||||
return &self.head[i]
|
||||
} else if i >= _DEFAULT_NODE_CAP && i<self.size {
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
if a < len(self.tail) {
|
||||
return &self.tail[a][b]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *linkedNodes) MoveOne(source int, target int) {
|
||||
if source == target {
|
||||
return
|
||||
}
|
||||
if source < 0 || source >= self.size || target < 0 || target >= self.size {
|
||||
return
|
||||
}
|
||||
// reserve source
|
||||
n := *self.At(source)
|
||||
if source < target {
|
||||
// move every element (source,target] one step back
|
||||
for i:=source; i<target; i++ {
|
||||
*self.At(i) = *self.At(i+1)
|
||||
}
|
||||
} else {
|
||||
// move every element [target,source) one step forward
|
||||
for i:=source; i>target; i-- {
|
||||
*self.At(i) = *self.At(i-1)
|
||||
}
|
||||
}
|
||||
// set target
|
||||
*self.At(target) = n
|
||||
}
|
||||
|
||||
func (self *linkedNodes) Pop() {
|
||||
if self == nil || self.size == 0 {
|
||||
return
|
||||
}
|
||||
self.Set(self.size-1, Node{})
|
||||
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
|
||||
if self.size <= i {
|
||||
self.size = i+1
|
||||
}
|
||||
return
|
||||
}
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
if a < 0 {
|
||||
self.head[b] = v
|
||||
} else {
|
||||
self.growTailLength(a+1)
|
||||
var n = &self.tail[a]
|
||||
if *n == nil {
|
||||
*n = new(nodeChunk)
|
||||
}
|
||||
(*n)[b] = v
|
||||
}
|
||||
if self.size <= i {
|
||||
self.size = i+1
|
||||
}
|
||||
}
|
||||
|
||||
func (self *linkedNodes) growTailLength(l int) {
|
||||
if l <= len(self.tail) {
|
||||
return
|
||||
}
|
||||
c := cap(self.tail)
|
||||
for c < l {
|
||||
c += 1 + c>>_APPEND_GROW_SHIFT
|
||||
}
|
||||
if c == cap(self.tail) {
|
||||
self.tail = self.tail[:l]
|
||||
return
|
||||
}
|
||||
tmp := make([]*nodeChunk, l, c)
|
||||
copy(tmp, self.tail)
|
||||
self.tail = tmp
|
||||
}
|
||||
|
||||
func (self *linkedNodes) ToSlice(con []Node) {
|
||||
if len(con) < self.size {
|
||||
return
|
||||
}
|
||||
i := (self.size-1)
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
if a < 0 {
|
||||
copy(con, self.head[:b+1])
|
||||
return
|
||||
} else {
|
||||
copy(con, self.head[:])
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
for i:=0; i<a; i++ {
|
||||
copy(con, self.tail[i][:])
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
copy(con, self.tail[a][:b+1])
|
||||
}
|
||||
|
||||
func (self *linkedNodes) FromSlice(con []Node) {
|
||||
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)
|
||||
return
|
||||
} else {
|
||||
copy(self.head[:], con)
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
if cap(self.tail) <= a {
|
||||
c := (a+1) + (a+1)>>_APPEND_GROW_SHIFT
|
||||
self.tail = make([]*nodeChunk, a+1, c)
|
||||
}
|
||||
self.tail = self.tail[:a+1]
|
||||
|
||||
for i:=0; i<a; i++ {
|
||||
self.tail[i] = new(nodeChunk)
|
||||
copy(self.tail[i][:], con)
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
self.tail[a] = new(nodeChunk)
|
||||
copy(self.tail[a][:b+1], con)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return (len(self.tail)+1)*_DEFAULT_NODE_CAP
|
||||
}
|
||||
|
||||
func (self *linkedPairs) Len() int {
|
||||
if self == nil {
|
||||
return 0
|
||||
}
|
||||
return self.size
|
||||
}
|
||||
|
||||
func (self *linkedPairs) At(i int) *Pair {
|
||||
if self == nil {
|
||||
return nil
|
||||
}
|
||||
if i >= 0 && i < _DEFAULT_NODE_CAP && i<self.size {
|
||||
return &self.head[i]
|
||||
} else if i >= _DEFAULT_NODE_CAP && i<self.size {
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
if a < len(self.tail) {
|
||||
return &self.tail[a][b]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
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 {
|
||||
self.size = i+1
|
||||
}
|
||||
return
|
||||
}
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
if a < 0 {
|
||||
self.head[b] = v
|
||||
} else {
|
||||
self.growTailLength(a+1)
|
||||
var n = &self.tail[a]
|
||||
if *n == nil {
|
||||
*n = new(pairChunk)
|
||||
}
|
||||
(*n)[b] = v
|
||||
}
|
||||
if self.size <= i {
|
||||
self.size = i+1
|
||||
}
|
||||
}
|
||||
|
||||
func (self *linkedPairs) growTailLength(l int) {
|
||||
if l <= len(self.tail) {
|
||||
return
|
||||
}
|
||||
c := cap(self.tail)
|
||||
for c < l {
|
||||
c += 1 + c>>_APPEND_GROW_SHIFT
|
||||
}
|
||||
if c == cap(self.tail) {
|
||||
self.tail = self.tail[:l]
|
||||
return
|
||||
}
|
||||
tmp := make([]*pairChunk, l, c)
|
||||
copy(tmp, self.tail)
|
||||
self.tail = tmp
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
return nil, -1
|
||||
}
|
||||
|
||||
func (self *linkedPairs) ToSlice(con []Pair) {
|
||||
if len(con) < self.size {
|
||||
return
|
||||
}
|
||||
i := self.size-1
|
||||
a, b := i/_DEFAULT_NODE_CAP-1, i%_DEFAULT_NODE_CAP
|
||||
|
||||
if a < 0 {
|
||||
copy(con, self.head[:b+1])
|
||||
return
|
||||
} else {
|
||||
copy(con, self.head[:])
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
for i:=0; i<a; i++ {
|
||||
copy(con, self.tail[i][:])
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
copy(con, self.tail[a][:b+1])
|
||||
}
|
||||
|
||||
func (self *linkedPairs) ToMap(con map[string]Node) {
|
||||
for i:=0; i<self.size; i++ {
|
||||
n := self.At(i)
|
||||
con[n.Key] = n.Value
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
self.copyPairs(self.head[:b+1], con, b+1)
|
||||
return
|
||||
} else {
|
||||
self.copyPairs(self.head[:], con, len(self.head))
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
if cap(self.tail) <= a {
|
||||
c := (a+1) + (a+1)>>_APPEND_GROW_SHIFT
|
||||
self.tail = make([]*pairChunk, a+1, c)
|
||||
}
|
||||
self.tail = self.tail[:a+1]
|
||||
|
||||
for i:=0; i<a; i++ {
|
||||
self.tail[i] = new(pairChunk)
|
||||
self.copyPairs(self.tail[i][:], con, len(self.tail[i]))
|
||||
con = con[_DEFAULT_NODE_CAP:]
|
||||
}
|
||||
|
||||
self.tail[a] = new(pairChunk)
|
||||
self.copyPairs(self.tail[a][:b+1], con, b+1)
|
||||
}
|
||||
|
||||
func (self *linkedPairs) Less(i, j int) bool {
|
||||
return lessFrom(self.At(i).Key, self.At(j).Key, 0)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (self *linkedPairs) Sort() {
|
||||
sort.Stable(self)
|
||||
}
|
||||
|
||||
// Compare two strings from the pos d.
|
||||
func lessFrom(a, b string, d int) bool {
|
||||
l := len(a)
|
||||
if l > len(b) {
|
||||
l = len(b)
|
||||
}
|
||||
for i := d; i < l; i++ {
|
||||
if a[i] == b[i] {
|
||||
continue
|
||||
}
|
||||
return a[i] < b[i]
|
||||
}
|
||||
return len(a) < len(b)
|
||||
}
|
||||
|
||||
type parseObjectStack struct {
|
||||
parser Parser
|
||||
v linkedPairs
|
||||
}
|
||||
|
||||
type parseArrayStack struct {
|
||||
parser Parser
|
||||
v linkedNodes
|
||||
}
|
||||
|
||||
func newLazyArray(p *Parser) Node {
|
||||
s := new(parseArrayStack)
|
||||
s.parser = *p
|
||||
return Node{
|
||||
t: _V_ARRAY_LAZY,
|
||||
p: unsafe.Pointer(s),
|
||||
}
|
||||
}
|
||||
|
||||
func newLazyObject(p *Parser) Node {
|
||||
s := new(parseObjectStack)
|
||||
s.parser = *p
|
||||
return Node{
|
||||
t: _V_OBJECT_LAZY,
|
||||
p: unsafe.Pointer(s),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Node) getParserAndArrayStack() (*Parser, *parseArrayStack) {
|
||||
stack := (*parseArrayStack)(self.p)
|
||||
return &stack.parser, stack
|
||||
}
|
||||
|
||||
func (self *Node) getParserAndObjectStack() (*Parser, *parseObjectStack) {
|
||||
stack := (*parseObjectStack)(self.p)
|
||||
return &stack.parser, stack
|
||||
}
|
||||
|
||||
557
vendor/github.com/bytedance/sonic/ast/decode.go
generated
vendored
Normal file
557
vendor/github.com/bytedance/sonic/ast/decode.go
generated
vendored
Normal file
@ -0,0 +1,557 @@
|
||||
/*
|
||||
* 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 ast
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bytedance/sonic/internal/native/types"
|
||||
"github.com/bytedance/sonic/internal/rt"
|
||||
"github.com/bytedance/sonic/internal/utils"
|
||||
"github.com/bytedance/sonic/unquote"
|
||||
)
|
||||
|
||||
|
||||
var bytesNull = []byte("null")
|
||||
|
||||
const (
|
||||
strNull = "null"
|
||||
bytesTrue = "true"
|
||||
bytesFalse = "false"
|
||||
bytesObject = "{}"
|
||||
bytesArray = "[]"
|
||||
)
|
||||
|
||||
//go:nocheckptr
|
||||
func skipBlank(src string, pos int) int {
|
||||
se := uintptr(rt.IndexChar(src, len(src)))
|
||||
sp := uintptr(rt.IndexChar(src, pos))
|
||||
|
||||
for sp < se {
|
||||
if !utils.IsSpace(*(*byte)(unsafe.Pointer(sp))) {
|
||||
break
|
||||
}
|
||||
sp += 1
|
||||
}
|
||||
if sp >= se {
|
||||
return -int(types.ERR_EOF)
|
||||
}
|
||||
runtime.KeepAlive(src)
|
||||
return int(sp - uintptr(rt.IndexChar(src, 0)))
|
||||
}
|
||||
|
||||
func decodeNull(src string, pos int) (ret int) {
|
||||
ret = pos + 4
|
||||
if ret > len(src) {
|
||||
return -int(types.ERR_EOF)
|
||||
}
|
||||
if src[pos:ret] == strNull {
|
||||
return ret
|
||||
} else {
|
||||
return -int(types.ERR_INVALID_CHAR)
|
||||
}
|
||||
}
|
||||
|
||||
func decodeTrue(src string, pos int) (ret int) {
|
||||
ret = pos + 4
|
||||
if ret > len(src) {
|
||||
return -int(types.ERR_EOF)
|
||||
}
|
||||
if src[pos:ret] == bytesTrue {
|
||||
return ret
|
||||
} else {
|
||||
return -int(types.ERR_INVALID_CHAR)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func decodeFalse(src string, pos int) (ret int) {
|
||||
ret = pos + 5
|
||||
if ret > len(src) {
|
||||
return -int(types.ERR_EOF)
|
||||
}
|
||||
if src[pos:ret] == bytesFalse {
|
||||
return ret
|
||||
}
|
||||
return -int(types.ERR_INVALID_CHAR)
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func decodeString(src string, pos int) (ret int, v string) {
|
||||
ret, ep := skipString(src, pos)
|
||||
if ep == -1 {
|
||||
(*rt.GoString)(unsafe.Pointer(&v)).Ptr = rt.IndexChar(src, pos+1)
|
||||
(*rt.GoString)(unsafe.Pointer(&v)).Len = ret - pos - 2
|
||||
return ret, v
|
||||
}
|
||||
|
||||
result, err := unquote.String(src[pos:ret])
|
||||
if err != 0 {
|
||||
return -int(types.ERR_INVALID_CHAR), ""
|
||||
}
|
||||
|
||||
runtime.KeepAlive(src)
|
||||
return ret, result
|
||||
}
|
||||
|
||||
func decodeBinary(src string, pos int) (ret int, v []byte) {
|
||||
var vv string
|
||||
ret, vv = decodeString(src, pos)
|
||||
if ret < 0 {
|
||||
return ret, nil
|
||||
}
|
||||
var err error
|
||||
v, err = base64.StdEncoding.DecodeString(vv)
|
||||
if err != nil {
|
||||
return -int(types.ERR_INVALID_CHAR), nil
|
||||
}
|
||||
return ret, v
|
||||
}
|
||||
|
||||
func isDigit(c byte) bool {
|
||||
return c >= '0' && c <= '9'
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func decodeInt64(src string, pos int) (ret int, v int64, err error) {
|
||||
sp := uintptr(rt.IndexChar(src, pos))
|
||||
ss := uintptr(sp)
|
||||
se := uintptr(rt.IndexChar(src, len(src)))
|
||||
if uintptr(sp) >= se {
|
||||
return -int(types.ERR_EOF), 0, nil
|
||||
}
|
||||
|
||||
if c := *(*byte)(unsafe.Pointer(sp)); c == '-' {
|
||||
sp += 1
|
||||
}
|
||||
if sp == se {
|
||||
return -int(types.ERR_EOF), 0, nil
|
||||
}
|
||||
|
||||
for ; sp < se; sp += uintptr(1) {
|
||||
if !isDigit(*(*byte)(unsafe.Pointer(sp))) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if sp < se {
|
||||
if c := *(*byte)(unsafe.Pointer(sp)); c == '.' || c == 'e' || c == 'E' {
|
||||
return -int(types.ERR_INVALID_NUMBER_FMT), 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
var vv string
|
||||
ret = int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
|
||||
(*rt.GoString)(unsafe.Pointer(&vv)).Ptr = unsafe.Pointer(ss)
|
||||
(*rt.GoString)(unsafe.Pointer(&vv)).Len = ret - pos
|
||||
|
||||
v, err = strconv.ParseInt(vv, 10, 64)
|
||||
if err != nil {
|
||||
//NOTICE: allow overflow here
|
||||
if err.(*strconv.NumError).Err == strconv.ErrRange {
|
||||
return ret, 0, err
|
||||
}
|
||||
return -int(types.ERR_INVALID_CHAR), 0, err
|
||||
}
|
||||
|
||||
runtime.KeepAlive(src)
|
||||
return ret, v, nil
|
||||
}
|
||||
|
||||
func isNumberChars(c byte) bool {
|
||||
return (c >= '0' && c <= '9') || c == '+' || c == '-' || c == 'e' || c == 'E' || c == '.'
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func decodeFloat64(src string, pos int) (ret int, v float64, err error) {
|
||||
sp := uintptr(rt.IndexChar(src, pos))
|
||||
ss := uintptr(sp)
|
||||
se := uintptr(rt.IndexChar(src, len(src)))
|
||||
if uintptr(sp) >= se {
|
||||
return -int(types.ERR_EOF), 0, nil
|
||||
}
|
||||
|
||||
if c := *(*byte)(unsafe.Pointer(sp)); c == '-' {
|
||||
sp += 1
|
||||
}
|
||||
if sp == se {
|
||||
return -int(types.ERR_EOF), 0, nil
|
||||
}
|
||||
|
||||
for ; sp < se; sp += uintptr(1) {
|
||||
if !isNumberChars(*(*byte)(unsafe.Pointer(sp))) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var vv string
|
||||
ret = int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
|
||||
(*rt.GoString)(unsafe.Pointer(&vv)).Ptr = unsafe.Pointer(ss)
|
||||
(*rt.GoString)(unsafe.Pointer(&vv)).Len = ret - pos
|
||||
|
||||
v, err = strconv.ParseFloat(vv, 64)
|
||||
if err != nil {
|
||||
//NOTICE: allow overflow here
|
||||
if err.(*strconv.NumError).Err == strconv.ErrRange {
|
||||
return ret, 0, err
|
||||
}
|
||||
return -int(types.ERR_INVALID_CHAR), 0, err
|
||||
}
|
||||
|
||||
runtime.KeepAlive(src)
|
||||
return ret, v, nil
|
||||
}
|
||||
|
||||
func decodeValue(src string, pos int, skipnum bool) (ret int, v types.JsonState) {
|
||||
pos = skipBlank(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, types.JsonState{Vt: types.ValueType(pos)}
|
||||
}
|
||||
switch c := src[pos]; c {
|
||||
case 'n':
|
||||
ret = decodeNull(src, pos)
|
||||
if ret < 0 {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
return ret, types.JsonState{Vt: types.V_NULL}
|
||||
case '"':
|
||||
var ep int
|
||||
ret, ep = skipString(src, pos)
|
||||
if ret < 0 {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
return ret, types.JsonState{Vt: types.V_STRING, Iv: int64(pos + 1), Ep: ep}
|
||||
case '{':
|
||||
return pos + 1, types.JsonState{Vt: types.V_OBJECT}
|
||||
case '[':
|
||||
return pos + 1, types.JsonState{Vt: types.V_ARRAY}
|
||||
case 't':
|
||||
ret = decodeTrue(src, pos)
|
||||
if ret < 0 {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
return ret, types.JsonState{Vt: types.V_TRUE}
|
||||
case 'f':
|
||||
ret = decodeFalse(src, pos)
|
||||
if ret < 0 {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
return ret, types.JsonState{Vt: types.V_FALSE}
|
||||
case '-', '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
if skipnum {
|
||||
ret = skipNumber(src, pos)
|
||||
if ret >= 0 {
|
||||
return ret, types.JsonState{Vt: types.V_DOUBLE, Iv: 0, Ep: pos}
|
||||
} else {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
} else {
|
||||
var iv int64
|
||||
ret, iv, _ = decodeInt64(src, pos)
|
||||
if ret >= 0 {
|
||||
return ret, types.JsonState{Vt: types.V_INTEGER, Iv: iv, Ep: pos}
|
||||
} else if ret != -int(types.ERR_INVALID_NUMBER_FMT) {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
var fv float64
|
||||
ret, fv, _ = decodeFloat64(src, pos)
|
||||
if ret >= 0 {
|
||||
return ret, types.JsonState{Vt: types.V_DOUBLE, Dv: fv, Ep: pos}
|
||||
} else {
|
||||
return ret, types.JsonState{Vt: types.ValueType(ret)}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return -int(types.ERR_INVALID_CHAR), types.JsonState{Vt:-types.ValueType(types.ERR_INVALID_CHAR)}
|
||||
}
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func skipNumber(src string, pos int) (ret int) {
|
||||
return utils.SkipNumber(src, pos)
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func skipString(src string, pos int) (ret int, ep int) {
|
||||
if pos+1 >= len(src) {
|
||||
return -int(types.ERR_EOF), -1
|
||||
}
|
||||
|
||||
sp := uintptr(rt.IndexChar(src, pos))
|
||||
se := uintptr(rt.IndexChar(src, len(src)))
|
||||
|
||||
// not start with quote
|
||||
if *(*byte)(unsafe.Pointer(sp)) != '"' {
|
||||
return -int(types.ERR_INVALID_CHAR), -1
|
||||
}
|
||||
sp += 1
|
||||
|
||||
ep = -1
|
||||
for sp < se {
|
||||
c := *(*byte)(unsafe.Pointer(sp))
|
||||
if c == '\\' {
|
||||
if ep == -1 {
|
||||
ep = int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
|
||||
}
|
||||
sp += 2
|
||||
continue
|
||||
}
|
||||
sp += 1
|
||||
if c == '"' {
|
||||
return int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr)), ep
|
||||
}
|
||||
}
|
||||
|
||||
runtime.KeepAlive(src)
|
||||
// not found the closed quote until EOF
|
||||
return -int(types.ERR_EOF), -1
|
||||
}
|
||||
|
||||
//go:nocheckptr
|
||||
func skipPair(src string, pos int, lchar byte, rchar byte) (ret int) {
|
||||
if pos+1 >= len(src) {
|
||||
return -int(types.ERR_EOF)
|
||||
}
|
||||
|
||||
sp := uintptr(rt.IndexChar(src, pos))
|
||||
se := uintptr(rt.IndexChar(src, len(src)))
|
||||
|
||||
if *(*byte)(unsafe.Pointer(sp)) != lchar {
|
||||
return -int(types.ERR_INVALID_CHAR)
|
||||
}
|
||||
|
||||
sp += 1
|
||||
nbrace := 1
|
||||
inquote := false
|
||||
|
||||
for sp < se {
|
||||
c := *(*byte)(unsafe.Pointer(sp))
|
||||
if c == '\\' {
|
||||
sp += 2
|
||||
continue
|
||||
} else if c == '"' {
|
||||
inquote = !inquote
|
||||
} else if c == lchar {
|
||||
if !inquote {
|
||||
nbrace += 1
|
||||
}
|
||||
} else if c == rchar {
|
||||
if !inquote {
|
||||
nbrace -= 1
|
||||
if nbrace == 0 {
|
||||
sp += 1
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
sp += 1
|
||||
}
|
||||
|
||||
if nbrace != 0 {
|
||||
return -int(types.ERR_INVALID_CHAR)
|
||||
}
|
||||
|
||||
runtime.KeepAlive(src)
|
||||
return int(uintptr(sp) - uintptr((*rt.GoString)(unsafe.Pointer(&src)).Ptr))
|
||||
}
|
||||
|
||||
func skipValueFast(src string, pos int) (ret int, start int) {
|
||||
pos = skipBlank(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
switch c := src[pos]; c {
|
||||
case 'n':
|
||||
ret = decodeNull(src, pos)
|
||||
case '"':
|
||||
ret, _ = skipString(src, pos)
|
||||
case '{':
|
||||
ret = skipPair(src, pos, '{', '}')
|
||||
case '[':
|
||||
ret = skipPair(src, pos, '[', ']')
|
||||
case 't':
|
||||
ret = decodeTrue(src, pos)
|
||||
case 'f':
|
||||
ret = decodeFalse(src, pos)
|
||||
case '-', '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
ret = skipNumber(src, pos)
|
||||
default:
|
||||
ret = -int(types.ERR_INVALID_CHAR)
|
||||
}
|
||||
return ret, pos
|
||||
}
|
||||
|
||||
func skipValue(src string, pos int) (ret int, start int) {
|
||||
pos = skipBlank(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
switch c := src[pos]; c {
|
||||
case 'n':
|
||||
ret = decodeNull(src, pos)
|
||||
case '"':
|
||||
ret, _ = skipString(src, pos)
|
||||
case '{':
|
||||
ret, _ = skipObject(src, pos)
|
||||
case '[':
|
||||
ret, _ = skipArray(src, pos)
|
||||
case 't':
|
||||
ret = decodeTrue(src, pos)
|
||||
case 'f':
|
||||
ret = decodeFalse(src, pos)
|
||||
case '-', '+', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
ret = skipNumber(src, pos)
|
||||
default:
|
||||
ret = -int(types.ERR_INVALID_CHAR)
|
||||
}
|
||||
return ret, pos
|
||||
}
|
||||
|
||||
func skipObject(src string, pos int) (ret int, start int) {
|
||||
start = skipBlank(src, pos)
|
||||
if start < 0 {
|
||||
return start, -1
|
||||
}
|
||||
|
||||
if src[start] != '{' {
|
||||
return -int(types.ERR_INVALID_CHAR), -1
|
||||
}
|
||||
|
||||
pos = start + 1
|
||||
pos = skipBlank(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
if src[pos] == '}' {
|
||||
return pos + 1, start
|
||||
}
|
||||
|
||||
for {
|
||||
pos, _ = skipString(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
|
||||
pos = skipBlank(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
if src[pos] != ':' {
|
||||
return -int(types.ERR_INVALID_CHAR), -1
|
||||
}
|
||||
|
||||
pos++
|
||||
pos, _ = skipValue(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
|
||||
pos = skipBlank(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
if src[pos] == '}' {
|
||||
return pos + 1, start
|
||||
}
|
||||
if src[pos] != ',' {
|
||||
return -int(types.ERR_INVALID_CHAR), -1
|
||||
}
|
||||
|
||||
pos++
|
||||
pos = skipBlank(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func skipArray(src string, pos int) (ret int, start int) {
|
||||
start = skipBlank(src, pos)
|
||||
if start < 0 {
|
||||
return start, -1
|
||||
}
|
||||
|
||||
if src[start] != '[' {
|
||||
return -int(types.ERR_INVALID_CHAR), -1
|
||||
}
|
||||
|
||||
pos = start + 1
|
||||
pos = skipBlank(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
if src[pos] == ']' {
|
||||
return pos + 1, start
|
||||
}
|
||||
|
||||
for {
|
||||
pos, _ = skipValue(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
|
||||
pos = skipBlank(src, pos)
|
||||
if pos < 0 {
|
||||
return pos, -1
|
||||
}
|
||||
if src[pos] == ']' {
|
||||
return pos + 1, start
|
||||
}
|
||||
if src[pos] != ',' {
|
||||
return -int(types.ERR_INVALID_CHAR), -1
|
||||
}
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeString decodes a JSON string from pos and return golang string.
|
||||
// - needEsc indicates if to unescaped escaping chars
|
||||
// - hasEsc tells if the returned string has escaping chars
|
||||
// - validStr enables validating UTF8 charset
|
||||
//
|
||||
func _DecodeString(src string, pos int, needEsc bool, validStr bool) (v string, ret int, hasEsc bool) {
|
||||
p := NewParserObj(src)
|
||||
p.p = pos
|
||||
switch val := p.decodeValue(); val.Vt {
|
||||
case types.V_STRING:
|
||||
str := p.s[val.Iv : p.p-1]
|
||||
if validStr && !validate_utf8(str) {
|
||||
return "", -int(types.ERR_INVALID_UTF8), false
|
||||
}
|
||||
/* fast path: no escape sequence */
|
||||
if val.Ep == -1 {
|
||||
return str, p.p, false
|
||||
} else if !needEsc {
|
||||
return str, p.p, true
|
||||
}
|
||||
/* unquote the string */
|
||||
out, err := unquote.String(str)
|
||||
/* check for errors */
|
||||
if err != 0 {
|
||||
return "", -int(err), true
|
||||
} else {
|
||||
return out, p.p, true
|
||||
}
|
||||
default:
|
||||
return "", -int(_ERR_UNSUPPORT_TYPE), false
|
||||
}
|
||||
}
|
||||
280
vendor/github.com/bytedance/sonic/ast/encode.go
generated
vendored
Normal file
280
vendor/github.com/bytedance/sonic/ast/encode.go
generated
vendored
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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 ast
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/bytedance/gopkg/lang/dirtmake"
|
||||
"github.com/bytedance/sonic/internal/rt"
|
||||
"github.com/bytedance/sonic/option"
|
||||
)
|
||||
|
||||
func quoteString(e *[]byte, s string) {
|
||||
*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 c == utf8.RuneError && size == 1 {
|
||||
// if start < i {
|
||||
// e.Write(s[start:i])
|
||||
// }
|
||||
// e.WriteString(`\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, '"')
|
||||
}
|
||||
|
||||
var bytesPool = sync.Pool{}
|
||||
|
||||
func (self *Node) MarshalJSON() ([]byte, error) {
|
||||
if self == nil {
|
||||
return bytesNull, nil
|
||||
}
|
||||
|
||||
// fast path for raw node
|
||||
if self.isRaw() {
|
||||
return rt.Str2Mem(self.toString()), nil
|
||||
}
|
||||
|
||||
buf := newBuffer()
|
||||
err := self.encode(buf)
|
||||
if err != nil {
|
||||
freeBuffer(buf)
|
||||
return nil, err
|
||||
}
|
||||
var ret []byte
|
||||
if !rt.CanSizeResue(cap(*buf)) {
|
||||
ret = *buf
|
||||
} else {
|
||||
ret = dirtmake.Bytes(len(*buf), len(*buf))
|
||||
copy(ret, *buf)
|
||||
freeBuffer(buf)
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func newBuffer() *[]byte {
|
||||
if ret := bytesPool.Get(); ret != nil {
|
||||
return ret.(*[]byte)
|
||||
} else {
|
||||
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() {
|
||||
return self.encodeRaw(buf)
|
||||
}
|
||||
switch int(self.itype()) {
|
||||
case V_NONE : return ErrNotExist
|
||||
case V_ERROR : return self.Check()
|
||||
case V_NULL : return self.encodeNull(buf)
|
||||
case V_TRUE : return self.encodeTrue(buf)
|
||||
case V_FALSE : return self.encodeFalse(buf)
|
||||
case V_ARRAY : return self.encodeArray(buf)
|
||||
case V_OBJECT: return self.encodeObject(buf)
|
||||
case V_STRING: return self.encodeString(buf)
|
||||
case V_NUMBER: return self.encodeNumber(buf)
|
||||
case V_ANY : return self.encodeInterface(buf)
|
||||
default : return ErrUnsupportType
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Node) encodeRaw(buf *[]byte) error {
|
||||
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, strNull...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeTrue(buf *[]byte) error {
|
||||
*buf = append(*buf, bytesTrue...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeFalse(buf *[]byte) error {
|
||||
*buf = append(*buf, bytesFalse...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeNumber(buf *[]byte) error {
|
||||
str := self.toString()
|
||||
*buf = append(*buf, str...)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeString(buf *[]byte) error {
|
||||
if self.l == 0 {
|
||||
*buf = append(*buf, '"', '"')
|
||||
return nil
|
||||
}
|
||||
|
||||
quote(buf, self.toString())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Node) encodeArray(buf *[]byte) error {
|
||||
if self.isLazy() {
|
||||
if err := self.skipAllIndex(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
nb := self.len()
|
||||
if nb == 0 {
|
||||
*buf = append(*buf, bytesArray...)
|
||||
return nil
|
||||
}
|
||||
|
||||
*buf = append(*buf, '[')
|
||||
|
||||
var started bool
|
||||
for i := 0; i < nb; i++ {
|
||||
n := self.nodeAt(i)
|
||||
if !n.Exists() {
|
||||
continue
|
||||
}
|
||||
if started {
|
||||
*buf = append(*buf, ',')
|
||||
}
|
||||
started = true
|
||||
if err := n.encode(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*buf = append(*buf, ']')
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Pair) encode(buf *[]byte) error {
|
||||
if len(*buf) == 0 {
|
||||
*buf = append(*buf, '"', '"', ':')
|
||||
return self.Value.encode(buf)
|
||||
}
|
||||
|
||||
quote(buf, self.Key)
|
||||
*buf = append(*buf, ':')
|
||||
|
||||
return self.Value.encode(buf)
|
||||
}
|
||||
|
||||
func (self *Node) encodeObject(buf *[]byte) error {
|
||||
if self.isLazy() {
|
||||
if err := self.skipAllKey(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
nb := self.len()
|
||||
if nb == 0 {
|
||||
*buf = append(*buf, bytesObject...)
|
||||
return nil
|
||||
}
|
||||
|
||||
*buf = append(*buf, '{')
|
||||
|
||||
var started bool
|
||||
for i := 0; i < nb; i++ {
|
||||
n := self.pairAt(i)
|
||||
if n == nil || !n.Value.Exists() {
|
||||
continue
|
||||
}
|
||||
if started {
|
||||
*buf = append(*buf, ',')
|
||||
}
|
||||
started = true
|
||||
if err := n.encode(buf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*buf = append(*buf, '}')
|
||||
return nil
|
||||
}
|
||||
134
vendor/github.com/bytedance/sonic/ast/error.go
generated
vendored
Normal file
134
vendor/github.com/bytedance/sonic/ast/error.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`strings`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
)
|
||||
|
||||
|
||||
func newError(err types.ParsingError, msg string) *Node {
|
||||
return &Node{
|
||||
t: V_ERROR,
|
||||
l: uint(err),
|
||||
p: unsafe.Pointer(&msg),
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
return ""
|
||||
} else {
|
||||
return *(*string)(self.p)
|
||||
}
|
||||
}
|
||||
|
||||
func newSyntaxError(err SyntaxError) *Node {
|
||||
msg := err.Description()
|
||||
return &Node{
|
||||
t: V_ERROR,
|
||||
l: uint(err.Code),
|
||||
p: unsafe.Pointer(&msg),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) syntaxError(err types.ParsingError) SyntaxError {
|
||||
return SyntaxError{
|
||||
Pos : self.p,
|
||||
Src : self.s,
|
||||
Code: err,
|
||||
}
|
||||
}
|
||||
|
||||
func unwrapError(err error) *Node {
|
||||
if se, ok := err.(*Node); ok {
|
||||
return se
|
||||
}else if sse, ok := err.(Node); ok {
|
||||
return &sse
|
||||
} else {
|
||||
msg := err.Error()
|
||||
return &Node{
|
||||
t: V_ERROR,
|
||||
p: unsafe.Pointer(&msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SyntaxError struct {
|
||||
Pos int
|
||||
Src string
|
||||
Code types.ParsingError
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (self SyntaxError) Error() string {
|
||||
return fmt.Sprintf("%q", self.Description())
|
||||
}
|
||||
|
||||
func (self SyntaxError) Description() string {
|
||||
return "Syntax error " + self.description()
|
||||
}
|
||||
|
||||
func (self SyntaxError) description() string {
|
||||
i := 16
|
||||
p := self.Pos - i
|
||||
q := self.Pos + i
|
||||
|
||||
/* check for empty source */
|
||||
if self.Src == "" {
|
||||
return fmt.Sprintf("no sources available, the input json is empty: %#v", self)
|
||||
}
|
||||
|
||||
/* prevent slicing before the beginning */
|
||||
if p < 0 {
|
||||
p, q, i = 0, q - p, i + p
|
||||
}
|
||||
|
||||
/* prevent slicing beyond the end */
|
||||
if n := len(self.Src); q > n {
|
||||
n = q - n
|
||||
q = len(self.Src)
|
||||
|
||||
/* move the left bound if possible */
|
||||
if p > n {
|
||||
i += n
|
||||
p -= n
|
||||
}
|
||||
}
|
||||
|
||||
/* left and right length */
|
||||
x := clamp_zero(i)
|
||||
y := clamp_zero(q - p - i - 1)
|
||||
|
||||
/* compose the error description */
|
||||
return fmt.Sprintf(
|
||||
"at index %d: %s\n\n\t%s\n\t%s^%s\n",
|
||||
self.Pos,
|
||||
self.Message(),
|
||||
self.Src[p:q],
|
||||
strings.Repeat(".", x),
|
||||
strings.Repeat(".", y),
|
||||
)
|
||||
}
|
||||
|
||||
func (self SyntaxError) Message() string {
|
||||
if self.Msg == "" {
|
||||
return self.Code.Message()
|
||||
}
|
||||
return self.Msg
|
||||
}
|
||||
|
||||
func clamp_zero(v int) int {
|
||||
if v < 0 {
|
||||
return 0
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
216
vendor/github.com/bytedance/sonic/ast/iterator.go
generated
vendored
Normal file
216
vendor/github.com/bytedance/sonic/ast/iterator.go
generated
vendored
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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 ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"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); err != nil {
|
||||
return ListIterator{}, err
|
||||
}
|
||||
return self.values(), nil
|
||||
}
|
||||
|
||||
func (self *Node) values() ListIterator {
|
||||
return ListIterator{Iterator{p: self}}
|
||||
}
|
||||
|
||||
// Properties returns iterator for object's children traversal
|
||||
func (self *Node) Properties() (ObjectIterator, error) {
|
||||
if err := self.should(types.V_OBJECT); err != nil {
|
||||
return ObjectIterator{}, err
|
||||
}
|
||||
return self.properties(), nil
|
||||
}
|
||||
|
||||
func (self *Node) properties() ObjectIterator {
|
||||
return ObjectIterator{Iterator{p: self}}
|
||||
}
|
||||
|
||||
type Iterator struct {
|
||||
i int
|
||||
p *Node
|
||||
}
|
||||
|
||||
func (self *Iterator) Pos() int {
|
||||
return self.i
|
||||
}
|
||||
|
||||
func (self *Iterator) Len() int {
|
||||
return self.p.len()
|
||||
}
|
||||
|
||||
// HasNext reports if it is the end of iteration or has error.
|
||||
func (self *Iterator) HasNext() bool {
|
||||
if !self.p.isLazy() {
|
||||
return self.p.Valid() && self.i < self.p.len()
|
||||
} else if self.p.t == _V_ARRAY_LAZY {
|
||||
return self.p.skipNextNode().Valid()
|
||||
} else if self.p.t == _V_OBJECT_LAZY {
|
||||
pair := self.p.skipNextPair()
|
||||
if pair == nil {
|
||||
return false
|
||||
}
|
||||
return pair.Value.Valid()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ListIterator is specialized iterator for V_ARRAY
|
||||
type ListIterator struct {
|
||||
Iterator
|
||||
}
|
||||
|
||||
// ObjectIterator is specialized iterator for V_ARRAY
|
||||
type ObjectIterator struct {
|
||||
Iterator
|
||||
}
|
||||
|
||||
func (self *ListIterator) next() *Node {
|
||||
next_start:
|
||||
if !self.HasNext() {
|
||||
return nil
|
||||
} else {
|
||||
n := self.p.nodeAt(self.i)
|
||||
self.i++
|
||||
if !n.Exists() {
|
||||
goto next_start
|
||||
}
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
// Next scans through children of underlying V_ARRAY,
|
||||
// copies each child to v, and returns .HasNext().
|
||||
func (self *ListIterator) Next(v *Node) bool {
|
||||
n := self.next()
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
*v = *n
|
||||
return true
|
||||
}
|
||||
|
||||
func (self *ObjectIterator) next() *Pair {
|
||||
next_start:
|
||||
if !self.HasNext() {
|
||||
return nil
|
||||
} else {
|
||||
n := self.p.pairAt(self.i)
|
||||
self.i++
|
||||
if n == nil || !n.Value.Exists() {
|
||||
goto next_start
|
||||
}
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
// Next scans through children of underlying V_OBJECT,
|
||||
// copies each child to v, and returns .HasNext().
|
||||
func (self *ObjectIterator) Next(p *Pair) bool {
|
||||
n := self.next()
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
*p = *n
|
||||
return true
|
||||
}
|
||||
|
||||
// Sequence represents scanning path of single-layer nodes.
|
||||
// Index indicates the value's order in both V_ARRAY and V_OBJECT json.
|
||||
// Key is the value's key (for V_OBJECT json only, otherwise it will be nil).
|
||||
type Sequence struct {
|
||||
Index int
|
||||
Key *string
|
||||
// Level int
|
||||
}
|
||||
|
||||
// String is string representation of one Sequence
|
||||
func (s Sequence) String() string {
|
||||
k := ""
|
||||
if s.Key != nil {
|
||||
k = *s.Key
|
||||
}
|
||||
return fmt.Sprintf("Sequence(%d, %q)", s.Index, k)
|
||||
}
|
||||
|
||||
type Scanner func(path Sequence, node *Node) bool
|
||||
|
||||
// ForEach scans one V_OBJECT node's children from JSON head to tail,
|
||||
// and pass the Sequence and Node of corresponding JSON value.
|
||||
//
|
||||
// Especially, if the node is not V_ARRAY or V_OBJECT,
|
||||
// the node itself will be returned and Sequence.Index == -1.
|
||||
//
|
||||
// NOTICE: An unset 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()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := iter.next()
|
||||
for v != nil {
|
||||
if !sc(Sequence{iter.i-1, nil}, v) {
|
||||
return nil
|
||||
}
|
||||
v = iter.next()
|
||||
}
|
||||
case types.V_OBJECT:
|
||||
iter, err := self.Properties()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v := iter.next()
|
||||
for v != nil {
|
||||
if !sc(Sequence{iter.i-1, &v.Key}, &v.Value) {
|
||||
return nil
|
||||
}
|
||||
v = iter.next()
|
||||
}
|
||||
default:
|
||||
if self.Check() != nil {
|
||||
return self
|
||||
}
|
||||
sc(Sequence{-1, nil}, self)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
1860
vendor/github.com/bytedance/sonic/ast/node.go
generated
vendored
Normal file
1860
vendor/github.com/bytedance/sonic/ast/node.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
768
vendor/github.com/bytedance/sonic/ast/parser.go
generated
vendored
Normal file
768
vendor/github.com/bytedance/sonic/ast/parser.go
generated
vendored
Normal file
@ -0,0 +1,768 @@
|
||||
/*
|
||||
* 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 ast
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/bytedance/sonic/internal/native/types"
|
||||
"github.com/bytedance/sonic/internal/rt"
|
||||
"github.com/bytedance/sonic/internal/utils"
|
||||
"github.com/bytedance/sonic/unquote"
|
||||
)
|
||||
|
||||
const (
|
||||
_DEFAULT_NODE_CAP int = 16
|
||||
_APPEND_GROW_SHIFT = 1
|
||||
)
|
||||
|
||||
const (
|
||||
_ERR_NOT_FOUND types.ParsingError = 33
|
||||
_ERR_UNSUPPORT_TYPE types.ParsingError = 34
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotExist means both key and value doesn't exist
|
||||
ErrNotExist error = newError(_ERR_NOT_FOUND, "value not exists")
|
||||
|
||||
// ErrUnsupportType means API on the node is unsupported
|
||||
ErrUnsupportType error = newError(_ERR_UNSUPPORT_TYPE, "unsupported type")
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
p int
|
||||
s string
|
||||
noLazy bool
|
||||
loadOnce bool
|
||||
skipValue bool
|
||||
dbuf *byte
|
||||
}
|
||||
|
||||
/** Parser Private Methods **/
|
||||
|
||||
func (self *Parser) delim() types.ParsingError {
|
||||
n := len(self.s)
|
||||
p := self.lspace(self.p)
|
||||
|
||||
/* check for EOF */
|
||||
if p >= n {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the delimiter */
|
||||
if self.s[p] != ':' {
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
|
||||
/* update the read pointer */
|
||||
self.p = p + 1
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *Parser) object() types.ParsingError {
|
||||
n := len(self.s)
|
||||
p := self.lspace(self.p)
|
||||
|
||||
/* check for EOF */
|
||||
if p >= n {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the delimiter */
|
||||
if self.s[p] != '{' {
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
|
||||
/* update the read pointer */
|
||||
self.p = p + 1
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *Parser) array() types.ParsingError {
|
||||
n := len(self.s)
|
||||
p := self.lspace(self.p)
|
||||
|
||||
/* check for EOF */
|
||||
if p >= n {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the delimiter */
|
||||
if self.s[p] != '[' {
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
|
||||
/* update the read pointer */
|
||||
self.p = p + 1
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *Parser) lspace(sp int) int {
|
||||
ns := len(self.s)
|
||||
for ; sp<ns && utils.IsSpace(self.s[sp]); sp+=1 {}
|
||||
|
||||
return sp
|
||||
}
|
||||
|
||||
func (self *Parser) backward() {
|
||||
for ; self.p >= 0 && utils.IsSpace(self.s[self.p]); self.p-=1 {}
|
||||
}
|
||||
|
||||
func (self *Parser) decodeArray(ret *linkedNodes) (Node, types.ParsingError) {
|
||||
sp := self.p
|
||||
ns := len(self.s)
|
||||
|
||||
/* check for EOF */
|
||||
if self.p = self.lspace(sp); self.p >= ns {
|
||||
return Node{}, types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for empty array */
|
||||
if self.s[self.p] == ']' {
|
||||
self.p++
|
||||
return Node{t: types.V_ARRAY}, 0
|
||||
}
|
||||
|
||||
/* allocate array space and parse every element */
|
||||
for {
|
||||
var val Node
|
||||
var err types.ParsingError
|
||||
|
||||
if self.skipValue {
|
||||
/* skip the value */
|
||||
var start int
|
||||
if start, err = self.skipFast(); err != 0 {
|
||||
return Node{}, err
|
||||
}
|
||||
if self.p > ns {
|
||||
return Node{}, types.ERR_EOF
|
||||
}
|
||||
t := switchRawType(self.s[start])
|
||||
if t == _V_NONE {
|
||||
return Node{}, types.ERR_INVALID_CHAR
|
||||
}
|
||||
val = newRawNode(self.s[start:self.p], t, false)
|
||||
}else{
|
||||
/* decode the value */
|
||||
if val, err = self.Parse(); err != 0 {
|
||||
return Node{}, err
|
||||
}
|
||||
}
|
||||
|
||||
/* add the value to result */
|
||||
ret.Push(val)
|
||||
self.p = self.lspace(self.p)
|
||||
|
||||
/* check for EOF */
|
||||
if self.p >= ns {
|
||||
return Node{}, types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the next character */
|
||||
switch self.s[self.p] {
|
||||
case ',' : self.p++
|
||||
case ']' : self.p++; return newArray(ret), 0
|
||||
default:
|
||||
// if val.isLazy() {
|
||||
// return newLazyArray(self, ret), 0
|
||||
// }
|
||||
return Node{}, types.ERR_INVALID_CHAR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) decodeObject(ret *linkedPairs) (Node, types.ParsingError) {
|
||||
sp := self.p
|
||||
ns := len(self.s)
|
||||
|
||||
/* check for EOF */
|
||||
if self.p = self.lspace(sp); self.p >= ns {
|
||||
return Node{}, types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for empty object */
|
||||
if self.s[self.p] == '}' {
|
||||
self.p++
|
||||
return Node{t: types.V_OBJECT}, 0
|
||||
}
|
||||
|
||||
/* decode each pair */
|
||||
for {
|
||||
var val Node
|
||||
var njs types.JsonState
|
||||
var err types.ParsingError
|
||||
|
||||
/* decode the key */
|
||||
if njs = self.decodeValue(); njs.Vt != types.V_STRING {
|
||||
return Node{}, types.ERR_INVALID_CHAR
|
||||
}
|
||||
|
||||
/* extract the key */
|
||||
idx := self.p - 1
|
||||
key := self.s[njs.Iv:idx]
|
||||
|
||||
/* check for escape sequence */
|
||||
if njs.Ep != -1 {
|
||||
if key, err = unquote.String(key); err != 0 {
|
||||
return Node{}, err
|
||||
}
|
||||
}
|
||||
|
||||
/* expect a ':' delimiter */
|
||||
if err = self.delim(); err != 0 {
|
||||
return Node{}, err
|
||||
}
|
||||
|
||||
|
||||
if self.skipValue {
|
||||
/* skip the value */
|
||||
var start int
|
||||
if start, err = self.skipFast(); err != 0 {
|
||||
return Node{}, err
|
||||
}
|
||||
if self.p > ns {
|
||||
return Node{}, types.ERR_EOF
|
||||
}
|
||||
t := switchRawType(self.s[start])
|
||||
if t == _V_NONE {
|
||||
return Node{}, types.ERR_INVALID_CHAR
|
||||
}
|
||||
val = newRawNode(self.s[start:self.p], t, false)
|
||||
} else {
|
||||
/* decode the value */
|
||||
if val, err = self.Parse(); err != 0 {
|
||||
return Node{}, err
|
||||
}
|
||||
}
|
||||
|
||||
/* add the value to result */
|
||||
// FIXME: ret's address may change here, thus previous referred node in ret may be invalid !!
|
||||
ret.Push(NewPair(key, val))
|
||||
self.p = self.lspace(self.p)
|
||||
|
||||
/* check for EOF */
|
||||
if self.p >= ns {
|
||||
return Node{}, types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the next character */
|
||||
switch self.s[self.p] {
|
||||
case ',' : self.p++
|
||||
case '}' : self.p++; return newObject(ret), 0
|
||||
default:
|
||||
// if val.isLazy() {
|
||||
// return newLazyObject(self, ret), 0
|
||||
// }
|
||||
return Node{}, types.ERR_INVALID_CHAR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) decodeString(iv int64, ep int) (Node, types.ParsingError) {
|
||||
p := self.p - 1
|
||||
s := self.s[iv:p]
|
||||
|
||||
/* fast path: no escape sequence */
|
||||
if ep == -1 {
|
||||
return NewString(s), 0
|
||||
}
|
||||
|
||||
/* unquote the string */
|
||||
out, err := unquote.String(s)
|
||||
|
||||
/* check for errors */
|
||||
if err != 0 {
|
||||
return Node{}, err
|
||||
} else {
|
||||
return newBytes(rt.Str2Mem(out)), 0
|
||||
}
|
||||
}
|
||||
|
||||
/** Parser Interface **/
|
||||
|
||||
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
|
||||
case types.V_NULL : return nullNode, 0
|
||||
case types.V_TRUE : return trueNode, 0
|
||||
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
|
||||
default : return Node{}, types.ParsingError(-val.Vt)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) searchKey(match string) types.ParsingError {
|
||||
ns := len(self.s)
|
||||
if err := self.object(); err != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
/* check for EOF */
|
||||
if self.p = self.lspace(self.p); self.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for empty object */
|
||||
if self.s[self.p] == '}' {
|
||||
self.p++
|
||||
return _ERR_NOT_FOUND
|
||||
}
|
||||
|
||||
var njs types.JsonState
|
||||
var err types.ParsingError
|
||||
/* decode each pair */
|
||||
for {
|
||||
|
||||
/* decode the key */
|
||||
if njs = self.decodeValue(); njs.Vt != types.V_STRING {
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
|
||||
/* extract the key */
|
||||
idx := self.p - 1
|
||||
key := self.s[njs.Iv:idx]
|
||||
|
||||
/* check for escape sequence */
|
||||
if njs.Ep != -1 {
|
||||
if key, err = unquote.String(key); err != 0 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
/* expect a ':' delimiter */
|
||||
if err = self.delim(); err != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
/* skip value */
|
||||
if key != match {
|
||||
if _, err = self.skipFast(); err != 0 {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
|
||||
/* check for EOF */
|
||||
self.p = self.lspace(self.p)
|
||||
if self.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the next character */
|
||||
switch self.s[self.p] {
|
||||
case ',':
|
||||
self.p++
|
||||
case '}':
|
||||
self.p++
|
||||
return _ERR_NOT_FOUND
|
||||
default:
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Parser) searchIndex(idx int) types.ParsingError {
|
||||
ns := len(self.s)
|
||||
if err := self.array(); err != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
/* check for EOF */
|
||||
if self.p = self.lspace(self.p); self.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for empty array */
|
||||
if self.s[self.p] == ']' {
|
||||
self.p++
|
||||
return _ERR_NOT_FOUND
|
||||
}
|
||||
|
||||
var err types.ParsingError
|
||||
/* allocate array space and parse every element */
|
||||
for i := 0; i < idx; i++ {
|
||||
|
||||
/* decode the value */
|
||||
if _, err = self.skipFast(); err != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
/* check for EOF */
|
||||
self.p = self.lspace(self.p)
|
||||
if self.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the next character */
|
||||
switch self.s[self.p] {
|
||||
case ',':
|
||||
self.p++
|
||||
case ']':
|
||||
self.p++
|
||||
return _ERR_NOT_FOUND
|
||||
default:
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *Node) skipNextNode() *Node {
|
||||
if !self.isLazy() {
|
||||
return nil
|
||||
}
|
||||
|
||||
parser, stack := self.getParserAndArrayStack()
|
||||
ret := &stack.v
|
||||
sp := parser.p
|
||||
ns := len(parser.s)
|
||||
|
||||
/* check for EOF */
|
||||
if parser.p = parser.lspace(sp); parser.p >= ns {
|
||||
return newSyntaxError(parser.syntaxError(types.ERR_EOF))
|
||||
}
|
||||
|
||||
/* check for empty array */
|
||||
if parser.s[parser.p] == ']' {
|
||||
parser.p++
|
||||
self.setArray(ret)
|
||||
return nil
|
||||
}
|
||||
|
||||
var val Node
|
||||
/* skip the value */
|
||||
if start, err := parser.skipFast(); err != 0 {
|
||||
return newSyntaxError(parser.syntaxError(err))
|
||||
} else {
|
||||
t := switchRawType(parser.s[start])
|
||||
if t == _V_NONE {
|
||||
return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))
|
||||
}
|
||||
val = newRawNode(parser.s[start:parser.p], t, false)
|
||||
}
|
||||
|
||||
/* add the value to result */
|
||||
ret.Push(val)
|
||||
self.l++
|
||||
parser.p = parser.lspace(parser.p)
|
||||
|
||||
/* check for EOF */
|
||||
if parser.p >= ns {
|
||||
return newSyntaxError(parser.syntaxError(types.ERR_EOF))
|
||||
}
|
||||
|
||||
/* check for the next character */
|
||||
switch parser.s[parser.p] {
|
||||
case ',':
|
||||
parser.p++
|
||||
return ret.At(ret.Len()-1)
|
||||
case ']':
|
||||
parser.p++
|
||||
self.setArray(ret)
|
||||
return ret.At(ret.Len()-1)
|
||||
default:
|
||||
return newSyntaxError(parser.syntaxError(types.ERR_INVALID_CHAR))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Node) skipNextPair() (*Pair) {
|
||||
if !self.isLazy() {
|
||||
return nil
|
||||
}
|
||||
|
||||
parser, stack := self.getParserAndObjectStack()
|
||||
ret := &stack.v
|
||||
sp := parser.p
|
||||
ns := len(parser.s)
|
||||
|
||||
/* check for EOF */
|
||||
if parser.p = parser.lspace(sp); parser.p >= ns {
|
||||
return newErrorPair(parser.syntaxError(types.ERR_EOF))
|
||||
}
|
||||
|
||||
/* check for empty object */
|
||||
if parser.s[parser.p] == '}' {
|
||||
parser.p++
|
||||
self.setObject(ret)
|
||||
return nil
|
||||
}
|
||||
|
||||
/* decode one pair */
|
||||
var val Node
|
||||
var njs types.JsonState
|
||||
var err types.ParsingError
|
||||
|
||||
/* decode the key */
|
||||
if njs = parser.decodeValue(); njs.Vt != types.V_STRING {
|
||||
return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
|
||||
}
|
||||
|
||||
/* extract the key */
|
||||
idx := parser.p - 1
|
||||
key := parser.s[njs.Iv:idx]
|
||||
|
||||
/* check for escape sequence */
|
||||
if njs.Ep != -1 {
|
||||
if key, err = unquote.String(key); err != 0 {
|
||||
return newErrorPair(parser.syntaxError(err))
|
||||
}
|
||||
}
|
||||
|
||||
/* expect a ':' delimiter */
|
||||
if err = parser.delim(); err != 0 {
|
||||
return newErrorPair(parser.syntaxError(err))
|
||||
}
|
||||
|
||||
/* skip the value */
|
||||
if start, err := parser.skipFast(); err != 0 {
|
||||
return newErrorPair(parser.syntaxError(err))
|
||||
} else {
|
||||
t := switchRawType(parser.s[start])
|
||||
if t == _V_NONE {
|
||||
return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
|
||||
}
|
||||
val = newRawNode(parser.s[start:parser.p], t, false)
|
||||
}
|
||||
|
||||
/* add the value to result */
|
||||
ret.Push(NewPair(key, val))
|
||||
self.l++
|
||||
parser.p = parser.lspace(parser.p)
|
||||
|
||||
/* check for EOF */
|
||||
if parser.p >= ns {
|
||||
return newErrorPair(parser.syntaxError(types.ERR_EOF))
|
||||
}
|
||||
|
||||
/* check for the next character */
|
||||
switch parser.s[parser.p] {
|
||||
case ',':
|
||||
parser.p++
|
||||
return ret.At(ret.Len()-1)
|
||||
case '}':
|
||||
parser.p++
|
||||
self.setObject(ret)
|
||||
return ret.At(ret.Len()-1)
|
||||
default:
|
||||
return newErrorPair(parser.syntaxError(types.ERR_INVALID_CHAR))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Parser Factory **/
|
||||
|
||||
// Loads parse all json into interface{}
|
||||
func Loads(src string) (int, interface{}, error) {
|
||||
ps := &Parser{s: src}
|
||||
np, err := ps.Parse()
|
||||
|
||||
/* check for errors */
|
||||
if err != 0 {
|
||||
return 0, nil, ps.ExportError(err)
|
||||
} else {
|
||||
x, err := np.Interface()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return ps.Pos(), x, nil
|
||||
}
|
||||
}
|
||||
|
||||
// LoadsUseNumber parse all json into interface{}, with numeric nodes cast to json.Number
|
||||
func LoadsUseNumber(src string) (int, interface{}, error) {
|
||||
ps := &Parser{s: src}
|
||||
np, err := ps.Parse()
|
||||
|
||||
/* check for errors */
|
||||
if err != 0 {
|
||||
return 0, nil, err
|
||||
} else {
|
||||
x, err := np.InterfaceUseNumber()
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return ps.Pos(), x, nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewParser returns pointer of new allocated parser
|
||||
func NewParser(src string) *Parser {
|
||||
return &Parser{s: src}
|
||||
}
|
||||
|
||||
// NewParser returns new allocated parser
|
||||
func NewParserObj(src string) Parser {
|
||||
return Parser{s: src}
|
||||
}
|
||||
|
||||
// decodeNumber controls if parser decodes the number values instead of skip them
|
||||
// WARN: once you set decodeNumber(true), please set decodeNumber(false) before you drop the parser
|
||||
// otherwise the memory CANNOT be reused
|
||||
func (self *Parser) decodeNumber(decode bool) {
|
||||
if !decode && self.dbuf != nil {
|
||||
types.FreeDbuf(self.dbuf)
|
||||
self.dbuf = nil
|
||||
return
|
||||
}
|
||||
if decode && self.dbuf == nil {
|
||||
self.dbuf = types.NewDbuf()
|
||||
}
|
||||
}
|
||||
|
||||
// ExportError converts types.ParsingError to std Error
|
||||
func (self *Parser) ExportError(err types.ParsingError) error {
|
||||
if err == _ERR_NOT_FOUND {
|
||||
return ErrNotExist
|
||||
}
|
||||
return fmt.Errorf("%q", SyntaxError{
|
||||
Pos : self.p,
|
||||
Src : self.s,
|
||||
Code: err,
|
||||
}.Description())
|
||||
}
|
||||
|
||||
func backward(src string, i int) int {
|
||||
for ; i>=0 && utils.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()
|
||||
}
|
||||
}
|
||||
157
vendor/github.com/bytedance/sonic/ast/search.go
generated
vendored
Normal file
157
vendor/github.com/bytedance/sonic/ast/search.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
/*
|
||||
* 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 ast
|
||||
|
||||
import (
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
`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 {
|
||||
return &Searcher{
|
||||
parser: Parser{
|
||||
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) {
|
||||
self.CopyReturn = true
|
||||
return self.getByPath(path...)
|
||||
}
|
||||
|
||||
// GetByPathNoCopy search in depth from top json and returns a **Referenced** json node at the path location
|
||||
//
|
||||
// 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(path...)
|
||||
}
|
||||
|
||||
func (self *Searcher) getByPath(path ...interface{}) (Node, error) {
|
||||
var err types.ParsingError
|
||||
var start int
|
||||
|
||||
self.parser.p = 0
|
||||
start, err = self.parser.getByPath(self.ValidateJSON, path...)
|
||||
if err != 0 {
|
||||
// for compatibility with old version
|
||||
if err == types.ERR_NOT_FOUND {
|
||||
return Node{}, ErrNotExist
|
||||
}
|
||||
if err == types.ERR_UNSUPPORT_TYPE {
|
||||
panic("path must be either int(>=0) or string")
|
||||
}
|
||||
return Node{}, self.parser.syntaxError(err)
|
||||
}
|
||||
|
||||
t := switchRawType(self.parser.s[start])
|
||||
if t == _V_NONE {
|
||||
return Node{}, self.parser.ExportError(err)
|
||||
}
|
||||
|
||||
// copy string to reducing memory usage
|
||||
var raw string
|
||||
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, self.ConcurrentRead), nil
|
||||
}
|
||||
|
||||
// GetByPath searches a path and returns relaction and types of target
|
||||
func _GetByPath(src string, path ...interface{}) (start int, end int, typ int, err error) {
|
||||
p := NewParserObj(src)
|
||||
s, e := p.getByPath(false, path...)
|
||||
if e != 0 {
|
||||
// for compatibility with old version
|
||||
if e == types.ERR_NOT_FOUND {
|
||||
return -1, -1, 0, ErrNotExist
|
||||
}
|
||||
if e == types.ERR_UNSUPPORT_TYPE {
|
||||
panic("path must be either int(>=0) or string")
|
||||
}
|
||||
return -1, -1, 0, p.syntaxError(e)
|
||||
}
|
||||
|
||||
t := switchRawType(p.s[s])
|
||||
if t == _V_NONE {
|
||||
return -1, -1, 0, ErrNotExist
|
||||
}
|
||||
if t == _V_NUMBER {
|
||||
p.p = 1 + backward(p.s, p.p-1)
|
||||
}
|
||||
return s, p.p, int(t), nil
|
||||
}
|
||||
|
||||
// ValidSyntax check if a json has a valid JSON syntax,
|
||||
// while not validate UTF-8 charset
|
||||
func _ValidSyntax(json string) bool {
|
||||
p := NewParserObj(json)
|
||||
_, e := p.skip()
|
||||
if e != 0 {
|
||||
return false
|
||||
}
|
||||
if skipBlank(p.s, p.p) != -int(types.ERR_EOF) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// SkipFast skip a json value in fast-skip algs,
|
||||
// while not strictly validate JSON syntax and UTF-8 charset.
|
||||
func _SkipFast(src string, i int) (int, int, error) {
|
||||
p := NewParserObj(src)
|
||||
p.p = i
|
||||
s, e := p.skipFast()
|
||||
if e != 0 {
|
||||
return -1, -1, p.ExportError(e)
|
||||
}
|
||||
t := switchRawType(p.s[s])
|
||||
if t == _V_NONE {
|
||||
return -1, -1, ErrNotExist
|
||||
}
|
||||
if t == _V_NUMBER {
|
||||
p.p = 1 + backward(p.s, p.p-1)
|
||||
}
|
||||
return s, p.p, nil
|
||||
}
|
||||
28
vendor/github.com/bytedance/sonic/ast/stubs.go
generated
vendored
Normal file
28
vendor/github.com/bytedance/sonic/ast/stubs.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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 ast
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/bytedance/sonic/internal/rt"
|
||||
)
|
||||
|
||||
//go:nosplit
|
||||
func mem2ptr(s []byte) unsafe.Pointer {
|
||||
return (*rt.GoSlice)(unsafe.Pointer(&s)).Ptr
|
||||
}
|
||||
333
vendor/github.com/bytedance/sonic/ast/visitor.go
generated
vendored
Normal file
333
vendor/github.com/bytedance/sonic/ast/visitor.go
generated
vendored
Normal file
@ -0,0 +1,333 @@
|
||||
/*
|
||||
* 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 ast
|
||||
|
||||
import (
|
||||
`encoding/json`
|
||||
`errors`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/unquote`
|
||||
)
|
||||
|
||||
// Visitor handles the callbacks during preorder traversal of a JSON AST.
|
||||
//
|
||||
// According to the JSON RFC8259, a JSON AST can be defined by
|
||||
// the following rules without separator / whitespace tokens.
|
||||
//
|
||||
// JSON-AST = value
|
||||
// value = false / null / true / object / array / number / string
|
||||
// object = begin-object [ member *( member ) ] end-object
|
||||
// member = string value
|
||||
// array = begin-array [ value *( value ) ] end-array
|
||||
//
|
||||
type Visitor interface {
|
||||
|
||||
// OnNull handles a JSON null value.
|
||||
OnNull() error
|
||||
|
||||
// OnBool handles a JSON true / false value.
|
||||
OnBool(v bool) error
|
||||
|
||||
// OnString handles a JSON string value.
|
||||
OnString(v string) error
|
||||
|
||||
// OnInt64 handles a JSON number value with int64 type.
|
||||
OnInt64(v int64, n json.Number) error
|
||||
|
||||
// OnFloat64 handles a JSON number value with float64 type.
|
||||
OnFloat64(v float64, n json.Number) error
|
||||
|
||||
// OnObjectBegin handles the beginning of a JSON object value with a
|
||||
// suggested capacity that can be used to make your custom object container.
|
||||
//
|
||||
// After this point the visitor will receive a sequence of callbacks like
|
||||
// [string, value, string, value, ......, ObjectEnd].
|
||||
//
|
||||
// Note:
|
||||
// 1. This is a recursive definition which means the value can
|
||||
// also be a JSON object / array described by a sequence of callbacks.
|
||||
// 2. The suggested capacity will be 0 if current object is empty.
|
||||
// 3. Currently sonic use a fixed capacity for non-empty object (keep in
|
||||
// sync with ast.Node) which might not be very suitable. This may be
|
||||
// improved in future version.
|
||||
OnObjectBegin(capacity int) error
|
||||
|
||||
// OnObjectKey handles a JSON object key string in member.
|
||||
OnObjectKey(key string) error
|
||||
|
||||
// OnObjectEnd handles the ending of a JSON object value.
|
||||
OnObjectEnd() error
|
||||
|
||||
// OnArrayBegin handles the beginning of a JSON array value with a
|
||||
// suggested capacity that can be used to make your custom array container.
|
||||
//
|
||||
// After this point the visitor will receive a sequence of callbacks like
|
||||
// [value, value, value, ......, ArrayEnd].
|
||||
//
|
||||
// Note:
|
||||
// 1. This is a recursive definition which means the value can
|
||||
// also be a JSON object / array described by a sequence of callbacks.
|
||||
// 2. The suggested capacity will be 0 if current array is empty.
|
||||
// 3. Currently sonic use a fixed capacity for non-empty array (keep in
|
||||
// sync with ast.Node) which might not be very suitable. This may be
|
||||
// improved in future version.
|
||||
OnArrayBegin(capacity int) error
|
||||
|
||||
// OnArrayEnd handles the ending of a JSON array value.
|
||||
OnArrayEnd() error
|
||||
}
|
||||
|
||||
// VisitorOptions contains all Visitor's options. The default value is an
|
||||
// empty VisitorOptions{}.
|
||||
type VisitorOptions struct {
|
||||
// OnlyNumber indicates parser to directly return number value without
|
||||
// conversion, then the first argument of OnInt64 / OnFloat64 will always
|
||||
// be zero.
|
||||
OnlyNumber bool
|
||||
}
|
||||
|
||||
var defaultVisitorOptions = &VisitorOptions{}
|
||||
|
||||
// Preorder decodes the whole JSON string and callbacks each AST node to visitor
|
||||
// during preorder traversal. Any visitor method with an error returned will
|
||||
// break the traversal and the given error will be directly returned. The opts
|
||||
// argument can be reused after every call.
|
||||
func Preorder(str string, visitor Visitor, opts *VisitorOptions) error {
|
||||
if opts == nil {
|
||||
opts = defaultVisitorOptions
|
||||
}
|
||||
// process VisitorOptions first to guarantee that all options will be
|
||||
// constant during decoding and make options more readable.
|
||||
var (
|
||||
optDecodeNumber = !opts.OnlyNumber
|
||||
)
|
||||
|
||||
tv := &traverser{
|
||||
parser: Parser{
|
||||
s: str,
|
||||
noLazy: true,
|
||||
skipValue: false,
|
||||
},
|
||||
visitor: visitor,
|
||||
}
|
||||
|
||||
if optDecodeNumber {
|
||||
tv.parser.decodeNumber(true)
|
||||
}
|
||||
|
||||
err := tv.decodeValue()
|
||||
|
||||
if optDecodeNumber {
|
||||
tv.parser.decodeNumber(false)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type traverser struct {
|
||||
parser Parser
|
||||
visitor Visitor
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with (*Parser).Parse method.
|
||||
func (self *traverser) decodeValue() error {
|
||||
switch val := self.parser.decodeValue(); val.Vt {
|
||||
case types.V_EOF:
|
||||
return types.ERR_EOF
|
||||
case types.V_NULL:
|
||||
return self.visitor.OnNull()
|
||||
case types.V_TRUE:
|
||||
return self.visitor.OnBool(true)
|
||||
case types.V_FALSE:
|
||||
return self.visitor.OnBool(false)
|
||||
case types.V_STRING:
|
||||
return self.decodeString(val.Iv, val.Ep)
|
||||
case types.V_DOUBLE:
|
||||
return self.visitor.OnFloat64(val.Dv,
|
||||
json.Number(self.parser.s[val.Ep:self.parser.p]))
|
||||
case types.V_INTEGER:
|
||||
return self.visitor.OnInt64(val.Iv,
|
||||
json.Number(self.parser.s[val.Ep:self.parser.p]))
|
||||
case types.V_ARRAY:
|
||||
return self.decodeArray()
|
||||
case types.V_OBJECT:
|
||||
return self.decodeObject()
|
||||
default:
|
||||
return types.ParsingError(-val.Vt)
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with (*Parser).decodeArray method.
|
||||
func (self *traverser) decodeArray() error {
|
||||
sp := self.parser.p
|
||||
ns := len(self.parser.s)
|
||||
|
||||
/* allocate array space and parse every element */
|
||||
if err := self.visitor.OnArrayBegin(_DEFAULT_NODE_CAP); err != nil {
|
||||
if err == VisitOPSkip {
|
||||
// NOTICE: for user needs to skip entry object
|
||||
self.parser.p -= 1
|
||||
if _, e := self.parser.skipFast(); e != 0 {
|
||||
return e
|
||||
}
|
||||
return self.visitor.OnArrayEnd()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
/* check for EOF */
|
||||
self.parser.p = self.parser.lspace(sp)
|
||||
if self.parser.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for empty array */
|
||||
if self.parser.s[self.parser.p] == ']' {
|
||||
self.parser.p++
|
||||
return self.visitor.OnArrayEnd()
|
||||
}
|
||||
|
||||
for {
|
||||
/* decode the value */
|
||||
if err := self.decodeValue(); err != nil {
|
||||
return err
|
||||
}
|
||||
self.parser.p = self.parser.lspace(self.parser.p)
|
||||
|
||||
/* check for EOF */
|
||||
if self.parser.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the next character */
|
||||
switch self.parser.s[self.parser.p] {
|
||||
case ',':
|
||||
self.parser.p++
|
||||
case ']':
|
||||
self.parser.p++
|
||||
return self.visitor.OnArrayEnd()
|
||||
default:
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with (*Parser).decodeObject method.
|
||||
func (self *traverser) decodeObject() error {
|
||||
sp := self.parser.p
|
||||
ns := len(self.parser.s)
|
||||
|
||||
/* allocate object space and decode each pair */
|
||||
if err := self.visitor.OnObjectBegin(_DEFAULT_NODE_CAP); err != nil {
|
||||
if err == VisitOPSkip {
|
||||
// NOTICE: for user needs to skip entry object
|
||||
self.parser.p -= 1
|
||||
if _, e := self.parser.skipFast(); e != 0 {
|
||||
return e
|
||||
}
|
||||
return self.visitor.OnObjectEnd()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
/* check for EOF */
|
||||
self.parser.p = self.parser.lspace(sp)
|
||||
if self.parser.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for empty object */
|
||||
if self.parser.s[self.parser.p] == '}' {
|
||||
self.parser.p++
|
||||
return self.visitor.OnObjectEnd()
|
||||
}
|
||||
|
||||
for {
|
||||
var njs types.JsonState
|
||||
var err types.ParsingError
|
||||
|
||||
/* decode the key */
|
||||
if njs = self.parser.decodeValue(); njs.Vt != types.V_STRING {
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
|
||||
/* extract the key */
|
||||
idx := self.parser.p - 1
|
||||
key := self.parser.s[njs.Iv:idx]
|
||||
|
||||
/* check for escape sequence */
|
||||
if njs.Ep != -1 {
|
||||
if key, err = unquote.String(key); err != 0 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := self.visitor.OnObjectKey(key); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
/* expect a ':' delimiter */
|
||||
if err = self.parser.delim(); err != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
/* decode the value */
|
||||
if err := self.decodeValue(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.parser.p = self.parser.lspace(self.parser.p)
|
||||
|
||||
/* check for EOF */
|
||||
if self.parser.p >= ns {
|
||||
return types.ERR_EOF
|
||||
}
|
||||
|
||||
/* check for the next character */
|
||||
switch self.parser.s[self.parser.p] {
|
||||
case ',':
|
||||
self.parser.p++
|
||||
case '}':
|
||||
self.parser.p++
|
||||
return self.visitor.OnObjectEnd()
|
||||
default:
|
||||
return types.ERR_INVALID_CHAR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with (*Parser).decodeString method.
|
||||
func (self *traverser) decodeString(iv int64, ep int) error {
|
||||
p := self.parser.p - 1
|
||||
s := self.parser.s[iv:p]
|
||||
|
||||
/* fast path: no escape sequence */
|
||||
if ep == -1 {
|
||||
return self.visitor.OnString(s)
|
||||
}
|
||||
|
||||
/* unquote the string */
|
||||
out, err := unquote.String(s)
|
||||
if err != 0 {
|
||||
return err
|
||||
}
|
||||
return self.visitor.OnString(out)
|
||||
}
|
||||
|
||||
// If visitor return this error on `OnObjectBegin()` or `OnArrayBegin()`,
|
||||
// the traverser will skip entry object or array
|
||||
var VisitOPSkip = errors.New("")
|
||||
143
vendor/github.com/bytedance/sonic/compat.go
generated
vendored
Normal file
143
vendor/github.com/bytedance/sonic/compat.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
// +build !amd64,!arm64 go1.26 !go1.17 arm64,!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 sonic
|
||||
|
||||
import (
|
||||
`bytes`
|
||||
`encoding/json`
|
||||
`io`
|
||||
`reflect`
|
||||
|
||||
`github.com/bytedance/sonic/option`
|
||||
)
|
||||
|
||||
const apiKind = UseStdJSON
|
||||
|
||||
type frozenConfig struct {
|
||||
Config
|
||||
}
|
||||
|
||||
// Froze convert the Config to API
|
||||
func (cfg Config) Froze() API {
|
||||
api := &frozenConfig{Config: cfg}
|
||||
return api
|
||||
}
|
||||
|
||||
func (cfg frozenConfig) marshalOptions(val interface{}, prefix, indent string) ([]byte, error) {
|
||||
w := bytes.NewBuffer([]byte{})
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetEscapeHTML(cfg.EscapeHTML)
|
||||
enc.SetIndent(prefix, indent)
|
||||
err := enc.Encode(val)
|
||||
out := w.Bytes()
|
||||
|
||||
// json.Encoder always appends '\n' after encoding,
|
||||
// which is not same with json.Marshal()
|
||||
if len(out) > 0 && out[len(out)-1] == '\n' {
|
||||
out = out[:len(out)-1]
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
// Marshal is implemented by sonic
|
||||
func (cfg frozenConfig) Marshal(val interface{}) ([]byte, error) {
|
||||
if !cfg.EscapeHTML {
|
||||
return cfg.marshalOptions(val, "", "")
|
||||
}
|
||||
return json.Marshal(val)
|
||||
}
|
||||
|
||||
// MarshalToString is implemented by sonic
|
||||
func (cfg frozenConfig) MarshalToString(val interface{}) (string, error) {
|
||||
out, err := cfg.Marshal(val)
|
||||
return string(out), err
|
||||
}
|
||||
|
||||
// MarshalIndent is implemented by sonic
|
||||
func (cfg frozenConfig) MarshalIndent(val interface{}, prefix, indent string) ([]byte, error) {
|
||||
if !cfg.EscapeHTML {
|
||||
return cfg.marshalOptions(val, prefix, indent)
|
||||
}
|
||||
return json.MarshalIndent(val, prefix, indent)
|
||||
}
|
||||
|
||||
// UnmarshalFromString is implemented by sonic
|
||||
func (cfg frozenConfig) UnmarshalFromString(buf string, val interface{}) error {
|
||||
r := bytes.NewBufferString(buf)
|
||||
dec := json.NewDecoder(r)
|
||||
if cfg.UseNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
if cfg.DisallowUnknownFields {
|
||||
dec.DisallowUnknownFields()
|
||||
}
|
||||
err := dec.Decode(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check the trailing chars
|
||||
offset := dec.InputOffset()
|
||||
if t, err := dec.Token(); !(t == nil && err == io.EOF) {
|
||||
return &json.SyntaxError{ Offset: offset}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unmarshal is implemented by sonic
|
||||
func (cfg frozenConfig) Unmarshal(buf []byte, val interface{}) error {
|
||||
return cfg.UnmarshalFromString(string(buf), val)
|
||||
}
|
||||
|
||||
// NewEncoder is implemented by sonic
|
||||
func (cfg frozenConfig) NewEncoder(writer io.Writer) Encoder {
|
||||
enc := json.NewEncoder(writer)
|
||||
if !cfg.EscapeHTML {
|
||||
enc.SetEscapeHTML(cfg.EscapeHTML)
|
||||
}
|
||||
return enc
|
||||
}
|
||||
|
||||
// NewDecoder is implemented by sonic
|
||||
func (cfg frozenConfig) NewDecoder(reader io.Reader) Decoder {
|
||||
dec := json.NewDecoder(reader)
|
||||
if cfg.UseNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
if cfg.DisallowUnknownFields {
|
||||
dec.DisallowUnknownFields()
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// Valid is implemented by sonic
|
||||
func (cfg frozenConfig) Valid(data []byte) bool {
|
||||
return json.Valid(data)
|
||||
}
|
||||
|
||||
// Pretouch compiles vt ahead-of-time to avoid JIT compilation on-the-fly, in
|
||||
// order to reduce the first-hit latency at **amd64** Arch.
|
||||
// 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.
|
||||
// * This is the none implement for !amd64.
|
||||
// It will be useful for someone who develop with !amd64 arch,like Mac M1.
|
||||
func Pretouch(vt reflect.Type, opts ...option.CompileOption) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
201
vendor/github.com/bytedance/sonic/decoder/decoder_compat.go
generated
vendored
Normal file
201
vendor/github.com/bytedance/sonic/decoder/decoder_compat.go
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
//go:build (!amd64 && !arm64) || go1.26 || !go1.17 || (arm64 && !go1.20)
|
||||
// +build !amd64,!arm64 go1.26 !go1.17 arm64,!go1.20
|
||||
|
||||
/*
|
||||
* 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 decoder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bytedance/sonic/internal/decoder/consts"
|
||||
"github.com/bytedance/sonic/internal/native/types"
|
||||
"github.com/bytedance/sonic/option"
|
||||
"github.com/bytedance/sonic/internal/compat"
|
||||
)
|
||||
|
||||
func init() {
|
||||
compat.Warn("sonic/decoder")
|
||||
}
|
||||
|
||||
const (
|
||||
_F_use_int64 = consts.F_use_int64
|
||||
_F_disable_urc = consts.F_disable_unknown
|
||||
_F_disable_unknown = consts.F_disable_unknown
|
||||
_F_copy_string = consts.F_copy_string
|
||||
|
||||
_F_use_number = consts.F_use_number
|
||||
_F_validate_string = consts.F_validate_string
|
||||
_F_allow_control = consts.F_allow_control
|
||||
_F_no_validate_json = consts.F_no_validate_json
|
||||
_F_case_sensitive = consts.F_case_sensitive
|
||||
)
|
||||
|
||||
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
|
||||
OptionCaseSensitive Options = 1 << _F_case_sensitive
|
||||
)
|
||||
|
||||
func (self *Decoder) SetOptions(opts Options) {
|
||||
if (opts & OptionUseNumber != 0) && (opts & 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
|
||||
f uint64
|
||||
s string
|
||||
}
|
||||
|
||||
// NewDecoder creates a new decoder instance.
|
||||
func NewDecoder(s string) *Decoder {
|
||||
return &Decoder{s: s}
|
||||
}
|
||||
|
||||
// Pos returns the current decoding position.
|
||||
func (self *Decoder) Pos() int {
|
||||
return self.i
|
||||
}
|
||||
|
||||
func (self *Decoder) Reset(s string) {
|
||||
self.s = s
|
||||
self.i = 0
|
||||
// self.f = 0
|
||||
}
|
||||
|
||||
// NOTE: api fallback do nothing
|
||||
func (self *Decoder) CheckTrailings() error {
|
||||
pos := self.i
|
||||
buf := self.s
|
||||
/* skip all the trailing spaces */
|
||||
if pos != len(buf) {
|
||||
for pos < len(buf) && (types.SPACE_MASK & (1 << buf[pos])) != 0 {
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
/* then it must be at EOF */
|
||||
if pos == len(buf) {
|
||||
return nil
|
||||
}
|
||||
|
||||
/* junk after JSON value */
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// 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 {
|
||||
r := bytes.NewBufferString(self.s)
|
||||
dec := json.NewDecoder(r)
|
||||
if (self.f & uint64(OptionUseNumber)) != 0 {
|
||||
dec.UseNumber()
|
||||
}
|
||||
if (self.f & uint64(OptionDisableUnknown)) != 0 {
|
||||
dec.DisallowUnknownFields()
|
||||
}
|
||||
return dec.Decode(val)
|
||||
}
|
||||
|
||||
// UseInt64 indicates the Decoder to unmarshal an integer into an interface{} as an
|
||||
// int64 instead of as a float64.
|
||||
func (self *Decoder) UseInt64() {
|
||||
self.f |= 1 << _F_use_int64
|
||||
self.f &^= 1 << _F_use_number
|
||||
}
|
||||
|
||||
// UseNumber indicates the Decoder to unmarshal a number into an interface{} as a
|
||||
// json.Number instead of as a float64.
|
||||
func (self *Decoder) UseNumber() {
|
||||
self.f &^= 1 << _F_use_int64
|
||||
self.f |= 1 << _F_use_number
|
||||
}
|
||||
|
||||
// UseUnicodeErrors indicates the Decoder to return an error when encounter invalid
|
||||
// UTF-8 escape sequences.
|
||||
func (self *Decoder) UseUnicodeErrors() {
|
||||
self.f |= 1 << _F_disable_urc
|
||||
}
|
||||
|
||||
// DisallowUnknownFields indicates the Decoder to return an error when the destination
|
||||
// is a struct and the input contains object keys which do not match any
|
||||
// non-ignored, exported fields in the destination.
|
||||
func (self *Decoder) DisallowUnknownFields() {
|
||||
self.f |= 1 << _F_disable_unknown
|
||||
}
|
||||
|
||||
// CopyString indicates the Decoder to decode string values by copying instead of referring.
|
||||
func (self *Decoder) CopyString() {
|
||||
self.f |= 1 << _F_copy_string
|
||||
}
|
||||
|
||||
// ValidateString causes the Decoder to validate string values when decoding string value
|
||||
// in JSON. Validation is that, returning error when unescaped control chars(0x00-0x1f) or
|
||||
// invalid UTF-8 chars in the string value of JSON.
|
||||
func (self *Decoder) ValidateString() {
|
||||
self.f |= 1 << _F_validate_string
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return nil
|
||||
}
|
||||
|
||||
type StreamDecoder = json.Decoder
|
||||
|
||||
// NewStreamDecoder adapts to encoding/json.NewDecoder API.
|
||||
//
|
||||
// NewStreamDecoder returns a new decoder that reads from r.
|
||||
func NewStreamDecoder(r io.Reader) *StreamDecoder {
|
||||
return json.NewDecoder(r)
|
||||
}
|
||||
|
||||
// SyntaxError represents json syntax error
|
||||
type SyntaxError json.SyntaxError
|
||||
|
||||
// Description
|
||||
func (s SyntaxError) Description() string {
|
||||
return (*json.SyntaxError)(unsafe.Pointer(&s)).Error()
|
||||
}
|
||||
// Error
|
||||
func (s SyntaxError) Error() string {
|
||||
return (*json.SyntaxError)(unsafe.Pointer(&s)).Error()
|
||||
}
|
||||
|
||||
// MismatchTypeError represents mismatching between json and object
|
||||
type MismatchTypeError json.UnmarshalTypeError
|
||||
72
vendor/github.com/bytedance/sonic/decoder/decoder_native.go
generated
vendored
Normal file
72
vendor/github.com/bytedance/sonic/decoder/decoder_native.go
generated
vendored
Normal file
@ -0,0 +1,72 @@
|
||||
//go:build (amd64 && go1.17 && !go1.26) || (arm64 && go1.20 && !go1.26)
|
||||
// +build amd64,go1.17,!go1.26 arm64,go1.20,!go1.26
|
||||
|
||||
|
||||
/*
|
||||
* 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 decoder
|
||||
|
||||
import (
|
||||
`github.com/bytedance/sonic/internal/decoder/api`
|
||||
)
|
||||
|
||||
// Decoder is the decoder context object
|
||||
type Decoder = api.Decoder
|
||||
|
||||
// SyntaxError represents json syntax error
|
||||
type SyntaxError = api.SyntaxError
|
||||
|
||||
// MismatchTypeError represents mismatching between json and object
|
||||
type MismatchTypeError = api.MismatchTypeError
|
||||
|
||||
// Options for decode.
|
||||
type Options = api.Options
|
||||
|
||||
const (
|
||||
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
|
||||
OptionCaseSensitive Options = api.OptionCaseSensitive
|
||||
)
|
||||
|
||||
// StreamDecoder is the decoder context object for streaming input.
|
||||
type StreamDecoder = api.StreamDecoder
|
||||
|
||||
var (
|
||||
// NewDecoder creates a new decoder instance.
|
||||
NewDecoder = api.NewDecoder
|
||||
|
||||
// NewStreamDecoder adapts to encoding/json.NewDecoder API.
|
||||
//
|
||||
// NewStreamDecoder returns a new decoder that reads from r.
|
||||
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 = 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 = api.Skip
|
||||
)
|
||||
262
vendor/github.com/bytedance/sonic/encoder/encoder_compat.go
generated
vendored
Normal file
262
vendor/github.com/bytedance/sonic/encoder/encoder_compat.go
generated
vendored
Normal file
@ -0,0 +1,262 @@
|
||||
// +build !amd64,!arm64 go1.26 !go1.17 arm64,!go1.20
|
||||
|
||||
/*
|
||||
* 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 encoder
|
||||
|
||||
import (
|
||||
`io`
|
||||
`bytes`
|
||||
`encoding/json`
|
||||
`reflect`
|
||||
|
||||
`github.com/bytedance/sonic/option`
|
||||
`github.com/bytedance/sonic/internal/compat`
|
||||
)
|
||||
|
||||
func init() {
|
||||
compat.Warn("sonic/encoder")
|
||||
}
|
||||
|
||||
// EnableFallback indicates if encoder use fallback
|
||||
const EnableFallback = true
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
||||
// CompactMarshaler indicates that the output JSON from json.Marshaler
|
||||
// is always compact and needs no validation
|
||||
CompactMarshaler Options = 1 << bitCompactMarshaler
|
||||
|
||||
// NoQuoteTextMarshaler indicates that the output text from encoding.TextMarshaler
|
||||
// is always escaped string and needs no quoting
|
||||
NoQuoteTextMarshaler Options = 1 << bitNoQuoteTextMarshaler
|
||||
|
||||
// NoNullSliceOrMap indicates all empty Array or Object are encoded as '[]' or '{}',
|
||||
// instead of 'null'
|
||||
NoNullSliceOrMap Options = 1 << bitNoNullSliceOrMap
|
||||
|
||||
// ValidateString indicates that encoder should validate the input string
|
||||
// before encoding it into JSON.
|
||||
ValidateString Options = 1 << bitValidateString
|
||||
|
||||
// NoValidateJSONMarshaler indicates that the encoder should not validate the output string
|
||||
// after encoding the JSONMarshaler to JSON.
|
||||
NoValidateJSONMarshaler Options = 1 << bitNoValidateJSONMarshaler
|
||||
|
||||
// NoEncoderNewline indicates that the encoder should not add a newline after every message
|
||||
NoEncoderNewline Options = 1 << bitNoEncoderNewline
|
||||
|
||||
// CompatibleWithStd is used to be compatible with std encoder.
|
||||
CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler
|
||||
)
|
||||
|
||||
// Encoder represents a specific set of encoder configurations.
|
||||
type Encoder struct {
|
||||
Opts Options
|
||||
prefix string
|
||||
indent string
|
||||
}
|
||||
|
||||
// Encode returns the JSON encoding of v.
|
||||
func (self *Encoder) Encode(v interface{}) ([]byte, error) {
|
||||
if self.indent != "" || self.prefix != "" {
|
||||
return EncodeIndented(v, self.prefix, self.indent, self.Opts)
|
||||
}
|
||||
return Encode(v, self.Opts)
|
||||
}
|
||||
|
||||
// SortKeys enables the SortMapKeys option.
|
||||
func (self *Encoder) SortKeys() *Encoder {
|
||||
self.Opts |= SortMapKeys
|
||||
return self
|
||||
}
|
||||
|
||||
// SetEscapeHTML specifies if option EscapeHTML opens
|
||||
func (self *Encoder) SetEscapeHTML(f bool) {
|
||||
if f {
|
||||
self.Opts |= EscapeHTML
|
||||
} else {
|
||||
self.Opts &= ^EscapeHTML
|
||||
}
|
||||
}
|
||||
|
||||
// SetValidateString specifies if option ValidateString opens
|
||||
func (self *Encoder) SetValidateString(f bool) {
|
||||
if f {
|
||||
self.Opts |= ValidateString
|
||||
} else {
|
||||
self.Opts &= ^ValidateString
|
||||
}
|
||||
}
|
||||
|
||||
// SetNoValidateJSONMarshaler specifies if option NoValidateJSONMarshaler opens
|
||||
func (self *Encoder) SetNoValidateJSONMarshaler(f bool) {
|
||||
if f {
|
||||
self.Opts |= NoValidateJSONMarshaler
|
||||
} else {
|
||||
self.Opts &= ^NoValidateJSONMarshaler
|
||||
}
|
||||
}
|
||||
|
||||
// SetNoEncoderNewline specifies if option NoEncoderNewline opens
|
||||
func (self *Encoder) SetNoEncoderNewline(f bool) {
|
||||
if f {
|
||||
self.Opts |= NoEncoderNewline
|
||||
} else {
|
||||
self.Opts &= ^NoEncoderNewline
|
||||
}
|
||||
}
|
||||
|
||||
// SetCompactMarshaler specifies if option CompactMarshaler opens
|
||||
func (self *Encoder) SetCompactMarshaler(f bool) {
|
||||
if f {
|
||||
self.Opts |= CompactMarshaler
|
||||
} else {
|
||||
self.Opts &= ^CompactMarshaler
|
||||
}
|
||||
}
|
||||
|
||||
// SetNoQuoteTextMarshaler specifies if option NoQuoteTextMarshaler opens
|
||||
func (self *Encoder) SetNoQuoteTextMarshaler(f bool) {
|
||||
if f {
|
||||
self.Opts |= NoQuoteTextMarshaler
|
||||
} else {
|
||||
self.Opts &= ^NoQuoteTextMarshaler
|
||||
}
|
||||
}
|
||||
|
||||
// SetIndent instructs the encoder to format each subsequent encoded
|
||||
// value as if indented by the package-level function EncodeIndent().
|
||||
// Calling SetIndent("", "") disables indentation.
|
||||
func (enc *Encoder) SetIndent(prefix, indent string) {
|
||||
enc.prefix = prefix
|
||||
enc.indent = indent
|
||||
}
|
||||
|
||||
// Quote returns the JSON-quoted version of s.
|
||||
func Quote(s string) string {
|
||||
/* check for empty string */
|
||||
if s == "" {
|
||||
return `""`
|
||||
}
|
||||
|
||||
out, _ := json.Marshal(s)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// Encode returns the JSON encoding of val, encoded with opts.
|
||||
func Encode(val interface{}, opts Options) ([]byte, error) {
|
||||
return json.Marshal(val)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if buf == nil {
|
||||
panic("user-supplied buffer buf is nil")
|
||||
}
|
||||
w := bytes.NewBuffer(*buf)
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetEscapeHTML((opts & EscapeHTML) != 0)
|
||||
err := enc.Encode(val)
|
||||
*buf = w.Bytes()
|
||||
l := len(*buf)
|
||||
if l > 0 && (opts & NoEncoderNewline != 0) && (*buf)[l-1] == '\n' {
|
||||
*buf = (*buf)[:l-1]
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 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
|
||||
// so that the JSON will be safe to embed inside HTML <script> tags.
|
||||
// For historical reasons, web browsers don't honor standard HTML
|
||||
// escaping within <script> tags, so an alternative JSON encoding must
|
||||
// be used.
|
||||
func HTMLEscape(dst []byte, src []byte) []byte {
|
||||
d := bytes.NewBuffer(dst)
|
||||
json.HTMLEscape(d, src)
|
||||
return d.Bytes()
|
||||
}
|
||||
|
||||
// EncodeIndented is like Encode 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 EncodeIndented(val interface{}, prefix string, indent string, opts Options) ([]byte, error) {
|
||||
w := bytes.NewBuffer([]byte{})
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetEscapeHTML((opts & EscapeHTML) != 0)
|
||||
enc.SetIndent(prefix, indent)
|
||||
err := enc.Encode(val)
|
||||
out := w.Bytes()
|
||||
return out, 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 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return json.Valid(data), 0
|
||||
}
|
||||
|
||||
// StreamEncoder uses io.Writer as
|
||||
type StreamEncoder = json.Encoder
|
||||
|
||||
// NewStreamEncoder adapts to encoding/json.NewDecoder API.
|
||||
//
|
||||
// NewStreamEncoder returns a new encoder that write to w.
|
||||
func NewStreamEncoder(w io.Writer) *StreamEncoder {
|
||||
return json.NewEncoder(w)
|
||||
}
|
||||
|
||||
121
vendor/github.com/bytedance/sonic/encoder/encoder_native.go
generated
vendored
Normal file
121
vendor/github.com/bytedance/sonic/encoder/encoder_native.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
// +build amd64,go1.17,!go1.26 arm64,go1.20,!go1.26
|
||||
|
||||
/*
|
||||
* 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 encoder
|
||||
|
||||
import (
|
||||
`github.com/bytedance/sonic/internal/encoder`
|
||||
)
|
||||
|
||||
// EnableFallback indicates if encoder use fallback
|
||||
const EnableFallback = false
|
||||
|
||||
// Encoder represents a specific set of encoder configurations.
|
||||
type Encoder = encoder.Encoder
|
||||
|
||||
// StreamEncoder uses io.Writer as input.
|
||||
type StreamEncoder = encoder.StreamEncoder
|
||||
|
||||
// Options is a set of encoding options.
|
||||
type Options = encoder.Options
|
||||
|
||||
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 = encoder.SortMapKeys
|
||||
|
||||
// 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 = encoder.EscapeHTML
|
||||
|
||||
// CompactMarshaler indicates that the output JSON from json.Marshaler
|
||||
// is always compact and needs no validation
|
||||
CompactMarshaler Options = encoder.CompactMarshaler
|
||||
|
||||
// NoQuoteTextMarshaler indicates that the output text from encoding.TextMarshaler
|
||||
// is always escaped string and needs no quoting
|
||||
NoQuoteTextMarshaler Options = encoder.NoQuoteTextMarshaler
|
||||
|
||||
// NoNullSliceOrMap indicates all empty Array or Object are encoded as '[]' or '{}',
|
||||
// instead of 'null'
|
||||
NoNullSliceOrMap Options = encoder.NoNullSliceOrMap
|
||||
|
||||
// ValidateString indicates that encoder should validate the input string
|
||||
// before encoding it into JSON.
|
||||
ValidateString Options = encoder.ValidateString
|
||||
|
||||
// NoValidateJSONMarshaler indicates that the encoder should not validate the output string
|
||||
// after encoding the JSONMarshaler to JSON.
|
||||
NoValidateJSONMarshaler Options = encoder.NoValidateJSONMarshaler
|
||||
|
||||
// NoEncoderNewline indicates that the encoder should not add a newline after every message
|
||||
NoEncoderNewline Options = encoder.NoEncoderNewline
|
||||
|
||||
// CompatibleWithStd is used to be compatible with std encoder.
|
||||
CompatibleWithStd Options = encoder.CompatibleWithStd
|
||||
|
||||
// EncodeNullForInfOrNan encodes Infinity or NaN float values as 'null'
|
||||
// instead of returning an error.
|
||||
EncodeNullForInfOrNan Options = encoder.EncodeNullForInfOrNan
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
// Encode returns the JSON encoding of val, encoded with opts.
|
||||
Encode = encoder.Encode
|
||||
|
||||
// EncodeIndented is like Encode 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.
|
||||
EncodeIndented = encoder.EncodeIndented
|
||||
|
||||
// EncodeInto is like Encode but uses a user-supplied buffer instead of allocating a new one.
|
||||
EncodeInto = encoder.EncodeInto
|
||||
|
||||
// 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
|
||||
// so that the JSON will be safe to embed inside HTML <script> tags.
|
||||
// For historical reasons, web browsers don't honor standard HTML
|
||||
// escaping within <script> tags, so an alternative JSON encoding must
|
||||
// be used.
|
||||
HTMLEscape = encoder.HTMLEscape
|
||||
|
||||
// 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 = encoder.Pretouch
|
||||
|
||||
// Quote returns the JSON-quoted version of s.
|
||||
Quote = encoder.Quote
|
||||
|
||||
// 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.
|
||||
Valid = encoder.Valid
|
||||
|
||||
// NewStreamEncoder adapts to encoding/json.NewDecoder API.
|
||||
//
|
||||
// NewStreamEncoder returns a new encoder that write to w.
|
||||
NewStreamEncoder = encoder.NewStreamEncoder
|
||||
)
|
||||
10
vendor/github.com/bytedance/sonic/go.work
generated
vendored
Normal file
10
vendor/github.com/bytedance/sonic/go.work
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
go 1.18
|
||||
|
||||
use (
|
||||
.
|
||||
./external_jsonlib_test
|
||||
./fuzz
|
||||
./generic_test
|
||||
./issue_test
|
||||
./loader
|
||||
)
|
||||
11
vendor/github.com/bytedance/sonic/go.work.sum
generated
vendored
Normal file
11
vendor/github.com/bytedance/sonic/go.work.sum
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
0
vendor/github.com/bytedance/sonic/internal/caching/asm.s
generated
vendored
Normal file
0
vendor/github.com/bytedance/sonic/internal/caching/asm.s
generated
vendored
Normal file
115
vendor/github.com/bytedance/sonic/internal/caching/fcache.go
generated
vendored
Normal file
115
vendor/github.com/bytedance/sonic/internal/caching/fcache.go
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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 caching
|
||||
|
||||
import (
|
||||
`strings`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
type FieldMap struct {
|
||||
N uint64
|
||||
b unsafe.Pointer
|
||||
m map[string]int
|
||||
}
|
||||
|
||||
type FieldEntry struct {
|
||||
ID int
|
||||
Name string
|
||||
Hash uint64
|
||||
}
|
||||
|
||||
const (
|
||||
FieldMap_N = int64(unsafe.Offsetof(FieldMap{}.N))
|
||||
FieldMap_b = int64(unsafe.Offsetof(FieldMap{}.b))
|
||||
FieldEntrySize = int64(unsafe.Sizeof(FieldEntry{}))
|
||||
)
|
||||
|
||||
func newBucket(n int) unsafe.Pointer {
|
||||
v := make([]FieldEntry, n)
|
||||
return (*rt.GoSlice)(unsafe.Pointer(&v)).Ptr
|
||||
}
|
||||
|
||||
func CreateFieldMap(n int) *FieldMap {
|
||||
return &FieldMap {
|
||||
N: uint64(n * 2),
|
||||
b: newBucket(n * 2), // LoadFactor = 0.5
|
||||
m: make(map[string]int, n * 2),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FieldMap) At(p uint64) *FieldEntry {
|
||||
off := uintptr(p) * uintptr(FieldEntrySize)
|
||||
return (*FieldEntry)(unsafe.Pointer(uintptr(self.b) + off))
|
||||
}
|
||||
|
||||
// Get searches FieldMap by name. JIT generated assembly does NOT call this
|
||||
// function, rather it implements its own version directly in assembly. So
|
||||
// we must ensure this function stays in sync with the JIT generated one.
|
||||
func (self *FieldMap) Get(name string) int {
|
||||
h := StrHash(name)
|
||||
p := h % self.N
|
||||
s := self.At(p)
|
||||
|
||||
/* find the element;
|
||||
* the hash map is never full, so the loop will always terminate */
|
||||
for s.Hash != 0 {
|
||||
if s.Hash == h && s.Name == name {
|
||||
return s.ID
|
||||
} else {
|
||||
p = (p + 1) % self.N
|
||||
s = self.At(p)
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return -1
|
||||
}
|
||||
|
||||
func (self *FieldMap) Set(name string, i int) {
|
||||
h := StrHash(name)
|
||||
p := h % self.N
|
||||
s := self.At(p)
|
||||
|
||||
/* searching for an empty slot;
|
||||
* the hash map is never full, so the loop will always terminate */
|
||||
for s.Hash != 0 {
|
||||
p = (p + 1) % self.N
|
||||
s = self.At(p)
|
||||
}
|
||||
|
||||
/* set the value */
|
||||
s.ID = i
|
||||
s.Hash = h
|
||||
s.Name = name
|
||||
|
||||
/* add the case-insensitive version, prefer the one with smaller field ID */
|
||||
key := strings.ToLower(name)
|
||||
if v, ok := self.m[key]; !ok || i < v {
|
||||
self.m[key] = i
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FieldMap) GetCaseInsensitive(name string) int {
|
||||
if i, ok := self.m[strings.ToLower(name)]; ok {
|
||||
return i
|
||||
} else {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
36
vendor/github.com/bytedance/sonic/internal/caching/hashing.go
generated
vendored
Normal file
36
vendor/github.com/bytedance/sonic/internal/caching/hashing.go
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 caching
|
||||
|
||||
import (
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
var (
|
||||
V_strhash = rt.UnpackEface(rt.Strhash)
|
||||
S_strhash = *(*uintptr)(V_strhash.Value)
|
||||
)
|
||||
|
||||
func StrHash(s string) uint64 {
|
||||
if v := rt.Strhash(unsafe.Pointer(&s), 0); v == 0 {
|
||||
return 1
|
||||
} else {
|
||||
return uint64(v)
|
||||
}
|
||||
}
|
||||
179
vendor/github.com/bytedance/sonic/internal/caching/pcache.go
generated
vendored
Normal file
179
vendor/github.com/bytedance/sonic/internal/caching/pcache.go
generated
vendored
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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 caching
|
||||
|
||||
import (
|
||||
`sync`
|
||||
`sync/atomic`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
/** Program Map **/
|
||||
|
||||
const (
|
||||
_LoadFactor = 0.5
|
||||
_InitCapacity = 4096 // must be a power of 2
|
||||
)
|
||||
|
||||
type _ProgramMap struct {
|
||||
n uint64
|
||||
m uint32
|
||||
b []_ProgramEntry
|
||||
}
|
||||
|
||||
type _ProgramEntry struct {
|
||||
vt *rt.GoType
|
||||
fn interface{}
|
||||
}
|
||||
|
||||
func newProgramMap() *_ProgramMap {
|
||||
return &_ProgramMap {
|
||||
n: 0,
|
||||
m: _InitCapacity - 1,
|
||||
b: make([]_ProgramEntry, _InitCapacity),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *_ProgramMap) copy() *_ProgramMap {
|
||||
fork := &_ProgramMap{
|
||||
n: self.n,
|
||||
m: self.m,
|
||||
b: make([]_ProgramEntry, len(self.b)),
|
||||
}
|
||||
for i, f := range self.b {
|
||||
fork.b[i] = f
|
||||
}
|
||||
return fork
|
||||
}
|
||||
|
||||
func (self *_ProgramMap) get(vt *rt.GoType) interface{} {
|
||||
i := self.m + 1
|
||||
p := vt.Hash & self.m
|
||||
|
||||
/* linear probing */
|
||||
for ; i > 0; i-- {
|
||||
if b := self.b[p]; b.vt == vt {
|
||||
return b.fn
|
||||
} else if b.vt == nil {
|
||||
break
|
||||
} else {
|
||||
p = (p + 1) & self.m
|
||||
}
|
||||
}
|
||||
|
||||
/* not found */
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *_ProgramMap) add(vt *rt.GoType, fn interface{}) *_ProgramMap {
|
||||
p := self.copy()
|
||||
f := float64(atomic.LoadUint64(&p.n) + 1) / float64(p.m + 1)
|
||||
|
||||
/* check for load factor */
|
||||
if f > _LoadFactor {
|
||||
p = p.rehash()
|
||||
}
|
||||
|
||||
/* insert the value */
|
||||
p.insert(vt, fn)
|
||||
return p
|
||||
}
|
||||
|
||||
func (self *_ProgramMap) rehash() *_ProgramMap {
|
||||
c := (self.m + 1) << 1
|
||||
r := &_ProgramMap{m: c - 1, b: make([]_ProgramEntry, int(c))}
|
||||
|
||||
/* rehash every entry */
|
||||
for i := uint32(0); i <= self.m; i++ {
|
||||
if b := self.b[i]; b.vt != nil {
|
||||
r.insert(b.vt, b.fn)
|
||||
}
|
||||
}
|
||||
|
||||
/* rebuild successful */
|
||||
return r
|
||||
}
|
||||
|
||||
func (self *_ProgramMap) insert(vt *rt.GoType, fn interface{}) {
|
||||
h := vt.Hash
|
||||
p := h & self.m
|
||||
|
||||
/* linear probing */
|
||||
for i := uint32(0); i <= self.m; i++ {
|
||||
if b := &self.b[p]; b.vt != nil {
|
||||
p += 1
|
||||
p &= self.m
|
||||
} else {
|
||||
b.vt = vt
|
||||
b.fn = fn
|
||||
atomic.AddUint64(&self.n, 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/* should never happens */
|
||||
panic("no available slots")
|
||||
}
|
||||
|
||||
/** RCU Program Cache **/
|
||||
|
||||
type ProgramCache struct {
|
||||
m sync.Mutex
|
||||
p unsafe.Pointer
|
||||
}
|
||||
|
||||
func CreateProgramCache() *ProgramCache {
|
||||
return &ProgramCache {
|
||||
m: sync.Mutex{},
|
||||
p: unsafe.Pointer(newProgramMap()),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ProgramCache) Reset() {
|
||||
self.m.Lock()
|
||||
defer self.m.Unlock()
|
||||
self.p = unsafe.Pointer(newProgramMap())
|
||||
}
|
||||
|
||||
func (self *ProgramCache) Get(vt *rt.GoType) interface{} {
|
||||
return (*_ProgramMap)(atomic.LoadPointer(&self.p)).get(vt)
|
||||
}
|
||||
|
||||
func (self *ProgramCache) Compute(vt *rt.GoType, compute func(*rt.GoType, ... interface{}) (interface{}, error), ex ...interface{}) (interface{}, error) {
|
||||
var err error
|
||||
var val interface{}
|
||||
|
||||
/* use defer to prevent inlining of this function */
|
||||
self.m.Lock()
|
||||
defer self.m.Unlock()
|
||||
|
||||
/* double check with write lock held */
|
||||
if val = self.Get(vt); val != nil {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
/* compute the value */
|
||||
if val, err = compute(vt, ex...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/* update the RCU cache */
|
||||
atomic.StorePointer(&self.p, unsafe.Pointer((*_ProgramMap)(atomic.LoadPointer(&self.p)).add(vt, val)))
|
||||
return val, nil
|
||||
}
|
||||
12
vendor/github.com/bytedance/sonic/internal/compat/warn.go
generated
vendored
Normal file
12
vendor/github.com/bytedance/sonic/internal/compat/warn.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
// +build !amd64,!arm64 go1.26 !go1.17 arm64,!go1.20
|
||||
|
||||
package compat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func Warn(prefix string) {
|
||||
fmt.Fprintf(os.Stderr, "WARNING: %s only supports (go1.17~1.24 && amd64 CPU) or (go1.20~1.24 && arm64 CPU), but your environment is not suitable and will fallback to encoding/json\n", prefix)
|
||||
}
|
||||
40
vendor/github.com/bytedance/sonic/internal/cpu/features.go
generated
vendored
Normal file
40
vendor/github.com/bytedance/sonic/internal/cpu/features.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 cpu
|
||||
|
||||
import (
|
||||
`fmt`
|
||||
`os`
|
||||
|
||||
`github.com/klauspost/cpuid/v2`
|
||||
)
|
||||
|
||||
var (
|
||||
HasAVX2 = cpuid.CPU.Has(cpuid.AVX2)
|
||||
HasSSE = cpuid.CPU.Has(cpuid.SSE)
|
||||
)
|
||||
|
||||
func init() {
|
||||
switch v := os.Getenv("SONIC_MODE"); v {
|
||||
case "" : break
|
||||
case "auto" : break
|
||||
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))
|
||||
}
|
||||
}
|
||||
175
vendor/github.com/bytedance/sonic/internal/decoder/api/decoder.go
generated
vendored
Normal file
175
vendor/github.com/bytedance/sonic/internal/decoder/api/decoder.go
generated
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* 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 (
|
||||
`reflect`
|
||||
|
||||
`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`
|
||||
)
|
||||
|
||||
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
|
||||
_F_case_sensitive = consts.F_case_sensitive
|
||||
|
||||
_MaxStack = consts.MaxStack
|
||||
|
||||
OptionUseInt64 = consts.OptionUseInt64
|
||||
OptionUseNumber = consts.OptionUseNumber
|
||||
OptionUseUnicodeErrors = consts.OptionUseUnicodeErrors
|
||||
OptionDisableUnknown = consts.OptionDisableUnknown
|
||||
OptionCopyString = consts.OptionCopyString
|
||||
OptionValidateString = consts.OptionValidateString
|
||||
OptionNoValidateJSON = consts.OptionNoValidateJSON
|
||||
OptionCaseSensitive = consts.OptionCaseSensitive
|
||||
)
|
||||
|
||||
type (
|
||||
Options = consts.Options
|
||||
MismatchTypeError = errors.MismatchTypeError
|
||||
SyntaxError = errors.SyntaxError
|
||||
)
|
||||
|
||||
func (self *Decoder) SetOptions(opts Options) {
|
||||
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
|
||||
f uint64
|
||||
s string
|
||||
}
|
||||
|
||||
// NewDecoder creates a new decoder instance.
|
||||
func NewDecoder(s string) *Decoder {
|
||||
return &Decoder{s: s}
|
||||
}
|
||||
|
||||
// Pos returns the current decoding position.
|
||||
func (self *Decoder) Pos() int {
|
||||
return self.i
|
||||
}
|
||||
|
||||
func (self *Decoder) Reset(s string) {
|
||||
self.s = s
|
||||
self.i = 0
|
||||
// self.f = 0
|
||||
}
|
||||
|
||||
func (self *Decoder) CheckTrailings() error {
|
||||
pos := self.i
|
||||
buf := self.s
|
||||
/* skip all the trailing spaces */
|
||||
if pos != len(buf) {
|
||||
for pos < len(buf) && (types.SPACE_MASK & (1 << buf[pos])) != 0 {
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
/* then it must be at EOF */
|
||||
if pos == len(buf) {
|
||||
return nil
|
||||
}
|
||||
|
||||
/* junk after JSON value */
|
||||
return SyntaxError {
|
||||
Src : buf,
|
||||
Pos : pos,
|
||||
Code : types.ERR_INVALID_CHAR,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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 {
|
||||
return decodeImpl(&self.s, &self.i, self.f, val)
|
||||
}
|
||||
|
||||
// UseInt64 indicates the Decoder to unmarshal an integer into an interface{} as an
|
||||
// int64 instead of as a float64.
|
||||
func (self *Decoder) UseInt64() {
|
||||
self.f |= 1 << _F_use_int64
|
||||
self.f &^= 1 << _F_use_number
|
||||
}
|
||||
|
||||
// UseNumber indicates the Decoder to unmarshal a number into an interface{} as a
|
||||
// json.Number instead of as a float64.
|
||||
func (self *Decoder) UseNumber() {
|
||||
self.f &^= 1 << _F_use_int64
|
||||
self.f |= 1 << _F_use_number
|
||||
}
|
||||
|
||||
// UseUnicodeErrors indicates the Decoder to return an error when encounter invalid
|
||||
// UTF-8 escape sequences.
|
||||
func (self *Decoder) UseUnicodeErrors() {
|
||||
self.f |= 1 << _F_disable_urc
|
||||
}
|
||||
|
||||
// DisallowUnknownFields indicates the Decoder to return an error when the destination
|
||||
// is a struct and the input contains object keys which do not match any
|
||||
// non-ignored, exported fields in the destination.
|
||||
func (self *Decoder) DisallowUnknownFields() {
|
||||
self.f |= 1 << _F_disable_unknown
|
||||
}
|
||||
|
||||
// CopyString indicates the Decoder to decode string values by copying instead of referring.
|
||||
func (self *Decoder) CopyString() {
|
||||
self.f |= 1 << _F_copy_string
|
||||
}
|
||||
|
||||
// ValidateString causes the Decoder to validate string values when decoding string value
|
||||
// in JSON. Validation is that, returning error when unescaped control chars(0x00-0x1f) or
|
||||
// invalid UTF-8 chars in the string value of JSON.
|
||||
func (self *Decoder) ValidateString() {
|
||||
self.f |= 1 << _F_validate_string
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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.
|
||||
// Otherwise, returns negative error code using start and invalid character position using end
|
||||
func Skip(data []byte) (start int, end int) {
|
||||
s := rt.Mem2Str(data)
|
||||
p := 0
|
||||
m := types.NewStateMachine()
|
||||
ret := native.SkipOne(&s, &p, m, uint64(0))
|
||||
types.FreeStateMachine(m)
|
||||
return ret, p
|
||||
}
|
||||
38
vendor/github.com/bytedance/sonic/internal/decoder/api/decoder_amd64.go
generated
vendored
Normal file
38
vendor/github.com/bytedance/sonic/internal/decoder/api/decoder_amd64.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
//go:build go1.17 && !go1.26
|
||||
// +build go1.17,!go1.26
|
||||
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
}
|
||||
38
vendor/github.com/bytedance/sonic/internal/decoder/api/decoder_arm64.go
generated
vendored
Normal file
38
vendor/github.com/bytedance/sonic/internal/decoder/api/decoder_arm64.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
// +build go1.17,!go1.26
|
||||
|
||||
/*
|
||||
* 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/decoder/optdec`
|
||||
`github.com/bytedance/sonic/internal/envs`
|
||||
)
|
||||
|
||||
var (
|
||||
pretouchImpl = optdec.Pretouch
|
||||
decodeImpl = optdec.Decode
|
||||
)
|
||||
|
||||
|
||||
func init() {
|
||||
// when in aarch64, we enable all optimization
|
||||
envs.EnableOptDec()
|
||||
envs.EnableFastMap()
|
||||
}
|
||||
|
||||
|
||||
247
vendor/github.com/bytedance/sonic/internal/decoder/api/stream.go
generated
vendored
Normal file
247
vendor/github.com/bytedance/sonic/internal/decoder/api/stream.go
generated
vendored
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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 (
|
||||
"bytes"
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
"github.com/bytedance/sonic/internal/native"
|
||||
"github.com/bytedance/sonic/internal/native/types"
|
||||
"github.com/bytedance/sonic/internal/rt"
|
||||
"github.com/bytedance/sonic/internal/utils"
|
||||
"github.com/bytedance/sonic/option"
|
||||
)
|
||||
|
||||
var (
|
||||
minLeftBufferShift uint = 1
|
||||
)
|
||||
|
||||
// StreamDecoder is the decoder context object for streaming input.
|
||||
type StreamDecoder struct {
|
||||
r io.Reader
|
||||
buf []byte
|
||||
scanp int
|
||||
scanned int64
|
||||
err error
|
||||
Decoder
|
||||
}
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func () interface{} {
|
||||
return make([]byte, 0, option.DefaultDecoderBufferSize)
|
||||
},
|
||||
}
|
||||
|
||||
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.
|
||||
func NewStreamDecoder(r io.Reader) *StreamDecoder {
|
||||
return &StreamDecoder{r : r}
|
||||
}
|
||||
|
||||
// Decode decodes input stream into val with corresponding data.
|
||||
// Redundantly bytes may be read and left in its buffer, and can be used at next call.
|
||||
// Either io error from underlying io.Reader (except io.EOF)
|
||||
// or syntax error from data will be recorded and stop subsequently decoding.
|
||||
func (self *StreamDecoder) Decode(val interface{}) (err error) {
|
||||
// read more data into buf
|
||||
if self.More() {
|
||||
var s = self.scanp
|
||||
try_skip:
|
||||
var e = len(self.buf)
|
||||
var src = rt.Mem2Str(self.buf[s:e])
|
||||
// try skip
|
||||
var x = 0;
|
||||
if y := native.SkipOneFast(&src, &x); y < 0 {
|
||||
if self.readMore() {
|
||||
goto try_skip
|
||||
}
|
||||
if self.err == nil {
|
||||
self.err = SyntaxError{e, self.s, types.ParsingError(-s), ""}
|
||||
self.setErr(self.err)
|
||||
}
|
||||
return self.err
|
||||
} else {
|
||||
s = y + s
|
||||
e = x + s
|
||||
}
|
||||
|
||||
// must copy string here for safety
|
||||
self.Decoder.Reset(string(self.buf[s:e]))
|
||||
err = self.Decoder.Decode(val)
|
||||
if err != nil {
|
||||
self.setErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
self.scanp = e
|
||||
_, empty := self.scan()
|
||||
if empty {
|
||||
// no remain valid bytes, thus we just recycle buffer
|
||||
mem := self.buf
|
||||
self.buf = nil
|
||||
freeBytes(mem)
|
||||
} else {
|
||||
// remain undecoded bytes, move them onto head
|
||||
n := copy(self.buf, self.buf[self.scanp:])
|
||||
self.buf = self.buf[:n]
|
||||
}
|
||||
|
||||
self.scanned += int64(self.scanp)
|
||||
self.scanp = 0
|
||||
}
|
||||
|
||||
return self.err
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return self.scanned + int64(self.scanp)
|
||||
}
|
||||
|
||||
// Buffered returns a reader of the data remaining in the Decoder's buffer.
|
||||
// The reader is valid until the next call to Decode.
|
||||
func (self *StreamDecoder) Buffered() io.Reader {
|
||||
return bytes.NewReader(self.buf[self.scanp:])
|
||||
}
|
||||
|
||||
// More reports whether there is another element in the
|
||||
// current array or object being parsed.
|
||||
func (self *StreamDecoder) More() bool {
|
||||
if self.err != nil {
|
||||
return false
|
||||
}
|
||||
c, err := self.peek()
|
||||
return err == nil && c != ']' && c != '}'
|
||||
}
|
||||
|
||||
// More reports whether there is another element in the
|
||||
// current array or object being parsed.
|
||||
func (self *StreamDecoder) readMore() bool {
|
||||
if self.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
var err error
|
||||
var n int
|
||||
for {
|
||||
// Grow buffer if not large enough.
|
||||
l := len(self.buf)
|
||||
realloc(&self.buf)
|
||||
|
||||
n, err = self.r.Read(self.buf[l:cap(self.buf)])
|
||||
self.buf = self.buf[: l+n]
|
||||
|
||||
self.scanp = l
|
||||
_, empty := self.scan()
|
||||
if !empty {
|
||||
return true
|
||||
}
|
||||
|
||||
// buffer has been scanned, now report any error
|
||||
if err != nil {
|
||||
self.setErr(err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StreamDecoder) setErr(err error) {
|
||||
self.err = err
|
||||
mem := self.buf[:0]
|
||||
self.buf = nil
|
||||
freeBytes(mem)
|
||||
}
|
||||
|
||||
func (self *StreamDecoder) peek() (byte, error) {
|
||||
var err error
|
||||
for {
|
||||
c, empty := self.scan()
|
||||
if !empty {
|
||||
return byte(c), nil
|
||||
}
|
||||
// buffer has been scanned, now report any error
|
||||
if err != nil {
|
||||
self.setErr(err)
|
||||
return 0, err
|
||||
}
|
||||
err = self.refill()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StreamDecoder) scan() (byte, bool) {
|
||||
for i := self.scanp; i < len(self.buf); i++ {
|
||||
c := self.buf[i]
|
||||
if utils.IsSpace(c) {
|
||||
continue
|
||||
}
|
||||
self.scanp = i
|
||||
return c, false
|
||||
}
|
||||
return 0, true
|
||||
}
|
||||
|
||||
|
||||
func (self *StreamDecoder) refill() error {
|
||||
// Make room to read more into the buffer.
|
||||
// First slide down data already consumed.
|
||||
if self.scanp > 0 {
|
||||
self.scanned += int64(self.scanp)
|
||||
n := copy(self.buf, self.buf[self.scanp:])
|
||||
self.buf = self.buf[:n]
|
||||
self.scanp = 0
|
||||
}
|
||||
|
||||
// Grow buffer if not large enough.
|
||||
realloc(&self.buf)
|
||||
|
||||
// Read. Delay error for next iteration (after scan).
|
||||
n, err := self.r.Read(self.buf[len(self.buf):cap(self.buf)])
|
||||
self.buf = self.buf[0 : len(self.buf)+n]
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func realloc(buf *[]byte) bool {
|
||||
l := uint(len(*buf))
|
||||
c := uint(cap(*buf))
|
||||
if c == 0 {
|
||||
*buf = bufPool.Get().([]byte)
|
||||
return true
|
||||
}
|
||||
if c - l <= c >> minLeftBufferShift {
|
||||
e := l+(l>>minLeftBufferShift)
|
||||
if e <= c {
|
||||
e = c*2
|
||||
}
|
||||
tmp := make([]byte, l, e)
|
||||
copy(tmp, *buf)
|
||||
*buf = tmp
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
37
vendor/github.com/bytedance/sonic/internal/decoder/consts/option.go
generated
vendored
Normal file
37
vendor/github.com/bytedance/sonic/internal/decoder/consts/option.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
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
|
||||
F_case_sensitive = 7
|
||||
)
|
||||
|
||||
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
|
||||
OptionCaseSensitive Options = 1 << F_case_sensitive
|
||||
)
|
||||
|
||||
const (
|
||||
MaxStack = 4096
|
||||
)
|
||||
191
vendor/github.com/bytedance/sonic/internal/decoder/errors/errors.go
generated
vendored
Normal file
191
vendor/github.com/bytedance/sonic/internal/decoder/errors/errors.go
generated
vendored
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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 errors
|
||||
|
||||
import (
|
||||
`encoding/json`
|
||||
`errors`
|
||||
`fmt`
|
||||
`reflect`
|
||||
`strconv`
|
||||
`strings`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
type SyntaxError struct {
|
||||
Pos int
|
||||
Src string
|
||||
Code types.ParsingError
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (self SyntaxError) Error() string {
|
||||
return fmt.Sprintf("%q", self.Description())
|
||||
}
|
||||
|
||||
func (self SyntaxError) Description() string {
|
||||
return "Syntax error " + self.description()
|
||||
}
|
||||
|
||||
func (self SyntaxError) description() string {
|
||||
/* check for empty source */
|
||||
if self.Src == "" {
|
||||
return fmt.Sprintf("no sources available, the input json is empty: %#v", self)
|
||||
}
|
||||
|
||||
p, x, q, y := calcBounds(len(self.Src), self.Pos)
|
||||
|
||||
/* compose the error description */
|
||||
return fmt.Sprintf(
|
||||
"at index %d: %s\n\n\t%s\n\t%s^%s\n",
|
||||
self.Pos,
|
||||
self.Message(),
|
||||
self.Src[p:q],
|
||||
strings.Repeat(".", x),
|
||||
strings.Repeat(".", y),
|
||||
)
|
||||
}
|
||||
|
||||
func calcBounds(size int, pos int) (lbound int, lwidth int, rbound int, rwidth int) {
|
||||
if pos >= size || pos < 0 {
|
||||
return 0, 0, size, 0
|
||||
}
|
||||
|
||||
i := 16
|
||||
lbound = pos - i
|
||||
rbound = pos + i
|
||||
|
||||
/* prevent slicing before the beginning */
|
||||
if lbound < 0 {
|
||||
lbound, rbound, i = 0, rbound - lbound, i + lbound
|
||||
}
|
||||
|
||||
/* prevent slicing beyond the end */
|
||||
if n := size; rbound > n {
|
||||
n = rbound - n
|
||||
rbound = size
|
||||
|
||||
/* move the left bound if possible */
|
||||
if lbound > n {
|
||||
i += n
|
||||
lbound -= n
|
||||
}
|
||||
}
|
||||
|
||||
/* left and right length */
|
||||
lwidth = clamp_zero(i)
|
||||
rwidth = clamp_zero(rbound - lbound - i - 1)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self SyntaxError) Message() string {
|
||||
if self.Msg == "" {
|
||||
return self.Code.Message()
|
||||
}
|
||||
return self.Msg
|
||||
}
|
||||
|
||||
func clamp_zero(v int) int {
|
||||
if v < 0 {
|
||||
return 0
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
/** JIT Error Helpers **/
|
||||
|
||||
var StackOverflow = &json.UnsupportedValueError {
|
||||
Str : "Value nesting too deep",
|
||||
Value : reflect.ValueOf("..."),
|
||||
}
|
||||
|
||||
func ErrorWrap(src string, pos int, code types.ParsingError) error {
|
||||
return *error_wrap_heap(src, pos, code)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func error_wrap_heap(src string, pos int, code types.ParsingError) *SyntaxError {
|
||||
return &SyntaxError {
|
||||
Pos : pos,
|
||||
Src : src,
|
||||
Code : code,
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorType(vt *rt.GoType) error {
|
||||
return &json.UnmarshalTypeError{Type: vt.Pack()}
|
||||
}
|
||||
|
||||
type MismatchTypeError struct {
|
||||
Pos int
|
||||
Src string
|
||||
Type reflect.Type
|
||||
}
|
||||
|
||||
func swithchJSONType (src string, pos int) string {
|
||||
var val string
|
||||
switch src[pos] {
|
||||
case 'f': fallthrough
|
||||
case 't': val = "bool"
|
||||
case '"': val = "string"
|
||||
case '{': val = "object"
|
||||
case '[': val = "array"
|
||||
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': val = "number"
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func (self MismatchTypeError) Error() string {
|
||||
se := SyntaxError {
|
||||
Pos : self.Pos,
|
||||
Src : self.Src,
|
||||
Code : types.ERR_MISMATCH,
|
||||
}
|
||||
return fmt.Sprintf("Mismatch type %s with value %s %q", self.Type.String(), swithchJSONType(self.Src, self.Pos), se.description())
|
||||
}
|
||||
|
||||
func (self MismatchTypeError) Description() string {
|
||||
se := SyntaxError {
|
||||
Pos : self.Pos,
|
||||
Src : self.Src,
|
||||
Code : types.ERR_MISMATCH,
|
||||
}
|
||||
return fmt.Sprintf("Mismatch type %s with value %s %s", self.Type.String(), swithchJSONType(self.Src, self.Pos), se.description())
|
||||
}
|
||||
|
||||
func ErrorMismatch(src string, pos int, vt *rt.GoType) error {
|
||||
return &MismatchTypeError {
|
||||
Pos : pos,
|
||||
Src : src,
|
||||
Type : vt.Pack(),
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorField(name string) error {
|
||||
return errors.New("json: unknown field " + strconv.Quote(name))
|
||||
}
|
||||
|
||||
func ErrorValue(value string, vtype reflect.Type) error {
|
||||
return &json.UnmarshalTypeError {
|
||||
Type : vtype,
|
||||
Value : value,
|
||||
}
|
||||
}
|
||||
0
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm.s
generated
vendored
Normal file
0
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm.s
generated
vendored
Normal file
121
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm_stubs_amd64_go117.go
generated
vendored
Normal file
121
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm_stubs_amd64_go117.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
// +build go1.17,!go1.21
|
||||
|
||||
// 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 jitdec
|
||||
|
||||
import (
|
||||
`strconv`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
`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(uintptr(unsafe.Pointer(&rt.RuntimeWriteBarrier))))
|
||||
|
||||
_F_gcWriteBarrierAX = jit.Func(rt.GcWriteBarrierAX)
|
||||
)
|
||||
|
||||
func (self *_Assembler) WritePtrAX(i int, rec obj.Addr, saveDI bool) {
|
||||
self.Emit("MOVQ", _V_writeBarrier, _R9)
|
||||
self.Emit("CMPL", jit.Ptr(_R9, 0), jit.Imm(0))
|
||||
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
if saveDI {
|
||||
self.save(_DI)
|
||||
}
|
||||
self.Emit("LEAQ", rec, _DI)
|
||||
self.call(_F_gcWriteBarrierAX)
|
||||
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, _R9)
|
||||
self.Emit("CMPL", jit.Ptr(_R9, 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.call(_F_gcWriteBarrierAX)
|
||||
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, _R9)
|
||||
self.Emit("CMPL", jit.Ptr(_R9, 0), jit.Imm(0))
|
||||
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
if saveDI {
|
||||
self.save(_DI)
|
||||
}
|
||||
self.Emit("LEAQ", rec, _DI)
|
||||
self.call(_F_gcWriteBarrierAX)
|
||||
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, _AX)
|
||||
self.Emit("CMPL", jit.Ptr(_AX, 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.call(_F_gcWriteBarrierAX)
|
||||
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}")
|
||||
}
|
||||
126
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm_stubs_amd64_go121.go
generated
vendored
Normal file
126
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/asm_stubs_amd64_go121.go
generated
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
// +build go1.21,!go1.26
|
||||
|
||||
// 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 jitdec
|
||||
|
||||
import (
|
||||
`strconv`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
`github.com/bytedance/sonic/internal/jit`
|
||||
`github.com/twitchyliquid64/golang-asm/obj`
|
||||
`github.com/twitchyliquid64/golang-asm/obj/x86`
|
||||
)
|
||||
|
||||
// Notice: gcWriteBarrier must use R11 register!!
|
||||
var _R11 = _IC
|
||||
|
||||
var (
|
||||
_V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&rt.RuntimeWriteBarrier))))
|
||||
|
||||
_F_gcWriteBarrier2 = jit.Func(rt.GcWriteBarrier2)
|
||||
)
|
||||
|
||||
func (self *_Assembler) WritePtrAX(i int, rec obj.Addr, saveDI bool) {
|
||||
self.Emit("MOVQ", _V_writeBarrier, _R9)
|
||||
self.Emit("CMPL", jit.Ptr(_R9, 0), jit.Imm(0))
|
||||
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
if saveDI {
|
||||
self.save(_DI, _R11)
|
||||
} else {
|
||||
self.save(_R11)
|
||||
}
|
||||
self.Emit("MOVQ", _F_gcWriteBarrier2, _R11)
|
||||
self.Rjmp("CALL", _R11)
|
||||
self.Emit("MOVQ", _AX, jit.Ptr(_R11, 0))
|
||||
self.Emit("MOVQ", rec, _DI)
|
||||
self.Emit("MOVQ", _DI, jit.Ptr(_R11, 8))
|
||||
if saveDI {
|
||||
self.load(_DI, _R11)
|
||||
} else {
|
||||
self.load(_R11)
|
||||
}
|
||||
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
self.Emit("MOVQ", _AX, rec)
|
||||
}
|
||||
|
||||
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, _R9)
|
||||
self.Emit("CMPL", jit.Ptr(_R9, 0), jit.Imm(0))
|
||||
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
if saveAX {
|
||||
self.save(_AX, _R11)
|
||||
} else {
|
||||
self.save(_R11)
|
||||
}
|
||||
self.Emit("MOVQ", _F_gcWriteBarrier2, _R11)
|
||||
self.Rjmp("CALL", _R11)
|
||||
self.Emit("MOVQ", ptr, jit.Ptr(_R11, 0))
|
||||
self.Emit("MOVQ", rec, _AX)
|
||||
self.Emit("MOVQ", _AX, jit.Ptr(_R11, 8))
|
||||
if saveAX {
|
||||
self.load(_AX, _R11)
|
||||
} else {
|
||||
self.load(_R11)
|
||||
}
|
||||
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
self.Emit("MOVQ", ptr, rec)
|
||||
}
|
||||
|
||||
func (self *_ValueDecoder) WritePtrAX(i int, rec obj.Addr, saveDI bool) {
|
||||
self.Emit("MOVQ", _V_writeBarrier, _R9)
|
||||
self.Emit("CMPL", jit.Ptr(_R9, 0), jit.Imm(0))
|
||||
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
if saveDI {
|
||||
self.save(_DI, _R11)
|
||||
} else {
|
||||
self.save(_R11)
|
||||
}
|
||||
self.Emit("MOVQ", _F_gcWriteBarrier2, _R11)
|
||||
self.Rjmp("CALL", _R11)
|
||||
self.Emit("MOVQ", _AX, jit.Ptr(_R11, 0))
|
||||
self.Emit("MOVQ", rec, _DI)
|
||||
self.Emit("MOVQ", _DI, jit.Ptr(_R11, 8))
|
||||
if saveDI {
|
||||
self.load(_DI, _R11)
|
||||
} else {
|
||||
self.load(_R11)
|
||||
}
|
||||
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
self.Emit("MOVQ", _AX, rec)
|
||||
}
|
||||
|
||||
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, _AX)
|
||||
self.Emit("CMPL", jit.Ptr(_AX, 0), jit.Imm(0))
|
||||
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
self.save(_R11)
|
||||
self.Emit("MOVQ", _F_gcWriteBarrier2, _R11)
|
||||
self.Rjmp("CALL", _R11)
|
||||
self.Emit("MOVQ", ptr, jit.Ptr(_R11, 0))
|
||||
self.Emit("MOVQ", rec, _AX)
|
||||
self.Emit("MOVQ", _AX, jit.Ptr(_R11, 8))
|
||||
self.load(_R11)
|
||||
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
self.Emit("MOVQ", ptr, rec)
|
||||
}
|
||||
2023
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/assembler_regabi_amd64.go
generated
vendored
Normal file
2023
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/assembler_regabi_amd64.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1229
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/compiler.go
generated
vendored
Normal file
1229
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/compiler.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
70
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/debug.go
generated
vendored
Normal file
70
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/debug.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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 jitdec
|
||||
|
||||
import (
|
||||
`os`
|
||||
`runtime`
|
||||
`runtime/debug`
|
||||
`strings`
|
||||
|
||||
`github.com/bytedance/sonic/internal/jit`
|
||||
)
|
||||
|
||||
|
||||
var (
|
||||
debugSyncGC = os.Getenv("SONIC_SYNC_GC") != ""
|
||||
debugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
|
||||
)
|
||||
|
||||
var (
|
||||
_Instr_End _Instr = newInsOp(_OP_nil_1)
|
||||
|
||||
_F_gc = jit.Func(runtime.GC)
|
||||
_F_force_gc = jit.Func(debug.FreeOSMemory)
|
||||
_F_println = jit.Func(println_wrapper)
|
||||
_F_print = jit.Func(print)
|
||||
)
|
||||
|
||||
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 (self *_Assembler) force_gc() {
|
||||
self.call_go(_F_gc)
|
||||
self.call_go(_F_force_gc)
|
||||
}
|
||||
|
||||
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.force_gc()
|
||||
}
|
||||
}
|
||||
141
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/decoder.go
generated
vendored
Normal file
141
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/decoder.go
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
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
|
||||
_F_case_sensitive = consts.F_case_sensitive
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
730
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/generic_regabi_amd64.go
generated
vendored
Normal file
730
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/generic_regabi_amd64.go
generated
vendored
Normal file
@ -0,0 +1,730 @@
|
||||
// +build go1.17,!go1.26
|
||||
|
||||
/*
|
||||
* 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 jitdec
|
||||
|
||||
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`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
/** Crucial Registers:
|
||||
*
|
||||
* ST(R13) && 0(SP) : ro, decoder stack
|
||||
* DF(AX) : ro, decoder flags
|
||||
* EP(BX) : wo, error pointer
|
||||
* IP(R10) : ro, input pointer
|
||||
* IL(R12) : ro, input length
|
||||
* IC(R11) : 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 = 48 // 48 bytes for saving the registers before CALL instructions
|
||||
_VD_locals = 96 // 96 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_R9 = jit.Ptr(_SP, _VD_fargs + _VD_saves + 56)
|
||||
)
|
||||
type _ValueDecoder struct {
|
||||
jit.BaseAssembler
|
||||
}
|
||||
|
||||
var (
|
||||
_VAR_cs_LR = jit.Ptr(_SP, _VD_fargs + _VD_saves + 64)
|
||||
_VAR_cs_p = jit.Ptr(_SP, _VD_fargs + _VD_saves + 72)
|
||||
_VAR_cs_n = jit.Ptr(_SP, _VD_fargs + _VD_saves + 80)
|
||||
_VAR_cs_d = jit.Ptr(_SP, _VD_fargs + _VD_saves + 88)
|
||||
)
|
||||
|
||||
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, _R9) // MOVQ ${fn}, AX
|
||||
self.Rjmp("CALL", _R9) // 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
|
||||
}
|
||||
|
||||
func (self *_ValueDecoder) callc(fn obj.Addr) {
|
||||
self.save(_IP)
|
||||
self.call(fn)
|
||||
self.load(_IP)
|
||||
}
|
||||
|
||||
func (self *_ValueDecoder) call_c(fn obj.Addr) {
|
||||
self.Emit("XCHGQ", _IC, _BX)
|
||||
self.callc(fn)
|
||||
self.Emit("XCHGQ", _IC, _BX)
|
||||
}
|
||||
|
||||
/** 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(rt.ConvTslice)
|
||||
_F_convTstring = jit.Func(rt.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.callc(_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", jit.Imm(_A_init_len), _BX) // MOVQ _A_init_len, BX
|
||||
self.Emit("MOVQ", jit.Imm(_A_init_cap), _CX) // MOVQ _A_init_cap, CX
|
||||
self.call_go(_F_makeslice) // CALL_GO runtime.makeslice
|
||||
|
||||
/* pack into an interface */
|
||||
self.Emit("MOVQ", jit.Imm(_A_init_len), _BX) // MOVQ _A_init_len, BX
|
||||
self.Emit("MOVQ", jit.Imm(_A_init_cap), _CX) // MOVQ _A_init_cap, CX
|
||||
self.call_go(_F_convTslice) // CALL_GO runtime.convTslice
|
||||
self.Emit("MOVQ", _AX, _R8) // MOVQ AX, 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(_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_0, 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)
|
||||
|
||||
/* 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" , _AX, _BX) // MOVQ AX, BX
|
||||
self.Emit("MOVQ" , _R8, _AX) // MOVQ R8, AX
|
||||
self.call_go(_F_convTstring) // CALL_GO runtime.convTstring
|
||||
self.Emit("MOVQ" , _AX, _R9) // MOVQ AX, 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", _AX, _DI) // MOVQ AX, DI
|
||||
self.Emit("MOVQ", _T_map, _AX) // MOVQ _T_map, AX
|
||||
self.Emit("MOVQ", _SI, _BX) // MOVQ SI, BX
|
||||
self.Emit("MOVQ", _R8, _CX) // MOVQ R9, CX
|
||||
self.call_go(_F_mapassign_faststr) // CALL_GO runtime.mapassign_faststr
|
||||
|
||||
/* 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, _BX) // MOVQ _T_byte, BX
|
||||
self.Emit("MOVB", jit.Imm(0), _CX) // MOVB $0, CX
|
||||
self.call_go(_F_mallocgc) // CALL_GO runtime.mallocgc
|
||||
self.Emit("MOVQ", _AX, _R9) // MOVQ AX, 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.Emit("MOVQ", _R9, _VAR_R9) // SAVE R9
|
||||
self.call_c(_F_unquote) // CALL unquote
|
||||
self.Emit("MOVQ", _VAR_R9, _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
|
||||
//TODO: use ss.Dv directly
|
||||
self.Emit("MOVSD", _VAR_ss_Dv, _X0) // MOVSD ss.Dv, X0
|
||||
|
||||
/* represent numbers as `float64` */
|
||||
self.Link("_use_float64") // _use_float64:
|
||||
self.Emit("MOVQ" , _X0, _AX) // MOVQ X0, AX
|
||||
self.call_go(_F_convT64) // CALL_GO runtime.convT64
|
||||
self.Emit("MOVQ" , _T_float64, _R8) // MOVQ _T_float64, R8
|
||||
self.Emit("MOVQ" , _AX, _R9) // MOVQ AX, 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, _AX) // MOVQ SI, AX
|
||||
self.Emit("MOVQ", _CX, _BX) // MOVQ CX, BX
|
||||
self.call_go(_F_convTstring) // CALL_GO runtime.convTstring
|
||||
self.Emit("MOVQ", _T_number, _R8) // MOVQ _T_number, R8
|
||||
self.Emit("MOVQ", _AX, _R9) // MOVQ AX, 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.call_go(_F_convT64) // CALL_GO runtime.convT64
|
||||
self.Emit("MOVQ", _T_int64, _R8) // MOVQ _T_int64, R8
|
||||
self.Emit("MOVQ", _AX, _R9) // MOVQ AX, 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.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))
|
||||
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") // JA _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), _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), _AX) // MOVQ ST.Vt[CX], AX
|
||||
self.Emit("BTQ" , _AX, _DI)
|
||||
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("MOVQ" , jit.Ptr(_SI, 0), _BX) // MOVQ (SI), BX
|
||||
self.Emit("MOVQ" , jit.Ptr(_SI, 8), _CX) // MOVQ 8(SI), CX
|
||||
self.Emit("MOVQ" , jit.Ptr(_SI, 16), _DI) // MOVQ 16(SI), DI
|
||||
self.Emit("MOVQ" , _DI, _SI) // MOVQ DI, 24(SP)
|
||||
self.Emit("SHLQ" , jit.Imm(1), _SI) // SHLQ $1, SI
|
||||
self.call_go(_F_growslice) // CALL_GO runtime.growslice
|
||||
self.Emit("MOVQ" , _AX, _DI) // MOVQ AX, DI
|
||||
self.Emit("MOVQ" , _BX, _DX) // MOVQ BX, DX
|
||||
self.Emit("MOVQ" , _CX, _AX) // MOVQ CX, 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.Emit("MOVQ", _R8, _VAR_cs_p)
|
||||
self.Emit("MOVQ", _AX, _VAR_cs_n)
|
||||
self.Emit("MOVQ", _DI, _VAR_cs_LR)
|
||||
self.Emit("MOVQ", _AX, _BX)
|
||||
self.Emit("MOVQ", _AX, _CX)
|
||||
self.Emit("MOVQ", _T_byte, _AX)
|
||||
self.call_go(_F_makeslice)
|
||||
self.Emit("MOVQ", _AX, _VAR_cs_d)
|
||||
self.Emit("MOVQ", _VAR_cs_p, _BX)
|
||||
self.Emit("MOVQ", _VAR_cs_n, _CX)
|
||||
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.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.call_go(_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) {
|
||||
rt.Throw(fmt.Sprintf("invalid value type: %d", vt))
|
||||
}
|
||||
37
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/generic_regabi_amd64_test.s
generated
vendored
Normal file
37
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/generic_regabi_amd64_test.s
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
// +build go1.17,!go1.26
|
||||
|
||||
//
|
||||
// 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) , R13
|
||||
MOVQ sp+8(FP) , R10
|
||||
MOVQ sn+16(FP), R12
|
||||
MOVQ ic+24(FP), R11
|
||||
MOVQ vp+32(FP), R15
|
||||
MOVQ df+40(FP), AX
|
||||
MOVQ ·_subr_decode_value(SB), BX
|
||||
CALL BX
|
||||
MOVQ R11, rp+48(FP)
|
||||
MOVQ BX, ex+56(FP)
|
||||
RET
|
||||
145
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/pools.go
generated
vendored
Normal file
145
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/pools.go
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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 jitdec
|
||||
|
||||
import (
|
||||
`sync`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/caching`
|
||||
`github.com/bytedance/sonic/internal/native/types`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
const (
|
||||
_MinSlice = 2
|
||||
_MaxStack = 4096 // 4k slots
|
||||
_MaxStackBytes = _MaxStack * _PtrBytes
|
||||
_MaxDigitNums = types.MaxDigitNums // used in atof fallback algorithm
|
||||
)
|
||||
|
||||
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{})
|
||||
)
|
||||
|
||||
var (
|
||||
stackPool = sync.Pool{}
|
||||
valueCache = []unsafe.Pointer(nil)
|
||||
fieldCache = []*caching.FieldMap(nil)
|
||||
fieldCacheMux = sync.Mutex{}
|
||||
programCache = caching.CreateProgramCache()
|
||||
)
|
||||
|
||||
type _Stack struct {
|
||||
sp uintptr
|
||||
sb [_MaxStack]unsafe.Pointer
|
||||
mm types.StateMachine
|
||||
vp [types.MAX_RECURSE]unsafe.Pointer
|
||||
dp [_MaxDigitNums]byte
|
||||
ep unsafe.Pointer
|
||||
}
|
||||
|
||||
type _Decoder func(
|
||||
s string,
|
||||
i int,
|
||||
vp unsafe.Pointer,
|
||||
sb *_Stack,
|
||||
fv uint64,
|
||||
sv string, // DO NOT pass value to this argument, since it is only used for local _VAR_sv
|
||||
vk unsafe.Pointer, // DO NOT pass value to this argument, since it is only used for local _VAR_vk
|
||||
) (int, error)
|
||||
|
||||
var _KeepAlive struct {
|
||||
s string
|
||||
i int
|
||||
vp unsafe.Pointer
|
||||
sb *_Stack
|
||||
fv uint64
|
||||
sv string
|
||||
vk unsafe.Pointer
|
||||
|
||||
ret int
|
||||
err error
|
||||
|
||||
frame_decoder [_FP_offs]byte
|
||||
frame_generic [_VD_offs]byte
|
||||
}
|
||||
|
||||
var (
|
||||
argPtrs = []bool{true, false, false, true, true, false, true, false, true}
|
||||
localPtrs = []bool{}
|
||||
)
|
||||
|
||||
var (
|
||||
argPtrs_generic = []bool{true}
|
||||
localPtrs_generic = []bool{}
|
||||
)
|
||||
|
||||
func newStack() *_Stack {
|
||||
if ret := stackPool.Get(); ret == nil {
|
||||
return new(_Stack)
|
||||
} else {
|
||||
return ret.(*_Stack)
|
||||
}
|
||||
}
|
||||
|
||||
func resetStack(p *_Stack) {
|
||||
rt.MemclrNoHeapPointers(unsafe.Pointer(p), _StackSize)
|
||||
}
|
||||
|
||||
func freeStack(p *_Stack) {
|
||||
p.sp = 0
|
||||
stackPool.Put(p)
|
||||
}
|
||||
|
||||
func freezeValue(v unsafe.Pointer) uintptr {
|
||||
valueCache = append(valueCache, v)
|
||||
return uintptr(v)
|
||||
}
|
||||
|
||||
func freezeFields(v *caching.FieldMap) int64 {
|
||||
fieldCacheMux.Lock()
|
||||
fieldCache = append(fieldCache, v)
|
||||
fieldCacheMux.Unlock()
|
||||
return referenceFields(v)
|
||||
}
|
||||
|
||||
func referenceFields(v *caching.FieldMap) int64 {
|
||||
return int64(uintptr(unsafe.Pointer(v)))
|
||||
}
|
||||
|
||||
func makeDecoder(vt *rt.GoType, _ ...interface{}) (interface{}, error) {
|
||||
if pp, err := newCompiler().compile(vt.Pack()); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return newAssembler(pp).Load(), nil
|
||||
}
|
||||
}
|
||||
|
||||
func findOrCompile(vt *rt.GoType) (_Decoder, error) {
|
||||
if val := programCache.Get(vt); val != nil {
|
||||
return val.(_Decoder), nil
|
||||
} else if ret, err := programCache.Compute(vt, makeDecoder); err == nil {
|
||||
return ret.(_Decoder), nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
58
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/primitives.go
generated
vendored
Normal file
58
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/primitives.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 jitdec
|
||||
|
||||
import (
|
||||
`encoding`
|
||||
`encoding/json`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/native`
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
func decodeTypedPointer(s string, i int, vt *rt.GoType, vp unsafe.Pointer, sb *_Stack, fv uint64) (int, error) {
|
||||
if fn, err := findOrCompile(vt); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
rt.MoreStack(_FP_size + _VD_size + native.MaxFrameSize)
|
||||
ret, err := fn(s, i, vp, sb, fv, "", nil)
|
||||
return ret, err
|
||||
}
|
||||
}
|
||||
|
||||
func decodeJsonUnmarshaler(vv interface{}, s string) error {
|
||||
return vv.(json.Unmarshaler).UnmarshalJSON(rt.Str2Mem(s))
|
||||
}
|
||||
|
||||
// used to distinguish between MismatchQuoted and other MismatchedTyped errors, see issue #670 and #716
|
||||
type MismatchQuotedError struct {}
|
||||
|
||||
func (*MismatchQuotedError) Error() string {
|
||||
return "mismatch quoted"
|
||||
}
|
||||
|
||||
func decodeJsonUnmarshalerQuoted(vv interface{}, s string) error {
|
||||
if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
|
||||
return &MismatchQuotedError{}
|
||||
}
|
||||
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))
|
||||
}
|
||||
58
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/types.go
generated
vendored
Normal file
58
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/types.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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 jitdec
|
||||
|
||||
import (
|
||||
`encoding`
|
||||
`encoding/base64`
|
||||
`encoding/json`
|
||||
`reflect`
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/internal/rt`
|
||||
)
|
||||
|
||||
var (
|
||||
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))
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
39
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/utils.go
generated
vendored
Normal file
39
vendor/github.com/bytedance/sonic/internal/decoder/jitdec/utils.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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 jitdec
|
||||
|
||||
import (
|
||||
`unsafe`
|
||||
|
||||
`github.com/bytedance/sonic/loader`
|
||||
)
|
||||
|
||||
//go:nosplit
|
||||
func pbool(v bool) uintptr {
|
||||
return freezeValue(unsafe.Pointer(&v))
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
func ptodec(p loader.Function) _Decoder {
|
||||
return *(*_Decoder)(unsafe.Pointer(&p))
|
||||
}
|
||||
|
||||
func assert_eq(v int64, exp int64, msg string) {
|
||||
if v != exp {
|
||||
panic(msg)
|
||||
}
|
||||
}
|
||||
174
vendor/github.com/bytedance/sonic/internal/decoder/optdec/compile_struct.go
generated
vendored
Normal file
174
vendor/github.com/bytedance/sonic/internal/decoder/optdec/compile_struct.go
generated
vendored
Normal file
@ -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,
|
||||
}
|
||||
}
|
||||
460
vendor/github.com/bytedance/sonic/internal/decoder/optdec/compiler.go
generated
vendored
Normal file
460
vendor/github.com/bytedance/sonic/internal/decoder/optdec/compiler.go
generated
vendored
Normal file
@ -0,0 +1,460 @@
|
||||
package optdec
|
||||
|
||||
import (
|
||||
"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:
|
||||
return &unsupportedTypeDecoder{
|
||||
typ: rt.UnpackType(vt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compiler) compilePtr(vt reflect.Type) decFunc {
|
||||
c.enter(vt)
|
||||
defer c.exit(vt)
|
||||
|
||||
// special 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 && et != rt.JsonNumberType {
|
||||
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 && mt.Key != rt.JsonNumberType {
|
||||
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
|
||||
}
|
||||
|
||||
/* NOTE: encoding/json 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
|
||||
// NOTE: actually, encoding/json can't use float as map key
|
||||
case reflect.Float32:
|
||||
return decodeFloat32Key
|
||||
case reflect.Float64:
|
||||
return decodeFloat64Key
|
||||
case reflect.String:
|
||||
if rt.UnpackType(vt.Key()) == rt.JsonNumberType {
|
||||
return decodeJsonNumberKey
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 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, string 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
vendor/github.com/bytedance/sonic/internal/decoder/optdec/const.go
generated
vendored
Normal file
60
vendor/github.com/bytedance/sonic/internal/decoder/optdec/const.go
generated
vendored
Normal 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
vendor/github.com/bytedance/sonic/internal/decoder/optdec/context.go
generated
vendored
Normal file
3
vendor/github.com/bytedance/sonic/internal/decoder/optdec/context.go
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
package optdec
|
||||
|
||||
type context = Context
|
||||
160
vendor/github.com/bytedance/sonic/internal/decoder/optdec/decoder.go
generated
vendored
Normal file
160
vendor/github.com/bytedance/sonic/internal/decoder/optdec/decoder.go
generated
vendored
Normal 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)
|
||||
}
|
||||
79
vendor/github.com/bytedance/sonic/internal/decoder/optdec/errors.go
generated
vendored
Normal file
79
vendor/github.com/bytedance/sonic/internal/decoder/optdec/errors.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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,
|
||||
}
|
||||
}
|
||||
|
||||
func error_unsuppoted(typ *rt.GoType) error {
|
||||
return &json.UnsupportedTypeError{
|
||||
Type: typ.Pack(),
|
||||
}
|
||||
}
|
||||
|
||||
294
vendor/github.com/bytedance/sonic/internal/decoder/optdec/functor.go
generated
vendored
Normal file
294
vendor/github.com/bytedance/sonic/internal/decoder/optdec/functor.go
generated
vendored
Normal file
@ -0,0 +1,294 @@
|
||||
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)
|
||||
}
|
||||
|
||||
type unsupportedTypeDecoder struct {
|
||||
typ *rt.GoType
|
||||
}
|
||||
|
||||
|
||||
func (d *unsupportedTypeDecoder) FromDom(vp unsafe.Pointer, node Node, ctx *context) error {
|
||||
if node.IsNull() {
|
||||
return nil
|
||||
}
|
||||
return error_unsuppoted(d.typ)
|
||||
}
|
||||
|
||||
95
vendor/github.com/bytedance/sonic/internal/decoder/optdec/helper.go
generated
vendored
Normal file
95
vendor/github.com/bytedance/sonic/internal/decoder/optdec/helper.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
package optdec
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/bytedance/sonic/internal/native"
|
||||
"github.com/bytedance/sonic/internal/utils"
|
||||
"github.com/bytedance/sonic/internal/native/types"
|
||||
)
|
||||
|
||||
|
||||
func SkipNumberFast(json string, start int) (int, bool) {
|
||||
// find the number ending, we parsed in native, it always 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 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// if not found number, return false
|
||||
if pos == start {
|
||||
return pos, false
|
||||
}
|
||||
return pos, true
|
||||
}
|
||||
|
||||
// pos is the start index of the raw
|
||||
func ValidNumberFast(raw string) bool {
|
||||
ret := utils.SkipNumber(raw, 0)
|
||||
if ret < 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// check trailing chars
|
||||
for ret < len(raw) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func SkipOneFast(json string, pos int) (string, error) {
|
||||
start := native.SkipOneFast(&json, &pos)
|
||||
if start < 0 {
|
||||
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
|
||||
}
|
||||
172
vendor/github.com/bytedance/sonic/internal/decoder/optdec/interface.go
generated
vendored
Normal file
172
vendor/github.com/bytedance/sonic/internal/decoder/optdec/interface.go
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
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 {
|
||||
/* check the defined pointer type for issue 379 */
|
||||
eface := (*rt.GoEface)(vp)
|
||||
|
||||
/*
|
||||
not pointer type, or nil pointer, or self-pointed interface{}, such as
|
||||
```go
|
||||
var v interface{}
|
||||
v = &v
|
||||
return v
|
||||
``` see `issue758_test.go`.
|
||||
*/
|
||||
if eface.Value == nil || eface.Type.Kind() != reflect.Ptr || eface.Value == vp {
|
||||
ret, err := node.AsEface(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*(*interface{})(vp) = ret
|
||||
return nil
|
||||
}
|
||||
|
||||
if node.IsNull() {
|
||||
if eface.Type.Indirect() || (!eface.Type.Indirect() && eface.Type.Pack().Elem().Kind() != reflect.Ptr) {
|
||||
*(*interface{})(vp) = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
etp := rt.PtrElem(eface.Type)
|
||||
vp = eface.Value
|
||||
|
||||
if eface.Type.IsNamed() {
|
||||
// check named pointer type, avoid call its `Unmarshaler`
|
||||
newp := vp
|
||||
etp = eface.Type
|
||||
vp = unsafe.Pointer(&newp)
|
||||
} else if !eface.Type.Indirect() {
|
||||
// check direct value
|
||||
etp = rt.UnpackType(eface.Type.Pack().Elem())
|
||||
}
|
||||
|
||||
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
|
||||
if vt.Kind() != reflect.Ptr || iface.Value == nil {
|
||||
return error_type(d.typ)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
458
vendor/github.com/bytedance/sonic/internal/decoder/optdec/map.go
generated
vendored
Normal file
458
vendor/github.com/bytedance/sonic/internal/decoder/optdec/map.go
generated
vendored
Normal file
@ -0,0 +1,458 @@
|
||||
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) (interface{}, error)
|
||||
|
||||
func decodeKeyU8(dec *mapDecoder, raw string) (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) (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) (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) (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 decodeKeyTextUnmarshaler(dec *mapDecoder, raw string) (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(rt.Str2Mem(key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func decodeFloat32Key(dec *mapDecoder, raw string) (interface{}, error) {
|
||||
key, err := Unquote(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret, err := ParseF64(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ret > math.MaxFloat32 || ret < -math.MaxFloat32 {
|
||||
return nil, error_value(key, dec.mapType.Key.Pack())
|
||||
}
|
||||
return float32(ret), nil
|
||||
}
|
||||
|
||||
func decodeFloat64Key(dec *mapDecoder, raw string) (interface{}, error) {
|
||||
key, err := Unquote(raw)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseF64(key)
|
||||
}
|
||||
|
||||
func decodeJsonNumberKey(dec *mapDecoder, raw string) (interface{}, error) {
|
||||
// skip the quote
|
||||
raw = raw[1:len(raw)-1]
|
||||
end, ok := SkipNumberFast(raw, 0)
|
||||
|
||||
// check trailing chars
|
||||
if !ok || end != len(raw) {
|
||||
return nil, error_value(raw, rt.JsonNumberType.Pack())
|
||||
}
|
||||
|
||||
return json.Number(raw[0:end]), 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 || d.keyDec == nil {
|
||||
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)
|
||||
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
|
||||
}
|
||||
270
vendor/github.com/bytedance/sonic/internal/decoder/optdec/native.go
generated
vendored
Normal file
270
vendor/github.com/bytedance/sonic/internal/decoder/optdec/native.go
generated
vendored
Normal file
@ -0,0 +1,270 @@
|
||||
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 consistent with native/parser.c
|
||||
type _nospaceBlock struct {
|
||||
_ [8]byte
|
||||
_ [8]byte
|
||||
}
|
||||
|
||||
// should consistent with native/parser.c
|
||||
type nodeBuf struct {
|
||||
ncur uintptr
|
||||
parent int64
|
||||
depth uint64
|
||||
nstart uintptr
|
||||
nend uintptr
|
||||
iskey bool
|
||||
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 consistent 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 := int(offset / unsafe.Sizeof(node{}))
|
||||
if curLen != 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 := curLen + calMaxNodeCap(len(p.Json) - int(p.cur - p.start))
|
||||
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
|
||||
}
|
||||
1298
vendor/github.com/bytedance/sonic/internal/decoder/optdec/node.go
generated
vendored
Normal file
1298
vendor/github.com/bytedance/sonic/internal/decoder/optdec/node.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
235
vendor/github.com/bytedance/sonic/internal/decoder/optdec/slice.go
generated
vendored
Normal file
235
vendor/github.com/bytedance/sonic/internal/decoder/optdec/slice.go
generated
vendored
Normal file
@ -0,0 +1,235 @@
|
||||
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 */
|
||||
addr := uintptr(vp) + uintptr(i)*d.elemType.Size
|
||||
n := uintptr(d.len-i) * d.elemType.Size
|
||||
|
||||
/* the boundary pointer may points to another unknown object, so we need to avoid using it */
|
||||
if n != 0 {
|
||||
rt.ClearMemory(d.elemType, unsafe.Pointer(addr), 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
|
||||
}
|
||||
|
||||
/* if slice is empty, just call `AsSliceEface` */
|
||||
if ((*rt.GoSlice)(vp)).Len == 0 {
|
||||
return node.AsSliceEface(ctx, vp)
|
||||
}
|
||||
|
||||
decoder := sliceDecoder{
|
||||
elemType: rt.AnyType,
|
||||
elemDec: &efaceDecoder{},
|
||||
typ: rt.SliceEfaceType.Pack(),
|
||||
}
|
||||
|
||||
return decoder.FromDom(vp, node, ctx)
|
||||
}
|
||||
|
||||
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)
|
||||
*(*[]byte)(vp) = s
|
||||
return err
|
||||
}
|
||||
|
||||
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
vendor/github.com/bytedance/sonic/internal/decoder/optdec/stringopts.go
generated
vendored
Normal file
360
vendor/github.com/bytedance/sonic/internal/decoder/optdec/stringopts.go
generated
vendored
Normal 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, ok := SkipNumberFast(s, 0)
|
||||
// has error or trailing chars
|
||||
if !ok || end != len(s) {
|
||||
return error_mismatch(node, ctx, jsonNumberType)
|
||||
}
|
||||
|
||||
*(*json.Number)(vp) = json.Number(num)
|
||||
return nil
|
||||
}
|
||||
62
vendor/github.com/bytedance/sonic/internal/decoder/optdec/structs.go
generated
vendored
Normal file
62
vendor/github.com/bytedance/sonic/internal/decoder/optdec/structs.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
package optdec
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bytedance/sonic/internal/decoder/consts"
|
||||
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, ctx.Options()&uint64(consts.OptionCaseSensitive) != 0)
|
||||
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
vendor/github.com/bytedance/sonic/internal/decoder/optdec/types.go
generated
vendored
Normal file
60
vendor/github.com/bytedance/sonic/internal/decoder/optdec/types.go
generated
vendored
Normal 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)
|
||||
}
|
||||
285
vendor/github.com/bytedance/sonic/internal/encoder/alg/mapiter.go
generated
vendored
Normal file
285
vendor/github.com/bytedance/sonic/internal/encoder/alg/mapiter.go
generated
vendored
Normal file
@ -0,0 +1,285 @@
|
||||
/*
|
||||
* 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 alg
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bytedance/sonic/internal/encoder/vars"
|
||||
"github.com/bytedance/sonic/internal/rt"
|
||||
)
|
||||
|
||||
type _MapPair struct {
|
||||
k string // when the map key is integer, k is pointed to m
|
||||
v unsafe.Pointer
|
||||
m [32]byte
|
||||
}
|
||||
|
||||
type MapIterator struct {
|
||||
It rt.GoMapIterator // must be the first field
|
||||
kv rt.GoSlice // slice of _MapPair
|
||||
ki int
|
||||
}
|
||||
|
||||
var (
|
||||
iteratorPool = sync.Pool{}
|
||||
iteratorPair = rt.UnpackType(reflect.TypeOf(_MapPair{}))
|
||||
)
|
||||
|
||||
func init() {
|
||||
if unsafe.Offsetof(MapIterator{}.It) != 0 {
|
||||
panic("_MapIterator.it is not the first field")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func newIterator() *MapIterator {
|
||||
if v := iteratorPool.Get(); v == nil {
|
||||
return new(MapIterator)
|
||||
} else {
|
||||
return resetIterator(v.(*MapIterator))
|
||||
}
|
||||
}
|
||||
|
||||
func resetIterator(p *MapIterator) *MapIterator {
|
||||
p.ki = 0
|
||||
p.It = rt.GoMapIterator{}
|
||||
p.kv.Len = 0
|
||||
return p
|
||||
}
|
||||
|
||||
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) {
|
||||
p = self.at(self.kv.Len)
|
||||
self.kv.Len++
|
||||
return
|
||||
}
|
||||
|
||||
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) {
|
||||
p := self.add()
|
||||
p.v = v
|
||||
tk := t.Kind()
|
||||
|
||||
// followed as `encoding/json/emcode.go:resolveKeyName
|
||||
if tk == reflect.String {
|
||||
p.k = *(*string)(k)
|
||||
return nil
|
||||
}
|
||||
|
||||
// check if the key implements the encoding.TextMarshaler interface
|
||||
if t.Pack().Implements(vars.EncodingTextMarshalerType) {
|
||||
if tk != reflect.Interface {
|
||||
return self.appendConcrete(p, t, k)
|
||||
} else {
|
||||
return self.appendInterface(p, t, k)
|
||||
}
|
||||
}
|
||||
|
||||
return self.appendGeneric(p, t, tk, k)
|
||||
}
|
||||
|
||||
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(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.Bool : if *(*bool)(k) { p.k = "true" } else { p.k = "false" }; return nil
|
||||
default : return vars.Error_type(t.Pack())
|
||||
}
|
||||
}
|
||||
|
||||
func (self *MapIterator) appendConcrete(p *_MapPair, t *rt.GoType, k unsafe.Pointer) error {
|
||||
// compiler has already checked that the type implements the encoding.MarshalText interface
|
||||
if !t.Indirect() {
|
||||
k = *(*unsafe.Pointer)(k)
|
||||
}
|
||||
|
||||
// check the TextMarshaler interface
|
||||
eface := rt.GoEface{Value: k, Type: t}.Pack()
|
||||
e, ok := eface.(encoding.TextMarshaler)
|
||||
if !ok {
|
||||
return vars.Error_type(t.Pack())
|
||||
}
|
||||
|
||||
// check for nil pointer
|
||||
if t.Kind() == reflect.Ptr && k == nil {
|
||||
p.k = ""
|
||||
return nil
|
||||
}
|
||||
|
||||
out, err := e.MarshalText()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.k = rt.Mem2Str(out)
|
||||
return nil
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
p.k, err = asText(k)
|
||||
return
|
||||
}
|
||||
|
||||
func IteratorStop(p *MapIterator) {
|
||||
iteratorPool.Put(p)
|
||||
}
|
||||
|
||||
func IteratorNext(p *MapIterator) {
|
||||
i := p.ki
|
||||
t := &p.It
|
||||
|
||||
/* check for unordered iteration */
|
||||
if i < 0 {
|
||||
rt.Mapiternext(t)
|
||||
return
|
||||
}
|
||||
|
||||
/* check for end of iteration */
|
||||
if p.ki >= p.kv.Len {
|
||||
t.K = nil
|
||||
t.V = nil
|
||||
return
|
||||
}
|
||||
|
||||
/* update the key-value pair, and increase the pointer */
|
||||
t.K = unsafe.Pointer(&p.at(p.ki).k)
|
||||
t.V = p.at(p.ki).v
|
||||
p.ki++
|
||||
}
|
||||
|
||||
func IteratorStart(t *rt.GoMapType, m unsafe.Pointer, fv uint64) (*MapIterator, error) {
|
||||
it := newIterator()
|
||||
rt.Mapiterinit(t, m, &it.It)
|
||||
count := rt.Maplen(m)
|
||||
|
||||
/* check for key-sorting, empty map don't need sorting */
|
||||
if count == 0 || (fv & (1<<BitSortMapKeys)) == 0 {
|
||||
it.ki = -1
|
||||
return it, nil
|
||||
}
|
||||
|
||||
/* pre-allocate space if needed */
|
||||
if count > it.kv.Cap {
|
||||
it.kv = rt.GrowSlice(iteratorPair, it.kv, count)
|
||||
}
|
||||
|
||||
/* dump all the key-value pairs */
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/* sort the keys, map with only 1 item don't need sorting */
|
||||
if it.ki = 1; count > 1 {
|
||||
radixQsort(it.data(), 0, maxDepth(it.kv.Len))
|
||||
}
|
||||
|
||||
/* load the first pair into iterator */
|
||||
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
|
||||
}
|
||||
|
||||
func IsValidNumber(s string) bool {
|
||||
// This function implements the JSON numbers grammar.
|
||||
// See https://tools.ietf.org/html/rfc7159#section-6
|
||||
// and https://www.json.org/img/number.png
|
||||
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
// Optional -
|
||||
if s[0] == '-' {
|
||||
s = s[1:]
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Digits
|
||||
switch {
|
||||
default:
|
||||
return false
|
||||
|
||||
case s[0] == '0':
|
||||
s = s[1:]
|
||||
|
||||
case '1' <= s[0] && s[0] <= '9':
|
||||
s = s[1:]
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// . followed by 1 or more digits.
|
||||
if len(s) >= 2 && s[0] == '.' && '0' <= s[1] && s[1] <= '9' {
|
||||
s = s[2:]
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// e or E followed by an optional - or + and
|
||||
// 1 or more digits.
|
||||
if len(s) >= 2 && (s[0] == 'e' || s[0] == 'E') {
|
||||
s = s[1:]
|
||||
if s[0] == '+' || s[0] == '-' {
|
||||
s = s[1:]
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we are at the end.
|
||||
return s == ""
|
||||
}
|
||||
31
vendor/github.com/bytedance/sonic/internal/encoder/alg/opts.go
generated
vendored
Normal file
31
vendor/github.com/bytedance/sonic/internal/encoder/alg/opts.go
generated
vendored
Normal 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
|
||||
)
|
||||
206
vendor/github.com/bytedance/sonic/internal/encoder/alg/sort.go
generated
vendored
Normal file
206
vendor/github.com/bytedance/sonic/internal/encoder/alg/sort.go
generated
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* 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 alg
|
||||
|
||||
// Algorithm 3-way Radix Quicksort, d means the radix.
|
||||
// Reference: https://algs4.cs.princeton.edu/51radix/Quick3string.java.html
|
||||
func radixQsort(kvs []_MapPair, d, maxDepth int) {
|
||||
for len(kvs) > 11 {
|
||||
// To avoid the worst case of quickSort (time: O(n^2)), use introsort here.
|
||||
// Reference: https://en.wikipedia.org/wiki/Introsort and
|
||||
// https://github.com/golang/go/issues/467
|
||||
if maxDepth == 0 {
|
||||
heapSort(kvs, 0, len(kvs))
|
||||
return
|
||||
}
|
||||
maxDepth--
|
||||
|
||||
p := pivot(kvs, d)
|
||||
lt, i, gt := 0, 0, len(kvs)
|
||||
for i < gt {
|
||||
c := byteAt(kvs[i].k, d)
|
||||
if c < p {
|
||||
swap(kvs, lt, i)
|
||||
i++
|
||||
lt++
|
||||
} else if c > p {
|
||||
gt--
|
||||
swap(kvs, i, gt)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
// kvs[0:lt] < v = kvs[lt:gt] < kvs[gt:len(kvs)]
|
||||
// Native implementation:
|
||||
// radixQsort(kvs[:lt], d, maxDepth)
|
||||
// if p > -1 {
|
||||
// radixQsort(kvs[lt:gt], d+1, maxDepth)
|
||||
// }
|
||||
// radixQsort(kvs[gt:], d, maxDepth)
|
||||
// Optimize as follows: make recursive calls only for the smaller parts.
|
||||
// Reference: https://www.geeksforgeeks.org/quicksort-tail-call-optimization-reducing-worst-case-space-log-n/
|
||||
if p == -1 {
|
||||
if lt > len(kvs) - gt {
|
||||
radixQsort(kvs[gt:], d, maxDepth)
|
||||
kvs = kvs[:lt]
|
||||
} else {
|
||||
radixQsort(kvs[:lt], d, maxDepth)
|
||||
kvs = kvs[gt:]
|
||||
}
|
||||
} else {
|
||||
ml := maxThree(lt, gt-lt, len(kvs)-gt)
|
||||
if ml == lt {
|
||||
radixQsort(kvs[lt:gt], d+1, maxDepth)
|
||||
radixQsort(kvs[gt:], d, maxDepth)
|
||||
kvs = kvs[:lt]
|
||||
} else if ml == gt-lt {
|
||||
radixQsort(kvs[:lt], d, maxDepth)
|
||||
radixQsort(kvs[gt:], d, maxDepth)
|
||||
kvs = kvs[lt:gt]
|
||||
d += 1
|
||||
} else {
|
||||
radixQsort(kvs[:lt], d, maxDepth)
|
||||
radixQsort(kvs[lt:gt], d+1, maxDepth)
|
||||
kvs = kvs[gt:]
|
||||
}
|
||||
}
|
||||
}
|
||||
insertRadixSort(kvs, d)
|
||||
}
|
||||
|
||||
func insertRadixSort(kvs []_MapPair, d int) {
|
||||
for i := 1; i < len(kvs); i++ {
|
||||
for j := i; j > 0 && lessFrom(kvs[j].k, kvs[j-1].k, d); j-- {
|
||||
swap(kvs, j, j-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func pivot(kvs []_MapPair, d int) int {
|
||||
m := len(kvs) >> 1
|
||||
if len(kvs) > 40 {
|
||||
// Tukey's ``Ninther,'' median of three mediankvs of three.
|
||||
t := len(kvs) / 8
|
||||
return medianThree(
|
||||
medianThree(byteAt(kvs[0].k, d), byteAt(kvs[t].k, d), byteAt(kvs[2*t].k, d)),
|
||||
medianThree(byteAt(kvs[m].k, d), byteAt(kvs[m-t].k, d), byteAt(kvs[m+t].k, d)),
|
||||
medianThree(byteAt(kvs[len(kvs)-1].k, d),
|
||||
byteAt(kvs[len(kvs)-1-t].k, d),
|
||||
byteAt(kvs[len(kvs)-1-2*t].k, d)))
|
||||
}
|
||||
return medianThree(byteAt(kvs[0].k, d), byteAt(kvs[m].k, d), byteAt(kvs[len(kvs)-1].k, d))
|
||||
}
|
||||
|
||||
func medianThree(i, j, k int) int {
|
||||
if i > j {
|
||||
i, j = j, i
|
||||
} // i < j
|
||||
if k < i {
|
||||
return i
|
||||
}
|
||||
if k > j {
|
||||
return j
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
func maxThree(i, j, k int) int {
|
||||
max := i
|
||||
if max < j {
|
||||
max = j
|
||||
}
|
||||
if max < k {
|
||||
max = k
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
// maxDepth returns a threshold at which quicksort should switch
|
||||
// to heapsort. It returnkvs 2*ceil(lg(n+1)).
|
||||
func maxDepth(n int) int {
|
||||
var depth int
|
||||
for i := n; i > 0; i >>= 1 {
|
||||
depth++
|
||||
}
|
||||
return depth * 2
|
||||
}
|
||||
|
||||
// siftDown implements the heap property on kvs[lo:hi].
|
||||
// first is an offset into the array where the root of the heap lies.
|
||||
func siftDown(kvs []_MapPair, lo, hi, first int) {
|
||||
root := lo
|
||||
for {
|
||||
child := 2*root + 1
|
||||
if child >= hi {
|
||||
break
|
||||
}
|
||||
if child+1 < hi && kvs[first+child].k < kvs[first+child+1].k {
|
||||
child++
|
||||
}
|
||||
if kvs[first+root].k >= kvs[first+child].k {
|
||||
return
|
||||
}
|
||||
swap(kvs, first+root, first+child)
|
||||
root = child
|
||||
}
|
||||
}
|
||||
|
||||
func heapSort(kvs []_MapPair, a, b int) {
|
||||
first := a
|
||||
lo := 0
|
||||
hi := b - a
|
||||
|
||||
// Build heap with the greatest element at top.
|
||||
for i := (hi - 1) / 2; i >= 0; i-- {
|
||||
siftDown(kvs, i, hi, first)
|
||||
}
|
||||
|
||||
// Pop elements, the largest first, into end of kvs.
|
||||
for i := hi - 1; i >= 0; i-- {
|
||||
swap(kvs, first, first+i)
|
||||
siftDown(kvs, lo, i, first)
|
||||
}
|
||||
}
|
||||
|
||||
// Note that _MapPair.k is NOT pointed to _MapPair.m when map key is integer after swap
|
||||
func swap(kvs []_MapPair, a, b int) {
|
||||
kvs[a].k, kvs[b].k = kvs[b].k, kvs[a].k
|
||||
kvs[a].v, kvs[b].v = kvs[b].v, kvs[a].v
|
||||
}
|
||||
|
||||
// Compare two strings from the pos d.
|
||||
func lessFrom(a, b string, d int) bool {
|
||||
l := len(a)
|
||||
if l > len(b) {
|
||||
l = len(b)
|
||||
}
|
||||
for i := d; i < l; i++ {
|
||||
if a[i] == b[i] {
|
||||
continue
|
||||
}
|
||||
return a[i] < b[i]
|
||||
}
|
||||
return len(a) < len(b)
|
||||
}
|
||||
|
||||
func byteAt(b string, p int) int {
|
||||
if p < len(b) {
|
||||
return int(b[p])
|
||||
}
|
||||
return -1
|
||||
}
|
||||
189
vendor/github.com/bytedance/sonic/internal/encoder/alg/spec.go
generated
vendored
Normal file
189
vendor/github.com/bytedance/sonic/internal/encoder/alg/spec.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
//go:build (amd64 && go1.16 && !go1.26) || (arm64 && go1.20 && !go1.26)
|
||||
// +build amd64,go1.16,!go1.26 arm64,go1.20,!go1.26
|
||||
|
||||
/**
|
||||
* 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"
|
||||
"strconv"
|
||||
"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
|
||||
|
||||
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)
|
||||
|
||||
buf = rt.GuardSlice2(buf, nb+1)
|
||||
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
|
||||
if nb > 0 {
|
||||
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) {
|
||||
return strconv.AppendInt(buf, v, 10)
|
||||
}
|
||||
|
||||
func U64toa(buf []byte, v uint64) ([]byte) {
|
||||
return strconv.AppendUint(buf, v, 10)
|
||||
}
|
||||
148
vendor/github.com/bytedance/sonic/internal/encoder/alg/spec_compat.go
generated
vendored
Normal file
148
vendor/github.com/bytedance/sonic/internal/encoder/alg/spec_compat.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
||||
// +build !amd64,!arm64 go1.26 !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)
|
||||
}
|
||||
699
vendor/github.com/bytedance/sonic/internal/encoder/compiler.go
generated
vendored
Normal file
699
vendor/github.com/bytedance/sonic/internal/encoder/compiler.go
generated
vendored
Normal file
@ -0,0 +1,699 @@
|
||||
/*
|
||||
* 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 (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bytedance/sonic/internal/encoder/ir"
|
||||
"github.com/bytedance/sonic/internal/encoder/vars"
|
||||
"github.com/bytedance/sonic/internal/encoder/vm"
|
||||
"github.com/bytedance/sonic/internal/resolver"
|
||||
"github.com/bytedance/sonic/internal/rt"
|
||||
"github.com/bytedance/sonic/option"
|
||||
)
|
||||
|
||||
func ForceUseVM() {
|
||||
vm.SetCompiler(makeEncoderVM)
|
||||
pretouchType = pretouchTypeVM
|
||||
encodeTypedPointer = vm.EncodeTypedPointer
|
||||
vars.UseVM = true
|
||||
}
|
||||
|
||||
var encodeTypedPointer func(buf *[]byte, vt *rt.GoType, vp *unsafe.Pointer, sb *vars.Stack, fv uint64) error
|
||||
|
||||
func makeEncoderVM(vt *rt.GoType, ex ...interface{}) (interface{}, error) {
|
||||
pp, err := NewCompiler().Compile(vt.Pack(), ex[0].(bool))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pp, nil
|
||||
}
|
||||
|
||||
var pretouchType func(_vt reflect.Type, opts option.CompileOptions, v uint8) (map[reflect.Type]uint8, error)
|
||||
|
||||
func pretouchTypeVM(_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) {
|
||||
pp, err := compiler.Compile(vt.Pack(), ex[0].(bool))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pp, nil
|
||||
}
|
||||
|
||||
/* find or compile */
|
||||
vt := rt.UnpackType(_vt)
|
||||
if val := vars.GetProgram(vt); val != nil {
|
||||
return nil, nil
|
||||
} else if _, err := vars.ComputeProgram(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)
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
opts option.CompileOptions
|
||||
pv bool
|
||||
tab map[reflect.Type]bool
|
||||
rec map[reflect.Type]uint8
|
||||
}
|
||||
|
||||
func NewCompiler() *Compiler {
|
||||
return &Compiler{
|
||||
opts: option.DefaultCompileOptions(),
|
||||
tab: map[reflect.Type]bool{},
|
||||
rec: map[reflect.Type]uint8{},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) apply(opts option.CompileOptions) *Compiler {
|
||||
self.opts = opts
|
||||
if self.opts.RecursiveDepth > 0 {
|
||||
self.rec = map[reflect.Type]uint8{}
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *Compiler) rescue(ep *error) {
|
||||
if val := recover(); val != nil {
|
||||
if err, ok := val.(error); ok {
|
||||
*ep = err
|
||||
} else {
|
||||
panic(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) Compile(vt reflect.Type, pv bool) (ret ir.Program, err error) {
|
||||
defer self.rescue(&err)
|
||||
self.compileOne(&ret, 0, vt, pv)
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Compiler) compileOne(p *ir.Program, sp int, vt reflect.Type, pv bool) {
|
||||
if self.tab[vt] {
|
||||
p.Vp(ir.OP_recurse, vt, pv)
|
||||
} else {
|
||||
self.compileRec(p, sp, vt, pv)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) tryCompileMarshaler(p *ir.Program, vt reflect.Type, pv bool) bool {
|
||||
pt := reflect.PtrTo(vt)
|
||||
|
||||
/* check for addressable `json.Marshaler` with pointer receiver */
|
||||
if pv && pt.Implements(vars.JsonMarshalerType) {
|
||||
addMarshalerOp(p, ir.OP_marshal_p, pt, vars.JsonMarshalerType)
|
||||
return true
|
||||
}
|
||||
|
||||
/* check for `json.Marshaler` */
|
||||
if vt.Implements(vars.JsonMarshalerType) {
|
||||
self.compileMarshaler(p, ir.OP_marshal, vt, vars.JsonMarshalerType)
|
||||
return true
|
||||
}
|
||||
|
||||
/* check for addressable `encoding.TextMarshaler` with pointer receiver */
|
||||
if pv && pt.Implements(vars.EncodingTextMarshalerType) {
|
||||
addMarshalerOp(p, ir.OP_marshal_text_p, pt, vars.EncodingTextMarshalerType)
|
||||
return true
|
||||
}
|
||||
|
||||
/* check for `encoding.TextMarshaler` */
|
||||
if vt.Implements(vars.EncodingTextMarshalerType) {
|
||||
self.compileMarshaler(p, ir.OP_marshal_text, vt, vars.EncodingTextMarshalerType)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *Compiler) compileRec(p *ir.Program, sp int, vt reflect.Type, pv bool) {
|
||||
pr := self.pv
|
||||
|
||||
if self.tryCompileMarshaler(p, vt, pv) {
|
||||
return
|
||||
}
|
||||
|
||||
/* enter the recursion, and compile the type */
|
||||
self.pv = pv
|
||||
self.tab[vt] = true
|
||||
self.compileOps(p, sp, vt)
|
||||
|
||||
/* exit the recursion */
|
||||
self.pv = pr
|
||||
delete(self.tab, vt)
|
||||
}
|
||||
|
||||
func (self *Compiler) compileOps(p *ir.Program, sp int, vt reflect.Type) {
|
||||
switch vt.Kind() {
|
||||
case reflect.Bool:
|
||||
p.Add(ir.OP_bool)
|
||||
case reflect.Int:
|
||||
p.Add(ir.OP_int())
|
||||
case reflect.Int8:
|
||||
p.Add(ir.OP_i8)
|
||||
case reflect.Int16:
|
||||
p.Add(ir.OP_i16)
|
||||
case reflect.Int32:
|
||||
p.Add(ir.OP_i32)
|
||||
case reflect.Int64:
|
||||
p.Add(ir.OP_i64)
|
||||
case reflect.Uint:
|
||||
p.Add(ir.OP_uint())
|
||||
case reflect.Uint8:
|
||||
p.Add(ir.OP_u8)
|
||||
case reflect.Uint16:
|
||||
p.Add(ir.OP_u16)
|
||||
case reflect.Uint32:
|
||||
p.Add(ir.OP_u32)
|
||||
case reflect.Uint64:
|
||||
p.Add(ir.OP_u64)
|
||||
case reflect.Uintptr:
|
||||
p.Add(ir.OP_uintptr())
|
||||
case reflect.Float32:
|
||||
p.Add(ir.OP_f32)
|
||||
case reflect.Float64:
|
||||
p.Add(ir.OP_f64)
|
||||
case reflect.String:
|
||||
self.compileString(p, vt)
|
||||
case reflect.Array:
|
||||
self.compileArray(p, sp, vt.Elem(), vt.Len())
|
||||
case reflect.Interface:
|
||||
self.compileInterface(p, vt)
|
||||
case reflect.Map:
|
||||
self.compileMap(p, sp, vt)
|
||||
case reflect.Ptr:
|
||||
self.compilePtr(p, sp, vt.Elem())
|
||||
case reflect.Slice:
|
||||
self.compileSlice(p, sp, vt.Elem())
|
||||
case reflect.Struct:
|
||||
self.compileStruct(p, sp, vt)
|
||||
default:
|
||||
self.compileUnsupportedType(p, vt)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) compileNil(p *ir.Program, sp int, vt reflect.Type, nil_op ir.Op, fn func(*ir.Program, int, reflect.Type)) {
|
||||
x := p.PC()
|
||||
p.Add(ir.OP_is_nil)
|
||||
fn(p, sp, vt)
|
||||
e := p.PC()
|
||||
p.Add(ir.OP_goto)
|
||||
p.Pin(x)
|
||||
p.Add(nil_op)
|
||||
p.Pin(e)
|
||||
}
|
||||
|
||||
func (self *Compiler) compilePtr(p *ir.Program, sp int, vt reflect.Type) {
|
||||
self.compileNil(p, sp, vt, ir.OP_null, self.compilePtrBody)
|
||||
}
|
||||
|
||||
func (self *Compiler) compilePtrBody(p *ir.Program, sp int, vt reflect.Type) {
|
||||
p.Tag(sp)
|
||||
p.Add(ir.OP_save)
|
||||
p.Add(ir.OP_deref)
|
||||
self.compileOne(p, sp+1, vt, true)
|
||||
p.Add(ir.OP_drop)
|
||||
}
|
||||
|
||||
func (self *Compiler) compileMap(p *ir.Program, sp int, vt reflect.Type) {
|
||||
self.compileNil(p, sp, vt, ir.OP_empty_obj, self.compileMapBody)
|
||||
}
|
||||
|
||||
func (self *Compiler) compileMapBody(p *ir.Program, sp int, vt reflect.Type) {
|
||||
p.Tag(sp + 1)
|
||||
p.Int(ir.OP_byte, '{')
|
||||
e := p.PC()
|
||||
p.Add(ir.OP_is_zero_map)
|
||||
p.Add(ir.OP_save)
|
||||
p.Rtt(ir.OP_map_iter, vt)
|
||||
p.Add(ir.OP_save)
|
||||
i := p.PC()
|
||||
p.Add(ir.OP_map_check_key)
|
||||
u := p.PC()
|
||||
p.Add(ir.OP_map_write_key)
|
||||
self.compileMapBodyKey(p, vt.Key())
|
||||
p.Pin(u)
|
||||
p.Int(ir.OP_byte, ':')
|
||||
p.Add(ir.OP_map_value_next)
|
||||
self.compileOne(p, sp+2, vt.Elem(), false)
|
||||
j := p.PC()
|
||||
p.Add(ir.OP_map_check_key)
|
||||
p.Int(ir.OP_byte, ',')
|
||||
v := p.PC()
|
||||
p.Add(ir.OP_map_write_key)
|
||||
self.compileMapBodyKey(p, vt.Key())
|
||||
p.Pin(v)
|
||||
p.Int(ir.OP_byte, ':')
|
||||
p.Add(ir.OP_map_value_next)
|
||||
self.compileOne(p, sp+2, vt.Elem(), false)
|
||||
p.Int(ir.OP_goto, j)
|
||||
p.Pin(i)
|
||||
p.Pin(j)
|
||||
p.Add(ir.OP_map_stop)
|
||||
p.Add(ir.OP_drop_2)
|
||||
p.Pin(e)
|
||||
p.Int(ir.OP_byte, '}')
|
||||
}
|
||||
|
||||
func (self *Compiler) compileMapBodyKey(p *ir.Program, vk reflect.Type) {
|
||||
// followed as `encoding/json/emcode.go:resolveKeyName
|
||||
if vk.Kind() == reflect.String {
|
||||
self.compileString(p, vk)
|
||||
return
|
||||
}
|
||||
|
||||
if !vk.Implements(vars.EncodingTextMarshalerType) {
|
||||
self.compileMapBodyTextKey(p, vk)
|
||||
} else {
|
||||
self.compileMapBodyUtextKey(p, vk)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) compileMapBodyTextKey(p *ir.Program, vk reflect.Type) {
|
||||
switch vk.Kind() {
|
||||
case reflect.Invalid:
|
||||
panic("map key is nil")
|
||||
case reflect.Bool:
|
||||
p.Key(ir.OP_bool)
|
||||
case reflect.Int:
|
||||
p.Key(ir.OP_int())
|
||||
case reflect.Int8:
|
||||
p.Key(ir.OP_i8)
|
||||
case reflect.Int16:
|
||||
p.Key(ir.OP_i16)
|
||||
case reflect.Int32:
|
||||
p.Key(ir.OP_i32)
|
||||
case reflect.Int64:
|
||||
p.Key(ir.OP_i64)
|
||||
case reflect.Uint:
|
||||
p.Key(ir.OP_uint())
|
||||
case reflect.Uint8:
|
||||
p.Key(ir.OP_u8)
|
||||
case reflect.Uint16:
|
||||
p.Key(ir.OP_u16)
|
||||
case reflect.Uint32:
|
||||
p.Key(ir.OP_u32)
|
||||
case reflect.Uint64:
|
||||
p.Key(ir.OP_u64)
|
||||
case reflect.Uintptr:
|
||||
p.Key(ir.OP_uintptr())
|
||||
case reflect.Float32:
|
||||
p.Key(ir.OP_f32)
|
||||
case reflect.Float64:
|
||||
p.Key(ir.OP_f64)
|
||||
case reflect.String:
|
||||
self.compileString(p, vk)
|
||||
default:
|
||||
panic(vars.Error_type(vk))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) compileMapBodyUtextKey(p *ir.Program, vk reflect.Type) {
|
||||
if vk.Kind() != reflect.Ptr {
|
||||
addMarshalerOp(p, ir.OP_marshal_text, vk, vars.EncodingTextMarshalerType)
|
||||
} else {
|
||||
self.compileMapBodyUtextPtr(p, vk)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) compileMapBodyUtextPtr(p *ir.Program, vk reflect.Type) {
|
||||
i := p.PC()
|
||||
p.Add(ir.OP_is_nil)
|
||||
addMarshalerOp(p, ir.OP_marshal_text, vk, vars.EncodingTextMarshalerType)
|
||||
j := p.PC()
|
||||
p.Add(ir.OP_goto)
|
||||
p.Pin(i)
|
||||
p.Str(ir.OP_text, "\"\"")
|
||||
p.Pin(j)
|
||||
}
|
||||
|
||||
func (self *Compiler) compileSlice(p *ir.Program, sp int, vt reflect.Type) {
|
||||
self.compileNil(p, sp, vt, ir.OP_empty_arr, self.compileSliceBody)
|
||||
}
|
||||
|
||||
func (self *Compiler) compileSliceBody(p *ir.Program, sp int, vt reflect.Type) {
|
||||
if vars.IsSimpleByte(vt) {
|
||||
p.Add(ir.OP_bin)
|
||||
} else {
|
||||
self.compileSliceArray(p, sp, vt)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) compileSliceArray(p *ir.Program, sp int, vt reflect.Type) {
|
||||
p.Tag(sp)
|
||||
p.Int(ir.OP_byte, '[')
|
||||
e := p.PC()
|
||||
p.Add(ir.OP_is_nil)
|
||||
p.Add(ir.OP_save)
|
||||
p.Add(ir.OP_slice_len)
|
||||
i := p.PC()
|
||||
p.Rtt(ir.OP_slice_next, vt)
|
||||
self.compileOne(p, sp+1, vt, true)
|
||||
j := p.PC()
|
||||
p.Rtt(ir.OP_slice_next, vt)
|
||||
p.Int(ir.OP_byte, ',')
|
||||
self.compileOne(p, sp+1, vt, true)
|
||||
p.Int(ir.OP_goto, j)
|
||||
p.Pin(i)
|
||||
p.Pin(j)
|
||||
p.Add(ir.OP_drop)
|
||||
p.Pin(e)
|
||||
p.Int(ir.OP_byte, ']')
|
||||
}
|
||||
|
||||
func (self *Compiler) compileArray(p *ir.Program, sp int, vt reflect.Type, nb int) {
|
||||
p.Tag(sp)
|
||||
p.Int(ir.OP_byte, '[')
|
||||
p.Add(ir.OP_save)
|
||||
|
||||
/* first item */
|
||||
if nb != 0 {
|
||||
self.compileOne(p, sp+1, vt, self.pv)
|
||||
p.Add(ir.OP_load)
|
||||
}
|
||||
|
||||
/* remaining items */
|
||||
for i := 1; i < nb; i++ {
|
||||
p.Int(ir.OP_byte, ',')
|
||||
p.Int(ir.OP_index, i*int(vt.Size()))
|
||||
self.compileOne(p, sp+1, vt, self.pv)
|
||||
p.Add(ir.OP_load)
|
||||
}
|
||||
|
||||
/* end of array */
|
||||
p.Add(ir.OP_drop)
|
||||
p.Int(ir.OP_byte, ']')
|
||||
}
|
||||
|
||||
func (self *Compiler) compileString(p *ir.Program, vt reflect.Type) {
|
||||
if vt != vars.JsonNumberType {
|
||||
p.Add(ir.OP_str)
|
||||
} else {
|
||||
p.Add(ir.OP_number)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) compileStruct(p *ir.Program, sp int, vt reflect.Type) {
|
||||
if sp >= self.opts.MaxInlineDepth || p.PC() >= vars.MAX_ILBUF || (sp > 0 && vt.NumField() >= vars.MAX_FIELDS) {
|
||||
p.Vp(ir.OP_recurse, vt, self.pv)
|
||||
if self.opts.RecursiveDepth > 0 {
|
||||
if self.pv {
|
||||
self.rec[vt] = 1
|
||||
} else {
|
||||
self.rec[vt] = 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.compileStructBody(p, sp, vt)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) compileStructBody(p *ir.Program, sp int, vt reflect.Type) {
|
||||
p.Tag(sp)
|
||||
p.Int(ir.OP_byte, '{')
|
||||
p.Add(ir.OP_save)
|
||||
p.Add(ir.OP_cond_set)
|
||||
|
||||
/* compile each field */
|
||||
fvs := resolver.ResolveStruct(vt)
|
||||
for i, fv := range fvs {
|
||||
var s []int
|
||||
var o resolver.Offset
|
||||
|
||||
/* "omitempty" for arrays */
|
||||
if fv.Type.Kind() == reflect.Array {
|
||||
if fv.Type.Len() == 0 && (fv.Opts&resolver.F_omitempty) != 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
/* index to the field */
|
||||
for _, o = range fv.Path {
|
||||
if p.Int(ir.OP_index, int(o.Size)); o.Kind == resolver.F_deref {
|
||||
s = append(s, p.PC())
|
||||
p.Add(ir.OP_is_nil)
|
||||
p.Add(ir.OP_deref)
|
||||
}
|
||||
}
|
||||
|
||||
/* check for "omitempty" option */
|
||||
if fv.Type.Kind() != reflect.Struct && fv.Type.Kind() != reflect.Array && (fv.Opts&resolver.F_omitempty) != 0 {
|
||||
s = append(s, p.PC())
|
||||
self.compileStructFieldEmpty(p, fv.Type)
|
||||
}
|
||||
/* check for "omitzero" option */
|
||||
if fv.Opts&resolver.F_omitzero != 0 {
|
||||
s = append(s, p.PC())
|
||||
p.VField(ir.OP_is_zero, &fvs[i])
|
||||
}
|
||||
|
||||
/* add the comma if not the first element */
|
||||
i := p.PC()
|
||||
p.Add(ir.OP_cond_testc)
|
||||
p.Int(ir.OP_byte, ',')
|
||||
p.Pin(i)
|
||||
|
||||
/* compile the key and value */
|
||||
ft := fv.Type
|
||||
p.Str(ir.OP_text, Quote(fv.Name)+":")
|
||||
|
||||
/* check for "stringnize" option */
|
||||
if (fv.Opts & resolver.F_stringize) == 0 {
|
||||
self.compileOne(p, sp+1, ft, self.pv)
|
||||
} else {
|
||||
self.compileStructFieldStr(p, sp+1, ft)
|
||||
}
|
||||
|
||||
/* patch the skipping jumps and reload the struct pointer */
|
||||
p.Rel(s)
|
||||
p.Add(ir.OP_load)
|
||||
}
|
||||
|
||||
/* end of object */
|
||||
p.Add(ir.OP_drop)
|
||||
p.Int(ir.OP_byte, '}')
|
||||
}
|
||||
|
||||
func (self *Compiler) compileStructFieldStr(p *ir.Program, sp int, vt reflect.Type) {
|
||||
// NOTICE: according to encoding/json, Marshaler type has higher priority than string option
|
||||
// see issue:
|
||||
if self.tryCompileMarshaler(p, vt, self.pv) {
|
||||
return
|
||||
}
|
||||
|
||||
pc := -1
|
||||
ft := vt
|
||||
sv := false
|
||||
|
||||
/* dereference the pointer if needed */
|
||||
if ft.Kind() == reflect.Ptr {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
|
||||
/* check if it can be stringized */
|
||||
switch ft.Kind() {
|
||||
case reflect.Bool:
|
||||
sv = true
|
||||
case reflect.Int:
|
||||
sv = true
|
||||
case reflect.Int8:
|
||||
sv = true
|
||||
case reflect.Int16:
|
||||
sv = true
|
||||
case reflect.Int32:
|
||||
sv = true
|
||||
case reflect.Int64:
|
||||
sv = true
|
||||
case reflect.Uint:
|
||||
sv = true
|
||||
case reflect.Uint8:
|
||||
sv = true
|
||||
case reflect.Uint16:
|
||||
sv = true
|
||||
case reflect.Uint32:
|
||||
sv = true
|
||||
case reflect.Uint64:
|
||||
sv = true
|
||||
case reflect.Uintptr:
|
||||
sv = true
|
||||
case reflect.Float32:
|
||||
sv = true
|
||||
case reflect.Float64:
|
||||
sv = true
|
||||
case reflect.String:
|
||||
sv = true
|
||||
}
|
||||
|
||||
/* if it's not, ignore the "string" and follow the regular path */
|
||||
if !sv {
|
||||
self.compileOne(p, sp, vt, self.pv)
|
||||
return
|
||||
}
|
||||
|
||||
/* dereference the pointer */
|
||||
if vt.Kind() == reflect.Ptr {
|
||||
pc = p.PC()
|
||||
vt = vt.Elem()
|
||||
p.Add(ir.OP_is_nil)
|
||||
p.Add(ir.OP_deref)
|
||||
}
|
||||
|
||||
/* special case of a double-quoted string */
|
||||
if ft != vars.JsonNumberType && ft.Kind() == reflect.String {
|
||||
p.Add(ir.OP_quote)
|
||||
} else {
|
||||
self.compileStructFieldQuoted(p, sp, vt)
|
||||
}
|
||||
|
||||
/* the "null" case of the pointer */
|
||||
if pc != -1 {
|
||||
e := p.PC()
|
||||
p.Add(ir.OP_goto)
|
||||
p.Pin(pc)
|
||||
p.Add(ir.OP_null)
|
||||
p.Pin(e)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) compileStructFieldEmpty(p *ir.Program, vt reflect.Type) {
|
||||
switch vt.Kind() {
|
||||
case reflect.Bool:
|
||||
p.Add(ir.OP_is_zero_1)
|
||||
case reflect.Int:
|
||||
p.Add(ir.OP_is_zero_ints())
|
||||
case reflect.Int8:
|
||||
p.Add(ir.OP_is_zero_1)
|
||||
case reflect.Int16:
|
||||
p.Add(ir.OP_is_zero_2)
|
||||
case reflect.Int32:
|
||||
p.Add(ir.OP_is_zero_4)
|
||||
case reflect.Int64:
|
||||
p.Add(ir.OP_is_zero_8)
|
||||
case reflect.Uint:
|
||||
p.Add(ir.OP_is_zero_ints())
|
||||
case reflect.Uint8:
|
||||
p.Add(ir.OP_is_zero_1)
|
||||
case reflect.Uint16:
|
||||
p.Add(ir.OP_is_zero_2)
|
||||
case reflect.Uint32:
|
||||
p.Add(ir.OP_is_zero_4)
|
||||
case reflect.Uint64:
|
||||
p.Add(ir.OP_is_zero_8)
|
||||
case reflect.Uintptr:
|
||||
p.Add(ir.OP_is_nil)
|
||||
case reflect.Float32:
|
||||
p.Add(ir.OP_is_zero_4)
|
||||
case reflect.Float64:
|
||||
p.Add(ir.OP_is_zero_8)
|
||||
case reflect.String:
|
||||
p.Add(ir.OP_is_nil_p1)
|
||||
case reflect.Interface:
|
||||
p.Add(ir.OP_is_nil)
|
||||
case reflect.Map:
|
||||
p.Add(ir.OP_is_zero_map)
|
||||
case reflect.Ptr:
|
||||
p.Add(ir.OP_is_nil)
|
||||
case reflect.Slice:
|
||||
p.Add(ir.OP_is_nil_p1)
|
||||
default:
|
||||
panic(vars.Error_type(vt))
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Compiler) compileStructFieldQuoted(p *ir.Program, sp int, vt reflect.Type) {
|
||||
p.Int(ir.OP_byte, '"')
|
||||
self.compileOne(p, sp, vt, self.pv)
|
||||
p.Int(ir.OP_byte, '"')
|
||||
}
|
||||
|
||||
func (self *Compiler) compileInterface(p *ir.Program, vt reflect.Type) {
|
||||
/* iface and efaces are different */
|
||||
if vt.NumMethod() == 0 {
|
||||
p.Add(ir.OP_eface)
|
||||
return
|
||||
}
|
||||
|
||||
x := p.PC()
|
||||
p.Add(ir.OP_is_nil_p1)
|
||||
p.Add(ir.OP_iface)
|
||||
|
||||
/* the "null" value */
|
||||
e := p.PC()
|
||||
p.Add(ir.OP_goto)
|
||||
p.Pin(x)
|
||||
p.Add(ir.OP_null)
|
||||
p.Pin(e)
|
||||
}
|
||||
|
||||
func (self *Compiler) compileUnsupportedType(p *ir.Program, vt reflect.Type) {
|
||||
p.Rtt(ir.OP_unsupported, vt)
|
||||
}
|
||||
|
||||
func (self *Compiler) compileMarshaler(p *ir.Program, op ir.Op, vt reflect.Type, mt reflect.Type) {
|
||||
pc := p.PC()
|
||||
vk := vt.Kind()
|
||||
|
||||
/* direct receiver */
|
||||
if vk != reflect.Ptr {
|
||||
addMarshalerOp(p, op, vt, mt)
|
||||
return
|
||||
}
|
||||
/* value receiver with a pointer type, check for nil before calling the marshaler */
|
||||
p.Add(ir.OP_is_nil)
|
||||
|
||||
addMarshalerOp(p, op, vt, mt)
|
||||
|
||||
i := p.PC()
|
||||
p.Add(ir.OP_goto)
|
||||
p.Pin(pc)
|
||||
p.Add(ir.OP_null)
|
||||
p.Pin(i)
|
||||
}
|
||||
|
||||
func addMarshalerOp(p *ir.Program, op ir.Op, vt reflect.Type, mt reflect.Type) {
|
||||
if vars.UseVM {
|
||||
itab := rt.GetItab(rt.IfaceType(rt.UnpackType(mt)), rt.UnpackType(vt), true)
|
||||
p.Vtab(op, vt, itab)
|
||||
} else {
|
||||
// OPT: get itab here
|
||||
p.Rtt(op, vt)
|
||||
}
|
||||
}
|
||||
24
vendor/github.com/bytedance/sonic/internal/encoder/encode_norace.go
generated
vendored
Normal file
24
vendor/github.com/bytedance/sonic/internal/encoder/encode_norace.go
generated
vendored
Normal 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
vendor/github.com/bytedance/sonic/internal/encoder/encode_race.go
generated
vendored
Normal file
54
vendor/github.com/bytedance/sonic/internal/encoder/encode_race.go
generated
vendored
Normal 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:])
|
||||
}
|
||||
}
|
||||
323
vendor/github.com/bytedance/sonic/internal/encoder/encoder.go
generated
vendored
Normal file
323
vendor/github.com/bytedance/sonic/internal/encoder/encoder.go
generated
vendored
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
* 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"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"runtime"
|
||||
|
||||
"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"
|
||||
"github.com/bytedance/gopkg/lang/dirtmake"
|
||||
)
|
||||
|
||||
// Options is a set of encoding options.
|
||||
type Options uint64
|
||||
|
||||
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 << 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 << alg.BitEscapeHTML
|
||||
|
||||
// CompactMarshaler indicates that the output JSON from json.Marshaler
|
||||
// is always compact and needs no validation
|
||||
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 << alg.BitNoQuoteTextMarshaler
|
||||
|
||||
// NoNullSliceOrMap indicates all empty Array or Object are encoded as '[]' or '{}',
|
||||
// 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 << alg.BitValidateString
|
||||
|
||||
// NoValidateJSONMarshaler indicates that the encoder should not validate the output string
|
||||
// after encoding the JSONMarshaler to JSON.
|
||||
NoValidateJSONMarshaler Options = 1 << alg.BitNoValidateJSONMarshaler
|
||||
|
||||
// NoEncoderNewline indicates that the encoder should not add a newline after every message
|
||||
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.
|
||||
type Encoder struct {
|
||||
Opts Options
|
||||
prefix string
|
||||
indent string
|
||||
}
|
||||
|
||||
// Encode returns the JSON encoding of v.
|
||||
func (self *Encoder) Encode(v interface{}) ([]byte, error) {
|
||||
if self.indent != "" || self.prefix != "" {
|
||||
return EncodeIndented(v, self.prefix, self.indent, self.Opts)
|
||||
}
|
||||
return Encode(v, self.Opts)
|
||||
}
|
||||
|
||||
// SortKeys enables the SortMapKeys option.
|
||||
func (self *Encoder) SortKeys() *Encoder {
|
||||
self.Opts |= SortMapKeys
|
||||
return self
|
||||
}
|
||||
|
||||
// SetEscapeHTML specifies if option EscapeHTML opens
|
||||
func (self *Encoder) SetEscapeHTML(f bool) {
|
||||
if f {
|
||||
self.Opts |= EscapeHTML
|
||||
} else {
|
||||
self.Opts &= ^EscapeHTML
|
||||
}
|
||||
}
|
||||
|
||||
// SetValidateString specifies if option ValidateString opens
|
||||
func (self *Encoder) SetValidateString(f bool) {
|
||||
if f {
|
||||
self.Opts |= ValidateString
|
||||
} else {
|
||||
self.Opts &= ^ValidateString
|
||||
}
|
||||
}
|
||||
|
||||
// SetNoValidateJSONMarshaler specifies if option NoValidateJSONMarshaler opens
|
||||
func (self *Encoder) SetNoValidateJSONMarshaler(f bool) {
|
||||
if f {
|
||||
self.Opts |= NoValidateJSONMarshaler
|
||||
} else {
|
||||
self.Opts &= ^NoValidateJSONMarshaler
|
||||
}
|
||||
}
|
||||
|
||||
// SetNoEncoderNewline specifies if option NoEncoderNewline opens
|
||||
func (self *Encoder) SetNoEncoderNewline(f bool) {
|
||||
if f {
|
||||
self.Opts |= NoEncoderNewline
|
||||
} else {
|
||||
self.Opts &= ^NoEncoderNewline
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SetCompactMarshaler specifies if option CompactMarshaler opens
|
||||
func (self *Encoder) SetCompactMarshaler(f bool) {
|
||||
if f {
|
||||
self.Opts |= CompactMarshaler
|
||||
} else {
|
||||
self.Opts &= ^CompactMarshaler
|
||||
}
|
||||
}
|
||||
|
||||
// SetNoQuoteTextMarshaler specifies if option NoQuoteTextMarshaler opens
|
||||
func (self *Encoder) SetNoQuoteTextMarshaler(f bool) {
|
||||
if f {
|
||||
self.Opts |= NoQuoteTextMarshaler
|
||||
} else {
|
||||
self.Opts &= ^NoQuoteTextMarshaler
|
||||
}
|
||||
}
|
||||
|
||||
// SetIndent instructs the encoder to format each subsequent encoded
|
||||
// value as if indented by the package-level function EncodeIndent().
|
||||
// Calling SetIndent("", "") disables indentation.
|
||||
func (enc *Encoder) SetIndent(prefix, indent string) {
|
||||
enc.prefix = prefix
|
||||
enc.indent = indent
|
||||
}
|
||||
|
||||
// Quote returns the JSON-quoted version of s.
|
||||
func Quote(s string) string {
|
||||
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 := vars.NewBytes()
|
||||
err := encodeIntoCheckRace(buf, val, opts)
|
||||
|
||||
/* check for errors */
|
||||
if err != nil {
|
||||
vars.FreeBytes(buf)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/* htmlescape or correct UTF-8 if opts enable */
|
||||
encodeFinishWithPool(buf, opts)
|
||||
|
||||
/* make a copy of the result */
|
||||
if rt.CanSizeResue(cap(*buf)) {
|
||||
ret = dirtmake.Bytes(len(*buf), len(*buf))
|
||||
copy(ret, *buf)
|
||||
vars.FreeBytes(buf)
|
||||
} else {
|
||||
ret = *buf
|
||||
}
|
||||
|
||||
/* return the buffer into pool */
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// 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 := encodeIntoCheckRace(buf, val, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*buf = encodeFinish(*buf, opts)
|
||||
return err
|
||||
}
|
||||
|
||||
func encodeInto(buf *[]byte, val interface{}, opts Options) error {
|
||||
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 {
|
||||
vars.ResetStack(stk)
|
||||
}
|
||||
vars.FreeStack(stk)
|
||||
|
||||
/* avoid GC ahead */
|
||||
runtime.KeepAlive(buf)
|
||||
runtime.KeepAlive(efv)
|
||||
return err
|
||||
}
|
||||
|
||||
func encodeFinish(buf []byte, opts Options) []byte {
|
||||
if opts & EscapeHTML != 0 {
|
||||
buf = HTMLEscape(nil, buf)
|
||||
}
|
||||
if (opts & ValidateString != 0) && !utf8.Validate(buf) {
|
||||
buf = utf8.CorrectWith(nil, buf, `\ufffd`)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func encodeFinishWithPool(buf *[]byte, opts Options) {
|
||||
if opts & EscapeHTML != 0 {
|
||||
dst := vars.NewBytes()
|
||||
// put the result bytes to buf and the old buf to dst to return to the pool.
|
||||
// we cannot return buf to the pool because it will be used by the caller.
|
||||
*buf, *dst = HTMLEscape(*dst, *buf), *buf
|
||||
vars.FreeBytes(dst)
|
||||
}
|
||||
if (opts & ValidateString != 0) && !utf8.Validate(*buf) {
|
||||
dst := vars.NewBytes()
|
||||
*buf, *dst = utf8.CorrectWith(*dst, *buf, `\ufffd`), *buf
|
||||
vars.FreeBytes(dst)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
// so that the JSON will be safe to embed inside HTML <script> tags.
|
||||
// For historical reasons, web browsers don't honor standard HTML
|
||||
// escaping within <script> tags, so an alternative JSON encoding must
|
||||
// be used.
|
||||
func HTMLEscape(dst []byte, src []byte) []byte {
|
||||
return alg.HtmlEscape(dst, src)
|
||||
}
|
||||
|
||||
// EncodeIndented is like Encode 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 EncodeIndented(val interface{}, prefix string, indent string, opts Options) ([]byte, error) {
|
||||
var err error
|
||||
var buf *bytes.Buffer
|
||||
|
||||
/* encode into the buffer */
|
||||
out := vars.NewBytes()
|
||||
err = EncodeInto(out, val, opts)
|
||||
|
||||
/* check for errors */
|
||||
if err != nil {
|
||||
vars.FreeBytes(out)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/* indent the JSON */
|
||||
buf = vars.NewBuffer()
|
||||
err = json.Indent(buf, *out, prefix, indent)
|
||||
vars.FreeBytes(out)
|
||||
|
||||
/* check for errors */
|
||||
if err != nil {
|
||||
vars.FreeBuffer(buf)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/* copy to the result buffer */
|
||||
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
|
||||
}
|
||||
|
||||
// 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]uint8{vt: 0}, cfg)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return alg.Valid(data)
|
||||
}
|
||||
501
vendor/github.com/bytedance/sonic/internal/encoder/ir/op.go
generated
vendored
Normal file
501
vendor/github.com/bytedance/sonic/internal/encoder/ir/op.go
generated
vendored
Normal file
@ -0,0 +1,501 @@
|
||||
/*
|
||||
* 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/resolver"
|
||||
"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
|
||||
OP_unsupported
|
||||
OP_is_zero
|
||||
)
|
||||
|
||||
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",
|
||||
OP_unsupported: "unsupported type",
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
type typAndField struct {
|
||||
vt reflect.Type
|
||||
fv *resolver.FieldMeta
|
||||
}
|
||||
|
||||
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 NewInsField(op Op, fv *resolver.FieldMeta) Instr {
|
||||
return Instr{
|
||||
o: op,
|
||||
p: unsafe.Pointer(fv),
|
||||
}
|
||||
}
|
||||
|
||||
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) VField() (*resolver.FieldMeta) {
|
||||
return (*resolver.FieldMeta)(self.p)
|
||||
}
|
||||
|
||||
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) GoType() *rt.GoType {
|
||||
return (*rt.GoType)(self.p)
|
||||
}
|
||||
|
||||
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) VField(op Op, fv *resolver.FieldMeta) {
|
||||
*self = append(*self, NewInsField(op, fv))
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
85
vendor/github.com/bytedance/sonic/internal/encoder/pools_amd64.go
generated
vendored
Normal file
85
vendor/github.com/bytedance/sonic/internal/encoder/pools_amd64.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 (
|
||||
"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
|
||||
}
|
||||
|
||||
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)
|
||||
encoder := func(vt *rt.GoType, ex ...interface{}) (interface{}, error) {
|
||||
pp, err := compiler.Compile(vt.Pack(), ex[0].(bool))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
as := x86.NewAssembler(pp)
|
||||
as.Name = vt.String()
|
||||
return as.Load(), nil
|
||||
}
|
||||
|
||||
/* find or compile */
|
||||
vt := rt.UnpackType(_vt)
|
||||
if val := vars.GetProgram(vt); val != nil {
|
||||
return nil, nil
|
||||
} else if _, err := vars.ComputeProgram(vt, encoder, v == 1); err == nil {
|
||||
return compiler.rec, nil
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
24
vendor/github.com/bytedance/sonic/internal/encoder/pools_compt.go
generated
vendored
Normal file
24
vendor/github.com/bytedance/sonic/internal/encoder/pools_compt.go
generated
vendored
Normal 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()
|
||||
}
|
||||
105
vendor/github.com/bytedance/sonic/internal/encoder/prim/primitives.go
generated
vendored
Normal file
105
vendor/github.com/bytedance/sonic/internal/encoder/prim/primitives.go
generated
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/**
|
||||
* 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 prim
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bytedance/sonic/internal/encoder/alg"
|
||||
"github.com/bytedance/sonic/internal/encoder/vars"
|
||||
"github.com/bytedance/sonic/internal/resolver"
|
||||
"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<<alg.BitCompactMarshaler) != 0 {
|
||||
return Compact(buf, ret)
|
||||
}
|
||||
if opt&(1<<alg.BitNoValidateJSONMarshaler) == 0 {
|
||||
if ok, s := alg.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<<alg.BitNoQuoteTextMarshaler) != 0 {
|
||||
*buf = append(*buf, ret...)
|
||||
return nil
|
||||
}
|
||||
*buf = alg.Quote(*buf, rt.Mem2Str(ret), false)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func IsZero(val unsafe.Pointer, fv *resolver.FieldMeta) bool {
|
||||
rv := reflect.NewAt(fv.Type, val).Elem()
|
||||
b1 := fv.IsZero == nil && rv.IsZero()
|
||||
b2 := fv.IsZero != nil && fv.IsZero(rv)
|
||||
return b1 || b2
|
||||
}
|
||||
91
vendor/github.com/bytedance/sonic/internal/encoder/stream.go
generated
vendored
Normal file
91
vendor/github.com/bytedance/sonic/internal/encoder/stream.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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"
|
||||
"io"
|
||||
|
||||
"github.com/bytedance/sonic/internal/encoder/vars"
|
||||
)
|
||||
|
||||
// StreamEncoder uses io.Writer as input.
|
||||
type StreamEncoder struct {
|
||||
w io.Writer
|
||||
Encoder
|
||||
}
|
||||
|
||||
// NewStreamEncoder adapts to encoding/json.NewDecoder API.
|
||||
//
|
||||
// NewStreamEncoder returns a new encoder that write to w.
|
||||
func NewStreamEncoder(w io.Writer) *StreamEncoder {
|
||||
return &StreamEncoder{w: w}
|
||||
}
|
||||
|
||||
// Encode encodes interface{} as JSON to io.Writer
|
||||
func (enc *StreamEncoder) Encode(val interface{}) (err error) {
|
||||
out := vars.NewBytes()
|
||||
|
||||
/* encode into the buffer */
|
||||
err = EncodeInto(out, val, enc.Opts)
|
||||
if err != nil {
|
||||
goto free_bytes
|
||||
}
|
||||
|
||||
if enc.indent != "" || enc.prefix != "" {
|
||||
/* indent the JSON */
|
||||
buf := vars.NewBuffer()
|
||||
err = json.Indent(buf, *out, enc.prefix, enc.indent)
|
||||
if err != nil {
|
||||
vars.FreeBuffer(buf)
|
||||
goto free_bytes
|
||||
}
|
||||
|
||||
// according to standard library, terminate each value with a newline...
|
||||
if enc.Opts & NoEncoderNewline == 0 {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
|
||||
/* copy into io.Writer */
|
||||
_, err = io.Copy(enc.w, buf)
|
||||
if err != nil {
|
||||
vars.FreeBuffer(buf)
|
||||
goto free_bytes
|
||||
}
|
||||
|
||||
} else {
|
||||
/* copy into io.Writer */
|
||||
var n int
|
||||
buf := *out
|
||||
for len(buf) > 0 {
|
||||
n, err = enc.w.Write(buf)
|
||||
buf = buf[n:]
|
||||
if err != nil {
|
||||
goto free_bytes
|
||||
}
|
||||
}
|
||||
|
||||
// according to standard library, terminate each value with a newline...
|
||||
if enc.Opts & NoEncoderNewline == 0 {
|
||||
enc.w.Write([]byte{'\n'})
|
||||
}
|
||||
}
|
||||
|
||||
free_bytes:
|
||||
vars.FreeBytes(out)
|
||||
return err
|
||||
}
|
||||
48
vendor/github.com/bytedance/sonic/internal/encoder/vars/cache.go
generated
vendored
Normal file
48
vendor/github.com/bytedance/sonic/internal/encoder/vars/cache.go
generated
vendored
Normal 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
vendor/github.com/bytedance/sonic/internal/encoder/vars/const.go
generated
vendored
Normal file
42
vendor/github.com/bytedance/sonic/internal/encoder/vars/const.go
generated
vendored
Normal 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") != ""
|
||||
91
vendor/github.com/bytedance/sonic/internal/encoder/vars/errors.go
generated
vendored
Normal file
91
vendor/github.com/bytedance/sonic/internal/encoder/vars/errors.go
generated
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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 (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bytedance/sonic/internal/rt"
|
||||
)
|
||||
|
||||
var ERR_too_deep = &json.UnsupportedValueError {
|
||||
Str : "Value nesting too deep",
|
||||
Value : reflect.ValueOf("..."),
|
||||
}
|
||||
|
||||
var ERR_nan_or_infinite = &json.UnsupportedValueError {
|
||||
Str : "NaN or ±Infinite",
|
||||
Value : reflect.ValueOf("NaN or ±Infinite"),
|
||||
}
|
||||
|
||||
func Error_type(vtype reflect.Type) error {
|
||||
return &json.UnsupportedTypeError{Type: vtype}
|
||||
}
|
||||
|
||||
func Error_number(number json.Number) error {
|
||||
return &json.UnsupportedValueError {
|
||||
Str : "invalid number literal: " + strconv.Quote(string(number)),
|
||||
Value : reflect.ValueOf(number),
|
||||
}
|
||||
}
|
||||
|
||||
func Error_unsuppoted(typ *rt.GoType) error {
|
||||
return &json.UnsupportedTypeError{Type: typ.Pack() }
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
func GoPanic(code int, val unsafe.Pointer, buf string) {
|
||||
sb := strings.Builder{}
|
||||
switch(code){
|
||||
case PanicNilPointerOfNonEmptyString:
|
||||
sb.WriteString(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.\n", (*rt.GoString)(val)))
|
||||
default:
|
||||
sb.WriteString("encoder error: ")
|
||||
sb.WriteString(strconv.Itoa(code))
|
||||
sb.WriteString("\n")
|
||||
}
|
||||
sb.WriteString("JSON: ")
|
||||
if len(buf) > maxJSONLength {
|
||||
sb.WriteString(buf[len(buf)-maxJSONLength:])
|
||||
} else {
|
||||
sb.WriteString(buf)
|
||||
}
|
||||
panic(sb.String())
|
||||
}
|
||||
|
||||
var maxJSONLength = 1024
|
||||
|
||||
func init() {
|
||||
if v := os.Getenv("SONIC_PANIC_MAX_JSON_LENGTH"); v != "" {
|
||||
if i, err := strconv.Atoi(v); err == nil {
|
||||
maxJSONLength = i
|
||||
}
|
||||
}
|
||||
}
|
||||
150
vendor/github.com/bytedance/sonic/internal/encoder/vars/stack.go
generated
vendored
Normal file
150
vendor/github.com/bytedance/sonic/internal/encoder/vars/stack.go
generated
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
/**
|
||||
* 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 ResetProgramCache() {
|
||||
programCache.Reset()
|
||||
}
|
||||
|
||||
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{}
|
||||
)
|
||||
47
vendor/github.com/bytedance/sonic/internal/encoder/vars/types.go
generated
vendored
Normal file
47
vendor/github.com/bytedance/sonic/internal/encoder/vars/types.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 (
|
||||
`encoding`
|
||||
`encoding/json`
|
||||
`reflect`
|
||||
)
|
||||
|
||||
var (
|
||||
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()
|
||||
)
|
||||
|
||||
func IsSimpleByte(vt reflect.Type) bool {
|
||||
if vt.Kind() != ByteType.Kind() {
|
||||
return false
|
||||
} else {
|
||||
return !isEitherMarshaler(vt) && !isEitherMarshaler(reflect.PtrTo(vt))
|
||||
}
|
||||
}
|
||||
|
||||
func isEitherMarshaler(vt reflect.Type) bool {
|
||||
return vt.Implements(JsonMarshalerType) || vt.Implements(EncodingTextMarshalerType)
|
||||
}
|
||||
46
vendor/github.com/bytedance/sonic/internal/encoder/vm/stbus.go
generated
vendored
Normal file
46
vendor/github.com/bytedance/sonic/internal/encoder/vm/stbus.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* 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/prim"
|
||||
"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 prim.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
|
||||
}
|
||||
375
vendor/github.com/bytedance/sonic/internal/encoder/vm/vm.go
generated
vendored
Normal file
375
vendor/github.com/bytedance/sonic/internal/encoder/vm/vm.go
generated
vendored
Normal file
@ -0,0 +1,375 @@
|
||||
// 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/prim"
|
||||
"github.com/bytedance/sonic/internal/encoder/vars"
|
||||
"github.com/bytedance/sonic/internal/rt"
|
||||
)
|
||||
|
||||
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 = rt.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 !alg.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 := *(*unsafe.Pointer)(p)
|
||||
if v == nil || rt.Maplen(v) == 0 {
|
||||
pc = ins.Vi()
|
||||
continue
|
||||
}
|
||||
case ir.OP_map_iter:
|
||||
v := *(*unsafe.Pointer)(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 := prim.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 := prim.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:
|
||||
fv := ins.VField()
|
||||
if prim.IsZero(p, fv) {
|
||||
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 := prim.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 := prim.EncodeJsonMarshaler(&buf, *(*json.Marshaler)(unsafe.Pointer(&it)), (flags)); err != nil {
|
||||
return err
|
||||
}
|
||||
case ir.OP_unsupported:
|
||||
return vars.Error_unsuppoted(ins.GoType())
|
||||
default:
|
||||
panic(fmt.Sprintf("not implement %s at %d", ins.Op().String(), pc))
|
||||
}
|
||||
}
|
||||
|
||||
*b = buf
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
53
vendor/github.com/bytedance/sonic/internal/encoder/x86/asm_stubs_amd64_go117.go
generated
vendored
Normal file
53
vendor/github.com/bytedance/sonic/internal/encoder/x86/asm_stubs_amd64_go117.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
//go:build go1.17 && !go1.21
|
||||
// +build go1.17,!go1.21
|
||||
|
||||
// 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 x86
|
||||
|
||||
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 (
|
||||
_V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&rt.RuntimeWriteBarrier))))
|
||||
|
||||
_F_gcWriteBarrierAX = jit.Func(rt.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, _BX)
|
||||
self.Emit("CMPL", jit.Ptr(_BX, 0), jit.Imm(0))
|
||||
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
self.xsave(_DI)
|
||||
self.Emit("MOVQ", ptr, _AX)
|
||||
self.Emit("LEAQ", rec, _DI)
|
||||
self.Emit("MOVQ", _F_gcWriteBarrierAX, _BX) // MOVQ ${fn}, AX
|
||||
self.Rjmp("CALL", _BX)
|
||||
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}")
|
||||
}
|
||||
52
vendor/github.com/bytedance/sonic/internal/encoder/x86/asm_stubs_amd64_go121.go
generated
vendored
Normal file
52
vendor/github.com/bytedance/sonic/internal/encoder/x86/asm_stubs_amd64_go121.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
//go:build go1.21 && !go1.26
|
||||
// +build go1.21,!go1.26
|
||||
|
||||
// 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 x86
|
||||
|
||||
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 (
|
||||
_V_writeBarrier = jit.Imm(int64(uintptr(unsafe.Pointer(&rt.RuntimeWriteBarrier))))
|
||||
|
||||
_F_gcWriteBarrier2 = jit.Func(rt.GcWriteBarrier2)
|
||||
)
|
||||
|
||||
func (self *Assembler) WritePtr(i int, ptr obj.Addr, old obj.Addr) {
|
||||
if old.Reg == x86.REG_AX || old.Index == x86.REG_AX {
|
||||
panic("rec contains AX!")
|
||||
}
|
||||
self.Emit("MOVQ", _V_writeBarrier, _BX)
|
||||
self.Emit("CMPL", jit.Ptr(_BX, 0), jit.Imm(0))
|
||||
self.Sjmp("JE", "_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
self.xsave(_SP_q)
|
||||
self.Emit("MOVQ", _F_gcWriteBarrier2, _BX) // MOVQ ${fn}, AX
|
||||
self.Rjmp("CALL", _BX)
|
||||
self.Emit("MOVQ", ptr, jit.Ptr(_SP_q, 0))
|
||||
self.Emit("MOVQ", old, _AX)
|
||||
self.Emit("MOVQ", _AX, jit.Ptr(_SP_q, 8))
|
||||
self.xload(_SP_q)
|
||||
self.Link("_no_writeBarrier" + strconv.Itoa(i) + "_{n}")
|
||||
self.Emit("MOVQ", ptr, old)
|
||||
}
|
||||
1223
vendor/github.com/bytedance/sonic/internal/encoder/x86/assembler_regabi_amd64.go
generated
vendored
Normal file
1223
vendor/github.com/bytedance/sonic/internal/encoder/x86/assembler_regabi_amd64.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user