From a225b810ff277fda4268ad72f4c3a207ab035808 Mon Sep 17 00:00:00 2001 From: "duanyi.aster" Date: Fri, 22 Jul 2022 19:29:45 +0800 Subject: [PATCH] fix:(native) StreamDecoder return error when run out of input buffer while skiping value --- decoder/stream.go | 16 +++++-- issue_test/issue263_test.go | 91 +++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 issue_test/issue263_test.go diff --git a/decoder/stream.go b/decoder/stream.go index 4cb34fc9b..14bcdc810 100644 --- a/decoder/stream.go +++ b/decoder/stream.go @@ -94,11 +94,12 @@ read_more: } first = false - if len(buf) > 0 { + l := len(buf) + if l > 0 { self.Decoder.Reset(string(buf)) err = self.Decoder.Decode(val) if err != nil { - if ee, ok := err.(SyntaxError); repeat && ok && ee.Code == types.ERR_EOF { + if repeat && repeatable(err, l) { goto read_more } self.err = err @@ -109,7 +110,7 @@ read_more: self.scanp = 0 } - if len(buf) > p { + if l > p { // remain undecoded bytes, so copy them into self.buf self.buf = append(self.buf[:0], buf[p:]...) } else { @@ -124,6 +125,15 @@ read_more: return err } +func repeatable(err error, l int) bool { + if ee, ok := err.(SyntaxError); ok { + if ee.Code == types.ERR_EOF || ee.Code == types.ERR_INVALID_CHAR && ee.Pos >= l { + return true + } + } + return false +} + // InputOffset returns the input stream byte offset of the current decoder position. // The offset gives the location of the end of the most recently returned token and the beginning of the next token. func (self *StreamDecoder) InputOffset() int64 { diff --git a/issue_test/issue263_test.go b/issue_test/issue263_test.go new file mode 100644 index 000000000..992d4d0c3 --- /dev/null +++ b/issue_test/issue263_test.go @@ -0,0 +1,91 @@ +package issue_test + +import ( + `bytes` + `testing` + + `github.com/bytedance/sonic/ast` + `github.com/bytedance/sonic/decoder` + `github.com/davecgh/go-spew/spew` +) + + +type Response struct { + Menu Menu `json:"menu"` +} + +type Menu struct { + Items []*Item `json:"items"` +} + +type Item struct { + ID string `json:"id"` +} + +func (i *Item) UnmarshalJSON(buf []byte) error { return nil +} + +func TestName(t *testing.T) { + q := `{ + "menu": { + "items": [{ + "id": "carrotcake", + "name": { + "en": "CarrotCake Name" + }, + "operational_name": "carrotCake-op", + "description": null, + "plu": "carrotCake45", + "ian": "carrotCake_ian_45", + "external_data": "", + "image": { + "url": "http://127.0.0.1:50207/image7.jpg" + }, + "tax_rate": "20", + "modifier_ids": [ + "add-hot-drinks-mod" + ], + "contains_alcohol": false, + "max_quantity": null, + "is_eligible_for_substitution": true, + "is_eligible_as_replacement": true + }, + { + "id": "cheeseburger", + "name": { + "en": "Cheeseburger Name" + }, + "operational_name": "cheeseburger-op", + "description": null, + "plu": "cheeseburger40", + "ian": "cheeseburger_ian_40", + "external_data": "", + "image": { + "url": "http://127.0.0.1:50207/image1.jpg" + }, + "tax_rate": "20", + "modifier_ids": [ + "add-drinks-mod" + ], + "contains_alcohol": false, + "max_quantity": null, + "is_eligible_for_substitution": true, + "is_eligible_as_replacement": true + } + ] + } +}` + + n, err := ast.NewSearcher(q).GetByPath("menu", "items", 1) + if err != nil { + t.Fatal(err) + } + spew.Dump(n.Interface()) + + var response Response + err = decoder.NewStreamDecoder(bytes.NewReader([]byte(q))).Decode(&response) + if err != nil { + t.Fatal(err) + } + +} \ No newline at end of file