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