// bsoninlines.h /* Copyright 2009 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include "util/atomic_int.h" #include "util/misc.h" #include "../util/hex.h" namespace mongo { inline BSONObjIterator BSONObj::begin() { return BSONObjIterator(*this); } inline BSONObj BSONElement::embeddedObjectUserCheck() const { uassert( 10065 , "invalid parameter: expected an object", isABSONObj() ); return BSONObj(value()); } inline BSONObj BSONElement::embeddedObject() const { assert( isABSONObj() ); return BSONObj(value()); } inline BSONObj BSONElement::codeWScopeObject() const { assert( type() == CodeWScope ); int strSizeWNull = *(int *)( value() + 4 ); return BSONObj( value() + 4 + 4 + strSizeWNull ); } inline BSONObj BSONObj::copy() const { char *p = (char*) malloc(objsize()); memcpy(p, objdata(), objsize()); return BSONObj(p, true); } // 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 char * newName ) const { BSONObjBuilder b(size()+6+(int)strlen(newName)); b.appendAs(*this,newName); return b.obj(); } inline bool BSONObj::hasElement(const char *name) const { if ( !isEmpty() ) { BSONObjIterator it(*this); while ( it.moreWithEOO() ) { BSONElement e = it.next(); if ( strcmp(name, e.fieldName()) == 0 ) return true; } } return false; } inline BSONElement BSONObj::getField(const StringData& name) const { BSONObjIterator i(*this); while ( i.more() ) { BSONElement e = i.next(); if ( strcmp(e.fieldName(), name.data()) == 0 ) return e; } return BSONElement(); } /* add all the fields from the object specified to this object */ inline BSONObjBuilder& BSONObjBuilder::appendElements(BSONObj x) { BSONObjIterator it(x); while ( it.moreWithEOO() ) { BSONElement e = it.next(); if ( e.eoo() ) break; append(e); } return *this; } inline bool BSONObj::isValid(){ int x = objsize(); return x > 0 && x <= 1024 * 1024 * 8; } 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 ) { _fieldName = 0; _builder = builder; } template inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<( T value ) { _builder->append(_fieldName, value); _fieldName = 0; return *_builder; } inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const BSONElement& e ) { _builder->appendAs( e , _fieldName ); _fieldName = 0; return *_builder; } inline Labeler BSONObjBuilderValueStream::operator<<( const Labeler::Label &l ) { return Labeler( l, this ); } inline void BSONObjBuilderValueStream::endField( const char *nextFieldName ) { if ( _fieldName && haveSubobj() ) { _builder->append( _fieldName, subobj()->done() ); } _subobj.reset(); _fieldName = nextFieldName; } inline BSONObjBuilder *BSONObjBuilderValueStream::subobj() { if ( !haveSubobj() ) _subobj.reset( new BSONObjBuilder() ); return _subobj.get(); } template inline BSONObjBuilder& Labeler::operator<<( T value ) { s_->subobj()->append( l_.l_, value ); return *s_->_builder; } inline BSONObjBuilder& Labeler::operator<<( const BSONElement& e ) { s_->subobj()->appendAs( e, l_.l_ ); return *s_->_builder; } // {a: {b:1}} -> {a.b:1} void nested2dotted(BSONObjBuilder& b, const BSONObj& obj, const string& base=""); inline BSONObj nested2dotted(const BSONObj& obj){ BSONObjBuilder b; nested2dotted(b, obj); return b.obj(); } // {a.b:1} -> {a: {b:1}} void dotted2nested(BSONObjBuilder& b, const BSONObj& obj); inline BSONObj dotted2nested(const BSONObj& obj){ BSONObjBuilder b; dotted2nested(b, obj); return b.obj(); } inline BSONObjIterator BSONObjBuilder::iterator() const { const char * s = _b.buf() + _offset; const char * e = _b.buf() + _b.len(); return BSONObjIterator( s , e ); } /* WARNING: nested/dotted conversions are not 100% reversible * nested2dotted(dotted2nested({a.b: {c:1}})) -> {a.b.c: 1} * also, dotted2nested ignores order */ typedef map 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 set BSONSortedElements; inline BSONSortedElements bson2set( const BSONObj& obj ){ BSONSortedElements s; BSONObjIterator it(obj); while ( it.more() ) s.insert( it.next() ); return s; } inline string BSONObj::toString( bool isArray, bool full ) const { if ( isEmpty() ) return "{}"; StringBuilder s; toString(s, isArray, full); return s.str(); } inline void BSONObj::toString(StringBuilder& s, bool isArray, bool full ) const { if ( isEmpty() ){ s << "{}"; 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() ); e.validate(); 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 ); } s << ( isArray ? " ]" : " }" ); } extern unsigned getRandomNumber(); inline void BSONElement::validate() const { const BSONType t = type(); switch( t ) { case DBRef: case Code: case Symbol: case mongo::String: { int x = valuestrsize(); if ( x > 0 && valuestr()[x-1] == 0 ) return; StringBuilder buf; buf << "Invalid dbref/code/string/symbol size: " << x << " strnlen:" << mongo::strnlen( valuestr() , x ); msgasserted( 10321 , buf.str() ); break; } case CodeWScope: { int totalSize = *( int * )( value() ); massert( 10322 , "Invalid CodeWScope size", totalSize >= 8 ); int strSizeWNull = *( int * )( value() + 4 ); massert( 10323 , "Invalid CodeWScope string size", totalSize >= strSizeWNull + 4 + 4 ); massert( 10324 , "Invalid CodeWScope string size", strSizeWNull > 0 && (strSizeWNull - 1) == mongo::strnlen( codeWScopeCode(), strSizeWNull ) ); massert( 10325 , "Invalid CodeWScope size", totalSize >= strSizeWNull + 4 + 4 + 4 ); int objSize = *( int * )( value() + 4 + 4 + strSizeWNull ); massert( 10326 , "Invalid CodeWScope object size", totalSize == 4 + 4 + strSizeWNull + objSize ); // Subobject validation handled elsewhere. } case Object: // We expect Object size validation to be handled elsewhere. default: break; } } 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 ) : mongo::strnlen( p, remain ); //massert( 10318 , "Invalid regex string", len1 != -1 ); // ERH - 4/28/10 - don't think this does anything p = p + len1 + 1; size_t len2 = ( maxLen == -1 ) ? strlen( p ) : mongo::strnlen( p, remain - len1 - 1 ); //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(); string msg = ss.str(); massert( 10320 , msg.c_str(),false); } } totalSize = x + fieldNameSize() + 1; // BSONType return totalSize; } inline 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 ) const { if ( includeFieldName && type() != EOO ) s << fieldName() << ": "; switch ( type() ) { case EOO: s << "EOO"; break; case mongo::Date: s << "new Date(" << date() << ')'; break; case RegEx: { s << "/" << regex() << '/'; const char *p = regexFlags(); if ( p ) s << p; } break; case NumberDouble: { stringstream tmp; tmp.precision( 16 ); tmp << number(); string n = tmp.str(); s << n; // indicate this is a double: if( strchr(n.c_str(), '.') == 0 && strchr(n.c_str(), 'E') == 0 && strchr(n.c_str(), 'N') == 0 ) s << ".0"; } 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); break; case mongo::Array: embeddedObject().toString(s, true, full); 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() > 80 ) { s.write(valuestr(), 70); 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"; if (full){ int len; const char* data = binDataClean(len); s << '(' << binDataType() << ", " << 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 char *name) const { BSONElement e = getField( name ); if ( e.eoo() ) { const char *p = strchr(name, '.'); if ( p ) { string left(name, p-name); BSONObj sub = getObjectField(left.c_str()); return sub.isEmpty() ? BSONElement() : sub.getFieldDotted(p+1); } } return e; } inline BSONObj BSONObj::getObjectField(const char *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 */ static char p[] = { 5, 0, 0, 0, 0 }; _objdata = p; } inline BSONObj BSONElement::Obj() const { return embeddedObjectUserCheck(); } inline BSONElement BSONElement::operator[] (const string& field) const { BSONObj o = Obj(); return o[field]; } inline void BSONObj::elems(vector &v) const { BSONObjIterator i(*this); while( i.more() ) v.push_back(i.next()); } inline void BSONObj::elems(list &v) const { BSONObjIterator i(*this); while( i.more() ) v.push_back(i.next()); } template void BSONObj::Vals(vector& v) const { BSONObjIterator i(*this); while( i.more() ) { T t; i.next().Val(t); v.push_back(t); } } template void BSONObj::Vals(list& v) const { BSONObjIterator i(*this); while( i.more() ) { T t; i.next().Val(t); v.push_back(t); } } template void BSONObj::vals(vector& v) const { BSONObjIterator i(*this); while( i.more() ) { try { T t; i.next().Val(t); v.push_back(t); } catch(...) { } } } template void BSONObj::vals(list& v) const { BSONObjIterator i(*this); while( i.more() ) { try { T t; i.next().Val(t); v.push_back(t); } catch(...) { } } } inline ostream& operator<<( ostream &s, const BSONObj &o ) { return s << o.toString(); } inline ostream& operator<<( ostream &s, const BSONElement &e ) { return s << e.toString(); } inline void BSONElement::Val(BSONObj& v) const { v = Obj(); } template inline BSONFieldValue BSONField::query( const char * q , const T& t ) const { BSONObjBuilder b; b.append( q , t ); return BSONFieldValue( _name , b.obj() ); } }