summaryrefslogtreecommitdiff
path: root/src/mongo/bson
diff options
context:
space:
mode:
authorIan Boros <ian.boros@mongodb.com>2020-11-18 15:56:15 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-12-04 19:39:52 +0000
commit1c0f7fc1b37c4495179765bd099bab5b603b76d5 (patch)
tree55c6b849ca9dab03a490b48f4d5a27a732fd651c /src/mongo/bson
parentf16974e78794ad1ce610df6a1566b4ba65ab284b (diff)
downloadmongo-1c0f7fc1b37c4495179765bd099bab5b603b76d5.tar.gz
SERVER-53183 Parameterize BSONObjBuilder by buffer allocator
Diffstat (limited to 'src/mongo/bson')
-rw-r--r--src/mongo/bson/bsonobjbuilder.cpp107
-rw-r--r--src/mongo/bson/bsonobjbuilder.h721
-rw-r--r--src/mongo/bson/bsonobjbuilder_test.cpp87
-rw-r--r--src/mongo/bson/timestamp.cpp8
-rw-r--r--src/mongo/bson/timestamp.h10
-rw-r--r--src/mongo/bson/util/builder.h55
6 files changed, 650 insertions, 338 deletions
diff --git a/src/mongo/bson/bsonobjbuilder.cpp b/src/mongo/bson/bsonobjbuilder.cpp
index 2467d381bbc..ec2cc72c6b8 100644
--- a/src/mongo/bson/bsonobjbuilder.cpp
+++ b/src/mongo/bson/bsonobjbuilder.cpp
@@ -40,7 +40,8 @@ namespace mongo {
using std::string;
-BSONObjBuilder& BSONObjBuilder::appendMinForType(StringData fieldName, int t) {
+template <class Derived, class B>
+Derived& BSONObjBuilderBase<Derived, B>::appendMinForType(StringData fieldName, int t) {
switch (t) {
// Shared canonical types
case NumberInt:
@@ -48,68 +49,69 @@ BSONObjBuilder& BSONObjBuilder::appendMinForType(StringData fieldName, int t) {
case NumberLong:
case NumberDecimal:
append(fieldName, std::numeric_limits<double>::quiet_NaN());
- return *this;
+ return static_cast<Derived&>(*this);
case Symbol:
case String:
append(fieldName, "");
- return *this;
+ return static_cast<Derived&>(*this);
case Date:
appendDate(fieldName, Date_t::min());
- return *this;
+ return static_cast<Derived&>(*this);
case bsonTimestamp:
appendTimestamp(fieldName, 0);
- return *this;
+ return static_cast<Derived&>(*this);
case Undefined: // shared with EOO
appendUndefined(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
// Separate canonical types
case MinKey:
appendMinKey(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
case MaxKey:
appendMaxKey(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
case jstOID: {
OID o;
appendOID(fieldName, &o);
- return *this;
+ return static_cast<Derived&>(*this);
}
case Bool:
appendBool(fieldName, false);
- return *this;
+ return static_cast<Derived&>(*this);
case jstNULL:
appendNull(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
case Object:
append(fieldName, BSONObj());
- return *this;
+ return static_cast<Derived&>(*this);
case Array:
appendArray(fieldName, BSONObj());
- return *this;
+ return static_cast<Derived&>(*this);
case BinData:
appendBinData(fieldName, 0, BinDataGeneral, (const char*)nullptr);
- return *this;
+ return static_cast<Derived&>(*this);
case RegEx:
appendRegex(fieldName, "");
- return *this;
+ return static_cast<Derived&>(*this);
case DBRef: {
OID o;
appendDBRef(fieldName, "", o);
- return *this;
+ return static_cast<Derived&>(*this);
}
case Code:
appendCode(fieldName, "");
- return *this;
+ return static_cast<Derived&>(*this);
case CodeWScope:
appendCodeWScope(fieldName, "", BSONObj());
- return *this;
+ return static_cast<Derived&>(*this);
};
LOGV2(20101, "type not supported for appendMinElementForType: {t}", "t"_attr = t);
uassert(10061, "type not supported for appendMinElementForType", false);
}
-BSONObjBuilder& BSONObjBuilder::appendMaxForType(StringData fieldName, int t) {
+template <class Derived, class B>
+Derived& BSONObjBuilderBase<Derived, B>::appendMaxForType(StringData fieldName, int t) {
switch (t) {
// Shared canonical types
case NumberInt:
@@ -117,83 +119,86 @@ BSONObjBuilder& BSONObjBuilder::appendMaxForType(StringData fieldName, int t) {
case NumberLong:
case NumberDecimal:
append(fieldName, std::numeric_limits<double>::infinity());
- return *this;
+ return static_cast<Derived&>(*this);
case Symbol:
case String:
appendMinForType(fieldName, Object);
- return *this;
+ return static_cast<Derived&>(*this);
case Date:
appendDate(fieldName, Date_t::max());
- return *this;
+ return static_cast<Derived&>(*this);
case bsonTimestamp:
append(fieldName, Timestamp::max());
- return *this;
+ return static_cast<Derived&>(*this);
case Undefined: // shared with EOO
appendUndefined(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
// Separate canonical types
case MinKey:
appendMinKey(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
case MaxKey:
appendMaxKey(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
case jstOID: {
OID o = OID::max();
appendOID(fieldName, &o);
- return *this;
+ return static_cast<Derived&>(*this);
}
case Bool:
appendBool(fieldName, true);
- return *this;
+ return static_cast<Derived&>(*this);
case jstNULL:
appendNull(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
case Object:
appendMinForType(fieldName, Array);
- return *this;
+ return static_cast<Derived&>(*this);
case Array:
appendMinForType(fieldName, BinData);
- return *this;
+ return static_cast<Derived&>(*this);
case BinData:
appendMinForType(fieldName, jstOID);
- return *this;
+ return static_cast<Derived&>(*this);
case RegEx:
appendMinForType(fieldName, DBRef);
- return *this;
+ return static_cast<Derived&>(*this);
case DBRef:
appendMinForType(fieldName, Code);
- return *this;
+ return static_cast<Derived&>(*this);
case Code:
appendMinForType(fieldName, CodeWScope);
- return *this;
+ return static_cast<Derived&>(*this);
case CodeWScope:
// This upper bound may change if a new bson type is added.
appendMinForType(fieldName, MaxKey);
- return *this;
+ return static_cast<Derived&>(*this);
}
LOGV2(20102, "type not supported for appendMaxElementForType: {t}", "t"_attr = t);
uassert(14853, "type not supported for appendMaxElementForType", false);
}
-BSONObjBuilder& BSONObjBuilder::appendDate(StringData fieldName, Date_t dt) {
+template <class Derived, class B>
+Derived& BSONObjBuilderBase<Derived, B>::appendDate(StringData fieldName, Date_t dt) {
_b.appendNum((char)Date);
_b.appendStr(fieldName);
_b.appendNum(dt.toMillisSinceEpoch());
- return *this;
+ return static_cast<Derived&>(*this);
}
/* add all the fields from the object specified to this object */
-BSONObjBuilder& BSONObjBuilder::appendElements(const BSONObj& x) {
+template <class Derived, class B>
+Derived& BSONObjBuilderBase<Derived, B>::appendElements(const BSONObj& x) {
if (!x.isEmpty())
_b.appendBuf(x.objdata() + 4, // skip over leading length
x.objsize() - 5); // ignore leading length and trailing \0
- return *this;
+ return static_cast<Derived&>(*this);
}
/* add all the fields from the object specified to this object if they don't exist */
-BSONObjBuilder& BSONObjBuilder::appendElementsUnique(const BSONObj& x) {
+template <class Derived, class B>
+Derived& BSONObjBuilderBase<Derived, B>::appendElementsUnique(const BSONObj& x) {
std::set<std::string> have;
{
BSONObjIterator i = iterator();
@@ -208,16 +213,18 @@ BSONObjBuilder& BSONObjBuilder::appendElementsUnique(const BSONObj& x) {
continue;
append(e);
}
- return *this;
+ return static_cast<Derived&>(*this);
}
-BSONObjIterator BSONObjBuilder::iterator() const {
+template <class Derived, class B>
+BSONObjIterator BSONObjBuilderBase<Derived, B>::iterator() const {
const char* s = _b.buf() + _offset;
const char* e = _b.buf() + _b.len();
return BSONObjIterator(s, e);
}
-bool BSONObjBuilder::hasField(StringData name) const {
+template <class Derived, class B>
+bool BSONObjBuilderBase<Derived, B>::hasField(StringData name) const {
BSONObjIterator i = iterator();
while (i.more())
if (name == i.next().fieldName())
@@ -225,15 +232,9 @@ bool BSONObjBuilder::hasField(StringData name) const {
return false;
}
-BSONObjBuilder::~BSONObjBuilder() {
- // If 'done' has not already been called, and we have a reference to an owning
- // BufBuilder but do not own it ourselves, then we must call _done to write in the
- // length. Otherwise, we own this memory and its lifetime ends with us, therefore
- // we can elide the write.
- if (!_doneCalled && _b.buf() && _buf.getSize() == 0) {
- _done();
- }
-}
+// Explicit instantiations
+template class BSONObjBuilderBase<BSONObjBuilder, BufBuilder>;
+template class BSONObjBuilderBase<UniqueBSONObjBuilder, UniqueBufBuilder>;
template class StringBuilderImpl<BufBuilder>;
template class StringBuilderImpl<StackBufBuilderBase<StackSizeDefault>>;
diff --git a/src/mongo/bson/bsonobjbuilder.h b/src/mongo/bson/bsonobjbuilder.h
index de68ff65353..8dcc098bba6 100644
--- a/src/mongo/bson/bsonobjbuilder.h
+++ b/src/mongo/bson/bsonobjbuilder.h
@@ -29,12 +29,6 @@
#pragma once
-/**
- * Classes in this file:
- * BSONObjBuilder
- * BSONArrayBuilder
- */
-
#include <cmath>
#include <cstdint>
#include <limits>
@@ -59,16 +53,27 @@ namespace mongo {
#pragma warning(disable : 4355)
#endif
-/** Utility for creating a BSONObj.
- See also the BSON() and BSON_ARRAY() macros.
-*/
-class BSONObjBuilder {
- BSONObjBuilder(const BSONObjBuilder&) = delete;
- BSONObjBuilder& operator=(const BSONObjBuilder&) = delete;
+/**
+ * CRTP Base class for BSONObj builder classes. Do not use this directly.
+ *
+ * Template arguments:
+ * -Derived: The Derived class
+ * -B: The buffer builder to use. See BufBuilder for an example.
+ *
+ * If adding a new subclass of this, an explicit template instantiation must be added in the cpp
+ * file. Derived classes also must implement a destructor which calls the base class _destruct()
+ * method.
+ */
+template <class Derived, class B>
+class BSONObjBuilderBase {
+ BSONObjBuilderBase(const BSONObjBuilderBase<Derived, B>&) = delete;
+ BSONObjBuilderBase& operator=(const BSONObjBuilderBase<Derived, B>&) = delete;
public:
+ BSONObjBuilderBase() : BSONObjBuilderBase(kDefaultSize) {}
+
/** @param initsize this is just a hint as to the final size of the object */
- BSONObjBuilder(int initsize = 512) : _b(_buf), _buf(initsize), _s(this) {
+ BSONObjBuilderBase(int initsize) : _b(_buf), _buf(initsize) {
// Skip over space for the object length. The length is filled in by _done.
_b.skip(sizeof(int));
@@ -80,8 +85,7 @@ public:
* This is for more efficient adding of subobjects/arrays. See docs for subobjStart for
* example.
*/
- BSONObjBuilder(BufBuilder& baseBuilder)
- : _b(baseBuilder), _buf(0), _offset(baseBuilder.len()), _s(this) {
+ BSONObjBuilderBase(B& baseBuilder) : _b(baseBuilder), _buf(0), _offset(baseBuilder.len()) {
// Skip over space for the object length, which is filled in by _done. We don't need a
// holder since we are a sub-builder, and some parent builder has already made the
// reservation.
@@ -95,19 +99,16 @@ public:
// building in to an existing BufBuilder that has already been built in to. Use with caution.
struct ResumeBuildingTag {};
- BSONObjBuilder(ResumeBuildingTag, BufBuilder& existingBuilder, std::size_t offset = 0)
- : _b(existingBuilder), _buf(0), _offset(offset), _s(this) {
+ BSONObjBuilderBase(ResumeBuildingTag, B& existingBuilder, std::size_t offset = 0)
+ : _b(existingBuilder), _buf(0), _offset(offset) {
invariant(_b.len() - offset >= BSONObj::kMinBSONLength);
_b.setlen(_b.len() - 1); // get rid of the previous EOO.
// Reserve space for our EOO.
_b.reserveBytes(1);
}
- BSONObjBuilder(const BSONSizeTracker& tracker)
- : _b(_buf),
- _buf(tracker.getSize()),
- _s(this),
- _tracker(const_cast<BSONSizeTracker*>(&tracker)) {
+ BSONObjBuilderBase(const BSONSizeTracker& tracker)
+ : _b(_buf), _buf(tracker.getSize()), _tracker(const_cast<BSONSizeTracker*>(&tracker)) {
// See the comments in the first constructor for details.
_b.skip(sizeof(int));
@@ -115,45 +116,16 @@ public:
_b.reserveBytes(1);
}
- /**
- * Creates a new BSONObjBuilder prefixed with the fields in 'prefix'.
- *
- * If prefix is an rvalue referring to the only view of the underlying BSON buffer, it will be
- * able to avoid copying and will just reuse the buffer. Therefore, you should try to std::move
- * into this constructor where possible.
- */
- BSONObjBuilder(BSONObj prefix) : _b(_buf), _buf(0), _s(this) {
- // If prefix wasn't owned or we don't have exclusive access to it, we must copy.
- if (!prefix.isOwned() || prefix.sharedBuffer().isShared()) {
- _b.grow(prefix.objsize()); // Make sure we won't need to realloc().
- _b.setlen(sizeof(int)); // Skip over size bytes (see first constructor).
- _b.reserveBytes(1); // Reserve room for our EOO byte.
- appendElements(prefix);
- return;
- }
-
- const auto size = prefix.objsize();
- const char* const firstByte = prefix.objdata();
- auto buf = prefix.releaseSharedBuffer().constCast();
- _offset = firstByte - buf.get();
- _b.useSharedBuffer(std::move(buf));
- _b.setlen(_offset + size - 1); // Position right before prefix's EOO byte.
- _b.reserveBytes(1); // Reserve room for our EOO byte.
- }
-
// Move constructible, but not assignable due to reference member.
- BSONObjBuilder(BSONObjBuilder&& other)
+ BSONObjBuilderBase(BSONObjBuilderBase<Derived, B>&& other)
: _b(&other._b == &other._buf ? _buf : other._b),
_buf(std::move(other._buf)),
_offset(std::move(other._offset)),
- _s(this), // Don't move from other._s because that will leave it pointing to other.
_tracker(std::move(other._tracker)),
_doneCalled(std::move(other._doneCalled)) {
other.abandon();
}
- ~BSONObjBuilder();
-
/**
* The start offset of the object being built by this builder within its buffer.
* Needed for the object-resuming constructor.
@@ -163,39 +135,39 @@ public:
}
/** add all the fields from the object specified to this object */
- BSONObjBuilder& appendElements(const BSONObj& x);
+ Derived& appendElements(const BSONObj& x);
/** add all the fields from the object specified to this object if they don't exist already */
- BSONObjBuilder& appendElementsUnique(const BSONObj& x);
+ Derived& appendElementsUnique(const BSONObj& x);
/** append element to the object we are building */
- BSONObjBuilder& append(const BSONElement& e) {
+ Derived& append(const BSONElement& e) {
// do not append eoo, that would corrupt us. the builder auto appends when done() is called.
verify(!e.eoo());
_b.appendBuf((void*)e.rawdata(), e.size());
- return *this;
+ return static_cast<Derived&>(*this);
}
/** append an element but with a new name */
- BSONObjBuilder& appendAs(const BSONElement& e, StringData fieldName) {
+ Derived& appendAs(const BSONElement& e, StringData fieldName) {
// do not append eoo, that would corrupt us. the builder auto appends when done() is called.
verify(!e.eoo());
_b.appendNum((char)e.type());
_b.appendStr(fieldName);
_b.appendBuf((void*)e.value(), e.valuesize());
- return *this;
+ return static_cast<Derived&>(*this);
}
/** add a subobject as a member */
- BSONObjBuilder& append(StringData fieldName, BSONObj subObj) {
+ Derived& append(StringData fieldName, BSONObj subObj) {
_b.appendNum((char)Object);
_b.appendStr(fieldName);
_b.appendBuf((void*)subObj.objdata(), subObj.objsize());
- return *this;
+ return static_cast<Derived&>(*this);
}
/** add a subobject as a member */
- BSONObjBuilder& appendObject(StringData fieldName, const char* objdata, int size = 0) {
+ Derived& appendObject(StringData fieldName, const char* objdata, int size = 0) {
verify(objdata);
if (size == 0) {
size = ConstDataView(objdata).read<LittleEndian<int>>();
@@ -206,7 +178,7 @@ public:
_b.appendNum((char)Object);
_b.appendStr(fieldName);
_b.appendBuf((void*)objdata, size);
- return *this;
+ return static_cast<Derived&>(*this);
}
/** add header for a new subobject and return bufbuilder for writing to
@@ -220,7 +192,7 @@ public:
* sub.done()
* // use b and convert to object
*/
- BufBuilder& subobjStart(StringData fieldName) {
+ B& subobjStart(StringData fieldName) {
_b.appendNum((char)Object);
_b.appendStr(fieldName);
return _b;
@@ -229,35 +201,36 @@ public:
/** add a subobject as a member with type Array. Thus arr object should have "0", "1", ...
style fields in it.
*/
- BSONObjBuilder& appendArray(StringData fieldName, const BSONObj& subObj) {
+ Derived& appendArray(StringData fieldName, const BSONObj& subObj) {
_b.appendNum((char)Array);
_b.appendStr(fieldName);
_b.appendBuf((void*)subObj.objdata(), subObj.objsize());
- return *this;
+
+ return static_cast<Derived&>(*this);
}
- BSONObjBuilder& append(StringData fieldName, BSONArray arr) {
+ Derived& append(StringData fieldName, BSONArray arr) {
return appendArray(fieldName, arr);
}
/** add header for a new subarray and return bufbuilder for writing to
the subarray's body */
- BufBuilder& subarrayStart(StringData fieldName) {
+ B& subarrayStart(StringData fieldName) {
_b.appendNum((char)Array);
_b.appendStr(fieldName);
return _b;
}
/** Append a boolean element */
- BSONObjBuilder& appendBool(StringData fieldName, int val) {
+ Derived& appendBool(StringData fieldName, int val) {
_b.appendNum((char)Bool);
_b.appendStr(fieldName);
_b.appendNum((char)(val ? 1 : 0));
- return *this;
+ return static_cast<Derived&>(*this);
}
/** Append elements that have the BSONObjAppendFormat trait */
template <typename T, typename = std::enable_if_t<IsBSONObjAppendable<T>::value>>
- BSONObjBuilder& append(StringData fieldName, const T& n) {
+ Derived& append(StringData fieldName, const T& n) {
constexpr BSONType type = BSONObjAppendFormat<T>::value;
_b.appendNum(static_cast<char>(type));
_b.appendStr(fieldName);
@@ -268,16 +241,16 @@ public:
} else {
_b.appendNum(n);
}
- return *this;
+ return static_cast<Derived&>(*this);
}
template <typename T,
typename = std::enable_if_t<!IsBSONObjAppendable<T>::value && std::is_integral_v<T>>,
typename = void>
- BSONObjBuilder& append(StringData fieldName, const T& n) = delete;
+ Derived& append(StringData fieldName, const T& n) = delete;
/** appends a number. if n < max(int)/2 then uses int, otherwise long long */
- BSONObjBuilder& appendIntOrLL(StringData fieldName, long long n) {
+ Derived& appendIntOrLL(StringData fieldName, long long n) {
// extra () to avoid max macro on windows
static const long long maxInt = (std::numeric_limits<int>::max)() / 2;
static const long long minInt = -maxInt;
@@ -286,35 +259,35 @@ public:
} else {
append(fieldName, n);
}
- return *this;
+ return static_cast<Derived&>(*this);
}
/**
* appendNumber is a series of method for appending the smallest sensible type
* mostly for JS
*/
- BSONObjBuilder& appendNumber(StringData fieldName, int n) {
+ Derived& appendNumber(StringData fieldName, int n) {
return append(fieldName, n);
}
- BSONObjBuilder& appendNumber(StringData fieldName, double d) {
+ Derived& appendNumber(StringData fieldName, double d) {
return append(fieldName, d);
}
- BSONObjBuilder& appendNumber(StringData fieldName, size_t n) {
+ Derived& appendNumber(StringData fieldName, size_t n) {
static const size_t maxInt = (1 << 30);
if (n < maxInt)
append(fieldName, static_cast<int>(n));
else
append(fieldName, static_cast<long long>(n));
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONObjBuilder& appendNumber(StringData fieldName, Decimal128 decNumber) {
+ Derived& appendNumber(StringData fieldName, Decimal128 decNumber) {
return append(fieldName, decNumber);
}
- BSONObjBuilder& appendNumber(StringData fieldName, long long llNumber) {
+ Derived& appendNumber(StringData fieldName, long long llNumber) {
static const long long maxInt = (1LL << 30);
static const long long minInt = -maxInt;
static const long long maxDouble = (1LL << 40);
@@ -328,16 +301,14 @@ public:
append(fieldName, llNumber);
}
- return *this;
+ return static_cast<Derived&>(*this);
}
/** Append a BSON Object ID (OID type).
@deprecated Generally, it is preferred to use the append append(name, oid)
method for this.
*/
- BSONObjBuilder& appendOID(StringData fieldName,
- OID* oid = nullptr,
- bool generateIfBlank = false) {
+ Derived& appendOID(StringData fieldName, OID* oid = nullptr, bool generateIfBlank = false) {
_b.appendNum((char)jstOID);
_b.appendStr(fieldName);
if (oid)
@@ -350,7 +321,7 @@ public:
tmp.clear();
_b.appendBuf(tmp.view().view(), OID::kOIDSize);
}
- return *this;
+ return static_cast<Derived&>(*this);
}
/**
@@ -358,18 +329,18 @@ public:
@param fieldName Field name, e.g., "_id".
@returns the builder object
*/
- BSONObjBuilder& append(StringData fieldName, OID oid) {
+ Derived& append(StringData fieldName, OID oid) {
_b.appendNum((char)jstOID);
_b.appendStr(fieldName);
_b.appendBuf(oid.view().view(), OID::kOIDSize);
- return *this;
+ return static_cast<Derived&>(*this);
}
/**
Generate and assign an object id for the _id field.
_id should be the first element in the object for good performance.
*/
- BSONObjBuilder& genOID() {
+ Derived& genOID() {
return append("_id", OID::gen());
}
@@ -377,18 +348,18 @@ public:
@param dt a C-style 32 bit date value, that is
the number of seconds since January 1, 1970, 00:00:00 GMT
*/
- BSONObjBuilder& appendTimeT(StringData fieldName, time_t dt) {
+ Derived& appendTimeT(StringData fieldName, time_t dt) {
_b.appendNum((char)Date);
_b.appendStr(fieldName);
_b.appendNum(static_cast<unsigned long long>(dt) * 1000);
- return *this;
+ return static_cast<Derived&>(*this);
}
/** Append a date.
@param dt a Java-style 64 bit date value, that is
the number of milliseconds since January 1, 1970, 00:00:00 GMT
*/
- BSONObjBuilder& appendDate(StringData fieldName, Date_t dt);
- BSONObjBuilder& append(StringData fieldName, Date_t dt) {
+ Derived& appendDate(StringData fieldName, Date_t dt);
+ Derived& append(StringData fieldName, Date_t dt) {
return appendDate(fieldName, dt);
}
@@ -396,109 +367,113 @@ public:
@param regex the regular expression pattern
@param regex options such as "i" or "g"
*/
- BSONObjBuilder& appendRegex(StringData fieldName, StringData regex, StringData options = "") {
+ Derived& appendRegex(StringData fieldName, StringData regex, StringData options = "") {
_b.appendNum((char)RegEx);
_b.appendStr(fieldName);
_b.appendStr(regex);
_b.appendStr(options);
- return *this;
+
+ return static_cast<Derived&>(*this);
}
- BSONObjBuilder& append(StringData fieldName, const BSONRegEx& regex) {
+ Derived& append(StringData fieldName, const BSONRegEx& regex) {
return appendRegex(fieldName, regex.pattern, regex.flags);
}
- BSONObjBuilder& appendCode(StringData fieldName, StringData code) {
+ Derived& appendCode(StringData fieldName, StringData code) {
_b.appendNum((char)Code);
_b.appendStr(fieldName);
_b.appendNum((int)code.size() + 1);
_b.appendStr(code);
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONObjBuilder& append(StringData fieldName, const BSONCode& code) {
+ Derived& append(StringData fieldName, const BSONCode& code) {
return appendCode(fieldName, code.code);
}
/** Append a string element.
@param sz size includes terminating null character */
- BSONObjBuilder& append(StringData fieldName, const char* str, int sz) {
+ Derived& append(StringData fieldName, const char* str, int sz) {
_b.appendNum((char)String);
_b.appendStr(fieldName);
_b.appendNum((int)sz);
_b.appendBuf(str, sz);
- return *this;
+
+ return static_cast<Derived&>(*this);
}
/** Append a string element */
- BSONObjBuilder& append(StringData fieldName, const char* str) {
+ Derived& append(StringData fieldName, const char* str) {
return append(fieldName, str, (int)strlen(str) + 1);
}
/** Append a string element */
- BSONObjBuilder& append(StringData fieldName, StringData str) {
+ Derived& append(StringData fieldName, StringData str) {
_b.appendNum((char)String);
_b.appendStr(fieldName);
_b.appendNum((int)str.size() + 1);
_b.appendStr(str, true);
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONObjBuilder& appendSymbol(StringData fieldName, StringData symbol) {
+ Derived& appendSymbol(StringData fieldName, StringData symbol) {
_b.appendNum((char)Symbol);
_b.appendStr(fieldName);
_b.appendNum((int)symbol.size() + 1);
_b.appendStr(symbol);
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONObjBuilder& append(StringData fieldName, const BSONSymbol& symbol) {
+ Derived& append(StringData fieldName, const BSONSymbol& symbol) {
return appendSymbol(fieldName, symbol.symbol);
}
/** Append a Null element to the object */
- BSONObjBuilder& appendNull(StringData fieldName) {
+ Derived& appendNull(StringData fieldName) {
_b.appendNum((char)jstNULL);
_b.appendStr(fieldName);
- return *this;
+
+ return static_cast<Derived&>(*this);
}
// Append an element that is less than all other keys.
- BSONObjBuilder& appendMinKey(StringData fieldName) {
+ Derived& appendMinKey(StringData fieldName) {
_b.appendNum((char)MinKey);
_b.appendStr(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
}
// Append an element that is greater than all other keys.
- BSONObjBuilder& appendMaxKey(StringData fieldName) {
+ Derived& appendMaxKey(StringData fieldName) {
_b.appendNum((char)MaxKey);
_b.appendStr(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
}
// Append a Timestamp field -- will be updated to next server Timestamp
- BSONObjBuilder& appendTimestamp(StringData fieldName);
+ Derived& appendTimestamp(StringData fieldName);
- BSONObjBuilder& appendTimestamp(StringData fieldName, unsigned long long val);
+ Derived& appendTimestamp(StringData fieldName, unsigned long long val);
/**
* To store a Timestamp in BSON, use this function.
* This captures both the secs and inc fields.
*/
- BSONObjBuilder& append(StringData fieldName, Timestamp timestamp);
+ Derived& append(StringData fieldName, Timestamp timestamp);
/*
Append an element of the deprecated DBRef type.
@deprecated
*/
- BSONObjBuilder& appendDBRef(StringData fieldName, StringData ns, const OID& oid) {
+ Derived& appendDBRef(StringData fieldName, StringData ns, const OID& oid) {
_b.appendNum((char)DBRef);
_b.appendStr(fieldName);
_b.appendNum((int)ns.size() + 1);
_b.appendStr(ns);
_b.appendBuf(oid.view().view(), OID::kOIDSize);
- return *this;
+
+ return static_cast<Derived&>(*this);
}
- BSONObjBuilder& append(StringData fieldName, const BSONDBRef& dbref) {
+ Derived& append(StringData fieldName, const BSONDBRef& dbref) {
return appendDBRef(fieldName, dbref.ns, dbref.oid);
}
@@ -509,19 +484,17 @@ public:
Use BinDataGeneral if you don't care about the type.
@param data the byte array
*/
- BSONObjBuilder& appendBinData(StringData fieldName,
- int len,
- BinDataType type,
- const void* data) {
+ Derived& appendBinData(StringData fieldName, int len, BinDataType type, const void* data) {
_b.appendNum((char)BinData);
_b.appendStr(fieldName);
_b.appendNum(len);
_b.appendNum((char)type);
_b.appendBuf(data, len);
- return *this;
+
+ return static_cast<Derived&>(*this);
}
- BSONObjBuilder& append(StringData fieldName, const BSONBinData& bd) {
+ Derived& append(StringData fieldName, const BSONBinData& bd) {
return appendBinData(fieldName, bd.length, bd.type, bd.data);
}
@@ -531,108 +504,97 @@ public:
@param data a byte array
@param len the length of data
*/
- BSONObjBuilder& appendBinDataArrayDeprecated(const char* fieldName, const void* data, int len) {
+ Derived& appendBinDataArrayDeprecated(const char* fieldName, const void* data, int len) {
_b.appendNum((char)BinData);
_b.appendStr(fieldName);
_b.appendNum(len + 4);
_b.appendNum((char)0x2);
_b.appendNum(len);
_b.appendBuf(data, len);
- return *this;
+
+ return static_cast<Derived&>(*this);
}
/** Append to the BSON object a field of type CodeWScope. This is a javascript code
fragment accompanied by some scope that goes with it.
*/
- BSONObjBuilder& appendCodeWScope(StringData fieldName, StringData code, const BSONObj& scope) {
+ Derived& appendCodeWScope(StringData fieldName, StringData code, const BSONObj& scope) {
_b.appendNum((char)CodeWScope);
_b.appendStr(fieldName);
_b.appendNum((int)(4 + 4 + code.size() + 1 + scope.objsize()));
_b.appendNum((int)code.size() + 1);
_b.appendStr(code);
_b.appendBuf((void*)scope.objdata(), scope.objsize());
- return *this;
+
+ return static_cast<Derived&>(*this);
}
- BSONObjBuilder& append(StringData fieldName, const BSONCodeWScope& cws) {
+ Derived& append(StringData fieldName, const BSONCodeWScope& cws) {
return appendCodeWScope(fieldName, cws.code, cws.scope);
}
- BSONObjBuilder& appendUndefined(StringData fieldName) {
+ Derived& appendUndefined(StringData fieldName) {
_b.appendNum((char)Undefined);
_b.appendStr(fieldName);
- return *this;
+ return static_cast<Derived&>(*this);
}
/* helper function -- see Query::where() for primary way to do this. */
- BSONObjBuilder& appendWhere(StringData code, const BSONObj& scope) {
+ Derived& appendWhere(StringData code, const BSONObj& scope) {
return appendCodeWScope("$where", code, scope);
}
/**
these are the min/max when comparing, not strict min/max elements for a given type
*/
- BSONObjBuilder& appendMinForType(StringData fieldName, int type);
- BSONObjBuilder& appendMaxForType(StringData fieldName, int type);
+ Derived& appendMinForType(StringData fieldName, int type);
+ Derived& appendMaxForType(StringData fieldName, int type);
/** Append an array of values. */
template <class T>
- BSONObjBuilder& append(StringData fieldName, const std::vector<T>& vals);
+ Derived& append(StringData fieldName, const std::vector<T>& vals);
template <class T>
- BSONObjBuilder& append(StringData fieldName, const std::list<T>& vals);
+ Derived& append(StringData fieldName, const std::list<T>& vals);
/** Append a set of values. */
template <class T>
- BSONObjBuilder& append(StringData fieldName, const std::set<T>& vals);
+ Derived& append(StringData fieldName, const std::set<T>& vals);
/**
* Append a map of values as a sub-object.
* Note: the keys of the map should be StringData-compatible (i.e. strings).
*/
template <class K, class T>
- BSONObjBuilder& append(StringData fieldName, const std::map<K, T>& vals);
+ Derived& append(StringData fieldName, const std::map<K, T>& vals);
/**
* Resets this BSONObjBulder to an empty state. All previously added fields are lost. If this
- * BSONObjBuilder is using an externally provided BufBuilder, this method does not affect the
- * bytes before the start of this object.
+ * BSONObjBuilderBase is using an externally provided BufBuilder, this method does not affect
+ * the bytes before the start of this object.
*
* Invalid to call if done() has already been called in order to finalize the BSONObj.
*/
void resetToEmpty() {
invariant(!_doneCalled);
- _s.reset();
+ static_cast<Derived*>(this)->doResetToEmpty();
// Reset the position the next write will go to right after our size reservation.
_b.setlen(_offset + sizeof(int));
}
- /**
- * destructive
- * The returned BSONObj will free the buffer when it is finished.
- * @return owned BSONObj
- */
- template <typename BSONTraits = BSONObj::DefaultSizeTrait>
- BSONObj obj() {
- massert(10335, "builder does not own memory", owned());
- auto out = done<BSONTraits>();
- out.shareOwnershipWith(_b.release());
- return out;
- }
-
/** Fetch the object we have built.
- BSONObjBuilder still frees the object when the builder goes out of
+ BSONObjBuilderBase still frees the object when the builder goes out of
scope -- very important to keep in mind. Use obj() if you
would like the BSONObj to last longer than the builder.
*/
template <typename BSONTraits = BSONObj::DefaultSizeTrait>
BSONObj done() {
- return BSONObj(_done(), BSONTraits{});
+ return BSONObj(static_cast<Derived*>(this)->_done(), BSONTraits{});
}
// Like 'done' above, but does not construct a BSONObj to return to the caller.
void doneFast() {
- (void)_done();
+ (void)static_cast<Derived*>(this)->_done();
}
/** Peek at what is in the builder, but leave the builder ready for more appends.
@@ -640,7 +602,7 @@ public:
Intended use case: append a field if not already there.
*/
BSONObj asTempObj() {
- const char* const buffer = _done();
+ const char* const buffer = static_cast<Derived*>(this)->_done();
// None of the code which resets this builder to the not-done state is expected to throw.
// If it does, that would be a violation of our expectations.
@@ -667,39 +629,6 @@ public:
_doneCalled = true;
}
- /** Stream oriented way to add field names and values. */
- BSONObjBuilderValueStream& operator<<(StringData name) {
- _s.endField(name);
- return _s;
- }
-
- /** Stream oriented way to add field names and values. */
- BSONObjBuilder& operator<<(GENOIDLabeler) {
- return genOID();
- }
-
- Labeler operator<<(const Labeler::Label& l) {
- massert(10336, "No subobject started", _s.subobjStarted());
- return _s << l;
- }
-
- template <typename T>
- BSONObjBuilderValueStream& operator<<(const BSONField<T>& f) {
- _s.endField(f.name());
- return _s;
- }
-
- template <typename T>
- BSONObjBuilder& operator<<(const BSONFieldValue<T>& v) {
- append(v.name(), v.value());
- return *this;
- }
-
- BSONObjBuilder& operator<<(const BSONElement& e) {
- append(e);
- return *this;
- }
-
bool isArray() const {
return false;
}
@@ -718,18 +647,28 @@ public:
return _b.len();
}
- BufBuilder& bb() {
+ B& bb() {
return _b;
}
-private:
+protected:
+ constexpr static size_t kDefaultSize = 512;
+
+ // Initializes the builder without allocating any space. Only used by subclasses.
+ struct InitEmptyTag {};
+ BSONObjBuilderBase(InitEmptyTag) : _b(_buf), _buf(0) {}
+
+ // Intentionally non-virtual.
+ ~BSONObjBuilderBase() {
+ // It is the derived class's responsibility to ensure that done() is called.
+ invariant(!needsDone());
+ }
+
char* _done() {
if (_doneCalled)
return _b.buf() + _offset;
- // TODO remove this or find some way to prevent it from failing. Since this is intended
- // for use with BSON() literal queries, it is less likely to result in oversized BSON.
- _s.endField();
+ static_cast<Derived*>(this)->doDone();
_b.claimReservedBytes(1); // Prevents adding EOO from failing.
_b.appendNum((char)EOO);
@@ -745,65 +684,242 @@ private:
return data;
}
- BufBuilder& _b;
- BufBuilder _buf;
+ bool needsDone() const {
+ // If 'done' has not already been called, and we have a reference to an owning
+ // BufBuilder but do not own it ourselves, then we must call _done to write in the
+ // length. Otherwise, we own this memory and its lifetime ends with us, therefore
+ // we can elide the write.
+ return !_doneCalled && _b.buf() && _buf.getSize() == 0;
+ }
+
+ // Must be called by derived class destructors.
+ void _destruct() {
+ if (needsDone()) {
+ _done();
+ }
+ }
+
+
+ B& _b;
+ B _buf;
int _offset = 0;
- BSONObjBuilderValueStream _s;
BSONSizeTracker* _tracker = nullptr;
bool _doneCalled = false;
};
-class BSONArrayBuilder {
+/**
+ * "Standard" class used for constructing BSONObj on the fly. Stores the BSON in a refcounted
+ * buffer.
+ */
+class BSONObjBuilder : public BSONObjBuilderBase<BSONObjBuilder, BufBuilder> {
+private:
+ using Super = BSONObjBuilderBase<BSONObjBuilder, BufBuilder>;
+ friend Super;
+
+public:
+ BSONObjBuilder() : Super(kDefaultSize), _s(this) {}
+
+ BSONObjBuilder(int initsize) : Super(initsize), _s(this) {}
+
+ BSONObjBuilder(BufBuilder& baseBuilder) : Super(baseBuilder), _s(this) {}
+
+ BSONObjBuilder(ResumeBuildingTag, BufBuilder& existingBuilder, std::size_t offset = 0)
+ : Super(ResumeBuildingTag{}, existingBuilder, offset), _s(this) {}
+
+ BSONObjBuilder(const BSONSizeTracker& tracker) : Super(tracker), _s(this) {}
+
+ /**
+ * Creates a new BSONObjBuilder prefixed with the fields in 'prefix'.
+ *
+ * If prefix is an rvalue referring to the only view of the underlying BSON buffer, it will be
+ * able to avoid copying and will just reuse the buffer. Therefore, you should try to std::move
+ * into this constructor where possible.
+ */
+ BSONObjBuilder(BSONObj prefix) : Super(Super::InitEmptyTag{}), _s(this) {
+ // If prefix wasn't owned or we don't have exclusive access to it, we must copy.
+ if (!prefix.isOwned() || prefix.sharedBuffer().isShared()) {
+ _b.grow(prefix.objsize()); // Make sure we won't need to realloc().
+ _b.setlen(sizeof(int)); // Skip over size bytes (see first constructor).
+ _b.reserveBytes(1); // Reserve room for our EOO byte.
+ appendElements(prefix);
+ return;
+ }
+
+ const auto size = prefix.objsize();
+ const char* const firstByte = prefix.objdata();
+ auto buf = prefix.releaseSharedBuffer().constCast();
+ _offset = firstByte - buf.get();
+ _b.useSharedBuffer(std::move(buf));
+ _b.setlen(_offset + size - 1); // Position right before prefix's EOO byte.
+ _b.reserveBytes(1); // Reserve room for our EOO byte.
+ }
+
+ BSONObjBuilder(BSONObjBuilder&& other) : Super(std::move(other)), _s(this) {}
+
+ ~BSONObjBuilder() {
+ Super::_destruct();
+ }
+
+ /**
+ * destructive
+ * The returned BSONObj will free the buffer when it is finished.
+ * @return owned BSONObj
+ */
+ template <typename BSONTraits = BSONObj::DefaultSizeTrait>
+ BSONObj obj() {
+ massert(10335, "builder does not own memory", owned());
+ auto out = done<BSONTraits>();
+ out.shareOwnershipWith(_b.release());
+ return out;
+ }
+
+ /** Stream oriented way to add field names and values. */
+ BSONObjBuilder& operator<<(GENOIDLabeler) {
+ genOID();
+ return *this;
+ }
+
+ template <typename T>
+ BSONObjBuilder& operator<<(const BSONFieldValue<T>& v) {
+ append(v.name(), v.value());
+ return *this;
+ }
+
+ BSONObjBuilder& operator<<(const BSONElement& e) {
+ append(e);
+ return *this;
+ }
+
+ /** Stream oriented way to add field names and values. */
+ BSONObjBuilderValueStream& operator<<(StringData name) {
+ _s.endField(name);
+ return _s;
+ }
+
+ Labeler operator<<(const Labeler::Label& l) {
+ massert(10336, "No subobject started", _s.subobjStarted());
+ return _s << l;
+ }
+
+ template <typename T>
+ BSONObjBuilderValueStream& operator<<(const BSONField<T>& f) {
+ _s.endField(f.name());
+ return _s;
+ }
+
+private:
+ // Compile-time "virtual" methods called by the base class.
+ void doDone() {
+ _s.endField();
+ }
+
+ void doResetToEmpty() {
+ _s.reset();
+ }
+
+ BSONObjBuilderValueStream _s;
+};
+
+/**
+ * Alternative to BSONObjBuilder which uses a non-refcounted buffer (UniqueBuffer) instead of a
+ * refcounted buffer (SharedBuffer).
+ *
+ * This should only be used when you care about having direct ownership over the BSONObj's
+ * underlying memory.
+ */
+class UniqueBSONObjBuilder : public BSONObjBuilderBase<UniqueBSONObjBuilder, UniqueBufBuilder> {
+private:
+ using Super = BSONObjBuilderBase<UniqueBSONObjBuilder, UniqueBufBuilder>;
+ friend Super;
+
+public:
+ using Super::BSONObjBuilderBase;
+
+ /**
+ * Creates a new UniqueBSONObjBuilder prefixed with the fields in 'prefix'.
+ */
+ UniqueBSONObjBuilder(BSONObj prefix) : Super(Super::InitEmptyTag{}) {
+ _b.grow(prefix.objsize()); // Make sure we won't need to realloc().
+ _b.setlen(sizeof(int)); // Skip over size bytes (see first constructor).
+ _b.reserveBytes(1); // Reserve room for our EOO byte.
+ appendElements(prefix);
+ }
+
+ UniqueBSONObjBuilder(UniqueBSONObjBuilder&&) = default;
+ UniqueBSONObjBuilder(const UniqueBSONObjBuilder&) = delete;
+ UniqueBSONObjBuilder& operator=(UniqueBSONObjBuilder&&) = default;
+ UniqueBSONObjBuilder& operator=(const UniqueBSONObjBuilder&) = delete;
+
+ ~UniqueBSONObjBuilder() {
+ Super::_destruct();
+ }
+
+ /**
+ * destructive
+ * The returned BSONObj will free the buffer when it is finished.
+ */
+ template <typename BSONTraits = BSONObj::DefaultSizeTrait>
+ BSONObj obj() {
+ massert(5318300, "builder does not own memory", owned());
+ auto out = done<BSONTraits>();
+ out.shareOwnershipWith(SharedBuffer(_b.release()));
+ return out;
+ }
+
+private:
+ // Compile-time "virtual" which must be provided to satisfy the base class.
+ void doDone() {
+ // Intentionally left empty.
+ }
+
+ void doResetToEmpty() {
+ // Intentionally left empty.
+ }
+};
+
+/**
+ * Base class for building BSON arrays. Similar to BSONObjBuilderBase.
+ */
+template <class Derived, class BSONObjBuilderType>
+class BSONArrayBuilderBase {
public:
- BSONArrayBuilder() {}
- BSONArrayBuilder(BufBuilder& _b) : _b(_b) {}
- BSONArrayBuilder(int initialSize) : _b(initialSize) {}
+ BSONArrayBuilderBase() {}
+ BSONArrayBuilderBase(int initialSize) : _b(initialSize) {}
template <typename T>
- BSONArrayBuilder& append(const T& x) {
+ Derived& append(const T& x) {
_b.append(_fieldCount, x);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& append(const BSONElement& e) {
+ Derived& append(const BSONElement& e) {
_b.appendAs(e, _fieldCount);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& operator<<(const BSONElement& e) {
+ Derived& operator<<(const BSONElement& e) {
return append(e);
}
template <typename T>
- BSONArrayBuilder& operator<<(const T& x) {
+ Derived& operator<<(const T& x) {
_b << _fieldCount << x;
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& appendNull() {
+ Derived& appendNull() {
_b.appendNull(_fieldCount);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& appendUndefined() {
+ Derived& appendUndefined() {
_b.appendUndefined(_fieldCount);
++_fieldCount;
- return *this;
- }
-
- /**
- * destructive - ownership moves to returned BSONArray
- * @return owned BSONArray
- */
- BSONArray arr() {
- return BSONArray(_b.obj());
- }
- BSONObj obj() {
- return _b.obj();
+ return static_cast<Derived&>(*this);
}
BSONObj done() {
@@ -815,65 +931,65 @@ public:
}
template <class T>
- BSONArrayBuilder& append(const std::list<T>& vals);
+ Derived& append(const std::list<T>& vals);
template <class T>
- BSONArrayBuilder& append(const std::set<T>& vals);
+ Derived& append(const std::set<T>& vals);
// These two just use next position
- BufBuilder& subobjStart() {
+ auto& subobjStart() {
return _b.subobjStart(_fieldCount++);
}
- BufBuilder& subarrayStart() {
+ auto& subarrayStart() {
return _b.subarrayStart(_fieldCount++);
}
- BSONArrayBuilder& appendRegex(StringData regex, StringData options = "") {
+ Derived& appendRegex(StringData regex, StringData options = "") {
_b.appendRegex(_fieldCount, regex, options);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& appendBinData(int len, BinDataType type, const void* data) {
+ Derived& appendBinData(int len, BinDataType type, const void* data) {
_b.appendBinData(_fieldCount, len, type, data);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& appendCode(StringData code) {
+ Derived& appendCode(StringData code) {
_b.appendCode(_fieldCount, code);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& appendCodeWScope(StringData code, const BSONObj& scope) {
+ Derived& appendCodeWScope(StringData code, const BSONObj& scope) {
_b.appendCodeWScope(_fieldCount, code, scope);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& appendTimeT(time_t dt) {
+ Derived& appendTimeT(time_t dt) {
_b.appendTimeT(_fieldCount, dt);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& appendDate(Date_t dt) {
+ Derived& appendDate(Date_t dt) {
_b.appendDate(_fieldCount, dt);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& appendBool(bool val) {
+ Derived& appendBool(bool val) {
_b.appendBool(_fieldCount, val);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
- BSONArrayBuilder& appendTimestamp(unsigned long long ts) {
+ Derived& appendTimestamp(unsigned long long ts) {
_b.appendTimestamp(_fieldCount, ts);
++_fieldCount;
- return *this;
+ return static_cast<Derived&>(*this);
}
bool isArray() const {
@@ -887,73 +1003,123 @@ public:
return _fieldCount;
}
- BufBuilder& bb() {
+ auto& bb() {
return _b.bb();
}
-private:
+ /**
+ * destructive - ownership moves to returned BSONArray
+ * @return owned BSONArray
+ */
+ BSONArray arr() {
+ return BSONArray(_b.obj());
+ }
+ BSONObj obj() {
+ return _b.obj();
+ }
+
+protected:
+ template <class BufBuilderType>
+ BSONArrayBuilderBase(BufBuilderType& builder) : _b(builder) {}
+
DecimalCounter<uint32_t> _fieldCount;
- BSONObjBuilder _b;
+ BSONObjBuilderType _b;
+};
+
+/**
+ * "Standard" class used for building BSON arrays.
+ */
+class BSONArrayBuilder : public BSONArrayBuilderBase<BSONArrayBuilder, BSONObjBuilder> {
+public:
+ using BSONArrayBuilderBase<BSONArrayBuilder, BSONObjBuilder>::BSONArrayBuilderBase;
+ BSONArrayBuilder(BufBuilder& bufBuilder)
+ : BSONArrayBuilderBase<BSONArrayBuilder, BSONObjBuilder>(bufBuilder) {}
+};
+
+/**
+ * Alternative to BSONArrayBuilder. This class is analogous to UniqueBSONObjBuilder.
+ */
+class UniqueBSONArrayBuilder
+ : public BSONArrayBuilderBase<UniqueBSONArrayBuilder, UniqueBSONObjBuilder> {
+public:
+ using BSONArrayBuilderBase<UniqueBSONArrayBuilder, UniqueBSONObjBuilder>::BSONArrayBuilderBase;
};
+template <class Derived, class B>
template <class T>
-inline BSONObjBuilder& BSONObjBuilder::append(StringData fieldName, const std::vector<T>& vals) {
- BSONObjBuilder arrBuilder(subarrayStart(fieldName));
+inline Derived& BSONObjBuilderBase<Derived, B>::append(StringData fieldName,
+ const std::vector<T>& vals) {
+ Derived arrBuilder(subarrayStart(fieldName));
DecimalCounter<size_t> n;
for (unsigned int i = 0; i < vals.size(); ++i) {
arrBuilder.append(StringData{n}, vals[i]);
++n;
}
- return *this;
+ return static_cast<Derived&>(*this);
}
-template <class L>
-inline BSONObjBuilder& _appendIt(BSONObjBuilder& _this, StringData fieldName, const L& vals) {
- BSONObjBuilder arrBuilder;
+template <class Builder, class L>
+inline void _appendIt(Builder& _this, StringData fieldName, const L& vals) {
+ typename std::remove_reference<Builder>::type arrBuilder;
DecimalCounter<size_t> n;
for (typename L::const_iterator i = vals.begin(); i != vals.end(); i++) {
arrBuilder.append(StringData{n}, *i);
++n;
}
_this.appendArray(fieldName, arrBuilder.done());
- return _this;
}
+template <class Derived, class B>
template <class T>
-inline BSONObjBuilder& BSONObjBuilder::append(StringData fieldName, const std::list<T>& vals) {
- return _appendIt<std::list<T>>(*this, fieldName, vals);
+inline Derived& BSONObjBuilderBase<Derived, B>::append(StringData fieldName,
+ const std::list<T>& vals) {
+ auto& derivedThis = static_cast<Derived&>(*this);
+ _appendIt<Derived, std::list<T>>(derivedThis, fieldName, vals);
+ return derivedThis;
}
+template <class Derived, class B>
template <class T>
-inline BSONObjBuilder& BSONObjBuilder::append(StringData fieldName, const std::set<T>& vals) {
- return _appendIt<std::set<T>>(*this, fieldName, vals);
+inline Derived& BSONObjBuilderBase<Derived, B>::append(StringData fieldName,
+ const std::set<T>& vals) {
+ auto& derivedThis = static_cast<Derived&>(*this);
+ _appendIt<Derived, std::set<T>>(derivedThis, fieldName, vals);
+ return derivedThis;
}
+template <class Derived, class BufBuilderType>
template <class K, class T>
-inline BSONObjBuilder& BSONObjBuilder::append(StringData fieldName, const std::map<K, T>& vals) {
- BSONObjBuilder bob;
+inline Derived& BSONObjBuilderBase<Derived, BufBuilderType>::append(StringData fieldName,
+ const std::map<K, T>& vals) {
+ typename std::remove_reference<Derived>::type bob;
for (typename std::map<K, T>::const_iterator i = vals.begin(); i != vals.end(); ++i) {
bob.append(i->first, i->second);
}
append(fieldName, bob.obj());
- return *this;
+ return static_cast<Derived&>(*this);
}
-template <class L>
-inline BSONArrayBuilder& _appendArrayIt(BSONArrayBuilder& _this, const L& vals) {
+template <class BSONArrayBuilderType, class L>
+inline void _appendArrayIt(BSONArrayBuilderType& arrBuilder, const L& vals) {
for (typename L::const_iterator i = vals.begin(); i != vals.end(); i++)
- _this.append(*i);
- return _this;
+ arrBuilder.append(*i);
}
+template <class Derived, class BSONObjBuilderType>
template <class T>
-inline BSONArrayBuilder& BSONArrayBuilder::append(const std::list<T>& vals) {
- return _appendArrayIt<std::list<T>>(*this, vals);
+inline Derived& BSONArrayBuilderBase<Derived, BSONObjBuilderType>::append(
+ const std::list<T>& vals) {
+ auto& derivedThis = static_cast<Derived&>(*this);
+ _appendArrayIt<Derived, std::list<T>>(derivedThis, vals);
+ return derivedThis;
}
+template <class Derived, class BSONObjBuilderType>
template <class T>
-inline BSONArrayBuilder& BSONArrayBuilder::append(const std::set<T>& vals) {
- return _appendArrayIt<std::set<T>>(*this, vals);
+inline Derived& BSONArrayBuilderBase<Derived, BSONObjBuilderType>::append(const std::set<T>& vals) {
+ auto& derivedThis = static_cast<Derived&>(*this);
+ _appendArrayIt<Derived, std::set<T>>(derivedThis, vals);
+ return derivedThis;
}
template <typename T>
@@ -1006,17 +1172,20 @@ BSONObjBuilder& Labeler::operator<<(T value) {
return *s_->_builder;
}
-inline BSONObjBuilder& BSONObjBuilder::append(StringData fieldName, Timestamp optime) {
- optime.append(_b, fieldName);
- return *this;
+template <class Derived, class B>
+inline Derived& BSONObjBuilderBase<Derived, B>::append(StringData fieldName, Timestamp optime) {
+ optime.append<B>(_b, fieldName);
+ return static_cast<Derived&>(*this);
}
-inline BSONObjBuilder& BSONObjBuilder::appendTimestamp(StringData fieldName) {
+template <class Derived, class B>
+inline Derived& BSONObjBuilderBase<Derived, B>::appendTimestamp(StringData fieldName) {
return append(fieldName, Timestamp());
}
-inline BSONObjBuilder& BSONObjBuilder::appendTimestamp(StringData fieldName,
- unsigned long long val) {
+template <class Derived, class B>
+inline Derived& BSONObjBuilderBase<Derived, B>::appendTimestamp(StringData fieldName,
+ unsigned long long val) {
return append(fieldName, Timestamp(val));
}
diff --git a/src/mongo/bson/bsonobjbuilder_test.cpp b/src/mongo/bson/bsonobjbuilder_test.cpp
index f8c992508bb..c47bd71a199 100644
--- a/src/mongo/bson/bsonobjbuilder_test.cpp
+++ b/src/mongo/bson/bsonobjbuilder_test.cpp
@@ -491,6 +491,93 @@ TEST(BSONObjBuilderTest, SizeChecks) {
}
}
+TEST(BSONObjBuilderTest, UniqueBuilderNoop) {
+ UniqueBSONObjBuilder bob;
+ // No invariants should trip in destroying a default constructed UniqueBSONObjBuilder.
+}
+
+TEST(BSONObjBuilderTest, UniqueBuilderFromPrefix) {
+ UniqueBSONObjBuilder bob(BSON("a" << 1 << "b" << 2));
+
+ bob.append("c", 3);
+ ASSERT_BSONOBJ_EQ(bob.obj(), BSON("a" << 1 << "b" << 2 << "c" << 3));
+}
+
+TEST(BSONObjBuilderTest, UniqueBuilderReleaseToObj) {
+ UniqueBSONObjBuilder bob;
+ {
+ UniqueBSONObjBuilder inner(bob.subobjStart("nested"));
+ inner.append("a", 1);
+
+ UniqueBSONObjBuilder inner2(std::move(inner));
+ ASSERT(!inner2.owned());
+ ASSERT_EQ(&inner2.bb(), &bob.bb());
+ }
+
+ bob.append("b", 2);
+
+ ASSERT_BSONOBJ_EQ(bob.obj(), BSON("nested" << BSON("a" << 1) << "b" << 2));
+}
+
+TEST(BSONObjBuilderTest, UniqueBuilderReleaseToBuffer) {
+ UniqueBSONObjBuilder bob;
+ {
+ UniqueBSONObjBuilder inner(bob.subobjStart("nested"));
+ inner.append("a", 1);
+
+ UniqueBSONObjBuilder inner2(std::move(inner));
+ ASSERT(!inner2.owned());
+ ASSERT_EQ(&inner2.bb(), &bob.bb());
+ }
+
+ bob.append("b", 2);
+
+ bob.doneFast();
+ char* rawData = bob.bb().release().release();
+ ASSERT_BSONOBJ_EQ(BSONObj(rawData), BSON("nested" << BSON("a" << 1) << "b" << 2));
+
+ {
+ // The memory will be freed when this goes out of scope.
+ auto tmp = UniqueBuffer::reclaim(rawData);
+ }
+}
+
+TEST(BSONObjBuilderTest, UniqueBuilderResetToEmpty) {
+ UniqueBSONObjBuilder bob;
+ bob.append("a", 1);
+ bob.append("b", 2);
+ bob.resetToEmpty();
+
+ bob.append("x", 1);
+ bob.append("y", 2);
+
+ ASSERT_BSONOBJ_EQ(bob.obj(), BSON("x" << 1 << "y" << 2));
+}
+
+TEST(BSONObjBuilderTest, UniqueArrayBuilderReleaseToObj) {
+ UniqueBSONArrayBuilder bab;
+ bab.append(1);
+ bab.append("hello");
+
+ BSONArray arr = bab.arr();
+ ASSERT_BSONOBJ_EQ(arr, BSON_ARRAY(1 << "hello"));
+}
+
+TEST(BSONObjBuilderTest, UniqueArrayBuilderReleaseToBuffer) {
+ UniqueBSONArrayBuilder bab;
+ bab.append(1);
+ bab.append("hello");
+
+ bab.doneFast();
+
+ char* rawData = bab.bb().release().release();
+ ASSERT_BSONOBJ_EQ(BSONObj(rawData), BSON_ARRAY(1 << "hello"));
+
+ {
+ // The memory will be freed when this goes out of scope.
+ auto tmp = UniqueBuffer::reclaim(rawData);
+ }
+}
} // namespace
} // namespace mongo
diff --git a/src/mongo/bson/timestamp.cpp b/src/mongo/bson/timestamp.cpp
index 14cc982ff49..c9f65f46cc1 100644
--- a/src/mongo/bson/timestamp.cpp
+++ b/src/mongo/bson/timestamp.cpp
@@ -49,14 +49,6 @@ Timestamp Timestamp::max() {
return Timestamp(t, i);
}
-void Timestamp::append(BufBuilder& builder, const StringData& fieldName) const {
- // No endian conversions needed, since we store in-memory representation
- // in little endian format, regardless of target endian.
- builder.appendNum(static_cast<char>(bsonTimestamp));
- builder.appendStr(fieldName);
- builder.appendNum(asULL());
-}
-
std::string Timestamp::toStringPretty() const {
std::stringstream ss;
ss << time_t_to_String_short(secs) << ':' << i;
diff --git a/src/mongo/bson/timestamp.h b/src/mongo/bson/timestamp.h
index a7849dc5410..924b26aea11 100644
--- a/src/mongo/bson/timestamp.h
+++ b/src/mongo/bson/timestamp.h
@@ -122,7 +122,15 @@ public:
// Append the BSON representation of this Timestamp to the given BufBuilder with the given
// name. This lives here because Timestamp manages its own serialization format.
- void append(BufBuilder& builder, const StringData& fieldName) const;
+
+ template <class Builder>
+ void append(Builder& builder, const StringData& fieldName) const {
+ // No endian conversions needed, since we store in-memory representation
+ // in little endian format, regardless of target endian.
+ builder.appendNum(static_cast<char>(bsonTimestamp));
+ builder.appendStr(fieldName);
+ builder.appendNum(asULL());
+ }
BSONObj toBSON() const;
private:
diff --git a/src/mongo/bson/util/builder.h b/src/mongo/bson/util/builder.h
index 4bcdd15b74d..66c0fb2c1cd 100644
--- a/src/mongo/bson/util/builder.h
+++ b/src/mongo/bson/util/builder.h
@@ -176,6 +176,50 @@ private:
SharedBufferFragmentBuilder& _fragmentBuilder;
};
+class UniqueBufferAllocator {
+ UniqueBufferAllocator(const UniqueBufferAllocator&) = delete;
+ UniqueBufferAllocator& operator=(const UniqueBufferAllocator&) = delete;
+
+public:
+ UniqueBufferAllocator() = default;
+ UniqueBufferAllocator(size_t sz) {
+ if (sz > 0)
+ malloc(sz);
+ }
+
+ // Allow moving but not copying. It would be an error for two UniqueBufferAllocators to use the
+ // same underlying buffer.
+ UniqueBufferAllocator(UniqueBufferAllocator&&) = default;
+ UniqueBufferAllocator& operator=(UniqueBufferAllocator&&) = default;
+
+ void malloc(size_t sz) {
+ _buf = UniqueBuffer::allocate(sz);
+ }
+
+ void realloc(size_t sz) {
+ _buf.realloc(sz);
+ }
+
+ void free() {
+ _buf = {};
+ }
+
+ UniqueBuffer release() {
+ return std::move(_buf);
+ }
+
+ size_t capacity() const {
+ return _buf.capacity();
+ }
+
+ char* get() const {
+ return _buf.get();
+ }
+
+private:
+ UniqueBuffer _buf;
+};
+
enum { StackSizeDefault = 512 };
template <size_t SZ>
class StackAllocator {
@@ -458,6 +502,17 @@ public:
};
MONGO_STATIC_ASSERT(std::is_move_constructible_v<BufBuilder>);
+class UniqueBufBuilder : public BasicBufBuilder<UniqueBufferAllocator> {
+public:
+ static constexpr size_t kDefaultInitSizeBytes = 512;
+ UniqueBufBuilder(size_t initsize = kDefaultInitSizeBytes) : BasicBufBuilder(initsize) {}
+
+ /* assume ownership of the buffer */
+ UniqueBuffer release() {
+ return _buf.release();
+ }
+};
+
/** The StackBufBuilder builds smaller datasets on the stack instead of using malloc.
this can be significantly faster for small bufs. However, you can not release() the
buffer with StackBufBuilder.