diff --git a/decode_interface.go b/decode_interface.go index a445848..2138f68 100644 --- a/decode_interface.go +++ b/decode_interface.go @@ -1,6 +1,7 @@ package json import ( + "encoding" "reflect" "unsafe" ) @@ -39,11 +40,56 @@ var ( ) ) +func decodeWithUnmarshaler(s *stream, unmarshaler Unmarshaler) error { + start := s.cursor + if err := s.skipValue(); err != nil { + return err + } + src := s.buf[start:s.cursor] + dst := make([]byte, len(src)) + copy(dst, src) + + if err := unmarshaler.UnmarshalJSON(dst); err != nil { + return err + } + return nil +} + +func decodeWithTextUnmarshaler(s *stream, unmarshaler encoding.TextUnmarshaler) error { + start := s.cursor + if err := s.skipValue(); err != nil { + return err + } + src := s.buf[start:s.cursor] + dst := make([]byte, len(src)) + copy(dst, src) + + if err := unmarshaler.UnmarshalText(dst); err != nil { + return err + } + return nil +} + func (d *interfaceDecoder) decodeStream(s *stream, p unsafe.Pointer) error { s.skipWhiteSpace() for { switch s.char() { case '{': + runtimeInterfaceValue := *(*interface{})(unsafe.Pointer(&interfaceHeader{ + typ: d.typ, + ptr: p, + })) + rv := reflect.ValueOf(runtimeInterfaceValue) + if rv.NumMethod() > 0 && rv.CanInterface() { + if u, ok := rv.Interface().(Unmarshaler); ok { + return decodeWithUnmarshaler(s, u) + } + if u, ok := rv.Interface().(encoding.TextUnmarshaler); ok { + return decodeWithTextUnmarshaler(s, u) + } + return nil + } + // empty interface var v map[string]interface{} ptr := unsafe.Pointer(&v) if err := newMapDecoder( diff --git a/decode_unmarshal_json.go b/decode_unmarshal_json.go index 7383738..f1095aa 100644 --- a/decode_unmarshal_json.go +++ b/decode_unmarshal_json.go @@ -57,11 +57,14 @@ func (d *unmarshalJSONDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer return 0, err } src := buf[start:end] + dst := make([]byte, len(src)) + copy(dst, src) + v := *(*interface{})(unsafe.Pointer(&interfaceHeader{ typ: d.typ, ptr: p, })) - if err := v.(Unmarshaler).UnmarshalJSON(src); err != nil { + if err := v.(Unmarshaler).UnmarshalJSON(dst); err != nil { d.annotateError(cursor, err) return 0, err } diff --git a/encode_vm.go b/encode_vm.go index 01f3fa0..9d783c6 100644 --- a/encode_vm.go +++ b/encode_vm.go @@ -267,6 +267,12 @@ func (e *Encoder) run(ctx *encodeRuntimeContext, b []byte, code *opcode) ([]byte code = code.next case opMarshalJSON: ptr := load(ctxptr, code.idx) + if ptr == 0 { + b = encodeNull(b) + b = encodeComma(b) + code = code.next + break + } v := e.ptrToInterface(code, ptr) bb, err := v.(Marshaler).MarshalJSON() if err != nil { diff --git a/encode_vm_escaped.go b/encode_vm_escaped.go index 0345338..416d1eb 100644 --- a/encode_vm_escaped.go +++ b/encode_vm_escaped.go @@ -229,6 +229,12 @@ func (e *Encoder) runEscaped(ctx *encodeRuntimeContext, b []byte, code *opcode) code = code.next case opMarshalJSON: ptr := load(ctxptr, code.idx) + if ptr == 0 { + b = encodeNull(b) + b = encodeComma(b) + code = code.next + break + } v := e.ptrToInterface(code, ptr) bb, err := v.(Marshaler).MarshalJSON() if err != nil { diff --git a/encode_vm_escaped_indent.go b/encode_vm_escaped_indent.go index 785c0a2..5adf19e 100644 --- a/encode_vm_escaped_indent.go +++ b/encode_vm_escaped_indent.go @@ -242,6 +242,12 @@ func (e *Encoder) runEscapedIndent(ctx *encodeRuntimeContext, b []byte, code *op code = code.next case opMarshalJSON: ptr := load(ctxptr, code.idx) + if ptr == 0 { + b = encodeNull(b) + b = encodeIndentComma(b) + code = code.next + break + } v := e.ptrToInterface(code, ptr) bb, err := v.(Marshaler).MarshalJSON() if err != nil { diff --git a/encode_vm_indent.go b/encode_vm_indent.go index 3507a46..ad9c30e 100644 --- a/encode_vm_indent.go +++ b/encode_vm_indent.go @@ -242,6 +242,12 @@ func (e *Encoder) runIndent(ctx *encodeRuntimeContext, b []byte, code *opcode) ( code = code.next case opMarshalJSON: ptr := load(ctxptr, code.idx) + if ptr == 0 { + b = encodeNull(b) + b = encodeIndentComma(b) + code = code.next + break + } v := e.ptrToInterface(code, ptr) bb, err := v.(Marshaler).MarshalJSON() if err != nil {