summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/goccy/go-json/internal/encoder/encoder.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/goccy/go-json/internal/encoder/encoder.go')
-rw-r--r--vendor/github.com/goccy/go-json/internal/encoder/encoder.go551
1 files changed, 551 insertions, 0 deletions
diff --git a/vendor/github.com/goccy/go-json/internal/encoder/encoder.go b/vendor/github.com/goccy/go-json/internal/encoder/encoder.go
new file mode 100644
index 0000000000..b7fa99a966
--- /dev/null
+++ b/vendor/github.com/goccy/go-json/internal/encoder/encoder.go
@@ -0,0 +1,551 @@
+package encoder
+
+import (
+ "bytes"
+ "encoding"
+ "encoding/base64"
+ "encoding/json"
+ "fmt"
+ "math"
+ "reflect"
+ "strconv"
+ "strings"
+ "sync"
+ "unsafe"
+
+ "github.com/goccy/go-json/internal/errors"
+ "github.com/goccy/go-json/internal/runtime"
+)
+
+func (t OpType) IsMultipleOpHead() bool {
+ switch t {
+ case OpStructHead:
+ return true
+ case OpStructHeadSlice:
+ return true
+ case OpStructHeadArray:
+ return true
+ case OpStructHeadMap:
+ return true
+ case OpStructHeadStruct:
+ return true
+ case OpStructHeadOmitEmpty:
+ return true
+ case OpStructHeadOmitEmptySlice:
+ return true
+ case OpStructHeadOmitEmptyArray:
+ return true
+ case OpStructHeadOmitEmptyMap:
+ return true
+ case OpStructHeadOmitEmptyStruct:
+ return true
+ case OpStructHeadSlicePtr:
+ return true
+ case OpStructHeadOmitEmptySlicePtr:
+ return true
+ case OpStructHeadArrayPtr:
+ return true
+ case OpStructHeadOmitEmptyArrayPtr:
+ return true
+ case OpStructHeadMapPtr:
+ return true
+ case OpStructHeadOmitEmptyMapPtr:
+ return true
+ }
+ return false
+}
+
+func (t OpType) IsMultipleOpField() bool {
+ switch t {
+ case OpStructField:
+ return true
+ case OpStructFieldSlice:
+ return true
+ case OpStructFieldArray:
+ return true
+ case OpStructFieldMap:
+ return true
+ case OpStructFieldStruct:
+ return true
+ case OpStructFieldOmitEmpty:
+ return true
+ case OpStructFieldOmitEmptySlice:
+ return true
+ case OpStructFieldOmitEmptyArray:
+ return true
+ case OpStructFieldOmitEmptyMap:
+ return true
+ case OpStructFieldOmitEmptyStruct:
+ return true
+ case OpStructFieldSlicePtr:
+ return true
+ case OpStructFieldOmitEmptySlicePtr:
+ return true
+ case OpStructFieldArrayPtr:
+ return true
+ case OpStructFieldOmitEmptyArrayPtr:
+ return true
+ case OpStructFieldMapPtr:
+ return true
+ case OpStructFieldOmitEmptyMapPtr:
+ return true
+ }
+ return false
+}
+
+type OpcodeSet struct {
+ Type *runtime.Type
+ NoescapeKeyCode *Opcode
+ EscapeKeyCode *Opcode
+ InterfaceNoescapeKeyCode *Opcode
+ InterfaceEscapeKeyCode *Opcode
+ CodeLength int
+ EndCode *Opcode
+}
+
+type CompiledCode struct {
+ Code *Opcode
+ Linked bool // whether recursive code already have linked
+ CurLen uintptr
+ NextLen uintptr
+}
+
+const StartDetectingCyclesAfter = 1000
+
+func Load(base uintptr, idx uintptr) uintptr {
+ addr := base + idx
+ return **(**uintptr)(unsafe.Pointer(&addr))
+}
+
+func Store(base uintptr, idx uintptr, p uintptr) {
+ addr := base + idx
+ **(**uintptr)(unsafe.Pointer(&addr)) = p
+}
+
+func LoadNPtr(base uintptr, idx uintptr, ptrNum int) uintptr {
+ addr := base + idx
+ p := **(**uintptr)(unsafe.Pointer(&addr))
+ if p == 0 {
+ return 0
+ }
+ return PtrToPtr(p)
+ /*
+ for i := 0; i < ptrNum; i++ {
+ if p == 0 {
+ return p
+ }
+ p = PtrToPtr(p)
+ }
+ return p
+ */
+}
+
+func PtrToUint64(p uintptr) uint64 { return **(**uint64)(unsafe.Pointer(&p)) }
+func PtrToFloat32(p uintptr) float32 { return **(**float32)(unsafe.Pointer(&p)) }
+func PtrToFloat64(p uintptr) float64 { return **(**float64)(unsafe.Pointer(&p)) }
+func PtrToBool(p uintptr) bool { return **(**bool)(unsafe.Pointer(&p)) }
+func PtrToBytes(p uintptr) []byte { return **(**[]byte)(unsafe.Pointer(&p)) }
+func PtrToNumber(p uintptr) json.Number { return **(**json.Number)(unsafe.Pointer(&p)) }
+func PtrToString(p uintptr) string { return **(**string)(unsafe.Pointer(&p)) }
+func PtrToSlice(p uintptr) *runtime.SliceHeader { return *(**runtime.SliceHeader)(unsafe.Pointer(&p)) }
+func PtrToPtr(p uintptr) uintptr {
+ return uintptr(**(**unsafe.Pointer)(unsafe.Pointer(&p)))
+}
+func PtrToNPtr(p uintptr, ptrNum int) uintptr {
+ for i := 0; i < ptrNum; i++ {
+ if p == 0 {
+ return 0
+ }
+ p = PtrToPtr(p)
+ }
+ return p
+}
+
+func PtrToUnsafePtr(p uintptr) unsafe.Pointer {
+ return *(*unsafe.Pointer)(unsafe.Pointer(&p))
+}
+func PtrToInterface(code *Opcode, p uintptr) interface{} {
+ return *(*interface{})(unsafe.Pointer(&emptyInterface{
+ typ: code.Type,
+ ptr: *(*unsafe.Pointer)(unsafe.Pointer(&p)),
+ }))
+}
+
+func ErrUnsupportedValue(code *Opcode, ptr uintptr) *errors.UnsupportedValueError {
+ v := *(*interface{})(unsafe.Pointer(&emptyInterface{
+ typ: code.Type,
+ ptr: *(*unsafe.Pointer)(unsafe.Pointer(&ptr)),
+ }))
+ return &errors.UnsupportedValueError{
+ Value: reflect.ValueOf(v),
+ Str: fmt.Sprintf("encountered a cycle via %s", code.Type),
+ }
+}
+
+func ErrUnsupportedFloat(v float64) *errors.UnsupportedValueError {
+ return &errors.UnsupportedValueError{
+ Value: reflect.ValueOf(v),
+ Str: strconv.FormatFloat(v, 'g', -1, 64),
+ }
+}
+
+func ErrMarshalerWithCode(code *Opcode, err error) *errors.MarshalerError {
+ return &errors.MarshalerError{
+ Type: runtime.RType2Type(code.Type),
+ Err: err,
+ }
+}
+
+type emptyInterface struct {
+ typ *runtime.Type
+ ptr unsafe.Pointer
+}
+
+type MapItem struct {
+ Key []byte
+ Value []byte
+}
+
+type Mapslice struct {
+ Items []MapItem
+}
+
+func (m *Mapslice) Len() int {
+ return len(m.Items)
+}
+
+func (m *Mapslice) Less(i, j int) bool {
+ return bytes.Compare(m.Items[i].Key, m.Items[j].Key) < 0
+}
+
+func (m *Mapslice) Swap(i, j int) {
+ m.Items[i], m.Items[j] = m.Items[j], m.Items[i]
+}
+
+type MapContext struct {
+ Pos []int
+ Slice *Mapslice
+ Buf []byte
+}
+
+var mapContextPool = sync.Pool{
+ New: func() interface{} {
+ return &MapContext{}
+ },
+}
+
+func NewMapContext(mapLen int) *MapContext {
+ ctx := mapContextPool.Get().(*MapContext)
+ if ctx.Slice == nil {
+ ctx.Slice = &Mapslice{
+ Items: make([]MapItem, 0, mapLen),
+ }
+ }
+ if cap(ctx.Pos) < (mapLen*2 + 1) {
+ ctx.Pos = make([]int, 0, mapLen*2+1)
+ ctx.Slice.Items = make([]MapItem, 0, mapLen)
+ } else {
+ ctx.Pos = ctx.Pos[:0]
+ ctx.Slice.Items = ctx.Slice.Items[:0]
+ }
+ ctx.Buf = ctx.Buf[:0]
+ return ctx
+}
+
+func ReleaseMapContext(c *MapContext) {
+ mapContextPool.Put(c)
+}
+
+//go:linkname MapIterInit reflect.mapiterinit
+//go:noescape
+func MapIterInit(mapType *runtime.Type, m unsafe.Pointer) unsafe.Pointer
+
+//go:linkname MapIterKey reflect.mapiterkey
+//go:noescape
+func MapIterKey(it unsafe.Pointer) unsafe.Pointer
+
+//go:linkname MapIterNext reflect.mapiternext
+//go:noescape
+func MapIterNext(it unsafe.Pointer)
+
+//go:linkname MapLen reflect.maplen
+//go:noescape
+func MapLen(m unsafe.Pointer) int
+
+func AppendByteSlice(_ *RuntimeContext, b []byte, src []byte) []byte {
+ if src == nil {
+ return append(b, `null`...)
+ }
+ encodedLen := base64.StdEncoding.EncodedLen(len(src))
+ b = append(b, '"')
+ pos := len(b)
+ remainLen := cap(b[pos:])
+ var buf []byte
+ if remainLen > encodedLen {
+ buf = b[pos : pos+encodedLen]
+ } else {
+ buf = make([]byte, encodedLen)
+ }
+ base64.StdEncoding.Encode(buf, src)
+ return append(append(b, buf...), '"')
+}
+
+func AppendFloat32(_ *RuntimeContext, b []byte, v float32) []byte {
+ f64 := float64(v)
+ abs := math.Abs(f64)
+ fmt := byte('f')
+ // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
+ if abs != 0 {
+ f32 := float32(abs)
+ if f32 < 1e-6 || f32 >= 1e21 {
+ fmt = 'e'
+ }
+ }
+ return strconv.AppendFloat(b, f64, fmt, -1, 32)
+}
+
+func AppendFloat64(_ *RuntimeContext, b []byte, v float64) []byte {
+ abs := math.Abs(v)
+ fmt := byte('f')
+ // Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
+ if abs != 0 {
+ if abs < 1e-6 || abs >= 1e21 {
+ fmt = 'e'
+ }
+ }
+ return strconv.AppendFloat(b, v, fmt, -1, 64)
+}
+
+func AppendBool(_ *RuntimeContext, b []byte, v bool) []byte {
+ if v {
+ return append(b, "true"...)
+ }
+ return append(b, "false"...)
+}
+
+var (
+ floatTable = [256]bool{
+ '0': true,
+ '1': true,
+ '2': true,
+ '3': true,
+ '4': true,
+ '5': true,
+ '6': true,
+ '7': true,
+ '8': true,
+ '9': true,
+ '.': true,
+ 'e': true,
+ 'E': true,
+ '+': true,
+ '-': true,
+ }
+)
+
+func AppendNumber(_ *RuntimeContext, b []byte, n json.Number) ([]byte, error) {
+ if len(n) == 0 {
+ return append(b, '0'), nil
+ }
+ for i := 0; i < len(n); i++ {
+ if !floatTable[n[i]] {
+ return nil, fmt.Errorf("json: invalid number literal %q", n)
+ }
+ }
+ b = append(b, n...)
+ return b, nil
+}
+
+func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
+ rv := reflect.ValueOf(v) // convert by dynamic interface type
+ if (code.Flags & AddrForMarshalerFlags) != 0 {
+ if rv.CanAddr() {
+ rv = rv.Addr()
+ } else {
+ newV := reflect.New(rv.Type())
+ newV.Elem().Set(rv)
+ rv = newV
+ }
+ }
+ v = rv.Interface()
+ var bb []byte
+ if (code.Flags & MarshalerContextFlags) != 0 {
+ marshaler, ok := v.(marshalerContext)
+ if !ok {
+ return AppendNull(ctx, b), nil
+ }
+ b, err := marshaler.MarshalJSON(ctx.Option.Context)
+ if err != nil {
+ return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
+ }
+ bb = b
+ } else {
+ marshaler, ok := v.(json.Marshaler)
+ if !ok {
+ return AppendNull(ctx, b), nil
+ }
+ b, err := marshaler.MarshalJSON()
+ if err != nil {
+ return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
+ }
+ bb = b
+ }
+ marshalBuf := ctx.MarshalBuf[:0]
+ marshalBuf = append(append(marshalBuf, bb...), nul)
+ compactedBuf, err := compact(b, marshalBuf, (ctx.Option.Flag&HTMLEscapeOption) != 0)
+ if err != nil {
+ return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
+ }
+ ctx.MarshalBuf = marshalBuf
+ return compactedBuf, nil
+}
+
+func AppendMarshalJSONIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
+ rv := reflect.ValueOf(v) // convert by dynamic interface type
+ if (code.Flags & AddrForMarshalerFlags) != 0 {
+ if rv.CanAddr() {
+ rv = rv.Addr()
+ } else {
+ newV := reflect.New(rv.Type())
+ newV.Elem().Set(rv)
+ rv = newV
+ }
+ }
+ v = rv.Interface()
+ var bb []byte
+ if (code.Flags & MarshalerContextFlags) != 0 {
+ marshaler, ok := v.(marshalerContext)
+ if !ok {
+ return AppendNull(ctx, b), nil
+ }
+ b, err := marshaler.MarshalJSON(ctx.Option.Context)
+ if err != nil {
+ return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
+ }
+ bb = b
+ } else {
+ marshaler, ok := v.(json.Marshaler)
+ if !ok {
+ return AppendNull(ctx, b), nil
+ }
+ b, err := marshaler.MarshalJSON()
+ if err != nil {
+ return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
+ }
+ bb = b
+ }
+ marshalBuf := ctx.MarshalBuf[:0]
+ marshalBuf = append(append(marshalBuf, bb...), nul)
+ indentedBuf, err := doIndent(
+ b,
+ marshalBuf,
+ string(ctx.Prefix)+strings.Repeat(string(ctx.IndentStr), int(ctx.BaseIndent+code.Indent)),
+ string(ctx.IndentStr),
+ (ctx.Option.Flag&HTMLEscapeOption) != 0,
+ )
+ if err != nil {
+ return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
+ }
+ ctx.MarshalBuf = marshalBuf
+ return indentedBuf, nil
+}
+
+func AppendMarshalText(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
+ rv := reflect.ValueOf(v) // convert by dynamic interface type
+ if (code.Flags & AddrForMarshalerFlags) != 0 {
+ if rv.CanAddr() {
+ rv = rv.Addr()
+ } else {
+ newV := reflect.New(rv.Type())
+ newV.Elem().Set(rv)
+ rv = newV
+ }
+ }
+ v = rv.Interface()
+ marshaler, ok := v.(encoding.TextMarshaler)
+ if !ok {
+ return AppendNull(ctx, b), nil
+ }
+ bytes, err := marshaler.MarshalText()
+ if err != nil {
+ return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
+ }
+ return AppendString(ctx, b, *(*string)(unsafe.Pointer(&bytes))), nil
+}
+
+func AppendMarshalTextIndent(ctx *RuntimeContext, code *Opcode, b []byte, v interface{}) ([]byte, error) {
+ rv := reflect.ValueOf(v) // convert by dynamic interface type
+ if (code.Flags & AddrForMarshalerFlags) != 0 {
+ if rv.CanAddr() {
+ rv = rv.Addr()
+ } else {
+ newV := reflect.New(rv.Type())
+ newV.Elem().Set(rv)
+ rv = newV
+ }
+ }
+ v = rv.Interface()
+ marshaler, ok := v.(encoding.TextMarshaler)
+ if !ok {
+ return AppendNull(ctx, b), nil
+ }
+ bytes, err := marshaler.MarshalText()
+ if err != nil {
+ return nil, &errors.MarshalerError{Type: reflect.TypeOf(v), Err: err}
+ }
+ return AppendString(ctx, b, *(*string)(unsafe.Pointer(&bytes))), nil
+}
+
+func AppendNull(_ *RuntimeContext, b []byte) []byte {
+ return append(b, "null"...)
+}
+
+func AppendComma(_ *RuntimeContext, b []byte) []byte {
+ return append(b, ',')
+}
+
+func AppendCommaIndent(_ *RuntimeContext, b []byte) []byte {
+ return append(b, ',', '\n')
+}
+
+func AppendStructEnd(_ *RuntimeContext, b []byte) []byte {
+ return append(b, '}', ',')
+}
+
+func AppendStructEndIndent(ctx *RuntimeContext, code *Opcode, b []byte) []byte {
+ b = append(b, '\n')
+ b = append(b, ctx.Prefix...)
+ indentNum := ctx.BaseIndent + code.Indent - 1
+ for i := uint32(0); i < indentNum; i++ {
+ b = append(b, ctx.IndentStr...)
+ }
+ return append(b, '}', ',', '\n')
+}
+
+func AppendIndent(ctx *RuntimeContext, b []byte, indent uint32) []byte {
+ b = append(b, ctx.Prefix...)
+ indentNum := ctx.BaseIndent + indent
+ for i := uint32(0); i < indentNum; i++ {
+ b = append(b, ctx.IndentStr...)
+ }
+ return b
+}
+
+func IsNilForMarshaler(v interface{}) bool {
+ rv := reflect.ValueOf(v)
+ switch rv.Kind() {
+ case reflect.Bool:
+ return !rv.Bool()
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ return rv.Int() == 0
+ case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
+ return rv.Uint() == 0
+ case reflect.Float32, reflect.Float64:
+ return math.Float64bits(rv.Float()) == 0
+ case reflect.Interface, reflect.Map, reflect.Ptr, reflect.Func:
+ return rv.IsNil()
+ case reflect.Slice:
+ return rv.IsNil() || rv.Len() == 0
+ }
+ return false
+}