Skip to content

Commit 41ebbc3

Browse files
GODRIVER-2725 Allow setting Encoder and Decoder options on a Client. (#1282)
Co-authored-by: Preston Vasquez <[email protected]>
1 parent 836d408 commit 41ebbc3

31 files changed

+1716
-697
lines changed

bson/bsoncodec/bsoncodec.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,16 +269,16 @@ func (dc *DecodeContext) ZeroStructs() {
269269
dc.zeroStructs = true
270270
}
271271

272-
// DefaultDocumentM will decode empty documents using the primitive.M type. This behavior is restricted to data typed as
273-
// "interface{}" or "map[string]interface{}".
272+
// DefaultDocumentM causes the Decoder to always unmarshal documents into the primitive.M type. This
273+
// behavior is restricted to data typed as "interface{}" or "map[string]interface{}".
274274
//
275275
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.DefaultDocumentM] instead.
276276
func (dc *DecodeContext) DefaultDocumentM() {
277277
dc.defaultDocumentType = reflect.TypeOf(primitive.M{})
278278
}
279279

280-
// DefaultDocumentD will decode empty documents using the primitive.D type. This behavior is restricted to data typed as
281-
// "interface{}" or "map[string]interface{}".
280+
// DefaultDocumentD causes the Decoder to always unmarshal documents into the primitive.D type. This
281+
// behavior is restricted to data typed as "interface{}" or "map[string]interface{}".
282282
//
283283
// Deprecated: Use [go.mongodb.org/mongo-driver/bson.Decoder.DefaultDocumentD] instead.
284284
func (dc *DecodeContext) DefaultDocumentD() {

bson/decoder.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,14 @@ func (d *Decoder) Decode(val interface{}) error {
137137
// Reset will reset the state of the decoder, using the same *DecodeContext used in
138138
// the original construction but using vr for reading.
139139
func (d *Decoder) Reset(vr bsonrw.ValueReader) error {
140+
// TODO:(GODRIVER-2719): Remove error return value.
140141
d.vr = vr
141142
return nil
142143
}
143144

144145
// SetRegistry replaces the current registry of the decoder with r.
145146
func (d *Decoder) SetRegistry(r *bsoncodec.Registry) error {
147+
// TODO:(GODRIVER-2719): Remove error return value.
146148
d.dc.Registry = r
147149
return nil
148150
}
@@ -151,18 +153,19 @@ func (d *Decoder) SetRegistry(r *bsoncodec.Registry) error {
151153
//
152154
// Deprecated: Use the Decoder configuration methods to set the desired unmarshal behavior instead.
153155
func (d *Decoder) SetContext(dc bsoncodec.DecodeContext) error {
156+
// TODO:(GODRIVER-2719): Remove error return value.
154157
d.dc = dc
155158
return nil
156159
}
157160

158-
// DefaultDocumentM will decode empty documents using the primitive.M type. This behavior is restricted to data typed as
159-
// "interface{}" or "map[string]interface{}".
161+
// DefaultDocumentM causes the Decoder to always unmarshal documents into the primitive.M type. This
162+
// behavior is restricted to data typed as "interface{}" or "map[string]interface{}".
160163
func (d *Decoder) DefaultDocumentM() {
161164
d.defaultDocumentM = true
162165
}
163166

164-
// DefaultDocumentD will decode empty documents using the primitive.D type. This behavior is restricted to data typed as
165-
// "interface{}" or "map[string]interface{}".
167+
// DefaultDocumentD causes the Decoder to always unmarshal documents into the primitive.D type. This
168+
// behavior is restricted to data typed as "interface{}" or "map[string]interface{}".
166169
func (d *Decoder) DefaultDocumentD() {
167170
d.defaultDocumentD = true
168171
}

bson/encoder.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type Encoder struct {
4242

4343
// NewEncoder returns a new encoder that uses the DefaultRegistry to write to vw.
4444
func NewEncoder(vw bsonrw.ValueWriter) (*Encoder, error) {
45+
// TODO:(GODRIVER-2719): Remove error return value.
4546
if vw == nil {
4647
return nil, errors.New("cannot create a new Encoder with a nil ValueWriter")
4748
}
@@ -121,12 +122,14 @@ func (e *Encoder) Encode(val interface{}) error {
121122
// Reset will reset the state of the Encoder, using the same *EncodeContext used in
122123
// the original construction but using vw.
123124
func (e *Encoder) Reset(vw bsonrw.ValueWriter) error {
125+
// TODO:(GODRIVER-2719): Remove error return value.
124126
e.vw = vw
125127
return nil
126128
}
127129

128130
// SetRegistry replaces the current registry of the Encoder with r.
129131
func (e *Encoder) SetRegistry(r *bsoncodec.Registry) error {
132+
// TODO:(GODRIVER-2719): Remove error return value.
130133
e.ec.Registry = r
131134
return nil
132135
}
@@ -135,6 +138,7 @@ func (e *Encoder) SetRegistry(r *bsoncodec.Registry) error {
135138
//
136139
// Deprecated: Use the Encoder configuration methods set the desired marshal behavior instead.
137140
func (e *Encoder) SetContext(ec bsoncodec.EncodeContext) error {
141+
// TODO:(GODRIVER-2719): Remove error return value.
138142
e.ec = ec
139143
return nil
140144
}

bson/unmarshal_test.go

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"math/rand"
1111
"reflect"
1212
"testing"
13-
"unsafe"
1413

1514
"go.mongodb.org/mongo-driver/bson/bsoncodec"
1615
"go.mongodb.org/mongo-driver/bson/bsonrw"
@@ -770,49 +769,7 @@ func TestUnmarshalByteSlicesUseDistinctArrays(t *testing.T) {
770769

771770
// Assert that the byte slice in the unmarshaled value does not share any memory
772771
// addresses with the input byte slice.
773-
assertDifferentArrays(t, data, tc.getByteSlice(got))
772+
assert.DifferentAddressRanges(t, data, tc.getByteSlice(got))
774773
})
775774
}
776775
}
777-
778-
// assertDifferentArrays asserts that two byte slices reference distinct memory ranges, meaning
779-
// they reference different underlying byte arrays.
780-
func assertDifferentArrays(t *testing.T, a, b []byte) {
781-
// Find the start and end memory addresses for the underlying byte array for each input byte
782-
// slice.
783-
sliceAddrRange := func(b []byte) (uintptr, uintptr) {
784-
sh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
785-
return sh.Data, sh.Data + uintptr(sh.Cap-1)
786-
}
787-
aStart, aEnd := sliceAddrRange(a)
788-
bStart, bEnd := sliceAddrRange(b)
789-
790-
// If "b" starts after "a" ends or "a" starts after "b" ends, there is no overlap.
791-
if bStart > aEnd || aStart > bEnd {
792-
return
793-
}
794-
795-
// Otherwise, calculate the overlap start and end and print the memory overlap error message.
796-
min := func(a, b uintptr) uintptr {
797-
if a < b {
798-
return a
799-
}
800-
return b
801-
}
802-
max := func(a, b uintptr) uintptr {
803-
if a > b {
804-
return a
805-
}
806-
return b
807-
}
808-
overlapLow := max(aStart, bStart)
809-
overlapHigh := min(aEnd, bEnd)
810-
811-
t.Errorf("Byte slices point to the same the same underlying byte array:\n"+
812-
"\ta addresses:\t%d ... %d\n"+
813-
"\tb addresses:\t%d ... %d\n"+
814-
"\toverlap:\t%d ... %d",
815-
aStart, aEnd,
816-
bStart, bEnd,
817-
overlapLow, overlapHigh)
818-
}

internal/assert/assertion_mongo.go

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright (C) MongoDB, Inc. 2023-present.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
// not use this file except in compliance with the License. You may obtain
5+
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
// assertion_mongo.go contains MongoDB-specific extensions to the "assert"
8+
// package.
9+
10+
package assert
11+
12+
import (
13+
"fmt"
14+
"reflect"
15+
"unsafe"
16+
)
17+
18+
// DifferentAddressRanges asserts that two byte slices reference distinct memory
19+
// address ranges, meaning they reference different underlying byte arrays.
20+
func DifferentAddressRanges(t TestingT, a, b []byte) (ok bool) {
21+
if h, ok := t.(tHelper); ok {
22+
h.Helper()
23+
}
24+
25+
if len(a) == 0 || len(b) == 0 {
26+
return true
27+
}
28+
29+
// Find the start and end memory addresses for the underlying byte array for
30+
// each input byte slice.
31+
sliceAddrRange := func(b []byte) (uintptr, uintptr) {
32+
sh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
33+
return sh.Data, sh.Data + uintptr(sh.Cap-1)
34+
}
35+
aStart, aEnd := sliceAddrRange(a)
36+
bStart, bEnd := sliceAddrRange(b)
37+
38+
// If "b" starts after "a" ends or "a" starts after "b" ends, there is no
39+
// overlap.
40+
if bStart > aEnd || aStart > bEnd {
41+
return true
42+
}
43+
44+
// Otherwise, calculate the overlap start and end and print the memory
45+
// overlap error message.
46+
min := func(a, b uintptr) uintptr {
47+
if a < b {
48+
return a
49+
}
50+
return b
51+
}
52+
max := func(a, b uintptr) uintptr {
53+
if a > b {
54+
return a
55+
}
56+
return b
57+
}
58+
overlapLow := max(aStart, bStart)
59+
overlapHigh := min(aEnd, bEnd)
60+
61+
t.Errorf("Byte slices point to the same underlying byte array:\n"+
62+
"\ta addresses:\t%d ... %d\n"+
63+
"\tb addresses:\t%d ... %d\n"+
64+
"\toverlap:\t%d ... %d",
65+
aStart, aEnd,
66+
bStart, bEnd,
67+
overlapLow, overlapHigh)
68+
69+
return false
70+
}
71+
72+
// EqualBSON asserts that the expected and actual BSON binary values are equal.
73+
// If the values are not equal, it prints both the binary and Extended JSON diff
74+
// of the BSON values. The provided BSON value types must implement the
75+
// fmt.Stringer interface.
76+
func EqualBSON(t TestingT, expected, actual interface{}) bool {
77+
if h, ok := t.(tHelper); ok {
78+
h.Helper()
79+
}
80+
81+
return Equal(t,
82+
expected,
83+
actual,
84+
`expected and actual BSON values do not match
85+
As Extended JSON:
86+
Expected: %s
87+
Actual : %s`,
88+
expected.(fmt.Stringer).String(),
89+
actual.(fmt.Stringer).String())
90+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// Copyright (C) MongoDB, Inc. 2023-present.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License"); you may
4+
// not use this file except in compliance with the License. You may obtain
5+
// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
7+
package assert
8+
9+
import (
10+
"testing"
11+
12+
"go.mongodb.org/mongo-driver/bson"
13+
)
14+
15+
func TestDifferentAddressRanges(t *testing.T) {
16+
t.Parallel()
17+
18+
slice := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
19+
20+
testCases := []struct {
21+
name string
22+
a []byte
23+
b []byte
24+
want bool
25+
}{
26+
{
27+
name: "distinct byte slices",
28+
a: []byte{0, 1, 2, 3},
29+
b: []byte{0, 1, 2, 3},
30+
want: true,
31+
},
32+
{
33+
name: "same byte slice",
34+
a: slice,
35+
b: slice,
36+
want: false,
37+
},
38+
{
39+
name: "whole and subslice",
40+
a: slice,
41+
b: slice[:4],
42+
want: false,
43+
},
44+
{
45+
name: "two subslices",
46+
a: slice[1:2],
47+
b: slice[3:4],
48+
want: false,
49+
},
50+
{
51+
name: "empty",
52+
a: []byte{0, 1, 2, 3},
53+
b: []byte{},
54+
want: true,
55+
},
56+
{
57+
name: "nil",
58+
a: []byte{0, 1, 2, 3},
59+
b: nil,
60+
want: true,
61+
},
62+
}
63+
64+
for _, tc := range testCases {
65+
tc := tc // Capture range variable.
66+
67+
t.Run(tc.name, func(t *testing.T) {
68+
t.Parallel()
69+
70+
got := DifferentAddressRanges(new(testing.T), tc.a, tc.b)
71+
if got != tc.want {
72+
t.Errorf("DifferentAddressRanges(%p, %p) = %v, want %v", tc.a, tc.b, got, tc.want)
73+
}
74+
})
75+
}
76+
}
77+
78+
func TestEqualBSON(t *testing.T) {
79+
t.Parallel()
80+
81+
testCases := []struct {
82+
name string
83+
expected interface{}
84+
actual interface{}
85+
want bool
86+
}{
87+
{
88+
name: "equal bson.Raw",
89+
expected: bson.Raw{5, 0, 0, 0, 0},
90+
actual: bson.Raw{5, 0, 0, 0, 0},
91+
want: true,
92+
},
93+
{
94+
name: "different bson.Raw",
95+
expected: bson.Raw{8, 0, 0, 0, 10, 120, 0, 0},
96+
actual: bson.Raw{5, 0, 0, 0, 0},
97+
want: false,
98+
},
99+
{
100+
name: "invalid bson.Raw",
101+
expected: bson.Raw{99, 99, 99, 99},
102+
actual: bson.Raw{5, 0, 0, 0, 0},
103+
want: false,
104+
},
105+
{
106+
name: "nil bson.Raw",
107+
expected: bson.Raw(nil),
108+
actual: bson.Raw(nil),
109+
want: true,
110+
},
111+
}
112+
113+
for _, tc := range testCases {
114+
tc := tc // Capture range variable.
115+
116+
t.Run(tc.name, func(t *testing.T) {
117+
t.Parallel()
118+
119+
got := EqualBSON(new(testing.T), tc.expected, tc.actual)
120+
if got != tc.want {
121+
t.Errorf("EqualBSON(%#v, %#v) = %v, want %v", tc.expected, tc.actual, got, tc.want)
122+
}
123+
})
124+
}
125+
}

0 commit comments

Comments
 (0)