marshal.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  	"bytes"
  11  	"encoding/json"
  12  	"sync"
  13  
  14  	"go.mongodb.org/mongo-driver/bson/bsoncodec"
  15  	"go.mongodb.org/mongo-driver/bson/bsonrw"
  16  	"go.mongodb.org/mongo-driver/bson/bsontype"
  17  )
  18  
  19  const defaultDstCap = 256
  20  
  21  var bvwPool = bsonrw.NewBSONValueWriterPool()
  22  var extjPool = bsonrw.NewExtJSONValueWriterPool()
  23  
  24  // Marshaler is the interface implemented by types that can marshal themselves
  25  // into a valid BSON document.
  26  //
  27  // Implementations of Marshaler must return a full BSON document. To create
  28  // custom BSON marshaling behavior for individual values in a BSON document,
  29  // implement the ValueMarshaler interface instead.
  30  type Marshaler interface {
  31  	MarshalBSON() ([]byte, error)
  32  }
  33  
  34  // ValueMarshaler is the interface implemented by types that can marshal
  35  // themselves into a valid BSON value. The format of the returned bytes must
  36  // match the returned type.
  37  //
  38  // Implementations of ValueMarshaler must return an individual BSON value. To
  39  // create custom BSON marshaling behavior for an entire BSON document, implement
  40  // the Marshaler interface instead.
  41  type ValueMarshaler interface {
  42  	MarshalBSONValue() (bsontype.Type, []byte, error)
  43  }
  44  
  45  // Marshal returns the BSON encoding of val as a BSON document. If val is not a type that can be transformed into a
  46  // document, MarshalValue should be used instead.
  47  //
  48  // Marshal will use the default registry created by NewRegistry to recursively
  49  // marshal val into a []byte. Marshal will inspect struct tags and alter the
  50  // marshaling process accordingly.
  51  func Marshal(val interface{}) ([]byte, error) {
  52  	return MarshalWithRegistry(DefaultRegistry, val)
  53  }
  54  
  55  // MarshalAppend will encode val as a BSON document and append the bytes to dst. If dst is not large enough to hold the
  56  // bytes, it will be grown. If val is not a type that can be transformed into a document, MarshalValueAppend should be
  57  // used instead.
  58  //
  59  // Deprecated: Use [NewEncoder] and pass the dst byte slice (wrapped by a bytes.Buffer) into
  60  // [bsonrw.NewBSONValueWriter]:
  61  //
  62  //	buf := bytes.NewBuffer(dst)
  63  //	vw, err := bsonrw.NewBSONValueWriter(buf)
  64  //	if err != nil {
  65  //		panic(err)
  66  //	}
  67  //	enc, err := bson.NewEncoder(vw)
  68  //	if err != nil {
  69  //		panic(err)
  70  //	}
  71  //
  72  // See [Encoder] for more examples.
  73  func MarshalAppend(dst []byte, val interface{}) ([]byte, error) {
  74  	return MarshalAppendWithRegistry(DefaultRegistry, dst, val)
  75  }
  76  
  77  // MarshalWithRegistry returns the BSON encoding of val as a BSON document. If val is not a type that can be transformed
  78  // into a document, MarshalValueWithRegistry should be used instead.
  79  //
  80  // Deprecated: Use [NewEncoder] and specify the Registry by calling [Encoder.SetRegistry] instead:
  81  //
  82  //	buf := new(bytes.Buffer)
  83  //	vw, err := bsonrw.NewBSONValueWriter(buf)
  84  //	if err != nil {
  85  //		panic(err)
  86  //	}
  87  //	enc, err := bson.NewEncoder(vw)
  88  //	if err != nil {
  89  //		panic(err)
  90  //	}
  91  //	enc.SetRegistry(reg)
  92  //
  93  // See [Encoder] for more examples.
  94  func MarshalWithRegistry(r *bsoncodec.Registry, val interface{}) ([]byte, error) {
  95  	dst := make([]byte, 0)
  96  	return MarshalAppendWithRegistry(r, dst, val)
  97  }
  98  
  99  // MarshalWithContext returns the BSON encoding of val as a BSON document using EncodeContext ec. If val is not a type
 100  // that can be transformed into a document, MarshalValueWithContext should be used instead.
 101  //
 102  // Deprecated: Use [NewEncoder] and use the Encoder configuration methods to set the desired marshal
 103  // behavior instead:
 104  //
 105  //	buf := bytes.NewBuffer(dst)
 106  //	vw, err := bsonrw.NewBSONValueWriter(buf)
 107  //	if err != nil {
 108  //		panic(err)
 109  //	}
 110  //	enc, err := bson.NewEncoder(vw)
 111  //	if err != nil {
 112  //		panic(err)
 113  //	}
 114  //	enc.IntMinSize()
 115  //
 116  // See [Encoder] for more examples.
 117  func MarshalWithContext(ec bsoncodec.EncodeContext, val interface{}) ([]byte, error) {
 118  	dst := make([]byte, 0)
 119  	return MarshalAppendWithContext(ec, dst, val)
 120  }
 121  
 122  // MarshalAppendWithRegistry will encode val as a BSON document using Registry r and append the bytes to dst. If dst is
 123  // not large enough to hold the bytes, it will be grown. If val is not a type that can be transformed into a document,
 124  // MarshalValueAppendWithRegistry should be used instead.
 125  //
 126  // Deprecated: Use [NewEncoder], and pass the dst byte slice (wrapped by a bytes.Buffer) into
 127  // [bsonrw.NewBSONValueWriter], and specify the Registry by calling [Encoder.SetRegistry] instead:
 128  //
 129  //	buf := bytes.NewBuffer(dst)
 130  //	vw, err := bsonrw.NewBSONValueWriter(buf)
 131  //	if err != nil {
 132  //		panic(err)
 133  //	}
 134  //	enc, err := bson.NewEncoder(vw)
 135  //	if err != nil {
 136  //		panic(err)
 137  //	}
 138  //	enc.SetRegistry(reg)
 139  //
 140  // See [Encoder] for more examples.
 141  func MarshalAppendWithRegistry(r *bsoncodec.Registry, dst []byte, val interface{}) ([]byte, error) {
 142  	return MarshalAppendWithContext(bsoncodec.EncodeContext{Registry: r}, dst, val)
 143  }
 144  
 145  // Pool of buffers for marshalling BSON.
 146  var bufPool = sync.Pool{
 147  	New: func() interface{} {
 148  		return new(bytes.Buffer)
 149  	},
 150  }
 151  
 152  // MarshalAppendWithContext will encode val as a BSON document using Registry r and EncodeContext ec and append the
 153  // bytes to dst. If dst is not large enough to hold the bytes, it will be grown. If val is not a type that can be
 154  // transformed into a document, MarshalValueAppendWithContext should be used instead.
 155  //
 156  // Deprecated: Use [NewEncoder], pass the dst byte slice (wrapped by a bytes.Buffer) into
 157  // [bsonrw.NewBSONValueWriter], and use the Encoder configuration methods to set the desired marshal
 158  // behavior instead:
 159  //
 160  //	buf := bytes.NewBuffer(dst)
 161  //	vw, err := bsonrw.NewBSONValueWriter(buf)
 162  //	if err != nil {
 163  //		panic(err)
 164  //	}
 165  //	enc, err := bson.NewEncoder(vw)
 166  //	if err != nil {
 167  //		panic(err)
 168  //	}
 169  //	enc.IntMinSize()
 170  //
 171  // See [Encoder] for more examples.
 172  func MarshalAppendWithContext(ec bsoncodec.EncodeContext, dst []byte, val interface{}) ([]byte, error) {
 173  	sw := bufPool.Get().(*bytes.Buffer)
 174  	defer func() {
 175  		// Proper usage of a sync.Pool requires each entry to have approximately
 176  		// the same memory cost. To obtain this property when the stored type
 177  		// contains a variably-sized buffer, we add a hard limit on the maximum
 178  		// buffer to place back in the pool. We limit the size to 16MiB because
 179  		// that's the maximum wire message size supported by any current MongoDB
 180  		// server.
 181  		//
 182  		// Comment based on
 183  		// https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/fmt/print.go;l=147
 184  		//
 185  		// Recycle byte slices that are smaller than 16MiB and at least half
 186  		// occupied.
 187  		if sw.Cap() < 16*1024*1024 && sw.Cap()/2 < sw.Len() {
 188  			bufPool.Put(sw)
 189  		}
 190  	}()
 191  
 192  	sw.Reset()
 193  	vw := bvwPool.Get(sw)
 194  	defer bvwPool.Put(vw)
 195  
 196  	enc := encPool.Get().(*Encoder)
 197  	defer encPool.Put(enc)
 198  
 199  	err := enc.Reset(vw)
 200  	if err != nil {
 201  		return nil, err
 202  	}
 203  	err = enc.SetContext(ec)
 204  	if err != nil {
 205  		return nil, err
 206  	}
 207  
 208  	err = enc.Encode(val)
 209  	if err != nil {
 210  		return nil, err
 211  	}
 212  
 213  	return append(dst, sw.Bytes()...), nil
 214  }
 215  
 216  // MarshalValue returns the BSON encoding of val.
 217  //
 218  // MarshalValue will use bson.DefaultRegistry to transform val into a BSON value. If val is a struct, this function will
 219  // inspect struct tags and alter the marshalling process accordingly.
 220  func MarshalValue(val interface{}) (bsontype.Type, []byte, error) {
 221  	return MarshalValueWithRegistry(DefaultRegistry, val)
 222  }
 223  
 224  // MarshalValueAppend will append the BSON encoding of val to dst. If dst is not large enough to hold the BSON encoding
 225  // of val, dst will be grown.
 226  //
 227  // Deprecated: Appending individual BSON elements to an existing slice will not be supported in Go
 228  // Driver 2.0.
 229  func MarshalValueAppend(dst []byte, val interface{}) (bsontype.Type, []byte, error) {
 230  	return MarshalValueAppendWithRegistry(DefaultRegistry, dst, val)
 231  }
 232  
 233  // MarshalValueWithRegistry returns the BSON encoding of val using Registry r.
 234  //
 235  // Deprecated: Using a custom registry to marshal individual BSON values will not be supported in Go
 236  // Driver 2.0.
 237  func MarshalValueWithRegistry(r *bsoncodec.Registry, val interface{}) (bsontype.Type, []byte, error) {
 238  	dst := make([]byte, 0)
 239  	return MarshalValueAppendWithRegistry(r, dst, val)
 240  }
 241  
 242  // MarshalValueWithContext returns the BSON encoding of val using EncodeContext ec.
 243  //
 244  // Deprecated: Using a custom EncodeContext to marshal individual BSON elements will not be
 245  // supported in Go Driver 2.0.
 246  func MarshalValueWithContext(ec bsoncodec.EncodeContext, val interface{}) (bsontype.Type, []byte, error) {
 247  	dst := make([]byte, 0)
 248  	return MarshalValueAppendWithContext(ec, dst, val)
 249  }
 250  
 251  // MarshalValueAppendWithRegistry will append the BSON encoding of val to dst using Registry r. If dst is not large
 252  // enough to hold the BSON encoding of val, dst will be grown.
 253  //
 254  // Deprecated: Appending individual BSON elements to an existing slice will not be supported in Go
 255  // Driver 2.0.
 256  func MarshalValueAppendWithRegistry(r *bsoncodec.Registry, dst []byte, val interface{}) (bsontype.Type, []byte, error) {
 257  	return MarshalValueAppendWithContext(bsoncodec.EncodeContext{Registry: r}, dst, val)
 258  }
 259  
 260  // MarshalValueAppendWithContext will append the BSON encoding of val to dst using EncodeContext ec. If dst is not large
 261  // enough to hold the BSON encoding of val, dst will be grown.
 262  //
 263  // Deprecated: Appending individual BSON elements to an existing slice will not be supported in Go
 264  // Driver 2.0.
 265  func MarshalValueAppendWithContext(ec bsoncodec.EncodeContext, dst []byte, val interface{}) (bsontype.Type, []byte, error) {
 266  	// get a ValueWriter configured to write to dst
 267  	sw := new(bsonrw.SliceWriter)
 268  	*sw = dst
 269  	vwFlusher := bvwPool.GetAtModeElement(sw)
 270  
 271  	// get an Encoder and encode the value
 272  	enc := encPool.Get().(*Encoder)
 273  	defer encPool.Put(enc)
 274  	if err := enc.Reset(vwFlusher); err != nil {
 275  		return 0, nil, err
 276  	}
 277  	if err := enc.SetContext(ec); err != nil {
 278  		return 0, nil, err
 279  	}
 280  	if err := enc.Encode(val); err != nil {
 281  		return 0, nil, err
 282  	}
 283  
 284  	// flush the bytes written because we cannot guarantee that a full document has been written
 285  	// after the flush, *sw will be in the format
 286  	// [value type, 0 (null byte to indicate end of empty element name), value bytes..]
 287  	if err := vwFlusher.Flush(); err != nil {
 288  		return 0, nil, err
 289  	}
 290  	buffer := *sw
 291  	return bsontype.Type(buffer[0]), buffer[2:], nil
 292  }
 293  
 294  // MarshalExtJSON returns the extended JSON encoding of val.
 295  func MarshalExtJSON(val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 296  	return MarshalExtJSONWithRegistry(DefaultRegistry, val, canonical, escapeHTML)
 297  }
 298  
 299  // MarshalExtJSONAppend will append the extended JSON encoding of val to dst.
 300  // If dst is not large enough to hold the extended JSON encoding of val, dst
 301  // will be grown.
 302  //
 303  // Deprecated: Use [NewEncoder] and pass the dst byte slice (wrapped by a bytes.Buffer) into
 304  // [bsonrw.NewExtJSONValueWriter] instead:
 305  //
 306  //	buf := bytes.NewBuffer(dst)
 307  //	vw, err := bsonrw.NewExtJSONValueWriter(buf, true, false)
 308  //	if err != nil {
 309  //		panic(err)
 310  //	}
 311  //	enc, err := bson.NewEncoder(vw)
 312  //	if err != nil {
 313  //		panic(err)
 314  //	}
 315  //
 316  // See [Encoder] for more examples.
 317  func MarshalExtJSONAppend(dst []byte, val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 318  	return MarshalExtJSONAppendWithRegistry(DefaultRegistry, dst, val, canonical, escapeHTML)
 319  }
 320  
 321  // MarshalExtJSONWithRegistry returns the extended JSON encoding of val using Registry r.
 322  //
 323  // Deprecated: Use [NewEncoder] and specify the Registry by calling [Encoder.SetRegistry] instead:
 324  //
 325  //	buf := new(bytes.Buffer)
 326  //	vw, err := bsonrw.NewBSONValueWriter(buf)
 327  //	if err != nil {
 328  //		panic(err)
 329  //	}
 330  //	enc, err := bson.NewEncoder(vw)
 331  //	if err != nil {
 332  //		panic(err)
 333  //	}
 334  //	enc.SetRegistry(reg)
 335  //
 336  // See [Encoder] for more examples.
 337  func MarshalExtJSONWithRegistry(r *bsoncodec.Registry, val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 338  	dst := make([]byte, 0, defaultDstCap)
 339  	return MarshalExtJSONAppendWithContext(bsoncodec.EncodeContext{Registry: r}, dst, val, canonical, escapeHTML)
 340  }
 341  
 342  // MarshalExtJSONWithContext returns the extended JSON encoding of val using Registry r.
 343  //
 344  // Deprecated: Use [NewEncoder] and use the Encoder configuration methods to set the desired marshal
 345  // behavior instead:
 346  //
 347  //	buf := new(bytes.Buffer)
 348  //	vw, err := bsonrw.NewBSONValueWriter(buf)
 349  //	if err != nil {
 350  //		panic(err)
 351  //	}
 352  //	enc, err := bson.NewEncoder(vw)
 353  //	if err != nil {
 354  //		panic(err)
 355  //	}
 356  //	enc.IntMinSize()
 357  //
 358  // See [Encoder] for more examples.
 359  func MarshalExtJSONWithContext(ec bsoncodec.EncodeContext, val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 360  	dst := make([]byte, 0, defaultDstCap)
 361  	return MarshalExtJSONAppendWithContext(ec, dst, val, canonical, escapeHTML)
 362  }
 363  
 364  // MarshalExtJSONAppendWithRegistry will append the extended JSON encoding of
 365  // val to dst using Registry r. If dst is not large enough to hold the BSON
 366  // encoding of val, dst will be grown.
 367  //
 368  // Deprecated: Use [NewEncoder], pass the dst byte slice (wrapped by a bytes.Buffer) into
 369  // [bsonrw.NewExtJSONValueWriter], and specify the Registry by calling [Encoder.SetRegistry]
 370  // instead:
 371  //
 372  //	buf := bytes.NewBuffer(dst)
 373  //	vw, err := bsonrw.NewExtJSONValueWriter(buf, true, false)
 374  //	if err != nil {
 375  //		panic(err)
 376  //	}
 377  //	enc, err := bson.NewEncoder(vw)
 378  //	if err != nil {
 379  //		panic(err)
 380  //	}
 381  //
 382  // See [Encoder] for more examples.
 383  func MarshalExtJSONAppendWithRegistry(r *bsoncodec.Registry, dst []byte, val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 384  	return MarshalExtJSONAppendWithContext(bsoncodec.EncodeContext{Registry: r}, dst, val, canonical, escapeHTML)
 385  }
 386  
 387  // MarshalExtJSONAppendWithContext will append the extended JSON encoding of
 388  // val to dst using Registry r. If dst is not large enough to hold the BSON
 389  // encoding of val, dst will be grown.
 390  //
 391  // Deprecated: Use [NewEncoder], pass the dst byte slice (wrapped by a bytes.Buffer) into
 392  // [bsonrw.NewExtJSONValueWriter], and use the Encoder configuration methods to set the desired marshal
 393  // behavior instead:
 394  //
 395  //	buf := bytes.NewBuffer(dst)
 396  //	vw, err := bsonrw.NewExtJSONValueWriter(buf, true, false)
 397  //	if err != nil {
 398  //		panic(err)
 399  //	}
 400  //	enc, err := bson.NewEncoder(vw)
 401  //	if err != nil {
 402  //		panic(err)
 403  //	}
 404  //	enc.IntMinSize()
 405  //
 406  // See [Encoder] for more examples.
 407  func MarshalExtJSONAppendWithContext(ec bsoncodec.EncodeContext, dst []byte, val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 408  	sw := new(bsonrw.SliceWriter)
 409  	*sw = dst
 410  	ejvw := extjPool.Get(sw, canonical, escapeHTML)
 411  	defer extjPool.Put(ejvw)
 412  
 413  	enc := encPool.Get().(*Encoder)
 414  	defer encPool.Put(enc)
 415  
 416  	err := enc.Reset(ejvw)
 417  	if err != nil {
 418  		return nil, err
 419  	}
 420  	err = enc.SetContext(ec)
 421  	if err != nil {
 422  		return nil, err
 423  	}
 424  
 425  	err = enc.Encode(val)
 426  	if err != nil {
 427  		return nil, err
 428  	}
 429  
 430  	return *sw, nil
 431  }
 432  
 433  // IndentExtJSON will prefix and indent the provided extended JSON src and append it to dst.
 434  func IndentExtJSON(dst *bytes.Buffer, src []byte, prefix, indent string) error {
 435  	return json.Indent(dst, src, prefix, indent)
 436  }
 437  
 438  // MarshalExtJSONIndent returns the extended JSON encoding of val with each line with prefixed
 439  // and indented.
 440  func MarshalExtJSONIndent(val interface{}, canonical, escapeHTML bool, prefix, indent string) ([]byte, error) {
 441  	marshaled, err := MarshalExtJSON(val, canonical, escapeHTML)
 442  	if err != nil {
 443  		return nil, err
 444  	}
 445  
 446  	var buf bytes.Buffer
 447  	err = IndentExtJSON(&buf, marshaled, prefix, indent)
 448  	if err != nil {
 449  		return nil, err
 450  	}
 451  
 452  	return buf.Bytes(), nil
 453  }
 454