diff options
Diffstat (limited to 'bson/bson-inl.h')
-rw-r--r-- | bson/bson-inl.h | 373 |
1 files changed, 348 insertions, 25 deletions
diff --git a/bson/bson-inl.h b/bson/bson-inl.h index 5b4c490..b86d667 100644 --- a/bson/bson-inl.h +++ b/bson/bson-inl.h @@ -1,4 +1,7 @@ -// bsoninlines.h +/** @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. * @@ -18,18 +21,158 @@ #pragma once #include <map> -#include "util/atomic_int.h" -#include "util/misc.h" -#include "../util/hex.h" +#include <limits> + +#if defined(_WIN32) +#undef max +#undef min +#endif namespace mongo { - inline BSONObjIterator BSONObj::begin() { + inline bool isNaN(double d) { + return d != d; + } + + /* 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 = min(lsz, rsz); + int res = memcmp(l.valuestr(), r.valuestr(), common); + if( res ) + return res; + // longer 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); + } + 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.codeWScopeScopeData() , r.codeWScopeScopeData() ); + if ( f ) + return f; + return 0; + } + default: + assert( false); + } + return -1; + } + + /* wo = "well ordered" */ + 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 ( isABSONObj() ) + if ( MONGO_likely(isABSONObj()) ) return BSONObj(value()); stringstream ss; ss << "invalid parameter: expected an object (" << fieldName() << ")"; @@ -48,6 +191,21 @@ namespace mongo { 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(); @@ -64,9 +222,10 @@ namespace mongo { getOwned() method. the presumption being that is better. */ inline NOINLINE_DECL BSONObj BSONObj::copy() const { - char *p = (char*) malloc(objsize()); - memcpy(p, objdata(), objsize()); - return BSONObj(p, true); + Holder *h = (Holder*) malloc(objsize() + sizeof(unsigned)); + h->zero(); + memcpy(h->data, objdata(), objsize()); + return BSONObj(h); } inline BSONObj BSONObj::getOwned() const { @@ -88,16 +247,18 @@ namespace mongo { 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; + 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; + } } } - return false; } inline BSONElement BSONObj::getField(const StringData& name) const { @@ -110,6 +271,21 @@ namespace mongo { return BSONElement(); } + inline int BSONObj::getIntField(const char *name) const { + BSONElement e = getField(name); + return e.isNumber() ? (int) e.number() : std::numeric_limits< int >::min(); + } + + inline bool BSONObj::getBoolField(const char *name) const { + BSONElement e = getField(name); + return e.type() == Bool ? e.boolean() : false; + } + + inline const char * BSONObj::getStringField(const char *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) { BSONObjIterator it(x); @@ -141,7 +317,7 @@ namespace mongo { } - inline bool BSONObj::isValid() { + inline bool BSONObj::isValid() const { int x = objsize(); return x > 0 && x <= BSONObjMaxInternalSize; } @@ -302,8 +478,6 @@ namespace mongo { s << ( isArray ? " ]" : " }" ); } - extern unsigned getRandomNumber(); - inline void BSONElement::validate() const { const BSONType t = type(); @@ -398,7 +572,7 @@ namespace mongo { break; case RegEx: { const char *p = value(); - size_t len1 = ( maxLen == -1 ) ? strlen( p ) : mongo::strnlen( p, remain ); + 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; @@ -417,7 +591,7 @@ namespace mongo { StringBuilder ss; ss << "BSONElement: bad type " << (int) type(); string msg = ss.str(); - massert( 10320 , msg.c_str(),false); + massert( 13655 , msg.c_str(),false); } } totalSize = x + fieldNameSize() + 1; // BSONType @@ -425,6 +599,72 @@ namespace mongo { 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(); + 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); @@ -438,7 +678,7 @@ namespace mongo { s << "EOO"; break; case mongo::Date: - s << "new Date(" << date() << ')'; + s << "new Date(" << (long long) date() << ')'; break; case RegEx: { s << "/" << regex() << '/'; @@ -492,8 +732,8 @@ namespace mongo { case Symbol: case mongo::String: s << '"'; - if ( !full && valuestrsize() > 80 ) { - s.write(valuestr(), 70); + if ( !full && valuestrsize() > 160 ) { + s.write(valuestr(), 150); s << "...\""; } else { @@ -662,4 +902,87 @@ namespace mongo { b.append( q , t ); return BSONFieldValue<BSONObj>( _name , b.obj() ); } + + // used by jsonString() + inline string escape( string s , bool escape_slash=false) { + StringBuilder ret; + for ( string::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 string BSONObj::hexDump() const { + stringstream ss; + const char *d = objdata(); + int size = objsize(); + for( int i = 0; i < size; ++i ) { + ss.width( 2 ); + ss.fill( '0' ); + ss << hex << (unsigned)(unsigned char)( d[ i ] ) << 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() ); + } + + assert( ! i.more() ); + assert( ! 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( strcmp(name.data(), fname) ) + b.append(e); + } + return b.obj(); + } } |