diff options
Diffstat (limited to 'src/mongo/bson/bsonobj.cpp')
-rw-r--r-- | src/mongo/bson/bsonobj.cpp | 1530 |
1 files changed, 756 insertions, 774 deletions
diff --git a/src/mongo/bson/bsonobj.cpp b/src/mongo/bson/bsonobj.cpp index 3374e5de1dc..0c9f8f01636 100644 --- a/src/mongo/bson/bsonobj.cpp +++ b/src/mongo/bson/bsonobj.cpp @@ -43,879 +43,861 @@ #include "mongo/util/stringutils.h" 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 { - char* storage = static_cast<char*>(mongoMalloc(sizeof(Holder) + objsize())); - memcpy(storage + sizeof(Holder), objdata(), objsize()); - return BSONObj::takeOwnership(storage); - } - - BSONObj BSONObj::getOwned() const { - if ( isOwned() ) - return *this; - return copy(); - } - - string BSONObj::jsonString( JsonStringFormat format, int pretty, bool isArray ) const { - - if ( isEmpty() ) return isArray ? "[]" : "{}"; - - StringBuilder s; - s << (isArray ? "[ " : "{ "); - BSONObjIterator i(*this); - BSONElement e = i.next(); - if ( !e.eoo() ) - while ( 1 ) { - s << e.jsonString( format, !isArray, pretty?pretty+1:0 ); - e = i.next(); - if ( e.eoo() ) - break; - s << ","; - if ( pretty ) { - s << '\n'; - for( int x = 0; x < pretty; x++ ) - s << " "; - } - else { - s << " "; - } +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 { + char* storage = static_cast<char*>(mongoMalloc(sizeof(Holder) + objsize())); + memcpy(storage + sizeof(Holder), objdata(), objsize()); + return BSONObj::takeOwnership(storage); +} + +BSONObj BSONObj::getOwned() const { + if (isOwned()) + return *this; + return copy(); +} + +string BSONObj::jsonString(JsonStringFormat format, int pretty, bool isArray) const { + if (isEmpty()) + return isArray ? "[]" : "{}"; + + StringBuilder s; + s << (isArray ? "[ " : "{ "); + BSONObjIterator i(*this); + BSONElement e = i.next(); + if (!e.eoo()) + while (1) { + s << e.jsonString(format, !isArray, pretty ? pretty + 1 : 0); + e = i.next(); + if (e.eoo()) + break; + s << ","; + if (pretty) { + s << '\n'; + for (int x = 0; x < pretty; x++) + s << " "; + } else { + s << " "; } - s << (isArray ? " ]" : " }"); - return s.str(); - } - - bool BSONObj::valid() const { - return validateBSON( objdata(), objsize() ).isOK(); - } - - int BSONObj::woCompare(const BSONObj& r, const Ordering &o, bool considerFieldName) const { - if ( isEmpty() ) - return r.isEmpty() ? 0 : -1; - if ( r.isEmpty() ) + } + s << (isArray ? " ]" : " }"); + return s.str(); +} + +bool BSONObj::valid() const { + return validateBSON(objdata(), objsize()).isOK(); +} + +int BSONObj::woCompare(const BSONObj& r, const Ordering& o, bool considerFieldName) const { + if (isEmpty()) + return r.isEmpty() ? 0 : -1; + if (r.isEmpty()) + return 1; + + BSONObjIterator i(*this); + BSONObjIterator j(r); + unsigned mask = 1; + while (1) { + // so far, equal... + + BSONElement l = i.next(); + BSONElement r = j.next(); + if (l.eoo()) + return r.eoo() ? 0 : -1; + if (r.eoo()) return 1; - BSONObjIterator i(*this); - BSONObjIterator j(r); - unsigned mask = 1; - while ( 1 ) { - // so far, equal... - - BSONElement l = i.next(); - BSONElement r = j.next(); - if ( l.eoo() ) - return r.eoo() ? 0 : -1; - if ( r.eoo() ) - return 1; - - int x; - { - x = l.woCompare( r, considerFieldName ); - if( o.descending(mask) ) - x = -x; - } - if ( x != 0 ) - return x; - mask <<= 1; + int x; + { + x = l.woCompare(r, considerFieldName); + if (o.descending(mask)) + x = -x; } - return -1; - } - - /* well ordered compare */ - int BSONObj::woCompare(const BSONObj &r, const BSONObj &idxKey, - bool considerFieldName) const { - if ( isEmpty() ) - return r.isEmpty() ? 0 : -1; - if ( r.isEmpty() ) + if (x != 0) + return x; + mask <<= 1; + } + return -1; +} + +/* well ordered compare */ +int BSONObj::woCompare(const BSONObj& r, const BSONObj& idxKey, bool considerFieldName) const { + if (isEmpty()) + return r.isEmpty() ? 0 : -1; + if (r.isEmpty()) + return 1; + + bool ordered = !idxKey.isEmpty(); + + BSONObjIterator i(*this); + BSONObjIterator j(r); + BSONObjIterator k(idxKey); + while (1) { + // so far, equal... + + BSONElement l = i.next(); + BSONElement r = j.next(); + BSONElement o; + if (ordered) + o = k.next(); + if (l.eoo()) + return r.eoo() ? 0 : -1; + if (r.eoo()) return 1; - bool ordered = !idxKey.isEmpty(); - - BSONObjIterator i(*this); - BSONObjIterator j(r); - BSONObjIterator k(idxKey); - while ( 1 ) { - // so far, equal... - - BSONElement l = i.next(); - BSONElement r = j.next(); - BSONElement o; - if ( ordered ) - o = k.next(); - if ( l.eoo() ) - return r.eoo() ? 0 : -1; - if ( r.eoo() ) - return 1; - - int x; - /* + int x; + /* if( ordered && o.type() == String && strcmp(o.valuestr(), "ascii-proto") == 0 && l.type() == String && r.type() == String ) { // note: no negative support yet, as this is just sort of a POC x = _stricmp(l.valuestr(), r.valuestr()); } else*/ { - x = l.woCompare( r, considerFieldName ); - if ( ordered && o.number() < 0 ) - x = -x; - } - if ( x != 0 ) - return x; - } - return -1; - } - - BSONObj staticNull = fromjson( "{'':null}" ); - BSONObj makeUndefined() { - BSONObjBuilder b; - b.appendUndefined( "" ); - return b.obj(); - } - BSONObj staticUndefined = makeUndefined(); - - /* well ordered compare */ - int BSONObj::woSortOrder(const BSONObj& other, const BSONObj& sortKey , bool useDotted ) const { - if ( isEmpty() ) - return other.isEmpty() ? 0 : -1; - if ( other.isEmpty() ) - return 1; - - uassert( 10060 , "woSortOrder needs a non-empty sortKey" , ! sortKey.isEmpty() ); - - BSONObjIterator i(sortKey); - while ( 1 ) { - BSONElement f = i.next(); - if ( f.eoo() ) - return 0; - - BSONElement l = useDotted ? getFieldDotted( f.fieldName() ) : getField( f.fieldName() ); - if ( l.eoo() ) - l = staticNull.firstElement(); - BSONElement r = useDotted ? other.getFieldDotted( f.fieldName() ) : other.getField( f.fieldName() ); - if ( r.eoo() ) - r = staticNull.firstElement(); - - int x = l.woCompare( r, false ); - if ( f.number() < 0 ) + x = l.woCompare(r, considerFieldName); + if (ordered && o.number() < 0) x = -x; - if ( x != 0 ) - return x; - } - return -1; - } - - size_t BSONObj::Hasher::operator()(const BSONObj& obj) const { - size_t hash = 0; - BSONForEach(elem, obj) { - boost::hash_combine(hash, BSONElement::Hasher()(elem)); } - return hash; - } - - bool BSONObj::isPrefixOf( const BSONObj& otherObj ) const { - BSONObjIterator a( *this ); - BSONObjIterator b( otherObj ); - - while ( a.more() && b.more() ) { - BSONElement x = a.next(); - BSONElement y = b.next(); - if ( x != y ) - return false; - } - - return ! a.more(); - } - - bool BSONObj::isFieldNamePrefixOf( const BSONObj& otherObj ) const { - BSONObjIterator a( *this ); - BSONObjIterator b( otherObj ); - - while ( a.more() && b.more() ) { - BSONElement x = a.next(); - BSONElement y = b.next(); - if ( ! str::equals( x.fieldName() , y.fieldName() ) ) { - return false; - } - } - - return ! a.more(); - } - - template <typename BSONElementColl> - void _getFieldsDotted( const BSONObj* obj, StringData name, BSONElementColl &ret, bool expandLastArray ) { - BSONElement e = obj->getField( name ); - - if ( e.eoo() ) { - size_t idx = name.find( '.' ); - if ( idx != string::npos ) { - StringData left = name.substr( 0, idx ); - StringData next = name.substr( idx + 1, name.size() ); - - BSONElement e = obj->getField( left ); - - if (e.type() == Object) { - e.embeddedObject().getFieldsDotted(next, ret, expandLastArray ); + if (x != 0) + return x; + } + return -1; +} + +BSONObj staticNull = fromjson("{'':null}"); +BSONObj makeUndefined() { + BSONObjBuilder b; + b.appendUndefined(""); + return b.obj(); +} +BSONObj staticUndefined = makeUndefined(); + +/* well ordered compare */ +int BSONObj::woSortOrder(const BSONObj& other, const BSONObj& sortKey, bool useDotted) const { + if (isEmpty()) + return other.isEmpty() ? 0 : -1; + if (other.isEmpty()) + return 1; + + uassert(10060, "woSortOrder needs a non-empty sortKey", !sortKey.isEmpty()); + + BSONObjIterator i(sortKey); + while (1) { + BSONElement f = i.next(); + if (f.eoo()) + return 0; + + BSONElement l = useDotted ? getFieldDotted(f.fieldName()) : getField(f.fieldName()); + if (l.eoo()) + l = staticNull.firstElement(); + BSONElement r = + useDotted ? other.getFieldDotted(f.fieldName()) : other.getField(f.fieldName()); + if (r.eoo()) + r = staticNull.firstElement(); + + int x = l.woCompare(r, false); + if (f.number() < 0) + x = -x; + if (x != 0) + return x; + } + return -1; +} + +size_t BSONObj::Hasher::operator()(const BSONObj& obj) const { + size_t hash = 0; + BSONForEach(elem, obj) { + boost::hash_combine(hash, BSONElement::Hasher()(elem)); + } + return hash; +} + +bool BSONObj::isPrefixOf(const BSONObj& otherObj) const { + BSONObjIterator a(*this); + BSONObjIterator b(otherObj); + + while (a.more() && b.more()) { + BSONElement x = a.next(); + BSONElement y = b.next(); + if (x != y) + return false; + } + + return !a.more(); +} + +bool BSONObj::isFieldNamePrefixOf(const BSONObj& otherObj) const { + BSONObjIterator a(*this); + BSONObjIterator b(otherObj); + + while (a.more() && b.more()) { + BSONElement x = a.next(); + BSONElement y = b.next(); + if (!str::equals(x.fieldName(), y.fieldName())) { + return false; + } + } + + return !a.more(); +} + +template <typename BSONElementColl> +void _getFieldsDotted(const BSONObj* obj, + StringData name, + BSONElementColl& ret, + bool expandLastArray) { + BSONElement e = obj->getField(name); + + if (e.eoo()) { + size_t idx = name.find('.'); + if (idx != string::npos) { + StringData left = name.substr(0, idx); + StringData next = name.substr(idx + 1, name.size()); + + BSONElement e = obj->getField(left); + + if (e.type() == Object) { + e.embeddedObject().getFieldsDotted(next, ret, expandLastArray); + } else if (e.type() == Array) { + bool allDigits = false; + if (next.size() > 0 && isdigit(next[0])) { + unsigned temp = 1; + while (temp < next.size() && isdigit(next[temp])) + temp++; + allDigits = temp == next.size() || next[temp] == '.'; } - else if (e.type() == Array) { - bool allDigits = false; - if ( next.size() > 0 && isdigit( next[0] ) ) { - unsigned temp = 1; - while ( temp < next.size() && isdigit( next[temp] ) ) - temp++; - allDigits = temp == next.size() || next[temp] == '.'; - } - if (allDigits) { - e.embeddedObject().getFieldsDotted(next, ret, expandLastArray ); - } - else { - BSONObjIterator i(e.embeddedObject()); - while ( i.more() ) { - BSONElement e2 = i.next(); - if (e2.type() == Object || e2.type() == Array) - e2.embeddedObject().getFieldsDotted(next, ret, expandLastArray ); - } + if (allDigits) { + e.embeddedObject().getFieldsDotted(next, ret, expandLastArray); + } else { + BSONObjIterator i(e.embeddedObject()); + while (i.more()) { + BSONElement e2 = i.next(); + if (e2.type() == Object || e2.type() == Array) + e2.embeddedObject().getFieldsDotted(next, ret, expandLastArray); } } - else { - // do nothing: no match - } - } - } - else { - if (e.type() == Array && expandLastArray) { - BSONObjIterator i(e.embeddedObject()); - while ( i.more() ) - ret.insert(i.next()); - } - else { - ret.insert(e); + } else { + // do nothing: no match } } - } - - void BSONObj::getFieldsDotted(StringData name, BSONElementSet &ret, bool expandLastArray ) const { - _getFieldsDotted( this, name, ret, expandLastArray ); - } - void BSONObj::getFieldsDotted(StringData name, BSONElementMSet &ret, bool expandLastArray ) const { - _getFieldsDotted( this, name, ret, expandLastArray ); - } - - BSONElement eooElement; - - BSONElement BSONObj::getFieldDottedOrArray(const char *&name) const { - const char *p = strchr(name, '.'); - - BSONElement sub; - - if ( p ) { - sub = getField( string(name, p-name) ); - name = p + 1; - } - else { - sub = getField( name ); - name = name + strlen(name); - } - - if ( sub.eoo() ) - return eooElement; - else if ( sub.type() == Array || name[0] == '\0' ) - return sub; - else if ( sub.type() == Object ) - return sub.embeddedObject().getFieldDottedOrArray( name ); - else - return eooElement; - } - - BSONObj BSONObj::extractFieldsUnDotted(const BSONObj& pattern) const { - BSONObjBuilder b; - BSONObjIterator i(pattern); - while ( i.moreWithEOO() ) { - BSONElement e = i.next(); - if ( e.eoo() ) - break; - BSONElement x = getField(e.fieldName()); - if ( !x.eoo() ) - b.appendAs(x, ""); - } - return b.obj(); - } - - BSONObj BSONObj::extractFields(const BSONObj& pattern , bool fillWithNull ) const { - BSONObjBuilder b(32); // scanandorder.h can make a zillion of these, so we start the allocation very small - BSONObjIterator i(pattern); - while ( i.moreWithEOO() ) { - BSONElement e = i.next(); - if ( e.eoo() ) - break; - BSONElement x = getFieldDotted(e.fieldName()); - if ( ! x.eoo() ) - b.appendAs( x, e.fieldName() ); - else if ( fillWithNull ) - b.appendNull( e.fieldName() ); - } - return b.obj(); - } - - BSONObj BSONObj::filterFieldsUndotted( const BSONObj &filter, bool inFilter ) const { - BSONObjBuilder b; - BSONObjIterator i( *this ); - while( i.moreWithEOO() ) { - BSONElement e = i.next(); - if ( e.eoo() ) + } else { + if (e.type() == Array && expandLastArray) { + BSONObjIterator i(e.embeddedObject()); + while (i.more()) + ret.insert(i.next()); + } else { + ret.insert(e); + } + } +} + +void BSONObj::getFieldsDotted(StringData name, BSONElementSet& ret, bool expandLastArray) const { + _getFieldsDotted(this, name, ret, expandLastArray); +} +void BSONObj::getFieldsDotted(StringData name, BSONElementMSet& ret, bool expandLastArray) const { + _getFieldsDotted(this, name, ret, expandLastArray); +} + +BSONElement eooElement; + +BSONElement BSONObj::getFieldDottedOrArray(const char*& name) const { + const char* p = strchr(name, '.'); + + BSONElement sub; + + if (p) { + sub = getField(string(name, p - name)); + name = p + 1; + } else { + sub = getField(name); + name = name + strlen(name); + } + + if (sub.eoo()) + return eooElement; + else if (sub.type() == Array || name[0] == '\0') + return sub; + else if (sub.type() == Object) + return sub.embeddedObject().getFieldDottedOrArray(name); + else + return eooElement; +} + +BSONObj BSONObj::extractFieldsUnDotted(const BSONObj& pattern) const { + BSONObjBuilder b; + BSONObjIterator i(pattern); + while (i.moreWithEOO()) { + BSONElement e = i.next(); + if (e.eoo()) + break; + BSONElement x = getField(e.fieldName()); + if (!x.eoo()) + b.appendAs(x, ""); + } + return b.obj(); +} + +BSONObj BSONObj::extractFields(const BSONObj& pattern, bool fillWithNull) const { + BSONObjBuilder b( + 32); // scanandorder.h can make a zillion of these, so we start the allocation very small + BSONObjIterator i(pattern); + while (i.moreWithEOO()) { + BSONElement e = i.next(); + if (e.eoo()) + break; + BSONElement x = getFieldDotted(e.fieldName()); + if (!x.eoo()) + b.appendAs(x, e.fieldName()); + else if (fillWithNull) + b.appendNull(e.fieldName()); + } + return b.obj(); +} + +BSONObj BSONObj::filterFieldsUndotted(const BSONObj& filter, bool inFilter) const { + BSONObjBuilder b; + BSONObjIterator i(*this); + while (i.moreWithEOO()) { + BSONElement e = i.next(); + if (e.eoo()) + break; + BSONElement x = filter.getField(e.fieldName()); + if ((x.eoo() && !inFilter) || (!x.eoo() && inFilter)) + b.append(e); + } + return b.obj(); +} + +BSONElement BSONObj::getFieldUsingIndexNames(StringData fieldName, const BSONObj& indexKey) const { + BSONObjIterator i(indexKey); + int j = 0; + while (i.moreWithEOO()) { + BSONElement f = i.next(); + if (f.eoo()) + return BSONElement(); + if (f.fieldName() == fieldName) + break; + ++j; + } + BSONObjIterator k(*this); + while (k.moreWithEOO()) { + BSONElement g = k.next(); + if (g.eoo()) + return BSONElement(); + if (j == 0) { + return g; + } + --j; + } + return BSONElement(); +} + +/* grab names of all the fields in this object */ +int BSONObj::getFieldNames(set<string>& fields) const { + int n = 0; + BSONObjIterator i(*this); + while (i.moreWithEOO()) { + BSONElement e = i.next(); + if (e.eoo()) + break; + fields.insert(e.fieldName()); + n++; + } + return n; +} + +/* note: addFields always adds _id even if not specified + returns n added not counting _id unless requested. +*/ +int BSONObj::addFields(BSONObj& from, set<string>& fields) { + verify(isEmpty() && !isOwned()); /* partial implementation for now... */ + + BSONObjBuilder b; + + int N = fields.size(); + int n = 0; + BSONObjIterator i(from); + bool gotId = false; + while (i.moreWithEOO()) { + BSONElement e = i.next(); + const char* fname = e.fieldName(); + if (fields.count(fname)) { + b.append(e); + ++n; + gotId = gotId || strcmp(fname, "_id") == 0; + if (n == N && gotId) break; - BSONElement x = filter.getField( e.fieldName() ); - if ( ( x.eoo() && !inFilter ) || - ( !x.eoo() && inFilter ) ) - b.append( e ); - } - return b.obj(); - } - - BSONElement BSONObj::getFieldUsingIndexNames(StringData fieldName, - const BSONObj &indexKey) const { - BSONObjIterator i( indexKey ); - int j = 0; - while( i.moreWithEOO() ) { - BSONElement f = i.next(); - if ( f.eoo() ) - return BSONElement(); - if ( f.fieldName() == fieldName ) + } else if (strcmp(fname, "_id") == 0) { + b.append(e); + gotId = true; + if (n == N && gotId) break; - ++j; - } - BSONObjIterator k( *this ); - while( k.moreWithEOO() ) { - BSONElement g = k.next(); - if ( g.eoo() ) - return BSONElement(); - if ( j == 0 ) { - return g; - } - --j; } - return BSONElement(); } - /* grab names of all the fields in this object */ - int BSONObj::getFieldNames(set<string>& fields) const { - int n = 0; - BSONObjIterator i(*this); - while ( i.moreWithEOO() ) { - BSONElement e = i.next(); - if ( e.eoo() ) - break; - fields.insert(e.fieldName()); - n++; - } - return n; + if (n) { + *this = b.obj(); } - /* note: addFields always adds _id even if not specified - returns n added not counting _id unless requested. - */ - int BSONObj::addFields(BSONObj& from, set<string>& fields) { - verify( isEmpty() && !isOwned() ); /* partial implementation for now... */ + return n; +} - BSONObjBuilder b; - - int N = fields.size(); - int n = 0; - BSONObjIterator i(from); - bool gotId = false; - while ( i.moreWithEOO() ) { - BSONElement e = i.next(); - const char *fname = e.fieldName(); - if ( fields.count(fname) ) { - b.append(e); - ++n; - gotId = gotId || strcmp(fname, "_id")==0; - if ( n == N && gotId ) - break; - } - else if ( strcmp(fname, "_id")==0 ) { - b.append(e); - gotId = true; - if ( n == N && gotId ) - break; - } - } - - if ( n ) { - *this = b.obj(); - } +bool BSONObj::couldBeArray() const { + BSONObjIterator i(*this); + int index = 0; + while (i.moreWithEOO()) { + BSONElement e = i.next(); + if (e.eoo()) + break; - return n; + // TODO: If actually important, may be able to do int->char* much faster + if (strcmp(e.fieldName(), ((string)(str::stream() << index)).c_str()) != 0) + return false; + index++; } + return true; +} - bool BSONObj::couldBeArray() const { - BSONObjIterator i( *this ); - int index = 0; - while( i.moreWithEOO() ){ - BSONElement e = i.next(); - if( e.eoo() ) break; - - // TODO: If actually important, may be able to do int->char* much faster - if( strcmp( e.fieldName(), ((string)( str::stream() << index )).c_str() ) != 0 ) - return false; - index++; - } - return true; - } - - BSONObj BSONObj::clientReadable() const { - BSONObjBuilder b; - BSONObjIterator i( *this ); - while( i.moreWithEOO() ) { - BSONElement e = i.next(); - if ( e.eoo() ) - break; - switch( e.type() ) { +BSONObj BSONObj::clientReadable() const { + BSONObjBuilder b; + BSONObjIterator i(*this); + while (i.moreWithEOO()) { + BSONElement e = i.next(); + if (e.eoo()) + break; + switch (e.type()) { case MinKey: { BSONObjBuilder m; - m.append( "$minElement", 1 ); - b.append( e.fieldName(), m.done() ); + m.append("$minElement", 1); + b.append(e.fieldName(), m.done()); break; } case MaxKey: { BSONObjBuilder m; - m.append( "$maxElement", 1 ); - b.append( e.fieldName(), m.done() ); + m.append("$maxElement", 1); + b.append(e.fieldName(), m.done()); break; } default: - b.append( e ); - } + b.append(e); } - return b.obj(); } + return b.obj(); +} - BSONObj BSONObj::replaceFieldNames( const BSONObj &names ) const { - BSONObjBuilder b; - BSONObjIterator i( *this ); - BSONObjIterator j( names ); - BSONElement f = j.moreWithEOO() ? j.next() : BSONObj().firstElement(); - while( i.moreWithEOO() ) { - BSONElement e = i.next(); - if ( e.eoo() ) - break; - if ( !f.eoo() ) { - b.appendAs( e, f.fieldName() ); - f = j.next(); - } - else { - b.append( e ); - } +BSONObj BSONObj::replaceFieldNames(const BSONObj& names) const { + BSONObjBuilder b; + BSONObjIterator i(*this); + BSONObjIterator j(names); + BSONElement f = j.moreWithEOO() ? j.next() : BSONObj().firstElement(); + while (i.moreWithEOO()) { + BSONElement e = i.next(); + if (e.eoo()) + break; + if (!f.eoo()) { + b.appendAs(e, f.fieldName()); + f = j.next(); + } else { + b.append(e); } - return b.obj(); } + return b.obj(); +} - Status BSONObj::_okForStorage(bool root, bool deep) const { - BSONObjIterator i( *this ); - - // The first field is special in the case of a DBRef where the first field must be $ref - bool first = true; - while ( i.more() ) { - BSONElement e = i.next(); - const char* name = e.fieldName(); - - // Cannot start with "$", unless dbref which must start with ($ref, $id) - if (str::startsWith(name, '$')) { - if ( first && - // $ref is a collection name and must be a String - str::equals(name, "$ref") && e.type() == String && - str::equals(i.next().fieldName(), "$id") ) { - - first = false; - // keep inspecting fields for optional "$db" - e = i.next(); - name = e.fieldName(); // "" if eoo() +Status BSONObj::_okForStorage(bool root, bool deep) const { + BSONObjIterator i(*this); - // optional $db field must be a String - if (str::equals(name, "$db") && e.type() == String) { - continue; //this element is fine, so continue on to siblings (if any more) - } + // The first field is special in the case of a DBRef where the first field must be $ref + bool first = true; + while (i.more()) { + BSONElement e = i.next(); + const char* name = e.fieldName(); + + // Cannot start with "$", unless dbref which must start with ($ref, $id) + if (str::startsWith(name, '$')) { + if (first && + // $ref is a collection name and must be a String + str::equals(name, "$ref") && + e.type() == String && str::equals(i.next().fieldName(), "$id")) { + first = false; + // keep inspecting fields for optional "$db" + e = i.next(); + name = e.fieldName(); // "" if eoo() - // Can't start with a "$", all other checks are done below (outside if blocks) - if (str::startsWith(name, '$')) { - return Status(ErrorCodes::DollarPrefixedFieldName, - str::stream() << name << " is not valid for storage."); - } + // optional $db field must be a String + if (str::equals(name, "$db") && e.type() == String) { + continue; // this element is fine, so continue on to siblings (if any more) } - else { - // not an okay, $ prefixed field name. + + // Can't start with a "$", all other checks are done below (outside if blocks) + if (str::startsWith(name, '$')) { return Status(ErrorCodes::DollarPrefixedFieldName, str::stream() << name << " is not valid for storage."); } - } - - // Do not allow "." in the field name - if (strchr(name, '.')) { - return Status(ErrorCodes::DottedFieldName, + } else { + // not an okay, $ prefixed field name. + return Status(ErrorCodes::DollarPrefixedFieldName, str::stream() << name << " is not valid for storage."); } + } - // (SERVER-9502) Do not allow storing an _id field with a RegEx type or - // Array type in a root document - if (root && (e.type() == RegEx || e.type() == Array || e.type() == Undefined) - && str::equals(name,"_id")) { - return Status(ErrorCodes::InvalidIdField, - str::stream() << name - << " is not valid for storage because it is of type " - << typeName(e.type())); - } + // Do not allow "." in the field name + if (strchr(name, '.')) { + return Status(ErrorCodes::DottedFieldName, + str::stream() << name << " is not valid for storage."); + } + + // (SERVER-9502) Do not allow storing an _id field with a RegEx type or + // Array type in a root document + if (root && (e.type() == RegEx || e.type() == Array || e.type() == Undefined) && + str::equals(name, "_id")) { + return Status(ErrorCodes::InvalidIdField, + str::stream() << name + << " is not valid for storage because it is of type " + << typeName(e.type())); + } - if ( deep && e.mayEncapsulate() ) { - switch ( e.type() ) { + if (deep && e.mayEncapsulate()) { + switch (e.type()) { case Object: - case Array: - { - Status s = e.embeddedObject()._okForStorage(false, true); - // TODO: combine field names for better error messages - if ( ! s.isOK() ) - return s; - } - break; - case CodeWScope: - { - Status s = e.codeWScopeObject()._okForStorage(false, true); - // TODO: combine field names for better error messages - if ( ! s.isOK() ) - return s; - } - break; + case Array: { + Status s = e.embeddedObject()._okForStorage(false, true); + // TODO: combine field names for better error messages + if (!s.isOK()) + return s; + } break; + case CodeWScope: { + Status s = e.codeWScopeObject()._okForStorage(false, true); + // TODO: combine field names for better error messages + if (!s.isOK()) + return s; + } break; default: - uassert( 12579, "unhandled cases in BSONObj okForStorage" , 0 ); - } + uassert(12579, "unhandled cases in BSONObj okForStorage", 0); } - - // After we have processed one field, we are no longer on the first field - first = false; - } - return Status::OK(); - } - - void BSONObj::dump() const { - LogstreamBuilder builder = log(); - builder << hex; - const char *p = objdata(); - for ( int i = 0; i < objsize(); i++ ) { - builder << i << '\t' << ( 0xff & ( (unsigned) *p ) ); - if ( *p >= 'A' && *p <= 'z' ) - builder << '\t' << *p; - builder << endl; - p++; } + + // After we have processed one field, we are no longer on the first field + first = false; } + return Status::OK(); +} - 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; - } - } - } +void BSONObj::dump() const { + LogstreamBuilder builder = log(); + builder << hex; + const char* p = objdata(); + for (int i = 0; i < objsize(); i++) { + builder << i << '\t' << (0xff & ((unsigned)*p)); + if (*p >= 'A' && *p <= 'z') + builder << '\t' << *p; + builder << endl; + p++; } +} - BSONElement BSONObj::getField(StringData name) const { - BSONObjIterator i(*this); - while ( i.more() ) { - BSONElement e = i.next(); - // We know that e has a cached field length since BSONObjIterator::next internally - // called BSONElement::size on the BSONElement that it returned, so it is more - // efficient to re-use that information by obtaining the field name as a - // StringData, which will be pre-populated with the cached length. - if ( name == e.fieldNameStringData() ) - return e; +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; + } } - return BSONElement(); } +} - int BSONObj::getIntField(StringData name) const { - BSONElement e = getField(name); - return e.isNumber() ? (int) e.number() : std::numeric_limits< int >::min(); +BSONElement BSONObj::getField(StringData name) const { + BSONObjIterator i(*this); + while (i.more()) { + BSONElement e = i.next(); + // We know that e has a cached field length since BSONObjIterator::next internally + // called BSONElement::size on the BSONElement that it returned, so it is more + // efficient to re-use that information by obtaining the field name as a + // StringData, which will be pre-populated with the cached length. + if (name == e.fieldNameStringData()) + return e; + } + return BSONElement(); +} + +int BSONObj::getIntField(StringData name) const { + BSONElement e = getField(name); + return e.isNumber() ? (int)e.number() : std::numeric_limits<int>::min(); +} + +bool BSONObj::getBoolField(StringData name) const { + BSONElement e = getField(name); + return e.type() == Bool ? e.boolean() : false; +} + +const char* BSONObj::getStringField(StringData name) const { + BSONElement e = getField(name); + return e.type() == String ? e.valuestr() : ""; +} + +bool BSONObj::getObjectID(BSONElement& e) const { + BSONElement f = getField("_id"); + if (!f.eoo()) { + e = f; + return true; } + return false; +} - bool BSONObj::getBoolField(StringData name) const { - BSONElement e = getField(name); - return e.type() == Bool ? e.boolean() : false; +BSONObj BSONObj::removeField(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(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(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; +} + +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 ? " ]" : " }"); +} - const char * BSONObj::getStringField(StringData name) const { - BSONElement e = getField(name); - return e.type() == String ? e.valuestr() : ""; - } +Status DataType::Handler<BSONObj>::load( + BSONObj* bson, const char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) { + auto len = ConstDataRange(ptr, ptr + length).read<LittleEndian<uint32_t>>(); - bool BSONObj::getObjectID(BSONElement& e) const { - BSONElement f = getField("_id"); - if( !f.eoo() ) { - e = f; - return true; - } - return false; + if (!len.isOK()) { + mongoutils::str::stream ss; + ss << "buffer size too small to read length at offset: " << debug_offset; + return Status(ErrorCodes::InvalidBSON, ss); } - BSONObj BSONObj::removeField(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(); + if (len.getValue() > length) { + mongoutils::str::stream ss; + ss << "length (" << len.getValue() << ") greater than buffer size (" << length + << ") at offset: " << debug_offset; + return Status(ErrorCodes::InvalidBSON, ss); } - - void BSONObj::elems(std::vector<BSONElement> &v) const { - BSONObjIterator i(*this); - while( i.more() ) - v.push_back(i.next()); + if (len.getValue() < BSONObj::kMinBSONLength) { + mongoutils::str::stream ss; + ss << "Invalid bson length (" << len.getValue() << ") at offset: " << debug_offset; + return Status(ErrorCodes::InvalidBSON, ss); } - void BSONObj::elems(std::list<BSONElement> &v) const { - BSONObjIterator i(*this); - while( i.more() ) - v.push_back(i.next()); - } + try { + BSONObj temp(ptr); - /* return has eoo() true if no match - supports "." notation to reach into embedded objects - */ - BSONElement BSONObj::getFieldDotted(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); - } + if (bson) { + *bson = std::move(temp); } + } catch (...) { + auto status = exceptionToStatus(); + mongoutils::str::stream ss; + ss << status.reason() << " at offset: " << debug_offset; - return e; + return Status(status.code(), ss); } - BSONObj BSONObj::getObjectField(StringData name) const { - BSONElement e = getField(name); - BSONType t = e.type(); - return t == Object || t == Array ? e.embeddedObject() : BSONObj(); + if (advanced) { + *advanced = len.getValue(); } - int BSONObj::nFields() const { - int n = 0; - BSONObjIterator i(*this); - while ( i.moreWithEOO() ) { - BSONElement e = i.next(); - if ( e.eoo() ) - break; - n++; - } - return n; - } + return Status::OK(); +} - std::string BSONObj::toString( bool isArray, bool full ) const { - if ( isEmpty() ) return (isArray ? "[]" : "{}"); - StringBuilder s; - toString(s, isArray, full); - return s.str(); +Status DataType::Handler<BSONObj>::store( + const BSONObj& bson, char* ptr, size_t length, size_t* advanced, std::ptrdiff_t debug_offset) { + if (bson.objsize() > static_cast<int>(length)) { + mongoutils::str::stream ss; + ss << "buffer too small to write bson of size (" << bson.objsize() + << ") at offset: " << debug_offset; + return Status(ErrorCodes::Overflow, ss); } - 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 ? " ]" : " }" ); + if (ptr) { + std::memcpy(ptr, bson.objdata(), bson.objsize()); } - Status DataType::Handler<BSONObj>::load(BSONObj* bson, const char *ptr, size_t length, - size_t *advanced, std::ptrdiff_t debug_offset) { - auto len = ConstDataRange(ptr, ptr + length).read<LittleEndian<uint32_t>>(); - - if (!len.isOK()) { - mongoutils::str::stream ss; - ss << "buffer size too small to read length at offset: " << debug_offset; - return Status(ErrorCodes::InvalidBSON, ss); - } - - if (len.getValue() > length) { - mongoutils::str::stream ss; - ss << "length (" << len.getValue() << ") greater than buffer size (" - << length << ") at offset: " << debug_offset; - return Status(ErrorCodes::InvalidBSON, ss); - } - - if (len.getValue() < BSONObj::kMinBSONLength) { - mongoutils::str::stream ss; - ss << "Invalid bson length (" << len.getValue() << ") at offset: " - << debug_offset; - return Status(ErrorCodes::InvalidBSON, ss); - } - - try { - BSONObj temp(ptr); - - if (bson) { - *bson = std::move(temp); - } - } - catch (...) { - auto status = exceptionToStatus(); - mongoutils::str::stream ss; - ss << status.reason() << " at offset: " << debug_offset; - - return Status(status.code(), ss); - } - - if (advanced) { - *advanced = len.getValue(); - } - - return Status::OK(); + if (advanced) { + *advanced = bson.objsize(); } - Status DataType::Handler<BSONObj>::store(const BSONObj& bson, char *ptr, size_t length, - size_t *advanced, std::ptrdiff_t debug_offset) { - if (bson.objsize() > static_cast<int>(length)) { - mongoutils::str::stream ss; - ss << "buffer too small to write bson of size (" << bson.objsize() - << ") at offset: " << debug_offset; - return Status(ErrorCodes::Overflow, ss); - } - - if (ptr) { - std::memcpy(ptr, bson.objdata(), bson.objsize()); - } + return Status::OK(); +} - if (advanced) { - *advanced = bson.objsize(); - } +std::ostream& operator<<(std::ostream& s, const BSONObj& o) { + return s << o.toString(); +} - return Status::OK(); - } +StringBuilder& operator<<(StringBuilder& s, const BSONObj& o) { + o.toString(s); + return s; +} - 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; - } +/** Compare two bson elements, provided as const char *'s, by field name. */ +class BSONIteratorSorted::ElementFieldCmp { +public: + ElementFieldCmp(bool isArray); + bool operator()(const char* s1, const char* s2) const; - /** Compare two bson elements, provided as const char *'s, by field name. */ - class BSONIteratorSorted::ElementFieldCmp { - public: - ElementFieldCmp( bool isArray ); - bool operator()( const char *s1, const char *s2 ) const; - private: - LexNumCmp _cmp; - }; +private: + LexNumCmp _cmp; +}; - BSONIteratorSorted::ElementFieldCmp::ElementFieldCmp( bool isArray ) : - _cmp( !isArray ) { - } +BSONIteratorSorted::ElementFieldCmp::ElementFieldCmp(bool isArray) : _cmp(!isArray) {} - bool BSONIteratorSorted::ElementFieldCmp::operator()( const char *s1, const char *s2 ) - const { - // Skip the type byte and compare field names. - return _cmp( s1 + 1, s2 + 1 ); - } +bool BSONIteratorSorted::ElementFieldCmp::operator()(const char* s1, const char* s2) const { + // Skip the type byte and compare field names. + return _cmp(s1 + 1, s2 + 1); +} - BSONIteratorSorted::BSONIteratorSorted( const BSONObj &o, const ElementFieldCmp &cmp ) - : _nfields(o.nFields()), _fields(new const char*[_nfields]) { - int x = 0; - BSONObjIterator i( o ); - while ( i.more() ) { - _fields[x++] = i.next().rawdata(); - verify( _fields[x-1] ); - } - verify( x == _nfields ); - std::sort( _fields.get() , _fields.get() + _nfields , cmp ); - _cur = 0; +BSONIteratorSorted::BSONIteratorSorted(const BSONObj& o, const ElementFieldCmp& cmp) + : _nfields(o.nFields()), _fields(new const char* [_nfields]) { + int x = 0; + BSONObjIterator i(o); + while (i.more()) { + _fields[x++] = i.next().rawdata(); + verify(_fields[x - 1]); } + verify(x == _nfields); + std::sort(_fields.get(), _fields.get() + _nfields, cmp); + _cur = 0; +} - BSONObjIteratorSorted::BSONObjIteratorSorted( const BSONObj &object ) : - BSONIteratorSorted( object, ElementFieldCmp( false ) ) { - } +BSONObjIteratorSorted::BSONObjIteratorSorted(const BSONObj& object) + : BSONIteratorSorted(object, ElementFieldCmp(false)) {} - BSONArrayIteratorSorted::BSONArrayIteratorSorted( const BSONArray &array ) : - BSONIteratorSorted( array, ElementFieldCmp( true ) ) { - } +BSONArrayIteratorSorted::BSONArrayIteratorSorted(const BSONArray& array) + : BSONIteratorSorted(array, ElementFieldCmp(true)) {} -} // namespace mongo +} // namespace mongo |