/** @file jsobj.cpp - BSON implementation http://www.mongodb.org/display/DOCS/BSON */ /* 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. */ #include "stdafx.h" #include "jsobj.h" #include "nonce.h" #include "../util/goodies.h" #include "../util/base64.h" #include "../util/md5.hpp" #include #include "../util/unittest.h" #include "../util/embedded_builder.h" #include "json.h" #include "jsobjmanipulator.h" #include "../util/optime.h" #include #undef assert #define assert xassert // make sure our assumptions are valid BOOST_STATIC_ASSERT( sizeof(int) == 4 ); BOOST_STATIC_ASSERT( sizeof(long long) == 8 ); BOOST_STATIC_ASSERT( sizeof(double) == 8 ); BOOST_STATIC_ASSERT( sizeof(mongo::Date_t) == 8 ); BOOST_STATIC_ASSERT( sizeof(mongo::OID) == 12 ); namespace mongo { BSONElement nullElement; ostream& operator<<( ostream &s, const OID &o ) { s << o.str(); return s; } IDLabeler GENOID; BSONObjBuilder& operator<<(BSONObjBuilder& b, IDLabeler& id) { OID oid; oid.init(); b.appendOID("_id", &oid); return b; } DateNowLabeler DATENOW; string BSONElement::toString( bool includeFieldName ) const { stringstream s; if ( includeFieldName && type() != EOO ) s << fieldName() << ": "; switch ( type() ) { case EOO: return "EOO"; case 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 Bool: s << ( boolean() ? "true" : "false" ); break; case Object: case Array: s << embeddedObject().toString(); 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() << ")"; break; case Code: if ( valuestrsize() > 80 ) s << string(valuestr()).substr(0, 70) << "..."; else { s << valuestr(); } break; case Symbol: case String: if ( valuestrsize() > 80 ) s << '"' << string(valuestr()).substr(0, 70) << "...\""; else { s << '"' << valuestr() << '"'; } break; case DBRef: s << "DBRef('" << valuestr() << "',"; { OID *x = (OID *) (valuestr() + valuestrsize()); s << *x << ')'; } break; case jstOID: s << "ObjId("; s << __oid() << ')'; break; case BinData: s << "BinData"; break; case Timestamp: s << "Timestamp " << timestampTime() << "|" << timestampInc(); break; default: s << "?type=" << type(); break; } return s.str(); } string escape( string s ) { stringstream ret; for ( string::iterator i = s.begin(); i != s.end(); ++i ) { switch ( *i ) { case '"': ret << "\\\""; break; case '\\': ret << "\\\\"; break; case '/': ret << "\\/"; 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 ) { ret << "\\u"; ret << hex; ret.width( 4 ); ret.fill( '0' ); ret << int( *i ); } else { ret << *i; } } } return ret.str(); } string BSONElement::jsonString( JsonStringFormat format, bool includeFieldNames ) const { stringstream s; if ( includeFieldNames ) s << '"' << escape( fieldName() ) << "\" : "; switch ( type() ) { case String: case Symbol: s << '"' << escape( valuestr() ) << '"'; break; case NumberLong: s << _numberLong(); break; case NumberInt: case NumberDouble: if ( number() >= -numeric_limits< double >::max() && number() <= numeric_limits< double >::max() ) { s.precision( 16 ); s << number(); } else { stringstream ss; ss << "Number " << number() << " cannot be represented in JSON"; string message = ss.str(); massert( message.c_str(), false ); } break; case Bool: s << ( boolean() ? "true" : "false" ); break; case jstNULL: s << "null"; break; case Object: s << embeddedObject().jsonString( format ); break; case Array: { if ( embeddedObject().isEmpty() ) { s << "[]"; break; } s << "[ "; BSONObjIterator i( embeddedObject() ); BSONElement e = i.next(); if ( !e.eoo() ) while ( 1 ) { s << e.jsonString( format, false ); e = i.next(); if ( e.eoo() ) break; s << ", "; } s << " ]"; break; } case DBRef: { OID *x = (OID *) (valuestr() + valuestrsize()); if ( format == TenGen ) s << "Dbref( "; else s << "{ \"$ref\" : "; s << '"' << valuestr() << "\", "; if ( format != TenGen ) s << "\"$id\" : "; s << '"' << *x << "\" "; if ( format == TenGen ) s << ')'; else s << '}'; break; } case jstOID: if ( format == TenGen ) { s << "ObjectId( "; } else { s << "{ \"$oid\" : "; } s << '"' << __oid() << '"'; if ( format == TenGen ) { s << " )"; } else { s << " }"; } break; case BinData: { int len = *(int *)( value() ); BinDataType type = BinDataType( *(char *)( (int *)( value() ) + 1 ) ); s << "{ \"$binary\" : \""; char *start = ( char * )( value() ) + sizeof( int ) + 1; base64::encode( s , start , len ); s << "\", \"$type\" : \"" << hex; s.width( 2 ); s.fill( '0' ); s << type << dec; s << "\" }"; break; } case Date: if ( format == Strict ) s << "{ \"$date\" : "; else s << "Date( "; s << date(); if ( format == Strict ) s << " }"; else s << " )"; break; case RegEx: if ( format == Strict ) s << "{ \"$regex\" : \""; else s << "/"; s << escape( regex() ); if ( format == Strict ) s << "\", \"$options\" : \"" << regexFlags() << "\" }"; else { s << "/"; // FIXME Worry about alpha order? for ( const char *f = regexFlags(); *f; ++f ) switch ( *f ) { case 'g': case 'i': case 'm': s << *f; default: break; } } break; default: stringstream ss; ss << "Cannot create a properly formatted JSON string with " << "element: " << toString() << " of type: " << type(); string message = ss.str(); massert( message.c_str(), false ); } return s.str(); } 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 Bool: x = 1; break; case NumberInt: x = 4; break; case Timestamp: case Date: case NumberDouble: case NumberLong: x = 8; break; case jstOID: x = 12; break; case Symbol: case Code: case String: massert( "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); x = valuestrsize() + 4; break; case CodeWScope: massert( "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); x = objsize(); break; case DBRef: massert( "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); x = valuestrsize() + 4 + 12; break; case Object: case Array: massert( "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); x = objsize(); break; case BinData: massert( "Insufficient bytes to calculate element size", maxLen == -1 || remain > 3 ); x = valuestrsize() + 4 + 1/*subtype*/; break; case RegEx: { const char *p = value(); int len1 = ( maxLen == -1 ) ? strlen( p ) : strnlen( p, remain ); massert( "Invalid regex string", len1 != -1 ); p = p + len1 + 1; int len2 = ( maxLen == -1 ) ? strlen( p ) : strnlen( p, remain - len1 - 1 ); massert( "Invalid regex options string", len2 != -1 ); x = len1 + 1 + len2 + 1; } break; default: { stringstream ss; ss << "BSONElement: bad type " << (int) type(); massert(ss.str().c_str(),false); } } totalSize = x + fieldNameSize() + 1; // BSONType return totalSize; } int BSONElement::getGtLtOp( int def ) const { const char *fn = fieldName(); if ( fn[0] == '$' && fn[1] ) { if ( fn[2] == 't' ) { if ( fn[1] == 'g' ) { if ( fn[3] == 0 ) return BSONObj::GT; else if ( fn[3] == 'e' && fn[4] == 0 ) return BSONObj::GTE; } else if ( fn[1] == 'l' ) { if ( fn[3] == 0 ) return BSONObj::LT; else if ( fn[3] == 'e' && fn[4] == 0 ) return BSONObj::LTE; } } else if ( fn[1] == 'n' && fn[2] == 'e' && fn[3] == 0) return BSONObj::NE; else if ( fn[1] == 'm' && fn[2] == 'o' && fn[3] == 'd' && fn[4] == 0 ) return BSONObj::opMOD; else if ( fn[1] == 't' && fn[2] == 'y' && fn[3] == 'p' && fn[4] == 'e' && fn[5] == 0 ) return BSONObj::opTYPE; else if ( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 ) return BSONObj::opIN; else if ( fn[1] == 'n' && fn[2] == 'i' && fn[3] == 'n' && fn[4] == 0 ) return BSONObj::NIN; else if ( fn[1] == 'a' && fn[2] == 'l' && fn[3] == 'l' && fn[4] == 0 ) return BSONObj::opALL; else if ( fn[1] == 's' && fn[2] == 'i' && fn[3] == 'z' && fn[4] == 'e' && fn[5] == 0 ) return BSONObj::opSIZE; else if ( fn[1] == 'e' && fn[2] == 'x' && fn[3] == 'i' && fn[4] == 's' && fn[5] == 't' && fn[6] == 's' && fn[7] == 0 ) return BSONObj::opEXISTS; else if ( fn[1] == 'r' && fn[2] == 'e' && fn[3] == 'g' && fn[4] == 'e' && fn[5] == 'x' && fn[6] == 0 ) return BSONObj::opREGEX; else if ( fn[1] == 'o' && fn[2] == 'p' && fn[3] == 't' && fn[4] == 'i' && fn[5] == 'o' && fn[6] == 'n' && fn[7] == 's' && fn[8] == 0 ) return BSONObj::opOPTIONS; } return def; } /* wo = "well ordered" */ 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; } /* must be same type when called, unless both sides are #s */ int compareElementValues(const BSONElement& l, const BSONElement& r) { int f; double x; switch ( l.type() ) { case EOO: case Undefined: 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: case Date: if ( l.date() < r.date() ) return -1; return l.date() == r.date() ? 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; } // else fall through case NumberInt: case NumberDouble: { double left = l.number(); double right = r.number(); bool lNan = !( left <= numeric_limits< double >::max() && left >= -numeric_limits< double >::max() ); bool rNan = !( right <= numeric_limits< double >::max() && right >= -numeric_limits< double >::max() ); if ( lNan ) { if ( rNan ) { return 0; } else { return -1; } } else if ( rNan ) { return 1; } x = left - right; if ( x < 0 ) return -1; return x == 0 ? 0 : 1; } case jstOID: return memcmp(l.value(), r.value(), 12); case Code: case Symbol: case String: /* todo: utf version */ return strcmp(l.valuestr(), r.valuestr()); case Object: case Array: return l.embeddedObject().woCompare( r.embeddedObject() ); case DBRef: case BinData: { int lsz = l.valuesize(); int rsz = r.valuesize(); if ( lsz - rsz != 0 ) return lsz - rsz; return memcmp(l.value(), r.value(), lsz); } 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: out() << "compareElementValues: bad type " << (int) l.type() << endl; assert(false); } return -1; } /** returns a string that when used as a matcher, would match a super set of regex() returns "" for complex regular expressions used to optimize queries in some simple regex cases that start with '^' */ string BSONElement::simpleRegex() const { string r = ""; if ( *regexFlags() ) return r; const char *i = regex(); if ( *i != '^' ) return r; ++i; // Empty string matches everything, won't limit our search. if ( !*i ) return r; stringstream ss; for( ; *i; ++i ){ char c = *i; if ( c == '*' || c == '?' ){ r = ss.str(); r = r.substr( 0 , r.size() - 1 ); break; } else if ( *i == ' ' || (*i>='0'&&*i<='9') || (*i>='@'&&*i<='Z') || (*i>='a'&&*i<='z') ){ ss << *i; } else { r = ss.str(); break; } } if ( r.size() == 0 && *i == 0 ) r = ss.str(); return r; } void BSONElement::validate() const { switch( type() ) { case DBRef: case Code: case Symbol: case String: massert( "Invalid dbref/code/string/symbol size", valuestrsize() > 0 && valuestrsize() - 1 == strnlen( valuestr(), valuestrsize() ) ); break; case CodeWScope: { int totalSize = *( int * )( value() ); massert( "Invalid CodeWScope size", totalSize >= 8 ); int strSizeWNull = *( int * )( value() + 4 ); massert( "Invalid CodeWScope string size", totalSize >= strSizeWNull + 4 + 4 ); massert( "Invalid CodeWScope string size", strSizeWNull > 0 && strSizeWNull - 1 == strnlen( codeWScopeCode(), strSizeWNull ) ); massert( "Invalid CodeWScope size", totalSize >= strSizeWNull + 4 + 4 + 4 ); int objSize = *( int * )( value() + 4 + 4 + strSizeWNull ); massert( "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; } } /* JSMatcher --------------------------------------*/ // If the element is something like: // a : { $gt : 3 } // we append // a : 3 // else we just append the element. // void appendElementHandlingGtLt(BSONObjBuilder& b, const BSONElement& e) { if ( e.type() == Object ) { BSONElement fe = e.embeddedObject().firstElement(); const char *fn = fe.fieldName(); if ( fn[0] == '$' && fn[1] && fn[2] == 't' ) { b.appendAs(fe, e.fieldName()); return; } } b.append(e); } int getGtLtOp(const BSONElement& e) { if ( e.type() != Object ) return BSONObj::Equality; BSONElement fe = e.embeddedObject().firstElement(); return fe.getGtLtOp(); } FieldCompareResult compareDottedFieldNames( const string& l , const string& r ){ size_t lstart = 0; size_t rstart = 0; while ( 1 ){ if ( lstart >= l.size() ){ if ( rstart >= r.size() ) return SAME; return RIGHT_SUBFIELD; } if ( rstart >= r.size() ) return LEFT_SUBFIELD; size_t a = l.find( '.' , lstart ); size_t b = r.find( '.' , rstart ); size_t lend = a == string::npos ? l.size() : a; size_t rend = b == string::npos ? r.size() : b; const string& c = l.substr( lstart , lend - lstart ); const string& d = r.substr( rstart , rend - rstart ); int x = c.compare( d ); if ( x < 0 ) return LEFT_BEFORE; if ( x > 0 ) return RIGHT_BEFORE; lstart = lend + 1; rstart = rend + 1; } } /* BSONObj ------------------------------------------------------------*/ BSONObj::EmptyObject BSONObj::emptyObject; string BSONObj::toString() const { if ( isEmpty() ) return "{}"; stringstream s; s << "{ "; BSONObjIterator i(*this); bool first = true; while ( 1 ) { massert( "Object does not end with EOO", i.moreWithEOO() ); BSONElement e = i.next( true ); massert( "Invalid element size", e.size() > 0 ); massert( "Element too large", e.size() < ( 1 << 30 ) ); int offset = e.rawdata() - this->objdata(); massert( "Element extends past end of object", e.size() + offset <= this->objsize() ); e.validate(); bool end = ( e.size() + offset == this->objsize() ); if ( e.eoo() ) { massert( "EOO Before end of object", end ); break; } if ( first ) first = false; else s << ", "; s << e.toString(); } s << " }"; return s.str(); } string BSONObj::md5() const { md5digest d; md5_state_t st; md5_init(&st); md5_append( &st , (const md5_byte_t*)_objdata , objsize() ); md5_finish(&st, d); return digestToString( d ); } string BSONObj::jsonString( JsonStringFormat format ) const { if ( isEmpty() ) return "{}"; stringstream s; s << "{ "; BSONObjIterator i(*this); BSONElement e = i.next(); if ( !e.eoo() ) while ( 1 ) { s << e.jsonString( format ); e = i.next(); if ( e.eoo() ) break; s << ", "; } s << " }"; return s.str(); } // todo: can be a little faster if we don't use toString() here. bool BSONObj::valid() const { try { toString(); } catch (...) { return false; } return true; } /* 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; int x = l.woCompare( r, considerFieldName ); if ( ordered && o.number() < 0 ) x = -x; if ( x != 0 ) return x; } return -1; } BSONObj staticNull = fromjson( "{'':null}" ); /* well ordered compare */ int BSONObj::woSortOrder(const BSONObj& other, const BSONObj& sortKey ) const{ if ( isEmpty() ) return other.isEmpty() ? 0 : -1; if ( other.isEmpty() ) return 1; uassert( "woSortOrder needs a non-empty sortKey" , ! sortKey.isEmpty() ); BSONObjIterator i(sortKey); while ( 1 ){ BSONElement f = i.next(); if ( f.eoo() ) return 0; BSONElement l = getField( f.fieldName() ); if ( l.eoo() ) l = staticNull.firstElement(); BSONElement r = 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; } BSONElement BSONObj::getField(const char *name) const { BSONObjIterator i(*this); while ( i.moreWithEOO() ) { BSONElement e = i.next(); if ( e.eoo() ) break; if ( strcmp(e.fieldName(), name) == 0 ) return e; } return nullElement; } /* return has eoo() true if no match supports "." notation to reach into embedded objects */ 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() ? nullElement : sub.getFieldDotted(p+1); } } return e; } /* jul09 : 'deep' and this function will be going away in the future - kept only for backward compatibility of datafiles for now. */ void trueDat( bool *deep ) { if( deep ) *deep = true; } void BSONObj::getFieldsDotted(const char *name, BSONElementSet &ret, bool *deep ) const { BSONElement e = getField( name ); if ( e.eoo() ) { const char *p = strchr(name, '.'); if ( p ) { string left(name, p-name); BSONElement e = getField( left ); if ( e.type() == Array ) { trueDat( deep ); BSONObjIterator i( e.embeddedObject() ); while( i.moreWithEOO() ) { BSONElement f = i.next(); if ( f.eoo() ) break; if ( f.type() == Object ) f.embeddedObject().getFieldsDotted(p+1, ret); } } else if ( e.type() == Object ) { e.embeddedObject().getFieldsDotted(p+1, ret); } } } else { if ( e.type() == Array ) { trueDat( deep ); BSONObjIterator i( e.embeddedObject() ); while( i.moreWithEOO() ) { BSONElement f = i.next(); if ( f.eoo() ) break; ret.insert( f ); } } else { ret.insert( e ); } } if ( ret.empty() && deep ) *deep = false; } BSONElement BSONObj::getFieldDottedOrArray(const char *&name) const { const char *p = strchr(name, '.'); string left; if ( p ) { left = string(name, p-name); name = p + 1; } else { left = string(name); name = name + strlen(name); } BSONElement sub = getField(left.c_str()); if ( sub.eoo() ) return nullElement; else if ( sub.type() == Array || strlen( name ) == 0 ) return sub; else if ( sub.type() == Object ) return sub.embeddedObject().getFieldDottedOrArray( name ); else return nullElement; } /* makes a new BSONObj with the fields specified in pattern. fields returned in the order they appear in pattern. if any field missing or undefined in the original object, that field in the output will be null. n^2 implementation bad if pattern and object have lots of fields - normally pattern doesn't so should be fine. */ BSONObj BSONObj::extractFieldsDotted(BSONObj pattern) const { BSONObjBuilder b; BSONObjIterator i(pattern); while (i.more()) { BSONElement e = i.next(); const char *name = e.fieldName(); BSONElement x = getFieldDotted( name ); if ( x.eoo() || x.type() == Undefined ) { b.appendNull(name); } else { b.appendAs(x, name); } } return b.done(); } /** sets element field names to empty string If a field in pattern is missing, it is omitted from the returned object. */ BSONObj BSONObj::extractFieldsUnDotted(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(const char *fieldName, const BSONObj &indexKey) const { BSONObjIterator i( indexKey ); int j = 0; while( i.moreWithEOO() ) { BSONElement f = i.next(); if ( f.eoo() ) return BSONElement(); if ( strcmp( f.fieldName(), fieldName ) == 0 ) 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(); } int BSONObj::getIntField(const char *name) const { BSONElement e = getField(name); return e.isNumber() ? (int) e.number() : INT_MIN; } bool BSONObj::getBoolField(const char *name) const { BSONElement e = getField(name); return e.type() == Bool ? e.boolean() : false; } const char * BSONObj::getStringField(const char *name) const { BSONElement e = getField(name); return e.type() == String ? e.valuestr() : ""; } BSONObj BSONObj::getObjectField(const char *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; } /* grab names of all the fields in this object */ int BSONObj::getFieldNames(set& 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& fields) { assert( 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; } else if ( strcmp(fname, "_id")==0 ) { b.append(e); gotId = true; if ( n == N && gotId ) break; } } if ( n ) { int len; init( b.decouple(len), true ); } return n; } 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() ); break; } case MaxKey: { BSONObjBuilder m; m.append( "$maxElement", 1 ); b.append( e.fieldName(), m.done() ); break; } default: b.append( e ); } } 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 ); } } return b.obj(); } 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(); } ostream& operator<<( ostream &s, const BSONObj &o ) { return s << o.toString(); } ostream& operator<<( ostream &s, const BSONElement &e ) { return s << e.toString(); } void nested2dotted(BSONObjBuilder& b, const BSONObj& obj, const string& base){ BSONObjIterator it(obj); while (it.more()){ BSONElement e = it.next(); if (e.type() == Object){ string newbase = base + e.fieldName() + "."; nested2dotted(b, e.embeddedObject(), newbase); }else{ string newbase = base + e.fieldName(); b.appendAs(e, newbase.c_str()); } } } void dotted2nested(BSONObjBuilder& b, const BSONObj& obj){ //use map to sort fields BSONMap sorted = bson2map(obj); EmbeddedBuilder eb(&b); for(BSONMap::const_iterator it=sorted.begin(); it!=sorted.end(); ++it){ eb.appendAs(it->second, it->first); } eb.done(); } /*-- test things ----------------------------------------------------*/ #pragma pack(1) struct MaxKeyData { MaxKeyData() { totsize=7; maxkey=MaxKey; name=0; eoo=EOO; } int totsize; char maxkey; char name; char eoo; } maxkeydata; BSONObj maxKey((const char *) &maxkeydata); struct MinKeyData { MinKeyData() { totsize=7; minkey=MinKey; name=0; eoo=EOO; } int totsize; char minkey; char name; char eoo; } minkeydata; BSONObj minKey((const char *) &minkeydata); struct JSObj0 { JSObj0() { totsize = 5; eoo = EOO; } int totsize; char eoo; } js0; #pragma pack() BSONElement::BSONElement() { data = &js0.eoo; fieldNameSize_ = 0; totalSize = 1; } struct BsonUnitTest : public UnitTest { void testRegex() { BSONObjBuilder b; b.appendRegex("x", "foo"); BSONObj o = b.done(); BSONObjBuilder c; c.appendRegex("x", "goo"); BSONObj p = c.done(); assert( !o.woEqual( p ) ); assert( o.woCompare( p ) < 0 ); { BSONObjBuilder b; b.appendRegex("r", "^foo"); BSONObj o = b.done(); assert( o.firstElement().simpleRegex() == "foo" ); } { BSONObjBuilder b; b.appendRegex("r", "^f?oo"); BSONObj o = b.done(); assert( o.firstElement().simpleRegex() == "" ); } { BSONObjBuilder b; b.appendRegex("r", "^fz?oo"); BSONObj o = b.done(); assert( o.firstElement().simpleRegex() == "f" ); } } void testoid() { OID id; id.init(); // sleepsecs(3); OID b; // goes with sleep above... // b.init(); // assert( memcmp(id.getData(), b.getData(), 12) < 0 ); b.init( id.str() ); assert( b == id ); } void testbounds(){ BSONObj l , r; { BSONObjBuilder b; b.append( "x" , numeric_limits::max() ); l = b.obj(); } { BSONObjBuilder b; b.append( "x" , numeric_limits::max() ); r = b.obj(); } assert( l.woCompare( r ) < 0 ); assert( r.woCompare( l ) > 0 ); { BSONObjBuilder b; b.append( "x" , numeric_limits::max() ); l = b.obj(); } assert( l.woCompare( r ) < 0 ); assert( r.woCompare( l ) > 0 ); } void testorder(){ { BSONObj x,y,z; { BSONObjBuilder b; b.append( "x" , (long long)2 ); x = b.obj(); } { BSONObjBuilder b; b.append( "x" , (int)3 ); y = b.obj(); } { BSONObjBuilder b; b.append( "x" , (long long)4 ); z = b.obj(); } assert( x.woCompare( y ) < 0 ); assert( x.woCompare( z ) < 0 ); assert( y.woCompare( x ) > 0 ); assert( z.woCompare( x ) > 0 ); assert( y.woCompare( z ) < 0 ); assert( z.woCompare( y ) > 0 ); } { BSONObj ll,d,i,n,u; { BSONObjBuilder b; b.append( "x" , (long long)2 ); ll = b.obj(); } { BSONObjBuilder b; b.append( "x" , (double)2 ); d = b.obj(); } { BSONObjBuilder b; b.append( "x" , (int)2 ); i = b.obj(); } { BSONObjBuilder b; b.appendNull( "x" ); n = b.obj(); } { BSONObjBuilder b; u = b.obj(); } assert( ll.woCompare( u ) == d.woCompare( u ) ); assert( ll.woCompare( u ) == i.woCompare( u ) ); BSONObj k = BSON( "x" << 1 ); assert( ll.woCompare( u , k ) == d.woCompare( u , k ) ); assert( ll.woCompare( u , k ) == i.woCompare( u , k ) ); assert( u.woCompare( ll ) == u.woCompare( d ) ); assert( u.woCompare( ll ) == u.woCompare( i ) ); assert( u.woCompare( ll , k ) == u.woCompare( d , k ) ); assert( u.woCompare( ll , k ) == u.woCompare( d , k ) ); assert( i.woCompare( n ) == d.woCompare( n ) ); assert( ll.woCompare( n ) == d.woCompare( n ) ); assert( ll.woCompare( n ) == i.woCompare( n ) ); assert( ll.woCompare( n , k ) == d.woCompare( n , k ) ); assert( ll.woCompare( n , k ) == i.woCompare( n , k ) ); assert( n.woCompare( ll ) == n.woCompare( d ) ); assert( n.woCompare( ll ) == n.woCompare( i ) ); assert( n.woCompare( ll , k ) == n.woCompare( d , k ) ); assert( n.woCompare( ll , k ) == n.woCompare( d , k ) ); } { BSONObj l,r; { BSONObjBuilder b; b.append( "x" , "eliot" ); l = b.obj(); } { BSONObjBuilder b; b.appendSymbol( "x" , "eliot" ); r = b.obj(); } assert( l.woCompare( r ) == 0 ); assert( r.woCompare( l ) == 0 ); } } void run() { testRegex(); BSONObjBuilder A,B,C; A.append("x", 2); B.append("x", 2.0); C.append("x", 2.1); BSONObj a = A.done(); BSONObj b = B.done(); BSONObj c = C.done(); assert( !a.woEqual( b ) ); // comments on operator== int cmp = a.woCompare(b); assert( cmp == 0 ); cmp = a.woCompare(c); assert( cmp < 0 ); testoid(); testbounds(); testorder(); } } bson_unittest; /* BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const char * value ) { _builder->append( _fieldName , value ); return *_builder; } BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const int value ) { _builder->append( _fieldName , value ); return *_builder; } BSONObjBuilder& BSONObjBuilderValueStream::operator<<( const double value ) { _builder->append( _fieldName , value ); return *_builder; } */ unsigned OID::_machine = (unsigned) security.getNonceInitSafe(); void OID::newState(){ // using fresh Security object to avoid buffered devrandom _machine = (unsigned) Security().getNonce(); } void OID::init() { static WrappingInt inc = (unsigned) security.getNonce(); unsigned t = (unsigned) time(0); char *T = (char *) &t; data[0] = T[3]; data[1] = T[2]; data[2] = T[1]; data[3] = T[0]; (unsigned&) data[4] = _machine; int new_inc = inc.atomicIncrement(); T = (char *) &new_inc; char * raw = (char*)&b; raw[0] = T[3]; raw[1] = T[2]; raw[2] = T[1]; raw[3] = T[0]; } void OID::init( string s ){ assert( s.size() == 24 ); const char *p = s.c_str(); char buf[3]; buf[2] = 0; for( int i = 0; i < 12; i++ ) { buf[0] = p[0]; buf[1] = p[1]; p += 2; stringstream ss(buf); unsigned z; ss >> hex >> z; data[i] = z; } /* string as = s.substr( 0 , 16 ); string bs = s.substr( 16 ); stringstream ssa(as); ssa >> hex >> a; stringstream ssb(bs); ssb >> hex >> b; */ } Labeler::Label GT( "$gt" ); Labeler::Label GTE( "$gte" ); Labeler::Label LT( "$lt" ); Labeler::Label LTE( "$lte" ); Labeler::Label NE( "$ne" ); Labeler::Label SIZE( "$size" ); void BSONElementManipulator::initTimestamp() { massert( "Expected CurrentTime type", element_.type() == Timestamp ); unsigned long long ×tamp = *( reinterpret_cast< unsigned long long* >( value() ) ); if ( timestamp == 0 ) timestamp = OpTime::now().asDate(); } void BSONObjBuilder::appendMinForType( const string& field , int t ){ switch ( t ){ case MinKey: appendMinKey( field.c_str() ); return; case MaxKey: appendMinKey( field.c_str() ); return; case NumberInt: case NumberDouble: case NumberLong: append( field.c_str() , - numeric_limits::max() ); return; case jstOID: { OID o; memset(&o, 0, sizeof(o)); appendOID( field.c_str() , &o); return; } case Bool: appendBool( field.c_str() , false); return; case Date: appendDate( field.c_str() , 0); return; case jstNULL: appendNull( field.c_str() ); return; case Symbol: case String: append( field.c_str() , "" ); return; case Object: append( field.c_str() , BSONObj() ); return; case Array: appendArray( field.c_str() , BSONObj() ); return; case BinData: appendBinData( field.c_str() , 0 , Function , (const char *) 0 ); return; case Undefined: appendUndefined( field.c_str() ); return; case RegEx: appendRegex( field.c_str() , "" ); return; case DBRef: { OID o; memset(&o, 0, sizeof(o)); appendDBRef( field.c_str() , "" , o ); return; } case Code: appendCode( field.c_str() , "" ); return; case CodeWScope: appendCodeWScope( field.c_str() , "" , BSONObj() ); return; case Timestamp: appendTimestamp( field.c_str() , 0); return; }; log() << "type not support for appendMinElementForType: " << t << endl; uassert( "type not supported for appendMinElementForType" , false ); } void BSONObjBuilder::appendMaxForType( const string& field , int t ){ switch ( t ){ case MinKey: appendMaxKey( field.c_str() ); break; case MaxKey: appendMaxKey( field.c_str() ); break; case NumberInt: case NumberDouble: case NumberLong: append( field.c_str() , numeric_limits::max() ); break; case BinData: appendMinForType( field , jstOID ); break; case jstOID: { OID o; memset(&o, 0xFF, sizeof(o)); appendOID( field.c_str() , &o); break; } case Undefined: case jstNULL: appendMinForType( field , NumberInt ); case Bool: appendBool( field.c_str() , true); break; case Date: appendDate( field.c_str() , 0xFFFFFFFFFFFFFFFFLL ); break; case Symbol: case String: append( field.c_str() , BSONObj() ); break; case Code: case CodeWScope: appendCodeWScope( field.c_str() , "ZZZ" , BSONObj() ); break; case Timestamp: appendTimestamp( field.c_str() , numeric_limits::max() ); break; default: appendMinForType( field , t + 1 ); } } const string BSONObjBuilder::numStrs[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", }; bool BSONObjBuilder::appendAsNumber( const string& fieldName , const string& data ){ if ( data.size() == 0 ) return false; unsigned int pos=0; if ( data[0] == '-' ) pos++; bool hasDec = false; for ( ; pos( data ); append( fieldName , num ); return true; } catch(bad_lexical_cast &){ return false; } } int BSONElementFieldSorter( const void * a , const void * b ){ const char * x = *((const char**)a); const char * y = *((const char**)b); x++; y++; return strcmp( x , y ); } BSONObjIteratorSorted::BSONObjIteratorSorted( const BSONObj& o ){ _nfields = o.nFields(); _fields = new const char*[_nfields]; int x = 0; BSONObjIterator i( o ); while ( i.more() ){ _fields[x++] = i.next().rawdata(); assert( _fields[x-1] ); } assert( x == _nfields ); qsort( _fields , _nfields , sizeof(char*) , BSONElementFieldSorter ); _cur = 0; } } // namespace mongo