mirror of https://github.com/goccy/go-json.git
231 lines
5.0 KiB
Go
231 lines
5.0 KiB
Go
package decoder
|
|
|
|
import (
|
|
"unsafe"
|
|
|
|
"github.com/goccy/go-json/internal/errors"
|
|
)
|
|
|
|
var (
|
|
isWhiteSpace = [256]bool{}
|
|
)
|
|
|
|
func init() {
|
|
isWhiteSpace[' '] = true
|
|
isWhiteSpace['\n'] = true
|
|
isWhiteSpace['\t'] = true
|
|
isWhiteSpace['\r'] = true
|
|
}
|
|
|
|
func char(ptr unsafe.Pointer, offset int64) byte {
|
|
return *(*byte)(unsafe.Pointer(uintptr(ptr) + uintptr(offset)))
|
|
}
|
|
|
|
func skipWhiteSpace(buf []byte, cursor int64) int64 {
|
|
for isWhiteSpace[buf[cursor]] {
|
|
cursor++
|
|
}
|
|
return cursor
|
|
}
|
|
|
|
func skipObject(buf []byte, cursor, depth int64) (int64, error) {
|
|
braceCount := 1
|
|
for {
|
|
switch buf[cursor] {
|
|
case '{':
|
|
braceCount++
|
|
depth++
|
|
if depth > maxDecodeNestingDepth {
|
|
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
|
}
|
|
case '}':
|
|
depth--
|
|
braceCount--
|
|
if braceCount == 0 {
|
|
return cursor + 1, nil
|
|
}
|
|
case '[':
|
|
depth++
|
|
if depth > maxDecodeNestingDepth {
|
|
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
|
}
|
|
case ']':
|
|
depth--
|
|
case '"':
|
|
for {
|
|
cursor++
|
|
switch buf[cursor] {
|
|
case '\\':
|
|
cursor++
|
|
if buf[cursor] == nul {
|
|
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
|
}
|
|
case '"':
|
|
goto SWITCH_OUT
|
|
case nul:
|
|
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
|
}
|
|
}
|
|
case nul:
|
|
return 0, errors.ErrUnexpectedEndOfJSON("object of object", cursor)
|
|
}
|
|
SWITCH_OUT:
|
|
cursor++
|
|
}
|
|
}
|
|
|
|
func skipArray(buf []byte, cursor, depth int64) (int64, error) {
|
|
bracketCount := 1
|
|
for {
|
|
switch buf[cursor] {
|
|
case '[':
|
|
bracketCount++
|
|
depth++
|
|
if depth > maxDecodeNestingDepth {
|
|
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
|
}
|
|
case ']':
|
|
bracketCount--
|
|
depth--
|
|
if bracketCount == 0 {
|
|
return cursor + 1, nil
|
|
}
|
|
case '{':
|
|
depth++
|
|
if depth > maxDecodeNestingDepth {
|
|
return 0, errors.ErrExceededMaxDepth(buf[cursor], cursor)
|
|
}
|
|
case '}':
|
|
depth--
|
|
case '"':
|
|
for {
|
|
cursor++
|
|
switch buf[cursor] {
|
|
case '\\':
|
|
cursor++
|
|
if buf[cursor] == nul {
|
|
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
|
}
|
|
case '"':
|
|
goto SWITCH_OUT
|
|
case nul:
|
|
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
|
}
|
|
}
|
|
case nul:
|
|
return 0, errors.ErrUnexpectedEndOfJSON("array of object", cursor)
|
|
}
|
|
SWITCH_OUT:
|
|
cursor++
|
|
}
|
|
}
|
|
|
|
func skipValue(buf []byte, cursor, depth int64) (int64, error) {
|
|
for {
|
|
switch buf[cursor] {
|
|
case ' ', '\t', '\n', '\r':
|
|
cursor++
|
|
continue
|
|
case '{':
|
|
return skipObject(buf, cursor+1, depth+1)
|
|
case '[':
|
|
return skipArray(buf, cursor+1, depth+1)
|
|
case '"':
|
|
for {
|
|
cursor++
|
|
switch buf[cursor] {
|
|
case '\\':
|
|
cursor++
|
|
if buf[cursor] == nul {
|
|
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
|
}
|
|
case '"':
|
|
return cursor + 1, nil
|
|
case nul:
|
|
return 0, errors.ErrUnexpectedEndOfJSON("string of object", cursor)
|
|
}
|
|
}
|
|
case '-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
|
for {
|
|
cursor++
|
|
if floatTable[buf[cursor]] {
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
return cursor, nil
|
|
case 't':
|
|
if err := validateTrue(buf, cursor); err != nil {
|
|
return 0, err
|
|
}
|
|
cursor += 4
|
|
return cursor, nil
|
|
case 'f':
|
|
if err := validateFalse(buf, cursor); err != nil {
|
|
return 0, err
|
|
}
|
|
cursor += 5
|
|
return cursor, nil
|
|
case 'n':
|
|
if err := validateNull(buf, cursor); err != nil {
|
|
return 0, err
|
|
}
|
|
cursor += 4
|
|
return cursor, nil
|
|
default:
|
|
return cursor, errors.ErrUnexpectedEndOfJSON("null", cursor)
|
|
}
|
|
}
|
|
}
|
|
|
|
func validateTrue(buf []byte, cursor int64) error {
|
|
if cursor+3 >= int64(len(buf)) {
|
|
return errors.ErrUnexpectedEndOfJSON("true", cursor)
|
|
}
|
|
if buf[cursor+1] != 'r' {
|
|
return errors.ErrInvalidCharacter(buf[cursor+1], "true", cursor)
|
|
}
|
|
if buf[cursor+2] != 'u' {
|
|
return errors.ErrInvalidCharacter(buf[cursor+2], "true", cursor)
|
|
}
|
|
if buf[cursor+3] != 'e' {
|
|
return errors.ErrInvalidCharacter(buf[cursor+3], "true", cursor)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateFalse(buf []byte, cursor int64) error {
|
|
if cursor+4 >= int64(len(buf)) {
|
|
return errors.ErrUnexpectedEndOfJSON("false", cursor)
|
|
}
|
|
if buf[cursor+1] != 'a' {
|
|
return errors.ErrInvalidCharacter(buf[cursor+1], "false", cursor)
|
|
}
|
|
if buf[cursor+2] != 'l' {
|
|
return errors.ErrInvalidCharacter(buf[cursor+2], "false", cursor)
|
|
}
|
|
if buf[cursor+3] != 's' {
|
|
return errors.ErrInvalidCharacter(buf[cursor+3], "false", cursor)
|
|
}
|
|
if buf[cursor+4] != 'e' {
|
|
return errors.ErrInvalidCharacter(buf[cursor+4], "false", cursor)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func validateNull(buf []byte, cursor int64) error {
|
|
if cursor+3 >= int64(len(buf)) {
|
|
return errors.ErrUnexpectedEndOfJSON("null", cursor)
|
|
}
|
|
if buf[cursor+1] != 'u' {
|
|
return errors.ErrInvalidCharacter(buf[cursor+1], "null", cursor)
|
|
}
|
|
if buf[cursor+2] != 'l' {
|
|
return errors.ErrInvalidCharacter(buf[cursor+2], "null", cursor)
|
|
}
|
|
if buf[cursor+3] != 'l' {
|
|
return errors.ErrInvalidCharacter(buf[cursor+3], "null", cursor)
|
|
}
|
|
return nil
|
|
}
|