1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015 |
- // 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 bsoncore
-
- import (
- "bytes"
- "encoding/base64"
- "fmt"
- "math"
- "sort"
- "strconv"
- "strings"
- "time"
- "unicode/utf8"
-
- "go.mongodb.org/mongo-driver/bson/bsontype"
- "go.mongodb.org/mongo-driver/bson/primitive"
- )
-
- // ElementTypeError specifies that a method to obtain a BSON value an incorrect type was called on a bson.Value.
- type ElementTypeError struct {
- Method string
- Type bsontype.Type
- }
-
- // Error implements the error interface.
- func (ete ElementTypeError) Error() string {
- return "Call of " + ete.Method + " on " + ete.Type.String() + " type"
- }
-
- // Value represents a BSON value with a type and raw bytes.
- type Value struct {
- Type bsontype.Type
- Data []byte
- }
-
- // Validate ensures the value is a valid BSON value.
- func (v Value) Validate() error {
- _, _, valid := readValue(v.Data, v.Type)
- if !valid {
- return NewInsufficientBytesError(v.Data, v.Data)
- }
- return nil
- }
-
- // IsNumber returns true if the type of v is a numeric BSON type.
- func (v Value) IsNumber() bool {
- switch v.Type {
- case bsontype.Double, bsontype.Int32, bsontype.Int64, bsontype.Decimal128:
- return true
- default:
- return false
- }
- }
-
- // AsInt32 returns a BSON number as an int32. If the BSON type is not a numeric one, this method
- // will panic.
- //
- // TODO(skriptble): Add support for Decimal128.
- func (v Value) AsInt32() int32 {
- if !v.IsNumber() {
- panic(ElementTypeError{"bsoncore.Value.AsInt32", v.Type})
- }
- var i32 int32
- switch v.Type {
- case bsontype.Double:
- f64, _, ok := ReadDouble(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- i32 = int32(f64)
- case bsontype.Int32:
- var ok bool
- i32, _, ok = ReadInt32(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- case bsontype.Int64:
- i64, _, ok := ReadInt64(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- i32 = int32(i64)
- case bsontype.Decimal128:
- panic(ElementTypeError{"bsoncore.Value.AsInt32", v.Type})
- }
- return i32
- }
-
- // AsInt32OK functions the same as AsInt32 but returns a boolean instead of panicking. False
- // indicates an error.
- //
- // TODO(skriptble): Add support for Decimal128.
- func (v Value) AsInt32OK() (int32, bool) {
- if !v.IsNumber() {
- return 0, false
- }
- var i32 int32
- switch v.Type {
- case bsontype.Double:
- f64, _, ok := ReadDouble(v.Data)
- if !ok {
- return 0, false
- }
- i32 = int32(f64)
- case bsontype.Int32:
- var ok bool
- i32, _, ok = ReadInt32(v.Data)
- if !ok {
- return 0, false
- }
- case bsontype.Int64:
- i64, _, ok := ReadInt64(v.Data)
- if !ok {
- return 0, false
- }
- i32 = int32(i64)
- case bsontype.Decimal128:
- return 0, false
- }
- return i32, true
- }
-
- // AsInt64 returns a BSON number as an int64. If the BSON type is not a numeric one, this method
- // will panic.
- //
- // TODO(skriptble): Add support for Decimal128.
- func (v Value) AsInt64() int64 {
- if !v.IsNumber() {
- panic(ElementTypeError{"bsoncore.Value.AsInt64", v.Type})
- }
- var i64 int64
- switch v.Type {
- case bsontype.Double:
- f64, _, ok := ReadDouble(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- i64 = int64(f64)
- case bsontype.Int32:
- var ok bool
- i32, _, ok := ReadInt32(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- i64 = int64(i32)
- case bsontype.Int64:
- var ok bool
- i64, _, ok = ReadInt64(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- case bsontype.Decimal128:
- panic(ElementTypeError{"bsoncore.Value.AsInt64", v.Type})
- }
- return i64
- }
-
- // AsInt64OK functions the same as AsInt64 but returns a boolean instead of panicking. False
- // indicates an error.
- //
- // TODO(skriptble): Add support for Decimal128.
- func (v Value) AsInt64OK() (int64, bool) {
- if !v.IsNumber() {
- return 0, false
- }
- var i64 int64
- switch v.Type {
- case bsontype.Double:
- f64, _, ok := ReadDouble(v.Data)
- if !ok {
- return 0, false
- }
- i64 = int64(f64)
- case bsontype.Int32:
- var ok bool
- i32, _, ok := ReadInt32(v.Data)
- if !ok {
- return 0, false
- }
- i64 = int64(i32)
- case bsontype.Int64:
- var ok bool
- i64, _, ok = ReadInt64(v.Data)
- if !ok {
- return 0, false
- }
- case bsontype.Decimal128:
- return 0, false
- }
- return i64, true
- }
-
- // AsFloat64 returns a BSON number as an float64. If the BSON type is not a numeric one, this method
- // will panic.
- //
- // TODO(skriptble): Add support for Decimal128.
- func (v Value) AsFloat64() float64 { return 0 }
-
- // AsFloat64OK functions the same as AsFloat64 but returns a boolean instead of panicking. False
- // indicates an error.
- //
- // TODO(skriptble): Add support for Decimal128.
- func (v Value) AsFloat64OK() (float64, bool) { return 0, false }
-
- // Add will add this value to another. This is currently only implemented for strings and numbers.
- // If either value is a string, the other type is coerced into a string and added to the other.
- //
- // This method will alter v and will attempt to reuse the []byte of v. If the []byte is too small,
- // it will be expanded.
- func (v *Value) Add(v2 Value) error { return nil }
-
- // Equal compaes v to v2 and returns true if they are equal.
- func (v Value) Equal(v2 Value) bool {
- if v.Type != v2.Type {
- return false
- }
-
- return bytes.Equal(v.Data, v2.Data)
- }
-
- // String implements the fmt.String interface. This method will return values in extended JSON
- // format. If the value is not valid, this returns an empty string
- func (v Value) String() string {
- switch v.Type {
- case bsontype.Double:
- f64, ok := v.DoubleOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$numberDouble":"%s"}`, formatDouble(f64))
- case bsontype.String:
- str, ok := v.StringValueOK()
- if !ok {
- return ""
- }
- return escapeString(str)
- case bsontype.EmbeddedDocument:
- doc, ok := v.DocumentOK()
- if !ok {
- return ""
- }
- return doc.String()
- case bsontype.Array:
- arr, ok := v.ArrayOK()
- if !ok {
- return ""
- }
- return docAsArray(arr, false)
- case bsontype.Binary:
- subtype, data, ok := v.BinaryOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$binary":{"base64":"%s","subType":"%02x"}}`, base64.StdEncoding.EncodeToString(data), subtype)
- case bsontype.Undefined:
- return `{"$undefined":true}`
- case bsontype.ObjectID:
- oid, ok := v.ObjectIDOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$oid":"%s"}`, oid.Hex())
- case bsontype.Boolean:
- b, ok := v.BooleanOK()
- if !ok {
- return ""
- }
- return strconv.FormatBool(b)
- case bsontype.DateTime:
- dt, ok := v.DateTimeOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$date":{"$numberLong":"%d"}}`, dt)
- case bsontype.Null:
- return "null"
- case bsontype.Regex:
- pattern, options, ok := v.RegexOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(
- `{"$regularExpression":{"pattern":%s,"options":"%s"}}`,
- escapeString(pattern), sortStringAlphebeticAscending(options),
- )
- case bsontype.DBPointer:
- ns, pointer, ok := v.DBPointerOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$dbPointer":{"$ref":%s,"$id":{"$oid":"%s"}}}`, escapeString(ns), pointer.Hex())
- case bsontype.JavaScript:
- js, ok := v.JavaScriptOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$code":%s}`, escapeString(js))
- case bsontype.Symbol:
- symbol, ok := v.SymbolOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$symbol":%s}`, escapeString(symbol))
- case bsontype.CodeWithScope:
- code, scope, ok := v.CodeWithScopeOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$code":%s,"$scope":%s}`, code, scope)
- case bsontype.Int32:
- i32, ok := v.Int32OK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$numberInt":"%d"}`, i32)
- case bsontype.Timestamp:
- t, i, ok := v.TimestampOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$timestamp":{"t":"%s","i":"%s"}}`, strconv.FormatUint(uint64(t), 10), strconv.FormatUint(uint64(i), 10))
- case bsontype.Int64:
- i64, ok := v.Int64OK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$numberLong":"%d"}`, i64)
- case bsontype.Decimal128:
- d128, ok := v.Decimal128OK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$numberDecimal":"%s"}`, d128.String())
- case bsontype.MinKey:
- return `{"$minKey":1}`
- case bsontype.MaxKey:
- return `{"$maxKey":1}`
- default:
- return ""
- }
- }
-
- // DebugString outputs a human readable version of Document. It will attempt to stringify the
- // valid components of the document even if the entire document is not valid.
- func (v Value) DebugString() string {
- switch v.Type {
- case bsontype.String:
- str, ok := v.StringValueOK()
- if !ok {
- return "<malformed>"
- }
- return escapeString(str)
- case bsontype.EmbeddedDocument:
- doc, ok := v.DocumentOK()
- if !ok {
- return "<malformed>"
- }
- return doc.DebugString()
- case bsontype.Array:
- arr, ok := v.ArrayOK()
- if !ok {
- return "<malformed>"
- }
- return docAsArray(arr, true)
- case bsontype.CodeWithScope:
- code, scope, ok := v.CodeWithScopeOK()
- if !ok {
- return ""
- }
- return fmt.Sprintf(`{"$code":%s,"$scope":%s}`, code, scope.DebugString())
- default:
- str := v.String()
- if str == "" {
- return "<malformed>"
- }
- return str
- }
- }
-
- // Double returns the float64 value for this element.
- // It panics if e's BSON type is not bsontype.Double.
- func (v Value) Double() float64 {
- if v.Type != bsontype.Double {
- panic(ElementTypeError{"bsoncore.Value.Double", v.Type})
- }
- f64, _, ok := ReadDouble(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return f64
- }
-
- // DoubleOK is the same as Double, but returns a boolean instead of panicking.
- func (v Value) DoubleOK() (float64, bool) {
- if v.Type != bsontype.Double {
- return 0, false
- }
- f64, _, ok := ReadDouble(v.Data)
- if !ok {
- return 0, false
- }
- return f64, true
- }
-
- // StringValue returns the string balue for this element.
- // It panics if e's BSON type is not bsontype.String.
- //
- // NOTE: This method is called StringValue to avoid a collision with the String method which
- // implements the fmt.Stringer interface.
- func (v Value) StringValue() string {
- if v.Type != bsontype.String {
- panic(ElementTypeError{"bsoncore.Value.StringValue", v.Type})
- }
- str, _, ok := ReadString(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return str
- }
-
- // StringValueOK is the same as StringValue, but returns a boolean instead of
- // panicking.
- func (v Value) StringValueOK() (string, bool) {
- if v.Type != bsontype.String {
- return "", false
- }
- str, _, ok := ReadString(v.Data)
- if !ok {
- return "", false
- }
- return str, true
- }
-
- // Document returns the BSON document the Value represents as a Document. It panics if the
- // value is a BSON type other than document.
- func (v Value) Document() Document {
- if v.Type != bsontype.EmbeddedDocument {
- panic(ElementTypeError{"bsoncore.Value.Document", v.Type})
- }
- doc, _, ok := ReadDocument(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return doc
- }
-
- // DocumentOK is the same as Document, except it returns a boolean
- // instead of panicking.
- func (v Value) DocumentOK() (Document, bool) {
- if v.Type != bsontype.EmbeddedDocument {
- return nil, false
- }
- doc, _, ok := ReadDocument(v.Data)
- if !ok {
- return nil, false
- }
- return doc, true
- }
-
- // Array returns the BSON array the Value represents as an Array. It panics if the
- // value is a BSON type other than array.
- func (v Value) Array() Document {
- if v.Type != bsontype.Array {
- panic(ElementTypeError{"bsoncore.Value.Array", v.Type})
- }
- arr, _, ok := ReadArray(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return arr
- }
-
- // ArrayOK is the same as Array, except it returns a boolean instead
- // of panicking.
- func (v Value) ArrayOK() (Document, bool) {
- if v.Type != bsontype.Array {
- return nil, false
- }
- arr, _, ok := ReadArray(v.Data)
- if !ok {
- return nil, false
- }
- return arr, true
- }
-
- // Binary returns the BSON binary value the Value represents. It panics if the value is a BSON type
- // other than binary.
- func (v Value) Binary() (subtype byte, data []byte) {
- if v.Type != bsontype.Binary {
- panic(ElementTypeError{"bsoncore.Value.Binary", v.Type})
- }
- subtype, data, _, ok := ReadBinary(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return subtype, data
- }
-
- // BinaryOK is the same as Binary, except it returns a boolean instead of
- // panicking.
- func (v Value) BinaryOK() (subtype byte, data []byte, ok bool) {
- if v.Type != bsontype.Binary {
- return 0x00, nil, false
- }
- subtype, data, _, ok = ReadBinary(v.Data)
- if !ok {
- return 0x00, nil, false
- }
- return subtype, data, true
- }
-
- // ObjectID returns the BSON objectid value the Value represents. It panics if the value is a BSON
- // type other than objectid.
- func (v Value) ObjectID() primitive.ObjectID {
- if v.Type != bsontype.ObjectID {
- panic(ElementTypeError{"bsoncore.Value.ObjectID", v.Type})
- }
- oid, _, ok := ReadObjectID(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return oid
- }
-
- // ObjectIDOK is the same as ObjectID, except it returns a boolean instead of
- // panicking.
- func (v Value) ObjectIDOK() (primitive.ObjectID, bool) {
- if v.Type != bsontype.ObjectID {
- return primitive.ObjectID{}, false
- }
- oid, _, ok := ReadObjectID(v.Data)
- if !ok {
- return primitive.ObjectID{}, false
- }
- return oid, true
- }
-
- // Boolean returns the boolean value the Value represents. It panics if the
- // value is a BSON type other than boolean.
- func (v Value) Boolean() bool {
- if v.Type != bsontype.Boolean {
- panic(ElementTypeError{"bsoncore.Value.Boolean", v.Type})
- }
- b, _, ok := ReadBoolean(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return b
- }
-
- // BooleanOK is the same as Boolean, except it returns a boolean instead of
- // panicking.
- func (v Value) BooleanOK() (bool, bool) {
- if v.Type != bsontype.Boolean {
- return false, false
- }
- b, _, ok := ReadBoolean(v.Data)
- if !ok {
- return false, false
- }
- return b, true
- }
-
- // DateTime returns the BSON datetime value the Value represents as a
- // unix timestamp. It panics if the value is a BSON type other than datetime.
- func (v Value) DateTime() int64 {
- if v.Type != bsontype.DateTime {
- panic(ElementTypeError{"bsoncore.Value.DateTime", v.Type})
- }
- dt, _, ok := ReadDateTime(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return dt
- }
-
- // DateTimeOK is the same as DateTime, except it returns a boolean instead of
- // panicking.
- func (v Value) DateTimeOK() (int64, bool) {
- if v.Type != bsontype.DateTime {
- return 0, false
- }
- dt, _, ok := ReadDateTime(v.Data)
- if !ok {
- return 0, false
- }
- return dt, true
- }
-
- // Time returns the BSON datetime value the Value represents. It panics if the value is a BSON
- // type other than datetime.
- func (v Value) Time() time.Time {
- if v.Type != bsontype.DateTime {
- panic(ElementTypeError{"bsoncore.Value.Time", v.Type})
- }
- dt, _, ok := ReadDateTime(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return time.Unix(int64(dt)/1000, int64(dt)%1000*1000000)
- }
-
- // TimeOK is the same as Time, except it returns a boolean instead of
- // panicking.
- func (v Value) TimeOK() (time.Time, bool) {
- if v.Type != bsontype.DateTime {
- return time.Time{}, false
- }
- dt, _, ok := ReadDateTime(v.Data)
- if !ok {
- return time.Time{}, false
- }
- return time.Unix(int64(dt)/1000, int64(dt)%1000*1000000), true
- }
-
- // Regex returns the BSON regex value the Value represents. It panics if the value is a BSON
- // type other than regex.
- func (v Value) Regex() (pattern, options string) {
- if v.Type != bsontype.Regex {
- panic(ElementTypeError{"bsoncore.Value.Regex", v.Type})
- }
- pattern, options, _, ok := ReadRegex(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return pattern, options
- }
-
- // RegexOK is the same as Regex, except it returns a boolean instead of
- // panicking.
- func (v Value) RegexOK() (pattern, options string, ok bool) {
- if v.Type != bsontype.Regex {
- return "", "", false
- }
- pattern, options, _, ok = ReadRegex(v.Data)
- if !ok {
- return "", "", false
- }
- return pattern, options, true
- }
-
- // DBPointer returns the BSON dbpointer value the Value represents. It panics if the value is a BSON
- // type other than DBPointer.
- func (v Value) DBPointer() (string, primitive.ObjectID) {
- if v.Type != bsontype.DBPointer {
- panic(ElementTypeError{"bsoncore.Value.DBPointer", v.Type})
- }
- ns, pointer, _, ok := ReadDBPointer(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return ns, pointer
- }
-
- // DBPointerOK is the same as DBPoitner, except that it returns a boolean
- // instead of panicking.
- func (v Value) DBPointerOK() (string, primitive.ObjectID, bool) {
- if v.Type != bsontype.DBPointer {
- return "", primitive.ObjectID{}, false
- }
- ns, pointer, _, ok := ReadDBPointer(v.Data)
- if !ok {
- return "", primitive.ObjectID{}, false
- }
- return ns, pointer, true
- }
-
- // JavaScript returns the BSON JavaScript code value the Value represents. It panics if the value is
- // a BSON type other than JavaScript code.
- func (v Value) JavaScript() string {
- if v.Type != bsontype.JavaScript {
- panic(ElementTypeError{"bsoncore.Value.JavaScript", v.Type})
- }
- js, _, ok := ReadJavaScript(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return js
- }
-
- // JavaScriptOK is the same as Javascript, excepti that it returns a boolean
- // instead of panicking.
- func (v Value) JavaScriptOK() (string, bool) {
- if v.Type != bsontype.JavaScript {
- return "", false
- }
- js, _, ok := ReadJavaScript(v.Data)
- if !ok {
- return "", false
- }
- return js, true
- }
-
- // Symbol returns the BSON symbol value the Value represents. It panics if the value is a BSON
- // type other than symbol.
- func (v Value) Symbol() string {
- if v.Type != bsontype.Symbol {
- panic(ElementTypeError{"bsoncore.Value.Symbol", v.Type})
- }
- symbol, _, ok := ReadSymbol(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return symbol
- }
-
- // SymbolOK is the same as Symbol, excepti that it returns a boolean
- // instead of panicking.
- func (v Value) SymbolOK() (string, bool) {
- if v.Type != bsontype.Symbol {
- return "", false
- }
- symbol, _, ok := ReadSymbol(v.Data)
- if !ok {
- return "", false
- }
- return symbol, true
- }
-
- // CodeWithScope returns the BSON JavaScript code with scope the Value represents.
- // It panics if the value is a BSON type other than JavaScript code with scope.
- func (v Value) CodeWithScope() (string, Document) {
- if v.Type != bsontype.CodeWithScope {
- panic(ElementTypeError{"bsoncore.Value.CodeWithScope", v.Type})
- }
- code, scope, _, ok := ReadCodeWithScope(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return code, scope
- }
-
- // CodeWithScopeOK is the same as CodeWithScope, except that it returns a boolean instead of
- // panicking.
- func (v Value) CodeWithScopeOK() (string, Document, bool) {
- if v.Type != bsontype.CodeWithScope {
- return "", nil, false
- }
- code, scope, _, ok := ReadCodeWithScope(v.Data)
- if !ok {
- return "", nil, false
- }
- return code, scope, true
- }
-
- // Int32 returns the int32 the Value represents. It panics if the value is a BSON type other than
- // int32.
- func (v Value) Int32() int32 {
- if v.Type != bsontype.Int32 {
- panic(ElementTypeError{"bsoncore.Value.Int32", v.Type})
- }
- i32, _, ok := ReadInt32(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return i32
- }
-
- // Int32OK is the same as Int32, except that it returns a boolean instead of
- // panicking.
- func (v Value) Int32OK() (int32, bool) {
- if v.Type != bsontype.Int32 {
- return 0, false
- }
- i32, _, ok := ReadInt32(v.Data)
- if !ok {
- return 0, false
- }
- return i32, true
- }
-
- // Timestamp returns the BSON timestamp value the Value represents. It panics if the value is a
- // BSON type other than timestamp.
- func (v Value) Timestamp() (t, i uint32) {
- if v.Type != bsontype.Timestamp {
- panic(ElementTypeError{"bsoncore.Value.Timestamp", v.Type})
- }
- t, i, _, ok := ReadTimestamp(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return t, i
- }
-
- // TimestampOK is the same as Timestamp, except that it returns a boolean
- // instead of panicking.
- func (v Value) TimestampOK() (t, i uint32, ok bool) {
- if v.Type != bsontype.Timestamp {
- return 0, 0, false
- }
- t, i, _, ok = ReadTimestamp(v.Data)
- if !ok {
- return 0, 0, false
- }
- return t, i, true
- }
-
- // Int64 returns the int64 the Value represents. It panics if the value is a BSON type other than
- // int64.
- func (v Value) Int64() int64 {
- if v.Type != bsontype.Int64 {
- panic(ElementTypeError{"bsoncore.Value.Int64", v.Type})
- }
- i64, _, ok := ReadInt64(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return i64
- }
-
- // Int64OK is the same as Int64, except that it returns a boolean instead of
- // panicking.
- func (v Value) Int64OK() (int64, bool) {
- if v.Type != bsontype.Int64 {
- return 0, false
- }
- i64, _, ok := ReadInt64(v.Data)
- if !ok {
- return 0, false
- }
- return i64, true
- }
-
- // Decimal128 returns the decimal the Value represents. It panics if the value is a BSON type other than
- // decimal.
- func (v Value) Decimal128() primitive.Decimal128 {
- if v.Type != bsontype.Decimal128 {
- panic(ElementTypeError{"bsoncore.Value.Decimal128", v.Type})
- }
- d128, _, ok := ReadDecimal128(v.Data)
- if !ok {
- panic(NewInsufficientBytesError(v.Data, v.Data))
- }
- return d128
- }
-
- // Decimal128OK is the same as Decimal128, except that it returns a boolean
- // instead of panicking.
- func (v Value) Decimal128OK() (primitive.Decimal128, bool) {
- if v.Type != bsontype.Decimal128 {
- return primitive.Decimal128{}, false
- }
- d128, _, ok := ReadDecimal128(v.Data)
- if !ok {
- return primitive.Decimal128{}, false
- }
- return d128, true
- }
-
- var hexChars = "0123456789abcdef"
-
- func escapeString(s string) string {
- escapeHTML := true
- var buf bytes.Buffer
- buf.WriteByte('"')
- start := 0
- for i := 0; i < len(s); {
- if b := s[i]; b < utf8.RuneSelf {
- if htmlSafeSet[b] || (!escapeHTML && safeSet[b]) {
- i++
- continue
- }
- if start < i {
- buf.WriteString(s[start:i])
- }
- switch b {
- case '\\', '"':
- buf.WriteByte('\\')
- buf.WriteByte(b)
- case '\n':
- buf.WriteByte('\\')
- buf.WriteByte('n')
- case '\r':
- buf.WriteByte('\\')
- buf.WriteByte('r')
- case '\t':
- buf.WriteByte('\\')
- buf.WriteByte('t')
- case '\b':
- buf.WriteByte('\\')
- buf.WriteByte('b')
- case '\f':
- buf.WriteByte('\\')
- buf.WriteByte('f')
- default:
- // This encodes bytes < 0x20 except for \t, \n and \r.
- // If escapeHTML is set, it also escapes <, >, and &
- // because they can lead to security holes when
- // user-controlled strings are rendered into JSON
- // and served to some browsers.
- buf.WriteString(`\u00`)
- buf.WriteByte(hexChars[b>>4])
- buf.WriteByte(hexChars[b&0xF])
- }
- i++
- start = i
- continue
- }
- c, size := utf8.DecodeRuneInString(s[i:])
- if c == utf8.RuneError && size == 1 {
- if start < i {
- buf.WriteString(s[start:i])
- }
- buf.WriteString(`\ufffd`)
- i += size
- start = i
- continue
- }
- // U+2028 is LINE SEPARATOR.
- // U+2029 is PARAGRAPH SEPARATOR.
- // They are both technically valid characters in JSON strings,
- // but don't work in JSONP, which has to be evaluated as JavaScript,
- // and can lead to security holes there. It is valid JSON to
- // escape them, so we do so unconditionally.
- // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
- if c == '\u2028' || c == '\u2029' {
- if start < i {
- buf.WriteString(s[start:i])
- }
- buf.WriteString(`\u202`)
- buf.WriteByte(hexChars[c&0xF])
- i += size
- start = i
- continue
- }
- i += size
- }
- if start < len(s) {
- buf.WriteString(s[start:])
- }
- buf.WriteByte('"')
- return buf.String()
- }
-
- func formatDouble(f float64) string {
- var s string
- if math.IsInf(f, 1) {
- s = "Infinity"
- } else if math.IsInf(f, -1) {
- s = "-Infinity"
- } else if math.IsNaN(f) {
- s = "NaN"
- } else {
- // Print exactly one decimalType place for integers; otherwise, print as many are necessary to
- // perfectly represent it.
- s = strconv.FormatFloat(f, 'G', -1, 64)
- if !strings.ContainsRune(s, '.') {
- s += ".0"
- }
- }
-
- return s
- }
-
- type sortableString []rune
-
- func (ss sortableString) Len() int {
- return len(ss)
- }
-
- func (ss sortableString) Less(i, j int) bool {
- return ss[i] < ss[j]
- }
-
- func (ss sortableString) Swap(i, j int) {
- oldI := ss[i]
- ss[i] = ss[j]
- ss[j] = oldI
- }
-
- func sortStringAlphebeticAscending(s string) string {
- ss := sortableString([]rune(s))
- sort.Sort(ss)
- return string([]rune(ss))
- }
-
- func docAsArray(d Document, debug bool) string {
- if len(d) < 5 {
- return ""
- }
- var buf bytes.Buffer
- buf.WriteByte('[')
-
- length, rem, _ := ReadLength(d) // We know we have enough bytes to read the length
-
- length -= 4
-
- var elem Element
- var ok bool
- first := true
- for length > 1 {
- if !first {
- buf.WriteByte(',')
- }
- elem, rem, ok = ReadElement(rem)
- length -= int32(len(elem))
- if !ok {
- return ""
- }
- if debug {
- fmt.Fprintf(&buf, "%s ", elem.Value().DebugString())
- } else {
- fmt.Fprintf(&buf, "%s", elem.Value())
- }
- first = false
- }
- buf.WriteByte(']')
-
- return buf.String()
- }
|