diff --git a/decode_test.go b/decode_test.go index fc7a16587..e0bfecb2d 100644 --- a/decode_test.go +++ b/decode_test.go @@ -1,3 +1,4 @@ +//go:build amd64 // +build amd64 /* @@ -34,12 +35,13 @@ import ( `strings` `testing` `time` - `unsafe` `unicode/utf8` + `unsafe` `github.com/bytedance/sonic/decoder` `github.com/bytedance/sonic/internal/native/types` `github.com/davecgh/go-spew/spew` + `github.com/stretchr/testify/assert` ) type T struct { @@ -1732,11 +1734,14 @@ func TestEmptyString(t *testing.T) { Number2 int `json:",string"` } data := `{"Number1":"1", "Number2":""}` - var t2 T2 + var t2, t3 T2 err := Unmarshal([]byte(data), &t2) if err == nil { t.Fatal("Decode: did not return error") } + err2 := json.Unmarshal([]byte(data), &t3) + assert.Equal(t, err == nil, err2 == nil) + assert.Equal(t, t2, t3) if t2.Number1 != 1 { t.Fatal("Decode: did not set Number1") } diff --git a/decoder/decoder.go b/decoder/decoder.go index 16c8592d7..5ff92234a 100644 --- a/decoder/decoder.go +++ b/decoder/decoder.go @@ -20,6 +20,7 @@ import ( `encoding/json` `reflect` `runtime` + `unsafe` `github.com/bytedance/sonic/internal/native` `github.com/bytedance/sonic/internal/native/types` @@ -95,11 +96,25 @@ func (self *Decoder) Decode(val interface{}) error { if vp == nil || vv.Type.Kind() != reflect.Ptr { return &json.InvalidUnmarshalError{Type: vv.Type.Pack()} } + initalized := (vv.Type.Pack().Elem().Kind() == reflect.Ptr) && (*(*unsafe.Pointer)(vp) != nil) /* create a new stack, and call the decoder */ sb, etp := newStack(), rt.PtrElem(vv.Type) nb, err := decodeTypedPointer(self.s, self.i, etp, vp, sb, self.f) + if err != nil { + // clear val memory when decode failed, + // not including MismatcheTypeError + if _, ok := err.(*MismatchTypeError); !ok { + ev := reflect.ValueOf(val).Elem() + if initalized { + ev.Elem().Set(reflect.Zero(ev.Elem().Type())) + } else { + ev.Set(reflect.Zero(ev.Type())) + } + } + } + /* return the stack back */ self.i = nb freeStack(sb) diff --git a/decoder/decoder_test.go b/decoder/decoder_test.go index be6eef666..d834c2841 100644 --- a/decoder/decoder_test.go +++ b/decoder/decoder_test.go @@ -87,7 +87,7 @@ func init() { } -func TestSkipError(t *testing.T) { +func TestSkipMismatchTypeError(t *testing.T) { println("TestSkipError") type skiptype struct { A int `json:"a"` @@ -132,6 +132,62 @@ func TestSkipError(t *testing.T) { } } +type testStruct struct { + A int `json:"a"` + B string `json:"b"` +} + +func TestClearMemWhenError(t *testing.T) { + var data = `{"a":1,"b":"1"]` + var v, v2 testStruct + _, err := decode(data, &v, false) + err2 := json.Unmarshal([]byte(data), &v2) + assert.Equal(t, err2 == nil, err == nil) + assert.Equal(t, v2, v) + + var z, z2 = new(testStruct), new(testStruct) + _, err = decode(data, z, false) + err2 = json.Unmarshal([]byte(data), z2) + assert.Equal(t, err2 == nil, err == nil) + assert.Equal(t, z2, z) + + var y, y2 *testStruct + _, err = decode(data, &y, false) + err2 = json.Unmarshal([]byte(data), &y2) + assert.Equal(t, err2 == nil, err == nil) + assert.Equal(t, y2, y) + + var x, x2 = new(testStruct), new(testStruct) + _, err = decode(data, &x, false) + err2 = json.Unmarshal([]byte(data), &x2) + assert.Equal(t, err2 == nil, err == nil) + assert.Equal(t, x2, x) + + var a, a2 interface{} + _, err = decode(data, &a, false) + err2 = json.Unmarshal([]byte(data), &a2) + assert.Equal(t, err2 == nil, err == nil) + assert.Equal(t, a2, a) + + var b, b2 = new(interface{}), new(interface{}) + _, err = decode(data, b, false) + err2 = json.Unmarshal([]byte(data), b2) + assert.Equal(t, err2 == nil, err == nil) + assert.Equal(t, b2, b) + + var c, c2 *interface{} + _, err = decode(data, &c, false) + err2 = json.Unmarshal([]byte(data), &c2) + assert.Equal(t, err2 == nil, err == nil) + assert.Equal(t, c2, c) + + var d, d2 = new(interface{}), new(interface{}) + _, err = decode(data, &d, false) + err2 = json.Unmarshal([]byte(data), &d2) + assert.Equal(t, err2 == nil, err == nil) + assert.Equal(t, d2, d) +} + func TestDecodeCorrupt(t *testing.T) { var ds = []string{ `{,}`,