diff options
author | Mathias Stearn <mathias@10gen.com> | 2013-04-11 18:20:33 -0400 |
---|---|---|
committer | Mathias Stearn <mathias@10gen.com> | 2013-04-24 16:59:03 -0400 |
commit | dccfd557ce411b890b708cbc259a14e023263a8e (patch) | |
tree | 462e160e5a7dcede90cbba52823697e5a28673e0 /src | |
parent | 6cea3cbbca67f682d16b93a50f9a90a4b7f2b405 (diff) | |
download | mongo-dccfd557ce411b890b708cbc259a14e023263a8e.tar.gz |
Add V8String class to avoid copying v8::Strings into std::strings
Still need to copy once into v8::Utf8Value but this saves the second copy
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/base/string_data.h | 15 | ||||
-rw-r--r-- | src/mongo/scripting/engine_v8.cpp | 56 | ||||
-rw-r--r-- | src/mongo/scripting/engine_v8.h | 18 | ||||
-rw-r--r-- | src/mongo/scripting/v8_utils.cpp | 7 | ||||
-rw-r--r-- | src/mongo/scripting/v8_utils.h | 42 |
5 files changed, 96 insertions, 42 deletions
diff --git a/src/mongo/base/string_data.h b/src/mongo/base/string_data.h index f764549f700..f2285cc5376 100644 --- a/src/mongo/base/string_data.h +++ b/src/mongo/base/string_data.h @@ -23,6 +23,8 @@ #include <limits> #include <string> +#include <third_party/murmurhash3/MurmurHash3.h> + namespace mongo { using std::string; @@ -126,6 +128,19 @@ namespace mongo { string toString() const { return string(_data, size()); } char operator[] ( unsigned pos ) const { return _data[pos]; } + /** + * Functor compatible with std::hash for std::unordered_{map,set} + * Warning: The hash function is subject to change. Do not use in cases where hashes need + * to be consistent across versions. + */ + struct Hasher { + size_t operator() (const StringData& str) const { + unsigned out; + MurmurHash3_x86_32(str.rawData(), str.size(), 0, &out); + return out; + } + }; + private: const char* _data; // is not guaranted to be null terminated (see "notes" above) mutable size_t _size; // 'size' does not include the null terminator diff --git a/src/mongo/scripting/engine_v8.cpp b/src/mongo/scripting/engine_v8.cpp index 1f144468d16..d0155ce37f1 100644 --- a/src/mongo/scripting/engine_v8.cpp +++ b/src/mongo/scripting/engine_v8.cpp @@ -163,12 +163,12 @@ namespace mongo { int outIndex = 0; V8Scope* scope = getScope(info.GetIsolate()); - unordered_set<string> added; + unordered_set<StringData, StringData::Hasher> added; // note here that if keys are parseable number, v8 will access them using index for (BSONObjIterator it(obj); it.more();) { const BSONElement& f = it.next(); - string sname = f.fieldName(); - if (holder->_removed.count(sname)) + StringData sname (f.fieldName(), f.fieldNameSize()-1); + if (holder->_removed.count(sname.toString())) continue; v8::Handle<v8::String> name = scope->v8StringData(sname); @@ -182,7 +182,7 @@ namespace mongo { const int len = fields->Length(); for (int field=0; field < len; field++) { v8::Handle<v8::String> name = fields->Get(field).As<v8::String>(); - string sname = toSTLString(name); + V8String sname (name); if (added.count(sname)) continue; out->Set(outIndex++, name); @@ -301,15 +301,13 @@ namespace mongo { v8::Handle<v8::Value> NamedReadOnlySet(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo& info) { - string key = toSTLString(property); - cout << "cannot write property " << key << " to read-only object" << endl; + cout << "cannot write property " << V8String(property) << " to read-only object" << endl; return value; } v8::Handle<v8::Boolean> NamedReadOnlyDelete(v8::Local<v8::String> property, const v8::AccessorInfo& info) { - string key = toSTLString(property); - cout << "cannot delete property " << key << " from read-only object" << endl; + cout << "cannot delete property " << V8String(property) << " from read-only object" << endl; return v8::Boolean::New(false); } @@ -621,7 +619,7 @@ namespace mongo { v8::External::Cast(*args.Callee()->Get(scope->strLitToV8("_native_data"))); BSONObjBuilder b; for (int i = 0; i < args.Length(); ++i) - scope->v8ToMongoElement(b, str::stream() << i, args[i]); + scope->v8ToMongoElement(b, BSONObjBuilder::numStr(i), args[i]); BSONObj nativeArgs = b.obj(); ret = function(nativeArgs, data->Value()); } @@ -901,7 +899,7 @@ namespace mongo { // --- functions ----- - bool hasFunctionIdentifier(const string& code) { + bool hasFunctionIdentifier(const StringData& code) { if (code.size() < 9 || code.find("function") != 0 ) return false; @@ -1078,7 +1076,7 @@ namespace mongo { if (printResult && !result->IsUndefined()) { // appears to only be used by shell - cout << toSTLString(result) << endl; + cout << V8String(result) << endl; } return true; @@ -1455,7 +1453,7 @@ namespace mongo { } void V8Scope::v8ToMongoNumber(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Value> value, BSONObj* originalParent) { double val = value->ToNumber()->Value(); @@ -1472,7 +1470,7 @@ namespace mongo { } void V8Scope::v8ToMongoNumberLong(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj) { // TODO might be nice to potentially speed this up with an indexed internal // field, but I don't yet know how to use an ObjectTemplate with a @@ -1490,7 +1488,7 @@ namespace mongo { } void V8Scope::v8ToMongoInternal(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj) { uint32_t bsonTypeRaw = obj->GetInternalField(0)->ToUint32()->Value(); BSONType bsonType = static_cast<BSONType>(bsonTypeRaw); @@ -1513,17 +1511,18 @@ namespace mongo { } void V8Scope::v8ToMongoRegex(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> v8Regex) { - string regex = toSTLString(v8Regex); + V8String v8RegexString (v8Regex); + StringData regex = v8RegexString; regex = regex.substr(1); - string r = regex.substr(0 ,regex.rfind("/")); - string o = regex.substr(regex.rfind("/") + 1); + StringData r = regex.substr(0 ,regex.rfind('/')); + StringData o = regex.substr(regex.rfind('/') + 1); b.appendRegex(elementName, r, o); } void V8Scope::v8ToMongoDBRef(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj) { OID oid; v8::Local<v8::Value> theid = obj->Get(strLitToV8("id")); @@ -1533,7 +1532,7 @@ namespace mongo { } void V8Scope::v8ToMongoBinData(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj) { int len = obj->Get(strLitToV8("len"))->ToInt32()->Value(); b.appendBinData(elementName, @@ -1543,7 +1542,7 @@ namespace mongo { } void V8Scope::v8ToMongoObjectID(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj) { OID oid; oid.init(toSTLString(obj->Get(strLitToV8("str")))); @@ -1551,7 +1550,7 @@ namespace mongo { } void V8Scope::v8ToMongoObject(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Value> value, int depth, BSONObj* originalParent) { @@ -1587,17 +1586,17 @@ namespace mongo { } } - void V8Scope::v8ToMongoElement(BSONObjBuilder & b, const string& sname, + void V8Scope::v8ToMongoElement(BSONObjBuilder & b, const StringData& sname, v8::Handle<v8::Value> value, int depth, BSONObj* originalParent) { if (value->IsString()) { - b.append(sname, toSTLString(value)); + b.append(sname, V8String(value)); return; } if (value->IsFunction()) { uassert(16716, "cannot convert native function to BSON", !value->ToObject()->Has(strLitToV8("_v8_function"))); - b.appendCode(sname, toSTLString(value)); + b.appendCode(sname, V8String(value)); return; } if (value->IsNumber()) { @@ -1667,11 +1666,12 @@ namespace mongo { v8::Local<v8::Array> names = o->GetOwnPropertyNames(); for (unsigned int i=0; i<names->Length(); i++) { v8::Local<v8::String> name = names->Get(i)->ToString(); - v8::Local<v8::Value> value = o->Get(name); - const string sname = toSTLString(name); - if (depth == 0 && sname == "_id") + + if (depth == 0 && name->StrictEquals(strLitToV8("_id"))) continue; // already handled above + V8String sname(name); + v8::Local<v8::Value> value = o->Get(name); v8ToMongoElement(b, sname, value, depth + 1, &originalBSON); } return b.obj(); diff --git a/src/mongo/scripting/engine_v8.h b/src/mongo/scripting/engine_v8.h index 0b1e2d173b4..2f6c1868c25 100644 --- a/src/mongo/scripting/engine_v8.h +++ b/src/mongo/scripting/engine_v8.h @@ -223,36 +223,36 @@ namespace mongo { */ mongo::BSONObj v8ToMongo(v8::Handle<v8::Object> obj, int depth = 0); void v8ToMongoElement(BSONObjBuilder& b, - const string& sname, + const StringData& sname, v8::Handle<v8::Value> value, int depth = 0, BSONObj* originalParent = 0); void v8ToMongoObject(BSONObjBuilder& b, - const string& sname, + const StringData& sname, v8::Handle<v8::Value> value, int depth, BSONObj* originalParent); void v8ToMongoNumber(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Value> value, BSONObj* originalParent); void v8ToMongoNumberLong(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj); void v8ToMongoInternal(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj); void v8ToMongoRegex(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> v8Regex); void v8ToMongoDBRef(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj); void v8ToMongoBinData(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj); void v8ToMongoObjectID(BSONObjBuilder& b, - const string& elementName, + const StringData& elementName, v8::Handle<v8::Object> obj); v8::Function* getNamedCons(const char* name); diff --git a/src/mongo/scripting/v8_utils.cpp b/src/mongo/scripting/v8_utils.cpp index fa6099d61e1..ec6357e99a4 100644 --- a/src/mongo/scripting/v8_utils.cpp +++ b/src/mongo/scripting/v8_utils.cpp @@ -35,10 +35,7 @@ using namespace std; namespace mongo { std::string toSTLString(const v8::Handle<v8::Value>& o) { - v8::String::Utf8Value str(o); - massert(16686, "error converting js type to Utf8Value", *str); - std::string s(*str, str.length()); - return s; + return StringData(V8String(o)).toString(); } /** Get the properties of an object (and its prototype) as a comma-delimited string */ @@ -96,7 +93,7 @@ namespace mongo { // arguments need to be copied into the isolate, go through bson BSONObjBuilder b; for(int i = 0; i < args.Length(); ++i) { - scope->v8ToMongoElement(b, mongoutils::str::stream() << "arg" << i, args[i]); + scope->v8ToMongoElement(b, "arg" + BSONObjBuilder::numStr(i), args[i]); } _args = b.obj(); } diff --git a/src/mongo/scripting/v8_utils.h b/src/mongo/scripting/v8_utils.h index 7f753d36bd3..299e1f6eef9 100644 --- a/src/mongo/scripting/v8_utils.h +++ b/src/mongo/scripting/v8_utils.h @@ -23,6 +23,9 @@ #include <string> #include <v8.h> +#include <mongo/base/string_data.h> +#include <mongo/util/assert_util.h> + namespace mongo { #define jsassert(x,msg) uassert(16664, (msg), (x)) @@ -38,6 +41,45 @@ namespace mongo { /** Simple v8 object to string conversion helper */ std::string toSTLString(const v8::Handle<v8::Value>& o); + /** Like toSTLString but doesn't allocate a new std::string + * + * This owns the string's memory so you need to be careful not to let the + * converted StringDatas outlive the V8Scope object. These rules are the + * same as converting from a std::string into a StringData. + * + * Safe: + * void someFunction(StringData argument); + * v8::Handle<v8::String> aString; + * + * someFunction(V8String(aString)); // passing down stack as temporary + * + * V8String named (aString); + * someFunction(named); // passing up stack as named value + * + * StringData sd = named; // scope of sd is less than named + * + * Unsafe: + * StringData _member; + * + * StringData returningFunction() { + * StringData sd = V8String(aString); // sd outlives the temporary + * + * V8String named(aString) + * _member = named; // _member outlives named scope + * + * return V8String(aString); // passing up stack + * } + */ + class V8String { + public: + explicit V8String(const v8::Handle<v8::Value>& o) :_str(o) { + massert(16686, "error converting js type to Utf8Value", *_str); + } + operator StringData () const { return StringData(*_str, _str.length()); } + private: + v8::String::Utf8Value _str; + }; + /** Get the properties of an object (and it's prototype) as a comma-delimited string */ std::string v8ObjectToString(const v8::Handle<v8::Object>& o); |