package fastjson
import (
)
type Parser struct {
b []byte
c cache
}
func ( *Parser) ( string) (*Value, error) {
= skipWS()
.b = append(.b[:0], ...)
.c.reset()
, , := .c.parseValue(b2s(.b), 0)
if != nil {
return nil, fmt.Errorf("cannot parse JSON: %s; unparsed tail: %q", , startEndString())
}
= skipWS()
if len() > 0 {
return nil, fmt.Errorf("unexpected tail: %q", startEndString())
}
return , nil
}
func ( *Parser) ( []byte) (*Value, error) {
return .Parse(b2s())
}
type cache struct {
vs []Value
}
func ( *cache) () {
:= .vs
for := range {
[].reset()
}
.vs = [:0]
}
func ( *cache) () *Value {
if cap(.vs) > len(.vs) {
.vs = .vs[:len(.vs)+1]
} else {
.vs = append(.vs, Value{})
}
return &.vs[len(.vs)-1]
}
func ( string) string {
if len() == 0 || [0] > 0x20 {
return
}
return skipWSSlow()
}
func ( string) string {
if len() == 0 || [0] != 0x20 && [0] != 0x0A && [0] != 0x09 && [0] != 0x0D {
return
}
for := 1; < len(); ++ {
if [] != 0x20 && [] != 0x0A && [] != 0x09 && [] != 0x0D {
return [:]
}
}
return ""
}
type kv struct {
k string
v *Value
}
const MaxDepth = 300
func ( *cache) ( string, int) (*Value, string, error) {
if len() == 0 {
return nil, , fmt.Errorf("cannot parse empty string")
}
++
if > MaxDepth {
return nil, , fmt.Errorf("too big depth for the nested JSON; it exceeds %d", MaxDepth)
}
if [0] == '{' {
, , := .parseObject([1:], )
if != nil {
return nil, , fmt.Errorf("cannot parse object: %s", )
}
return , , nil
}
if [0] == '[' {
, , := .parseArray([1:], )
if != nil {
return nil, , fmt.Errorf("cannot parse array: %s", )
}
return , , nil
}
if [0] == '"' {
, , := parseRawString([1:])
if != nil {
return nil, , fmt.Errorf("cannot parse string: %s", )
}
:= .getValue()
.t = typeRawString
.s =
return , , nil
}
if [0] == 't' {
if len() < len("true") || [:len("true")] != "true" {
return nil, , fmt.Errorf("unexpected value found: %q", )
}
return valueTrue, [len("true"):], nil
}
if [0] == 'f' {
if len() < len("false") || [:len("false")] != "false" {
return nil, , fmt.Errorf("unexpected value found: %q", )
}
return valueFalse, [len("false"):], nil
}
if [0] == 'n' {
if len() < len("null") || [:len("null")] != "null" {
if len() >= 3 && strings.EqualFold([:3], "nan") {
:= .getValue()
.t = TypeNumber
.s = [:3]
return , [3:], nil
}
return nil, , fmt.Errorf("unexpected value found: %q", )
}
return valueNull, [len("null"):], nil
}
, , := parseRawNumber()
if != nil {
return nil, , fmt.Errorf("cannot parse number: %s", )
}
:= .getValue()
.t = TypeNumber
.s =
return , , nil
}
func ( *cache) ( string, int) (*Value, string, error) {
= skipWS()
if len() == 0 {
return nil, , fmt.Errorf("missing ']'")
}
if [0] == ']' {
:= .getValue()
.t = TypeArray
.a = .a[:0]
return , [1:], nil
}
:= .getValue()
.t = TypeArray
.a = .a[:0]
for {
var *Value
var error
= skipWS()
, , = .parseValue(, )
if != nil {
return nil, , fmt.Errorf("cannot parse array value: %s", )
}
.a = append(.a, )
= skipWS()
if len() == 0 {
return nil, , fmt.Errorf("unexpected end of array")
}
if [0] == ',' {
= [1:]
continue
}
if [0] == ']' {
= [1:]
return , , nil
}
return nil, , fmt.Errorf("missing ',' after array value")
}
}
func ( *cache) ( string, int) (*Value, string, error) {
= skipWS()
if len() == 0 {
return nil, , fmt.Errorf("missing '}'")
}
if [0] == '}' {
:= .getValue()
.t = TypeObject
.o.reset()
return , [1:], nil
}
:= .getValue()
.t = TypeObject
.o.reset()
for {
var error
:= .o.getKV()
= skipWS()
if len() == 0 || [0] != '"' {
return nil, , fmt.Errorf(`cannot find opening '"" for object key`)
}
.k, , = parseRawKey([1:])
if != nil {
return nil, , fmt.Errorf("cannot parse object key: %s", )
}
= skipWS()
if len() == 0 || [0] != ':' {
return nil, , fmt.Errorf("missing ':' after object key")
}
= [1:]
= skipWS()
.v, , = .parseValue(, )
if != nil {
return nil, , fmt.Errorf("cannot parse object value: %s", )
}
= skipWS()
if len() == 0 {
return nil, , fmt.Errorf("unexpected end of object")
}
if [0] == ',' {
= [1:]
continue
}
if [0] == '}' {
return , [1:], nil
}
return nil, , fmt.Errorf("missing ',' after object value")
}
}
func ( []byte, string) []byte {
if !hasSpecialChars() {
= append(, '"')
= append(, ...)
= append(, '"')
return
}
return strconv.AppendQuote(, )
}
func ( string) bool {
if strings.IndexByte(, '"') >= 0 || strings.IndexByte(, '\\') >= 0 {
return true
}
for := range len() {
if [] < 0x20 {
return true
}
}
return false
}
func ( string) string {
:= strings.IndexByte(, '\\')
if < 0 {
return
}
:= s2b()
= [:]
= [+1:]
for len() > 0 {
:= [0]
= [1:]
switch {
case '"':
= append(, '"')
case '\\':
= append(, '\\')
case '/':
= append(, '/')
case 'b':
= append(, '\b')
case 'f':
= append(, '\f')
case 'n':
= append(, '\n')
case 'r':
= append(, '\r')
case 't':
= append(, '\t')
case 'u':
if len() < 4 {
= append(, "\\u"...)
break
}
:= [:4]
, := strconv.ParseUint(, 16, 16)
if != nil {
= append(, "\\u"...)
break
}
= [4:]
if !utf16.IsSurrogate(rune()) {
= append(, string(rune())...)
break
}
if len() < 6 || [0] != '\\' || [1] != 'u' {
= append(, "\\u"...)
= append(, ...)
break
}
, := strconv.ParseUint([2:6], 16, 16)
if != nil {
= append(, "\\u"...)
= append(, ...)
break
}
:= utf16.DecodeRune(rune(), rune())
= append(, string()...)
= [6:]
default:
= append(, '\\', )
}
= strings.IndexByte(, '\\')
if < 0 {
= append(, ...)
break
}
= append(, [:]...)
= [+1:]
}
return b2s()
}
func ( string) (string, string, error) {
for := range len() {
if [] == '"' {
return [:], [+1:], nil
}
if [] == '\\' {
return parseRawString()
}
}
return , "", fmt.Errorf(`missing closing '"'`)
}
func ( string) (string, string, error) {
:= strings.IndexByte(, '"')
if < 0 {
return , "", fmt.Errorf(`missing closing '"'`)
}
if == 0 || [-1] != '\\' {
return [:], [+1:], nil
}
:=
for {
:= - 1
for > 0 && [-1] == '\\' {
--
}
if uint(-)%2 == 0 {
return [:len()-len()+], [+1:], nil
}
= [+1:]
= strings.IndexByte(, '"')
if < 0 {
return , "", fmt.Errorf(`missing closing '"'`)
}
if == 0 || [-1] != '\\' {
return [:len()-len()+], [+1:], nil
}
}
}
func ( string) (string, string, error) {
for := range len() {
:= []
if ( >= '0' && <= '9') || == '.' || == '-' || == 'e' || == 'E' || == '+' {
continue
}
if == 0 || == 1 && ([0] == '-' || [0] == '+') {
if len([:]) >= 3 {
:= [ : +3]
if strings.EqualFold(, "inf") || strings.EqualFold(, "nan") {
return [:+3], [+3:], nil
}
}
return "", , fmt.Errorf("unexpected char: %q", [:1])
}
:= [:]
= [:]
return , , nil
}
return , "", nil
}
type Object struct {
kvs []kv
keysUnescaped bool
}
func ( *Object) () {
clear(.kvs)
.kvs = .kvs[:0]
.keysUnescaped = false
}
func ( *Object) ( []byte) []byte {
= append(, '{')
:= .kvs
for := range {
:= &[]
if .keysUnescaped {
= escapeString(, .k)
} else {
= append(, '"')
= append(, .k...)
= append(, '"')
}
= append(, ':')
= .v.MarshalTo()
if != len(.kvs)-1 {
= append(, ',')
}
}
= append(, '}')
return
}
func ( *Object) () string {
:= .MarshalTo(nil)
return b2s()
}
func ( *Object) () *kv {
if cap(.kvs) > len(.kvs) {
.kvs = .kvs[:len(.kvs)+1]
} else {
.kvs = append(.kvs, kv{})
}
return &.kvs[len(.kvs)-1]
}
func ( *Object) () {
if .keysUnescaped {
return
}
:= .kvs
for := range {
:= &[]
.k = unescapeStringBestEffort(.k)
}
.keysUnescaped = true
}
func ( *Object) () int {
return len(.kvs)
}
func ( *Object) ( string) *Value {
if !.keysUnescaped && strings.IndexByte(, '\\') < 0 {
:= .kvs
for := range {
:= &[]
if .k == {
return .v
}
}
}
.unescapeKeys()
:= .kvs
for := range {
:= &[]
if .k == {
return .v
}
}
return nil
}
func ( *Object) ( func( []byte, *Value)) {
if == nil {
return
}
.unescapeKeys()
:= .kvs
for := range {
:= &[]
(s2b(.k), .v)
}
}
type Value struct {
o Object
a []*Value
s string
t Type
}
func ( *Value) () {
.o.reset()
clear(.a)
.a = .a[:0]
.s = ""
.t = 0
}
func ( *Value) ( []byte) []byte {
switch .t {
case typeRawString:
= append(, '"')
= append(, .s...)
= append(, '"')
return
case TypeObject:
return .o.MarshalTo()
case TypeArray:
= append(, '[')
for , := range .a {
= .()
if != len(.a)-1 {
= append(, ',')
}
}
= append(, ']')
return
case TypeString:
return escapeString(, .s)
case TypeNumber:
return append(, .s...)
case TypeTrue:
return append(, "true"...)
case TypeFalse:
return append(, "false"...)
case TypeNull:
return append(, "null"...)
default:
panic(fmt.Errorf("BUG: unexpected Value type: %d", .t))
}
}
func ( *Value) () string {
:= .MarshalTo(nil)
return b2s()
}
type Type int
const (
TypeNull Type = 0
TypeObject Type = 1
TypeArray Type = 2
TypeString Type = 3
TypeNumber Type = 4
TypeTrue Type = 5
TypeFalse Type = 6
typeRawString Type = 7
)
func ( Type) () string {
switch {
case TypeObject:
return "object"
case TypeArray:
return "array"
case TypeString:
return "string"
case TypeNumber:
return "number"
case TypeTrue:
return "true"
case TypeFalse:
return "false"
case TypeNull:
return "null"
default:
panic(fmt.Errorf("BUG: unknown Value type: %d", ))
}
}
func ( *Value) () Type {
if .t == typeRawString {
.s = unescapeStringBestEffort(.s)
.t = TypeString
}
return .t
}
func ( *Value) ( ...string) bool {
= .Get(...)
return != nil
}
func ( *Value) ( ...string) *Value {
if == nil {
return nil
}
for , := range {
if .t == TypeObject {
= .o.Get()
if == nil {
return nil
}
} else if .t == TypeArray {
, := strconv.Atoi()
if != nil || < 0 || >= len(.a) {
return nil
}
= .a[]
} else {
return nil
}
}
return
}
func ( *Value) ( ...string) *Object {
= .Get(...)
if == nil || .t != TypeObject {
return nil
}
return &.o
}
func ( *Value) ( ...string) []*Value {
= .Get(...)
if == nil || .t != TypeArray {
return nil
}
return .a
}
func ( *Value) ( ...string) float64 {
= .Get(...)
if == nil || .Type() != TypeNumber {
return 0
}
return fastfloat.ParseBestEffort(.s)
}
func ( *Value) ( ...string) int {
= .Get(...)
if == nil || .Type() != TypeNumber {
return 0
}
:= fastfloat.ParseInt64BestEffort(.s)
:= int()
if int64() != {
return 0
}
return
}
func ( *Value) ( ...string) uint {
= .Get(...)
if == nil || .Type() != TypeNumber {
return 0
}
:= fastfloat.ParseUint64BestEffort(.s)
:= uint()
if uint64() != {
return 0
}
return
}
func ( *Value) ( ...string) int64 {
= .Get(...)
if == nil || .Type() != TypeNumber {
return 0
}
return fastfloat.ParseInt64BestEffort(.s)
}
func ( *Value) ( ...string) uint64 {
= .Get(...)
if == nil || .Type() != TypeNumber {
return 0
}
return fastfloat.ParseUint64BestEffort(.s)
}
func ( *Value) ( ...string) []byte {
= .Get(...)
if == nil || .Type() != TypeString {
return nil
}
return s2b(.s)
}
func ( *Value) ( ...string) bool {
= .Get(...)
if != nil && .t == TypeTrue {
return true
}
return false
}
func ( *Value) () (*Object, error) {
if .t != TypeObject {
return nil, fmt.Errorf("value doesn't contain object; it contains %s", .Type())
}
return &.o, nil
}
func ( *Value) () ([]*Value, error) {
if .t != TypeArray {
return nil, fmt.Errorf("value doesn't contain array; it contains %s", .Type())
}
return .a, nil
}
func ( *Value) () ([]byte, error) {
if .Type() != TypeString {
return nil, fmt.Errorf("value doesn't contain string; it contains %s", .Type())
}
return s2b(.s), nil
}
func ( *Value) () (float64, error) {
if .Type() != TypeNumber {
return 0, fmt.Errorf("value doesn't contain number; it contains %s", .Type())
}
return fastfloat.Parse(.s)
}
func ( *Value) () (int, error) {
if .Type() != TypeNumber {
return 0, fmt.Errorf("value doesn't contain number; it contains %s", .Type())
}
, := fastfloat.ParseInt64(.s)
if != nil {
return 0,
}
:= int()
if int64() != {
return 0, fmt.Errorf("number %q doesn't fit int", .s)
}
return , nil
}
func ( *Value) () (uint, error) {
if .Type() != TypeNumber {
return 0, fmt.Errorf("value doesn't contain number; it contains %s", .Type())
}
, := fastfloat.ParseUint64(.s)
if != nil {
return 0,
}
:= uint()
if uint64() != {
return 0, fmt.Errorf("number %q doesn't fit uint", .s)
}
return , nil
}
func ( *Value) () (int64, error) {
if .Type() != TypeNumber {
return 0, fmt.Errorf("value doesn't contain number; it contains %s", .Type())
}
return fastfloat.ParseInt64(.s)
}
func ( *Value) () (uint64, error) {
if .Type() != TypeNumber {
return 0, fmt.Errorf("value doesn't contain number; it contains %s", .Type())
}
return fastfloat.ParseUint64(.s)
}
func ( *Value) () (bool, error) {
if .t == TypeTrue {
return true, nil
}
if .t == TypeFalse {
return false, nil
}
return false, fmt.Errorf("value doesn't contain bool; it contains %s", .Type())
}
var (
valueTrue = &Value{t: TypeTrue}
valueFalse = &Value{t: TypeFalse}
valueNull = &Value{t: TypeNull}
)