1055 lines
23 KiB
Go
1055 lines
23 KiB
Go
// 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.
|
|
// gobson - BSON library for Go.
|
|
|
|
package bson
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"net/url"
|
|
"reflect"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type decoder struct {
|
|
in []byte
|
|
i int
|
|
docType reflect.Type
|
|
}
|
|
|
|
var typeM = reflect.TypeOf(M{})
|
|
|
|
func newDecoder(in []byte) *decoder {
|
|
return &decoder{in, 0, typeM}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Some helper functions.
|
|
|
|
func corrupted() {
|
|
panic("Document is corrupted")
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Unmarshaling of documents.
|
|
|
|
const (
|
|
setterUnknown = iota
|
|
setterNone
|
|
setterType
|
|
setterAddr
|
|
)
|
|
|
|
var setterStyles map[reflect.Type]int
|
|
var setterIface reflect.Type
|
|
var setterMutex sync.RWMutex
|
|
|
|
func init() {
|
|
var iface Setter
|
|
setterIface = reflect.TypeOf(&iface).Elem()
|
|
setterStyles = make(map[reflect.Type]int)
|
|
}
|
|
|
|
func setterStyle(outt reflect.Type) int {
|
|
setterMutex.RLock()
|
|
style := setterStyles[outt]
|
|
setterMutex.RUnlock()
|
|
if style != setterUnknown {
|
|
return style
|
|
}
|
|
|
|
setterMutex.Lock()
|
|
defer setterMutex.Unlock()
|
|
if outt.Implements(setterIface) {
|
|
style = setterType
|
|
} else if reflect.PtrTo(outt).Implements(setterIface) {
|
|
style = setterAddr
|
|
} else {
|
|
style = setterNone
|
|
}
|
|
setterStyles[outt] = style
|
|
return style
|
|
}
|
|
|
|
func getSetter(outt reflect.Type, out reflect.Value) Setter {
|
|
style := setterStyle(outt)
|
|
if style == setterNone {
|
|
return nil
|
|
}
|
|
if style == setterAddr {
|
|
if !out.CanAddr() {
|
|
return nil
|
|
}
|
|
out = out.Addr()
|
|
} else if outt.Kind() == reflect.Ptr && out.IsNil() {
|
|
out.Set(reflect.New(outt.Elem()))
|
|
}
|
|
return out.Interface().(Setter)
|
|
}
|
|
|
|
func clearMap(m reflect.Value) {
|
|
var none reflect.Value
|
|
for _, k := range m.MapKeys() {
|
|
m.SetMapIndex(k, none)
|
|
}
|
|
}
|
|
|
|
func (d *decoder) readDocTo(out reflect.Value) {
|
|
var elemType reflect.Type
|
|
outt := out.Type()
|
|
outk := outt.Kind()
|
|
|
|
for {
|
|
if outk == reflect.Ptr && out.IsNil() {
|
|
out.Set(reflect.New(outt.Elem()))
|
|
}
|
|
if setter := getSetter(outt, out); setter != nil {
|
|
raw := d.readRaw(ElementDocument)
|
|
err := setter.SetBSON(raw)
|
|
if _, ok := err.(*TypeError); err != nil && !ok {
|
|
panic(err)
|
|
}
|
|
return
|
|
}
|
|
if outk == reflect.Ptr {
|
|
out = out.Elem()
|
|
outt = out.Type()
|
|
outk = out.Kind()
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
|
|
var fieldsMap map[string]fieldInfo
|
|
var inlineMap reflect.Value
|
|
if outt == typeRaw {
|
|
out.Set(reflect.ValueOf(d.readRaw(ElementDocument)))
|
|
return
|
|
}
|
|
|
|
origout := out
|
|
if outk == reflect.Interface {
|
|
if d.docType.Kind() == reflect.Map {
|
|
mv := reflect.MakeMap(d.docType)
|
|
out.Set(mv)
|
|
out = mv
|
|
} else {
|
|
dv := reflect.New(d.docType).Elem()
|
|
out.Set(dv)
|
|
out = dv
|
|
}
|
|
outt = out.Type()
|
|
outk = outt.Kind()
|
|
}
|
|
|
|
docType := d.docType
|
|
keyType := typeString
|
|
convertKey := false
|
|
switch outk {
|
|
case reflect.Map:
|
|
keyType = outt.Key()
|
|
if keyType != typeString {
|
|
convertKey = true
|
|
}
|
|
elemType = outt.Elem()
|
|
if elemType == typeIface {
|
|
d.docType = outt
|
|
}
|
|
if out.IsNil() {
|
|
out.Set(reflect.MakeMap(out.Type()))
|
|
} else if out.Len() > 0 {
|
|
clearMap(out)
|
|
}
|
|
case reflect.Struct:
|
|
sinfo, err := getStructInfo(out.Type())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fieldsMap = sinfo.FieldsMap
|
|
out.Set(sinfo.Zero)
|
|
if sinfo.InlineMap != -1 {
|
|
inlineMap = out.Field(sinfo.InlineMap)
|
|
if !inlineMap.IsNil() && inlineMap.Len() > 0 {
|
|
clearMap(inlineMap)
|
|
}
|
|
elemType = inlineMap.Type().Elem()
|
|
if elemType == typeIface {
|
|
d.docType = inlineMap.Type()
|
|
}
|
|
}
|
|
case reflect.Slice:
|
|
switch outt.Elem() {
|
|
case typeDocElem:
|
|
origout.Set(d.readDocElems(outt))
|
|
return
|
|
case typeRawDocElem:
|
|
origout.Set(d.readRawDocElems(outt))
|
|
return
|
|
}
|
|
fallthrough
|
|
default:
|
|
panic("Unsupported document type for unmarshalling: " + out.Type().String())
|
|
}
|
|
|
|
end := int(d.readInt32())
|
|
end += d.i - 4
|
|
if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
|
|
corrupted()
|
|
}
|
|
for d.in[d.i] != '\x00' {
|
|
kind := d.readByte()
|
|
name := d.readCStr()
|
|
if d.i >= end {
|
|
corrupted()
|
|
}
|
|
|
|
switch outk {
|
|
case reflect.Map:
|
|
e := reflect.New(elemType).Elem()
|
|
if d.readElemTo(e, kind) {
|
|
k := reflect.ValueOf(name)
|
|
if convertKey {
|
|
mapKeyType := out.Type().Key()
|
|
mapKeyKind := mapKeyType.Kind()
|
|
|
|
switch mapKeyKind {
|
|
case reflect.Int:
|
|
fallthrough
|
|
case reflect.Int8:
|
|
fallthrough
|
|
case reflect.Int16:
|
|
fallthrough
|
|
case reflect.Int32:
|
|
fallthrough
|
|
case reflect.Int64:
|
|
fallthrough
|
|
case reflect.Uint:
|
|
fallthrough
|
|
case reflect.Uint8:
|
|
fallthrough
|
|
case reflect.Uint16:
|
|
fallthrough
|
|
case reflect.Uint32:
|
|
fallthrough
|
|
case reflect.Uint64:
|
|
fallthrough
|
|
case reflect.Float32:
|
|
fallthrough
|
|
case reflect.Float64:
|
|
parsed := d.parseMapKeyAsFloat(k, mapKeyKind)
|
|
k = reflect.ValueOf(parsed)
|
|
case reflect.String:
|
|
mapKeyType = keyType
|
|
default:
|
|
panic("BSON map must have string or decimal keys. Got: " + outt.String())
|
|
}
|
|
|
|
k = k.Convert(mapKeyType)
|
|
}
|
|
out.SetMapIndex(k, e)
|
|
}
|
|
case reflect.Struct:
|
|
if info, ok := fieldsMap[name]; ok {
|
|
if info.Inline == nil {
|
|
d.readElemTo(out.Field(info.Num), kind)
|
|
} else {
|
|
d.readElemTo(out.FieldByIndex(info.Inline), kind)
|
|
}
|
|
} else if inlineMap.IsValid() {
|
|
if inlineMap.IsNil() {
|
|
inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
|
|
}
|
|
e := reflect.New(elemType).Elem()
|
|
if d.readElemTo(e, kind) {
|
|
inlineMap.SetMapIndex(reflect.ValueOf(name), e)
|
|
}
|
|
} else {
|
|
d.dropElem(kind)
|
|
}
|
|
case reflect.Slice:
|
|
}
|
|
|
|
if d.i >= end {
|
|
corrupted()
|
|
}
|
|
}
|
|
d.i++ // '\x00'
|
|
if d.i != end {
|
|
corrupted()
|
|
}
|
|
d.docType = docType
|
|
}
|
|
|
|
func (decoder) parseMapKeyAsFloat(k reflect.Value, mapKeyKind reflect.Kind) float64 {
|
|
parsed, err := strconv.ParseFloat(k.String(), 64)
|
|
if err != nil {
|
|
panic("Map key is defined to be a decimal type (" + mapKeyKind.String() + ") but got error " +
|
|
err.Error())
|
|
}
|
|
|
|
return parsed
|
|
}
|
|
|
|
func (d *decoder) readArrayDocTo(out reflect.Value) {
|
|
end := int(d.readInt32())
|
|
end += d.i - 4
|
|
if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
|
|
corrupted()
|
|
}
|
|
i := 0
|
|
l := out.Len()
|
|
for d.in[d.i] != '\x00' {
|
|
if i >= l {
|
|
panic("Length mismatch on array field")
|
|
}
|
|
kind := d.readByte()
|
|
for d.i < end && d.in[d.i] != '\x00' {
|
|
d.i++
|
|
}
|
|
if d.i >= end {
|
|
corrupted()
|
|
}
|
|
d.i++
|
|
d.readElemTo(out.Index(i), kind)
|
|
if d.i >= end {
|
|
corrupted()
|
|
}
|
|
i++
|
|
}
|
|
if i != l {
|
|
panic("Length mismatch on array field")
|
|
}
|
|
d.i++ // '\x00'
|
|
if d.i != end {
|
|
corrupted()
|
|
}
|
|
}
|
|
|
|
func (d *decoder) readSliceDoc(t reflect.Type) interface{} {
|
|
tmp := make([]reflect.Value, 0, 8)
|
|
elemType := t.Elem()
|
|
if elemType == typeRawDocElem {
|
|
d.dropElem(ElementArray)
|
|
return reflect.Zero(t).Interface()
|
|
}
|
|
if elemType == typeRaw {
|
|
return d.readSliceOfRaw()
|
|
}
|
|
|
|
end := int(d.readInt32())
|
|
end += d.i - 4
|
|
if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
|
|
corrupted()
|
|
}
|
|
for d.in[d.i] != '\x00' {
|
|
kind := d.readByte()
|
|
for d.i < end && d.in[d.i] != '\x00' {
|
|
d.i++
|
|
}
|
|
if d.i >= end {
|
|
corrupted()
|
|
}
|
|
d.i++
|
|
e := reflect.New(elemType).Elem()
|
|
if d.readElemTo(e, kind) {
|
|
tmp = append(tmp, e)
|
|
}
|
|
if d.i >= end {
|
|
corrupted()
|
|
}
|
|
}
|
|
d.i++ // '\x00'
|
|
if d.i != end {
|
|
corrupted()
|
|
}
|
|
|
|
n := len(tmp)
|
|
slice := reflect.MakeSlice(t, n, n)
|
|
for i := 0; i != n; i++ {
|
|
slice.Index(i).Set(tmp[i])
|
|
}
|
|
return slice.Interface()
|
|
}
|
|
|
|
func BSONElementSize(kind byte, offset int, buffer []byte) (int, error) {
|
|
switch kind {
|
|
case ElementFloat64: // Float64
|
|
return 8, nil
|
|
case ElementJavaScriptWithoutScope: // JavaScript without scope
|
|
fallthrough
|
|
case ElementSymbol: // Symbol
|
|
fallthrough
|
|
case ElementString: // UTF-8 string
|
|
size, err := getSize(offset, buffer)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if size < 1 {
|
|
return 0, errors.New("String size can't be less then one byte")
|
|
}
|
|
size += 4
|
|
if offset+size > len(buffer) {
|
|
return 0, io.ErrUnexpectedEOF
|
|
}
|
|
if buffer[offset+size-1] != 0 {
|
|
return 0, errors.New("Invalid string: non zero-terminated")
|
|
}
|
|
return size, nil
|
|
case ElementArray: // Array
|
|
fallthrough
|
|
case ElementDocument: // Document
|
|
size, err := getSize(offset, buffer)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if size < 5 {
|
|
return 0, errors.New("Declared document size is too small")
|
|
}
|
|
return size, nil
|
|
case ElementBinary: // Binary
|
|
size, err := getSize(offset, buffer)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if size < 0 {
|
|
return 0, errors.New("Binary data size can't be negative")
|
|
}
|
|
return size + 5, nil
|
|
case Element06: // Undefined (obsolete, but still seen in the wild)
|
|
return 0, nil
|
|
case ElementObjectId: // ObjectId
|
|
return 12, nil
|
|
case ElementBool: // Bool
|
|
return 1, nil
|
|
case ElementDatetime: // Timestamp
|
|
return 8, nil
|
|
case ElementNil: // Nil
|
|
return 0, nil
|
|
case ElementRegEx: // RegEx
|
|
end := offset
|
|
for i := 0; i < 2; i++ {
|
|
for end < len(buffer) && buffer[end] != '\x00' {
|
|
end++
|
|
}
|
|
end++
|
|
}
|
|
if end > len(buffer) {
|
|
return 0, io.ErrUnexpectedEOF
|
|
}
|
|
return end - offset, nil
|
|
case ElementDBPointer: // DBPointer
|
|
size, err := getSize(offset, buffer)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if size < 1 {
|
|
return 0, errors.New("String size can't be less then one byte")
|
|
}
|
|
return size + 12 + 4, nil
|
|
case ElementJavaScriptWithScope: // JavaScript with scope
|
|
size, err := getSize(offset, buffer)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if size < 4+5+5 {
|
|
return 0, errors.New("Declared document element is too small")
|
|
}
|
|
return size, nil
|
|
case ElementInt32: // Int32
|
|
return 4, nil
|
|
case ElementTimestamp: // Mongo-specific timestamp
|
|
return 8, nil
|
|
case ElementInt64: // Int64
|
|
return 8, nil
|
|
case ElementDecimal128: // Decimal128
|
|
return 16, nil
|
|
case ElementMaxKey: // Max key
|
|
return 0, nil
|
|
case ElementMinKey: // Min key
|
|
return 0, nil
|
|
default:
|
|
return 0, errors.New(fmt.Sprintf("Unknown element kind (0x%02X)", kind))
|
|
}
|
|
}
|
|
|
|
func (d *decoder) readRaw(kind byte) Raw {
|
|
size, err := BSONElementSize(kind, d.i, d.in)
|
|
if err != nil {
|
|
corrupted()
|
|
}
|
|
if d.i+size > len(d.in) {
|
|
corrupted()
|
|
}
|
|
d.i += size
|
|
return Raw{
|
|
Kind: kind,
|
|
Data: d.in[d.i-size : d.i],
|
|
}
|
|
}
|
|
|
|
func (d *decoder) readSliceOfRaw() interface{} {
|
|
tmp := make([]Raw, 0, 8)
|
|
end := int(d.readInt32())
|
|
end += d.i - 4
|
|
if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
|
|
corrupted()
|
|
}
|
|
for d.in[d.i] != '\x00' {
|
|
kind := d.readByte()
|
|
for d.i < end && d.in[d.i] != '\x00' {
|
|
d.i++
|
|
}
|
|
if d.i >= end {
|
|
corrupted()
|
|
}
|
|
d.i++
|
|
e := d.readRaw(kind)
|
|
tmp = append(tmp, e)
|
|
if d.i >= end {
|
|
corrupted()
|
|
}
|
|
}
|
|
d.i++ // '\x00'
|
|
if d.i != end {
|
|
corrupted()
|
|
}
|
|
return tmp
|
|
}
|
|
|
|
var typeSlice = reflect.TypeOf([]interface{}{})
|
|
var typeIface = typeSlice.Elem()
|
|
|
|
func (d *decoder) readDocElems(typ reflect.Type) reflect.Value {
|
|
docType := d.docType
|
|
d.docType = typ
|
|
slice := make([]DocElem, 0, 8)
|
|
d.readDocWith(func(kind byte, name string) {
|
|
e := DocElem{Name: name}
|
|
v := reflect.ValueOf(&e.Value)
|
|
if d.readElemTo(v.Elem(), kind) {
|
|
slice = append(slice, e)
|
|
}
|
|
})
|
|
slicev := reflect.New(typ).Elem()
|
|
slicev.Set(reflect.ValueOf(slice))
|
|
d.docType = docType
|
|
return slicev
|
|
}
|
|
|
|
func (d *decoder) readRawDocElems(typ reflect.Type) reflect.Value {
|
|
docType := d.docType
|
|
d.docType = typ
|
|
slice := make([]RawDocElem, 0, 8)
|
|
d.readDocWith(func(kind byte, name string) {
|
|
e := RawDocElem{Name: name, Value: d.readRaw(kind)}
|
|
slice = append(slice, e)
|
|
})
|
|
slicev := reflect.New(typ).Elem()
|
|
slicev.Set(reflect.ValueOf(slice))
|
|
d.docType = docType
|
|
return slicev
|
|
}
|
|
|
|
func (d *decoder) readDocWith(f func(kind byte, name string)) {
|
|
end := int(d.readInt32())
|
|
end += d.i - 4
|
|
if end <= d.i || end > len(d.in) || d.in[end-1] != '\x00' {
|
|
corrupted()
|
|
}
|
|
for d.in[d.i] != '\x00' {
|
|
kind := d.readByte()
|
|
name := d.readCStr()
|
|
if d.i >= end {
|
|
corrupted()
|
|
}
|
|
f(kind, name)
|
|
if d.i >= end {
|
|
corrupted()
|
|
}
|
|
}
|
|
d.i++ // '\x00'
|
|
if d.i != end {
|
|
corrupted()
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Unmarshaling of individual elements within a document.
|
|
func (d *decoder) dropElem(kind byte) {
|
|
size, err := BSONElementSize(kind, d.i, d.in)
|
|
if err != nil {
|
|
corrupted()
|
|
}
|
|
if d.i+size > len(d.in) {
|
|
corrupted()
|
|
}
|
|
d.i += size
|
|
}
|
|
|
|
// Attempt to decode an element from the document and put it into out.
|
|
// If the types are not compatible, the returned ok value will be
|
|
// false and out will be unchanged.
|
|
func (d *decoder) readElemTo(out reflect.Value, kind byte) (good bool) {
|
|
outt := out.Type()
|
|
|
|
if outt == typeRaw {
|
|
out.Set(reflect.ValueOf(d.readRaw(kind)))
|
|
return true
|
|
}
|
|
|
|
if outt == typeRawPtr {
|
|
raw := d.readRaw(kind)
|
|
out.Set(reflect.ValueOf(&raw))
|
|
return true
|
|
}
|
|
|
|
if kind == ElementDocument {
|
|
// Delegate unmarshaling of documents.
|
|
outt := out.Type()
|
|
outk := out.Kind()
|
|
switch outk {
|
|
case reflect.Interface, reflect.Ptr, reflect.Struct, reflect.Map:
|
|
d.readDocTo(out)
|
|
return true
|
|
}
|
|
if setterStyle(outt) != setterNone {
|
|
d.readDocTo(out)
|
|
return true
|
|
}
|
|
if outk == reflect.Slice {
|
|
switch outt.Elem() {
|
|
case typeDocElem:
|
|
out.Set(d.readDocElems(outt))
|
|
case typeRawDocElem:
|
|
out.Set(d.readRawDocElems(outt))
|
|
default:
|
|
d.dropElem(kind)
|
|
}
|
|
return true
|
|
}
|
|
d.dropElem(kind)
|
|
return true
|
|
}
|
|
|
|
if setter := getSetter(outt, out); setter != nil {
|
|
err := setter.SetBSON(d.readRaw(kind))
|
|
if err == ErrSetZero {
|
|
out.Set(reflect.Zero(outt))
|
|
return true
|
|
}
|
|
if err == nil {
|
|
return true
|
|
}
|
|
if _, ok := err.(*TypeError); !ok {
|
|
panic(err)
|
|
}
|
|
return false
|
|
}
|
|
|
|
var in interface{}
|
|
|
|
switch kind {
|
|
case ElementFloat64:
|
|
in = d.readFloat64()
|
|
case ElementString:
|
|
in = d.readStr()
|
|
case ElementDocument:
|
|
panic("Can't happen. Handled above.")
|
|
case ElementArray:
|
|
outt := out.Type()
|
|
if setterStyle(outt) != setterNone {
|
|
// Skip the value so its data is handed to the setter below.
|
|
d.dropElem(kind)
|
|
break
|
|
}
|
|
for outt.Kind() == reflect.Ptr {
|
|
outt = outt.Elem()
|
|
}
|
|
switch outt.Kind() {
|
|
case reflect.Array:
|
|
d.readArrayDocTo(out)
|
|
return true
|
|
case reflect.Slice:
|
|
in = d.readSliceDoc(outt)
|
|
default:
|
|
in = d.readSliceDoc(typeSlice)
|
|
}
|
|
case ElementBinary:
|
|
b := d.readBinary()
|
|
if b.Kind == BinaryGeneric || b.Kind == BinaryBinaryOld {
|
|
in = b.Data
|
|
} else {
|
|
in = b
|
|
}
|
|
case Element06: // Undefined (obsolete, but still seen in the wild)
|
|
in = Undefined
|
|
case ElementObjectId:
|
|
in = ObjectId(d.readBytes(12))
|
|
case ElementBool:
|
|
in = d.readBool()
|
|
case ElementDatetime: // Timestamp
|
|
// MongoDB handles timestamps as milliseconds.
|
|
i := d.readInt64()
|
|
if i == -62135596800000 {
|
|
in = time.Time{} // In UTC for convenience.
|
|
} else {
|
|
in = time.Unix(i/1e3, i%1e3*1e6).UTC()
|
|
}
|
|
case ElementNil:
|
|
in = nil
|
|
case ElementRegEx:
|
|
in = d.readRegEx()
|
|
case ElementDBPointer:
|
|
in = DBPointer{Namespace: d.readStr(), Id: ObjectId(d.readBytes(12))}
|
|
case ElementJavaScriptWithoutScope:
|
|
in = JavaScript{Code: d.readStr()}
|
|
case ElementSymbol:
|
|
in = Symbol(d.readStr())
|
|
case ElementJavaScriptWithScope:
|
|
start := d.i
|
|
l := int(d.readInt32())
|
|
js := JavaScript{d.readStr(), make(M)}
|
|
d.readDocTo(reflect.ValueOf(js.Scope))
|
|
if d.i != start+l {
|
|
corrupted()
|
|
}
|
|
in = js
|
|
case ElementInt32:
|
|
in = int(d.readInt32())
|
|
case ElementTimestamp: // Mongo-specific timestamp
|
|
in = MongoTimestamp(d.readInt64())
|
|
case ElementInt64:
|
|
switch out.Type() {
|
|
case typeTimeDuration:
|
|
in = time.Duration(time.Duration(d.readInt64()) * time.Millisecond)
|
|
default:
|
|
in = d.readInt64()
|
|
}
|
|
case ElementDecimal128:
|
|
in = Decimal128{
|
|
l: uint64(d.readInt64()),
|
|
h: uint64(d.readInt64()),
|
|
}
|
|
case ElementMaxKey:
|
|
in = MaxKey
|
|
case ElementMinKey:
|
|
in = MinKey
|
|
default:
|
|
panic(fmt.Sprintf("Unknown element kind (0x%02X)", kind))
|
|
}
|
|
|
|
if in == nil {
|
|
out.Set(reflect.Zero(outt))
|
|
return true
|
|
}
|
|
|
|
outk := outt.Kind()
|
|
|
|
// Dereference and initialize pointer if necessary.
|
|
first := true
|
|
for outk == reflect.Ptr {
|
|
if !out.IsNil() {
|
|
out = out.Elem()
|
|
} else {
|
|
elem := reflect.New(outt.Elem())
|
|
if first {
|
|
// Only set if value is compatible.
|
|
first = false
|
|
defer func(out, elem reflect.Value) {
|
|
if good {
|
|
out.Set(elem)
|
|
}
|
|
}(out, elem)
|
|
} else {
|
|
out.Set(elem)
|
|
}
|
|
out = elem
|
|
}
|
|
outt = out.Type()
|
|
outk = outt.Kind()
|
|
}
|
|
|
|
inv := reflect.ValueOf(in)
|
|
if outt == inv.Type() {
|
|
out.Set(inv)
|
|
return true
|
|
}
|
|
|
|
switch outk {
|
|
case reflect.Interface:
|
|
out.Set(inv)
|
|
return true
|
|
case reflect.String:
|
|
switch inv.Kind() {
|
|
case reflect.String:
|
|
out.SetString(inv.String())
|
|
return true
|
|
case reflect.Slice:
|
|
if b, ok := in.([]byte); ok {
|
|
out.SetString(string(b))
|
|
return true
|
|
}
|
|
case reflect.Int, reflect.Int64:
|
|
if outt == typeJSONNumber {
|
|
out.SetString(strconv.FormatInt(inv.Int(), 10))
|
|
return true
|
|
}
|
|
case reflect.Float64:
|
|
if outt == typeJSONNumber {
|
|
out.SetString(strconv.FormatFloat(inv.Float(), 'f', -1, 64))
|
|
return true
|
|
}
|
|
}
|
|
case reflect.Slice, reflect.Array:
|
|
// Remember, array (0x04) slices are built with the correct
|
|
// element type. If we are here, must be a cross BSON kind
|
|
// conversion (e.g. 0x05 unmarshalling on string).
|
|
if outt.Elem().Kind() != reflect.Uint8 {
|
|
break
|
|
}
|
|
switch inv.Kind() {
|
|
case reflect.String:
|
|
slice := []byte(inv.String())
|
|
out.Set(reflect.ValueOf(slice))
|
|
return true
|
|
case reflect.Slice:
|
|
switch outt.Kind() {
|
|
case reflect.Array:
|
|
reflect.Copy(out, inv)
|
|
case reflect.Slice:
|
|
out.SetBytes(inv.Bytes())
|
|
}
|
|
return true
|
|
}
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
switch inv.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
out.SetInt(inv.Int())
|
|
return true
|
|
case reflect.Float32, reflect.Float64:
|
|
out.SetInt(int64(inv.Float()))
|
|
return true
|
|
case reflect.Bool:
|
|
if inv.Bool() {
|
|
out.SetInt(1)
|
|
} else {
|
|
out.SetInt(0)
|
|
}
|
|
return true
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
panic("can't happen: no uint types in BSON (!?)")
|
|
}
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
switch inv.Kind() {
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
out.SetUint(uint64(inv.Int()))
|
|
return true
|
|
case reflect.Float32, reflect.Float64:
|
|
out.SetUint(uint64(inv.Float()))
|
|
return true
|
|
case reflect.Bool:
|
|
if inv.Bool() {
|
|
out.SetUint(1)
|
|
} else {
|
|
out.SetUint(0)
|
|
}
|
|
return true
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
panic("Can't happen. No uint types in BSON.")
|
|
}
|
|
case reflect.Float32, reflect.Float64:
|
|
switch inv.Kind() {
|
|
case reflect.Float32, reflect.Float64:
|
|
out.SetFloat(inv.Float())
|
|
return true
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
out.SetFloat(float64(inv.Int()))
|
|
return true
|
|
case reflect.Bool:
|
|
if inv.Bool() {
|
|
out.SetFloat(1)
|
|
} else {
|
|
out.SetFloat(0)
|
|
}
|
|
return true
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
panic("Can't happen. No uint types in BSON?")
|
|
}
|
|
case reflect.Bool:
|
|
switch inv.Kind() {
|
|
case reflect.Bool:
|
|
out.SetBool(inv.Bool())
|
|
return true
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
out.SetBool(inv.Int() != 0)
|
|
return true
|
|
case reflect.Float32, reflect.Float64:
|
|
out.SetBool(inv.Float() != 0)
|
|
return true
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
panic("Can't happen. No uint types in BSON?")
|
|
}
|
|
case reflect.Struct:
|
|
if outt == typeURL && inv.Kind() == reflect.String {
|
|
u, err := url.Parse(inv.String())
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
out.Set(reflect.ValueOf(u).Elem())
|
|
return true
|
|
}
|
|
if outt == typeBinary {
|
|
if b, ok := in.([]byte); ok {
|
|
out.Set(reflect.ValueOf(Binary{Data: b}))
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Parsers of basic types.
|
|
|
|
func (d *decoder) readRegEx() RegEx {
|
|
re := RegEx{}
|
|
re.Pattern = d.readCStr()
|
|
re.Options = d.readCStr()
|
|
return re
|
|
}
|
|
|
|
func (d *decoder) readBinary() Binary {
|
|
l := d.readInt32()
|
|
b := Binary{}
|
|
b.Kind = d.readByte()
|
|
if b.Kind == BinaryBinaryOld && l > 4 {
|
|
// Weird obsolete format with redundant length.
|
|
rl := d.readInt32()
|
|
if rl != l-4 {
|
|
corrupted()
|
|
}
|
|
l = rl
|
|
}
|
|
b.Data = d.readBytes(l)
|
|
return b
|
|
}
|
|
|
|
func (d *decoder) readStr() string {
|
|
l := d.readInt32()
|
|
b := d.readBytes(l - 1)
|
|
if d.readByte() != '\x00' {
|
|
corrupted()
|
|
}
|
|
return string(b)
|
|
}
|
|
|
|
func (d *decoder) readCStr() string {
|
|
start := d.i
|
|
end := start
|
|
l := len(d.in)
|
|
for ; end != l; end++ {
|
|
if d.in[end] == '\x00' {
|
|
break
|
|
}
|
|
}
|
|
d.i = end + 1
|
|
if d.i > l {
|
|
corrupted()
|
|
}
|
|
return string(d.in[start:end])
|
|
}
|
|
|
|
func (d *decoder) readBool() bool {
|
|
b := d.readByte()
|
|
if b == 0 {
|
|
return false
|
|
}
|
|
if b == 1 {
|
|
return true
|
|
}
|
|
panic(fmt.Sprintf("encoded boolean must be 1 or 0, found %d", b))
|
|
}
|
|
|
|
func (d *decoder) readFloat64() float64 {
|
|
return math.Float64frombits(uint64(d.readInt64()))
|
|
}
|
|
|
|
func (d *decoder) readInt32() int32 {
|
|
b := d.readBytes(4)
|
|
return int32((uint32(b[0]) << 0) |
|
|
(uint32(b[1]) << 8) |
|
|
(uint32(b[2]) << 16) |
|
|
(uint32(b[3]) << 24))
|
|
}
|
|
|
|
func getSize(offset int, b []byte) (int, error) {
|
|
if offset+4 > len(b) {
|
|
return 0, io.ErrUnexpectedEOF
|
|
}
|
|
return int((uint32(b[offset]) << 0) |
|
|
(uint32(b[offset+1]) << 8) |
|
|
(uint32(b[offset+2]) << 16) |
|
|
(uint32(b[offset+3]) << 24)), nil
|
|
}
|
|
|
|
func (d *decoder) readInt64() int64 {
|
|
b := d.readBytes(8)
|
|
return int64((uint64(b[0]) << 0) |
|
|
(uint64(b[1]) << 8) |
|
|
(uint64(b[2]) << 16) |
|
|
(uint64(b[3]) << 24) |
|
|
(uint64(b[4]) << 32) |
|
|
(uint64(b[5]) << 40) |
|
|
(uint64(b[6]) << 48) |
|
|
(uint64(b[7]) << 56))
|
|
}
|
|
|
|
func (d *decoder) readByte() byte {
|
|
i := d.i
|
|
d.i++
|
|
if d.i > len(d.in) {
|
|
corrupted()
|
|
}
|
|
return d.in[i]
|
|
}
|
|
|
|
func (d *decoder) readBytes(length int32) []byte {
|
|
if length < 0 {
|
|
corrupted()
|
|
}
|
|
start := d.i
|
|
d.i += int(length)
|
|
if d.i < start || d.i > len(d.in) {
|
|
corrupted()
|
|
}
|
|
return d.in[start : start+int(length)]
|
|
}
|