Skip to content

Commit 3c586a7

Browse files
committed
SERVER-32302 Use a table to compute the size of each BSONElement
1 parent 0d38ef5 commit 3c586a7

File tree

1 file changed

+69
-56
lines changed

1 file changed

+69
-56
lines changed

src/mongo/bson/bsonelement.cpp

Lines changed: 69 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -590,65 +590,78 @@ BSONElement BSONElement::operator[](StringData field) const {
590590
return o[field];
591591
}
592592

593+
namespace {
594+
NOINLINE_DECL void msgAssertedBadType[[noreturn]](int8_t type) {
595+
msgasserted(10320, str::stream() << "BSONElement: bad type " << (int)type);
596+
}
597+
} // namespace
593598
int BSONElement::computeSize() const {
594-
int x = 0;
595-
switch (type()) {
596-
case EOO:
597-
case Undefined:
598-
case jstNULL:
599-
case MaxKey:
600-
case MinKey:
601-
break;
602-
case mongo::Bool:
603-
x = 1;
604-
break;
605-
case NumberInt:
606-
x = 4;
607-
break;
608-
case bsonTimestamp:
609-
case mongo::Date:
610-
case NumberDouble:
611-
case NumberLong:
612-
x = 8;
613-
break;
614-
case NumberDecimal:
615-
x = 16;
616-
break;
617-
case jstOID:
618-
x = OID::kOIDSize;
619-
break;
620-
case Symbol:
621-
case Code:
622-
case mongo::String:
623-
x = valuestrsize() + 4;
624-
break;
625-
case DBRef:
626-
x = valuestrsize() + 4 + 12;
627-
break;
628-
case CodeWScope:
629-
case Object:
630-
case mongo::Array:
631-
x = objsize();
632-
break;
633-
case BinData:
634-
x = valuestrsize() + 4 + 1 /*subtype*/;
635-
break;
636-
case RegEx: {
637-
const char* p = value();
638-
size_t len1 = strlen(p);
639-
p = p + len1 + 1;
640-
size_t len2;
641-
len2 = strlen(p);
642-
x = (int)(len1 + 1 + len2 + 1);
643-
} break;
644-
default: {
645-
StringBuilder ss;
646-
ss << "BSONElement: bad type " << (int)type();
647-
std::string msg = ss.str();
648-
massert(10320, msg.c_str(), false);
599+
enum SizeStyle : uint8_t {
600+
kFixed, // Total size is a fixed amount + key length.
601+
kIntPlusFixed, // Like Fixed, but also add in the int32 immediately following the key.
602+
kRegEx, // Handled specially.
603+
};
604+
struct SizeInfo {
605+
uint8_t style : 2;
606+
uint8_t bytes : 6; // Includes type byte. Excludes field name and variable lengths.
607+
};
608+
MONGO_STATIC_ASSERT(sizeof(SizeInfo) == 1);
609+
610+
// This table should take 20 bytes. Align to next power of 2 to avoid splitting across cache
611+
// lines unnecessarily.
612+
static constexpr SizeInfo kSizeInfoTable alignas(32)[] = {
613+
{SizeStyle::kFixed, 1}, // EOO
614+
{SizeStyle::kFixed, 9}, // NumberDouble
615+
{SizeStyle::kIntPlusFixed, 5}, // String
616+
{SizeStyle::kIntPlusFixed, 1}, // Object
617+
{SizeStyle::kIntPlusFixed, 1}, // Array
618+
{SizeStyle::kIntPlusFixed, 6}, // BinData
619+
{SizeStyle::kFixed, 1}, // Undefined
620+
{SizeStyle::kFixed, 13}, // OID
621+
{SizeStyle::kFixed, 2}, // Bool
622+
{SizeStyle::kFixed, 9}, // Date
623+
{SizeStyle::kFixed, 1}, // Null
624+
{SizeStyle::kRegEx}, // Regex
625+
{SizeStyle::kIntPlusFixed, 17}, // DBRef
626+
{SizeStyle::kIntPlusFixed, 5}, // Code
627+
{SizeStyle::kIntPlusFixed, 5}, // Symbol
628+
{SizeStyle::kIntPlusFixed, 1}, // CodeWScope
629+
{SizeStyle::kFixed, 5}, // Int
630+
{SizeStyle::kFixed, 9}, // Timestamp
631+
{SizeStyle::kFixed, 9}, // Long
632+
{SizeStyle::kFixed, 17}, // Decimal
633+
};
634+
MONGO_STATIC_ASSERT((sizeof(kSizeInfoTable) / sizeof(kSizeInfoTable[0])) == JSTypeMax + 1);
635+
636+
// This is the start of the runtime code for this function. Everything above happens at compile
637+
// time. This function attempts to push complex handling of unlikely events out-of-line to
638+
// ensure that the common cases never need to spill any registers (at least on x64 with
639+
// gcc-5.4), which reduces the function call overhead.
640+
int8_t type = *data;
641+
if (MONGO_unlikely(type < 0 || type > JSTypeMax)) {
642+
if (MONGO_unlikely(type != MinKey && type != MaxKey)) {
643+
msgAssertedBadType(type);
649644
}
645+
646+
// MinKey and MaxKey should be treated the same as Null
647+
type = jstNULL;
650648
}
651-
return x + fieldNameSize() + 1; // BSONType
649+
650+
const auto sizeInfo = kSizeInfoTable[type];
651+
if (sizeInfo.style == SizeStyle::kFixed)
652+
return sizeInfo.bytes + fieldNameSize();
653+
if (MONGO_likely(sizeInfo.style == SizeStyle::kIntPlusFixed))
654+
return sizeInfo.bytes + fieldNameSize() + valuestrsize();
655+
656+
return [this, type]() NOINLINE_DECL {
657+
// Regex is two c-strings back-to-back.
658+
invariant(type == BSONType::RegEx);
659+
const char* p = value();
660+
size_t len1 = strlen(p);
661+
p = p + len1 + 1;
662+
size_t len2 = strlen(p);
663+
return (len1 + 1 + len2 + 1) + fieldNameSize() + 1;
664+
}();
652665
}
653666

654667
std::string BSONElement::toString(bool includeFieldName, bool full) const {

0 commit comments

Comments
 (0)