@@ -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
593598int 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
654667std::string BSONElement::toString (bool includeFieldName, bool full) const {
0 commit comments