mirror of https://github.com/goccy/go-json.git
Merge pull request #97 from goccy/feature/fix-interface
Fix interface operation and optimize map operation
This commit is contained in:
commit
72e9fa8193
|
@ -472,3 +472,68 @@ func Benchmark_Encode_LargeStructCached_GoJsonNoEscape(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func benchMapValue() map[string]interface{} {
|
||||||
|
return map[string]interface{}{
|
||||||
|
"a": 1,
|
||||||
|
"b": 2.1,
|
||||||
|
"c": "hello",
|
||||||
|
"d": struct {
|
||||||
|
V int
|
||||||
|
}{
|
||||||
|
V: 1,
|
||||||
|
},
|
||||||
|
"e": true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Encode_MapInterface_EncodingJson(b *testing.B) {
|
||||||
|
v := benchMapValue()
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := json.Marshal(v); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Encode_MapInterface_JsonIter(b *testing.B) {
|
||||||
|
v := benchMapValue()
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := json.Marshal(v); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Encode_MapInterface_Jettison(b *testing.B) {
|
||||||
|
v := benchMapValue()
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := jettison.Marshal(v); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Encode_MapInterface_SegmentioJson(b *testing.B) {
|
||||||
|
v := benchMapValue()
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := segmentiojson.Marshal(v); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Encode_MapInterface_GoJson(b *testing.B) {
|
||||||
|
v := benchMapValue()
|
||||||
|
b.ReportAllocs()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if _, err := gojson.Marshal(v); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -315,7 +315,6 @@ func (t opType) fieldToStringTagField() opType {
|
||||||
opTypes := []opType{
|
opTypes := []opType{
|
||||||
createOpType("End", "Op"),
|
createOpType("End", "Op"),
|
||||||
createOpType("Interface", "Op"),
|
createOpType("Interface", "Op"),
|
||||||
createOpType("InterfaceEnd", "Op"),
|
|
||||||
createOpType("Ptr", "Op"),
|
createOpType("Ptr", "Op"),
|
||||||
createOpType("NPtr", "Op"),
|
createOpType("NPtr", "Op"),
|
||||||
createOpType("SliceHead", "SliceHead"),
|
createOpType("SliceHead", "SliceHead"),
|
||||||
|
|
61
encode.go
61
encode.go
|
@ -22,6 +22,7 @@ type Encoder struct {
|
||||||
enabledIndent bool
|
enabledIndent bool
|
||||||
enabledHTMLEscape bool
|
enabledHTMLEscape bool
|
||||||
unorderedMap bool
|
unorderedMap bool
|
||||||
|
baseIndent int
|
||||||
prefix []byte
|
prefix []byte
|
||||||
indentStr []byte
|
indentStr []byte
|
||||||
}
|
}
|
||||||
|
@ -112,6 +113,7 @@ func (e *Encoder) EncodeWithOption(v interface{}, opts ...EncodeOption) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||||
|
e.ptr = header.ptr
|
||||||
buf, err := e.encode(header, v == nil)
|
buf, err := e.encode(header, v == nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -155,6 +157,7 @@ func (e *Encoder) release() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) reset() {
|
func (e *Encoder) reset() {
|
||||||
|
e.baseIndent = 0
|
||||||
e.enabledHTMLEscape = true
|
e.enabledHTMLEscape = true
|
||||||
e.enabledIndent = false
|
e.enabledIndent = false
|
||||||
e.unorderedMap = false
|
e.unorderedMap = false
|
||||||
|
@ -192,23 +195,31 @@ func (e *Encoder) encode(header *interfaceHeader, isNil bool) ([]byte, error) {
|
||||||
typ := header.typ
|
typ := header.typ
|
||||||
|
|
||||||
typeptr := uintptr(unsafe.Pointer(typ))
|
typeptr := uintptr(unsafe.Pointer(typ))
|
||||||
|
codeSet, err := e.compileToGetCodeSet(typeptr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := e.ctx
|
||||||
|
p := uintptr(header.ptr)
|
||||||
|
ctx.init(p, codeSet.codeLength)
|
||||||
|
if e.enabledIndent {
|
||||||
|
if e.enabledHTMLEscape {
|
||||||
|
return e.runEscapedIndent(ctx, b, codeSet)
|
||||||
|
} else {
|
||||||
|
return e.runIndent(ctx, b, codeSet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e.enabledHTMLEscape {
|
||||||
|
return e.runEscaped(ctx, b, codeSet)
|
||||||
|
}
|
||||||
|
return e.run(ctx, b, codeSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) compileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
||||||
opcodeMap := loadOpcodeMap()
|
opcodeMap := loadOpcodeMap()
|
||||||
if codeSet, exists := opcodeMap[typeptr]; exists {
|
if codeSet, exists := opcodeMap[typeptr]; exists {
|
||||||
ctx := e.ctx
|
return codeSet, nil
|
||||||
p := uintptr(header.ptr)
|
|
||||||
ctx.init(p, codeSet.codeLength)
|
|
||||||
|
|
||||||
if e.enabledIndent {
|
|
||||||
if e.enabledHTMLEscape {
|
|
||||||
return e.runEscapedIndent(ctx, b, codeSet.code)
|
|
||||||
} else {
|
|
||||||
return e.runIndent(ctx, b, codeSet.code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e.enabledHTMLEscape {
|
|
||||||
return e.runEscaped(ctx, b, codeSet.code)
|
|
||||||
}
|
|
||||||
return e.run(ctx, b, codeSet.code)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// noescape trick for header.typ ( reflect.*rtype )
|
// noescape trick for header.typ ( reflect.*rtype )
|
||||||
|
@ -230,21 +241,7 @@ func (e *Encoder) encode(header *interfaceHeader, isNil bool) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
storeOpcodeSet(typeptr, codeSet, opcodeMap)
|
storeOpcodeSet(typeptr, codeSet, opcodeMap)
|
||||||
p := uintptr(header.ptr)
|
return codeSet, nil
|
||||||
ctx := e.ctx
|
|
||||||
ctx.init(p, codeLength)
|
|
||||||
|
|
||||||
if e.enabledIndent {
|
|
||||||
if e.enabledHTMLEscape {
|
|
||||||
return e.runEscapedIndent(ctx, b, codeSet.code)
|
|
||||||
} else {
|
|
||||||
return e.runIndent(ctx, b, codeSet.code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e.enabledHTMLEscape {
|
|
||||||
return e.runEscaped(ctx, b, codeSet.code)
|
|
||||||
}
|
|
||||||
return e.run(ctx, b, codeSet.code)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeFloat32(b []byte, v float32) []byte {
|
func encodeFloat32(b []byte, v float32) []byte {
|
||||||
|
@ -303,7 +300,7 @@ func appendStructEnd(b []byte) []byte {
|
||||||
func (e *Encoder) appendStructEndIndent(b []byte, indent int) []byte {
|
func (e *Encoder) appendStructEndIndent(b []byte, indent int) []byte {
|
||||||
b = append(b, '\n')
|
b = append(b, '\n')
|
||||||
b = append(b, e.prefix...)
|
b = append(b, e.prefix...)
|
||||||
b = append(b, bytes.Repeat(e.indentStr, indent)...)
|
b = append(b, bytes.Repeat(e.indentStr, e.baseIndent+indent)...)
|
||||||
return append(b, '}', ',', '\n')
|
return append(b, '}', ',', '\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -324,5 +321,5 @@ func encodeByteSlice(b []byte, src []byte) []byte {
|
||||||
|
|
||||||
func (e *Encoder) encodeIndent(b []byte, indent int) []byte {
|
func (e *Encoder) encodeIndent(b []byte, indent int) []byte {
|
||||||
b = append(b, e.prefix...)
|
b = append(b, e.prefix...)
|
||||||
return append(b, bytes.Repeat(e.indentStr, indent)...)
|
return append(b, bytes.Repeat(e.indentStr, e.baseIndent+indent)...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,67 @@
|
||||||
package json
|
package json
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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 encodeMapContext struct {
|
||||||
|
iter unsafe.Pointer
|
||||||
|
pos []int
|
||||||
|
slice *mapslice
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var mapContextPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return &encodeMapContext{}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMapContext(mapLen int) *encodeMapContext {
|
||||||
|
ctx := mapContextPool.Get().(*encodeMapContext)
|
||||||
|
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 *encodeMapContext) {
|
||||||
|
mapContextPool.Put(c)
|
||||||
|
}
|
||||||
|
|
||||||
type encodeCompileContext struct {
|
type encodeCompileContext struct {
|
||||||
typ *rtype
|
typ *rtype
|
||||||
root bool
|
root bool
|
||||||
|
|
|
@ -131,7 +131,7 @@ func (c *opcode) totalLength() int {
|
||||||
var idx int
|
var idx int
|
||||||
for code := c; code.op != opEnd; {
|
for code := c; code.op != opEnd; {
|
||||||
idx = int(code.idx / uintptrSize)
|
idx = int(code.idx / uintptrSize)
|
||||||
if code.op == opInterfaceEnd || code.op == opStructFieldRecursiveEnd {
|
if code.op == opStructFieldRecursiveEnd {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
switch code.op.codeType() {
|
switch code.op.codeType() {
|
||||||
|
|
5542
encode_optype.go
5542
encode_optype.go
File diff suppressed because it is too large
Load Diff
122
encode_vm.go
122
encode_vm.go
|
@ -49,10 +49,11 @@ func errMarshaler(code *opcode, err error) *MarshalerError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte, error) {
|
func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ([]byte, error) {
|
||||||
recursiveLevel := 0
|
recursiveLevel := 0
|
||||||
ptrOffset := uintptr(0)
|
ptrOffset := uintptr(0)
|
||||||
ctxptr := ctx.ptr()
|
ctxptr := ctx.ptr()
|
||||||
|
code := codeSet.code
|
||||||
|
|
||||||
for {
|
for {
|
||||||
switch code.op {
|
switch code.op {
|
||||||
|
@ -206,65 +207,46 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.seenPtr = append(ctx.seenPtr, ptr)
|
ctx.seenPtr = append(ctx.seenPtr, ptr)
|
||||||
v := e.ptrToInterface(code, ptr)
|
iface := (*interfaceHeader)(e.ptrToUnsafePtr(ptr))
|
||||||
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(&v))
|
if iface == nil || iface.ptr == nil {
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
if rv.IsNil() {
|
|
||||||
b = encodeNull(b)
|
b = encodeNull(b)
|
||||||
b = encodeComma(b)
|
b = encodeComma(b)
|
||||||
code = code.next
|
code = code.next
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
vv := rv.Interface()
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(iface))
|
||||||
header := (*interfaceHeader)(unsafe.Pointer(&vv))
|
ifaceCodeSet, err := e.compileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
|
||||||
if header.typ.Kind() == reflect.Ptr {
|
|
||||||
if rv.Elem().IsNil() {
|
|
||||||
b = encodeNull(b)
|
|
||||||
b = encodeComma(b)
|
|
||||||
code = code.next
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c, err := e.compileHead(&encodeCompileContext{
|
|
||||||
typ: header.typ,
|
|
||||||
root: code.root,
|
|
||||||
indent: code.indent,
|
|
||||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
beforeLastCode := c.beforeLastCode()
|
|
||||||
lastCode := beforeLastCode.next
|
totalLength := uintptr(codeSet.codeLength)
|
||||||
lastCode.idx = beforeLastCode.idx + uintptrSize
|
nextTotalLength := uintptr(ifaceCodeSet.codeLength)
|
||||||
totalLength := uintptr(code.totalLength())
|
|
||||||
nextTotalLength := uintptr(c.totalLength())
|
|
||||||
curlen := uintptr(len(ctx.ptrs))
|
curlen := uintptr(len(ctx.ptrs))
|
||||||
offsetNum := ptrOffset / uintptrSize
|
offsetNum := ptrOffset / uintptrSize
|
||||||
oldOffset := ptrOffset
|
|
||||||
ptrOffset += totalLength * uintptrSize
|
|
||||||
|
|
||||||
newLen := offsetNum + totalLength + nextTotalLength
|
newLen := offsetNum + totalLength + nextTotalLength
|
||||||
if curlen < newLen {
|
if curlen < newLen {
|
||||||
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
|
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
|
||||||
}
|
}
|
||||||
ctxptr = ctx.ptr() + ptrOffset // assign new ctxptr
|
oldPtrs := ctx.ptrs
|
||||||
|
|
||||||
store(ctxptr, 0, uintptr(header.ptr))
|
newPtrs := ctx.ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
|
||||||
store(ctxptr, lastCode.idx, oldOffset)
|
newPtrs[0] = uintptr(iface.ptr)
|
||||||
|
|
||||||
// link lastCode ( opInterfaceEnd ) => code.next
|
ctx.ptrs = newPtrs
|
||||||
lastCode.op = opInterfaceEnd
|
|
||||||
lastCode.next = code.next
|
|
||||||
|
|
||||||
code = c
|
bb, err := e.run(ctx, b, ifaceCodeSet)
|
||||||
recursiveLevel++
|
if err != nil {
|
||||||
case opInterfaceEnd:
|
return nil, err
|
||||||
recursiveLevel--
|
}
|
||||||
// restore ctxptr
|
|
||||||
offset := load(ctxptr, code.idx)
|
ctx.ptrs = oldPtrs
|
||||||
ctxptr = ctx.ptr() + offset
|
ctxptr = ctx.ptr()
|
||||||
ptrOffset = offset
|
ctx.seenPtr = ctx.seenPtr[:len(ctx.seenPtr)-1]
|
||||||
|
|
||||||
|
b = bb
|
||||||
code = code.next
|
code = code.next
|
||||||
case opMarshalJSON:
|
case opMarshalJSON:
|
||||||
ptr := load(ctxptr, code.idx)
|
ptr := load(ctxptr, code.idx)
|
||||||
|
@ -396,11 +378,10 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
||||||
store(ctxptr, code.length, uintptr(mlen))
|
store(ctxptr, code.length, uintptr(mlen))
|
||||||
store(ctxptr, code.mapIter, uintptr(iter))
|
store(ctxptr, code.mapIter, uintptr(iter))
|
||||||
if !e.unorderedMap {
|
if !e.unorderedMap {
|
||||||
pos := make([]int, 0, mlen)
|
mapCtx := newMapContext(mlen)
|
||||||
pos = append(pos, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
posPtr := unsafe.Pointer(&pos)
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(mapCtx))
|
||||||
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
store(ctxptr, code.end.mapPos, uintptr(unsafe.Pointer(mapCtx)))
|
||||||
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
|
||||||
}
|
}
|
||||||
key := mapiterkey(iter)
|
key := mapiterkey(iter)
|
||||||
store(ctxptr, code.next.idx, uintptr(key))
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
@ -437,11 +418,10 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
||||||
key := mapiterkey(iter)
|
key := mapiterkey(iter)
|
||||||
store(ctxptr, code.next.idx, uintptr(key))
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
if !e.unorderedMap {
|
if !e.unorderedMap {
|
||||||
pos := make([]int, 0, mlen)
|
mapCtx := newMapContext(mlen)
|
||||||
pos = append(pos, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
posPtr := unsafe.Pointer(&pos)
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(mapCtx))
|
||||||
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
store(ctxptr, code.end.mapPos, uintptr(unsafe.Pointer(mapCtx)))
|
||||||
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
|
||||||
}
|
}
|
||||||
code = code.next
|
code = code.next
|
||||||
} else {
|
} else {
|
||||||
|
@ -469,8 +449,8 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ptr := load(ctxptr, code.end.mapPos)
|
ptr := load(ctxptr, code.end.mapPos)
|
||||||
posPtr := (*[]int)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)))
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
*posPtr = append(*posPtr, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
if idx < length {
|
if idx < length {
|
||||||
ptr := load(ctxptr, code.mapIter)
|
ptr := load(ctxptr, code.mapIter)
|
||||||
iter := e.ptrToUnsafePtr(ptr)
|
iter := e.ptrToUnsafePtr(ptr)
|
||||||
|
@ -488,8 +468,8 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
||||||
b[last] = ':'
|
b[last] = ':'
|
||||||
} else {
|
} else {
|
||||||
ptr := load(ctxptr, code.end.mapPos)
|
ptr := load(ctxptr, code.end.mapPos)
|
||||||
posPtr := (*[]int)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)))
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
*posPtr = append(*posPtr, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
}
|
}
|
||||||
ptr := load(ctxptr, code.mapIter)
|
ptr := load(ctxptr, code.mapIter)
|
||||||
iter := e.ptrToUnsafePtr(ptr)
|
iter := e.ptrToUnsafePtr(ptr)
|
||||||
|
@ -500,14 +480,9 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
||||||
case opMapEnd:
|
case opMapEnd:
|
||||||
// this operation only used by sorted map.
|
// this operation only used by sorted map.
|
||||||
length := int(load(ctxptr, code.length))
|
length := int(load(ctxptr, code.length))
|
||||||
type mapKV struct {
|
|
||||||
key string
|
|
||||||
value string
|
|
||||||
}
|
|
||||||
kvs := make([]mapKV, 0, length)
|
|
||||||
ptr := load(ctxptr, code.mapPos)
|
ptr := load(ctxptr, code.mapPos)
|
||||||
posPtr := e.ptrToUnsafePtr(ptr)
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
pos := *(*[]int)(posPtr)
|
pos := mapCtx.pos
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < length; i++ {
|
||||||
startKey := pos[i*2]
|
startKey := pos[i*2]
|
||||||
startValue := pos[i*2+1]
|
startValue := pos[i*2+1]
|
||||||
|
@ -517,25 +492,24 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte
|
||||||
} else {
|
} else {
|
||||||
endValue = len(b)
|
endValue = len(b)
|
||||||
}
|
}
|
||||||
kvs = append(kvs, mapKV{
|
mapCtx.slice.items = append(mapCtx.slice.items, mapItem{
|
||||||
key: string(b[startKey:startValue]),
|
key: b[startKey:startValue],
|
||||||
value: string(b[startValue:endValue]),
|
value: b[startValue:endValue],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sort.Slice(kvs, func(i, j int) bool {
|
sort.Sort(mapCtx.slice)
|
||||||
return kvs[i].key < kvs[j].key
|
buf := mapCtx.buf
|
||||||
})
|
for _, item := range mapCtx.slice.items {
|
||||||
buf := b[pos[0]:]
|
buf = append(buf, item.key...)
|
||||||
buf = buf[:0]
|
|
||||||
for _, kv := range kvs {
|
|
||||||
buf = append(buf, []byte(kv.key)...)
|
|
||||||
buf[len(buf)-1] = ':'
|
buf[len(buf)-1] = ':'
|
||||||
buf = append(buf, []byte(kv.value)...)
|
buf = append(buf, item.value...)
|
||||||
}
|
}
|
||||||
buf[len(buf)-1] = '}'
|
buf[len(buf)-1] = '}'
|
||||||
buf = append(buf, ',')
|
buf = append(buf, ',')
|
||||||
b = b[:pos[0]]
|
b = b[:pos[0]]
|
||||||
b = append(b, buf...)
|
b = append(b, buf...)
|
||||||
|
mapCtx.buf = buf
|
||||||
|
releaseMapContext(mapCtx)
|
||||||
code = code.next
|
code = code.next
|
||||||
case opStructFieldPtrAnonymousHeadRecursive:
|
case opStructFieldPtrAnonymousHeadRecursive:
|
||||||
store(ctxptr, code.idx, e.ptrToPtr(load(ctxptr, code.idx)))
|
store(ctxptr, code.idx, e.ptrToPtr(load(ctxptr, code.idx)))
|
||||||
|
|
|
@ -11,11 +11,13 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *Encoder) runEscaped(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte, error) {
|
func (e *Encoder) runEscaped(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ([]byte, error) {
|
||||||
recursiveLevel := 0
|
recursiveLevel := 0
|
||||||
ptrOffset := uintptr(0)
|
ptrOffset := uintptr(0)
|
||||||
ctxptr := ctx.ptr()
|
ctxptr := ctx.ptr()
|
||||||
|
|
||||||
|
code := codeSet.code
|
||||||
|
|
||||||
for {
|
for {
|
||||||
switch code.op {
|
switch code.op {
|
||||||
default:
|
default:
|
||||||
|
@ -168,65 +170,46 @@ func (e *Encoder) runEscaped(ctx *encodeRuntimeContext, b []byte, code *opcode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.seenPtr = append(ctx.seenPtr, ptr)
|
ctx.seenPtr = append(ctx.seenPtr, ptr)
|
||||||
v := e.ptrToInterface(code, ptr)
|
iface := (*interfaceHeader)(e.ptrToUnsafePtr(ptr))
|
||||||
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(&v))
|
if iface == nil || iface.ptr == nil {
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
if rv.IsNil() {
|
|
||||||
b = encodeNull(b)
|
b = encodeNull(b)
|
||||||
b = encodeComma(b)
|
b = encodeComma(b)
|
||||||
code = code.next
|
code = code.next
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
vv := rv.Interface()
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(iface))
|
||||||
header := (*interfaceHeader)(unsafe.Pointer(&vv))
|
ifaceCodeSet, err := e.compileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
|
||||||
if header.typ.Kind() == reflect.Ptr {
|
|
||||||
if rv.Elem().IsNil() {
|
|
||||||
b = encodeNull(b)
|
|
||||||
b = encodeComma(b)
|
|
||||||
code = code.next
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c, err := e.compileHead(&encodeCompileContext{
|
|
||||||
typ: header.typ,
|
|
||||||
root: code.root,
|
|
||||||
indent: code.indent,
|
|
||||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
beforeLastCode := c.beforeLastCode()
|
|
||||||
lastCode := beforeLastCode.next
|
totalLength := uintptr(codeSet.codeLength)
|
||||||
lastCode.idx = beforeLastCode.idx + uintptrSize
|
nextTotalLength := uintptr(ifaceCodeSet.codeLength)
|
||||||
totalLength := uintptr(code.totalLength())
|
|
||||||
nextTotalLength := uintptr(c.totalLength())
|
|
||||||
curlen := uintptr(len(ctx.ptrs))
|
curlen := uintptr(len(ctx.ptrs))
|
||||||
offsetNum := ptrOffset / uintptrSize
|
offsetNum := ptrOffset / uintptrSize
|
||||||
oldOffset := ptrOffset
|
|
||||||
ptrOffset += totalLength * uintptrSize
|
|
||||||
|
|
||||||
newLen := offsetNum + totalLength + nextTotalLength
|
newLen := offsetNum + totalLength + nextTotalLength
|
||||||
if curlen < newLen {
|
if curlen < newLen {
|
||||||
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
|
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
|
||||||
}
|
}
|
||||||
ctxptr = ctx.ptr() + ptrOffset // assign new ctxptr
|
oldPtrs := ctx.ptrs
|
||||||
|
|
||||||
store(ctxptr, 0, uintptr(header.ptr))
|
newPtrs := ctx.ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
|
||||||
store(ctxptr, lastCode.idx, oldOffset)
|
newPtrs[0] = uintptr(iface.ptr)
|
||||||
|
|
||||||
// link lastCode ( opInterfaceEnd ) => code.next
|
ctx.ptrs = newPtrs
|
||||||
lastCode.op = opInterfaceEnd
|
|
||||||
lastCode.next = code.next
|
|
||||||
|
|
||||||
code = c
|
bb, err := e.runEscaped(ctx, b, ifaceCodeSet)
|
||||||
recursiveLevel++
|
if err != nil {
|
||||||
case opInterfaceEnd:
|
return nil, err
|
||||||
recursiveLevel--
|
}
|
||||||
// restore ctxptr
|
|
||||||
offset := load(ctxptr, code.idx)
|
ctx.ptrs = oldPtrs
|
||||||
ctxptr = ctx.ptr() + offset
|
ctxptr = ctx.ptr()
|
||||||
ptrOffset = offset
|
ctx.seenPtr = ctx.seenPtr[:len(ctx.seenPtr)-1]
|
||||||
|
|
||||||
|
b = bb
|
||||||
code = code.next
|
code = code.next
|
||||||
case opMarshalJSON:
|
case opMarshalJSON:
|
||||||
ptr := load(ctxptr, code.idx)
|
ptr := load(ctxptr, code.idx)
|
||||||
|
@ -358,11 +341,10 @@ func (e *Encoder) runEscaped(ctx *encodeRuntimeContext, b []byte, code *opcode)
|
||||||
store(ctxptr, code.length, uintptr(mlen))
|
store(ctxptr, code.length, uintptr(mlen))
|
||||||
store(ctxptr, code.mapIter, uintptr(iter))
|
store(ctxptr, code.mapIter, uintptr(iter))
|
||||||
if !e.unorderedMap {
|
if !e.unorderedMap {
|
||||||
pos := make([]int, 0, mlen)
|
mapCtx := newMapContext(mlen)
|
||||||
pos = append(pos, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
posPtr := unsafe.Pointer(&pos)
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(mapCtx))
|
||||||
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
store(ctxptr, code.end.mapPos, uintptr(unsafe.Pointer(mapCtx)))
|
||||||
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
|
||||||
}
|
}
|
||||||
key := mapiterkey(iter)
|
key := mapiterkey(iter)
|
||||||
store(ctxptr, code.next.idx, uintptr(key))
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
@ -399,11 +381,9 @@ func (e *Encoder) runEscaped(ctx *encodeRuntimeContext, b []byte, code *opcode)
|
||||||
key := mapiterkey(iter)
|
key := mapiterkey(iter)
|
||||||
store(ctxptr, code.next.idx, uintptr(key))
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
if !e.unorderedMap {
|
if !e.unorderedMap {
|
||||||
pos := make([]int, 0, mlen)
|
mapCtx := newMapContext(mlen)
|
||||||
pos = append(pos, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
posPtr := unsafe.Pointer(&pos)
|
store(ctxptr, code.end.mapPos, uintptr(unsafe.Pointer(mapCtx)))
|
||||||
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
|
||||||
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
|
||||||
}
|
}
|
||||||
code = code.next
|
code = code.next
|
||||||
} else {
|
} else {
|
||||||
|
@ -431,8 +411,8 @@ func (e *Encoder) runEscaped(ctx *encodeRuntimeContext, b []byte, code *opcode)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ptr := load(ctxptr, code.end.mapPos)
|
ptr := load(ctxptr, code.end.mapPos)
|
||||||
posPtr := (*[]int)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)))
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
*posPtr = append(*posPtr, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
if idx < length {
|
if idx < length {
|
||||||
ptr := load(ctxptr, code.mapIter)
|
ptr := load(ctxptr, code.mapIter)
|
||||||
iter := e.ptrToUnsafePtr(ptr)
|
iter := e.ptrToUnsafePtr(ptr)
|
||||||
|
@ -450,8 +430,8 @@ func (e *Encoder) runEscaped(ctx *encodeRuntimeContext, b []byte, code *opcode)
|
||||||
b[last] = ':'
|
b[last] = ':'
|
||||||
} else {
|
} else {
|
||||||
ptr := load(ctxptr, code.end.mapPos)
|
ptr := load(ctxptr, code.end.mapPos)
|
||||||
posPtr := (*[]int)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)))
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
*posPtr = append(*posPtr, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
}
|
}
|
||||||
ptr := load(ctxptr, code.mapIter)
|
ptr := load(ctxptr, code.mapIter)
|
||||||
iter := e.ptrToUnsafePtr(ptr)
|
iter := e.ptrToUnsafePtr(ptr)
|
||||||
|
@ -462,14 +442,9 @@ func (e *Encoder) runEscaped(ctx *encodeRuntimeContext, b []byte, code *opcode)
|
||||||
case opMapEnd:
|
case opMapEnd:
|
||||||
// this operation only used by sorted map.
|
// this operation only used by sorted map.
|
||||||
length := int(load(ctxptr, code.length))
|
length := int(load(ctxptr, code.length))
|
||||||
type mapKV struct {
|
|
||||||
key string
|
|
||||||
value string
|
|
||||||
}
|
|
||||||
kvs := make([]mapKV, 0, length)
|
|
||||||
ptr := load(ctxptr, code.mapPos)
|
ptr := load(ctxptr, code.mapPos)
|
||||||
posPtr := e.ptrToUnsafePtr(ptr)
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
pos := *(*[]int)(posPtr)
|
pos := mapCtx.pos
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < length; i++ {
|
||||||
startKey := pos[i*2]
|
startKey := pos[i*2]
|
||||||
startValue := pos[i*2+1]
|
startValue := pos[i*2+1]
|
||||||
|
@ -479,25 +454,24 @@ func (e *Encoder) runEscaped(ctx *encodeRuntimeContext, b []byte, code *opcode)
|
||||||
} else {
|
} else {
|
||||||
endValue = len(b)
|
endValue = len(b)
|
||||||
}
|
}
|
||||||
kvs = append(kvs, mapKV{
|
mapCtx.slice.items = append(mapCtx.slice.items, mapItem{
|
||||||
key: string(b[startKey:startValue]),
|
key: b[startKey:startValue],
|
||||||
value: string(b[startValue:endValue]),
|
value: b[startValue:endValue],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sort.Slice(kvs, func(i, j int) bool {
|
sort.Sort(mapCtx.slice)
|
||||||
return kvs[i].key < kvs[j].key
|
buf := mapCtx.buf
|
||||||
})
|
for _, item := range mapCtx.slice.items {
|
||||||
buf := b[pos[0]:]
|
buf = append(buf, item.key...)
|
||||||
buf = buf[:0]
|
|
||||||
for _, kv := range kvs {
|
|
||||||
buf = append(buf, []byte(kv.key)...)
|
|
||||||
buf[len(buf)-1] = ':'
|
buf[len(buf)-1] = ':'
|
||||||
buf = append(buf, []byte(kv.value)...)
|
buf = append(buf, item.value...)
|
||||||
}
|
}
|
||||||
buf[len(buf)-1] = '}'
|
buf[len(buf)-1] = '}'
|
||||||
buf = append(buf, ',')
|
buf = append(buf, ',')
|
||||||
b = b[:pos[0]]
|
b = b[:pos[0]]
|
||||||
b = append(b, buf...)
|
b = append(b, buf...)
|
||||||
|
mapCtx.buf = buf
|
||||||
|
releaseMapContext(mapCtx)
|
||||||
code = code.next
|
code = code.next
|
||||||
case opStructFieldPtrAnonymousHeadRecursive:
|
case opStructFieldPtrAnonymousHeadRecursive:
|
||||||
store(ctxptr, code.idx, e.ptrToPtr(load(ctxptr, code.idx)))
|
store(ctxptr, code.idx, e.ptrToPtr(load(ctxptr, code.idx)))
|
||||||
|
|
|
@ -11,11 +11,10 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte, error) {
|
func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ([]byte, error) {
|
||||||
recursiveLevel := 0
|
|
||||||
var seenPtr map[uintptr]struct{}
|
|
||||||
ptrOffset := uintptr(0)
|
ptrOffset := uintptr(0)
|
||||||
ctxptr := ctx.ptr()
|
ctxptr := ctx.ptr()
|
||||||
|
code := codeSet.code
|
||||||
|
|
||||||
for {
|
for {
|
||||||
switch code.op {
|
switch code.op {
|
||||||
|
@ -163,83 +162,55 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, code *op
|
||||||
code = code.next
|
code = code.next
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if seenPtr == nil {
|
for _, seen := range ctx.seenPtr {
|
||||||
seenPtr = map[uintptr]struct{}{}
|
if ptr == seen {
|
||||||
|
return nil, errUnsupportedValue(code, ptr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if _, exists := seenPtr[ptr]; exists {
|
ctx.seenPtr = append(ctx.seenPtr, ptr)
|
||||||
return nil, errUnsupportedValue(code, ptr)
|
iface := (*interfaceHeader)(e.ptrToUnsafePtr(ptr))
|
||||||
}
|
if iface == nil || iface.ptr == nil {
|
||||||
seenPtr[ptr] = struct{}{}
|
|
||||||
v := e.ptrToInterface(code, ptr)
|
|
||||||
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(&v))
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
if rv.IsNil() {
|
|
||||||
b = encodeNull(b)
|
b = encodeNull(b)
|
||||||
b = encodeIndentComma(b)
|
b = encodeIndentComma(b)
|
||||||
code = code.next
|
code = code.next
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
vv := rv.Interface()
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(iface))
|
||||||
header := (*interfaceHeader)(unsafe.Pointer(&vv))
|
ifaceCodeSet, err := e.compileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
|
||||||
typ := header.typ
|
if err != nil {
|
||||||
if typ.Kind() == reflect.Ptr {
|
return nil, err
|
||||||
typ = typ.Elem()
|
|
||||||
}
|
}
|
||||||
var c *opcode
|
|
||||||
if typ.Kind() == reflect.Map {
|
totalLength := uintptr(codeSet.codeLength)
|
||||||
code, err := e.compileMap(&encodeCompileContext{
|
nextTotalLength := uintptr(ifaceCodeSet.codeLength)
|
||||||
typ: typ,
|
|
||||||
root: code.root,
|
|
||||||
indent: code.indent,
|
|
||||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
|
||||||
}, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c = code
|
|
||||||
} else {
|
|
||||||
code, err := e.compile(&encodeCompileContext{
|
|
||||||
typ: typ,
|
|
||||||
root: code.root,
|
|
||||||
indent: code.indent,
|
|
||||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c = code
|
|
||||||
}
|
|
||||||
beforeLastCode := c.beforeLastCode()
|
|
||||||
lastCode := beforeLastCode.next
|
|
||||||
lastCode.idx = beforeLastCode.idx + uintptrSize
|
|
||||||
totalLength := uintptr(code.totalLength())
|
|
||||||
nextTotalLength := uintptr(c.totalLength())
|
|
||||||
curlen := uintptr(len(ctx.ptrs))
|
curlen := uintptr(len(ctx.ptrs))
|
||||||
offsetNum := ptrOffset / uintptrSize
|
offsetNum := ptrOffset / uintptrSize
|
||||||
oldOffset := ptrOffset
|
|
||||||
ptrOffset += totalLength * uintptrSize
|
|
||||||
|
|
||||||
newLen := offsetNum + totalLength + nextTotalLength
|
newLen := offsetNum + totalLength + nextTotalLength
|
||||||
if curlen < newLen {
|
if curlen < newLen {
|
||||||
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
|
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
|
||||||
}
|
}
|
||||||
ctxptr = ctx.ptr() + ptrOffset // assign new ctxptr
|
oldPtrs := ctx.ptrs
|
||||||
|
|
||||||
store(ctxptr, 0, uintptr(header.ptr))
|
newPtrs := ctx.ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
|
||||||
store(ctxptr, lastCode.idx, oldOffset)
|
newPtrs[0] = uintptr(iface.ptr)
|
||||||
|
|
||||||
// link lastCode ( opInterfaceEnd ) => code.next
|
ctx.ptrs = newPtrs
|
||||||
lastCode.op = opInterfaceEnd
|
|
||||||
lastCode.next = code.next
|
|
||||||
|
|
||||||
code = c
|
oldBaseIndent := e.baseIndent
|
||||||
recursiveLevel++
|
e.baseIndent = code.indent
|
||||||
case opInterfaceEnd:
|
bb, err := e.runEscapedIndent(ctx, b, ifaceCodeSet)
|
||||||
recursiveLevel--
|
if err != nil {
|
||||||
// restore ctxptr
|
return nil, err
|
||||||
offset := load(ctxptr, code.idx)
|
}
|
||||||
ctxptr = ctx.ptr() + offset
|
e.baseIndent = oldBaseIndent
|
||||||
ptrOffset = offset
|
|
||||||
|
ctx.ptrs = oldPtrs
|
||||||
|
ctxptr = ctx.ptr()
|
||||||
|
ctx.seenPtr = ctx.seenPtr[:len(ctx.seenPtr)-1]
|
||||||
|
|
||||||
|
b = bb
|
||||||
code = code.next
|
code = code.next
|
||||||
case opMarshalJSON:
|
case opMarshalJSON:
|
||||||
ptr := load(ctxptr, code.idx)
|
ptr := load(ctxptr, code.idx)
|
||||||
|
@ -265,7 +236,7 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, code *op
|
||||||
if err := encodeWithIndent(
|
if err := encodeWithIndent(
|
||||||
&buf,
|
&buf,
|
||||||
bb,
|
bb,
|
||||||
string(e.prefix)+string(bytes.Repeat(e.indentStr, code.indent)),
|
string(e.prefix)+string(bytes.Repeat(e.indentStr, e.baseIndent+code.indent)),
|
||||||
string(e.indentStr),
|
string(e.indentStr),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -434,11 +405,10 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, code *op
|
||||||
store(ctxptr, code.mapIter, uintptr(iter))
|
store(ctxptr, code.mapIter, uintptr(iter))
|
||||||
|
|
||||||
if !e.unorderedMap {
|
if !e.unorderedMap {
|
||||||
pos := make([]int, 0, mlen)
|
mapCtx := newMapContext(mlen)
|
||||||
pos = append(pos, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
posPtr := unsafe.Pointer(&pos)
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(mapCtx))
|
||||||
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
store(ctxptr, code.end.mapPos, uintptr(unsafe.Pointer(mapCtx)))
|
||||||
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
|
||||||
} else {
|
} else {
|
||||||
b = e.encodeIndent(b, code.next.indent)
|
b = e.encodeIndent(b, code.next.indent)
|
||||||
}
|
}
|
||||||
|
@ -481,11 +451,10 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, code *op
|
||||||
store(ctxptr, code.next.idx, uintptr(key))
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
|
||||||
if !e.unorderedMap {
|
if !e.unorderedMap {
|
||||||
pos := make([]int, 0, mlen)
|
mapCtx := newMapContext(mlen)
|
||||||
pos = append(pos, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
posPtr := unsafe.Pointer(&pos)
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(mapCtx))
|
||||||
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
store(ctxptr, code.end.mapPos, uintptr(unsafe.Pointer(mapCtx)))
|
||||||
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
|
||||||
} else {
|
} else {
|
||||||
b = e.encodeIndent(b, code.next.indent)
|
b = e.encodeIndent(b, code.next.indent)
|
||||||
}
|
}
|
||||||
|
@ -519,8 +488,8 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, code *op
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ptr := load(ctxptr, code.end.mapPos)
|
ptr := load(ctxptr, code.end.mapPos)
|
||||||
posPtr := (*[]int)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)))
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
*posPtr = append(*posPtr, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
if idx < length {
|
if idx < length {
|
||||||
ptr := load(ctxptr, code.mapIter)
|
ptr := load(ctxptr, code.mapIter)
|
||||||
iter := e.ptrToUnsafePtr(ptr)
|
iter := e.ptrToUnsafePtr(ptr)
|
||||||
|
@ -537,8 +506,8 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, code *op
|
||||||
b = append(b, ':', ' ')
|
b = append(b, ':', ' ')
|
||||||
} else {
|
} else {
|
||||||
ptr := load(ctxptr, code.end.mapPos)
|
ptr := load(ctxptr, code.end.mapPos)
|
||||||
posPtr := (*[]int)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)))
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
*posPtr = append(*posPtr, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
}
|
}
|
||||||
ptr := load(ctxptr, code.mapIter)
|
ptr := load(ctxptr, code.mapIter)
|
||||||
iter := e.ptrToUnsafePtr(ptr)
|
iter := e.ptrToUnsafePtr(ptr)
|
||||||
|
@ -549,13 +518,9 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, code *op
|
||||||
case opMapEnd:
|
case opMapEnd:
|
||||||
// this operation only used by sorted map
|
// this operation only used by sorted map
|
||||||
length := int(load(ctxptr, code.length))
|
length := int(load(ctxptr, code.length))
|
||||||
type mapKV struct {
|
|
||||||
key string
|
|
||||||
value string
|
|
||||||
}
|
|
||||||
kvs := make([]mapKV, 0, length)
|
|
||||||
ptr := load(ctxptr, code.mapPos)
|
ptr := load(ctxptr, code.mapPos)
|
||||||
pos := *(*[]int)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)))
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
|
pos := mapCtx.pos
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < length; i++ {
|
||||||
startKey := pos[i*2]
|
startKey := pos[i*2]
|
||||||
startValue := pos[i*2+1]
|
startValue := pos[i*2+1]
|
||||||
|
@ -565,32 +530,31 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, code *op
|
||||||
} else {
|
} else {
|
||||||
endValue = len(b)
|
endValue = len(b)
|
||||||
}
|
}
|
||||||
kvs = append(kvs, mapKV{
|
mapCtx.slice.items = append(mapCtx.slice.items, mapItem{
|
||||||
key: string(b[startKey:startValue]),
|
key: b[startKey:startValue],
|
||||||
value: string(b[startValue:endValue]),
|
value: b[startValue:endValue],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sort.Slice(kvs, func(i, j int) bool {
|
sort.Sort(mapCtx.slice)
|
||||||
return kvs[i].key < kvs[j].key
|
buf := mapCtx.buf
|
||||||
})
|
for _, item := range mapCtx.slice.items {
|
||||||
buf := b[pos[0]:]
|
|
||||||
buf = buf[:0]
|
|
||||||
for _, kv := range kvs {
|
|
||||||
buf = append(buf, e.prefix...)
|
buf = append(buf, e.prefix...)
|
||||||
buf = append(buf, bytes.Repeat(e.indentStr, code.indent+1)...)
|
buf = append(buf, bytes.Repeat(e.indentStr, e.baseIndent+code.indent+1)...)
|
||||||
|
buf = append(buf, item.key...)
|
||||||
buf = append(buf, []byte(kv.key)...)
|
|
||||||
buf[len(buf)-2] = ':'
|
buf[len(buf)-2] = ':'
|
||||||
buf[len(buf)-1] = ' '
|
buf[len(buf)-1] = ' '
|
||||||
buf = append(buf, []byte(kv.value)...)
|
buf = append(buf, item.value...)
|
||||||
}
|
}
|
||||||
buf = buf[:len(buf)-2]
|
buf = buf[:len(buf)-2]
|
||||||
buf = append(buf, '\n')
|
buf = append(buf, '\n')
|
||||||
buf = append(buf, e.prefix...)
|
buf = append(buf, e.prefix...)
|
||||||
buf = append(buf, bytes.Repeat(e.indentStr, code.indent)...)
|
buf = append(buf, bytes.Repeat(e.indentStr, e.baseIndent+code.indent)...)
|
||||||
buf = append(buf, '}', ',', '\n')
|
buf = append(buf, '}', ',', '\n')
|
||||||
|
|
||||||
b = b[:pos[0]]
|
b = b[:pos[0]]
|
||||||
b = append(b, buf...)
|
b = append(b, buf...)
|
||||||
|
mapCtx.buf = buf
|
||||||
|
releaseMapContext(mapCtx)
|
||||||
code = code.next
|
code = code.next
|
||||||
case opStructFieldPtrHead:
|
case opStructFieldPtrHead:
|
||||||
p := load(ctxptr, code.idx)
|
p := load(ctxptr, code.idx)
|
||||||
|
|
|
@ -11,11 +11,10 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte, error) {
|
func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet) ([]byte, error) {
|
||||||
recursiveLevel := 0
|
|
||||||
var seenPtr map[uintptr]struct{}
|
|
||||||
ptrOffset := uintptr(0)
|
ptrOffset := uintptr(0)
|
||||||
ctxptr := ctx.ptr()
|
ctxptr := ctx.ptr()
|
||||||
|
code := codeSet.code
|
||||||
|
|
||||||
for {
|
for {
|
||||||
switch code.op {
|
switch code.op {
|
||||||
|
@ -163,83 +162,55 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) (
|
||||||
code = code.next
|
code = code.next
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if seenPtr == nil {
|
for _, seen := range ctx.seenPtr {
|
||||||
seenPtr = map[uintptr]struct{}{}
|
if ptr == seen {
|
||||||
|
return nil, errUnsupportedValue(code, ptr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if _, exists := seenPtr[ptr]; exists {
|
ctx.seenPtr = append(ctx.seenPtr, ptr)
|
||||||
return nil, errUnsupportedValue(code, ptr)
|
iface := (*interfaceHeader)(e.ptrToUnsafePtr(ptr))
|
||||||
}
|
if iface == nil || iface.ptr == nil {
|
||||||
seenPtr[ptr] = struct{}{}
|
|
||||||
v := e.ptrToInterface(code, ptr)
|
|
||||||
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(&v))
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
if rv.IsNil() {
|
|
||||||
b = encodeNull(b)
|
b = encodeNull(b)
|
||||||
b = encodeIndentComma(b)
|
b = encodeIndentComma(b)
|
||||||
code = code.next
|
code = code.next
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
vv := rv.Interface()
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(iface))
|
||||||
header := (*interfaceHeader)(unsafe.Pointer(&vv))
|
ifaceCodeSet, err := e.compileToGetCodeSet(uintptr(unsafe.Pointer(iface.typ)))
|
||||||
typ := header.typ
|
if err != nil {
|
||||||
if typ.Kind() == reflect.Ptr {
|
return nil, err
|
||||||
typ = typ.Elem()
|
|
||||||
}
|
}
|
||||||
var c *opcode
|
|
||||||
if typ.Kind() == reflect.Map {
|
totalLength := uintptr(codeSet.codeLength)
|
||||||
code, err := e.compileMap(&encodeCompileContext{
|
nextTotalLength := uintptr(ifaceCodeSet.codeLength)
|
||||||
typ: typ,
|
|
||||||
root: code.root,
|
|
||||||
indent: code.indent,
|
|
||||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
|
||||||
}, false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c = code
|
|
||||||
} else {
|
|
||||||
code, err := e.compile(&encodeCompileContext{
|
|
||||||
typ: typ,
|
|
||||||
root: code.root,
|
|
||||||
indent: code.indent,
|
|
||||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c = code
|
|
||||||
}
|
|
||||||
beforeLastCode := c.beforeLastCode()
|
|
||||||
lastCode := beforeLastCode.next
|
|
||||||
lastCode.idx = beforeLastCode.idx + uintptrSize
|
|
||||||
totalLength := uintptr(code.totalLength())
|
|
||||||
nextTotalLength := uintptr(c.totalLength())
|
|
||||||
curlen := uintptr(len(ctx.ptrs))
|
curlen := uintptr(len(ctx.ptrs))
|
||||||
offsetNum := ptrOffset / uintptrSize
|
offsetNum := ptrOffset / uintptrSize
|
||||||
oldOffset := ptrOffset
|
|
||||||
ptrOffset += totalLength * uintptrSize
|
|
||||||
|
|
||||||
newLen := offsetNum + totalLength + nextTotalLength
|
newLen := offsetNum + totalLength + nextTotalLength
|
||||||
if curlen < newLen {
|
if curlen < newLen {
|
||||||
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
|
ctx.ptrs = append(ctx.ptrs, make([]uintptr, newLen-curlen)...)
|
||||||
}
|
}
|
||||||
ctxptr = ctx.ptr() + ptrOffset // assign new ctxptr
|
oldPtrs := ctx.ptrs
|
||||||
|
|
||||||
store(ctxptr, 0, uintptr(header.ptr))
|
newPtrs := ctx.ptrs[(ptrOffset+totalLength*uintptrSize)/uintptrSize:]
|
||||||
store(ctxptr, lastCode.idx, oldOffset)
|
newPtrs[0] = uintptr(iface.ptr)
|
||||||
|
|
||||||
// link lastCode ( opInterfaceEnd ) => code.next
|
ctx.ptrs = newPtrs
|
||||||
lastCode.op = opInterfaceEnd
|
|
||||||
lastCode.next = code.next
|
|
||||||
|
|
||||||
code = c
|
oldBaseIndent := e.baseIndent
|
||||||
recursiveLevel++
|
e.baseIndent = code.indent
|
||||||
case opInterfaceEnd:
|
bb, err := e.runIndent(ctx, b, ifaceCodeSet)
|
||||||
recursiveLevel--
|
if err != nil {
|
||||||
// restore ctxptr
|
return nil, err
|
||||||
offset := load(ctxptr, code.idx)
|
}
|
||||||
ctxptr = ctx.ptr() + offset
|
e.baseIndent = oldBaseIndent
|
||||||
ptrOffset = offset
|
|
||||||
|
ctx.ptrs = oldPtrs
|
||||||
|
ctxptr = ctx.ptr()
|
||||||
|
ctx.seenPtr = ctx.seenPtr[:len(ctx.seenPtr)-1]
|
||||||
|
|
||||||
|
b = bb
|
||||||
code = code.next
|
code = code.next
|
||||||
case opMarshalJSON:
|
case opMarshalJSON:
|
||||||
ptr := load(ctxptr, code.idx)
|
ptr := load(ctxptr, code.idx)
|
||||||
|
@ -265,7 +236,7 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) (
|
||||||
if err := encodeWithIndent(
|
if err := encodeWithIndent(
|
||||||
&buf,
|
&buf,
|
||||||
bb,
|
bb,
|
||||||
string(e.prefix)+string(bytes.Repeat(e.indentStr, code.indent)),
|
string(e.prefix)+string(bytes.Repeat(e.indentStr, e.baseIndent+code.indent)),
|
||||||
string(e.indentStr),
|
string(e.indentStr),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -434,11 +405,10 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) (
|
||||||
store(ctxptr, code.mapIter, uintptr(iter))
|
store(ctxptr, code.mapIter, uintptr(iter))
|
||||||
|
|
||||||
if !e.unorderedMap {
|
if !e.unorderedMap {
|
||||||
pos := make([]int, 0, mlen)
|
mapCtx := newMapContext(mlen)
|
||||||
pos = append(pos, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
posPtr := unsafe.Pointer(&pos)
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(mapCtx))
|
||||||
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
store(ctxptr, code.end.mapPos, uintptr(unsafe.Pointer(mapCtx)))
|
||||||
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
|
||||||
} else {
|
} else {
|
||||||
b = e.encodeIndent(b, code.next.indent)
|
b = e.encodeIndent(b, code.next.indent)
|
||||||
}
|
}
|
||||||
|
@ -481,11 +451,10 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) (
|
||||||
store(ctxptr, code.next.idx, uintptr(key))
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
|
||||||
if !e.unorderedMap {
|
if !e.unorderedMap {
|
||||||
pos := make([]int, 0, mlen)
|
mapCtx := newMapContext(mlen)
|
||||||
pos = append(pos, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
posPtr := unsafe.Pointer(&pos)
|
ctx.keepRefs = append(ctx.keepRefs, unsafe.Pointer(mapCtx))
|
||||||
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
store(ctxptr, code.end.mapPos, uintptr(unsafe.Pointer(mapCtx)))
|
||||||
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
|
||||||
} else {
|
} else {
|
||||||
b = e.encodeIndent(b, code.next.indent)
|
b = e.encodeIndent(b, code.next.indent)
|
||||||
}
|
}
|
||||||
|
@ -519,8 +488,8 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) (
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ptr := load(ctxptr, code.end.mapPos)
|
ptr := load(ctxptr, code.end.mapPos)
|
||||||
posPtr := (*[]int)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)))
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
*posPtr = append(*posPtr, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
if idx < length {
|
if idx < length {
|
||||||
ptr := load(ctxptr, code.mapIter)
|
ptr := load(ctxptr, code.mapIter)
|
||||||
iter := e.ptrToUnsafePtr(ptr)
|
iter := e.ptrToUnsafePtr(ptr)
|
||||||
|
@ -537,8 +506,8 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) (
|
||||||
b = append(b, ':', ' ')
|
b = append(b, ':', ' ')
|
||||||
} else {
|
} else {
|
||||||
ptr := load(ctxptr, code.end.mapPos)
|
ptr := load(ctxptr, code.end.mapPos)
|
||||||
posPtr := (*[]int)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)))
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
*posPtr = append(*posPtr, len(b))
|
mapCtx.pos = append(mapCtx.pos, len(b))
|
||||||
}
|
}
|
||||||
ptr := load(ctxptr, code.mapIter)
|
ptr := load(ctxptr, code.mapIter)
|
||||||
iter := e.ptrToUnsafePtr(ptr)
|
iter := e.ptrToUnsafePtr(ptr)
|
||||||
|
@ -549,13 +518,9 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) (
|
||||||
case opMapEnd:
|
case opMapEnd:
|
||||||
// this operation only used by sorted map
|
// this operation only used by sorted map
|
||||||
length := int(load(ctxptr, code.length))
|
length := int(load(ctxptr, code.length))
|
||||||
type mapKV struct {
|
|
||||||
key string
|
|
||||||
value string
|
|
||||||
}
|
|
||||||
kvs := make([]mapKV, 0, length)
|
|
||||||
ptr := load(ctxptr, code.mapPos)
|
ptr := load(ctxptr, code.mapPos)
|
||||||
pos := *(*[]int)(*(*unsafe.Pointer)(unsafe.Pointer(&ptr)))
|
mapCtx := (*encodeMapContext)(e.ptrToUnsafePtr(ptr))
|
||||||
|
pos := mapCtx.pos
|
||||||
for i := 0; i < length; i++ {
|
for i := 0; i < length; i++ {
|
||||||
startKey := pos[i*2]
|
startKey := pos[i*2]
|
||||||
startValue := pos[i*2+1]
|
startValue := pos[i*2+1]
|
||||||
|
@ -565,32 +530,31 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) (
|
||||||
} else {
|
} else {
|
||||||
endValue = len(b)
|
endValue = len(b)
|
||||||
}
|
}
|
||||||
kvs = append(kvs, mapKV{
|
mapCtx.slice.items = append(mapCtx.slice.items, mapItem{
|
||||||
key: string(b[startKey:startValue]),
|
key: b[startKey:startValue],
|
||||||
value: string(b[startValue:endValue]),
|
value: b[startValue:endValue],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sort.Slice(kvs, func(i, j int) bool {
|
sort.Sort(mapCtx.slice)
|
||||||
return kvs[i].key < kvs[j].key
|
buf := mapCtx.buf
|
||||||
})
|
for _, item := range mapCtx.slice.items {
|
||||||
buf := b[pos[0]:]
|
|
||||||
buf = buf[:0]
|
|
||||||
for _, kv := range kvs {
|
|
||||||
buf = append(buf, e.prefix...)
|
buf = append(buf, e.prefix...)
|
||||||
buf = append(buf, bytes.Repeat(e.indentStr, code.indent+1)...)
|
buf = append(buf, bytes.Repeat(e.indentStr, e.baseIndent+code.indent+1)...)
|
||||||
|
buf = append(buf, item.key...)
|
||||||
buf = append(buf, []byte(kv.key)...)
|
|
||||||
buf[len(buf)-2] = ':'
|
buf[len(buf)-2] = ':'
|
||||||
buf[len(buf)-1] = ' '
|
buf[len(buf)-1] = ' '
|
||||||
buf = append(buf, []byte(kv.value)...)
|
buf = append(buf, item.value...)
|
||||||
}
|
}
|
||||||
buf = buf[:len(buf)-2]
|
buf = buf[:len(buf)-2]
|
||||||
buf = append(buf, '\n')
|
buf = append(buf, '\n')
|
||||||
buf = append(buf, e.prefix...)
|
buf = append(buf, e.prefix...)
|
||||||
buf = append(buf, bytes.Repeat(e.indentStr, code.indent)...)
|
buf = append(buf, bytes.Repeat(e.indentStr, e.baseIndent+code.indent)...)
|
||||||
buf = append(buf, '}', ',', '\n')
|
buf = append(buf, '}', ',', '\n')
|
||||||
|
|
||||||
b = b[:pos[0]]
|
b = b[:pos[0]]
|
||||||
b = append(b, buf...)
|
b = append(b, buf...)
|
||||||
|
mapCtx.buf = buf
|
||||||
|
releaseMapContext(mapCtx)
|
||||||
code = code.next
|
code = code.next
|
||||||
case opStructFieldPtrHead:
|
case opStructFieldPtrHead:
|
||||||
p := load(ctxptr, code.idx)
|
p := load(ctxptr, code.idx)
|
||||||
|
|
Loading…
Reference in New Issue