diff --git a/encode.go b/encode.go index ebf49f7..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) EncodeOp { +func (m *opcodeMap) Get(k string) *opcode { if v, ok := m.Load(k); ok { - return v.(EncodeOp) + return v.(*opcode) } return nil } -func (m *EncodeOpMap) Set(k string, op 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. @@ -163,6 +156,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...) @@ -179,616 +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(e, typ, p) - return nil - } - op, err := e.compile(typ) - if err != nil { - if err == errCompileSlowPath { - slowOp, err := e.compileSlowPath(typ) - if err != nil { - return err - } - op = slowOp - } else { + code.ptr = p + if err := e.run(code); err != nil { return err } + return nil + } + code, err := e.compile(typ) + if err != nil { + return err } if name != "" { - cachedEncodeOp.Set(name, op) + cachedOpcode.Set(name, code) } p := uintptr(header.ptr) - 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 new file mode 100644 index 0000000..8d1aebd --- /dev/null +++ b/encode_compile.go @@ -0,0 +1,409 @@ +package json + +import ( + "fmt" + "reflect" + "strings" + "unsafe" + + "golang.org/x/xerrors" +) + +func (e *Encoder) compile(typ *rtype) (*opcode, error) { + switch typ.Kind() { + case reflect.Ptr: + return e.compilePtr(typ) + case reflect.Slice: + return e.compileSlice(typ) + case reflect.Array: + return e.compileArray(typ) + case reflect.Map: + return e.compileMap(typ) + case reflect.Struct: + return e.compileStruct(typ) + case reflect.Int: + return e.compileInt(typ) + case reflect.Int8: + return e.compileInt8(typ) + case reflect.Int16: + return e.compileInt16(typ) + case reflect.Int32: + return e.compileInt32(typ) + case reflect.Int64: + return e.compileInt64(typ) + case reflect.Uint: + return e.compileUint(typ) + case reflect.Uint8: + return e.compileUint8(typ) + case reflect.Uint16: + return e.compileUint16(typ) + case reflect.Uint32: + return e.compileUint32(typ) + case reflect.Uint64: + return e.compileUint64(typ) + case reflect.Uintptr: + return e.compileUint(typ) + case reflect.Float32: + return e.compileFloat32(typ) + case reflect.Float64: + return e.compileFloat64(typ) + case reflect.String: + return e.compileString(typ) + case reflect.Bool: + return e.compileBool(typ) + case reflect.Interface: + return e.compileInterface(typ) + } + return nil, xerrors.Errorf("failed to encode type %s: %w", typ.String(), ErrUnsupportedType) +} + +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) + } + return code +} + +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) compileInt(typ *rtype) (*opcode, error) { + return newOpCode(opInt, typ, newEndOp()), nil +} + +func (e *Encoder) compileInt8(typ *rtype) (*opcode, error) { + return newOpCode(opInt8, typ, newEndOp()), nil +} + +func (e *Encoder) compileInt16(typ *rtype) (*opcode, error) { + return newOpCode(opInt16, typ, newEndOp()), nil +} + +func (e *Encoder) compileInt32(typ *rtype) (*opcode, error) { + return newOpCode(opInt32, typ, newEndOp()), nil +} + +func (e *Encoder) compileInt64(typ *rtype) (*opcode, error) { + return newOpCode(opInt64, typ, newEndOp()), nil +} + +func (e *Encoder) compileUint(typ *rtype) (*opcode, error) { + return newOpCode(opUint, typ, newEndOp()), nil +} + +func (e *Encoder) compileUint8(typ *rtype) (*opcode, error) { + return newOpCode(opUint8, typ, newEndOp()), nil +} + +func (e *Encoder) compileUint16(typ *rtype) (*opcode, error) { + return newOpCode(opUint16, typ, newEndOp()), nil +} + +func (e *Encoder) compileUint32(typ *rtype) (*opcode, error) { + return newOpCode(opUint32, typ, newEndOp()), nil +} + +func (e *Encoder) compileUint64(typ *rtype) (*opcode, error) { + return newOpCode(opUint64, typ, newEndOp()), nil +} + +func (e *Encoder) compileFloat32(typ *rtype) (*opcode, error) { + return newOpCode(opFloat32, typ, newEndOp()), nil +} + +func (e *Encoder) compileFloat64(typ *rtype) (*opcode, error) { + return newOpCode(opFloat64, typ, newEndOp()), nil +} + +func (e *Encoder) compileString(typ *rtype) (*opcode, error) { + return newOpCode(opString, typ, newEndOp()), nil +} + +func (e *Encoder) compileBool(typ *rtype) (*opcode, error) { + return newOpCode(opBool, typ, newEndOp()), nil +} + +func (e *Encoder) compileInterface(typ *rtype) (*opcode, error) { + return newOpCode(opInterface, typ, newEndOp()), nil +} + +func (e *Encoder) compileSlice(typ *rtype) (*opcode, error) { + elem := typ.Elem() + size := elem.Size() + code, err := e.compile(elem) + if err != nil { + return nil, err + } + + // header => opcode => elem => end + // ^ | + // |________| + + header := newSliceHeaderCode() + elemCode := &sliceElemCode{opcodeHeader: &opcodeHeader{op: opSliceElem}, size: size} + end := newOpCode(opSliceEnd, nil, newEndOp()) + + header.elem = elemCode + header.end = end + header.next = code + code.beforeLastCode().next = (*opcode)(unsafe.Pointer(elemCode)) + elemCode.next = code + elemCode.end = end + return (*opcode)(unsafe.Pointer(header)), nil +} + +func (e *Encoder) compileArray(typ *rtype) (*opcode, error) { + elem := typ.Elem() + alen := typ.Len() + size := elem.Size() + code, err := e.compile(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 +} + +//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.compile(keyType) + if err != nil { + return nil, err + } + valueType := typ.Elem() + valueCode, err := e.compile(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 +} + +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 + // ^ | + // |__________| + fieldNum := typ.NumField() + fieldIdx := 0 + var ( + head *structFieldCode + code *opcode + 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.compile(fieldType) + if err != nil { + 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.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.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 + 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 + case opBool: + fieldCode.op = opStructFieldBool + default: + code = valueCode.beforeLastCode() + } + } + prevField.nextField = newEndOp() + fieldIdx++ + } + structEndCode := newOpCode(opStructEnd, nil, nil) + head.end = structEndCode + code.next = structEndCode + structEndCode.next = newEndOp() + return (*opcode)(unsafe.Pointer(head)), nil +} diff --git a/encode_opcode.go b/encode_opcode.go new file mode 100644 index 0000000..292eb6c --- /dev/null +++ b/encode_opcode.go @@ -0,0 +1,451 @@ +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 + opInterface + opPtr + opSliceHead + opSliceElem + opSliceEnd + opArrayHead + opArrayElem + opArrayEnd + opMapHead + opMapKey + opMapValue + opMapEnd + 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 +) + +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 opInterface: + return "INTERFACE" + case opPtr: + return "PTR" + case opSliceHead: + return "SLICE_HEAD" + case opSliceElem: + 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: + 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" + } + 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 + switch code.op { + case opArrayElem: + nextCode = code.toArrayElemCode().end + case opSliceElem: + nextCode = code.toSliceElemCode().end + case opMapKey: + nextCode = code.toMapKeyCode().end + default: + 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)) + switch code.op { + case opArrayElem: + code = code.toArrayElemCode().end + case opSliceElem: + code = code.toSliceElemCode().end + case opMapKey: + code = code.toMapKeyCode().end + default: + code = code.next + } + } + 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) 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 + end *opcode +} + +func newSliceHeaderCode() *sliceHeaderCode { + return &sliceHeaderCode{ + opcodeHeader: &opcodeHeader{ + op: opSliceHead, + }, + } +} + +type sliceElemCode struct { + *opcodeHeader + idx uintptr + len uintptr + size uintptr + data uintptr + end *opcode +} + +func (c *sliceElemCode) set(header *reflect.SliceHeader) { + c.idx = uintptr(0) + c.len = uintptr(header.Len) + c.data = header.Data +} + +type arrayHeaderCode struct { + *opcodeHeader + len uintptr + elem *arrayElemCode + end *opcode +} + +func newArrayHeaderCode(alen int) *arrayHeaderCode { + return &arrayHeaderCode{ + opcodeHeader: &opcodeHeader{ + op: opArrayHead, + }, + len: uintptr(alen), + } +} + +type arrayElemCode struct { + *opcodeHeader + idx uintptr + len uintptr + size uintptr + end *opcode +} + +type structFieldCode struct { + *opcodeHeader + key []byte + offset uintptr + nextField *opcode + end *opcode +} + +type mapHeaderCode struct { + *opcodeHeader + key *mapKeyCode + value *mapValueCode + end *opcode +} + +type mapKeyCode struct { + *opcodeHeader + idx int + len int + iter unsafe.Pointer + end *opcode +} + +func (c *mapKeyCode) set(len int, iter unsafe.Pointer) { + c.idx = 0 + c.len = len + c.iter = iter +} + +type mapValueCode struct { + *opcodeHeader + iter unsafe.Pointer +} + +func (c *mapValueCode) set(iter unsafe.Pointer) { + c.iter = iter +} + +func newMapHeaderCode(typ *rtype) *mapHeaderCode { + return &mapHeaderCode{ + opcodeHeader: &opcodeHeader{ + op: opMapHead, + typ: typ, + }, + } +} + +func newMapKeyCode() *mapKeyCode { + return &mapKeyCode{ + opcodeHeader: &opcodeHeader{ + op: opMapKey, + }, + } +} + +func newMapValueCode() *mapValueCode { + return &mapValueCode{ + opcodeHeader: &opcodeHeader{ + op: opMapValue, + }, + } +} 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 new file mode 100644 index 0000000..07de78c --- /dev/null +++ b/encode_vm.go @@ -0,0 +1,545 @@ +package json + +import ( + "reflect" + "unsafe" +) + +func (e *Encoder) run(code *opcode) error { + 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 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.compile(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() + if p == 0 { + e.encodeString("null") + code = headerCode.end.next + } else { + e.encodeByte('[') + header := (*reflect.SliceHeader)(unsafe.Pointer(p)) + 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() + c.idx++ + if c.idx < c.len { + e.encodeByte(',') + code = code.next + code.ptr = c.data + c.idx*c.size + } else { + 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 + case opStructFieldHead: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + e.encodeBytes(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.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 + case opStructFieldHeadString: + field := code.toStructFieldCode() + ptr := field.ptr + if ptr == 0 { + e.encodeString("null") + code = field.end + } else { + e.encodeByte('{') + 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.encodeBytes(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.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.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 + case opEnd: + goto END + } + } +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)) }