diff --git a/decode.go b/decode.go index d092057..56b9c24 100644 --- a/decode.go +++ b/decode.go @@ -23,6 +23,7 @@ type decoder interface { type Decoder struct { s *stream disallowUnknownFields bool + structTypeToDecoder map[uintptr]decoder } type decoderMap struct { @@ -61,7 +62,9 @@ const ( func NewDecoder(r io.Reader) *Decoder { s := &stream{r: r} s.read() - return &Decoder{s: s} + return &Decoder{ + s: s, + } } // Buffered returns a reader of the data remaining in the Decoder's @@ -90,7 +93,7 @@ func (d *Decoder) decode(src []byte, header *interfaceHeader) error { } dec := cachedDecoder.get(typeptr) if dec == nil { - + d.structTypeToDecoder = map[uintptr]decoder{} compiledDec, err := d.compileHead(copiedType) if err != nil { return err @@ -155,6 +158,7 @@ func (d *Decoder) Decode(v interface{}) error { dec := cachedDecoder.get(typeptr) if dec == nil { + d.structTypeToDecoder = map[uintptr]decoder{} compiledDec, err := d.compileHead(typ) if err != nil { return err diff --git a/decode_compile.go b/decode_compile.go index bb6ab06..1ed11aa 100644 --- a/decode_compile.go +++ b/decode_compile.go @@ -204,6 +204,12 @@ func (d *Decoder) compileInterface(typ *rtype) (decoder, error) { func (d *Decoder) compileStruct(typ *rtype) (decoder, error) { fieldNum := typ.NumField() fieldMap := map[string]*structFieldSet{} + typeptr := uintptr(unsafe.Pointer(typ)) + if dec, exists := d.structTypeToDecoder[typeptr]; exists { + return dec, nil + } + structDec := newStructDecoder(fieldMap) + d.structTypeToDecoder[typeptr] = structDec for i := 0; i < fieldNum; i++ { field := typ.Field(i) if isIgnoredStructField(field) { @@ -222,5 +228,6 @@ func (d *Decoder) compileStruct(typ *rtype) (decoder, error) { fieldMap[tag.key] = fieldSet fieldMap[strings.ToLower(tag.key)] = fieldSet } - return newStructDecoder(fieldMap), nil + delete(d.structTypeToDecoder, typeptr) + return structDec, nil } diff --git a/encode_vm.go b/encode_vm.go index b9f4eaf..8714a4c 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -74,9 +74,12 @@ func (e *Encoder) run(code *opcode) error { if ptr == 0 || header.Data == 0 { e.encodeNull() } else { - s := base64.StdEncoding.EncodeToString(e.ptrToBytes(code.ptr)) + b := e.ptrToBytes(code.ptr) + encodedLen := base64.StdEncoding.EncodedLen(len(b)) e.encodeByte('"') - e.encodeBytes(*(*[]byte)(unsafe.Pointer(&s))) + buf := make([]byte, encodedLen) + base64.StdEncoding.Encode(buf, b) + e.encodeBytes(buf) e.encodeByte('"') } code = code.next @@ -550,7 +553,7 @@ func (e *Encoder) run(code *opcode) error { e.encodeBytes(field.key) } code = field.next - code.ptr = ptr + code.ptr = ptr + field.offset field.nextField.ptr = ptr } case opStructFieldAnonymousHead: