pointer_codec.go raw

   1  // Copyright (C) MongoDB, Inc. 2017-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 bsoncodec
   8  
   9  import (
  10  	"reflect"
  11  
  12  	"go.mongodb.org/mongo-driver/bson/bsonrw"
  13  	"go.mongodb.org/mongo-driver/bson/bsontype"
  14  )
  15  
  16  var _ ValueEncoder = &PointerCodec{}
  17  var _ ValueDecoder = &PointerCodec{}
  18  
  19  // PointerCodec is the Codec used for pointers.
  20  //
  21  // Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with the
  22  // PointerCodec registered.
  23  type PointerCodec struct {
  24  	ecache typeEncoderCache
  25  	dcache typeDecoderCache
  26  }
  27  
  28  // NewPointerCodec returns a PointerCodec that has been initialized.
  29  //
  30  // Deprecated: Use [go.mongodb.org/mongo-driver/bson.NewRegistry] to get a registry with the
  31  // PointerCodec registered.
  32  func NewPointerCodec() *PointerCodec {
  33  	return &PointerCodec{}
  34  }
  35  
  36  // EncodeValue handles encoding a pointer by either encoding it to BSON Null if the pointer is nil
  37  // or looking up an encoder for the type of value the pointer points to.
  38  func (pc *PointerCodec) EncodeValue(ec EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
  39  	if val.Kind() != reflect.Ptr {
  40  		if !val.IsValid() {
  41  			return vw.WriteNull()
  42  		}
  43  		return ValueEncoderError{Name: "PointerCodec.EncodeValue", Kinds: []reflect.Kind{reflect.Ptr}, Received: val}
  44  	}
  45  
  46  	if val.IsNil() {
  47  		return vw.WriteNull()
  48  	}
  49  
  50  	typ := val.Type()
  51  	if v, ok := pc.ecache.Load(typ); ok {
  52  		if v == nil {
  53  			return ErrNoEncoder{Type: typ}
  54  		}
  55  		return v.EncodeValue(ec, vw, val.Elem())
  56  	}
  57  	// TODO(charlie): handle concurrent requests for the same type
  58  	enc, err := ec.LookupEncoder(typ.Elem())
  59  	enc = pc.ecache.LoadOrStore(typ, enc)
  60  	if err != nil {
  61  		return err
  62  	}
  63  	return enc.EncodeValue(ec, vw, val.Elem())
  64  }
  65  
  66  // DecodeValue handles decoding a pointer by looking up a decoder for the type it points to and
  67  // using that to decode. If the BSON value is Null, this method will set the pointer to nil.
  68  func (pc *PointerCodec) DecodeValue(dc DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
  69  	if !val.CanSet() || val.Kind() != reflect.Ptr {
  70  		return ValueDecoderError{Name: "PointerCodec.DecodeValue", Kinds: []reflect.Kind{reflect.Ptr}, Received: val}
  71  	}
  72  
  73  	typ := val.Type()
  74  	if vr.Type() == bsontype.Null {
  75  		val.Set(reflect.Zero(typ))
  76  		return vr.ReadNull()
  77  	}
  78  	if vr.Type() == bsontype.Undefined {
  79  		val.Set(reflect.Zero(typ))
  80  		return vr.ReadUndefined()
  81  	}
  82  
  83  	if val.IsNil() {
  84  		val.Set(reflect.New(typ.Elem()))
  85  	}
  86  
  87  	if v, ok := pc.dcache.Load(typ); ok {
  88  		if v == nil {
  89  			return ErrNoDecoder{Type: typ}
  90  		}
  91  		return v.DecodeValue(dc, vr, val.Elem())
  92  	}
  93  	// TODO(charlie): handle concurrent requests for the same type
  94  	dec, err := dc.LookupDecoder(typ.Elem())
  95  	dec = pc.dcache.LoadOrStore(typ, dec)
  96  	if err != nil {
  97  		return err
  98  	}
  99  	return dec.DecodeValue(dc, vr, val.Elem())
 100  }
 101