123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837 |
- // BSON library for Go
- //
- // Copyright (c) 2010-2012 - Gustavo Niemeyer <gustavo@niemeyer.net>
- //
- // All rights reserved.
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are met:
- //
- // 1. Redistributions of source code must retain the above copyright notice, this
- // list of conditions and the following disclaimer.
- // 2. Redistributions in binary form must reproduce the above copyright notice,
- // this list of conditions and the following disclaimer in the documentation
- // and/or other materials provided with the distribution.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
- // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
- // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- // Package bson is an implementation of the BSON specification for Go:
- //
- // http://bsonspec.org
- //
- // It was created as part of the mgo MongoDB driver for Go, but is standalone
- // and may be used on its own without the driver.
- package bson
- import (
- "bytes"
- "crypto/md5"
- "crypto/rand"
- "encoding/binary"
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "math"
- "os"
- "reflect"
- "runtime"
- "strings"
- "sync"
- "sync/atomic"
- "time"
- )
- //go:generate go run bson_corpus_spec_test_generator.go
- // --------------------------------------------------------------------------
- // The public API.
- // Element types constants from BSON specification.
- const (
- ElementFloat64 byte = 0x01
- ElementString byte = 0x02
- ElementDocument byte = 0x03
- ElementArray byte = 0x04
- ElementBinary byte = 0x05
- Element06 byte = 0x06
- ElementObjectId byte = 0x07
- ElementBool byte = 0x08
- ElementDatetime byte = 0x09
- ElementNil byte = 0x0A
- ElementRegEx byte = 0x0B
- ElementDBPointer byte = 0x0C
- ElementJavaScriptWithoutScope byte = 0x0D
- ElementSymbol byte = 0x0E
- ElementJavaScriptWithScope byte = 0x0F
- ElementInt32 byte = 0x10
- ElementTimestamp byte = 0x11
- ElementInt64 byte = 0x12
- ElementDecimal128 byte = 0x13
- ElementMinKey byte = 0xFF
- ElementMaxKey byte = 0x7F
- BinaryGeneric byte = 0x00
- BinaryFunction byte = 0x01
- BinaryBinaryOld byte = 0x02
- BinaryUUIDOld byte = 0x03
- BinaryUUID byte = 0x04
- BinaryMD5 byte = 0x05
- BinaryUserDefined byte = 0x80
- )
- // Getter interface: a value implementing the bson.Getter interface will have its GetBSON
- // method called when the given value has to be marshalled, and the result
- // of this method will be marshaled in place of the actual object.
- //
- // If GetBSON returns return a non-nil error, the marshalling procedure
- // will stop and error out with the provided value.
- type Getter interface {
- GetBSON() (interface{}, error)
- }
- // Setter interface: a value implementing the bson.Setter interface will receive the BSON
- // value via the SetBSON method during unmarshaling, and the object
- // itself will not be changed as usual.
- //
- // If setting the value works, the method should return nil or alternatively
- // bson.ErrSetZero to set the respective field to its zero value (nil for
- // pointer types). If SetBSON returns a value of type bson.TypeError, the
- // BSON value will be omitted from a map or slice being decoded and the
- // unmarshalling will continue. If it returns any other non-nil error, the
- // unmarshalling procedure will stop and error out with the provided value.
- //
- // This interface is generally useful in pointer receivers, since the method
- // will want to change the receiver. A type field that implements the Setter
- // interface doesn't have to be a pointer, though.
- //
- // Unlike the usual behavior, unmarshalling onto a value that implements a
- // Setter interface will NOT reset the value to its zero state. This allows
- // the value to decide by itself how to be unmarshalled.
- //
- // For example:
- //
- // type MyString string
- //
- // func (s *MyString) SetBSON(raw bson.Raw) error {
- // return raw.Unmarshal(s)
- // }
- //
- type Setter interface {
- SetBSON(raw Raw) error
- }
- // ErrSetZero may be returned from a SetBSON method to have the value set to
- // its respective zero value. When used in pointer values, this will set the
- // field to nil rather than to the pre-allocated value.
- var ErrSetZero = errors.New("set to zero")
- // M is a convenient alias for a map[string]interface{} map, useful for
- // dealing with BSON in a native way. For instance:
- //
- // bson.M{"a": 1, "b": true}
- //
- // There's no special handling for this type in addition to what's done anyway
- // for an equivalent map type. Elements in the map will be dumped in an
- // undefined ordered. See also the bson.D type for an ordered alternative.
- type M map[string]interface{}
- // D represents a BSON document containing ordered elements. For example:
- //
- // bson.D{{"a", 1}, {"b", true}}
- //
- // In some situations, such as when creating indexes for MongoDB, the order in
- // which the elements are defined is important. If the order is not important,
- // using a map is generally more comfortable. See bson.M and bson.RawD.
- type D []DocElem
- // DocElem is an element of the bson.D document representation.
- type DocElem struct {
- Name string
- Value interface{}
- }
- // Map returns a map out of the ordered element name/value pairs in d.
- func (d D) Map() (m M) {
- m = make(M, len(d))
- for _, item := range d {
- m[item.Name] = item.Value
- }
- return m
- }
- // The Raw type represents raw unprocessed BSON documents and elements.
- // Kind is the kind of element as defined per the BSON specification, and
- // Data is the raw unprocessed data for the respective element.
- // Using this type it is possible to unmarshal or marshal values partially.
- //
- // Relevant documentation:
- //
- // http://bsonspec.org/#/specification
- //
- type Raw struct {
- Kind byte
- Data []byte
- }
- // RawD represents a BSON document containing raw unprocessed elements.
- // This low-level representation may be useful when lazily processing
- // documents of uncertain content, or when manipulating the raw content
- // documents in general.
- type RawD []RawDocElem
- // RawDocElem elements of RawD type.
- type RawDocElem struct {
- Name string
- Value Raw
- }
- // ObjectId is a unique ID identifying a BSON value. It must be exactly 12 bytes
- // long. MongoDB objects by default have such a property set in their "_id"
- // property.
- //
- // http://www.mongodb.org/display/DOCS/Object+Ids
- type ObjectId string
- // ObjectIdHex returns an ObjectId from the provided hex representation.
- // Calling this function with an invalid hex representation will
- // cause a runtime panic. See the IsObjectIdHex function.
- func ObjectIdHex(s string) ObjectId {
- d, err := hex.DecodeString(s)
- if err != nil || len(d) != 12 {
- panic(fmt.Sprintf("invalid input to ObjectIdHex: %q", s))
- }
- return ObjectId(d)
- }
- // IsObjectIdHex returns whether s is a valid hex representation of
- // an ObjectId. See the ObjectIdHex function.
- func IsObjectIdHex(s string) bool {
- if len(s) != 24 {
- return false
- }
- _, err := hex.DecodeString(s)
- return err == nil
- }
- // objectIdCounter is atomically incremented when generating a new ObjectId
- // using NewObjectId() function. It's used as a counter part of an id.
- var objectIdCounter = readRandomUint32()
- // readRandomUint32 returns a random objectIdCounter.
- func readRandomUint32() uint32 {
- var b [4]byte
- _, err := io.ReadFull(rand.Reader, b[:])
- if err != nil {
- panic(fmt.Errorf("cannot read random object id: %v", err))
- }
- return uint32((uint32(b[0]) << 0) | (uint32(b[1]) << 8) | (uint32(b[2]) << 16) | (uint32(b[3]) << 24))
- }
- // machineId stores machine id generated once and used in subsequent calls
- // to NewObjectId function.
- var machineId = readMachineId()
- var processId = os.Getpid()
- // readMachineId generates and returns a machine id.
- // If this function fails to get the hostname it will cause a runtime error.
- func readMachineId() []byte {
- var sum [3]byte
- id := sum[:]
- hostname, err1 := os.Hostname()
- if err1 != nil {
- _, err2 := io.ReadFull(rand.Reader, id)
- if err2 != nil {
- panic(fmt.Errorf("cannot get hostname: %v; %v", err1, err2))
- }
- return id
- }
- hw := md5.New()
- hw.Write([]byte(hostname))
- copy(id, hw.Sum(nil))
- return id
- }
- // NewObjectId returns a new unique ObjectId.
- func NewObjectId() ObjectId {
- var b [12]byte
- // Timestamp, 4 bytes, big endian
- binary.BigEndian.PutUint32(b[:], uint32(time.Now().Unix()))
- // Machine, first 3 bytes of md5(hostname)
- b[4] = machineId[0]
- b[5] = machineId[1]
- b[6] = machineId[2]
- // Pid, 2 bytes, specs don't specify endianness, but we use big endian.
- b[7] = byte(processId >> 8)
- b[8] = byte(processId)
- // Increment, 3 bytes, big endian
- i := atomic.AddUint32(&objectIdCounter, 1)
- b[9] = byte(i >> 16)
- b[10] = byte(i >> 8)
- b[11] = byte(i)
- return ObjectId(b[:])
- }
- // NewObjectIdWithTime returns a dummy ObjectId with the timestamp part filled
- // with the provided number of seconds from epoch UTC, and all other parts
- // filled with zeroes. It's not safe to insert a document with an id generated
- // by this method, it is useful only for queries to find documents with ids
- // generated before or after the specified timestamp.
- func NewObjectIdWithTime(t time.Time) ObjectId {
- var b [12]byte
- binary.BigEndian.PutUint32(b[:4], uint32(t.Unix()))
- return ObjectId(string(b[:]))
- }
- // String returns a hex string representation of the id.
- // Example: ObjectIdHex("4d88e15b60f486e428412dc9").
- func (id ObjectId) String() string {
- return fmt.Sprintf(`ObjectIdHex("%x")`, string(id))
- }
- // Hex returns a hex representation of the ObjectId.
- func (id ObjectId) Hex() string {
- return hex.EncodeToString([]byte(id))
- }
- // MarshalJSON turns a bson.ObjectId into a json.Marshaller.
- func (id ObjectId) MarshalJSON() ([]byte, error) {
- return []byte(fmt.Sprintf(`"%x"`, string(id))), nil
- }
- var nullBytes = []byte("null")
- // UnmarshalJSON turns *bson.ObjectId into a json.Unmarshaller.
- func (id *ObjectId) UnmarshalJSON(data []byte) error {
- if len(data) > 0 && (data[0] == '{' || data[0] == 'O') {
- var v struct {
- Id json.RawMessage `json:"$oid"`
- Func struct {
- Id json.RawMessage
- } `json:"$oidFunc"`
- }
- err := jdec(data, &v)
- if err == nil {
- if len(v.Id) > 0 {
- data = []byte(v.Id)
- } else {
- data = []byte(v.Func.Id)
- }
- }
- }
- if len(data) == 2 && data[0] == '"' && data[1] == '"' || bytes.Equal(data, nullBytes) {
- *id = ""
- return nil
- }
- if len(data) != 26 || data[0] != '"' || data[25] != '"' {
- return fmt.Errorf("invalid ObjectId in JSON: %s", string(data))
- }
- var buf [12]byte
- _, err := hex.Decode(buf[:], data[1:25])
- if err != nil {
- return fmt.Errorf("invalid ObjectId in JSON: %s (%s)", string(data), err)
- }
- *id = ObjectId(string(buf[:]))
- return nil
- }
- // MarshalText turns bson.ObjectId into an encoding.TextMarshaler.
- func (id ObjectId) MarshalText() ([]byte, error) {
- return []byte(fmt.Sprintf("%x", string(id))), nil
- }
- // UnmarshalText turns *bson.ObjectId into an encoding.TextUnmarshaler.
- func (id *ObjectId) UnmarshalText(data []byte) error {
- if len(data) == 1 && data[0] == ' ' || len(data) == 0 {
- *id = ""
- return nil
- }
- if len(data) != 24 {
- return fmt.Errorf("invalid ObjectId: %s", data)
- }
- var buf [12]byte
- _, err := hex.Decode(buf[:], data[:])
- if err != nil {
- return fmt.Errorf("invalid ObjectId: %s (%s)", data, err)
- }
- *id = ObjectId(string(buf[:]))
- return nil
- }
- // Valid returns true if id is valid. A valid id must contain exactly 12 bytes.
- func (id ObjectId) Valid() bool {
- return len(id) == 12
- }
- // byteSlice returns byte slice of id from start to end.
- // Calling this function with an invalid id will cause a runtime panic.
- func (id ObjectId) byteSlice(start, end int) []byte {
- if len(id) != 12 {
- panic(fmt.Sprintf("invalid ObjectId: %q", string(id)))
- }
- return []byte(string(id)[start:end])
- }
- // Time returns the timestamp part of the id.
- // It's a runtime error to call this method with an invalid id.
- func (id ObjectId) Time() time.Time {
- // First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch.
- secs := int64(binary.BigEndian.Uint32(id.byteSlice(0, 4)))
- return time.Unix(secs, 0)
- }
- // Machine returns the 3-byte machine id part of the id.
- // It's a runtime error to call this method with an invalid id.
- func (id ObjectId) Machine() []byte {
- return id.byteSlice(4, 7)
- }
- // Pid returns the process id part of the id.
- // It's a runtime error to call this method with an invalid id.
- func (id ObjectId) Pid() uint16 {
- return binary.BigEndian.Uint16(id.byteSlice(7, 9))
- }
- // Counter returns the incrementing value part of the id.
- // It's a runtime error to call this method with an invalid id.
- func (id ObjectId) Counter() int32 {
- b := id.byteSlice(9, 12)
- // Counter is stored as big-endian 3-byte value
- return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]))
- }
- // The Symbol type is similar to a string and is used in languages with a
- // distinct symbol type.
- type Symbol string
- // Now returns the current time with millisecond precision. MongoDB stores
- // timestamps with the same precision, so a Time returned from this method
- // will not change after a roundtrip to the database. That's the only reason
- // why this function exists. Using the time.Now function also works fine
- // otherwise.
- func Now() time.Time {
- return time.Unix(0, time.Now().UnixNano()/1e6*1e6)
- }
- // MongoTimestamp is a special internal type used by MongoDB that for some
- // strange reason has its own datatype defined in BSON.
- type MongoTimestamp int64
- // Time returns the time part of ts which is stored with second precision.
- func (ts MongoTimestamp) Time() time.Time {
- return time.Unix(int64(uint64(ts)>>32), 0)
- }
- // Counter returns the counter part of ts.
- func (ts MongoTimestamp) Counter() uint32 {
- return uint32(ts)
- }
- // NewMongoTimestamp creates a timestamp using the given
- // date `t` (with second precision) and counter `c` (unique for `t`).
- //
- // Returns an error if time `t` is not between 1970-01-01T00:00:00Z
- // and 2106-02-07T06:28:15Z (inclusive).
- //
- // Note that two MongoTimestamps should never have the same (time, counter) combination:
- // the caller must ensure the counter `c` is increased if creating multiple MongoTimestamp
- // values for the same time `t` (ignoring fractions of seconds).
- func NewMongoTimestamp(t time.Time, c uint32) (MongoTimestamp, error) {
- u := t.Unix()
- if u < 0 || u > math.MaxUint32 {
- return -1, errors.New("invalid value for time")
- }
- i := int64(u<<32 | int64(c))
- return MongoTimestamp(i), nil
- }
- type orderKey int64
- // MaxKey is a special value that compares higher than all other possible BSON
- // values in a MongoDB database.
- var MaxKey = orderKey(1<<63 - 1)
- // MinKey is a special value that compares lower than all other possible BSON
- // values in a MongoDB database.
- var MinKey = orderKey(-1 << 63)
- type undefined struct{}
- // Undefined represents the undefined BSON value.
- var Undefined undefined
- // Binary is a representation for non-standard binary values. Any kind should
- // work, but the following are known as of this writing:
- //
- // 0x00 - Generic. This is decoded as []byte(data), not Binary{0x00, data}.
- // 0x01 - Function (!?)
- // 0x02 - Obsolete generic.
- // 0x03 - UUID
- // 0x05 - MD5
- // 0x80 - User defined.
- //
- type Binary struct {
- Kind byte
- Data []byte
- }
- // RegEx represents a regular expression. The Options field may contain
- // individual characters defining the way in which the pattern should be
- // applied, and must be sorted. Valid options as of this writing are 'i' for
- // case insensitive matching, 'm' for multi-line matching, 'x' for verbose
- // mode, 'l' to make \w, \W, and similar be locale-dependent, 's' for dot-all
- // mode (a '.' matches everything), and 'u' to make \w, \W, and similar match
- // unicode. The value of the Options parameter is not verified before being
- // marshaled into the BSON format.
- type RegEx struct {
- Pattern string
- Options string
- }
- // JavaScript is a type that holds JavaScript code. If Scope is non-nil, it
- // will be marshaled as a mapping from identifiers to values that may be
- // used when evaluating the provided Code.
- type JavaScript struct {
- Code string
- Scope interface{}
- }
- // DBPointer refers to a document id in a namespace.
- //
- // This type is deprecated in the BSON specification and should not be used
- // except for backwards compatibility with ancient applications.
- type DBPointer struct {
- Namespace string
- Id ObjectId
- }
- const initialBufferSize = 64
- func handleErr(err *error) {
- if r := recover(); r != nil {
- if _, ok := r.(runtime.Error); ok {
- panic(r)
- } else if _, ok := r.(externalPanic); ok {
- panic(r)
- } else if s, ok := r.(string); ok {
- *err = errors.New(s)
- } else if e, ok := r.(error); ok {
- *err = e
- } else {
- panic(r)
- }
- }
- }
- // Marshal serializes the in value, which may be a map or a struct value.
- // In the case of struct values, only exported fields will be serialized,
- // and the order of serialized fields will match that of the struct itself.
- // The lowercased field name is used as the key for each exported field,
- // but this behavior may be changed using the respective field tag.
- // The tag may also contain flags to tweak the marshalling behavior for
- // the field. The tag formats accepted are:
- //
- // "[<key>][,<flag1>[,<flag2>]]"
- //
- // `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
- //
- // The following flags are currently supported:
- //
- // 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 int64 value as an int32, if that's feasible
- // while preserving the numeric value.
- //
- // 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.
- //
- // Some examples:
- //
- // 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"
- // }
- //
- func Marshal(in interface{}) (out []byte, err error) {
- return MarshalBuffer(in, make([]byte, 0, initialBufferSize))
- }
- // MarshalBuffer behaves the same way as Marshal, except that instead of
- // allocating a new byte slice it tries to use the received byte slice and
- // only allocates more memory if necessary to fit the marshaled value.
- func MarshalBuffer(in interface{}, buf []byte) (out []byte, err error) {
- defer handleErr(&err)
- e := &encoder{buf}
- e.addDoc(reflect.ValueOf(in))
- return e.out, nil
- }
- // Unmarshal deserializes data from in into the out value. The out value
- // must be a map, a pointer to a struct, or a pointer to a bson.D value.
- // In the case of struct values, only exported fields will be deserialized.
- // The lowercased field name is used as the key for each exported field,
- // but this behavior may be changed using the respective field tag.
- // The tag may also contain flags to tweak the marshalling behavior for
- // the field. The tag formats accepted are:
- //
- // "[<key>][,<flag1>[,<flag2>]]"
- //
- // `(...) bson:"[<key>][,<flag1>[,<flag2>]]" (...)`
- //
- // The following flags are currently supported during unmarshal (see the
- // Marshal method for other flags):
- //
- // inline Inline the field, which must be a struct or a map.
- // Inlined structs are handled as if its fields were part
- // of the outer struct. An inlined map causes keys that do
- // not match any other struct field to be inserted in the
- // map rather than being discarded as usual.
- //
- // The target field or element types of out may not necessarily match
- // the BSON values of the provided data. The following conversions are
- // made automatically:
- //
- // - Numeric types are converted if at least the integer part of the
- // value would be preserved correctly
- // - Bools are converted to numeric types as 1 or 0
- // - Numeric types are converted to bools as true if not 0 or false otherwise
- // - Binary and string BSON data is converted to a string, array or byte slice
- //
- // If the value would not fit the type and cannot be converted, it's
- // silently skipped.
- //
- // Pointer values are initialized when necessary.
- func Unmarshal(in []byte, out interface{}) (err error) {
- if raw, ok := out.(*Raw); ok {
- raw.Kind = 3
- raw.Data = in
- return nil
- }
- defer handleErr(&err)
- v := reflect.ValueOf(out)
- switch v.Kind() {
- case reflect.Ptr:
- fallthrough
- case reflect.Map:
- d := newDecoder(in)
- d.readDocTo(v)
- if d.i < len(d.in) {
- return errors.New("document is corrupted")
- }
- case reflect.Struct:
- return errors.New("unmarshal can't deal with struct values. Use a pointer")
- default:
- return errors.New("unmarshal needs a map or a pointer to a struct")
- }
- return nil
- }
- // Unmarshal deserializes raw into the out value. If the out value type
- // is not compatible with raw, a *bson.TypeError is returned.
- //
- // See the Unmarshal function documentation for more details on the
- // unmarshalling process.
- func (raw Raw) Unmarshal(out interface{}) (err error) {
- defer handleErr(&err)
- v := reflect.ValueOf(out)
- switch v.Kind() {
- case reflect.Ptr:
- v = v.Elem()
- fallthrough
- case reflect.Map:
- d := newDecoder(raw.Data)
- good := d.readElemTo(v, raw.Kind)
- if !good {
- return &TypeError{v.Type(), raw.Kind}
- }
- case reflect.Struct:
- return errors.New("raw Unmarshal can't deal with struct values. Use a pointer")
- default:
- return errors.New("raw Unmarshal needs a map or a valid pointer")
- }
- return nil
- }
- // TypeError store details for type error occuring
- // during unmarshaling
- type TypeError struct {
- Type reflect.Type
- Kind byte
- }
- func (e *TypeError) Error() string {
- return fmt.Sprintf("BSON kind 0x%02x isn't compatible with type %s", e.Kind, e.Type.String())
- }
- // --------------------------------------------------------------------------
- // Maintain a mapping of keys to structure field indexes
- type structInfo struct {
- FieldsMap map[string]fieldInfo
- FieldsList []fieldInfo
- InlineMap int
- Zero reflect.Value
- }
- type fieldInfo struct {
- Key string
- Num int
- OmitEmpty bool
- MinSize bool
- Inline []int
- }
- var structMap = make(map[reflect.Type]*structInfo)
- var structMapMutex sync.RWMutex
- type externalPanic string
- func (e externalPanic) String() string {
- return string(e)
- }
- func getStructInfo(st reflect.Type) (*structInfo, error) {
- structMapMutex.RLock()
- sinfo, found := structMap[st]
- structMapMutex.RUnlock()
- if found {
- return sinfo, nil
- }
- n := st.NumField()
- fieldsMap := make(map[string]fieldInfo)
- fieldsList := make([]fieldInfo, 0, n)
- inlineMap := -1
- for i := 0; i != n; i++ {
- field := st.Field(i)
- if field.PkgPath != "" && !field.Anonymous {
- continue // Private field
- }
- info := fieldInfo{Num: i}
- tag := field.Tag.Get("bson")
- // Fall-back to JSON struct tag, if feature flag is set.
- if tag == "" && useJSONTagFallback {
- tag = field.Tag.Get("json")
- }
- // If there's no bson/json tag available.
- if tag == "" {
- // If there's no tag, and also no tag: value splits (i.e. no colon)
- // then assume the entire tag is the value
- if strings.Index(string(field.Tag), ":") < 0 {
- tag = string(field.Tag)
- }
- }
- if tag == "-" {
- continue
- }
- inline := false
- fields := strings.Split(tag, ",")
- if len(fields) > 1 {
- for _, flag := range fields[1:] {
- switch flag {
- case "omitempty":
- info.OmitEmpty = true
- case "minsize":
- info.MinSize = true
- case "inline":
- inline = true
- default:
- msg := fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)
- panic(externalPanic(msg))
- }
- }
- tag = fields[0]
- }
- if inline {
- switch field.Type.Kind() {
- case reflect.Map:
- if inlineMap >= 0 {
- return nil, errors.New("Multiple ,inline maps in struct " + st.String())
- }
- if field.Type.Key() != reflect.TypeOf("") {
- return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
- }
- inlineMap = info.Num
- case reflect.Ptr:
- // allow only pointer to struct
- if kind := field.Type.Elem().Kind(); kind != reflect.Struct {
- return nil, errors.New("Option ,inline allows a pointer only to a struct, was given pointer to " + kind.String())
- }
- field.Type = field.Type.Elem()
- fallthrough
- case reflect.Struct:
- sinfo, err := getStructInfo(field.Type)
- if err != nil {
- return nil, err
- }
- for _, finfo := range sinfo.FieldsList {
- if _, found := fieldsMap[finfo.Key]; found {
- msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
- return nil, errors.New(msg)
- }
- if finfo.Inline == nil {
- finfo.Inline = []int{i, finfo.Num}
- } else {
- finfo.Inline = append([]int{i}, finfo.Inline...)
- }
- fieldsMap[finfo.Key] = finfo
- fieldsList = append(fieldsList, finfo)
- }
- default:
- panic("Option ,inline needs a struct value or a pointer to a struct or map field")
- }
- continue
- }
- if tag != "" {
- info.Key = tag
- } else {
- info.Key = strings.ToLower(field.Name)
- }
- if _, found = fieldsMap[info.Key]; found {
- msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
- return nil, errors.New(msg)
- }
- fieldsList = append(fieldsList, info)
- fieldsMap[info.Key] = info
- }
- sinfo = &structInfo{
- fieldsMap,
- fieldsList,
- inlineMap,
- reflect.New(st).Elem(),
- }
- structMapMutex.Lock()
- structMap[st] = sinfo
- structMapMutex.Unlock()
- return sinfo, nil
- }
|