From 7645618fd3914cb8a20561625913c20d49504a49 Mon Sep 17 00:00:00 2001 From: Antonin Kral Date: Wed, 11 Aug 2010 12:38:57 +0200 Subject: Imported Upstream version 1.6.0 --- bson/README | 7 + bson/bson.h | 123 ++++++ bson/bson_db.h | 70 +++ bson/bsondemo/bsondemo.cpp | 107 +++++ bson/bsondemo/bsondemo.vcproj | 243 +++++++++++ bson/bsondemo/bsondemo.vcxproj | 193 +++++++++ bson/bsondemo/bsondemo.vcxproj.filters | 52 +++ bson/bsonelement.h | 549 ++++++++++++++++++++++++ bson/bsoninlines.h | 597 ++++++++++++++++++++++++++ bson/bsonmisc.h | 195 +++++++++ bson/bsonobj.h | 394 +++++++++++++++++ bson/bsonobjbuilder.h | 749 +++++++++++++++++++++++++++++++++ bson/bsonobjiterator.h | 131 ++++++ bson/bsontypes.h | 107 +++++ bson/inline_decls.h | 33 ++ bson/oid.h | 113 +++++ bson/ordering.h | 66 +++ bson/stringdata.h | 64 +++ bson/util/atomic_int.h | 99 +++++ bson/util/builder.h | 226 ++++++++++ bson/util/misc.h | 94 +++++ 21 files changed, 4212 insertions(+) create mode 100644 bson/README create mode 100644 bson/bson.h create mode 100644 bson/bson_db.h create mode 100644 bson/bsondemo/bsondemo.cpp create mode 100644 bson/bsondemo/bsondemo.vcproj create mode 100644 bson/bsondemo/bsondemo.vcxproj create mode 100644 bson/bsondemo/bsondemo.vcxproj.filters create mode 100644 bson/bsonelement.h create mode 100644 bson/bsoninlines.h create mode 100644 bson/bsonmisc.h create mode 100644 bson/bsonobj.h create mode 100644 bson/bsonobjbuilder.h create mode 100644 bson/bsonobjiterator.h create mode 100644 bson/bsontypes.h create mode 100644 bson/inline_decls.h create mode 100644 bson/oid.h create mode 100644 bson/ordering.h create mode 100644 bson/stringdata.h create mode 100644 bson/util/atomic_int.h create mode 100644 bson/util/builder.h create mode 100644 bson/util/misc.h (limited to 'bson') diff --git a/bson/README b/bson/README new file mode 100644 index 0000000..01ed654 --- /dev/null +++ b/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/bson/bson.h b/bson/bson.h new file mode 100644 index 0000000..3d92831 --- /dev/null +++ b/bson/bson.h @@ -0,0 +1,123 @@ +/* NOTE: Standalone bson header for when not using MongoDB. + See also: bsondemo. + + MongoDB includes ../db/jsobj.h instead. This file, however, pulls in much less code / dependencies. +*/ + +/** @file bson.h + BSON classes +*/ + +/* + * 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. + */ + +/** + BSONObj and its helpers + + "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 +#include +#include +#include "util/builder.h" + +namespace bson { + + using std::string; + using std::stringstream; + + class assertion : public std::exception { + public: + assertion( unsigned u , const string& s ) + : id( u ) , msg( s ){ + mongo::StringBuilder 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 "../bson/bsontypes.h" +#include "../bson/oid.h" +#include "../bson/bsonelement.h" +#include "../bson/bsonobj.h" +#include "../bson/bsonmisc.h" +#include "../bson/bsonobjbuilder.h" +#include "../bson/bsonobjiterator.h" +#include "../bson/bsoninlines.h" + +namespace mongo { + + inline unsigned getRandomNumber() { +#if defined(_WIN32) + return rand(); +#else + return random(); +#endif + } + +} diff --git a/bson/bson_db.h b/bson/bson_db.h new file mode 100644 index 0000000..18cd59f --- /dev/null +++ b/bson/bson_db.h @@ -0,0 +1,70 @@ +/** @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" + +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; + } + + +} diff --git a/bson/bsondemo/bsondemo.cpp b/bson/bsondemo/bsondemo.cpp new file mode 100644 index 0000000..b0da1b8 --- /dev/null +++ b/bson/bsondemo/bsondemo.cpp @@ -0,0 +1,107 @@ +/** @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). +*/ + +/* + * 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 +#include + +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 v; + y.elems(v); + cout << v[0] << endl; + + /* into an array */ + list 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 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 strs; + x.vals(strs); + cout << strs.size() << " strings, first one: " << strs[0] << endl; + + iter(y); + return 0; +} diff --git a/bson/bsondemo/bsondemo.vcproj b/bson/bsondemo/bsondemo.vcproj new file mode 100644 index 0000000..8432ceb --- /dev/null +++ b/bson/bsondemo/bsondemo.vcproj @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bson/bsondemo/bsondemo.vcxproj b/bson/bsondemo/bsondemo.vcxproj new file mode 100644 index 0000000..bb82a50 --- /dev/null +++ b/bson/bsondemo/bsondemo.vcxproj @@ -0,0 +1,193 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {C9DB5EB7-81AA-4185-BAA1-DA035654402F} + bsondemo + Win32Proj + + + + Application + Unicode + true + + + Application + Unicode + true + + + Application + Unicode + + + Application + Unicode + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + true + true + $(SolutionDir)$(Configuration)\ + $(SolutionDir)$(Configuration)\ + $(Configuration)\ + $(Configuration)\ + false + false + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + + + + Disabled + c:\boost;\boost + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + No + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + EditAndContinue + true + + + true + Console + MachineX86 + + + + + Disabled + c:\boost;\boost + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + Level3 + ProgramDatabase + No + true + + + true + Console + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + c:\boost;\boost + No + true + + + true + Console + true + true + MachineX86 + + + + + MaxSpeed + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level3 + ProgramDatabase + c:\boost;\boost + No + true + + + true + Console + true + true + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bson/bsondemo/bsondemo.vcxproj.filters b/bson/bsondemo/bsondemo.vcxproj.filters new file mode 100644 index 0000000..35f14d5 --- /dev/null +++ b/bson/bsondemo/bsondemo.vcxproj.filters @@ -0,0 +1,52 @@ + + + + + + + + bson + + + bson + + + bson + + + bson + + + bson + + + bson + + + bson + + + bson + + + bson + + + bson + + + bson + + + bson + + + bson + + + + + {ea599740-3c6f-40dd-a121-e825d82ae4aa} + + + diff --git a/bson/bsonelement.h b/bson/bsonelement.h new file mode 100644 index 0000000..2bbc640 --- /dev/null +++ b/bson/bsonelement.h @@ -0,0 +1,549 @@ +// 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 +#include +#include "util/builder.h" + +namespace bson { + typedef mongo::BSONElement be; + typedef mongo::BSONObj bo; + typedef mongo::BSONObjBuilder bob; +} + +namespace mongo { + + class OpTime; + class BSONElement; + + /* 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: + + -------- 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(); } + BSONObj Obj() const; + vector Array() const; // see implementation for detailed comments + mongo::OID OID() const { return chk(jstOID).__oid(); } + void Null() const { chk(isNull()); } + void OK() const { chk(ok()); } + + /** 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 = -1 ) 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; + } + + /** Retrieve a java style date value from the element. + Ensure element is of type Date before calling. + */ + 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., treat zero and null 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 double value for this field. MUST be NumberInt type. */ + int _numberInt() const {return *reinterpret_cast< const int* >( value() ); } + /** Return double 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. */ + 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: + 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: + if (binDataType() != ByteArrayDeprecated){ + return binData(len); + } else { + // Skip extra size + len = valuestrsize() - 4; + return value() + 5 + 4; + } + } + + BinDataType binDataType() const { + // BinData: + 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; + } + + /** Well ordered comparison. + @return <0: l0: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 ); + } + + 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; + } + + // If maxLen is specified, don't scan more than maxLen bytes. + explicit BSONElement(const char *d, int maxLen = -1) : data(d) { + fieldNameSize_ = -1; + if ( eoo() ) + fieldNameSize_ = 0; + else { + if ( maxLen != -1 ) { + int size = (int) strnlen( fieldName(), maxLen - 1 ); + massert( 10333 , "Invalid field name", size != -1 ); + fieldNameSize_ = size + 1; + } + } + 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; + ss << "wrong type for BSONElement (" << 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; + } + + /** 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. */ + 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/bson/bsoninlines.h b/bson/bsoninlines.h new file mode 100644 index 0000000..f4140a3 --- /dev/null +++ b/bson/bsoninlines.h @@ -0,0 +1,597 @@ +// bsoninlines.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 +#include "util/atomic_int.h" +#include "util/misc.h" +#include "../util/hex.h" + +namespace mongo { + + inline BSONObjIterator BSONObj::begin() { + return BSONObjIterator(*this); + } + + inline BSONObj BSONElement::embeddedObjectUserCheck() const { + uassert( 10065 , "invalid parameter: expected an object", isABSONObj() ); + return BSONObj(value()); + } + + 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 ); + } + + inline BSONObj BSONObj::copy() const { + char *p = (char*) malloc(objsize()); + memcpy(p, objdata(), objsize()); + return BSONObj(p, true); + } + + // 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 bool BSONObj::hasElement(const char *name) const { + if ( !isEmpty() ) { + BSONObjIterator it(*this); + while ( it.moreWithEOO() ) { + BSONElement e = it.next(); + if ( strcmp(name, e.fieldName()) == 0 ) + return true; + } + } + return false; + } + + 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(); + } + + /* 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; + } + + inline bool BSONObj::isValid(){ + int x = objsize(); + return x > 0 && x <= 1024 * 1024 * 8; + } + + 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 + 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 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 ); + } + + /* WARNING: nested/dotted conversions are not 100% reversible + * nested2dotted(dotted2nested({a.b: {c:1}})) -> {a.b.c: 1} + * also, dotted2nested ignores order + */ + + typedef map 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 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 ? " ]" : " }" ); + } + + extern unsigned getRandomNumber(); + + inline void BSONElement::validate() const { + const BSONType t = type(); + + switch( t ) { + case DBRef: + case Code: + case Symbol: + case mongo::String: { + int x = valuestrsize(); + if ( x > 0 && valuestr()[x-1] == 0 ) + return; + StringBuilder buf; + buf << "Invalid dbref/code/string/symbol size: " << x << " 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 ) : 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 = ( maxLen == -1 ) ? strlen( p ) : mongo::strnlen( p, remain - len1 - 1 ); + //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( 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(" << date() << ')'; + break; + case RegEx: + { + s << "/" << regex() << '/'; + const char *p = regexFlags(); + if ( p ) s << p; + } + break; + case NumberDouble: + { + stringstream tmp; + tmp.precision( 16 ); + tmp << number(); + string n = tmp.str(); + s << n; + // indicate this is a double: + if( strchr(n.c_str(), '.') == 0 && strchr(n.c_str(), 'E') == 0 && strchr(n.c_str(), 'N') == 0 ) + s << ".0"; + } + break; + case NumberLong: + s << _numberLong(); + break; + case NumberInt: + s << _numberInt(); + break; + case 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() > 80 ) { + s.write(valuestr(), 70); + 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 */ + static char p[] = { 5, 0, 0, 0, 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 &v) const { + BSONObjIterator i(*this); + while( i.more() ) + v.push_back(i.next()); + } + + inline void BSONObj::elems(list &v) const { + BSONObjIterator i(*this); + while( i.more() ) + v.push_back(i.next()); + } + + template + void BSONObj::Vals(vector& v) const { + BSONObjIterator i(*this); + while( i.more() ) { + T t; + i.next().Val(t); + v.push_back(t); + } + } + template + void BSONObj::Vals(list& v) const { + BSONObjIterator i(*this); + while( i.more() ) { + T t; + i.next().Val(t); + v.push_back(t); + } + } + + template + void BSONObj::vals(vector& v) const { + BSONObjIterator i(*this); + while( i.more() ) { + try { + T t; + i.next().Val(t); + v.push_back(t); + } catch(...) { } + } + } + template + void BSONObj::vals(list& 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 void BSONElement::Val(BSONObj& v) const { v = Obj(); } + + template + inline BSONFieldValue BSONField::query( const char * q , const T& t ) const { + BSONObjBuilder b; + b.append( q , t ); + return BSONFieldValue( _name , b.obj() ); + } +} diff --git a/bson/bsonmisc.h b/bson/bsonmisc.h new file mode 100644 index 0000000..40ec6d3 --- /dev/null +++ b/bson/bsonmisc.h @@ -0,0 +1,195 @@ +// @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; + } + private: + BSONObj order; + }; + + class BSONObjCmpDefaultOrder : public BSONObjCmp { + public: + BSONObjCmpDefaultOrder() : BSONObjCmp( BSONObj() ) {} + }; + + typedef set< BSONObj, BSONObjCmpDefaultOrder > BSONObjSetDefaultOrder; + + 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 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 + 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 + BSONObjBuilder& operator<<( T value ); + + BSONObjBuilder& operator<<(DateNowLabeler& 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 ) + _pos = 0; + } + + /** + * right now choosing largest size + */ + int getSize() const { + int x = 16; // sane min + for ( int i=0; i x ) + x = _sizes[i]; + } + return x; + } + + private: + enum { SIZE = 10 }; + int _pos; + int _sizes[SIZE]; + }; + +} diff --git a/bson/bsonobj.h b/bson/bsonobj.h new file mode 100644 index 0000000..0e99f28 --- /dev/null +++ b/bson/bsonobj.h @@ -0,0 +1,394 @@ +// @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 +#include +#include +#include "util/builder.h" +#include "stringdata.h" + +namespace mongo { + + typedef set< BSONElement, BSONElementCmpWithoutField > BSONElementSet; + + /** + 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 + {}* EOO + + totalSize includes itself. + + Data: + Bool: + EOO: nothing follows + Undefined: nothing follows + OID: an OID object + NumberDouble: + NumberInt: + String: + Date: <8bytes> + Regex: + Object: a nested object, leading with its entire size, which terminates with EOO. + Array: same as object + DBRef: + DBRef: a database reference: basically a collection name plus an Object ID + BinData: + 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: + \endcode + */ + class BSONObj { + public: + /** Construct a BSONObj from data in the proper format. + @param ifree true if the BSONObj should free() the msgdata when + it destructs. + */ + explicit BSONObj(const char *msgdata, bool ifree = false) { + init(msgdata, ifree); + } + BSONObj(const Record *r); + /** Construct an empty BSONObj -- that is, {}. */ + BSONObj(); + // defensive + ~BSONObj() { _objdata = 0; } + + void appendSelfToBufBuilder(BufBuilder& b) const { + assert( objsize() ); + b.appendBuf(reinterpret_cast( objdata() ), objsize()); + } + + /** 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& fields); /* returns n added */ + + /** 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& fields) const; + + /** return has eoo() true if no match + supports "." notation to reach into embedded objects + */ + BSONElement getFieldDotted(const char *name) const; + /** return has eoo() true if no match + supports "." notation to reach into embedded objects + */ + BSONElement getFieldDotted(const string& name) const { + return getFieldDotted( name.c_str() ); + } + + /** Like getFieldDotted(), but expands multikey arrays and returns all matching objects + */ + void getFieldsDotted(const StringData& name, BSONElementSet &ret ) 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 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 "" 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 */ + 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; + + /** @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(objdata())); + } + + /** performs a cursory check on the object's size only. */ + bool isValid(); + + /** @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 l0 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 l0 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; + + /** This is "shallow equality" -- ints and doubles won't match. for a + deep equality test use woCompare (which is slower). + */ + bool woEqual(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); + } + + /** @return true if field exists in the object */ + bool hasElement(const char *name) const; + + /** 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; + + /** makes a copy of the object. */ + BSONObj copy() const; + + /* make sure the data buffer is under the control of this BSONObj and not a remote buffer */ + BSONObj getOwned() const{ + if ( !isOwned() ) + return copy(); + return *this; + } + bool isOwned() const { return _holder.get() != 0; } + + /** @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 woCompare( other ) == 0; + } + + 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 &) const; + /** add all elements of the object to the specified list */ + void elems(list &) const; + + /** add all values of the object to the specified vector. If type mismatches, exception. */ + template + void Vals(vector &) const; + /** add all values of the object to the specified list. If type mismatches, exception. */ + template + void Vals(list &) const; + + /** add all values of the object to the specified vector. If type mismatches, skip. */ + template + void vals(vector &) const; + /** add all values of the object to the specified list. If type mismatches, skip. */ + template + void vals(list &) const; + + friend class BSONObjIterator; + typedef BSONObjIterator iterator; + BSONObjIterator begin(); + +private: + class Holder { + public: + Holder( const char *objdata ) : + _objdata( objdata ) { + } + ~Holder() { + free((void *)_objdata); + _objdata = 0; + } + private: + const char *_objdata; + }; + const char *_objdata; + boost::shared_ptr< Holder > _holder; + void init(const char *data, bool ifree) { + if ( ifree ) + _holder.reset( new Holder( data ) ); + _objdata = data; + if ( ! isValid() ){ + StringBuilder ss; + int os = objsize(); + ss << "Invalid BSONObj spec size: " << os << " (" << toHex( &os, 4 ) << ")"; + try { + BSONElement e = firstElement(); + ss << " first element:" << e.toString() << " "; + } + catch ( ... ){} + string s = ss.str(); + massert( 10334 , s , 0 ); + } + } + }; + ostream& operator<<( ostream &s, const BSONObj &o ); + ostream& operator<<( ostream &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/bson/bsonobjbuilder.h b/bson/bsonobjbuilder.h new file mode 100644 index 0000000..fdfe4de --- /dev/null +++ b/bson/bsonobjbuilder.h @@ -0,0 +1,749 @@ +/* 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 +#include +using namespace std; + +namespace mongo { + +#if defined(_WIN32) +// warning: 'this' : used in base member initializer list +#pragma warning( disable : 4355 ) +#endif + + template + 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 + 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 make( const T& t ) const { + return BSONFieldValue( _name , t ); + } + + BSONFieldValue gt( const T& t ) const { return query( "$gt" , t ); } + BSONFieldValue lt( const T& t ) const { return query( "$lt" , t ); } + + BSONFieldValue query( const char * q , const T& t ) const; + + BSONFieldValue operator()( const T& t ) const { + return BSONFieldValue( _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), _offset( 0 ), _s( this ) , _tracker(0) , _doneCalled(false) { + _b.skip(4); /*leave room for size field*/ + } + + /** @param baseBuilder construct a BSONObjBuilder using an existing BufBuilder */ + 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() ), _offset(0), _s( this ) , _tracker( (BSONSizeTracker*)(&tracker) ) , _doneCalled(false) { + _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); + + /** 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 */ + 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::max() / 2 ) ) + 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 , long long l ){ + static long long maxInt = (int)pow( 2.0 , 30.0 ); + static long long maxDouble = (long long)pow( 2.0 , 40.0 ); + + if ( l < maxInt ) + append( fieldName , (int)l ); + else if ( l < 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(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 char *regex, const char *options = "") { + _b.appendNum((char) RegEx); + _b.appendStr(fieldName); + _b.appendStr(regex); + _b.appendStr(options); + return *this; + } + /** Append a regular expression value + @param regex the regular expression pattern + @param regex options such as "i" or "g" + */ + BSONObjBuilder& appendRegex(const StringData& fieldName, string regex, string options = "") { + return appendRegex(fieldName, regex.c_str(), options.c_str()); + } + BSONObjBuilder& appendCode(const StringData& fieldName, const char *code) { + _b.appendNum((char) Code); + _b.appendStr(fieldName); + _b.appendNum((int) strlen(code)+1); + _b.appendStr(code); + return *this; + } + /** Append a string element. len DOES include terminating nul */ + BSONObjBuilder& append(const StringData& fieldName, const char *str, int len) { + _b.appendNum((char) String); + _b.appendStr(fieldName); + _b.appendNum((int)len); + _b.appendBuf(str, len); + 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, string str) { + return append(fieldName, str.c_str(), (int) str.size()+1); + } + BSONObjBuilder& appendSymbol(const StringData& fieldName, const char *symbol) { + _b.appendNum((char) Symbol); + _b.appendStr(fieldName); + _b.appendNum((int) strlen(symbol)+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 char *ns, const OID &oid ) { + _b.appendNum( (char) DBRef ); + _b.appendStr( fieldName ); + _b.appendNum( (int) strlen( ns ) + 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 char *data ) { + _b.appendNum( (char) BinData ); + _b.appendStr( fieldName ); + _b.appendNum( len ); + _b.appendNum( (char) type ); + _b.appendBuf( (void *) data, len ); + return *this; + } + BSONObjBuilder& appendBinData( const StringData& fieldName, int len, BinDataType type, const unsigned char *data ) { + return appendBinData(fieldName, len, type, (const char *) data); + } + + /** + 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 char * data , int len ){ + _b.appendNum( (char) BinData ); + _b.appendStr( fieldName ); + _b.appendNum( len + 4 ); + _b.appendNum( (char)0x2 ); + _b.appendNum( len ); + _b.appendBuf( (void *) 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 char *code, const BSONObj &scope ) { + _b.appendNum( (char) CodeWScope ); + _b.appendStr( fieldName ); + _b.appendNum( ( int )( 4 + 4 + strlen( code ) + 1 + scope.objsize() ) ); + _b.appendNum( ( int ) strlen( code ) + 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 char *code, const BSONObj &scope ){ + appendCodeWScope( "$where" , code , scope ); + } + void appendWhere( const string &code, const BSONObj &scope ){ + appendWhere( code.c_str(), 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 ); + + /** The returned BSONObj will free the buffer when it is finished. */ + BSONObj obj() { + bool own = owned(); + massert( 10335 , "builder does not own memory", own ); + int l; + return BSONObj(decouple(l), true); + } + + /** 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) + 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 + BSONObjBuilderValueStream& operator<<( const BSONField& f ) { + _s.endField( f.name().c_str() ); + return _s; + } + + template + BSONObjBuilder& operator<<( const BSONFieldValue& v ) { + append( v.name().c_str() , v.value() ); + 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 ; + + 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 + }; + + class BSONArrayBuilder : boost::noncopyable { + public: + BSONArrayBuilder() : _i(0), _b() {} + BSONArrayBuilder( BufBuilder &_b ) : _i(0), _b(_b) {} + + template + BSONArrayBuilder& append(const T& x){ + _b.append(num().c_str(), x); + return *this; + } + + BSONArrayBuilder& append(const BSONElement& e){ + _b.appendAs(e, num()); + return *this; + } + + template + BSONArrayBuilder& operator<<(const T& x){ + return append(x); + } + + void appendNull() { + _b.appendNull(num().c_str()); + } + + BSONArray arr(){ return BSONArray(_b.obj()); } + + BSONObj done() { return _b.done(); } + + void doneFast() { _b.doneFast(); } + + template + BSONArrayBuilder& append(const StringData& name, const T& x){ + fill( name ); + append( x ); + return *this; + } + + BufBuilder &subobjStart( const char *name = "0" ) { + fill( name ); + return _b.subobjStart( num().c_str() ); + } + + BufBuilder &subarrayStart( const char *name ) { + fill( name ); + return _b.subarrayStart( num().c_str() ); + } + + void appendArray( const StringData& name, BSONObj subObj ) { + fill( name ); + _b.appendArray( num().c_str(), subObj ); + } + + void appendAs( const BSONElement &e, const char *name ) { + fill( name ); + append( e ); + } + + private: + void fill( const StringData& name ) { + char *r; + int n = strtol( name.data(), &r, 10 ); + if ( *r ) + uasserted( 13048, (string)"can't append to array using string field name [" + name.data() + "]" ); + while( _i < n ) + 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 T > + inline BSONObjBuilder& BSONObjBuilder::append( const StringData& fieldName, const list< T >& vals ) { + BSONObjBuilder arrBuilder; + int n = 0; + for( typename list< T >::const_iterator i = vals.begin(); i != vals.end(); i++ ) + arrBuilder.append( numStr(n++), *i ); + appendArray( fieldName, arrBuilder.done() ); + return *this; + } + + // $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/bson/bsonobjiterator.h b/bson/bsonobjiterator.h new file mode 100644 index 0000000..c8224d2 --- /dev/null +++ b/bson/bsonobjiterator.h @@ -0,0 +1,131 @@ +// 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 // 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. + + 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 ( sz == 0 ) { + _pos = _theend = 0; + return; + } + _pos = jso.objdata() + 4; + _theend = jso.objdata() + sz; + } + + BSONObjIterator( const char * start , const char * end ){ + _pos = start + 4; + _theend = end; + } + + /** @return true if more elements exist to be enumerated. */ + bool moreWithEOO() { + return _pos < _theend; + } + bool more(){ + return _pos < _theend && _pos[0]; + } + /** @return the next element in the object. For the final element, element.eoo() will be true. */ + BSONElement next( bool checkEnd = false ) { + assert( _pos < _theend ); + BSONElement e( _pos, checkEnd ? (int)(_theend - _pos) : -1 ); + _pos += e.size( checkEnd ? (int)(_theend - _pos) : -1 ); + return e; + } + + void operator++() { next(); } + void operator++(int) { next(); } + + BSONElement operator*() { + assert( _pos < _theend ); + return BSONElement(_pos, -1); + } + + 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; + }; + +/** 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/bson/bsontypes.h b/bson/bsontypes.h new file mode 100644 index 0000000..27f2aaf --- /dev/null +++ b/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/bson/inline_decls.h b/bson/inline_decls.h new file mode 100644 index 0000000..aab9810 --- /dev/null +++ b/bson/inline_decls.h @@ -0,0 +1,33 @@ +// inline.h + +/** +* Copyright (C) 2010 10gen Inc. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License, version 3, +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +#pragma once + +#if defined(__GNUC__) + +#define NOINLINE_DECL __attribute__((noinline)) + +#elif defined(_MSC_VER) + +#define NOINLINE_DECL __declspec(noinline) + +#else + +#define NOINLINE_DECL + +#endif diff --git a/bson/oid.h b/bson/oid.h new file mode 100644 index 0000000..c1bf34d --- /dev/null +++ b/bson/oid.h @@ -0,0 +1,113 @@ +// 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 "../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(). + */ + class OID { + union { + struct{ + long long a; + unsigned b; + }; + unsigned char data[12]; + }; + static unsigned _machine; + public: + /** call this after a fork */ + static void newState(); + + /** initialize to 'null' */ + void clear() { a = 0; b = 0; } + + const unsigned char *getData() const { return data; } + + bool operator==(const OID& r) { + return a==r.a&&b==r.b; + } + bool operator!=(const OID& r) { + return a!=r.a||b!=r.b; + } + + /** 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; } + + static unsigned staticMachine(){ return _machine; } + /** + sets the contents to a new oid / randomized value + */ + void init(); + + /** Set to the hex string value specified. */ + 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; } + + int compare( const OID& other ) const { return memcmp( data , other.data , 12 ); } + + bool operator<( const OID& other ) const { return compare( other ) < 0; } + }; +#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 + 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/bson/ordering.h b/bson/ordering.h new file mode 100644 index 0000000..fbbfbec --- /dev/null +++ b/bson/ordering.h @@ -0,0 +1,66 @@ +// 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 { + + /** A precomputation of a BSON key pattern. + 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 { + const unsigned bits; + const unsigned nkeys; + Ordering(unsigned b,unsigned n) : bits(b),nkeys(n) { } + public: + /** 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 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,n); + } + }; + +} diff --git a/bson/stringdata.h b/bson/stringdata.h new file mode 100644 index 0000000..ccf30f7 --- /dev/null +++ b/bson/stringdata.h @@ -0,0 +1,64 @@ +// 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. + */ + +#ifndef BSON_STRINDATA_HEADER +#define BSON_STRINDATA_HEADER + +#include +#include + +namespace mongo { + + using std::string; + + class StringData { + public: + StringData( const char* c ) + : _data(c), _size((unsigned) strlen(c)) {} + + StringData( const string& s ) + : _data(s.c_str()), _size((unsigned) s.size()) {} + + struct LiteralTag {}; + template + StringData( const char (&val)[N], LiteralTag ) + : _data(&val[0]), _size(N-1) {} + + // 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, size_t strlenOfc ) + : _data(c), _size((unsigned) strlenOfc) {} + + const char* const data() const { return _data; } + const unsigned size() const { return _size; } + + private: + // TODO - Hook this class up in the BSON machinery + // There are two assumptions here that we may want to review then. + // '_data' *always* finishes with a null terminator + // 'size' does *not* account for the null terminator + // These assumptions may make it easier to minimize changes to existing code + const char* const _data; + const unsigned _size; + }; + +} // namespace mongo + +#endif // BSON_STRINGDATA_HEADER diff --git a/bson/util/atomic_int.h b/bson/util/atomic_int.h new file mode 100644 index 0000000..f4d2749 --- /dev/null +++ b/bson/util/atomic_int.h @@ -0,0 +1,99 @@ +// 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 +#endif + +namespace mongo { + + struct AtomicUInt{ + AtomicUInt() : x(0) {} + AtomicUInt(unsigned z) : x(z) { } + volatile unsigned x; + operator unsigned() const { + return x; + } + inline AtomicUInt operator++(); // ++prefix + inline AtomicUInt operator++(int);// postfix++ + inline AtomicUInt operator--(); // --prefix + inline AtomicUInt operator--(int); // postfix-- + }; + +#if defined(_WIN32) + AtomicUInt AtomicUInt::operator++(){ + // InterlockedIncrement returns the new value + return InterlockedIncrement((volatile long*)&x); //long is 32bits in Win64 + } + 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 + 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__)) + // 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/bson/util/builder.h b/bson/util/builder.h new file mode 100644 index 0000000..75a1ad8 --- /dev/null +++ b/bson/util/builder.h @@ -0,0 +1,226 @@ +/* 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 +#include +#include +#include + +#include "../inline_decls.h" +#include "../stringdata.h" + +namespace mongo { + + class StringBuilder; + + void msgasserted(int msgid, const char *msg); + + class BufBuilder { + public: + BufBuilder(int initsize = 512) : size(initsize) { + if ( size > 0 ) { + data = (char *) malloc(size); + if( data == 0 ) + msgasserted(10000, "out of memory BufBuilder"); + } else { + data = 0; + } + l = 0; + } + ~BufBuilder() { + kill(); + } + + void kill() { + if ( data ) { + free(data); + data = 0; + } + } + + void reset( int maxSize = 0 ){ + l = 0; + if ( maxSize && size > maxSize ){ + free(data); + data = (char*)malloc(maxSize); + size = maxSize; + } + } + + /* leave room for some stuff later */ + 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 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); + } + + void appendStr(const StringData &str , bool includeEOO = true ) { + const int len = str.size() + ( includeEOO ? 1 : 0 ); + memcpy(grow(len), str.data(), len); + } + + int len() const { + return l; + } + + void setlen( int newLen ){ + l = newLen; + } + + /* returns the pre-grow write position */ + inline char* grow(int by) { + int oldlen = l; + l += by; + if ( l > size ) { + grow_reallocate(); + } + return data + oldlen; + } + + int getSize() const { return size; } + + 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 > 64 * 1024 * 1024 ) + msgasserted(10000, "BufBuilder grow() > 64MB"); + data = (char *) realloc(data, a); + size= a; + } + + char *data; + int l; + int size; + + friend class StringBuilder; + }; + +#if defined(_WIN32) +#pragma warning( disable : 4996 ) +#endif + + class StringBuilder { + public: + StringBuilder( int initsize=256 ) + : _buf( initsize ){ + } + +#define SBNUM(val,maxSize,macro) \ + int prev = _buf.l; \ + int z = sprintf( _buf.grow(maxSize) , macro , (val) ); \ + _buf.l = prev + z; \ + return *this; + + StringBuilder& operator<<( double x ){ + SBNUM( x , 25 , "%g" ); + } + StringBuilder& operator<<( int x ){ + SBNUM( x , 11 , "%d" ); + } + StringBuilder& operator<<( unsigned x ){ + SBNUM( x , 11 , "%u" ); + } + StringBuilder& operator<<( long x ){ + SBNUM( x , 22 , "%ld" ); + } + StringBuilder& operator<<( unsigned long x ){ + SBNUM( x , 22 , "%lu" ); + } + StringBuilder& operator<<( long long x ){ + SBNUM( x , 22 , "%lld" ); + } + StringBuilder& operator<<( unsigned long long x ){ + SBNUM( x , 22 , "%llu" ); + } + StringBuilder& operator<<( short x ){ + SBNUM( x , 8 , "%hd" ); + } + StringBuilder& operator<<( char c ){ + _buf.grow( 1 )[0] = c; + return *this; + } +#undef SBNUM + + 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; + } + + // access + + void reset( int maxSize = 0 ){ + _buf.reset( maxSize ); + } + + std::string str(){ + return std::string(_buf.data, _buf.l); + } + + private: + BufBuilder _buf; + }; + +} // namespace mongo diff --git a/bson/util/misc.h b/bson/util/misc.h new file mode 100644 index 0000000..cad9a28 --- /dev/null +++ b/bson/util/misc.h @@ -0,0 +1,94 @@ +/* @file util.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 + +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; } + 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; + } +} -- cgit v1.2.3