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.
472 lines
7.4 KiB
472 lines
7.4 KiB
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 |
|
}
|
|
|