diff options
Diffstat (limited to 'bson')
-rw-r--r-- | bson/bson-inl.h (renamed from bson/bsoninlines.h) | 187 | ||||
-rw-r--r-- | bson/bson.h | 30 | ||||
-rw-r--r-- | bson/bson_db.h | 28 | ||||
-rw-r--r-- | bson/bsondemo/bsondemo.cpp | 26 | ||||
-rw-r--r-- | bson/bsonelement.h | 662 | ||||
-rw-r--r-- | bson/bsonmisc.h | 78 | ||||
-rw-r--r-- | bson/bsonobj.h | 212 | ||||
-rw-r--r-- | bson/bsonobjbuilder.h | 265 | ||||
-rw-r--r-- | bson/bsonobjiterator.h | 70 | ||||
-rw-r--r-- | bson/bsontypes.h | 112 | ||||
-rw-r--r-- | bson/inline_decls.h | 2 | ||||
-rw-r--r-- | bson/oid.cpp | 154 | ||||
-rw-r--r-- | bson/oid.h | 95 | ||||
-rw-r--r-- | bson/ordering.h | 10 | ||||
-rw-r--r-- | bson/stringdata.h | 37 | ||||
-rw-r--r-- | bson/util/atomic_int.h | 40 | ||||
-rw-r--r-- | bson/util/builder.h | 149 | ||||
-rw-r--r-- | bson/util/misc.h | 4 |
18 files changed, 1268 insertions, 893 deletions
diff --git a/bson/bsoninlines.h b/bson/bson-inl.h index 0a2e59b..5b4c490 100644 --- a/bson/bsoninlines.h +++ b/bson/bson-inl.h @@ -24,13 +24,17 @@ namespace mongo { - inline BSONObjIterator BSONObj::begin() { + inline BSONObjIterator BSONObj::begin() { return BSONObjIterator(*this); } inline BSONObj BSONElement::embeddedObjectUserCheck() const { - uassert( 10065 , "invalid parameter: expected an object", isABSONObj() ); - return BSONObj(value()); + if ( isABSONObj() ) + return BSONObj(value()); + stringstream ss; + ss << "invalid parameter: expected an object (" << fieldName() << ")"; + uasserted( 10065 , ss.str() ); + return BSONObj(); // never reachable } inline BSONObj BSONElement::embeddedObject() const { @@ -43,13 +47,34 @@ namespace mongo { int strSizeWNull = *(int *)( value() + 4 ); return BSONObj( value() + 4 + 4 + strSizeWNull ); } - - inline BSONObj BSONObj::copy() const { + + inline NOINLINE_DECL void BSONObj::_assertInvalid() const { + StringBuilder ss; + int os = objsize(); + ss << "Invalid BSONObj size: " << os << " (0x" << toHex( &os, 4 ) << ')'; + try { + BSONElement e = firstElement(); + ss << " first element: " << e.toString(); + } + catch ( ... ) { } + massert( 10334 , ss.str() , 0 ); + } + + /* the idea with NOINLINE_DECL here is to keep this from inlining in the + getOwned() method. the presumption being that is better. + */ + inline NOINLINE_DECL BSONObj BSONObj::copy() const { char *p = (char*) malloc(objsize()); memcpy(p, objdata(), objsize()); return BSONObj(p, true); } + inline BSONObj BSONObj::getOwned() const { + if ( isOwned() ) + return *this; + return copy(); + } + // wrap this element up as a singleton object. inline BSONObj BSONElement::wrap() const { BSONObjBuilder b(size()+6); @@ -63,7 +88,6 @@ namespace mongo { return b.obj(); } - inline bool BSONObj::hasElement(const char *name) const { if ( !isEmpty() ) { BSONObjIterator it(*this); @@ -97,14 +121,34 @@ namespace mongo { return *this; } - inline bool BSONObj::isValid(){ + /* add all the fields from the object specified to this object if they don't exist */ + inline BSONObjBuilder& BSONObjBuilder::appendElementsUnique(BSONObj x) { + set<string> have; + { + BSONObjIterator i = iterator(); + while ( i.more() ) + have.insert( i.next().fieldName() ); + } + + BSONObjIterator it(x); + while ( it.more() ) { + BSONElement e = it.next(); + if ( have.count( e.fieldName() ) ) + continue; + append(e); + } + return *this; + } + + + inline bool BSONObj::isValid() { int x = objsize(); - return x > 0 && x <= 1024 * 1024 * 8; + return x > 0 && x <= BSONObjMaxInternalSize; } - inline bool BSONObj::getObjectID(BSONElement& e) const { + inline bool BSONObj::getObjectID(BSONElement& e) const { BSONElement f = getField("_id"); - if( !f.eoo() ) { + if( !f.eoo() ) { e = f; return true; } @@ -115,21 +159,21 @@ namespace mongo { _fieldName = 0; _builder = builder; } - - template<class T> - inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<( T value ) { + + template<class T> + inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<( T value ) { _builder->append(_fieldName, value); _fieldName = 0; return *_builder; } - inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const BSONElement& e ) { + inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const BSONElement& e ) { _builder->appendAs( e , _fieldName ); _fieldName = 0; return *_builder; } - inline Labeler BSONObjBuilderValueStream::operator<<( const Labeler::Label &l ) { + inline Labeler BSONObjBuilderValueStream::operator<<( const Labeler::Label &l ) { return Labeler( l, this ); } @@ -139,29 +183,29 @@ namespace mongo { } _subobj.reset(); _fieldName = nextFieldName; - } + } inline BSONObjBuilder *BSONObjBuilderValueStream::subobj() { if ( !haveSubobj() ) _subobj.reset( new BSONObjBuilder() ); return _subobj.get(); } - + template<class T> inline BSONObjBuilder& Labeler::operator<<( T value ) { s_->subobj()->append( l_.l_, value ); return *s_->_builder; - } + } inline BSONObjBuilder& Labeler::operator<<( const BSONElement& e ) { s_->subobj()->appendAs( e, l_.l_ ); return *s_->_builder; - } + } // {a: {b:1}} -> {a.b:1} void nested2dotted(BSONObjBuilder& b, const BSONObj& obj, const string& base=""); - inline BSONObj nested2dotted(const BSONObj& obj){ + inline BSONObj nested2dotted(const BSONObj& obj) { BSONObjBuilder b; nested2dotted(b, obj); return b.obj(); @@ -169,7 +213,7 @@ namespace mongo { // {a.b:1} -> {a: {b:1}} void dotted2nested(BSONObjBuilder& b, const BSONObj& obj); - inline BSONObj dotted2nested(const BSONObj& obj){ + inline BSONObj dotted2nested(const BSONObj& obj) { BSONObjBuilder b; dotted2nested(b, obj); return b.obj(); @@ -180,17 +224,25 @@ namespace mongo { const char * e = _b.buf() + _b.len(); return BSONObjIterator( s , e ); } - + + inline bool BSONObjBuilder::hasField( const StringData& name ) const { + BSONObjIterator i = iterator(); + while ( i.more() ) + if ( strcmp( name.data() , i.next().fieldName() ) == 0 ) + return true; + return false; + } + /* WARNING: nested/dotted conversions are not 100% reversible * nested2dotted(dotted2nested({a.b: {c:1}})) -> {a.b.c: 1} * also, dotted2nested ignores order */ typedef map<string, BSONElement> BSONMap; - inline BSONMap bson2map(const BSONObj& obj){ + inline BSONMap bson2map(const BSONObj& obj) { BSONMap m; BSONObjIterator it(obj); - while (it.more()){ + while (it.more()) { BSONElement e = it.next(); m[e.fieldName()] = e; } @@ -204,7 +256,7 @@ namespace mongo { }; typedef set<BSONElement, BSONElementFieldNameCmp> BSONSortedElements; - inline BSONSortedElements bson2set( const BSONObj& obj ){ + inline BSONSortedElements bson2set( const BSONObj& obj ) { BSONSortedElements s; BSONObjIterator it(obj); while ( it.more() ) @@ -219,7 +271,7 @@ namespace mongo { return s.str(); } inline void BSONObj::toString(StringBuilder& s, bool isArray, bool full ) const { - if ( isEmpty() ){ + if ( isEmpty() ) { s << "{}"; return; } @@ -234,7 +286,7 @@ namespace mongo { massert( 10329 , "Element too large", e.size() < ( 1 << 30 ) ); int offset = (int) (e.rawdata() - this->objdata()); massert( 10330 , "Element extends past end of object", - e.size() + offset <= this->objsize() ); + e.size() + offset <= this->objsize() ); e.validate(); bool end = ( e.size() + offset == this->objsize() ); if ( e.eoo() ) { @@ -254,17 +306,20 @@ namespace mongo { inline void BSONElement::validate() const { const BSONType t = type(); - + switch( t ) { case DBRef: case Code: case Symbol: case mongo::String: { - int x = valuestrsize(); - if ( x > 0 && valuestr()[x-1] == 0 ) + unsigned x = (unsigned) valuestrsize(); + bool lenOk = x > 0 && x < (unsigned) BSONObjMaxInternalSize; + if( lenOk && valuestr()[x-1] == 0 ) return; StringBuilder buf; - buf << "Invalid dbref/code/string/symbol size: " << x << " strnlen:" << mongo::strnlen( valuestr() , x ); + buf << "Invalid dbref/code/string/symbol size: " << x; + if( lenOk ) + buf << " strnlen:" << mongo::strnlen( valuestr() , x ); msgasserted( 10321 , buf.str() ); break; } @@ -341,13 +396,19 @@ namespace mongo { massert( 10317 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); x = valuestrsize() + 4 + 1/*subtype*/; break; - case RegEx: - { + case RegEx: { const char *p = value(); size_t len1 = ( maxLen == -1 ) ? strlen( p ) : mongo::strnlen( p, remain ); //massert( 10318 , "Invalid regex string", len1 != -1 ); // ERH - 4/28/10 - don't think this does anything p = p + len1 + 1; - size_t len2 = ( maxLen == -1 ) ? strlen( p ) : mongo::strnlen( p, remain - len1 - 1 ); + size_t len2; + if( maxLen == -1 ) + len2 = strlen( p ); + else { + size_t x = remain - len1 - 1; + assert( x <= 0x7fffffff ); + len2 = mongo::strnlen( p, (int) x ); + } //massert( 10319 , "Invalid regex options string", len2 != -1 ); // ERH - 4/28/10 - don't think this does anything x = (int) (len1 + 1 + len2 + 1); } @@ -379,13 +440,12 @@ namespace mongo { case mongo::Date: s << "new Date(" << date() << ')'; break; - case RegEx: - { - s << "/" << regex() << '/'; - const char *p = regexFlags(); - if ( p ) s << p; - } - break; + case RegEx: { + s << "/" << regex() << '/'; + const char *p = regexFlags(); + if ( p ) s << p; + } + break; case NumberDouble: s.appendDoubleNice( number() ); break; @@ -418,13 +478,14 @@ namespace mongo { break; case CodeWScope: s << "CodeWScope( " - << codeWScopeCode() << ", " << codeWScopeObject().toString(false, full) << ")"; + << codeWScopeCode() << ", " << codeWScopeObject().toString(false, full) << ")"; break; case Code: if ( !full && valuestrsize() > 80 ) { s.write(valuestr(), 70); s << "..."; - } else { + } + else { s.write(valuestr(), valuestrsize()-1); } break; @@ -434,7 +495,8 @@ namespace mongo { if ( !full && valuestrsize() > 80 ) { s.write(valuestr(), 70); s << "...\""; - } else { + } + else { s.write(valuestr(), valuestrsize()-1); s << '"'; } @@ -452,7 +514,7 @@ namespace mongo { break; case BinData: s << "BinData"; - if (full){ + if (full) { int len; const char* data = binDataClean(len); s << '(' << binDataType() << ", " << toHex(data, len) << ')'; @@ -503,14 +565,17 @@ namespace mongo { } inline BSONObj::BSONObj() { - /* LITTLE ENDIAN */ - static char p[] = { 5, 0, 0, 0, 0 }; + /* little endian ordering here, but perhaps that is ok regardless as BSON is spec'd + to be little endian external to the system. (i.e. the rest of the implementation of bson, + not this part, fails to support big endian) + */ + static char p[] = { /*size*/5, 0, 0, 0, /*eoo*/0 }; _objdata = p; } inline BSONObj BSONElement::Obj() const { return embeddedObjectUserCheck(); } - inline BSONElement BSONElement::operator[] (const string& field) const { + inline BSONElement BSONElement::operator[] (const string& field) const { BSONObj o = Obj(); return o[field]; } @@ -521,14 +586,14 @@ namespace mongo { v.push_back(i.next()); } - inline void BSONObj::elems(list<BSONElement> &v) const { + inline void BSONObj::elems(list<BSONElement> &v) const { BSONObjIterator i(*this); while( i.more() ) v.push_back(i.next()); } template <class T> - void BSONObj::Vals(vector<T>& v) const { + void BSONObj::Vals(vector<T>& v) const { BSONObjIterator i(*this); while( i.more() ) { T t; @@ -537,7 +602,7 @@ namespace mongo { } } template <class T> - void BSONObj::Vals(list<T>& v) const { + void BSONObj::Vals(list<T>& v) const { BSONObjIterator i(*this); while( i.more() ) { T t; @@ -547,25 +612,27 @@ namespace mongo { } template <class T> - void BSONObj::vals(vector<T>& v) const { + void BSONObj::vals(vector<T>& v) const { BSONObjIterator i(*this); while( i.more() ) { try { T t; i.next().Val(t); v.push_back(t); - } catch(...) { } + } + catch(...) { } } } template <class T> - void BSONObj::vals(list<T>& v) const { + void BSONObj::vals(list<T>& v) const { BSONObjIterator i(*this); while( i.more() ) { try { T t; i.next().Val(t); v.push_back(t); - } catch(...) { } + } + catch(...) { } } } @@ -577,6 +644,16 @@ namespace mongo { return s << e.toString(); } + inline StringBuilder& operator<<( StringBuilder &s, const BSONObj &o ) { + o.toString( s ); + return s; + } + inline StringBuilder& operator<<( StringBuilder &s, const BSONElement &e ) { + e.toString( s ); + return s; + } + + inline void BSONElement::Val(BSONObj& v) const { v = Obj(); } template<typename T> diff --git a/bson/bson.h b/bson/bson.h index 3d92831..ba1b751 100644 --- a/bson/bson.h +++ b/bson/bson.h @@ -1,10 +1,10 @@ -/* NOTE: Standalone bson header for when not using MongoDB. +/* NOTE: Standalone bson header for when not using MongoDB. See also: bsondemo. MongoDB includes ../db/jsobj.h instead. This file, however, pulls in much less code / dependencies. */ -/** @file bson.h +/** @file bson.h BSON classes */ @@ -25,7 +25,7 @@ */ /** - BSONObj and its helpers + bo and its helpers "BSON" stands for "binary JSON" -- ie a binary way to represent objects that would be represented in JSON (plus a few extensions useful for databases & other languages). @@ -47,15 +47,15 @@ #include <boost/utility.hpp> #include "util/builder.h" -namespace bson { +namespace bson { using std::string; using std::stringstream; - class assertion : public std::exception { + class assertion : public std::exception { public: assertion( unsigned u , const string& s ) - : id( u ) , msg( s ){ + : id( u ) , msg( s ) { mongo::StringBuilder ss; ss << "BsonAssertion id: " << u << " " << s; full = ss.str(); @@ -64,7 +64,7 @@ namespace bson { virtual ~assertion() throw() {} virtual const char* what() const throw() { return full.c_str(); } - + unsigned id; string msg; string full; @@ -72,9 +72,9 @@ namespace bson { } namespace mongo { -#if !defined(assert) +#if !defined(assert) inline void assert(bool expr) { - if(!expr) { + if(!expr) { throw bson::assertion( 0 , "assertion failure in bson library" ); } } @@ -88,12 +88,12 @@ namespace mongo { if( !expr ) uasserted( msgid , msg ); } - inline void msgasserted(int msgid, const char *msg) { + inline void msgasserted(int msgid, const char *msg) { throw bson::assertion( msgid , msg ); } inline void msgasserted(int msgid, const std::string &msg) { msgasserted(msgid, msg.c_str()); } - inline void massert(unsigned msgid, std::string msg, bool expr) { - if(!expr) { + inline void massert(unsigned msgid, std::string msg, bool expr) { + if(!expr) { std::cout << "assertion failure in bson library: " << msgid << ' ' << msg << std::endl; throw bson::assertion( msgid , msg ); } @@ -108,15 +108,15 @@ namespace mongo { #include "../bson/bsonmisc.h" #include "../bson/bsonobjbuilder.h" #include "../bson/bsonobjiterator.h" -#include "../bson/bsoninlines.h" +#include "../bson/bson-inl.h" -namespace mongo { +namespace mongo { inline unsigned getRandomNumber() { #if defined(_WIN32) return rand(); #else - return random(); + return random(); #endif } diff --git a/bson/bson_db.h b/bson/bson_db.h index 18cd59f..71f92aa 100644 --- a/bson/bson_db.h +++ b/bson/bson_db.h @@ -1,10 +1,10 @@ -/** @file bson_db.h +/** @file bson_db.h - This file contains the implementation of BSON-related methods that are required + This file contains the implementation of BSON-related methods that are required by the MongoDB database server. - Normally, for standalone BSON usage, you do not want this file - it will tend to - pull in some other files from the MongoDB project. Thus, bson.h (the main file + Normally, for standalone BSON usage, you do not want this file - it will tend to + pull in some other files from the MongoDB project. Thus, bson.h (the main file one would use) does not include this file. */ @@ -26,6 +26,7 @@ #pragma once #include "../util/optime.h" +#include "../util/time_support.h" namespace mongo { @@ -34,10 +35,10 @@ namespace mongo { Append a timestamp element to the object being ebuilt. @param time - in millis (but stored in seconds) */ - inline BSONObjBuilder& BSONObjBuilder::appendTimestamp( const StringData& fieldName , unsigned long long time , unsigned int inc ){ + inline BSONObjBuilder& BSONObjBuilder::appendTimestamp( const StringData& fieldName , unsigned long long time , unsigned int inc ) { OpTime t( (unsigned) (time / 1000) , inc ); appendTimestamp( fieldName , t.asDate() ); - return *this; + return *this; } inline OpTime BSONElement::_opTime() const { @@ -47,7 +48,7 @@ namespace mongo { } inline string BSONElement::_asCode() const { - switch( type() ){ + switch( type() ) { case mongo::String: case Code: return string(valuestr(), valuestrsize()-1); @@ -60,11 +61,22 @@ namespace mongo { return ""; } - inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(DateNowLabeler& id){ + inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(DateNowLabeler& id) { _builder->appendDate(_fieldName, jsTime()); _fieldName = 0; return *_builder; } + inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(MinKeyLabeler& id) { + _builder->appendMinKey(_fieldName); + _fieldName = 0; + return *_builder; + } + + inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(MaxKeyLabeler& id) { + _builder->appendMaxKey(_fieldName); + _fieldName = 0; + return *_builder; + } } diff --git a/bson/bsondemo/bsondemo.cpp b/bson/bsondemo/bsondemo.cpp index b0da1b8..ec83f5e 100644 --- a/bson/bsondemo/bsondemo.cpp +++ b/bson/bsondemo/bsondemo.cpp @@ -1,4 +1,4 @@ -/** @file bsondemo.cpp +/** @file bsondemo.cpp Example of use of BSON from C++. @@ -29,17 +29,16 @@ using namespace std; using namespace bson; -void iter(bo o) { +void iter(bo o) { /* iterator example */ cout << "\niter()\n"; - for( bo::iterator i(o); i.more(); ) { + for( bo::iterator i(o); i.more(); ) { cout << ' ' << i.next().toString() << '\n'; } } -int main() -{ - cout << "build bits: " << 8 * sizeof(char *) << '\n' << endl; +int main() { + cout << "build bits: " << 8 * sizeof(char *) << '\n' << endl; /* a bson object defaults on construction to { } */ bo empty; @@ -47,7 +46,7 @@ int main() /* make a simple { name : 'joe', age : 33.7 } object */ { - bob b; + bob b; b.append("name", "joe"); b.append("age", 33.7); b.obj(); @@ -73,7 +72,7 @@ int main() /* reach in and get subobj.z */ cout << "subobj.z: " << y.getFieldDotted("subobj.z").Number() << endl; - + /* alternate syntax: */ cout << "subobj.z: " << y["subobj"]["z"].Number() << endl; @@ -83,19 +82,19 @@ int main() cout << v[0] << endl; /* into an array */ - list<be> L; + list<be> L; y.elems(L); bo sub = y["subobj"].Obj(); - /* grab all the int's that were in subobj. if it had elements that were not ints, we throw an exception - (capital V on Vals() means exception if wrong type found + /* grab all the int's that were in subobj. if it had elements that were not ints, we throw an exception + (capital V on Vals() means exception if wrong type found */ vector<int> myints; sub.Vals(myints); cout << "my ints: " << myints[0] << ' ' << myints[1] << endl; - /* grab all the string values from x. if the field isn't of string type, just skip it -- + /* grab all the string values from x. if the field isn't of string type, just skip it -- lowercase v on vals() indicates skip don't throw. */ vector<string> strs; @@ -103,5 +102,6 @@ int main() cout << strs.size() << " strings, first one: " << strs[0] << endl; iter(y); - return 0; + return 0; } + diff --git a/bson/bsonelement.h b/bson/bsonelement.h index 534c773..23d59fa 100644 --- a/bson/bsonelement.h +++ b/bson/bsonelement.h @@ -36,378 +36,384 @@ namespace mongo { 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 + /** 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() */ - 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(). + 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 */ - 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 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(); } + 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(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; - void toString(StringBuilder& s, bool includeFieldName = true, bool full=false) const; - string jsonString( JsonStringFormat format, bool includeFieldNames = true, int pretty = 0 ) const; - operator string() const { return toString(); } + string toString( bool includeFieldName = true, bool full=false) const; + void toString(StringBuilder& s, bool includeFieldName = true, bool full=false) const; + string jsonString( JsonStringFormat format, bool includeFieldNames = true, int pretty = 0 ) const; + operator string() const { return toString(); } - /** Returns the type of the element */ - BSONType type() const { return (BSONType) *data; } + /** 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; + /** retrieve a field within this element + throws exception if *this is not an embedded object + */ + BSONElement operator[] (const string& field) 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; } + /** 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; - /** 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; + /** 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; } - /** Wrap this element up as a singleton object. */ - BSONObj wrap() const; + /** 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 with a new name. */ - BSONObj wrap( const char* newName) const; + /** Wrap this element up as a singleton object. */ + BSONObj wrap() 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; - } + /** Wrap this element up as a singleton object with a new name. */ + BSONObj wrap( const char* newName) const; - /** 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; - } + /** 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; + } - bool isBoolean() const { return type() == mongo::Bool; } + /** 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; + } - /** @return value of a boolean element. - You must assure element is a boolean before - calling. */ - bool boolean() const { - return *value() ? true : false; - } + bool isBoolean() const { return type() == mongo::Bool; } - /** 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() ); - } + /** @return value of a boolean element. + You must assure element is a boolean before + calling. */ + bool boolean() const { + return *value() ? true : false; + } - /** 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; + /** 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() ); + } - /** True if number, string, bool, date, OID */ - bool isSimpleType() const; + /** 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(); } - /** True if element is of a numeric type. */ - bool isNumber() const; + /** 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() ); } - /** 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() ); } + /** True if element is null. */ + bool isNull() const { + return type() == jstNULL; + } - /** 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(); } + /** Size (length) of a string element. + You must assure of type String first. */ + int valuestrsize() const { + return *reinterpret_cast< const int* >( value() ); + } - /** 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() ); } + // for objects the size *includes* the size of the size field + int objsize() const { + return *reinterpret_cast< const int* >( 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() ); - } + /** 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; + } - // for objects the size *includes* the size of the size field - int objsize() const { - return *reinterpret_cast< const int* >( value() ); - } + /** 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 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 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 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 the embedded object this element holds. */ + BSONObj embeddedObject() const; - /** 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; - } + /* uasserts if not an object */ + BSONObj embeddedObjectUserCheck() const; - /** Get the embedded object this element holds. */ - BSONObj embeddedObject() const; + BSONObj codeWScopeObject() const; - /* uasserts if not an object */ - BSONObj embeddedObjectUserCheck() 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; + } + } - BSONObj codeWScopeObject() const; + BinDataType binDataType() const { + // BinData: <int len> <byte subtype> <byte[len] data> + assert( type() == BinData ); + unsigned char c = (value() + 4)[0]; + return (BinDataType)c; + } - /** 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; + /** Retrieve the regex string for a Regex element */ + const char *regex() const { + assert(type() == RegEx); + return value(); } - } - - 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; + } - /** 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; + } - /** 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 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; - /** 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; } - 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; + /** 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; + /** 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]; - } + 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 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 ); - } + 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; - } + 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; } - totalSize = -1; - } - string _asCode() const; - OpTime _opTime() const; + // 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; + } -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 */ + string _asCode() const; + OpTime _opTime() const; - friend class BSONObjIterator; - friend class BSONObj; - const BSONElement& chk(int t) const { - if ( t != type() ){ - StringBuilder ss; - ss << "wrong type for BSONElement (" << fieldName() << ") " << type() << " != " << t; - uasserted(13111, ss.str() ); + private: + const char *data; + mutable int fieldNameSize_; // cached value + int fieldNameSize() const { + if ( fieldNameSize_ == -1 ) + fieldNameSize_ = (int)strlen( fieldName() ) + 1; + return fieldNameSize_; } - return *this; - } - const BSONElement& chk(bool expr) const { - uassert(13118, "unexpected or missing type value in BSON object", expr); - return *this; - } -}; + mutable int totalSize; /* caches the computed size */ + + friend class BSONObjIterator; + friend class BSONObj; + const BSONElement& chk(int t) const { + if ( t != type() ) { + StringBuilder 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 ){ + switch ( t ) { case MinKey: case MaxKey: return t; @@ -448,7 +454,7 @@ private: assert(0); return -1; } - } + } inline bool BSONElement::trueValue() const { switch( type() ) { @@ -464,7 +470,7 @@ private: case jstNULL: case Undefined: return false; - + default: ; } @@ -478,13 +484,13 @@ private: case NumberDouble: case NumberInt: return true; - default: + default: return false; } } inline bool BSONElement::isSimpleType() const { - switch( type() ){ + switch( type() ) { case NumberLong: case NumberDouble: case NumberInt: @@ -493,7 +499,7 @@ private: case mongo::Date: case jstOID: return true; - default: + default: return false; } } @@ -512,7 +518,7 @@ private: } /** 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 { + inline int BSONElement::numberInt() const { switch( type() ) { case NumberDouble: return (int) _numberDouble(); @@ -526,7 +532,7 @@ private: } /** Retrieve long value for the element safely. Zero returned if not a number. */ - inline long long BSONElement::numberLong() const { + inline long long BSONElement::numberLong() const { switch( type() ) { case NumberDouble: return (long long) _numberDouble(); @@ -537,7 +543,7 @@ private: default: return 0; } - } + } inline BSONElement::BSONElement() { static char z = 0; diff --git a/bson/bsonmisc.h b/bson/bsonmisc.h index 40ec6d3..96be12a 100644 --- a/bson/bsonmisc.h +++ b/bson/bsonmisc.h @@ -26,7 +26,7 @@ namespace mongo { return l.woCompare( r, false ) < 0; } }; - + class BSONObjCmp { public: BSONObjCmp( const BSONObj &_order = BSONObj() ) : order( _order ) {} @@ -54,26 +54,26 @@ namespace mongo { FieldCompareResult compareDottedFieldNames( const string& l , const string& r ); -/** Use BSON macro to build a BSONObj from a stream + /** Use BSON macro to build a BSONObj from a stream + + e.g., + BSON( "name" << "joe" << "age" << 33 ) - e.g., - BSON( "name" << "joe" << "age" << 33 ) + with auto-generated object id: + BSON( GENOID << "name" << "joe" << "age" << 33 ) - with auto-generated object id: - BSON( GENOID << "name" << "joe" << "age" << 33 ) - - The labels GT, GTE, LT, LTE, NE can be helpful for stream-oriented construction - of a BSONObj, particularly when assembling a Query. For example, - BSON( "a" << GT << 23.4 << NE << 30 << "b" << 2 ) produces the object - { a: { \$gt: 23.4, \$ne: 30 }, b: 2 }. -*/ + The labels GT, GTE, LT, LTE, NE can be helpful for stream-oriented construction + of a BSONObj, particularly when assembling a Query. For example, + BSON( "a" << GT << 23.4 << NE << 30 << "b" << 2 ) produces the object + { a: { \$gt: 23.4, \$ne: 30 }, b: 2 }. + */ #define BSON(x) (( mongo::BSONObjBuilder(64) << x ).obj()) -/** Use BSON_ARRAY macro like BSON macro, but without keys + /** Use BSON_ARRAY macro like BSON macro, but without keys - BSONArray arr = BSON_ARRAY( "hello" << 1 << BSON( "foo" << BSON_ARRAY( "bar" << "baz" << "qux" ) ) ); + BSONArray arr = BSON_ARRAY( "hello" << 1 << BSON( "foo" << BSON_ARRAY( "bar" << "baz" << "qux" ) ) ); - */ + */ #define BSON_ARRAY(x) (( mongo::BSONArrayBuilder() << x ).arr()) /* Utility class to auto assign object IDs. @@ -83,11 +83,18 @@ namespace mongo { extern struct GENOIDLabeler { } GENOID; /* Utility class to add a Date element with the current time - Example: + Example: cout << BSON( "created" << DATENOW ); // { created : "2009-10-09 11:41:42" } */ extern struct DateNowLabeler { } DATENOW; + /* Utility class to add the minKey (minus infinity) to a given attribute + Example: + cout << BSON( "a" << MINKEY ); // { "a" : { "$minKey" : 1 } } + */ + extern struct MinKeyLabeler { } MINKEY; + extern struct MaxKeyLabeler { } MAXKEY; + // Utility class to implement GT, GTE, etc as described above. class Labeler { public: @@ -99,17 +106,17 @@ namespace mongo { template<class T> BSONObjBuilder& operator<<( T value ); - /* the value of the element e is appended i.e. for + /* the value of the element e is appended i.e. for "age" << GT << someElement - one gets - { age : { $gt : someElement's value } } + one gets + { age : { $gt : someElement's value } } */ BSONObjBuilder& operator<<( const BSONElement& e ); private: const Label &l_; BSONObjBuilderValueStream *s_; }; - + extern Labeler::Label GT; extern Labeler::Label GTE; extern Labeler::Label LT; @@ -126,7 +133,7 @@ namespace mongo { inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e); inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e, const BSONObj& f); // definitions in bsonobjbuilder.h b/c of incomplete types - + // Utility class to implement BSON( key << val ) as described above. class BSONObjBuilderValueStream : public boost::noncopyable { public: @@ -134,17 +141,20 @@ namespace mongo { BSONObjBuilderValueStream( BSONObjBuilder * builder ); BSONObjBuilder& operator<<( const BSONElement& e ); - - template<class T> + + template<class T> BSONObjBuilder& operator<<( T value ); BSONObjBuilder& operator<<(DateNowLabeler& id); - + + BSONObjBuilder& operator<<(MinKeyLabeler& id); + BSONObjBuilder& operator<<(MaxKeyLabeler& id); + Labeler operator<<( const Labeler::Label &l ); void endField( const char *nextFieldName = 0 ); bool subobjStarted() const { return _fieldName != 0; } - + private: const char * _fieldName; BSONObjBuilder * _builder; @@ -153,39 +163,39 @@ namespace mongo { BSONObjBuilder *subobj(); auto_ptr< BSONObjBuilder > _subobj; }; - + /** used in conjuction with BSONObjBuilder, allows for proper buffer size to prevent crazy memory usage */ class BSONSizeTracker { public: - BSONSizeTracker(){ + BSONSizeTracker() { _pos = 0; for ( int i=0; i<SIZE; i++ ) _sizes[i] = 512; // this is the default, so just be consistent } - - ~BSONSizeTracker(){ + + ~BSONSizeTracker() { } - - void got( int size ){ + + void got( int size ) { _sizes[_pos++] = size; if ( _pos >= SIZE ) _pos = 0; } - + /** * right now choosing largest size */ int getSize() const { int x = 16; // sane min - for ( int i=0; i<SIZE; i++ ){ + for ( int i=0; i<SIZE; i++ ) { if ( _sizes[i] > x ) x = _sizes[i]; } return x; } - + private: enum { SIZE = 10 }; int _pos; diff --git a/bson/bsonobj.h b/bson/bsonobj.h index a802526..3ca6b8c 100644 --- a/bson/bsonobj.h +++ b/bson/bsonobj.h @@ -28,23 +28,23 @@ namespace mongo { typedef set< BSONElement, BSONElementCmpWithoutField > BSONElementSet; /** - C++ representation of a "BSON" object -- that is, an extended JSON-style + C++ representation of a "BSON" object -- that is, an extended JSON-style object in a binary representation. See bsonspec.org. - Note that BSONObj's have a smart pointer capability built in -- so you can + Note that BSONObj's have a smart pointer capability built in -- so you can pass them around by value. The reference counts used to implement this do not use locking, so copying and destroying BSONObj's are not thread-safe operations. BSON object format: - + code <unsigned totalSize> {<byte BSONType><cstring FieldName><Data>}* EOO - + totalSize includes itself. - + Data: Bool: <byte> EOO: nothing follows @@ -67,31 +67,65 @@ namespace mongo { */ class BSONObj { public: - /** Construct a BSONObj from data in the proper format. - @param ifree true if the BSONObj should free() the msgdata when - it destructs. - */ + + /** Construct a BSONObj from data in the proper format. + @param ifree true if the BSONObj should free() the msgdata when + it destructs. + */ explicit BSONObj(const char *msgdata, bool ifree = false) { init(msgdata, ifree); } - BSONObj(const Record *r); + + explicit BSONObj(const Record *r); + /** Construct an empty BSONObj -- that is, {}. */ BSONObj(); - // defensive - ~BSONObj() { _objdata = 0; } - void appendSelfToBufBuilder(BufBuilder& b) const { - assert( objsize() ); - b.appendBuf(reinterpret_cast<const void *>( objdata() ), objsize()); - } + ~BSONObj() { /*defensive:*/ _objdata = 0; } + + /** + A BSONObj can use a buffer it "owns" or one it does not. + + OWNED CASE + If the BSONObj owns the buffer, the buffer can be shared among several BSONObj's (by assignment). + In this case the buffer is basically implemented as a shared_ptr. + Since BSONObj's are typically immutable, this works well. + + UNOWNED CASE + A BSONObj can also point to BSON data in some other data structure it does not "own" or free later. + For example, in a memory mapped file. In this case, it is important the original data stays in + scope for as long as the BSONObj is in use. If you think the original data may go out of scope, + call BSONObj::getOwned() to promote your BSONObj to having its own copy. + + On a BSONObj assignment, if the source is unowned, both the source and dest will have unowned + pointers to the original buffer after the assignment. - /** Readable representation of a BSON object in an extended JSON-style notation. + If you are not sure about ownership but need the buffer to last as long as the BSONObj, call + getOwned(). getOwned() is a no-op if the buffer is already owned. If not already owned, a malloc + and memcpy will result. + + Most ways to create BSONObj's create 'owned' variants. Unowned versions can be created with: + (1) specifying true for the ifree parameter in the constructor + (2) calling BSONObjBuilder::done(). Use BSONObjBuilder::obj() to get an owned copy + (3) retrieving a subobject retrieves an unowned pointer into the parent BSON object + + @return true if this is in owned mode + */ + bool isOwned() const { return _holder.get() != 0; } + + /* make sure the data buffer is under the control of this BSONObj and not a remote buffer */ + BSONObj getOwned() const; + + /** @return a new full (and owned) copy of the object. */ + BSONObj copy() const; + + /** Readable representation of a BSON object in an extended JSON-style notation. This is an abbreviated representation which might be used for logging. */ string toString( bool isArray = false, bool full=false ) const; void toString(StringBuilder& s, bool isArray = false, bool full=false ) const; - - /** Properly formatted JSON string. + + /** Properly formatted JSON string. @param pretty if true we try to add some lf's and indentation */ string jsonString( JsonStringFormat format = Strict, int pretty = 0 ) const; @@ -126,38 +160,36 @@ namespace mongo { names with respect to the returned element. */ BSONElement getFieldDottedOrArray(const char *&name) const; - /** Get the field of the specified name. eoo() is true on the returned - element if not found. + /** Get the field of the specified name. eoo() is true on the returned + element if not found. */ BSONElement getField(const StringData& name) const; - /** Get the field of the specified name. eoo() is true on the returned - element if not found. + /** Get the field of the specified name. eoo() is true on the returned + element if not found. */ - BSONElement operator[] (const char *field) const { + BSONElement operator[] (const char *field) const { return getField(field); } - BSONElement operator[] (const string& field) const { + BSONElement operator[] (const string& field) const { return getField(field); } - BSONElement operator[] (int field) const { + BSONElement operator[] (int field) const { StringBuilder ss; ss << field; string s = ss.str(); return getField(s.c_str()); } - /** @return true if field exists */ - bool hasField( const char * name )const { - return ! getField( name ).eoo(); - } + /** @return true if field exists */ + bool hasField( const char * name ) const { return ! getField( name ).eoo(); } /** @return "" if DNE or wrong type */ const char * getStringField(const char *name) const; - /** @return subobject of the given name */ + /** @return subobject of the given name */ BSONObj getObjectField(const char *name) const; /** @return INT_MIN if not present - does some type conversions */ @@ -172,26 +204,24 @@ namespace mongo { object. */ BSONObj extractFieldsUnDotted(BSONObj pattern) const; - + /** extract items from object which match a pattern object. - e.g., if pattern is { x : 1, y : 1 }, builds an object with - x and y elements of this object, if they are present. + e.g., if pattern is { x : 1, y : 1 }, builds an object with + x and y elements of this object, if they are present. returns elements with original field names */ BSONObj extractFields(const BSONObj &pattern , bool fillWithNull=false) const; - + BSONObj filterFieldsUndotted(const BSONObj &filter, bool inFilter) const; BSONElement getFieldUsingIndexNames(const char *fieldName, const BSONObj &indexKey) const; - + /** @return the raw data of the object */ const char *objdata() const { return _objdata; } /** @return total size of the BSON object in bytes */ - int objsize() const { - return *(reinterpret_cast<const int*>(objdata())); - } + int objsize() const { return *(reinterpret_cast<const int*>(objdata())); } /** performs a cursory check on the object's size only. */ bool isValid(); @@ -201,32 +231,30 @@ namespace mongo { */ bool okForStorage() const; - /** @return true if object is empty -- i.e., {} */ - bool isEmpty() const { - return objsize() <= 5; - } + /** @return true if object is empty -- i.e., {} */ + bool isEmpty() const { return objsize() <= 5; } void dump() const; /** Alternative output format */ string hexDump() const; - + /**wo='well ordered'. fields must be in same order in each object. - Ordering is with respect to the signs of the elements + Ordering is with respect to the signs of the elements and allows ascending / descending key mixing. - @return <0 if l<r. 0 if l==r. >0 if l>r + @return <0 if l<r. 0 if l==r. >0 if l>r */ int woCompare(const BSONObj& r, const Ordering &o, bool considerFieldName=true) const; /**wo='well ordered'. fields must be in same order in each object. - Ordering is with respect to the signs of the elements + Ordering is with respect to the signs of the elements and allows ascending / descending key mixing. - @return <0 if l<r. 0 if l==r. >0 if l>r + @return <0 if l<r. 0 if l==r. >0 if l>r */ int woCompare(const BSONObj& r, const BSONObj &ordering = BSONObj(), bool considerFieldName=true) const; - + bool operator<( const BSONObj& other ) const { return woCompare( other ) < 0; } bool operator<=( const BSONObj& other ) const { return woCompare( other ) <= 0; } @@ -249,31 +277,18 @@ namespace mongo { return false; } - /** @return first field of the object */ - BSONElement firstElement() const { - return BSONElement(objdata() + 4); - } + /** @return first field of the object */ + BSONElement firstElement() const { return BSONElement(objdata() + 4); } - /** @return true if field exists in the object */ + /** @return true if field exists in the object */ bool hasElement(const char *name) const; - /** Get the _id field from the object. For good performance drivers should - assure that _id is the first element of the object; however, correct operation + /** Get the _id field from the object. For good performance drivers should + assure that _id is the first element of the object; however, correct operation is assured regardless. @return true if found - */ - bool getObjectID(BSONElement& e) const; - - /** makes a copy of the object. */ - BSONObj copy() const; - - /* make sure the data buffer is under the control of this BSONObj and not a remote buffer */ - BSONObj getOwned() const{ - if ( !isOwned() ) - return copy(); - return *this; - } - bool isOwned() const { return _holder.get() != 0; } + */ + bool getObjectID(BSONElement& e) const; /** @return A hash code for the object */ int hash() const { @@ -289,18 +304,18 @@ namespace mongo { // string identifier equivalents. // TODO Support conversion of element types other than min and max. BSONObj clientReadable() const; - + /** Return new object with the field names replaced by those in the passed object. */ BSONObj replaceFieldNames( const BSONObj &obj ) const; - + /** true unless corrupt */ bool valid() const; - + /** @return an md5 value for this object. */ string md5() const; - - bool operator==( const BSONObj& other ) const{ + + bool operator==( const BSONObj& other ) const { return woCompare( other ) == 0; } @@ -324,14 +339,21 @@ namespace mongo { opNEAR = 0x13, opWITHIN = 0x14, opMAX_DISTANCE=0x15 - }; + }; /** add all elements of the object to the specified vector */ void elems(vector<BSONElement> &) const; /** add all elements of the object to the specified list */ void elems(list<BSONElement> &) const; - /** add all values of the object to the specified vector. If type mismatches, exception. */ + /** add all values of the object to the specified vector. If type mismatches, exception. + this is most useful when the BSONObj is an array, but can be used with non-arrays too in theory. + + example: + bo sub = y["subobj"].Obj(); + vector<int> myints; + sub.Vals(myints); + */ template <class T> void Vals(vector<T> &) const; /** add all values of the object to the specified list. If type mismatches, exception. */ @@ -347,13 +369,25 @@ namespace mongo { friend class BSONObjIterator; typedef BSONObjIterator iterator; + + /** use something like this: + for( BSONObj::iterator i = myObj.begin(); i.more(); ) { + BSONElement e = i.next(); + ... + } + */ BSONObjIterator begin(); -private: + void appendSelfToBufBuilder(BufBuilder& b) const { + assert( objsize() ); + b.appendBuf(reinterpret_cast<const void *>( objdata() ), objsize()); + } + + private: class Holder { public: Holder( const char *objdata ) : - _objdata( objdata ) { + _objdata( objdata ) { } ~Holder() { free((void *)_objdata); @@ -362,29 +396,27 @@ private: private: const char *_objdata; }; + const char *_objdata; boost::shared_ptr< Holder > _holder; + + void _assertInvalid() const; void init(const char *data, bool ifree) { if ( ifree ) _holder.reset( new Holder( data ) ); _objdata = data; - if ( ! isValid() ){ - StringBuilder ss; - int os = objsize(); - ss << "Invalid BSONObj spec size: " << os << " (" << toHex( &os, 4 ) << ")"; - try { - BSONElement e = firstElement(); - ss << " first element:" << e.toString() << " "; - } - catch ( ... ){} - string s = ss.str(); - massert( 10334 , s , 0 ); - } + if ( !isValid() ) + _assertInvalid(); } }; + ostream& operator<<( ostream &s, const BSONObj &o ); ostream& operator<<( ostream &s, const BSONElement &e ); + StringBuilder& operator<<( StringBuilder &s, const BSONObj &o ); + StringBuilder& operator<<( StringBuilder &s, const BSONElement &e ); + + struct BSONArray : BSONObj { // Don't add anything other than forwarding constructors!!! BSONArray(): BSONObj() {} diff --git a/bson/bsonobjbuilder.h b/bson/bsonobjbuilder.h index fdfe4de..a39b529 100644 --- a/bson/bsonobjbuilder.h +++ b/bson/bsonobjbuilder.h @@ -36,7 +36,7 @@ namespace mongo { template<typename T> class BSONFieldValue { public: - BSONFieldValue( const string& name , const T& t ){ + BSONFieldValue( const string& name , const T& t ) { _name = name; _t = t; } @@ -52,8 +52,8 @@ namespace mongo { template<typename T> class BSONField { public: - BSONField( const string& name , const string& longName="" ) - : _name(name), _longName(longName){} + BSONField( const string& name , const string& longName="" ) + : _name(name), _longName(longName) {} const string& name() const { return _name; } operator string() const { return _name; } @@ -65,11 +65,11 @@ namespace mongo { BSONFieldValue<BSONObj> lt( const T& t ) const { return query( "$lt" , t ); } BSONFieldValue<BSONObj> query( const char * q , const T& t ) const; - + BSONFieldValue<T> operator()( const T& t ) const { return BSONFieldValue<T>( _name , t ); } - + private: string _name; string _longName; @@ -85,17 +85,18 @@ namespace mongo { _b.skip(4); /*leave room for size field*/ } + /* dm why do we have this/need this? not clear to me, comment please tx. */ /** @param baseBuilder construct a BSONObjBuilder using an existing BufBuilder */ BSONObjBuilder( BufBuilder &baseBuilder ) : _b( baseBuilder ), _buf( 0 ), _offset( baseBuilder.len() ), _s( this ) , _tracker(0) , _doneCalled(false) { _b.skip( 4 ); } - + BSONObjBuilder( const BSONSizeTracker & tracker ) : _b(_buf) , _buf(tracker.getSize() ), _offset(0), _s( this ) , _tracker( (BSONSizeTracker*)(&tracker) ) , _doneCalled(false) { _b.skip( 4 ); } - ~BSONObjBuilder(){ - if ( !_doneCalled && _b.buf() && _buf.getSize() == 0 ){ + ~BSONObjBuilder() { + if ( !_doneCalled && _b.buf() && _buf.getSize() == 0 ) { _done(); } } @@ -103,6 +104,9 @@ namespace mongo { /** add all the fields from the object specified to this object */ BSONObjBuilder& appendElements(BSONObj x); + /** add all the fields from the object specified to this object if they don't exist already */ + BSONObjBuilder& appendElementsUnique( BSONObj x ); + /** append element to the object we are building */ BSONObjBuilder& append( const BSONElement& e) { assert( !e.eoo() ); // do not append eoo, that would corrupt us. the builder auto appends when done() is called. @@ -111,7 +115,7 @@ namespace mongo { } /** append an element but with a new name */ - BSONObjBuilder& appendAs(const BSONElement& e, const StringData& fieldName) { + BSONObjBuilder& appendAs(const BSONElement& e, const StringData& fieldName) { assert( !e.eoo() ); // do not append eoo, that would corrupt us. the builder auto appends when done() is called. _b.appendNum((char) e.type()); _b.appendStr(fieldName); @@ -128,14 +132,14 @@ namespace mongo { } /** add a subobject as a member */ - BSONObjBuilder& appendObject(const StringData& fieldName, const char * objdata , int size = 0 ){ + BSONObjBuilder& appendObject(const StringData& fieldName, const char * objdata , int size = 0 ) { assert( objdata ); - if ( size == 0 ){ + if ( size == 0 ) { size = *((int*)objdata); } - + assert( size > 4 && size < 100000000 ); - + _b.appendNum((char) Object); _b.appendStr(fieldName); _b.appendBuf((void*)objdata, size ); @@ -150,7 +154,7 @@ namespace mongo { _b.appendStr(fieldName); return _b; } - + /** add a subobject as a member with type Array. Thus arr object should have "0", "1", ... style fields in it. */ @@ -160,9 +164,9 @@ namespace mongo { _b.appendBuf((void *) subObj.objdata(), subObj.objsize()); return *this; } - BSONObjBuilder& append(const StringData& fieldName, BSONArray arr) { - return appendArray(fieldName, arr); - } + BSONObjBuilder& append(const StringData& fieldName, BSONArray arr) { + return appendArray(fieldName, arr); + } /** add header for a new subarray and return bufbuilder for writing to the subarray's body */ @@ -171,7 +175,7 @@ namespace mongo { _b.appendStr(fieldName); return _b; } - + /** Append a boolean element */ BSONObjBuilder& appendBool(const StringData& fieldName, int val) { _b.appendNum((char) Bool); @@ -184,10 +188,10 @@ namespace mongo { BSONObjBuilder& append(const StringData& fieldName, bool val) { _b.appendNum((char) Bool); _b.appendStr(fieldName); - _b.appendNum((char) (val?1:0)); + _b.appendNum((char) (val?1:0)); return *this; } - + /** Append a 32 bit integer element */ BSONObjBuilder& append(const StringData& fieldName, int n) { _b.appendNum((char) NumberInt); @@ -197,20 +201,20 @@ namespace mongo { } /** Append a 32 bit unsigned element - cast to a signed int. */ - BSONObjBuilder& append(const StringData& fieldName, unsigned n) { - return append(fieldName, (int) n); + BSONObjBuilder& append(const StringData& fieldName, unsigned n) { + return append(fieldName, (int) n); } /** Append a NumberLong */ - BSONObjBuilder& append(const StringData& fieldName, long long n) { + BSONObjBuilder& append(const StringData& fieldName, long long n) { _b.appendNum((char) NumberLong); _b.appendStr(fieldName); _b.appendNum(n); - return *this; + return *this; } /** appends a number. if n < max(int)/2 then uses int, otherwise long long */ - BSONObjBuilder& appendIntOrLL( const StringData& fieldName , long long n ){ + BSONObjBuilder& appendIntOrLL( const StringData& fieldName , long long n ) { long long x = n; if ( x < 0 ) x = x * -1; @@ -225,15 +229,26 @@ namespace mongo { * appendNumber is a series of method for appending the smallest sensible type * mostly for JS */ - BSONObjBuilder& appendNumber( const StringData& fieldName , int n ){ + BSONObjBuilder& appendNumber( const StringData& fieldName , int n ) { return append( fieldName , n ); } - BSONObjBuilder& appendNumber( const StringData& fieldName , double d ){ + BSONObjBuilder& appendNumber( const StringData& fieldName , double d ) { return append( fieldName , d ); } - BSONObjBuilder& appendNumber( const StringData& fieldName , long long l ){ + BSONObjBuilder& appendNumber( const StringData& fieldName , size_t n ) { + static size_t maxInt = (size_t)pow( 2.0 , 30.0 ); + + if ( n < maxInt ) + append( fieldName , (int)n ); + else + append( fieldName , (long long)n ); + return *this; + } + + + BSONObjBuilder& appendNumber( const StringData& fieldName , long long l ) { static long long maxInt = (int)pow( 2.0 , 30.0 ); static long long maxDouble = (long long)pow( 2.0 , 40.0 ); @@ -245,7 +260,7 @@ namespace mongo { append( fieldName , l ); return *this; } - + /** Append a double element */ BSONObjBuilder& append(const StringData& fieldName, double n) { _b.appendNum((char) NumberDouble); @@ -259,8 +274,8 @@ namespace mongo { */ bool appendAsNumber( const StringData& fieldName , const string& data ); - /** Append a BSON Object ID (OID type). - @deprecated Generally, it is preferred to use the append append(name, oid) + /** Append a BSON Object ID (OID type). + @deprecated Generally, it is preferred to use the append append(name, oid) method for this. */ BSONObjBuilder& appendOID(const StringData& fieldName, OID *oid = 0 , bool generateIfBlank = false ) { @@ -279,8 +294,8 @@ namespace mongo { return *this; } - /** - Append a BSON Object ID. + /** + Append a BSON Object ID. @param fieldName Field name, e.g., "_id". @returns the builder object */ @@ -309,14 +324,14 @@ namespace mongo { _b.appendNum(static_cast<unsigned long long>(dt) * 1000); return *this; } - /** Append a date. - @param dt a Java-style 64 bit date value, that is + /** Append a date. + @param dt a Java-style 64 bit date value, that is the number of milliseconds since January 1, 1970, 00:00:00 GMT */ BSONObjBuilder& appendDate(const StringData& fieldName, Date_t dt) { /* easy to pass a time_t to this and get a bad result. thus this warning. */ #if defined(_DEBUG) && defined(MONGO_EXPOSE_MACROS) - if( dt > 0 && dt <= 0xffffffff ) { + if( dt > 0 && dt <= 0xffffffff ) { static int n; if( n++ == 0 ) log() << "DEV WARNING appendDate() called with a tiny (but nonzero) date" << endl; @@ -335,27 +350,22 @@ namespace mongo { @param regex the regular expression pattern @param regex options such as "i" or "g" */ - BSONObjBuilder& appendRegex(const StringData& fieldName, const char *regex, const char *options = "") { + BSONObjBuilder& appendRegex(const StringData& fieldName, const StringData& regex, const StringData& options = "") { _b.appendNum((char) RegEx); _b.appendStr(fieldName); _b.appendStr(regex); _b.appendStr(options); return *this; } - /** Append a regular expression value - @param regex the regular expression pattern - @param regex options such as "i" or "g" - */ - BSONObjBuilder& appendRegex(const StringData& fieldName, string regex, string options = "") { - return appendRegex(fieldName, regex.c_str(), options.c_str()); - } - BSONObjBuilder& appendCode(const StringData& fieldName, const char *code) { + + BSONObjBuilder& appendCode(const StringData& fieldName, const StringData& code) { _b.appendNum((char) Code); _b.appendStr(fieldName); - _b.appendNum((int) strlen(code)+1); + _b.appendNum((int) code.size()+1); _b.appendStr(code); return *this; } + /** Append a string element. len DOES include terminating nul */ BSONObjBuilder& append(const StringData& fieldName, const char *str, int len) { _b.appendNum((char) String); @@ -369,48 +379,51 @@ namespace mongo { return append(fieldName, str, (int) strlen(str)+1); } /** Append a string element */ - BSONObjBuilder& append(const StringData& fieldName, string str) { + BSONObjBuilder& append(const StringData& fieldName, const string& str) { return append(fieldName, str.c_str(), (int) str.size()+1); } - BSONObjBuilder& appendSymbol(const StringData& fieldName, const char *symbol) { + + BSONObjBuilder& appendSymbol(const StringData& fieldName, const StringData& symbol) { _b.appendNum((char) Symbol); _b.appendStr(fieldName); - _b.appendNum((int) strlen(symbol)+1); + _b.appendNum((int) symbol.size()+1); _b.appendStr(symbol); - return *this; } + return *this; + } /** Append a Null element to the object */ BSONObjBuilder& appendNull( const StringData& fieldName ) { _b.appendNum( (char) jstNULL ); _b.appendStr( fieldName ); - return *this; } + return *this; + } // Append an element that is less than all other keys. BSONObjBuilder& appendMinKey( const StringData& fieldName ) { _b.appendNum( (char) MinKey ); _b.appendStr( fieldName ); - return *this; + return *this; } // Append an element that is greater than all other keys. BSONObjBuilder& appendMaxKey( const StringData& fieldName ) { _b.appendNum( (char) MaxKey ); _b.appendStr( fieldName ); - return *this; + return *this; } - + // Append a Timestamp field -- will be updated to next OpTime on db insert. BSONObjBuilder& appendTimestamp( const StringData& fieldName ) { _b.appendNum( (char) Timestamp ); _b.appendStr( fieldName ); _b.appendNum( (unsigned long long) 0 ); - return *this; + return *this; } BSONObjBuilder& appendTimestamp( const StringData& fieldName , unsigned long long val ) { _b.appendNum( (char) Timestamp ); _b.appendStr( fieldName ); _b.appendNum( val ); - return *this; + return *this; } /** @@ -419,24 +432,24 @@ namespace mongo { @param time - in millis (but stored in seconds) */ BSONObjBuilder& appendTimestamp( const StringData& fieldName , unsigned long long time , unsigned int inc ); - + /* Append an element of the deprecated DBRef type. - @deprecated + @deprecated */ - BSONObjBuilder& appendDBRef( const StringData& fieldName, const char *ns, const OID &oid ) { + BSONObjBuilder& appendDBRef( const StringData& fieldName, const StringData& ns, const OID &oid ) { _b.appendNum( (char) DBRef ); _b.appendStr( fieldName ); - _b.appendNum( (int) strlen( ns ) + 1 ); + _b.appendNum( (int) ns.size() + 1 ); _b.appendStr( ns ); _b.appendBuf( (void *) &oid, 12 ); - return *this; + return *this; } - /** Append a binary data element + /** Append a binary data element @param fieldName name of the field @param len length of the binary data in bytes - @param subtype subtype information for the data. @see enum BinDataType in bsontypes.h. + @param subtype subtype information for the data. @see enum BinDataType in bsontypes.h. Use BinDataGeneral if you don't care about the type. @param data the byte array */ @@ -446,36 +459,36 @@ namespace mongo { _b.appendNum( len ); _b.appendNum( (char) type ); _b.appendBuf( (void *) data, len ); - return *this; + return *this; } BSONObjBuilder& appendBinData( const StringData& fieldName, int len, BinDataType type, const unsigned char *data ) { return appendBinData(fieldName, len, type, (const char *) data); } - + /** Subtype 2 is deprecated. Append a BSON bindata bytearray element. @param data a byte array @param len the length of data */ - BSONObjBuilder& appendBinDataArrayDeprecated( const char * fieldName , const char * data , int len ){ + BSONObjBuilder& appendBinDataArrayDeprecated( const char * fieldName , const char * data , int len ) { _b.appendNum( (char) BinData ); _b.appendStr( fieldName ); _b.appendNum( len + 4 ); _b.appendNum( (char)0x2 ); _b.appendNum( len ); - _b.appendBuf( (void *) data, len ); - return *this; + _b.appendBuf( (void *) data, len ); + return *this; } - /** Append to the BSON object a field of type CodeWScope. This is a javascript code + /** Append to the BSON object a field of type CodeWScope. This is a javascript code fragment accompanied by some scope that goes with it. */ - BSONObjBuilder& appendCodeWScope( const StringData& fieldName, const char *code, const BSONObj &scope ) { + BSONObjBuilder& appendCodeWScope( const StringData& fieldName, const StringData& code, const BSONObj &scope ) { _b.appendNum( (char) CodeWScope ); _b.appendStr( fieldName ); - _b.appendNum( ( int )( 4 + 4 + strlen( code ) + 1 + scope.objsize() ) ); - _b.appendNum( ( int ) strlen( code ) + 1 ); + _b.appendNum( ( int )( 4 + 4 + code.size() + 1 + scope.objsize() ) ); + _b.appendNum( ( int ) code.size() + 1 ); _b.appendStr( code ); _b.appendBuf( ( void * )scope.objdata(), scope.objsize() ); return *this; @@ -485,15 +498,12 @@ namespace mongo { _b.appendNum( (char) Undefined ); _b.appendStr( fieldName ); } - + /* helper function -- see Query::where() for primary way to do this. */ - void appendWhere( const char *code, const BSONObj &scope ){ + void appendWhere( const StringData& code, const BSONObj &scope ) { appendCodeWScope( "$where" , code , scope ); } - void appendWhere( const string &code, const BSONObj &scope ){ - appendWhere( code.c_str(), scope ); - } - + /** these are the min/max when comparing, not strict min/max elements for a given type */ @@ -507,7 +517,11 @@ namespace mongo { template < class T > BSONObjBuilder& append( const StringData& fieldName, const list< T >& vals ); - /** The returned BSONObj will free the buffer when it is finished. */ + /** + * destructive + * The returned BSONObj will free the buffer when it is finished. + * @return owned BSONObj + */ BSONObj obj() { bool own = owned(); massert( 10335 , "builder does not own memory", own ); @@ -516,12 +530,12 @@ namespace mongo { } /** Fetch the object we have built. - BSONObjBuilder still frees the object when the builder goes out of - scope -- very important to keep in mind. Use obj() if you - would like the BSONObj to last longer than the builder. + BSONObjBuilder still frees the object when the builder goes out of + scope -- very important to keep in mind. Use obj() if you + would like the BSONObj to last longer than the builder. */ BSONObj done() { - return BSONObj(_done()); + return BSONObj(_done(), /*ifree*/false); } // Like 'done' above, but does not construct a BSONObj to return to the caller. @@ -591,25 +605,29 @@ namespace mongo { BSONObjBuilderValueStream& operator<<( const BSONField<T>& f ) { _s.endField( f.name().c_str() ); return _s; - } + } template<typename T> BSONObjBuilder& operator<<( const BSONFieldValue<T>& v ) { append( v.name().c_str() , v.value() ); return *this; - } - + } + /** @return true if we are using our own bufbuilder, and not an alternate that was given to us in our constructor */ bool owned() const { return &_b == &_buf; } BSONObjIterator iterator() const ; - + + bool hasField( const StringData& name ) const ; + + int len() const { return _b.len(); } + private: char* _done() { if ( _doneCalled ) return _b.buf() + _offset; - + _doneCalled = true; _s.endField(); _b.appendNum((char) EOO); @@ -635,82 +653,89 @@ namespace mongo { public: BSONArrayBuilder() : _i(0), _b() {} BSONArrayBuilder( BufBuilder &_b ) : _i(0), _b(_b) {} + BSONArrayBuilder( int initialSize ) : _i(0), _b(initialSize) {} template <typename T> - BSONArrayBuilder& append(const T& x){ - _b.append(num().c_str(), x); + BSONArrayBuilder& append(const T& x) { + _b.append(num(), x); return *this; } - BSONArrayBuilder& append(const BSONElement& e){ + BSONArrayBuilder& append(const BSONElement& e) { _b.appendAs(e, num()); return *this; } - + template <typename T> - BSONArrayBuilder& operator<<(const T& x){ + BSONArrayBuilder& operator<<(const T& x) { return append(x); } - + void appendNull() { - _b.appendNull(num().c_str()); + _b.appendNull(num()); } - BSONArray arr(){ return BSONArray(_b.obj()); } - + /** + * destructive - ownership moves to returned BSONArray + * @return owned BSONArray + */ + BSONArray arr() { return BSONArray(_b.obj()); } + BSONObj done() { return _b.done(); } - + void doneFast() { _b.doneFast(); } - + template <typename T> - BSONArrayBuilder& append(const StringData& name, const T& x){ + BSONArrayBuilder& append(const StringData& name, const T& x) { fill( name ); append( x ); return *this; } - - BufBuilder &subobjStart( const char *name = "0" ) { + + BufBuilder &subobjStart( const StringData& name = "0" ) { fill( name ); - return _b.subobjStart( num().c_str() ); + return _b.subobjStart( num() ); } BufBuilder &subarrayStart( const char *name ) { fill( name ); - return _b.subarrayStart( num().c_str() ); + return _b.subarrayStart( num() ); } - + void appendArray( const StringData& name, BSONObj subObj ) { fill( name ); - _b.appendArray( num().c_str(), subObj ); + _b.appendArray( num(), subObj ); } - - void appendAs( const BSONElement &e, const char *name ) { + + void appendAs( const BSONElement &e, const char *name) { fill( name ); append( e ); } - + + int len() const { return _b.len(); } + private: void fill( const StringData& name ) { char *r; - int n = strtol( name.data(), &r, 10 ); + long int n = strtol( name.data(), &r, 10 ); if ( *r ) uasserted( 13048, (string)"can't append to array using string field name [" + name.data() + "]" ); while( _i < n ) append( nullElt() ); } - + static BSONElement nullElt() { static BSONObj n = nullObj(); return n.firstElement(); } - + static BSONObj nullObj() { BSONObjBuilder _b; _b.appendNull( "" ); return _b.obj(); } - - string num(){ return _b.numStr(_i++); } + + string num() { return _b.numStr(_i++); } int _i; BSONObjBuilder _b; }; @@ -736,14 +761,14 @@ namespace mongo { // $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT 6)); inline BSONObj OR(const BSONObj& a, const BSONObj& b) - { return BSON( "$or" << BSON_ARRAY(a << b) ); } + { return BSON( "$or" << BSON_ARRAY(a << b) ); } inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c) - { return BSON( "$or" << BSON_ARRAY(a << b << c) ); } + { return BSON( "$or" << BSON_ARRAY(a << b << c) ); } inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d) - { return BSON( "$or" << BSON_ARRAY(a << b << c << d) ); } + { return BSON( "$or" << BSON_ARRAY(a << b << c << d) ); } inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e) - { return BSON( "$or" << BSON_ARRAY(a << b << c << d << e) ); } + { return BSON( "$or" << BSON_ARRAY(a << b << c << d << e) ); } inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e, const BSONObj& f) - { return BSON( "$or" << BSON_ARRAY(a << b << c << d << e << f) ); } - + { return BSON( "$or" << BSON_ARRAY(a << b << c << d << e << f) ); } + } diff --git a/bson/bsonobjiterator.h b/bson/bsonobjiterator.h index c8224d2..6e6a69e 100644 --- a/bson/bsonobjiterator.h +++ b/bson/bsonobjiterator.h @@ -20,6 +20,7 @@ #include <boost/preprocessor/cat.hpp> // like the ## operator but works with __LINE__ namespace mongo { + /** iterator for a BSONObj Note each BSONObj ends with an EOO element: so you will get more() on an empty @@ -30,7 +31,7 @@ namespace mongo { */ class BSONObjIterator { public: - /** Create an iterator for a BSON object. + /** Create an iterator for a BSON object. */ BSONObjIterator(const BSONObj& jso) { int sz = jso.objsize(); @@ -42,18 +43,17 @@ namespace mongo { _theend = jso.objdata() + sz; } - BSONObjIterator( const char * start , const char * end ){ + BSONObjIterator( const char * start , const char * end ) { _pos = start + 4; _theend = end; } - + /** @return true if more elements exist to be enumerated. */ - bool moreWithEOO() { - return _pos < _theend; - } - bool more(){ - return _pos < _theend && _pos[0]; - } + bool more() { return _pos < _theend && _pos[0]; } + + /** @return true if more elements exist to be enumerated INCLUDING the EOO element which is always at the end. */ + bool moreWithEOO() { return _pos < _theend; } + /** @return the next element in the object. For the final element, element.eoo() will be true. */ BSONElement next( bool checkEnd = false ) { assert( _pos < _theend ); @@ -78,18 +78,18 @@ namespace mongo { class BSONObjIteratorSorted { public: BSONObjIteratorSorted( const BSONObj& o ); - - ~BSONObjIteratorSorted(){ + + ~BSONObjIteratorSorted() { assert( _fields ); delete[] _fields; _fields = 0; } - bool more(){ + bool more() { return _cur < _nfields; } - - BSONElement next(){ + + BSONElement next() { assert( _fields ); if ( _cur < _nfields ) return BSONElement( _fields[_cur++] ); @@ -102,30 +102,30 @@ namespace mongo { int _cur; }; -/** Similar to BOOST_FOREACH - * - * because the iterator is defined outside of the for, you must use {} around - * the surrounding scope. Don't do this: - * - * if (foo) - * BSONForEach(e, obj) - * doSomething(e); - * - * but this is OK: - * - * if (foo) { - * BSONForEach(e, obj) - * doSomething(e); - * } - * - */ + /** Similar to BOOST_FOREACH + * + * because the iterator is defined outside of the for, you must use {} around + * the surrounding scope. Don't do this: + * + * if (foo) + * BSONForEach(e, obj) + * doSomething(e); + * + * but this is OK: + * + * if (foo) { + * BSONForEach(e, obj) + * doSomething(e); + * } + * + */ #define BSONForEach(e, obj) \ BSONObjIterator BOOST_PP_CAT(it_,__LINE__)(obj); \ for ( BSONElement e; \ - (BOOST_PP_CAT(it_,__LINE__).more() ? \ - (e = BOOST_PP_CAT(it_,__LINE__).next(), true) : \ - false) ; \ - /*nothing*/ ) + (BOOST_PP_CAT(it_,__LINE__).more() ? \ + (e = BOOST_PP_CAT(it_,__LINE__).next(), true) : \ + false) ; \ + /*nothing*/ ) } diff --git a/bson/bsontypes.h b/bson/bsontypes.h index 27f2aaf..9d95e8e 100644 --- a/bson/bsontypes.h +++ b/bson/bsontypes.h @@ -39,69 +39,69 @@ namespace mongo { extern BSONObj maxKey; extern BSONObj minKey; -/** - the complete list of valid BSON types - see also bsonspec.org -*/ -enum BSONType { - /** smaller than all other types */ - MinKey=-1, - /** end of object */ - EOO=0, - /** double precision floating point value */ - NumberDouble=1, - /** character string, stored in utf8 */ - String=2, - /** an embedded object */ - Object=3, - /** an embedded array */ - Array=4, - /** binary data */ - BinData=5, - /** Undefined type */ - Undefined=6, - /** ObjectId */ - jstOID=7, - /** boolean type */ - Bool=8, - /** date type */ - Date=9, - /** null type */ - jstNULL=10, - /** regular expression, a pattern with options */ - RegEx=11, - /** deprecated / will be redesigned */ - DBRef=12, - /** deprecated / use CodeWScope */ - Code=13, - /** a programming language (e.g., Python) symbol */ - Symbol=14, - /** javascript code that can execute on the database server, with SavedContext */ - CodeWScope=15, - /** 32 bit signed integer */ - NumberInt = 16, - /** Updated to a Date with value next OpTime on insert */ - Timestamp = 17, - /** 64 bit integer */ - NumberLong = 18, - /** max type that is not MaxKey */ - JSTypeMax=18, - /** larger than all other types */ - MaxKey=127 -}; + /** + the complete list of valid BSON types + see also bsonspec.org + */ + enum BSONType { + /** smaller than all other types */ + MinKey=-1, + /** end of object */ + EOO=0, + /** double precision floating point value */ + NumberDouble=1, + /** character string, stored in utf8 */ + String=2, + /** an embedded object */ + Object=3, + /** an embedded array */ + Array=4, + /** binary data */ + BinData=5, + /** Undefined type */ + Undefined=6, + /** ObjectId */ + jstOID=7, + /** boolean type */ + Bool=8, + /** date type */ + Date=9, + /** null type */ + jstNULL=10, + /** regular expression, a pattern with options */ + RegEx=11, + /** deprecated / will be redesigned */ + DBRef=12, + /** deprecated / use CodeWScope */ + Code=13, + /** a programming language (e.g., Python) symbol */ + Symbol=14, + /** javascript code that can execute on the database server, with SavedContext */ + CodeWScope=15, + /** 32 bit signed integer */ + NumberInt = 16, + /** Updated to a Date with value next OpTime on insert */ + Timestamp = 17, + /** 64 bit integer */ + NumberLong = 18, + /** max type that is not MaxKey */ + JSTypeMax=18, + /** larger than all other types */ + MaxKey=127 + }; /* subtypes of BinData. bdtCustom and above are ones that the JS compiler understands, but are opaque to the database. */ - enum BinDataType { + enum BinDataType { BinDataGeneral=0, - Function=1, + Function=1, ByteArrayDeprecated=2, /* use BinGeneral instead */ - bdtUUID = 3, - MD5Type=5, - bdtCustom=128 + bdtUUID = 3, + MD5Type=5, + bdtCustom=128 }; - + } diff --git a/bson/inline_decls.h b/bson/inline_decls.h index aab9810..1605611 100644 --- a/bson/inline_decls.h +++ b/bson/inline_decls.h @@ -26,7 +26,7 @@ #define NOINLINE_DECL __declspec(noinline) -#else +#else #define NOINLINE_DECL diff --git a/bson/oid.cpp b/bson/oid.cpp new file mode 100644 index 0000000..6aa0730 --- /dev/null +++ b/bson/oid.cpp @@ -0,0 +1,154 @@ +// @file oid.cpp + +/* 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. + */ + +#include "pch.h" +#include "oid.h" +#include "util/atomic_int.h" +#include "../db/nonce.h" + +BOOST_STATIC_ASSERT( sizeof(mongo::OID) == 12 ); + +namespace mongo { + + // machine # before folding in the process id + OID::MachineAndPid OID::ourMachine; + + unsigned OID::ourPid() { + unsigned pid; +#if defined(_WIN32) + pid = (unsigned short) GetCurrentProcessId(); +#elif defined(__linux__) || defined(__APPLE__) || defined(__sunos__) + pid = (unsigned short) getpid(); +#else + pid = (unsigned short) security.getNonce(); +#endif + return pid; + } + + void OID::foldInPid(OID::MachineAndPid& x) { + unsigned p = ourPid(); + x._pid ^= (unsigned short) p; + // when the pid is greater than 16 bits, let the high bits modulate the machine id field. + unsigned short& rest = (unsigned short &) x._machineNumber[1]; + rest ^= p >> 16; + } + + OID::MachineAndPid OID::genMachineAndPid() { + BOOST_STATIC_ASSERT( sizeof(mongo::OID::MachineAndPid) == 5 ); + + // this is not called often, so the following is not expensive, and gives us some + // testing that nonce generation is working right and that our OIDs are (perhaps) ok. + { + nonce a = security.getNonce(); + nonce b = security.getNonce(); + nonce c = security.getNonce(); + assert( !(a==b && b==c) ); + } + + unsigned long long n = security.getNonce(); + OID::MachineAndPid x = ourMachine = (OID::MachineAndPid&) n; + foldInPid(x); + return x; + } + + // after folding in the process id + OID::MachineAndPid OID::ourMachineAndPid = OID::genMachineAndPid(); + + void OID::regenMachineId() { + ourMachineAndPid = genMachineAndPid(); + } + + inline bool OID::MachineAndPid::operator!=(const OID::MachineAndPid& rhs) const { + return _pid != rhs._pid || _machineNumber != rhs._machineNumber; + } + + unsigned OID::getMachineId() { + unsigned char x[4]; + x[0] = ourMachineAndPid._machineNumber[0]; + x[1] = ourMachineAndPid._machineNumber[1]; + x[2] = ourMachineAndPid._machineNumber[2]; + x[3] = 0; + return (unsigned&) x[0]; + } + + void OID::justForked() { + MachineAndPid x = ourMachine; + // we let the random # for machine go into all 5 bytes of MachineAndPid, and then + // xor in the pid into _pid. this reduces the probability of collisions. + foldInPid(x); + ourMachineAndPid = genMachineAndPid(); + assert( x != ourMachineAndPid ); + ourMachineAndPid = x; + } + + void OID::init() { + static AtomicUInt inc = (unsigned) security.getNonce(); + + { + unsigned t = (unsigned) time(0); + unsigned char *T = (unsigned char *) &t; + _time[0] = T[3]; // big endian order because we use memcmp() to compare OID's + _time[1] = T[2]; + _time[2] = T[1]; + _time[3] = T[0]; + } + + _machineAndPid = ourMachineAndPid; + + { + int new_inc = inc++; + unsigned char *T = (unsigned char *) &new_inc; + _inc[0] = T[2]; + _inc[1] = T[1]; + _inc[2] = T[0]; + } + } + + void OID::init( string s ) { + assert( s.size() == 24 ); + const char *p = s.c_str(); + for( int i = 0; i < 12; i++ ) { + data[i] = fromHex(p); + p += 2; + } + } + + void OID::init(Date_t date, bool max) { + int time = (int) (date / 1000); + char* T = (char *) &time; + data[0] = T[3]; + data[1] = T[2]; + data[2] = T[1]; + data[3] = T[0]; + + if (max) + *(long long*)(data + 4) = 0xFFFFFFFFFFFFFFFFll; + else + *(long long*)(data + 4) = 0x0000000000000000ll; + } + + time_t OID::asTimeT() { + int time; + char* T = (char *) &time; + T[0] = data[3]; + T[1] = data[2]; + T[2] = data[1]; + T[3] = data[0]; + return time; + } + +} @@ -22,56 +22,48 @@ namespace mongo { #pragma pack(1) - /** Object ID type. - BSON objects typically have an _id field for the object id. This field should be the first - member of the object when present. class OID is a special type that is a 12 byte id which + /** Object ID type. + BSON objects typically have an _id field for the object id. This field should be the first + member of the object when present. class OID is a special type that is a 12 byte id which is likely to be unique to the system. You may also use other types for _id's. - When _id field is missing from a BSON object, on an insert the database may insert one + When _id field is missing from a BSON object, on an insert the database may insert one automatically in certain circumstances. Warning: You must call OID::newState() after a fork(). + + Typical contents of the BSON ObjectID is a 12-byte value consisting of a 4-byte timestamp (seconds since epoch), + a 3-byte machine id, a 2-byte process id, and a 3-byte counter. Note that the timestamp and counter fields must + be stored big endian unlike the rest of BSON. This is because they are compared byte-by-byte and we want to ensure + a mostly increasing order. */ class OID { - union { - struct{ - long long a; - unsigned b; - }; - unsigned char data[12]; - }; - static unsigned _machine; public: - /** call this after a fork */ - static void newState(); + OID() : a(0), b(0) { } - /** initialize to 'null' */ - void clear() { a = 0; b = 0; } + /** init from a 24 char hex string */ + explicit OID(const string &s) { init(s); } - const unsigned char *getData() const { return data; } + /** initialize to 'null' */ + void clear() { a = 0; b = 0; } - bool operator==(const OID& r) { - return a==r.a&&b==r.b; - } - bool operator!=(const OID& r) { - return a!=r.a||b!=r.b; - } + const unsigned char *getData() const { return data; } - /** The object ID output as 24 hex digits. */ - string str() const { - return toHexLower(data, 12); - } + bool operator==(const OID& r) const { return a==r.a && b==r.b; } + bool operator!=(const OID& r) const { return a!=r.a || b!=r.b; } + int compare( const OID& other ) const { return memcmp( data , other.data , 12 ); } + bool operator<( const OID& other ) const { return compare( other ) < 0; } + bool operator<=( const OID& other ) const { return compare( other ) <= 0; } + /** @return the object ID output as 24 hex digits */ + string str() const { return toHexLower(data, 12); } string toString() const { return str(); } static OID gen() { OID o; o.init(); return o; } - - static unsigned staticMachine(){ return _machine; } - /** - sets the contents to a new oid / randomized value - */ + + /** sets the contents to a new oid / randomized value */ void init(); - /** Set to the hex string value specified. */ + /** init from a 24 char hex string */ void init( string s ); /** Set to the min/max OID that could be generated at given timestamp. */ @@ -79,12 +71,39 @@ namespace mongo { time_t asTimeT(); Date_t asDateT() { return asTimeT() * (long long)1000; } - + bool isSet() const { return a || b; } - - int compare( const OID& other ) const { return memcmp( data , other.data , 12 ); } - - bool operator<( const OID& other ) const { return compare( other ) < 0; } + + /** call this after a fork to update the process id */ + static void justForked(); + + static unsigned getMachineId(); // features command uses + static void regenMachineId(); // used by unit tests + + private: + struct MachineAndPid { + unsigned char _machineNumber[3]; + unsigned short _pid; + bool operator!=(const OID::MachineAndPid& rhs) const; + }; + static MachineAndPid ourMachine, ourMachineAndPid; + union { + struct { + // 12 bytes total + unsigned char _time[4]; + MachineAndPid _machineAndPid; + unsigned char _inc[3]; + }; + struct { + long long a; + unsigned b; + }; + unsigned char data[12]; + }; + + static unsigned ourPid(); + static void foldInPid(MachineAndPid& x); + static MachineAndPid genMachineAndPid(); }; #pragma pack() diff --git a/bson/ordering.h b/bson/ordering.h index fbbfbec..749e20d 100644 --- a/bson/ordering.h +++ b/bson/ordering.h @@ -23,7 +23,7 @@ namespace mongo { The constructor is private to make conversion more explicit so we notice where we call make(). Over time we should push this up higher and higher. */ - class Ordering { + class Ordering { const unsigned bits; const unsigned nkeys; Ordering(unsigned b,unsigned n) : bits(b),nkeys(n) { } @@ -32,13 +32,13 @@ namespace mongo { get(0) == 1 get(1) == -1 */ - int get(int i) const { + int get(int i) const { return ((1 << i) & bits) ? -1 : 1; } // for woCompare... unsigned descending(unsigned mask) const { return bits & mask; } - + operator string() const { StringBuilder buf(32); for ( unsigned i=0; i<nkeys; i++) @@ -50,7 +50,7 @@ namespace mongo { unsigned b = 0; BSONObjIterator k(obj); unsigned n = 0; - while( 1 ) { + while( 1 ) { BSONElement e = k.next(); if( e.eoo() ) break; @@ -62,5 +62,5 @@ namespace mongo { return Ordering(b,n); } }; - + } diff --git a/bson/stringdata.h b/bson/stringdata.h index ccf30f7..46cdb7a 100644 --- a/bson/stringdata.h +++ b/bson/stringdata.h @@ -25,36 +25,49 @@ namespace mongo { using std::string; + // A StringData object wraps a 'const string&' or a 'const char*' without + // copying its contents. The most common usage is as a function argument that + // takes any of the two forms of strings above. Fundamentally, this class tries + // go around the fact that string literals in C++ are char[N]'s. + // + // Note that the object StringData wraps around must be alive while the StringDAta + // is. + class StringData { public: - StringData( const char* c ) + // Construct a StringData explicilty, for the case where the lenght of + // string is not known. 'c' must be a pointer to a null-terminated string. + StringData( const char* c ) : _data(c), _size((unsigned) strlen(c)) {} + // Construct a StringData explicitly, for the case where the length of the string + // is already known. 'c' must be a pointer to a null-terminated string, and strlenOfc + // must be the length that std::strlen(c) would return, a.k.a the index of the + // terminator in c. + StringData( const char* c, size_t strlenOfc ) + : _data(c), _size((unsigned) strlenOfc) {} + + // Construct a StringData explicitly, for the case of a std::string. StringData( const string& s ) : _data(s.c_str()), _size((unsigned) s.size()) {} - + + // Construct a StringData explicitly, for the case of a literal whose size is + // known at compile time. struct LiteralTag {}; template<size_t N> StringData( const char (&val)[N], LiteralTag ) : _data(&val[0]), _size(N-1) {} - // Construct a StringData explicitly, for the case where the - // length of the string is already known. 'c' must be a - // pointer to a null-terminated string, and strlenOfc must be - // the length that std::strlen(c) would return, a.k.a the - // index of the terminator in c. - StringData( const char* c, size_t strlenOfc ) - : _data(c), _size((unsigned) strlenOfc) {} + // accessors const char* const data() const { return _data; } const unsigned size() const { return _size; } private: - // TODO - Hook this class up in the BSON machinery - // There are two assumptions here that we may want to review then. + // There are two assumptions we use bellow. // '_data' *always* finishes with a null terminator // 'size' does *not* account for the null terminator - // These assumptions may make it easier to minimize changes to existing code + // These assumptions may make it easier to minimize changes to existing code. const char* const _data; const unsigned _size; }; diff --git a/bson/util/atomic_int.h b/bson/util/atomic_int.h index f4d2749..1573552 100644 --- a/bson/util/atomic_int.h +++ b/bson/util/atomic_int.h @@ -24,51 +24,55 @@ namespace mongo { - struct AtomicUInt{ + struct AtomicUInt { AtomicUInt() : x(0) {} AtomicUInt(unsigned z) : x(z) { } - volatile unsigned x; - operator unsigned() const { - return x; - } + + operator unsigned() const { return x; } + unsigned get() const { return x; } + inline AtomicUInt operator++(); // ++prefix inline AtomicUInt operator++(int);// postfix++ inline AtomicUInt operator--(); // --prefix inline AtomicUInt operator--(int); // postfix-- + + inline void zero() { x = 0; } // TODO: this isn't thread safe + + volatile unsigned x; }; #if defined(_WIN32) - AtomicUInt AtomicUInt::operator++(){ + AtomicUInt AtomicUInt::operator++() { // InterlockedIncrement returns the new value return InterlockedIncrement((volatile long*)&x); //long is 32bits in Win64 } - AtomicUInt AtomicUInt::operator++(int){ + AtomicUInt AtomicUInt::operator++(int) { return InterlockedIncrement((volatile long*)&x)-1; } - AtomicUInt AtomicUInt::operator--(){ + AtomicUInt AtomicUInt::operator--() { return InterlockedDecrement((volatile long*)&x); } - AtomicUInt AtomicUInt::operator--(int){ + AtomicUInt AtomicUInt::operator--(int) { return InterlockedDecrement((volatile long*)&x)+1; } #elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) // this is in GCC >= 4.1 - AtomicUInt AtomicUInt::operator++(){ + AtomicUInt AtomicUInt::operator++() { return __sync_add_and_fetch(&x, 1); } - AtomicUInt AtomicUInt::operator++(int){ + AtomicUInt AtomicUInt::operator++(int) { return __sync_fetch_and_add(&x, 1); } - AtomicUInt AtomicUInt::operator--(){ + AtomicUInt AtomicUInt::operator--() { return __sync_add_and_fetch(&x, -1); } - AtomicUInt AtomicUInt::operator--(int){ + AtomicUInt AtomicUInt::operator--(int) { return __sync_fetch_and_add(&x, -1); } #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) // from boost 1.39 interprocess/detail/atomic.hpp - inline unsigned atomic_int_helper(volatile unsigned *x, int val){ + inline unsigned atomic_int_helper(volatile unsigned *x, int val) { int r; asm volatile ( @@ -80,16 +84,16 @@ namespace mongo { ); return r; } - AtomicUInt AtomicUInt::operator++(){ + AtomicUInt AtomicUInt::operator++() { return atomic_int_helper(&x, 1)+1; } - AtomicUInt AtomicUInt::operator++(int){ + AtomicUInt AtomicUInt::operator++(int) { return atomic_int_helper(&x, 1); } - AtomicUInt AtomicUInt::operator--(){ + AtomicUInt AtomicUInt::operator--() { return atomic_int_helper(&x, -1)-1; } - AtomicUInt AtomicUInt::operator--(int){ + AtomicUInt AtomicUInt::operator--(int) { return atomic_int_helper(&x, -1); } #else diff --git a/bson/util/builder.h b/bson/util/builder.h index 9d9eda2..6f4ff9e 100644 --- a/bson/util/builder.h +++ b/bson/util/builder.h @@ -27,6 +27,24 @@ namespace mongo { + /* Note the limit here is rather arbitrary and is simply a standard. generally the code works + with any object that fits in ram. + + Also note that the server has some basic checks to enforce this limit but those checks are not exhaustive + for example need to check for size too big after + update $push (append) operation + various db.eval() type operations + */ + const int BSONObjMaxUserSize = 16 * 1024 * 1024; + + /* + Sometimeswe we need objects slightly larger - an object in the replication local.oplog + is slightly larger than a user object for example. + */ + const int BSONObjMaxInternalSize = BSONObjMaxUserSize + ( 16 * 1024 ); + + const int BufferMaxSize = 64 * 1024 * 1024; + class StringBuilder; void msgasserted(int msgid, const char *msg); @@ -38,7 +56,8 @@ namespace mongo { data = (char *) malloc(size); if( data == 0 ) msgasserted(10000, "out of memory BufBuilder"); - } else { + } + else { data = 0; } l = 0; @@ -54,16 +73,18 @@ namespace mongo { } } - void reset( int maxSize = 0 ){ + void reset( int maxSize = 0 ) { l = 0; - if ( maxSize && size > maxSize ){ + if ( maxSize && size > maxSize ) { free(data); data = (char*)malloc(maxSize); size = maxSize; - } + } } - /* leave room for some stuff later */ + /** leave room for some stuff later + @return point to region that was skipped. pointer may change later (on realloc), so for immediate use only + */ char* skip(int n) { return grow(n); } /* note this may be deallocated (realloced) if you keep writing. */ @@ -73,10 +94,10 @@ namespace mongo { /* assume ownership of the buffer - you must then free() it */ void decouple() { data = 0; } - void appendChar(char j){ + void appendChar(char j) { *((char*)grow(sizeof(char))) = j; } - void appendNum(char j){ + void appendNum(char j) { *((char*)grow(sizeof(char))) = j; } void appendNum(short j) { @@ -105,18 +126,19 @@ namespace mongo { memcpy(grow((int) len), src, len); } + template<class T> + void appendStruct(const T& s) { + appendBuf(&s, sizeof(T)); + } + void appendStr(const StringData &str , bool includeEOO = true ) { const int len = str.size() + ( includeEOO ? 1 : 0 ); memcpy(grow(len), str.data(), len); } - int len() const { - return l; - } - - void setlen( int newLen ){ - l = newLen; - } + int len() const { return l; } + void setlen( int newLen ) { l = newLen; } + int getSize() const { return size; } /* returns the pre-grow write position */ inline char* grow(int by) { @@ -128,18 +150,16 @@ namespace mongo { return data + oldlen; } - int getSize() const { return size; } - private: /* "slow" portion of 'grow()' */ - void NOINLINE_DECL grow_reallocate(){ + void NOINLINE_DECL grow_reallocate() { int a = size * 2; if ( a == 0 ) a = 512; if ( l > a ) a = l + 16 * 1024; - if( a > 64 * 1024 * 1024 ) - msgasserted(10000, "BufBuilder grow() > 64MB"); + if ( a > BufferMaxSize ) + msgasserted(13548, "BufBuilder grow() > 64MB"); data = (char *) realloc(data, a); size= a; } @@ -152,87 +172,90 @@ namespace mongo { }; #if defined(_WIN32) +#pragma warning( push ) +// warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. #pragma warning( disable : 4996 ) #endif + /** stringstream deals with locale so this is a lot faster than std::stringstream for UTF8 */ class StringBuilder { public: StringBuilder( int initsize=256 ) - : _buf( initsize ){ + : _buf( initsize ) { } -#define SBNUM(val,maxSize,macro) \ - int prev = _buf.l; \ - int z = sprintf( _buf.grow(maxSize) , macro , (val) ); \ - assert( z >= 0 ); \ - _buf.l = prev + z; \ - return *this; - - StringBuilder& operator<<( double x ){ - SBNUM( x , 25 , "%g" ); + StringBuilder& operator<<( double x ) { + return SBNUM( x , 25 , "%g" ); } - StringBuilder& operator<<( int x ){ - SBNUM( x , 11 , "%d" ); + StringBuilder& operator<<( int x ) { + return SBNUM( x , 11 , "%d" ); } - StringBuilder& operator<<( unsigned x ){ - SBNUM( x , 11 , "%u" ); + StringBuilder& operator<<( unsigned x ) { + return SBNUM( x , 11 , "%u" ); } - StringBuilder& operator<<( long x ){ - SBNUM( x , 22 , "%ld" ); + StringBuilder& operator<<( long x ) { + return SBNUM( x , 22 , "%ld" ); } - StringBuilder& operator<<( unsigned long x ){ - SBNUM( x , 22 , "%lu" ); + StringBuilder& operator<<( unsigned long x ) { + return SBNUM( x , 22 , "%lu" ); } - StringBuilder& operator<<( long long x ){ - SBNUM( x , 22 , "%lld" ); + StringBuilder& operator<<( long long x ) { + return SBNUM( x , 22 , "%lld" ); } - StringBuilder& operator<<( unsigned long long x ){ - SBNUM( x , 22 , "%llu" ); + StringBuilder& operator<<( unsigned long long x ) { + return SBNUM( x , 22 , "%llu" ); } - StringBuilder& operator<<( short x ){ - SBNUM( x , 8 , "%hd" ); + StringBuilder& operator<<( short x ) { + return SBNUM( x , 8 , "%hd" ); } - StringBuilder& operator<<( char c ){ + StringBuilder& operator<<( char c ) { _buf.grow( 1 )[0] = c; return *this; } -#undef SBNUM - void appendDoubleNice( double x ){ + void appendDoubleNice( double x ) { int prev = _buf.l; char * start = _buf.grow( 32 ); int z = sprintf( start , "%.16g" , x ); assert( z >= 0 ); _buf.l = prev + z; - if( strchr(start, '.') == 0 && strchr(start, 'E') == 0 && strchr(start, 'N') == 0 ){ + if( strchr(start, '.') == 0 && strchr(start, 'E') == 0 && strchr(start, 'N') == 0 ) { write( ".0" , 2 ); } } - void write( const char* buf, int len){ - memcpy( _buf.grow( len ) , buf , len ); - } + void write( const char* buf, int len) { memcpy( _buf.grow( len ) , buf , len ); } - void append( const StringData& str ){ - memcpy( _buf.grow( str.size() ) , str.data() , str.size() ); - } - StringBuilder& operator<<( const StringData& str ){ + void append( const StringData& str ) { memcpy( _buf.grow( str.size() ) , str.data() , str.size() ); } + + StringBuilder& operator<<( const StringData& str ) { append( str ); return *this; } - - // access - void reset( int maxSize = 0 ){ - _buf.reset( maxSize ); - } - - std::string str(){ - return std::string(_buf.data, _buf.l); - } + void reset( int maxSize = 0 ) { _buf.reset( maxSize ); } + + std::string str() const { return std::string(_buf.data, _buf.l); } private: BufBuilder _buf; + + // non-copyable, non-assignable + StringBuilder( const StringBuilder& ); + StringBuilder& operator=( const StringBuilder& ); + + template <typename T> + StringBuilder& SBNUM(T val,int maxSize,const char *macro) { + int prev = _buf.l; + int z = sprintf( _buf.grow(maxSize) , macro , (val) ); + assert( z >= 0 ); + _buf.l = prev + z; + return *this; + } }; +#if defined(_WIN32) +#pragma warning( pop ) +#endif + } // namespace mongo diff --git a/bson/util/misc.h b/bson/util/misc.h index cad9a28..b31f36f 100644 --- a/bson/util/misc.h +++ b/bson/util/misc.h @@ -34,7 +34,7 @@ namespace mongo { buf[24] = 0; // don't want the \n } - inline string time_t_to_String(time_t t = time(0) ){ + inline string time_t_to_String(time_t t = time(0) ) { char buf[64]; #if defined(_WIN32) ctime_s(buf, sizeof(buf), &t); @@ -76,7 +76,7 @@ namespace mongo { Date_t(unsigned long long m): millis(m) {} operator unsigned long long&() { return millis; } operator const unsigned long long&() const { return millis; } - string toString() const { + string toString() const { char buf[64]; time_t_to_String(millis/1000, buf); return buf; |