summaryrefslogtreecommitdiff
path: root/src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go')
-rw-r--r--src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go731
1 files changed, 0 insertions, 731 deletions
diff --git a/src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go b/src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go
deleted file mode 100644
index 9e223edf9d0..00000000000
--- a/src/mongo/gotools/src/github.com/mongodb/mongo-tools/vendor/go.mongodb.org/mongo-driver/bson/bsonrw/extjson_parser.go
+++ /dev/null
@@ -1,731 +0,0 @@
-// Copyright (C) MongoDB, Inc. 2017-present.
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may
-// not use this file except in compliance with the License. You may obtain
-// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
-
-package bsonrw
-
-import (
- "errors"
- "fmt"
- "io"
-
- "go.mongodb.org/mongo-driver/bson/bsontype"
-)
-
-const maxNestingDepth = 200
-
-// ErrInvalidJSON indicates the JSON input is invalid
-var ErrInvalidJSON = errors.New("invalid JSON input")
-
-type jsonParseState byte
-
-const (
- jpsStartState jsonParseState = iota
- jpsSawBeginObject
- jpsSawEndObject
- jpsSawBeginArray
- jpsSawEndArray
- jpsSawColon
- jpsSawComma
- jpsSawKey
- jpsSawValue
- jpsDoneState
- jpsInvalidState
-)
-
-type jsonParseMode byte
-
-const (
- jpmInvalidMode jsonParseMode = iota
- jpmObjectMode
- jpmArrayMode
-)
-
-type extJSONValue struct {
- t bsontype.Type
- v interface{}
-}
-
-type extJSONObject struct {
- keys []string
- values []*extJSONValue
-}
-
-type extJSONParser struct {
- js *jsonScanner
- s jsonParseState
- m []jsonParseMode
- k string
- v *extJSONValue
-
- err error
- canonical bool
- depth int
- maxDepth int
-
- emptyObject bool
-}
-
-// newExtJSONParser returns a new extended JSON parser, ready to to begin
-// parsing from the first character of the argued json input. It will not
-// perform any read-ahead and will therefore not report any errors about
-// malformed JSON at this point.
-func newExtJSONParser(r io.Reader, canonical bool) *extJSONParser {
- return &extJSONParser{
- js: &jsonScanner{r: r},
- s: jpsStartState,
- m: []jsonParseMode{},
- canonical: canonical,
- maxDepth: maxNestingDepth,
- }
-}
-
-// peekType examines the next value and returns its BSON Type
-func (ejp *extJSONParser) peekType() (bsontype.Type, error) {
- var t bsontype.Type
- var err error
-
- ejp.advanceState()
- switch ejp.s {
- case jpsSawValue:
- t = ejp.v.t
- case jpsSawBeginArray:
- t = bsontype.Array
- case jpsInvalidState:
- err = ejp.err
- case jpsSawComma:
- // in array mode, seeing a comma means we need to progress again to actually observe a type
- if ejp.peekMode() == jpmArrayMode {
- return ejp.peekType()
- }
- case jpsSawEndArray:
- // this would only be a valid state if we were in array mode, so return end-of-array error
- err = ErrEOA
- case jpsSawBeginObject:
- // peek key to determine type
- ejp.advanceState()
- switch ejp.s {
- case jpsSawEndObject: // empty embedded document
- t = bsontype.EmbeddedDocument
- ejp.emptyObject = true
- case jpsInvalidState:
- err = ejp.err
- case jpsSawKey:
- t = wrapperKeyBSONType(ejp.k)
-
- if t == bsontype.JavaScript {
- // just saw $code, need to check for $scope at same level
- _, err := ejp.readValue(bsontype.JavaScript)
-
- if err != nil {
- break
- }
-
- switch ejp.s {
- case jpsSawEndObject: // type is TypeJavaScript
- case jpsSawComma:
- ejp.advanceState()
- if ejp.s == jpsSawKey && ejp.k == "$scope" {
- t = bsontype.CodeWithScope
- } else {
- err = fmt.Errorf("invalid extended JSON: unexpected key %s in CodeWithScope object", ejp.k)
- }
- case jpsInvalidState:
- err = ejp.err
- default:
- err = ErrInvalidJSON
- }
- }
- }
- }
-
- return t, err
-}
-
-// readKey parses the next key and its type and returns them
-func (ejp *extJSONParser) readKey() (string, bsontype.Type, error) {
- if ejp.emptyObject {
- ejp.emptyObject = false
- return "", 0, ErrEOD
- }
-
- // advance to key (or return with error)
- switch ejp.s {
- case jpsStartState:
- ejp.advanceState()
- if ejp.s == jpsSawBeginObject {
- ejp.advanceState()
- }
- case jpsSawBeginObject:
- ejp.advanceState()
- case jpsSawValue, jpsSawEndObject, jpsSawEndArray:
- ejp.advanceState()
- switch ejp.s {
- case jpsSawBeginObject, jpsSawComma:
- ejp.advanceState()
- case jpsSawEndObject:
- return "", 0, ErrEOD
- case jpsDoneState:
- return "", 0, io.EOF
- case jpsInvalidState:
- return "", 0, ejp.err
- default:
- return "", 0, ErrInvalidJSON
- }
- case jpsSawKey: // do nothing (key was peeked before)
- default:
- return "", 0, invalidRequestError("key")
- }
-
- // read key
- var key string
-
- switch ejp.s {
- case jpsSawKey:
- key = ejp.k
- case jpsSawEndObject:
- return "", 0, ErrEOD
- case jpsInvalidState:
- return "", 0, ejp.err
- default:
- return "", 0, invalidRequestError("key")
- }
-
- // check for colon
- ejp.advanceState()
- if err := ensureColon(ejp.s, key); err != nil {
- return "", 0, err
- }
-
- // peek at the value to determine type
- t, err := ejp.peekType()
- if err != nil {
- return "", 0, err
- }
-
- return key, t, nil
-}
-
-// readValue returns the value corresponding to the Type returned by peekType
-func (ejp *extJSONParser) readValue(t bsontype.Type) (*extJSONValue, error) {
- if ejp.s == jpsInvalidState {
- return nil, ejp.err
- }
-
- var v *extJSONValue
-
- switch t {
- case bsontype.Null, bsontype.Boolean, bsontype.String:
- if ejp.s != jpsSawValue {
- return nil, invalidRequestError(t.String())
- }
- v = ejp.v
- case bsontype.Int32, bsontype.Int64, bsontype.Double:
- // relaxed version allows these to be literal number values
- if ejp.s == jpsSawValue {
- v = ejp.v
- break
- }
- fallthrough
- case bsontype.Decimal128, bsontype.Symbol, bsontype.ObjectID, bsontype.MinKey, bsontype.MaxKey, bsontype.Undefined:
- switch ejp.s {
- case jpsSawKey:
- // read colon
- ejp.advanceState()
- if err := ensureColon(ejp.s, ejp.k); err != nil {
- return nil, err
- }
-
- // read value
- ejp.advanceState()
- if ejp.s != jpsSawValue || !ejp.ensureExtValueType(t) {
- return nil, invalidJSONErrorForType("value", t)
- }
-
- v = ejp.v
-
- // read end object
- ejp.advanceState()
- if ejp.s != jpsSawEndObject {
- return nil, invalidJSONErrorForType("} after value", t)
- }
- default:
- return nil, invalidRequestError(t.String())
- }
- case bsontype.Binary, bsontype.Regex, bsontype.Timestamp, bsontype.DBPointer:
- if ejp.s != jpsSawKey {
- return nil, invalidRequestError(t.String())
- }
- // read colon
- ejp.advanceState()
- if err := ensureColon(ejp.s, ejp.k); err != nil {
- return nil, err
- }
-
- ejp.advanceState()
- if t == bsontype.Binary && ejp.s == jpsSawValue {
- // convert legacy $binary format
- base64 := ejp.v
-
- ejp.advanceState()
- if ejp.s != jpsSawComma {
- return nil, invalidJSONErrorForType(",", bsontype.Binary)
- }
-
- ejp.advanceState()
- key, t, err := ejp.readKey()
- if err != nil {
- return nil, err
- }
- if key != "$type" {
- return nil, invalidJSONErrorForType("$type", bsontype.Binary)
- }
-
- subType, err := ejp.readValue(t)
- if err != nil {
- return nil, err
- }
-
- ejp.advanceState()
- if ejp.s != jpsSawEndObject {
- return nil, invalidJSONErrorForType("2 key-value pairs and then }", bsontype.Binary)
- }
-
- v = &extJSONValue{
- t: bsontype.EmbeddedDocument,
- v: &extJSONObject{
- keys: []string{"base64", "subType"},
- values: []*extJSONValue{base64, subType},
- },
- }
- break
- }
-
- // read KV pairs
- if ejp.s != jpsSawBeginObject {
- return nil, invalidJSONErrorForType("{", t)
- }
-
- keys, vals, err := ejp.readObject(2, true)
- if err != nil {
- return nil, err
- }
-
- ejp.advanceState()
- if ejp.s != jpsSawEndObject {
- return nil, invalidJSONErrorForType("2 key-value pairs and then }", t)
- }
-
- v = &extJSONValue{t: bsontype.EmbeddedDocument, v: &extJSONObject{keys: keys, values: vals}}
-
- case bsontype.DateTime:
- switch ejp.s {
- case jpsSawValue:
- v = ejp.v
- case jpsSawKey:
- // read colon
- ejp.advanceState()
- if err := ensureColon(ejp.s, ejp.k); err != nil {
- return nil, err
- }
-
- ejp.advanceState()
- switch ejp.s {
- case jpsSawBeginObject:
- keys, vals, err := ejp.readObject(1, true)
- if err != nil {
- return nil, err
- }
- v = &extJSONValue{t: bsontype.EmbeddedDocument, v: &extJSONObject{keys: keys, values: vals}}
- case jpsSawValue:
- if ejp.canonical {
- return nil, invalidJSONError("{")
- }
- v = ejp.v
- default:
- if ejp.canonical {
- return nil, invalidJSONErrorForType("object", t)
- }
- return nil, invalidJSONErrorForType("ISO-8601 Internet Date/Time Format as decribed in RFC-3339", t)
- }
-
- ejp.advanceState()
- if ejp.s != jpsSawEndObject {
- return nil, invalidJSONErrorForType("value and then }", t)
- }
- default:
- return nil, invalidRequestError(t.String())
- }
- case bsontype.JavaScript:
- switch ejp.s {
- case jpsSawKey:
- // read colon
- ejp.advanceState()
- if err := ensureColon(ejp.s, ejp.k); err != nil {
- return nil, err
- }
-
- // read value
- ejp.advanceState()
- if ejp.s != jpsSawValue {
- return nil, invalidJSONErrorForType("value", t)
- }
- v = ejp.v
-
- // read end object or comma and just return
- ejp.advanceState()
- case jpsSawEndObject:
- v = ejp.v
- default:
- return nil, invalidRequestError(t.String())
- }
- case bsontype.CodeWithScope:
- if ejp.s == jpsSawKey && ejp.k == "$scope" {
- v = ejp.v // this is the $code string from earlier
-
- // read colon
- ejp.advanceState()
- if err := ensureColon(ejp.s, ejp.k); err != nil {
- return nil, err
- }
-
- // read {
- ejp.advanceState()
- if ejp.s != jpsSawBeginObject {
- return nil, invalidJSONError("$scope to be embedded document")
- }
- } else {
- return nil, invalidRequestError(t.String())
- }
- case bsontype.EmbeddedDocument, bsontype.Array:
- return nil, invalidRequestError(t.String())
- }
-
- return v, nil
-}
-
-// readObject is a utility method for reading full objects of known (or expected) size
-// it is useful for extended JSON types such as binary, datetime, regex, and timestamp
-func (ejp *extJSONParser) readObject(numKeys int, started bool) ([]string, []*extJSONValue, error) {
- keys := make([]string, numKeys)
- vals := make([]*extJSONValue, numKeys)
-
- if !started {
- ejp.advanceState()
- if ejp.s != jpsSawBeginObject {
- return nil, nil, invalidJSONError("{")
- }
- }
-
- for i := 0; i < numKeys; i++ {
- key, t, err := ejp.readKey()
- if err != nil {
- return nil, nil, err
- }
-
- switch ejp.s {
- case jpsSawKey:
- v, err := ejp.readValue(t)
- if err != nil {
- return nil, nil, err
- }
-
- keys[i] = key
- vals[i] = v
- case jpsSawValue:
- keys[i] = key
- vals[i] = ejp.v
- default:
- return nil, nil, invalidJSONError("value")
- }
- }
-
- ejp.advanceState()
- if ejp.s != jpsSawEndObject {
- return nil, nil, invalidJSONError("}")
- }
-
- return keys, vals, nil
-}
-
-// advanceState reads the next JSON token from the scanner and transitions
-// from the current state based on that token's type
-func (ejp *extJSONParser) advanceState() {
- if ejp.s == jpsDoneState || ejp.s == jpsInvalidState {
- return
- }
-
- jt, err := ejp.js.nextToken()
-
- if err != nil {
- ejp.err = err
- ejp.s = jpsInvalidState
- return
- }
-
- valid := ejp.validateToken(jt.t)
- if !valid {
- ejp.err = unexpectedTokenError(jt)
- ejp.s = jpsInvalidState
- return
- }
-
- switch jt.t {
- case jttBeginObject:
- ejp.s = jpsSawBeginObject
- ejp.pushMode(jpmObjectMode)
- ejp.depth++
-
- if ejp.depth > ejp.maxDepth {
- ejp.err = nestingDepthError(jt.p, ejp.depth)
- ejp.s = jpsInvalidState
- }
- case jttEndObject:
- ejp.s = jpsSawEndObject
- ejp.depth--
-
- if ejp.popMode() != jpmObjectMode {
- ejp.err = unexpectedTokenError(jt)
- ejp.s = jpsInvalidState
- }
- case jttBeginArray:
- ejp.s = jpsSawBeginArray
- ejp.pushMode(jpmArrayMode)
- case jttEndArray:
- ejp.s = jpsSawEndArray
-
- if ejp.popMode() != jpmArrayMode {
- ejp.err = unexpectedTokenError(jt)
- ejp.s = jpsInvalidState
- }
- case jttColon:
- ejp.s = jpsSawColon
- case jttComma:
- ejp.s = jpsSawComma
- case jttEOF:
- ejp.s = jpsDoneState
- if len(ejp.m) != 0 {
- ejp.err = unexpectedTokenError(jt)
- ejp.s = jpsInvalidState
- }
- case jttString:
- switch ejp.s {
- case jpsSawComma:
- if ejp.peekMode() == jpmArrayMode {
- ejp.s = jpsSawValue
- ejp.v = extendJSONToken(jt)
- return
- }
- fallthrough
- case jpsSawBeginObject:
- ejp.s = jpsSawKey
- ejp.k = jt.v.(string)
- return
- }
- fallthrough
- default:
- ejp.s = jpsSawValue
- ejp.v = extendJSONToken(jt)
- }
-}
-
-var jpsValidTransitionTokens = map[jsonParseState]map[jsonTokenType]bool{
- jpsStartState: {
- jttBeginObject: true,
- jttBeginArray: true,
- jttInt32: true,
- jttInt64: true,
- jttDouble: true,
- jttString: true,
- jttBool: true,
- jttNull: true,
- jttEOF: true,
- },
- jpsSawBeginObject: {
- jttEndObject: true,
- jttString: true,
- },
- jpsSawEndObject: {
- jttEndObject: true,
- jttEndArray: true,
- jttComma: true,
- jttEOF: true,
- },
- jpsSawBeginArray: {
- jttBeginObject: true,
- jttBeginArray: true,
- jttEndArray: true,
- jttInt32: true,
- jttInt64: true,
- jttDouble: true,
- jttString: true,
- jttBool: true,
- jttNull: true,
- },
- jpsSawEndArray: {
- jttEndObject: true,
- jttEndArray: true,
- jttComma: true,
- jttEOF: true,
- },
- jpsSawColon: {
- jttBeginObject: true,
- jttBeginArray: true,
- jttInt32: true,
- jttInt64: true,
- jttDouble: true,
- jttString: true,
- jttBool: true,
- jttNull: true,
- },
- jpsSawComma: {
- jttBeginObject: true,
- jttBeginArray: true,
- jttInt32: true,
- jttInt64: true,
- jttDouble: true,
- jttString: true,
- jttBool: true,
- jttNull: true,
- },
- jpsSawKey: {
- jttColon: true,
- },
- jpsSawValue: {
- jttEndObject: true,
- jttEndArray: true,
- jttComma: true,
- jttEOF: true,
- },
- jpsDoneState: {},
- jpsInvalidState: {},
-}
-
-func (ejp *extJSONParser) validateToken(jtt jsonTokenType) bool {
- switch ejp.s {
- case jpsSawEndObject:
- // if we are at depth zero and the next token is a '{',
- // we can consider it valid only if we are not in array mode.
- if jtt == jttBeginObject && ejp.depth == 0 {
- return ejp.peekMode() != jpmArrayMode
- }
- case jpsSawComma:
- switch ejp.peekMode() {
- // the only valid next token after a comma inside a document is a string (a key)
- case jpmObjectMode:
- return jtt == jttString
- case jpmInvalidMode:
- return false
- }
- }
-
- _, ok := jpsValidTransitionTokens[ejp.s][jtt]
- return ok
-}
-
-// ensureExtValueType returns true if the current value has the expected
-// value type for single-key extended JSON types. For example,
-// {"$numberInt": v} v must be TypeString
-func (ejp *extJSONParser) ensureExtValueType(t bsontype.Type) bool {
- switch t {
- case bsontype.MinKey, bsontype.MaxKey:
- return ejp.v.t == bsontype.Int32
- case bsontype.Undefined:
- return ejp.v.t == bsontype.Boolean
- case bsontype.Int32, bsontype.Int64, bsontype.Double, bsontype.Decimal128, bsontype.Symbol, bsontype.ObjectID:
- return ejp.v.t == bsontype.String
- default:
- return false
- }
-}
-
-func (ejp *extJSONParser) pushMode(m jsonParseMode) {
- ejp.m = append(ejp.m, m)
-}
-
-func (ejp *extJSONParser) popMode() jsonParseMode {
- l := len(ejp.m)
- if l == 0 {
- return jpmInvalidMode
- }
-
- m := ejp.m[l-1]
- ejp.m = ejp.m[:l-1]
-
- return m
-}
-
-func (ejp *extJSONParser) peekMode() jsonParseMode {
- l := len(ejp.m)
- if l == 0 {
- return jpmInvalidMode
- }
-
- return ejp.m[l-1]
-}
-
-func extendJSONToken(jt *jsonToken) *extJSONValue {
- var t bsontype.Type
-
- switch jt.t {
- case jttInt32:
- t = bsontype.Int32
- case jttInt64:
- t = bsontype.Int64
- case jttDouble:
- t = bsontype.Double
- case jttString:
- t = bsontype.String
- case jttBool:
- t = bsontype.Boolean
- case jttNull:
- t = bsontype.Null
- default:
- return nil
- }
-
- return &extJSONValue{t: t, v: jt.v}
-}
-
-func ensureColon(s jsonParseState, key string) error {
- if s != jpsSawColon {
- return fmt.Errorf("invalid JSON input: missing colon after key \"%s\"", key)
- }
-
- return nil
-}
-
-func invalidRequestError(s string) error {
- return fmt.Errorf("invalid request to read %s", s)
-}
-
-func invalidJSONError(expected string) error {
- return fmt.Errorf("invalid JSON input; expected %s", expected)
-}
-
-func invalidJSONErrorForType(expected string, t bsontype.Type) error {
- return fmt.Errorf("invalid JSON input; expected %s for %s", expected, t)
-}
-
-func unexpectedTokenError(jt *jsonToken) error {
- switch jt.t {
- case jttInt32, jttInt64, jttDouble:
- return fmt.Errorf("invalid JSON input; unexpected number (%v) at position %d", jt.v, jt.p)
- case jttString:
- return fmt.Errorf("invalid JSON input; unexpected string (\"%v\") at position %d", jt.v, jt.p)
- case jttBool:
- return fmt.Errorf("invalid JSON input; unexpected boolean literal (%v) at position %d", jt.v, jt.p)
- case jttNull:
- return fmt.Errorf("invalid JSON input; unexpected null literal at position %d", jt.p)
- case jttEOF:
- return fmt.Errorf("invalid JSON input; unexpected end of input at position %d", jt.p)
- default:
- return fmt.Errorf("invalid JSON input; unexpected %c at position %d", jt.v.(byte), jt.p)
- }
-}
-
-func nestingDepthError(p, depth int) error {
- return fmt.Errorf("invalid JSON input; nesting too deep (%d levels) at position %d", depth, p)
-}