/** * Copyright (c) 2012 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. */ #pragma once #include #include #include "mongo/base/static_assert.h" #include "mongo/bson/bsonmisc.h" #include "mongo/bson/bsonobj.h" #include "mongo/bson/bsontypes.h" #include "mongo/bson/oid.h" #include "mongo/bson/timestamp.h" #include "mongo/util/debug_util.h" #include "mongo/util/intrusive_counter.h" namespace mongo { class Document; class DocumentStorage; class Value; // TODO: a MutableVector, similar to MutableDocument /// A heap-allocated reference-counted std::vector class RCVector : public RefCountable { public: RCVector() {} RCVector(std::vector v) : vec(std::move(v)) {} std::vector vec; }; class RCCodeWScope : public RefCountable { public: RCCodeWScope(const std::string& str, BSONObj obj) : code(str), scope(obj.getOwned()) {} const std::string code; const BSONObj scope; // Not worth converting to Document for now }; class RCDBRef : public RefCountable { public: RCDBRef(const std::string& str, const OID& o) : ns(str), oid(o) {} const std::string ns; const OID oid; }; class RCDecimal : public RefCountable { public: RCDecimal(const Decimal128& decVal) : decimalValue(decVal) {} const Decimal128 decimalValue; }; class ValueStorage { public: // Note: it is important the memory is zeroed out (by calling zero()) at the start of every // constructor. Much code relies on every byte being predictably initialized to zero. // This is a "missing" Value ValueStorage() { zero(); type = EOO; } explicit ValueStorage(BSONType t) { zero(); type = t; } ValueStorage(BSONType t, int i) { zero(); type = t; intValue = i; } ValueStorage(BSONType t, long long l) { zero(); type = t; longValue = l; } ValueStorage(BSONType t, double d) { zero(); type = t; doubleValue = d; } ValueStorage(BSONType t, const Decimal128& d) { zero(); type = t; putDecimal(d); } ValueStorage(BSONType t, Timestamp r) { zero(); type = t; timestampValue = r.asULL(); } ValueStorage(BSONType t, bool b) { zero(); type = t; boolValue = b; } ValueStorage(BSONType t, const Document& d) { zero(); type = t; putDocument(d); } ValueStorage(BSONType t, const RCVector* a) { zero(); type = t; putVector(a); } ValueStorage(BSONType t, StringData s) { zero(); type = t; putString(s); } ValueStorage(BSONType t, const BSONBinData& bd) { zero(); type = t; putBinData(bd); } ValueStorage(BSONType t, const BSONRegEx& re) { zero(); type = t; putRegEx(re); } ValueStorage(BSONType t, const BSONCodeWScope& cs) { zero(); type = t; putCodeWScope(cs); } ValueStorage(BSONType t, const BSONDBRef& dbref) { zero(); type = t; putDBRef(dbref); } ValueStorage(BSONType t, const OID& o) { zero(); type = t; memcpy(&oid, o.view().view(), OID::kOIDSize); } ValueStorage(const ValueStorage& rhs) { memcpy(this, &rhs, sizeof(*this)); memcpyed(); } ValueStorage(ValueStorage&& rhs) noexcept { memcpy(this, &rhs, sizeof(*this)); rhs.zero(); // Reset rhs to the missing state. TODO consider only doing this if refCounter. } ~ValueStorage() { DEV verifyRefCountingIfShould(); if (refCounter) intrusive_ptr_release(genericRCPtr); DEV memset(this, 0xee, sizeof(*this)); } ValueStorage& operator=(const ValueStorage& rhs) { // This is designed to be effectively a no-op on self-assign, without needing an explicit // check. This requires that rhs's refcount is incremented before ours is released, and that // we use memmove rather than memcpy. DEV rhs.verifyRefCountingIfShould(); if (rhs.refCounter) intrusive_ptr_add_ref(rhs.genericRCPtr); DEV verifyRefCountingIfShould(); if (refCounter) intrusive_ptr_release(genericRCPtr); memmove(this, &rhs, sizeof(*this)); return *this; } ValueStorage& operator=(ValueStorage&& rhs) noexcept { DEV verifyRefCountingIfShould(); if (refCounter) intrusive_ptr_release(genericRCPtr); memmove(this, &rhs, sizeof(*this)); rhs.zero(); // Reset rhs to the missing state. TODO consider only doing this if refCounter. return *this; } void swap(ValueStorage& rhs) { // Don't need to update ref-counts because they will be the same in the end char temp[sizeof(ValueStorage)]; memcpy(temp, this, sizeof(*this)); memcpy(this, &rhs, sizeof(*this)); memcpy(&rhs, temp, sizeof(*this)); } /// Call this after memcpying to update ref counts if needed void memcpyed() const { DEV verifyRefCountingIfShould(); if (refCounter) intrusive_ptr_add_ref(genericRCPtr); } /// These are only to be called during Value construction on an empty Value void putString(StringData s); void putVector(const RCVector* v); void putDocument(const Document& d); void putRegEx(const BSONRegEx& re); void putBinData(const BSONBinData& bd) { putRefCountable(RCString::create(StringData(static_cast(bd.data), bd.length))); binSubType = bd.type; } void putDBRef(const BSONDBRef& dbref) { putRefCountable(new RCDBRef(dbref.ns.toString(), dbref.oid)); } void putCodeWScope(const BSONCodeWScope& cws) { putRefCountable(new RCCodeWScope(cws.code.toString(), cws.scope)); } void putDecimal(const Decimal128& d) { putRefCountable(new RCDecimal(d)); } void putRefCountable(boost::intrusive_ptr ptr) { genericRCPtr = ptr.get(); if (genericRCPtr) { intrusive_ptr_add_ref(genericRCPtr); refCounter = true; } DEV verifyRefCountingIfShould(); } StringData getString() const { if (shortStr) { return StringData(shortStrStorage, shortStrSize); } else { dassert(typeid(*genericRCPtr) == typeid(const RCString)); const RCString* stringPtr = static_cast(genericRCPtr); return StringData(stringPtr->c_str(), stringPtr->size()); } } const std::vector& getArray() const { dassert(typeid(*genericRCPtr) == typeid(const RCVector)); const RCVector* arrayPtr = static_cast(genericRCPtr); return arrayPtr->vec; } boost::intrusive_ptr getCodeWScope() const { dassert(typeid(*genericRCPtr) == typeid(const RCCodeWScope)); return static_cast(genericRCPtr); } boost::intrusive_ptr getDBRef() const { dassert(typeid(*genericRCPtr) == typeid(const RCDBRef)); return static_cast(genericRCPtr); } Decimal128 getDecimal() const { dassert(typeid(*genericRCPtr) == typeid(const RCDecimal)); const RCDecimal* decPtr = static_cast(genericRCPtr); return decPtr->decimalValue; } // Document is incomplete here so this can't be inline Document getDocument() const; BSONType bsonType() const { return BSONType(type); } BinDataType binDataType() const { dassert(type == BinData); return BinDataType(binSubType); } void zero() { memset(this, 0, sizeof(*this)); } // Byte-for-byte identical bool identical(const ValueStorage& other) const { return (i64[0] == other.i64[0] && i64[1] == other.i64[1]); } void verifyRefCountingIfShould() const; // This data is public because this should only be used by Value which would be a friend union { #pragma pack(1) struct { // byte 1 signed char type; // byte 2 struct { bool refCounter : 1; // true if we need to refCount bool shortStr : 1; // true if we are using short strings // reservedFlags: 6; }; // bytes 3-16; union { unsigned char oid[12]; struct { char shortStrSize; // TODO Consider moving into flags union (4 bits) char shortStrStorage[16 /*total bytes*/ - 3 /*offset*/ - 1 /*NUL byte*/]; union { char nulTerminator; }; }; struct { union { unsigned char binSubType; char pad[6]; char stringCache[6]; // TODO copy first few bytes of strings in here }; union { // 8 bytes long and 8-byte aligned // There should be no pointers to non-const data const RefCountable* genericRCPtr; double doubleValue; bool boolValue; int intValue; long long longValue; unsigned long long timestampValue; long long dateValue; }; }; }; }; #pragma pack() // covers the whole ValueStorage long long i64[2]; // Forces the ValueStorage type to have at least pointer alignment. Can't use alignas on the // type since that causes issues on MSVC. void* forcePointerAlignment; }; }; MONGO_STATIC_ASSERT(sizeof(ValueStorage) == 16); MONGO_STATIC_ASSERT(alignof(ValueStorage) >= alignof(void*)); }