Skip to content

Commit

Permalink
feat: pass offset to unexpected byte error
Browse files Browse the repository at this point in the history
Simplify error reporting.
  • Loading branch information
tdakkota committed Jan 16, 2023
1 parent 2f8261b commit 1e1234b
Show file tree
Hide file tree
Showing 16 changed files with 138 additions and 106 deletions.
7 changes: 6 additions & 1 deletion dec.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ type Decoder struct {
head int // offset in buf to start of current json stream
tail int // offset in buf to end of current json stream

depth int
streamOffset int // for reader, offset in stream to start of current buf contents
depth int
}

const defaultBuf = 512
Expand Down Expand Up @@ -114,6 +115,10 @@ func DecodeStr(input string) *Decoder {
return DecodeBytes([]byte(input))
}

func (d *Decoder) offset() int {
return d.streamOffset + d.head
}

// Reset resets reader and underlying state, next reads will use provided io.Reader.
func (d *Decoder) Reset(reader io.Reader) {
d.reader = reader
Expand Down
19 changes: 10 additions & 9 deletions dec_arr.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (d *Decoder) Elem() (ok bool, err error) {
case '[':
c, err := d.more()
if err != nil {
return false, errors.Wrap(err, "next")
return false, err
}
if c != ']' {
d.unread()
Expand All @@ -29,24 +29,24 @@ func (d *Decoder) Elem() (ok bool, err error) {
case ',':
return true, nil
default:
return false, errors.Wrap(badToken(c), `"[" or "," or "]" expected`)
return false, errors.Wrap(badToken(c, d.offset()), `"[", "," or "]" expected`)
}
}

// Arr decodes array and invokes callback on each array element.
func (d *Decoder) Arr(f func(d *Decoder) error) error {
if err := d.consume('['); err != nil {
return errors.Wrap(err, "start")
return errors.Wrap(err, `"[" expected`)
}
if f == nil {
return d.skipArr()
}
if err := d.incDepth(); err != nil {
return errors.Wrap(err, "inc")
return err
}
c, err := d.more()
if err != nil {
return err
return errors.Wrap(err, `value or "]" expected`)
}
if c == ']' {
return d.decDepth()
Expand All @@ -58,23 +58,24 @@ func (d *Decoder) Arr(f func(d *Decoder) error) error {

c, err = d.more()
if err != nil {
return errors.Wrap(err, "next")
return errors.Wrap(err, `"," or "]" expected`)
}
for c == ',' {
// Skip whitespace before reading element.
if _, err := d.next(); err != nil {
return errors.Wrap(err, "next")
return err
}
d.unread()
if err := f(d); err != nil {
return errors.Wrap(err, "callback")
}
if c, err = d.next(); err != nil {
return errors.Wrap(err, "next")
return err
}
}
if c != ']' {
return errors.Wrap(badToken(c), "end")
err := badToken(c, d.offset()-1)
return errors.Wrap(err, `"]" expected`)
}
return d.decDepth()
}
7 changes: 4 additions & 3 deletions dec_arr_iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ type ArrIter struct {
// ArrIter creates new array iterator.
func (d *Decoder) ArrIter() (ArrIter, error) {
if err := d.consume('['); err != nil {
return ArrIter{}, errors.Wrap(err, "start")
return ArrIter{}, errors.Wrap(err, `"[" expected`)
}
if err := d.incDepth(); err != nil {
return ArrIter{}, errors.Wrap(err, "inc")
return ArrIter{}, err
}
if _, err := d.more(); err != nil {
return ArrIter{}, err
Expand Down Expand Up @@ -46,7 +46,8 @@ func (i *ArrIter) Next() bool {
}
if i.comma {
if c != ',' {
i.err = badToken(c)
err := badToken(c, dec.offset()-1)
i.err = errors.Wrap(err, `"," expected`)
return false
}
} else {
Expand Down
13 changes: 8 additions & 5 deletions dec_bool.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ func (d *Decoder) Bool() (bool, error) {
return false, err
}

var buf [4]byte
var (
offset = d.offset()
buf [4]byte
)
if err := d.readExact4(&buf); err != nil {
return false, err
}
Expand All @@ -20,19 +23,19 @@ func (d *Decoder) Bool() (bool, error) {
return false, err
}
if c != 'e' {
return false, badToken(c)
return false, badToken(c, offset+4)
}
return false, nil
default:
switch c := buf[0]; c {
case 't':
const encodedTrue = 't' | 'r'<<8 | 'u'<<16 | 'e'<<24
return false, findInvalidToken4(buf, encodedTrue)
return false, findInvalidToken4(buf, encodedTrue, offset)
case 'f':
const encodedFals = 'f' | 'a'<<8 | 'l'<<16 | 's'<<24
return false, findInvalidToken4(buf, encodedFals)
return false, findInvalidToken4(buf, encodedFals, offset)
default:
return false, badToken(c)
return false, badToken(c, offset)
}
}
}
6 changes: 5 additions & 1 deletion dec_capture.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ func (d *Decoder) Capture(f func(d *Decoder) error) error {

if d.reader != nil {
// TODO(tdakkota): May it be more efficient?
var buf bytes.Buffer
var (
buf bytes.Buffer
streamOffset = d.streamOffset
)
reader := io.TeeReader(d.reader, &buf)
defer func() {
d.reader = io.MultiReader(&buf, d.reader)
d.streamOffset = streamOffset
}()
d.reader = reader
}
Expand Down
17 changes: 17 additions & 0 deletions dec_error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package jx

import "fmt"

// badTokenErr means that Token was unexpected while decoding.
type badTokenErr struct {
Token byte
Offset int
}

func (e *badTokenErr) Error() string {
return fmt.Sprintf("unexpected byte %d %q at %d", e.Token, e.Token, e.Offset)
}

func badToken(c byte, offset int) error {
return &badTokenErr{Token: c, Offset: offset}
}
6 changes: 3 additions & 3 deletions dec_float.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (d *Decoder) BigInt() (*big.Int, error) {
func (d *Decoder) Float32() (float32, error) {
c, err := d.more()
if err != nil {
return 0, errors.Wrap(err, "byte")
return 0, err
}
if c != '-' {
d.unread()
Expand Down Expand Up @@ -227,7 +227,7 @@ func (d *Decoder) float32Slow() (float32, error) {
func (d *Decoder) Float64() (float64, error) {
c, err := d.more()
if err != nil {
return 0, errors.Wrap(err, "byte")
return 0, err
}
if floatDigits[c] >= 0 {
d.unread()
Expand All @@ -241,7 +241,7 @@ func (d *Decoder) Float64() (float64, error) {
}
return -v, err
default:
return 0, badToken(c)
return 0, badToken(c, d.offset())
}
}

Expand Down
30 changes: 16 additions & 14 deletions dec_int.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func (d *Decoder) UInt() (uint, error) {
func (d *Decoder) Int8() (int8, error) {
c, err := d.byte()
if err != nil {
return 0, errors.Wrap(err, "byte")
return 0, err
}
if c == '-' {
val, err := d.readUInt8()
Expand Down Expand Up @@ -108,7 +108,7 @@ func (d *Decoder) UInt8() (uint8, error) {
func (d *Decoder) readUInt8() (uint8, error) {
c, err := d.byte()
if err != nil {
return 0, errors.Wrap(err, "byte")
return 0, err
}
ind := intDigits[c]
if ind == 0 {
Expand Down Expand Up @@ -171,7 +171,7 @@ func (d *Decoder) readUInt8() (uint8, error) {
func (d *Decoder) Int16() (int16, error) {
c, err := d.byte()
if err != nil {
return 0, errors.Wrap(err, "byte")
return 0, err
}
if c == '-' {
val, err := d.readUInt16()
Expand Down Expand Up @@ -202,7 +202,7 @@ func (d *Decoder) UInt16() (uint16, error) {
func (d *Decoder) readUInt16() (uint16, error) {
c, err := d.byte()
if err != nil {
return 0, errors.Wrap(err, "byte")
return 0, err
}
ind := intDigits[c]
if ind == 0 {
Expand Down Expand Up @@ -277,7 +277,7 @@ func (d *Decoder) readUInt16() (uint16, error) {
func (d *Decoder) Int32() (int32, error) {
c, err := d.byte()
if err != nil {
return 0, errors.Wrap(err, "byte")
return 0, err
}
if c == '-' {
val, err := d.readUInt32()
Expand Down Expand Up @@ -308,7 +308,7 @@ func (d *Decoder) UInt32() (uint32, error) {
func (d *Decoder) readUInt32() (uint32, error) {
c, err := d.byte()
if err != nil {
return 0, errors.Wrap(err, "byte")
return 0, err
}
ind := intDigits[c]
if ind == 0 {
Expand Down Expand Up @@ -401,7 +401,7 @@ func (d *Decoder) readUInt32() (uint32, error) {
func (d *Decoder) Int64() (int64, error) {
c, err := d.byte()
if err != nil {
return 0, errors.Wrap(err, "byte")
return 0, err
}
if c == '-' {
c, err := d.next()
Expand Down Expand Up @@ -431,7 +431,7 @@ func (d *Decoder) Int64() (int64, error) {
func (d *Decoder) UInt64() (uint64, error) {
c, err := d.byte()
if err != nil {
return 0, errors.Wrap(err, "byte")
return 0, err
}
return d.readUInt64(c)
}
Expand All @@ -442,7 +442,8 @@ func (d *Decoder) readUInt64(c byte) (uint64, error) {
return 0, nil // single zero
}
if ind == invalidCharForNumber {
return 0, errors.Wrap(badToken(c), "invalid number")
err := badToken(c, d.offset()-1)
return 0, errors.Wrap(err, "invalid number")
}
value := uint64(ind)
if d.tail-d.head > 10 {
Expand Down Expand Up @@ -514,12 +515,13 @@ func (d *Decoder) readUInt64(c byte) (uint64, error) {
}
value = (value << 3) + (value << 1) + uint64(ind)
}
err := d.read()
if err == io.EOF {
switch err := d.read(); err {
case io.EOF:
return value, nil
}
if err != nil {
return 0, errors.Wrap(err, "read")
case nil:
continue
default:
return 0, err
}
}
}
7 changes: 5 additions & 2 deletions dec_null.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ func (d *Decoder) Null() error {
return err
}

var buf [4]byte
var (
offset = d.offset()
buf [4]byte
)
if err := d.readExact4(&buf); err != nil {
return err
}

if string(buf[:]) != "null" {
const encodedNull = 'n' | 'u'<<8 | 'l'<<16 | 'l'<<24
return findInvalidToken4(buf, encodedNull)
return findInvalidToken4(buf, encodedNull, offset)
}
return nil
}
3 changes: 2 additions & 1 deletion dec_num.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func (d *Decoder) NumAppend(v Num) (Num, error) {
func (d *Decoder) num(v Num, forceAppend bool) (Num, error) {
switch d.Next() {
case String:
offset := d.offset()
start := d.head

str, err := d.str(value{raw: true})
Expand All @@ -44,7 +45,7 @@ func (d *Decoder) num(v Num, forceAppend bool) (Num, error) {
return Num{}, errors.Wrap(err, "skip number")
}
default:
return nil, badToken(c)
return nil, badToken(c, offset)
}
}

Expand Down
Loading

0 comments on commit 1e1234b

Please sign in to comment.