Skip to content
8 changes: 4 additions & 4 deletions bson/array_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ import (
type arrayCodec struct{}

// EncodeValue is the ValueEncoder for bsoncore.Array values.
func (ac *arrayCodec) EncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Type() != tCoreArray {
return ValueEncoderError{Name: "CoreArrayEncodeValue", Types: []reflect.Type{tCoreArray}, Received: val}
func (ac *arrayCodec) EncodeValue(_ EncodeContext, vw ValueWriter, val any) error {
arr, ok := val.(bsoncore.Array)
if !ok {
return ValueEncoderError{Name: "CoreArrayEncodeValue", Types: []reflect.Type{tCoreArray}, Received: reflect.ValueOf(val)}
}

arr := val.Interface().(bsoncore.Array)
return copyArrayFromBytes(vw, arr)
}

Expand Down
19 changes: 19 additions & 0 deletions bson/bsoncodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,25 @@ func (fn ValueEncoderFunc) EncodeValue(ec EncodeContext, vw ValueWriter, val ref
return fn(ec, vw, val)
}

// defaultValueEncoderFunc is an adapter function that allows a function with
// the correct signature to be used as a ValueEncoder.
type defaultValueEncoderFunc func(EncodeContext, ValueWriter, reflect.Value) error

// EncodeValue implements the ValueEncoder interface.
func (fn defaultValueEncoderFunc) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {
return fn(ec, vw, val)
}

type reflectFreeValueEncoder interface {
EncodeValue(ec EncodeContext, vw ValueWriter, val any) error
}

type reflectFreeValueEncoderFunc func(ec EncodeContext, vw ValueWriter, val any) error

func (fn reflectFreeValueEncoderFunc) EncodeValue(ec EncodeContext, vw ValueWriter, val any) error {
return fn(ec, vw, val)
}

// ValueDecoder is the interface implemented by types that can decode BSON to a provided Go type.
// Implementations should ensure that the value they receive is settable. Similar to ValueEncoderFunc,
// ValueDecoderFunc is provided to allow the use of a function with the correct signature as a
Expand Down
17 changes: 1 addition & 16 deletions bson/byte_slice_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,13 @@ import (
)

// byteSliceCodec is the Codec used for []byte values.
type byteSliceCodec struct {
// encodeNilAsEmpty causes EncodeValue to marshal nil Go byte slices as empty BSON binary values
// instead of BSON null.
encodeNilAsEmpty bool
}
type byteSliceCodec struct{}

// Assert that byteSliceCodec satisfies the typeDecoder interface, which allows it to be
// used by collection type decoders (e.g. map, slice, etc) to set individual values in a
// collection.
var _ typeDecoder = &byteSliceCodec{}

// EncodeValue is the ValueEncoder for []byte.
func (bsc *byteSliceCodec) EncodeValue(ec EncodeContext, vw ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Type() != tByteSlice {
return ValueEncoderError{Name: "ByteSliceEncodeValue", Types: []reflect.Type{tByteSlice}, Received: val}
}
if val.IsNil() && !bsc.encodeNilAsEmpty && !ec.nilByteSliceAsEmpty {
return vw.WriteNull()
}
return vw.WriteBinary(val.Interface().([]byte))
}

func (bsc *byteSliceCodec) decodeType(_ DecodeContext, vr ValueReader, t reflect.Type) (reflect.Value, error) {
if t != tByteSlice {
return emptyValue, ValueDecoderError{
Expand Down
33 changes: 33 additions & 0 deletions bson/codec_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,39 @@ func (c *typeEncoderCache) Clone() *typeEncoderCache {
return cc
}

type typeReflectFreeEncoderCache struct {
cache sync.Map // map[reflect.Type]typeReflectFreeEncoderCache
}

func (c *typeReflectFreeEncoderCache) Store(rt reflect.Type, enc reflectFreeValueEncoder) {
c.cache.Store(rt, enc)
}

func (c *typeReflectFreeEncoderCache) Load(rt reflect.Type) (reflectFreeValueEncoder, bool) {
if v, _ := c.cache.Load(rt); v != nil {
return v.(reflectFreeValueEncoder), true
}
return nil, false
}

func (c *typeReflectFreeEncoderCache) LoadOrStore(rt reflect.Type, enc reflectFreeValueEncoder) reflectFreeValueEncoder {
if v, loaded := c.cache.LoadOrStore(rt, enc); loaded {
enc = v.(reflectFreeValueEncoder)
}
return enc
}

func (c *typeReflectFreeEncoderCache) Clone() *typeReflectFreeEncoderCache {
cc := new(typeReflectFreeEncoderCache)
c.cache.Range(func(k, v interface{}) bool {
if k != nil && v != nil {
cc.cache.Store(k, v)
}
return true
})
return cc
}

type typeDecoderCache struct {
cache sync.Map // map[reflect.Type]ValueDecoder
}
Expand Down
54 changes: 30 additions & 24 deletions bson/default_value_decoders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3414,20 +3414,22 @@ func TestDefaultValueDecoders(t *testing.T) {
// the top-level to decode to registered type when unmarshalling to interface{}

topLevelReg := &Registry{
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
reflectFreeTypeEncoders: new(typeReflectFreeEncoderCache),
}
registerDefaultEncoders(topLevelReg)
registerDefaultDecoders(topLevelReg)
topLevelReg.RegisterTypeMapEntry(Type(0), reflect.TypeOf(M{}))

embeddedReg := &Registry{
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
reflectFreeTypeEncoders: new(typeReflectFreeEncoderCache),
}
registerDefaultEncoders(embeddedReg)
registerDefaultDecoders(embeddedReg)
Expand Down Expand Up @@ -3470,10 +3472,11 @@ func TestDefaultValueDecoders(t *testing.T) {
// type information is not available.

reg := &Registry{
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
reflectFreeTypeEncoders: new(typeReflectFreeEncoderCache),
}
registerDefaultEncoders(reg)
registerDefaultDecoders(reg)
Expand Down Expand Up @@ -3564,10 +3567,11 @@ func TestDefaultValueDecoders(t *testing.T) {

// Use a registry that has all default decoders with the custom interface{} decoder that always errors.
nestedRegistry := &Registry{
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
reflectFreeTypeEncoders: new(typeReflectFreeEncoderCache),
}
registerDefaultDecoders(nestedRegistry)
nestedRegistry.RegisterTypeDecoder(tEmpty, ValueDecoderFunc(emptyInterfaceErrorDecode))
Expand Down Expand Up @@ -3721,10 +3725,11 @@ func TestDefaultValueDecoders(t *testing.T) {
)

reg := &Registry{
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
reflectFreeTypeEncoders: new(typeReflectFreeEncoderCache),
}
registerDefaultDecoders(reg)
reg.RegisterTypeMapEntry(TypeBoolean, reflect.TypeOf(mybool(true)))
Expand Down Expand Up @@ -3795,10 +3800,11 @@ func buildDocument(elems []byte) []byte {

func buildDefaultRegistry() *Registry {
reg := &Registry{
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
typeEncoders: new(typeEncoderCache),
typeDecoders: new(typeDecoderCache),
kindEncoders: new(kindEncoderCache),
kindDecoders: new(kindDecoderCache),
reflectFreeTypeEncoders: new(typeReflectFreeEncoderCache),
}
registerDefaultEncoders(reg)
registerDefaultDecoders(reg)
Expand Down
Loading
Loading