summaryrefslogtreecommitdiff
path: root/src/mongo/bson/bsonelement.cpp
diff options
context:
space:
mode:
authorEric Milkie <milkie@10gen.com>2014-07-11 07:55:21 -0400
committerEric Milkie <milkie@10gen.com>2014-07-17 11:54:45 -0400
commit980bcad5c1789ea470417c5eae4619f39288685f (patch)
treee49bde0f5fb9ef9ef57feaadb0ff4c51d44bb41c /src/mongo/bson/bsonelement.cpp
parent2b55561913f26870b0d4b1c8ec180139819eeba6 (diff)
downloadmongo-980bcad5c1789ea470417c5eae4619f39288685f.tar.gz
SERVER-14592 de-inline bson and remove bson namespace
Diffstat (limited to 'src/mongo/bson/bsonelement.cpp')
-rw-r--r--src/mongo/bson/bsonelement.cpp550
1 files changed, 550 insertions, 0 deletions
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