|
@@ -0,0 +1,738 @@
|
|
|
|
+// 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"
|
|
|
|
+ "os"
|
|
|
|
+ "reflect"
|
|
|
|
+ "runtime"
|
|
|
|
+ "strings"
|
|
|
|
+ "sync"
|
|
|
|
+ "sync/atomic"
|
|
|
|
+ "time"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+// --------------------------------------------------------------------------
|
|
|
|
+// The public API.
|
|
|
|
+
|
|
|
|
+// 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)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 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.SetZero 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
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// SetZero 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 SetZero = 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
|
|
|
|
+
|
|
|
|
+// See the 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 uint32 = 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 errors.New(fmt.Sprintf("invalid ObjectId in JSON: %s", string(data)))
|
|
|
|
+ }
|
|
|
|
+ var buf [12]byte
|
|
|
|
+ _, err := hex.Decode(buf[:], data[1:25])
|
|
|
|
+ if err != nil {
|
|
|
|
+ return errors.New(fmt.Sprintf("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
|
|
|
|
+
|
|
|
|
+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) {
|
|
|
|
+ defer handleErr(&err)
|
|
|
|
+ e := &encoder{make([]byte, 0, initialBufferSize)}
|
|
|
|
+ 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)
|
|
|
|
+ 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
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+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")
|
|
|
|
+ if tag == "" && 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.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 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
|
|
|
|
+}
|