encoder.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  	"reflect"
  12  	"sync"
  13  
  14  	"go.mongodb.org/mongo-driver/bson/bsoncodec"
  15  	"go.mongodb.org/mongo-driver/bson/bsonrw"
  16  )
  17  
  18  // This pool is used to keep the allocations of Encoders down. This is only used for the Marshal*
  19  // methods and is not consumable from outside of this package. The Encoders retrieved from this pool
  20  // must have both Reset and SetRegistry called on them.
  21  var encPool = sync.Pool{
  22  	New: func() interface{} {
  23  		return new(Encoder)
  24  	},
  25  }
  26  
  27  // An Encoder writes a serialization format to an output stream. It writes to a bsonrw.ValueWriter
  28  // as the destination of BSON data.
  29  type Encoder struct {
  30  	ec bsoncodec.EncodeContext
  31  	vw bsonrw.ValueWriter
  32  
  33  	errorOnInlineDuplicates bool
  34  	intMinSize              bool
  35  	stringifyMapKeysWithFmt bool
  36  	nilMapAsEmpty           bool
  37  	nilSliceAsEmpty         bool
  38  	nilByteSliceAsEmpty     bool
  39  	omitZeroStruct          bool
  40  	useJSONStructTags       bool
  41  }
  42  
  43  // NewEncoder returns a new encoder that uses the DefaultRegistry to write to vw.
  44  func NewEncoder(vw bsonrw.ValueWriter) (*Encoder, error) {
  45  	// TODO:(GODRIVER-2719): Remove error return value.
  46  	if vw == nil {
  47  		return nil, errors.New("cannot create a new Encoder with a nil ValueWriter")
  48  	}
  49  
  50  	return &Encoder{
  51  		ec: bsoncodec.EncodeContext{Registry: DefaultRegistry},
  52  		vw: vw,
  53  	}, nil
  54  }
  55  
  56  // NewEncoderWithContext returns a new encoder that uses EncodeContext ec to write to vw.
  57  //
  58  // Deprecated: Use [NewEncoder] and use the Encoder configuration methods to set the desired marshal
  59  // behavior instead.
  60  func NewEncoderWithContext(ec bsoncodec.EncodeContext, vw bsonrw.ValueWriter) (*Encoder, error) {
  61  	if ec.Registry == nil {
  62  		ec = bsoncodec.EncodeContext{Registry: DefaultRegistry}
  63  	}
  64  	if vw == nil {
  65  		return nil, errors.New("cannot create a new Encoder with a nil ValueWriter")
  66  	}
  67  
  68  	return &Encoder{
  69  		ec: ec,
  70  		vw: vw,
  71  	}, nil
  72  }
  73  
  74  // Encode writes the BSON encoding of val to the stream.
  75  //
  76  // See [Marshal] for details about BSON marshaling behavior.
  77  func (e *Encoder) Encode(val interface{}) error {
  78  	if marshaler, ok := val.(Marshaler); ok {
  79  		// TODO(skriptble): Should we have a MarshalAppender interface so that we can have []byte reuse?
  80  		buf, err := marshaler.MarshalBSON()
  81  		if err != nil {
  82  			return err
  83  		}
  84  		return bsonrw.Copier{}.CopyDocumentFromBytes(e.vw, buf)
  85  	}
  86  
  87  	encoder, err := e.ec.LookupEncoder(reflect.TypeOf(val))
  88  	if err != nil {
  89  		return err
  90  	}
  91  
  92  	// Copy the configurations applied to the Encoder over to the EncodeContext, which actually
  93  	// communicates those configurations to the default ValueEncoders.
  94  	if e.errorOnInlineDuplicates {
  95  		e.ec.ErrorOnInlineDuplicates()
  96  	}
  97  	if e.intMinSize {
  98  		e.ec.MinSize = true
  99  	}
 100  	if e.stringifyMapKeysWithFmt {
 101  		e.ec.StringifyMapKeysWithFmt()
 102  	}
 103  	if e.nilMapAsEmpty {
 104  		e.ec.NilMapAsEmpty()
 105  	}
 106  	if e.nilSliceAsEmpty {
 107  		e.ec.NilSliceAsEmpty()
 108  	}
 109  	if e.nilByteSliceAsEmpty {
 110  		e.ec.NilByteSliceAsEmpty()
 111  	}
 112  	if e.omitZeroStruct {
 113  		e.ec.OmitZeroStruct()
 114  	}
 115  	if e.useJSONStructTags {
 116  		e.ec.UseJSONStructTags()
 117  	}
 118  
 119  	return encoder.EncodeValue(e.ec, e.vw, reflect.ValueOf(val))
 120  }
 121  
 122  // Reset will reset the state of the Encoder, using the same *EncodeContext used in
 123  // the original construction but using vw.
 124  func (e *Encoder) Reset(vw bsonrw.ValueWriter) error {
 125  	// TODO:(GODRIVER-2719): Remove error return value.
 126  	e.vw = vw
 127  	return nil
 128  }
 129  
 130  // SetRegistry replaces the current registry of the Encoder with r.
 131  func (e *Encoder) SetRegistry(r *bsoncodec.Registry) error {
 132  	// TODO:(GODRIVER-2719): Remove error return value.
 133  	e.ec.Registry = r
 134  	return nil
 135  }
 136  
 137  // SetContext replaces the current EncodeContext of the encoder with ec.
 138  //
 139  // Deprecated: Use the Encoder configuration methods set the desired marshal behavior instead.
 140  func (e *Encoder) SetContext(ec bsoncodec.EncodeContext) error {
 141  	// TODO:(GODRIVER-2719): Remove error return value.
 142  	e.ec = ec
 143  	return nil
 144  }
 145  
 146  // ErrorOnInlineDuplicates causes the Encoder to return an error if there is a duplicate field in
 147  // the marshaled BSON when the "inline" struct tag option is set.
 148  func (e *Encoder) ErrorOnInlineDuplicates() {
 149  	e.errorOnInlineDuplicates = true
 150  }
 151  
 152  // IntMinSize causes the Encoder to marshal Go integer values (int, int8, int16, int32, int64, uint,
 153  // uint8, uint16, uint32, or uint64) as the minimum BSON int size (either 32 or 64 bits) that can
 154  // represent the integer value.
 155  func (e *Encoder) IntMinSize() {
 156  	e.intMinSize = true
 157  }
 158  
 159  // StringifyMapKeysWithFmt causes the Encoder to convert Go map keys to BSON document field name
 160  // strings using fmt.Sprint instead of the default string conversion logic.
 161  func (e *Encoder) StringifyMapKeysWithFmt() {
 162  	e.stringifyMapKeysWithFmt = true
 163  }
 164  
 165  // NilMapAsEmpty causes the Encoder to marshal nil Go maps as empty BSON documents instead of BSON
 166  // null.
 167  func (e *Encoder) NilMapAsEmpty() {
 168  	e.nilMapAsEmpty = true
 169  }
 170  
 171  // NilSliceAsEmpty causes the Encoder to marshal nil Go slices as empty BSON arrays instead of BSON
 172  // null.
 173  func (e *Encoder) NilSliceAsEmpty() {
 174  	e.nilSliceAsEmpty = true
 175  }
 176  
 177  // NilByteSliceAsEmpty causes the Encoder to marshal nil Go byte slices as empty BSON binary values
 178  // instead of BSON null.
 179  func (e *Encoder) NilByteSliceAsEmpty() {
 180  	e.nilByteSliceAsEmpty = true
 181  }
 182  
 183  // TODO(GODRIVER-2820): Update the description to remove the note about only examining exported
 184  // TODO struct fields once the logic is updated to also inspect private struct fields.
 185  
 186  // OmitZeroStruct causes the Encoder to consider the zero value for a struct (e.g. MyStruct{})
 187  // as empty and omit it from the marshaled BSON when the "omitempty" struct tag option is set.
 188  //
 189  // Note that the Encoder only examines exported struct fields when determining if a struct is the
 190  // zero value. It considers pointers to a zero struct value (e.g. &MyStruct{}) not empty.
 191  func (e *Encoder) OmitZeroStruct() {
 192  	e.omitZeroStruct = true
 193  }
 194  
 195  // UseJSONStructTags causes the Encoder to fall back to using the "json" struct tag if a "bson"
 196  // struct tag is not specified.
 197  func (e *Encoder) UseJSONStructTags() {
 198  	e.useJSONStructTags = true
 199  }
 200