Support Map and Interfacfe{} type for encoding of vm based

This commit is contained in:
Masaaki Goshima 2020-04-30 13:39:47 +09:00
parent 95b2194742
commit 090887bf7e
4 changed files with 309 additions and 14 deletions

View File

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

View File

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

View File

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

View File

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