Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion bson/bson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ import (
"errors"
"net/url"
"reflect"
"strings"
"testing"
"time"
"strings"

"github.com/globalsign/mgo/bson"
. "gopkg.in/check.v1"
Expand Down Expand Up @@ -111,6 +111,10 @@ var sampleItems = []testItemType{
{bson.M{"BSON": []interface{}{"awesome", float64(5.05), 1986}},
"1\x00\x00\x00\x04BSON\x00&\x00\x00\x00\x020\x00\x08\x00\x00\x00" +
"awesome\x00\x011\x00333333\x14@\x102\x00\xc2\x07\x00\x00\x00\x00"},
{bson.M{"slice": []uint8{1, 2}},
"\x13\x00\x00\x00\x05slice\x00\x02\x00\x00\x00\x00\x01\x02\x00"},
{bson.M{"slice": []byte{1, 2}},
"\x13\x00\x00\x00\x05slice\x00\x02\x00\x00\x00\x00\x01\x02\x00"},
}

func (s *S) TestMarshalSampleItems(c *C) {
Expand Down Expand Up @@ -343,6 +347,27 @@ func (s *S) TestOneWayMarshalItems(c *C) {
}
}

// --------------------------------------------------------------------------
// Some ops marshaling operations which would encode []uint8 or []byte in array.

var arrayOpsMarshalItems = []testItemType{
{bson.M{"_": bson.M{"$in": []uint8{1, 2}}},
"\x03_\x00\x1d\x00\x00\x00\x04$in\x00\x13\x00\x00\x00\x100\x00\x01\x00\x00\x00\x101\x00\x02\x00\x00\x00\x00\x00"},
{bson.M{"_": bson.M{"$nin": []uint8{1, 2}}},
"\x03_\x00\x1e\x00\x00\x00\x04$nin\x00\x13\x00\x00\x00\x100\x00\x01\x00\x00\x00\x101\x00\x02\x00\x00\x00\x00\x00"},
{bson.M{"_": bson.M{"$all": []uint8{1, 2}}},
"\x03_\x00\x1e\x00\x00\x00\x04$all\x00\x13\x00\x00\x00\x100\x00\x01\x00\x00\x00\x101\x00\x02\x00\x00\x00\x00\x00"},
}

func (s *S) TestArrayOpsMarshalItems(c *C) {
for i, item := range arrayOpsMarshalItems {
data, err := bson.Marshal(item.obj)
c.Assert(err, IsNil)
c.Assert(string(data), Equals, wrapInDoc(item.data),
Commentf("Failed on item %d", i))
}
}

// --------------------------------------------------------------------------
// Two-way tests for user-defined structures using the samples
// from bsonspec.org.
Expand Down
41 changes: 30 additions & 11 deletions bson/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ var (
typeTimeDuration = reflect.TypeOf(time.Duration(0))
)

var (
// spec for []uint8 or []byte encoding
arrayOps = map[string]bool{
Copy link

@szank szank Mar 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One nitpick here ( sorry! )
IIRC map[string]struct{} could be used here, and that one has an optimised lookup code when you want to only check for the presence of an element.

this would transform the map lookups to

if _, ok := arrayOps[name]; ok {
[...]
}

Which is not as clean. I'll let you make a call here. I haven't benchmarked the code, so I can't tell how much faster it is, but it is the idiomatic go way of doing things FWIW.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the way will cause one more alloc, which can be avoid from previous.

"$in": true,
"$nin": true,
"$all": true,
}
)

const itoaCacheSize = 32

const (
Expand Down Expand Up @@ -423,8 +432,13 @@ func (e *encoder) addElem(name string, v reflect.Value, minSize bool) {
vt := v.Type()
et := vt.Elem()
if et.Kind() == reflect.Uint8 {
e.addElemName(0x05, name)
e.addBinary(0x00, v.Bytes())
if arrayOps[name] {
e.addElemName(0x04, name)
e.addDoc(v)
} else {
e.addElemName(0x05, name)
e.addBinary(0x00, v.Bytes())
}
} else if et == typeDocElem || et == typeRawDocElem {
e.addElemName(0x03, name)
e.addDoc(v)
Expand All @@ -436,16 +450,21 @@ func (e *encoder) addElem(name string, v reflect.Value, minSize bool) {
case reflect.Array:
et := v.Type().Elem()
if et.Kind() == reflect.Uint8 {
e.addElemName(0x05, name)
if v.CanAddr() {
e.addBinary(0x00, v.Slice(0, v.Len()).Interface().([]byte))
if arrayOps[name] {
e.addElemName(0x04, name)
e.addDoc(v)
} else {
n := v.Len()
e.addInt32(int32(n))
e.addBytes(0x00)
for i := 0; i < n; i++ {
el := v.Index(i)
e.addBytes(byte(el.Uint()))
e.addElemName(0x05, name)
if v.CanAddr() {
e.addBinary(0x00, v.Slice(0, v.Len()).Interface().([]byte))
} else {
n := v.Len()
e.addInt32(int32(n))
e.addBytes(0x00)
for i := 0; i < n; i++ {
el := v.Index(i)
e.addBytes(byte(el.Uint()))
}
}
}
} else {
Expand Down