From 4f3b1262b28ab449e002428a4dc7584d138699c3 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Thu, 30 Apr 2020 00:31:50 +0900 Subject: [PATCH 1/5] Add VirtualMachine for Encoder --- encode.go | 30 +++--- encode_compile.go | 233 ++++++++++++++++++++++++++++++++++++++++++++++ encode_opcode.go | 198 +++++++++++++++++++++++++++++++++++++++ encode_vm.go | 152 ++++++++++++++++++++++++++++++ 4 files changed, 602 insertions(+), 11 deletions(-) create mode 100644 encode_compile.go create mode 100644 encode_opcode.go create mode 100644 encode_vm.go diff --git a/encode.go b/encode.go index ebf49f7..4e7cb15 100644 --- a/encode.go +++ b/encode.go @@ -29,14 +29,14 @@ type EncodeOpMap struct { sync.Map } -func (m *EncodeOpMap) Get(k string) EncodeOp { +func (m *EncodeOpMap) Get(k string) *opcode { //EncodeOp { if v, ok := m.Load(k); ok { - return v.(EncodeOp) + return v.(*opcode) //(EncodeOp) } return nil } -func (m *EncodeOpMap) Set(k string, op EncodeOp) { +func (m *EncodeOpMap) Set(k string, op *opcode) { // EncodeOp) { m.Store(k, op) } @@ -181,17 +181,23 @@ func (e *Encoder) encode(v interface{}) error { name := typ.String() if op := cachedEncodeOp.Get(name); op != nil { 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 } - op, err := e.compile(typ) + op, err := e.compileOp(typ) if err != nil { if err == errCompileSlowPath { - slowOp, err := e.compileSlowPath(typ) - if err != nil { - return err - } - op = slowOp + /* + slowOp, err := e.compileSlowPath(typ) + if err != nil { + return err + } + op = slowOp + */ } else { return err } @@ -200,7 +206,9 @@ func (e *Encoder) encode(v interface{}) error { cachedEncodeOp.Set(name, op) } p := uintptr(header.ptr) - op(e, typ, p) + op.ptr = p + e.run(op) + //op(e, typ, p) return nil } diff --git a/encode_compile.go b/encode_compile.go new file mode 100644 index 0000000..7be73c9 --- /dev/null +++ b/encode_compile.go @@ -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 +} diff --git a/encode_opcode.go b/encode_opcode.go new file mode 100644 index 0000000..992ed58 --- /dev/null +++ b/encode_opcode.go @@ -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 +} diff --git a/encode_vm.go b/encode_vm.go new file mode 100644 index 0000000..1695904 --- /dev/null +++ b/encode_vm.go @@ -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 +} From 40544f1ea20be26f477cb624091197df247c7a3b Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Thu, 30 Apr 2020 01:44:48 +0900 Subject: [PATCH 2/5] Optimize opcode --- encode_compile.go | 82 +++++++++++++++++++++------------------- encode_opcode.go | 61 ++++++++++++++++++------------ encode_vm.go | 96 +++++++++++++++++++++++++++-------------------- 3 files changed, 137 insertions(+), 102 deletions(-) diff --git a/encode_compile.go b/encode_compile.go index 7be73c9..83f89b9 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -54,18 +54,21 @@ func (e *Encoder) compileOp(typ *rtype) (*opcode, error) { } func (e *Encoder) compilePtrOp(typ *rtype) (*opcode, error) { - elem := typ.Elem() - code, err := e.compileOp(elem) + code, err := e.compileOp(typ.Elem()) if err != nil { return nil, err } - return &opcode{ - opcodeHeader: &opcodeHeader{ - op: opPtr, - typ: typ, - next: code, - }, - }, nil + switch code.op { + case opStructFieldHead: + code.op = opStructFieldPtrHead + case opStructFieldHeadInt: + code.op = opStructFieldPtrHeadInt + case opStructFieldHeadString: + code.op = opStructFieldPtrHeadString + default: + return newOpCode(opPtr, typ, code), nil + } + return code, nil } func (e *Encoder) compileIntOp(typ *rtype) (*opcode, error) { @@ -132,22 +135,21 @@ func (e *Encoder) compileSliceOp(typ *rtype) (*opcode, error) { return nil, err } - // header => firstElem => opcode => elem => end - // ^ | - // |________| + // header => opcode => elem => end + // ^ | + // |________| - header := &opcode{opcodeHeader: &opcodeHeader{op: opSliceHead}} - firstElem := &sliceElemCode{opcodeHeader: &opcodeHeader{op: opSliceElemFirst}} + header := newSliceHeaderCode() elemCode := &sliceElemCode{opcodeHeader: &opcodeHeader{op: opSliceElem}, size: size} - end := &opcode{opcodeHeader: &opcodeHeader{op: opSliceEnd}} + end := newOpCode(opSliceEnd, nil, nil) - header.next = (*opcode)(unsafe.Pointer(firstElem)) - firstElem.next = code - firstElem.elem = elemCode + header.elem = elemCode + header.end = end + header.next = code code.beforeLastCode().next = (*opcode)(unsafe.Pointer(elemCode)) elemCode.next = code elemCode.end = end - end.next = &opcode{opcodeHeader: &opcodeHeader{op: opEnd}} + end.next = newEndOp() return (*opcode)(unsafe.Pointer(header)), nil } @@ -157,9 +159,11 @@ func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) { // |________| fieldNum := typ.NumField() fieldIdx := 0 - header := &opcode{opcodeHeader: &opcodeHeader{op: opStructHead}} - code := header - var prevField *structFieldCode + var ( + head *structFieldCode + code *opcode + prevField *structFieldCode + ) for i := 0; i < fieldNum; i++ { field := typ.Field(i) if e.isIgnoredStructField(field) { @@ -182,22 +186,22 @@ func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) { if fieldIdx == 0 { fieldCode := &structFieldCode{ opcodeHeader: &opcodeHeader{ - op: opStructFieldFirst, + op: opStructFieldHead, typ: fieldType, next: valueCode, }, key: key, offset: field.Offset, } - code.next = (*opcode)(unsafe.Pointer(fieldCode)) + head = fieldCode + code = (*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 { + switch valueCode.op { + case opInt: + fieldCode.op = opStructFieldHeadInt + case opString: + fieldCode.op = opStructFieldHeadString + default: code = valueCode.beforeLastCode() } } else { @@ -213,21 +217,23 @@ func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) { code.next = (*opcode)(unsafe.Pointer(fieldCode)) prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode)) prevField = fieldCode - if valueCode.op == opInt { + switch valueCode.op { + case opInt: fieldCode.op = opStructFieldInt code = (*opcode)(unsafe.Pointer(fieldCode)) - } else if valueCode.op == opString { + case opString: fieldCode.op = opStructFieldString code = (*opcode)(unsafe.Pointer(fieldCode)) - } else { + default: code = valueCode.beforeLastCode() } } - prevField.nextField = &opcode{opcodeHeader: &opcodeHeader{op: opEnd}} + prevField.nextField = newEndOp() fieldIdx++ } - structEndCode := &opcode{opcodeHeader: &opcodeHeader{op: opStructEnd}} + structEndCode := newOpCode(opStructEnd, nil, nil) + head.end = structEndCode code.next = structEndCode - structEndCode.next = &opcode{opcodeHeader: &opcodeHeader{op: opEnd}} - return header, nil + structEndCode.next = newEndOp() + return (*opcode)(unsafe.Pointer(head)), nil } diff --git a/encode_opcode.go b/encode_opcode.go index 992ed58..81a789c 100644 --- a/encode_opcode.go +++ b/encode_opcode.go @@ -27,15 +27,16 @@ const ( opBool opPtr opSliceHead - opSliceElemFirst opSliceElem opSliceEnd - opStructHead - opStructFieldFirst + opStructFieldHead + opStructFieldHeadInt + opStructFieldHeadString + opStructFieldPtrHead + opStructFieldPtrHeadInt + opStructFieldPtrHeadString opStructField - opStructFieldFirstInt opStructFieldInt - opStructFieldFirstString opStructFieldString opStructEnd ) @@ -76,24 +77,26 @@ func (t opType) String() string { 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 opStructFieldHead: + return "STRUCT_FIELD_HEAD" + case opStructFieldHeadInt: + return "STRUCT_FIELD_HEAD_INT" + case opStructFieldHeadString: + return "STRUCT_FIELD_HEAD_STRING" + case opStructFieldPtrHead: + return "STRUCT_FIELD_PTR_HEAD" + case opStructFieldPtrHeadInt: + return "STRUCT_FIELD_PTR_HEAD_INT" + case opStructFieldPtrHeadString: + return "STRUCT_FIELD_PTR_HEAD_STRING" 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: @@ -157,18 +160,32 @@ func (c *opcode) dump() string { return strings.Join(codes, "\n") } +func (c *opcode) toSliceHeaderCode() *sliceHeaderCode { + return (*sliceHeaderCode)(unsafe.Pointer(c)) +} + 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 sliceHeaderCode struct { + *opcodeHeader + elem *sliceElemCode + end *opcode +} + +func newSliceHeaderCode() *sliceHeaderCode { + return &sliceHeaderCode{ + opcodeHeader: &opcodeHeader{ + op: opSliceHead, + }, + } +} + type sliceElemCode struct { *opcodeHeader idx uintptr @@ -185,14 +202,10 @@ func (c *sliceElemCode) set(header *reflect.SliceHeader) { c.data = header.Data } -type structHeaderCode struct { - *opcodeHeader - end *opcode -} - type structFieldCode struct { *opcodeHeader key string offset uintptr nextField *opcode + end *opcode } diff --git a/encode_vm.go b/encode_vm.go index 1695904..c122b36 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -59,24 +59,22 @@ func (e *Encoder) run(code *opcode) error { code = code.next case opSliceHead: p := code.ptr + headerCode := code.toSliceHeaderCode() if p == 0 { e.encodeString("null") - code = code.next.toSliceElemCode().end + code = headerCode.end.next } 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 + headerCode := code.toSliceHeaderCode() + headerCode.elem.set(header) + if header.Len > 0 { + code = code.next + code.ptr = header.Data + } else { + e.encodeByte(']') + code = headerCode.end.next + } } case opSliceElem: c := code.toSliceElemCode() @@ -86,39 +84,57 @@ func (e *Encoder) run(code *opcode) error { code = code.next code.ptr = c.data + c.idx*c.size } else { - code = c.end + e.encodeByte(']') + code = c.end.next } - case opSliceEnd: - e.encodeByte(']') - code = code.next - case opStructHead: - ptr := code.ptr + case opStructFieldPtrHead: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHead: + field := code.toStructFieldCode() + ptr := field.ptr if ptr == 0 { e.encodeString("null") - code = code.toStructHeaderCode().end + code = field.end } else { e.encodeByte('{') - code = code.next - code.ptr = ptr + e.encodeString(field.key) + code = field.next + code.ptr = field.ptr + field.offset + field.nextField.ptr = field.ptr + } + case opStructFieldPtrHeadInt: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeString(field.key) + e.encodeInt(e.ptrToInt(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadString: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadString: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeString(field.key) + e.encodeEscapedString(e.ptrToString(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next } - 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() From 95b2194742b86054cdb069f22afa05da3e2e00b8 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Thu, 30 Apr 2020 11:56:56 +0900 Subject: [PATCH 3/5] Add optimized code --- encode.go | 4 + encode_compile.go | 127 +++++++++++++++----- encode_opcode.go | 111 +++++++++++++++++- encode_vm.go | 288 +++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 492 insertions(+), 38 deletions(-) diff --git a/encode.go b/encode.go index 4e7cb15..8f7cb17 100644 --- a/encode.go +++ b/encode.go @@ -163,6 +163,10 @@ func (e *Encoder) encodeBool(v bool) { e.buf = strconv.AppendBool(e.buf, v) } +func (e *Encoder) encodeBytes(b []byte) { + e.buf = append(e.buf, b...) +} + func (e *Encoder) encodeString(s string) { b := *(*[]byte)(unsafe.Pointer(&s)) e.buf = append(e.buf, b...) diff --git a/encode_compile.go b/encode_compile.go index 83f89b9..b4c3102 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -53,22 +53,50 @@ func (e *Encoder) compileOp(typ *rtype) (*opcode, error) { return nil, xerrors.Errorf("failed to encode type %s: %w", typ.String(), ErrUnsupportedType) } -func (e *Encoder) compilePtrOp(typ *rtype) (*opcode, error) { - code, err := e.compileOp(typ.Elem()) - if err != nil { - return nil, err - } +func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode { switch code.op { case opStructFieldHead: code.op = opStructFieldPtrHead case opStructFieldHeadInt: code.op = opStructFieldPtrHeadInt + case opStructFieldHeadInt8: + code.op = opStructFieldPtrHeadInt8 + case opStructFieldHeadInt16: + code.op = opStructFieldPtrHeadInt16 + case opStructFieldHeadInt32: + code.op = opStructFieldPtrHeadInt32 + case opStructFieldHeadInt64: + code.op = opStructFieldPtrHeadInt64 + case opStructFieldHeadUint: + code.op = opStructFieldPtrHeadUint + case opStructFieldHeadUint8: + code.op = opStructFieldPtrHeadUint8 + case opStructFieldHeadUint16: + code.op = opStructFieldPtrHeadUint16 + case opStructFieldHeadUint32: + code.op = opStructFieldPtrHeadUint32 + case opStructFieldHeadUint64: + code.op = opStructFieldPtrHeadUint64 + case opStructFieldHeadFloat32: + code.op = opStructFieldPtrHeadFloat32 + case opStructFieldHeadFloat64: + code.op = opStructFieldPtrHeadFloat64 case opStructFieldHeadString: code.op = opStructFieldPtrHeadString + case opStructFieldHeadBool: + code.op = opStructFieldPtrHeadBool default: - return newOpCode(opPtr, typ, code), nil + return newOpCode(opPtr, typ, code) } - return code, nil + return code +} + +func (e *Encoder) compilePtrOp(typ *rtype) (*opcode, error) { + code, err := e.compileOp(typ.Elem()) + if err != nil { + return nil, err + } + return e.optimizeStructFieldPtrHead(typ, code), nil } func (e *Encoder) compileIntOp(typ *rtype) (*opcode, error) { @@ -154,9 +182,9 @@ func (e *Encoder) compileSliceOp(typ *rtype) (*opcode, error) { } func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) { - // header => firstField => structField => end - // ^ | - // |________| + // header => code => structField => code => end + // ^ | + // |__________| fieldNum := typ.NumField() fieldIdx := 0 var ( @@ -183,47 +211,86 @@ func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) { return nil, err } key := fmt.Sprintf(`"%s":`, keyName) + fieldCode := &structFieldCode{ + opcodeHeader: &opcodeHeader{ + typ: fieldType, + next: valueCode, + }, + key: []byte(key), + offset: field.Offset, + } if fieldIdx == 0 { - fieldCode := &structFieldCode{ - opcodeHeader: &opcodeHeader{ - op: opStructFieldHead, - typ: fieldType, - next: valueCode, - }, - key: key, - offset: field.Offset, - } + fieldCode.op = opStructFieldHead head = fieldCode code = (*opcode)(unsafe.Pointer(fieldCode)) prevField = fieldCode switch valueCode.op { case opInt: fieldCode.op = opStructFieldHeadInt + case opInt8: + fieldCode.op = opStructFieldHeadInt8 + case opInt16: + fieldCode.op = opStructFieldHeadInt16 + case opInt32: + fieldCode.op = opStructFieldHeadInt32 + case opInt64: + fieldCode.op = opStructFieldHeadInt64 + case opUint: + fieldCode.op = opStructFieldHeadUint + case opUint8: + fieldCode.op = opStructFieldHeadUint8 + case opUint16: + fieldCode.op = opStructFieldHeadUint16 + case opUint32: + fieldCode.op = opStructFieldHeadUint32 + case opUint64: + fieldCode.op = opStructFieldHeadUint64 + case opFloat32: + fieldCode.op = opStructFieldHeadFloat32 + case opFloat64: + fieldCode.op = opStructFieldHeadFloat64 case opString: fieldCode.op = opStructFieldHeadString + case opBool: + fieldCode.op = opStructFieldHeadBool default: code = valueCode.beforeLastCode() } } else { - fieldCode := &structFieldCode{ - opcodeHeader: &opcodeHeader{ - op: opStructField, - typ: fieldType, - next: valueCode, - }, - key: key, - offset: field.Offset, - } + fieldCode.op = opStructField code.next = (*opcode)(unsafe.Pointer(fieldCode)) prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode)) prevField = fieldCode + code = (*opcode)(unsafe.Pointer(fieldCode)) switch valueCode.op { case opInt: fieldCode.op = opStructFieldInt - code = (*opcode)(unsafe.Pointer(fieldCode)) + case opInt8: + fieldCode.op = opStructFieldInt8 + case opInt16: + fieldCode.op = opStructFieldInt16 + case opInt32: + fieldCode.op = opStructFieldInt32 + case opInt64: + fieldCode.op = opStructFieldInt64 + case opUint: + fieldCode.op = opStructFieldUint + case opUint8: + fieldCode.op = opStructFieldUint8 + case opUint16: + fieldCode.op = opStructFieldUint16 + case opUint32: + fieldCode.op = opStructFieldUint32 + case opUint64: + fieldCode.op = opStructFieldUint64 + case opFloat32: + fieldCode.op = opStructFieldFloat32 + case opFloat64: + fieldCode.op = opStructFieldFloat64 case opString: fieldCode.op = opStructFieldString - code = (*opcode)(unsafe.Pointer(fieldCode)) + case opBool: + fieldCode.op = opStructFieldBool default: code = valueCode.beforeLastCode() } diff --git a/encode_opcode.go b/encode_opcode.go index 81a789c..a7a5cb4 100644 --- a/encode_opcode.go +++ b/encode_opcode.go @@ -31,13 +31,49 @@ const ( opSliceEnd opStructFieldHead opStructFieldHeadInt + opStructFieldHeadInt8 + opStructFieldHeadInt16 + opStructFieldHeadInt32 + opStructFieldHeadInt64 + opStructFieldHeadUint + opStructFieldHeadUint8 + opStructFieldHeadUint16 + opStructFieldHeadUint32 + opStructFieldHeadUint64 + opStructFieldHeadFloat32 + opStructFieldHeadFloat64 opStructFieldHeadString + opStructFieldHeadBool opStructFieldPtrHead opStructFieldPtrHeadInt + opStructFieldPtrHeadInt8 + opStructFieldPtrHeadInt16 + opStructFieldPtrHeadInt32 + opStructFieldPtrHeadInt64 + opStructFieldPtrHeadUint + opStructFieldPtrHeadUint8 + opStructFieldPtrHeadUint16 + opStructFieldPtrHeadUint32 + opStructFieldPtrHeadUint64 + opStructFieldPtrHeadFloat32 + opStructFieldPtrHeadFloat64 opStructFieldPtrHeadString + opStructFieldPtrHeadBool opStructField opStructFieldInt + opStructFieldInt8 + opStructFieldInt16 + opStructFieldInt32 + opStructFieldInt64 + opStructFieldUint + opStructFieldUint8 + opStructFieldUint16 + opStructFieldUint32 + opStructFieldUint64 + opStructFieldFloat32 + opStructFieldFloat64 opStructFieldString + opStructFieldBool opStructEnd ) @@ -85,20 +121,92 @@ func (t opType) String() string { return "STRUCT_FIELD_HEAD" case opStructFieldHeadInt: return "STRUCT_FIELD_HEAD_INT" + case opStructFieldHeadInt8: + return "STRUCT_FIELD_HEAD_INT8" + case opStructFieldHeadInt16: + return "STRUCT_FIELD_HEAD_INT16" + case opStructFieldHeadInt32: + return "STRUCT_FIELD_HEAD_INT32" + case opStructFieldHeadInt64: + return "STRUCT_FIELD_HEAD_INT64" + case opStructFieldHeadUint: + return "STRUCT_FIELD_HEAD_UINT" + case opStructFieldHeadUint8: + return "STRUCT_FIELD_HEAD_UINT8" + case opStructFieldHeadUint16: + return "STRUCT_FIELD_HEAD_UINT16" + case opStructFieldHeadUint32: + return "STRUCT_FIELD_HEAD_UINT32" + case opStructFieldHeadUint64: + return "STRUCT_FIELD_HEAD_UINT64" + case opStructFieldHeadFloat32: + return "STRUCT_FIELD_HEAD_FLOAT32" + case opStructFieldHeadFloat64: + return "STRUCT_FIELD_HEAD_FLOAT64" case opStructFieldHeadString: return "STRUCT_FIELD_HEAD_STRING" + case opStructFieldHeadBool: + return "STRUCT_FIELD_HEAD_BOOL" case opStructFieldPtrHead: return "STRUCT_FIELD_PTR_HEAD" case opStructFieldPtrHeadInt: return "STRUCT_FIELD_PTR_HEAD_INT" + case opStructFieldPtrHeadInt8: + return "STRUCT_FIELD_PTR_HEAD_INT8" + case opStructFieldPtrHeadInt16: + return "STRUCT_FIELD_PTR_HEAD_INT16" + case opStructFieldPtrHeadInt32: + return "STRUCT_FIELD_PTR_HEAD_INT32" + case opStructFieldPtrHeadInt64: + return "STRUCT_FIELD_PTR_HEAD_INT64" + case opStructFieldPtrHeadUint: + return "STRUCT_FIELD_PTR_HEAD_UINT" + case opStructFieldPtrHeadUint8: + return "STRUCT_FIELD_PTR_HEAD_UINT8" + case opStructFieldPtrHeadUint16: + return "STRUCT_FIELD_PTR_HEAD_UINT16" + case opStructFieldPtrHeadUint32: + return "STRUCT_FIELD_PTR_HEAD_UINT32" + case opStructFieldPtrHeadUint64: + return "STRUCT_FIELD_PTR_HEAD_UINT64" + case opStructFieldPtrHeadFloat32: + return "STRUCT_FIELD_PTR_HEAD_FLOAT32" + case opStructFieldPtrHeadFloat64: + return "STRUCT_FIELD_PTR_HEAD_FLOAT64" case opStructFieldPtrHeadString: return "STRUCT_FIELD_PTR_HEAD_STRING" + case opStructFieldPtrHeadBool: + return "STRUCT_FIELD_PTR_HEAD_BOOL" case opStructField: return "STRUCT_FIELD" case opStructFieldInt: return "STRUCT_FIELD_INT" + case opStructFieldInt8: + return "STRUCT_FIELD_INT8" + case opStructFieldInt16: + return "STRUCT_FIELD_INT16" + case opStructFieldInt32: + return "STRUCT_FIELD_INT32" + case opStructFieldInt64: + return "STRUCT_FIELD_INT64" + case opStructFieldUint: + return "STRUCT_FIELD_UINT" + case opStructFieldUint8: + return "STRUCT_FIELD_UINT8" + case opStructFieldUint16: + return "STRUCT_FIELD_UINT16" + case opStructFieldUint32: + return "STRUCT_FIELD_UINT32" + case opStructFieldUint64: + return "STRUCT_FIELD_UINT64" + case opStructFieldFloat32: + return "STRUCT_FIELD_FLOAT32" + case opStructFieldFloat64: + return "STRUCT_FIELD_FLOAT64" case opStructFieldString: return "STRUCT_FIELD_STRING" + case opStructFieldBool: + return "STRUCT_FIELD_BOOL" case opStructEnd: return "STRUCT_END" } @@ -192,7 +300,6 @@ type sliceElemCode struct { len uintptr size uintptr data uintptr - elem *sliceElemCode // first => elem end *opcode } @@ -204,7 +311,7 @@ func (c *sliceElemCode) set(header *reflect.SliceHeader) { type structFieldCode struct { *opcodeHeader - key string + key []byte offset uintptr nextField *opcode end *opcode diff --git a/encode_vm.go b/encode_vm.go index c122b36..93df834 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -98,7 +98,7 @@ func (e *Encoder) run(code *opcode) error { code = field.end } else { e.encodeByte('{') - e.encodeString(field.key) + e.encodeBytes(field.key) code = field.next code.ptr = field.ptr + field.offset field.nextField.ptr = field.ptr @@ -114,11 +114,187 @@ func (e *Encoder) run(code *opcode) error { code = field.end } else { e.encodeByte('{') - e.encodeString(field.key) + e.encodeBytes(field.key) e.encodeInt(e.ptrToInt(field.ptr + field.offset)) field.nextField.ptr = field.ptr code = field.next } + case opStructFieldPtrHeadInt8: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt8: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeInt8(e.ptrToInt8(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadInt16: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt16: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeInt16(e.ptrToInt16(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadInt32: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt32: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeInt32(e.ptrToInt32(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadInt64: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadInt64: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeInt64(e.ptrToInt64(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeUint(e.ptrToUint(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint8: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint8: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeUint8(e.ptrToUint8(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint16: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint16: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeUint16(e.ptrToUint16(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint32: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint32: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeUint32(e.ptrToUint32(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadUint64: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadUint64: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeUint64(e.ptrToUint64(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadFloat32: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadFloat32: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeFloat32(e.ptrToFloat32(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } + case opStructFieldPtrHeadFloat64: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadFloat64: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeFloat64(e.ptrToFloat64(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } case opStructFieldPtrHeadString: code.ptr = e.ptrToPtr(code.ptr) fallthrough @@ -130,15 +306,31 @@ func (e *Encoder) run(code *opcode) error { code = field.end } else { e.encodeByte('{') - e.encodeString(field.key) + e.encodeBytes(field.key) e.encodeEscapedString(e.ptrToString(field.ptr + field.offset)) field.nextField.ptr = field.ptr code = field.next } + case opStructFieldPtrHeadBool: + code.ptr = e.ptrToPtr(code.ptr) + fallthrough + case opStructFieldHeadBool: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(field.key) + e.encodeBool(e.ptrToBool(field.ptr + field.offset)) + field.nextField.ptr = field.ptr + code = field.next + } case opStructField: e.encodeByte(',') c := code.toStructFieldCode() - e.encodeString(c.key) + e.encodeBytes(c.key) code = code.next code.ptr = c.ptr + c.offset c.nextField.ptr = c.ptr @@ -146,16 +338,100 @@ func (e *Encoder) run(code *opcode) error { e.encodeByte(',') c := code.toStructFieldCode() c.nextField.ptr = c.ptr - e.encodeString(c.key) + e.encodeBytes(c.key) e.encodeInt(e.ptrToInt(c.ptr + c.offset)) code = code.next + case opStructFieldInt8: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeInt8(e.ptrToInt8(c.ptr + c.offset)) + code = code.next + case opStructFieldInt16: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeInt16(e.ptrToInt16(c.ptr + c.offset)) + code = code.next + case opStructFieldInt32: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeInt32(e.ptrToInt32(c.ptr + c.offset)) + code = code.next + case opStructFieldInt64: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeInt64(e.ptrToInt64(c.ptr + c.offset)) + code = code.next + case opStructFieldUint: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeUint(e.ptrToUint(c.ptr + c.offset)) + code = code.next + case opStructFieldUint8: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeUint8(e.ptrToUint8(c.ptr + c.offset)) + code = code.next + case opStructFieldUint16: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeUint16(e.ptrToUint16(c.ptr + c.offset)) + code = code.next + case opStructFieldUint32: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeUint32(e.ptrToUint32(c.ptr + c.offset)) + code = code.next + case opStructFieldUint64: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeUint64(e.ptrToUint64(c.ptr + c.offset)) + code = code.next + case opStructFieldFloat32: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeFloat32(e.ptrToFloat32(c.ptr + c.offset)) + code = code.next + case opStructFieldFloat64: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeFloat64(e.ptrToFloat64(c.ptr + c.offset)) + code = code.next case opStructFieldString: e.encodeByte(',') c := code.toStructFieldCode() c.nextField.ptr = c.ptr - e.encodeString(c.key) + e.encodeBytes(c.key) e.encodeEscapedString(e.ptrToString(c.ptr + c.offset)) code = code.next + case opStructFieldBool: + e.encodeByte(',') + c := code.toStructFieldCode() + c.nextField.ptr = c.ptr + e.encodeBytes(c.key) + e.encodeBool(e.ptrToBool(c.ptr + c.offset)) + code = code.next case opStructEnd: e.encodeByte('}') code = code.next From 090887bf7ec386cfea450a8c859aa4c559d531b9 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Thu, 30 Apr 2020 13:39:47 +0900 Subject: [PATCH 4/5] Support Map and Interfacfe{} type for encoding of vm based --- encode_compile.go | 77 ++++++++++++++++++++++++- encode_opcode.go | 141 ++++++++++++++++++++++++++++++++++++++++++++-- encode_test.go | 14 ++++- encode_vm.go | 91 ++++++++++++++++++++++++++++-- 4 files changed, 309 insertions(+), 14 deletions(-) diff --git a/encode_compile.go b/encode_compile.go index b4c3102..503ac4d 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -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 } diff --git a/encode_opcode.go b/encode_opcode.go index a7a5cb4..292eb6c 100644 --- a/encode_opcode.go +++ b/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, + }, + } +} diff --git a/encode_test.go b/encode_test.go index ebe14f0..489009f 100644 --- a/encode_test.go +++ b/encode_test.go @@ -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) diff --git a/encode_vm.go b/encode_vm.go index 93df834..2c05b79 100644 --- a/encode_vm.go +++ b/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 From 4edbe3a346546e13e88b127002757bcaa80eebe5 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Thu, 30 Apr 2020 13:52:24 +0900 Subject: [PATCH 5/5] Refactor encoder --- encode.go | 639 ++-------------------------------------------- encode_compile.go | 128 ++++++---- encode_vm.go | 20 +- 3 files changed, 114 insertions(+), 673 deletions(-) diff --git a/encode.go b/encode.go index 8f7cb17..0ecd1e2 100644 --- a/encode.go +++ b/encode.go @@ -1,15 +1,11 @@ package json import ( - "fmt" "io" "reflect" "strconv" - "strings" "sync" "unsafe" - - "golang.org/x/xerrors" ) // An Encoder writes JSON values to an output stream. @@ -19,31 +15,28 @@ type Encoder struct { pool sync.Pool } -type EncodeOp func(*Encoder, *rtype, uintptr) error - const ( bufSize = 1024 ) -type EncodeOpMap struct { +type opcodeMap struct { sync.Map } -func (m *EncodeOpMap) Get(k string) *opcode { //EncodeOp { +func (m *opcodeMap) Get(k string) *opcode { if v, ok := m.Load(k); ok { - return v.(*opcode) //(EncodeOp) + return v.(*opcode) } return nil } -func (m *EncodeOpMap) Set(k string, op *opcode) { // EncodeOp) { +func (m *opcodeMap) Set(k string, op *opcode) { m.Store(k, op) } var ( - encPool sync.Pool - cachedEncodeOp EncodeOpMap - errCompileSlowPath = xerrors.New("json: detect dynamic type ( interface{} ) and compile with slow path") + encPool sync.Pool + cachedOpcode opcodeMap ) func init() { @@ -55,7 +48,7 @@ func init() { } }, } - cachedEncodeOp = EncodeOpMap{} + cachedOpcode = opcodeMap{} } // NewEncoder returns a new encoder that writes to w. @@ -183,624 +176,22 @@ func (e *Encoder) encode(v interface{}) error { typ = typ.Elem() } name := typ.String() - if op := cachedEncodeOp.Get(name); op != nil { + if code := cachedOpcode.Get(name); code != nil { p := uintptr(header.ptr) - op.ptr = p - if err := e.run(op); err != nil { + code.ptr = p + if err := e.run(code); err != nil { return err } - //op(e, typ, p) return nil } - op, err := e.compileOp(typ) + code, err := e.compile(typ) if err != nil { - if err == errCompileSlowPath { - /* - slowOp, err := e.compileSlowPath(typ) - if err != nil { - return err - } - op = slowOp - */ - } else { - return err - } + return err } if name != "" { - cachedEncodeOp.Set(name, op) + cachedOpcode.Set(name, code) } p := uintptr(header.ptr) - op.ptr = p - e.run(op) - //op(e, typ, p) - return nil + code.ptr = p + return e.run(code) } - -func (e *Encoder) compile(typ *rtype) (EncodeOp, error) { - switch typ.Kind() { - case reflect.Ptr: - return e.compilePtr(typ) - case reflect.Slice: - return e.compileSlice(typ) - case reflect.Struct: - return e.compileStruct(typ) - case reflect.Map: - return e.compileMap(typ) - case reflect.Array: - return e.compileArray(typ) - case reflect.Int: - return e.compileInt() - case reflect.Int8: - return e.compileInt8() - case reflect.Int16: - return e.compileInt16() - case reflect.Int32: - return e.compileInt32() - case reflect.Int64: - return e.compileInt64() - case reflect.Uint: - return e.compileUint() - case reflect.Uint8: - return e.compileUint8() - case reflect.Uint16: - return e.compileUint16() - case reflect.Uint32: - return e.compileUint32() - case reflect.Uint64: - return e.compileUint64() - case reflect.Uintptr: - return e.compileUint() - case reflect.Float32: - return e.compileFloat32() - case reflect.Float64: - return e.compileFloat64() - case reflect.String: - return e.compileString() - case reflect.Bool: - return e.compileBool() - case reflect.Interface: - return nil, errCompileSlowPath - } - return nil, xerrors.Errorf("failed to encode type %s: %w", typ.String(), ErrUnsupportedType) -} - -func (e *Encoder) compileSlowPath(typ *rtype) (EncodeOp, error) { - switch typ.Kind() { - case reflect.Ptr: - return e.compilePtrSlowPath(typ) - case reflect.Slice: - return e.compileSliceSlowPath(typ) - case reflect.Struct: - return e.compileStructSlowPath(typ) - case reflect.Map: - return e.compileMapSlowPath(typ) - case reflect.Array: - return e.compileArraySlowPath(typ) - case reflect.Int: - return e.compileInt() - case reflect.Int8: - return e.compileInt8() - case reflect.Int16: - return e.compileInt16() - case reflect.Int32: - return e.compileInt32() - case reflect.Int64: - return e.compileInt64() - case reflect.Uint: - return e.compileUint() - case reflect.Uint8: - return e.compileUint8() - case reflect.Uint16: - return e.compileUint16() - case reflect.Uint32: - return e.compileUint32() - case reflect.Uint64: - return e.compileUint64() - case reflect.Uintptr: - return e.compileUint() - case reflect.Float32: - return e.compileFloat32() - case reflect.Float64: - return e.compileFloat64() - case reflect.String: - return e.compileString() - case reflect.Bool: - return e.compileBool() - case reflect.Interface: - return e.compileInterface() - } - return nil, xerrors.Errorf("failed to encode type %s: %w", typ.String(), ErrUnsupportedType) -} - -func (e *Encoder) compilePtr(typ *rtype) (EncodeOp, error) { - elem := typ.Elem() - op, err := e.compile(elem) - if err != nil { - return nil, err - } - return func(enc *Encoder, typ *rtype, p uintptr) error { - return op(enc, elem, e.ptrToPtr(p)) - }, nil -} - -func (e *Encoder) compilePtrSlowPath(typ *rtype) (EncodeOp, error) { - elem := typ.Elem() - op, err := e.compileSlowPath(elem) - if err != nil { - return nil, err - } - return func(enc *Encoder, typ *rtype, p uintptr) error { - return op(enc, typ.Elem(), e.ptrToPtr(p)) - }, nil -} - -func (e *Encoder) compileInt() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeInt(e.ptrToInt(p)) - return nil - }, nil -} - -func (e *Encoder) compileInt8() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeInt8(e.ptrToInt8(p)) - return nil - }, nil -} - -func (e *Encoder) compileInt16() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeInt16(e.ptrToInt16(p)) - return nil - }, nil -} - -func (e *Encoder) compileInt32() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeInt32(e.ptrToInt32(p)) - return nil - }, nil -} - -func (e *Encoder) compileInt64() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeInt64(e.ptrToInt64(p)) - return nil - }, nil -} - -func (e *Encoder) compileUint() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeUint(e.ptrToUint(p)) - return nil - }, nil -} - -func (e *Encoder) compileUint8() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeUint8(e.ptrToUint8(p)) - return nil - }, nil -} - -func (e *Encoder) compileUint16() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeUint16(e.ptrToUint16(p)) - return nil - }, nil -} - -func (e *Encoder) compileUint32() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeUint32(e.ptrToUint32(p)) - return nil - }, nil -} - -func (e *Encoder) compileUint64() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeUint64(e.ptrToUint64(p)) - return nil - }, nil -} - -func (e *Encoder) compileFloat32() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeFloat32(e.ptrToFloat32(p)) - return nil - }, nil -} - -func (e *Encoder) compileFloat64() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeFloat64(e.ptrToFloat64(p)) - return nil - }, nil -} - -func (e *Encoder) compileString() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeEscapedString(e.ptrToString(p)) - return nil - }, nil -} - -func (e *Encoder) compileBool() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, p uintptr) error { - enc.encodeBool(e.ptrToBool(p)) - return nil - }, nil -} - -func (e *Encoder) compileSlice(typ *rtype) (EncodeOp, error) { - elem := typ.Elem() - size := elem.Size() - op, err := e.compile(elem) - if err != nil { - return nil, err - } - return func(enc *Encoder, typ *rtype, base uintptr) error { - if base == 0 { - enc.encodeString("null") - return nil - } - enc.encodeByte('[') - slice := (*reflect.SliceHeader)(unsafe.Pointer(base)) - num := slice.Len - for i := 0; i < num; i++ { - if err := op(enc, elem, slice.Data+uintptr(i)*size); err != nil { - return err - } - if i != num-1 { - enc.encodeByte(',') - } - } - enc.encodeByte(']') - return nil - }, nil -} - -func (e *Encoder) compileSliceSlowPath(typ *rtype) (EncodeOp, error) { - op, err := e.compileSlowPath(typ.Elem()) - if err != nil { - return nil, err - } - return func(enc *Encoder, typ *rtype, base uintptr) error { - if base == 0 { - enc.encodeString("null") - return nil - } - size := typ.Elem().Size() - enc.encodeByte('[') - slice := (*reflect.SliceHeader)(unsafe.Pointer(base)) - num := slice.Len - for i := 0; i < num; i++ { - if err := op(enc, typ.Elem(), slice.Data+uintptr(i)*size); err != nil { - return err - } - if i != num-1 { - enc.encodeByte(',') - } - } - enc.encodeByte(']') - return nil - }, nil -} - -func (e *Encoder) compileArray(typ *rtype) (EncodeOp, error) { - elem := typ.Elem() - alen := typ.Len() - size := elem.Size() - op, err := e.compile(elem) - if err != nil { - return nil, err - } - return func(enc *Encoder, typ *rtype, base uintptr) error { - if base == 0 { - enc.encodeString("null") - return nil - } - enc.encodeByte('[') - for i := 0; i < alen; i++ { - if i != 0 { - enc.encodeByte(',') - } - if err := op(enc, elem, base+uintptr(i)*size); err != nil { - return err - } - } - enc.encodeByte(']') - return nil - }, nil -} - -func (e *Encoder) compileArraySlowPath(typ *rtype) (EncodeOp, error) { - elem := typ.Elem() - alen := typ.Len() - op, err := e.compileSlowPath(elem) - if err != nil { - return nil, err - } - return func(enc *Encoder, typ *rtype, base uintptr) error { - if base == 0 { - enc.encodeString("null") - return nil - } - elem := typ.Elem() - size := elem.Size() - enc.encodeByte('[') - for i := 0; i < alen; i++ { - if i != 0 { - enc.encodeByte(',') - } - if err := op(enc, elem, base+uintptr(i)*size); err != nil { - return err - } - } - enc.encodeByte(']') - return nil - }, nil -} - -func (e *Encoder) getTag(field reflect.StructField) string { - return field.Tag.Get("json") -} - -func (e *Encoder) isIgnoredStructField(field reflect.StructField) bool { - if field.PkgPath != "" && !field.Anonymous { - // private field - return true - } - tag := e.getTag(field) - if tag == "-" { - return true - } - return false -} - -type encodeStructField struct { - op EncodeOp - fieldIndex int -} - -func (e *Encoder) compileStruct(typ *rtype) (EncodeOp, error) { - fieldNum := typ.NumField() - opQueue := make([]*encodeStructField, 0, fieldNum) - - 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) - op, err := e.compile(fieldType) - if err != nil { - return nil, err - } - key := fmt.Sprintf(`"%s":`, keyName) - structField := &encodeStructField{fieldIndex: i} - structField.op = func(enc *Encoder, typ *rtype, base uintptr) error { - enc.encodeString(key) - return op(enc, fieldType, base+field.Offset) - } - opQueue = append(opQueue, structField) - } - - queueNum := len(opQueue) - return func(enc *Encoder, typ *rtype, base uintptr) error { - if base == 0 { - enc.encodeString("null") - return nil - } - enc.encodeByte('{') - for i := 0; i < queueNum; i++ { - if err := opQueue[i].op(enc, typ, base); err != nil { - return err - } - if i != queueNum-1 { - enc.encodeByte(',') - } - } - enc.encodeByte('}') - return nil - }, nil -} - -func (e *Encoder) compileStructSlowPath(typ *rtype) (EncodeOp, error) { - fieldNum := typ.NumField() - opQueue := make([]*encodeStructField, 0, fieldNum) - - 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) - op, err := e.compileSlowPath(fieldType) - if err != nil { - return nil, err - } - key := fmt.Sprintf(`"%s":`, keyName) - structField := &encodeStructField{fieldIndex: i} - structField.op = func(enc *Encoder, typ *rtype, base uintptr) error { - enc.encodeString(key) - fieldType := type2rtype(typ.Field(structField.fieldIndex).Type) - return op(enc, fieldType, base+field.Offset) - } - opQueue = append(opQueue, structField) - } - - queueNum := len(opQueue) - return func(enc *Encoder, typ *rtype, base uintptr) error { - if base == 0 { - enc.encodeString("null") - return nil - } - enc.encodeByte('{') - for i := 0; i < queueNum; i++ { - if err := opQueue[i].op(enc, typ, base); err != nil { - return err - } - if i != queueNum-1 { - enc.encodeByte(',') - } - } - enc.encodeByte('}') - return nil - }, nil -} - -//go:linkname mapiterinit reflect.mapiterinit -//go:noescape -func mapiterinit(mapType *rtype, m unsafe.Pointer) unsafe.Pointer - -//go:linkname mapiterkey reflect.mapiterkey -//go:noescape -func mapiterkey(it unsafe.Pointer) unsafe.Pointer - -//go:linkname mapiternext reflect.mapiternext -//go:noescape -func mapiternext(it unsafe.Pointer) - -//go:linkname maplen reflect.maplen -//go:noescape -func maplen(m unsafe.Pointer) int - -type valueType struct { - typ unsafe.Pointer - ptr unsafe.Pointer -} - -func (e *Encoder) compileMap(typ *rtype) (EncodeOp, error) { - keyType := typ.Key() - keyOp, err := e.compile(keyType) - if err != nil { - return nil, err - } - valueType := typ.Elem() - valueOp, err := e.compile(valueType) - if err != nil { - return nil, err - } - return func(enc *Encoder, typ *rtype, base uintptr) error { - if base == 0 { - enc.encodeString("null") - return nil - } - enc.encodeByte('{') - mlen := maplen(unsafe.Pointer(base)) - iter := mapiterinit(typ, unsafe.Pointer(base)) - for i := 0; i < mlen; i++ { - key := mapiterkey(iter) - if i != 0 { - enc.encodeByte(',') - } - value := mapitervalue(iter) - keyptr := uintptr(key) - if err := keyOp(enc, keyType, keyptr); err != nil { - return err - } - enc.encodeByte(':') - valueptr := uintptr(value) - if err := valueOp(enc, valueType, valueptr); err != nil { - return err - } - mapiternext(iter) - } - enc.encodeByte('}') - return nil - }, nil -} - -func (e *Encoder) compileMapSlowPath(typ *rtype) (EncodeOp, error) { - keyOp, err := e.compileSlowPath(typ.Key()) - if err != nil { - return nil, err - } - valueOp, err := e.compileSlowPath(typ.Elem()) - if err != nil { - return nil, err - } - return func(enc *Encoder, typ *rtype, base uintptr) error { - if base == 0 { - enc.encodeString("null") - return nil - } - enc.encodeByte('{') - mlen := maplen(unsafe.Pointer(base)) - iter := mapiterinit(typ, unsafe.Pointer(base)) - for i := 0; i < mlen; i++ { - key := mapiterkey(iter) - if i != 0 { - enc.encodeByte(',') - } - value := mapitervalue(iter) - keyptr := uintptr(key) - if err := keyOp(enc, typ.Key(), keyptr); err != nil { - return err - } - enc.encodeByte(':') - valueptr := uintptr(value) - if err := valueOp(enc, typ.Elem(), valueptr); err != nil { - return err - } - mapiternext(iter) - } - enc.encodeByte('}') - return nil - }, nil -} - -func (e *Encoder) compileInterface() (EncodeOp, error) { - return func(enc *Encoder, typ *rtype, base uintptr) error { - v := *(*interface{})(unsafe.Pointer(&interfaceHeader{ - typ: typ, - ptr: unsafe.Pointer(base), - })) - vv := reflect.ValueOf(v).Interface() - header := (*interfaceHeader)(unsafe.Pointer(&vv)) - t := header.typ - if t.Kind() == reflect.Ptr { - t = t.Elem() - } - op, err := e.compileSlowPath(t) - if err != nil { - return err - } - return op(enc, t, uintptr(header.ptr)) - }, nil -} - -func (e *Encoder) ptrToPtr(p uintptr) uintptr { return *(*uintptr)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToInt(p uintptr) int { return *(*int)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToInt8(p uintptr) int8 { return *(*int8)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToInt16(p uintptr) int16 { return *(*int16)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToInt32(p uintptr) int32 { return *(*int32)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToInt64(p uintptr) int64 { return *(*int64)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToUint(p uintptr) uint { return *(*uint)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToUint8(p uintptr) uint8 { return *(*uint8)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToUint16(p uintptr) uint16 { return *(*uint16)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToUint32(p uintptr) uint32 { return *(*uint32)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToUint64(p uintptr) uint64 { return *(*uint64)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToFloat32(p uintptr) float32 { return *(*float32)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToFloat64(p uintptr) float64 { return *(*float64)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToBool(p uintptr) bool { return *(*bool)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToByte(p uintptr) byte { return *(*byte)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToBytes(p uintptr) []byte { return *(*[]byte)(unsafe.Pointer(p)) } -func (e *Encoder) ptrToString(p uintptr) string { return *(*string)(unsafe.Pointer(p)) } diff --git a/encode_compile.go b/encode_compile.go index 503ac4d..8d1aebd 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -9,50 +9,50 @@ import ( "golang.org/x/xerrors" ) -func (e *Encoder) compileOp(typ *rtype) (*opcode, error) { +func (e *Encoder) compile(typ *rtype) (*opcode, error) { switch typ.Kind() { case reflect.Ptr: - return e.compilePtrOp(typ) + return e.compilePtr(typ) case reflect.Slice: - return e.compileSliceOp(typ) + return e.compileSlice(typ) case reflect.Array: - return e.compileArrayOp(typ) + return e.compileArray(typ) case reflect.Map: - return e.compileMapOp(typ) + return e.compileMap(typ) case reflect.Struct: - return e.compileStructOp(typ) + return e.compileStruct(typ) case reflect.Int: - return e.compileIntOp(typ) + return e.compileInt(typ) case reflect.Int8: - return e.compileInt8Op(typ) + return e.compileInt8(typ) case reflect.Int16: - return e.compileInt16Op(typ) + return e.compileInt16(typ) case reflect.Int32: - return e.compileInt32Op(typ) + return e.compileInt32(typ) case reflect.Int64: - return e.compileInt64Op(typ) + return e.compileInt64(typ) case reflect.Uint: - return e.compileUintOp(typ) + return e.compileUint(typ) case reflect.Uint8: - return e.compileUint8Op(typ) + return e.compileUint8(typ) case reflect.Uint16: - return e.compileUint16Op(typ) + return e.compileUint16(typ) case reflect.Uint32: - return e.compileUint32Op(typ) + return e.compileUint32(typ) case reflect.Uint64: - return e.compileUint64Op(typ) + return e.compileUint64(typ) case reflect.Uintptr: - return e.compileUintOp(typ) + return e.compileUint(typ) case reflect.Float32: - return e.compileFloat32Op(typ) + return e.compileFloat32(typ) case reflect.Float64: - return e.compileFloat64Op(typ) + return e.compileFloat64(typ) case reflect.String: - return e.compileStringOp(typ) + return e.compileString(typ) case reflect.Bool: - return e.compileBoolOp(typ) + return e.compileBool(typ) case reflect.Interface: - return e.compileInterfaceOp(typ) + return e.compileInterface(typ) } return nil, xerrors.Errorf("failed to encode type %s: %w", typ.String(), ErrUnsupportedType) } @@ -95,78 +95,78 @@ func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode { return code } -func (e *Encoder) compilePtrOp(typ *rtype) (*opcode, error) { - code, err := e.compileOp(typ.Elem()) +func (e *Encoder) compilePtr(typ *rtype) (*opcode, error) { + code, err := e.compile(typ.Elem()) if err != nil { return nil, err } return e.optimizeStructFieldPtrHead(typ, code), nil } -func (e *Encoder) compileIntOp(typ *rtype) (*opcode, error) { +func (e *Encoder) compileInt(typ *rtype) (*opcode, error) { return newOpCode(opInt, typ, newEndOp()), nil } -func (e *Encoder) compileInt8Op(typ *rtype) (*opcode, error) { +func (e *Encoder) compileInt8(typ *rtype) (*opcode, error) { return newOpCode(opInt8, typ, newEndOp()), nil } -func (e *Encoder) compileInt16Op(typ *rtype) (*opcode, error) { +func (e *Encoder) compileInt16(typ *rtype) (*opcode, error) { return newOpCode(opInt16, typ, newEndOp()), nil } -func (e *Encoder) compileInt32Op(typ *rtype) (*opcode, error) { +func (e *Encoder) compileInt32(typ *rtype) (*opcode, error) { return newOpCode(opInt32, typ, newEndOp()), nil } -func (e *Encoder) compileInt64Op(typ *rtype) (*opcode, error) { +func (e *Encoder) compileInt64(typ *rtype) (*opcode, error) { return newOpCode(opInt64, typ, newEndOp()), nil } -func (e *Encoder) compileUintOp(typ *rtype) (*opcode, error) { +func (e *Encoder) compileUint(typ *rtype) (*opcode, error) { return newOpCode(opUint, typ, newEndOp()), nil } -func (e *Encoder) compileUint8Op(typ *rtype) (*opcode, error) { +func (e *Encoder) compileUint8(typ *rtype) (*opcode, error) { return newOpCode(opUint8, typ, newEndOp()), nil } -func (e *Encoder) compileUint16Op(typ *rtype) (*opcode, error) { +func (e *Encoder) compileUint16(typ *rtype) (*opcode, error) { return newOpCode(opUint16, typ, newEndOp()), nil } -func (e *Encoder) compileUint32Op(typ *rtype) (*opcode, error) { +func (e *Encoder) compileUint32(typ *rtype) (*opcode, error) { return newOpCode(opUint32, typ, newEndOp()), nil } -func (e *Encoder) compileUint64Op(typ *rtype) (*opcode, error) { +func (e *Encoder) compileUint64(typ *rtype) (*opcode, error) { return newOpCode(opUint64, typ, newEndOp()), nil } -func (e *Encoder) compileFloat32Op(typ *rtype) (*opcode, error) { +func (e *Encoder) compileFloat32(typ *rtype) (*opcode, error) { return newOpCode(opFloat32, typ, newEndOp()), nil } -func (e *Encoder) compileFloat64Op(typ *rtype) (*opcode, error) { +func (e *Encoder) compileFloat64(typ *rtype) (*opcode, error) { return newOpCode(opFloat64, typ, newEndOp()), nil } -func (e *Encoder) compileStringOp(typ *rtype) (*opcode, error) { +func (e *Encoder) compileString(typ *rtype) (*opcode, error) { return newOpCode(opString, typ, newEndOp()), nil } -func (e *Encoder) compileBoolOp(typ *rtype) (*opcode, error) { +func (e *Encoder) compileBool(typ *rtype) (*opcode, error) { return newOpCode(opBool, typ, newEndOp()), nil } -func (e *Encoder) compileInterfaceOp(typ *rtype) (*opcode, error) { +func (e *Encoder) compileInterface(typ *rtype) (*opcode, error) { return newOpCode(opInterface, typ, newEndOp()), nil } -func (e *Encoder) compileSliceOp(typ *rtype) (*opcode, error) { +func (e *Encoder) compileSlice(typ *rtype) (*opcode, error) { elem := typ.Elem() size := elem.Size() - code, err := e.compileOp(elem) + code, err := e.compile(elem) if err != nil { return nil, err } @@ -188,11 +188,11 @@ func (e *Encoder) compileSliceOp(typ *rtype) (*opcode, error) { return (*opcode)(unsafe.Pointer(header)), nil } -func (e *Encoder) compileArrayOp(typ *rtype) (*opcode, error) { +func (e *Encoder) compileArray(typ *rtype) (*opcode, error) { elem := typ.Elem() alen := typ.Len() size := elem.Size() - code, err := e.compileOp(elem) + code, err := e.compile(elem) if err != nil { return nil, err } @@ -219,17 +219,33 @@ func (e *Encoder) compileArrayOp(typ *rtype) (*opcode, error) { return (*opcode)(unsafe.Pointer(header)), nil } -func (e *Encoder) compileMapOp(typ *rtype) (*opcode, error) { +//go:linkname mapiterinit reflect.mapiterinit +//go:noescape +func mapiterinit(mapType *rtype, m unsafe.Pointer) unsafe.Pointer + +//go:linkname mapiterkey reflect.mapiterkey +//go:noescape +func mapiterkey(it unsafe.Pointer) unsafe.Pointer + +//go:linkname mapiternext reflect.mapiternext +//go:noescape +func mapiternext(it unsafe.Pointer) + +//go:linkname maplen reflect.maplen +//go:noescape +func maplen(m unsafe.Pointer) int + +func (e *Encoder) compileMap(typ *rtype) (*opcode, error) { // header => code => value => code => key => code => value => code => end // ^ | // |_______________________| keyType := typ.Key() - keyCode, err := e.compileOp(keyType) + keyCode, err := e.compile(keyType) if err != nil { return nil, err } valueType := typ.Elem() - valueCode, err := e.compileOp(valueType) + valueCode, err := e.compile(valueType) if err != nil { return nil, err } @@ -252,7 +268,23 @@ func (e *Encoder) compileMapOp(typ *rtype) (*opcode, error) { return (*opcode)(unsafe.Pointer(header)), nil } -func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) { +func (e *Encoder) getTag(field reflect.StructField) string { + return field.Tag.Get("json") +} + +func (e *Encoder) isIgnoredStructField(field reflect.StructField) bool { + if field.PkgPath != "" && !field.Anonymous { + // private field + return true + } + tag := e.getTag(field) + if tag == "-" { + return true + } + return false +} + +func (e *Encoder) compileStruct(typ *rtype) (*opcode, error) { // header => code => structField => code => end // ^ | // |__________| @@ -277,7 +309,7 @@ func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) { } } fieldType := type2rtype(field.Type) - valueCode, err := e.compileOp(fieldType) + valueCode, err := e.compile(fieldType) if err != nil { return nil, err } diff --git a/encode_vm.go b/encode_vm.go index 2c05b79..07de78c 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -66,7 +66,7 @@ func (e *Encoder) run(code *opcode) error { if typ.Kind() == reflect.Ptr { typ = typ.Elem() } - c, err := e.compileOp(typ) + c, err := e.compile(typ) if err != nil { return err } @@ -525,3 +525,21 @@ func (e *Encoder) run(code *opcode) error { END: return nil } + +func (e *Encoder) ptrToPtr(p uintptr) uintptr { return *(*uintptr)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToInt(p uintptr) int { return *(*int)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToInt8(p uintptr) int8 { return *(*int8)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToInt16(p uintptr) int16 { return *(*int16)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToInt32(p uintptr) int32 { return *(*int32)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToInt64(p uintptr) int64 { return *(*int64)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToUint(p uintptr) uint { return *(*uint)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToUint8(p uintptr) uint8 { return *(*uint8)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToUint16(p uintptr) uint16 { return *(*uint16)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToUint32(p uintptr) uint32 { return *(*uint32)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToUint64(p uintptr) uint64 { return *(*uint64)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToFloat32(p uintptr) float32 { return *(*float32)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToFloat64(p uintptr) float64 { return *(*float64)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToBool(p uintptr) bool { return *(*bool)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToByte(p uintptr) byte { return *(*byte)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToBytes(p uintptr) []byte { return *(*[]byte)(unsafe.Pointer(p)) } +func (e *Encoder) ptrToString(p uintptr) string { return *(*string)(unsafe.Pointer(p)) }