diff options
author | Eric Milkie <milkie@10gen.com> | 2014-07-11 07:55:21 -0400 |
---|---|---|
committer | Eric Milkie <milkie@10gen.com> | 2014-07-17 11:54:45 -0400 |
commit | 980bcad5c1789ea470417c5eae4619f39288685f (patch) | |
tree | e49bde0f5fb9ef9ef57feaadb0ff4c51d44bb41c /src/mongo/bson/bsonelement.cpp | |
parent | 2b55561913f26870b0d4b1c8ec180139819eeba6 (diff) | |
download | mongo-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.cpp | 550 |
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 |