Merge pull request #97 from goccy/feature/fix-interface

Fix interface operation and optimize map operation
This commit is contained in:
Masaaki Goshima 2021-01-25 13:22:30 +09:00 committed by GitHub
commit 72e9fa8193
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 3143 additions and 3150 deletions

View File

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

View File

@ -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"),

View File

@ -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)...)
} }

View File

@ -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

View File

@ -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() {

File diff suppressed because it is too large Load Diff

View File

@ -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)))

View File

@ -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)))

View File

@ -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)

View File

@ -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)