/** * Copyright (c) 2011 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/platform/basic.h" #include "mongo/db/pipeline/value.h" #include #include #include #include "mongo/base/compare_numbers.h" #include "mongo/db/jsobj.h" #include "mongo/db/pipeline/document.h" #include "mongo/util/hex.h" #include "mongo/util/mongoutils/str.h" namespace mongo { using namespace mongoutils; using boost::intrusive_ptr; using std::min; using std::numeric_limits; using std::ostream; using std::string; using std::stringstream; using std::vector; void ValueStorage::verifyRefCountingIfShould() const { switch (type) { case MinKey: case MaxKey: case jstOID: case Date: case bsonTimestamp: case EOO: case jstNULL: case Undefined: case Bool: case NumberInt: case NumberLong: case NumberDouble: // the above types never reference external data verify(!refCounter); break; case String: case RegEx: case Code: case Symbol: // the above types reference data when not using short-string optimization verify(refCounter == !shortStr); break; case BinData: // TODO this should probably support short-string optimization case Array: // TODO this should probably support empty-is-NULL optimization case DBRef: case CodeWScope: // the above types always reference external data. verify(refCounter); verify(bool(genericRCPtr)); break; case Object: // Objects either hold a NULL ptr or should be ref-counting verify(refCounter == bool(genericRCPtr)); break; } } void ValueStorage::putString(StringData s) { // Note: this also stores data portion of BinData const size_t sizeNoNUL = s.size(); if (sizeNoNUL <= sizeof(shortStrStorage)) { shortStr = true; shortStrSize = s.size(); s.copyTo(shortStrStorage, false); // no NUL // All memory is zeroed before this is called. // Note this may be past end of shortStrStorage and into nulTerminator dassert(shortStrStorage[sizeNoNUL] == '\0'); } else { putRefCountable(RCString::create(s)); } } void ValueStorage::putDocument(const Document& d) { putRefCountable(d._storage); } void ValueStorage::putVector(const RCVector* vec) { fassert(16485, vec); putRefCountable(vec); } void ValueStorage::putRegEx(const BSONRegEx& re) { const size_t patternLen = re.pattern.size(); const size_t flagsLen = re.flags.size(); const size_t totalLen = patternLen + 1 /*middle NUL*/ + flagsLen; // Need to copy since putString doesn't support scatter-gather. std::unique_ptr buf(new char[totalLen]); re.pattern.copyTo(buf.get(), true); re.flags.copyTo(buf.get() + patternLen + 1, false); // no NUL putString(StringData(buf.get(), totalLen)); } Document ValueStorage::getDocument() const { if (!genericRCPtr) return Document(); dassert(typeid(*genericRCPtr) == typeid(const DocumentStorage)); const DocumentStorage* documentPtr = static_cast(genericRCPtr); return Document(documentPtr); } // not in header because document is fwd declared Value::Value(const BSONObj& obj) : _storage(Object, Document(obj)) {} Value::Value(const BSONElement& elem) : _storage(elem.type()) { switch (elem.type()) { // These are all type-only, no data case EOO: case MinKey: case MaxKey: case Undefined: case jstNULL: break; case NumberDouble: _storage.doubleValue = elem.Double(); break; case Code: case Symbol: case String: _storage.putString(elem.valueStringData()); break; case Object: { _storage.putDocument(Document(elem.embeddedObject())); break; } case Array: { intrusive_ptr vec(new RCVector); BSONForEach(sub, elem.embeddedObject()) { vec->vec.push_back(Value(sub)); } _storage.putVector(vec.get()); break; } case jstOID: BOOST_STATIC_ASSERT(sizeof(_storage.oid) == OID::kOIDSize); memcpy(_storage.oid, elem.OID().view().view(), OID::kOIDSize); break; case Bool: _storage.boolValue = elem.boolean(); break; case Date: _storage.dateValue = elem.date().toMillisSinceEpoch(); break; case RegEx: { _storage.putRegEx(BSONRegEx(elem.regex(), elem.regexFlags())); break; } case NumberInt: _storage.intValue = elem.numberInt(); break; case bsonTimestamp: _storage.timestampValue = elem.timestamp().asULL(); break; case NumberLong: _storage.longValue = elem.numberLong(); break; case CodeWScope: { StringData code(elem.codeWScopeCode(), elem.codeWScopeCodeLen() - 1); _storage.putCodeWScope(BSONCodeWScope(code, elem.codeWScopeObject())); break; } case BinData: { int len; const char* data = elem.binData(len); _storage.putBinData(BSONBinData(data, len, elem.binDataType())); break; } case DBRef: _storage.putDBRef(BSONDBRef(elem.dbrefNS(), elem.dbrefOID())); break; } } Value::Value(const BSONArray& arr) : _storage(Array) { intrusive_ptr vec(new RCVector); BSONForEach(sub, arr) { vec->vec.push_back(Value(sub)); } _storage.putVector(vec.get()); } Value Value::createIntOrLong(long long longValue) { int intValue = longValue; if (intValue != longValue) { // it is too large to be an int and should remain a long return Value(longValue); } // should be an int since all arguments were int and it fits return Value(intValue); } double Value::getDouble() const { BSONType type = getType(); if (type == NumberInt) return _storage.intValue; if (type == NumberLong) return static_cast(_storage.longValue); verify(type == NumberDouble); return _storage.doubleValue; } Document Value::getDocument() const { verify(getType() == Object); return _storage.getDocument(); } Value Value::operator[](size_t index) const { if (getType() != Array || index >= getArrayLength()) return Value(); return getArray()[index]; } Value Value::operator[](StringData name) const { if (getType() != Object) return Value(); return getDocument()[name]; } BSONObjBuilder& operator<<(BSONObjBuilderValueStream& builder, const Value& val) { switch (val.getType()) { case EOO: return builder.builder(); // nothing appended case MinKey: return builder << MINKEY; case MaxKey: return builder << MAXKEY; case jstNULL: return builder << BSONNULL; case Undefined: return builder << BSONUndefined; case jstOID: return builder << val.getOid(); case NumberInt: return builder << val.getInt(); case NumberLong: return builder << val.getLong(); case NumberDouble: return builder << val.getDouble(); case String: return builder << val.getStringData(); case Bool: return builder << val.getBool(); case Date: return builder << Date_t::fromMillisSinceEpoch(val.getDate()); case bsonTimestamp: return builder << val.getTimestamp(); case Object: return builder << val.getDocument(); case Symbol: return builder << BSONSymbol(val.getStringData()); case Code: return builder << BSONCode(val.getStringData()); case RegEx: return builder << BSONRegEx(val.getRegex(), val.getRegexFlags()); case DBRef: return builder << BSONDBRef(val._storage.getDBRef()->ns, val._storage.getDBRef()->oid); case BinData: return builder << BSONBinData(val.getStringData().rawData(), // looking for void* val.getStringData().size(), val._storage.binDataType()); case CodeWScope: return builder << BSONCodeWScope(val._storage.getCodeWScope()->code, val._storage.getCodeWScope()->scope); case Array: { const vector& array = val.getArray(); const size_t n = array.size(); BSONArrayBuilder arrayBuilder(builder.subarrayStart()); for (size_t i = 0; i < n; i++) { array[i].addToBsonArray(&arrayBuilder); } arrayBuilder.doneFast(); return builder.builder(); } } verify(false); } void Value::addToBsonObj(BSONObjBuilder* pBuilder, StringData fieldName) const { *pBuilder << fieldName << *this; } void Value::addToBsonArray(BSONArrayBuilder* pBuilder) const { if (!missing()) { // don't want to increment builder's counter *pBuilder << *this; } } bool Value::coerceToBool() const { // TODO Unify the implementation with BSONElement::trueValue(). switch (getType()) { case CodeWScope: case MinKey: case DBRef: case Code: case MaxKey: case String: case Object: case Array: case BinData: case jstOID: case Date: case RegEx: case Symbol: case bsonTimestamp: return true; case EOO: case jstNULL: case Undefined: return false; case Bool: return _storage.boolValue; case NumberInt: return _storage.intValue; case NumberLong: return _storage.longValue; case NumberDouble: return _storage.doubleValue; } verify(false); } int Value::coerceToInt() const { switch (getType()) { case NumberInt: return _storage.intValue; case NumberLong: return static_cast(_storage.longValue); case NumberDouble: return static_cast(_storage.doubleValue); default: uassert(16003, str::stream() << "can't convert from BSON type " << typeName(getType()) << " to int", false); } // switch(getType()) } long long Value::coerceToLong() const { switch (getType()) { case NumberLong: return _storage.longValue; case NumberInt: return static_cast(_storage.intValue); case NumberDouble: return static_cast(_storage.doubleValue); default: uassert(16004, str::stream() << "can't convert from BSON type " << typeName(getType()) << " to long", false); } // switch(getType()) } double Value::coerceToDouble() const { switch (getType()) { case NumberDouble: return _storage.doubleValue; case NumberInt: return static_cast(_storage.intValue); case NumberLong: return static_cast(_storage.longValue); default: uassert(16005, str::stream() << "can't convert from BSON type " << typeName(getType()) << " to double", false); } // switch(getType()) } long long Value::coerceToDate() const { switch (getType()) { case Date: return getDate(); case bsonTimestamp: return getTimestamp().getSecs() * 1000LL; default: uassert(16006, str::stream() << "can't convert from BSON type " << typeName(getType()) << " to Date", false); } // switch(getType()) } time_t Value::coerceToTimeT() const { long long millis = coerceToDate(); if (millis < 0) { // We want the division below to truncate toward -inf rather than 0 // eg Dec 31, 1969 23:59:58.001 should be -2 seconds rather than -1 // This is needed to get the correct values from coerceToTM if (-1999 / 1000 != -2) { // this is implementation defined millis -= 1000 - 1; } } const long long seconds = millis / 1000; uassert(16421, "Can't handle date values outside of time_t range", seconds >= std::numeric_limits::min() && seconds <= std::numeric_limits::max()); return static_cast(seconds); } tm Value::coerceToTm() const { // See implementation in Date_t. // Can't reuse that here because it doesn't support times before 1970 time_t dtime = coerceToTimeT(); tm out; #if defined(_WIN32) // Both the argument order and the return values differ bool itWorked = gmtime_s(&out, &dtime) == 0; #else bool itWorked = gmtime_r(&dtime, &out) != NULL; #endif if (!itWorked) { if (dtime < 0) { // Windows docs say it doesn't support these, but empirically it seems to work uasserted(16422, "gmtime failed - your system doesn't support dates before 1970"); } else { uasserted(16423, str::stream() << "gmtime failed to convert time_t of " << dtime); } } return out; } static string tmToISODateString(const tm& time) { char buf[128]; size_t len = strftime(buf, 128, "%Y-%m-%dT%H:%M:%S", &time); verify(len > 0); verify(len < 128); return buf; } string Value::coerceToString() const { switch (getType()) { case NumberDouble: return str::stream() << _storage.doubleValue; case NumberInt: return str::stream() << _storage.intValue; case NumberLong: return str::stream() << _storage.longValue; case Code: case Symbol: case String: return getStringData().toString(); case bsonTimestamp: return getTimestamp().toStringPretty(); case Date: return tmToISODateString(coerceToTm()); case EOO: case jstNULL: case Undefined: return ""; default: uassert(16007, str::stream() << "can't convert from BSON type " << typeName(getType()) << " to String", false); } // switch(getType()) } Timestamp Value::coerceToTimestamp() const { switch (getType()) { case bsonTimestamp: return getTimestamp(); default: uassert(16378, str::stream() << "can't convert from BSON type " << typeName(getType()) << " to timestamp", false); } // switch(getType()) } // Helper function for Value::compare. // Better than l-r for cases where difference > MAX_INT template inline static int cmp(const T& left, const T& right) { if (left < right) { return -1; } else if (left == right) { return 0; } else { dassert(left > right); return 1; } } int Value::compare(const Value& rL, const Value& rR) { // Note, this function needs to behave identically to BSON's compareElementValues(). // Additionally, any changes here must be replicated in hash_combine(). BSONType lType = rL.getType(); BSONType rType = rR.getType(); int ret = lType == rType ? 0 // fast-path common case : cmp(canonicalizeBSONType(lType), canonicalizeBSONType(rType)); if (ret) return ret; switch (lType) { // Order of types is the same as in compareElementValues() to make it easier to verify // These are valueless types case EOO: case Undefined: case jstNULL: case MaxKey: case MinKey: return ret; case Bool: return rL.getBool() - rR.getBool(); case bsonTimestamp: // unsigned return cmp(rL._storage.timestampValue, rR._storage.timestampValue); case Date: // signed return cmp(rL._storage.dateValue, rR._storage.dateValue); // Numbers should compare by equivalence even if different types case NumberInt: { // All types can precisely represent all NumberInts, so it is safe to simply convert to // whatever rhs's type is. switch (rType) { case NumberInt: return compareInts(rL._storage.intValue, rR._storage.intValue); case NumberLong: return compareLongs(rL._storage.intValue, rR._storage.longValue); case NumberDouble: return compareDoubles(rL._storage.intValue, rR._storage.doubleValue); default: invariant(false); } } case NumberLong: { switch (rType) { case NumberLong: return compareLongs(rL._storage.longValue, rR._storage.longValue); case NumberInt: return compareLongs(rL._storage.longValue, rR._storage.intValue); case NumberDouble: return compareLongToDouble(rL._storage.longValue, rR._storage.doubleValue); default: invariant(false); } } case NumberDouble: { switch (rType) { case NumberDouble: return compareDoubles(rL._storage.doubleValue, rR._storage.doubleValue); case NumberInt: return compareDoubles(rL._storage.doubleValue, rR._storage.intValue); case NumberLong: return compareDoubleToLong(rL._storage.doubleValue, rR._storage.longValue); default: invariant(false); } } case jstOID: return memcmp(rL._storage.oid, rR._storage.oid, OID::kOIDSize); case Code: case Symbol: case String: return rL.getStringData().compare(rR.getStringData()); case Object: return Document::compare(rL.getDocument(), rR.getDocument()); case Array: { const vector& lArr = rL.getArray(); const vector& rArr = rR.getArray(); const size_t elems = std::min(lArr.size(), rArr.size()); for (size_t i = 0; i < elems; i++) { // compare the two corresponding elements ret = Value::compare(lArr[i], rArr[i]); if (ret) return ret; // values are unequal } // if we get here we are either equal or one is prefix of the other return cmp(lArr.size(), rArr.size()); } case DBRef: { intrusive_ptr l = rL._storage.getDBRef(); intrusive_ptr r = rR._storage.getDBRef(); ret = cmp(l->ns.size(), r->ns.size()); if (ret) return ret; return l->oid.compare(r->oid); } case BinData: { ret = cmp(rL.getStringData().size(), rR.getStringData().size()); if (ret) return ret; // Need to compare as an unsigned char rather than enum since BSON uses memcmp ret = cmp(rL._storage.binSubType, rR._storage.binSubType); if (ret) return ret; return rL.getStringData().compare(rR.getStringData()); } case RegEx: // same as String in this impl but keeping order same as compareElementValues return rL.getStringData().compare(rR.getStringData()); case CodeWScope: { intrusive_ptr l = rL._storage.getCodeWScope(); intrusive_ptr r = rR._storage.getCodeWScope(); ret = l->code.compare(r->code); if (ret) return ret; return l->scope.woCompare(r->scope); } } verify(false); } void Value::hash_combine(size_t& seed) const { BSONType type = getType(); boost::hash_combine(seed, canonicalizeBSONType(type)); switch (type) { // Order of types is the same as in Value::compare() and compareElementValues(). // These are valueless types case EOO: case Undefined: case jstNULL: case MaxKey: case MinKey: return; case Bool: boost::hash_combine(seed, getBool()); break; case bsonTimestamp: case Date: BOOST_STATIC_ASSERT(sizeof(_storage.dateValue) == sizeof(_storage.timestampValue)); boost::hash_combine(seed, _storage.dateValue); break; // This converts all numbers to doubles, which ignores the low-order bits of // NumberLongs > 2**53, but that is ok since the hash will still be the same for // equal numbers and is still likely to be different for different numbers. // SERVER-16851 case NumberDouble: case NumberLong: case NumberInt: { const double dbl = getDouble(); if (std::isnan(dbl)) { boost::hash_combine(seed, numeric_limits::quiet_NaN()); } else { boost::hash_combine(seed, dbl); } break; } case jstOID: getOid().hash_combine(seed); break; case Code: case Symbol: case String: { StringData sd = getStringData(); MurmurHash3_x86_32(sd.rawData(), sd.size(), seed, &seed); break; } case Object: getDocument().hash_combine(seed); break; case Array: { const vector& vec = getArray(); for (size_t i = 0; i < vec.size(); i++) vec[i].hash_combine(seed); break; } case DBRef: boost::hash_combine(seed, _storage.getDBRef()->ns); _storage.getDBRef()->oid.hash_combine(seed); break; case BinData: { StringData sd = getStringData(); MurmurHash3_x86_32(sd.rawData(), sd.size(), seed, &seed); boost::hash_combine(seed, _storage.binDataType()); break; } case RegEx: { StringData sd = getStringData(); MurmurHash3_x86_32(sd.rawData(), sd.size(), seed, &seed); break; } case CodeWScope: { intrusive_ptr cws = _storage.getCodeWScope(); boost::hash_combine(seed, StringData::Hasher()(cws->code)); boost::hash_combine(seed, BSONObj::Hasher()(cws->scope)); break; } } } BSONType Value::getWidestNumeric(BSONType lType, BSONType rType) { if (lType == NumberDouble) { switch (rType) { case NumberDouble: case NumberLong: case NumberInt: return NumberDouble; default: break; } } else if (lType == NumberLong) { switch (rType) { case NumberDouble: return NumberDouble; case NumberLong: case NumberInt: return NumberLong; default: break; } } else if (lType == NumberInt) { switch (rType) { case NumberDouble: return NumberDouble; case NumberLong: return NumberLong; case NumberInt: return NumberInt; default: break; } } // Reachable, but callers must subsequently err out in this case. return Undefined; } bool Value::integral() const { switch (getType()) { case NumberInt: return true; case NumberLong: return (_storage.longValue <= numeric_limits::max() && _storage.longValue >= numeric_limits::min()); case NumberDouble: return (_storage.doubleValue <= numeric_limits::max() && _storage.doubleValue >= numeric_limits::min() && _storage.doubleValue == static_cast(_storage.doubleValue)); default: return false; } } size_t Value::getApproximateSize() const { switch (getType()) { case Code: case RegEx: case Symbol: case BinData: case String: return sizeof(Value) + (_storage.shortStr ? 0 // string stored inline, so no extra mem usage : sizeof(RCString) + _storage.getString().size()); case Object: return sizeof(Value) + getDocument().getApproximateSize(); case Array: { size_t size = sizeof(Value); size += sizeof(RCVector); const size_t n = getArray().size(); for (size_t i = 0; i < n; ++i) { size += getArray()[i].getApproximateSize(); } return size; } case CodeWScope: return sizeof(Value) + sizeof(RCCodeWScope) + _storage.getCodeWScope()->code.size() + _storage.getCodeWScope()->scope.objsize(); case DBRef: return sizeof(Value) + sizeof(RCDBRef) + _storage.getDBRef()->ns.size(); // These types are always contained within the Value case EOO: case MinKey: case MaxKey: case NumberDouble: case jstOID: case Bool: case Date: case NumberInt: case bsonTimestamp: case NumberLong: case jstNULL: case Undefined: return sizeof(Value); } verify(false); } string Value::toString() const { // TODO use StringBuilder when operator << is ready stringstream out; out << *this; return out.str(); } ostream& operator<<(ostream& out, const Value& val) { switch (val.getType()) { case EOO: return out << "MISSING"; case MinKey: return out << "MinKey"; case MaxKey: return out << "MaxKey"; case jstOID: return out << val.getOid(); case String: return out << '"' << val.getString() << '"'; case RegEx: return out << '/' << val.getRegex() << '/' << val.getRegexFlags(); case Symbol: return out << "Symbol(\"" << val.getSymbol() << "\")"; case Code: return out << "Code(\"" << val.getCode() << "\")"; case Bool: return out << (val.getBool() ? "true" : "false"); case NumberDouble: return out << val.getDouble(); case NumberLong: return out << val.getLong(); case NumberInt: return out << val.getInt(); case jstNULL: return out << "null"; case Undefined: return out << "undefined"; case Date: return out << tmToISODateString(val.coerceToTm()); case bsonTimestamp: return out << val.getTimestamp().toString(); case Object: return out << val.getDocument().toString(); case Array: { out << "["; const size_t n = val.getArray().size(); for (size_t i = 0; i < n; i++) { if (i) out << ", "; out << val.getArray()[i]; } out << "]"; return out; } case CodeWScope: return out << "CodeWScope(\"" << val._storage.getCodeWScope()->code << "\", " << val._storage.getCodeWScope()->scope << ')'; case BinData: return out << "BinData(" << val._storage.binDataType() << ", \"" << toHex(val._storage.getString().rawData(), val._storage.getString().size()) << "\")"; case DBRef: return out << "DBRef(\"" << val._storage.getDBRef()->ns << "\", " << val._storage.getDBRef()->oid << ')'; } // Not in default case to trigger better warning if a case is missing verify(false); } void Value::serializeForSorter(BufBuilder& buf) const { buf.appendChar(getType()); switch (getType()) { // type-only types case EOO: case MinKey: case MaxKey: case jstNULL: case Undefined: break; // simple types case jstOID: buf.appendStruct(_storage.oid); break; case NumberInt: buf.appendNum(_storage.intValue); break; case NumberLong: buf.appendNum(_storage.longValue); break; case NumberDouble: buf.appendNum(_storage.doubleValue); break; case Bool: buf.appendChar(_storage.boolValue); break; case Date: buf.appendNum(_storage.dateValue); break; case bsonTimestamp: buf.appendStruct(getTimestamp()); break; // types that are like strings case String: case Symbol: case Code: { StringData str = getStringData(); buf.appendNum(int(str.size())); buf.appendStr(str, /*NUL byte*/ false); break; } case BinData: { StringData str = getStringData(); buf.appendChar(_storage.binDataType()); buf.appendNum(int(str.size())); buf.appendStr(str, /*NUL byte*/ false); break; } case RegEx: buf.appendStr(getRegex(), /*NUL byte*/ true); buf.appendStr(getRegexFlags(), /*NUL byte*/ true); break; case Object: getDocument().serializeForSorter(buf); break; case DBRef: buf.appendStruct(_storage.getDBRef()->oid); buf.appendStr(_storage.getDBRef()->ns, /*NUL byte*/ true); break; case CodeWScope: { intrusive_ptr cws = _storage.getCodeWScope(); buf.appendNum(int(cws->code.size())); buf.appendStr(cws->code, /*NUL byte*/ false); cws->scope.serializeForSorter(buf); break; } case Array: { const vector& array = getArray(); const int numElems = array.size(); buf.appendNum(numElems); for (int i = 0; i < numElems; i++) array[i].serializeForSorter(buf); break; } } } Value Value::deserializeForSorter(BufReader& buf, const SorterDeserializeSettings& settings) { const BSONType type = BSONType(buf.read()); // need sign extension for MinKey switch (type) { // type-only types case EOO: case MinKey: case MaxKey: case jstNULL: case Undefined: return Value(ValueStorage(type)); // simple types case jstOID: return Value(OID::from(buf.skip(OID::kOIDSize))); case NumberInt: return Value(buf.read()); case NumberLong: return Value(buf.read()); case NumberDouble: return Value(buf.read()); case Bool: return Value(bool(buf.read())); case Date: return Value(Date_t::fromMillisSinceEpoch(buf.read())); case bsonTimestamp: return Value(buf.read()); // types that are like strings case String: case Symbol: case Code: { int size = buf.read(); const char* str = static_cast(buf.skip(size)); return Value(ValueStorage(type, StringData(str, size))); } case BinData: { BinDataType bdt = BinDataType(buf.read()); int size = buf.read(); const void* data = buf.skip(size); return Value(BSONBinData(data, size, bdt)); } case RegEx: { StringData regex = buf.readCStr(); StringData flags = buf.readCStr(); return Value(BSONRegEx(regex, flags)); } case Object: return Value( Document::deserializeForSorter(buf, Document::SorterDeserializeSettings())); case DBRef: { OID oid = OID::from(buf.skip(OID::kOIDSize)); StringData ns = buf.readCStr(); return Value(BSONDBRef(ns, oid)); } case CodeWScope: { int size = buf.read(); const char* str = static_cast(buf.skip(size)); BSONObj bson = BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings()); return Value(BSONCodeWScope(StringData(str, size), bson)); } case Array: { const int numElems = buf.read(); vector array; array.reserve(numElems); for (int i = 0; i < numElems; i++) array.push_back(deserializeForSorter(buf, settings)); return Value(std::move(array)); } } verify(false); } }