Skip to content

Commit

Permalink
Support Map and Interfacfe{} type for encoding of vm based
Browse files Browse the repository at this point in the history
  • Loading branch information
goccy committed Apr 30, 2020
1 parent 95b2194 commit 090887b
Show file tree
Hide file tree
Showing 4 changed files with 309 additions and 14 deletions.
77 changes: 74 additions & 3 deletions encode_compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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()
Expand All @@ -169,15 +177,78 @@ 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
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) 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
end.next = newEndOp()
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
}

Expand Down
141 changes: 137 additions & 4 deletions encode_opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,18 @@ const (
opFloat64
opString
opBool
opInterface
opPtr
opSliceHead
opSliceElem
opSliceEnd
opArrayHead
opArrayElem
opArrayEnd
opMapHead
opMapKey
opMapValue
opMapEnd
opStructFieldHead
opStructFieldHeadInt
opStructFieldHeadInt8
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
}
Expand All @@ -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
Expand Down Expand Up @@ -309,10 +363,89 @@ 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
offset uintptr
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,
},
}
}
14 changes: 11 additions & 3 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading

0 comments on commit 090887b

Please sign in to comment.