Skip to content

Commit

Permalink
feat: improve null decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
tdakkota committed Jan 14, 2022
1 parent 8242f8a commit a9b7a05
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 2 deletions.
16 changes: 16 additions & 0 deletions dec.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,22 @@ func (d *Decoder) read() error {
return nil
}

func (d *Decoder) readAtLeast(min int) error {
if d.reader == nil {
d.head = d.tail
return io.ErrUnexpectedEOF
}

n, err := io.ReadAtLeast(d.reader, d.buf, min)
if err != nil {
return err
}

d.head = 0
d.tail = n
return nil
}

func (d *Decoder) unread() { d.head-- }

// limit maximum depth of nesting, as allowed by https://tools.ietf.org/html/rfc7159#section-9
Expand Down
27 changes: 25 additions & 2 deletions dec_skip.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,40 @@ package jx

import (
"io"
"math/bits"

"github.com/go-faster/errors"
)

// Null reads a json object as null and
// returns whether it's a null or not.
func (d *Decoder) Null() error {
if err := d.consume('n'); err != nil {
const encodedNull = 'n' | 'u'<<8 | 'l'<<16 | 'l'<<24

if buf := d.buf[d.head:d.tail]; len(buf) >= 4 {
c := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
if mask := c ^ encodedNull; mask != 0 {
idx := bits.TrailingZeros32(mask) / 8
return badToken(buf[idx])
}
d.head += 4
return nil
}

var buf [4]byte
n := copy(buf[:], d.buf[d.head:d.tail])
if err := d.readAtLeast(4 - n); err != nil {
return err
}
return d.skipThreeBytes('u', 'l', 'l') // null
copy(buf[n:], d.buf[d.head:d.tail])

c := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
if mask := c ^ encodedNull; mask != 0 {
idx := bits.TrailingZeros32(mask) / 8
return badToken(buf[idx])
}
d.head += 4
return nil
}

// Bool reads a json object as Bool
Expand Down
22 changes: 22 additions & 0 deletions null_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,25 @@ func Test_decode_null_skip(t *testing.T) {
t.FailNow()
}
}

func TestNullError(t *testing.T) {
a := require.New(t)
var (
b = [4]byte{'n', 'u', 'l', 'l'}
valid = b
)
for i := range b {
// Reset buffer.
b = valid
for c := byte(0); c < 255; c++ {
// Skip expected value.
if valid[i] == c {
continue
}
b[i] = c
var token badTokenErr
a.ErrorAs(DecodeBytes(b[:]).Null(), &token)
a.Equalf(c, token.Token, "%c != %c (%q)", c, token.Token, b)
}
}
}

0 comments on commit a9b7a05

Please sign in to comment.