123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- // Copyright (C) MongoDB, Inc. 2017-present.
- //
- // Licensed under the Apache License, Version 2.0 (the "License"); you may
- // not use this file except in compliance with the License. You may obtain
- // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
- package bsoncodec
- import (
- "reflect"
- "sync"
- "go.mongodb.org/mongo-driver/bson/bsonrw"
- "go.mongodb.org/mongo-driver/bson/bsontype"
- )
- var defaultPointerCodec = &PointerCodec{
- ecache: make(map[reflect.Type]ValueEncoder),
- dcache: make(map[reflect.Type]ValueDecoder),
- }
- var _ ValueEncoder = &PointerCodec{}
- var _ ValueDecoder = &PointerCodec{}
- // PointerCodec is the Codec used for pointers.
- type PointerCodec struct {
- ecache map[reflect.Type]ValueEncoder
- dcache map[reflect.Type]ValueDecoder
- l sync.RWMutex
- }
- // NewPointerCodec returns a PointerCodec that has been initialized.
- func NewPointerCodec() *PointerCodec {
- return &PointerCodec{
- ecache: make(map[reflect.Type]ValueEncoder),
- dcache: make(map[reflect.Type]ValueDecoder),
- }
- }
- // EncodeValue handles encoding a pointer by either encoding it to BSON Null if the pointer is nil
- // or looking up an encoder for the type of value the pointer points to.
- func (pc *PointerCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
- if val.Kind() != reflect.Ptr {
- if !val.IsValid() {
- return vw.WriteNull()
- }
- return ValueEncoderError{Name: "PointerCodec.EncodeValue", Kinds: []reflect.Kind{reflect.Ptr}, Received: val}
- }
- if val.IsNil() {
- return vw.WriteNull()
- }
- pc.l.RLock()
- enc, ok := pc.ecache[val.Type()]
- pc.l.RUnlock()
- if ok {
- if enc == nil {
- return ErrNoEncoder{Type: val.Type()}
- }
- return enc.EncodeValue(ec, vw, val.Elem())
- }
- enc, err := ec.LookupEncoder(val.Type().Elem())
- pc.l.Lock()
- pc.ecache[val.Type()] = enc
- pc.l.Unlock()
- if err != nil {
- return err
- }
- return enc.EncodeValue(ec, vw, val.Elem())
- }
- // DecodeValue handles decoding a pointer by looking up a decoder for the type it points to and
- // using that to decode. If the BSON value is Null, this method will set the pointer to nil.
- func (pc *PointerCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
- if !val.CanSet() || val.Kind() != reflect.Ptr {
- return ValueDecoderError{Name: "PointerCodec.DecodeValue", Kinds: []reflect.Kind{reflect.Ptr}, Received: val}
- }
- if vr.Type() == bsontype.Null {
- val.Set(reflect.Zero(val.Type()))
- return vr.ReadNull()
- }
- if val.IsNil() {
- val.Set(reflect.New(val.Type().Elem()))
- }
- pc.l.RLock()
- dec, ok := pc.dcache[val.Type()]
- pc.l.RUnlock()
- if ok {
- if dec == nil {
- return ErrNoDecoder{Type: val.Type()}
- }
- return dec.DecodeValue(dc, vr, val.Elem())
- }
- dec, err := dc.LookupDecoder(val.Type().Elem())
- pc.l.Lock()
- pc.dcache[val.Type()] = dec
- pc.l.Unlock()
- if err != nil {
- return err
- }
- return dec.DecodeValue(dc, vr, val.Elem())
- }
|