diff options
author | Mathias Stearn <mathias@10gen.com> | 2019-04-24 18:18:39 -0400 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2019-05-01 19:14:20 -0400 |
commit | f6d863188b71b420f13b8249579d34de76d80d9c (patch) | |
tree | 3d1de115082ca8bde41e6b9029b2bbb1d6e4cbb9 | |
parent | 3ca7cf08c389d877f4e427012533fa0785d96d10 (diff) | |
download | mongo-f6d863188b71b420f13b8249579d34de76d80d9c.tar.gz |
SERVER-40802 move some expensive and commonly instantiated functions out of line
-rw-r--r-- | src/mongo/bson/bsonobjbuilder.cpp | 32 | ||||
-rw-r--r-- | src/mongo/bson/bsonobjbuilder.h | 10 | ||||
-rw-r--r-- | src/mongo/bson/util/builder.h | 35 | ||||
-rw-r--r-- | src/mongo/unittest/unittest.cpp | 23 | ||||
-rw-r--r-- | src/mongo/unittest/unittest.h | 134 | ||||
-rw-r--r-- | src/mongo/unittest/unittest_test.cpp | 25 |
6 files changed, 209 insertions, 50 deletions
diff --git a/src/mongo/bson/bsonobjbuilder.cpp b/src/mongo/bson/bsonobjbuilder.cpp index cd928af0f9f..dc607e14726 100644 --- a/src/mongo/bson/bsonobjbuilder.cpp +++ b/src/mongo/bson/bsonobjbuilder.cpp @@ -228,6 +228,17 @@ 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(); + } +} + + const string BSONObjBuilder::numStrs[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", @@ -244,4 +255,25 @@ const string BSONObjBuilder::numStrs[] = { // numStrs is initialized because it is a static variable bool BSONObjBuilder::numStrsReady = (numStrs[0].size() > 0); +template <typename Alloc> +void _BufBuilder<Alloc>::grow_reallocate(int minSize) { + if (minSize > BufferMaxSize) { + std::stringstream ss; + ss << "BufBuilder attempted to grow() to " << minSize << " bytes, past the 64MB limit."; + msgasserted(13548, ss.str().c_str()); + } + + int a = 64; + while (a < minSize) + a = a * 2; + + _buf.realloc(a); + size = a; +} + +template class _BufBuilder<SharedBufferAllocator>; +template class _BufBuilder<StackAllocator>; +template class StringBuilderImpl<SharedBufferAllocator>; +template class StringBuilderImpl<StackAllocator>; + } // namespace mongo diff --git a/src/mongo/bson/bsonobjbuilder.h b/src/mongo/bson/bsonobjbuilder.h index c33021d6928..75fe33d1d26 100644 --- a/src/mongo/bson/bsonobjbuilder.h +++ b/src/mongo/bson/bsonobjbuilder.h @@ -165,15 +165,7 @@ public: other.abandon(); } - ~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(); - } - } + ~BSONObjBuilder(); /** * The start offset of the object being built by this builder within its buffer. diff --git a/src/mongo/bson/util/builder.h b/src/mongo/bson/util/builder.h index 5a85a5fd219..ad890ceb074 100644 --- a/src/mongo/bson/util/builder.h +++ b/src/mongo/bson/util/builder.h @@ -32,9 +32,9 @@ #include <cfloat> #include <cinttypes> #include <cstdint> +#include <cstdio> +#include <cstring> #include <sstream> -#include <stdio.h> -#include <string.h> #include <string> #include <boost/optional.hpp> @@ -49,6 +49,7 @@ #include "mongo/stdx/type_traits.h" #include "mongo/util/allocator.h" #include "mongo/util/assert_util.h" +#include "mongo/util/concepts.h" #include "mongo/util/itoa.h" #include "mongo/util/shared_buffer.h" @@ -199,6 +200,7 @@ public: } /* assume ownership of the buffer */ + REQUIRES_FOR_NON_TEMPLATE(std::is_same_v<BufferAllocator, SharedBufferAllocator>) SharedBuffer release() { return _buf.release(); } @@ -246,10 +248,8 @@ public: appendNumImpl(high); } - template <typename Int64_t, - typename = stdx::enable_if_t<std::is_same<Int64_t, int64_t>::value && - !std::is_same<int64_t, long long>::value>> - void appendNum(Int64_t j) { + REQUIRES_FOR_NON_TEMPLATE(!std::is_same_v<int64_t, long long>) + void appendNum(int64_t j) { appendNumImpl(j); } @@ -322,8 +322,8 @@ public: * Replaces the buffer backing this BufBuilder with the passed in SharedBuffer. * Only legal to call when this builder is empty and when the SharedBuffer isn't shared. */ + REQUIRES_FOR_NON_TEMPLATE(std::is_same_v<BufferAllocator, SharedBufferAllocator>) void useSharedBuffer(SharedBuffer buf) { - MONGO_STATIC_ASSERT(std::is_same<BufferAllocator, SharedBufferAllocator>()); invariant(l == 0); // Can only do this while empty. invariant(reservedBytes == 0); size = buf.capacity(); @@ -340,20 +340,7 @@ private: DataView(grow(sizeof(t))).write(tagLittleEndian(t)); } /* "slow" portion of 'grow()' */ - void NOINLINE_DECL grow_reallocate(int minSize) { - if (minSize > BufferMaxSize) { - std::stringstream ss; - ss << "BufBuilder attempted to grow() to " << minSize << " bytes, past the 64MB limit."; - msgasserted(13548, ss.str().c_str()); - } - - int a = 64; - while (a < minSize) - a = a * 2; - - _buf.realloc(a); - size = a; - } + void grow_reallocate(int minSize); BufferAllocator _buf; int l; @@ -536,4 +523,10 @@ private: typedef StringBuilderImpl<SharedBufferAllocator> StringBuilder; typedef StringBuilderImpl<StackAllocator> StackStringBuilder; + +extern template class _BufBuilder<SharedBufferAllocator>; +extern template class _BufBuilder<StackAllocator>; +extern template class StringBuilderImpl<SharedBufferAllocator>; +extern template class StringBuilderImpl<StackAllocator>; + } // namespace mongo diff --git a/src/mongo/unittest/unittest.cpp b/src/mongo/unittest/unittest.cpp index 1eef67c81d7..ce120527e80 100644 --- a/src/mongo/unittest/unittest.cpp +++ b/src/mongo/unittest/unittest.cpp @@ -520,5 +520,28 @@ std::vector<std::string> getAllSuiteNames() { return result; } +template <ComparisonOp op> +ComparisonAssertion<op> ComparisonAssertion<op>::make(const char* theFile, + unsigned theLine, + StringData aExpression, + StringData bExpression, + StringData a, + StringData b) { + return ComparisonAssertion(theFile, theLine, aExpression, bExpression, a, b); +} + +template <ComparisonOp op> +ComparisonAssertion<op> ComparisonAssertion<op>::make(const char* theFile, + unsigned theLine, + StringData aExpression, + StringData bExpression, + const void* a, + const void* b) { + return ComparisonAssertion(theFile, theLine, aExpression, bExpression, a, b); +} + +// Provide definitions for common instantiations of ComparisonAssertion. +INSTANTIATE_COMPARISON_ASSERTION_CTORS(); + } // namespace unittest } // namespace mongo diff --git a/src/mongo/unittest/unittest.h b/src/mongo/unittest/unittest.h index 13e037773a6..7d52c333934 100644 --- a/src/mongo/unittest/unittest.h +++ b/src/mongo/unittest/unittest.h @@ -103,8 +103,9 @@ * Binary comparison utility macro. Do not use directly. */ #define ASSERT_COMPARISON_(OP, a, b) \ - if (auto ca = ::mongo::unittest::ComparisonAssertion<::mongo::unittest::ComparisonOp::OP>( \ - __FILE__, __LINE__, #a, #b, a, b)) \ + if (auto ca = \ + ::mongo::unittest::ComparisonAssertion<::mongo::unittest::ComparisonOp::OP>::make( \ + __FILE__, __LINE__, #a, #b, a, b)) \ ca.failure().stream() /** @@ -116,17 +117,17 @@ /** * Assert a function call returns its input unchanged. */ -#define ASSERT_IDENTITY(INPUT, FUNCTION) \ - [&](auto&& v) { \ - if (auto ca = \ - ::mongo::unittest::ComparisonAssertion<::mongo::unittest::ComparisonOp::kEq>( \ - __FILE__, \ - __LINE__, \ - #INPUT, \ - #FUNCTION "(" #INPUT ")", \ - v, \ - FUNCTION(std::forward<decltype(v)>(v)))) \ - ca.failure().stream(); \ +#define ASSERT_IDENTITY(INPUT, FUNCTION) \ + [&](auto&& v) { \ + if (auto ca = ::mongo::unittest::ComparisonAssertion< \ + ::mongo::unittest::ComparisonOp::kEq>::make(__FILE__, \ + __LINE__, \ + #INPUT, \ + #FUNCTION "(" #INPUT ")", \ + v, \ + FUNCTION( \ + std::forward<decltype(v)>(v)))) \ + ca.failure().stream(); \ }(INPUT) /** @@ -636,14 +637,13 @@ private: return ">="_sd; } -public: template <typename A, typename B> - ComparisonAssertion(const std::string& theFile, - unsigned theLine, - StringData aExpression, - StringData bExpression, - const A& a, - const B& b) { + NOINLINE_DECL ComparisonAssertion(const char* theFile, + unsigned theLine, + StringData aExpression, + StringData bExpression, + const A& a, + const B& b) { if (comparator(OpTag<op>{})(a, b)) { return; } @@ -653,6 +653,41 @@ public: << opName << " " << b << ")"; _assertion = std::make_unique<TestAssertionFailure>(theFile, theLine, os.str()); } + +public: + // Use a single implementation (identical to the templated one) for all string-like types. + // This is particularly important to avoid making unique instantiations for each length of + // string literal. + static ComparisonAssertion make(const char* theFile, + unsigned theLine, + StringData aExpression, + StringData bExpression, + StringData a, + StringData b); + + + // Use a single implementation (identical to the templated one) for all pointer and array types. + // Note: this is selected instead of the StringData overload for char* and string literals + // because they are supposed to compare pointers, not contents. + static ComparisonAssertion make(const char* theFile, + unsigned theLine, + StringData aExpression, + StringData bExpression, + const void* a, + const void* b); + TEMPLATE(typename A, typename B) + REQUIRES(!(std::is_convertible_v<A, StringData> && std::is_convertible_v<B, StringData>)&& // + !(std::is_pointer_v<A> && std::is_pointer_v<B>)&& // + !(std::is_array_v<A> && std::is_array_v<B>)) + static ComparisonAssertion make(const char* theFile, + unsigned theLine, + StringData aExpression, + StringData bExpression, + const A& a, + const B& b) { + return ComparisonAssertion(theFile, theLine, aExpression, bExpression, a, b); + } + explicit operator bool() const { return static_cast<bool>(_assertion); } @@ -664,6 +699,65 @@ private: std::unique_ptr<TestAssertionFailure> _assertion; }; +// Explicit instantiation of ComparisonAssertion ctor and factory for a pair of types. +#define TEMPLATE_COMPARISON_ASSERTION_CTOR_CROSS(EXTERN, OP, A, B) \ + EXTERN template ComparisonAssertion<ComparisonOp::OP>::ComparisonAssertion( \ + const char*, unsigned, StringData, StringData, const A&, const B&); \ + EXTERN template ComparisonAssertion<ComparisonOp::OP> \ + ComparisonAssertion<ComparisonOp::OP>::make( \ + const char*, unsigned, StringData, StringData, const A&, const B&); \ + EXTERN template ComparisonAssertion<ComparisonOp::OP>::ComparisonAssertion( \ + const char*, unsigned, StringData, StringData, const B&, const A&); \ + EXTERN template ComparisonAssertion<ComparisonOp::OP> \ + ComparisonAssertion<ComparisonOp::OP>::make( \ + const char*, unsigned, StringData, StringData, const B&, const A&) + +// Explicit instantiation of ComparisonAssertion ctor and factory for a single type. +#define TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(EXTERN, OP, T) \ + EXTERN template ComparisonAssertion<ComparisonOp::OP>::ComparisonAssertion( \ + const char*, unsigned, StringData, StringData, const T&, const T&); \ + EXTERN template ComparisonAssertion<ComparisonOp::OP> \ + ComparisonAssertion<ComparisonOp::OP>::make( \ + const char*, unsigned, StringData, StringData, const T&, const T&) + +// Call with `extern` to declace extern instantiations, and with no args to explicitly instantiate. +#define INSTANTIATE_COMPARISON_ASSERTION_CTORS(...) \ + __VA_ARGS__ template class ComparisonAssertion<ComparisonOp::kEq>; \ + __VA_ARGS__ template class ComparisonAssertion<ComparisonOp::kNe>; \ + __VA_ARGS__ template class ComparisonAssertion<ComparisonOp::kGt>; \ + __VA_ARGS__ template class ComparisonAssertion<ComparisonOp::kGe>; \ + __VA_ARGS__ template class ComparisonAssertion<ComparisonOp::kLt>; \ + __VA_ARGS__ template class ComparisonAssertion<ComparisonOp::kLe>; \ + \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, int); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, long); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, long long); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, unsigned int); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, unsigned long); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, unsigned long long); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, bool); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, double); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, OID); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, BSONType); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, Timestamp); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, Date_t); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, Status); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kEq, ErrorCodes::Error); \ + \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_CROSS(__VA_ARGS__, kEq, int, long); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_CROSS(__VA_ARGS__, kEq, int, long long); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_CROSS(__VA_ARGS__, kEq, long, long long); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_CROSS(__VA_ARGS__, kEq, unsigned int, unsigned long); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_CROSS(__VA_ARGS__, kEq, Status, ErrorCodes::Error); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_CROSS(__VA_ARGS__, kEq, ErrorCodes::Error, int); \ + \ + /* These are the only types that are often used with ASSERT_NE*/ \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kNe, Status); \ + TEMPLATE_COMPARISON_ASSERTION_CTOR_SELF(__VA_ARGS__, kNe, unsigned long); + +// Declare that these definitions will be provided in unittest.cpp. +INSTANTIATE_COMPARISON_ASSERTION_CTORS(extern); + /** * Get the value out of a StatusWith<T>, or throw an exception if it is not OK. */ diff --git a/src/mongo/unittest/unittest_test.cpp b/src/mongo/unittest/unittest_test.cpp index 204f54211cb..cbeeb6f2322 100644 --- a/src/mongo/unittest/unittest_test.cpp +++ b/src/mongo/unittest/unittest_test.cpp @@ -269,6 +269,31 @@ TEST(UnitTestSelfTest, StackTraceForAssertion) { ASSERT_STRING_CONTAINS(stacktrace, "printStackTrace"); } +TEST(UnitTestSelfTest, ComparisonAssertionOverloadResolution) { + using namespace mongo; + + char xBuf[] = "x"; // Guaranteed different address than "x". + const char* x = xBuf; + + // At least one StringData, compare contents: + ASSERT_EQ("x"_sd, "x"_sd); + ASSERT_EQ("x"_sd, "x"); + ASSERT_EQ("x"_sd, xBuf); + ASSERT_EQ("x"_sd, x); + ASSERT_EQ("x", "x"_sd); + ASSERT_EQ(xBuf, "x"_sd); + ASSERT_EQ(x, "x"_sd); + + // Otherwise, compare pointers: + ASSERT_EQ(x, x); + ASSERT_EQ(xBuf, xBuf); + ASSERT_EQ(x, xBuf); + ASSERT_NE("x", xBuf); + ASSERT_NE("x", x); + ASSERT_NE(xBuf, "x"); + ASSERT_NE(x, "x"); +} + ASSERT_DOES_NOT_COMPILE(typename Char = char, *std::declval<Char>()); ASSERT_DOES_NOT_COMPILE(bool B = false, std::enable_if_t<B, int>{}); |