Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
10 changes: 0 additions & 10 deletions bson/array_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ import (
// arrayCodec is the Codec used for bsoncore.Array values.
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}
}

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

// DecodeValue is the ValueDecoder for bsoncore.Array values.
func (ac *arrayCodec) DecodeValue(_ DecodeContext, vr ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Type() != tCoreArray {
Expand Down
14 changes: 14 additions & 0 deletions bson/bsoncodec.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,20 @@ func (fn ValueEncoderFunc) EncodeValue(ec EncodeContext, vw ValueWriter, val ref
return fn(ec, vw, val)
}

// reflectFreeValueEncoder is a reflect-free version of ValueEncoder.
type reflectFreeValueEncoder interface {
EncodeValue(ec EncodeContext, vw ValueWriter, val any) error
}

// reflectFreeValueEncoderFunc is an adapter function that allows a function
// with the correct signature to be used as a reflectFreeValueEncoder.
type reflectFreeValueEncoderFunc func(ec EncodeContext, vw ValueWriter, val any) error

// EncodeValue implements the reflectFreeValueEncoder interface.
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 reflectFreeTypeEncoderCache struct {
cache sync.Map // map[reflect.Type]typeReflectFreeEncoderCache
}

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

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

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

func (c *reflectFreeTypeEncoderCache) Clone() *reflectFreeTypeEncoderCache {
cc := new(reflectFreeTypeEncoderCache)
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(reflectFreeTypeEncoderCache),
}
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(reflectFreeTypeEncoderCache),
}
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(reflectFreeTypeEncoderCache),
}
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(reflectFreeTypeEncoderCache),
}
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(reflectFreeTypeEncoderCache),
}
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(reflectFreeTypeEncoderCache),
}
registerDefaultEncoders(reg)
registerDefaultDecoders(reg)
Expand Down
Loading
Loading