diff options
author | Ian Boros <ian.boros@mongodb.com> | 2020-11-18 15:56:15 -0500 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2020-12-04 19:39:52 +0000 |
commit | 1c0f7fc1b37c4495179765bd099bab5b603b76d5 (patch) | |
tree | 55c6b849ca9dab03a490b48f4d5a27a732fd651c /src/mongo/bson | |
parent | f16974e78794ad1ce610df6a1566b4ba65ab284b (diff) | |
download | mongo-1c0f7fc1b37c4495179765bd099bab5b603b76d5.tar.gz |
SERVER-53183 Parameterize BSONObjBuilder by buffer allocator
Diffstat (limited to 'src/mongo/bson')
-rw-r--r-- | src/mongo/bson/bsonobjbuilder.cpp | 107 | ||||
-rw-r--r-- | src/mongo/bson/bsonobjbuilder.h | 721 | ||||
-rw-r--r-- | src/mongo/bson/bsonobjbuilder_test.cpp | 87 | ||||
-rw-r--r-- | src/mongo/bson/timestamp.cpp | 8 | ||||
-rw-r--r-- | src/mongo/bson/timestamp.h | 10 | ||||
-rw-r--r-- | src/mongo/bson/util/builder.h | 55 |
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. |