diff --git a/src/encoding/json/stream.go b/src/encoding/json/stream.go index f6b62c4cf6..75a4270df7 100644 --- a/src/encoding/json/stream.go +++ b/src/encoding/json/stream.go @@ -12,12 +12,13 @@ import ( // A Decoder reads and decodes JSON values from an input stream. type Decoder struct { - r io.Reader - buf []byte - d decodeState - scanp int // start of unread data in buf - scan scanner - err error + r io.Reader + buf []byte + d decodeState + scanp int // start of unread data in buf + scanned int64 // amount of data already scanned + scan scanner + err error tokenState int tokenStack []int @@ -55,7 +56,7 @@ func (dec *Decoder) Decode(v interface{}) error { } if !dec.tokenValueAllowed() { - return &SyntaxError{msg: "not at beginning of value"} + return &SyntaxError{msg: "not at beginning of value", Offset: dec.offset()} } // Read whole value into buffer. @@ -140,6 +141,7 @@ func (dec *Decoder) refill() error { // Make room to read more into the buffer. // First slide down data already consumed. if dec.scanp > 0 { + dec.scanned += int64(dec.scanp) n := copy(dec.buf, dec.buf[dec.scanp:]) dec.buf = dec.buf[:n] dec.scanp = 0 @@ -306,7 +308,7 @@ func (dec *Decoder) tokenPrepareForDecode() error { return err } if c != ',' { - return &SyntaxError{"expected comma after array element", 0} + return &SyntaxError{"expected comma after array element", dec.offset()} } dec.scanp++ dec.tokenState = tokenArrayValue @@ -316,7 +318,7 @@ func (dec *Decoder) tokenPrepareForDecode() error { return err } if c != ':' { - return &SyntaxError{"expected colon after object key", 0} + return &SyntaxError{"expected colon after object key", dec.offset()} } dec.scanp++ dec.tokenState = tokenObjectValue @@ -433,7 +435,6 @@ func (dec *Decoder) Token() (Token, error) { err := dec.Decode(&x) dec.tokenState = old if err != nil { - clearOffset(err) return nil, err } dec.tokenState = tokenObjectColon @@ -447,7 +448,6 @@ func (dec *Decoder) Token() (Token, error) { } var x interface{} if err := dec.Decode(&x); err != nil { - clearOffset(err) return nil, err } return x, nil @@ -455,12 +455,6 @@ func (dec *Decoder) Token() (Token, error) { } } -func clearOffset(err error) { - if s, ok := err.(*SyntaxError); ok { - s.Offset = 0 - } -} - func (dec *Decoder) tokenError(c byte) (Token, error) { var context string switch dec.tokenState { @@ -477,7 +471,7 @@ func (dec *Decoder) tokenError(c byte) (Token, error) { case tokenObjectComma: context = " after object key:value pair" } - return nil, &SyntaxError{"invalid character " + quoteChar(c) + " " + context, 0} + return nil, &SyntaxError{"invalid character " + quoteChar(c) + " " + context, dec.offset()} } // More reports whether there is another element in the @@ -505,3 +499,7 @@ func (dec *Decoder) peek() (byte, error) { err = dec.refill() } } + +func (dec *Decoder) offset() int64 { + return dec.scanned + int64(dec.scanp) +} diff --git a/src/encoding/json/stream_test.go b/src/encoding/json/stream_test.go index d0b3ffbce9..83c01d170c 100644 --- a/src/encoding/json/stream_test.go +++ b/src/encoding/json/stream_test.go @@ -342,11 +342,18 @@ var tokenStreamCases []tokenStreamCase = []tokenStreamCase{ {json: ` [{"a": 1} {"a": 2}] `, expTokens: []interface{}{ Delim('['), decodeThis{map[string]interface{}{"a": float64(1)}}, - decodeThis{&SyntaxError{"expected comma after array element", 0}}, + decodeThis{&SyntaxError{"expected comma after array element", 11}}, }}, - {json: `{ "a" 1 }`, expTokens: []interface{}{ - Delim('{'), "a", - decodeThis{&SyntaxError{"expected colon after object key", 0}}, + {json: `{ "` + strings.Repeat("a", 513) + `" 1 }`, expTokens: []interface{}{ + Delim('{'), strings.Repeat("a", 513), + decodeThis{&SyntaxError{"expected colon after object key", 518}}, + }}, + {json: `{ "\a" }`, expTokens: []interface{}{ + Delim('{'), + &SyntaxError{"invalid character 'a' in string escape code", 3}, + }}, + {json: ` \a`, expTokens: []interface{}{ + &SyntaxError{"invalid character '\\\\' looking for beginning of value", 1}, }}, } @@ -367,15 +374,15 @@ func TestDecodeInStream(t *testing.T) { tk, err = dec.Token() } if experr, ok := etk.(error); ok { - if err == nil || err.Error() != experr.Error() { - t.Errorf("case %v: Expected error %v in %q, but was %v", ci, experr, tcase.json, err) + if err == nil || !reflect.DeepEqual(err, experr) { + t.Errorf("case %v: Expected error %#v in %q, but was %#v", ci, experr, tcase.json, err) } break } else if err == io.EOF { t.Errorf("case %v: Unexpected EOF in %q", ci, tcase.json) break } else if err != nil { - t.Errorf("case %v: Unexpected error '%v' in %q", ci, err, tcase.json) + t.Errorf("case %v: Unexpected error '%#v' in %q", ci, err, tcase.json) break } if !reflect.DeepEqual(tk, etk) {