diff options
Diffstat (limited to 'src/mongo/bson/bsonelement.h')
| -rw-r--r-- | src/mongo/bson/bsonelement.h | 648 |
1 files changed, 648 insertions, 0 deletions
diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h new file mode 100644 index 00000000000..8a72dfe66b7 --- /dev/null +++ b/src/mongo/bson/bsonelement.h @@ -0,0 +1,648 @@ +// bsonelement.h + +/* Copyright 2009 10gen Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string.h> // strlen +#include <string> +#include <vector> + +#include "mongo/bson/bsontypes.h" +#include "mongo/bson/oid.h" +#include "mongo/platform/float_utils.h" + +namespace mongo { + class OpTime; + class BSONObj; + class BSONElement; + class BSONObjBuilder; +} + +namespace bson { + typedef mongo::BSONElement be; + typedef mongo::BSONObj bo; + typedef mongo::BSONObjBuilder bob; +} + +namespace mongo { + + /* l and r MUST have same type when called: check that first. */ + int compareElementValues(const BSONElement& l, const BSONElement& r); + + + /** 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: + <type><fieldName ><value> + -------- size() ------------ + -fieldNameSize- + value() + type() + */ + class BSONElement { + public: + /** These functions, which start with a capital letter, throw a UserException 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).valuestr(); } + Date_t Date() const { return chk(mongo::Date).date(); } + double Number() const { return chk(isNumber()).number(); } + 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<BSONElement> Array() const; // see implementation for detailed comments + mongo::OID OID() const { return chk(jstOID).__oid(); } + void Null() const { chk(isNull()); } // throw UserException if not null + void OK() const { chk(ok()); } // throw UserException if element DNE + + /** @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 UserException 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(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(); } + + std::string toString( bool includeFieldName = true, bool full=false) const; + void toString(StringBuilder& s, bool includeFieldName = true, bool full=false, int depth=0) const; + std::string jsonString( JsonStringFormat format, bool includeFieldNames = true, int pretty = 0 ) const; + operator std::string() const { return toString(); } + + /** Returns the type of the element */ + BSONType type() const { return (BSONType) *reinterpret_cast< const signed char * >(data); } + + /** retrieve a field within this element + throws exception if *this is not an embedded object + */ + BSONElement operator[] (const std::string& field) const; + + /** returns the tyoe of the element fixed for the main type + the main purpose is numbers. any numeric type will return NumberDouble + Note: if the order changes, indexes have to be re-built or than can be corruption + */ + int canonicalType() const; + + /** 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. + @param maxLen If maxLen is specified, don't scan more than maxLen bytes to calculate size. + */ + int size( int maxLen ) const; + int size() const; + + /** 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( const char* 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; + } + + + int fieldNameSize() const { + if ( fieldNameSize_ == -1 ) + fieldNameSize_ = (int)strlen( fieldName() ) + 1; + return fieldNameSize_; + } + + /** 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 *reinterpret_cast< const Date_t* >( value() ); + } + + /** 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 number, string, bool, date, OID */ + bool isSimpleType() 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 (reinterpret_cast< const PackedDouble* >( value() ))->d; } + /** Return int value for this field. MUST be NumberInt type. */ + int _numberInt() const {return *reinterpret_cast< const int* >( value() ); } + /** Return long long value for this field. MUST be NumberLong type. */ + long long _numberLong() const {return *reinterpret_cast< const long long* >( value() ); } + + /** 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; + + /** 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. */ + const mongo::OID &__oid() const { return *reinterpret_cast< const mongo::OID* >( value() ); } + + /** True if element is null. */ + bool isNull() const { + return type() == jstNULL; + } + + /** Size (length) of a string element. + You must assure of type String first. + @return string size including terminating null + */ + int valuestrsize() const { + return *reinterpret_cast< const int* >( value() ); + } + + // for objects the size *includes* the size of the size field + int objsize() const { + return *reinterpret_cast< const int* >( value() ); + } + + /** 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; + } + + /** Get the string value of the element. If not a string returns "". */ + const char *valuestrsafe() const { + return type() == mongo::String ? valuestr() : ""; + } + /** Get the string value of the element. If not a string returns "". */ + std::string str() const { + return type() == mongo::String ? std::string(valuestr(), valuestrsize()-1) : std::string(); + } + + /** 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 *(int *)( value() + 4 ); + } + + /** 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: <int len> <byte subtype> <byte[len] data> + 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: <int len> <byte subtype> <byte[len] data> + if (binDataType() != ByteArrayDeprecated) { + return binData(len); + } + else { + // Skip extra size + len = valuestrsize() - 4; + return value() + 5 + 4; + } + } + + BinDataType binDataType() const { + // BinData: <int len> <byte subtype> <byte[len] data> + verify( type() == BinData ); + unsigned char c = (value() + 4)[0]; + return (BinDataType)c; + } + + /** Retrieve the regex 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; + } + + /** like operator== but doesn't check the fieldname, + just the value. + */ + bool valuesEqual(const BSONElement& r) const { + return woCompare( r , false ) == 0; + } + + /** Returns true if elements are equal. */ + bool operator==(const BSONElement& r) const { + return woCompare( r , true ) == 0; + } + /** Returns true if elements are unequal. */ + bool operator!=(const BSONElement& r) const { return !operator==(r); } + + /** Well ordered comparison. + @return <0: l<r. 0:l==r. >0:l>r + order by type, field name, and field value. + If considerFieldName is true, pay attention to the field name. + */ + int woCompare( const BSONElement &e, bool considerFieldName = true ) const; + + const char * rawdata() const { return data; } + + /** 0 == Equality, just not defined yet */ + int getGtLtOp( int def = 0 ) const; + + /** Constructs an empty element */ + BSONElement(); + + /** Check that data is internally consistent. */ + void validate() const; + + /** 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; + } + } + + Date_t timestampTime() const { + unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; + return t * 1000; + } + unsigned int timestampInc() const { + return ((unsigned int*)(value() ))[0]; + } + + 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 + *reinterpret_cast< const int* >( start ); + return *reinterpret_cast< const mongo::OID* >( start ); + } + + /** this does not use fieldName in the comparison, just the value */ + bool operator<( const BSONElement& other ) const { + int x = (int)canonicalType() - (int)other.canonicalType(); + if ( x < 0 ) return true; + else if ( x > 0 ) return false; + return compareElementValues(*this,other) < 0; + } + + // @param maxLen don't scan more than maxLen bytes + explicit BSONElement(const char *d, int maxLen) : data(d) { + if ( eoo() ) { + totalSize = 1; + fieldNameSize_ = 0; + } + else { + totalSize = -1; + fieldNameSize_ = -1; + if ( maxLen != -1 ) { + int size = (int) strnlen( fieldName(), maxLen - 1 ); + uassert( 10333 , "Invalid field name", size != -1 ); + fieldNameSize_ = size + 1; + } + } + } + + explicit BSONElement(const char *d) : data(d) { + fieldNameSize_ = -1; + totalSize = -1; + if ( eoo() ) { + fieldNameSize_ = 0; + totalSize = 1; + } + } + + std::string _asCode() const; + OpTime _opTime() const; + + private: + const char *data; + mutable int fieldNameSize_; // cached value + + mutable int totalSize; /* caches the computed size */ + + friend class BSONObjIterator; + friend class BSONObj; + const BSONElement& chk(int 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; + msgasserted(13111, ss.str() ); + } + return *this; + } + const BSONElement& chk(bool expr) const { + massert(13118, "unexpected or missing type value in BSON object", expr); + return *this; + } + }; + + inline int BSONElement::canonicalType() const { + BSONType t = type(); + switch ( t ) { + case MinKey: + case MaxKey: + return t; + case EOO: + case Undefined: + return 0; + case jstNULL: + return 5; + case NumberDouble: + case NumberInt: + case NumberLong: + return 10; + case mongo::String: + case Symbol: + return 15; + case Object: + return 20; + case mongo::Array: + return 25; + case BinData: + return 30; + case jstOID: + return 35; + case mongo::Bool: + return 40; + case mongo::Date: + case Timestamp: + return 45; + case RegEx: + return 50; + case DBRef: + return 55; + case Code: + return 60; + case CodeWScope: + return 65; + default: + verify(0); + return -1; + } + } + + inline bool BSONElement::trueValue() const { + // NOTE Behavior changes must be replicated in Value::coerceToBool(). + switch( type() ) { + case NumberLong: + return *reinterpret_cast< const long long* >( value() ) != 0; + case NumberDouble: + return (reinterpret_cast < const PackedDouble* >(value ()))->d != 0; + case NumberInt: + return *reinterpret_cast< const int* >( value() ) != 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 NumberInt: + return true; + default: + return false; + } + } + + inline bool BSONElement::isSimpleType() const { + switch( type() ) { + case NumberLong: + case NumberDouble: + case NumberInt: + case mongo::String: + case mongo::Bool: + case mongo::Date: + case jstOID: + return true; + default: + return false; + } + } + + inline double BSONElement::numberDouble() const { + switch( type() ) { + case NumberDouble: + return _numberDouble(); + case NumberInt: + return *reinterpret_cast< const int* >( value() ); + case NumberLong: + return (double) *reinterpret_cast< const long long* >( value() ); + 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(); + 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(); + default: + return 0; + } + } + + /** 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 */ + inline long long BSONElement::safeNumberLong() const { + double d; + switch( type() ) { + case NumberDouble: + d = numberDouble(); + if ( isNaN( d ) ){ + return 0; + } + if ( d > (double) std::numeric_limits<long long>::max() ){ + return std::numeric_limits<long long>::max(); + } + if ( d < std::numeric_limits<long long>::min() ){ + return std::numeric_limits<long long>::min(); + } + default: + return numberLong(); + } + } + + inline BSONElement::BSONElement() { + static char z = 0; + data = &z; + fieldNameSize_ = 0; + totalSize = 1; + } + +} |
