diff options
Diffstat (limited to 'src')
35 files changed, 992 insertions, 1074 deletions
diff --git a/src/mongo/bson/bson-inl.h b/src/mongo/bson/bson-inl.h deleted file mode 100644 index 8925192de27..00000000000 --- a/src/mongo/bson/bson-inl.h +++ /dev/null @@ -1,1010 +0,0 @@ -/** @file bsoninlines.h - a goal here is that the most common bson methods can be used inline-only, a la boost. - thus some things are inline that wouldn't necessarily be otherwise. -*/ - -/* Copyright 2009 10gen Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the GNU Affero General Public License in all respects - * for all of the code used other than as permitted herein. If you modify - * file(s) with this exception, you may extend this exception to your - * version of the file(s), but you are not obligated to do so. If you do not - * wish to do so, delete this exception statement from your version. If you - * delete this exception statement from all source files in the program, - * then also delete it in the license file. - */ - -#pragma once - -#include <map> -#include <limits> - -namespace mongo { - - /* must be same type when called, unless both sides are #s - this large function is in header to facilitate inline-only use of bson - */ - inline int compareElementValues(const BSONElement& l, const BSONElement& r) { - int f; - - switch ( l.type() ) { - case EOO: - case Undefined: // EOO and Undefined are same canonicalType - case jstNULL: - case MaxKey: - case MinKey: - f = l.canonicalType() - r.canonicalType(); - if ( f<0 ) return -1; - return f==0 ? 0 : 1; - case Bool: - return *l.value() - *r.value(); - case Timestamp: - // unsigned compare for timestamps - note they are not really dates but (ordinal + time_t) - if ( l.date() < r.date() ) - return -1; - return l.date() == r.date() ? 0 : 1; - case Date: - { - long long a = (long long) l.Date().millis; - long long b = (long long) r.Date().millis; - if( a < b ) - return -1; - return a == b ? 0 : 1; - } - case NumberLong: - if( r.type() == NumberLong ) { - long long L = l._numberLong(); - long long R = r._numberLong(); - if( L < R ) return -1; - if( L == R ) return 0; - return 1; - } - goto dodouble; - case NumberInt: - if( r.type() == NumberInt ) { - int L = l._numberInt(); - int R = r._numberInt(); - if( L < R ) return -1; - return L == R ? 0 : 1; - } - // else fall through - case NumberDouble: -dodouble: - { - double left = l.number(); - double right = r.number(); - if( left < right ) - return -1; - if( left == right ) - return 0; - if( isNaN(left) ) - return isNaN(right) ? 0 : -1; - return 1; - } - case jstOID: - return memcmp(l.value(), r.value(), 12); - case Code: - case Symbol: - case String: - /* todo: a utf sort order version one day... */ - { - // we use memcmp as we allow zeros in UTF8 strings - int lsz = l.valuestrsize(); - int rsz = r.valuestrsize(); - int common = std::min(lsz, rsz); - int res = memcmp(l.valuestr(), r.valuestr(), common); - if( res ) - return res; - // longer std::string is the greater one - return lsz-rsz; - } - case Object: - case Array: - return l.embeddedObject().woCompare( r.embeddedObject() ); - case DBRef: { - int lsz = l.valuesize(); - int rsz = r.valuesize(); - if ( lsz - rsz != 0 ) return lsz - rsz; - return memcmp(l.value(), r.value(), lsz); - } - case BinData: { - int lsz = l.objsize(); // our bin data size in bytes, not including the subtype byte - int rsz = r.objsize(); - if ( lsz - rsz != 0 ) return lsz - rsz; - return memcmp(l.value()+4, r.value()+4, lsz+1 /*+1 for subtype byte*/); - } - case RegEx: { - int c = strcmp(l.regex(), r.regex()); - if ( c ) - return c; - return strcmp(l.regexFlags(), r.regexFlags()); - } - case CodeWScope : { - f = l.canonicalType() - r.canonicalType(); - if ( f ) - return f; - f = strcmp( l.codeWScopeCode() , r.codeWScopeCode() ); - if ( f ) - return f; - f = strcmp( l.codeWScopeScopeDataUnsafe() , r.codeWScopeScopeDataUnsafe() ); - if ( f ) - return f; - return 0; - } - default: - verify( false); - } - return -1; - } - - /* wo = "well ordered" - note: (mongodb related) : this can only change in behavior when index version # changes - */ - inline int BSONElement::woCompare( const BSONElement &e, - bool considerFieldName ) const { - int lt = (int) canonicalType(); - int rt = (int) e.canonicalType(); - int x = lt - rt; - if( x != 0 && (!isNumber() || !e.isNumber()) ) - return x; - if ( considerFieldName ) { - x = strcmp(fieldName(), e.fieldName()); - if ( x != 0 ) - return x; - } - x = compareElementValues(*this, e); - return x; - } - - inline BSONObjIterator BSONObj::begin() const { - return BSONObjIterator(*this); - } - - inline BSONObj BSONElement::embeddedObjectUserCheck() const { - if ( MONGO_likely(isABSONObj()) ) - return BSONObj(value()); - std::stringstream ss; - ss << "invalid parameter: expected an object (" << fieldName() << ")"; - uasserted( 10065 , ss.str() ); - return BSONObj(); // never reachable - } - - inline BSONObj BSONElement::embeddedObject() const { - verify( isABSONObj() ); - return BSONObj(value()); - } - - inline BSONObj BSONElement::codeWScopeObject() const { - verify( type() == CodeWScope ); - int strSizeWNull = *(int *)( value() + 4 ); - return BSONObj( value() + 4 + 4 + strSizeWNull ); - } - - // deep (full) equality - inline bool BSONObj::equal(const BSONObj &rhs) const { - BSONObjIterator i(*this); - BSONObjIterator j(rhs); - BSONElement l,r; - do { - // so far, equal... - l = i.next(); - r = j.next(); - if ( l.eoo() ) - return r.eoo(); - } while( l == r ); - return false; - } - - inline NOINLINE_DECL void BSONObj::_assertInvalid() const { - StringBuilder ss; - int os = objsize(); - ss << "BSONObj size: " << os << " (0x" << integerToHex( os ) << ") is invalid. " - << "Size must be between 0 and " << BSONObjMaxInternalSize - << "(" << ( BSONObjMaxInternalSize/(1024*1024) ) << "MB)"; - 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 { - Holder *h = (Holder*) malloc(objsize() + sizeof(unsigned)); - h->zero(); - memcpy(h->data, objdata(), objsize()); - return BSONObj(h); - } - - 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); - b.append(*this); - return b.obj(); - } - - inline BSONObj BSONElement::wrap( const StringData& newName ) const { - BSONObjBuilder b(size() + 6 + newName.size()); - b.appendAs(*this,newName); - return b.obj(); - } - - inline void BSONObj::getFields(unsigned n, const char **fieldNames, BSONElement *fields) const { - BSONObjIterator i(*this); - while ( i.more() ) { - BSONElement e = i.next(); - const char *p = e.fieldName(); - for( unsigned i = 0; i < n; i++ ) { - if( strcmp(p, fieldNames[i]) == 0 ) { - fields[i] = e; - break; - } - } - } - } - - inline BSONElement BSONObj::getField(const StringData& name) const { - BSONObjIterator i(*this); - while ( i.more() ) { - BSONElement e = i.next(); - if ( name == e.fieldName() ) - return e; - } - return BSONElement(); - } - - inline int BSONObj::getIntField(const StringData& name) const { - BSONElement e = getField(name); - return e.isNumber() ? (int) e.number() : std::numeric_limits< int >::min(); - } - - inline bool BSONObj::getBoolField(const StringData& name) const { - BSONElement e = getField(name); - return e.type() == Bool ? e.boolean() : false; - } - - inline const char * BSONObj::getStringField(const StringData& name) const { - BSONElement e = getField(name); - return e.type() == String ? e.valuestr() : ""; - } - - /* add all the fields from the object specified to this object */ - inline BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) { - if (!x.isEmpty()) - _b.appendBuf( - x.objdata() + 4, // skip over leading length - x.objsize() - 5); // ignore leading length and trailing \0 - return *this; - } - - /* add all the fields from the object specified to this object if they don't exist */ - inline BSONObjBuilder& BSONObjBuilder::appendElementsUnique(BSONObj x) { - std::set<std::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() const { - int x = objsize(); - return x > 0 && x <= BSONObjMaxInternalSize; - } - - inline bool BSONObj::getObjectID(BSONElement& e) const { - BSONElement f = getField("_id"); - if( !f.eoo() ) { - e = f; - return true; - } - return false; - } - - inline BSONObjBuilderValueStream::BSONObjBuilderValueStream( BSONObjBuilder * builder ) { - _builder = builder; - } - - template<class T> - inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<( T value ) { - _builder->append(_fieldName, value); - _fieldName = StringData(); - return *_builder; - } - - inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const BSONElement& e ) { - _builder->appendAs( e , _fieldName ); - _fieldName = StringData(); - return *_builder; - } - - inline BufBuilder& BSONObjBuilderValueStream::subobjStart() { - StringData tmp = _fieldName; - _fieldName = StringData(); - return _builder->subobjStart(tmp); - } - - inline BufBuilder& BSONObjBuilderValueStream::subarrayStart() { - StringData tmp = _fieldName; - _fieldName = StringData(); - return _builder->subarrayStart(tmp); - } - - inline Labeler BSONObjBuilderValueStream::operator<<( const Labeler::Label &l ) { - return Labeler( l, this ); - } - - inline void BSONObjBuilderValueStream::endField( const StringData& nextFieldName ) { - if ( haveSubobj() ) { - verify( _fieldName.rawData() ); - _builder->append( _fieldName, subobj()->done() ); - _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; - } - - inline BSONObjIterator BSONObjBuilder::iterator() const { - const char * s = _b.buf() + _offset; - 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 ( name == i.next().fieldName() ) - return true; - return false; - } - - typedef std::map<std::string, BSONElement> BSONMap; - inline BSONMap bson2map(const BSONObj& obj) { - BSONMap m; - BSONObjIterator it(obj); - while (it.more()) { - BSONElement e = it.next(); - m[e.fieldName()] = e; - } - return m; - } - - struct BSONElementFieldNameCmp { - bool operator()( const BSONElement &l, const BSONElement &r ) const { - return strcmp( l.fieldName() , r.fieldName() ) <= 0; - } - }; - - typedef std::set<BSONElement, BSONElementFieldNameCmp> BSONSortedElements; - inline BSONSortedElements bson2set( const BSONObj& obj ) { - BSONSortedElements s; - BSONObjIterator it(obj); - while ( it.more() ) - s.insert( it.next() ); - return s; - } - - inline std::string BSONObj::toString( bool isArray, bool full ) const { - if ( isEmpty() ) return (isArray ? "[]" : "{}"); - StringBuilder s; - toString(s, isArray, full); - return s.str(); - } - inline void BSONObj::toString( StringBuilder& s, bool isArray, bool full, int depth ) const { - if ( isEmpty() ) { - s << (isArray ? "[]" : "{}"); - return; - } - - s << ( isArray ? "[ " : "{ " ); - BSONObjIterator i(*this); - bool first = true; - while ( 1 ) { - massert( 10327 , "Object does not end with EOO", i.moreWithEOO() ); - BSONElement e = i.next( true ); - massert( 10328 , "Invalid element size", e.size() > 0 ); - 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() ); - bool end = ( e.size() + offset == this->objsize() ); - if ( e.eoo() ) { - massert( 10331 , "EOO Before end of object", end ); - break; - } - if ( first ) - first = false; - else - s << ", "; - e.toString( s, !isArray, full, depth ); - } - s << ( isArray ? " ]" : " }" ); - } - - inline int BSONElement::size( int maxLen ) const { - if ( totalSize >= 0 ) - return totalSize; - - int remain = maxLen - fieldNameSize() - 1; - - int x = 0; - switch ( type() ) { - case EOO: - case Undefined: - case jstNULL: - case MaxKey: - case MinKey: - break; - case mongo::Bool: - x = 1; - break; - case NumberInt: - x = 4; - break; - case Timestamp: - case mongo::Date: - case NumberDouble: - case NumberLong: - x = 8; - break; - case jstOID: - x = 12; - break; - case Symbol: - case Code: - case mongo::String: - massert( 10313 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); - x = valuestrsize() + 4; - break; - case CodeWScope: - massert( 10314 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); - x = objsize(); - break; - - case DBRef: - massert( 10315 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); - x = valuestrsize() + 4 + 12; - break; - case Object: - case mongo::Array: - massert( 10316 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); - x = objsize(); - break; - case BinData: - massert( 10317 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); - x = valuestrsize() + 4 + 1/*subtype*/; - break; - case RegEx: { - const char *p = value(); - size_t len1 = ( maxLen == -1 ) ? strlen( p ) : (size_t)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; - if( maxLen == -1 ) - len2 = strlen( p ); - else { - size_t x = remain - len1 - 1; - verify( 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); - } - break; - default: { - StringBuilder ss; - ss << "BSONElement: bad type " << (int) type(); - std::string msg = ss.str(); - massert( 13655 , msg.c_str(),false); - } - } - totalSize = x + fieldNameSize() + 1; // BSONType - - return totalSize; - } - - inline int BSONElement::size() const { - if ( totalSize >= 0 ) - return totalSize; - - int x = 0; - switch ( type() ) { - case EOO: - case Undefined: - case jstNULL: - case MaxKey: - case MinKey: - break; - case mongo::Bool: - x = 1; - break; - case NumberInt: - x = 4; - break; - case Timestamp: - case mongo::Date: - case NumberDouble: - case NumberLong: - x = 8; - break; - case jstOID: - x = 12; - break; - case Symbol: - case Code: - case mongo::String: - x = valuestrsize() + 4; - break; - case DBRef: - x = valuestrsize() + 4 + 12; - break; - case CodeWScope: - case Object: - case mongo::Array: - x = objsize(); - break; - case BinData: - x = valuestrsize() + 4 + 1/*subtype*/; - break; - case RegEx: - { - const char *p = value(); - size_t len1 = strlen(p); - p = p + len1 + 1; - size_t len2; - len2 = strlen( p ); - x = (int) (len1 + 1 + len2 + 1); - } - break; - default: - { - StringBuilder ss; - ss << "BSONElement: bad type " << (int) type(); - std::string msg = ss.str(); - massert(10320 , msg.c_str(),false); - } - } - totalSize = x + fieldNameSize() + 1; // BSONType - - return totalSize; - } - - inline std::string BSONElement::toString( bool includeFieldName, bool full ) const { - StringBuilder s; - toString(s, includeFieldName, full); - return s.str(); - } - inline void BSONElement::toString( StringBuilder& s, bool includeFieldName, bool full, int depth ) const { - - if ( depth > BSONObj::maxToStringRecursionDepth ) { - // check if we want the full/complete string - if ( full ) { - StringBuilder s; - s << "Reached maximum recursion depth of "; - s << BSONObj::maxToStringRecursionDepth; - uassert(16150, s.str(), full != true); - } - s << "..."; - return; - } - - if ( includeFieldName && type() != EOO ) - s << fieldName() << ": "; - switch ( type() ) { - case EOO: - s << "EOO"; - break; - case mongo::Date: - s << "new Date(" << (long long) date() << ')'; - break; - case RegEx: { - s << "/" << regex() << '/'; - const char *p = regexFlags(); - if ( p ) s << p; - } - break; - case NumberDouble: - s.appendDoubleNice( number() ); - break; - case NumberLong: - s << _numberLong(); - break; - case NumberInt: - s << _numberInt(); - break; - case mongo::Bool: - s << ( boolean() ? "true" : "false" ); - break; - case Object: - embeddedObject().toString(s, false, full, depth+1); - break; - case mongo::Array: - embeddedObject().toString(s, true, full, depth+1); - break; - case Undefined: - s << "undefined"; - break; - case jstNULL: - s << "null"; - break; - case MaxKey: - s << "MaxKey"; - break; - case MinKey: - s << "MinKey"; - break; - case CodeWScope: - s << "CodeWScope( " - << codeWScopeCode() << ", " << codeWScopeObject().toString(false, full) << ")"; - break; - case Code: - if ( !full && valuestrsize() > 80 ) { - s.write(valuestr(), 70); - s << "..."; - } - else { - s.write(valuestr(), valuestrsize()-1); - } - break; - case Symbol: - case mongo::String: - s << '"'; - if ( !full && valuestrsize() > 160 ) { - s.write(valuestr(), 150); - s << "...\""; - } - else { - s.write(valuestr(), valuestrsize()-1); - s << '"'; - } - break; - case DBRef: - s << "DBRef('" << valuestr() << "',"; - { - mongo::OID *x = (mongo::OID *) (valuestr() + valuestrsize()); - s << *x << ')'; - } - break; - case jstOID: - s << "ObjectId('"; - s << __oid() << "')"; - break; - case BinData: - s << "BinData(" << binDataType() << ", "; - { - int len; - const char *data = binDataClean(len); - if ( !full && len > 80 ) { - s << toHex(data, 70) << "...)"; - } - else { - s << toHex(data, len) << ")"; - } - } - break; - case Timestamp: - s << "Timestamp " << timestampTime() << "|" << timestampInc(); - break; - default: - s << "?type=" << type(); - break; - } - } - - /* return has eoo() true if no match - supports "." notation to reach into embedded objects - */ - inline BSONElement BSONObj::getFieldDotted(const StringData& name) const { - BSONElement e = getField(name); - if (e.eoo()) { - size_t dot_offset = name.find('.'); - if (dot_offset != std::string::npos) { - StringData left = name.substr(0, dot_offset); - StringData right = name.substr(dot_offset + 1); - BSONObj sub = getObjectField(left); - return sub.isEmpty() ? BSONElement() : sub.getFieldDotted(right); - } - } - - return e; - } - - inline BSONObj BSONObj::getObjectField(const StringData& name) const { - BSONElement e = getField(name); - BSONType t = e.type(); - return t == Object || t == Array ? e.embeddedObject() : BSONObj(); - } - - inline int BSONObj::nFields() const { - int n = 0; - BSONObjIterator i(*this); - while ( i.moreWithEOO() ) { - BSONElement e = i.next(); - if ( e.eoo() ) - break; - n++; - } - return n; - } - - inline BSONObj::BSONObj() { - /* 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 std::string& field) const { - BSONObj o = Obj(); - return o[field]; - } - - inline void BSONObj::elems(std::vector<BSONElement> &v) const { - BSONObjIterator i(*this); - while( i.more() ) - v.push_back(i.next()); - } - - inline void BSONObj::elems(std::list<BSONElement> &v) const { - BSONObjIterator i(*this); - while( i.more() ) - v.push_back(i.next()); - } - - template <class T> - void BSONObj::Vals(std::vector<T>& v) const { - BSONObjIterator i(*this); - while( i.more() ) { - T t; - i.next().Val(t); - v.push_back(t); - } - } - template <class T> - void BSONObj::Vals(std::list<T>& v) const { - BSONObjIterator i(*this); - while( i.more() ) { - T t; - i.next().Val(t); - v.push_back(t); - } - } - - template <class T> - void BSONObj::vals(std::vector<T>& v) const { - BSONObjIterator i(*this); - while( i.more() ) { - try { - T t; - i.next().Val(t); - v.push_back(t); - } - catch(...) { } - } - } - template <class T> - void BSONObj::vals(std::list<T>& v) const { - BSONObjIterator i(*this); - while( i.more() ) { - try { - T t; - i.next().Val(t); - v.push_back(t); - } - catch(...) { } - } - } - - inline std::ostream& operator<<( std::ostream &s, const BSONObj &o ) { - return s << o.toString(); - } - - inline std::ostream& operator<<( std::ostream &s, const BSONElement &e ) { - 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> - inline BSONFieldValue<BSONObj> BSONField<T>::query( const char * q , const T& t ) const { - BSONObjBuilder b; - b.append( q , t ); - return BSONFieldValue<BSONObj>( _name , b.obj() ); - } - - // used by jsonString() - inline std::string escape( const std::string& s , bool escape_slash=false) { - StringBuilder ret; - for ( std::string::const_iterator i = s.begin(); i != s.end(); ++i ) { - switch ( *i ) { - case '"': - ret << "\\\""; - break; - case '\\': - ret << "\\\\"; - break; - case '/': - ret << (escape_slash ? "\\/" : "/"); - break; - case '\b': - ret << "\\b"; - break; - case '\f': - ret << "\\f"; - break; - case '\n': - ret << "\\n"; - break; - case '\r': - ret << "\\r"; - break; - case '\t': - ret << "\\t"; - break; - default: - if ( *i >= 0 && *i <= 0x1f ) { - //TODO: these should be utf16 code-units not bytes - char c = *i; - ret << "\\u00" << toHexLower(&c, 1); - } - else { - ret << *i; - } - } - } - return ret.str(); - } - - inline std::string BSONObj::hexDump() const { - std::stringstream ss; - const char *d = objdata(); - int size = objsize(); - for( int i = 0; i < size; ++i ) { - ss.width( 2 ); - ss.fill( '0' ); - ss << std::hex << (unsigned)(unsigned char)( d[ i ] ) << std::dec; - if ( ( d[ i ] >= '0' && d[ i ] <= '9' ) || ( d[ i ] >= 'A' && d[ i ] <= 'z' ) ) - ss << '\'' << d[ i ] << '\''; - if ( i != size - 1 ) - ss << ' '; - } - return ss.str(); - } - - inline void BSONObjBuilder::appendKeys( const BSONObj& keyPattern , const BSONObj& values ) { - BSONObjIterator i(keyPattern); - BSONObjIterator j(values); - - while ( i.more() && j.more() ) { - appendAs( j.next() , i.next().fieldName() ); - } - - verify( ! i.more() ); - verify( ! j.more() ); - } - - inline BSONObj BSONObj::removeField(const StringData& name) const { - BSONObjBuilder b; - BSONObjIterator i(*this); - while ( i.more() ) { - BSONElement e = i.next(); - const char *fname = e.fieldName(); - if ( name != fname ) - b.append(e); - } - return b.obj(); - } - - template<typename T> bool BSONObj::coerceVector( std::vector<T>* out ) const { - BSONObjIterator i( *this ); - while ( i.more() ) { - BSONElement e = i.next(); - T t; - if ( ! e.coerce<T>( &t ) ) - return false; - out->push_back( t ); - } - return true; - } - - - template<> inline bool BSONElement::coerce<std::string>( std::string* out ) const { - if ( type() != mongo::String ) - return false; - *out = String(); - return true; - } - - template<> inline bool BSONElement::coerce<int>( int* out ) const { - if ( !isNumber() ) - return false; - *out = numberInt(); - return true; - } - - template<> inline bool BSONElement::coerce<double>( double* out ) const { - if ( !isNumber() ) - return false; - *out = numberDouble(); - return true; - } - - template<> inline bool BSONElement::coerce<bool>( bool* out ) const { - *out = trueValue(); - return true; - } - - template<> inline bool BSONElement::coerce< std::vector<std::string> >( std::vector<std::string>* out ) const { - if ( type() != mongo::Array ) - return false; - return Obj().coerceVector<std::string>( out ); - } - - -} diff --git a/src/mongo/bson/bson_db.h b/src/mongo/bson/bson_db.h index 3f2792f1599..bd1b7b53a33 100644 --- a/src/mongo/bson/bson_db.h +++ b/src/mongo/bson/bson_db.h @@ -110,4 +110,17 @@ namespace mongo { return *_builder; } + template<class T> inline + BSONObjBuilder& BSONObjBuilderValueStream::operator<<( T value ) { + _builder->append(_fieldName, value); + _fieldName = StringData(); + return *_builder; + } + + template<class T> + BSONObjBuilder& Labeler::operator<<( T value ) { + s_->subobj()->append( l_.l_, value ); + return *s_->_builder; + } + } diff --git a/src/mongo/bson/bsonelement.cpp b/src/mongo/bson/bsonelement.cpp index 4c7a8c9f662..47e9e0a1544 100644 --- a/src/mongo/bson/bsonelement.cpp +++ b/src/mongo/bson/bsonelement.cpp @@ -364,5 +364,555 @@ namespace mongo { } return v; } + + /* wo = "well ordered" + note: (mongodb related) : this can only change in behavior when index version # changes + */ + int BSONElement::woCompare( const BSONElement &e, + bool considerFieldName ) const { + int lt = (int) canonicalType(); + int rt = (int) e.canonicalType(); + int x = lt - rt; + if( x != 0 && (!isNumber() || !e.isNumber()) ) + return x; + if ( considerFieldName ) { + x = strcmp(fieldName(), e.fieldName()); + if ( x != 0 ) + return x; + } + x = compareElementValues(*this, e); + return x; + } + + + BSONObj BSONElement::embeddedObjectUserCheck() const { + if ( MONGO_likely(isABSONObj()) ) + return BSONObj(value()); + std::stringstream ss; + ss << "invalid parameter: expected an object (" << fieldName() << ")"; + uasserted( 10065 , ss.str() ); + return BSONObj(); // never reachable + } + + BSONObj BSONElement::embeddedObject() const { + verify( isABSONObj() ); + return BSONObj(value()); + } + + BSONObj BSONElement::codeWScopeObject() const { + verify( type() == CodeWScope ); + int strSizeWNull = *(int *)( value() + 4 ); + return BSONObj( value() + 4 + 4 + strSizeWNull ); + } + + // wrap this element up as a singleton object. + BSONObj BSONElement::wrap() const { + BSONObjBuilder b(size()+6); + b.append(*this); + return b.obj(); + } + + BSONObj BSONElement::wrap( const StringData& newName ) const { + BSONObjBuilder b(size() + 6 + newName.size()); + b.appendAs(*this,newName); + return b.obj(); + } + + void BSONElement::Val(BSONObj& v) const { + v = Obj(); + } + + BSONObj BSONElement::Obj() const { + return embeddedObjectUserCheck(); + } + + BSONElement BSONElement::operator[] (const std::string& field) const { + BSONObj o = Obj(); + return o[field]; + } + + int BSONElement::size( int maxLen ) const { + if ( totalSize >= 0 ) + return totalSize; + + int remain = maxLen - fieldNameSize() - 1; + + int x = 0; + switch ( type() ) { + case EOO: + case Undefined: + case jstNULL: + case MaxKey: + case MinKey: + break; + case mongo::Bool: + x = 1; + break; + case NumberInt: + x = 4; + break; + case Timestamp: + case mongo::Date: + case NumberDouble: + case NumberLong: + x = 8; + break; + case jstOID: + x = 12; + break; + case Symbol: + case Code: + case mongo::String: + massert( 10313 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); + x = valuestrsize() + 4; + break; + case CodeWScope: + massert( 10314 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); + x = objsize(); + break; + + case DBRef: + massert( 10315 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); + x = valuestrsize() + 4 + 12; + break; + case Object: + case mongo::Array: + massert( 10316 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); + x = objsize(); + break; + case BinData: + massert( 10317 , "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); + x = valuestrsize() + 4 + 1/*subtype*/; + break; + case RegEx: { + const char *p = value(); + size_t len1 = ( maxLen == -1 ) ? strlen( p ) : (size_t)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; + if( maxLen == -1 ) + len2 = strlen( p ); + else { + size_t x = remain - len1 - 1; + verify( 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); + } + break; + default: { + StringBuilder ss; + ss << "BSONElement: bad type " << (int) type(); + std::string msg = ss.str(); + massert( 13655 , msg.c_str(),false); + } + } + totalSize = x + fieldNameSize() + 1; // BSONType + + return totalSize; + } + + int BSONElement::size() const { + if ( totalSize >= 0 ) + return totalSize; + + int x = 0; + switch ( type() ) { + case EOO: + case Undefined: + case jstNULL: + case MaxKey: + case MinKey: + break; + case mongo::Bool: + x = 1; + break; + case NumberInt: + x = 4; + break; + case Timestamp: + case mongo::Date: + case NumberDouble: + case NumberLong: + x = 8; + break; + case jstOID: + x = 12; + break; + case Symbol: + case Code: + case mongo::String: + x = valuestrsize() + 4; + break; + case DBRef: + x = valuestrsize() + 4 + 12; + break; + case CodeWScope: + case Object: + case mongo::Array: + x = objsize(); + break; + case BinData: + x = valuestrsize() + 4 + 1/*subtype*/; + break; + case RegEx: + { + const char *p = value(); + size_t len1 = strlen(p); + p = p + len1 + 1; + size_t len2; + len2 = strlen( p ); + x = (int) (len1 + 1 + len2 + 1); + } + break; + default: + { + StringBuilder ss; + ss << "BSONElement: bad type " << (int) type(); + std::string msg = ss.str(); + massert(10320 , msg.c_str(),false); + } + } + totalSize = x + fieldNameSize() + 1; // BSONType + + return totalSize; + } + + std::string BSONElement::toString( bool includeFieldName, bool full ) const { + StringBuilder s; + toString(s, includeFieldName, full); + return s.str(); + } + + void BSONElement::toString( StringBuilder& s, bool includeFieldName, bool full, int depth ) const { + + if ( depth > BSONObj::maxToStringRecursionDepth ) { + // check if we want the full/complete string + if ( full ) { + StringBuilder s; + s << "Reached maximum recursion depth of "; + s << BSONObj::maxToStringRecursionDepth; + uassert(16150, s.str(), full != true); + } + s << "..."; + return; + } + + if ( includeFieldName && type() != EOO ) + s << fieldName() << ": "; + switch ( type() ) { + case EOO: + s << "EOO"; + break; + case mongo::Date: + s << "new Date(" << (long long) date() << ')'; + break; + case RegEx: { + s << "/" << regex() << '/'; + const char *p = regexFlags(); + if ( p ) s << p; + } + break; + case NumberDouble: + s.appendDoubleNice( number() ); + break; + case NumberLong: + s << _numberLong(); + break; + case NumberInt: + s << _numberInt(); + break; + case mongo::Bool: + s << ( boolean() ? "true" : "false" ); + break; + case Object: + embeddedObject().toString(s, false, full, depth+1); + break; + case mongo::Array: + embeddedObject().toString(s, true, full, depth+1); + break; + case Undefined: + s << "undefined"; + break; + case jstNULL: + s << "null"; + break; + case MaxKey: + s << "MaxKey"; + break; + case MinKey: + s << "MinKey"; + break; + case CodeWScope: + s << "CodeWScope( " + << codeWScopeCode() << ", " << codeWScopeObject().toString(false, full) << ")"; + break; + case Code: + if ( !full && valuestrsize() > 80 ) { + s.write(valuestr(), 70); + s << "..."; + } + else { + s.write(valuestr(), valuestrsize()-1); + } + break; + case Symbol: + case mongo::String: + s << '"'; + if ( !full && valuestrsize() > 160 ) { + s.write(valuestr(), 150); + s << "...\""; + } + else { + s.write(valuestr(), valuestrsize()-1); + s << '"'; + } + break; + case DBRef: + s << "DBRef('" << valuestr() << "',"; + { + mongo::OID *x = (mongo::OID *) (valuestr() + valuestrsize()); + s << *x << ')'; + } + break; + case jstOID: + s << "ObjectId('"; + s << __oid() << "')"; + break; + case BinData: + s << "BinData(" << binDataType() << ", "; + { + int len; + const char *data = binDataClean(len); + if ( !full && len > 80 ) { + s << toHex(data, 70) << "...)"; + } + else { + s << toHex(data, len) << ")"; + } + } + break; + case Timestamp: + s << "Timestamp " << timestampTime() << "|" << timestampInc(); + break; + default: + s << "?type=" << type(); + break; + } + } + + std::ostream& operator<<( std::ostream &s, const BSONElement &e ) { + return s << e.toString(); + } + + StringBuilder& operator<<( StringBuilder &s, const BSONElement &e ) { + e.toString( s ); + return s; + } + + template<> bool BSONElement::coerce<std::string>( std::string* out ) const { + if ( type() != mongo::String ) + return false; + *out = String(); + return true; + } + + template<> bool BSONElement::coerce<int>( int* out ) const { + if ( !isNumber() ) + return false; + *out = numberInt(); + return true; + } + + template<> bool BSONElement::coerce<double>( double* out ) const { + if ( !isNumber() ) + return false; + *out = numberDouble(); + return true; + } + + template<> bool BSONElement::coerce<bool>( bool* out ) const { + *out = trueValue(); + return true; + } + + template<> bool BSONElement::coerce< std::vector<std::string> >( std::vector<std::string>* out ) const { + if ( type() != mongo::Array ) + return false; + return Obj().coerceVector<std::string>( out ); + } + + template<typename T> bool BSONObj::coerceVector( std::vector<T>* out ) const { + BSONObjIterator i( *this ); + while ( i.more() ) { + BSONElement e = i.next(); + T t; + if ( ! e.coerce<T>( &t ) ) + return false; + out->push_back( t ); + } + return true; + } + + // used by jsonString() + std::string escape( const std::string& s , bool escape_slash) { + StringBuilder ret; + for ( std::string::const_iterator i = s.begin(); i != s.end(); ++i ) { + switch ( *i ) { + case '"': + ret << "\\\""; + break; + case '\\': + ret << "\\\\"; + break; + case '/': + ret << (escape_slash ? "\\/" : "/"); + break; + case '\b': + ret << "\\b"; + break; + case '\f': + ret << "\\f"; + break; + case '\n': + ret << "\\n"; + break; + case '\r': + ret << "\\r"; + break; + case '\t': + ret << "\\t"; + break; + default: + if ( *i >= 0 && *i <= 0x1f ) { + //TODO: these should be utf16 code-units not bytes + char c = *i; + ret << "\\u00" << toHexLower(&c, 1); + } + else { + ret << *i; + } + } + } + return ret.str(); + } + + /* must be same type when called, unless both sides are #s + this large function is in header to facilitate inline-only use of bson + */ + int compareElementValues(const BSONElement& l, const BSONElement& r) { + int f; + + switch ( l.type() ) { + case EOO: + case Undefined: // EOO and Undefined are same canonicalType + case jstNULL: + case MaxKey: + case MinKey: + f = l.canonicalType() - r.canonicalType(); + if ( f<0 ) return -1; + return f==0 ? 0 : 1; + case Bool: + return *l.value() - *r.value(); + case Timestamp: + // unsigned compare for timestamps - note they are not really dates but (ordinal + time_t) + if ( l.date() < r.date() ) + return -1; + return l.date() == r.date() ? 0 : 1; + case Date: + { + long long a = (long long) l.Date().millis; + long long b = (long long) r.Date().millis; + if( a < b ) + return -1; + return a == b ? 0 : 1; + } + case NumberLong: + if( r.type() == NumberLong ) { + long long L = l._numberLong(); + long long R = r._numberLong(); + if( L < R ) return -1; + if( L == R ) return 0; + return 1; + } + goto dodouble; + case NumberInt: + if( r.type() == NumberInt ) { + int L = l._numberInt(); + int R = r._numberInt(); + if( L < R ) return -1; + return L == R ? 0 : 1; + } + // else fall through + case NumberDouble: +dodouble: + { + double left = l.number(); + double right = r.number(); + if( left < right ) + return -1; + if( left == right ) + return 0; + if( isNaN(left) ) + return isNaN(right) ? 0 : -1; + return 1; + } + case jstOID: + return memcmp(l.value(), r.value(), 12); + case Code: + case Symbol: + case String: + /* todo: a utf sort order version one day... */ + { + // we use memcmp as we allow zeros in UTF8 strings + int lsz = l.valuestrsize(); + int rsz = r.valuestrsize(); + int common = std::min(lsz, rsz); + int res = memcmp(l.valuestr(), r.valuestr(), common); + if( res ) + return res; + // longer std::string is the greater one + return lsz-rsz; + } + case Object: + case Array: + return l.embeddedObject().woCompare( r.embeddedObject() ); + case DBRef: { + int lsz = l.valuesize(); + int rsz = r.valuesize(); + if ( lsz - rsz != 0 ) return lsz - rsz; + return memcmp(l.value(), r.value(), lsz); + } + case BinData: { + int lsz = l.objsize(); // our bin data size in bytes, not including the subtype byte + int rsz = r.objsize(); + if ( lsz - rsz != 0 ) return lsz - rsz; + return memcmp(l.value()+4, r.value()+4, lsz+1 /*+1 for subtype byte*/); + } + case RegEx: { + int c = strcmp(l.regex(), r.regex()); + if ( c ) + return c; + return strcmp(l.regexFlags(), r.regexFlags()); + } + case CodeWScope : { + f = l.canonicalType() - r.canonicalType(); + if ( f ) + return f; + f = strcmp( l.codeWScopeCode() , r.codeWScopeCode() ); + if ( f ) + return f; + f = strcmp( l.codeWScopeScopeDataUnsafe() , r.codeWScopeScopeDataUnsafe() ); + if ( f ) + return f; + return 0; + } + default: + verify( false); + } + return -1; + } + } // namespace mongo diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h index dd751c8b286..2c827398892 100644 --- a/src/mongo/bson/bsonelement.h +++ b/src/mongo/bson/bsonelement.h @@ -44,15 +44,10 @@ namespace mongo { class BSONObj; class BSONElement; class BSONObjBuilder; -} -namespace bson { - typedef mongo::BSONElement be; - typedef mongo::BSONObj bo; - typedef mongo::BSONObjBuilder bob; -} - -namespace mongo { + typedef BSONElement be; + typedef BSONObj bo; + typedef BSONObjBuilder bob; /* l and r MUST have same type when called: check that first. */ int compareElementValues(const BSONElement& l, const BSONElement& r); @@ -644,4 +639,7 @@ namespace mongo { totalSize = 1; } + // TODO(SERVER-14596): move to a better place; take a StringData. + std::string escape( const std::string& s , bool escape_slash=false); + } diff --git a/src/mongo/bson/bsonmisc.cpp b/src/mongo/bson/bsonmisc.cpp index c6088dbfdf1..b1b213791b9 100644 --- a/src/mongo/bson/bsonmisc.cpp +++ b/src/mongo/bson/bsonmisc.cpp @@ -68,4 +68,50 @@ namespace mongo { MinKeyLabeler MINKEY; MaxKeyLabeler MAXKEY; + BSONObjBuilderValueStream::BSONObjBuilderValueStream( BSONObjBuilder * builder ) { + _builder = builder; + } + + BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const BSONElement& e ) { + _builder->appendAs( e , _fieldName ); + _fieldName = StringData(); + return *_builder; + } + + BufBuilder& BSONObjBuilderValueStream::subobjStart() { + StringData tmp = _fieldName; + _fieldName = StringData(); + return _builder->subobjStart(tmp); + } + + BufBuilder& BSONObjBuilderValueStream::subarrayStart() { + StringData tmp = _fieldName; + _fieldName = StringData(); + return _builder->subarrayStart(tmp); + } + + Labeler BSONObjBuilderValueStream::operator<<( const Labeler::Label &l ) { + return Labeler( l, this ); + } + + void BSONObjBuilderValueStream::endField( const StringData& nextFieldName ) { + if ( haveSubobj() ) { + verify( _fieldName.rawData() ); + _builder->append( _fieldName, subobj()->done() ); + _subobj.reset(); + } + _fieldName = nextFieldName; + } + + BSONObjBuilder *BSONObjBuilderValueStream::subobj() { + if ( !haveSubobj() ) + _subobj.reset( new BSONObjBuilder() ); + return _subobj.get(); + } + + BSONObjBuilder& Labeler::operator<<( const BSONElement& e ) { + s_->subobj()->appendAs( e, l_.l_ ); + return *s_->_builder; + } + } // namespace mongo diff --git a/src/mongo/bson/bsonmisc.h b/src/mongo/bson/bsonmisc.h index 0a2cd60b14c..af96981128e 100644 --- a/src/mongo/bson/bsonmisc.h +++ b/src/mongo/bson/bsonmisc.h @@ -32,7 +32,6 @@ #include <memory> #include "mongo/bson/bsonelement.h" -#include "mongo/client/export_macros.h" namespace mongo { @@ -91,38 +90,38 @@ namespace mongo { Example: std::cout << BSON( GENOID << "z" << 3 ); // { _id : ..., z : 3 } */ - struct MONGO_CLIENT_API GENOIDLabeler { }; - extern MONGO_CLIENT_API GENOIDLabeler GENOID; + struct GENOIDLabeler { }; + extern GENOIDLabeler GENOID; /* Utility class to add a Date element with the current time Example: std::cout << BSON( "created" << DATENOW ); // { created : "2009-10-09 11:41:42" } */ - struct MONGO_CLIENT_API DateNowLabeler { }; - extern MONGO_CLIENT_API DateNowLabeler DATENOW; + struct DateNowLabeler { }; + extern DateNowLabeler DATENOW; /* Utility class to assign a NULL value to a given attribute Example: std::cout << BSON( "a" << BSONNULL ); // { a : null } */ - struct MONGO_CLIENT_API NullLabeler { }; - extern MONGO_CLIENT_API NullLabeler BSONNULL; + struct NullLabeler { }; + extern NullLabeler BSONNULL; /* Utility class to assign an Undefined value to a given attribute Example: std::cout << BSON( "a" << BSONUndefined ); // { a : undefined } */ - struct MONGO_CLIENT_API UndefinedLabeler { }; - extern MONGO_CLIENT_API UndefinedLabeler BSONUndefined; + struct UndefinedLabeler { }; + extern UndefinedLabeler BSONUndefined; /* Utility class to add the minKey (minus infinity) to a given attribute Example: std::cout << BSON( "a" << MINKEY ); // { "a" : { "$minKey" : 1 } } */ - struct MONGO_CLIENT_API MinKeyLabeler { }; - extern MONGO_CLIENT_API MinKeyLabeler MINKEY; - struct MONGO_CLIENT_API MaxKeyLabeler { }; - extern MONGO_CLIENT_API MaxKeyLabeler MAXKEY; + struct MinKeyLabeler { }; + extern MinKeyLabeler MINKEY; + struct MaxKeyLabeler { }; + extern MaxKeyLabeler MAXKEY; // Utility class to implement GT, GTE, etc as described above. class Labeler { @@ -187,13 +186,13 @@ namespace mongo { OID oid; }; - extern MONGO_CLIENT_API Labeler::Label GT; - extern MONGO_CLIENT_API Labeler::Label GTE; - extern MONGO_CLIENT_API Labeler::Label LT; - extern MONGO_CLIENT_API Labeler::Label LTE; - extern MONGO_CLIENT_API Labeler::Label NE; - extern MONGO_CLIENT_API Labeler::Label NIN; - extern MONGO_CLIENT_API Labeler::Label BSIZE; + extern Labeler::Label GT; + extern Labeler::Label GTE; + extern Labeler::Label LT; + extern Labeler::Label LTE; + extern Labeler::Label NE; + extern Labeler::Label NIN; + extern Labeler::Label BSIZE; // $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT << 6)); @@ -206,7 +205,7 @@ namespace mongo { // definitions in bsonobjbuilder.h b/c of incomplete types // Utility class to implement BSON( key << val ) as described above. - class MONGO_CLIENT_API BSONObjBuilderValueStream : public boost::noncopyable { + class BSONObjBuilderValueStream : public boost::noncopyable { public: friend class Labeler; BSONObjBuilderValueStream( BSONObjBuilder * builder ); diff --git a/src/mongo/bson/bsonobj.cpp b/src/mongo/bson/bsonobj.cpp index 0614ab98a83..0fc1a2461dd 100644 --- a/src/mongo/bson/bsonobj.cpp +++ b/src/mongo/bson/bsonobj.cpp @@ -38,6 +38,52 @@ namespace mongo { using namespace std; /* BSONObj ------------------------------------------------------------*/ + // deep (full) equality + bool BSONObj::equal(const BSONObj &rhs) const { + BSONObjIterator i(*this); + BSONObjIterator j(rhs); + BSONElement l,r; + do { + // so far, equal... + l = i.next(); + r = j.next(); + if ( l.eoo() ) + return r.eoo(); + } while( l == r ); + return false; + } + + void BSONObj::_assertInvalid() const { + StringBuilder ss; + int os = objsize(); + ss << "BSONObj size: " << os << " (0x" << integerToHex( os ) << ") is invalid. " + << "Size must be between 0 and " << BSONObjMaxInternalSize + << "(" << ( BSONObjMaxInternalSize/(1024*1024) ) << "MB)"; + try { + BSONElement e = firstElement(); + ss << " First element: " << e.toString(); + } + catch ( ... ) { } + massert( 10334 , ss.str() , 0 ); + } + + BSONObj BSONObj::copy() const { + Holder *h = (Holder*) malloc(objsize() + sizeof(unsigned)); + h->zero(); + memcpy(h->data, objdata(), objsize()); + return BSONObj(h); + } + + BSONObj BSONObj::getOwned() const { + if ( isOwned() ) + return *this; + return copy(); + } + + BSONObjIterator BSONObj::begin() const { + return BSONObjIterator(*this); + } + string BSONObj::md5() const { md5digest d; md5_state_t st; @@ -589,4 +635,234 @@ namespace mongo { } } + void BSONObj::getFields(unsigned n, const char **fieldNames, BSONElement *fields) const { + BSONObjIterator i(*this); + while ( i.more() ) { + BSONElement e = i.next(); + const char *p = e.fieldName(); + for( unsigned i = 0; i < n; i++ ) { + if( strcmp(p, fieldNames[i]) == 0 ) { + fields[i] = e; + break; + } + } + } + } + + BSONElement BSONObj::getField(const StringData& name) const { + BSONObjIterator i(*this); + while ( i.more() ) { + BSONElement e = i.next(); + if ( name == e.fieldName() ) + return e; + } + return BSONElement(); + } + + int BSONObj::getIntField(const StringData& name) const { + BSONElement e = getField(name); + return e.isNumber() ? (int) e.number() : std::numeric_limits< int >::min(); + } + + bool BSONObj::getBoolField(const StringData& name) const { + BSONElement e = getField(name); + return e.type() == Bool ? e.boolean() : false; + } + + const char * BSONObj::getStringField(const StringData& name) const { + BSONElement e = getField(name); + return e.type() == String ? e.valuestr() : ""; + } + + bool BSONObj::isValid() const { + int x = objsize(); + return x > 0 && x <= BSONObjMaxInternalSize; + } + + bool BSONObj::getObjectID(BSONElement& e) const { + BSONElement f = getField("_id"); + if( !f.eoo() ) { + e = f; + return true; + } + return false; + } + + BSONObj BSONObj::removeField(const StringData& name) const { + BSONObjBuilder b; + BSONObjIterator i(*this); + while ( i.more() ) { + BSONElement e = i.next(); + const char *fname = e.fieldName(); + if ( name != fname ) + b.append(e); + } + return b.obj(); + } + + std::string BSONObj::hexDump() const { + std::stringstream ss; + const char *d = objdata(); + int size = objsize(); + for( int i = 0; i < size; ++i ) { + ss.width( 2 ); + ss.fill( '0' ); + ss << std::hex << (unsigned)(unsigned char)( d[ i ] ) << std::dec; + if ( ( d[ i ] >= '0' && d[ i ] <= '9' ) || ( d[ i ] >= 'A' && d[ i ] <= 'z' ) ) + ss << '\'' << d[ i ] << '\''; + if ( i != size - 1 ) + ss << ' '; + } + return ss.str(); + } + + + void BSONObj::elems(std::vector<BSONElement> &v) const { + BSONObjIterator i(*this); + while( i.more() ) + v.push_back(i.next()); + } + + void BSONObj::elems(std::list<BSONElement> &v) const { + BSONObjIterator i(*this); + while( i.more() ) + v.push_back(i.next()); + } + + /* return has eoo() true if no match + supports "." notation to reach into embedded objects + */ + BSONElement BSONObj::getFieldDotted(const StringData& name) const { + BSONElement e = getField(name); + if (e.eoo()) { + size_t dot_offset = name.find('.'); + if (dot_offset != std::string::npos) { + StringData left = name.substr(0, dot_offset); + StringData right = name.substr(dot_offset + 1); + BSONObj sub = getObjectField(left); + return sub.isEmpty() ? BSONElement() : sub.getFieldDotted(right); + } + } + + return e; + } + + BSONObj BSONObj::getObjectField(const StringData& name) const { + BSONElement e = getField(name); + BSONType t = e.type(); + return t == Object || t == Array ? e.embeddedObject() : BSONObj(); + } + + int BSONObj::nFields() const { + int n = 0; + BSONObjIterator i(*this); + while ( i.moreWithEOO() ) { + BSONElement e = i.next(); + if ( e.eoo() ) + break; + n++; + } + return n; + } + + BSONObj::BSONObj() { + /* 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; + } + + std::string BSONObj::toString( bool isArray, bool full ) const { + if ( isEmpty() ) return (isArray ? "[]" : "{}"); + StringBuilder s; + toString(s, isArray, full); + return s.str(); + } + void BSONObj::toString( StringBuilder& s, bool isArray, bool full, int depth ) const { + if ( isEmpty() ) { + s << (isArray ? "[]" : "{}"); + return; + } + + s << ( isArray ? "[ " : "{ " ); + BSONObjIterator i(*this); + bool first = true; + while ( 1 ) { + massert( 10327 , "Object does not end with EOO", i.moreWithEOO() ); + BSONElement e = i.next( true ); + massert( 10328 , "Invalid element size", e.size() > 0 ); + 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() ); + bool end = ( e.size() + offset == this->objsize() ); + if ( e.eoo() ) { + massert( 10331 , "EOO Before end of object", end ); + break; + } + if ( first ) + first = false; + else + s << ", "; + e.toString( s, !isArray, full, depth ); + } + s << ( isArray ? " ]" : " }" ); + } + + std::ostream& operator<<( std::ostream &s, const BSONObj &o ) { + return s << o.toString(); + } + + StringBuilder& operator<<( StringBuilder &s, const BSONObj &o ) { + o.toString( s ); + return s; + } + + template <class T> + void BSONObj::Vals(std::vector<T>& v) const { + BSONObjIterator i(*this); + while( i.more() ) { + T t; + i.next().Val(t); + v.push_back(t); + } + } + template <class T> + void BSONObj::Vals(std::list<T>& v) const { + BSONObjIterator i(*this); + while( i.more() ) { + T t; + i.next().Val(t); + v.push_back(t); + } + } + + template <class T> + void BSONObj::vals(std::vector<T>& v) const { + BSONObjIterator i(*this); + while( i.more() ) { + try { + T t; + i.next().Val(t); + v.push_back(t); + } + catch(...) { } + } + } + template <class T> + void BSONObj::vals(std::list<T>& v) const { + BSONObjIterator i(*this); + while( i.more() ) { + try { + T t; + i.next().Val(t); + v.push_back(t); + } + catch(...) { } + } + } + + } // namespace mongo diff --git a/src/mongo/bson/bsonobjbuilder.cpp b/src/mongo/bson/bsonobjbuilder.cpp index 7f40c2adc3b..c75644c198c 100644 --- a/src/mongo/bson/bsonobjbuilder.cpp +++ b/src/mongo/bson/bsonobjbuilder.cpp @@ -192,4 +192,60 @@ namespace mongo { return false; } } + + /* add all the fields from the object specified to this object */ + BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) { + if (!x.isEmpty()) + _b.appendBuf( + x.objdata() + 4, // skip over leading length + x.objsize() - 5); // ignore leading length and trailing \0 + return *this; + } + + /* add all the fields from the object specified to this object if they don't exist */ + BSONObjBuilder& BSONObjBuilder::appendElementsUnique(BSONObj x) { + std::set<std::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; + } + + void BSONObjBuilder::appendKeys( const BSONObj& keyPattern , const BSONObj& values ) { + BSONObjIterator i(keyPattern); + BSONObjIterator j(values); + + while ( i.more() && j.more() ) { + appendAs( j.next() , i.next().fieldName() ); + } + + verify( ! i.more() ); + verify( ! j.more() ); + } + + BSONObjIterator BSONObjBuilder::iterator() const { + const char * s = _b.buf() + _offset; + const char * e = _b.buf() + _b.len(); + return BSONObjIterator( s , e ); + } + + bool BSONObjBuilder::hasField( const StringData& name ) const { + BSONObjIterator i = iterator(); + while ( i.more() ) + if ( name == i.next().fieldName() ) + return true; + return false; + } + + } // namespace mongo diff --git a/src/mongo/bson/bsonobjbuilder.h b/src/mongo/bson/bsonobjbuilder.h index 80dce815faf..3495ca9313d 100644 --- a/src/mongo/bson/bsonobjbuilder.h +++ b/src/mongo/bson/bsonobjbuilder.h @@ -40,10 +40,10 @@ #include <limits> #include "mongo/base/parse_number.h" +#include "mongo/bson/bson_field.h" #include "mongo/bson/bsonelement.h" -#include "mongo/bson/bsonobj.h" #include "mongo/bson/bsonmisc.h" -#include "mongo/bson/bson_field.h" +#include "mongo/bson/bsonobj.h" #include "mongo/client/export_macros.h" #if defined(_DEBUG) && defined(MONGO_EXPOSE_MACROS) @@ -886,6 +886,13 @@ namespace mongo { inline BSONArrayBuilder& BSONArrayBuilder::append( const std::set< T >& vals ) { return _appendArrayIt< std::set< T > >( *this, vals ); } + + template <typename T> + inline BSONFieldValue<BSONObj> BSONField<T>::query(const char * q, const T& t) const { + BSONObjBuilder b; + b.append( q , t ); + return BSONFieldValue<BSONObj>( _name , b.obj() ); + } // $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT 6)); diff --git a/src/mongo/bson/bsontypes.h b/src/mongo/bson/bsontypes.h index 3bb6ab22f02..d23ba94c773 100644 --- a/src/mongo/bson/bsontypes.h +++ b/src/mongo/bson/bsontypes.h @@ -31,8 +31,6 @@ #include "mongo/util/assert_util.h" -namespace bson { } - namespace mongo { class BSONArrayBuilder; diff --git a/src/mongo/client/dbclientinterface.h b/src/mongo/client/dbclientinterface.h index ecb7390299e..907b5abd416 100644 --- a/src/mongo/client/dbclientinterface.h +++ b/src/mongo/client/dbclientinterface.h @@ -35,6 +35,7 @@ #include "mongo/pch.h" #include "mongo/base/string_data.h" +#include "mongo/bson/bson_field.h" #include "mongo/client/export_macros.h" #include "mongo/db/jsobj.h" #include "mongo/logger/log_severity.h" diff --git a/src/mongo/client/examples/mongoperf.cpp b/src/mongo/client/examples/mongoperf.cpp index be1d76b081b..9b96879b5cb 100644 --- a/src/mongo/client/examples/mongoperf.cpp +++ b/src/mongo/client/examples/mongoperf.cpp @@ -56,7 +56,6 @@ using namespace std; using namespace mongo; -using namespace bson; int dummy; unsigned recSizeKB; diff --git a/src/mongo/db/auth/privilege_parser.h b/src/mongo/db/auth/privilege_parser.h index 7c892602032..ac35ff0f114 100644 --- a/src/mongo/db/auth/privilege_parser.h +++ b/src/mongo/db/auth/privilege_parser.h @@ -32,6 +32,7 @@ #include <vector> #include "mongo/base/string_data.h" +#include "mongo/bson/bson_field.h" #include "mongo/db/jsobj.h" #include "mongo/s/bson_serializable.h" diff --git a/src/mongo/db/dbwebserver.cpp b/src/mongo/db/dbwebserver.cpp index 33d5e7da9a5..ccf9c5ffaee 100644 --- a/src/mongo/db/dbwebserver.cpp +++ b/src/mongo/db/dbwebserver.cpp @@ -63,7 +63,6 @@ namespace mongo { using namespace html; - using namespace bson; struct Timing { Timing() { diff --git a/src/mongo/db/jsobj.h b/src/mongo/db/jsobj.h index 0122b0c53bd..def5c4da503 100644 --- a/src/mongo/db/jsobj.h +++ b/src/mongo/db/jsobj.h @@ -51,7 +51,6 @@ #include "mongo/bson/bsonmisc.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/bson/bsonobjiterator.h" -#include "mongo/bson/bson-inl.h" #include "mongo/bson/ordering.h" #include "mongo/base/string_data.h" #include "mongo/bson/bson_db.h" diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp index e2bdd1dc066..ff82ffbdb79 100644 --- a/src/mongo/db/matcher/expression_parser.cpp +++ b/src/mongo/db/matcher/expression_parser.cpp @@ -34,7 +34,6 @@ #include "mongo/bson/bsonobj.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/bson/bsonobjiterator.h" -#include "mongo/bson/bson-inl.h" #include "mongo/db/matcher/expression_array.h" #include "mongo/db/matcher/expression_leaf.h" #include "mongo/db/matcher/expression_tree.h" diff --git a/src/mongo/db/matcher/expression_parser_tree.cpp b/src/mongo/db/matcher/expression_parser_tree.cpp index 04a25dac779..01b4ee7a981 100644 --- a/src/mongo/db/matcher/expression_parser_tree.cpp +++ b/src/mongo/db/matcher/expression_parser_tree.cpp @@ -33,7 +33,6 @@ #include "mongo/bson/bsonobj.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/bson/bsonobjiterator.h" -#include "mongo/bson/bson-inl.h" #include "mongo/db/matcher/expression_array.h" #include "mongo/db/matcher/expression_leaf.h" #include "mongo/db/matcher/expression_tree.h" diff --git a/src/mongo/db/matcher/expression_tree.cpp b/src/mongo/db/matcher/expression_tree.cpp index 95e272c1cb3..d45c84ebeda 100644 --- a/src/mongo/db/matcher/expression_tree.cpp +++ b/src/mongo/db/matcher/expression_tree.cpp @@ -34,7 +34,6 @@ #include "mongo/bson/bsonmisc.h" #include "mongo/bson/bsonobjbuilder.h" #include "mongo/bson/bsonobjiterator.h" -#include "mongo/bson/bson-inl.h" #include "mongo/util/log.h" namespace mongo { diff --git a/src/mongo/db/query/type_explain.h b/src/mongo/db/query/type_explain.h index a151c78715d..f19b8ca107a 100644 --- a/src/mongo/db/query/type_explain.h +++ b/src/mongo/db/query/type_explain.h @@ -31,6 +31,7 @@ #include <string> #include "mongo/base/string_data.h" +#include "mongo/bson/bson_field.h" #include "mongo/s/bson_serializable.h" namespace mongo { diff --git a/src/mongo/db/repl/health.cpp b/src/mongo/db/repl/health.cpp index 960c56267b0..1bc54dbd5ac 100644 --- a/src/mongo/db/repl/health.cpp +++ b/src/mongo/db/repl/health.cpp @@ -53,7 +53,6 @@ namespace repl { mutex ScopedConn::mapMutex("ScopedConn::mapMutex"); using namespace html; - using namespace bson; static RamLog * _rsLog = RamLog::get("rs"); Tee* rsLog = _rsLog; diff --git a/src/mongo/db/repl/heartbeat.cpp b/src/mongo/db/repl/heartbeat.cpp index 90012ec837c..6fd72d74477 100644 --- a/src/mongo/db/repl/heartbeat.cpp +++ b/src/mongo/db/repl/heartbeat.cpp @@ -53,8 +53,6 @@ namespace repl { MONGO_FP_DECLARE(rsDelayHeartbeatResponse); - using namespace bson; - namespace { /** * Returns true if there is no data on this server. Useful when starting replication. diff --git a/src/mongo/db/repl/replset_commands.cpp b/src/mongo/db/repl/replset_commands.cpp index 36cfd2a0e31..c77c1761f41 100644 --- a/src/mongo/db/repl/replset_commands.cpp +++ b/src/mongo/db/repl/replset_commands.cpp @@ -40,8 +40,6 @@ #include "mongo/db/repl/rs_config.h" #include "mongo/db/repl/write_concern.h" -using namespace bson; - namespace mongo { namespace repl { diff --git a/src/mongo/db/repl/replset_web_handler.cpp b/src/mongo/db/repl/replset_web_handler.cpp index 175883e967e..1ab296a59c3 100644 --- a/src/mongo/db/repl/replset_web_handler.cpp +++ b/src/mongo/db/repl/replset_web_handler.cpp @@ -38,7 +38,6 @@ namespace mongo { namespace repl { - using namespace bson; using namespace html; class ReplSetHandler : public DbWebHandler { diff --git a/src/mongo/db/repl/rs.cpp b/src/mongo/db/repl/rs.cpp index de4b06fb91a..a007b80f66d 100644 --- a/src/mongo/db/repl/rs.cpp +++ b/src/mongo/db/repl/rs.cpp @@ -39,13 +39,9 @@ #include "mongo/db/repl/repl_set_impl.h" #include "mongo/db/server_parameters.h" -using namespace std; - namespace mongo { namespace repl { - using namespace bson; - ReplSet *theReplSet = 0; // This is a bitmask with the first bit set. It's used to mark connections that should be kept diff --git a/src/mongo/db/repl/rs_config.cpp b/src/mongo/db/repl/rs_config.cpp index 45db18f0066..0867776d71e 100644 --- a/src/mongo/db/repl/rs_config.cpp +++ b/src/mongo/db/repl/rs_config.cpp @@ -45,8 +45,6 @@ #include "mongo/util/net/hostandport.h" #include "mongo/util/text.h" -using namespace bson; - namespace mongo { MONGO_LOG_DEFAULT_COMPONENT_FILE(::mongo::logger::LogComponent::kReplication); diff --git a/src/mongo/db/repl/rs_initialsync.cpp b/src/mongo/db/repl/rs_initialsync.cpp index 72094a53308..0ca30eabd27 100644 --- a/src/mongo/db/repl/rs_initialsync.cpp +++ b/src/mongo/db/repl/rs_initialsync.cpp @@ -55,7 +55,6 @@ namespace mongo { namespace repl { using namespace mongoutils; - using namespace bson; // add try/catch with sleep diff --git a/src/mongo/db/repl/rs_initiate.cpp b/src/mongo/db/repl/rs_initiate.cpp index b6a71b438dd..9232e6275fb 100644 --- a/src/mongo/db/repl/rs_initiate.cpp +++ b/src/mongo/db/repl/rs_initiate.cpp @@ -53,7 +53,6 @@ #include "mongo/util/mmap.h" #include "mongo/util/mongoutils/str.h" -using namespace bson; using namespace mongoutils; namespace mongo { diff --git a/src/mongo/db/repl/rs_initiate.h b/src/mongo/db/repl/rs_initiate.h index edc4c59d841..c9ff7566d7e 100644 --- a/src/mongo/db/repl/rs_initiate.h +++ b/src/mongo/db/repl/rs_initiate.h @@ -29,9 +29,8 @@ #pragma once namespace mongo { -namespace bson { class BSONObjBuilder; -} // namespace bson + namespace repl { class ReplSetConfig; diff --git a/src/mongo/db/repl/rs_rollback.cpp b/src/mongo/db/repl/rs_rollback.cpp index 32093b02374..4f7f31c81bf 100644 --- a/src/mongo/db/repl/rs_rollback.cpp +++ b/src/mongo/db/repl/rs_rollback.cpp @@ -89,8 +89,6 @@ namespace mongo { namespace repl { - using namespace bson; - class RSFatalException : public std::exception { public: RSFatalException(std::string m = "replica set fatal exception") diff --git a/src/mongo/db/repl/rs_sync.cpp b/src/mongo/db/repl/rs_sync.cpp index b0a16002336..e6ef65ba88f 100644 --- a/src/mongo/db/repl/rs_sync.cpp +++ b/src/mongo/db/repl/rs_sync.cpp @@ -58,8 +58,6 @@ namespace mongo { namespace repl { - using namespace bson; - MONGO_EXPORT_STARTUP_SERVER_PARAMETER(maxSyncSourceLagSecs, int, 30); MONGO_INITIALIZER(maxSyncSourceLagSecsCheck) (InitializerContext*) { if (maxSyncSourceLagSecs < 1) { diff --git a/src/mongo/db/restapi.cpp b/src/mongo/db/restapi.cpp index cd6619d2d06..031612b59cd 100644 --- a/src/mongo/db/restapi.cpp +++ b/src/mongo/db/restapi.cpp @@ -51,7 +51,6 @@ namespace mongo { bool getInitialSyncCompleted(); - using namespace bson; using namespace html; class RESTHandler : public DbWebHandler { diff --git a/src/mongo/dbtests/jsobjtests.cpp b/src/mongo/dbtests/jsobjtests.cpp index 4881461d5e1..43aa6587531 100644 --- a/src/mongo/dbtests/jsobjtests.cpp +++ b/src/mongo/dbtests/jsobjtests.cpp @@ -41,6 +41,17 @@ #include "mongo/util/stringutils.h" namespace mongo { + typedef std::map<std::string, BSONElement> BSONMap; + BSONMap bson2map(const BSONObj& obj) { + BSONMap m; + BSONObjIterator it(obj); + while (it.more()) { + BSONElement e = it.next(); + m[e.fieldName()] = e; + } + return m; + } + void dotted2nested(BSONObjBuilder& b, const BSONObj& obj) { //use map to sort fields BSONMap sorted = bson2map(obj); diff --git a/src/mongo/dbtests/perftests.cpp b/src/mongo/dbtests/perftests.cpp index d8158d5eb6f..6125b91eeb5 100644 --- a/src/mongo/dbtests/perftests.cpp +++ b/src/mongo/dbtests/perftests.cpp @@ -63,8 +63,6 @@ #include <mutex> #endif -using namespace bson; - namespace PerfTests { const bool profiling = false; diff --git a/src/mongo/s/type_chunk.h b/src/mongo/s/type_chunk.h index 8bbd5ad3a9a..7819025bb71 100644 --- a/src/mongo/s/type_chunk.h +++ b/src/mongo/s/type_chunk.h @@ -32,6 +32,7 @@ #include "mongo/base/disallow_copying.h" #include "mongo/base/string_data.h" +#include "mongo/bson/bson_field.h" #include "mongo/db/jsobj.h" #include "mongo/s/chunk_version.h" diff --git a/src/mongo/server.h b/src/mongo/server.h index 157c23d7f7c..0565511b09f 100644 --- a/src/mongo/server.h +++ b/src/mongo/server.h @@ -47,8 +47,6 @@ #include "mongo/bson/inline_decls.h" -//using namespace bson; - /* Note: do not clutter code with these -- ONLY use in hot spots / significant loops. */ // branch prediction. indicate we expect to be true |