Add VirtualMachine for Encoder

This commit is contained in:
Masaaki Goshima 2020-04-30 00:31:50 +09:00
parent 3b27b5f267
commit 4f3b1262b2
4 changed files with 602 additions and 11 deletions

View File

@ -29,14 +29,14 @@ type EncodeOpMap struct {
sync.Map sync.Map
} }
func (m *EncodeOpMap) Get(k string) EncodeOp { func (m *EncodeOpMap) Get(k string) *opcode { //EncodeOp {
if v, ok := m.Load(k); ok { if v, ok := m.Load(k); ok {
return v.(EncodeOp) return v.(*opcode) //(EncodeOp)
} }
return nil return nil
} }
func (m *EncodeOpMap) Set(k string, op EncodeOp) { func (m *EncodeOpMap) Set(k string, op *opcode) { // EncodeOp) {
m.Store(k, op) m.Store(k, op)
} }
@ -181,17 +181,23 @@ func (e *Encoder) encode(v interface{}) error {
name := typ.String() name := typ.String()
if op := cachedEncodeOp.Get(name); op != nil { if op := cachedEncodeOp.Get(name); op != nil {
p := uintptr(header.ptr) p := uintptr(header.ptr)
op(e, typ, p) op.ptr = p
if err := e.run(op); err != nil {
return err
}
//op(e, typ, p)
return nil return nil
} }
op, err := e.compile(typ) op, err := e.compileOp(typ)
if err != nil { if err != nil {
if err == errCompileSlowPath { if err == errCompileSlowPath {
/*
slowOp, err := e.compileSlowPath(typ) slowOp, err := e.compileSlowPath(typ)
if err != nil { if err != nil {
return err return err
} }
op = slowOp op = slowOp
*/
} else { } else {
return err return err
} }
@ -200,7 +206,9 @@ func (e *Encoder) encode(v interface{}) error {
cachedEncodeOp.Set(name, op) cachedEncodeOp.Set(name, op)
} }
p := uintptr(header.ptr) p := uintptr(header.ptr)
op(e, typ, p) op.ptr = p
e.run(op)
//op(e, typ, p)
return nil return nil
} }

233
encode_compile.go Normal file
View File

@ -0,0 +1,233 @@
package json
import (
"fmt"
"reflect"
"strings"
"unsafe"
"golang.org/x/xerrors"
)
func (e *Encoder) compileOp(typ *rtype) (*opcode, error) {
switch typ.Kind() {
case reflect.Ptr:
return e.compilePtrOp(typ)
case reflect.Slice:
return e.compileSliceOp(typ)
case reflect.Struct:
return e.compileStructOp(typ)
case reflect.Int:
return e.compileIntOp(typ)
case reflect.Int8:
return e.compileInt8Op(typ)
case reflect.Int16:
return e.compileInt16Op(typ)
case reflect.Int32:
return e.compileInt32Op(typ)
case reflect.Int64:
return e.compileInt64Op(typ)
case reflect.Uint:
return e.compileUintOp(typ)
case reflect.Uint8:
return e.compileUint8Op(typ)
case reflect.Uint16:
return e.compileUint16Op(typ)
case reflect.Uint32:
return e.compileUint32Op(typ)
case reflect.Uint64:
return e.compileUint64Op(typ)
case reflect.Uintptr:
return e.compileUintOp(typ)
case reflect.Float32:
return e.compileFloat32Op(typ)
case reflect.Float64:
return e.compileFloat64Op(typ)
case reflect.String:
return e.compileStringOp(typ)
case reflect.Bool:
return e.compileBoolOp(typ)
case reflect.Interface:
return nil, errCompileSlowPath
}
return nil, xerrors.Errorf("failed to encode type %s: %w", typ.String(), ErrUnsupportedType)
}
func (e *Encoder) compilePtrOp(typ *rtype) (*opcode, error) {
elem := typ.Elem()
code, err := e.compileOp(elem)
if err != nil {
return nil, err
}
return &opcode{
opcodeHeader: &opcodeHeader{
op: opPtr,
typ: typ,
next: code,
},
}, nil
}
func (e *Encoder) compileIntOp(typ *rtype) (*opcode, error) {
return newOpCode(opInt, typ, newEndOp()), nil
}
func (e *Encoder) compileInt8Op(typ *rtype) (*opcode, error) {
return newOpCode(opInt8, typ, newEndOp()), nil
}
func (e *Encoder) compileInt16Op(typ *rtype) (*opcode, error) {
return newOpCode(opInt16, typ, newEndOp()), nil
}
func (e *Encoder) compileInt32Op(typ *rtype) (*opcode, error) {
return newOpCode(opInt32, typ, newEndOp()), nil
}
func (e *Encoder) compileInt64Op(typ *rtype) (*opcode, error) {
return newOpCode(opInt64, typ, newEndOp()), nil
}
func (e *Encoder) compileUintOp(typ *rtype) (*opcode, error) {
return newOpCode(opUint, typ, newEndOp()), nil
}
func (e *Encoder) compileUint8Op(typ *rtype) (*opcode, error) {
return newOpCode(opUint8, typ, newEndOp()), nil
}
func (e *Encoder) compileUint16Op(typ *rtype) (*opcode, error) {
return newOpCode(opUint16, typ, newEndOp()), nil
}
func (e *Encoder) compileUint32Op(typ *rtype) (*opcode, error) {
return newOpCode(opUint32, typ, newEndOp()), nil
}
func (e *Encoder) compileUint64Op(typ *rtype) (*opcode, error) {
return newOpCode(opUint64, typ, newEndOp()), nil
}
func (e *Encoder) compileFloat32Op(typ *rtype) (*opcode, error) {
return newOpCode(opFloat32, typ, newEndOp()), nil
}
func (e *Encoder) compileFloat64Op(typ *rtype) (*opcode, error) {
return newOpCode(opFloat64, typ, newEndOp()), nil
}
func (e *Encoder) compileStringOp(typ *rtype) (*opcode, error) {
return newOpCode(opString, typ, newEndOp()), nil
}
func (e *Encoder) compileBoolOp(typ *rtype) (*opcode, error) {
return newOpCode(opBool, typ, newEndOp()), nil
}
func (e *Encoder) compileSliceOp(typ *rtype) (*opcode, error) {
elem := typ.Elem()
size := elem.Size()
code, err := e.compileOp(elem)
if err != nil {
return nil, err
}
// header => firstElem => opcode => elem => end
// ^ |
// |________|
header := &opcode{opcodeHeader: &opcodeHeader{op: opSliceHead}}
firstElem := &sliceElemCode{opcodeHeader: &opcodeHeader{op: opSliceElemFirst}}
elemCode := &sliceElemCode{opcodeHeader: &opcodeHeader{op: opSliceElem}, size: size}
end := &opcode{opcodeHeader: &opcodeHeader{op: opSliceEnd}}
header.next = (*opcode)(unsafe.Pointer(firstElem))
firstElem.next = code
firstElem.elem = elemCode
code.beforeLastCode().next = (*opcode)(unsafe.Pointer(elemCode))
elemCode.next = code
elemCode.end = end
end.next = &opcode{opcodeHeader: &opcodeHeader{op: opEnd}}
return (*opcode)(unsafe.Pointer(header)), nil
}
func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) {
// header => firstField => structField => end
// ^ |
// |________|
fieldNum := typ.NumField()
fieldIdx := 0
header := &opcode{opcodeHeader: &opcodeHeader{op: opStructHead}}
code := header
var prevField *structFieldCode
for i := 0; i < fieldNum; i++ {
field := typ.Field(i)
if e.isIgnoredStructField(field) {
continue
}
keyName := field.Name
tag := e.getTag(field)
opts := strings.Split(tag, ",")
if len(opts) > 0 {
if opts[0] != "" {
keyName = opts[0]
}
}
fieldType := type2rtype(field.Type)
valueCode, err := e.compileOp(fieldType)
if err != nil {
return nil, err
}
key := fmt.Sprintf(`"%s":`, keyName)
if fieldIdx == 0 {
fieldCode := &structFieldCode{
opcodeHeader: &opcodeHeader{
op: opStructFieldFirst,
typ: fieldType,
next: valueCode,
},
key: key,
offset: field.Offset,
}
code.next = (*opcode)(unsafe.Pointer(fieldCode))
prevField = fieldCode
if valueCode.op == opInt {
fieldCode.op = opStructFieldFirstInt
code = (*opcode)(unsafe.Pointer(fieldCode))
} else if valueCode.op == opString {
fieldCode.op = opStructFieldFirstString
code = (*opcode)(unsafe.Pointer(fieldCode))
} else {
code = valueCode.beforeLastCode()
}
} else {
fieldCode := &structFieldCode{
opcodeHeader: &opcodeHeader{
op: opStructField,
typ: fieldType,
next: valueCode,
},
key: key,
offset: field.Offset,
}
code.next = (*opcode)(unsafe.Pointer(fieldCode))
prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode))
prevField = fieldCode
if valueCode.op == opInt {
fieldCode.op = opStructFieldInt
code = (*opcode)(unsafe.Pointer(fieldCode))
} else if valueCode.op == opString {
fieldCode.op = opStructFieldString
code = (*opcode)(unsafe.Pointer(fieldCode))
} else {
code = valueCode.beforeLastCode()
}
}
prevField.nextField = &opcode{opcodeHeader: &opcodeHeader{op: opEnd}}
fieldIdx++
}
structEndCode := &opcode{opcodeHeader: &opcodeHeader{op: opStructEnd}}
code.next = structEndCode
structEndCode.next = &opcode{opcodeHeader: &opcodeHeader{op: opEnd}}
return header, nil
}

198
encode_opcode.go Normal file
View File

@ -0,0 +1,198 @@
package json
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
type opType int
const (
opEnd opType = iota
opInt
opInt8
opInt16
opInt32
opInt64
opUint
opUint8
opUint16
opUint32
opUint64
opFloat32
opFloat64
opString
opBool
opPtr
opSliceHead
opSliceElemFirst
opSliceElem
opSliceEnd
opStructHead
opStructFieldFirst
opStructField
opStructFieldFirstInt
opStructFieldInt
opStructFieldFirstString
opStructFieldString
opStructEnd
)
func (t opType) String() string {
switch t {
case opEnd:
return "END"
case opInt:
return "INT"
case opInt8:
return "INT8"
case opInt16:
return "INT16"
case opInt32:
return "INT32"
case opInt64:
return "INT64"
case opUint:
return "UINT"
case opUint8:
return "UINT8"
case opUint16:
return "UINT16"
case opUint32:
return "UINT32"
case opUint64:
return "UINT64"
case opFloat32:
return "FLOAT32"
case opFloat64:
return "FLOAT64"
case opString:
return "STRING"
case opBool:
return "BOOL"
case opPtr:
return "PTR"
case opSliceHead:
return "SLICE_HEAD"
case opSliceElemFirst:
return "SLICE_ELEM_FIRST"
case opSliceElem:
return "SLICE_ELEM"
case opSliceEnd:
return "SLICE_END"
case opStructHead:
return "STRUCT_HEAD"
case opStructFieldFirst:
return "STRUCT_FIELD_FIRST"
case opStructField:
return "STRUCT_FIELD"
case opStructFieldFirstInt:
return "STRUCT_FIELD_FIRST_INT"
case opStructFieldInt:
return "STRUCT_FIELD_INT"
case opStructFieldFirstString:
return "STRUCT_FIELD_FIRST_STRING"
case opStructFieldString:
return "STRUCT_FIELD_STRING"
case opStructEnd:
return "STRUCT_END"
}
return ""
}
type opcodeHeader struct {
op opType
typ *rtype
ptr uintptr
next *opcode
}
type opcode struct {
*opcodeHeader
}
func newOpCode(op opType, typ *rtype, next *opcode) *opcode {
return &opcode{
opcodeHeader: &opcodeHeader{
op: op,
typ: typ,
next: next,
},
}
}
func newEndOp() *opcode {
return newOpCode(opEnd, nil, nil)
}
func (c *opcode) beforeLastCode() *opcode {
code := c
for {
var nextCode *opcode
if code.op == opSliceElem {
nextCode = code.toSliceElemCode().end
} else {
nextCode = code.next
}
if nextCode.op == opEnd {
return code
}
code = nextCode
}
return nil
}
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 {
code = code.toSliceElemCode().end
} else {
code = code.next
}
}
return strings.Join(codes, "\n")
}
func (c *opcode) toSliceElemCode() *sliceElemCode {
return (*sliceElemCode)(unsafe.Pointer(c))
}
func (c *opcode) toStructHeaderCode() *structHeaderCode {
return (*structHeaderCode)(unsafe.Pointer(c))
}
func (c *opcode) toStructFieldCode() *structFieldCode {
return (*structFieldCode)(unsafe.Pointer(c))
}
type sliceElemCode struct {
*opcodeHeader
idx uintptr
len uintptr
size uintptr
data uintptr
elem *sliceElemCode // first => elem
end *opcode
}
func (c *sliceElemCode) set(header *reflect.SliceHeader) {
c.idx = uintptr(0)
c.len = uintptr(header.Len)
c.data = header.Data
}
type structHeaderCode struct {
*opcodeHeader
end *opcode
}
type structFieldCode struct {
*opcodeHeader
key string
offset uintptr
nextField *opcode
}

152
encode_vm.go Normal file
View File

@ -0,0 +1,152 @@
package json
import (
"reflect"
"unsafe"
)
func (e *Encoder) run(code *opcode) error {
//fmt.Println("================")
//fmt.Println(code.dump())
//fmt.Println("================")
for {
switch code.op {
case opPtr:
ptr := code.ptr
code = code.next
code.ptr = e.ptrToPtr(ptr)
case opInt:
e.encodeInt(e.ptrToInt(code.ptr))
code = code.next
case opInt8:
e.encodeInt8(e.ptrToInt8(code.ptr))
code = code.next
case opInt16:
e.encodeInt16(e.ptrToInt16(code.ptr))
code = code.next
case opInt32:
e.encodeInt32(e.ptrToInt32(code.ptr))
code = code.next
case opInt64:
e.encodeInt64(e.ptrToInt64(code.ptr))
code = code.next
case opUint:
e.encodeUint(e.ptrToUint(code.ptr))
code = code.next
case opUint8:
e.encodeUint8(e.ptrToUint8(code.ptr))
code = code.next
case opUint16:
e.encodeUint16(e.ptrToUint16(code.ptr))
code = code.next
case opUint32:
e.encodeUint32(e.ptrToUint32(code.ptr))
code = code.next
case opUint64:
e.encodeUint64(e.ptrToUint64(code.ptr))
code = code.next
case opFloat32:
e.encodeFloat32(e.ptrToFloat32(code.ptr))
code = code.next
case opFloat64:
e.encodeFloat64(e.ptrToFloat64(code.ptr))
code = code.next
case opString:
e.encodeEscapedString(e.ptrToString(code.ptr))
code = code.next
case opBool:
e.encodeBool(e.ptrToBool(code.ptr))
code = code.next
case opSliceHead:
p := code.ptr
if p == 0 {
e.encodeString("null")
code = code.next.toSliceElemCode().end
} else {
e.encodeByte('[')
header := (*reflect.SliceHeader)(unsafe.Pointer(p))
firstElem := code.next.toSliceElemCode()
firstElem.set(header)
firstElem.elem.set(header)
code = code.next
}
case opSliceElemFirst:
c := code.toSliceElemCode()
if c.len > 0 {
code = code.next
code.ptr = c.data
} else {
code = c.end
}
case opSliceElem:
c := code.toSliceElemCode()
c.idx++
if c.idx < c.len {
e.encodeByte(',')
code = code.next
code.ptr = c.data + c.idx*c.size
} else {
code = c.end
}
case opSliceEnd:
e.encodeByte(']')
code = code.next
case opStructHead:
ptr := code.ptr
if ptr == 0 {
e.encodeString("null")
code = code.toStructHeaderCode().end
} else {
e.encodeByte('{')
code = code.next
code.ptr = ptr
}
case opStructFieldFirst:
c := code.toStructFieldCode()
e.encodeString(c.key)
code = code.next
code.ptr = c.ptr + c.offset
c.nextField.ptr = c.ptr
case opStructFieldFirstInt:
c := code.toStructFieldCode()
e.encodeString(c.key)
c.nextField.ptr = c.ptr
e.encodeInt(e.ptrToInt(c.ptr + c.offset))
code = code.next
case opStructFieldFirstString:
c := code.toStructFieldCode()
e.encodeString(c.key)
c.nextField.ptr = c.ptr
e.encodeEscapedString(e.ptrToString(c.ptr + c.offset))
code = code.next
case opStructField:
e.encodeByte(',')
c := code.toStructFieldCode()
e.encodeString(c.key)
code = code.next
code.ptr = c.ptr + c.offset
c.nextField.ptr = c.ptr
case opStructFieldInt:
e.encodeByte(',')
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeString(c.key)
e.encodeInt(e.ptrToInt(c.ptr + c.offset))
code = code.next
case opStructFieldString:
e.encodeByte(',')
c := code.toStructFieldCode()
c.nextField.ptr = c.ptr
e.encodeString(c.key)
e.encodeEscapedString(e.ptrToString(c.ptr + c.offset))
code = code.next
case opStructEnd:
e.encodeByte('}')
code = code.next
case opEnd:
goto END
}
}
END:
return nil
}