mirror of https://github.com/goccy/go-json.git
Support Map and Interfacfe{} type for encoding of vm based
This commit is contained in:
parent
95b2194742
commit
090887bf7e
|
@ -15,6 +15,10 @@ func (e *Encoder) compileOp(typ *rtype) (*opcode, error) {
|
|||
return e.compilePtrOp(typ)
|
||||
case reflect.Slice:
|
||||
return e.compileSliceOp(typ)
|
||||
case reflect.Array:
|
||||
return e.compileArrayOp(typ)
|
||||
case reflect.Map:
|
||||
return e.compileMapOp(typ)
|
||||
case reflect.Struct:
|
||||
return e.compileStructOp(typ)
|
||||
case reflect.Int:
|
||||
|
@ -48,7 +52,7 @@ func (e *Encoder) compileOp(typ *rtype) (*opcode, error) {
|
|||
case reflect.Bool:
|
||||
return e.compileBoolOp(typ)
|
||||
case reflect.Interface:
|
||||
return nil, errCompileSlowPath
|
||||
return e.compileInterfaceOp(typ)
|
||||
}
|
||||
return nil, xerrors.Errorf("failed to encode type %s: %w", typ.String(), ErrUnsupportedType)
|
||||
}
|
||||
|
@ -155,6 +159,10 @@ func (e *Encoder) compileBoolOp(typ *rtype) (*opcode, error) {
|
|||
return newOpCode(opBool, typ, newEndOp()), nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileInterfaceOp(typ *rtype) (*opcode, error) {
|
||||
return newOpCode(opInterface, typ, newEndOp()), nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileSliceOp(typ *rtype) (*opcode, error) {
|
||||
elem := typ.Elem()
|
||||
size := elem.Size()
|
||||
|
@ -169,7 +177,7 @@ func (e *Encoder) compileSliceOp(typ *rtype) (*opcode, error) {
|
|||
|
||||
header := newSliceHeaderCode()
|
||||
elemCode := &sliceElemCode{opcodeHeader: &opcodeHeader{op: opSliceElem}, size: size}
|
||||
end := newOpCode(opSliceEnd, nil, nil)
|
||||
end := newOpCode(opSliceEnd, nil, newEndOp())
|
||||
|
||||
header.elem = elemCode
|
||||
header.end = end
|
||||
|
@ -177,7 +185,70 @@ func (e *Encoder) compileSliceOp(typ *rtype) (*opcode, error) {
|
|||
code.beforeLastCode().next = (*opcode)(unsafe.Pointer(elemCode))
|
||||
elemCode.next = code
|
||||
elemCode.end = end
|
||||
end.next = newEndOp()
|
||||
return (*opcode)(unsafe.Pointer(header)), nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileArrayOp(typ *rtype) (*opcode, error) {
|
||||
elem := typ.Elem()
|
||||
alen := typ.Len()
|
||||
size := elem.Size()
|
||||
code, err := e.compileOp(elem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// header => opcode => elem => end
|
||||
// ^ |
|
||||
// |________|
|
||||
|
||||
header := newArrayHeaderCode(alen)
|
||||
elemCode := &arrayElemCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
op: opArrayElem,
|
||||
},
|
||||
len: uintptr(alen),
|
||||
size: size,
|
||||
}
|
||||
end := newOpCode(opArrayEnd, nil, newEndOp())
|
||||
|
||||
header.elem = elemCode
|
||||
header.end = end
|
||||
header.next = code
|
||||
code.beforeLastCode().next = (*opcode)(unsafe.Pointer(elemCode))
|
||||
elemCode.next = code
|
||||
elemCode.end = end
|
||||
return (*opcode)(unsafe.Pointer(header)), nil
|
||||
}
|
||||
|
||||
func (e *Encoder) compileMapOp(typ *rtype) (*opcode, error) {
|
||||
// header => code => value => code => key => code => value => code => end
|
||||
// ^ |
|
||||
// |_______________________|
|
||||
keyType := typ.Key()
|
||||
keyCode, err := e.compileOp(keyType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valueType := typ.Elem()
|
||||
valueCode, err := e.compileOp(valueType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
header := newMapHeaderCode(typ)
|
||||
key := newMapKeyCode()
|
||||
value := newMapValueCode()
|
||||
header.key = key
|
||||
header.value = value
|
||||
end := newOpCode(opMapEnd, nil, newEndOp())
|
||||
|
||||
header.next = keyCode
|
||||
keyCode.beforeLastCode().next = (*opcode)(unsafe.Pointer(value))
|
||||
value.next = valueCode
|
||||
valueCode.beforeLastCode().next = (*opcode)(unsafe.Pointer(key))
|
||||
key.next = keyCode
|
||||
|
||||
header.end = end
|
||||
key.end = end
|
||||
|
||||
return (*opcode)(unsafe.Pointer(header)), nil
|
||||
}
|
||||
|
||||
|
|
141
encode_opcode.go
141
encode_opcode.go
|
@ -25,10 +25,18 @@ const (
|
|||
opFloat64
|
||||
opString
|
||||
opBool
|
||||
opInterface
|
||||
opPtr
|
||||
opSliceHead
|
||||
opSliceElem
|
||||
opSliceEnd
|
||||
opArrayHead
|
||||
opArrayElem
|
||||
opArrayEnd
|
||||
opMapHead
|
||||
opMapKey
|
||||
opMapValue
|
||||
opMapEnd
|
||||
opStructFieldHead
|
||||
opStructFieldHeadInt
|
||||
opStructFieldHeadInt8
|
||||
|
@ -109,6 +117,8 @@ func (t opType) String() string {
|
|||
return "STRING"
|
||||
case opBool:
|
||||
return "BOOL"
|
||||
case opInterface:
|
||||
return "INTERFACE"
|
||||
case opPtr:
|
||||
return "PTR"
|
||||
case opSliceHead:
|
||||
|
@ -117,6 +127,20 @@ func (t opType) String() string {
|
|||
return "SLICE_ELEM"
|
||||
case opSliceEnd:
|
||||
return "SLICE_END"
|
||||
case opArrayHead:
|
||||
return "ARRAY_HEAD"
|
||||
case opArrayElem:
|
||||
return "ARRAY_ELEM"
|
||||
case opArrayEnd:
|
||||
return "ARRAY_END"
|
||||
case opMapHead:
|
||||
return "MAP_HEAD"
|
||||
case opMapKey:
|
||||
return "MAP_KEY"
|
||||
case opMapValue:
|
||||
return "MAP_VALUE"
|
||||
case opMapEnd:
|
||||
return "MAP_END"
|
||||
case opStructFieldHead:
|
||||
return "STRUCT_FIELD_HEAD"
|
||||
case opStructFieldHeadInt:
|
||||
|
@ -242,9 +266,14 @@ func (c *opcode) beforeLastCode() *opcode {
|
|||
code := c
|
||||
for {
|
||||
var nextCode *opcode
|
||||
if code.op == opSliceElem {
|
||||
switch code.op {
|
||||
case opArrayElem:
|
||||
nextCode = code.toArrayElemCode().end
|
||||
case opSliceElem:
|
||||
nextCode = code.toSliceElemCode().end
|
||||
} else {
|
||||
case opMapKey:
|
||||
nextCode = code.toMapKeyCode().end
|
||||
default:
|
||||
nextCode = code.next
|
||||
}
|
||||
if nextCode.op == opEnd {
|
||||
|
@ -259,9 +288,14 @@ func (c *opcode) dump() string {
|
|||
codes := []string{}
|
||||
for code := c; code.op != opEnd; {
|
||||
codes = append(codes, fmt.Sprintf("%s", code.op))
|
||||
if code.op == opSliceElem {
|
||||
switch code.op {
|
||||
case opArrayElem:
|
||||
code = code.toArrayElemCode().end
|
||||
case opSliceElem:
|
||||
code = code.toSliceElemCode().end
|
||||
} else {
|
||||
case opMapKey:
|
||||
code = code.toMapKeyCode().end
|
||||
default:
|
||||
code = code.next
|
||||
}
|
||||
}
|
||||
|
@ -276,10 +310,30 @@ func (c *opcode) toSliceElemCode() *sliceElemCode {
|
|||
return (*sliceElemCode)(unsafe.Pointer(c))
|
||||
}
|
||||
|
||||
func (c *opcode) toArrayHeaderCode() *arrayHeaderCode {
|
||||
return (*arrayHeaderCode)(unsafe.Pointer(c))
|
||||
}
|
||||
|
||||
func (c *opcode) toArrayElemCode() *arrayElemCode {
|
||||
return (*arrayElemCode)(unsafe.Pointer(c))
|
||||
}
|
||||
|
||||
func (c *opcode) toStructFieldCode() *structFieldCode {
|
||||
return (*structFieldCode)(unsafe.Pointer(c))
|
||||
}
|
||||
|
||||
func (c *opcode) toMapHeadCode() *mapHeaderCode {
|
||||
return (*mapHeaderCode)(unsafe.Pointer(c))
|
||||
}
|
||||
|
||||
func (c *opcode) toMapKeyCode() *mapKeyCode {
|
||||
return (*mapKeyCode)(unsafe.Pointer(c))
|
||||
}
|
||||
|
||||
func (c *opcode) toMapValueCode() *mapValueCode {
|
||||
return (*mapValueCode)(unsafe.Pointer(c))
|
||||
}
|
||||
|
||||
type sliceHeaderCode struct {
|
||||
*opcodeHeader
|
||||
elem *sliceElemCode
|
||||
|
@ -309,6 +363,30 @@ func (c *sliceElemCode) set(header *reflect.SliceHeader) {
|
|||
c.data = header.Data
|
||||
}
|
||||
|
||||
type arrayHeaderCode struct {
|
||||
*opcodeHeader
|
||||
len uintptr
|
||||
elem *arrayElemCode
|
||||
end *opcode
|
||||
}
|
||||
|
||||
func newArrayHeaderCode(alen int) *arrayHeaderCode {
|
||||
return &arrayHeaderCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
op: opArrayHead,
|
||||
},
|
||||
len: uintptr(alen),
|
||||
}
|
||||
}
|
||||
|
||||
type arrayElemCode struct {
|
||||
*opcodeHeader
|
||||
idx uintptr
|
||||
len uintptr
|
||||
size uintptr
|
||||
end *opcode
|
||||
}
|
||||
|
||||
type structFieldCode struct {
|
||||
*opcodeHeader
|
||||
key []byte
|
||||
|
@ -316,3 +394,58 @@ type structFieldCode struct {
|
|||
nextField *opcode
|
||||
end *opcode
|
||||
}
|
||||
|
||||
type mapHeaderCode struct {
|
||||
*opcodeHeader
|
||||
key *mapKeyCode
|
||||
value *mapValueCode
|
||||
end *opcode
|
||||
}
|
||||
|
||||
type mapKeyCode struct {
|
||||
*opcodeHeader
|
||||
idx int
|
||||
len int
|
||||
iter unsafe.Pointer
|
||||
end *opcode
|
||||
}
|
||||
|
||||
func (c *mapKeyCode) set(len int, iter unsafe.Pointer) {
|
||||
c.idx = 0
|
||||
c.len = len
|
||||
c.iter = iter
|
||||
}
|
||||
|
||||
type mapValueCode struct {
|
||||
*opcodeHeader
|
||||
iter unsafe.Pointer
|
||||
}
|
||||
|
||||
func (c *mapValueCode) set(iter unsafe.Pointer) {
|
||||
c.iter = iter
|
||||
}
|
||||
|
||||
func newMapHeaderCode(typ *rtype) *mapHeaderCode {
|
||||
return &mapHeaderCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
op: opMapHead,
|
||||
typ: typ,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newMapKeyCode() *mapKeyCode {
|
||||
return &mapKeyCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
op: opMapKey,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newMapValueCode() *mapValueCode {
|
||||
return &mapValueCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
op: opMapValue,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,10 +93,18 @@ func Test_Encoder(t *testing.T) {
|
|||
assertEq(t, "struct", `{"a":-1,"b":1,"c":"hello world"}`, string(bytes))
|
||||
})
|
||||
t.Run("slice", func(t *testing.T) {
|
||||
bytes, err := json.Marshal([]int{1, 2, 3, 4})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "slice", `[1,2,3,4]`, string(bytes))
|
||||
t.Run("[]int", func(t *testing.T) {
|
||||
bytes, err := json.Marshal([]int{1, 2, 3, 4})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "[]int", `[1,2,3,4]`, string(bytes))
|
||||
})
|
||||
t.Run("[]interface{}", func(t *testing.T) {
|
||||
bytes, err := json.Marshal([]interface{}{1, 2.1, "hello"})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "[]interface{}", `[1,2.1,"hello"]`, string(bytes))
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("array", func(t *testing.T) {
|
||||
bytes, err := json.Marshal([4]int{1, 2, 3, 4})
|
||||
assertErr(t, err)
|
||||
|
|
91
encode_vm.go
91
encode_vm.go
|
@ -6,9 +6,6 @@ import (
|
|||
)
|
||||
|
||||
func (e *Encoder) run(code *opcode) error {
|
||||
//fmt.Println("================")
|
||||
//fmt.Println(code.dump())
|
||||
//fmt.Println("================")
|
||||
for {
|
||||
switch code.op {
|
||||
case opPtr:
|
||||
|
@ -57,6 +54,25 @@ func (e *Encoder) run(code *opcode) error {
|
|||
case opBool:
|
||||
e.encodeBool(e.ptrToBool(code.ptr))
|
||||
code = code.next
|
||||
case opInterface:
|
||||
ptr := code.ptr
|
||||
v := *(*interface{})(unsafe.Pointer(&interfaceHeader{
|
||||
typ: code.typ,
|
||||
ptr: unsafe.Pointer(ptr),
|
||||
}))
|
||||
vv := reflect.ValueOf(v).Interface()
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&vv))
|
||||
typ := header.typ
|
||||
if typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
}
|
||||
c, err := e.compileOp(typ)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.ptr = uintptr(header.ptr)
|
||||
c.beforeLastCode().next = code.next
|
||||
code = c
|
||||
case opSliceHead:
|
||||
p := code.ptr
|
||||
headerCode := code.toSliceHeaderCode()
|
||||
|
@ -66,7 +82,6 @@ func (e *Encoder) run(code *opcode) error {
|
|||
} else {
|
||||
e.encodeByte('[')
|
||||
header := (*reflect.SliceHeader)(unsafe.Pointer(p))
|
||||
headerCode := code.toSliceHeaderCode()
|
||||
headerCode.elem.set(header)
|
||||
if header.Len > 0 {
|
||||
code = code.next
|
||||
|
@ -87,6 +102,74 @@ func (e *Encoder) run(code *opcode) error {
|
|||
e.encodeByte(']')
|
||||
code = c.end.next
|
||||
}
|
||||
case opArrayHead:
|
||||
p := code.ptr
|
||||
headerCode := code.toArrayHeaderCode()
|
||||
if p == 0 {
|
||||
e.encodeString("null")
|
||||
code = headerCode.end.next
|
||||
} else {
|
||||
e.encodeByte('[')
|
||||
if headerCode.len > 0 {
|
||||
code = code.next
|
||||
code.ptr = p
|
||||
headerCode.elem.ptr = p
|
||||
} else {
|
||||
e.encodeByte(']')
|
||||
code = headerCode.end.next
|
||||
}
|
||||
}
|
||||
case opArrayElem:
|
||||
c := code.toArrayElemCode()
|
||||
c.idx++
|
||||
if c.idx < c.len {
|
||||
e.encodeByte(',')
|
||||
code = code.next
|
||||
code.ptr = c.ptr + c.idx*c.size
|
||||
} else {
|
||||
e.encodeByte(']')
|
||||
code = c.end.next
|
||||
}
|
||||
case opMapHead:
|
||||
ptr := code.ptr
|
||||
mapHeadCode := code.toMapHeadCode()
|
||||
if ptr == 0 {
|
||||
e.encodeString("null")
|
||||
code = mapHeadCode.end.next
|
||||
} else {
|
||||
e.encodeByte('{')
|
||||
mlen := maplen(unsafe.Pointer(ptr))
|
||||
if mlen > 0 {
|
||||
iter := mapiterinit(code.typ, unsafe.Pointer(ptr))
|
||||
mapHeadCode.key.set(mlen, iter)
|
||||
mapHeadCode.value.set(iter)
|
||||
key := mapiterkey(iter)
|
||||
code.next.ptr = uintptr(key)
|
||||
code = code.next
|
||||
} else {
|
||||
e.encodeByte('}')
|
||||
code = mapHeadCode.end.next
|
||||
}
|
||||
}
|
||||
case opMapKey:
|
||||
c := code.toMapKeyCode()
|
||||
c.idx++
|
||||
if c.idx < c.len {
|
||||
e.encodeByte(',')
|
||||
key := mapiterkey(c.iter)
|
||||
c.next.ptr = uintptr(key)
|
||||
code = c.next
|
||||
} else {
|
||||
e.encodeByte('}')
|
||||
code = c.end.next
|
||||
}
|
||||
case opMapValue:
|
||||
e.encodeByte(':')
|
||||
c := code.toMapValueCode()
|
||||
value := mapitervalue(c.iter)
|
||||
c.next.ptr = uintptr(value)
|
||||
mapiternext(c.iter)
|
||||
code = c.next
|
||||
case opStructFieldPtrHead:
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
fallthrough
|
||||
|
|
Loading…
Reference in New Issue