0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00
mongodb/bson/bsonelement.h
2010-07-21 15:39:59 -04:00

548 lines
17 KiB
C++

// BSONElement
/* 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 <vector>
#include <string.h>
namespace bson {
typedef mongo::BSONElement be;
typedef mongo::BSONObj bo;
typedef mongo::BSONObjBuilder bob;
}
namespace mongo {
class OpTime;
class BSONElement;
/* 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:
string foo = obj["foo"].String(); // exception if not a string type or DNE
*/
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(); }
BSONObj Obj() const;
vector<BSONElement> Array() const; // see implementation for detailed comments
mongo::OID OID() const { return chk(jstOID).__oid(); }
void Null() const { chk(isNull()); }
void OK() const { chk(ok()); }
/** 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(string& v) const { v = String(); }
/** Use ok() to check if a value is assigned:
if( myObj["foo"].ok() ) ...
*/
bool ok() const { return !eoo(); }
string toString( bool includeFieldName = true, bool full=false) const;
operator string() const { return toString(); }
string jsonString( JsonStringFormat format, bool includeFieldNames = true, int pretty = 0 ) const;
/** Returns the type of the element */
BSONType type() const { return (BSONType) *data; }
/** retrieve a field within this element
throws exception if *this is not an embedded object
*/
BSONElement operator[] (const 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 = -1 ) 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;
}
/** 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;
}
/** Retrieve a java style date value from the element.
Ensure element is of type Date before calling.
*/
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., treat zero and null 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 double* >( value() ); }
/** Return double value for this field. MUST be NumberInt type. */
int _numberInt() const {return *reinterpret_cast< const int* >( value() ); }
/** Return double 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. */
long long numberLong() 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. */
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 "". */
string str() const {
return type() == mongo::String ? string(valuestr(), valuestrsize()-1) : string();
}
/** Get javascript code of a CodeWScope data element. */
const char * codeWScopeCode() const {
return value() + 8;
}
/** Get the scope SavedContext of a CodeWScope data element. */
const char * codeWScopeScopeData() const {
// TODO fix
return codeWScopeCode() + strlen( codeWScopeCode() ) + 1;
}
/** 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>
assert( 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>
assert( type() == BinData );
unsigned char c = (value() + 4)[0];
return (BinDataType)c;
}
/** Retrieve the regex string for a Regex element */
const char *regex() const {
assert(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;
}
/** 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 );
}
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;
}
// If maxLen is specified, don't scan more than maxLen bytes.
explicit BSONElement(const char *d, int maxLen = -1) : data(d) {
fieldNameSize_ = -1;
if ( eoo() )
fieldNameSize_ = 0;
else {
if ( maxLen != -1 ) {
int size = (int) strnlen( fieldName(), maxLen - 1 );
massert( 10333 , "Invalid field name", size != -1 );
fieldNameSize_ = size + 1;
}
}
totalSize = -1;
}
string _asCode() const;
OpTime _opTime() const;
private:
const char *data;
mutable int fieldNameSize_; // cached value
int fieldNameSize() const {
if ( fieldNameSize_ == -1 )
fieldNameSize_ = (int)strlen( fieldName() ) + 1;
return fieldNameSize_;
}
mutable int totalSize; /* caches the computed size */
friend class BSONObjIterator;
friend class BSONObj;
const BSONElement& chk(int t) const {
if ( t != type() ){
stringstream ss;
ss << "wrong type for BSONElement (" << fieldName() << ") " << type() << " != " << t;
uasserted(13111, ss.str() );
}
return *this;
}
const BSONElement& chk(bool expr) const {
uassert(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:
assert(0);
return -1;
}
}
inline bool BSONElement::trueValue() const {
switch( type() ) {
case NumberLong:
return *reinterpret_cast< const long long* >( value() ) != 0;
case NumberDouble:
return *reinterpret_cast< const double* >( value() ) != 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;
}
/** 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. */
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;
}
}
inline BSONElement::BSONElement() {
static char z = 0;
data = &z;
fieldNameSize_ = 0;
totalSize = 1;
}
}