summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMathias Stearn <mathias@10gen.com>2019-04-24 18:18:39 -0400
committerMathias Stearn <mathias@10gen.com>2019-05-01 19:14:20 -0400
commitf6d863188b71b420f13b8249579d34de76d80d9c (patch)
tree3d1de115082ca8bde41e6b9029b2bbb1d6e4cbb9
parent3ca7cf08c389d877f4e427012533fa0785d96d10 (diff)
downloadmongo-f6d863188b71b420f13b8249579d34de76d80d9c.tar.gz
SERVER-40802 move some expensive and commonly instantiated functions out of line
-rw-r--r--src/mongo/bson/bsonobjbuilder.cpp32
-rw-r--r--src/mongo/bson/bsonobjbuilder.h10
-rw-r--r--src/mongo/bson/util/builder.h35
-rw-r--r--src/mongo/unittest/unittest.cpp23
-rw-r--r--src/mongo/unittest/unittest.h134
-rw-r--r--src/mongo/unittest/unittest_test.cpp25
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>{});