mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 19:52:25 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			453 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			453 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (C) MongoDB, Inc. 2017-present.
 | |
| //
 | |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may
 | |
| // not use this file except in compliance with the License. You may obtain
 | |
| // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 | |
| 
 | |
| package bson
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/json"
 | |
| 	"sync"
 | |
| 
 | |
| 	"go.mongodb.org/mongo-driver/bson/bsoncodec"
 | |
| 	"go.mongodb.org/mongo-driver/bson/bsonrw"
 | |
| 	"go.mongodb.org/mongo-driver/bson/bsontype"
 | |
| )
 | |
| 
 | |
| const defaultDstCap = 256
 | |
| 
 | |
| var bvwPool = bsonrw.NewBSONValueWriterPool()
 | |
| var extjPool = bsonrw.NewExtJSONValueWriterPool()
 | |
| 
 | |
| // Marshaler is the interface implemented by types that can marshal themselves
 | |
| // into a valid BSON document.
 | |
| //
 | |
| // Implementations of Marshaler must return a full BSON document. To create
 | |
| // custom BSON marshaling behavior for individual values in a BSON document,
 | |
| // implement the ValueMarshaler interface instead.
 | |
| type Marshaler interface {
 | |
| 	MarshalBSON() ([]byte, error)
 | |
| }
 | |
| 
 | |
| // ValueMarshaler is the interface implemented by types that can marshal
 | |
| // themselves into a valid BSON value. The format of the returned bytes must
 | |
| // match the returned type.
 | |
| //
 | |
| // Implementations of ValueMarshaler must return an individual BSON value. To
 | |
| // create custom BSON marshaling behavior for an entire BSON document, implement
 | |
| // the Marshaler interface instead.
 | |
| type ValueMarshaler interface {
 | |
| 	MarshalBSONValue() (bsontype.Type, []byte, error)
 | |
| }
 | |
| 
 | |
| // Marshal returns the BSON encoding of val as a BSON document. If val is not a type that can be transformed into a
 | |
| // document, MarshalValue should be used instead.
 | |
| //
 | |
| // Marshal will use the default registry created by NewRegistry to recursively
 | |
| // marshal val into a []byte. Marshal will inspect struct tags and alter the
 | |
| // marshaling process accordingly.
 | |
| func Marshal(val interface{}) ([]byte, error) {
 | |
| 	return MarshalWithRegistry(DefaultRegistry, val)
 | |
| }
 | |
| 
 | |
| // MarshalAppend will encode val as a BSON document and append the 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 transformed into a document, MarshalValueAppend should be
 | |
| // used instead.
 | |
| //
 | |
| // Deprecated: Use [NewEncoder] and pass the dst byte slice (wrapped by a bytes.Buffer) into
 | |
| // [bsonrw.NewBSONValueWriter]:
 | |
| //
 | |
| //	buf := bytes.NewBuffer(dst)
 | |
| //	vw, err := bsonrw.NewBSONValueWriter(buf)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc, err := bson.NewEncoder(vw)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //
 | |
| // See [Encoder] for more examples.
 | |
| func MarshalAppend(dst []byte, val interface{}) ([]byte, error) {
 | |
| 	return MarshalAppendWithRegistry(DefaultRegistry, dst, val)
 | |
| }
 | |
| 
 | |
| // MarshalWithRegistry returns the BSON encoding of val as a BSON document. If val is not a type that can be transformed
 | |
| // into a document, MarshalValueWithRegistry should be used instead.
 | |
| //
 | |
| // Deprecated: Use [NewEncoder] and specify the Registry by calling [Encoder.SetRegistry] instead:
 | |
| //
 | |
| //	buf := new(bytes.Buffer)
 | |
| //	vw, err := bsonrw.NewBSONValueWriter(buf)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc, err := bson.NewEncoder(vw)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc.SetRegistry(reg)
 | |
| //
 | |
| // See [Encoder] for more examples.
 | |
| func MarshalWithRegistry(r *bsoncodec.Registry, val interface{}) ([]byte, error) {
 | |
| 	dst := make([]byte, 0)
 | |
| 	return MarshalAppendWithRegistry(r, dst, val)
 | |
| }
 | |
| 
 | |
| // MarshalWithContext returns the BSON encoding of val as a BSON document using EncodeContext ec. If val is not a type
 | |
| // that can be transformed into a document, MarshalValueWithContext should be used instead.
 | |
| //
 | |
| // Deprecated: Use [NewEncoder] and use the Encoder configuration methods to set the desired marshal
 | |
| // behavior instead:
 | |
| //
 | |
| //	buf := bytes.NewBuffer(dst)
 | |
| //	vw, err := bsonrw.NewBSONValueWriter(buf)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc, err := bson.NewEncoder(vw)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc.IntMinSize()
 | |
| //
 | |
| // See [Encoder] for more examples.
 | |
| func MarshalWithContext(ec bsoncodec.EncodeContext, val interface{}) ([]byte, error) {
 | |
| 	dst := make([]byte, 0)
 | |
| 	return MarshalAppendWithContext(ec, dst, val)
 | |
| }
 | |
| 
 | |
| // MarshalAppendWithRegistry will encode val as a BSON document using Registry r and append the 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 transformed into a document,
 | |
| // MarshalValueAppendWithRegistry should be used instead.
 | |
| //
 | |
| // Deprecated: Use [NewEncoder], and pass the dst byte slice (wrapped by a bytes.Buffer) into
 | |
| // [bsonrw.NewBSONValueWriter], and specify the Registry by calling [Encoder.SetRegistry] instead:
 | |
| //
 | |
| //	buf := bytes.NewBuffer(dst)
 | |
| //	vw, err := bsonrw.NewBSONValueWriter(buf)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc, err := bson.NewEncoder(vw)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc.SetRegistry(reg)
 | |
| //
 | |
| // See [Encoder] for more examples.
 | |
| func MarshalAppendWithRegistry(r *bsoncodec.Registry, dst []byte, val interface{}) ([]byte, error) {
 | |
| 	return MarshalAppendWithContext(bsoncodec.EncodeContext{Registry: r}, dst, val)
 | |
| }
 | |
| 
 | |
| // Pool of buffers for marshalling BSON.
 | |
| var bufPool = sync.Pool{
 | |
| 	New: func() interface{} {
 | |
| 		return new(bytes.Buffer)
 | |
| 	},
 | |
| }
 | |
| 
 | |
| // MarshalAppendWithContext will encode val as a BSON document using Registry r and EncodeContext ec and append the
 | |
| // 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
 | |
| // transformed into a document, MarshalValueAppendWithContext should be used instead.
 | |
| //
 | |
| // Deprecated: Use [NewEncoder], pass the dst byte slice (wrapped by a bytes.Buffer) into
 | |
| // [bsonrw.NewBSONValueWriter], and use the Encoder configuration methods to set the desired marshal
 | |
| // behavior instead:
 | |
| //
 | |
| //	buf := bytes.NewBuffer(dst)
 | |
| //	vw, err := bsonrw.NewBSONValueWriter(buf)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc, err := bson.NewEncoder(vw)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc.IntMinSize()
 | |
| //
 | |
| // See [Encoder] for more examples.
 | |
| func MarshalAppendWithContext(ec bsoncodec.EncodeContext, dst []byte, val interface{}) ([]byte, error) {
 | |
| 	sw := bufPool.Get().(*bytes.Buffer)
 | |
| 	defer func() {
 | |
| 		// Proper usage of a sync.Pool requires each entry to have approximately
 | |
| 		// the same memory cost. To obtain this property when the stored type
 | |
| 		// contains a variably-sized buffer, we add a hard limit on the maximum
 | |
| 		// buffer to place back in the pool. We limit the size to 16MiB because
 | |
| 		// that's the maximum wire message size supported by any current MongoDB
 | |
| 		// server.
 | |
| 		//
 | |
| 		// Comment based on
 | |
| 		// https://cs.opensource.google/go/go/+/refs/tags/go1.19:src/fmt/print.go;l=147
 | |
| 		//
 | |
| 		// Recycle byte slices that are smaller than 16MiB and at least half
 | |
| 		// occupied.
 | |
| 		if sw.Cap() < 16*1024*1024 && sw.Cap()/2 < sw.Len() {
 | |
| 			bufPool.Put(sw)
 | |
| 		}
 | |
| 	}()
 | |
| 
 | |
| 	sw.Reset()
 | |
| 	vw := bvwPool.Get(sw)
 | |
| 	defer bvwPool.Put(vw)
 | |
| 
 | |
| 	enc := encPool.Get().(*Encoder)
 | |
| 	defer encPool.Put(enc)
 | |
| 
 | |
| 	err := enc.Reset(vw)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	err = enc.SetContext(ec)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	err = enc.Encode(val)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return append(dst, sw.Bytes()...), nil
 | |
| }
 | |
| 
 | |
| // MarshalValue returns the BSON encoding of val.
 | |
| //
 | |
| // MarshalValue will use bson.DefaultRegistry to transform val into a BSON value. If val is a struct, this function will
 | |
| // inspect struct tags and alter the marshalling process accordingly.
 | |
| func MarshalValue(val interface{}) (bsontype.Type, []byte, error) {
 | |
| 	return MarshalValueWithRegistry(DefaultRegistry, val)
 | |
| }
 | |
| 
 | |
| // MarshalValueAppend will append the BSON encoding of val to dst. If dst is not large enough to hold the BSON encoding
 | |
| // of val, dst will be grown.
 | |
| //
 | |
| // Deprecated: Appending individual BSON elements to an existing slice will not be supported in Go
 | |
| // Driver 2.0.
 | |
| func MarshalValueAppend(dst []byte, val interface{}) (bsontype.Type, []byte, error) {
 | |
| 	return MarshalValueAppendWithRegistry(DefaultRegistry, dst, val)
 | |
| }
 | |
| 
 | |
| // MarshalValueWithRegistry returns the BSON encoding of val using Registry r.
 | |
| //
 | |
| // Deprecated: Using a custom registry to marshal individual BSON values will not be supported in Go
 | |
| // Driver 2.0.
 | |
| func MarshalValueWithRegistry(r *bsoncodec.Registry, val interface{}) (bsontype.Type, []byte, error) {
 | |
| 	dst := make([]byte, 0)
 | |
| 	return MarshalValueAppendWithRegistry(r, dst, val)
 | |
| }
 | |
| 
 | |
| // MarshalValueWithContext returns the BSON encoding of val using EncodeContext ec.
 | |
| //
 | |
| // Deprecated: Using a custom EncodeContext to marshal individual BSON elements will not be
 | |
| // supported in Go Driver 2.0.
 | |
| func MarshalValueWithContext(ec bsoncodec.EncodeContext, val interface{}) (bsontype.Type, []byte, error) {
 | |
| 	dst := make([]byte, 0)
 | |
| 	return MarshalValueAppendWithContext(ec, dst, val)
 | |
| }
 | |
| 
 | |
| // MarshalValueAppendWithRegistry will append the BSON encoding of val to dst using Registry r. If dst is not large
 | |
| // enough to hold the BSON encoding of val, dst will be grown.
 | |
| //
 | |
| // Deprecated: Appending individual BSON elements to an existing slice will not be supported in Go
 | |
| // Driver 2.0.
 | |
| func MarshalValueAppendWithRegistry(r *bsoncodec.Registry, dst []byte, val interface{}) (bsontype.Type, []byte, error) {
 | |
| 	return MarshalValueAppendWithContext(bsoncodec.EncodeContext{Registry: r}, dst, val)
 | |
| }
 | |
| 
 | |
| // MarshalValueAppendWithContext will append the BSON encoding of val to dst using EncodeContext ec. If dst is not large
 | |
| // enough to hold the BSON encoding of val, dst will be grown.
 | |
| //
 | |
| // Deprecated: Appending individual BSON elements to an existing slice will not be supported in Go
 | |
| // Driver 2.0.
 | |
| func MarshalValueAppendWithContext(ec bsoncodec.EncodeContext, dst []byte, val interface{}) (bsontype.Type, []byte, error) {
 | |
| 	// get a ValueWriter configured to write to dst
 | |
| 	sw := new(bsonrw.SliceWriter)
 | |
| 	*sw = dst
 | |
| 	vwFlusher := bvwPool.GetAtModeElement(sw)
 | |
| 
 | |
| 	// get an Encoder and encode the value
 | |
| 	enc := encPool.Get().(*Encoder)
 | |
| 	defer encPool.Put(enc)
 | |
| 	if err := enc.Reset(vwFlusher); err != nil {
 | |
| 		return 0, nil, err
 | |
| 	}
 | |
| 	if err := enc.SetContext(ec); err != nil {
 | |
| 		return 0, nil, err
 | |
| 	}
 | |
| 	if err := enc.Encode(val); err != nil {
 | |
| 		return 0, nil, err
 | |
| 	}
 | |
| 
 | |
| 	// flush the bytes written because we cannot guarantee that a full document has been written
 | |
| 	// after the flush, *sw will be in the format
 | |
| 	// [value type, 0 (null byte to indicate end of empty element name), value bytes..]
 | |
| 	if err := vwFlusher.Flush(); err != nil {
 | |
| 		return 0, nil, err
 | |
| 	}
 | |
| 	buffer := *sw
 | |
| 	return bsontype.Type(buffer[0]), buffer[2:], nil
 | |
| }
 | |
| 
 | |
| // MarshalExtJSON returns the extended JSON encoding of val.
 | |
| func MarshalExtJSON(val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 | |
| 	return MarshalExtJSONWithRegistry(DefaultRegistry, val, canonical, escapeHTML)
 | |
| }
 | |
| 
 | |
| // MarshalExtJSONAppend will append the extended JSON encoding of val to dst.
 | |
| // If dst is not large enough to hold the extended JSON encoding of val, dst
 | |
| // will be grown.
 | |
| //
 | |
| // Deprecated: Use [NewEncoder] and pass the dst byte slice (wrapped by a bytes.Buffer) into
 | |
| // [bsonrw.NewExtJSONValueWriter] instead:
 | |
| //
 | |
| //	buf := bytes.NewBuffer(dst)
 | |
| //	vw, err := bsonrw.NewExtJSONValueWriter(buf, true, false)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc, err := bson.NewEncoder(vw)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //
 | |
| // See [Encoder] for more examples.
 | |
| func MarshalExtJSONAppend(dst []byte, val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 | |
| 	return MarshalExtJSONAppendWithRegistry(DefaultRegistry, dst, val, canonical, escapeHTML)
 | |
| }
 | |
| 
 | |
| // MarshalExtJSONWithRegistry returns the extended JSON encoding of val using Registry r.
 | |
| //
 | |
| // Deprecated: Use [NewEncoder] and specify the Registry by calling [Encoder.SetRegistry] instead:
 | |
| //
 | |
| //	buf := new(bytes.Buffer)
 | |
| //	vw, err := bsonrw.NewBSONValueWriter(buf)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc, err := bson.NewEncoder(vw)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc.SetRegistry(reg)
 | |
| //
 | |
| // See [Encoder] for more examples.
 | |
| func MarshalExtJSONWithRegistry(r *bsoncodec.Registry, val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 | |
| 	dst := make([]byte, 0, defaultDstCap)
 | |
| 	return MarshalExtJSONAppendWithContext(bsoncodec.EncodeContext{Registry: r}, dst, val, canonical, escapeHTML)
 | |
| }
 | |
| 
 | |
| // MarshalExtJSONWithContext returns the extended JSON encoding of val using Registry r.
 | |
| //
 | |
| // Deprecated: Use [NewEncoder] and use the Encoder configuration methods to set the desired marshal
 | |
| // behavior instead:
 | |
| //
 | |
| //	buf := new(bytes.Buffer)
 | |
| //	vw, err := bsonrw.NewBSONValueWriter(buf)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc, err := bson.NewEncoder(vw)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc.IntMinSize()
 | |
| //
 | |
| // See [Encoder] for more examples.
 | |
| func MarshalExtJSONWithContext(ec bsoncodec.EncodeContext, val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 | |
| 	dst := make([]byte, 0, defaultDstCap)
 | |
| 	return MarshalExtJSONAppendWithContext(ec, dst, val, canonical, escapeHTML)
 | |
| }
 | |
| 
 | |
| // MarshalExtJSONAppendWithRegistry will append the extended JSON encoding of
 | |
| // val to dst using Registry r. If dst is not large enough to hold the BSON
 | |
| // encoding of val, dst will be grown.
 | |
| //
 | |
| // Deprecated: Use [NewEncoder], pass the dst byte slice (wrapped by a bytes.Buffer) into
 | |
| // [bsonrw.NewExtJSONValueWriter], and specify the Registry by calling [Encoder.SetRegistry]
 | |
| // instead:
 | |
| //
 | |
| //	buf := bytes.NewBuffer(dst)
 | |
| //	vw, err := bsonrw.NewExtJSONValueWriter(buf, true, false)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc, err := bson.NewEncoder(vw)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //
 | |
| // See [Encoder] for more examples.
 | |
| func MarshalExtJSONAppendWithRegistry(r *bsoncodec.Registry, dst []byte, val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 | |
| 	return MarshalExtJSONAppendWithContext(bsoncodec.EncodeContext{Registry: r}, dst, val, canonical, escapeHTML)
 | |
| }
 | |
| 
 | |
| // MarshalExtJSONAppendWithContext will append the extended JSON encoding of
 | |
| // val to dst using Registry r. If dst is not large enough to hold the BSON
 | |
| // encoding of val, dst will be grown.
 | |
| //
 | |
| // Deprecated: Use [NewEncoder], pass the dst byte slice (wrapped by a bytes.Buffer) into
 | |
| // [bsonrw.NewExtJSONValueWriter], and use the Encoder configuration methods to set the desired marshal
 | |
| // behavior instead:
 | |
| //
 | |
| //	buf := bytes.NewBuffer(dst)
 | |
| //	vw, err := bsonrw.NewExtJSONValueWriter(buf, true, false)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc, err := bson.NewEncoder(vw)
 | |
| //	if err != nil {
 | |
| //		panic(err)
 | |
| //	}
 | |
| //	enc.IntMinSize()
 | |
| //
 | |
| // See [Encoder] for more examples.
 | |
| func MarshalExtJSONAppendWithContext(ec bsoncodec.EncodeContext, dst []byte, val interface{}, canonical, escapeHTML bool) ([]byte, error) {
 | |
| 	sw := new(bsonrw.SliceWriter)
 | |
| 	*sw = dst
 | |
| 	ejvw := extjPool.Get(sw, canonical, escapeHTML)
 | |
| 	defer extjPool.Put(ejvw)
 | |
| 
 | |
| 	enc := encPool.Get().(*Encoder)
 | |
| 	defer encPool.Put(enc)
 | |
| 
 | |
| 	err := enc.Reset(ejvw)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	err = enc.SetContext(ec)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	err = enc.Encode(val)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return *sw, nil
 | |
| }
 | |
| 
 | |
| // IndentExtJSON will prefix and indent the provided extended JSON src and append it to dst.
 | |
| func IndentExtJSON(dst *bytes.Buffer, src []byte, prefix, indent string) error {
 | |
| 	return json.Indent(dst, src, prefix, indent)
 | |
| }
 | |
| 
 | |
| // MarshalExtJSONIndent returns the extended JSON encoding of val with each line with prefixed
 | |
| // and indented.
 | |
| func MarshalExtJSONIndent(val interface{}, canonical, escapeHTML bool, prefix, indent string) ([]byte, error) {
 | |
| 	marshaled, err := MarshalExtJSON(val, canonical, escapeHTML)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	var buf bytes.Buffer
 | |
| 	err = IndentExtJSON(&buf, marshaled, prefix, indent)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return buf.Bytes(), nil
 | |
| }
 |