Upgrade dependent version: go.mongodb.org/mongo-driver (#5320)

Upgrade dependent version: go.mongodb.org/mongo-driver v1.3.2 -> v1.10.4

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>

Signed-off-by: hongzhouzi <hongzhouzi@kubesphere.io>
This commit is contained in:
hongzhouzi
2022-12-02 15:08:55 +08:00
committed by GitHub
parent 493586d9a6
commit 0ca413cea3
61 changed files with 3025 additions and 1695 deletions

View File

@@ -7,8 +7,6 @@
// Based on gopkg.in/mgo.v2/bson by Gustavo Niemeyer
// See THIRD-PARTY-NOTICES for original license terms.
// +build go1.9
package bson // import "go.mongodb.org/mongo-driver/bson"
import (
@@ -25,9 +23,11 @@ type Zeroer interface {
// D is an ordered representation of a BSON document. This type should be used when the order of the elements matters,
// such as MongoDB command documents. If the order of the elements does not matter, an M should be used instead.
//
// A D should not be constructed with duplicate key names, as that can cause undefined server behavior.
//
// Example usage:
//
// bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
// bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
type D = primitive.D
// E represents a BSON element for a D. It is usually used inside a D.
@@ -39,12 +39,12 @@ type E = primitive.E
//
// Example usage:
//
// bson.M{"foo": "bar", "hello": "world", "pi": 3.14159}
// bson.M{"foo": "bar", "hello": "world", "pi": 3.14159}
type M = primitive.M
// An A is an ordered representation of a BSON array.
//
// Example usage:
//
// bson.A{"bar", "world", 3.14159, bson.D{{"qux", 12345}}}
// bson.A{"bar", "world", 3.14159, bson.D{{"qux", 12345}}}
type A = primitive.A

View File

@@ -1,81 +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
// +build !go1.9
package bson // import "go.mongodb.org/mongo-driver/bson"
import (
"math"
"strconv"
"strings"
)
// Zeroer allows custom struct types to implement a report of zero
// state. All struct types that don't implement Zeroer or where IsZero
// returns false are considered to be not zero.
type Zeroer interface {
IsZero() bool
}
// D is an ordered representation of a BSON document. This type should be used when the order of the elements matters,
// such as MongoDB command documents. If the order of the elements does not matter, an M should be used instead.
//
// Example usage:
//
// bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
type D []E
// Map creates a map from the elements of the D.
func (d D) Map() M {
m := make(M, len(d))
for _, e := range d {
m[e.Key] = e.Value
}
return m
}
// E represents a BSON element for a D. It is usually used inside a D.
type E struct {
Key string
Value interface{}
}
// M is an unordered representation of a BSON document. This type should be used when the order of the elements does not
// matter. This type is handled as a regular map[string]interface{} when encoding and decoding. Elements will be
// serialized in an undefined, random order. If the order of the elements matters, a D should be used instead.
//
// Example usage:
//
// bson.M{"foo": "bar", "hello": "world", "pi": 3.14159}
type M map[string]interface{}
// An A is an ordered representation of a BSON array.
//
// Example usage:
//
// bson.A{"bar", "world", 3.14159, bson.D{{"qux", 12345}}}
type A []interface{}
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
}

View File

@@ -0,0 +1,50 @@
// 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 bsoncodec
import (
"reflect"
"go.mongodb.org/mongo-driver/bson/bsonrw"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)
// ArrayCodec is the Codec used for bsoncore.Array values.
type ArrayCodec struct{}
var defaultArrayCodec = NewArrayCodec()
// NewArrayCodec returns an ArrayCodec.
func NewArrayCodec() *ArrayCodec {
return &ArrayCodec{}
}
// EncodeValue is the ValueEncoder for bsoncore.Array values.
func (ac *ArrayCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Type() != tCoreArray {
return ValueEncoderError{Name: "CoreArrayEncodeValue", Types: []reflect.Type{tCoreArray}, Received: val}
}
arr := val.Interface().(bsoncore.Array)
return bsonrw.Copier{}.CopyArrayFromBytes(vw, arr)
}
// DecodeValue is the ValueDecoder for bsoncore.Array values.
func (ac *ArrayCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Type() != tCoreArray {
return ValueDecoderError{Name: "CoreArrayDecodeValue", Types: []reflect.Type{tCoreArray}, Received: val}
}
if val.IsNil() {
val.Set(reflect.MakeSlice(val.Type(), 0, 0))
}
val.SetLen(0)
arr, err := bsonrw.Copier{}.AppendArrayBytes(val.Interface().(bsoncore.Array), vr)
val.Set(reflect.ValueOf(arr))
return err
}

View File

@@ -13,6 +13,11 @@ import (
"go.mongodb.org/mongo-driver/bson/bsonrw"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
)
var (
emptyValue = reflect.Value{}
)
// Marshaler is an interface implemented by types that can marshal themselves
@@ -39,7 +44,7 @@ type Unmarshaler interface {
}
// ValueUnmarshaler is an interface implemented by types that can unmarshal a
// BSON value representaiton of themselves. The BSON bytes and type can be
// BSON value representation of themselves. The BSON bytes and type can be
// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it
// wishes to retain the data after returning.
type ValueUnmarshaler interface {
@@ -114,11 +119,32 @@ type EncodeContext struct {
type DecodeContext struct {
*Registry
Truncate bool
// Ancestor is the type of a containing document. This is mainly used to determine what type
// should be used when decoding an embedded document into an empty interface. For example, if
// Ancestor is a bson.M, BSON embedded document values being decoded into an empty interface
// will be decoded into a bson.M.
//
// Deprecated: Use DefaultDocumentM or DefaultDocumentD instead.
Ancestor reflect.Type
// defaultDocumentType specifies the Go type to decode top-level and nested BSON documents into. In particular, the
// usage for this field is restricted to data typed as "interface{}" or "map[string]interface{}". If DocumentType is
// set to a type that a BSON document cannot be unmarshaled into (e.g. "string"), unmarshalling will result in an
// error. DocumentType overrides the Ancestor field.
defaultDocumentType reflect.Type
}
// DefaultDocumentM will decode empty documents using the primitive.M type. This behavior is restricted to data typed as
// "interface{}" or "map[string]interface{}".
func (dc *DecodeContext) DefaultDocumentM() {
dc.defaultDocumentType = reflect.TypeOf(primitive.M{})
}
// DefaultDocumentD will decode empty documents using the primitive.D type. This behavior is restricted to data typed as
// "interface{}" or "map[string]interface{}".
func (dc *DecodeContext) DefaultDocumentD() {
dc.defaultDocumentType = reflect.TypeOf(primitive.D{})
}
// ValueCodec is the interface that groups the methods to encode and decode
@@ -156,6 +182,55 @@ func (fn ValueDecoderFunc) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader,
return fn(dc, vr, val)
}
// typeDecoder is the interface implemented by types that can handle the decoding of a value given its type.
type typeDecoder interface {
decodeType(DecodeContext, bsonrw.ValueReader, reflect.Type) (reflect.Value, error)
}
// typeDecoderFunc is an adapter function that allows a function with the correct signature to be used as a typeDecoder.
type typeDecoderFunc func(DecodeContext, bsonrw.ValueReader, reflect.Type) (reflect.Value, error)
func (fn typeDecoderFunc) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) {
return fn(dc, vr, t)
}
// decodeAdapter allows two functions with the correct signatures to be used as both a ValueDecoder and typeDecoder.
type decodeAdapter struct {
ValueDecoderFunc
typeDecoderFunc
}
var _ ValueDecoder = decodeAdapter{}
var _ typeDecoder = decodeAdapter{}
// decodeTypeOrValue calls decoder.decodeType is decoder is a typeDecoder. Otherwise, it allocates a new element of type
// t and calls decoder.DecodeValue on it.
func decodeTypeOrValue(decoder ValueDecoder, dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) {
td, _ := decoder.(typeDecoder)
return decodeTypeOrValueWithInfo(decoder, td, dc, vr, t, true)
}
func decodeTypeOrValueWithInfo(vd ValueDecoder, td typeDecoder, dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type, convert bool) (reflect.Value, error) {
if td != nil {
val, err := td.decodeType(dc, vr, t)
if err == nil && convert && val.Type() != t {
// This conversion step is necessary for slices and maps. If a user declares variables like:
//
// type myBool bool
// var m map[string]myBool
//
// and tries to decode BSON bytes into the map, the decoding will fail if this conversion is not present
// because we'll try to assign a value of type bool to one of type myBool.
val = val.Convert(t)
}
return val, err
}
val := reflect.New(t).Elem()
err := vd.DecodeValue(dc, vr, val)
return val, err
}
// CodecZeroer is the interface implemented by Codecs that can also determine if
// a value of the type that would be encoded is zero.
type CodecZeroer interface {

View File

@@ -15,14 +15,17 @@ import (
"go.mongodb.org/mongo-driver/bson/bsontype"
)
var defaultByteSliceCodec = NewByteSliceCodec()
// ByteSliceCodec is the Codec used for []byte values.
type ByteSliceCodec struct {
EncodeNilAsEmpty bool
}
var _ ValueCodec = &ByteSliceCodec{}
var (
defaultByteSliceCodec = NewByteSliceCodec()
_ ValueCodec = defaultByteSliceCodec
_ typeDecoder = defaultByteSliceCodec
)
// NewByteSliceCodec returns a StringCodec with options opts.
func NewByteSliceCodec(opts ...*bsonoptions.ByteSliceCodecOptions) *ByteSliceCodec {
@@ -45,10 +48,13 @@ func (bsc *ByteSliceCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter,
return vw.WriteBinary(val.Interface().([]byte))
}
// DecodeValue is the ValueDecoder for []byte.
func (bsc *ByteSliceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Type() != tByteSlice {
return ValueDecoderError{Name: "ByteSliceDecodeValue", Types: []reflect.Type{tByteSlice}, Received: val}
func (bsc *ByteSliceCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) {
if t != tByteSlice {
return emptyValue, ValueDecoderError{
Name: "ByteSliceDecodeValue",
Types: []reflect.Type{tByteSlice},
Received: reflect.Zero(t),
}
}
var data []byte
@@ -57,31 +63,49 @@ func (bsc *ByteSliceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader,
case bsontype.String:
str, err := vr.ReadString()
if err != nil {
return err
return emptyValue, err
}
data = []byte(str)
case bsontype.Symbol:
sym, err := vr.ReadSymbol()
if err != nil {
return err
return emptyValue, err
}
data = []byte(sym)
case bsontype.Binary:
var subtype byte
data, subtype, err = vr.ReadBinary()
if err != nil {
return err
return emptyValue, err
}
if subtype != bsontype.BinaryGeneric && subtype != bsontype.BinaryBinaryOld {
return fmt.Errorf("ByteSliceDecodeValue can only be used to decode subtype 0x00 or 0x02 for %s, got %v", bsontype.Binary, subtype)
return emptyValue, decodeBinaryError{subtype: subtype, typeName: "[]byte"}
}
case bsontype.Null:
val.Set(reflect.Zero(val.Type()))
return vr.ReadNull()
err = vr.ReadNull()
case bsontype.Undefined:
err = vr.ReadUndefined()
default:
return fmt.Errorf("cannot decode %v into a []byte", vrType)
return emptyValue, fmt.Errorf("cannot decode %v into a []byte", vrType)
}
if err != nil {
return emptyValue, err
}
val.Set(reflect.ValueOf(data))
return reflect.ValueOf(data), nil
}
// DecodeValue is the ValueDecoder for []byte.
func (bsc *ByteSliceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Type() != tByteSlice {
return ValueDecoderError{Name: "ByteSliceDecodeValue", Types: []reflect.Type{tByteSlice}, Received: val}
}
elem, err := bsc.decodeType(dc, vr, tByteSlice)
if err != nil {
return err
}
val.Set(elem)
return nil
}

View File

@@ -0,0 +1,63 @@
// 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 bsoncodec
import (
"reflect"
"go.mongodb.org/mongo-driver/bson/bsonrw"
)
// condAddrEncoder is the encoder used when a pointer to the encoding value has an encoder.
type condAddrEncoder struct {
canAddrEnc ValueEncoder
elseEnc ValueEncoder
}
var _ ValueEncoder = (*condAddrEncoder)(nil)
// newCondAddrEncoder returns an condAddrEncoder.
func newCondAddrEncoder(canAddrEnc, elseEnc ValueEncoder) *condAddrEncoder {
encoder := condAddrEncoder{canAddrEnc: canAddrEnc, elseEnc: elseEnc}
return &encoder
}
// EncodeValue is the ValueEncoderFunc for a value that may be addressable.
func (cae *condAddrEncoder) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
if val.CanAddr() {
return cae.canAddrEnc.EncodeValue(ec, vw, val)
}
if cae.elseEnc != nil {
return cae.elseEnc.EncodeValue(ec, vw, val)
}
return ErrNoEncoder{Type: val.Type()}
}
// condAddrDecoder is the decoder used when a pointer to the value has a decoder.
type condAddrDecoder struct {
canAddrDec ValueDecoder
elseDec ValueDecoder
}
var _ ValueDecoder = (*condAddrDecoder)(nil)
// newCondAddrDecoder returns an CondAddrDecoder.
func newCondAddrDecoder(canAddrDec, elseDec ValueDecoder) *condAddrDecoder {
decoder := condAddrDecoder{canAddrDec: canAddrDec, elseDec: elseDec}
return &decoder
}
// DecodeValue is the ValueDecoderFunc for a value that may be addressable.
func (cad *condAddrDecoder) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if val.CanAddr() {
return cad.canAddrDec.DecodeValue(dc, vr, val)
}
if cad.elseDec != nil {
return cad.elseDec.DecodeValue(dc, vr, val)
}
return ErrNoDecoder{Type: val.Type()}
}

File diff suppressed because it is too large Load Diff

View File

@@ -30,7 +30,7 @@ var errInvalidValue = errors.New("cannot encode invalid element")
var sliceWriterPool = sync.Pool{
New: func() interface{} {
sw := make(bsonrw.SliceWriter, 0, 0)
sw := make(bsonrw.SliceWriter, 0)
return &sw
},
}
@@ -70,6 +70,7 @@ func (dve DefaultValueEncoders) RegisterDefaultEncoders(rb *RegistryBuilder) {
RegisterTypeEncoder(tByteSlice, defaultByteSliceCodec).
RegisterTypeEncoder(tTime, defaultTimeCodec).
RegisterTypeEncoder(tEmpty, defaultEmptyInterfaceCodec).
RegisterTypeEncoder(tCoreArray, defaultArrayCodec).
RegisterTypeEncoder(tOID, ValueEncoderFunc(dve.ObjectIDEncodeValue)).
RegisterTypeEncoder(tDecimal, ValueEncoderFunc(dve.Decimal128EncodeValue)).
RegisterTypeEncoder(tJSONNumber, ValueEncoderFunc(dve.JSONNumberEncodeValue)).
@@ -104,7 +105,7 @@ func (dve DefaultValueEncoders) RegisterDefaultEncoders(rb *RegistryBuilder) {
RegisterDefaultEncoder(reflect.Map, defaultMapCodec).
RegisterDefaultEncoder(reflect.Slice, defaultSliceCodec).
RegisterDefaultEncoder(reflect.String, defaultStringCodec).
RegisterDefaultEncoder(reflect.Struct, defaultStructCodec).
RegisterDefaultEncoder(reflect.Struct, newDefaultStructCodec()).
RegisterDefaultEncoder(reflect.Ptr, NewPointerCodec()).
RegisterHookEncoder(tValueMarshaler, ValueEncoderFunc(dve.ValueMarshalerEncodeValue)).
RegisterHookEncoder(tMarshaler, ValueEncoderFunc(dve.MarshalerEncodeValue)).
@@ -150,8 +151,8 @@ func (dve DefaultValueEncoders) IntEncodeValue(ec EncodeContext, vw bsonrw.Value
}
// UintEncodeValue is the ValueEncoderFunc for uint types.
// This method is deprecated and does not have any stability guarantees. It may be removed in the
// future. Use UIntCodec.EncodeValue instead.
//
// Deprecated: UintEncodeValue is not registered by default. Use UintCodec.EncodeValue instead.
func (dve DefaultValueEncoders) UintEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
switch val.Kind() {
case reflect.Uint8, reflect.Uint16:
@@ -185,8 +186,8 @@ func (dve DefaultValueEncoders) FloatEncodeValue(ec EncodeContext, vw bsonrw.Val
}
// StringEncodeValue is the ValueEncoderFunc for string types.
// This method is deprecated and does not have any stability guarantees. It may be removed in the
// future. Use StringCodec.EncodeValue instead.
//
// Deprecated: StringEncodeValue is not registered by default. Use StringCodec.EncodeValue instead.
func (dve DefaultValueEncoders) StringEncodeValue(ectx EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
if val.Kind() != reflect.String {
return ValueEncoderError{
@@ -245,19 +246,20 @@ func (dve DefaultValueEncoders) URLEncodeValue(ec EncodeContext, vw bsonrw.Value
}
// TimeEncodeValue is the ValueEncoderFunc for time.TIme.
// This method is deprecated and does not have any stability guarantees. It may be removed in the
// future. Use TimeCodec.EncodeValue instead.
//
// Deprecated: TimeEncodeValue is not registered by default. Use TimeCodec.EncodeValue instead.
func (dve DefaultValueEncoders) TimeEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Type() != tTime {
return ValueEncoderError{Name: "TimeEncodeValue", Types: []reflect.Type{tTime}, Received: val}
}
tt := val.Interface().(time.Time)
return vw.WriteDateTime(tt.Unix()*1000 + int64(tt.Nanosecond()/1e6))
dt := primitive.NewDateTimeFromTime(tt)
return vw.WriteDateTime(int64(dt))
}
// ByteSliceEncodeValue is the ValueEncoderFunc for []byte.
// This method is deprecated and does not have any stability guarantees. It may be removed in the
// future. Use ByteSliceCodec.EncodeValue instead.
//
// Deprecated: ByteSliceEncodeValue is not registered by default. Use ByteSliceCodec.EncodeValue instead.
func (dve DefaultValueEncoders) ByteSliceEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Type() != tByteSlice {
return ValueEncoderError{Name: "ByteSliceEncodeValue", Types: []reflect.Type{tByteSlice}, Received: val}
@@ -269,8 +271,8 @@ func (dve DefaultValueEncoders) ByteSliceEncodeValue(ec EncodeContext, vw bsonrw
}
// MapEncodeValue is the ValueEncoderFunc for map[string]* types.
// This method is deprecated and does not have any stability guarantees. It may be removed in the
// future. Use MapCodec.EncodeValue instead.
//
// Deprecated: MapEncodeValue is not registered by default. Use MapCodec.EncodeValue instead.
func (dve DefaultValueEncoders) MapEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Kind() != reflect.Map || val.Type().Key().Kind() != reflect.String {
return ValueEncoderError{Name: "MapEncodeValue", Kinds: []reflect.Kind{reflect.Map}, Received: val}
@@ -331,14 +333,7 @@ func (dve DefaultValueEncoders) mapEncodeValue(ec EncodeContext, dw bsonrw.Docum
continue
}
if enc, ok := currEncoder.(ValueEncoder); ok {
err = enc.EncodeValue(ec, vw, currVal)
if err != nil {
return err
}
continue
}
err = encoder.EncodeValue(ec, vw, currVal)
err = currEncoder.EncodeValue(ec, vw, currVal)
if err != nil {
return err
}
@@ -419,8 +414,8 @@ func (dve DefaultValueEncoders) ArrayEncodeValue(ec EncodeContext, vw bsonrw.Val
}
// SliceEncodeValue is the ValueEncoderFunc for slice types.
// This method is deprecated and does not have any stability guarantees. It may be removed in the
// future. Use SliceCodec.EncodeValue instead.
//
// Deprecated: SliceEncodeValue is not registered by default. Use SliceCodec.EncodeValue instead.
func (dve DefaultValueEncoders) SliceEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Kind() != reflect.Slice {
return ValueEncoderError{Name: "SliceEncodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: val}
@@ -501,8 +496,8 @@ func (dve DefaultValueEncoders) lookupElementEncoder(ec EncodeContext, origEncod
}
// EmptyInterfaceEncodeValue is the ValueEncoderFunc for interface{}.
// This method is deprecated and does not have any stability guarantees. It may be removed in the
// future. Use EmptyInterfaceCodec.EncodeValue instead.
//
// Deprecated: EmptyInterfaceEncodeValue is not registered by default. Use EmptyInterfaceCodec.EncodeValue instead.
func (dve DefaultValueEncoders) EmptyInterfaceEncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Type() != tEmpty {
return ValueEncoderError{Name: "EmptyInterfaceEncodeValue", Types: []reflect.Type{tEmpty}, Received: val}

View File

@@ -1,3 +1,9 @@
// Copyright (C) MongoDB, Inc. 2022-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 bsoncodec provides a system for encoding values to BSON representations and decoding
// values from BSON representations. This package considers both binary BSON and ExtendedJSON as
// BSON representations. The types in this package enable a flexible system for handling this
@@ -11,7 +17,7 @@
// 2) A Registry that holds these ValueEncoders and ValueDecoders and provides methods for
// retrieving them.
//
// ValueEncoders and ValueDecoders
// # ValueEncoders and ValueDecoders
//
// The ValueEncoder interface is implemented by types that can encode a provided Go type to BSON.
// The value to encode is provided as a reflect.Value and a bsonrw.ValueWriter is used within the
@@ -25,7 +31,7 @@
// allow the use of a function with the correct signature as a ValueDecoder. A DecodeContext
// instance is provided and serves similar functionality to the EncodeContext.
//
// Registry and RegistryBuilder
// # Registry and RegistryBuilder
//
// A Registry is an immutable store for ValueEncoders, ValueDecoders, and a type map. See the Registry type
// documentation for examples of registering various custom encoders and decoders. A Registry can be constructed using a
@@ -47,15 +53,15 @@
// values decode as Go int32 and int64 instances, respectively, when decoding into a bson.D. The following code would
// change the behavior so these values decode as Go int instances instead:
//
// intType := reflect.TypeOf(int(0))
// registryBuilder.RegisterTypeMapEntry(bsontype.Int32, intType).RegisterTypeMapEntry(bsontype.Int64, intType)
// intType := reflect.TypeOf(int(0))
// registryBuilder.RegisterTypeMapEntry(bsontype.Int32, intType).RegisterTypeMapEntry(bsontype.Int64, intType)
//
// 4. Kind encoder/decoders - These can be registered using the RegisterDefaultEncoder and RegisterDefaultDecoder
// methods. The registered codec will be invoked when encoding or decoding values whose reflect.Kind matches the
// registered reflect.Kind as long as the value's type doesn't match a registered type or hook encoder/decoder first.
// These methods should be used to change the behavior for all values for a specific kind.
//
// Registry Lookup Procedure
// # Registry Lookup Procedure
//
// When looking up an encoder in a Registry, the precedence rules are as follows:
//
@@ -73,7 +79,7 @@
// rules apply for decoders, with the exception that an error of type ErrNoDecoder will be returned if no decoder is
// found.
//
// DefaultValueEncoders and DefaultValueDecoders
// # DefaultValueEncoders and DefaultValueDecoders
//
// The DefaultValueEncoders and DefaultValueDecoders types provide a full set of ValueEncoders and
// ValueDecoders for handling a wide range of Go types, including all of the types within the

View File

@@ -15,14 +15,17 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive"
)
var defaultEmptyInterfaceCodec = NewEmptyInterfaceCodec()
// EmptyInterfaceCodec is the Codec used for interface{} values.
type EmptyInterfaceCodec struct {
DecodeBinaryAsSlice bool
}
var _ ValueCodec = &EmptyInterfaceCodec{}
var (
defaultEmptyInterfaceCodec = NewEmptyInterfaceCodec()
_ ValueCodec = defaultEmptyInterfaceCodec
_ typeDecoder = defaultEmptyInterfaceCodec
)
// NewEmptyInterfaceCodec returns a EmptyInterfaceCodec with options opts.
func NewEmptyInterfaceCodec(opts ...*bsonoptions.EmptyInterfaceCodecOptions) *EmptyInterfaceCodec {
@@ -54,11 +57,18 @@ func (eic EmptyInterfaceCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWrit
func (eic EmptyInterfaceCodec) getEmptyInterfaceDecodeType(dc DecodeContext, valueType bsontype.Type) (reflect.Type, error) {
isDocument := valueType == bsontype.Type(0) || valueType == bsontype.EmbeddedDocument
if isDocument && dc.Ancestor != nil {
// Using ancestor information rather than looking up the type map entry forces consistent decoding.
// If we're decoding into a bson.D, subdocuments should also be decoded as bson.D, even if a type map entry
// has been registered.
return dc.Ancestor, nil
if isDocument {
if dc.defaultDocumentType != nil {
// If the bsontype is an embedded document and the DocumentType is set on the DecodeContext, then return
// that type.
return dc.defaultDocumentType, nil
}
if dc.Ancestor != nil {
// Using ancestor information rather than looking up the type map entry forces consistent decoding.
// If we're decoding into a bson.D, subdocuments should also be decoded as bson.D, even if a type map entry
// has been registered.
return dc.Ancestor, nil
}
}
rtype, err := dc.LookupTypeMapEntry(valueType)
@@ -86,33 +96,31 @@ func (eic EmptyInterfaceCodec) getEmptyInterfaceDecodeType(dc DecodeContext, val
return nil, err
}
// DecodeValue is the ValueDecoderFunc for interface{}.
func (eic EmptyInterfaceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Type() != tEmpty {
return ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: val}
func (eic EmptyInterfaceCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) {
if t != tEmpty {
return emptyValue, ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: reflect.Zero(t)}
}
rtype, err := eic.getEmptyInterfaceDecodeType(dc, vr.Type())
if err != nil {
switch vr.Type() {
case bsontype.Null:
val.Set(reflect.Zero(val.Type()))
return vr.ReadNull()
return reflect.Zero(t), vr.ReadNull()
default:
return err
return emptyValue, err
}
}
decoder, err := dc.LookupDecoder(rtype)
if err != nil {
return err
return emptyValue, err
}
elem := reflect.New(rtype).Elem()
err = decoder.DecodeValue(dc, vr, elem)
elem, err := decodeTypeOrValue(decoder, dc, vr, rtype)
if err != nil {
return err
return emptyValue, err
}
if eic.DecodeBinaryAsSlice && rtype == tBinary {
binElem := elem.Interface().(primitive.Binary)
if binElem.Subtype == bsontype.BinaryGeneric || binElem.Subtype == bsontype.BinaryBinaryOld {
@@ -120,6 +128,20 @@ func (eic EmptyInterfaceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueRead
}
}
return elem, nil
}
// DecodeValue is the ValueDecoderFunc for interface{}.
func (eic EmptyInterfaceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Type() != tEmpty {
return ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: val}
}
elem, err := eic.decodeType(dc, vr, val.Type())
if err != nil {
return err
}
val.Set(elem)
return nil
}

View File

@@ -7,6 +7,7 @@
package bsoncodec
import (
"encoding"
"fmt"
"reflect"
"strconv"
@@ -20,12 +21,29 @@ var defaultMapCodec = NewMapCodec()
// MapCodec is the Codec used for map values.
type MapCodec struct {
DecodeZerosMap bool
EncodeNilAsEmpty bool
DecodeZerosMap bool
EncodeNilAsEmpty bool
EncodeKeysWithStringer bool
}
var _ ValueCodec = &MapCodec{}
// KeyMarshaler is the interface implemented by an object that can marshal itself into a string key.
// This applies to types used as map keys and is similar to encoding.TextMarshaler.
type KeyMarshaler interface {
MarshalKey() (key string, err error)
}
// KeyUnmarshaler is the interface implemented by an object that can unmarshal a string representation
// of itself. This applies to types used as map keys and is similar to encoding.TextUnmarshaler.
//
// UnmarshalKey must be able to decode the form generated by MarshalKey.
// UnmarshalKey must copy the text if it wishes to retain the text
// after returning.
type KeyUnmarshaler interface {
UnmarshalKey(key string) error
}
// NewMapCodec returns a MapCodec with options opts.
func NewMapCodec(opts ...*bsonoptions.MapCodecOptions) *MapCodec {
mapOpt := bsonoptions.MergeMapCodecOptions(opts...)
@@ -37,6 +55,9 @@ func NewMapCodec(opts ...*bsonoptions.MapCodecOptions) *MapCodec {
if mapOpt.EncodeNilAsEmpty != nil {
codec.EncodeNilAsEmpty = *mapOpt.EncodeNilAsEmpty
}
if mapOpt.EncodeKeysWithStringer != nil {
codec.EncodeKeysWithStringer = *mapOpt.EncodeKeysWithStringer
}
return &codec
}
@@ -79,7 +100,11 @@ func (mc *MapCodec) mapEncodeValue(ec EncodeContext, dw bsonrw.DocumentWriter, v
keys := val.MapKeys()
for _, key := range keys {
keyStr := fmt.Sprint(key)
keyStr, err := mc.encodeKey(key)
if err != nil {
return err
}
if collisionFn != nil && collisionFn(keyStr) {
return fmt.Errorf("Key %s of inlined map conflicts with a struct field name", key)
}
@@ -102,14 +127,7 @@ func (mc *MapCodec) mapEncodeValue(ec EncodeContext, dw bsonrw.DocumentWriter, v
continue
}
if enc, ok := currEncoder.(ValueEncoder); ok {
err = enc.EncodeValue(ec, vw, currVal)
if err != nil {
return err
}
continue
}
err = encoder.EncodeValue(ec, vw, currVal)
err = currEncoder.EncodeValue(ec, vw, currVal)
if err != nil {
return err
}
@@ -129,6 +147,9 @@ func (mc *MapCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val ref
case bsontype.Null:
val.Set(reflect.Zero(val.Type()))
return vr.ReadNull()
case bsontype.Undefined:
val.Set(reflect.Zero(val.Type()))
return vr.ReadUndefined()
default:
return fmt.Errorf("cannot decode %v into a %s", vrType, val.Type())
}
@@ -151,13 +172,13 @@ func (mc *MapCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val ref
if err != nil {
return err
}
eTypeDecoder, _ := decoder.(typeDecoder)
if eType == tEmpty {
dc.Ancestor = val.Type()
}
keyType := val.Type().Key()
keyKind := keyType.Kind()
for {
key, vr, err := dr.ReadElement()
@@ -168,31 +189,16 @@ func (mc *MapCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val ref
return err
}
k := reflect.ValueOf(key)
if keyType != tString {
switch keyKind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
reflect.Float32, reflect.Float64:
parsed, err := strconv.ParseFloat(k.String(), 64)
if err != nil {
return fmt.Errorf("Map key is defined to be a decimal type (%v) but got error %v", keyKind, err)
}
k = reflect.ValueOf(parsed)
case reflect.String: // if keyType wraps string
default:
return fmt.Errorf("BSON map must have string or decimal keys. Got:%v", val.Type())
}
k = k.Convert(keyType)
}
elem := reflect.New(eType).Elem()
err = decoder.DecodeValue(dc, vr, elem)
k, err := mc.decodeKey(key, keyType)
if err != nil {
return err
}
elem, err := decodeTypeOrValueWithInfo(decoder, eTypeDecoder, dc, vr, eType, true)
if err != nil {
return newDecodeError(key, err)
}
val.SetMapIndex(k, elem)
}
return nil
@@ -204,3 +210,100 @@ func clearMap(m reflect.Value) {
m.SetMapIndex(k, none)
}
}
func (mc *MapCodec) encodeKey(val reflect.Value) (string, error) {
if mc.EncodeKeysWithStringer {
return fmt.Sprint(val), nil
}
// keys of any string type are used directly
if val.Kind() == reflect.String {
return val.String(), nil
}
// KeyMarshalers are marshaled
if km, ok := val.Interface().(KeyMarshaler); ok {
if val.Kind() == reflect.Ptr && val.IsNil() {
return "", nil
}
buf, err := km.MarshalKey()
if err == nil {
return buf, nil
}
return "", err
}
// keys implement encoding.TextMarshaler are marshaled.
if km, ok := val.Interface().(encoding.TextMarshaler); ok {
if val.Kind() == reflect.Ptr && val.IsNil() {
return "", nil
}
buf, err := km.MarshalText()
if err != nil {
return "", err
}
return string(buf), nil
}
switch val.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(val.Int(), 10), nil
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return strconv.FormatUint(val.Uint(), 10), nil
}
return "", fmt.Errorf("unsupported key type: %v", val.Type())
}
var keyUnmarshalerType = reflect.TypeOf((*KeyUnmarshaler)(nil)).Elem()
var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
func (mc *MapCodec) decodeKey(key string, keyType reflect.Type) (reflect.Value, error) {
keyVal := reflect.ValueOf(key)
var err error
switch {
// First, if EncodeKeysWithStringer is not enabled, try to decode withKeyUnmarshaler
case !mc.EncodeKeysWithStringer && reflect.PtrTo(keyType).Implements(keyUnmarshalerType):
keyVal = reflect.New(keyType)
v := keyVal.Interface().(KeyUnmarshaler)
err = v.UnmarshalKey(key)
keyVal = keyVal.Elem()
// Try to decode encoding.TextUnmarshalers.
case reflect.PtrTo(keyType).Implements(textUnmarshalerType):
keyVal = reflect.New(keyType)
v := keyVal.Interface().(encoding.TextUnmarshaler)
err = v.UnmarshalText([]byte(key))
keyVal = keyVal.Elem()
// Otherwise, go to type specific behavior
default:
switch keyType.Kind() {
case reflect.String:
keyVal = reflect.ValueOf(key).Convert(keyType)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, parseErr := strconv.ParseInt(key, 10, 64)
if parseErr != nil || reflect.Zero(keyType).OverflowInt(n) {
err = fmt.Errorf("failed to unmarshal number key %v", key)
}
keyVal = reflect.ValueOf(n).Convert(keyType)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
n, parseErr := strconv.ParseUint(key, 10, 64)
if parseErr != nil || reflect.Zero(keyType).OverflowUint(n) {
err = fmt.Errorf("failed to unmarshal number key %v", key)
break
}
keyVal = reflect.ValueOf(n).Convert(keyType)
case reflect.Float32, reflect.Float64:
if mc.EncodeKeysWithStringer {
parsed, err := strconv.ParseFloat(key, 64)
if err != nil {
return keyVal, fmt.Errorf("Map key is defined to be a decimal type (%v) but got error %v", keyType.Kind(), err)
}
keyVal = reflect.ValueOf(parsed)
break
}
fallthrough
default:
return keyVal, fmt.Errorf("unsupported key type: %v", keyType)
}
}
return keyVal, err
}

View File

@@ -14,11 +14,6 @@ import (
"go.mongodb.org/mongo-driver/bson/bsontype"
)
var defaultPointerCodec = &PointerCodec{
ecache: make(map[reflect.Type]ValueEncoder),
dcache: make(map[reflect.Type]ValueDecoder),
}
var _ ValueEncoder = &PointerCodec{}
var _ ValueDecoder = &PointerCodec{}
@@ -83,6 +78,10 @@ func (pc *PointerCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val
val.Set(reflect.Zero(val.Type()))
return vr.ReadNull()
}
if vr.Type() == bsontype.Undefined {
val.Set(reflect.Zero(val.Type()))
return vr.ReadUndefined()
}
if val.IsNil() {
val.Set(reflect.New(val.Type().Elem()))

View File

@@ -54,12 +54,6 @@ func (entme ErrNoTypeMapEntry) Error() string {
// ErrNotInterface is returned when the provided type is not an interface.
var ErrNotInterface = errors.New("The provided type is not an interface")
var defaultRegistry *Registry
func init() {
defaultRegistry = buildDefaultRegistry()
}
// A RegistryBuilder is used to build a Registry. This type is not goroutine
// safe.
type RegistryBuilder struct {
@@ -187,8 +181,9 @@ func (rb *RegistryBuilder) RegisterHookDecoder(t reflect.Type, dec ValueDecoder)
return rb
}
// RegisterEncoder has been deprecated and will be removed in a future major version release. Use RegisterTypeEncoder
// or RegisterHookEncoder instead.
// RegisterEncoder registers the provided type and encoder pair.
//
// Deprecated: Use RegisterTypeEncoder or RegisterHookEncoder instead.
func (rb *RegistryBuilder) RegisterEncoder(t reflect.Type, enc ValueEncoder) *RegistryBuilder {
if t == tEmpty {
rb.typeEncoders[t] = enc
@@ -210,8 +205,9 @@ func (rb *RegistryBuilder) RegisterEncoder(t reflect.Type, enc ValueEncoder) *Re
return rb
}
// RegisterDecoder has been deprecated and will be removed in a future major version release. Use RegisterTypeDecoder
// or RegisterHookDecoder instead.
// RegisterDecoder registers the provided type and decoder pair.
//
// Deprecated: Use RegisterTypeDecoder or RegisterHookDecoder instead.
func (rb *RegistryBuilder) RegisterDecoder(t reflect.Type, dec ValueDecoder) *RegistryBuilder {
if t == nil {
rb.typeDecoders[nil] = dec
@@ -258,6 +254,7 @@ func (rb *RegistryBuilder) RegisterDefaultDecoder(kind reflect.Kind, dec ValueDe
// By default, BSON documents will decode into interface{} values as bson.D. To change the default type for BSON
// documents, a type map entry for bsontype.EmbeddedDocument should be registered. For example, to force BSON documents
// to decode to bson.Raw, use the following code:
//
// rb.RegisterTypeMapEntry(bsontype.EmbeddedDocument, reflect.TypeOf(bson.Raw{}))
func (rb *RegistryBuilder) RegisterTypeMapEntry(bt bsontype.Type, rt reflect.Type) *RegistryBuilder {
rb.typeMap[bt] = rt
@@ -302,7 +299,7 @@ func (rb *RegistryBuilder) Build() *Registry {
return registry
}
// LookupEncoder inspects the registry for an encoder for the given type. The lookup precendence works as follows:
// LookupEncoder inspects the registry for an encoder for the given type. The lookup precedence works as follows:
//
// 1. An encoder registered for the exact type. If the given type represents an interface, an encoder registered using
// RegisterTypeEncoder for the interface will be selected.
@@ -325,7 +322,7 @@ func (r *Registry) LookupEncoder(t reflect.Type) (ValueEncoder, error) {
return enc, nil
}
enc, found = r.lookupInterfaceEncoder(t)
enc, found = r.lookupInterfaceEncoder(t, true)
if found {
r.mu.Lock()
r.typeEncoders[t] = enc
@@ -359,19 +356,28 @@ func (r *Registry) lookupTypeEncoder(t reflect.Type) (ValueEncoder, bool) {
return enc, found
}
func (r *Registry) lookupInterfaceEncoder(t reflect.Type) (ValueEncoder, bool) {
func (r *Registry) lookupInterfaceEncoder(t reflect.Type, allowAddr bool) (ValueEncoder, bool) {
if t == nil {
return nil, false
}
for _, ienc := range r.interfaceEncoders {
if t.Implements(ienc.i) || reflect.PtrTo(t).Implements(ienc.i) {
if t.Implements(ienc.i) {
return ienc.ve, true
}
if allowAddr && t.Kind() != reflect.Ptr && reflect.PtrTo(t).Implements(ienc.i) {
// if *t implements an interface, this will catch if t implements an interface further ahead
// in interfaceEncoders
defaultEnc, found := r.lookupInterfaceEncoder(t, false)
if !found {
defaultEnc = r.kindEncoders[t.Kind()]
}
return newCondAddrEncoder(ienc.ve, defaultEnc), true
}
}
return nil, false
}
// LookupDecoder inspects the registry for an decoder for the given type. The lookup precendence works as follows:
// LookupDecoder inspects the registry for an decoder for the given type. The lookup precedence works as follows:
//
// 1. A decoder registered for the exact type. If the given type represents an interface, a decoder registered using
// RegisterTypeDecoder for the interface will be selected.
@@ -397,7 +403,7 @@ func (r *Registry) LookupDecoder(t reflect.Type) (ValueDecoder, error) {
return dec, nil
}
dec, found = r.lookupInterfaceDecoder(t)
dec, found = r.lookupInterfaceDecoder(t, true)
if found {
r.mu.Lock()
r.typeDecoders[t] = dec
@@ -424,13 +430,20 @@ func (r *Registry) lookupTypeDecoder(t reflect.Type) (ValueDecoder, bool) {
return dec, found
}
func (r *Registry) lookupInterfaceDecoder(t reflect.Type) (ValueDecoder, bool) {
func (r *Registry) lookupInterfaceDecoder(t reflect.Type, allowAddr bool) (ValueDecoder, bool) {
for _, idec := range r.interfaceDecoders {
if !t.Implements(idec.i) && !reflect.PtrTo(t).Implements(idec.i) {
continue
if t.Implements(idec.i) {
return idec.vd, true
}
if allowAddr && t.Kind() != reflect.Ptr && reflect.PtrTo(t).Implements(idec.i) {
// if *t implements an interface, this will catch if t implements an interface further ahead
// in interfaceDecoders
defaultDec, found := r.lookupInterfaceDecoder(t, false)
if !found {
defaultDec = r.kindDecoders[t.Kind()]
}
return newCondAddrDecoder(idec.vd, defaultDec), true
}
return idec.vd, true
}
return nil, false
}

View File

@@ -123,6 +123,9 @@ func (sc *SliceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val r
case bsontype.Null:
val.Set(reflect.Zero(val.Type()))
return vr.ReadNull()
case bsontype.Undefined:
val.Set(reflect.Zero(val.Type()))
return vr.ReadUndefined()
case bsontype.Type(0), bsontype.EmbeddedDocument:
if val.Type().Elem() != tE {
return fmt.Errorf("cannot decode document into %s", val.Type())
@@ -149,8 +152,8 @@ func (sc *SliceCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val r
}
return nil
case bsontype.String:
if val.Type().Elem() != tByte {
return fmt.Errorf("SliceDecodeValue can only decode a string into a byte array, got %v", vrType)
if sliceType := val.Type().Elem(); sliceType != tByte {
return fmt.Errorf("SliceDecodeValue can only decode a string into a byte array, got %v", sliceType)
}
str, err := vr.ReadString()
if err != nil {

View File

@@ -15,14 +15,17 @@ import (
"go.mongodb.org/mongo-driver/bson/bsontype"
)
var defaultStringCodec = NewStringCodec()
// StringCodec is the Codec used for struct values.
type StringCodec struct {
DecodeObjectIDAsHex bool
}
var _ ValueCodec = &StringCodec{}
var (
defaultStringCodec = NewStringCodec()
_ ValueCodec = defaultStringCodec
_ typeDecoder = defaultStringCodec
)
// NewStringCodec returns a StringCodec with options opts.
func NewStringCodec(opts ...*bsonoptions.StringCodecOptions) *StringCodec {
@@ -43,23 +46,27 @@ func (sc *StringCodec) EncodeValue(ectx EncodeContext, vw bsonrw.ValueWriter, va
return vw.WriteString(val.String())
}
// DecodeValue is the ValueDecoder for string types.
func (sc *StringCodec) DecodeValue(dctx DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Kind() != reflect.String {
return ValueDecoderError{Name: "StringDecodeValue", Kinds: []reflect.Kind{reflect.String}, Received: val}
func (sc *StringCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) {
if t.Kind() != reflect.String {
return emptyValue, ValueDecoderError{
Name: "StringDecodeValue",
Kinds: []reflect.Kind{reflect.String},
Received: reflect.Zero(t),
}
}
var str string
var err error
switch vr.Type() {
case bsontype.String:
str, err = vr.ReadString()
if err != nil {
return err
return emptyValue, err
}
case bsontype.ObjectID:
oid, err := vr.ReadObjectID()
if err != nil {
return err
return emptyValue, err
}
if sc.DecodeObjectIDAsHex {
str = oid.Hex()
@@ -70,25 +77,43 @@ func (sc *StringCodec) DecodeValue(dctx DecodeContext, vr bsonrw.ValueReader, va
case bsontype.Symbol:
str, err = vr.ReadSymbol()
if err != nil {
return err
return emptyValue, err
}
case bsontype.Binary:
data, subtype, err := vr.ReadBinary()
if err != nil {
return err
return emptyValue, err
}
if subtype != bsontype.BinaryGeneric && subtype != bsontype.BinaryBinaryOld {
return fmt.Errorf("SliceDecodeValue can only be used to decode subtype 0x00 or 0x02 for %s, got %v", bsontype.Binary, subtype)
return emptyValue, decodeBinaryError{subtype: subtype, typeName: "string"}
}
str = string(data)
case bsontype.Null:
if err = vr.ReadNull(); err != nil {
return err
return emptyValue, err
}
case bsontype.Undefined:
if err = vr.ReadUndefined(); err != nil {
return emptyValue, err
}
default:
return fmt.Errorf("cannot decode %v into a string type", vr.Type())
return emptyValue, fmt.Errorf("cannot decode %v into a string type", vr.Type())
}
val.SetString(str)
return reflect.ValueOf(str), nil
}
// DecodeValue is the ValueDecoder for string types.
func (sc *StringCodec) DecodeValue(dctx DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Kind() != reflect.String {
return ValueDecoderError{Name: "StringDecodeValue", Kinds: []reflect.Kind{reflect.String}, Received: val}
}
elem, err := sc.decodeType(dctx, vr, val.Type())
if err != nil {
return err
}
val.SetString(elem.String())
return nil
}

View File

@@ -10,6 +10,7 @@ import (
"errors"
"fmt"
"reflect"
"sort"
"strings"
"sync"
"time"
@@ -19,9 +20,35 @@ import (
"go.mongodb.org/mongo-driver/bson/bsontype"
)
var defaultStructCodec = &StructCodec{
cache: make(map[reflect.Type]*structDescription),
parser: DefaultStructTagParser,
// DecodeError represents an error that occurs when unmarshalling BSON bytes into a native Go type.
type DecodeError struct {
keys []string
wrapped error
}
// Unwrap returns the underlying error
func (de *DecodeError) Unwrap() error {
return de.wrapped
}
// Error implements the error interface.
func (de *DecodeError) Error() string {
// The keys are stored in reverse order because the de.keys slice is builtup while propagating the error up the
// stack of BSON keys, so we call de.Keys(), which reverses them.
keyPath := strings.Join(de.Keys(), ".")
return fmt.Sprintf("error decoding key %s: %v", keyPath, de.wrapped)
}
// Keys returns the BSON key path that caused an error as a slice of strings. The keys in the slice are in top-down
// order. For example, if the document being unmarshalled was {a: {b: {c: 1}}} and the value for c was supposed to be
// a string, the keys slice will be ["a", "b", "c"].
func (de *DecodeError) Keys() []string {
reversedKeys := make([]string, 0, len(de.keys))
for idx := len(de.keys) - 1; idx >= 0; idx-- {
reversedKeys = append(reversedKeys, de.keys[idx])
}
return reversedKeys
}
// Zeroer allows custom struct types to implement a report of zero
@@ -33,13 +60,14 @@ type Zeroer interface {
// StructCodec is the Codec used for struct values.
type StructCodec struct {
cache map[reflect.Type]*structDescription
l sync.RWMutex
parser StructTagParser
DecodeZeroStruct bool
DecodeDeepZeroInline bool
EncodeOmitDefaultStruct bool
AllowUnexportedFields bool
cache map[reflect.Type]*structDescription
l sync.RWMutex
parser StructTagParser
DecodeZeroStruct bool
DecodeDeepZeroInline bool
EncodeOmitDefaultStruct bool
AllowUnexportedFields bool
OverwriteDuplicatedInlinedFields bool
}
var _ ValueEncoder = &StructCodec{}
@@ -67,6 +95,9 @@ func NewStructCodec(p StructTagParser, opts ...*bsonoptions.StructCodecOptions)
if structOpt.EncodeOmitDefaultStruct != nil {
codec.EncodeOmitDefaultStruct = *structOpt.EncodeOmitDefaultStruct
}
if structOpt.OverwriteDuplicatedInlinedFields != nil {
codec.OverwriteDuplicatedInlinedFields = *structOpt.OverwriteDuplicatedInlinedFields
}
if structOpt.AllowUnexportedFields != nil {
codec.AllowUnexportedFields = *structOpt.AllowUnexportedFields
}
@@ -166,6 +197,19 @@ func (sc *StructCodec) EncodeValue(r EncodeContext, vw bsonrw.ValueWriter, val r
return dw.WriteDocumentEnd()
}
func newDecodeError(key string, original error) error {
de, ok := original.(*DecodeError)
if !ok {
return &DecodeError{
keys: []string{key},
wrapped: original,
}
}
de.keys = append(de.keys, key)
return de
}
// DecodeValue implements the Codec interface.
// By default, map types in val will not be cleared. If a map has existing key/value pairs, it will be extended with the new ones from vr.
// For slices, the decoder will set the length of the slice to zero and append all elements. The underlying array will not be cleared.
@@ -181,6 +225,13 @@ func (sc *StructCodec) DecodeValue(r DecodeContext, vr bsonrw.ValueReader, val r
return err
}
val.Set(reflect.Zero(val.Type()))
return nil
case bsontype.Undefined:
if err := vr.ReadUndefined(); err != nil {
return err
}
val.Set(reflect.Zero(val.Type()))
return nil
default:
@@ -267,7 +318,8 @@ func (sc *StructCodec) DecodeValue(r DecodeContext, vr bsonrw.ValueReader, val r
}
if !field.CanSet() { // Being settable is a super set of being addressable.
return fmt.Errorf("cannot decode element '%s' into field %v; it is not settable", name, field)
innerErr := fmt.Errorf("field %v is not settable", field)
return newDecodeError(fd.name, innerErr)
}
if field.Kind() == reflect.Ptr && field.IsNil() {
field.Set(reflect.New(field.Type().Elem()))
@@ -276,19 +328,12 @@ func (sc *StructCodec) DecodeValue(r DecodeContext, vr bsonrw.ValueReader, val r
dctx := DecodeContext{Registry: r.Registry, Truncate: fd.truncate || r.Truncate}
if fd.decoder == nil {
return ErrNoDecoder{Type: field.Elem().Type()}
return newDecodeError(fd.name, ErrNoDecoder{Type: field.Elem().Type()})
}
if decoder, ok := fd.decoder.(ValueDecoder); ok {
err = decoder.DecodeValue(dctx, vr, field.Elem())
if err != nil {
return err
}
continue
}
err = fd.decoder.DecodeValue(dctx, vr, field)
err = fd.decoder.DecodeValue(dctx, vr, field.Elem())
if err != nil {
return err
return newDecodeError(fd.name, err)
}
}
@@ -350,7 +395,8 @@ type structDescription struct {
}
type fieldDescription struct {
name string
name string // BSON key name
fieldName string // struct field name
idx int
omitEmpty bool
minSize bool
@@ -360,6 +406,35 @@ type fieldDescription struct {
decoder ValueDecoder
}
type byIndex []fieldDescription
func (bi byIndex) Len() int { return len(bi) }
func (bi byIndex) Swap(i, j int) { bi[i], bi[j] = bi[j], bi[i] }
func (bi byIndex) Less(i, j int) bool {
// If a field is inlined, its index in the top level struct is stored at inline[0]
iIdx, jIdx := bi[i].idx, bi[j].idx
if len(bi[i].inline) > 0 {
iIdx = bi[i].inline[0]
}
if len(bi[j].inline) > 0 {
jIdx = bi[j].inline[0]
}
if iIdx != jIdx {
return iIdx < jIdx
}
for k, biik := range bi[i].inline {
if k >= len(bi[j].inline) {
return false
}
if biik != bi[j].inline[k] {
return biik < bi[j].inline[k]
}
}
return len(bi[i].inline) < len(bi[j].inline)
}
func (sc *StructCodec) describeStruct(r *Registry, t reflect.Type) (*structDescription, error) {
// We need to analyze the struct, including getting the tags, collecting
// information about inlining, and create a map of the field name to the field.
@@ -377,6 +452,7 @@ func (sc *StructCodec) describeStruct(r *Registry, t reflect.Type) (*structDescr
inlineMap: -1,
}
var fields []fieldDescription
for i := 0; i < numFields; i++ {
sf := t.Field(i)
if sf.PkgPath != "" && (!sc.AllowUnexportedFields || !sf.Anonymous) {
@@ -394,7 +470,12 @@ func (sc *StructCodec) describeStruct(r *Registry, t reflect.Type) (*structDescr
decoder = nil
}
description := fieldDescription{idx: i, encoder: encoder, decoder: decoder}
description := fieldDescription{
fieldName: sf.Name,
idx: i,
encoder: encoder,
decoder: decoder,
}
stags, err := sc.parser.ParseStructTags(sf)
if err != nil {
@@ -431,31 +512,62 @@ func (sc *StructCodec) describeStruct(r *Registry, t reflect.Type) (*structDescr
return nil, err
}
for _, fd := range inlinesf.fl {
if _, exists := sd.fm[fd.name]; exists {
return nil, fmt.Errorf("(struct %s) duplicated key %s", t.String(), fd.name)
}
if fd.inline == nil {
fd.inline = []int{i, fd.idx}
} else {
fd.inline = append([]int{i}, fd.inline...)
}
sd.fm[fd.name] = fd
sd.fl = append(sd.fl, fd)
fields = append(fields, fd)
}
default:
return nil, fmt.Errorf("(struct %s) inline fields must be a struct, a struct pointer, or a map", t.String())
}
continue
}
if _, exists := sd.fm[description.name]; exists {
return nil, fmt.Errorf("struct %s) duplicated key %s", t.String(), description.name)
}
sd.fm[description.name] = description
sd.fl = append(sd.fl, description)
fields = append(fields, description)
}
// Sort fieldDescriptions by name and use dominance rules to determine which should be added for each name
sort.Slice(fields, func(i, j int) bool {
x := fields
// sort field by name, breaking ties with depth, then
// breaking ties with index sequence.
if x[i].name != x[j].name {
return x[i].name < x[j].name
}
if len(x[i].inline) != len(x[j].inline) {
return len(x[i].inline) < len(x[j].inline)
}
return byIndex(x).Less(i, j)
})
for advance, i := 0, 0; i < len(fields); i += advance {
// One iteration per name.
// Find the sequence of fields with the name of this first field.
fi := fields[i]
name := fi.name
for advance = 1; i+advance < len(fields); advance++ {
fj := fields[i+advance]
if fj.name != name {
break
}
}
if advance == 1 { // Only one field with this name
sd.fl = append(sd.fl, fi)
sd.fm[name] = fi
continue
}
dominant, ok := dominantField(fields[i : i+advance])
if !ok || !sc.OverwriteDuplicatedInlinedFields {
return nil, fmt.Errorf("struct %s has duplicated key %s", t.String(), name)
}
sd.fl = append(sd.fl, dominant)
sd.fm[name] = dominant
}
sort.Sort(byIndex(sd.fl))
sc.l.Lock()
sc.cache[t] = sd
sc.l.Unlock()
@@ -463,6 +575,22 @@ func (sc *StructCodec) describeStruct(r *Registry, t reflect.Type) (*structDescr
return sd, nil
}
// dominantField looks through the fields, all of which are known to
// have the same name, to find the single field that dominates the
// others using Go's inlining rules. If there are multiple top-level
// fields, the boolean will be false: This condition is an error in Go
// and we skip all the fields.
func dominantField(fields []fieldDescription) (fieldDescription, bool) {
// The fields are sorted in increasing index-length order, then by presence of tag.
// That means that the first field is the dominant one. We need only check
// for error cases: two fields at top level.
if len(fields) > 1 &&
len(fields[0].inline) == len(fields[1].inline) {
return fieldDescription{}, false
}
return fields[0], true
}
func fieldByIndexErr(v reflect.Value, index []int) (result reflect.Value, err error) {
defer func() {
if recovered := recover(); recovered != nil {

View File

@@ -34,21 +34,21 @@ func (stpf StructTagParserFunc) ParseStructTags(sf reflect.StructField) (StructT
//
// The properties are defined below:
//
// OmitEmpty Only include the field if it's not set to the zero value for the type or to
// empty slices or maps.
// OmitEmpty Only include the field if it's not set to the zero value for the type or to
// empty slices or maps.
//
// MinSize Marshal an integer of a type larger than 32 bits value as an int32, if that's
// feasible while preserving the numeric value.
// MinSize Marshal an integer of a type larger than 32 bits value as an int32, if that's
// feasible while preserving the numeric value.
//
// Truncate When unmarshaling a BSON double, it is permitted to lose precision to fit within
// a float32.
// Truncate When unmarshaling a BSON double, it is permitted to lose precision to fit within
// a float32.
//
// Inline Inline the field, which must be a struct or a map, causing all of its fields
// or keys to be processed as if they were part of the outer struct. For maps,
// keys must not conflict with the bson keys of other struct fields.
// Inline Inline the field, which must be a struct or a map, causing all of its fields
// or keys to be processed as if they were part of the outer struct. For maps,
// keys must not conflict with the bson keys of other struct fields.
//
// Skip This struct field should be skipped. This is usually denoted by parsing a "-"
// for the name.
// Skip This struct field should be skipped. This is usually denoted by parsing a "-"
// for the name.
//
// TODO(skriptble): Add tags for undefined as nil and for null as nil.
type StructTags struct {
@@ -67,20 +67,20 @@ type StructTags struct {
// If there is no name in the struct tag fields, the struct field name is lowercased.
// The tag formats accepted are:
//
// "[<key>][,<flag1>[,<flag2>]]"
// "[<key>][,<flag1>[,<flag2>]]"
//
// `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
// `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
//
// An example:
//
// type T struct {
// A bool
// B int "myb"
// C string "myc,omitempty"
// D string `bson:",omitempty" json:"jsonkey"`
// E int64 ",minsize"
// F int64 "myf,omitempty,minsize"
// }
// type T struct {
// A bool
// B int "myb"
// C string "myc,omitempty"
// D string `bson:",omitempty" json:"jsonkey"`
// E int64 ",minsize"
// F int64 "myf,omitempty,minsize"
// }
//
// A struct tag either consisting entirely of '-' or with a bson key with a
// value consisting entirely of '-' will return a StructTags with Skip true and
@@ -91,6 +91,10 @@ var DefaultStructTagParser StructTagParserFunc = func(sf reflect.StructField) (S
if !ok && !strings.Contains(string(sf.Tag), ":") && len(sf.Tag) > 0 {
tag = string(sf.Tag)
}
return parseTags(key, tag)
}
func parseTags(key string, tag string) (StructTags, error) {
var st StructTags
if tag == "-" {
st.Skip = true
@@ -117,3 +121,19 @@ var DefaultStructTagParser StructTagParserFunc = func(sf reflect.StructField) (S
return st, nil
}
// JSONFallbackStructTagParser has the same behavior as DefaultStructTagParser
// but will also fallback to parsing the json tag instead on a field where the
// bson tag isn't available.
var JSONFallbackStructTagParser StructTagParserFunc = func(sf reflect.StructField) (StructTags, error) {
key := strings.ToLower(sf.Name)
tag, ok := sf.Tag.Lookup("bson")
if !ok {
tag, ok = sf.Tag.Lookup("json")
}
if !ok && !strings.Contains(string(sf.Tag), ":") && len(sf.Tag) > 0 {
tag = string(sf.Tag)
}
return parseTags(key, tag)
}

View File

@@ -14,20 +14,24 @@ import (
"go.mongodb.org/mongo-driver/bson/bsonoptions"
"go.mongodb.org/mongo-driver/bson/bsonrw"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
)
const (
timeFormatString = "2006-01-02T15:04:05.999Z07:00"
)
var defaultTimeCodec = NewTimeCodec()
// TimeCodec is the Codec used for time.Time values.
type TimeCodec struct {
UseLocalTimeZone bool
}
var _ ValueCodec = &TimeCodec{}
var (
defaultTimeCodec = NewTimeCodec()
_ ValueCodec = defaultTimeCodec
_ typeDecoder = defaultTimeCodec
)
// NewTimeCodec returns a TimeCodec with options opts.
func NewTimeCodec(opts ...*bsonoptions.TimeCodecOptions) *TimeCodec {
@@ -40,10 +44,13 @@ func NewTimeCodec(opts ...*bsonoptions.TimeCodecOptions) *TimeCodec {
return &codec
}
// DecodeValue is the ValueDecoderFunc for time.Time.
func (tc *TimeCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Type() != tTime {
return ValueDecoderError{Name: "TimeDecodeValue", Types: []reflect.Type{tTime}, Received: val}
func (tc *TimeCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) {
if t != tTime {
return emptyValue, ValueDecoderError{
Name: "TimeDecodeValue",
Types: []reflect.Type{tTime},
Received: reflect.Zero(t),
}
}
var timeVal time.Time
@@ -51,43 +58,61 @@ func (tc *TimeCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val re
case bsontype.DateTime:
dt, err := vr.ReadDateTime()
if err != nil {
return err
return emptyValue, err
}
timeVal = time.Unix(dt/1000, dt%1000*1000000)
case bsontype.String:
// assume strings are in the isoTimeFormat
timeStr, err := vr.ReadString()
if err != nil {
return err
return emptyValue, err
}
timeVal, err = time.Parse(timeFormatString, timeStr)
if err != nil {
return err
return emptyValue, err
}
case bsontype.Int64:
i64, err := vr.ReadInt64()
if err != nil {
return err
return emptyValue, err
}
timeVal = time.Unix(i64/1000, i64%1000*1000000)
case bsontype.Timestamp:
t, _, err := vr.ReadTimestamp()
if err != nil {
return err
return emptyValue, err
}
timeVal = time.Unix(int64(t), 0)
case bsontype.Null:
if err := vr.ReadNull(); err != nil {
return err
return emptyValue, err
}
case bsontype.Undefined:
if err := vr.ReadUndefined(); err != nil {
return emptyValue, err
}
default:
return fmt.Errorf("cannot decode %v into a time.Time", vrType)
return emptyValue, fmt.Errorf("cannot decode %v into a time.Time", vrType)
}
if !tc.UseLocalTimeZone {
timeVal = timeVal.UTC()
}
val.Set(reflect.ValueOf(timeVal))
return reflect.ValueOf(timeVal), nil
}
// DecodeValue is the ValueDecoderFunc for time.Time.
func (tc *TimeCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Type() != tTime {
return ValueDecoderError{Name: "TimeDecodeValue", Types: []reflect.Type{tTime}, Received: val}
}
elem, err := tc.decodeType(dc, vr, tTime)
if err != nil {
return err
}
val.Set(elem)
return nil
}
@@ -97,5 +122,6 @@ func (tc *TimeCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val re
return ValueEncoderError{Name: "TimeEncodeValue", Types: []reflect.Type{tTime}, Received: val}
}
tt := val.Interface().(time.Time)
return vw.WriteDateTime(tt.Unix()*1000 + int64(tt.Nanosecond()/1e6))
dt := primitive.NewDateTimeFromTime(tt)
return vw.WriteDateTime(int64(dt))
}

View File

@@ -16,36 +16,12 @@ import (
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)
var ptBool = reflect.TypeOf((*bool)(nil))
var ptInt8 = reflect.TypeOf((*int8)(nil))
var ptInt16 = reflect.TypeOf((*int16)(nil))
var ptInt32 = reflect.TypeOf((*int32)(nil))
var ptInt64 = reflect.TypeOf((*int64)(nil))
var ptInt = reflect.TypeOf((*int)(nil))
var ptUint8 = reflect.TypeOf((*uint8)(nil))
var ptUint16 = reflect.TypeOf((*uint16)(nil))
var ptUint32 = reflect.TypeOf((*uint32)(nil))
var ptUint64 = reflect.TypeOf((*uint64)(nil))
var ptUint = reflect.TypeOf((*uint)(nil))
var ptFloat32 = reflect.TypeOf((*float32)(nil))
var ptFloat64 = reflect.TypeOf((*float64)(nil))
var ptString = reflect.TypeOf((*string)(nil))
var tBool = reflect.TypeOf(false)
var tFloat32 = reflect.TypeOf(float32(0))
var tFloat64 = reflect.TypeOf(float64(0))
var tInt = reflect.TypeOf(int(0))
var tInt8 = reflect.TypeOf(int8(0))
var tInt16 = reflect.TypeOf(int16(0))
var tInt32 = reflect.TypeOf(int32(0))
var tInt64 = reflect.TypeOf(int64(0))
var tString = reflect.TypeOf("")
var tTime = reflect.TypeOf(time.Time{})
var tUint = reflect.TypeOf(uint(0))
var tUint8 = reflect.TypeOf(uint8(0))
var tUint16 = reflect.TypeOf(uint16(0))
var tUint32 = reflect.TypeOf(uint32(0))
var tUint64 = reflect.TypeOf(uint64(0))
var tEmpty = reflect.TypeOf((*interface{})(nil)).Elem()
var tByteSlice = reflect.TypeOf([]byte(nil))
@@ -74,8 +50,8 @@ var tDecimal = reflect.TypeOf(primitive.Decimal128{})
var tMinKey = reflect.TypeOf(primitive.MinKey{})
var tMaxKey = reflect.TypeOf(primitive.MaxKey{})
var tD = reflect.TypeOf(primitive.D{})
var tM = reflect.TypeOf(primitive.M{})
var tA = reflect.TypeOf(primitive.A{})
var tE = reflect.TypeOf(primitive.E{})
var tCoreDocument = reflect.TypeOf(bsoncore.Document{})
var tCoreArray = reflect.TypeOf(bsoncore.Array{})

View File

@@ -7,7 +7,6 @@
package bsoncodec
import (
"errors"
"fmt"
"math"
"reflect"
@@ -17,14 +16,17 @@ import (
"go.mongodb.org/mongo-driver/bson/bsontype"
)
var defaultUIntCodec = NewUIntCodec()
// UIntCodec is the Codec used for uint values.
type UIntCodec struct {
EncodeToMinSize bool
}
var _ ValueCodec = &UIntCodec{}
var (
defaultUIntCodec = NewUIntCodec()
_ ValueCodec = defaultUIntCodec
_ typeDecoder = defaultUIntCodec
)
// NewUIntCodec returns a UIntCodec with options opts.
func NewUIntCodec(opts ...*bsonoptions.UIntCodecOptions) *UIntCodec {
@@ -64,6 +66,93 @@ func (uic *UIntCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val r
}
}
func (uic *UIntCodec) decodeType(dc DecodeContext, vr bsonrw.ValueReader, t reflect.Type) (reflect.Value, error) {
var i64 int64
var err error
switch vrType := vr.Type(); vrType {
case bsontype.Int32:
i32, err := vr.ReadInt32()
if err != nil {
return emptyValue, err
}
i64 = int64(i32)
case bsontype.Int64:
i64, err = vr.ReadInt64()
if err != nil {
return emptyValue, err
}
case bsontype.Double:
f64, err := vr.ReadDouble()
if err != nil {
return emptyValue, err
}
if !dc.Truncate && math.Floor(f64) != f64 {
return emptyValue, errCannotTruncate
}
if f64 > float64(math.MaxInt64) {
return emptyValue, fmt.Errorf("%g overflows int64", f64)
}
i64 = int64(f64)
case bsontype.Boolean:
b, err := vr.ReadBoolean()
if err != nil {
return emptyValue, err
}
if b {
i64 = 1
}
case bsontype.Null:
if err = vr.ReadNull(); err != nil {
return emptyValue, err
}
case bsontype.Undefined:
if err = vr.ReadUndefined(); err != nil {
return emptyValue, err
}
default:
return emptyValue, fmt.Errorf("cannot decode %v into an integer type", vrType)
}
switch t.Kind() {
case reflect.Uint8:
if i64 < 0 || i64 > math.MaxUint8 {
return emptyValue, fmt.Errorf("%d overflows uint8", i64)
}
return reflect.ValueOf(uint8(i64)), nil
case reflect.Uint16:
if i64 < 0 || i64 > math.MaxUint16 {
return emptyValue, fmt.Errorf("%d overflows uint16", i64)
}
return reflect.ValueOf(uint16(i64)), nil
case reflect.Uint32:
if i64 < 0 || i64 > math.MaxUint32 {
return emptyValue, fmt.Errorf("%d overflows uint32", i64)
}
return reflect.ValueOf(uint32(i64)), nil
case reflect.Uint64:
if i64 < 0 {
return emptyValue, fmt.Errorf("%d overflows uint64", i64)
}
return reflect.ValueOf(uint64(i64)), nil
case reflect.Uint:
if i64 < 0 || int64(uint(i64)) != i64 { // Can we fit this inside of an uint
return emptyValue, fmt.Errorf("%d overflows uint", i64)
}
return reflect.ValueOf(uint(i64)), nil
default:
return emptyValue, ValueDecoderError{
Name: "UintDecodeValue",
Kinds: []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},
Received: reflect.Zero(t),
}
}
}
// DecodeValue is the ValueDecoder for uint types.
func (uic *UIntCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
if !val.CanSet() {
@@ -74,77 +163,11 @@ func (uic *UIntCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val r
}
}
var i64 int64
var err error
switch vrType := vr.Type(); vrType {
case bsontype.Int32:
i32, err := vr.ReadInt32()
if err != nil {
return err
}
i64 = int64(i32)
case bsontype.Int64:
i64, err = vr.ReadInt64()
if err != nil {
return err
}
case bsontype.Double:
f64, err := vr.ReadDouble()
if err != nil {
return err
}
if !dc.Truncate && math.Floor(f64) != f64 {
return errors.New("UintDecodeValue can only truncate float64 to an integer type when truncation is enabled")
}
if f64 > float64(math.MaxInt64) {
return fmt.Errorf("%g overflows int64", f64)
}
i64 = int64(f64)
case bsontype.Boolean:
b, err := vr.ReadBoolean()
if err != nil {
return err
}
if b {
i64 = 1
}
case bsontype.Null:
if err = vr.ReadNull(); err != nil {
return err
}
default:
return fmt.Errorf("cannot decode %v into an integer type", vrType)
elem, err := uic.decodeType(dc, vr, val.Type())
if err != nil {
return err
}
switch val.Kind() {
case reflect.Uint8:
if i64 < 0 || i64 > math.MaxUint8 {
return fmt.Errorf("%d overflows uint8", i64)
}
case reflect.Uint16:
if i64 < 0 || i64 > math.MaxUint16 {
return fmt.Errorf("%d overflows uint16", i64)
}
case reflect.Uint32:
if i64 < 0 || i64 > math.MaxUint32 {
return fmt.Errorf("%d overflows uint32", i64)
}
case reflect.Uint64:
if i64 < 0 {
return fmt.Errorf("%d overflows uint64", i64)
}
case reflect.Uint:
if i64 < 0 || int64(uint(i64)) != i64 { // Can we fit this inside of an uint
return fmt.Errorf("%d overflows uint", i64)
}
default:
return ValueDecoderError{
Name: "UintDecodeValue",
Kinds: []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},
Received: val,
}
}
val.SetUint(uint64(i64))
val.SetUint(elem.Uint())
return nil
}

View File

@@ -0,0 +1,8 @@
// Copyright (C) MongoDB, Inc. 2022-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 bsonoptions defines the optional configurations for the BSON codecs.
package bsonoptions

View File

@@ -10,6 +10,12 @@ package bsonoptions
type MapCodecOptions struct {
DecodeZerosMap *bool // Specifies if the map should be zeroed before decoding into it. Defaults to false.
EncodeNilAsEmpty *bool // Specifies if a nil map should encode as an empty document instead of null. Defaults to false.
// Specifies how keys should be handled. If false, the behavior matches encoding/json, where the encoding key type must
// either be a string, an integer type, or implement bsoncodec.KeyMarshaler and the decoding key type must either be a
// string, an integer type, or implement bsoncodec.KeyUnmarshaler. If true, keys are encoded with fmt.Sprint() and the
// encoding key type must be a string, an integer type, or a float. If true, the use of Stringer will override
// TextMarshaler/TextUnmarshaler. Defaults to false.
EncodeKeysWithStringer *bool
}
// MapCodec creates a new *MapCodecOptions
@@ -23,12 +29,22 @@ func (t *MapCodecOptions) SetDecodeZerosMap(b bool) *MapCodecOptions {
return t
}
// SetEncodeNilAsEmpty specifies if a nil map should encode as an empty document instead of null. Defaults to false.
// SetEncodeNilAsEmpty specifies if a nil map should encode as an empty document instead of null. Defaults to false.
func (t *MapCodecOptions) SetEncodeNilAsEmpty(b bool) *MapCodecOptions {
t.EncodeNilAsEmpty = &b
return t
}
// SetEncodeKeysWithStringer specifies how keys should be handled. If false, the behavior matches encoding/json, where the
// encoding key type must either be a string, an integer type, or implement bsoncodec.KeyMarshaler and the decoding key
// type must either be a string, an integer type, or implement bsoncodec.KeyUnmarshaler. If true, keys are encoded with
// fmt.Sprint() and the encoding key type must be a string, an integer type, or a float. If true, the use of Stringer
// will override TextMarshaler/TextUnmarshaler. Defaults to false.
func (t *MapCodecOptions) SetEncodeKeysWithStringer(b bool) *MapCodecOptions {
t.EncodeKeysWithStringer = &b
return t
}
// MergeMapCodecOptions combines the given *MapCodecOptions into a single *MapCodecOptions in a last one wins fashion.
func MergeMapCodecOptions(opts ...*MapCodecOptions) *MapCodecOptions {
s := MapCodec()
@@ -42,6 +58,9 @@ func MergeMapCodecOptions(opts ...*MapCodecOptions) *MapCodecOptions {
if opt.EncodeNilAsEmpty != nil {
s.EncodeNilAsEmpty = opt.EncodeNilAsEmpty
}
if opt.EncodeKeysWithStringer != nil {
s.EncodeKeysWithStringer = opt.EncodeKeysWithStringer
}
}
return s

View File

@@ -6,12 +6,15 @@
package bsonoptions
var defaultOverwriteDuplicatedInlinedFields = true
// StructCodecOptions represents all possible options for struct encoding and decoding.
type StructCodecOptions struct {
DecodeZeroStruct *bool // Specifies if structs should be zeroed before decoding into them. Defaults to false.
DecodeDeepZeroInline *bool // Specifies if structs should be recursively zeroed when a inline value is decoded. Defaults to false.
EncodeOmitDefaultStruct *bool // Specifies if default structs should be considered empty by omitempty. Defaults to false.
AllowUnexportedFields *bool // Specifies if unexported fields should be marshaled/unmarshaled. Defaults to false.
DecodeZeroStruct *bool // Specifies if structs should be zeroed before decoding into them. Defaults to false.
DecodeDeepZeroInline *bool // Specifies if structs should be recursively zeroed when a inline value is decoded. Defaults to false.
EncodeOmitDefaultStruct *bool // Specifies if default structs should be considered empty by omitempty. Defaults to false.
AllowUnexportedFields *bool // Specifies if unexported fields should be marshaled/unmarshaled. Defaults to false.
OverwriteDuplicatedInlinedFields *bool // Specifies if fields in inlined structs can be overwritten by higher level struct fields with the same key. Defaults to true.
}
// StructCodec creates a new *StructCodecOptions
@@ -38,6 +41,15 @@ func (t *StructCodecOptions) SetEncodeOmitDefaultStruct(b bool) *StructCodecOpti
return t
}
// SetOverwriteDuplicatedInlinedFields specifies if inlined struct fields can be overwritten by higher level struct fields with the
// same bson key. When true and decoding, values will be written to the outermost struct with a matching key, and when
// encoding, keys will have the value of the top-most matching field. When false, decoding and encoding will error if
// there are duplicate keys after the struct is inlined. Defaults to true.
func (t *StructCodecOptions) SetOverwriteDuplicatedInlinedFields(b bool) *StructCodecOptions {
t.OverwriteDuplicatedInlinedFields = &b
return t
}
// SetAllowUnexportedFields specifies if unexported fields should be marshaled/unmarshaled. Defaults to false.
func (t *StructCodecOptions) SetAllowUnexportedFields(b bool) *StructCodecOptions {
t.AllowUnexportedFields = &b
@@ -46,7 +58,9 @@ func (t *StructCodecOptions) SetAllowUnexportedFields(b bool) *StructCodecOption
// MergeStructCodecOptions combines the given *StructCodecOptions into a single *StructCodecOptions in a last one wins fashion.
func MergeStructCodecOptions(opts ...*StructCodecOptions) *StructCodecOptions {
s := StructCodec()
s := &StructCodecOptions{
OverwriteDuplicatedInlinedFields: &defaultOverwriteDuplicatedInlinedFields,
}
for _, opt := range opts {
if opt == nil {
continue
@@ -61,6 +75,9 @@ func MergeStructCodecOptions(opts ...*StructCodecOptions) *StructCodecOptions {
if opt.EncodeOmitDefaultStruct != nil {
s.EncodeOmitDefaultStruct = opt.EncodeOmitDefaultStruct
}
if opt.OverwriteDuplicatedInlinedFields != nil {
s.OverwriteDuplicatedInlinedFields = opt.OverwriteDuplicatedInlinedFields
}
if opt.AllowUnexportedFields != nil {
s.AllowUnexportedFields = opt.AllowUnexportedFields
}

View File

@@ -45,6 +45,22 @@ func (c Copier) CopyDocument(dst ValueWriter, src ValueReader) error {
return c.copyDocumentCore(dw, dr)
}
// CopyArrayFromBytes copies the values from a BSON array represented as a
// []byte to a ValueWriter.
func (c Copier) CopyArrayFromBytes(dst ValueWriter, src []byte) error {
aw, err := dst.WriteArray()
if err != nil {
return err
}
err = c.CopyBytesToArrayWriter(aw, src)
if err != nil {
return err
}
return aw.WriteArrayEnd()
}
// CopyDocumentFromBytes copies the values from a BSON document represented as a
// []byte to a ValueWriter.
func (c Copier) CopyDocumentFromBytes(dst ValueWriter, src []byte) error {
@@ -61,9 +77,29 @@ func (c Copier) CopyDocumentFromBytes(dst ValueWriter, src []byte) error {
return dw.WriteDocumentEnd()
}
type writeElementFn func(key string) (ValueWriter, error)
// CopyBytesToArrayWriter copies the values from a BSON Array represented as a []byte to an
// ArrayWriter.
func (c Copier) CopyBytesToArrayWriter(dst ArrayWriter, src []byte) error {
wef := func(_ string) (ValueWriter, error) {
return dst.WriteArrayElement()
}
return c.copyBytesToValueWriter(src, wef)
}
// CopyBytesToDocumentWriter copies the values from a BSON document represented as a []byte to a
// DocumentWriter.
func (c Copier) CopyBytesToDocumentWriter(dst DocumentWriter, src []byte) error {
wef := func(key string) (ValueWriter, error) {
return dst.WriteDocumentElement(key)
}
return c.copyBytesToValueWriter(src, wef)
}
func (c Copier) copyBytesToValueWriter(src []byte, wef writeElementFn) error {
// TODO(skriptble): Create errors types here. Anything thats a tag should be a property.
length, rem, ok := bsoncore.ReadLength(src)
if !ok {
@@ -93,15 +129,18 @@ func (c Copier) CopyBytesToDocumentWriter(dst DocumentWriter, src []byte) error
if !ok {
return fmt.Errorf("invalid key found. remaining bytes=%v", rem)
}
dvw, err := dst.WriteDocumentElement(key)
// write as either array element or document element using writeElementFn
vw, err := wef(key)
if err != nil {
return err
}
val, rem, ok = bsoncore.ReadValue(rem, t)
if !ok {
return fmt.Errorf("not enough bytes available to read type. bytes=%d type=%s", len(rem), t)
}
err = c.CopyValueFromBytes(dvw, t, val.Data)
err = c.CopyValueFromBytes(vw, t, val.Data)
if err != nil {
return err
}
@@ -133,6 +172,23 @@ func (c Copier) AppendDocumentBytes(dst []byte, src ValueReader) ([]byte, error)
return dst, err
}
// AppendArrayBytes copies an array from the ValueReader to dst.
func (c Copier) AppendArrayBytes(dst []byte, src ValueReader) ([]byte, error) {
if br, ok := src.(BytesReader); ok {
_, dst, err := br.ReadValueBytes(dst)
return dst, err
}
vw := vwPool.Get().(*valueWriter)
defer vwPool.Put(vw)
vw.reset(dst)
err := c.copyArray(vw, src)
dst = vw.buf
return dst, err
}
// CopyValueFromBytes will write the value represtend by t and src to dst.
func (c Copier) CopyValueFromBytes(dst ValueWriter, t bsontype.Type, src []byte) error {
if wvb, ok := dst.(BytesWriter); ok {

View File

@@ -7,9 +7,12 @@
package bsonrw
import (
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
"go.mongodb.org/mongo-driver/bson/bsontype"
)
@@ -66,6 +69,7 @@ type extJSONParser struct {
maxDepth int
emptyObject bool
relaxedUUID bool
}
// newExtJSONParser returns a new extended JSON parser, ready to to begin
@@ -119,6 +123,12 @@ func (ejp *extJSONParser) peekType() (bsontype.Type, error) {
}
t = wrapperKeyBSONType(ejp.k)
// if $uuid is encountered, parse as binary subtype 4
if ejp.k == "$uuid" {
ejp.relaxedUUID = true
t = bsontype.Binary
}
switch t {
case bsontype.JavaScript:
// just saw $code, need to check for $scope at same level
@@ -273,6 +283,64 @@ func (ejp *extJSONParser) readValue(t bsontype.Type) (*extJSONValue, error) {
ejp.advanceState()
if t == bsontype.Binary && ejp.s == jpsSawValue {
// convert relaxed $uuid format
if ejp.relaxedUUID {
defer func() { ejp.relaxedUUID = false }()
uuid, err := ejp.v.parseSymbol()
if err != nil {
return nil, err
}
// RFC 4122 defines the length of a UUID as 36 and the hyphens in a UUID as appearing
// in the 8th, 13th, 18th, and 23rd characters.
//
// See https://tools.ietf.org/html/rfc4122#section-3
valid := len(uuid) == 36 &&
string(uuid[8]) == "-" &&
string(uuid[13]) == "-" &&
string(uuid[18]) == "-" &&
string(uuid[23]) == "-"
if !valid {
return nil, fmt.Errorf("$uuid value does not follow RFC 4122 format regarding length and hyphens")
}
// remove hyphens
uuidNoHyphens := strings.Replace(uuid, "-", "", -1)
if len(uuidNoHyphens) != 32 {
return nil, fmt.Errorf("$uuid value does not follow RFC 4122 format regarding length and hyphens")
}
// convert hex to bytes
bytes, err := hex.DecodeString(uuidNoHyphens)
if err != nil {
return nil, fmt.Errorf("$uuid value does not follow RFC 4122 format regarding hex bytes: %v", err)
}
ejp.advanceState()
if ejp.s != jpsSawEndObject {
return nil, invalidJSONErrorForType("$uuid and value and then }", bsontype.Binary)
}
base64 := &extJSONValue{
t: bsontype.String,
v: base64.StdEncoding.EncodeToString(bytes),
}
subType := &extJSONValue{
t: bsontype.String,
v: "04",
}
v = &extJSONValue{
t: bsontype.EmbeddedDocument,
v: &extJSONObject{
keys: []string{"base64", "subType"},
values: []*extJSONValue{base64, subType},
},
}
break
}
// convert legacy $binary format
base64 := ejp.v
@@ -355,7 +423,7 @@ func (ejp *extJSONParser) readValue(t bsontype.Type) (*extJSONValue, error) {
if ejp.canonical {
return nil, invalidJSONErrorForType("object", t)
}
return nil, invalidJSONErrorForType("ISO-8601 Internet Date/Time Format as decribed in RFC-3339", t)
return nil, invalidJSONErrorForType("ISO-8601 Internet Date/Time Format as described in RFC-3339", t)
}
ejp.advanceState()

View File

@@ -159,29 +159,35 @@ func (ejvr *extJSONValueReader) pop() {
}
}
func (ejvr *extJSONValueReader) skipDocument() error {
// read entire document until ErrEOD (using readKey and readValue)
_, typ, err := ejvr.p.readKey()
for err == nil {
_, err = ejvr.p.readValue(typ)
if err != nil {
break
func (ejvr *extJSONValueReader) skipObject() {
// read entire object until depth returns to 0 (last ending } or ] seen)
depth := 1
for depth > 0 {
ejvr.p.advanceState()
// If object is empty, raise depth and continue. When emptyObject is true, the
// parser has already read both the opening and closing brackets of an empty
// object ("{}"), so the next valid token will be part of the parent document,
// not part of the nested document.
//
// If there is a comma, there are remaining fields, emptyObject must be set back
// to false, and comma must be skipped with advanceState().
if ejvr.p.emptyObject {
if ejvr.p.s == jpsSawComma {
ejvr.p.emptyObject = false
ejvr.p.advanceState()
}
depth--
continue
}
_, typ, err = ejvr.p.readKey()
switch ejvr.p.s {
case jpsSawBeginObject, jpsSawBeginArray:
depth++
case jpsSawEndObject, jpsSawEndArray:
depth--
}
}
return err
}
func (ejvr *extJSONValueReader) skipArray() error {
// read entire array until ErrEOA (using peekType)
_, err := ejvr.p.peekType()
for err == nil {
_, err = ejvr.p.peekType()
}
return err
}
func (ejvr *extJSONValueReader) invalidTransitionErr(destination mode, name string, modes []mode) error {
@@ -234,30 +240,9 @@ func (ejvr *extJSONValueReader) Skip() error {
t := ejvr.stack[ejvr.frame].vType
switch t {
case bsontype.Array:
// read entire array until ErrEOA
err := ejvr.skipArray()
if err != ErrEOA {
return err
}
case bsontype.EmbeddedDocument:
// read entire doc until ErrEOD
err := ejvr.skipDocument()
if err != ErrEOD {
return err
}
case bsontype.CodeWithScope:
// read the code portion and set up parser in document mode
_, err := ejvr.p.readValue(t)
if err != nil {
return err
}
// read until ErrEOD
err = ejvr.skipDocument()
if err != ErrEOD {
return err
}
case bsontype.Array, bsontype.EmbeddedDocument, bsontype.CodeWithScope:
// read entire array, doc or CodeWithScope
ejvr.skipObject()
default:
_, err := ejvr.p.readValue(t)
if err != nil {

View File

@@ -19,7 +19,7 @@ import (
)
func wrapperKeyBSONType(key string) bsontype.Type {
switch string(key) {
switch key {
case "$numberInt":
return bsontype.Int32
case "$numberLong":
@@ -46,12 +46,6 @@ func wrapperKeyBSONType(key string) bsontype.Type {
return bsontype.DBPointer
case "$date":
return bsontype.DateTime
case "$ref":
fallthrough
case "$id":
fallthrough
case "$db":
return bsontype.EmbeddedDocument // dbrefs aren't bson types
case "$minKey":
return bsontype.MinKey
case "$maxKey":
@@ -217,7 +211,7 @@ func parseDatetimeString(data string) (int64, error) {
return 0, fmt.Errorf("invalid $date value string: %s", data)
}
return t.Unix()*1e3 + int64(t.Nanosecond())/1e6, nil
return int64(primitive.NewDateTimeFromTime(t)), nil
}
func parseDatetimeObject(data *extJSONObject) (d int64, err error) {
@@ -275,7 +269,7 @@ func (ejv *extJSONValue) parseDouble() (float64, error) {
return 0, fmt.Errorf("$numberDouble value should be string, but instead is %s", ejv.t)
}
switch string(ejv.v.(string)) {
switch ejv.v.(string) {
case "Infinity":
return math.Inf(1), nil
case "-Infinity":
@@ -370,7 +364,7 @@ func (ejv *extJSONValue) parseRegex() (pattern, options string, err error) {
for i, key := range regexObj.keys {
val := regexObj.values[i]
switch string(key) {
switch key {
case "pattern":
if patFound {
return "", "", errors.New("duplicate pattern key in $regularExpression")

View File

@@ -10,7 +10,6 @@ import (
"bytes"
"encoding/base64"
"fmt"
"go.mongodb.org/mongo-driver/bson/primitive"
"io"
"math"
"sort"
@@ -19,13 +18,9 @@ import (
"sync"
"time"
"unicode/utf8"
)
var ejvwPool = sync.Pool{
New: func() interface{} {
return new(extJSONValueWriter)
},
}
"go.mongodb.org/mongo-driver/bson/primitive"
)
// ExtJSONValueWriterPool is a pool for ExtJSON ValueWriters.
type ExtJSONValueWriterPool struct {

View File

@@ -13,8 +13,8 @@ import (
"io"
"math"
"strconv"
"strings"
"unicode"
"unicode/utf16"
)
type jsonTokenType byte
@@ -162,6 +162,31 @@ func isValueTerminator(c byte) bool {
return c == ',' || c == '}' || c == ']' || isWhiteSpace(c)
}
// getu4 decodes the 4-byte hex sequence from the beginning of s, returning the hex value as a rune,
// or it returns -1. Note that the "\u" from the unicode escape sequence should not be present.
// It is copied and lightly modified from the Go JSON decode function at
// https://github.com/golang/go/blob/1b0a0316802b8048d69da49dc23c5a5ab08e8ae8/src/encoding/json/decode.go#L1169-L1188
func getu4(s []byte) rune {
if len(s) < 4 {
return -1
}
var r rune
for _, c := range s[:4] {
switch {
case '0' <= c && c <= '9':
c = c - '0'
case 'a' <= c && c <= 'f':
c = c - 'a' + 10
case 'A' <= c && c <= 'F':
c = c - 'A' + 10
default:
return -1
}
r = r*16 + rune(c)
}
return r
}
// scanString reads from an opening '"' to a closing '"' and handles escaped characters
func (js *jsonScanner) scanString() (*jsonToken, error) {
var b bytes.Buffer
@@ -179,9 +204,18 @@ func (js *jsonScanner) scanString() (*jsonToken, error) {
return nil, err
}
evalNextChar:
switch c {
case '\\':
c, err = js.readNextByte()
if err != nil {
if err == io.EOF {
return nil, errors.New("end of input in JSON string")
}
return nil, err
}
evalNextEscapeChar:
switch c {
case '"', '\\', '/':
b.WriteByte(c)
@@ -202,13 +236,68 @@ func (js *jsonScanner) scanString() (*jsonToken, error) {
return nil, fmt.Errorf("invalid unicode sequence in JSON string: %s", us)
}
s := fmt.Sprintf(`\u%s`, us)
s, err = strconv.Unquote(strings.Replace(strconv.Quote(s), `\\u`, `\u`, 1))
if err != nil {
return nil, err
rn := getu4(us)
// If the rune we just decoded is the high or low value of a possible surrogate pair,
// try to decode the next sequence as the low value of a surrogate pair. We're
// expecting the next sequence to be another Unicode escape sequence (e.g. "\uDD1E"),
// but need to handle cases where the input is not a valid surrogate pair.
// For more context on unicode surrogate pairs, see:
// https://www.christianfscott.com/rust-chars-vs-go-runes/
// https://www.unicode.org/glossary/#high_surrogate_code_point
if utf16.IsSurrogate(rn) {
c, err = js.readNextByte()
if err != nil {
if err == io.EOF {
return nil, errors.New("end of input in JSON string")
}
return nil, err
}
// If the next value isn't the beginning of a backslash escape sequence, write
// the Unicode replacement character for the surrogate value and goto the
// beginning of the next char eval block.
if c != '\\' {
b.WriteRune(unicode.ReplacementChar)
goto evalNextChar
}
c, err = js.readNextByte()
if err != nil {
if err == io.EOF {
return nil, errors.New("end of input in JSON string")
}
return nil, err
}
// If the next value isn't the beginning of a unicode escape sequence, write the
// Unicode replacement character for the surrogate value and goto the beginning
// of the next escape char eval block.
if c != 'u' {
b.WriteRune(unicode.ReplacementChar)
goto evalNextEscapeChar
}
err = js.readNNextBytes(us, 4, 0)
if err != nil {
return nil, fmt.Errorf("invalid unicode sequence in JSON string: %s", us)
}
rn2 := getu4(us)
// Try to decode the pair of runes as a utf16 surrogate pair. If that fails, write
// the Unicode replacement character for the surrogate value and the 2nd decoded rune.
if rnPair := utf16.DecodeRune(rn, rn2); rnPair != unicode.ReplacementChar {
b.WriteRune(rnPair)
} else {
b.WriteRune(unicode.ReplacementChar)
b.WriteRune(rn2)
}
break
}
b.WriteString(s)
b.WriteRune(rn)
default:
return nil, fmt.Errorf("invalid escape sequence in JSON string '\\%c'", c)
}

View File

@@ -86,12 +86,11 @@ type valueReader struct {
// NewBSONDocumentReader returns a ValueReader using b for the underlying BSON
// representation. Parameter b must be a BSON Document.
//
// TODO(skriptble): There's a lack of symmetry between the reader and writer, since the reader takes
// a []byte while the writer takes an io.Writer. We should have two versions of each, one that takes
// a []byte and one that takes an io.Reader or io.Writer. The []byte version will need to return a
// thing that can return the finished []byte since it might be reallocated when appended to.
func NewBSONDocumentReader(b []byte) ValueReader {
// TODO(skriptble): There's a lack of symmetry between the reader and writer, since the reader takes a []byte while the
// TODO writer takes an io.Writer. We should have two versions of each, one that takes a []byte and one that takes an
// TODO io.Reader or io.Writer. The []byte version will need to return a thing that can return the finished []byte since
// TODO it might be reallocated when appended to.
return newValueReader(b)
}
@@ -384,9 +383,13 @@ func (vr *valueReader) ReadBinary() (b []byte, btype byte, err error) {
if err != nil {
return nil, 0, err
}
// Make a copy of the returned byte slice because it's just a subslice from the valueReader's
// buffer and is not safe to return in the unmarshaled value.
cp := make([]byte, len(b))
copy(cp, b)
vr.pop()
return b, btype, nil
return cp, btype, nil
}
func (vr *valueReader) ReadBoolean() (bool, error) {
@@ -737,6 +740,9 @@ func (vr *valueReader) ReadValue() (ValueReader, error) {
return vr, nil
}
// readBytes reads length bytes from the valueReader starting at the current offset. Note that the
// returned byte slice is a subslice from the valueReader buffer and must be converted or copied
// before returning in an unmarshaled value.
func (vr *valueReader) readBytes(length int32) ([]byte, error) {
if length < 0 {
return nil, fmt.Errorf("invalid length: %d", length)
@@ -748,6 +754,7 @@ func (vr *valueReader) readBytes(length int32) ([]byte, error) {
start := vr.offset
vr.offset += int64(length)
return vr.d[start : start+int64(length)], nil
}
@@ -790,16 +797,6 @@ func (vr *valueReader) readCString() (string, error) {
return string(vr.d[start : start+int64(idx)]), nil
}
func (vr *valueReader) skipCString() error {
idx := bytes.IndexByte(vr.d[vr.offset:], 0x00)
if idx < 0 {
return io.EOF
}
// idx does not include the null byte
vr.offset += int64(idx) + 1
return nil
}
func (vr *valueReader) readString() (string, error) {
length, err := vr.readLength()
if err != nil {

View File

@@ -12,6 +12,7 @@ import (
"io"
"math"
"strconv"
"strings"
"sync"
"go.mongodb.org/mongo-driver/bson/bsontype"
@@ -46,11 +47,9 @@ func NewBSONValueWriterPool() *BSONValueWriterPool {
// Get retrieves a BSON ValueWriter from the pool and resets it to use w as the destination.
func (bvwp *BSONValueWriterPool) Get(w io.Writer) ValueWriter {
vw := bvwp.pool.Get().(*valueWriter)
if writer, ok := w.(*SliceWriter); ok {
vw.reset(*writer)
vw.w = writer
return vw
}
// TODO: Having to call reset here with the same buffer doesn't really make sense.
vw.reset(vw.buf)
vw.buf = vw.buf[:0]
vw.w = w
return vw
@@ -71,11 +70,6 @@ func (bvwp *BSONValueWriterPool) Put(vw ValueWriter) (ok bool) {
return false
}
if _, ok := bvw.w.(*SliceWriter); ok {
bvw.buf = nil
}
bvw.w = nil
bvwp.pool.Put(bvw)
return true
}
@@ -247,7 +241,12 @@ func (vw *valueWriter) invalidTransitionError(destination mode, name string, mod
func (vw *valueWriter) writeElementHeader(t bsontype.Type, destination mode, callerName string, addmodes ...mode) error {
switch vw.stack[vw.frame].mode {
case mElement:
vw.buf = bsoncore.AppendHeader(vw.buf, t, vw.stack[vw.frame].key)
key := vw.stack[vw.frame].key
if !isValidCString(key) {
return errors.New("BSON element key cannot contain null bytes")
}
vw.buf = bsoncore.AppendHeader(vw.buf, t, key)
case mValue:
// TODO: Do this with a cache of the first 1000 or so array keys.
vw.buf = bsoncore.AppendHeader(vw.buf, t, strconv.Itoa(vw.stack[vw.frame].arrkey))
@@ -430,6 +429,9 @@ func (vw *valueWriter) WriteObjectID(oid primitive.ObjectID) error {
}
func (vw *valueWriter) WriteRegex(pattern string, options string) error {
if !isValidCString(pattern) || !isValidCString(options) {
return errors.New("BSON regex values cannot contain null bytes")
}
if err := vw.writeElementHeader(bsontype.Regex, mode(0), "WriteRegex"); err != nil {
return err
}
@@ -527,7 +529,7 @@ func (vw *valueWriter) WriteDocumentEnd() error {
vw.pop()
if vw.stack[vw.frame].mode == mCodeWithScope {
// We ignore the error here because of the gaurantee of writeLength.
// We ignore the error here because of the guarantee of writeLength.
// See the docs for writeLength for more info.
_ = vw.writeLength()
vw.pop()
@@ -540,10 +542,6 @@ func (vw *valueWriter) Flush() error {
return nil
}
if sw, ok := vw.w.(*SliceWriter); ok {
*sw = vw.buf
return nil
}
if _, err := vw.w.Write(vw.buf); err != nil {
return err
}
@@ -602,3 +600,7 @@ func (vw *valueWriter) writeLength() error {
vw.buf[start+3] = byte(length >> 24)
return nil
}
func isValidCString(cs string) bool {
return !strings.ContainsRune(cs, '\x00')
}

View File

@@ -76,27 +76,3 @@ func (sw *SliceWriter) Write(p []byte) (int, error) {
*sw = append(*sw, p...)
return written, nil
}
type writer []byte
func (w *writer) Write(p []byte) (int, error) {
index := len(*w)
return w.WriteAt(p, int64(index))
}
func (w *writer) WriteAt(p []byte, off int64) (int, error) {
newend := off + int64(len(p))
if newend < int64(len(*w)) {
newend = int64(len(*w))
}
if newend > int64(cap(*w)) {
buf := make([]byte, int64(2*cap(*w))+newend)
copy(buf, *w)
*w = buf
}
*w = []byte(*w)[:newend]
copy([]byte(*w)[off:], p)
return len(p), nil
}

View File

@@ -38,6 +38,8 @@ const (
BinaryUUIDOld byte = 0x03
BinaryUUID byte = 0x04
BinaryMD5 byte = 0x05
BinaryEncrypted byte = 0x06
BinaryColumn byte = 0x07
BinaryUserDefined byte = 0x80
)

View File

@@ -33,6 +33,11 @@ var decPool = sync.Pool{
type Decoder struct {
dc bsoncodec.DecodeContext
vr bsonrw.ValueReader
// We persist defaultDocumentM and defaultDocumentD on the Decoder to prevent overwriting from
// (*Decoder).SetContext.
defaultDocumentM bool
defaultDocumentD bool
}
// NewDecoder returns a new decoder that uses the DefaultRegistry to read from vr.
@@ -95,6 +100,12 @@ func (d *Decoder) Decode(val interface{}) error {
if err != nil {
return err
}
if d.defaultDocumentM {
d.dc.DefaultDocumentM()
}
if d.defaultDocumentD {
d.dc.DefaultDocumentD()
}
return decoder.DecodeValue(d.dc, d.vr, rval)
}
@@ -116,3 +127,15 @@ func (d *Decoder) SetContext(dc bsoncodec.DecodeContext) error {
d.dc = dc
return nil
}
// DefaultDocumentM will decode empty documents using the primitive.M type. This behavior is restricted to data typed as
// "interface{}" or "map[string]interface{}".
func (d *Decoder) DefaultDocumentM() {
d.defaultDocumentM = true
}
// DefaultDocumentD will decode empty documents using the primitive.D type. This behavior is restricted to data typed as
// "interface{}" or "map[string]interface{}".
func (d *Decoder) DefaultDocumentD() {
d.defaultDocumentD = true
}

View File

@@ -9,112 +9,133 @@
// The BSON library handles marshalling and unmarshalling of values through a configurable codec system. For a description
// of the codec system and examples of registering custom codecs, see the bsoncodec package.
//
// Raw BSON
// # Raw BSON
//
// The Raw family of types is used to validate and retrieve elements from a slice of bytes. This
// type is most useful when you want do lookups on BSON bytes without unmarshaling it into another
// type.
//
// Example:
// var raw bson.Raw = ... // bytes from somewhere
// err := raw.Validate()
// if err != nil { return err }
// val := raw.Lookup("foo")
// i32, ok := val.Int32OK()
// // do something with i32...
//
// Native Go Types
// var raw bson.Raw = ... // bytes from somewhere
// err := raw.Validate()
// if err != nil { return err }
// val := raw.Lookup("foo")
// i32, ok := val.Int32OK()
// // do something with i32...
//
// # Native Go Types
//
// The D and M types defined in this package can be used to build representations of BSON using native Go types. D is a
// slice and M is a map. For more information about the use cases for these types, see the documentation on the type
// definitions.
//
// Note that a D should not be constructed with duplicate key names, as that can cause undefined server behavior.
//
// Example:
// bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
// bson.M{"foo": "bar", "hello": "world", "pi": 3.14159}
//
// bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
// bson.M{"foo": "bar", "hello": "world", "pi": 3.14159}
//
// When decoding BSON to a D or M, the following type mappings apply when unmarshalling:
//
// 1. BSON int32 unmarshals to an int32.
// 2. BSON int64 unmarshals to an int64.
// 3. BSON double unmarshals to a float64.
// 4. BSON string unmarshals to a string.
// 5. BSON boolean unmarshals to a bool.
// 6. BSON embedded document unmarshals to the parent type (i.e. D for a D, M for an M).
// 7. BSON array unmarshals to a bson.A.
// 8. BSON ObjectId unmarshals to a primitive.ObjectID.
// 9. BSON datetime unmarshals to a primitive.Datetime.
// 10. BSON binary unmarshals to a primitive.Binary.
// 11. BSON regular expression unmarshals to a primitive.Regex.
// 12. BSON JavaScript unmarshals to a primitive.JavaScript.
// 13. BSON code with scope unmarshals to a primitive.CodeWithScope.
// 14. BSON timestamp unmarshals to an primitive.Timestamp.
// 15. BSON 128-bit decimal unmarshals to an primitive.Decimal128.
// 16. BSON min key unmarshals to an primitive.MinKey.
// 17. BSON max key unmarshals to an primitive.MaxKey.
// 18. BSON undefined unmarshals to a primitive.Undefined.
// 19. BSON null unmarshals to a primitive.Null.
// 20. BSON DBPointer unmarshals to a primitive.DBPointer.
// 21. BSON symbol unmarshals to a primitive.Symbol.
// 1. BSON int32 unmarshals to an int32.
// 2. BSON int64 unmarshals to an int64.
// 3. BSON double unmarshals to a float64.
// 4. BSON string unmarshals to a string.
// 5. BSON boolean unmarshals to a bool.
// 6. BSON embedded document unmarshals to the parent type (i.e. D for a D, M for an M).
// 7. BSON array unmarshals to a bson.A.
// 8. BSON ObjectId unmarshals to a primitive.ObjectID.
// 9. BSON datetime unmarshals to a primitive.DateTime.
// 10. BSON binary unmarshals to a primitive.Binary.
// 11. BSON regular expression unmarshals to a primitive.Regex.
// 12. BSON JavaScript unmarshals to a primitive.JavaScript.
// 13. BSON code with scope unmarshals to a primitive.CodeWithScope.
// 14. BSON timestamp unmarshals to an primitive.Timestamp.
// 15. BSON 128-bit decimal unmarshals to an primitive.Decimal128.
// 16. BSON min key unmarshals to an primitive.MinKey.
// 17. BSON max key unmarshals to an primitive.MaxKey.
// 18. BSON undefined unmarshals to a primitive.Undefined.
// 19. BSON null unmarshals to nil.
// 20. BSON DBPointer unmarshals to a primitive.DBPointer.
// 21. BSON symbol unmarshals to a primitive.Symbol.
//
// The above mappings also apply when marshalling a D or M to BSON. Some other useful marshalling mappings are:
//
// 1. time.Time marshals to a BSON datetime.
// 2. int8, int16, and int32 marshal to a BSON int32.
// 3. int marshals to a BSON int32 if the value is between math.MinInt32 and math.MaxInt32, inclusive, and a BSON int64
// otherwise.
// 4. int64 marshals to BSON int64.
// 5. uint8 and uint16 marshal to a BSON int32.
// 6. uint, uint32, and uint64 marshal to a BSON int32 if the value is between math.MinInt32 and math.MaxInt32,
// inclusive, and BSON int64 otherwise.
// 7. BSON null values will unmarshal into the zero value of a field (e.g. unmarshalling a BSON null value into a string
// will yield the empty string.).
// 1. time.Time marshals to a BSON datetime.
// 2. int8, int16, and int32 marshal to a BSON int32.
// 3. int marshals to a BSON int32 if the value is between math.MinInt32 and math.MaxInt32, inclusive, and a BSON int64
// otherwise.
// 4. int64 marshals to BSON int64.
// 5. uint8 and uint16 marshal to a BSON int32.
// 6. uint, uint32, and uint64 marshal to a BSON int32 if the value is between math.MinInt32 and math.MaxInt32,
// inclusive, and BSON int64 otherwise.
// 7. BSON null and undefined values will unmarshal into the zero value of a field (e.g. unmarshalling a BSON null or
// undefined value into a string will yield the empty string.).
//
// Structs
// # Structs
//
// Structs can be marshalled/unmarshalled to/from BSON. When transforming structs to/from BSON, the following rules
// apply:
// Structs can be marshalled/unmarshalled to/from BSON or Extended JSON. When transforming structs to/from BSON or Extended
// JSON, the following rules apply:
//
// 1. Only exported fields in structs will be marshalled or unmarshalled.
// 1. Only exported fields in structs will be marshalled or unmarshalled.
//
// 2. When marshalling a struct, each field will be lowercased to generate the key for the corresponding BSON element.
// For example, a struct field named "Foo" will generate key "foo". This can be overriden via a struct tag (e.g.
// 2. When marshalling a struct, each field will be lowercased to generate the key for the corresponding BSON element.
// For example, a struct field named "Foo" will generate key "foo". This can be overridden via a struct tag (e.g.
// `bson:"fooField"` to generate key "fooField" instead).
//
// 3. An embedded struct field is marshalled as a subdocument. The key will be the lowercased name of the field's type.
// 3. An embedded struct field is marshalled as a subdocument. The key will be the lowercased name of the field's type.
//
// 4. A pointer field is marshalled as the underlying type if the pointer is non-nil. If the pointer is nil, it is
// 4. A pointer field is marshalled as the underlying type if the pointer is non-nil. If the pointer is nil, it is
// marshalled as a BSON null value.
//
// 5. When unmarshalling, a field of type interface{} will follow the D/M type mappings listed above. BSON documents
// 5. When unmarshalling, a field of type interface{} will follow the D/M type mappings listed above. BSON documents
// unmarshalled into an interface{} field will be unmarshalled as a D.
//
// The following struct tags can be used to configure behavior:
// The encoding of each struct field can be customized by the "bson" struct tag.
//
// 1. omitempty: If the omitempty struct tag is specified on a field, the field will not be marshalled if it is set to
// the zero value. By default, a struct field is only considered empty if the field's type implements the Zeroer
// interface and the IsZero method returns true. Struct fields of types that do not implement Zeroer are always
// marshalled as embedded documents. This tag should be used for all slice and map values.
// This tag behavior is configurable, and different struct tag behavior can be configured by initializing a new
// bsoncodec.StructCodec with the desired tag parser and registering that StructCodec onto the Registry. By default, JSON tags
// are not honored, but that can be enabled by creating a StructCodec with JSONFallbackStructTagParser, like below:
//
// 2. minsize: If the minsize struct tag is specified on a field of type int64, uint, uint32, or uint64 and the value of
// Example:
//
// structcodec, _ := bsoncodec.NewStructCodec(bsoncodec.JSONFallbackStructTagParser)
//
// The bson tag gives the name of the field, possibly followed by a comma-separated list of options.
// The name may be empty in order to specify options without overriding the default field name. The following options can be used
// to configure behavior:
//
// 1. omitempty: If the omitempty struct tag is specified on a field, the field will not be marshalled if it is set to
// the zero value. Fields with language primitive types such as integers, booleans, and strings are considered empty if
// their value is equal to the zero value for the type (i.e. 0 for integers, false for booleans, and "" for strings).
// Slices, maps, and arrays are considered empty if they are of length zero. Interfaces and pointers are considered
// empty if their value is nil. By default, structs are only considered empty if the struct type implements the
// bsoncodec.Zeroer interface and the IsZero method returns true. Struct fields whose types do not implement Zeroer are
// never considered empty and will be marshalled as embedded documents.
// NOTE: It is recommended that this tag be used for all slice and map fields.
//
// 2. minsize: If the minsize struct tag is specified on a field of type int64, uint, uint32, or uint64 and the value of
// the field can fit in a signed int32, the field will be serialized as a BSON int32 rather than a BSON int64. For other
// types, this tag is ignored.
//
// 3. truncate: If the truncate struct tag is specified on a field with a non-float numeric type, BSON doubles unmarshalled
// into that field will be trucated at the decimal point. For example, if 3.14 is unmarshalled into a field of type int,
// 3. truncate: If the truncate struct tag is specified on a field with a non-float numeric type, BSON doubles unmarshalled
// into that field will be truncated at the decimal point. For example, if 3.14 is unmarshalled into a field of type int,
// it will be unmarshalled as 3. If this tag is not specified, the decoder will throw an error if the value cannot be
// decoded without losing precision. For float64 or non-numeric types, this tag is ignored.
//
// 4. inline: If the inline struct tag is specified for a struct or map field, the field will be "flattened" when
// 4. inline: If the inline struct tag is specified for a struct or map field, the field will be "flattened" when
// marshalling and "un-flattened" when unmarshalling. This means that all of the fields in that struct/map will be
// pulled up one level and will become top-level fields rather than being fields in a nested document. For example, if a
// map field named "Map" with value map[string]interface{}{"foo": "bar"} is inlined, the resulting document will be
// {"foo": "bar"} instead of {"map": {"foo": "bar"}}. There can only be one inlined map field in a struct. If there are
// duplicated fields in the resulting document when an inlined field is marshalled, an error will be returned. This tag
// can be used with fields that are pointers to structs. If an inlined pointer field is nil, it will not be marshalled.
// For fields that are not maps or structs, this tag is ignored.
// duplicated fields in the resulting document when an inlined struct is marshalled, the inlined field will be overwritten.
// If there are duplicated fields in the resulting document when an inlined map is marshalled, an error will be returned.
// This tag can be used with fields that are pointers to structs. If an inlined pointer field is nil, it will not be
// marshalled. For fields that are not maps or structs, this tag is ignored.
//
// Marshalling and Unmarshalling
// # Marshalling and Unmarshalling
//
// Manually marshalling and unmarshalling can be done with the Marshal and Unmarshal family of functions.
package bson

View File

@@ -7,6 +7,9 @@
package bson
import (
"bytes"
"encoding/json"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/bson/bsonrw"
"go.mongodb.org/mongo-driver/bson/bsontype"
@@ -52,14 +55,14 @@ func MarshalAppend(dst []byte, val interface{}) ([]byte, error) {
// MarshalWithRegistry returns the BSON encoding of val as a BSON document. If val is not a type that can be transformed
// into a document, MarshalValueWithRegistry should be used instead.
func MarshalWithRegistry(r *bsoncodec.Registry, val interface{}) ([]byte, error) {
dst := make([]byte, 0, 256) // TODO: make the default cap a constant
dst := make([]byte, 0)
return MarshalAppendWithRegistry(r, dst, val)
}
// MarshalWithContext returns the BSON encoding of val as a BSON document using EncodeContext ec. If val is not a type
// that can be transformed into a document, MarshalValueWithContext should be used instead.
func MarshalWithContext(ec bsoncodec.EncodeContext, val interface{}) ([]byte, error) {
dst := make([]byte, 0, 256) // TODO: make the default cap a constant
dst := make([]byte, 0)
return MarshalAppendWithContext(ec, dst, val)
}
@@ -115,13 +118,13 @@ func MarshalValueAppend(dst []byte, val interface{}) (bsontype.Type, []byte, err
// MarshalValueWithRegistry returns the BSON encoding of val using Registry r.
func MarshalValueWithRegistry(r *bsoncodec.Registry, val interface{}) (bsontype.Type, []byte, error) {
dst := make([]byte, 0, defaultDstCap)
dst := make([]byte, 0)
return MarshalValueAppendWithRegistry(r, dst, val)
}
// MarshalValueWithContext returns the BSON encoding of val using EncodeContext ec.
func MarshalValueWithContext(ec bsoncodec.EncodeContext, val interface{}) (bsontype.Type, []byte, error) {
dst := make([]byte, 0, defaultDstCap)
dst := make([]byte, 0)
return MarshalValueAppendWithContext(ec, dst, val)
}
@@ -221,3 +224,25 @@ func MarshalExtJSONAppendWithContext(ec bsoncodec.EncodeContext, dst []byte, val
return *sw, nil
}
// IndentExtJSON will prefix and indent the provided extended JSON src and append it to dst.
func IndentExtJSON(dst *bytes.Buffer, src []byte, prefix, indent string) error {
return json.Indent(dst, src, prefix, indent)
}
// MarshalExtJSONIndent returns the extended JSON encoding of val with each line with prefixed
// and indented.
func MarshalExtJSONIndent(val interface{}, canonical, escapeHTML bool, prefix, indent string) ([]byte, error) {
marshaled, err := MarshalExtJSON(val, canonical, escapeHTML)
if err != nil {
return nil, err
}
var buf bytes.Buffer
err = IndentExtJSON(&buf, marshaled, prefix, indent)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}

View File

@@ -10,6 +10,7 @@
package primitive
import (
"encoding/json"
"errors"
"fmt"
"math/big"
@@ -132,11 +133,9 @@ Loop:
}
// BigInt returns significand as big.Int and exponent, bi * 10 ^ exp.
func (d Decimal128) BigInt() (bi *big.Int, exp int, err error) {
func (d Decimal128) BigInt() (*big.Int, int, error) {
high, low := d.GetBytes()
var posSign bool // positive sign
posSign = high>>63&1 == 0
posSign := high>>63&1 == 0 // positive sign
switch high >> 58 & (1<<5 - 1) {
case 0x1F:
@@ -148,6 +147,7 @@ func (d Decimal128) BigInt() (bi *big.Int, exp int, err error) {
return nil, 0, ErrParseNegInf
}
var exp int
if high>>61&3 == 3 {
// Bits: 1*sign 2*ignored 14*exponent 111*significand.
// Implicit 0b100 prefix in significand.
@@ -170,7 +170,7 @@ func (d Decimal128) BigInt() (bi *big.Int, exp int, err error) {
return new(big.Int), 0, nil
}
bi = big.NewInt(0)
bi := big.NewInt(0)
const host32bit = ^uint(0)>>32 == 0
if host32bit {
bi.SetBits([]big.Word{big.Word(low), big.Word(low >> 32), big.Word(high), big.Word(high >> 32)})
@@ -181,7 +181,7 @@ func (d Decimal128) BigInt() (bi *big.Int, exp int, err error) {
if !posSign {
return bi.Neg(bi), exp, nil
}
return
return bi, exp, nil
}
// IsNaN returns whether d is NaN.
@@ -191,10 +191,9 @@ func (d Decimal128) IsNaN() bool {
// IsInf returns:
//
// +1 d == Infinity
// 0 other case
// -1 d == -Infinity
//
// +1 d == Infinity
// 0 other case
// -1 d == -Infinity
func (d Decimal128) IsInf() int {
if d.h>>58&(1<<5-1) != 0x1E {
return 0
@@ -206,6 +205,54 @@ func (d Decimal128) IsInf() int {
return -1
}
// IsZero returns true if d is the empty Decimal128.
func (d Decimal128) IsZero() bool {
return d.h == 0 && d.l == 0
}
// MarshalJSON returns Decimal128 as a string.
func (d Decimal128) MarshalJSON() ([]byte, error) {
return json.Marshal(d.String())
}
// UnmarshalJSON creates a primitive.Decimal128 from a JSON string, an extended JSON $numberDecimal value, or the string
// "null". If b is a JSON string or extended JSON value, d will have the value of that string, and if b is "null", d will
// be unchanged.
func (d *Decimal128) UnmarshalJSON(b []byte) error {
// Ignore "null" to keep parity with the standard library. Decoding a JSON null into a non-pointer Decimal128 field
// will leave the field unchanged. For pointer values, encoding/json will set the pointer to nil and will not
// enter the UnmarshalJSON hook.
if string(b) == "null" {
return nil
}
var res interface{}
err := json.Unmarshal(b, &res)
if err != nil {
return err
}
str, ok := res.(string)
// Extended JSON
if !ok {
m, ok := res.(map[string]interface{})
if !ok {
return errors.New("not an extended JSON Decimal128: expected document")
}
d128, ok := m["$numberDecimal"]
if !ok {
return errors.New("not an extended JSON Decimal128: expected key $numberDecimal")
}
str, ok = d128.(string)
if !ok {
return errors.New("not an extended JSON Decimal128: expected decimal to be string")
}
}
*d, err = ParseDecimal128(str)
return err
}
func divmod(h, l uint64, div uint32) (qh, ql uint64, rem uint32) {
div64 := uint64(div)
a := h >> 32

View File

@@ -10,8 +10,8 @@
package primitive
import (
"bytes"
"crypto/rand"
"encoding"
"encoding/binary"
"encoding/hex"
"encoding/json"
@@ -34,6 +34,9 @@ var NilObjectID ObjectID
var objectIDCounter = readRandomUint32()
var processUnique = processUniqueBytes()
var _ encoding.TextMarshaler = ObjectID{}
var _ encoding.TextUnmarshaler = &ObjectID{}
// NewObjectID generates a new ObjectID.
func NewObjectID() ObjectID {
return NewObjectIDFromTimestamp(time.Now())
@@ -67,37 +70,67 @@ func (id ObjectID) String() string {
// IsZero returns true if id is the empty ObjectID.
func (id ObjectID) IsZero() bool {
return bytes.Equal(id[:], NilObjectID[:])
return id == NilObjectID
}
// ObjectIDFromHex creates a new ObjectID from a hex string. It returns an error if the hex string is not a
// valid ObjectID.
func ObjectIDFromHex(s string) (ObjectID, error) {
if len(s) != 24 {
return NilObjectID, ErrInvalidHex
}
b, err := hex.DecodeString(s)
if err != nil {
return NilObjectID, err
}
if len(b) != 12 {
return NilObjectID, ErrInvalidHex
}
var oid [12]byte
copy(oid[:], b[:])
copy(oid[:], b)
return oid, nil
}
// IsValidObjectID returns true if the provided hex string represents a valid ObjectID and false if not.
func IsValidObjectID(s string) bool {
_, err := ObjectIDFromHex(s)
return err == nil
}
// MarshalText returns the ObjectID as UTF-8-encoded text. Implementing this allows us to use ObjectID
// as a map key when marshalling JSON. See https://pkg.go.dev/encoding#TextMarshaler
func (id ObjectID) MarshalText() ([]byte, error) {
return []byte(id.Hex()), nil
}
// UnmarshalText populates the byte slice with the ObjectID. Implementing this allows us to use ObjectID
// as a map key when unmarshalling JSON. See https://pkg.go.dev/encoding#TextUnmarshaler
func (id *ObjectID) UnmarshalText(b []byte) error {
oid, err := ObjectIDFromHex(string(b))
if err != nil {
return err
}
*id = oid
return nil
}
// MarshalJSON returns the ObjectID as a string
func (id ObjectID) MarshalJSON() ([]byte, error) {
return json.Marshal(id.Hex())
}
// UnmarshalJSON populates the byte slice with the ObjectID. If the byte slice is 64 bytes long, it
// UnmarshalJSON populates the byte slice with the ObjectID. If the byte slice is 24 bytes long, it
// will be populated with the hex representation of the ObjectID. If the byte slice is twelve bytes
// long, it will be populated with the BSON representation of the ObjectID. Otherwise, it will
// return an error.
// long, it will be populated with the BSON representation of the ObjectID. This method also accepts empty strings and
// decodes them as NilObjectID. For any other inputs, an error will be returned.
func (id *ObjectID) UnmarshalJSON(b []byte) error {
// Ignore "null" to keep parity with the standard library. Decoding a JSON null into a non-pointer ObjectID field
// will leave the field unchanged. For pointer values, encoding/json will set the pointer to nil and will not
// enter the UnmarshalJSON hook.
if string(b) == "null" {
return nil
}
var err error
switch len(b) {
case 12:
@@ -125,6 +158,12 @@ func (id *ObjectID) UnmarshalJSON(b []byte) error {
}
}
// An empty string is not a valid ObjectID, but we treat it as a special value that decodes as NilObjectID.
if len(str) == 0 {
copy(id[:], NilObjectID[:])
return nil
}
if len(str) != 24 {
return fmt.Errorf("cannot unmarshal into an ObjectID, the length must be 24 but it is %d", len(str))
}

View File

@@ -4,7 +4,7 @@
// 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 primitive contains types similar to Go primitives for BSON types can do not have direct
// Package primitive contains types similar to Go primitives for BSON types that do not have direct
// Go primitive representations.
package primitive // import "go.mongodb.org/mongo-driver/bson/primitive"
@@ -21,7 +21,7 @@ type Binary struct {
Data []byte
}
// Equal compares bp to bp2 and returns true is the are equal.
// Equal compares bp to bp2 and returns true if they are equal.
func (bp Binary) Equal(bp2 Binary) bool {
if bp.Subtype != bp2.Subtype {
return false
@@ -29,7 +29,7 @@ func (bp Binary) Equal(bp2 Binary) bool {
return bytes.Equal(bp.Data, bp2.Data)
}
// IsZero returns if bp is the empty Binary
// IsZero returns if bp is the empty Binary.
func (bp Binary) IsZero() bool {
return bp.Subtype == 0 && len(bp.Data) == 0
}
@@ -40,11 +40,32 @@ type Undefined struct{}
// DateTime represents the BSON datetime value.
type DateTime int64
// MarshalJSON marshal to time type
var _ json.Marshaler = DateTime(0)
var _ json.Unmarshaler = (*DateTime)(nil)
// MarshalJSON marshal to time type.
func (d DateTime) MarshalJSON() ([]byte, error) {
return json.Marshal(d.Time())
}
// UnmarshalJSON creates a primitive.DateTime from a JSON string.
func (d *DateTime) UnmarshalJSON(data []byte) error {
// Ignore "null" to keep parity with the time.Time type and the standard library. Decoding "null" into a non-pointer
// DateTime field will leave the field unchanged. For pointer values, the encoding/json will set the pointer to nil
// and will not defer to the UnmarshalJSON hook.
if string(data) == "null" {
return nil
}
var tempTime time.Time
if err := json.Unmarshal(data, &tempTime); err != nil {
return err
}
*d = NewDateTimeFromTime(tempTime)
return nil
}
// Time returns the date as a time type.
func (d DateTime) Time() time.Time {
return time.Unix(int64(d)/1000, int64(d)%1000*1000000)
@@ -52,7 +73,7 @@ func (d DateTime) Time() time.Time {
// NewDateTimeFromTime creates a new DateTime from a Time.
func NewDateTimeFromTime(t time.Time) DateTime {
return DateTime(t.UnixNano() / 1000000)
return DateTime(t.Unix()*1e3 + int64(t.Nanosecond())/1e6)
}
// Null represents the BSON null value.
@@ -68,12 +89,12 @@ func (rp Regex) String() string {
return fmt.Sprintf(`{"pattern": "%s", "options": "%s"}`, rp.Pattern, rp.Options)
}
// Equal compares rp to rp2 and returns true is the are equal.
// Equal compares rp to rp2 and returns true if they are equal.
func (rp Regex) Equal(rp2 Regex) bool {
return rp.Pattern == rp2.Pattern && rp.Options == rp2.Options
}
// IsZero returns if rp is the empty Regex
// IsZero returns if rp is the empty Regex.
func (rp Regex) IsZero() bool {
return rp.Pattern == "" && rp.Options == ""
}
@@ -88,12 +109,12 @@ func (d DBPointer) String() string {
return fmt.Sprintf(`{"db": "%s", "pointer": "%s"}`, d.DB, d.Pointer)
}
// Equal compares d to d2 and returns true is the are equal.
// Equal compares d to d2 and returns true if they are equal.
func (d DBPointer) Equal(d2 DBPointer) bool {
return d.DB == d2.DB && bytes.Equal(d.Pointer[:], d2.Pointer[:])
return d == d2
}
// IsZero returns if d is the empty DBPointer
// IsZero returns if d is the empty DBPointer.
func (d DBPointer) IsZero() bool {
return d.DB == "" && d.Pointer.IsZero()
}
@@ -120,12 +141,12 @@ type Timestamp struct {
I uint32
}
// Equal compares tp to tp2 and returns true is the are equal.
// Equal compares tp to tp2 and returns true if they are equal.
func (tp Timestamp) Equal(tp2 Timestamp) bool {
return tp.T == tp2.T && tp.I == tp2.I
}
// IsZero returns if tp is the zero Timestamp
// IsZero returns if tp is the zero Timestamp.
func (tp Timestamp) IsZero() bool {
return tp.T == 0 && tp.I == 0
}
@@ -161,7 +182,7 @@ type MaxKey struct{}
//
// Example usage:
//
// bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
// bson.D{{"foo", "bar"}, {"hello", "world"}, {"pi", 3.14159}}
type D []E
// Map creates a map from the elements of the D.
@@ -185,12 +206,12 @@ type E struct {
//
// Example usage:
//
// bson.M{"foo": "bar", "hello": "world", "pi": 3.14159}.
// bson.M{"foo": "bar", "hello": "world", "pi": 3.14159}
type M map[string]interface{}
// An A is an ordered representation of a BSON array.
//
// Example usage:
//
// bson.A{"bar", "world", 3.14159, bson.D{{"qux", 12345}}}
// bson.A{"bar", "world", 3.14159, bson.D{{"qux", 12345}}}
type A []interface{}

View File

@@ -14,6 +14,9 @@ import (
"go.mongodb.org/mongo-driver/bson/bsonrw"
)
var tRawValue = reflect.TypeOf(RawValue{})
var tRaw = reflect.TypeOf(Raw(nil))
var primitiveCodecs PrimitiveCodecs
// PrimitiveCodecs is a namespace for all of the default bsoncodec.Codecs for the primitive types
@@ -87,25 +90,3 @@ func (PrimitiveCodecs) RawDecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.Valu
val.Set(reflect.ValueOf(rdr))
return err
}
func (pc PrimitiveCodecs) encodeRaw(ec bsoncodec.EncodeContext, dw bsonrw.DocumentWriter, raw Raw) error {
var copier bsonrw.Copier
elems, err := raw.Elements()
if err != nil {
return err
}
for _, elem := range elems {
dvw, err := dw.WriteDocumentElement(elem.Key())
if err != nil {
return err
}
val := elem.Value()
err = copier.CopyValueFromBytes(dvw, val.Type, val.Value)
if err != nil {
return err
}
}
return dw.WriteDocumentEnd()
}

View File

@@ -15,7 +15,6 @@ import (
// ErrNilReader indicates that an operation was attempted on a nil bson.Reader.
var ErrNilReader = errors.New("nil reader")
var errValidateDone = errors.New("validation loop complete")
// Raw is a wrapper around a byte slice. It will interpret the slice as a
// BSON document. This type is a wrapper around a bsoncore.Document. Errors returned from the
@@ -84,9 +83,3 @@ func (r Raw) IndexErr(index uint) (RawElement, error) {
// String implements the fmt.Stringer interface.
func (r Raw) String() string { return bsoncore.Document(r).String() }
// readi32 is a helper function for reading an int32 from slice of bytes.
func readi32(b []byte) int32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return int32(b[0]) | int32(b[1])<<8 | int32(b[2])<<16 | int32(b[3])<<24
}

View File

@@ -104,7 +104,9 @@ func (rv RawValue) UnmarshalWithContext(dc *bsoncodec.DecodeContext, val interfa
}
func convertFromCoreValue(v bsoncore.Value) RawValue { return RawValue{Type: v.Type, Value: v.Data} }
func convertToCoreValue(v RawValue) bsoncore.Value { return bsoncore.Value{Type: v.Type, Data: v.Value} }
func convertToCoreValue(v RawValue) bsoncore.Value {
return bsoncore.Value{Type: v.Type, Data: v.Value}
}
// Validate ensures the value is a valid BSON value.
func (rv RawValue) Validate() error { return convertToCoreValue(rv).Validate() }
@@ -176,7 +178,9 @@ func (rv RawValue) ObjectID() primitive.ObjectID { return convertToCoreValue(rv)
// ObjectIDOK is the same as ObjectID, except it returns a boolean instead of
// panicking.
func (rv RawValue) ObjectIDOK() (primitive.ObjectID, bool) { return convertToCoreValue(rv).ObjectIDOK() }
func (rv RawValue) ObjectIDOK() (primitive.ObjectID, bool) {
return convertToCoreValue(rv).ObjectIDOK()
}
// Boolean returns the boolean value the Value represents. It panics if the
// value is a BSON type other than boolean.
@@ -214,7 +218,9 @@ func (rv RawValue) RegexOK() (pattern, options string, ok bool) {
// DBPointer returns the BSON dbpointer value the Value represents. It panics if the value is a BSON
// type other than DBPointer.
func (rv RawValue) DBPointer() (string, primitive.ObjectID) { return convertToCoreValue(rv).DBPointer() }
func (rv RawValue) DBPointer() (string, primitive.ObjectID) {
return convertToCoreValue(rv).DBPointer()
}
// DBPointerOK is the same as DBPoitner, except that it returns a boolean
// instead of panicking.
@@ -260,6 +266,14 @@ func (rv RawValue) Int32() int32 { return convertToCoreValue(rv).Int32() }
// panicking.
func (rv RawValue) Int32OK() (int32, bool) { return convertToCoreValue(rv).Int32OK() }
// AsInt32 returns a BSON number as an int32. If the BSON type is not a numeric one, this method
// will panic.
func (rv RawValue) AsInt32() int32 { return convertToCoreValue(rv).AsInt32() }
// AsInt32OK is the same as AsInt32, except that it returns a boolean instead of
// panicking.
func (rv RawValue) AsInt32OK() (int32, bool) { return convertToCoreValue(rv).AsInt32OK() }
// Timestamp returns the BSON timestamp value the Value represents. It panics if the value is a
// BSON type other than timestamp.
func (rv RawValue) Timestamp() (t, i uint32) { return convertToCoreValue(rv).Timestamp() }
@@ -276,6 +290,14 @@ func (rv RawValue) Int64() int64 { return convertToCoreValue(rv).Int64() }
// panicking.
func (rv RawValue) Int64OK() (int64, bool) { return convertToCoreValue(rv).Int64OK() }
// AsInt64 returns a BSON number as an int64. If the BSON type is not a numeric one, this method
// will panic.
func (rv RawValue) AsInt64() int64 { return convertToCoreValue(rv).AsInt64() }
// AsInt64OK is the same as AsInt64, except that it returns a boolean instead of
// panicking.
func (rv RawValue) AsInt64OK() (int64, bool) { return convertToCoreValue(rv).AsInt64OK() }
// Decimal128 returns the decimal the Value represents. It panics if the value is a BSON type other than
// decimal.
func (rv RawValue) Decimal128() primitive.Decimal128 { return convertToCoreValue(rv).Decimal128() }

View File

@@ -13,7 +13,7 @@ import "go.mongodb.org/mongo-driver/bson/bsoncodec"
var DefaultRegistry = NewRegistryBuilder().Build()
// NewRegistryBuilder creates a new RegistryBuilder configured with the default encoders and
// deocders from the bsoncodec.DefaultValueEncoders and bsoncodec.DefaultValueDecoders types and the
// decoders from the bsoncodec.DefaultValueEncoders and bsoncodec.DefaultValueDecoders types and the
// PrimitiveCodecs type in this package.
func NewRegistryBuilder() *bsoncodec.RegistryBuilder {
rb := bsoncodec.NewRegistryBuilder()

View File

@@ -7,11 +7,7 @@
package bson
import (
"reflect"
"time"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// These constants uniquely refer to each BSON type.
@@ -38,48 +34,3 @@ const (
TypeMinKey = bsontype.MinKey
TypeMaxKey = bsontype.MaxKey
)
var tBinary = reflect.TypeOf(primitive.Binary{})
var tBool = reflect.TypeOf(false)
var tCodeWithScope = reflect.TypeOf(primitive.CodeWithScope{})
var tDBPointer = reflect.TypeOf(primitive.DBPointer{})
var tDecimal = reflect.TypeOf(primitive.Decimal128{})
var tD = reflect.TypeOf(D{})
var tA = reflect.TypeOf(A{})
var tDateTime = reflect.TypeOf(primitive.DateTime(0))
var tUndefined = reflect.TypeOf(primitive.Undefined{})
var tNull = reflect.TypeOf(primitive.Null{})
var tRawValue = reflect.TypeOf(RawValue{})
var tFloat32 = reflect.TypeOf(float32(0))
var tFloat64 = reflect.TypeOf(float64(0))
var tInt = reflect.TypeOf(int(0))
var tInt8 = reflect.TypeOf(int8(0))
var tInt16 = reflect.TypeOf(int16(0))
var tInt32 = reflect.TypeOf(int32(0))
var tInt64 = reflect.TypeOf(int64(0))
var tJavaScript = reflect.TypeOf(primitive.JavaScript(""))
var tOID = reflect.TypeOf(primitive.ObjectID{})
var tRaw = reflect.TypeOf(Raw(nil))
var tRegex = reflect.TypeOf(primitive.Regex{})
var tString = reflect.TypeOf("")
var tSymbol = reflect.TypeOf(primitive.Symbol(""))
var tTime = reflect.TypeOf(time.Time{})
var tTimestamp = reflect.TypeOf(primitive.Timestamp{})
var tUint = reflect.TypeOf(uint(0))
var tUint8 = reflect.TypeOf(uint8(0))
var tUint16 = reflect.TypeOf(uint16(0))
var tUint32 = reflect.TypeOf(uint32(0))
var tUint64 = reflect.TypeOf(uint64(0))
var tMinKey = reflect.TypeOf(primitive.MinKey{})
var tMaxKey = reflect.TypeOf(primitive.MaxKey{})
var tEmpty = reflect.TypeOf((*interface{})(nil)).Elem()
var tEmptySlice = reflect.TypeOf([]interface{}(nil))
var zeroVal reflect.Value
// this references the quantity of milliseconds between zero time and
// the unix epoch. useful for making sure that we convert time.Time
// objects correctly to match the legacy bson library's handling of
// time.Time values.
const zeroEpochMs = int64(62135596800000)

View File

@@ -23,7 +23,7 @@ type Unmarshaler interface {
}
// ValueUnmarshaler is an interface implemented by types that can unmarshal a
// BSON value representaiton of themselves. The BSON bytes and type can be
// BSON value representation of themselves. The BSON bytes and type can be
// assumed to be valid. UnmarshalBSONValue must copy the BSON value bytes if it
// wishes to retain the data after returning.
type ValueUnmarshaler interface {

View File

@@ -0,0 +1,164 @@
// 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"
"fmt"
"io"
"strconv"
)
// NewArrayLengthError creates and returns an error for when the length of an array exceeds the
// bytes available.
func NewArrayLengthError(length, rem int) error {
return lengthError("array", length, rem)
}
// Array is a raw bytes representation of a BSON array.
type Array []byte
// NewArrayFromReader reads an array from r. This function will only validate the length is
// correct and that the array ends with a null byte.
func NewArrayFromReader(r io.Reader) (Array, error) {
return newBufferFromReader(r)
}
// Index searches for and retrieves the value at the given index. This method will panic if
// the array is invalid or if the index is out of bounds.
func (a Array) Index(index uint) Value {
value, err := a.IndexErr(index)
if err != nil {
panic(err)
}
return value
}
// IndexErr searches for and retrieves the value at the given index.
func (a Array) IndexErr(index uint) (Value, error) {
elem, err := indexErr(a, index)
if err != nil {
return Value{}, err
}
return elem.Value(), err
}
// DebugString outputs a human readable version of Array. It will attempt to stringify the
// valid components of the array even if the entire array is not valid.
func (a Array) DebugString() string {
if len(a) < 5 {
return "<malformed>"
}
var buf bytes.Buffer
buf.WriteString("Array")
length, rem, _ := ReadLength(a) // We know we have enough bytes to read the length
buf.WriteByte('(')
buf.WriteString(strconv.Itoa(int(length)))
length -= 4
buf.WriteString(")[")
var elem Element
var ok bool
for length > 1 {
elem, rem, ok = ReadElement(rem)
length -= int32(len(elem))
if !ok {
buf.WriteString(fmt.Sprintf("<malformed (%d)>", length))
break
}
fmt.Fprintf(&buf, "%s", elem.Value().DebugString())
if length != 1 {
buf.WriteByte(',')
}
}
buf.WriteByte(']')
return buf.String()
}
// String outputs an ExtendedJSON version of Array. If the Array is not valid, this method
// returns an empty string.
func (a Array) String() string {
if len(a) < 5 {
return ""
}
var buf bytes.Buffer
buf.WriteByte('[')
length, rem, _ := ReadLength(a) // We know we have enough bytes to read the length
length -= 4
var elem Element
var ok bool
for length > 1 {
elem, rem, ok = ReadElement(rem)
length -= int32(len(elem))
if !ok {
return ""
}
fmt.Fprintf(&buf, "%s", elem.Value().String())
if length > 1 {
buf.WriteByte(',')
}
}
if length != 1 { // Missing final null byte or inaccurate length
return ""
}
buf.WriteByte(']')
return buf.String()
}
// Values returns this array as a slice of values. The returned slice will contain valid values.
// If the array is not valid, the values up to the invalid point will be returned along with an
// error.
func (a Array) Values() ([]Value, error) {
return values(a)
}
// Validate validates the array and ensures the elements contained within are valid.
func (a Array) Validate() error {
length, rem, ok := ReadLength(a)
if !ok {
return NewInsufficientBytesError(a, rem)
}
if int(length) > len(a) {
return NewArrayLengthError(int(length), len(a))
}
if a[length-1] != 0x00 {
return ErrMissingNull
}
length -= 4
var elem Element
var keyNum int64
for length > 1 {
elem, rem, ok = ReadElement(rem)
length -= int32(len(elem))
if !ok {
return NewInsufficientBytesError(a, rem)
}
// validate element
err := elem.Validate()
if err != nil {
return err
}
// validate keys increase numerically
if fmt.Sprint(keyNum) != elem.Key() {
return fmt.Errorf("array key %q is out of order or invalid", elem.Key())
}
keyNum++
}
if len(rem) < 1 || rem[0] != 0x00 {
return ErrMissingNull
}
return nil
}

View File

@@ -0,0 +1,201 @@
// 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 (
"strconv"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// ArrayBuilder builds a bson array
type ArrayBuilder struct {
arr []byte
indexes []int32
keys []int
}
// NewArrayBuilder creates a new ArrayBuilder
func NewArrayBuilder() *ArrayBuilder {
return (&ArrayBuilder{}).startArray()
}
// startArray reserves the array's length and sets the index to where the length begins
func (a *ArrayBuilder) startArray() *ArrayBuilder {
var index int32
index, a.arr = AppendArrayStart(a.arr)
a.indexes = append(a.indexes, index)
a.keys = append(a.keys, 0)
return a
}
// Build updates the length of the array and index to the beginning of the documents length
// bytes, then returns the array (bson bytes)
func (a *ArrayBuilder) Build() Array {
lastIndex := len(a.indexes) - 1
lastKey := len(a.keys) - 1
a.arr, _ = AppendArrayEnd(a.arr, a.indexes[lastIndex])
a.indexes = a.indexes[:lastIndex]
a.keys = a.keys[:lastKey]
return a.arr
}
// incrementKey() increments the value keys and returns the key to be used to a.appendArray* functions
func (a *ArrayBuilder) incrementKey() string {
idx := len(a.keys) - 1
key := strconv.Itoa(a.keys[idx])
a.keys[idx]++
return key
}
// AppendInt32 will append i32 to ArrayBuilder.arr
func (a *ArrayBuilder) AppendInt32(i32 int32) *ArrayBuilder {
a.arr = AppendInt32Element(a.arr, a.incrementKey(), i32)
return a
}
// AppendDocument will append doc to ArrayBuilder.arr
func (a *ArrayBuilder) AppendDocument(doc []byte) *ArrayBuilder {
a.arr = AppendDocumentElement(a.arr, a.incrementKey(), doc)
return a
}
// AppendArray will append arr to ArrayBuilder.arr
func (a *ArrayBuilder) AppendArray(arr []byte) *ArrayBuilder {
a.arr = AppendArrayElement(a.arr, a.incrementKey(), arr)
return a
}
// AppendDouble will append f to ArrayBuilder.doc
func (a *ArrayBuilder) AppendDouble(f float64) *ArrayBuilder {
a.arr = AppendDoubleElement(a.arr, a.incrementKey(), f)
return a
}
// AppendString will append str to ArrayBuilder.doc
func (a *ArrayBuilder) AppendString(str string) *ArrayBuilder {
a.arr = AppendStringElement(a.arr, a.incrementKey(), str)
return a
}
// AppendObjectID will append oid to ArrayBuilder.doc
func (a *ArrayBuilder) AppendObjectID(oid primitive.ObjectID) *ArrayBuilder {
a.arr = AppendObjectIDElement(a.arr, a.incrementKey(), oid)
return a
}
// AppendBinary will append a BSON binary element using subtype, and
// b to a.arr
func (a *ArrayBuilder) AppendBinary(subtype byte, b []byte) *ArrayBuilder {
a.arr = AppendBinaryElement(a.arr, a.incrementKey(), subtype, b)
return a
}
// AppendUndefined will append a BSON undefined element using key to a.arr
func (a *ArrayBuilder) AppendUndefined() *ArrayBuilder {
a.arr = AppendUndefinedElement(a.arr, a.incrementKey())
return a
}
// AppendBoolean will append a boolean element using b to a.arr
func (a *ArrayBuilder) AppendBoolean(b bool) *ArrayBuilder {
a.arr = AppendBooleanElement(a.arr, a.incrementKey(), b)
return a
}
// AppendDateTime will append datetime element dt to a.arr
func (a *ArrayBuilder) AppendDateTime(dt int64) *ArrayBuilder {
a.arr = AppendDateTimeElement(a.arr, a.incrementKey(), dt)
return a
}
// AppendNull will append a null element to a.arr
func (a *ArrayBuilder) AppendNull() *ArrayBuilder {
a.arr = AppendNullElement(a.arr, a.incrementKey())
return a
}
// AppendRegex will append pattern and options to a.arr
func (a *ArrayBuilder) AppendRegex(pattern, options string) *ArrayBuilder {
a.arr = AppendRegexElement(a.arr, a.incrementKey(), pattern, options)
return a
}
// AppendDBPointer will append ns and oid to a.arr
func (a *ArrayBuilder) AppendDBPointer(ns string, oid primitive.ObjectID) *ArrayBuilder {
a.arr = AppendDBPointerElement(a.arr, a.incrementKey(), ns, oid)
return a
}
// AppendJavaScript will append js to a.arr
func (a *ArrayBuilder) AppendJavaScript(js string) *ArrayBuilder {
a.arr = AppendJavaScriptElement(a.arr, a.incrementKey(), js)
return a
}
// AppendSymbol will append symbol to a.arr
func (a *ArrayBuilder) AppendSymbol(symbol string) *ArrayBuilder {
a.arr = AppendSymbolElement(a.arr, a.incrementKey(), symbol)
return a
}
// AppendCodeWithScope will append code and scope to a.arr
func (a *ArrayBuilder) AppendCodeWithScope(code string, scope Document) *ArrayBuilder {
a.arr = AppendCodeWithScopeElement(a.arr, a.incrementKey(), code, scope)
return a
}
// AppendTimestamp will append t and i to a.arr
func (a *ArrayBuilder) AppendTimestamp(t, i uint32) *ArrayBuilder {
a.arr = AppendTimestampElement(a.arr, a.incrementKey(), t, i)
return a
}
// AppendInt64 will append i64 to a.arr
func (a *ArrayBuilder) AppendInt64(i64 int64) *ArrayBuilder {
a.arr = AppendInt64Element(a.arr, a.incrementKey(), i64)
return a
}
// AppendDecimal128 will append d128 to a.arr
func (a *ArrayBuilder) AppendDecimal128(d128 primitive.Decimal128) *ArrayBuilder {
a.arr = AppendDecimal128Element(a.arr, a.incrementKey(), d128)
return a
}
// AppendMaxKey will append a max key element to a.arr
func (a *ArrayBuilder) AppendMaxKey() *ArrayBuilder {
a.arr = AppendMaxKeyElement(a.arr, a.incrementKey())
return a
}
// AppendMinKey will append a min key element to a.arr
func (a *ArrayBuilder) AppendMinKey() *ArrayBuilder {
a.arr = AppendMinKeyElement(a.arr, a.incrementKey())
return a
}
// AppendValue appends a BSON value to the array.
func (a *ArrayBuilder) AppendValue(val Value) *ArrayBuilder {
a.arr = AppendValueElement(a.arr, a.incrementKey(), val)
return a
}
// StartArray starts building an inline Array. After this document is completed,
// the user must call a.FinishArray
func (a *ArrayBuilder) StartArray() *ArrayBuilder {
a.arr = AppendHeader(a.arr, bsontype.Array, a.incrementKey())
a.startArray()
return a
}
// FinishArray builds the most recent array created
func (a *ArrayBuilder) FinishArray() *ArrayBuilder {
a.arr = a.Build()
return a
}

View File

@@ -0,0 +1,189 @@
// 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 (
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// DocumentBuilder builds a bson document
type DocumentBuilder struct {
doc []byte
indexes []int32
}
// startDocument reserves the document's length and set the index to where the length begins
func (db *DocumentBuilder) startDocument() *DocumentBuilder {
var index int32
index, db.doc = AppendDocumentStart(db.doc)
db.indexes = append(db.indexes, index)
return db
}
// NewDocumentBuilder creates a new DocumentBuilder
func NewDocumentBuilder() *DocumentBuilder {
return (&DocumentBuilder{}).startDocument()
}
// Build updates the length of the document and index to the beginning of the documents length
// bytes, then returns the document (bson bytes)
func (db *DocumentBuilder) Build() Document {
last := len(db.indexes) - 1
db.doc, _ = AppendDocumentEnd(db.doc, db.indexes[last])
db.indexes = db.indexes[:last]
return db.doc
}
// AppendInt32 will append an int32 element using key and i32 to DocumentBuilder.doc
func (db *DocumentBuilder) AppendInt32(key string, i32 int32) *DocumentBuilder {
db.doc = AppendInt32Element(db.doc, key, i32)
return db
}
// AppendDocument will append a bson embedded document element using key
// and doc to DocumentBuilder.doc
func (db *DocumentBuilder) AppendDocument(key string, doc []byte) *DocumentBuilder {
db.doc = AppendDocumentElement(db.doc, key, doc)
return db
}
// AppendArray will append a bson array using key and arr to DocumentBuilder.doc
func (db *DocumentBuilder) AppendArray(key string, arr []byte) *DocumentBuilder {
db.doc = AppendHeader(db.doc, bsontype.Array, key)
db.doc = AppendArray(db.doc, arr)
return db
}
// AppendDouble will append a double element using key and f to DocumentBuilder.doc
func (db *DocumentBuilder) AppendDouble(key string, f float64) *DocumentBuilder {
db.doc = AppendDoubleElement(db.doc, key, f)
return db
}
// AppendString will append str to DocumentBuilder.doc with the given key
func (db *DocumentBuilder) AppendString(key string, str string) *DocumentBuilder {
db.doc = AppendStringElement(db.doc, key, str)
return db
}
// AppendObjectID will append oid to DocumentBuilder.doc with the given key
func (db *DocumentBuilder) AppendObjectID(key string, oid primitive.ObjectID) *DocumentBuilder {
db.doc = AppendObjectIDElement(db.doc, key, oid)
return db
}
// AppendBinary will append a BSON binary element using key, subtype, and
// b to db.doc
func (db *DocumentBuilder) AppendBinary(key string, subtype byte, b []byte) *DocumentBuilder {
db.doc = AppendBinaryElement(db.doc, key, subtype, b)
return db
}
// AppendUndefined will append a BSON undefined element using key to db.doc
func (db *DocumentBuilder) AppendUndefined(key string) *DocumentBuilder {
db.doc = AppendUndefinedElement(db.doc, key)
return db
}
// AppendBoolean will append a boolean element using key and b to db.doc
func (db *DocumentBuilder) AppendBoolean(key string, b bool) *DocumentBuilder {
db.doc = AppendBooleanElement(db.doc, key, b)
return db
}
// AppendDateTime will append a datetime element using key and dt to db.doc
func (db *DocumentBuilder) AppendDateTime(key string, dt int64) *DocumentBuilder {
db.doc = AppendDateTimeElement(db.doc, key, dt)
return db
}
// AppendNull will append a null element using key to db.doc
func (db *DocumentBuilder) AppendNull(key string) *DocumentBuilder {
db.doc = AppendNullElement(db.doc, key)
return db
}
// AppendRegex will append pattern and options using key to db.doc
func (db *DocumentBuilder) AppendRegex(key, pattern, options string) *DocumentBuilder {
db.doc = AppendRegexElement(db.doc, key, pattern, options)
return db
}
// AppendDBPointer will append ns and oid to using key to db.doc
func (db *DocumentBuilder) AppendDBPointer(key string, ns string, oid primitive.ObjectID) *DocumentBuilder {
db.doc = AppendDBPointerElement(db.doc, key, ns, oid)
return db
}
// AppendJavaScript will append js using the provided key to db.doc
func (db *DocumentBuilder) AppendJavaScript(key, js string) *DocumentBuilder {
db.doc = AppendJavaScriptElement(db.doc, key, js)
return db
}
// AppendSymbol will append a BSON symbol element using key and symbol db.doc
func (db *DocumentBuilder) AppendSymbol(key, symbol string) *DocumentBuilder {
db.doc = AppendSymbolElement(db.doc, key, symbol)
return db
}
// AppendCodeWithScope will append code and scope using key to db.doc
func (db *DocumentBuilder) AppendCodeWithScope(key string, code string, scope Document) *DocumentBuilder {
db.doc = AppendCodeWithScopeElement(db.doc, key, code, scope)
return db
}
// AppendTimestamp will append t and i to db.doc using provided key
func (db *DocumentBuilder) AppendTimestamp(key string, t, i uint32) *DocumentBuilder {
db.doc = AppendTimestampElement(db.doc, key, t, i)
return db
}
// AppendInt64 will append i64 to dst using key to db.doc
func (db *DocumentBuilder) AppendInt64(key string, i64 int64) *DocumentBuilder {
db.doc = AppendInt64Element(db.doc, key, i64)
return db
}
// AppendDecimal128 will append d128 to db.doc using provided key
func (db *DocumentBuilder) AppendDecimal128(key string, d128 primitive.Decimal128) *DocumentBuilder {
db.doc = AppendDecimal128Element(db.doc, key, d128)
return db
}
// AppendMaxKey will append a max key element using key to db.doc
func (db *DocumentBuilder) AppendMaxKey(key string) *DocumentBuilder {
db.doc = AppendMaxKeyElement(db.doc, key)
return db
}
// AppendMinKey will append a min key element using key to db.doc
func (db *DocumentBuilder) AppendMinKey(key string) *DocumentBuilder {
db.doc = AppendMinKeyElement(db.doc, key)
return db
}
// AppendValue will append a BSON element with the provided key and value to the document.
func (db *DocumentBuilder) AppendValue(key string, val Value) *DocumentBuilder {
db.doc = AppendValueElement(db.doc, key, val)
return db
}
// StartDocument starts building an inline document element with the provided key
// After this document is completed, the user must call finishDocument
func (db *DocumentBuilder) StartDocument(key string) *DocumentBuilder {
db.doc = AppendHeader(db.doc, bsontype.EmbeddedDocument, key)
db = db.startDocument()
return db
}
// FinishDocument builds the most recent document created
func (db *DocumentBuilder) FinishDocument() *DocumentBuilder {
db.doc = db.Build()
return db
}

View File

@@ -15,7 +15,7 @@
// enough bytes. This library attempts to do no validation, it will only return
// false if there are not enough bytes for an item to be read. For example, the
// ReadDocument function checks the length, if that length is larger than the
// number of bytes availble, it will return false, if there are enough bytes, it
// number of bytes available, it will return false, if there are enough bytes, it
// will return those bytes and true. It is the consumers responsibility to
// validate those bytes.
//
@@ -30,35 +30,45 @@ import (
"fmt"
"math"
"strconv"
"strings"
"time"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// EmptyDocumentLength is the length of a document that has been started/ended but has no elements.
const EmptyDocumentLength = 5
const (
// EmptyDocumentLength is the length of a document that has been started/ended but has no elements.
EmptyDocumentLength = 5
// nullTerminator is a string version of the 0 byte that is appended at the end of cstrings.
nullTerminator = string(byte(0))
invalidKeyPanicMsg = "BSON element keys cannot contain null bytes"
invalidRegexPanicMsg = "BSON regex values cannot contain null bytes"
)
// AppendType will append t to dst and return the extended buffer.
func AppendType(dst []byte, t bsontype.Type) []byte { return append(dst, byte(t)) }
// AppendKey will append key to dst and return the extended buffer.
func AppendKey(dst []byte, key string) []byte { return append(dst, key+string(0x00)...) }
func AppendKey(dst []byte, key string) []byte { return append(dst, key+nullTerminator...) }
// AppendHeader will append Type t and key to dst and return the extended
// buffer.
func AppendHeader(dst []byte, t bsontype.Type, key string) []byte {
if !isValidCString(key) {
panic(invalidKeyPanicMsg)
}
dst = AppendType(dst, t)
dst = append(dst, key...)
return append(dst, 0x00)
// return append(AppendType(dst, t), key+string(0x00)...)
}
// TODO(skriptble): All of the Read* functions should return src resliced to start just after what
// was read.
// TODO(skriptble): All of the Read* functions should return src resliced to start just after what was read.
// ReadType will return the first byte of the provided []byte as a type. If
// there is no availble byte, false is returned.
// there is no available byte, false is returned.
func ReadType(src []byte) (bsontype.Type, []byte, bool) {
if len(src) < 1 {
return 0, src, false
@@ -186,12 +196,13 @@ func ReadString(src []byte) (string, []byte, bool) {
// AppendDocumentStart reserves a document's length and returns the index where the length begins.
// This index can later be used to write the length of the document.
//
// TODO(skriptble): We really need AppendDocumentStart and AppendDocumentEnd.
// AppendDocumentStart would handle calling ReserveLength and providing the index of the start of
// the document. AppendDocumentEnd would handle taking that start index, adding the null byte,
// calculating the length, and filling in the length at the start of the document.
func AppendDocumentStart(dst []byte) (index int32, b []byte) { return ReserveLength(dst) }
func AppendDocumentStart(dst []byte) (index int32, b []byte) {
// TODO(skriptble): We really need AppendDocumentStart and AppendDocumentEnd. AppendDocumentStart would handle calling
// TODO ReserveLength and providing the index of the start of the document. AppendDocumentEnd would handle taking that
// TODO start index, adding the null byte, calculating the length, and filling in the length at the start of the
// TODO document.
return ReserveLength(dst)
}
// AppendDocumentStartInline functions the same as AppendDocumentStart but takes a pointer to the
// index int32 which allows this function to be used inline.
@@ -220,7 +231,7 @@ func AppendDocumentEnd(dst []byte, index int32) ([]byte, error) {
// AppendDocument will append doc to dst and return the extended buffer.
func AppendDocument(dst []byte, doc []byte) []byte { return append(dst, doc...) }
// AppendDocumentElement will append a BSON embeded document element using key
// AppendDocumentElement will append a BSON embedded document element using key
// and doc to dst and return the extended buffer.
func AppendDocumentElement(dst []byte, key string, doc []byte) []byte {
return AppendDocument(AppendHeader(dst, bsontype.EmbeddedDocument, key), doc)
@@ -297,7 +308,7 @@ func BuildArrayElement(dst []byte, key string, values ...Value) []byte {
// ReadArray will read an array from src. If there are not enough bytes it
// will return false.
func ReadArray(src []byte) (arr Document, rem []byte, ok bool) { return readLengthBytes(src) }
func ReadArray(src []byte) (arr Array, rem []byte, ok bool) { return readLengthBytes(src) }
// AppendBinary will append subtype and b to dst and return the extended buffer.
func AppendBinary(dst []byte, subtype byte, b []byte) []byte {
@@ -427,7 +438,11 @@ func AppendNullElement(dst []byte, key string) []byte { return AppendHeader(dst,
// AppendRegex will append pattern and options to dst and return the extended buffer.
func AppendRegex(dst []byte, pattern, options string) []byte {
return append(dst, pattern+string(0x00)+options+string(0x00)...)
if !isValidCString(pattern) || !isValidCString(options) {
panic(invalidRegexPanicMsg)
}
return append(dst, pattern+nullTerminator+options+nullTerminator...)
}
// AppendRegexElement will append a BSON regex element using key, pattern, and
@@ -815,7 +830,7 @@ func readstring(src []byte) (string, []byte, bool) {
if !ok {
return "", src, false
}
if len(src[4:]) < int(l) {
if len(src[4:]) < int(l) || l == 0 {
return "", src, false
}
@@ -841,3 +856,7 @@ func appendBinarySubtype2(dst []byte, subtype byte, b []byte) []byte {
dst = appendLength(dst, int32(len(b)))
return append(dst, b...)
}
func isValidCString(cs string) bool {
return !strings.ContainsRune(cs, '\x00')
}

View File

@@ -13,34 +13,35 @@ import (
"io"
"strconv"
"github.com/go-stack/stack"
"go.mongodb.org/mongo-driver/bson/bsontype"
)
// DocumentValidationError is an error type returned when attempting to validate a document.
type DocumentValidationError string
// ValidationError is an error type returned when attempting to validate a document or array.
type ValidationError string
func (dve DocumentValidationError) Error() string { return string(dve) }
func (ve ValidationError) Error() string { return string(ve) }
// NewDocumentLengthError creates and returns an error for when the length of a document exceeds the
// bytes available.
func NewDocumentLengthError(length, rem int) error {
return DocumentValidationError(
fmt.Sprintf("document length exceeds available bytes. length=%d remainingBytes=%d", length, rem),
)
return lengthError("document", length, rem)
}
func lengthError(bufferType string, length, rem int) error {
return ValidationError(fmt.Sprintf("%v length exceeds available bytes. length=%d remainingBytes=%d",
bufferType, length, rem))
}
// InsufficientBytesError indicates that there were not enough bytes to read the next component.
type InsufficientBytesError struct {
Source []byte
Remaining []byte
Stack stack.CallStack
}
// NewInsufficientBytesError creates a new InsufficientBytesError with the given Document, remaining
// bytes, and the current stack.
// NewInsufficientBytesError creates a new InsufficientBytesError with the given Document and
// remaining bytes.
func NewInsufficientBytesError(src, rem []byte) InsufficientBytesError {
return InsufficientBytesError{Source: src, Remaining: rem, Stack: stack.Trace().TrimRuntime()}
return InsufficientBytesError{Source: src, Remaining: rem}
}
// Error implements the error interface.
@@ -48,28 +49,6 @@ func (ibe InsufficientBytesError) Error() string {
return "too few bytes to read next component"
}
// ErrorStack returns a string representing the stack at the point where the error occurred.
func (ibe InsufficientBytesError) ErrorStack() string {
s := bytes.NewBufferString("too few bytes to read next component: [")
for i, call := range ibe.Stack {
if i != 0 {
s.WriteString(", ")
}
// go vet doesn't like %k even though it's part of stack's API, so we move the format
// string so it doesn't complain. (We also can't make it a constant, or go vet still
// complains.)
callFormat := "%k.%n %v"
s.WriteString(fmt.Sprintf(callFormat, call, call, call))
}
s.WriteRune(']')
return s.String()
}
// Equal checks that err2 also is an ErrTooSmall.
func (ibe InsufficientBytesError) Equal(err2 error) bool {
switch err2.(type) {
@@ -94,15 +73,16 @@ func (idte InvalidDepthTraversalError) Error() string {
)
}
// ErrMissingNull is returned when a document's last byte is not null.
const ErrMissingNull DocumentValidationError = "document end is missing null byte"
// ErrMissingNull is returned when a document or array's last byte is not null.
const ErrMissingNull ValidationError = "document or array end is missing null byte"
// ErrInvalidLength indicates that a length in a binary representation of a BSON document or array
// is invalid.
const ErrInvalidLength ValidationError = "document or array length is invalid"
// ErrNilReader indicates that an operation was attempted on a nil io.Reader.
var ErrNilReader = errors.New("nil reader")
// ErrInvalidLength indicates that a length in a binary representation of a BSON document is invalid.
var ErrInvalidLength = errors.New("document length is invalid")
// ErrEmptyKey indicates that no key was provided to a Lookup method.
var ErrEmptyKey = errors.New("empty key provided")
@@ -115,12 +95,13 @@ var ErrOutOfBounds = errors.New("out of bounds")
// Document is a raw bytes representation of a BSON document.
type Document []byte
// Array is a raw bytes representation of a BSON array.
type Array = Document
// NewDocumentFromReader reads a document from r. This function will only validate the length is
// correct and that the document ends with a null byte.
func NewDocumentFromReader(r io.Reader) (Document, error) {
return newBufferFromReader(r)
}
func newBufferFromReader(r io.Reader) ([]byte, error) {
if r == nil {
return nil, ErrNilReader
}
@@ -137,20 +118,20 @@ func NewDocumentFromReader(r io.Reader) (Document, error) {
if length < 0 {
return nil, ErrInvalidLength
}
document := make([]byte, length)
buffer := make([]byte, length)
copy(document, lengthBytes[:])
copy(buffer, lengthBytes[:])
_, err = io.ReadFull(r, document[4:])
_, err = io.ReadFull(r, buffer[4:])
if err != nil {
return nil, err
}
if document[length-1] != 0x00 {
if buffer[length-1] != 0x00 {
return nil, ErrMissingNull
}
return document, nil
return buffer, nil
}
// Lookup searches the document, potentially recursively, for the given key. If there are multiple
@@ -181,7 +162,8 @@ func (d Document) LookupErr(key ...string) (Value, error) {
if !ok {
return Value{}, NewInsufficientBytesError(d, rem)
}
if elem.Key() != key[0] {
// We use `KeyBytes` rather than `Key` to avoid a needless string alloc.
if string(elem.KeyBytes()) != key[0] {
continue
}
if len(key) > 1 {
@@ -194,7 +176,8 @@ func (d Document) LookupErr(key ...string) (Value, error) {
}
return val, nil
case bsontype.Array:
val, err := elem.Value().Array().LookupErr(key[1:]...)
// Convert to Document to continue Lookup recursion.
val, err := Document(elem.Value().Array()).LookupErr(key[1:]...)
if err != nil {
return Value{}, err
}
@@ -220,9 +203,13 @@ func (d Document) Index(index uint) Element {
// IndexErr searches for and retrieves the element at the given index.
func (d Document) IndexErr(index uint) (Element, error) {
length, rem, ok := ReadLength(d)
return indexErr(d, index)
}
func indexErr(b []byte, index uint) (Element, error) {
length, rem, ok := ReadLength(b)
if !ok {
return nil, NewInsufficientBytesError(d, rem)
return nil, NewInsufficientBytesError(b, rem)
}
length -= 4
@@ -233,7 +220,7 @@ func (d Document) IndexErr(index uint) (Element, error) {
elem, rem, ok = ReadElement(rem)
length -= int32(len(elem))
if !ok {
return nil, NewInsufficientBytesError(d, rem)
return nil, NewInsufficientBytesError(b, rem)
}
if current != index {
current++
@@ -337,9 +324,13 @@ func (d Document) Elements() ([]Element, error) {
// If the document is not valid, the values up to the invalid point will be returned along with an
// error.
func (d Document) Values() ([]Value, error) {
length, rem, ok := ReadLength(d)
return values(d)
}
func values(b []byte) ([]Value, error) {
length, rem, ok := ReadLength(b)
if !ok {
return nil, NewInsufficientBytesError(d, rem)
return nil, NewInsufficientBytesError(b, rem)
}
length -= 4
@@ -350,7 +341,7 @@ func (d Document) Values() ([]Value, error) {
elem, rem, ok = ReadElement(rem)
length -= int32(len(elem))
if !ok {
return vals, NewInsufficientBytesError(d, rem)
return vals, NewInsufficientBytesError(b, rem)
}
if err := elem.Value().Validate(); err != nil {
return vals, err
@@ -367,7 +358,7 @@ func (d Document) Validate() error {
return NewInsufficientBytesError(d, rem)
}
if int(length) > len(d) {
return d.lengtherror(int(length), len(d))
return NewDocumentLengthError(int(length), len(d))
}
if d[length-1] != 0x00 {
return ErrMissingNull
@@ -393,7 +384,3 @@ func (d Document) Validate() error {
}
return nil
}
func (Document) lengtherror(length, rem int) error {
return DocumentValidationError(fmt.Sprintf("document length exceeds available bytes. length=%d remainingBytes=%d", length, rem))
}

View File

@@ -1,3 +1,9 @@
// Copyright (C) MongoDB, Inc. 2022-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 (
@@ -90,8 +96,8 @@ func (ds *DocumentSequence) Empty() bool {
}
}
//ResetIterator resets the iteration point for the Next method to the beginning of the document
//sequence.
// ResetIterator resets the iteration point for the Next method to the beginning of the document
// sequence.
func (ds *DocumentSequence) ResetIterator() {
if ds == nil {
return

View File

@@ -250,7 +250,7 @@ func (v Value) String() string {
if !ok {
return ""
}
return docAsArray(arr, false)
return arr.String()
case bsontype.Binary:
subtype, data, ok := v.BinaryOK()
if !ok {
@@ -366,7 +366,7 @@ func (v Value) DebugString() string {
if !ok {
return "<malformed>"
}
return docAsArray(arr, true)
return arr.DebugString()
case bsontype.CodeWithScope:
code, scope, ok := v.CodeWithScopeOK()
if !ok {
@@ -464,7 +464,7 @@ func (v Value) DocumentOK() (Document, bool) {
// 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 {
func (v Value) Array() Array {
if v.Type != bsontype.Array {
panic(ElementTypeError{"bsoncore.Value.Array", v.Type})
}
@@ -477,7 +477,7 @@ func (v Value) Array() Document {
// ArrayOK is the same as Array, except it returns a boolean instead
// of panicking.
func (v Value) ArrayOK() (Document, bool) {
func (v Value) ArrayOK() (Array, bool) {
if v.Type != bsontype.Array {
return nil, false
}
@@ -602,7 +602,7 @@ func (v Value) Time() time.Time {
if !ok {
panic(NewInsufficientBytesError(v.Data, v.Data))
}
return time.Unix(int64(dt)/1000, int64(dt)%1000*1000000)
return time.Unix(dt/1000, dt%1000*1000000)
}
// TimeOK is the same as Time, except it returns a boolean instead of
@@ -615,7 +615,7 @@ func (v Value) TimeOK() (time.Time, bool) {
if !ok {
return time.Time{}, false
}
return time.Unix(int64(dt)/1000, int64(dt)%1000*1000000), true
return time.Unix(dt/1000, dt%1000*1000000), true
}
// Regex returns the BSON regex value the Value represents. It panics if the value is a BSON
@@ -978,38 +978,3 @@ func sortStringAlphebeticAscending(s string) string {
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()
}