Skip to content

Commit e608edc

Browse files
committed
Add v2 of BSON encoder and decoder framework
GODRIVER-494 GODRIVER-495 GODRIVER-496 GODRIVER-497 GODRIVER-498 GODRIVER-499 GODRIVER-500 GODRIVER-501 GODRIVER-516 Change-Id: Ib28a1b564846438ab0f3f9c95c70cea1cb48c8fa
1 parent b75c1ad commit e608edc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+17115
-28
lines changed

bson/array.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ func (a *Array) Validate() (uint32, error) {
7171
}
7272

7373
// Lookup returns the value in the array at the given index or an error if it cannot be found.
74+
//
75+
// TODO: We should fix this to align with the semantics of the *Document type,
76+
// e.g. have Lookup return just a *Value or panic if it's out of bounds and have
77+
// a LookupOK that returns a bool. Although if we want to align with the
78+
// semantics of how Go arrays and slices work, we would not provide a LookupOK
79+
// and force users to use the Len method before hand to avoid panics.
7480
func (a *Array) Lookup(index uint) (*Value, error) {
7581
v, ok := a.doc.ElementAtOK(index)
7682
if !ok {
@@ -334,3 +340,8 @@ func (a *Array) MarshalBSON() ([]byte, error) {
334340
func (a *Array) Iterator() (*ArrayIterator, error) {
335341
return NewArrayIterator(a)
336342
}
343+
344+
// Equal compares this document to another, returning true if they are equal.
345+
func (a *Array) Equal(a2 *Array) bool {
346+
return a.doc.Equal(a2.doc)
347+
}

bson/array_iterator.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ func NewArrayIterator(array *Array) (*ArrayIterator, error) {
2222
return iter, nil
2323
}
2424

25+
func newArrayIterator(a *Array) *ArrayIterator {
26+
return &ArrayIterator{array: a}
27+
}
28+
2529
// Next fetches the next value in the Array, returning whether or not it could be fetched successfully. If true is
2630
// returned, call Value to get the value. If false is returned, call Err to check if an error occurred.
2731
func (iter *ArrayIterator) Next() bool {

bson/benchmark_test.go

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package bson
2+
3+
import "testing"
4+
5+
type encodetest struct {
6+
Field1String string
7+
Field1Int64 int64
8+
Field1Float64 float64
9+
Field2String string
10+
Field2Int64 int64
11+
Field2Float64 float64
12+
Field3String string
13+
Field3Int64 int64
14+
Field3Float64 float64
15+
Field4String string
16+
Field4Int64 int64
17+
Field4Float64 float64
18+
}
19+
20+
type nestedtest1 struct {
21+
Nested nestedtest2
22+
}
23+
24+
type nestedtest2 struct {
25+
Nested nestedtest3
26+
}
27+
28+
type nestedtest3 struct {
29+
Nested nestedtest4
30+
}
31+
32+
type nestedtest4 struct {
33+
Nested nestedtest5
34+
}
35+
36+
type nestedtest5 struct {
37+
Nested nestedtest6
38+
}
39+
40+
type nestedtest6 struct {
41+
Nested nestedtest7
42+
}
43+
44+
type nestedtest7 struct {
45+
Nested nestedtest8
46+
}
47+
48+
type nestedtest8 struct {
49+
Nested nestedtest9
50+
}
51+
52+
type nestedtest9 struct {
53+
Nested nestedtest10
54+
}
55+
56+
type nestedtest10 struct {
57+
Nested nestedtest11
58+
}
59+
60+
type nestedtest11 struct {
61+
Nested encodetest
62+
}
63+
64+
var encodetestInstance = encodetest{
65+
Field1String: "foo",
66+
Field1Int64: 1,
67+
Field1Float64: 3.0,
68+
Field2String: "bar",
69+
Field2Int64: 2,
70+
Field2Float64: 3.1,
71+
Field3String: "baz",
72+
Field3Int64: 3,
73+
Field3Float64: 3.14,
74+
Field4String: "qux",
75+
Field4Int64: 4,
76+
Field4Float64: 3.141,
77+
}
78+
79+
var nestedInstance = nestedtest1{
80+
nestedtest2{
81+
nestedtest3{
82+
nestedtest4{
83+
nestedtest5{
84+
nestedtest6{
85+
nestedtest7{
86+
nestedtest8{
87+
nestedtest9{
88+
nestedtest10{
89+
nestedtest11{
90+
encodetest{
91+
Field1String: "foo",
92+
Field1Int64: 1,
93+
Field1Float64: 3.0,
94+
Field2String: "bar",
95+
Field2Int64: 2,
96+
Field2Float64: 3.1,
97+
Field3String: "baz",
98+
Field3Int64: 3,
99+
Field3Float64: 3.14,
100+
Field4String: "qux",
101+
Field4Int64: 4,
102+
Field4Float64: 3.141,
103+
},
104+
},
105+
},
106+
},
107+
},
108+
},
109+
},
110+
},
111+
},
112+
},
113+
},
114+
}
115+
116+
func BenchmarkEncodingv1(b *testing.B) {
117+
for i := 0; i < b.N; i++ {
118+
_, _ = Marshal(encodetestInstance)
119+
}
120+
}
121+
122+
func BenchmarkEncodingv2(b *testing.B) {
123+
for i := 0; i < b.N; i++ {
124+
_, _ = Marshalv2(encodetestInstance)
125+
}
126+
}
127+
128+
func BenchmarkEncodingv2ToDocument(b *testing.B) {
129+
var buf []byte
130+
for i := 0; i < b.N; i++ {
131+
buf, _ = Marshalv2(encodetestInstance)
132+
_, _ = ReadDocument(buf)
133+
}
134+
}
135+
136+
func BenchmarkEncodingDocument(b *testing.B) {
137+
for i := 0; i < b.N; i++ {
138+
_, _ = MarshalDocument(encodetestInstance)
139+
}
140+
}
141+
142+
func BenchmarkEncodingv1Nested(b *testing.B) {
143+
for i := 0; i < b.N; i++ {
144+
_, _ = Marshal(nestedInstance)
145+
}
146+
}
147+
148+
func BenchmarkEncodingv2Nested(b *testing.B) {
149+
for i := 0; i < b.N; i++ {
150+
_, _ = Marshalv2(nestedInstance)
151+
}
152+
}
153+
154+
func BenchmarkEncodingv2ToDocumentNested(b *testing.B) {
155+
var buf []byte
156+
for i := 0; i < b.N; i++ {
157+
buf, _ = Marshalv2(nestedInstance)
158+
_, _ = ReadDocument(buf)
159+
}
160+
}
161+
162+
func BenchmarkEncodingDocumentNested(b *testing.B) {
163+
for i := 0; i < b.N; i++ {
164+
_, _ = MarshalDocument(nestedInstance)
165+
}
166+
}

bson/bson.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,10 @@ package bson
1616
// `data[n[1]:]`. Since there is no value end byte, an unvalidated document
1717
// could result in parsing errors.
1818
type node [2]uint32
19+
20+
// Zeroer allows custom struct types to implement a report of zero
21+
// state. All struct types that don't implement Zeroer or where IsZero
22+
// returns false are considered to be not zero.
23+
type Zeroer interface {
24+
IsZero() bool
25+
}

bson/bson_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
package bson
88

99
import (
10+
"bytes"
1011
"encoding/binary"
1112
"math"
13+
"reflect"
1214
"testing"
1315
"time"
1416

@@ -129,3 +131,40 @@ func TestTimeRoundTrip(t *testing.T) {
129131
assert.True(t, rtval.Value.IsZero())
130132

131133
}
134+
135+
func TestBasicEncode(t *testing.T) {
136+
for _, tc := range marshalingTestCases {
137+
t.Run(tc.name, func(t *testing.T) {
138+
got := make(writer, 0, 1024)
139+
vw := newValueWriter(&got)
140+
reg := NewRegistryBuilder().Build()
141+
codec, err := reg.Lookup(reflect.TypeOf(tc.val))
142+
noerr(t, err)
143+
err = codec.EncodeValue(EncodeContext{Registry: reg}, vw, tc.val)
144+
noerr(t, err)
145+
146+
if !bytes.Equal(got, tc.want) {
147+
t.Errorf("Bytes are not equal. got %v; want %v", Reader(got), Reader(tc.want))
148+
t.Errorf("Bytes:\n%v\n%v", got, tc.want)
149+
}
150+
})
151+
}
152+
}
153+
154+
func TestBasicDecode(t *testing.T) {
155+
for _, tc := range unmarshalingTestCases {
156+
t.Run(tc.name, func(t *testing.T) {
157+
got := reflect.New(tc.sType).Interface()
158+
vr := newValueReader(tc.data)
159+
reg := NewRegistryBuilder().Build()
160+
codec, err := reg.Lookup(reflect.TypeOf(got))
161+
noerr(t, err)
162+
err = codec.DecodeValue(DecodeContext{Registry: reg}, vr, got)
163+
noerr(t, err)
164+
165+
if !reflect.DeepEqual(got, tc.want) {
166+
t.Errorf("Results do not match. got %+v; want %+v", got, tc.want)
167+
}
168+
})
169+
}
170+
}

0 commit comments

Comments
 (0)