mirror of https://github.com/goccy/go-json.git
Supported sorted map
This commit is contained in:
parent
5c3efd11af
commit
898d58b8b8
|
@ -204,6 +204,7 @@ func (t opType) fieldToStringTagField() opType {
|
||||||
"MapHead",
|
"MapHead",
|
||||||
"MapKey",
|
"MapKey",
|
||||||
"MapValue",
|
"MapValue",
|
||||||
|
"MapEnd",
|
||||||
"StructFieldRecursive",
|
"StructFieldRecursive",
|
||||||
"StructField",
|
"StructField",
|
||||||
}
|
}
|
||||||
|
@ -243,7 +244,7 @@ func (t opType) fieldToStringTagField() opType {
|
||||||
{"SortedMapKey", "SortedMapKeyIndent", "MapKey"},
|
{"SortedMapKey", "SortedMapKeyIndent", "MapKey"},
|
||||||
{"SortedRootMapKey", "SortedRootMapKeyIndent", "MapKey"},
|
{"SortedRootMapKey", "SortedRootMapKeyIndent", "MapKey"},
|
||||||
{"SortedMapValue", "SortedMapValueIndent", "MapValue"},
|
{"SortedMapValue", "SortedMapValueIndent", "MapValue"},
|
||||||
{"SortedMapEnd", "SortedMapEndIndent", "Op"},
|
{"SortedMapEnd", "SortedMapEndIndent", "MapEnd"},
|
||||||
{"StructFieldHead", "StructFieldHeadIndent", "StructField"},
|
{"StructFieldHead", "StructFieldHeadIndent", "StructField"},
|
||||||
{"StructFieldHeadOmitEmpty", "StructFieldHeadOmitEmptyIndent", "StructField"},
|
{"StructFieldHeadOmitEmpty", "StructFieldHeadOmitEmptyIndent", "StructField"},
|
||||||
{"StructFieldHeadStringTag", "StructFieldHeadStringTagIndent", "StructField"},
|
{"StructFieldHeadStringTag", "StructFieldHeadStringTagIndent", "StructField"},
|
||||||
|
|
|
@ -398,26 +398,15 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
|
||||||
|
|
||||||
header.mapKey = key
|
header.mapKey = key
|
||||||
header.mapValue = value
|
header.mapValue = value
|
||||||
end := newOpCode(ctx, opMapEnd)
|
|
||||||
|
end := newMapEndCode(ctx, header)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
|
|
||||||
if ctx.withIndent {
|
if ctx.withIndent {
|
||||||
if header.op == opMapHead {
|
header.op = header.op.toIndent()
|
||||||
if ctx.root {
|
key.op = key.op.toIndent()
|
||||||
header.op = opRootMapHeadIndent
|
value.op = value.op.toIndent()
|
||||||
} else {
|
end.op = end.op.toIndent()
|
||||||
header.op = opMapHeadIndent
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
header.op = opMapHeadLoadIndent
|
|
||||||
}
|
|
||||||
if ctx.root {
|
|
||||||
key.op = opRootMapKeyIndent
|
|
||||||
} else {
|
|
||||||
key.op = opMapKeyIndent
|
|
||||||
}
|
|
||||||
value.op = opMapValueIndent
|
|
||||||
end.op = opMapEndIndent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header.next = keyCode
|
header.next = keyCode
|
||||||
|
@ -428,6 +417,7 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
|
||||||
|
|
||||||
header.end = end
|
header.end = end
|
||||||
key.end = end
|
key.end = end
|
||||||
|
value.end = end
|
||||||
|
|
||||||
return (*opcode)(unsafe.Pointer(header)), nil
|
return (*opcode)(unsafe.Pointer(header)), nil
|
||||||
}
|
}
|
||||||
|
@ -462,13 +452,13 @@ func (e *Encoder) typeToHeaderType(op opType) opType {
|
||||||
return opStructFieldHeadString
|
return opStructFieldHeadString
|
||||||
case opBool:
|
case opBool:
|
||||||
return opStructFieldHeadBool
|
return opStructFieldHeadBool
|
||||||
case opMapHead:
|
case opMapHead, opSortedMapHead:
|
||||||
return opStructFieldHeadMap
|
return opStructFieldHeadMap
|
||||||
case opMapHeadLoad:
|
case opMapHeadLoad, opSortedMapHeadLoad:
|
||||||
return opStructFieldHeadMapLoad
|
return opStructFieldHeadMapLoad
|
||||||
case opMapHeadIndent:
|
case opMapHeadIndent, opSortedMapHeadIndent:
|
||||||
return opStructFieldHeadMapIndent
|
return opStructFieldHeadMapIndent
|
||||||
case opMapHeadLoadIndent:
|
case opMapHeadLoadIndent, opSortedMapHeadLoadIndent:
|
||||||
return opStructFieldHeadMapLoadIndent
|
return opStructFieldHeadMapLoadIndent
|
||||||
case opArrayHead:
|
case opArrayHead:
|
||||||
return opStructFieldHeadArray
|
return opStructFieldHeadArray
|
||||||
|
@ -520,13 +510,13 @@ func (e *Encoder) typeToFieldType(op opType) opType {
|
||||||
return opStructFieldString
|
return opStructFieldString
|
||||||
case opBool:
|
case opBool:
|
||||||
return opStructFieldBool
|
return opStructFieldBool
|
||||||
case opMapHead:
|
case opMapHead, opSortedMapHead:
|
||||||
return opStructFieldMap
|
return opStructFieldMap
|
||||||
case opMapHeadLoad:
|
case opMapHeadLoad, opSortedMapHeadLoad:
|
||||||
return opStructFieldMapLoad
|
return opStructFieldMapLoad
|
||||||
case opMapHeadIndent:
|
case opMapHeadIndent, opSortedMapHeadIndent:
|
||||||
return opStructFieldMapIndent
|
return opStructFieldMapIndent
|
||||||
case opMapHeadLoadIndent:
|
case opMapHeadLoadIndent, opSortedMapHeadLoadIndent:
|
||||||
return opStructFieldMapLoadIndent
|
return opStructFieldMapLoadIndent
|
||||||
case opArrayHead:
|
case opArrayHead:
|
||||||
return opStructFieldArray
|
return opStructFieldArray
|
||||||
|
|
|
@ -24,6 +24,8 @@ type opcode struct {
|
||||||
elemIdx uintptr // offset to access array/slice/map elem
|
elemIdx uintptr // offset to access array/slice/map elem
|
||||||
length uintptr // offset to access slice/map length or array length
|
length uintptr // offset to access slice/map length or array length
|
||||||
mapIter uintptr // offset to access map iterator
|
mapIter uintptr // offset to access map iterator
|
||||||
|
headPos uintptr // offset to access head position of map
|
||||||
|
mapPos uintptr // offset to access position list for sorted map
|
||||||
offset uintptr // offset size from struct header
|
offset uintptr // offset size from struct header
|
||||||
size uintptr // array/slice elem size
|
size uintptr // array/slice elem size
|
||||||
|
|
||||||
|
@ -87,6 +89,8 @@ func (c *opcode) copy(codeMap map[uintptr]*opcode) *opcode {
|
||||||
elemIdx: c.elemIdx,
|
elemIdx: c.elemIdx,
|
||||||
length: c.length,
|
length: c.length,
|
||||||
mapIter: c.mapIter,
|
mapIter: c.mapIter,
|
||||||
|
headPos: c.headPos,
|
||||||
|
mapPos: c.mapPos,
|
||||||
offset: c.offset,
|
offset: c.offset,
|
||||||
size: c.size,
|
size: c.size,
|
||||||
}
|
}
|
||||||
|
@ -194,6 +198,19 @@ func (c *opcode) dumpMapHead(code *opcode) string {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *opcode) dumpMapEnd(code *opcode) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
`[%d]%s%s ([idx:%d][headPos:%d][mapPos:%d][length:%d])`,
|
||||||
|
code.displayIdx,
|
||||||
|
strings.Repeat("-", code.indent),
|
||||||
|
code.op,
|
||||||
|
code.idx/uintptrSize,
|
||||||
|
code.headPos/uintptrSize,
|
||||||
|
code.mapPos/uintptrSize,
|
||||||
|
code.length/uintptrSize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *opcode) dumpElem(code *opcode) string {
|
func (c *opcode) dumpElem(code *opcode) string {
|
||||||
var length uintptr
|
var length uintptr
|
||||||
if code.op.codeType() == codeArrayElem {
|
if code.op.codeType() == codeArrayElem {
|
||||||
|
@ -270,6 +287,9 @@ func (c *opcode) dump() string {
|
||||||
case codeMapValue:
|
case codeMapValue:
|
||||||
codes = append(codes, c.dumpValue(code))
|
codes = append(codes, c.dumpValue(code))
|
||||||
code = code.next
|
code = code.next
|
||||||
|
case codeMapEnd:
|
||||||
|
codes = append(codes, c.dumpMapEnd(code))
|
||||||
|
code = code.next
|
||||||
case codeStructField:
|
case codeStructField:
|
||||||
codes = append(codes, c.dumpField(code))
|
codes = append(codes, c.dumpField(code))
|
||||||
code = code.next
|
code = code.next
|
||||||
|
@ -369,9 +389,9 @@ func newArrayElemCode(ctx *encodeCompileContext, head *opcode, length int, size
|
||||||
func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
|
func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
|
||||||
var op opType
|
var op opType
|
||||||
if withLoad {
|
if withLoad {
|
||||||
op = opMapHeadLoad
|
op = opSortedMapHeadLoad
|
||||||
} else {
|
} else {
|
||||||
op = opMapHead
|
op = opSortedMapHead
|
||||||
}
|
}
|
||||||
idx := opcodeOffset(ctx.ptrIndex)
|
idx := opcodeOffset(ctx.ptrIndex)
|
||||||
ctx.incPtrIndex()
|
ctx.incPtrIndex()
|
||||||
|
@ -394,7 +414,7 @@ func newMapHeaderCode(ctx *encodeCompileContext, withLoad bool) *opcode {
|
||||||
|
|
||||||
func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||||
return &opcode{
|
return &opcode{
|
||||||
op: opMapKey,
|
op: opSortedMapKey,
|
||||||
displayIdx: ctx.opcodeIndex,
|
displayIdx: ctx.opcodeIndex,
|
||||||
idx: opcodeOffset(ctx.ptrIndex),
|
idx: opcodeOffset(ctx.ptrIndex),
|
||||||
elemIdx: head.elemIdx,
|
elemIdx: head.elemIdx,
|
||||||
|
@ -406,7 +426,7 @@ func newMapKeyCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||||
|
|
||||||
func newMapValueCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
func newMapValueCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||||
return &opcode{
|
return &opcode{
|
||||||
op: opMapValue,
|
op: opSortedMapValue,
|
||||||
displayIdx: ctx.opcodeIndex,
|
displayIdx: ctx.opcodeIndex,
|
||||||
idx: opcodeOffset(ctx.ptrIndex),
|
idx: opcodeOffset(ctx.ptrIndex),
|
||||||
elemIdx: head.elemIdx,
|
elemIdx: head.elemIdx,
|
||||||
|
@ -416,6 +436,24 @@ func newMapValueCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newMapEndCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||||
|
headPos := opcodeOffset(ctx.ptrIndex)
|
||||||
|
ctx.incPtrIndex()
|
||||||
|
mapPos := opcodeOffset(ctx.ptrIndex)
|
||||||
|
ctx.incPtrIndex()
|
||||||
|
idx := opcodeOffset(ctx.ptrIndex)
|
||||||
|
return &opcode{
|
||||||
|
op: opSortedMapEnd,
|
||||||
|
displayIdx: ctx.opcodeIndex,
|
||||||
|
idx: idx,
|
||||||
|
length: head.length,
|
||||||
|
headPos: headPos,
|
||||||
|
mapPos: mapPos,
|
||||||
|
indent: ctx.indent,
|
||||||
|
next: newEndOp(ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func newInterfaceCode(ctx *encodeCompileContext) *opcode {
|
func newInterfaceCode(ctx *encodeCompileContext) *opcode {
|
||||||
return &opcode{
|
return &opcode{
|
||||||
op: opInterface,
|
op: opInterface,
|
||||||
|
|
|
@ -12,8 +12,9 @@ const (
|
||||||
codeMapHead codeType = 5
|
codeMapHead codeType = 5
|
||||||
codeMapKey codeType = 6
|
codeMapKey codeType = 6
|
||||||
codeMapValue codeType = 7
|
codeMapValue codeType = 7
|
||||||
codeStructFieldRecursive codeType = 8
|
codeMapEnd codeType = 8
|
||||||
codeStructField codeType = 9
|
codeStructFieldRecursive codeType = 9
|
||||||
|
codeStructField codeType = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
type opType int
|
type opType int
|
||||||
|
@ -2462,7 +2463,7 @@ func (t opType) codeType() codeType {
|
||||||
case opSortedMapValue:
|
case opSortedMapValue:
|
||||||
return codeMapValue
|
return codeMapValue
|
||||||
case opSortedMapEnd:
|
case opSortedMapEnd:
|
||||||
return codeOp
|
return codeMapEnd
|
||||||
case opStructFieldHead:
|
case opStructFieldHead:
|
||||||
return codeStructField
|
return codeStructField
|
||||||
case opStructFieldHeadOmitEmpty:
|
case opStructFieldHeadOmitEmpty:
|
||||||
|
@ -3256,7 +3257,7 @@ func (t opType) codeType() codeType {
|
||||||
case opSortedMapValueIndent:
|
case opSortedMapValueIndent:
|
||||||
return codeMapValue
|
return codeMapValue
|
||||||
case opSortedMapEndIndent:
|
case opSortedMapEndIndent:
|
||||||
return codeOp
|
return codeMapEnd
|
||||||
case opStructFieldHeadIndent:
|
case opStructFieldHeadIndent:
|
||||||
return codeStructField
|
return codeStructField
|
||||||
case opStructFieldHeadOmitEmptyIndent:
|
case opStructFieldHeadOmitEmptyIndent:
|
||||||
|
|
|
@ -384,7 +384,7 @@ func Test_Marshal(t *testing.T) {
|
||||||
"d": 4,
|
"d": 4,
|
||||||
})
|
})
|
||||||
assertErr(t, err)
|
assertErr(t, err)
|
||||||
assertEq(t, "map", len(`{"a":1,"b":2,"c":3,"d":4}`), len(string(bytes)))
|
assertEq(t, "map", `{"a":1,"b":2,"c":3,"d":4}`, string(bytes))
|
||||||
})
|
})
|
||||||
t.Run("map[string]interface{}", func(t *testing.T) {
|
t.Run("map[string]interface{}", func(t *testing.T) {
|
||||||
type T struct {
|
type T struct {
|
||||||
|
@ -400,7 +400,7 @@ func Test_Marshal(t *testing.T) {
|
||||||
}
|
}
|
||||||
bytes, err := json.Marshal(v)
|
bytes, err := json.Marshal(v)
|
||||||
assertErr(t, err)
|
assertErr(t, err)
|
||||||
assertEq(t, "map[string]interface{}", len(`{"a":1,"b":2.1,"c":{"A":10},"d":4}`), len(string(bytes)))
|
assertEq(t, "map[string]interface{}", `{"a":1,"b":2.1,"c":{"A":10},"d":4}`, string(bytes))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -474,7 +474,7 @@ func Test_MarshalIndent(t *testing.T) {
|
||||||
}, prefix, indent)
|
}, prefix, indent)
|
||||||
assertErr(t, err)
|
assertErr(t, err)
|
||||||
result := "{\n-\t\"a\": 1,\n-\t\"b\": 2,\n-\t\"c\": 3,\n-\t\"d\": 4\n-}"
|
result := "{\n-\t\"a\": 1,\n-\t\"b\": 2,\n-\t\"c\": 3,\n-\t\"d\": 4\n-}"
|
||||||
assertEq(t, "map", len(result), len(string(bytes)))
|
assertEq(t, "map", result, string(bytes))
|
||||||
})
|
})
|
||||||
t.Run("map[string]interface{}", func(t *testing.T) {
|
t.Run("map[string]interface{}", func(t *testing.T) {
|
||||||
type T struct {
|
type T struct {
|
||||||
|
@ -493,7 +493,7 @@ func Test_MarshalIndent(t *testing.T) {
|
||||||
bytes, err := json.MarshalIndent(v, prefix, indent)
|
bytes, err := json.MarshalIndent(v, prefix, indent)
|
||||||
assertErr(t, err)
|
assertErr(t, err)
|
||||||
result := "{\n-\t\"a\": 1,\n-\t\"b\": 2.1,\n-\t\"c\": {\n-\t\t\"E\": 10,\n-\t\t\"F\": 11\n-\t},\n-\t\"d\": 4\n-}"
|
result := "{\n-\t\"a\": 1,\n-\t\"b\": 2.1,\n-\t\"c\": {\n-\t\t\"E\": 10,\n-\t\t\"F\": 11\n-\t},\n-\t\"d\": 4\n-}"
|
||||||
assertEq(t, "map[string]interface{}", len(result), len(string(bytes)))
|
assertEq(t, "map[string]interface{}", result, string(bytes))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -898,7 +898,7 @@ func TestTextMarshalerMapKeysAreSorted(t *testing.T) {
|
||||||
t.Fatalf("Failed to Marshal text.Marshaler: %v", err)
|
t.Fatalf("Failed to Marshal text.Marshaler: %v", err)
|
||||||
}
|
}
|
||||||
const want = `{"a:z":3,"x:y":1,"y:x":2,"z:a":4}`
|
const want = `{"a:z":3,"x:y":1,"y:x":2,"z:a":4}`
|
||||||
if len(string(b)) != len(want) {
|
if string(b) != want {
|
||||||
t.Errorf("Marshal map with text.Marshaler keys: got %#q, want %#q", b, want)
|
t.Errorf("Marshal map with text.Marshaler keys: got %#q, want %#q", b, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -914,7 +914,7 @@ func TestNilMarshalerTextMapKey(t *testing.T) {
|
||||||
t.Fatalf("Failed to Marshal *text.Marshaler: %v", err)
|
t.Fatalf("Failed to Marshal *text.Marshaler: %v", err)
|
||||||
}
|
}
|
||||||
const want = `{"":1,"A:B":2}`
|
const want = `{"":1,"A:B":2}`
|
||||||
if len(string(b)) != len(want) {
|
if string(b) != want {
|
||||||
t.Errorf("Marshal map with *text.Marshaler keys: got %#q, want %#q", b, want)
|
t.Errorf("Marshal map with *text.Marshaler keys: got %#q, want %#q", b, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
250
encode_vm.go
250
encode_vm.go
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -425,6 +426,125 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
e.encodeBytes([]byte{']', '\n'})
|
e.encodeBytes([]byte{']', '\n'})
|
||||||
code = code.end.next
|
code = code.end.next
|
||||||
}
|
}
|
||||||
|
case opSortedMapHead:
|
||||||
|
ptr := load(ctxptr, code.idx)
|
||||||
|
if ptr == 0 {
|
||||||
|
e.encodeNull()
|
||||||
|
code = code.end.next
|
||||||
|
} else {
|
||||||
|
e.encodeByte('{')
|
||||||
|
mlen := maplen(unsafe.Pointer(ptr))
|
||||||
|
if mlen > 0 {
|
||||||
|
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||||
|
ctx.keepRefs = append(ctx.keepRefs, iter)
|
||||||
|
store(ctxptr, code.elemIdx, 0)
|
||||||
|
store(ctxptr, code.length, uintptr(mlen))
|
||||||
|
store(ctxptr, code.mapIter, uintptr(iter))
|
||||||
|
pos := make([]int, 0, mlen)
|
||||||
|
pos = append(pos, len(e.buf))
|
||||||
|
posPtr := unsafe.Pointer(&pos)
|
||||||
|
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
||||||
|
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
||||||
|
key := mapiterkey(iter)
|
||||||
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
code = code.next
|
||||||
|
} else {
|
||||||
|
e.encodeByte('}')
|
||||||
|
code = code.end.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case opSortedMapHeadLoad:
|
||||||
|
ptr := load(ctxptr, code.idx)
|
||||||
|
if ptr == 0 {
|
||||||
|
e.encodeNull()
|
||||||
|
code = code.end.next
|
||||||
|
} else {
|
||||||
|
// load pointer
|
||||||
|
ptr = uintptr(*(*unsafe.Pointer)(unsafe.Pointer(ptr)))
|
||||||
|
e.encodeByte('{')
|
||||||
|
mlen := maplen(unsafe.Pointer(ptr))
|
||||||
|
if mlen > 0 {
|
||||||
|
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||||
|
ctx.keepRefs = append(ctx.keepRefs, iter)
|
||||||
|
store(ctxptr, code.elemIdx, 0)
|
||||||
|
store(ctxptr, code.length, uintptr(mlen))
|
||||||
|
store(ctxptr, code.mapIter, uintptr(iter))
|
||||||
|
key := mapiterkey(iter)
|
||||||
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
pos := make([]int, 0, mlen)
|
||||||
|
pos = append(pos, len(e.buf))
|
||||||
|
posPtr := unsafe.Pointer(&pos)
|
||||||
|
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
||||||
|
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
||||||
|
code = code.next
|
||||||
|
} else {
|
||||||
|
e.encodeByte('}')
|
||||||
|
code = code.end.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case opSortedMapKey:
|
||||||
|
idx := load(ctxptr, code.elemIdx)
|
||||||
|
length := load(ctxptr, code.length)
|
||||||
|
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
|
||||||
|
*posPtr = append(*posPtr, len(e.buf))
|
||||||
|
idx++
|
||||||
|
if idx < length {
|
||||||
|
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
||||||
|
store(ctxptr, code.elemIdx, idx)
|
||||||
|
key := mapiterkey(iter)
|
||||||
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
code = code.next
|
||||||
|
} else {
|
||||||
|
code = code.end
|
||||||
|
}
|
||||||
|
case opSortedMapValue:
|
||||||
|
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
|
||||||
|
*posPtr = append(*posPtr, len(e.buf))
|
||||||
|
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
||||||
|
value := mapitervalue(iter)
|
||||||
|
store(ctxptr, code.next.idx, uintptr(value))
|
||||||
|
mapiternext(iter)
|
||||||
|
code = code.next
|
||||||
|
case opSortedMapEnd:
|
||||||
|
length := int(load(ctxptr, code.length))
|
||||||
|
type mapKV struct {
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
kvs := make([]mapKV, 0, length)
|
||||||
|
posPtr := unsafe.Pointer(load(ctxptr, code.mapPos))
|
||||||
|
pos := *(*[]int)(posPtr)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
startKey := pos[i*2]
|
||||||
|
startValue := pos[i*2+1]
|
||||||
|
var endValue int
|
||||||
|
if i+1 < length {
|
||||||
|
endValue = pos[i*2+2]
|
||||||
|
} else {
|
||||||
|
endValue = len(e.buf)
|
||||||
|
}
|
||||||
|
kvs = append(kvs, mapKV{
|
||||||
|
key: string(e.buf[startKey:startValue]),
|
||||||
|
value: string(e.buf[startValue:endValue]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
sort.Slice(kvs, func(i, j int) bool {
|
||||||
|
return kvs[i].key < kvs[j].key
|
||||||
|
})
|
||||||
|
buf := e.buf[pos[0]:]
|
||||||
|
buf = buf[:0]
|
||||||
|
for idx, kv := range kvs {
|
||||||
|
if idx != 0 {
|
||||||
|
buf = append(buf, ',')
|
||||||
|
}
|
||||||
|
buf = append(buf, []byte(kv.key)...)
|
||||||
|
buf = append(buf, ':')
|
||||||
|
buf = append(buf, []byte(kv.value)...)
|
||||||
|
}
|
||||||
|
buf = append(buf, '}')
|
||||||
|
e.buf = e.buf[:pos[0]]
|
||||||
|
e.buf = append(e.buf, buf...)
|
||||||
|
code = code.next
|
||||||
case opMapHead:
|
case opMapHead:
|
||||||
ptr := load(ctxptr, code.idx)
|
ptr := load(ctxptr, code.idx)
|
||||||
if ptr == 0 {
|
if ptr == 0 {
|
||||||
|
@ -493,6 +613,136 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
store(ctxptr, code.next.idx, uintptr(value))
|
store(ctxptr, code.next.idx, uintptr(value))
|
||||||
mapiternext(iter)
|
mapiternext(iter)
|
||||||
code = code.next
|
code = code.next
|
||||||
|
case opSortedMapHeadIndent:
|
||||||
|
ptr := load(ctxptr, code.idx)
|
||||||
|
if ptr == 0 {
|
||||||
|
e.encodeNull()
|
||||||
|
code = code.end.next
|
||||||
|
} else {
|
||||||
|
mlen := maplen(unsafe.Pointer(ptr))
|
||||||
|
if mlen > 0 {
|
||||||
|
e.encodeBytes([]byte{'{', '\n'})
|
||||||
|
|
||||||
|
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||||
|
ctx.keepRefs = append(ctx.keepRefs, iter)
|
||||||
|
store(ctxptr, code.elemIdx, 0)
|
||||||
|
store(ctxptr, code.length, uintptr(mlen))
|
||||||
|
store(ctxptr, code.mapIter, uintptr(iter))
|
||||||
|
pos := make([]int, 0, mlen)
|
||||||
|
pos = append(pos, len(e.buf))
|
||||||
|
posPtr := unsafe.Pointer(&pos)
|
||||||
|
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
||||||
|
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
||||||
|
key := mapiterkey(iter)
|
||||||
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
code = code.next
|
||||||
|
} else {
|
||||||
|
e.encodeIndent(code.indent)
|
||||||
|
e.encodeBytes([]byte{'{', '}'})
|
||||||
|
code = code.end.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case opSortedMapHeadLoadIndent:
|
||||||
|
ptr := load(ctxptr, code.idx)
|
||||||
|
if ptr == 0 {
|
||||||
|
e.encodeNull()
|
||||||
|
code = code.end.next
|
||||||
|
} else {
|
||||||
|
// load pointer
|
||||||
|
ptr = uintptr(*(*unsafe.Pointer)(unsafe.Pointer(ptr)))
|
||||||
|
mlen := maplen(unsafe.Pointer(ptr))
|
||||||
|
if mlen > 0 {
|
||||||
|
e.encodeBytes([]byte{'{', '\n'})
|
||||||
|
|
||||||
|
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||||
|
ctx.keepRefs = append(ctx.keepRefs, iter)
|
||||||
|
store(ctxptr, code.elemIdx, 0)
|
||||||
|
store(ctxptr, code.length, uintptr(mlen))
|
||||||
|
store(ctxptr, code.mapIter, uintptr(iter))
|
||||||
|
key := mapiterkey(iter)
|
||||||
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
pos := make([]int, 0, mlen)
|
||||||
|
pos = append(pos, len(e.buf))
|
||||||
|
posPtr := unsafe.Pointer(&pos)
|
||||||
|
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
||||||
|
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
||||||
|
code = code.next
|
||||||
|
} else {
|
||||||
|
e.encodeIndent(code.indent)
|
||||||
|
e.encodeBytes([]byte{'{', '}'})
|
||||||
|
code = code.end.next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case opSortedMapKeyIndent:
|
||||||
|
idx := load(ctxptr, code.elemIdx)
|
||||||
|
length := load(ctxptr, code.length)
|
||||||
|
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
|
||||||
|
*posPtr = append(*posPtr, len(e.buf))
|
||||||
|
idx++
|
||||||
|
if idx < length {
|
||||||
|
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
||||||
|
store(ctxptr, code.elemIdx, idx)
|
||||||
|
key := mapiterkey(iter)
|
||||||
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
code = code.next
|
||||||
|
} else {
|
||||||
|
code = code.end
|
||||||
|
}
|
||||||
|
case opSortedMapValueIndent:
|
||||||
|
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
|
||||||
|
*posPtr = append(*posPtr, len(e.buf))
|
||||||
|
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
||||||
|
value := mapitervalue(iter)
|
||||||
|
store(ctxptr, code.next.idx, uintptr(value))
|
||||||
|
mapiternext(iter)
|
||||||
|
code = code.next
|
||||||
|
case opSortedMapEndIndent:
|
||||||
|
length := int(load(ctxptr, code.length))
|
||||||
|
type mapKV struct {
|
||||||
|
key string
|
||||||
|
value string
|
||||||
|
}
|
||||||
|
kvs := make([]mapKV, 0, length)
|
||||||
|
pos := *(*[]int)(unsafe.Pointer(load(ctxptr, code.mapPos)))
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
startKey := pos[i*2]
|
||||||
|
startValue := pos[i*2+1]
|
||||||
|
var endValue int
|
||||||
|
if i+1 < length {
|
||||||
|
endValue = pos[i*2+2]
|
||||||
|
} else {
|
||||||
|
endValue = len(e.buf)
|
||||||
|
}
|
||||||
|
kvs = append(kvs, mapKV{
|
||||||
|
key: string(e.buf[startKey:startValue]),
|
||||||
|
value: string(e.buf[startValue:endValue]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
fmt.Println("kvs = ", kvs)
|
||||||
|
sort.Slice(kvs, func(i, j int) bool {
|
||||||
|
return kvs[i].key < kvs[j].key
|
||||||
|
})
|
||||||
|
buf := e.buf[pos[0]:]
|
||||||
|
buf = buf[:0]
|
||||||
|
for idx, kv := range kvs {
|
||||||
|
if idx != 0 {
|
||||||
|
buf = append(buf, []byte{',', '\n'}...)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = append(buf, e.prefix...)
|
||||||
|
buf = append(buf, bytes.Repeat(e.indentStr, code.indent+1)...)
|
||||||
|
|
||||||
|
buf = append(buf, []byte(kv.key)...)
|
||||||
|
buf = append(buf, []byte{':', ' '}...)
|
||||||
|
buf = append(buf, []byte(kv.value)...)
|
||||||
|
}
|
||||||
|
buf = append(buf, '\n')
|
||||||
|
buf = append(buf, e.prefix...)
|
||||||
|
buf = append(buf, bytes.Repeat(e.indentStr, code.indent)...)
|
||||||
|
buf = append(buf, '}')
|
||||||
|
e.buf = e.buf[:pos[0]]
|
||||||
|
e.buf = append(e.buf, buf...)
|
||||||
|
code = code.next
|
||||||
case opMapHeadIndent:
|
case opMapHeadIndent:
|
||||||
ptr := load(ctxptr, code.idx)
|
ptr := load(ctxptr, code.idx)
|
||||||
if ptr == 0 {
|
if ptr == 0 {
|
||||||
|
|
Loading…
Reference in New Issue