chore: migrate to gitea
Some checks failed
golangci-lint / lint (push) Failing after 21s
Test / test (push) Failing after 2m17s

This commit is contained in:
2026-01-27 01:40:31 +01:00
parent a9bca767a9
commit 1a27ed5274
3163 changed files with 1216358 additions and 1529 deletions

View 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 == ""
}

View File

@@ -0,0 +1,31 @@
/**
* Copyright 2024 ByteDance Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package alg
const (
BitSortMapKeys = iota
BitEscapeHTML
BitCompactMarshaler
BitNoQuoteTextMarshaler
BitNoNullSliceOrMap
BitValidateString
BitNoValidateJSONMarshaler
BitNoEncoderNewline
BitEncodeNullForInfOrNan
BitPointerValue = 63
)

View 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
}

View File

@@ -0,0 +1,189 @@
//go:build (amd64 && go1.16 && !go1.27) || (arm64 && go1.20 && !go1.27)
// +build amd64,go1.16,!go1.27 arm64,go1.20,!go1.27
/**
* 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)
}

View File

@@ -0,0 +1,148 @@
// +build !amd64,!arm64 go1.27 !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)
}

View 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)
}
}

View File

@@ -0,0 +1,24 @@
//go:build !race
// +build !race
/*
* Copyright 2021 ByteDance Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package encoder
func encodeIntoCheckRace(buf *[]byte, val interface{}, opts Options) error {
return encodeInto(buf, val, opts)
}

View File

@@ -0,0 +1,54 @@
//go:build race
// +build race
/*
* Copyright 2021 ByteDance Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package encoder
import (
`encoding/json`
`github.com/bytedance/sonic/internal/rt`
)
func helpDetectDataRace(val interface{}) {
var out []byte
defer func() {
if v := recover(); v != nil {
// NOTICE: help user to locate where panic occurs
println("panic when encoding on: ", truncate(out))
panic(v)
}
}()
out, _ = json.Marshal(val)
}
func encodeIntoCheckRace(buf *[]byte, val interface{}, opts Options) error {
err := encodeInto(buf, val, opts)
/* put last to make the panic from sonic will always be caught at first */
helpDetectDataRace(val)
return err
}
func truncate(json []byte) string {
if len(json) <= 256 {
return rt.Mem2Str(json)
} else {
return rt.Mem2Str(json[len(json)-256:])
}
}

View 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)
}

View 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")
}

View 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
}
}

View File

@@ -0,0 +1,24 @@
//go:build !amd64
// +build !amd64
/*
* Copyright 2021 ByteDance Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package encoder
func init() {
ForceUseVM()
}

View 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
}

View 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
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2021 ByteDance Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package vars
import (
"unsafe"
"github.com/bytedance/sonic/internal/rt"
)
type Encoder func(
rb *[]byte,
vp unsafe.Pointer,
sb *Stack,
fv uint64,
) error
func FindOrCompile(vt *rt.GoType, pv bool, compiler func(*rt.GoType, ... interface{}) (interface{}, error)) (interface{}, error) {
if val := programCache.Get(vt); val != nil {
return val, nil
} else if ret, err := programCache.Compute(vt, compiler, pv); err == nil {
return ret, nil
} else {
return nil, err
}
}
func GetProgram(vt *rt.GoType) (interface{}) {
return programCache.Get(vt)
}
func ComputeProgram(vt *rt.GoType, compute func(*rt.GoType, ... interface{}) (interface{}, error), pv bool) (interface{}, error) {
return programCache.Compute(vt, compute, pv)
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright 2024 ByteDance Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package vars
import (
"os"
"unsafe"
)
const (
MaxStack = 4096 // 4k states
StackSize = unsafe.Sizeof(Stack{})
StateSize = int64(unsafe.Sizeof(State{}))
StackLimit = MaxStack * StateSize
)
const (
MAX_ILBUF = 100000 // cutoff at 100k of IL instructions
MAX_FIELDS = 50 // cutoff at 50 fields struct
)
var (
DebugSyncGC = os.Getenv("SONIC_SYNC_GC") != ""
DebugAsyncGC = os.Getenv("SONIC_NO_ASYNC_GC") == ""
DebugCheckPtr = os.Getenv("SONIC_CHECK_POINTER") != ""
)
var UseVM = os.Getenv("SONIC_ENCODER_USE_VM") != ""

View 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
}
}
}

View 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{}
)

View 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)
}

View 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
}

View 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,
}
}

View 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}")
}

View File

@@ -0,0 +1,52 @@
//go:build go1.21 && !go1.27
// +build go1.21,!go1.27
// 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)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
// +build go1.17,!go1.17
/*
* Copyright 2021 ByteDance Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package x86
import (
`os`
`strings`
`runtime`
`runtime/debug`
`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_null)
_F_gc = jit.Func(runtime.GC)
_F_force_gc = jit.Func(debug.FreeOSMemory)
_F_println = jit.Func(println_wrapper)
)
func println_wrapper(i int, op1 int, op2 int){
println(i, " Intrs ", op1, _OpNames[op1], "next: ", op2, _OpNames[op2])
}
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()
}
}

View File

@@ -0,0 +1,201 @@
//go:build go1.17 && !go1.27
// +build go1.17,!go1.27
/*
* 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 x86
import (
"fmt"
"runtime"
"strings"
"unsafe"
"github.com/bytedance/sonic/internal/encoder/ir"
"github.com/bytedance/sonic/internal/encoder/vars"
"github.com/bytedance/sonic/internal/jit"
"github.com/twitchyliquid64/golang-asm/obj"
)
const _FP_debug = 128
var (
_Instr_End = ir.NewInsOp(ir.OP_is_nil)
_F_gc = jit.Func(gc)
_F_println = jit.Func(println_wrapper)
_F_print = jit.Func(print)
)
func (self *Assembler) dsave(r ...obj.Addr) {
for i, v := range r {
if i > _FP_debug/8-1 {
panic("too many registers to save")
} else {
self.Emit("MOVQ", v, jit.Ptr(_SP, _FP_fargs+_FP_saves+_FP_locals+int64(i)*8))
}
}
}
func (self *Assembler) dload(r ...obj.Addr) {
for i, v := range r {
if i > _FP_debug/8-1 {
panic("too many registers to load")
} else {
self.Emit("MOVQ", jit.Ptr(_SP, _FP_fargs+_FP_saves+_FP_locals+int64(i)*8), v)
}
}
}
func println_wrapper(i int, op1 int, op2 int) {
println(i, " Intrs ", op1, ir.OpNames[op1], "next: ", op2, ir.OpNames[op2])
}
func print(i int) {
println(i)
}
func gc() {
if !vars.DebugSyncGC {
return
}
runtime.GC()
// debug.FreeOSMemory()
}
func (self *Assembler) dcall(fn obj.Addr) {
self.Emit("MOVQ", fn, _R10) // MOVQ ${fn}, R10
self.Rjmp("CALL", _R10) // CALL R10
}
func (self *Assembler) debug_gc() {
if !vars.DebugSyncGC {
return
}
self.dsave(_REG_debug...)
self.dcall(_F_gc)
self.dload(_REG_debug...)
}
func (self *Assembler) debug_instr(i int, v *ir.Instr) {
if vars.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 := ir.OpNames[next.Op()]
if strings.Contains(name, "save") {
return
}
}
// self.debug_gc()
}
}
//go:noescape
//go:linkname checkptrBase runtime.checkptrBase
func checkptrBase(p unsafe.Pointer) uintptr
//go:noescape
//go:linkname findObject runtime.findObject
func findObject(p, refBase, refOff uintptr) (base uintptr, s unsafe.Pointer, objIndex uintptr)
var (
_F_checkptr = jit.Func(checkptr)
_F_printptr = jit.Func(printptr)
)
var (
_R10 = jit.Reg("R10")
)
var _REG_debug = []obj.Addr{
jit.Reg("AX"),
jit.Reg("BX"),
jit.Reg("CX"),
jit.Reg("DX"),
jit.Reg("DI"),
jit.Reg("SI"),
jit.Reg("BP"),
jit.Reg("SP"),
jit.Reg("R8"),
jit.Reg("R9"),
jit.Reg("R10"),
jit.Reg("R11"),
jit.Reg("R12"),
jit.Reg("R13"),
jit.Reg("R14"),
jit.Reg("R15"),
}
func checkptr(ptr uintptr) {
if ptr == 0 {
return
}
fmt.Printf("pointer: %x\n", ptr)
f := checkptrBase(unsafe.Pointer(uintptr(ptr)))
if f == 0 {
fmt.Printf("! unknown-based pointer: %x\n", ptr)
} else if f == 1 {
fmt.Printf("! stack pointer: %x\n", ptr)
} else {
fmt.Printf("base: %x\n", f)
}
findobj(ptr)
}
func findobj(ptr uintptr) {
base, s, objIndex := findObject(ptr, 0, 0)
if s != nil && base == 0 {
fmt.Printf("! invalid pointer: %x\n", ptr)
}
fmt.Printf("objIndex: %d\n", objIndex)
}
func (self *Assembler) check_ptr(ptr obj.Addr, lea bool) {
if !vars.DebugCheckPtr {
return
}
self.dsave(_REG_debug...)
if lea {
self.Emit("LEAQ", ptr, _R10)
} else {
self.Emit("MOVQ", ptr, _R10)
}
self.Emit("MOVQ", _R10, jit.Ptr(_SP, 0))
self.dcall(_F_checkptr)
self.dload(_REG_debug...)
}
func printptr(i int, ptr uintptr) {
fmt.Printf("[%d] ptr: %x\n", i, ptr)
}
func (self *Assembler) print_ptr(i int, ptr obj.Addr, lea bool) {
self.dsave(_REG_debug...)
if lea {
self.Emit("LEAQ", ptr, _R10)
} else {
self.Emit("MOVQ", ptr, _R10)
}
self.Emit("MOVQ", jit.Imm(int64(i)), _AX)
self.Emit("MOVQ", _R10, _BX)
self.dcall(_F_printptr)
self.dload(_REG_debug...)
}

View File

@@ -0,0 +1,52 @@
/**
* 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 x86
import (
"unsafe"
_ "unsafe"
"github.com/bytedance/sonic/internal/encoder/alg"
"github.com/bytedance/sonic/internal/encoder/prim"
"github.com/bytedance/sonic/internal/encoder/vars"
"github.com/bytedance/sonic/internal/rt"
"github.com/bytedance/sonic/loader"
_ "github.com/cloudwego/base64x"
)
var compiler func(*rt.GoType, ... interface{}) (interface{}, error)
func SetCompiler(c func(*rt.GoType, ... interface{}) (interface{}, error)) {
compiler = c
}
func ptoenc(p loader.Function) vars.Encoder {
return *(*vars.Encoder)(unsafe.Pointer(&p))
}
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 fn, err := vars.FindOrCompile(vt, (fv&(1<<alg.BitPointerValue)) != 0, compiler); err != nil {
return err
} else if vt.Indirect() {
return fn.(vars.Encoder)(buf, *vp, sb, fv)
} else {
return fn.(vars.Encoder)(buf, unsafe.Pointer(vp), sb, fv)
}
}