diff options
Diffstat (limited to 'src/mongo/bson')
-rw-r--r-- | src/mongo/bson/README | 7 | ||||
-rw-r--r-- | src/mongo/bson/bson-inl.h | 1007 | ||||
-rw-r--r-- | src/mongo/bson/bson.h | 110 | ||||
-rw-r--r-- | src/mongo/bson/bson_db.h | 88 | ||||
-rw-r--r-- | src/mongo/bson/bsondemo/bsondemo.cpp | 113 | ||||
-rw-r--r-- | src/mongo/bson/bsondemo/bsondemo.vcproj | 243 | ||||
-rw-r--r-- | src/mongo/bson/bsondemo/bsondemo.vcxproj | 193 | ||||
-rw-r--r-- | src/mongo/bson/bsondemo/bsondemo.vcxproj.filters | 52 | ||||
-rw-r--r-- | src/mongo/bson/bsonelement.h | 583 | ||||
-rw-r--r-- | src/mongo/bson/bsonmisc.h | 211 | ||||
-rw-r--r-- | src/mongo/bson/bsonobj.h | 497 | ||||
-rw-r--r-- | src/mongo/bson/bsonobjbuilder.h | 842 | ||||
-rw-r--r-- | src/mongo/bson/bsonobjiterator.h | 161 | ||||
-rw-r--r-- | src/mongo/bson/bsontypes.h | 107 | ||||
-rw-r--r-- | src/mongo/bson/inline_decls.h | 68 | ||||
-rw-r--r-- | src/mongo/bson/oid.cpp | 173 | ||||
-rw-r--r-- | src/mongo/bson/oid.h | 138 | ||||
-rw-r--r-- | src/mongo/bson/ordering.h | 73 | ||||
-rw-r--r-- | src/mongo/bson/stringdata.h | 71 | ||||
-rw-r--r-- | src/mongo/bson/util/atomic_int.h | 106 | ||||
-rw-r--r-- | src/mongo/bson/util/builder.h | 322 | ||||
-rw-r--r-- | src/mongo/bson/util/misc.h | 121 |
22 files changed, 5286 insertions, 0 deletions
diff --git a/src/mongo/bson/README b/src/mongo/bson/README new file mode 100644 index 00000000000..01ed654bcd2 --- /dev/null +++ b/src/mongo/bson/README @@ -0,0 +1,7 @@ +"BSON" stands for "binary JSON" - a binary storage format that is JSON inspired +(and adds a couple extra types such as Date). + +This is the C++ implementation. Implementations which translate BSON<->JSON +are available for most languages at bsonspec.org. + + diff --git a/src/mongo/bson/bson-inl.h b/src/mongo/bson/bson-inl.h new file mode 100644 index 00000000000..9e8b3654802 --- /dev/null +++ b/src/mongo/bson/bson-inl.h @@ -0,0 +1,1007 @@ +/** @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. + * + * 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 <map> +#include <limits> + +#if defined(_WIN32) +#undef max +#undef min +#endif + +namespace mongo { + + inline bool isNaN(double d) { + return d != d; + } + + inline bool isInf(double d, int* sign = 0) { + volatile double tmp = d; + + if ((tmp == d) && ((tmp - d) != 0.0)) { + if ( sign ) { + *sign = (d < 0.0 ? -1 : 1); + } + return true; + } + + if ( sign ) { + *sign = 0; + } + + return false; + } + + /* 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 /*+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.codeWScopeScopeData() , r.codeWScopeScopeData() ); + if ( f ) + return f; + return 0; + } + default: + assert( false); + } + return -1; + } + + /* wo = "well ordered" + note: (mongodb related) : this can only change in behavior when index version # changes + */ + 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 ( MONGO_likely(isABSONObj()) ) + return BSONObj(value()); + stringstream ss; + ss << "invalid parameter: expected an object (" << fieldName() << ")"; + uasserted( 10065 , ss.str() ); + return BSONObj(); // never reachable + } + + 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 ); + } + + // 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(); + ss << "Invalid BSONObj size: " << os << " (0x" << toHex( &os, 4 ) << ')'; + try { + BSONElement e = firstElement(); + ss << " first element: " << e.toString(); + } + catch ( ... ) { } + massert( 10334 , ss.str() , 0 ); + } + + /* the idea with NOINLINE_DECL here is to keep this from inlining in the + getOwned() method. the presumption being that is better. + */ + inline NOINLINE_DECL BSONObj BSONObj::copy() const { + Holder *h = (Holder*) malloc(objsize() + sizeof(unsigned)); + h->zero(); + memcpy(h->data, objdata(), objsize()); + return BSONObj(h); + } + + inline BSONObj BSONObj::getOwned() const { + if ( isOwned() ) + return *this; + return copy(); + } + + // 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 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; + } + } + } + } + + 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(); + } + + 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); + while ( it.moreWithEOO() ) { + BSONElement e = it.next(); + if ( e.eoo() ) break; + append(e); + } + return *this; + } + + /* add all the fields from the object specified to this object if they don't exist */ + inline BSONObjBuilder& BSONObjBuilder::appendElementsUnique(BSONObj x) { + set<string> have; + { + BSONObjIterator i = iterator(); + while ( i.more() ) + have.insert( i.next().fieldName() ); + } + + BSONObjIterator it(x); + while ( it.more() ) { + BSONElement e = it.next(); + if ( have.count( e.fieldName() ) ) + continue; + append(e); + } + return *this; + } + + + inline bool BSONObj::isValid() const { + int x = objsize(); + return x > 0 && x <= BSONObjMaxInternalSize; + } + + 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<class T> + 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<class T> 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 ); + } + + inline bool BSONObjBuilder::hasField( const StringData& name ) const { + BSONObjIterator i = iterator(); + while ( i.more() ) + if ( strcmp( name.data() , i.next().fieldName() ) == 0 ) + return true; + return false; + } + + /* WARNING: nested/dotted conversions are not 100% reversible + * nested2dotted(dotted2nested({a.b: {c:1}})) -> {a.b.c: 1} + * also, dotted2nested ignores order + */ + + typedef map<string, BSONElement> 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<BSONElement, BSONElementFieldNameCmp> 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 ? " ]" : " }" ); + } + + inline void BSONElement::validate() const { + const BSONType t = type(); + + switch( t ) { + case DBRef: + case Code: + case Symbol: + case mongo::String: { + unsigned x = (unsigned) valuestrsize(); + bool lenOk = x > 0 && x < (unsigned) BSONObjMaxInternalSize; + if( lenOk && valuestr()[x-1] == 0 ) + return; + StringBuilder buf; + buf << "Invalid dbref/code/string/symbol size: " << x; + if( lenOk ) + buf << " 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 ) : (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; + assert( 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(); + string msg = ss.str(); + massert( 13655 , msg.c_str(),false); + } + } + totalSize = x + fieldNameSize() + 1; // BSONType + + 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); + 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(" << (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); + 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() > 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"; + 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 ordering here, but perhaps that is ok regardless as BSON is spec'd + to be little endian external to the system. (i.e. the rest of the implementation of bson, + not this part, fails to support big endian) + */ + static char p[] = { /*size*/5, 0, 0, 0, /*eoo*/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<BSONElement> &v) const { + BSONObjIterator i(*this); + while( i.more() ) + v.push_back(i.next()); + } + + inline void BSONObj::elems(list<BSONElement> &v) const { + BSONObjIterator i(*this); + while( i.more() ) + v.push_back(i.next()); + } + + template <class T> + void BSONObj::Vals(vector<T>& v) const { + BSONObjIterator i(*this); + while( i.more() ) { + T t; + i.next().Val(t); + v.push_back(t); + } + } + template <class T> + void BSONObj::Vals(list<T>& v) const { + BSONObjIterator i(*this); + while( i.more() ) { + T t; + i.next().Val(t); + v.push_back(t); + } + } + + template <class T> + void BSONObj::vals(vector<T>& v) const { + BSONObjIterator i(*this); + while( i.more() ) { + try { + T t; + i.next().Val(t); + v.push_back(t); + } + catch(...) { } + } + } + template <class T> + void BSONObj::vals(list<T>& 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 StringBuilder& operator<<( StringBuilder &s, const BSONObj &o ) { + o.toString( s ); + return s; + } + inline StringBuilder& operator<<( StringBuilder &s, const BSONElement &e ) { + e.toString( s ); + return s; + } + + + inline void BSONElement::Val(BSONObj& v) const { v = Obj(); } + + template<typename T> + inline BSONFieldValue<BSONObj> BSONField<T>::query( const char * q , const T& t ) const { + BSONObjBuilder b; + 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(); + } +} diff --git a/src/mongo/bson/bson.h b/src/mongo/bson/bson.h new file mode 100644 index 00000000000..9515adfd829 --- /dev/null +++ b/src/mongo/bson/bson.h @@ -0,0 +1,110 @@ +/** @file bson.h + + Main bson include file for mongodb c++ clients. MongoDB includes ../db/jsobj.h instead. + This file, however, pulls in much less code / dependencies. + + @see bsondemo +*/ + +/* + * 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. + */ + +/** + Main include file for C++ BSON module when using standalone (sans MongoDB client). + + "BSON" stands for "binary JSON" -- ie a binary way to represent objects that would be + represented in JSON (plus a few extensions useful for databases & other languages). + + http://www.bsonspec.org/ +*/ + +#pragma once + +#if defined(MONGO_EXPOSE_MACROS) +#error this header is for client programs, not the mongo database itself. include jsobj.h instead. +/* because we define simplistic assert helpers here that don't pull in a bunch of util -- so that + BSON can be used header only. + */ +#endif + +#include <cstdlib> +#include <memory> +#include <iostream> +#include <sstream> +#include <boost/utility.hpp> + +namespace bson { + + using std::string; + using std::stringstream; + + class assertion : public std::exception { + public: + assertion( unsigned u , const string& s ) + : id( u ) , msg( s ) { + stringstream ss; + ss << "BsonAssertion id: " << u << " " << s; + full = ss.str(); + } + + virtual ~assertion() throw() {} + + virtual const char* what() const throw() { return full.c_str(); } + + unsigned id; + string msg; + string full; + }; +} + +namespace mongo { +#if !defined(assert) + inline void assert(bool expr) { + if(!expr) { + throw bson::assertion( 0 , "assertion failure in bson library" ); + } + } +#endif +#if !defined(uassert) + inline void uasserted(unsigned msgid, std::string s) { + throw bson::assertion( msgid , s ); + } + + inline void uassert(unsigned msgid, std::string msg, bool expr) { + if( !expr ) + uasserted( msgid , msg ); + } + inline void msgasserted(int msgid, const char *msg) { + throw bson::assertion( msgid , msg ); + } + inline void msgasserted(int msgid, const std::string &msg) { msgasserted(msgid, msg.c_str()); } + inline void massert(unsigned msgid, std::string msg, bool expr) { + if(!expr) { + std::cout << "assertion failure in bson library: " << msgid << ' ' << msg << std::endl; + throw bson::assertion( msgid , msg ); + } + } +#endif +} + +#include "util/builder.h" +#include "bsontypes.h" +#include "oid.h" +#include "bsonelement.h" +#include "bsonobj.h" +#include "bsonobjbuilder.h" +#include "bsonobjiterator.h" +#include "bson-inl.h" diff --git a/src/mongo/bson/bson_db.h b/src/mongo/bson/bson_db.h new file mode 100644 index 00000000000..3f597bde3e1 --- /dev/null +++ b/src/mongo/bson/bson_db.h @@ -0,0 +1,88 @@ +/** @file bson_db.h + + This file contains the implementation of BSON-related methods that are required + by the MongoDB database server. + + Normally, for standalone BSON usage, you do not want this file - it will tend to + pull in some other files from the MongoDB project. Thus, bson.h (the main file + one would use) does not include this file. +*/ + +/* 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 "../util/optime.h" +#include "../util/time_support.h" + +namespace mongo { + + /** + Timestamps are a special BSON datatype that is used internally for replication. + Append a timestamp element to the object being ebuilt. + @param time - in millis (but stored in seconds) + */ + inline BSONObjBuilder& BSONObjBuilder::appendTimestamp( const StringData& fieldName , unsigned long long time , unsigned int inc ) { + OpTime t( (unsigned) (time / 1000) , inc ); + appendTimestamp( fieldName , t.asDate() ); + return *this; + } + + inline OpTime BSONElement::_opTime() const { + if( type() == mongo::Date || type() == Timestamp ) + return OpTime( *reinterpret_cast< const unsigned long long* >( value() ) ); + return OpTime(); + } + + inline string BSONElement::_asCode() const { + switch( type() ) { + case mongo::String: + case Code: + return string(valuestr(), valuestrsize()-1); + case CodeWScope: + return string(codeWScopeCode(), *(int*)(valuestr())-1); + default: + log() << "can't convert type: " << (int)(type()) << " to code" << endl; + } + uassert( 10062 , "not code" , 0 ); + return ""; + } + + inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(DateNowLabeler& id) { + _builder->appendDate(_fieldName, jsTime()); + _fieldName = 0; + return *_builder; + } + + inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(NullLabeler& id) { + _builder->appendNull(_fieldName); + _fieldName = 0; + return *_builder; + } + + inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(MinKeyLabeler& id) { + _builder->appendMinKey(_fieldName); + _fieldName = 0; + return *_builder; + } + + inline BSONObjBuilder& BSONObjBuilderValueStream::operator<<(MaxKeyLabeler& id) { + _builder->appendMaxKey(_fieldName); + _fieldName = 0; + return *_builder; + } + +} diff --git a/src/mongo/bson/bsondemo/bsondemo.cpp b/src/mongo/bson/bsondemo/bsondemo.cpp new file mode 100644 index 00000000000..b53a7b39baa --- /dev/null +++ b/src/mongo/bson/bsondemo/bsondemo.cpp @@ -0,0 +1,113 @@ +/** @file bsondemo.cpp + + Example of use of BSON from C++. + + Requires boost (headers only). + Works headers only (the parts actually exercised herein that is - some functions require .cpp files). + + To build and run: + g++ -o bsondemo bsondemo.cpp + ./bsondemo + + Windows: project files are available in this directory for bsondemo.cpp for use with Visual Studio. +*/ + +/* + * Copyright 2010 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 "../bson.h" +#include <iostream> +#include <vector> + +using namespace std; +using namespace bson; + +void iter(bo o) { + /* iterator example */ + cout << "\niter()\n"; + for( bo::iterator i(o); i.more(); ) { + cout << ' ' << i.next().toString() << '\n'; + } +} + +int main() { + cout << "build bits: " << 8 * sizeof(char *) << '\n' << endl; + + /* a bson object defaults on construction to { } */ + bo empty; + cout << "empty: " << empty << endl; + + /* make a simple { name : 'joe', age : 33.7 } object */ + { + bob b; + b.append("name", "joe"); + b.append("age", 33.7); + b.obj(); + } + + /* make { name : 'joe', age : 33.7 } with a more compact notation. */ + bo x = bob().append("name", "joe").append("age", 33.7).obj(); + + /* convert from bson to json */ + string json = x.toString(); + cout << "json for x:" << json << endl; + + /* access some fields of bson object x */ + cout << "Some x things: " << x["name"] << ' ' << x["age"].Number() << ' ' << x.isEmpty() << endl; + + /* make a bit more complex object with some nesting + { x : 'asdf', y : true, subobj : { z : 3, q : 4 } } + */ + bo y = BSON( "x" << "asdf" << "y" << true << "subobj" << BSON( "z" << 3 << "q" << 4 ) ); + + /* print it */ + cout << "y: " << y << endl; + + /* reach in and get subobj.z */ + cout << "subobj.z: " << y.getFieldDotted("subobj.z").Number() << endl; + + /* alternate syntax: */ + cout << "subobj.z: " << y["subobj"]["z"].Number() << endl; + + /* fetch all *top level* elements from object y into a vector */ + vector<be> v; + y.elems(v); + cout << v[0] << endl; + + /* into an array */ + list<be> L; + y.elems(L); + + bo sub = y["subobj"].Obj(); + + /* grab all the int's that were in subobj. if it had elements that were not ints, we throw an exception + (capital V on Vals() means exception if wrong type found + */ + vector<int> myints; + sub.Vals(myints); + cout << "my ints: " << myints[0] << ' ' << myints[1] << endl; + + /* grab all the string values from x. if the field isn't of string type, just skip it -- + lowercase v on vals() indicates skip don't throw. + */ + vector<string> strs; + x.vals(strs); + cout << strs.size() << " strings, first one: " << strs[0] << endl; + + iter(y); + return 0; +} + diff --git a/src/mongo/bson/bsondemo/bsondemo.vcproj b/src/mongo/bson/bsondemo/bsondemo.vcproj new file mode 100644 index 00000000000..8432cebfd87 --- /dev/null +++ b/src/mongo/bson/bsondemo/bsondemo.vcproj @@ -0,0 +1,243 @@ +<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="bsondemo"
+ ProjectGUID="{C9DB5EB7-81AA-4185-BAA1-DA035654402F}"
+ RootNamespace="bsondemo"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="c:\program files\boost\latest;c:\boost;\boost"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="c:\program files\boost\latest;c:\boost;\boost"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\bsondemo.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="bson"
+ >
+ <File
+ RelativePath="..\bson.h"
+ >
+ </File>
+ <File
+ RelativePath="..\bson_db.h"
+ >
+ </File>
+ <File
+ RelativePath="..\bsonelement.h"
+ >
+ </File>
+ <File
+ RelativePath="..\bsoninlines.h"
+ >
+ </File>
+ <File
+ RelativePath="..\bsonmisc.h"
+ >
+ </File>
+ <File
+ RelativePath="..\bsonobj.h"
+ >
+ </File>
+ <File
+ RelativePath="..\bsonobjbuilder.h"
+ >
+ </File>
+ <File
+ RelativePath="..\bsonobjiterator.h"
+ >
+ </File>
+ <File
+ RelativePath="..\bsontypes.h"
+ >
+ </File>
+ <File
+ RelativePath="..\oid.h"
+ >
+ </File>
+ <File
+ RelativePath="..\ordering.h"
+ >
+ </File>
+ <Filter
+ Name="util"
+ >
+ <File
+ RelativePath="..\util\builder.h"
+ >
+ </File>
+ <File
+ RelativePath="..\util\misc.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src/mongo/bson/bsondemo/bsondemo.vcxproj b/src/mongo/bson/bsondemo/bsondemo.vcxproj new file mode 100644 index 00000000000..2ad53894d7d --- /dev/null +++ b/src/mongo/bson/bsondemo/bsondemo.vcxproj @@ -0,0 +1,193 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{C9DB5EB7-81AA-4185-BAA1-DA035654402F}</ProjectGuid>
+ <RootNamespace>bsondemo</RootNamespace>
+ <Keyword>Win32Proj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)$(Configuration)\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Configuration)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>c:\boost;\boost</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>No</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>EditAndContinue</DebugInformationFormat>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>c:\boost;\boost</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <MinimalRebuild>No</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <AdditionalIncludeDirectories>c:\boost;\boost</AdditionalIncludeDirectories>
+ <MinimalRebuild>No</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MaxSpeed</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <AdditionalIncludeDirectories>c:\boost;\boost</AdditionalIncludeDirectories>
+ <MinimalRebuild>No</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <SubSystem>Console</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="bsondemo.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\bson.h" />
+ <ClInclude Include="..\bson_db.h" />
+ <ClInclude Include="..\bsonelement.h" />
+ <ClInclude Include="..\bsoninlines.h" />
+ <ClInclude Include="..\bsonmisc.h" />
+ <ClInclude Include="..\bsonobj.h" />
+ <ClInclude Include="..\bsonobjbuilder.h" />
+ <ClInclude Include="..\bsonobjiterator.h" />
+ <ClInclude Include="..\bsontypes.h" />
+ <ClInclude Include="..\oid.h" />
+ <ClInclude Include="..\ordering.h" />
+ <ClInclude Include="..\util\builder.h" />
+ <ClInclude Include="..\util\misc.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
diff --git a/src/mongo/bson/bsondemo/bsondemo.vcxproj.filters b/src/mongo/bson/bsondemo/bsondemo.vcxproj.filters new file mode 100644 index 00000000000..35f14d5193b --- /dev/null +++ b/src/mongo/bson/bsondemo/bsondemo.vcxproj.filters @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="bsondemo.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\ordering.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\bsonelement.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\bsoninlines.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\bsonmisc.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\bsonobj.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\bsonobjbuilder.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\bsonobjiterator.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\bsontypes.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\util\builder.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\util\misc.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\oid.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\bson_db.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ <ClInclude Include="..\bson.h">
+ <Filter>bson</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="bson">
+ <UniqueIdentifier>{ea599740-3c6f-40dd-a121-e825d82ae4aa}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+</Project>
diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h new file mode 100644 index 00000000000..57cc2ae5775 --- /dev/null +++ b/src/mongo/bson/bsonelement.h @@ -0,0 +1,583 @@ +// BSONElement + +/* 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 <vector> +#include <string.h> +#include "util/builder.h" +#include "bsontypes.h" + +namespace mongo { + class OpTime; + class BSONObj; + class BSONElement; + class BSONObjBuilder; +} + +namespace bson { + typedef mongo::BSONElement be; + typedef mongo::BSONObj bo; + typedef mongo::BSONObjBuilder bob; +} + +namespace mongo { + + /* l and r MUST have same type when called: check that first. */ + int compareElementValues(const BSONElement& l, const BSONElement& r); + + + /** BSONElement represents an "element" in a BSONObj. So for the object { a : 3, b : "abc" }, + 'a : 3' is the first element (key+value). + + The BSONElement object points into the BSONObj's data. Thus the BSONObj must stay in scope + for the life of the BSONElement. + + internals: + <type><fieldName ><value> + -------- size() ------------ + -fieldNameSize- + value() + type() + */ + class BSONElement { + public: + /** These functions, which start with a capital letter, throw a UserException if the + element is not of the required type. Example: + + string foo = obj["foo"].String(); // exception if not a string type or DNE + */ + string String() const { return chk(mongo::String).valuestr(); } + Date_t Date() const { return chk(mongo::Date).date(); } + double Number() const { return chk(isNumber()).number(); } + double Double() const { return chk(NumberDouble)._numberDouble(); } + long long Long() const { return chk(NumberLong)._numberLong(); } + int Int() const { return chk(NumberInt)._numberInt(); } + bool Bool() const { return chk(mongo::Bool).boolean(); } + vector<BSONElement> Array() const; // see implementation for detailed comments + mongo::OID OID() const { return chk(jstOID).__oid(); } + void Null() const { chk(isNull()); } // throw UserException if not null + void OK() const { chk(ok()); } // throw UserException if element DNE + + /** @return the embedded object associated with this field. + Note the returned object is a reference to within the parent bson object. If that + object is out of scope, this pointer will no longer be valid. Call getOwned() on the + returned BSONObj if you need your own copy. + throws UserException if the element is not of type object. + */ + BSONObj Obj() const; + + /** populate v with the value of the element. If type does not match, throw exception. + useful in templates -- see also BSONObj::Vals(). + */ + void Val(Date_t& v) const { v = Date(); } + void Val(long long& v) const { v = Long(); } + void Val(bool& v) const { v = Bool(); } + void Val(BSONObj& v) const; + void Val(mongo::OID& v) const { v = OID(); } + void Val(int& v) const { v = Int(); } + void Val(double& v) const { v = Double(); } + void Val(string& v) const { v = String(); } + + /** Use ok() to check if a value is assigned: + if( myObj["foo"].ok() ) ... + */ + bool ok() const { return !eoo(); } + + string toString( bool includeFieldName = true, bool full=false) const; + void toString(StringBuilder& s, bool includeFieldName = true, bool full=false) const; + string jsonString( JsonStringFormat format, bool includeFieldNames = true, int pretty = 0 ) const; + operator string() const { return toString(); } + + /** Returns the type of the element */ + BSONType type() const { return (BSONType) *data; } + + /** retrieve a field within this element + throws exception if *this is not an embedded object + */ + BSONElement operator[] (const string& field) const; + + /** returns the tyoe of the element fixed for the main type + the main purpose is numbers. any numeric type will return NumberDouble + Note: if the order changes, indexes have to be re-built or than can be corruption + */ + int canonicalType() const; + + /** Indicates if it is the end-of-object element, which is present at the end of + every BSON object. + */ + bool eoo() const { return type() == EOO; } + + /** Size of the element. + @param maxLen If maxLen is specified, don't scan more than maxLen bytes to calculate size. + */ + int size( int maxLen ) const; + int size() const; + + /** Wrap this element up as a singleton object. */ + BSONObj wrap() const; + + /** Wrap this element up as a singleton object with a new name. */ + BSONObj wrap( const char* newName) const; + + /** field name of the element. e.g., for + name : "Joe" + "name" is the fieldname + */ + const char * fieldName() const { + if ( eoo() ) return ""; // no fieldname for it. + return data + 1; + } + + /** raw data of the element's value (so be careful). */ + const char * value() const { + return (data + fieldNameSize() + 1); + } + /** size in bytes of the element's value (when applicable). */ + int valuesize() const { + return size() - fieldNameSize() - 1; + } + + bool isBoolean() const { return type() == mongo::Bool; } + + /** @return value of a boolean element. + You must assure element is a boolean before + calling. */ + bool boolean() const { + return *value() ? true : false; + } + + bool booleanSafe() const { return isBoolean() && boolean(); } + + /** Retrieve a java style date value from the element. + Ensure element is of type Date before calling. + @see Bool(), trueValue() + */ + Date_t date() const { + return *reinterpret_cast< const Date_t* >( value() ); + } + + /** Convert the value to boolean, regardless of its type, in a javascript-like fashion + (i.e., treats zero and null and eoo as false). + */ + bool trueValue() const; + + /** True if number, string, bool, date, OID */ + bool isSimpleType() const; + + /** True if element is of a numeric type. */ + bool isNumber() const; + + /** Return double value for this field. MUST be NumberDouble type. */ + double _numberDouble() const {return *reinterpret_cast< const double* >( value() ); } + /** Return int value for this field. MUST be NumberInt type. */ + int _numberInt() const {return *reinterpret_cast< const int* >( value() ); } + /** Return long long value for this field. MUST be NumberLong type. */ + long long _numberLong() const {return *reinterpret_cast< const long long* >( value() ); } + + /** Retrieve int value for the element safely. Zero returned if not a number. */ + int numberInt() const; + /** Retrieve long value for the element safely. Zero returned if not a number. */ + long long numberLong() const; + /** Retrieve the numeric value of the element. If not of a numeric type, returns 0. + Note: casts to double, data loss may occur with large (>52 bit) NumberLong values. + */ + double numberDouble() const; + /** Retrieve the numeric value of the element. If not of a numeric type, returns 0. + Note: casts to double, data loss may occur with large (>52 bit) NumberLong values. + */ + double number() const { return numberDouble(); } + + /** Retrieve the object ID stored in the object. + You must ensure the element is of type jstOID first. */ + const mongo::OID &__oid() const { return *reinterpret_cast< const mongo::OID* >( value() ); } + + /** True if element is null. */ + bool isNull() const { + return type() == jstNULL; + } + + /** Size (length) of a string element. + You must assure of type String first. + @return string size including terminating null + */ + int valuestrsize() const { + return *reinterpret_cast< const int* >( value() ); + } + + // for objects the size *includes* the size of the size field + int objsize() const { + return *reinterpret_cast< const int* >( value() ); + } + + /** Get a string's value. Also gives you start of the real data for an embedded object. + You must assure data is of an appropriate type first -- see also valuestrsafe(). + */ + const char * valuestr() const { + return value() + 4; + } + + /** Get the string value of the element. If not a string returns "". */ + const char *valuestrsafe() const { + return type() == mongo::String ? valuestr() : ""; + } + /** Get the string value of the element. If not a string returns "". */ + string str() const { + return type() == mongo::String ? string(valuestr(), valuestrsize()-1) : string(); + } + + /** Get javascript code of a CodeWScope data element. */ + const char * codeWScopeCode() const { + return value() + 8; + } + /** Get the scope SavedContext of a CodeWScope data element. */ + const char * codeWScopeScopeData() const { + // TODO fix + return codeWScopeCode() + strlen( codeWScopeCode() ) + 1; + } + + /** Get the embedded object this element holds. */ + BSONObj embeddedObject() const; + + /* uasserts if not an object */ + BSONObj embeddedObjectUserCheck() const; + + BSONObj codeWScopeObject() const; + + /** Get raw binary data. Element must be of type BinData. Doesn't handle type 2 specially */ + const char *binData(int& len) const { + // BinData: <int len> <byte subtype> <byte[len] data> + assert( type() == BinData ); + len = valuestrsize(); + return value() + 5; + } + /** Get binary data. Element must be of type BinData. Handles type 2 */ + const char *binDataClean(int& len) const { + // BinData: <int len> <byte subtype> <byte[len] data> + if (binDataType() != ByteArrayDeprecated) { + return binData(len); + } + else { + // Skip extra size + len = valuestrsize() - 4; + return value() + 5 + 4; + } + } + + BinDataType binDataType() const { + // BinData: <int len> <byte subtype> <byte[len] data> + assert( type() == BinData ); + unsigned char c = (value() + 4)[0]; + return (BinDataType)c; + } + + /** Retrieve the regex string for a Regex element */ + const char *regex() const { + assert(type() == RegEx); + return value(); + } + + /** Retrieve the regex flags (options) for a Regex element */ + const char *regexFlags() const { + const char *p = regex(); + return p + strlen(p) + 1; + } + + /** like operator== but doesn't check the fieldname, + just the value. + */ + bool valuesEqual(const BSONElement& r) const { + return woCompare( r , false ) == 0; + } + + /** Returns true if elements are equal. */ + bool operator==(const BSONElement& r) const { + return woCompare( r , true ) == 0; + } + /** Returns true if elements are unequal. */ + bool operator!=(const BSONElement& r) const { return !operator==(r); } + + /** Well ordered comparison. + @return <0: l<r. 0:l==r. >0:l>r + order by type, field name, and field value. + If considerFieldName is true, pay attention to the field name. + */ + int woCompare( const BSONElement &e, bool considerFieldName = true ) const; + + const char * rawdata() const { return data; } + + /** 0 == Equality, just not defined yet */ + int getGtLtOp( int def = 0 ) const; + + /** Constructs an empty element */ + BSONElement(); + + /** Check that data is internally consistent. */ + void validate() const; + + /** True if this element may contain subobjects. */ + bool mayEncapsulate() const { + switch ( type() ) { + case Object: + case mongo::Array: + case CodeWScope: + return true; + default: + return false; + } + } + + /** True if this element can be a BSONObj */ + bool isABSONObj() const { + switch( type() ) { + case Object: + case mongo::Array: + return true; + default: + return false; + } + } + + Date_t timestampTime() const { + unsigned long long t = ((unsigned int*)(value() + 4 ))[0]; + return t * 1000; + } + unsigned int timestampInc() const { + return ((unsigned int*)(value() ))[0]; + } + + const char * dbrefNS() const { + uassert( 10063 , "not a dbref" , type() == DBRef ); + return value() + 4; + } + + const mongo::OID& dbrefOID() const { + uassert( 10064 , "not a dbref" , type() == DBRef ); + const char * start = value(); + start += 4 + *reinterpret_cast< const int* >( start ); + return *reinterpret_cast< const mongo::OID* >( start ); + } + + /** this does not use fieldName in the comparison, just the value */ + bool operator<( const BSONElement& other ) const { + int x = (int)canonicalType() - (int)other.canonicalType(); + if ( x < 0 ) return true; + else if ( x > 0 ) return false; + return compareElementValues(*this,other) < 0; + } + + // @param maxLen don't scan more than maxLen bytes + explicit BSONElement(const char *d, int maxLen) : data(d) { + if ( eoo() ) { + totalSize = 1; + fieldNameSize_ = 0; + } + else { + totalSize = -1; + fieldNameSize_ = -1; + if ( maxLen != -1 ) { + int size = (int) strnlen( fieldName(), maxLen - 1 ); + uassert( 10333 , "Invalid field name", size != -1 ); + fieldNameSize_ = size + 1; + } + } + } + + explicit BSONElement(const char *d) : data(d) { + fieldNameSize_ = -1; + totalSize = -1; + if ( eoo() ) { + fieldNameSize_ = 0; + totalSize = 1; + } + } + + string _asCode() const; + OpTime _opTime() const; + + private: + const char *data; + mutable int fieldNameSize_; // cached value + int fieldNameSize() const { + if ( fieldNameSize_ == -1 ) + fieldNameSize_ = (int)strlen( fieldName() ) + 1; + return fieldNameSize_; + } + mutable int totalSize; /* caches the computed size */ + + friend class BSONObjIterator; + friend class BSONObj; + const BSONElement& chk(int t) const { + if ( t != type() ) { + StringBuilder ss; + if( eoo() ) + ss << "field not found, expected type " << t; + else + ss << "wrong type for field (" << fieldName() << ") " << type() << " != " << t; + uasserted(13111, ss.str() ); + } + return *this; + } + const BSONElement& chk(bool expr) const { + uassert(13118, "unexpected or missing type value in BSON object", expr); + return *this; + } + }; + + + inline int BSONElement::canonicalType() const { + BSONType t = type(); + switch ( t ) { + case MinKey: + case MaxKey: + return t; + case EOO: + case Undefined: + return 0; + case jstNULL: + return 5; + case NumberDouble: + case NumberInt: + case NumberLong: + return 10; + case mongo::String: + case Symbol: + return 15; + case Object: + return 20; + case mongo::Array: + return 25; + case BinData: + return 30; + case jstOID: + return 35; + case mongo::Bool: + return 40; + case mongo::Date: + case Timestamp: + return 45; + case RegEx: + return 50; + case DBRef: + return 55; + case Code: + return 60; + case CodeWScope: + return 65; + default: + assert(0); + return -1; + } + } + + inline bool BSONElement::trueValue() const { + switch( type() ) { + case NumberLong: + return *reinterpret_cast< const long long* >( value() ) != 0; + case NumberDouble: + return *reinterpret_cast< const double* >( value() ) != 0; + case NumberInt: + return *reinterpret_cast< const int* >( value() ) != 0; + case mongo::Bool: + return boolean(); + case EOO: + case jstNULL: + case Undefined: + return false; + + default: + ; + } + return true; + } + + /** @return true if element is of a numeric type. */ + inline bool BSONElement::isNumber() const { + switch( type() ) { + case NumberLong: + case NumberDouble: + case NumberInt: + return true; + default: + return false; + } + } + + inline bool BSONElement::isSimpleType() const { + switch( type() ) { + case NumberLong: + case NumberDouble: + case NumberInt: + case mongo::String: + case mongo::Bool: + case mongo::Date: + case jstOID: + return true; + default: + return false; + } + } + + inline double BSONElement::numberDouble() const { + switch( type() ) { + case NumberDouble: + return _numberDouble(); + case NumberInt: + return *reinterpret_cast< const int* >( value() ); + case NumberLong: + return (double) *reinterpret_cast< const long long* >( value() ); + default: + return 0; + } + } + + /** Retrieve int value for the element safely. Zero returned if not a number. Converted to int if another numeric type. */ + inline int BSONElement::numberInt() const { + switch( type() ) { + case NumberDouble: + return (int) _numberDouble(); + case NumberInt: + return _numberInt(); + case NumberLong: + return (int) _numberLong(); + default: + return 0; + } + } + + /** Retrieve long value for the element safely. Zero returned if not a number. */ + inline long long BSONElement::numberLong() const { + switch( type() ) { + case NumberDouble: + return (long long) _numberDouble(); + case NumberInt: + return _numberInt(); + case NumberLong: + return _numberLong(); + default: + return 0; + } + } + + inline BSONElement::BSONElement() { + static char z = 0; + data = &z; + fieldNameSize_ = 0; + totalSize = 1; + } + +} diff --git a/src/mongo/bson/bsonmisc.h b/src/mongo/bson/bsonmisc.h new file mode 100644 index 00000000000..8a379396d17 --- /dev/null +++ b/src/mongo/bson/bsonmisc.h @@ -0,0 +1,211 @@ +// @file bsonmisc.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 + +namespace mongo { + + int getGtLtOp(const BSONElement& e); + + struct BSONElementCmpWithoutField { + bool operator()( const BSONElement &l, const BSONElement &r ) const { + return l.woCompare( r, false ) < 0; + } + }; + + class BSONObjCmp { + public: + BSONObjCmp( const BSONObj &order = BSONObj() ) : _order( order ) {} + bool operator()( const BSONObj &l, const BSONObj &r ) const { + return l.woCompare( r, _order ) < 0; + } + BSONObj order() const { return _order; } + private: + BSONObj _order; + }; + + typedef set<BSONObj,BSONObjCmp> BSONObjSet; + + enum FieldCompareResult { + LEFT_SUBFIELD = -2, + LEFT_BEFORE = -1, + SAME = 0, + RIGHT_BEFORE = 1 , + RIGHT_SUBFIELD = 2 + }; + + FieldCompareResult compareDottedFieldNames( const string& l , const string& r ); + + /** Use BSON macro to build a BSONObj from a stream + + e.g., + BSON( "name" << "joe" << "age" << 33 ) + + with auto-generated object id: + BSON( GENOID << "name" << "joe" << "age" << 33 ) + + The labels GT, GTE, LT, LTE, NE can be helpful for stream-oriented construction + of a BSONObj, particularly when assembling a Query. For example, + BSON( "a" << GT << 23.4 << NE << 30 << "b" << 2 ) produces the object + { a: { \$gt: 23.4, \$ne: 30 }, b: 2 }. + */ +#define BSON(x) (( mongo::BSONObjBuilder(64) << x ).obj()) + + /** Use BSON_ARRAY macro like BSON macro, but without keys + + BSONArray arr = BSON_ARRAY( "hello" << 1 << BSON( "foo" << BSON_ARRAY( "bar" << "baz" << "qux" ) ) ); + + */ +#define BSON_ARRAY(x) (( mongo::BSONArrayBuilder() << x ).arr()) + + /* Utility class to auto assign object IDs. + Example: + cout << BSON( GENOID << "z" << 3 ); // { _id : ..., z : 3 } + */ + extern struct GENOIDLabeler { } GENOID; + + /* Utility class to add a Date element with the current time + Example: + cout << BSON( "created" << DATENOW ); // { created : "2009-10-09 11:41:42" } + */ + extern struct DateNowLabeler { } DATENOW; + + /* Utility class to assign a NULL value to a given attribute + Example: + cout << BSON( "a" << BSONNULL ); // { a : null } + */ + extern struct NullLabeler { } BSONNULL; + + /* Utility class to add the minKey (minus infinity) to a given attribute + Example: + cout << BSON( "a" << MINKEY ); // { "a" : { "$minKey" : 1 } } + */ + extern struct MinKeyLabeler { } MINKEY; + extern struct MaxKeyLabeler { } MAXKEY; + + // Utility class to implement GT, GTE, etc as described above. + class Labeler { + public: + struct Label { + Label( const char *l ) : l_( l ) {} + const char *l_; + }; + Labeler( const Label &l, BSONObjBuilderValueStream *s ) : l_( l ), s_( s ) {} + template<class T> + BSONObjBuilder& operator<<( T value ); + + /* the value of the element e is appended i.e. for + "age" << GT << someElement + one gets + { age : { $gt : someElement's value } } + */ + BSONObjBuilder& operator<<( const BSONElement& e ); + private: + const Label &l_; + BSONObjBuilderValueStream *s_; + }; + + extern Labeler::Label GT; + extern Labeler::Label GTE; + extern Labeler::Label LT; + extern Labeler::Label LTE; + extern Labeler::Label NE; + extern Labeler::Label SIZE; + + + // $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT << 6)); + // becomes : {$or: [{x: {$gt: 7}}, {y: {$lt: 6}}]} + inline BSONObj OR(const BSONObj& a, const BSONObj& b); + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c); + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d); + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e); + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e, const BSONObj& f); + // definitions in bsonobjbuilder.h b/c of incomplete types + + // Utility class to implement BSON( key << val ) as described above. + class BSONObjBuilderValueStream : public boost::noncopyable { + public: + friend class Labeler; + BSONObjBuilderValueStream( BSONObjBuilder * builder ); + + BSONObjBuilder& operator<<( const BSONElement& e ); + + template<class T> + BSONObjBuilder& operator<<( T value ); + + BSONObjBuilder& operator<<(DateNowLabeler& id); + + BSONObjBuilder& operator<<(NullLabeler& id); + + BSONObjBuilder& operator<<(MinKeyLabeler& id); + BSONObjBuilder& operator<<(MaxKeyLabeler& id); + + Labeler operator<<( const Labeler::Label &l ); + + void endField( const char *nextFieldName = 0 ); + bool subobjStarted() const { return _fieldName != 0; } + + private: + const char * _fieldName; + BSONObjBuilder * _builder; + + bool haveSubobj() const { return _subobj.get() != 0; } + BSONObjBuilder *subobj(); + auto_ptr< BSONObjBuilder > _subobj; + }; + + /** + used in conjuction with BSONObjBuilder, allows for proper buffer size to prevent crazy memory usage + */ + class BSONSizeTracker { + public: + BSONSizeTracker() { + _pos = 0; + for ( int i=0; i<SIZE; i++ ) + _sizes[i] = 512; // this is the default, so just be consistent + } + + ~BSONSizeTracker() { + } + + void got( int size ) { + _sizes[_pos++] = size; + if ( _pos >= SIZE ) + _pos = 0; + } + + /** + * right now choosing largest size + */ + int getSize() const { + int x = 16; // sane min + for ( int i=0; i<SIZE; i++ ) { + if ( _sizes[i] > x ) + x = _sizes[i]; + } + return x; + } + + private: + enum { SIZE = 10 }; + int _pos; + int _sizes[SIZE]; + }; + + // considers order + bool fieldsMatch(const BSONObj& lhs, const BSONObj& rhs); +} diff --git a/src/mongo/bson/bsonobj.h b/src/mongo/bson/bsonobj.h new file mode 100644 index 00000000000..e8ce462403b --- /dev/null +++ b/src/mongo/bson/bsonobj.h @@ -0,0 +1,497 @@ +// @file bsonobj.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 <boost/intrusive_ptr.hpp> +#include <set> +#include <list> +#include <vector> +#include "util/atomic_int.h" +#include "util/builder.h" +#include "stringdata.h" + +namespace mongo { + + typedef set< BSONElement, BSONElementCmpWithoutField > BSONElementSet; + typedef multiset< BSONElement, BSONElementCmpWithoutField > BSONElementMSet; + + /** + C++ representation of a "BSON" object -- that is, an extended JSON-style + object in a binary representation. + + See bsonspec.org. + + Note that BSONObj's have a smart pointer capability built in -- so you can + pass them around by value. The reference counts used to implement this + do not use locking, so copying and destroying BSONObj's are not thread-safe + operations. + + BSON object format: + + code + <unsigned totalSize> {<byte BSONType><cstring FieldName><Data>}* EOO + + totalSize includes itself. + + Data: + Bool: <byte> + EOO: nothing follows + Undefined: nothing follows + OID: an OID object + NumberDouble: <double> + NumberInt: <int32> + String: <unsigned32 strsizewithnull><cstring> + Date: <8bytes> + Regex: <cstring regex><cstring options> + Object: a nested object, leading with its entire size, which terminates with EOO. + Array: same as object + DBRef: <strlen> <cstring ns> <oid> + DBRef: a database reference: basically a collection name plus an Object ID + BinData: <int len> <byte subtype> <byte[len] data> + Code: a function (not a closure): same format as String. + Symbol: a language symbol (say a python symbol). same format as String. + Code With Scope: <total size><String><Object> + \endcode + */ + class BSONObj { + public: + + /** Construct a BSONObj from data in the proper format. + * Use this constructor when something else owns msgdata's buffer + */ + explicit BSONObj(const char *msgdata) { + init(msgdata); + } + + /** Construct a BSONObj from data in the proper format. + * Use this constructor when you want BSONObj to free(holder) when it is no longer needed + * BSONObj::Holder has an extra 4 bytes for a ref-count before the start of the object + */ + class Holder; + explicit BSONObj(Holder* holder) { + init(holder); + } + + explicit BSONObj(const Record *r); + + /** Construct an empty BSONObj -- that is, {}. */ + BSONObj(); + + ~BSONObj() { + _objdata = 0; // defensive + } + + /** + A BSONObj can use a buffer it "owns" or one it does not. + + OWNED CASE + If the BSONObj owns the buffer, the buffer can be shared among several BSONObj's (by assignment). + In this case the buffer is basically implemented as a shared_ptr. + Since BSONObj's are typically immutable, this works well. + + UNOWNED CASE + A BSONObj can also point to BSON data in some other data structure it does not "own" or free later. + For example, in a memory mapped file. In this case, it is important the original data stays in + scope for as long as the BSONObj is in use. If you think the original data may go out of scope, + call BSONObj::getOwned() to promote your BSONObj to having its own copy. + + On a BSONObj assignment, if the source is unowned, both the source and dest will have unowned + pointers to the original buffer after the assignment. + + If you are not sure about ownership but need the buffer to last as long as the BSONObj, call + getOwned(). getOwned() is a no-op if the buffer is already owned. If not already owned, a malloc + and memcpy will result. + + Most ways to create BSONObj's create 'owned' variants. Unowned versions can be created with: + (1) specifying true for the ifree parameter in the constructor + (2) calling BSONObjBuilder::done(). Use BSONObjBuilder::obj() to get an owned copy + (3) retrieving a subobject retrieves an unowned pointer into the parent BSON object + + @return true if this is in owned mode + */ + bool isOwned() const { return _holder.get() != 0; } + + /** assure the data buffer is under the control of this BSONObj and not a remote buffer + @see isOwned() + */ + BSONObj getOwned() const; + + /** @return a new full (and owned) copy of the object. */ + BSONObj copy() const; + + /** Readable representation of a BSON object in an extended JSON-style notation. + This is an abbreviated representation which might be used for logging. + */ + string toString( bool isArray = false, bool full=false ) const; + void toString(StringBuilder& s, bool isArray = false, bool full=false ) const; + + /** Properly formatted JSON string. + @param pretty if true we try to add some lf's and indentation + */ + string jsonString( JsonStringFormat format = Strict, int pretty = 0 ) const; + + /** note: addFields always adds _id even if not specified */ + int addFields(BSONObj& from, set<string>& fields); /* returns n added */ + + /** remove specified field and return a new object with the remaining fields. + slowish as builds a full new object + */ + BSONObj removeField(const StringData& name) const; + + /** returns # of top level fields in the object + note: iterates to count the fields + */ + int nFields() const; + + /** adds the field names to the fields set. does NOT clear it (appends). */ + int getFieldNames(set<string>& fields) const; + + /** @return the specified element. element.eoo() will be true if not found. + @param name field to find. supports dot (".") notation to reach into embedded objects. + for example "x.y" means "in the nested object in field x, retrieve field y" + */ + BSONElement getFieldDotted(const char *name) const; + /** @return the specified element. element.eoo() will be true if not found. + @param name field to find. supports dot (".") notation to reach into embedded objects. + for example "x.y" means "in the nested object in field x, retrieve field y" + */ + BSONElement getFieldDotted(const string& name) const { + return getFieldDotted( name.c_str() ); + } + + /** Like getFieldDotted(), but expands arrays and returns all matching objects. + * Turning off expandLastArray allows you to retrieve nested array objects instead of + * their contents. + */ + void getFieldsDotted(const StringData& name, BSONElementSet &ret, bool expandLastArray = true ) const; + void getFieldsDotted(const StringData& name, BSONElementMSet &ret, bool expandLastArray = true ) const; + + /** Like getFieldDotted(), but returns first array encountered while traversing the + dotted fields of name. The name variable is updated to represent field + names with respect to the returned element. */ + BSONElement getFieldDottedOrArray(const char *&name) const; + + /** Get the field of the specified name. eoo() is true on the returned + element if not found. + */ + BSONElement getField(const StringData& name) const; + + /** Get several fields at once. This is faster than separate getField() calls as the size of + elements iterated can then be calculated only once each. + @param n number of fieldNames, and number of elements in the fields array + @param fields if a field is found its element is stored in its corresponding position in this array. + if not found the array element is unchanged. + */ + void getFields(unsigned n, const char **fieldNames, BSONElement *fields) const; + + /** Get the field of the specified name. eoo() is true on the returned + element if not found. + */ + BSONElement operator[] (const char *field) const { + return getField(field); + } + + BSONElement operator[] (const string& field) const { + return getField(field); + } + + BSONElement operator[] (int field) const { + StringBuilder ss; + ss << field; + string s = ss.str(); + return getField(s.c_str()); + } + + /** @return true if field exists */ + bool hasField( const char * name ) const { return !getField(name).eoo(); } + /** @return true if field exists */ + bool hasElement(const char *name) const { return hasField(name); } + + /** @return "" if DNE or wrong type */ + const char * getStringField(const char *name) const; + + /** @return subobject of the given name */ + BSONObj getObjectField(const char *name) const; + + /** @return INT_MIN if not present - does some type conversions */ + int getIntField(const char *name) const; + + /** @return false if not present + @see BSONElement::trueValue() + */ + bool getBoolField(const char *name) const; + + /** + sets element field names to empty string + If a field in pattern is missing, it is omitted from the returned + object. + */ + BSONObj extractFieldsUnDotted(BSONObj pattern) const; + + /** extract items from object which match a pattern object. + e.g., if pattern is { x : 1, y : 1 }, builds an object with + x and y elements of this object, if they are present. + returns elements with original field names + */ + BSONObj extractFields(const BSONObj &pattern , bool fillWithNull=false) const; + + BSONObj filterFieldsUndotted(const BSONObj &filter, bool inFilter) const; + + BSONElement getFieldUsingIndexNames(const char *fieldName, const BSONObj &indexKey) const; + + /** arrays are bson objects with numeric and increasing field names + @return true if field names are numeric and increasing + */ + bool couldBeArray() const; + + /** @return the raw data of the object */ + const char *objdata() const { + return _objdata; + } + /** @return total size of the BSON object in bytes */ + int objsize() const { return *(reinterpret_cast<const int*>(objdata())); } + + /** performs a cursory check on the object's size only. */ + bool isValid() const; + + /** @return if the user is a valid user doc + criter: isValid() no . or $ field names + */ + bool okForStorage() const; + + /** @return true if object is empty -- i.e., {} */ + bool isEmpty() const { return objsize() <= 5; } + + void dump() const; + + /** Alternative output format */ + string hexDump() const; + + /**wo='well ordered'. fields must be in same order in each object. + Ordering is with respect to the signs of the elements + and allows ascending / descending key mixing. + @return <0 if l<r. 0 if l==r. >0 if l>r + */ + int woCompare(const BSONObj& r, const Ordering &o, + bool considerFieldName=true) const; + + /**wo='well ordered'. fields must be in same order in each object. + Ordering is with respect to the signs of the elements + and allows ascending / descending key mixing. + @return <0 if l<r. 0 if l==r. >0 if l>r + */ + int woCompare(const BSONObj& r, const BSONObj &ordering = BSONObj(), + bool considerFieldName=true) const; + + bool operator<( const BSONObj& other ) const { return woCompare( other ) < 0; } + bool operator<=( const BSONObj& other ) const { return woCompare( other ) <= 0; } + bool operator>( const BSONObj& other ) const { return woCompare( other ) > 0; } + bool operator>=( const BSONObj& other ) const { return woCompare( other ) >= 0; } + + /** + * @param useDotted whether to treat sort key fields as possibly dotted and expand into them + */ + int woSortOrder( const BSONObj& r , const BSONObj& sortKey , bool useDotted=false ) const; + + bool equal(const BSONObj& r) const; + + /** This is "shallow equality" -- ints and doubles won't match. for a + deep equality test use woCompare (which is slower). + */ + bool binaryEqual(const BSONObj& r) const { + int os = objsize(); + if ( os == r.objsize() ) { + return (os == 0 || memcmp(objdata(),r.objdata(),os)==0); + } + return false; + } + + /** @return first field of the object */ + BSONElement firstElement() const { return BSONElement(objdata() + 4); } + + /** faster than firstElement().fieldName() - for the first element we can easily find the fieldname without + computing the element size. + */ + const char * firstElementFieldName() const { + const char *p = objdata() + 4; + return *p == EOO ? "" : p+1; + } + + BSONType firstElementType() const { + const char *p = objdata() + 4; + return (BSONType) *p; + } + + /** Get the _id field from the object. For good performance drivers should + assure that _id is the first element of the object; however, correct operation + is assured regardless. + @return true if found + */ + bool getObjectID(BSONElement& e) const; + + /** @return A hash code for the object */ + int hash() const { + unsigned x = 0; + const char *p = objdata(); + for ( int i = 0; i < objsize(); i++ ) + x = x * 131 + p[i]; + return (x & 0x7fffffff) | 0x8000000; // must be > 0 + } + + // Return a version of this object where top level elements of types + // that are not part of the bson wire protocol are replaced with + // string identifier equivalents. + // TODO Support conversion of element types other than min and max. + BSONObj clientReadable() const; + + /** Return new object with the field names replaced by those in the + passed object. */ + BSONObj replaceFieldNames( const BSONObj &obj ) const; + + /** true unless corrupt */ + bool valid() const; + + /** @return an md5 value for this object. */ + string md5() const; + + bool operator==( const BSONObj& other ) const { return equal( other ); } + bool operator!=(const BSONObj& other) const { return !operator==( other); } + + enum MatchType { + Equality = 0, + LT = 0x1, + LTE = 0x3, + GTE = 0x6, + GT = 0x4, + opIN = 0x8, // { x : { $in : [1,2,3] } } + NE = 0x9, + opSIZE = 0x0A, + opALL = 0x0B, + NIN = 0x0C, + opEXISTS = 0x0D, + opMOD = 0x0E, + opTYPE = 0x0F, + opREGEX = 0x10, + opOPTIONS = 0x11, + opELEM_MATCH = 0x12, + opNEAR = 0x13, + opWITHIN = 0x14, + opMAX_DISTANCE=0x15 + }; + + /** add all elements of the object to the specified vector */ + void elems(vector<BSONElement> &) const; + /** add all elements of the object to the specified list */ + void elems(list<BSONElement> &) const; + + /** add all values of the object to the specified vector. If type mismatches, exception. + this is most useful when the BSONObj is an array, but can be used with non-arrays too in theory. + + example: + bo sub = y["subobj"].Obj(); + vector<int> myints; + sub.Vals(myints); + */ + template <class T> + void Vals(vector<T> &) const; + /** add all values of the object to the specified list. If type mismatches, exception. */ + template <class T> + void Vals(list<T> &) const; + + /** add all values of the object to the specified vector. If type mismatches, skip. */ + template <class T> + void vals(vector<T> &) const; + /** add all values of the object to the specified list. If type mismatches, skip. */ + template <class T> + void vals(list<T> &) const; + + friend class BSONObjIterator; + typedef BSONObjIterator iterator; + + /** use something like this: + for( BSONObj::iterator i = myObj.begin(); i.more(); ) { + BSONElement e = i.next(); + ... + } + */ + BSONObjIterator begin() const; + + void appendSelfToBufBuilder(BufBuilder& b) const { + assert( objsize() ); + b.appendBuf(reinterpret_cast<const void *>( objdata() ), objsize()); + } + +#pragma pack(1) + class Holder : boost::noncopyable { + private: + Holder(); // this class should never be explicitly created + AtomicUInt refCount; + public: + char data[4]; // start of object + + void zero() { refCount.zero(); } + + // these are called automatically by boost::intrusive_ptr + friend void intrusive_ptr_add_ref(Holder* h) { h->refCount++; } + friend void intrusive_ptr_release(Holder* h) { +#if defined(_DEBUG) // cant use dassert or DEV here + assert((int)h->refCount > 0); // make sure we haven't already freed the buffer +#endif + if(--(h->refCount) == 0){ +#if defined(_DEBUG) + unsigned sz = (unsigned&) *h->data; + assert(sz < BSONObjMaxInternalSize * 3); + memset(h->data, 0xdd, sz); +#endif + free(h); + } + } + }; +#pragma pack() + + private: + const char *_objdata; + boost::intrusive_ptr< Holder > _holder; + + void _assertInvalid() const; + + void init(Holder *holder) { + _holder = holder; // holder is now managed by intrusive_ptr + init(holder->data); + } + void init(const char *data) { + _objdata = data; + if ( !isValid() ) + _assertInvalid(); + } + }; + + ostream& operator<<( ostream &s, const BSONObj &o ); + ostream& operator<<( ostream &s, const BSONElement &e ); + + StringBuilder& operator<<( StringBuilder &s, const BSONObj &o ); + StringBuilder& operator<<( StringBuilder &s, const BSONElement &e ); + + + struct BSONArray : BSONObj { + // Don't add anything other than forwarding constructors!!! + BSONArray(): BSONObj() {} + explicit BSONArray(const BSONObj& obj): BSONObj(obj) {} + }; + +} diff --git a/src/mongo/bson/bsonobjbuilder.h b/src/mongo/bson/bsonobjbuilder.h new file mode 100644 index 00000000000..1fdbcba18a6 --- /dev/null +++ b/src/mongo/bson/bsonobjbuilder.h @@ -0,0 +1,842 @@ +/* bsonobjbuilder.h + + Classes in this file: + BSONObjBuilder + BSONArrayBuilder +*/ + +/* 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 <limits> +#include <cmath> +#include <boost/static_assert.hpp> +#include "bsonelement.h" +#include "bsonobj.h" +#include "bsonmisc.h" + +namespace mongo { + + using namespace std; + +#if defined(_WIN32) +// warning: 'this' : used in base member initializer list +#pragma warning( disable : 4355 ) +#endif + + template<typename T> + class BSONFieldValue { + public: + BSONFieldValue( const string& name , const T& t ) { + _name = name; + _t = t; + } + + const T& value() const { return _t; } + const string& name() const { return _name; } + + private: + string _name; + T _t; + }; + + template<typename T> + class BSONField { + public: + BSONField( const string& name , const string& longName="" ) + : _name(name), _longName(longName) {} + const string& name() const { return _name; } + operator string() const { return _name; } + + BSONFieldValue<T> make( const T& t ) const { + return BSONFieldValue<T>( _name , t ); + } + + BSONFieldValue<BSONObj> gt( const T& t ) const { return query( "$gt" , t ); } + BSONFieldValue<BSONObj> lt( const T& t ) const { return query( "$lt" , t ); } + + BSONFieldValue<BSONObj> query( const char * q , const T& t ) const; + + BSONFieldValue<T> operator()( const T& t ) const { + return BSONFieldValue<T>( _name , t ); + } + + private: + string _name; + string _longName; + }; + + /** Utility for creating a BSONObj. + See also the BSON() and BSON_ARRAY() macros. + */ + class BSONObjBuilder : boost::noncopyable { + public: + /** @param initsize this is just a hint as to the final size of the object */ + BSONObjBuilder(int initsize=512) : _b(_buf), _buf(initsize + sizeof(unsigned)), _offset( sizeof(unsigned) ), _s( this ) , _tracker(0) , _doneCalled(false) { + _b.appendNum((unsigned)0); // ref-count + _b.skip(4); /*leave room for size field and ref-count*/ + } + + /** @param baseBuilder construct a BSONObjBuilder using an existing BufBuilder + * This is for more efficient adding of subobjects/arrays. See docs for subobjStart for example. + */ + BSONObjBuilder( BufBuilder &baseBuilder ) : _b( baseBuilder ), _buf( 0 ), _offset( baseBuilder.len() ), _s( this ) , _tracker(0) , _doneCalled(false) { + _b.skip( 4 ); + } + + BSONObjBuilder( const BSONSizeTracker & tracker ) : _b(_buf) , _buf(tracker.getSize() + sizeof(unsigned) ), _offset( sizeof(unsigned) ), _s( this ) , _tracker( (BSONSizeTracker*)(&tracker) ) , _doneCalled(false) { + _b.appendNum((unsigned)0); // ref-count + _b.skip(4); + } + + ~BSONObjBuilder() { + if ( !_doneCalled && _b.buf() && _buf.getSize() == 0 ) { + _done(); + } + } + + /** add all the fields from the object specified to this object */ + BSONObjBuilder& appendElements(BSONObj x); + + /** add all the fields from the object specified to this object if they don't exist already */ + BSONObjBuilder& appendElementsUnique( BSONObj x ); + + /** append element to the object we are building */ + BSONObjBuilder& append( const BSONElement& e) { + assert( !e.eoo() ); // do not append eoo, that would corrupt us. the builder auto appends when done() is called. + _b.appendBuf((void*) e.rawdata(), e.size()); + return *this; + } + + /** append an element but with a new name */ + BSONObjBuilder& appendAs(const BSONElement& e, const StringData& fieldName) { + assert( !e.eoo() ); // do not append eoo, that would corrupt us. the builder auto appends when done() is called. + _b.appendNum((char) e.type()); + _b.appendStr(fieldName); + _b.appendBuf((void *) e.value(), e.valuesize()); + return *this; + } + + /** add a subobject as a member */ + BSONObjBuilder& append(const StringData& fieldName, BSONObj subObj) { + _b.appendNum((char) Object); + _b.appendStr(fieldName); + _b.appendBuf((void *) subObj.objdata(), subObj.objsize()); + return *this; + } + + /** add a subobject as a member */ + BSONObjBuilder& appendObject(const StringData& fieldName, const char * objdata , int size = 0 ) { + assert( objdata ); + if ( size == 0 ) { + size = *((int*)objdata); + } + + assert( size > 4 && size < 100000000 ); + + _b.appendNum((char) Object); + _b.appendStr(fieldName); + _b.appendBuf((void*)objdata, size ); + return *this; + } + + /** add header for a new subobject and return bufbuilder for writing to + * the subobject's body + * + * example: + * + * BSONObjBuilder b; + * BSONObjBuilder sub (b.subobjStart("fieldName")); + * // use sub + * sub.done() + * // use b and convert to object + */ + BufBuilder &subobjStart(const StringData& fieldName) { + _b.appendNum((char) Object); + _b.appendStr(fieldName); + return _b; + } + + /** add a subobject as a member with type Array. Thus arr object should have "0", "1", ... + style fields in it. + */ + BSONObjBuilder& appendArray(const StringData& fieldName, const BSONObj &subObj) { + _b.appendNum((char) Array); + _b.appendStr(fieldName); + _b.appendBuf((void *) subObj.objdata(), subObj.objsize()); + return *this; + } + BSONObjBuilder& append(const StringData& fieldName, BSONArray arr) { + return appendArray(fieldName, arr); + } + + /** add header for a new subarray and return bufbuilder for writing to + the subarray's body */ + BufBuilder &subarrayStart(const StringData& fieldName) { + _b.appendNum((char) Array); + _b.appendStr(fieldName); + return _b; + } + + /** Append a boolean element */ + BSONObjBuilder& appendBool(const StringData& fieldName, int val) { + _b.appendNum((char) Bool); + _b.appendStr(fieldName); + _b.appendNum((char) (val?1:0)); + return *this; + } + + /** Append a boolean element */ + BSONObjBuilder& append(const StringData& fieldName, bool val) { + _b.appendNum((char) Bool); + _b.appendStr(fieldName); + _b.appendNum((char) (val?1:0)); + return *this; + } + + /** Append a 32 bit integer element */ + BSONObjBuilder& append(const StringData& fieldName, int n) { + _b.appendNum((char) NumberInt); + _b.appendStr(fieldName); + _b.appendNum(n); + return *this; + } + + /** Append a 32 bit unsigned element - cast to a signed int. */ + BSONObjBuilder& append(const StringData& fieldName, unsigned n) { + return append(fieldName, (int) n); + } + + /** Append a NumberLong */ + BSONObjBuilder& append(const StringData& fieldName, long long n) { + _b.appendNum((char) NumberLong); + _b.appendStr(fieldName); + _b.appendNum(n); + return *this; + } + + /** appends a number. if n < max(int)/2 then uses int, otherwise long long */ + BSONObjBuilder& appendIntOrLL( const StringData& fieldName , long long n ) { + long long x = n; + if ( x < 0 ) + x = x * -1; + if ( x < ( (numeric_limits<int>::max)() / 2 ) ) // extra () to avoid max macro on windows + append( fieldName , (int)n ); + else + append( fieldName , n ); + return *this; + } + + /** + * appendNumber is a series of method for appending the smallest sensible type + * mostly for JS + */ + BSONObjBuilder& appendNumber( const StringData& fieldName , int n ) { + return append( fieldName , n ); + } + + BSONObjBuilder& appendNumber( const StringData& fieldName , double d ) { + return append( fieldName , d ); + } + + BSONObjBuilder& appendNumber( const StringData& fieldName , size_t n ) { + static size_t maxInt = (size_t)pow( 2.0 , 30.0 ); + + if ( n < maxInt ) + append( fieldName , (int)n ); + else + append( fieldName , (long long)n ); + return *this; + } + + BSONObjBuilder& appendNumber( const StringData& fieldName , long long l ) { + static long long maxInt = (int)pow( 2.0 , 30.0 ); + static long long maxDouble = (long long)pow( 2.0 , 40.0 ); + long long x = l >= 0 ? l : -l; + if ( x < maxInt ) + append( fieldName , (int)l ); + else if ( x < maxDouble ) + append( fieldName , (double)l ); + else + append( fieldName , l ); + return *this; + } + + /** Append a double element */ + BSONObjBuilder& append(const StringData& fieldName, double n) { + _b.appendNum((char) NumberDouble); + _b.appendStr(fieldName); + _b.appendNum(n); + return *this; + } + + /** tries to append the data as a number + * @return true if the data was able to be converted to a number + */ + bool appendAsNumber( const StringData& fieldName , const string& data ); + + /** Append a BSON Object ID (OID type). + @deprecated Generally, it is preferred to use the append append(name, oid) + method for this. + */ + BSONObjBuilder& appendOID(const StringData& fieldName, OID *oid = 0 , bool generateIfBlank = false ) { + _b.appendNum((char) jstOID); + _b.appendStr(fieldName); + if ( oid ) + _b.appendBuf( (void *) oid, 12 ); + else { + OID tmp; + if ( generateIfBlank ) + tmp.init(); + else + tmp.clear(); + _b.appendBuf( (void *) &tmp, 12 ); + } + return *this; + } + + /** + Append a BSON Object ID. + @param fieldName Field name, e.g., "_id". + @returns the builder object + */ + BSONObjBuilder& append( const StringData& fieldName, OID oid ) { + _b.appendNum((char) jstOID); + _b.appendStr(fieldName); + _b.appendBuf( (void *) &oid, 12 ); + return *this; + } + + /** + Generate and assign an object id for the _id field. + _id should be the first element in the object for good performance. + */ + BSONObjBuilder& genOID() { + return append("_id", OID::gen()); + } + + /** Append a time_t date. + @param dt a C-style 32 bit date value, that is + the number of seconds since January 1, 1970, 00:00:00 GMT + */ + BSONObjBuilder& appendTimeT(const StringData& fieldName, time_t dt) { + _b.appendNum((char) Date); + _b.appendStr(fieldName); + _b.appendNum(static_cast<unsigned long long>(dt) * 1000); + return *this; + } + /** Append a date. + @param dt a Java-style 64 bit date value, that is + the number of milliseconds since January 1, 1970, 00:00:00 GMT + */ + BSONObjBuilder& appendDate(const StringData& fieldName, Date_t dt) { + /* easy to pass a time_t to this and get a bad result. thus this warning. */ +#if defined(_DEBUG) && defined(MONGO_EXPOSE_MACROS) + if( dt > 0 && dt <= 0xffffffff ) { + static int n; + if( n++ == 0 ) + log() << "DEV WARNING appendDate() called with a tiny (but nonzero) date" << endl; + } +#endif + _b.appendNum((char) Date); + _b.appendStr(fieldName); + _b.appendNum(dt); + return *this; + } + BSONObjBuilder& append(const StringData& fieldName, Date_t dt) { + return appendDate(fieldName, dt); + } + + /** Append a regular expression value + @param regex the regular expression pattern + @param regex options such as "i" or "g" + */ + BSONObjBuilder& appendRegex(const StringData& fieldName, const StringData& regex, const StringData& options = "") { + _b.appendNum((char) RegEx); + _b.appendStr(fieldName); + _b.appendStr(regex); + _b.appendStr(options); + return *this; + } + + BSONObjBuilder& appendCode(const StringData& fieldName, const StringData& code) { + _b.appendNum((char) Code); + _b.appendStr(fieldName); + _b.appendNum((int) code.size()+1); + _b.appendStr(code); + return *this; + } + + /** Append a string element. + @param sz size includes terminating null character */ + BSONObjBuilder& append(const StringData& fieldName, const char *str, int sz) { + _b.appendNum((char) String); + _b.appendStr(fieldName); + _b.appendNum((int)sz); + _b.appendBuf(str, sz); + return *this; + } + /** Append a string element */ + BSONObjBuilder& append(const StringData& fieldName, const char *str) { + return append(fieldName, str, (int) strlen(str)+1); + } + /** Append a string element */ + BSONObjBuilder& append(const StringData& fieldName, const string& str) { + return append(fieldName, str.c_str(), (int) str.size()+1); + } + + BSONObjBuilder& appendSymbol(const StringData& fieldName, const StringData& symbol) { + _b.appendNum((char) Symbol); + _b.appendStr(fieldName); + _b.appendNum((int) symbol.size()+1); + _b.appendStr(symbol); + return *this; + } + + /** Append a Null element to the object */ + BSONObjBuilder& appendNull( const StringData& fieldName ) { + _b.appendNum( (char) jstNULL ); + _b.appendStr( fieldName ); + return *this; + } + + // Append an element that is less than all other keys. + BSONObjBuilder& appendMinKey( const StringData& fieldName ) { + _b.appendNum( (char) MinKey ); + _b.appendStr( fieldName ); + return *this; + } + // Append an element that is greater than all other keys. + BSONObjBuilder& appendMaxKey( const StringData& fieldName ) { + _b.appendNum( (char) MaxKey ); + _b.appendStr( fieldName ); + return *this; + } + + // Append a Timestamp field -- will be updated to next OpTime on db insert. + BSONObjBuilder& appendTimestamp( const StringData& fieldName ) { + _b.appendNum( (char) Timestamp ); + _b.appendStr( fieldName ); + _b.appendNum( (unsigned long long) 0 ); + return *this; + } + + BSONObjBuilder& appendTimestamp( const StringData& fieldName , unsigned long long val ) { + _b.appendNum( (char) Timestamp ); + _b.appendStr( fieldName ); + _b.appendNum( val ); + return *this; + } + + /** + Timestamps are a special BSON datatype that is used internally for replication. + Append a timestamp element to the object being ebuilt. + @param time - in millis (but stored in seconds) + */ + BSONObjBuilder& appendTimestamp( const StringData& fieldName , unsigned long long time , unsigned int inc ); + + /* + Append an element of the deprecated DBRef type. + @deprecated + */ + BSONObjBuilder& appendDBRef( const StringData& fieldName, const StringData& ns, const OID &oid ) { + _b.appendNum( (char) DBRef ); + _b.appendStr( fieldName ); + _b.appendNum( (int) ns.size() + 1 ); + _b.appendStr( ns ); + _b.appendBuf( (void *) &oid, 12 ); + return *this; + } + + /** Append a binary data element + @param fieldName name of the field + @param len length of the binary data in bytes + @param subtype subtype information for the data. @see enum BinDataType in bsontypes.h. + Use BinDataGeneral if you don't care about the type. + @param data the byte array + */ + BSONObjBuilder& appendBinData( const StringData& fieldName, int len, BinDataType type, const void *data ) { + _b.appendNum( (char) BinData ); + _b.appendStr( fieldName ); + _b.appendNum( len ); + _b.appendNum( (char) type ); + _b.appendBuf( data, len ); + return *this; + } + + /** + Subtype 2 is deprecated. + Append a BSON bindata bytearray element. + @param data a byte array + @param len the length of data + */ + BSONObjBuilder& appendBinDataArrayDeprecated( const char * fieldName , const void * data , int len ) { + _b.appendNum( (char) BinData ); + _b.appendStr( fieldName ); + _b.appendNum( len + 4 ); + _b.appendNum( (char)0x2 ); + _b.appendNum( len ); + _b.appendBuf( data, len ); + return *this; + } + + /** Append to the BSON object a field of type CodeWScope. This is a javascript code + fragment accompanied by some scope that goes with it. + */ + BSONObjBuilder& appendCodeWScope( const StringData& fieldName, const StringData& code, const BSONObj &scope ) { + _b.appendNum( (char) CodeWScope ); + _b.appendStr( fieldName ); + _b.appendNum( ( int )( 4 + 4 + code.size() + 1 + scope.objsize() ) ); + _b.appendNum( ( int ) code.size() + 1 ); + _b.appendStr( code ); + _b.appendBuf( ( void * )scope.objdata(), scope.objsize() ); + return *this; + } + + void appendUndefined( const StringData& fieldName ) { + _b.appendNum( (char) Undefined ); + _b.appendStr( fieldName ); + } + + /* helper function -- see Query::where() for primary way to do this. */ + void appendWhere( const StringData& code, const BSONObj &scope ) { + appendCodeWScope( "$where" , code , scope ); + } + + /** + these are the min/max when comparing, not strict min/max elements for a given type + */ + void appendMinForType( const StringData& fieldName , int type ); + void appendMaxForType( const StringData& fieldName , int type ); + + /** Append an array of values. */ + template < class T > + BSONObjBuilder& append( const StringData& fieldName, const vector< T >& vals ); + + template < class T > + BSONObjBuilder& append( const StringData& fieldName, const list< T >& vals ); + + /** Append a set of values. */ + template < class T > + BSONObjBuilder& append( const StringData& fieldName, const set< T >& vals ); + + /** + * destructive + * The returned BSONObj will free the buffer when it is finished. + * @return owned BSONObj + */ + BSONObj obj() { + bool own = owned(); + massert( 10335 , "builder does not own memory", own ); + doneFast(); + BSONObj::Holder* h = (BSONObj::Holder*)_b.buf(); + decouple(); // sets _b.buf() to NULL + return BSONObj(h); + } + + /** Fetch the object we have built. + BSONObjBuilder still frees the object when the builder goes out of + scope -- very important to keep in mind. Use obj() if you + would like the BSONObj to last longer than the builder. + */ + BSONObj done() { + return BSONObj(_done()); + } + + // Like 'done' above, but does not construct a BSONObj to return to the caller. + void doneFast() { + (void)_done(); + } + + /** Peek at what is in the builder, but leave the builder ready for more appends. + The returned object is only valid until the next modification or destruction of the builder. + Intended use case: append a field if not already there. + */ + BSONObj asTempObj() { + BSONObj temp(_done()); + _b.setlen(_b.len()-1); //next append should overwrite the EOO + _doneCalled = false; + return temp; + } + + /* assume ownership of the buffer - you must then free it (with free()) */ + char* decouple(int& l) { + char *x = _done(); + assert( x ); + l = _b.len(); + _b.decouple(); + return x; + } + void decouple() { + _b.decouple(); // post done() call version. be sure jsobj frees... + } + + void appendKeys( const BSONObj& keyPattern , const BSONObj& values ); + + static string numStr( int i ) { + if (i>=0 && i<100 && numStrsReady) + return numStrs[i]; + StringBuilder o; + o << i; + return o.str(); + } + + /** Stream oriented way to add field names and values. */ + BSONObjBuilderValueStream &operator<<(const char * name ) { + _s.endField( name ); + return _s; + } + + /** Stream oriented way to add field names and values. */ + BSONObjBuilder& operator<<( GENOIDLabeler ) { return genOID(); } + + // prevent implicit string conversions which would allow bad things like BSON( BSON( "foo" << 1 ) << 2 ) + struct ForceExplicitString { + ForceExplicitString( const string &str ) : str_( str ) {} + string str_; + }; + + /** Stream oriented way to add field names and values. */ + BSONObjBuilderValueStream &operator<<( const ForceExplicitString& name ) { + return operator<<( name.str_.c_str() ); + } + + Labeler operator<<( const Labeler::Label &l ) { + massert( 10336 , "No subobject started", _s.subobjStarted() ); + return _s << l; + } + + template<typename T> + BSONObjBuilderValueStream& operator<<( const BSONField<T>& f ) { + _s.endField( f.name().c_str() ); + return _s; + } + + template<typename T> + BSONObjBuilder& operator<<( const BSONFieldValue<T>& v ) { + append( v.name().c_str() , v.value() ); + return *this; + } + + BSONObjBuilder& operator<<( const BSONElement& e ){ + append( e ); + return *this; + } + + /** @return true if we are using our own bufbuilder, and not an alternate that was given to us in our constructor */ + bool owned() const { return &_b == &_buf; } + + BSONObjIterator iterator() const ; + + bool hasField( const StringData& name ) const ; + + int len() const { return _b.len(); } + + BufBuilder& bb() { return _b; } + + private: + char* _done() { + if ( _doneCalled ) + return _b.buf() + _offset; + + _doneCalled = true; + _s.endField(); + _b.appendNum((char) EOO); + char *data = _b.buf() + _offset; + int size = _b.len() - _offset; + *((int*)data) = size; + if ( _tracker ) + _tracker->got( size ); + return data; + } + + BufBuilder &_b; + BufBuilder _buf; + int _offset; + BSONObjBuilderValueStream _s; + BSONSizeTracker * _tracker; + bool _doneCalled; + + static const string numStrs[100]; // cache of 0 to 99 inclusive + static bool numStrsReady; // for static init safety. see comments in db/jsobj.cpp + }; + + class BSONArrayBuilder : boost::noncopyable { + public: + BSONArrayBuilder() : _i(0), _b() {} + BSONArrayBuilder( BufBuilder &_b ) : _i(0), _b(_b) {} + BSONArrayBuilder( int initialSize ) : _i(0), _b(initialSize) {} + + template <typename T> + BSONArrayBuilder& append(const T& x) { + _b.append(num(), x); + return *this; + } + + BSONArrayBuilder& append(const BSONElement& e) { + _b.appendAs(e, num()); + return *this; + } + + template <typename T> + BSONArrayBuilder& operator<<(const T& x) { + return append(x); + } + + void appendNull() { + _b.appendNull(num()); + } + + /** + * destructive - ownership moves to returned BSONArray + * @return owned BSONArray + */ + BSONArray arr() { return BSONArray(_b.obj()); } + + BSONObj done() { return _b.done(); } + + void doneFast() { _b.doneFast(); } + + template <typename T> + BSONArrayBuilder& append(const StringData& name, const T& x) { + fill( name ); + append( x ); + return *this; + } + + // These two just use next position + BufBuilder &subobjStart() { return _b.subobjStart( num() ); } + BufBuilder &subarrayStart() { return _b.subarrayStart( num() ); } + + // These fill missing entries up to pos. if pos is < next pos is ignored + BufBuilder &subobjStart(int pos) { + fill(pos); + return _b.subobjStart( num() ); + } + BufBuilder &subarrayStart(int pos) { + fill(pos); + return _b.subarrayStart( num() ); + } + + // These should only be used where you really need interface compatability with BSONObjBuilder + // Currently they are only used by update.cpp and it should probably stay that way + BufBuilder &subobjStart( const StringData& name ) { + fill( name ); + return _b.subobjStart( num() ); + } + + BufBuilder &subarrayStart( const char *name ) { + fill( name ); + return _b.subarrayStart( num() ); + } + + void appendArray( const StringData& name, BSONObj subObj ) { + fill( name ); + _b.appendArray( num(), subObj ); + } + + void appendAs( const BSONElement &e, const char *name) { + fill( name ); + append( e ); + } + + int len() const { return _b.len(); } + int arrSize() const { return _i; } + + private: + // These two are undefined privates to prevent their accidental + // use as we don't support unsigned ints in BSON + BSONObjBuilder& append(const StringData& fieldName, unsigned int val); + BSONObjBuilder& append(const StringData& fieldName, unsigned long long val); + + void fill( const StringData& name ) { + char *r; + long int n = strtol( name.data(), &r, 10 ); + if ( *r ) + uasserted( 13048, (string)"can't append to array using string field name [" + name.data() + "]" ); + fill(n); + } + + void fill (int upTo){ + // if this is changed make sure to update error message and jstests/set7.js + const int maxElems = 1500000; + BOOST_STATIC_ASSERT(maxElems < (BSONObjMaxUserSize/10)); + uassert(15891, "can't backfill array to larger than 1,500,000 elements", upTo <= maxElems); + + while( _i < upTo ) + append( nullElt() ); + } + + static BSONElement nullElt() { + static BSONObj n = nullObj(); + return n.firstElement(); + } + + static BSONObj nullObj() { + BSONObjBuilder _b; + _b.appendNull( "" ); + return _b.obj(); + } + + string num() { return _b.numStr(_i++); } + int _i; + BSONObjBuilder _b; + }; + + template < class T > + inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldName, const vector< T >& vals ) { + BSONObjBuilder arrBuilder; + for ( unsigned int i = 0; i < vals.size(); ++i ) + arrBuilder.append( numStr( i ), vals[ i ] ); + appendArray( fieldName, arrBuilder.done() ); + return *this; + } + + template < class L > + inline BSONObjBuilder& _appendIt( BSONObjBuilder& _this, const StringData& fieldName, const L& vals ) { + BSONObjBuilder arrBuilder; + int n = 0; + for( typename L::const_iterator i = vals.begin(); i != vals.end(); i++ ) + arrBuilder.append( BSONObjBuilder::numStr(n++), *i ); + _this.appendArray( fieldName, arrBuilder.done() ); + return _this; + } + + template < class T > + inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldName, const list< T >& vals ) { + return _appendIt< list< T > >( *this, fieldName, vals ); + } + + template < class T > + inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldName, const set< T >& vals ) { + return _appendIt< set< T > >( *this, fieldName, vals ); + } + + + // $or helper: OR(BSON("x" << GT << 7), BSON("y" << LT 6)); + inline BSONObj OR(const BSONObj& a, const BSONObj& b) + { return BSON( "$or" << BSON_ARRAY(a << b) ); } + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c) + { return BSON( "$or" << BSON_ARRAY(a << b << c) ); } + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d) + { return BSON( "$or" << BSON_ARRAY(a << b << c << d) ); } + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e) + { return BSON( "$or" << BSON_ARRAY(a << b << c << d << e) ); } + inline BSONObj OR(const BSONObj& a, const BSONObj& b, const BSONObj& c, const BSONObj& d, const BSONObj& e, const BSONObj& f) + { return BSON( "$or" << BSON_ARRAY(a << b << c << d << e << f) ); } + +} diff --git a/src/mongo/bson/bsonobjiterator.h b/src/mongo/bson/bsonobjiterator.h new file mode 100644 index 00000000000..39ae24d9b86 --- /dev/null +++ b/src/mongo/bson/bsonobjiterator.h @@ -0,0 +1,161 @@ +// bsonobjiterator.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 <boost/preprocessor/cat.hpp> // like the ## operator but works with __LINE__ + +namespace mongo { + + /** iterator for a BSONObj + + Note each BSONObj ends with an EOO element: so you will get more() on an empty + object, although next().eoo() will be true. + + The BSONObj must stay in scope for the duration of the iterator's execution. + + todo: we may want to make a more stl-like iterator interface for this + with things like begin() and end() + */ + class BSONObjIterator { + public: + /** Create an iterator for a BSON object. + */ + BSONObjIterator(const BSONObj& jso) { + int sz = jso.objsize(); + if ( MONGO_unlikely(sz == 0) ) { + _pos = _theend = 0; + return; + } + _pos = jso.objdata() + 4; + _theend = jso.objdata() + sz - 1; + } + + BSONObjIterator( const char * start , const char * end ) { + _pos = start + 4; + _theend = end - 1; + } + + /** @return true if more elements exist to be enumerated. */ + bool more() { return _pos < _theend; } + + /** @return true if more elements exist to be enumerated INCLUDING the EOO element which is always at the end. */ + bool moreWithEOO() { return _pos <= _theend; } + + /** @return the next element in the object. For the final element, element.eoo() will be true. */ + BSONElement next( bool checkEnd ) { + assert( _pos <= _theend ); + BSONElement e( _pos, checkEnd ? (int)(_theend + 1 - _pos) : -1 ); + _pos += e.size( checkEnd ? (int)(_theend + 1 - _pos) : -1 ); + return e; + } + BSONElement next() { + assert( _pos <= _theend ); + BSONElement e(_pos); + _pos += e.size(); + return e; + } + void operator++() { next(); } + void operator++(int) { next(); } + + BSONElement operator*() { + assert( _pos <= _theend ); + return BSONElement(_pos); + } + + private: + const char* _pos; + const char* _theend; + }; + + class BSONObjIteratorSorted { + public: + BSONObjIteratorSorted( const BSONObj& o ); + + ~BSONObjIteratorSorted() { + assert( _fields ); + delete[] _fields; + _fields = 0; + } + + bool more() { + return _cur < _nfields; + } + + BSONElement next() { + assert( _fields ); + if ( _cur < _nfields ) + return BSONElement( _fields[_cur++] ); + return BSONElement(); + } + + private: + const char ** _fields; + int _nfields; + int _cur; + }; + + /** transform a BSON array into a vector of BSONElements. + we match array # positions with their vector position, and ignore + any fields with non-numeric field names. + */ + inline vector<BSONElement> BSONElement::Array() const { + chk(mongo::Array); + vector<BSONElement> v; + BSONObjIterator i(Obj()); + while( i.more() ) { + BSONElement e = i.next(); + const char *f = e.fieldName(); + try { + unsigned u = stringToNum(f); + assert( u < 1000000 ); + if( u >= v.size() ) + v.resize(u+1); + v[u] = e; + } + catch(unsigned) { } + } + return v; + } + + /** Similar to BOOST_FOREACH + * + * because the iterator is defined outside of the for, you must use {} around + * the surrounding scope. Don't do this: + * + * if (foo) + * BSONForEach(e, obj) + * doSomething(e); + * + * but this is OK: + * + * if (foo) { + * BSONForEach(e, obj) + * doSomething(e); + * } + * + */ + +#define BSONForEach(e, obj) \ + BSONObjIterator BOOST_PP_CAT(it_,__LINE__)(obj); \ + for ( BSONElement e; \ + (BOOST_PP_CAT(it_,__LINE__).more() ? \ + (e = BOOST_PP_CAT(it_,__LINE__).next(), true) : \ + false) ; \ + /*nothing*/ ) + +} diff --git a/src/mongo/bson/bsontypes.h b/src/mongo/bson/bsontypes.h new file mode 100644 index 00000000000..9d95e8e9ad4 --- /dev/null +++ b/src/mongo/bson/bsontypes.h @@ -0,0 +1,107 @@ +// bsontypes.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 "util/misc.h" + +namespace bson { } + +namespace mongo { + + using namespace std; + + class BSONArrayBuilder; + class BSONElement; + class BSONObj; + class BSONObjBuilder; + class BSONObjBuilderValueStream; + class BSONObjIterator; + class Ordering; + class Record; + struct BSONArray; // empty subclass of BSONObj useful for overloading + struct BSONElementCmpWithoutField; + + extern BSONObj maxKey; + extern BSONObj minKey; + + /** + the complete list of valid BSON types + see also bsonspec.org + */ + enum BSONType { + /** smaller than all other types */ + MinKey=-1, + /** end of object */ + EOO=0, + /** double precision floating point value */ + NumberDouble=1, + /** character string, stored in utf8 */ + String=2, + /** an embedded object */ + Object=3, + /** an embedded array */ + Array=4, + /** binary data */ + BinData=5, + /** Undefined type */ + Undefined=6, + /** ObjectId */ + jstOID=7, + /** boolean type */ + Bool=8, + /** date type */ + Date=9, + /** null type */ + jstNULL=10, + /** regular expression, a pattern with options */ + RegEx=11, + /** deprecated / will be redesigned */ + DBRef=12, + /** deprecated / use CodeWScope */ + Code=13, + /** a programming language (e.g., Python) symbol */ + Symbol=14, + /** javascript code that can execute on the database server, with SavedContext */ + CodeWScope=15, + /** 32 bit signed integer */ + NumberInt = 16, + /** Updated to a Date with value next OpTime on insert */ + Timestamp = 17, + /** 64 bit integer */ + NumberLong = 18, + /** max type that is not MaxKey */ + JSTypeMax=18, + /** larger than all other types */ + MaxKey=127 + }; + + /* subtypes of BinData. + bdtCustom and above are ones that the JS compiler understands, but are + opaque to the database. + */ + enum BinDataType { + BinDataGeneral=0, + Function=1, + ByteArrayDeprecated=2, /* use BinGeneral instead */ + bdtUUID = 3, + MD5Type=5, + bdtCustom=128 + }; + +} + diff --git a/src/mongo/bson/inline_decls.h b/src/mongo/bson/inline_decls.h new file mode 100644 index 00000000000..30da9b4560d --- /dev/null +++ b/src/mongo/bson/inline_decls.h @@ -0,0 +1,68 @@ +// inline_decls.h + +/* Copyright 2010 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 + +#if defined(__GNUC__) + +#define NOINLINE_DECL __attribute__((noinline)) + +#elif defined(_MSC_VER) + +#define NOINLINE_DECL __declspec(noinline) + +#else + +#define NOINLINE_DECL + +#endif + +namespace mongo { + +/* Note: do not clutter code with these -- ONLY use in hot spots / significant loops. */ + +#if !defined(__GNUC__) + +// branch prediction. indicate we expect to be true +# define MONGO_likely(x) ((bool)(x)) + +// branch prediction. indicate we expect to be false +# define MONGO_unlikely(x) ((bool)(x)) + +# if defined(_WIN32) + // prefetch data from memory + inline void prefetch(const void *p) { +#if defined(_MM_HINT_T0) + _mm_prefetch((char *) p, _MM_HINT_T0); +#endif + } +#else + inline void prefetch(void *p) { } +#endif + +#else + +# define MONGO_likely(x) ( __builtin_expect((bool)(x), 1) ) +# define MONGO_unlikely(x) ( __builtin_expect((bool)(x), 0) ) + + inline void prefetch(void *p) { + __builtin_prefetch(p); + } + +#endif + +} diff --git a/src/mongo/bson/oid.cpp b/src/mongo/bson/oid.cpp new file mode 100644 index 00000000000..3aee14a3585 --- /dev/null +++ b/src/mongo/bson/oid.cpp @@ -0,0 +1,173 @@ +// @file oid.cpp + +/* 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 "pch.h" +#include "oid.h" +#include "util/atomic_int.h" +#include "../db/nonce.h" +#include "bsonobjbuilder.h" + +BOOST_STATIC_ASSERT( sizeof(mongo::OID) == 12 ); + +namespace mongo { + + // machine # before folding in the process id + OID::MachineAndPid OID::ourMachine; + + unsigned OID::ourPid() { + unsigned pid; +#if defined(_WIN32) + pid = (unsigned short) GetCurrentProcessId(); +#elif defined(__linux__) || defined(__APPLE__) || defined(__sunos__) + pid = (unsigned short) getpid(); +#else + pid = (unsigned short) Security::getNonce(); +#endif + return pid; + } + + void OID::foldInPid(OID::MachineAndPid& x) { + unsigned p = ourPid(); + x._pid ^= (unsigned short) p; + // when the pid is greater than 16 bits, let the high bits modulate the machine id field. + unsigned short& rest = (unsigned short &) x._machineNumber[1]; + rest ^= p >> 16; + } + + OID::MachineAndPid OID::genMachineAndPid() { + BOOST_STATIC_ASSERT( sizeof(mongo::OID::MachineAndPid) == 5 ); + + // this is not called often, so the following is not expensive, and gives us some + // testing that nonce generation is working right and that our OIDs are (perhaps) ok. + { + nonce64 a = Security::getNonceDuringInit(); + nonce64 b = Security::getNonceDuringInit(); + nonce64 c = Security::getNonceDuringInit(); + assert( !(a==b && b==c) ); + } + + unsigned long long n = Security::getNonceDuringInit(); + OID::MachineAndPid x = ourMachine = (OID::MachineAndPid&) n; + foldInPid(x); + return x; + } + + // after folding in the process id + OID::MachineAndPid OID::ourMachineAndPid = OID::genMachineAndPid(); + + void OID::regenMachineId() { + ourMachineAndPid = genMachineAndPid(); + } + + inline bool OID::MachineAndPid::operator!=(const OID::MachineAndPid& rhs) const { + return _pid != rhs._pid || _machineNumber != rhs._machineNumber; + } + + unsigned OID::getMachineId() { + unsigned char x[4]; + x[0] = ourMachineAndPid._machineNumber[0]; + x[1] = ourMachineAndPid._machineNumber[1]; + x[2] = ourMachineAndPid._machineNumber[2]; + x[3] = 0; + return (unsigned&) x[0]; + } + + void OID::justForked() { + MachineAndPid x = ourMachine; + // we let the random # for machine go into all 5 bytes of MachineAndPid, and then + // xor in the pid into _pid. this reduces the probability of collisions. + foldInPid(x); + ourMachineAndPid = genMachineAndPid(); + assert( x != ourMachineAndPid ); + ourMachineAndPid = x; + } + + void OID::init() { + static AtomicUInt inc = (unsigned) Security::getNonce(); + + { + unsigned t = (unsigned) time(0); + unsigned char *T = (unsigned char *) &t; + _time[0] = T[3]; // big endian order because we use memcmp() to compare OID's + _time[1] = T[2]; + _time[2] = T[1]; + _time[3] = T[0]; + } + + _machineAndPid = ourMachineAndPid; + + { + int new_inc = inc++; + unsigned char *T = (unsigned char *) &new_inc; + _inc[0] = T[2]; + _inc[1] = T[1]; + _inc[2] = T[0]; + } + } + + void OID::init( string s ) { + assert( s.size() == 24 ); + const char *p = s.c_str(); + for( int i = 0; i < 12; i++ ) { + data[i] = fromHex(p); + p += 2; + } + } + + void OID::init(Date_t date, bool max) { + int time = (int) (date / 1000); + char* T = (char *) &time; + data[0] = T[3]; + data[1] = T[2]; + data[2] = T[1]; + data[3] = T[0]; + + if (max) + *(long long*)(data + 4) = 0xFFFFFFFFFFFFFFFFll; + else + *(long long*)(data + 4) = 0x0000000000000000ll; + } + + time_t OID::asTimeT() { + int time; + char* T = (char *) &time; + T[0] = data[3]; + T[1] = data[2]; + T[2] = data[1]; + T[3] = data[0]; + return time; + } + + 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", + }; + + // This is to ensure that BSONObjBuilder doesn't try to use numStrs before the strings have been constructed + // I've tested just making numStrs a char[][], but the overhead of constructing the strings each time was too high + // numStrsReady will be 0 until after numStrs is initialized because it is a static variable + bool BSONObjBuilder::numStrsReady = (numStrs[0].size() > 0); + +} diff --git a/src/mongo/bson/oid.h b/src/mongo/bson/oid.h new file mode 100644 index 00000000000..e5963a0e81d --- /dev/null +++ b/src/mongo/bson/oid.h @@ -0,0 +1,138 @@ +// oid.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 <boost/functional/hash.hpp> +#include "../util/hex.h" + +namespace mongo { + +#pragma pack(1) + /** Object ID type. + BSON objects typically have an _id field for the object id. This field should be the first + member of the object when present. class OID is a special type that is a 12 byte id which + is likely to be unique to the system. You may also use other types for _id's. + When _id field is missing from a BSON object, on an insert the database may insert one + automatically in certain circumstances. + + Warning: You must call OID::newState() after a fork(). + + Typical contents of the BSON ObjectID is a 12-byte value consisting of a 4-byte timestamp (seconds since epoch), + a 3-byte machine id, a 2-byte process id, and a 3-byte counter. Note that the timestamp and counter fields must + be stored big endian unlike the rest of BSON. This is because they are compared byte-by-byte and we want to ensure + a mostly increasing order. + */ + class OID { + public: + OID() : a(0), b(0) { } + + /** init from a 24 char hex string */ + explicit OID(const string &s) { init(s); } + + /** initialize to 'null' */ + void clear() { a = 0; b = 0; } + + const unsigned char *getData() const { return data; } + + bool operator==(const OID& r) const { return a==r.a && b==r.b; } + bool operator!=(const OID& r) const { return a!=r.a || b!=r.b; } + int compare( const OID& other ) const { return memcmp( data , other.data , 12 ); } + bool operator<( const OID& other ) const { return compare( other ) < 0; } + bool operator<=( const OID& other ) const { return compare( other ) <= 0; } + + /** @return the object ID output as 24 hex digits */ + string str() const { return toHexLower(data, 12); } + string toString() const { return str(); } + + static OID gen() { OID o; o.init(); return o; } + + /** sets the contents to a new oid / randomized value */ + void init(); + + /** init from a 24 char hex string */ + void init( string s ); + + /** Set to the min/max OID that could be generated at given timestamp. */ + void init( Date_t date, bool max=false ); + + time_t asTimeT(); + Date_t asDateT() { return asTimeT() * (long long)1000; } + + bool isSet() const { return a || b; } + + void hash_combine(size_t &seed) const { + boost::hash_combine(seed, a); + boost::hash_combine(seed, b); + } + + /** call this after a fork to update the process id */ + static void justForked(); + + static unsigned getMachineId(); // features command uses + static void regenMachineId(); // used by unit tests + + private: + struct MachineAndPid { + unsigned char _machineNumber[3]; + unsigned short _pid; + bool operator!=(const OID::MachineAndPid& rhs) const; + }; + static MachineAndPid ourMachine, ourMachineAndPid; + union { + struct { + // 12 bytes total + unsigned char _time[4]; + MachineAndPid _machineAndPid; + unsigned char _inc[3]; + }; + struct { + long long a; + unsigned b; + }; + unsigned char data[12]; + }; + + static unsigned ourPid(); + static void foldInPid(MachineAndPid& x); + static MachineAndPid genMachineAndPid(); + }; +#pragma pack() + + ostream& operator<<( ostream &s, const OID &o ); + inline StringBuilder& operator<< (StringBuilder& s, const OID& o) { return (s << o.str()); } + + /** Formatting mode for generating JSON from BSON. + See <http://mongodb.onconfluence.com/display/DOCS/Mongo+Extended+JSON> + for details. + */ + enum JsonStringFormat { + /** strict RFC format */ + Strict, + /** 10gen format, which is close to JS format. This form is understandable by + javascript running inside the Mongo server via eval() */ + TenGen, + /** Javascript JSON compatible */ + JS + }; + + inline ostream& operator<<( ostream &s, const OID &o ) { + s << o.str(); + return s; + } + +} diff --git a/src/mongo/bson/ordering.h b/src/mongo/bson/ordering.h new file mode 100644 index 00000000000..bca3296f340 --- /dev/null +++ b/src/mongo/bson/ordering.h @@ -0,0 +1,73 @@ +// ordering.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 + +namespace mongo { + + // todo: ideally move to db/ instead of bson/, but elim any dependencies first + + /** A precomputation of a BSON index or sort key pattern. That is something like: + { a : 1, b : -1 } + The constructor is private to make conversion more explicit so we notice where we call make(). + Over time we should push this up higher and higher. + */ + class Ordering { + unsigned bits; + Ordering(unsigned b) : bits(b) { } + public: + Ordering(const Ordering& r) : bits(r.bits) { } + void operator=(const Ordering& r) { + bits = r.bits; + } + + /** so, for key pattern { a : 1, b : -1 } + get(0) == 1 + get(1) == -1 + */ + int get(int i) const { + return ((1 << i) & bits) ? -1 : 1; + } + + // for woCompare... + unsigned descending(unsigned mask) const { return bits & mask; } + + /*operator string() const { + StringBuilder buf(32); + for ( unsigned i=0; i<nkeys; i++) + buf.append( get(i) > 0 ? "+" : "-" ); + return buf.str(); + }*/ + + static Ordering make(const BSONObj& obj) { + unsigned b = 0; + BSONObjIterator k(obj); + unsigned n = 0; + while( 1 ) { + BSONElement e = k.next(); + if( e.eoo() ) + break; + uassert( 13103, "too many compound keys", n <= 31 ); + if( e.number() < 0 ) + b |= (1 << n); + n++; + } + return Ordering(b); + } + }; + +} diff --git a/src/mongo/bson/stringdata.h b/src/mongo/bson/stringdata.h new file mode 100644 index 00000000000..1fb4e7d25d3 --- /dev/null +++ b/src/mongo/bson/stringdata.h @@ -0,0 +1,71 @@ +// stringdata.h + +/* Copyright 2010 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 <string> +#include <cstring> + +namespace mongo { + + using std::string; + + /** A StringData object wraps a 'const string&' or a 'const char*' without + * copying its contents. The most common usage is as a function argument that + * takes any of the two forms of strings above. Fundamentally, this class tries + * go around the fact that string literals in C++ are char[N]'s. + * + * Note that the object StringData wraps around must be alive while the StringData + * is. + */ + class StringData { + public: + /** Construct a StringData, for the case where the length of + * string is not known. 'c' must be a pointer to a null-terminated string. + */ + StringData( const char* c ) + : _data(c), _size((unsigned) strlen(c)) {} + + /** Construct a StringData explicitly, for the case where the length of the string + * is already known. 'c' must be a pointer to a null-terminated string, and strlenOfc + * must be the length that std::strlen(c) would return, a.k.a the index of the + * terminator in c. + */ + StringData( const char* c, unsigned len ) + : _data(c), _size(len) {} + + /** Construct a StringData, for the case of a std::string. */ + StringData( const string& s ) + : _data(s.c_str()), _size((unsigned) s.size()) {} + + // Construct a StringData explicitly, for the case of a literal whose size is + // known at compile time. + struct LiteralTag {}; + template<size_t N> + StringData( const char (&val)[N], LiteralTag ) + : _data(&val[0]), _size(N-1) {} + + // accessors + const char* data() const { return _data; } + unsigned size() const { return _size; } + + private: + const char* const _data; // is always null terminated + const unsigned _size; // 'size' does not include the null terminator + }; + +} // namespace mongo diff --git a/src/mongo/bson/util/atomic_int.h b/src/mongo/bson/util/atomic_int.h new file mode 100644 index 00000000000..e85a023c3bc --- /dev/null +++ b/src/mongo/bson/util/atomic_int.h @@ -0,0 +1,106 @@ +// atomic_int.h +// atomic wrapper for unsigned + +/* 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 + +#if defined(_WIN32) +# include <windows.h> +#endif + +namespace mongo { + + struct AtomicUInt { + AtomicUInt() : x(0) {} + AtomicUInt(unsigned z) : x(z) { } + + operator unsigned() const { return x; } + unsigned get() const { return x; } + + inline AtomicUInt operator++(); // ++prefix + inline AtomicUInt operator++(int);// postfix++ + inline AtomicUInt operator--(); // --prefix + inline AtomicUInt operator--(int); // postfix-- + + inline void zero(); + + volatile unsigned x; + }; + +#if defined(_WIN32) + void AtomicUInt::zero() { + InterlockedExchange((volatile long*)&x, 0); + } + AtomicUInt AtomicUInt::operator++() { + return InterlockedIncrement((volatile long*)&x); + } + AtomicUInt AtomicUInt::operator++(int) { + return InterlockedIncrement((volatile long*)&x)-1; + } + AtomicUInt AtomicUInt::operator--() { + return InterlockedDecrement((volatile long*)&x); + } + AtomicUInt AtomicUInt::operator--(int) { + return InterlockedDecrement((volatile long*)&x)+1; + } +#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) + // this is in GCC >= 4.1 + inline void AtomicUInt::zero() { x = 0; } // TODO: this isn't thread safe - maybe + AtomicUInt AtomicUInt::operator++() { + return __sync_add_and_fetch(&x, 1); + } + AtomicUInt AtomicUInt::operator++(int) { + return __sync_fetch_and_add(&x, 1); + } + AtomicUInt AtomicUInt::operator--() { + return __sync_add_and_fetch(&x, -1); + } + AtomicUInt AtomicUInt::operator--(int) { + return __sync_fetch_and_add(&x, -1); + } +#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + inline void AtomicUInt::zero() { x = 0; } // TODO: this isn't thread safe + // from boost 1.39 interprocess/detail/atomic.hpp + inline unsigned atomic_int_helper(volatile unsigned *x, int val) { + int r; + asm volatile + ( + "lock\n\t" + "xadd %1, %0": + "+m"( *x ), "=r"( r ): // outputs (%0, %1) + "1"( val ): // inputs (%2 == %1) + "memory", "cc" // clobbers + ); + return r; + } + AtomicUInt AtomicUInt::operator++() { + return atomic_int_helper(&x, 1)+1; + } + AtomicUInt AtomicUInt::operator++(int) { + return atomic_int_helper(&x, 1); + } + AtomicUInt AtomicUInt::operator--() { + return atomic_int_helper(&x, -1)-1; + } + AtomicUInt AtomicUInt::operator--(int) { + return atomic_int_helper(&x, -1); + } +#else +# error "unsupported compiler or platform" +#endif + +} // namespace mongo diff --git a/src/mongo/bson/util/builder.h b/src/mongo/bson/util/builder.h new file mode 100644 index 00000000000..f189f58b27e --- /dev/null +++ b/src/mongo/bson/util/builder.h @@ -0,0 +1,322 @@ +/* builder.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 <string> +#include <string.h> +#include <stdio.h> +#include "../inline_decls.h" +#include "../stringdata.h" + +namespace mongo { + + /* Note the limit here is rather arbitrary and is simply a standard. generally the code works + with any object that fits in ram. + + Also note that the server has some basic checks to enforce this limit but those checks are not exhaustive + for example need to check for size too big after + update $push (append) operation + various db.eval() type operations + */ + const int BSONObjMaxUserSize = 16 * 1024 * 1024; + + /* + Sometimeswe we need objects slightly larger - an object in the replication local.oplog + is slightly larger than a user object for example. + */ + const int BSONObjMaxInternalSize = BSONObjMaxUserSize + ( 16 * 1024 ); + + const int BufferMaxSize = 64 * 1024 * 1024; + + class StringBuilder; + + void msgasserted(int msgid, const char *msg); + + class TrivialAllocator { + public: + void* Malloc(size_t sz) { return malloc(sz); } + void* Realloc(void *p, size_t sz) { return realloc(p, sz); } + void Free(void *p) { free(p); } + }; + + class StackAllocator { + public: + enum { SZ = 512 }; + void* Malloc(size_t sz) { + if( sz <= SZ ) return buf; + return malloc(sz); + } + void* Realloc(void *p, size_t sz) { + if( p == buf ) { + if( sz <= SZ ) return buf; + void *d = malloc(sz); + if ( d == 0 ) + msgasserted( 15912 , "out of memory StackAllocator::Realloc" ); + memcpy(d, p, SZ); + return d; + } + return realloc(p, sz); + } + void Free(void *p) { + if( p != buf ) + free(p); + } + private: + char buf[SZ]; + }; + + template< class Allocator > + class _BufBuilder { + // non-copyable, non-assignable + _BufBuilder( const _BufBuilder& ); + _BufBuilder& operator=( const _BufBuilder& ); + Allocator al; + public: + _BufBuilder(int initsize = 512) : size(initsize) { + if ( size > 0 ) { + data = (char *) al.Malloc(size); + if( data == 0 ) + msgasserted(10000, "out of memory BufBuilder"); + } + else { + data = 0; + } + l = 0; + } + ~_BufBuilder() { kill(); } + + void kill() { + if ( data ) { + al.Free(data); + data = 0; + } + } + + void reset() { + l = 0; + } + void reset( int maxSize ) { + l = 0; + if ( maxSize && size > maxSize ) { + al.Free(data); + data = (char*)al.Malloc(maxSize); + if ( data == 0 ) + msgasserted( 15913 , "out of memory BufBuilder::reset" ); + size = maxSize; + } + } + + /** leave room for some stuff later + @return point to region that was skipped. pointer may change later (on realloc), so for immediate use only + */ + char* skip(int n) { return grow(n); } + + /* note this may be deallocated (realloced) if you keep writing. */ + char* buf() { return data; } + const char* buf() const { return data; } + + /* assume ownership of the buffer - you must then free() it */ + void decouple() { data = 0; } + + void appendUChar(unsigned char j) { + *((unsigned char*)grow(sizeof(unsigned char))) = j; + } + void appendChar(char j) { + *((char*)grow(sizeof(char))) = j; + } + void appendNum(char j) { + *((char*)grow(sizeof(char))) = j; + } + void appendNum(short j) { + *((short*)grow(sizeof(short))) = j; + } + void appendNum(int j) { + *((int*)grow(sizeof(int))) = j; + } + void appendNum(unsigned j) { + *((unsigned*)grow(sizeof(unsigned))) = j; + } + void appendNum(bool j) { + *((bool*)grow(sizeof(bool))) = j; + } + void appendNum(double j) { + *((double*)grow(sizeof(double))) = j; + } + void appendNum(long long j) { + *((long long*)grow(sizeof(long long))) = j; + } + void appendNum(unsigned long long j) { + *((unsigned long long*)grow(sizeof(unsigned long long))) = j; + } + + void appendBuf(const void *src, size_t len) { + memcpy(grow((int) len), src, len); + } + + template<class T> + void appendStruct(const T& s) { + appendBuf(&s, sizeof(T)); + } + + void appendStr(const StringData &str , bool includeEndingNull = true ) { + const int len = str.size() + ( includeEndingNull ? 1 : 0 ); + memcpy(grow(len), str.data(), len); + } + + /** @return length of current string */ + int len() const { return l; } + void setlen( int newLen ) { l = newLen; } + /** @return size of the buffer */ + int getSize() const { return size; } + + /* returns the pre-grow write position */ + inline char* grow(int by) { + int oldlen = l; + l += by; + if ( l > size ) { + grow_reallocate(); + } + return data + oldlen; + } + + private: + /* "slow" portion of 'grow()' */ + void NOINLINE_DECL grow_reallocate() { + int a = size * 2; + if ( a == 0 ) + a = 512; + if ( l > a ) + a = l + 16 * 1024; + if ( a > BufferMaxSize ) + msgasserted(13548, "BufBuilder grow() > 64MB"); + data = (char *) al.Realloc(data, a); + size= a; + } + + char *data; + int l; + int size; + + friend class StringBuilder; + }; + + typedef _BufBuilder<TrivialAllocator> BufBuilder; + + /** The StackBufBuilder builds smaller datasets on the stack instead of using malloc. + this can be significantly faster for small bufs. However, you can not decouple() the + buffer with StackBufBuilder. + While designed to be a variable on the stack, if you were to dynamically allocate one, + nothing bad would happen. In fact in some circumstances this might make sense, say, + embedded in some other object. + */ + class StackBufBuilder : public _BufBuilder<StackAllocator> { + public: + StackBufBuilder() : _BufBuilder<StackAllocator>(StackAllocator::SZ) { } + void decouple(); // not allowed. not implemented. + }; + +#if defined(_WIN32) +#pragma warning( push ) +// warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. +#pragma warning( disable : 4996 ) +#endif + + /** stringstream deals with locale so this is a lot faster than std::stringstream for UTF8 */ + class StringBuilder { + public: + StringBuilder( int initsize=256 ) + : _buf( initsize ) { + } + + StringBuilder& operator<<( double x ) { + return SBNUM( x , 25 , "%g" ); + } + StringBuilder& operator<<( int x ) { + return SBNUM( x , 11 , "%d" ); + } + StringBuilder& operator<<( unsigned x ) { + return SBNUM( x , 11 , "%u" ); + } + StringBuilder& operator<<( long x ) { + return SBNUM( x , 22 , "%ld" ); + } + StringBuilder& operator<<( unsigned long x ) { + return SBNUM( x , 22 , "%lu" ); + } + StringBuilder& operator<<( long long x ) { + return SBNUM( x , 22 , "%lld" ); + } + StringBuilder& operator<<( unsigned long long x ) { + return SBNUM( x , 22 , "%llu" ); + } + StringBuilder& operator<<( short x ) { + return SBNUM( x , 8 , "%hd" ); + } + StringBuilder& operator<<( char c ) { + _buf.grow( 1 )[0] = c; + return *this; + } + + void appendDoubleNice( double x ) { + int prev = _buf.l; + char * start = _buf.grow( 32 ); + int z = sprintf( start , "%.16g" , x ); + assert( z >= 0 ); + _buf.l = prev + z; + if( strchr(start, '.') == 0 && strchr(start, 'E') == 0 && strchr(start, 'N') == 0 ) { + write( ".0" , 2 ); + } + } + + void write( const char* buf, int len) { memcpy( _buf.grow( len ) , buf , len ); } + + void append( const StringData& str ) { memcpy( _buf.grow( str.size() ) , str.data() , str.size() ); } + + StringBuilder& operator<<( const StringData& str ) { + append( str ); + return *this; + } + + void reset( int maxSize = 0 ) { _buf.reset( maxSize ); } + + std::string str() const { return std::string(_buf.data, _buf.l); } + + int len() const { return _buf.l; } + + private: + BufBuilder _buf; + + // non-copyable, non-assignable + StringBuilder( const StringBuilder& ); + StringBuilder& operator=( const StringBuilder& ); + + template <typename T> + StringBuilder& SBNUM(T val,int maxSize,const char *macro) { + int prev = _buf.l; + int z = sprintf( _buf.grow(maxSize) , macro , (val) ); + assert( z >= 0 ); + _buf.l = prev + z; + return *this; + } + }; + +#if defined(_WIN32) +#pragma warning( pop ) +#endif + +} // namespace mongo diff --git a/src/mongo/bson/util/misc.h b/src/mongo/bson/util/misc.h new file mode 100644 index 00000000000..b547c981bdf --- /dev/null +++ b/src/mongo/bson/util/misc.h @@ -0,0 +1,121 @@ +/* @file misc.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 <ctime> + +namespace mongo { + + using namespace std; + + inline void time_t_to_String(time_t t, char *buf) { +#if defined(_WIN32) + ctime_s(buf, 32, &t); +#else + ctime_r(&t, buf); +#endif + buf[24] = 0; // don't want the \n + } + + inline string time_t_to_String(time_t t = time(0) ) { + char buf[64]; +#if defined(_WIN32) + ctime_s(buf, sizeof(buf), &t); +#else + ctime_r(&t, buf); +#endif + buf[24] = 0; // don't want the \n + return buf; + } + + inline string time_t_to_String_no_year(time_t t) { + char buf[64]; +#if defined(_WIN32) + ctime_s(buf, sizeof(buf), &t); +#else + ctime_r(&t, buf); +#endif + buf[19] = 0; + return buf; + } + + inline string time_t_to_String_short(time_t t) { + char buf[64]; +#if defined(_WIN32) + ctime_s(buf, sizeof(buf), &t); +#else + ctime_r(&t, buf); +#endif + buf[19] = 0; + if( buf[0] && buf[1] && buf[2] && buf[3] ) + return buf + 4; // skip day of week + return buf; + } + + struct Date_t { + // TODO: make signed (and look for related TODO's) + unsigned long long millis; + Date_t(): millis(0) {} + Date_t(unsigned long long m): millis(m) {} + operator unsigned long long&() { return millis; } + operator const unsigned long long&() const { return millis; } + void toTm (tm *buf) { + time_t dtime = (time_t) millis/1000; +#if defined(_WIN32) + gmtime_s(buf, &dtime); +#else + gmtime_r(&dtime, buf); +#endif + } + string toString() const { + char buf[64]; + time_t_to_String(millis/1000, buf); + return buf; + } + }; + + // Like strlen, but only scans up to n bytes. + // Returns -1 if no '0' found. + inline int strnlen( const char *s, int n ) { + for( int i = 0; i < n; ++i ) + if ( !s[ i ] ) + return i; + return -1; + } + + inline bool isNumber( char c ) { + return c >= '0' && c <= '9'; + } + + inline unsigned stringToNum(const char *str) { + unsigned x = 0; + const char *p = str; + while( 1 ) { + if( !isNumber(*p) ) { + if( *p == 0 && p != str ) + break; + throw 0; + } + x = x * 10 + *p++ - '0'; + } + return x; + } + +} |