/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * 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 * Server Side Public License for more details. * * You should have received a copy of the Server Side 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 Server Side 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. */ #pragma once #include #include #include // strlen #include #include #include "mongo/base/data_range.h" #include "mongo/base/data_type_endian.h" #include "mongo/base/data_view.h" #include "mongo/base/string_data_comparator_interface.h" #include "mongo/bson/bson_comparator_interface_base.h" #include "mongo/bson/bsontypes.h" #include "mongo/bson/oid.h" #include "mongo/bson/timestamp.h" #include "mongo/config.h" #include "mongo/platform/decimal128.h" #include "mongo/platform/strnlen.h" namespace mongo { class BSONObj; class BSONElement; class BSONObjBuilder; class Timestamp; typedef BSONElement be; typedef BSONObj bo; typedef BSONObjBuilder bob; /** BSONElement represents an "element" in a BSONObj. So for the object { a : 3, b : "abc" }, 'a : 3' is the first element (key+value). The BSONElement object points into the BSONObj's data. Thus the BSONObj must stay in scope for the life of the BSONElement. internals: -------- size() ------------ -fieldNameSize- value() type() */ class BSONElement { public: // Declared in bsonobj_comparator_interface.h. class ComparatorInterface; /** * Operator overloads for relops return a DeferredComparison which can subsequently be evaluated * by a BSONObj::ComparatorInterface. */ using DeferredComparison = BSONComparatorInterfaceBase::DeferredComparison; /** * Set of rules that dictate the behavior of the comparison APIs. */ using ComparisonRules = BSONComparatorInterfaceBase::ComparisonRules; using ComparisonRulesSet = BSONComparatorInterfaceBase::ComparisonRulesSet; /** * Compares two BSON elements of the same canonical type. * * Returns <0 if 'l' is less than the element 'r'. * >0 if 'l' is greater than the element 'r'. * 0 if 'l' is equal to the element 'r'. */ static int compareElements(const BSONElement& l, const BSONElement& r, ComparisonRulesSet rules, const StringData::ComparatorInterface* comparator); /** These functions, which start with a capital letter, throw if the element is not of the required type. Example: std::string foo = obj["foo"].String(); // std::exception if not a std::string type or DNE */ std::string String() const { return chk(mongo::String).str(); } const StringData checkAndGetStringData() const { return chk(mongo::String).valueStringData(); } Date_t Date() const { return chk(mongo::Date).date(); } double Number() const { uassert(13118, str::stream() << "expected " << fieldName() << " to have a numeric type, but it is a " << type(), isNumber()); return number(); } Decimal128 Decimal() const { return chk(NumberDecimal)._numberDecimal(); } double Double() const { return chk(NumberDouble)._numberDouble(); } long long Long() const { return chk(NumberLong)._numberLong(); } int Int() const { return chk(NumberInt)._numberInt(); } bool Bool() const { return chk(mongo::Bool).boolean(); } std::vector Array() const; // see implementation for detailed comments mongo::OID OID() const { return chk(jstOID).__oid(); } /** @return the embedded object associated with this field. Note the returned object is a reference to within the parent bson object. If that object is out of scope, this pointer will no longer be valid. Call getOwned() on the returned BSONObj if you need your own copy. throws AssertionException if the element is not of type object. */ BSONObj Obj() const; /** populate v with the value of the element. If type does not match, throw exception. useful in templates -- see also BSONObj::Vals(). */ void Val(Date_t& v) const { v = Date(); } void Val(long long& v) const { v = Long(); } void Val(Decimal128& v) const { v = Decimal(); } void Val(bool& v) const { v = Bool(); } void Val(BSONObj& v) const; void Val(mongo::OID& v) const { v = OID(); } void Val(int& v) const { v = Int(); } void Val(double& v) const { v = Double(); } void Val(std::string& v) const { v = String(); } /** Use ok() to check if a value is assigned: if( myObj["foo"].ok() ) ... */ bool ok() const { return !eoo(); } /** * True if this element has a value (ie not EOO). * * Makes it easier to check for a field's existence and use it: * if (auto elem = myObj["foo"]) { * // Use elem * } * else { * // default behavior * } */ explicit operator bool() const { return ok(); } std::string toString(bool includeFieldName = true, bool full = false) const; void toString(StringBuilder& s, bool includeFieldName = true, bool full = false, bool redactValues = false, int depth = 0) const; std::string jsonString(JsonStringFormat format, bool includeFieldNames = true, int pretty = 0) const; void jsonStringStream(JsonStringFormat format, bool includeFieldNames, int pretty, std::stringstream& s) const; operator std::string() const { return toString(); } /** Returns the type of the element */ BSONType type() const { const signed char typeByte = ConstDataView(data).read(); return static_cast(typeByte); } /** retrieve a field within this element throws exception if *this is not an embedded object */ BSONElement operator[](StringData field) const; /** See canonicalizeBSONType in bsontypes.h */ int canonicalType() const { return canonicalizeBSONType(type()); } /** Indicates if it is the end-of-object element, which is present at the end of every BSON object. */ bool eoo() const { return type() == EOO; } /** * Size of the element. */ int size() const { return totalSize; } /** Wrap this element up as a singleton object. */ BSONObj wrap() const; /** Wrap this element up as a singleton object with a new name. */ BSONObj wrap(StringData newName) const; /** field name of the element. e.g., for name : "Joe" "name" is the fieldname */ const char* fieldName() const { if (eoo()) return ""; // no fieldname for it. return data + 1; } /** * NOTE: size includes the NULL terminator. */ int fieldNameSize() const { return fieldNameSize_; } const StringData fieldNameStringData() const { return StringData(fieldName(), eoo() ? 0 : fieldNameSize() - 1); } /** raw data of the element's value (so be careful). */ const char* value() const { return (data + fieldNameSize() + 1); } /** size in bytes of the element's value (when applicable). */ int valuesize() const { return size() - fieldNameSize() - 1; } bool isBoolean() const { return type() == mongo::Bool; } /** @return value of a boolean element. You must assure element is a boolean before calling. */ bool boolean() const { return *value() ? true : false; } bool booleanSafe() const { return isBoolean() && boolean(); } /** Retrieve a java style date value from the element. Ensure element is of type Date before calling. @see Bool(), trueValue() */ Date_t date() const { return Date_t::fromMillisSinceEpoch(ConstDataView(value()).read>()); } /** Convert the value to boolean, regardless of its type, in a javascript-like fashion (i.e., treats zero and null and eoo as false). */ bool trueValue() const; /** True if element is of a numeric type. */ bool isNumber() const; /** Return double value for this field. MUST be NumberDouble type. */ double _numberDouble() const { return ConstDataView(value()).read>(); } /** Return int value for this field. MUST be NumberInt type. */ int _numberInt() const { return ConstDataView(value()).read>(); } /** Return decimal128 value for this field. MUST be NumberDecimal type. */ Decimal128 _numberDecimal() const { uint64_t low = ConstDataView(value()).read>(); uint64_t high = ConstDataView(value() + sizeof(long long)).read>(); return Decimal128(Decimal128::Value({low, high})); } /** Return long long value for this field. MUST be NumberLong type. */ long long _numberLong() const { return ConstDataView(value()).read>(); } /** Retrieve int value for the element safely. Zero returned if not a number. */ int numberInt() const; /** Retrieve long value for the element safely. Zero returned if not a number. * Behavior is not defined for double values that are NaNs, or too large/small * to be represented by long longs */ long long numberLong() const; /** Like numberLong() but with well-defined behavior for doubles that * are NaNs, or too large/small to be represented as long longs. * NaNs -> 0 * very large doubles -> LLONG_MAX * very small doubles -> LLONG_MIN */ long long safeNumberLong() const; /** This safeNumberLongForHash() function does the same thing as safeNumberLong, but it * preserves edge-case behavior from older versions. */ long long safeNumberLongForHash() const; /** * Parses a BSONElement of any numeric type into a positive long long, failing if the value * is any of the following: * * - NaN. * - Negative. * - A floating point number which is not integral. * - Too large to fit within a 64-bit signed integer. */ StatusWith parseIntegerElementToNonNegativeLong() const; /** * Parses a BSONElement of any numeric type into a long long, failing if the value * is any of the following: * * - NaN. * - A floating point number which is not integral. * - Too large in the positive or negative direction to fit within a 64-bit signed integer. */ StatusWith parseIntegerElementToLong() const; /** * Parses a BSONElement of any numeric type into an integer, failing if the value is: * * - NaN * - a non-integral number * - too large in the positive or negative direction to fit in an int */ StatusWith parseIntegerElementToInt() const; /** Retrieve decimal value for the element safely. */ Decimal128 numberDecimal() const; /** Retrieve the numeric value of the element. If not of a numeric type, returns 0. Note: casts to double, data loss may occur with large (>52 bit) NumberLong values. */ double numberDouble() const; /** Retrieve the numeric value of the element. If not of a numeric type, returns 0. Note: casts to double, data loss may occur with large (>52 bit) NumberLong values. */ double number() const { return numberDouble(); } /** Retrieve the object ID stored in the object. You must ensure the element is of type jstOID first. */ mongo::OID __oid() const { return OID::from(value()); } /** True if element is null. */ bool isNull() const { return type() == jstNULL; } /** Size of a BSON String element. Requires that type() == mongo::String. @return String size including its null-termination. */ int valuestrsize() const { return ConstDataView(value()).read>(); } // for objects the size *includes* the size of the size field size_t objsize() const { return ConstDataView(value()).read>(); } /** Get a string's value. Also gives you start of the real data for an embedded object. You must assure data is of an appropriate type first -- see also valuestrsafe(). */ const char* valuestr() const { return value() + 4; } /** Like valuestr, but returns a valid empty string if `type() != mongo::String`. */ const char* valuestrsafe() const { return type() == mongo::String ? valuestr() : ""; } /** Like valuestrsafe, but returns StringData. */ StringData valueStringDataSafe() const { return type() == mongo::String ? StringData(valuestr(), valuestrsize() - 1) : StringData(); } /** Like valuestrsafe, but returns std::string. */ std::string str() const { return valueStringDataSafe().toString(); } /** * Returns a StringData pointing into this element's data. Does not validate that the * element is actually of type String. */ const StringData valueStringData() const { return StringData(valuestr(), valuestrsize() - 1); } /** Get javascript code of a CodeWScope data element. */ const char* codeWScopeCode() const { massert(16177, "not codeWScope", type() == CodeWScope); return value() + 4 + 4; // two ints precede code (see BSON spec) } /** Get length of the code part of the CodeWScope object * This INCLUDES the null char at the end */ int codeWScopeCodeLen() const { massert(16178, "not codeWScope", type() == CodeWScope); return ConstDataView(value() + 4).read>(); } /** Get the scope SavedContext of a CodeWScope data element. * * This function is DEPRECATED, since it can error if there are * null chars in the codeWScopeCode. However, some existing indexes * may be based on an incorrect ordering derived from this function, * so it may still need to be used in certain cases. * */ const char* codeWScopeScopeDataUnsafe() const { // This can error if there are null chars in the codeWScopeCode return codeWScopeCode() + strlen(codeWScopeCode()) + 1; } /* Get the scope SavedContext of a CodeWScope data element. * * This is the corrected version of codeWScopeScopeDataUnsafe(), * but note that existing uses might rely on the behavior of * that function so be careful in choosing which version to use. */ const char* codeWScopeScopeData() const { return codeWScopeCode() + codeWScopeCodeLen(); } /** Get the embedded object this element holds. */ BSONObj embeddedObject() const; /* uasserts if not an object */ BSONObj embeddedObjectUserCheck() const; BSONObj codeWScopeObject() const; /** Get raw binary data. Element must be of type BinData. Doesn't handle type 2 specially */ const char* binData(int& len) const { // BinData: verify(type() == BinData); len = valuestrsize(); return value() + 5; } /** Get binary data. Element must be of type BinData. Handles type 2 */ const char* binDataClean(int& len) const { // BinData: if (binDataType() != ByteArrayDeprecated) { return binData(len); } else { // Skip extra size len = valuestrsize() - 4; return value() + 5 + 4; } } BinDataType binDataType() const { // BinData: verify(type() == BinData); unsigned char c = (value() + 4)[0]; return static_cast(c); } std::vector _binDataVector() const { if (binDataType() != ByteArrayDeprecated) { return std::vector(reinterpret_cast(value()) + 5, reinterpret_cast(value()) + 5 + valuestrsize()); } else { // Skip the extra int32 size return std::vector(reinterpret_cast(value()) + 4, reinterpret_cast(value()) + 4 + valuestrsize() - 4); } } /** Retrieve the regex std::string for a Regex element */ const char* regex() const { verify(type() == RegEx); return value(); } /** Retrieve the regex flags (options) for a Regex element */ const char* regexFlags() const { const char* p = regex(); return p + strlen(p) + 1; } // // Comparison API. // // BSONElement instances can be compared via a raw bytewise comparison or a logical comparison. // // Logical comparison can be done either using woCompare() or with operator overloads. Most // callers should prefer operator overloads. Note that the operator overloads return a // DeferredComparison, which must subsequently be evaluated by a // BSONElement::ComparatorInterface. See bsonelement_comparator_interface.h for details. // /** * Compares the raw bytes of the two BSONElements, including the field names. This will treat * different types (e.g. integers and doubles) as distinct values, even if they have the same * field name and bit pattern in the value portion of the BSON element. */ bool binaryEqual(const BSONElement& rhs) const; /** * Compares the raw bytes of the two BSONElements, excluding the field names. This will treat * different types (e.g integers and doubles) as distinct values, even if they have the same bit * pattern in the value portion of the BSON element. */ bool binaryEqualValues(const BSONElement& rhs) const; /** * Compares two BSON Elements using the rules specified by 'rules' and the 'comparator' for * string comparisons. * * Returns <0 if 'this' is less than 'elem'. * >0 if 'this' is greater than 'elem'. * 0 if 'this' is equal to 'elem'. */ int woCompare(const BSONElement& elem, ComparisonRulesSet rules = ComparisonRules::kConsiderFieldName, const StringData::ComparatorInterface* comparator = nullptr) const; DeferredComparison operator<(const BSONElement& other) const { return DeferredComparison(DeferredComparison::Type::kLT, *this, other); } DeferredComparison operator<=(const BSONElement& other) const { return DeferredComparison(DeferredComparison::Type::kLTE, *this, other); } DeferredComparison operator>(const BSONElement& other) const { return DeferredComparison(DeferredComparison::Type::kGT, *this, other); } DeferredComparison operator>=(const BSONElement& other) const { return DeferredComparison(DeferredComparison::Type::kGTE, *this, other); } DeferredComparison operator==(const BSONElement& other) const { return DeferredComparison(DeferredComparison::Type::kEQ, *this, other); } DeferredComparison operator!=(const BSONElement& other) const { return DeferredComparison(DeferredComparison::Type::kNE, *this, other); } const char* rawdata() const { return data; } /** Constructs an empty element */ BSONElement(); /** True if this element may contain subobjects. */ bool mayEncapsulate() const { switch (type()) { case Object: case mongo::Array: case CodeWScope: return true; default: return false; } } /** True if this element can be a BSONObj */ bool isABSONObj() const { switch (type()) { case Object: case mongo::Array: return true; default: return false; } } Timestamp timestamp() const { if (type() == mongo::Date || type() == bsonTimestamp) { return Timestamp(ConstDataView(value()).read>().value); } return Timestamp(); } bool isBinData(BinDataType bdt) const { return (type() == BinData) && (binDataType() == bdt); } const std::array uuid() const { int len = 0; const char* data = nullptr; if (isBinData(BinDataType::newUUID)) { data = binData(len); } uassert(ErrorCodes::InvalidUUID, "uuid must be a 16-byte binary field with UUID (4) subtype", len == 16); std::array result; memcpy(&result, data, len); return result; } const std::array md5() const { int len = 0; const char* data = nullptr; if (isBinData(BinDataType::MD5Type)) { data = binData(len); } uassert(40437, "md5 must be a 16-byte binary field with MD5 (5) subtype", len == 16); std::array result; memcpy(&result, data, len); return result; } Date_t timestampTime() const { unsigned long long t = ConstDataView(value() + 4).read>(); return Date_t::fromMillisSinceEpoch(t * 1000); } unsigned int timestampInc() const { return ConstDataView(value()).read>(); } unsigned long long timestampValue() const { return ConstDataView(value()).read>(); } const char* dbrefNS() const { uassert(10063, "not a dbref", type() == DBRef); return value() + 4; } const mongo::OID dbrefOID() const { uassert(10064, "not a dbref", type() == DBRef); const char* start = value(); start += 4 + ConstDataView(start).read>(); return mongo::OID::from(start); } // @param maxLen don't scan more than maxLen bytes explicit BSONElement(const char* d) : data(d) { if (eoo()) { fieldNameSize_ = 0; totalSize = 1; } else { fieldNameSize_ = strlen(d + 1 /*skip type*/) + 1 /*include NUL byte*/; totalSize = computeSize(); } } struct CachedSizeTag {}; // Opts in to next constructor. /** * Construct a BSONElement where you already know the length of the name and/or the total size * of the element. fieldNameSize includes the null terminator. You may pass -1 for either or * both sizes to indicate that they are unknown and should be computed. */ BSONElement(const char* d, int fieldNameSize, int totalSize, CachedSizeTag) : data(d) { if (eoo()) { fieldNameSize_ = 0; this->totalSize = 1; } else { if (fieldNameSize == -1) { fieldNameSize_ = strlen(d + 1 /*skip type*/) + 1 /*include NUL byte*/; } else { fieldNameSize_ = fieldNameSize; } if (totalSize == -1) { this->totalSize = computeSize(); } else { this->totalSize = totalSize; } } } std::string _asCode() const; bool coerce(std::string* out) const; bool coerce(int* out) const; bool coerce(long long* out) const; bool coerce(double* out) const; bool coerce(bool* out) const; bool coerce(Decimal128* out) const; bool coerce(std::vector* out) const; /** * Constant double representation of 2^63, the smallest value that will overflow a long long. * * It is not safe to obtain this value by casting std::numeric_limits::max() to * double, because the conversion loses precision, and the C++ standard leaves it up to the * implementation to decide whether to round up to 2^63 or round down to the next representable * value (2^63 - 2^10). */ static const double kLongLongMaxPlusOneAsDouble; private: const char* data; int fieldNameSize_; // internal size includes null terminator int totalSize; friend class BSONObjIterator; friend class BSONObjStlIterator; friend class BSONObj; const BSONElement& chk(BSONType t) const { if (t != type()) { StringBuilder ss; if (eoo()) ss << "field not found, expected type " << t; else ss << "wrong type for field (" << fieldName() << ") " << type() << " != " << t; uasserted(13111, ss.str()); } return *this; } // Only called from constructors. int computeSize() const; }; inline bool BSONElement::trueValue() const { // NOTE Behavior changes must be replicated in Value::coerceToBool(). switch (type()) { case NumberLong: return _numberLong() != 0; case NumberDouble: return _numberDouble() != 0; case NumberDecimal: return _numberDecimal().isNotEqual(Decimal128(0)); case NumberInt: return _numberInt() != 0; case mongo::Bool: return boolean(); case EOO: case jstNULL: case Undefined: return false; default: return true; } } /** @return true if element is of a numeric type. */ inline bool BSONElement::isNumber() const { switch (type()) { case NumberLong: case NumberDouble: case NumberDecimal: case NumberInt: return true; default: return false; } } inline Decimal128 BSONElement::numberDecimal() const { switch (type()) { case NumberDouble: return Decimal128(_numberDouble()); case NumberInt: return Decimal128(_numberInt()); case NumberLong: return Decimal128(static_cast(_numberLong())); case NumberDecimal: return _numberDecimal(); default: return Decimal128::kNormalizedZero; } } inline double BSONElement::numberDouble() const { switch (type()) { case NumberDouble: return _numberDouble(); case NumberInt: return _numberInt(); case NumberLong: return _numberLong(); case NumberDecimal: return _numberDecimal().toDouble(); default: return 0; } } /** Retrieve int value for the element safely. Zero returned if not a number. Converted to int if * another numeric type. */ inline int BSONElement::numberInt() const { switch (type()) { case NumberDouble: return (int)_numberDouble(); case NumberInt: return _numberInt(); case NumberLong: return (int)_numberLong(); case NumberDecimal: return _numberDecimal().toInt(); default: return 0; } } /** Retrieve long value for the element safely. Zero returned if not a number. */ inline long long BSONElement::numberLong() const { switch (type()) { case NumberDouble: return (long long)_numberDouble(); case NumberInt: return _numberInt(); case NumberLong: return _numberLong(); case NumberDecimal: return _numberDecimal().toLong(); default: return 0; } } /** Like numberLong() but with well-defined behavior for doubles and decimals that * are NaNs, or too large/small to be represented as long longs. * NaNs -> 0 * very large values -> LLONG_MAX * very small values -> LLONG_MIN */ inline long long BSONElement::safeNumberLong() const { switch (type()) { case NumberDouble: { double d = numberDouble(); if (std::isnan(d)) { return 0; } if (!(d < kLongLongMaxPlusOneAsDouble)) { return std::numeric_limits::max(); } if (d < std::numeric_limits::min()) { return std::numeric_limits::min(); } return numberLong(); } case NumberDecimal: { Decimal128 d = numberDecimal(); if (d.isNaN()) { return 0; } if (d.isGreater(Decimal128(std::numeric_limits::max()))) { return static_cast(std::numeric_limits::max()); } if (d.isLess(Decimal128(std::numeric_limits::min()))) { return static_cast(std::numeric_limits::min()); } return numberLong(); } default: return numberLong(); } } /** * This safeNumberLongForHash() function does the same thing as safeNumberLong, but it preserves * edge-case behavior from older versions. It's provided for use by hash functions that need to * maintain compatibility with older versions. Don't make any changes to safeNumberLong() without * ensuring that this function (which is implemented in terms of safeNumberLong()) has exactly the * same behavior. * * Historically, safeNumberLong() used a check that would consider 2^63 to be safe to cast to * int64_t, but that cast actually overflows. On most platforms, the undefined cast of 2^63 to * int64_t would roll over to -2^63, and that's the behavior we preserve here explicitly. * * The new safeNumberLong() function uses a tight bound, allowing it to correctly clamp double 2^63 * to the max 64-bit int (2^63 - 1). */ inline long long BSONElement::safeNumberLongForHash() const { // Rather than relying on the undefined overflow conversion, we maintain compatibility by // explicitly checking for a 2^63 double value and returning -2^63. if (NumberDouble == type() && numberDouble() == BSONElement::kLongLongMaxPlusOneAsDouble) { return std::numeric_limits::lowest(); } else { return safeNumberLong(); } } inline BSONElement::BSONElement() { // This needs to be 2 elements because we check the strlen of data + 1 and GCC sees that as // accessing beyond the end of a constant string, even though we always check whether the // element is an eoo. static const char kEooElement[2] = {'\0', '\0'}; data = kEooElement; fieldNameSize_ = 0; totalSize = 1; } } // namespace mongo