You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
473 lines
7.4 KiB
473 lines
7.4 KiB
3 years ago
|
package jx
|
||
|
|
||
|
import (
|
||
|
"io"
|
||
|
|
||
|
"github.com/go-faster/errors"
|
||
|
)
|
||
|
|
||
|
// Skip skips a json object and positions to relatively the next json object.
|
||
|
func (d *Decoder) Skip() error {
|
||
|
c, err := d.next()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
switch c {
|
||
|
case '"':
|
||
|
if err := d.skipStr(); err != nil {
|
||
|
return errors.Wrap(err, "str")
|
||
|
}
|
||
|
return nil
|
||
|
case 'n':
|
||
|
d.unread()
|
||
|
return d.Null()
|
||
|
case 't', 'f':
|
||
|
d.unread()
|
||
|
_, err := d.Bool()
|
||
|
return err
|
||
|
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||
|
d.unread()
|
||
|
return d.skipNumber()
|
||
|
case '[':
|
||
|
if err := d.skipArr(); err != nil {
|
||
|
return errors.Wrap(err, "array")
|
||
|
}
|
||
|
return nil
|
||
|
case '{':
|
||
|
if err := d.skipObj(); err != nil {
|
||
|
return errors.Wrap(err, "object")
|
||
|
}
|
||
|
return nil
|
||
|
default:
|
||
|
return badToken(c)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
skipNumberSet = [256]byte{
|
||
|
'0': 1,
|
||
|
'1': 1,
|
||
|
'2': 1,
|
||
|
'3': 1,
|
||
|
'4': 1,
|
||
|
'5': 1,
|
||
|
'6': 1,
|
||
|
'7': 1,
|
||
|
'8': 1,
|
||
|
'9': 1,
|
||
|
|
||
|
',': 2,
|
||
|
']': 2,
|
||
|
'}': 2,
|
||
|
' ': 2,
|
||
|
'\t': 2,
|
||
|
'\n': 2,
|
||
|
'\r': 2,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
// skipNumber reads one JSON number.
|
||
|
//
|
||
|
// Assumes d.buf is not empty.
|
||
|
func (d *Decoder) skipNumber() error {
|
||
|
const (
|
||
|
digitTag byte = 1
|
||
|
closerTag byte = 2
|
||
|
)
|
||
|
c := d.buf[d.head]
|
||
|
d.head++
|
||
|
switch c {
|
||
|
case '-':
|
||
|
c, err := d.byte()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
// Character after '-' must be a digit.
|
||
|
if skipNumberSet[c] != digitTag {
|
||
|
return badToken(c)
|
||
|
}
|
||
|
if c != '0' {
|
||
|
break
|
||
|
}
|
||
|
fallthrough
|
||
|
case '0':
|
||
|
// If buffer is empty, try to read more.
|
||
|
if d.head == d.tail {
|
||
|
err := d.read()
|
||
|
if err != nil {
|
||
|
// There is no data anymore.
|
||
|
if err == io.EOF {
|
||
|
return nil
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
c = d.buf[d.head]
|
||
|
if skipNumberSet[c] == closerTag {
|
||
|
return nil
|
||
|
}
|
||
|
switch c {
|
||
|
case '.':
|
||
|
goto stateDot
|
||
|
case 'e', 'E':
|
||
|
goto stateExp
|
||
|
default:
|
||
|
return badToken(c)
|
||
|
}
|
||
|
}
|
||
|
for {
|
||
|
for i, c := range d.buf[d.head:d.tail] {
|
||
|
switch skipNumberSet[c] {
|
||
|
case closerTag:
|
||
|
d.head += i
|
||
|
return nil
|
||
|
case digitTag:
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
switch c {
|
||
|
case '.':
|
||
|
d.head += i
|
||
|
goto stateDot
|
||
|
case 'e', 'E':
|
||
|
d.head += i
|
||
|
goto stateExp
|
||
|
default:
|
||
|
return badToken(c)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if err := d.read(); err != nil {
|
||
|
// There is no data anymore.
|
||
|
if err == io.EOF {
|
||
|
d.head = d.tail
|
||
|
return nil
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
stateDot:
|
||
|
d.head++
|
||
|
{
|
||
|
var last byte = '.'
|
||
|
for {
|
||
|
for i, c := range d.buf[d.head:d.tail] {
|
||
|
switch skipNumberSet[c] {
|
||
|
case closerTag:
|
||
|
d.head += i
|
||
|
// Check that dot is not last character.
|
||
|
if last == '.' {
|
||
|
return io.ErrUnexpectedEOF
|
||
|
}
|
||
|
return nil
|
||
|
case digitTag:
|
||
|
last = c
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
switch c {
|
||
|
case 'e', 'E':
|
||
|
if last == '.' {
|
||
|
return badToken(c)
|
||
|
}
|
||
|
d.head += i
|
||
|
goto stateExp
|
||
|
default:
|
||
|
return badToken(c)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if err := d.read(); err != nil {
|
||
|
// There is no data anymore.
|
||
|
if err == io.EOF {
|
||
|
d.head = d.tail
|
||
|
// Check that dot is not last character.
|
||
|
if last == '.' {
|
||
|
return io.ErrUnexpectedEOF
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
stateExp:
|
||
|
d.head++
|
||
|
// There must be a number or sign after e.
|
||
|
{
|
||
|
numOrSign, err := d.byte()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if skipNumberSet[numOrSign] != digitTag { // If next character is not a digit, check for sign.
|
||
|
if numOrSign == '-' || numOrSign == '+' {
|
||
|
num, err := d.byte()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
// There must be a number after sign.
|
||
|
if skipNumberSet[num] != digitTag {
|
||
|
return badToken(num)
|
||
|
}
|
||
|
} else {
|
||
|
return badToken(numOrSign)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for {
|
||
|
for i, c := range d.buf[d.head:d.tail] {
|
||
|
if skipNumberSet[c] == closerTag {
|
||
|
d.head += i
|
||
|
return nil
|
||
|
}
|
||
|
if skipNumberSet[c] == 0 {
|
||
|
return badToken(c)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if err := d.read(); err != nil {
|
||
|
// There is no data anymore.
|
||
|
if err == io.EOF {
|
||
|
d.head = d.tail
|
||
|
return nil
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
escapedStrSet = [256]byte{
|
||
|
'"': '"',
|
||
|
'\\': '\\',
|
||
|
'/': '/',
|
||
|
'b': '\b',
|
||
|
'f': '\f',
|
||
|
'n': '\n',
|
||
|
'r': '\r',
|
||
|
't': '\t',
|
||
|
'u': 'u',
|
||
|
}
|
||
|
hexSet = [256]byte{
|
||
|
'0': 0x0 + 1, '1': 0x1 + 1, '2': 0x2 + 1, '3': 0x3 + 1,
|
||
|
'4': 0x4 + 1, '5': 0x5 + 1, '6': 0x6 + 1, '7': 0x7 + 1,
|
||
|
'8': 0x8 + 1, '9': 0x9 + 1,
|
||
|
|
||
|
'A': 0xA + 1, 'B': 0xB + 1, 'C': 0xC + 1, 'D': 0xD + 1,
|
||
|
'E': 0xE + 1, 'F': 0xF + 1,
|
||
|
|
||
|
'a': 0xa + 1, 'b': 0xb + 1, 'c': 0xc + 1, 'd': 0xd + 1,
|
||
|
'e': 0xe + 1, 'f': 0xf + 1,
|
||
|
}
|
||
|
)
|
||
|
|
||
|
// skipStr reads one JSON string.
|
||
|
//
|
||
|
// Assumes first quote was consumed.
|
||
|
func (d *Decoder) skipStr() error {
|
||
|
var (
|
||
|
c byte
|
||
|
i int
|
||
|
)
|
||
|
readStr:
|
||
|
for {
|
||
|
i = 0
|
||
|
buf := d.buf[d.head:d.tail]
|
||
|
for len(buf) >= 8 {
|
||
|
c = buf[0]
|
||
|
if safeSet[c] != 0 {
|
||
|
goto readTok
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
c = buf[1]
|
||
|
if safeSet[c] != 0 {
|
||
|
goto readTok
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
c = buf[2]
|
||
|
if safeSet[c] != 0 {
|
||
|
goto readTok
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
c = buf[3]
|
||
|
if safeSet[c] != 0 {
|
||
|
goto readTok
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
c = buf[4]
|
||
|
if safeSet[c] != 0 {
|
||
|
goto readTok
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
c = buf[5]
|
||
|
if safeSet[c] != 0 {
|
||
|
goto readTok
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
c = buf[6]
|
||
|
if safeSet[c] != 0 {
|
||
|
goto readTok
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
c = buf[7]
|
||
|
if safeSet[c] != 0 {
|
||
|
goto readTok
|
||
|
}
|
||
|
i++
|
||
|
|
||
|
buf = buf[8:]
|
||
|
}
|
||
|
var n int
|
||
|
for n, c = range buf {
|
||
|
if safeSet[c] != 0 {
|
||
|
i += n
|
||
|
goto readTok
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if err := d.read(); err != nil {
|
||
|
if err == io.EOF {
|
||
|
err = io.ErrUnexpectedEOF
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
readTok:
|
||
|
; // Bug in cover tool, see https://github.com/golang/go/issues/28319.
|
||
|
switch {
|
||
|
case c == '"':
|
||
|
d.head += i + 1
|
||
|
return nil
|
||
|
case c == '\\':
|
||
|
d.head += i + 1
|
||
|
v, err := d.byte()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
switch escapedStrSet[v] {
|
||
|
case 'u':
|
||
|
for i := 0; i < 4; i++ {
|
||
|
h, err := d.byte()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if hexSet[h] == 0 {
|
||
|
return badToken(h)
|
||
|
}
|
||
|
}
|
||
|
case 0:
|
||
|
return badToken(v)
|
||
|
}
|
||
|
case c < ' ':
|
||
|
return badToken(c)
|
||
|
}
|
||
|
goto readStr
|
||
|
}
|
||
|
|
||
|
// skipObj reads JSON object.
|
||
|
//
|
||
|
// Assumes first bracket was consumed.
|
||
|
func (d *Decoder) skipObj() error {
|
||
|
if err := d.incDepth(); err != nil {
|
||
|
return errors.Wrap(err, "inc")
|
||
|
}
|
||
|
|
||
|
c, err := d.more()
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "next")
|
||
|
}
|
||
|
switch c {
|
||
|
case '}':
|
||
|
return d.decDepth()
|
||
|
case '"':
|
||
|
d.unread()
|
||
|
default:
|
||
|
return badToken(c)
|
||
|
}
|
||
|
|
||
|
for {
|
||
|
if err := d.consume('"'); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
if err := d.skipStr(); err != nil {
|
||
|
return errors.Wrap(err, "read field name")
|
||
|
}
|
||
|
if err := d.consume(':'); err != nil {
|
||
|
return errors.Wrap(err, "field")
|
||
|
}
|
||
|
if err := d.Skip(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
c, err := d.more()
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "read comma")
|
||
|
}
|
||
|
switch c {
|
||
|
case ',':
|
||
|
continue
|
||
|
case '}':
|
||
|
return d.decDepth()
|
||
|
default:
|
||
|
return badToken(c)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// skipArr reads JSON array.
|
||
|
//
|
||
|
// Assumes first bracket was consumed.
|
||
|
func (d *Decoder) skipArr() error {
|
||
|
if err := d.incDepth(); err != nil {
|
||
|
return errors.Wrap(err, "inc")
|
||
|
}
|
||
|
|
||
|
c, err := d.more()
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "next")
|
||
|
}
|
||
|
if c == ']' {
|
||
|
return d.decDepth()
|
||
|
}
|
||
|
d.unread()
|
||
|
|
||
|
for {
|
||
|
if err := d.Skip(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
c, err := d.more()
|
||
|
if err != nil {
|
||
|
return errors.Wrap(err, "read comma")
|
||
|
}
|
||
|
switch c {
|
||
|
case ',':
|
||
|
continue
|
||
|
case ']':
|
||
|
return d.decDepth()
|
||
|
default:
|
||
|
return badToken(c)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// skipSpace skips space characters.
|
||
|
//
|
||
|
// Returns io.ErrUnexpectedEOF if got io.EOF.
|
||
|
func (d *Decoder) skipSpace() error {
|
||
|
// Skip space.
|
||
|
if _, err := d.more(); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
d.unread()
|
||
|
return nil
|
||
|
}
|