mirror of
https://github.com/ii64/sonic.git
synced 2026-06-21 00:46:43 +08:00
251 lines
6.6 KiB
Go
251 lines
6.6 KiB
Go
/*
|
|
* Copyright 2021 ByteDance Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package encoder
|
|
|
|
import (
|
|
`encoding`
|
|
`encoding/json`
|
|
`reflect`
|
|
`unsafe`
|
|
|
|
`github.com/bytedance/sonic/internal/native`
|
|
`github.com/bytedance/sonic/internal/rt`
|
|
)
|
|
|
|
/** Encoder Primitives **/
|
|
|
|
var _QuoteTab = [256]string {
|
|
'\x00' : `\u0000`,
|
|
'\x01' : `\u0001`,
|
|
'\x02' : `\u0002`,
|
|
'\x03' : `\u0003`,
|
|
'\x04' : `\u0004`,
|
|
'\x05' : `\u0005`,
|
|
'\x06' : `\u0006`,
|
|
'\x07' : `\u0007`,
|
|
'\b' : `\b`,
|
|
'\t' : `\t`,
|
|
'\n' : `\n`,
|
|
'\x0b' : `\u000b`,
|
|
'\f' : `\f`,
|
|
'\r' : `\r`,
|
|
'\x0e' : `\u000e`,
|
|
'\x0f' : `\u000f`,
|
|
'\x10' : `\u0010`,
|
|
'\x11' : `\u0011`,
|
|
'\x12' : `\u0012`,
|
|
'\x13' : `\u0013`,
|
|
'\x14' : `\u0014`,
|
|
'\x15' : `\u0015`,
|
|
'\x16' : `\u0016`,
|
|
'\x17' : `\u0017`,
|
|
'\x18' : `\u0018`,
|
|
'\x19' : `\u0019`,
|
|
'\x1a' : `\u001a`,
|
|
'\x1b' : `\u001b`,
|
|
'\x1c' : `\u001c`,
|
|
'\x1d' : `\u001d`,
|
|
'\x1e' : `\u001e`,
|
|
'\x1f' : `\u001f`,
|
|
'"' : `\"`,
|
|
'\\' : `\\`,
|
|
}
|
|
|
|
var _DoubleQuoteTab = [256]string {
|
|
'\x00' : `\\u0000`,
|
|
'\x01' : `\\u0001`,
|
|
'\x02' : `\\u0002`,
|
|
'\x03' : `\\u0003`,
|
|
'\x04' : `\\u0004`,
|
|
'\x05' : `\\u0005`,
|
|
'\x06' : `\\u0006`,
|
|
'\x07' : `\\u0007`,
|
|
'\b' : `\\b`,
|
|
'\t' : `\\t`,
|
|
'\n' : `\\n`,
|
|
'\x0b' : `\\u000b`,
|
|
'\f' : `\\f`,
|
|
'\r' : `\\r`,
|
|
'\x0e' : `\\u000e`,
|
|
'\x0f' : `\\u000f`,
|
|
'\x10' : `\\u0010`,
|
|
'\x11' : `\\u0011`,
|
|
'\x12' : `\\u0012`,
|
|
'\x13' : `\\u0013`,
|
|
'\x14' : `\\u0014`,
|
|
'\x15' : `\\u0015`,
|
|
'\x16' : `\\u0016`,
|
|
'\x17' : `\\u0017`,
|
|
'\x18' : `\\u0018`,
|
|
'\x19' : `\\u0019`,
|
|
'\x1a' : `\\u001a`,
|
|
'\x1b' : `\\u001b`,
|
|
'\x1c' : `\\u001c`,
|
|
'\x1d' : `\\u001d`,
|
|
'\x1e' : `\\u001e`,
|
|
'\x1f' : `\\u001f`,
|
|
'"' : `\\\"`,
|
|
'\\' : `\\\\`,
|
|
}
|
|
|
|
func encodeNil(rb *[]byte) error {
|
|
*rb = append(*rb, 'n', 'u', 'l', 'l')
|
|
return nil
|
|
}
|
|
|
|
func encodeStr(buf *[]byte, val string) error {
|
|
*buf = append(*buf, '"')
|
|
encodeQuote(buf, native.Lquote(&val, 0), val)
|
|
*buf = append(*buf, '"')
|
|
return nil
|
|
}
|
|
|
|
func encodeQuote(buf *[]byte, i int, val string) {
|
|
p := 0
|
|
n := len(val)
|
|
|
|
/* quote all the characters, if any */
|
|
for i < n {
|
|
*buf = append(*buf, rt.Str2Mem(val[p:i])...)
|
|
*buf = append(*buf, rt.Str2Mem(_QuoteTab[val[i]])...)
|
|
p, i = i + 1, native.Lquote(&val, i + 1)
|
|
}
|
|
|
|
/* add the remaining characters */
|
|
if p < n {
|
|
*buf = append(*buf, rt.Str2Mem(val[p:])...)
|
|
}
|
|
}
|
|
|
|
func encodeDoubleQuote(buf *[]byte, i int, val string) {
|
|
p := 0
|
|
n := len(val)
|
|
|
|
/* quote all the characters, if any */
|
|
for i < n {
|
|
*buf = append(*buf, rt.Str2Mem(val[p:i])...)
|
|
*buf = append(*buf, rt.Str2Mem(_DoubleQuoteTab[val[i]])...)
|
|
p, i = i + 1, native.Lquote(&val, i + 1)
|
|
}
|
|
|
|
/* add the remaining characters */
|
|
if p < n {
|
|
*buf = append(*buf, rt.Str2Mem(val[p:])...)
|
|
}
|
|
}
|
|
|
|
func encodeTypedPointer(buf *[]byte, vt *rt.GoType, vp *unsafe.Pointer, sb *_Stack) error {
|
|
if vt == nil {
|
|
return encodeNil(buf)
|
|
} else if fn, err := findOrCompile(vt); err != nil {
|
|
return err
|
|
} else if vt.Indir() {
|
|
return fn(buf, *vp, sb)
|
|
} else {
|
|
return fn(buf, unsafe.Pointer(vp), sb)
|
|
}
|
|
}
|
|
|
|
func encodeJsonMarshaler(buf *[]byte, val json.Marshaler) error {
|
|
if ret, err := val.MarshalJSON(); err != nil {
|
|
return err
|
|
} else {
|
|
return compact(buf, ret)
|
|
}
|
|
}
|
|
|
|
func encodeTextMarshaler(buf *[]byte, val encoding.TextMarshaler) error {
|
|
if ret, err := val.MarshalText(); err != nil {
|
|
return err
|
|
} else {
|
|
return encodeStr(buf, rt.Mem2Str(ret))
|
|
}
|
|
}
|
|
|
|
func isZeroMem(p unsafe.Pointer, nb int) bool {
|
|
return native.Lzero(p, nb) == 0
|
|
}
|
|
|
|
func isZeroSafe(p unsafe.Pointer, vt *rt.GoType) bool {
|
|
if isZeroMem(p, vt.Size()) {
|
|
return true
|
|
} else {
|
|
return isZeroTyped(p, vt)
|
|
}
|
|
}
|
|
|
|
func isZeroTyped(p unsafe.Pointer, vt *rt.GoType) bool {
|
|
switch vt.Kind() {
|
|
case reflect.Map : return (*(**rt.GoMap)(p)).Count == 0
|
|
case reflect.Slice : return (*rt.GoSlice)(p).Len == 0
|
|
case reflect.String : return (*rt.GoString)(p).Len == 0
|
|
case reflect.Struct : return isZeroStruct(p, vt)
|
|
case reflect.Interface : return (*rt.GoEface)(p).Value == nil
|
|
default : return false
|
|
}
|
|
}
|
|
|
|
func isZeroStruct(p unsafe.Pointer, vt *rt.GoType) bool {
|
|
var dp uintptr
|
|
var fp unsafe.Pointer
|
|
|
|
/* check for each field */
|
|
for _, fv := range (*rt.GoStructType)(unsafe.Pointer(vt)).Fields {
|
|
dp = fv.OffEmbed >> 1
|
|
fp = unsafe.Pointer(uintptr(p) + dp)
|
|
|
|
/* check for the field */
|
|
if !isZeroSafe(fp, fv.Type) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
/* all tests are passed */
|
|
return true
|
|
}
|
|
|
|
func isTrivialZeroable(vt reflect.Type) bool {
|
|
switch vt.Kind() {
|
|
case reflect.Bool : return true
|
|
case reflect.Int : return true
|
|
case reflect.Int8 : return true
|
|
case reflect.Int16 : return true
|
|
case reflect.Int32 : return true
|
|
case reflect.Int64 : return true
|
|
case reflect.Uint : return true
|
|
case reflect.Uint8 : return true
|
|
case reflect.Uint16 : return true
|
|
case reflect.Uint32 : return true
|
|
case reflect.Uint64 : return true
|
|
case reflect.Uintptr : return true
|
|
case reflect.Float32 : return true
|
|
case reflect.Float64 : return true
|
|
case reflect.String : return false
|
|
case reflect.Array : return true
|
|
case reflect.Interface : return false
|
|
case reflect.Map : return false
|
|
case reflect.Ptr : return true
|
|
case reflect.Slice : return false
|
|
case reflect.Struct : return isStructTrivialZeroable(vt)
|
|
default : return false
|
|
}
|
|
}
|
|
|
|
func isStructTrivialZeroable(vt reflect.Type) bool {
|
|
for i := 0; i < vt.NumField(); i++ { if !isTrivialZeroable(vt.Field(i).Type) { return false } }
|
|
return true
|
|
}
|