diff options
| author | Antonin Kral <a.kral@bobek.cz> | 2012-08-29 20:54:51 +0200 |
|---|---|---|
| committer | Antonin Kral <a.kral@bobek.cz> | 2012-08-29 20:54:51 +0200 |
| commit | 83957b73f9177f6e38bd5375bd93ca1f6a47188c (patch) | |
| tree | f20b7d6ac9a9c64ff5bb6b5910a24abbb356b1d5 /src/mongo/db/pipeline/value.cpp | |
| parent | 5071d203970edd4c995493d810abe20987e76fe9 (diff) | |
Imported Upstream version 2.2.0upstream/2.2.0
Diffstat (limited to 'src/mongo/db/pipeline/value.cpp')
| -rw-r--r-- | src/mongo/db/pipeline/value.cpp | 1129 |
1 files changed, 1129 insertions, 0 deletions
diff --git a/src/mongo/db/pipeline/value.cpp b/src/mongo/db/pipeline/value.cpp new file mode 100644 index 00000000000..ff75022a4fb --- /dev/null +++ b/src/mongo/db/pipeline/value.cpp @@ -0,0 +1,1129 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "pch.h" +#include "db/pipeline/value.h" + +#include <boost/functional/hash.hpp> +#include "db/jsobj.h" +#include "db/pipeline/builder.h" +#include "db/pipeline/document.h" +#include "util/mongoutils/str.h" + +namespace mongo { + using namespace mongoutils; + + const intrusive_ptr<const Value> Value::pFieldUndefined( + new ValueStatic(Undefined)); + const intrusive_ptr<const Value> Value::pFieldNull(new ValueStatic()); + const intrusive_ptr<const Value> Value::pFieldTrue(new ValueStatic(true)); + const intrusive_ptr<const Value> Value::pFieldFalse(new ValueStatic(false)); + const intrusive_ptr<const Value> Value::pFieldMinusOne(new ValueStatic(-1)); + const intrusive_ptr<const Value> Value::pFieldZero(new ValueStatic(0)); + const intrusive_ptr<const Value> Value::pFieldOne(new ValueStatic(1)); + + Value::~Value() { + } + + Value::Value(): type(jstNULL) {} + + Value::Value(BSONType theType): type(theType) { + switch(type) { + case Undefined: + case jstNULL: + case Object: // empty + case Array: // empty + break; + + case Bool: + boolValue = false; + break; + + case NumberDouble: + doubleValue = 0; + break; + + case NumberInt: + intValue = 0; + break; + + case NumberLong: + longValue = 0; + break; + + case Date: + dateValue = 0; + break; + + case Timestamp: + timestampValue = 0; + break; + + default: + // nothing else is allowed + uassert(16001, str::stream() << + "can't create empty Value of type " << typeName(type), false); + break; + } + } + + Value::Value(bool value) + : type(Bool) + , boolValue(value) + {} + + intrusive_ptr<const Value> Value::createFromBsonElement( + BSONElement *pBsonElement) { + switch (pBsonElement->type()) { + case Undefined: + return getUndefined(); + case jstNULL: + return getNull(); + case Bool: + if (pBsonElement->boolean()) + return getTrue(); + else + return getFalse(); + default: + intrusive_ptr<const Value> pValue(new Value(pBsonElement)); + return pValue; + } + } + + Value::Value(BSONElement *pBsonElement): + type(pBsonElement->type()), + pDocumentValue(), + vpValue() { + switch(type) { + case NumberDouble: + doubleValue = pBsonElement->Double(); + break; + + case String: + stringValue = pBsonElement->str(); + break; + + case Object: { + BSONObj document(pBsonElement->embeddedObject()); + pDocumentValue = Document::createFromBsonObj(&document); + break; + } + + case Array: { + vector<BSONElement> vElement(pBsonElement->Array()); + const size_t n = vElement.size(); + + vpValue.reserve(n); // save on realloc()ing + + for(size_t i = 0; i < n; ++i) { + vpValue.push_back( + Value::createFromBsonElement(&vElement[i])); + } + break; + } + + case jstOID: + BOOST_STATIC_ASSERT(sizeof(oidValue) == sizeof(OID)); + memcpy(oidValue, pBsonElement->OID().getData(), sizeof(oidValue)); + break; + + case Bool: + boolValue = pBsonElement->Bool(); + break; + + case Date: + // this is really signed but typed as unsigned for historical reasons + dateValue = static_cast<long long>(pBsonElement->Date().millis); + break; + + case RegEx: + stringValue = pBsonElement->regex(); + // TODO pBsonElement->regexFlags(); + break; + + case NumberInt: + intValue = pBsonElement->numberInt(); + break; + + case Timestamp: + // asDate is a poorly named function that returns a ReplTime + timestampValue = pBsonElement->_opTime().asDate(); + break; + + case NumberLong: + longValue = pBsonElement->numberLong(); + break; + + case Undefined: + case jstNULL: + break; + + case BinData: + case Symbol: + case CodeWScope: + + /* these shouldn't happen in this context */ + case MinKey: + case EOO: + case DBRef: + case Code: + case MaxKey: + uassert(16002, str::stream() << + "can't create Value of BSON type " << typeName(type), false); + break; + } + } + + Value::Value(int value) + : type(NumberInt) + , intValue(value) + {} + + intrusive_ptr<const Value> Value::createInt(int value) { + intrusive_ptr<const Value> pValue(new Value(value)); + return pValue; + } + + intrusive_ptr<const Value> Value::createIntOrLong(long long value) { + if (value > numeric_limits<int>::max() || value < numeric_limits<int>::min()) { + // it is too large to be an int and should remain a long + return new Value(value); + } + + // should be an int since all arguments were int and it fits + return createInt(value); + } + + Value::Value(long long value) + : type(NumberLong) + , longValue(value) + {} + + intrusive_ptr<const Value> Value::createLong(long long value) { + intrusive_ptr<const Value> pValue(new Value(value)); + return pValue; + } + + Value::Value(double value) + : type(NumberDouble) + , doubleValue(value) + {} + + intrusive_ptr<const Value> Value::createDouble(double value) { + intrusive_ptr<const Value> pValue(new Value(value)); + return pValue; + } + + intrusive_ptr<const Value> Value::createDate(const long long &value) { + // Can't directly construct because constructor would clash with createLong + intrusive_ptr<Value> pValue(new Value(Date)); + pValue->dateValue = value; + return pValue; + } + + Value::Value(const OpTime& value) + : type(Timestamp) + , timestampValue(value.asDate()) + {} + + intrusive_ptr<const Value> Value::createTimestamp(const OpTime& value) { + intrusive_ptr<const Value> pValue(new Value(value)); + return pValue; + } + + Value::Value(const string &value): + type(String), + pDocumentValue(), + vpValue() { + stringValue = value; + } + + intrusive_ptr<const Value> Value::createString(const string &value) { + intrusive_ptr<const Value> pValue(new Value(value)); + return pValue; + } + + Value::Value(const intrusive_ptr<Document> &pDocument): + type(Object), + pDocumentValue(pDocument), + vpValue() { + } + + intrusive_ptr<const Value> Value::createDocument( + const intrusive_ptr<Document> &pDocument) { + intrusive_ptr<const Value> pValue(new Value(pDocument)); + return pValue; + } + + Value::Value(const vector<intrusive_ptr<const Value> > &thevpValue): + type(Array), + pDocumentValue(), + vpValue(thevpValue) { + } + + intrusive_ptr<const Value> Value::createArray( + const vector<intrusive_ptr<const Value> > &vpValue) { + intrusive_ptr<const Value> pValue(new Value(vpValue)); + return pValue; + } + + double Value::getDouble() const { + BSONType type = getType(); + if (type == NumberInt) + return intValue; + if (type == NumberLong) + return static_cast< double >( longValue ); + + verify(type == NumberDouble); + return doubleValue; + } + + string Value::getString() const { + verify(getType() == String); + return stringValue; + } + + intrusive_ptr<Document> Value::getDocument() const { + verify(getType() == Object); + return pDocumentValue; + } + + ValueIterator::~ValueIterator() { + } + + Value::vi::~vi() { + } + + bool Value::vi::more() const { + return (nextIndex < size); + } + + intrusive_ptr<const Value> Value::vi::next() { + verify(more()); + return (*pvpValue)[nextIndex++]; + } + + Value::vi::vi(const intrusive_ptr<const Value> &pValue, + const vector<intrusive_ptr<const Value> > *thepvpValue): + size(thepvpValue->size()), + nextIndex(0), + pvpValue(thepvpValue) { + } + + intrusive_ptr<ValueIterator> Value::getArray() const { + verify(getType() == Array); + intrusive_ptr<ValueIterator> pVI( + new vi(intrusive_ptr<const Value>(this), &vpValue)); + return pVI; + } + + OID Value::getOid() const { + verify(getType() == jstOID); + return OID(oidValue); + } + + bool Value::getBool() const { + verify(getType() == Bool); + return boolValue; + } + + long long Value::getDate() const { + verify(getType() == Date); + return dateValue; + } + + OpTime Value::getTimestamp() const { + verify(getType() == Timestamp); + return timestampValue; + } + + string Value::getRegex() const { + verify(getType() == RegEx); + return stringValue; + } + + string Value::getSymbol() const { + verify(getType() == Symbol); + return stringValue; + } + + int Value::getInt() const { + verify(getType() == NumberInt); + return intValue; + } + + long long Value::getLong() const { + BSONType type = getType(); + if (type == NumberInt) + return intValue; + + verify(type == NumberLong); + return longValue; + } + + void Value::addToBson(Builder *pBuilder) const { + switch(getType()) { + case NumberDouble: + pBuilder->append(getDouble()); + break; + + case String: + pBuilder->append(getString()); + break; + + case Object: { + intrusive_ptr<Document> pDocument(getDocument()); + BSONObjBuilder subBuilder; + pDocument->toBson(&subBuilder); + subBuilder.done(); + pBuilder->append(&subBuilder); + break; + } + + case Array: { + const size_t n = vpValue.size(); + BSONArrayBuilder arrayBuilder(n); + for(size_t i = 0; i < n; ++i) { + vpValue[i]->addToBsonArray(&arrayBuilder); + } + + pBuilder->append(&arrayBuilder); + break; + } + + case BinData: + // pBuilder->appendBinData(fieldName, ...); + verify(false); // CW TODO unimplemented + break; + + case jstOID: + pBuilder->append(getOid()); + break; + + case Bool: + pBuilder->append(getBool()); + break; + + case Date: + pBuilder->append(Date_t(getDate())); + break; + + case RegEx: + pBuilder->append(getRegex()); + break; + + case Symbol: + pBuilder->append(getSymbol()); + break; + + case CodeWScope: + verify(false); // CW TODO unimplemented + break; + + case NumberInt: + pBuilder->append(getInt()); + break; + + case Timestamp: + pBuilder->append(getTimestamp()); + break; + + case NumberLong: + pBuilder->append(getLong()); + break; + + case Undefined: + pBuilder->appendUndefined(); + break; + + case jstNULL: + pBuilder->append(); + break; + + /* these shouldn't appear in this context */ + case MinKey: + case EOO: + case DBRef: + case Code: + case MaxKey: + verify(false); // CW TODO better message + break; + } + } + + void Value::addToBsonObj(BSONObjBuilder *pBuilder, string fieldName) const { + BuilderObj objBuilder(pBuilder, fieldName); + addToBson(&objBuilder); + } + + void Value::addToBsonArray(BSONArrayBuilder *pBuilder) const { + BuilderArray arrBuilder(pBuilder); + addToBson(&arrBuilder); + } + + bool Value::coerceToBool() const { + // TODO Unify the implementation with BSONElement::trueValue(). + BSONType type = getType(); + switch(type) { + case NumberDouble: + if (doubleValue != 0) + return true; + break; + + case String: + case Object: + case Array: + case BinData: + case jstOID: + case Date: + case RegEx: + case Symbol: + case Timestamp: + return true; + + case Bool: + if (boolValue) + return true; + break; + + case CodeWScope: + verify(false); // CW TODO unimplemented + break; + + case NumberInt: + if (intValue != 0) + return true; + break; + + case NumberLong: + if (longValue != 0) + return true; + break; + + case jstNULL: + case Undefined: + /* nothing to do */ + break; + + /* these shouldn't happen in this context */ + case MinKey: + case EOO: + case DBRef: + case Code: + case MaxKey: + verify(false); // CW TODO better message + break; + } + + return false; + } + + int Value::coerceToInt() const { + switch(type) { + case NumberDouble: + return (int)doubleValue; + + case NumberInt: + return intValue; + + case NumberLong: + return (int)longValue; + + case jstNULL: + case Undefined: + break; + + case String: + default: + uassert(16003, str::stream() << + "can't convert from BSON type " << typeName(type) << + " to int", + false); + } // switch(type) + + return (int)0; + } + + long long Value::coerceToLong() const { + switch(type) { + case NumberDouble: + return (long long)doubleValue; + + case NumberInt: + return intValue; + + case NumberLong: + return longValue; + + case jstNULL: + case Undefined: + break; + + case String: + default: + uassert(16004, str::stream() << + "can't convert from BSON type " << typeName(type) << + " to long", + false); + } // switch(type) + + return (long long)0; + } + + double Value::coerceToDouble() const { + switch(type) { + case NumberDouble: + return doubleValue; + + case NumberInt: + return (double)intValue; + + case NumberLong: + return (double)longValue; + + case jstNULL: + case Undefined: + break; + + case String: + default: + uassert(16005, str::stream() << + "can't convert from BSON type " << typeName(type) << + " to double", + false); + } // switch(type) + + return (double)0; + } + + long long Value::coerceToDate() const { + switch(type) { + + case Date: + return dateValue; + + case Timestamp: + return getTimestamp().getSecs() * 1000LL; + + default: + uassert(16006, str::stream() << + "can't convert from BSON type " << typeName(type) << " to Date", + false); + } // switch(type) + } + + 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<time_t>::min() && + seconds <= std::numeric_limits<time_t>::max()); + + return static_cast<time_t>(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 { + stringstream ss; + switch(type) { + case NumberDouble: + ss << doubleValue; + return ss.str(); + + case NumberInt: + ss << intValue; + return ss.str(); + + case NumberLong: + ss << longValue; + return ss.str(); + + case String: + return stringValue; + + case Timestamp: + ss << getTimestamp().toStringPretty(); + return ss.str(); + + case Date: + return tmToISODateString(coerceToTm()); + + case jstNULL: + case Undefined: + break; + + default: + uassert(16007, str::stream() << + "can't convert from BSON type " << typeName(type) << + " to String", + false); + } // switch(type) + + return ""; + } + + OpTime Value::coerceToTimestamp() const { + switch(type) { + + case Timestamp: + return timestampValue; + + default: + uassert(16378, str::stream() << + "can't convert from BSON type " << typeName(type) << + " to timestamp", + false); + } // switch(type) + } + + int Value::compare(const intrusive_ptr<const Value> &rL, + const intrusive_ptr<const Value> &rR) { + BSONType lType = rL->getType(); + BSONType rType = rR->getType(); + + /* + Special handling for Undefined and NULL values; these are types, + so it's easier to handle them here before we go below to handle + values of the same types. This allows us to compare Undefined and + NULL values with everything else. As coded now: + (*) Undefined is less than everything except itself (which is equal) + (*) NULL is less than everything except Undefined and itself + */ + if (lType == Undefined) { + if (rType == Undefined) + return 0; + + /* if rType is anything else, the left value is less */ + return -1; + } + + if (lType == jstNULL) { + if (rType == Undefined) + return 1; + if (rType == jstNULL) + return 0; + + return -1; + } + + if ((rType == Undefined) || (rType == jstNULL)) { + /* + We know the left value isn't Undefined, because of the above. + Count a NULL value as greater than an undefined one. + */ + return 1; + } + + /* if the comparisons are numeric, prepare to promote the values */ + if (((lType == NumberDouble) || (lType == NumberLong) || + (lType == NumberInt)) && + ((rType == NumberDouble) || (rType == NumberLong) || + (rType == NumberInt))) { + + /* if the biggest type of either is a double, compare as doubles */ + if ((lType == NumberDouble) || (rType == NumberDouble)) { + const double left = rL->getDouble(); + const double right = rR->getDouble(); + if (left < right) + return -1; + if (left > right) + return 1; + return 0; + } + + /* if the biggest type of either is a long, compare as longs */ + if ((lType == NumberLong) || (rType == NumberLong)) { + const long long left = rL->getLong(); + const long long right = rR->getLong(); + if (left < right) + return -1; + if (left > right) + return 1; + return 0; + } + + /* if we got here, they must both be ints; compare as ints */ + { + const int left = rL->getInt(); + const int right = rR->getInt(); + if (left < right) + return -1; + if (left > right) + return 1; + return 0; + } + } + + // CW TODO for now, only compare like values + uassert(16016, str::stream() << + "can't compare values of BSON types " << typeName(lType) << + " and " << typeName(rType), + lType == rType); + + switch(lType) { + case NumberDouble: + case NumberInt: + case NumberLong: + /* these types were handled above */ + verify(false); + + case String: + return rL->stringValue.compare(rR->stringValue); + + case Object: + return Document::compare(rL->getDocument(), rR->getDocument()); + + case Array: { + intrusive_ptr<ValueIterator> pli(rL->getArray()); + intrusive_ptr<ValueIterator> pri(rR->getArray()); + + while(true) { + /* have we run out of left array? */ + if (!pli->more()) { + if (!pri->more()) + return 0; // the arrays are the same length + + return -1; // the left array is shorter + } + + /* have we run out of right array? */ + if (!pri->more()) + return 1; // the right array is shorter + + /* compare the two corresponding elements */ + intrusive_ptr<const Value> plv(pli->next()); + intrusive_ptr<const Value> prv(pri->next()); + const int cmp = Value::compare(plv, prv); + if (cmp) + return cmp; // values are unequal + } + + /* NOTREACHED */ + verify(false); + break; + } + + case BinData: + case Symbol: + case CodeWScope: + uassert(16017, str::stream() << + "comparisons of values of BSON type " << typeName(lType) << + " are not supported", false); + // pBuilder->appendBinData(fieldName, ...); + break; + + case jstOID: + if (rL->getOid() < rR->getOid()) + return -1; + if (rL->getOid() == rR->getOid()) + return 0; + return 1; + + case Bool: + if (rL->boolValue == rR->boolValue) + return 0; + if (rL->boolValue) + return 1; + return -1; + + case Date: { + long long l = rL->dateValue; + long long r = rR->dateValue; + if (l < r) + return -1; + if (l > r) + return 1; + return 0; + } + + case RegEx: + return rL->stringValue.compare(rR->stringValue); + + case Timestamp: + if (rL->timestampValue < rR->timestampValue) + return -1; + if (rL->timestampValue > rR->timestampValue) + return 1; + return 0; + + case Undefined: + case jstNULL: + return 0; // treat two Undefined or NULL values as equal + + /* these shouldn't happen in this context */ + case MinKey: + case EOO: + case DBRef: + case Code: + case MaxKey: + verify(false); + break; + } // switch(lType) + + /* NOTREACHED */ + return 0; + } + + void Value::hash_combine(size_t &seed) const { + BSONType type = getType(); + + switch(type) { + /* + Numbers whose values are equal need to hash to the same thing + as well. Note that Value::compare() promotes numeric values to + their largest common form in order for comparisons to work. + We must hash all numeric values as if they are doubles so that + things like grouping work. We don't know what values will come + down the pipe later, but if we start out with int representations + of a value, and later see double representations of it, they need + to end up in the same buckets. + */ + case NumberDouble: + case NumberLong: + case NumberInt: + { + const double d = getDouble(); + boost::hash_combine(seed, d); + break; + } + + case String: + boost::hash_combine(seed, stringValue); + break; + + case Object: + getDocument()->hash_combine(seed); + break; + + case Array: { + intrusive_ptr<ValueIterator> pIter(getArray()); + while(pIter->more()) { + intrusive_ptr<const Value> pValue(pIter->next()); + pValue->hash_combine(seed); + }; + break; + } + + case BinData: + case Symbol: + case CodeWScope: + uassert(16018, str::stream() << + "hashes of values of BSON type " << typeName(type) << + " are not supported", false); + break; + + case jstOID: + getOid().hash_combine(seed); + break; + + case Bool: + boost::hash_combine(seed, boolValue); + break; + + case Date: + boost::hash_combine(seed, dateValue); + break; + + case RegEx: + boost::hash_combine(seed, stringValue); + break; + + case Timestamp: + boost::hash_combine(seed, timestampValue); + break; + + case Undefined: + case jstNULL: + break; + + /* these shouldn't happen in this context */ + case MinKey: + case EOO: + case DBRef: + case Code: + case MaxKey: + verify(false); // CW TODO better message + break; + } // switch(type) + } + + BSONType Value::getWidestNumeric(BSONType lType, BSONType rType) { + if (lType == NumberDouble) { + switch(rType) { + case NumberDouble: + case NumberLong: + case NumberInt: + case jstNULL: + case Undefined: + return NumberDouble; + + default: + break; + } + } + else if (lType == NumberLong) { + switch(rType) { + case NumberDouble: + return NumberDouble; + + case NumberLong: + case NumberInt: + case jstNULL: + case Undefined: + return NumberLong; + + default: + break; + } + } + else if (lType == NumberInt) { + switch(rType) { + case NumberDouble: + return NumberDouble; + + case NumberLong: + return NumberLong; + + case NumberInt: + case jstNULL: + case Undefined: + return NumberInt; + + default: + break; + } + } + else if ((lType == jstNULL) || (lType == Undefined)) { + 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; + } + + size_t Value::getApproximateSize() const { + switch(type) { + case String: + return sizeof(Value) + stringValue.length(); + + case Object: + return sizeof(Value) + pDocumentValue->getApproximateSize(); + + case Array: { + size_t size = sizeof(Value); + const size_t n = vpValue.size(); + for(size_t i = 0; i < n; ++i) { + size += vpValue[i]->getApproximateSize(); + } + return size; + } + + case NumberDouble: + case BinData: + case jstOID: + case Bool: + case Date: + case RegEx: + case Symbol: + case CodeWScope: + case NumberInt: + case Timestamp: + case NumberLong: + case jstNULL: + case Undefined: + return sizeof(Value); + + /* these shouldn't happen in this context */ + case MinKey: + case EOO: + case DBRef: + case Code: + case MaxKey: + verify(false); // CW TODO better message + return sizeof(Value); + } + + /* + We shouldn't get here. In order to make the implementor think about + these cases, they are all listed explicitly, above. The compiler + should complain if they aren't all listed, because there's no + default. However, not all the compilers seem to do that. Therefore, + this final catch-all is here. + */ + verify(false); + return sizeof(Value); + } + + + void ValueStatic::addRef() const { + } + + void ValueStatic::release() const { + } + +} |
