decoder.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 bson
   8  
   9  import (
  10  	"errors"
  11  	"fmt"
  12  	"reflect"
  13  	"sync"
  14  
  15  	"go.mongodb.org/mongo-driver/bson/bsoncodec"
  16  	"go.mongodb.org/mongo-driver/bson/bsonrw"
  17  )
  18  
  19  // ErrDecodeToNil is the error returned when trying to decode to a nil value
  20  var ErrDecodeToNil = errors.New("cannot Decode to nil value")
  21  
  22  // This pool is used to keep the allocations of Decoders down. This is only used for the Marshal*
  23  // methods and is not consumable from outside of this package. The Decoders retrieved from this pool
  24  // must have both Reset and SetRegistry called on them.
  25  var decPool = sync.Pool{
  26  	New: func() interface{} {
  27  		return new(Decoder)
  28  	},
  29  }
  30  
  31  // A Decoder reads and decodes BSON documents from a stream. It reads from a bsonrw.ValueReader as
  32  // the source of BSON data.
  33  type Decoder struct {
  34  	dc bsoncodec.DecodeContext
  35  	vr bsonrw.ValueReader
  36  
  37  	// We persist defaultDocumentM and defaultDocumentD on the Decoder to prevent overwriting from
  38  	// (*Decoder).SetContext.
  39  	defaultDocumentM bool
  40  	defaultDocumentD bool
  41  
  42  	binaryAsSlice     bool
  43  	useJSONStructTags bool
  44  	useLocalTimeZone  bool
  45  	zeroMaps          bool
  46  	zeroStructs       bool
  47  }
  48  
  49  // NewDecoder returns a new decoder that uses the DefaultRegistry to read from vr.
  50  func NewDecoder(vr bsonrw.ValueReader) (*Decoder, error) {
  51  	if vr == nil {
  52  		return nil, errors.New("cannot create a new Decoder with a nil ValueReader")
  53  	}
  54  
  55  	return &Decoder{
  56  		dc: bsoncodec.DecodeContext{Registry: DefaultRegistry},
  57  		vr: vr,
  58  	}, nil
  59  }
  60  
  61  // NewDecoderWithContext returns a new decoder that uses DecodeContext dc to read from vr.
  62  //
  63  // Deprecated: Use [NewDecoder] and use the Decoder configuration methods set the desired unmarshal
  64  // behavior instead.
  65  func NewDecoderWithContext(dc bsoncodec.DecodeContext, vr bsonrw.ValueReader) (*Decoder, error) {
  66  	if dc.Registry == nil {
  67  		dc.Registry = DefaultRegistry
  68  	}
  69  	if vr == nil {
  70  		return nil, errors.New("cannot create a new Decoder with a nil ValueReader")
  71  	}
  72  
  73  	return &Decoder{
  74  		dc: dc,
  75  		vr: vr,
  76  	}, nil
  77  }
  78  
  79  // Decode reads the next BSON document from the stream and decodes it into the
  80  // value pointed to by val.
  81  //
  82  // See [Unmarshal] for details about BSON unmarshaling behavior.
  83  func (d *Decoder) Decode(val interface{}) error {
  84  	if unmarshaler, ok := val.(Unmarshaler); ok {
  85  		// TODO(skriptble): Reuse a []byte here and use the AppendDocumentBytes method.
  86  		buf, err := bsonrw.Copier{}.CopyDocumentToBytes(d.vr)
  87  		if err != nil {
  88  			return err
  89  		}
  90  		return unmarshaler.UnmarshalBSON(buf)
  91  	}
  92  
  93  	rval := reflect.ValueOf(val)
  94  	switch rval.Kind() {
  95  	case reflect.Ptr:
  96  		if rval.IsNil() {
  97  			return ErrDecodeToNil
  98  		}
  99  		rval = rval.Elem()
 100  	case reflect.Map:
 101  		if rval.IsNil() {
 102  			return ErrDecodeToNil
 103  		}
 104  	default:
 105  		return fmt.Errorf("argument to Decode must be a pointer or a map, but got %v", rval)
 106  	}
 107  	decoder, err := d.dc.LookupDecoder(rval.Type())
 108  	if err != nil {
 109  		return err
 110  	}
 111  
 112  	if d.defaultDocumentM {
 113  		d.dc.DefaultDocumentM()
 114  	}
 115  	if d.defaultDocumentD {
 116  		d.dc.DefaultDocumentD()
 117  	}
 118  	if d.binaryAsSlice {
 119  		d.dc.BinaryAsSlice()
 120  	}
 121  	if d.useJSONStructTags {
 122  		d.dc.UseJSONStructTags()
 123  	}
 124  	if d.useLocalTimeZone {
 125  		d.dc.UseLocalTimeZone()
 126  	}
 127  	if d.zeroMaps {
 128  		d.dc.ZeroMaps()
 129  	}
 130  	if d.zeroStructs {
 131  		d.dc.ZeroStructs()
 132  	}
 133  
 134  	return decoder.DecodeValue(d.dc, d.vr, rval)
 135  }
 136  
 137  // Reset will reset the state of the decoder, using the same *DecodeContext used in
 138  // the original construction but using vr for reading.
 139  func (d *Decoder) Reset(vr bsonrw.ValueReader) error {
 140  	// TODO:(GODRIVER-2719): Remove error return value.
 141  	d.vr = vr
 142  	return nil
 143  }
 144  
 145  // SetRegistry replaces the current registry of the decoder with r.
 146  func (d *Decoder) SetRegistry(r *bsoncodec.Registry) error {
 147  	// TODO:(GODRIVER-2719): Remove error return value.
 148  	d.dc.Registry = r
 149  	return nil
 150  }
 151  
 152  // SetContext replaces the current registry of the decoder with dc.
 153  //
 154  // Deprecated: Use the Decoder configuration methods to set the desired unmarshal behavior instead.
 155  func (d *Decoder) SetContext(dc bsoncodec.DecodeContext) error {
 156  	// TODO:(GODRIVER-2719): Remove error return value.
 157  	d.dc = dc
 158  	return nil
 159  }
 160  
 161  // DefaultDocumentM causes the Decoder to always unmarshal documents into the primitive.M type. This
 162  // behavior is restricted to data typed as "interface{}" or "map[string]interface{}".
 163  func (d *Decoder) DefaultDocumentM() {
 164  	d.defaultDocumentM = true
 165  }
 166  
 167  // DefaultDocumentD causes the Decoder to always unmarshal documents into the primitive.D type. This
 168  // behavior is restricted to data typed as "interface{}" or "map[string]interface{}".
 169  func (d *Decoder) DefaultDocumentD() {
 170  	d.defaultDocumentD = true
 171  }
 172  
 173  // AllowTruncatingDoubles causes the Decoder to truncate the fractional part of BSON "double" values
 174  // when attempting to unmarshal them into a Go integer (int, int8, int16, int32, or int64) struct
 175  // field. The truncation logic does not apply to BSON "decimal128" values.
 176  func (d *Decoder) AllowTruncatingDoubles() {
 177  	d.dc.Truncate = true
 178  }
 179  
 180  // BinaryAsSlice causes the Decoder to unmarshal BSON binary field values that are the "Generic" or
 181  // "Old" BSON binary subtype as a Go byte slice instead of a primitive.Binary.
 182  func (d *Decoder) BinaryAsSlice() {
 183  	d.binaryAsSlice = true
 184  }
 185  
 186  // UseJSONStructTags causes the Decoder to fall back to using the "json" struct tag if a "bson"
 187  // struct tag is not specified.
 188  func (d *Decoder) UseJSONStructTags() {
 189  	d.useJSONStructTags = true
 190  }
 191  
 192  // UseLocalTimeZone causes the Decoder to unmarshal time.Time values in the local timezone instead
 193  // of the UTC timezone.
 194  func (d *Decoder) UseLocalTimeZone() {
 195  	d.useLocalTimeZone = true
 196  }
 197  
 198  // ZeroMaps causes the Decoder to delete any existing values from Go maps in the destination value
 199  // passed to Decode before unmarshaling BSON documents into them.
 200  func (d *Decoder) ZeroMaps() {
 201  	d.zeroMaps = true
 202  }
 203  
 204  // ZeroStructs causes the Decoder to delete any existing values from Go structs in the destination
 205  // value passed to Decode before unmarshaling BSON documents into them.
 206  func (d *Decoder) ZeroStructs() {
 207  	d.zeroStructs = true
 208  }
 209