forked from mirror/go-json
Enable switch map processing at runtime
This commit is contained in:
parent
898d58b8b8
commit
aaea586778
|
@ -238,13 +238,6 @@ func (t opType) fieldToStringTagField() opType {
|
||||||
{"RootMapKey", "RootMapKeyIndent", "MapKey"},
|
{"RootMapKey", "RootMapKeyIndent", "MapKey"},
|
||||||
{"MapValue", "MapValueIndent", "MapValue"},
|
{"MapValue", "MapValueIndent", "MapValue"},
|
||||||
{"MapEnd", "MapEndIndent", "Op"},
|
{"MapEnd", "MapEndIndent", "Op"},
|
||||||
{"SortedMapHead", "SortedMapHeadIndent", "MapHead"},
|
|
||||||
{"SortedMapHeadLoad", "SortedMapHeadLoadIndent", "MapHead"},
|
|
||||||
{"SortedRootMapHead", "SortedRootMapHeadIndent", "MapHead"},
|
|
||||||
{"SortedMapKey", "SortedMapKeyIndent", "MapKey"},
|
|
||||||
{"SortedRootMapKey", "SortedRootMapKeyIndent", "MapKey"},
|
|
||||||
{"SortedMapValue", "SortedMapValueIndent", "MapValue"},
|
|
||||||
{"SortedMapEnd", "SortedMapEndIndent", "MapEnd"},
|
|
||||||
{"StructFieldHead", "StructFieldHeadIndent", "StructField"},
|
{"StructFieldHead", "StructFieldHeadIndent", "StructField"},
|
||||||
{"StructFieldHeadOmitEmpty", "StructFieldHeadOmitEmptyIndent", "StructField"},
|
{"StructFieldHeadOmitEmpty", "StructFieldHeadOmitEmptyIndent", "StructField"},
|
||||||
{"StructFieldHeadStringTag", "StructFieldHeadStringTagIndent", "StructField"},
|
{"StructFieldHeadStringTag", "StructFieldHeadStringTagIndent", "StructField"},
|
||||||
|
|
12
encode.go
12
encode.go
|
@ -18,6 +18,7 @@ type Encoder struct {
|
||||||
buf []byte
|
buf []byte
|
||||||
enabledIndent bool
|
enabledIndent bool
|
||||||
enabledHTMLEscape bool
|
enabledHTMLEscape bool
|
||||||
|
unorderedMap bool
|
||||||
prefix []byte
|
prefix []byte
|
||||||
indentStr []byte
|
indentStr []byte
|
||||||
structTypeToCompiledCode map[uintptr]*compiledCode
|
structTypeToCompiledCode map[uintptr]*compiledCode
|
||||||
|
@ -88,6 +89,16 @@ func NewEncoder(w io.Writer) *Encoder {
|
||||||
//
|
//
|
||||||
// See the documentation for Marshal for details about the conversion of Go values to JSON.
|
// See the documentation for Marshal for details about the conversion of Go values to JSON.
|
||||||
func (e *Encoder) Encode(v interface{}) error {
|
func (e *Encoder) Encode(v interface{}) error {
|
||||||
|
return e.EncodeWithOption(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeWithOption call Encode with EncodeOption.
|
||||||
|
func (e *Encoder) EncodeWithOption(v interface{}, opts ...EncodeOption) error {
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt(e); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := e.encode(v); err != nil {
|
if err := e.encode(v); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -126,6 +137,7 @@ func (e *Encoder) reset() {
|
||||||
e.buf = e.buf[:0]
|
e.buf = e.buf[:0]
|
||||||
e.enabledHTMLEscape = true
|
e.enabledHTMLEscape = true
|
||||||
e.enabledIndent = false
|
e.enabledIndent = false
|
||||||
|
e.unorderedMap = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) {
|
func (e *Encoder) encodeForMarshal(v interface{}) ([]byte, error) {
|
||||||
|
|
|
@ -452,13 +452,13 @@ func (e *Encoder) typeToHeaderType(op opType) opType {
|
||||||
return opStructFieldHeadString
|
return opStructFieldHeadString
|
||||||
case opBool:
|
case opBool:
|
||||||
return opStructFieldHeadBool
|
return opStructFieldHeadBool
|
||||||
case opMapHead, opSortedMapHead:
|
case opMapHead:
|
||||||
return opStructFieldHeadMap
|
return opStructFieldHeadMap
|
||||||
case opMapHeadLoad, opSortedMapHeadLoad:
|
case opMapHeadLoad:
|
||||||
return opStructFieldHeadMapLoad
|
return opStructFieldHeadMapLoad
|
||||||
case opMapHeadIndent, opSortedMapHeadIndent:
|
case opMapHeadIndent:
|
||||||
return opStructFieldHeadMapIndent
|
return opStructFieldHeadMapIndent
|
||||||
case opMapHeadLoadIndent, opSortedMapHeadLoadIndent:
|
case opMapHeadLoadIndent:
|
||||||
return opStructFieldHeadMapLoadIndent
|
return opStructFieldHeadMapLoadIndent
|
||||||
case opArrayHead:
|
case opArrayHead:
|
||||||
return opStructFieldHeadArray
|
return opStructFieldHeadArray
|
||||||
|
@ -510,13 +510,13 @@ func (e *Encoder) typeToFieldType(op opType) opType {
|
||||||
return opStructFieldString
|
return opStructFieldString
|
||||||
case opBool:
|
case opBool:
|
||||||
return opStructFieldBool
|
return opStructFieldBool
|
||||||
case opMapHead, opSortedMapHead:
|
case opMapHead:
|
||||||
return opStructFieldMap
|
return opStructFieldMap
|
||||||
case opMapHeadLoad, opSortedMapHeadLoad:
|
case opMapHeadLoad:
|
||||||
return opStructFieldMapLoad
|
return opStructFieldMapLoad
|
||||||
case opMapHeadIndent, opSortedMapHeadIndent:
|
case opMapHeadIndent:
|
||||||
return opStructFieldMapIndent
|
return opStructFieldMapIndent
|
||||||
case opMapHeadLoadIndent, opSortedMapHeadLoadIndent:
|
case opMapHeadLoadIndent:
|
||||||
return opStructFieldMapLoadIndent
|
return opStructFieldMapLoadIndent
|
||||||
case opArrayHead:
|
case opArrayHead:
|
||||||
return opStructFieldArray
|
return opStructFieldArray
|
||||||
|
|
|
@ -389,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 = opSortedMapHeadLoad
|
op = opMapHeadLoad
|
||||||
} else {
|
} else {
|
||||||
op = opSortedMapHead
|
op = opMapHead
|
||||||
}
|
}
|
||||||
idx := opcodeOffset(ctx.ptrIndex)
|
idx := opcodeOffset(ctx.ptrIndex)
|
||||||
ctx.incPtrIndex()
|
ctx.incPtrIndex()
|
||||||
|
@ -414,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: opSortedMapKey,
|
op: opMapKey,
|
||||||
displayIdx: ctx.opcodeIndex,
|
displayIdx: ctx.opcodeIndex,
|
||||||
idx: opcodeOffset(ctx.ptrIndex),
|
idx: opcodeOffset(ctx.ptrIndex),
|
||||||
elemIdx: head.elemIdx,
|
elemIdx: head.elemIdx,
|
||||||
|
@ -426,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: opSortedMapValue,
|
op: opMapValue,
|
||||||
displayIdx: ctx.opcodeIndex,
|
displayIdx: ctx.opcodeIndex,
|
||||||
idx: opcodeOffset(ctx.ptrIndex),
|
idx: opcodeOffset(ctx.ptrIndex),
|
||||||
elemIdx: head.elemIdx,
|
elemIdx: head.elemIdx,
|
||||||
|
@ -443,7 +443,7 @@ func newMapEndCode(ctx *encodeCompileContext, head *opcode) *opcode {
|
||||||
ctx.incPtrIndex()
|
ctx.incPtrIndex()
|
||||||
idx := opcodeOffset(ctx.ptrIndex)
|
idx := opcodeOffset(ctx.ptrIndex)
|
||||||
return &opcode{
|
return &opcode{
|
||||||
op: opSortedMapEnd,
|
op: opMapEnd,
|
||||||
displayIdx: ctx.opcodeIndex,
|
displayIdx: ctx.opcodeIndex,
|
||||||
idx: idx,
|
idx: idx,
|
||||||
length: head.length,
|
length: head.length,
|
||||||
|
|
1620
encode_optype.go
1620
encode_optype.go
File diff suppressed because it is too large
Load Diff
|
@ -377,14 +377,18 @@ func Test_Marshal(t *testing.T) {
|
||||||
})
|
})
|
||||||
t.Run("map", func(t *testing.T) {
|
t.Run("map", func(t *testing.T) {
|
||||||
t.Run("map[string]int", func(t *testing.T) {
|
t.Run("map[string]int", func(t *testing.T) {
|
||||||
bytes, err := json.Marshal(map[string]int{
|
v := map[string]int{
|
||||||
"a": 1,
|
"a": 1,
|
||||||
"b": 2,
|
"b": 2,
|
||||||
"c": 3,
|
"c": 3,
|
||||||
"d": 4,
|
"d": 4,
|
||||||
})
|
}
|
||||||
|
bytes, err := json.Marshal(v)
|
||||||
assertErr(t, err)
|
assertErr(t, err)
|
||||||
assertEq(t, "map", `{"a":1,"b":2,"c":3,"d":4}`, string(bytes))
|
assertEq(t, "map", `{"a":1,"b":2,"c":3,"d":4}`, string(bytes))
|
||||||
|
b, err := json.MarshalWithOption(v, json.UnorderedMap())
|
||||||
|
assertErr(t, err)
|
||||||
|
assertEq(t, "unordered map", len(`{"a":1,"b":2,"c":3,"d":4}`), len(string(b)))
|
||||||
})
|
})
|
||||||
t.Run("map[string]interface{}", func(t *testing.T) {
|
t.Run("map[string]interface{}", func(t *testing.T) {
|
||||||
type T struct {
|
type T struct {
|
||||||
|
|
323
encode_vm.go
323
encode_vm.go
|
@ -426,7 +426,7 @@ 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:
|
case opMapHead:
|
||||||
ptr := load(ctxptr, code.idx)
|
ptr := load(ctxptr, code.idx)
|
||||||
if ptr == 0 {
|
if ptr == 0 {
|
||||||
e.encodeNull()
|
e.encodeNull()
|
||||||
|
@ -440,11 +440,13 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
store(ctxptr, code.elemIdx, 0)
|
store(ctxptr, code.elemIdx, 0)
|
||||||
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 {
|
||||||
pos := make([]int, 0, mlen)
|
pos := make([]int, 0, mlen)
|
||||||
pos = append(pos, len(e.buf))
|
pos = append(pos, len(e.buf))
|
||||||
posPtr := unsafe.Pointer(&pos)
|
posPtr := unsafe.Pointer(&pos)
|
||||||
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
||||||
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
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))
|
||||||
code = code.next
|
code = code.next
|
||||||
|
@ -453,7 +455,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
code = code.end.next
|
code = code.end.next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case opSortedMapHeadLoad:
|
case opMapHeadLoad:
|
||||||
ptr := load(ctxptr, code.idx)
|
ptr := load(ctxptr, code.idx)
|
||||||
if ptr == 0 {
|
if ptr == 0 {
|
||||||
e.encodeNull()
|
e.encodeNull()
|
||||||
|
@ -471,23 +473,38 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
store(ctxptr, code.mapIter, uintptr(iter))
|
store(ctxptr, code.mapIter, uintptr(iter))
|
||||||
key := mapiterkey(iter)
|
key := mapiterkey(iter)
|
||||||
store(ctxptr, code.next.idx, uintptr(key))
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
if !e.unorderedMap {
|
||||||
pos := make([]int, 0, mlen)
|
pos := make([]int, 0, mlen)
|
||||||
pos = append(pos, len(e.buf))
|
pos = append(pos, len(e.buf))
|
||||||
posPtr := unsafe.Pointer(&pos)
|
posPtr := unsafe.Pointer(&pos)
|
||||||
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
ctx.keepRefs = append(ctx.keepRefs, posPtr)
|
||||||
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
store(ctxptr, code.end.mapPos, uintptr(posPtr))
|
||||||
|
}
|
||||||
code = code.next
|
code = code.next
|
||||||
} else {
|
} else {
|
||||||
e.encodeByte('}')
|
e.encodeByte('}')
|
||||||
code = code.end.next
|
code = code.end.next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case opSortedMapKey:
|
case opMapKey:
|
||||||
idx := load(ctxptr, code.elemIdx)
|
idx := load(ctxptr, code.elemIdx)
|
||||||
length := load(ctxptr, code.length)
|
length := load(ctxptr, code.length)
|
||||||
|
idx++
|
||||||
|
if e.unorderedMap {
|
||||||
|
if idx < length {
|
||||||
|
e.encodeByte(',')
|
||||||
|
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 {
|
||||||
|
e.encodeByte('}')
|
||||||
|
code = code.end.next
|
||||||
|
}
|
||||||
|
} else {
|
||||||
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
|
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
|
||||||
*posPtr = append(*posPtr, len(e.buf))
|
*posPtr = append(*posPtr, len(e.buf))
|
||||||
idx++
|
|
||||||
if idx < length {
|
if idx < length {
|
||||||
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
||||||
store(ctxptr, code.elemIdx, idx)
|
store(ctxptr, code.elemIdx, idx)
|
||||||
|
@ -497,15 +514,21 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
} else {
|
} else {
|
||||||
code = code.end
|
code = code.end
|
||||||
}
|
}
|
||||||
case opSortedMapValue:
|
}
|
||||||
|
case opMapValue:
|
||||||
|
if e.unorderedMap {
|
||||||
|
e.encodeByte(':')
|
||||||
|
} else {
|
||||||
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
|
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
|
||||||
*posPtr = append(*posPtr, len(e.buf))
|
*posPtr = append(*posPtr, len(e.buf))
|
||||||
|
}
|
||||||
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
||||||
value := mapitervalue(iter)
|
value := mapitervalue(iter)
|
||||||
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 opSortedMapEnd:
|
case opMapEnd:
|
||||||
|
// this operation only used by sorted map.
|
||||||
length := int(load(ctxptr, code.length))
|
length := int(load(ctxptr, code.length))
|
||||||
type mapKV struct {
|
type mapKV struct {
|
||||||
key string
|
key string
|
||||||
|
@ -545,204 +568,6 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
e.buf = e.buf[:pos[0]]
|
e.buf = e.buf[:pos[0]]
|
||||||
e.buf = append(e.buf, buf...)
|
e.buf = append(e.buf, buf...)
|
||||||
code = code.next
|
code = code.next
|
||||||
case opMapHead:
|
|
||||||
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))
|
|
||||||
key := mapiterkey(iter)
|
|
||||||
store(ctxptr, code.next.idx, uintptr(key))
|
|
||||||
code = code.next
|
|
||||||
} else {
|
|
||||||
e.encodeByte('}')
|
|
||||||
code = code.end.next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case opMapHeadLoad:
|
|
||||||
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))
|
|
||||||
code = code.next
|
|
||||||
} else {
|
|
||||||
e.encodeByte('}')
|
|
||||||
code = code.end.next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case opMapKey:
|
|
||||||
idx := load(ctxptr, code.elemIdx)
|
|
||||||
length := load(ctxptr, code.length)
|
|
||||||
idx++
|
|
||||||
if idx < length {
|
|
||||||
e.encodeByte(',')
|
|
||||||
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 {
|
|
||||||
e.encodeByte('}')
|
|
||||||
code = code.end.next
|
|
||||||
}
|
|
||||||
case opMapValue:
|
|
||||||
e.encodeByte(':')
|
|
||||||
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
|
||||||
value := mapitervalue(iter)
|
|
||||||
store(ctxptr, code.next.idx, uintptr(value))
|
|
||||||
mapiternext(iter)
|
|
||||||
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 {
|
||||||
|
@ -758,10 +583,20 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
store(ctxptr, code.elemIdx, 0)
|
store(ctxptr, code.elemIdx, 0)
|
||||||
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 {
|
||||||
|
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))
|
||||||
|
} else {
|
||||||
|
e.encodeIndent(code.next.indent)
|
||||||
|
}
|
||||||
|
|
||||||
key := mapiterkey(iter)
|
key := mapiterkey(iter)
|
||||||
store(ctxptr, code.next.idx, uintptr(key))
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
code = code.next
|
code = code.next
|
||||||
e.encodeIndent(code.indent)
|
|
||||||
} else {
|
} else {
|
||||||
e.encodeIndent(code.indent)
|
e.encodeIndent(code.indent)
|
||||||
e.encodeBytes([]byte{'{', '}'})
|
e.encodeBytes([]byte{'{', '}'})
|
||||||
|
@ -787,8 +622,18 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
store(ctxptr, code.mapIter, uintptr(iter))
|
store(ctxptr, code.mapIter, uintptr(iter))
|
||||||
key := mapiterkey(iter)
|
key := mapiterkey(iter)
|
||||||
store(ctxptr, code.next.idx, uintptr(key))
|
store(ctxptr, code.next.idx, uintptr(key))
|
||||||
|
|
||||||
|
if !e.unorderedMap {
|
||||||
|
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))
|
||||||
|
} else {
|
||||||
|
e.encodeIndent(code.next.indent)
|
||||||
|
}
|
||||||
|
|
||||||
code = code.next
|
code = code.next
|
||||||
e.encodeIndent(code.indent)
|
|
||||||
} else {
|
} else {
|
||||||
e.encodeIndent(code.indent)
|
e.encodeIndent(code.indent)
|
||||||
e.encodeBytes([]byte{'{', '}'})
|
e.encodeBytes([]byte{'{', '}'})
|
||||||
|
@ -824,6 +669,7 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
idx := load(ctxptr, code.elemIdx)
|
idx := load(ctxptr, code.elemIdx)
|
||||||
length := load(ctxptr, code.length)
|
length := load(ctxptr, code.length)
|
||||||
idx++
|
idx++
|
||||||
|
if e.unorderedMap {
|
||||||
if idx < length {
|
if idx < length {
|
||||||
e.encodeBytes([]byte{',', '\n'})
|
e.encodeBytes([]byte{',', '\n'})
|
||||||
e.encodeIndent(code.indent)
|
e.encodeIndent(code.indent)
|
||||||
|
@ -838,6 +684,19 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
e.encodeByte('}')
|
e.encodeByte('}')
|
||||||
code = code.end.next
|
code = code.end.next
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
|
||||||
|
*posPtr = append(*posPtr, len(e.buf))
|
||||||
|
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 opRootMapKeyIndent:
|
case opRootMapKeyIndent:
|
||||||
idx := load(ctxptr, code.elemIdx)
|
idx := load(ctxptr, code.elemIdx)
|
||||||
length := load(ctxptr, code.length)
|
length := load(ctxptr, code.length)
|
||||||
|
@ -857,12 +716,64 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, code *opcode) error {
|
||||||
code = code.end.next
|
code = code.end.next
|
||||||
}
|
}
|
||||||
case opMapValueIndent:
|
case opMapValueIndent:
|
||||||
|
if e.unorderedMap {
|
||||||
e.encodeBytes([]byte{':', ' '})
|
e.encodeBytes([]byte{':', ' '})
|
||||||
|
} else {
|
||||||
|
posPtr := (*[]int)(unsafe.Pointer(load(ctxptr, code.end.mapPos)))
|
||||||
|
*posPtr = append(*posPtr, len(e.buf))
|
||||||
|
}
|
||||||
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
iter := unsafe.Pointer(load(ctxptr, code.mapIter))
|
||||||
value := mapitervalue(iter)
|
value := mapitervalue(iter)
|
||||||
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 opMapEndIndent:
|
||||||
|
// this operation only used by sorted map
|
||||||
|
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]),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
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 opStructFieldRecursive:
|
case opStructFieldRecursive:
|
||||||
ptr := load(ctxptr, code.idx)
|
ptr := load(ctxptr, code.idx)
|
||||||
if ptr != 0 {
|
if ptr != 0 {
|
||||||
|
|
20
json.go
20
json.go
|
@ -154,8 +154,18 @@ type Unmarshaler interface {
|
||||||
// an infinite recursion.
|
// an infinite recursion.
|
||||||
//
|
//
|
||||||
func Marshal(v interface{}) ([]byte, error) {
|
func Marshal(v interface{}) ([]byte, error) {
|
||||||
|
return MarshalWithOption(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalWithOption returns the JSON encoding of v with EncodeOption.
|
||||||
|
func MarshalWithOption(v interface{}, opts ...EncodeOption) ([]byte, error) {
|
||||||
var b *bytes.Buffer
|
var b *bytes.Buffer
|
||||||
enc := NewEncoder(b)
|
enc := NewEncoder(b)
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt(enc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
bytes, err := enc.encodeForMarshal(v)
|
bytes, err := enc.encodeForMarshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
enc.release()
|
enc.release()
|
||||||
|
@ -169,8 +179,18 @@ func Marshal(v interface{}) ([]byte, error) {
|
||||||
// Each JSON element in the output will begin on a new line beginning with prefix
|
// Each JSON element in the output will begin on a new line beginning with prefix
|
||||||
// followed by one or more copies of indent according to the indentation nesting.
|
// followed by one or more copies of indent according to the indentation nesting.
|
||||||
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||||
|
return MarshalIndentWithOption(v, prefix, indent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalIndentWithOption is like Marshal but applies Indent to format the output with EncodeOption.
|
||||||
|
func MarshalIndentWithOption(v interface{}, prefix, indent string, opts ...EncodeOption) ([]byte, error) {
|
||||||
var b *bytes.Buffer
|
var b *bytes.Buffer
|
||||||
enc := NewEncoder(b)
|
enc := NewEncoder(b)
|
||||||
|
for _, opt := range opts {
|
||||||
|
if err := opt(enc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
enc.SetIndent(prefix, indent)
|
enc.SetIndent(prefix, indent)
|
||||||
bytes, err := enc.encodeForMarshal(v)
|
bytes, err := enc.encodeForMarshal(v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in New Issue