From c319fd121467d1b8b4aa18687c6801dcacdb4187 Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 30 Jan 2015 15:05:28 +0300 Subject: [PATCH 1/5] node: speed up ParseEncoding Handle most popular cases in a trie-style, branching on a first character. Remove useless HandleScope which was only eating time without producing any value. --- src/node.cc | 83 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 22 deletions(-) diff --git a/src/node.cc b/src/node.cc index 72ddbe178e37b9..d9270c6320080d 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1148,45 +1148,72 @@ Handle MakeCallback(Isolate* isolate, } -enum encoding ParseEncoding(Isolate* isolate, - Handle encoding_v, - enum encoding _default) { - HandleScope scope(isolate); - - if (!encoding_v->IsString()) - return _default; - - node::Utf8Value encoding(isolate, encoding_v); +enum encoding ParseEncoding(const char* encoding, enum encoding _default) { + switch (encoding[0]) { + case 'u': + // utf8, utf16le + if (encoding[1] == 't' && encoding[2] == 'f') { + // Skip `-` + encoding += encoding[3] == '-' ? 4 : 3; + if (encoding[0] == '8' && encoding[1] == '\0') + return UTF8; + if (strncasecmp(encoding, "16le", 4) == 0) + return UCS2; + + // ucs2 + } else if (encoding[1] == 'c' && encoding[2] == 's') { + encoding += encoding[3] == '-' ? 4 : 3; + if (encoding[0] == '2' && encoding[1] == '\0') + return UCS2; + } + break; + case 'b': + // binary + if (encoding[1] == 'i') { + if (strncmp(encoding + 2, "nary", 4) == 0) + return BINARY; + + // buffer + } else if (encoding[1] == 'u') { + if (strncmp(encoding + 2, "ffer", 4) == 0) + return BUFFER; + } + break; + case '\0': + return _default; + default: + break; + } - if (strcasecmp(*encoding, "utf8") == 0) { + if (strcasecmp(encoding, "utf8") == 0) { return UTF8; - } else if (strcasecmp(*encoding, "utf-8") == 0) { + } else if (strcasecmp(encoding, "utf-8") == 0) { return UTF8; - } else if (strcasecmp(*encoding, "ascii") == 0) { + } else if (strcasecmp(encoding, "ascii") == 0) { return ASCII; - } else if (strcasecmp(*encoding, "base64") == 0) { + } else if (strcasecmp(encoding, "base64") == 0) { return BASE64; - } else if (strcasecmp(*encoding, "ucs2") == 0) { + } else if (strcasecmp(encoding, "ucs2") == 0) { return UCS2; - } else if (strcasecmp(*encoding, "ucs-2") == 0) { + } else if (strcasecmp(encoding, "ucs-2") == 0) { return UCS2; - } else if (strcasecmp(*encoding, "utf16le") == 0) { + } else if (strcasecmp(encoding, "utf16le") == 0) { return UCS2; - } else if (strcasecmp(*encoding, "utf-16le") == 0) { + } else if (strcasecmp(encoding, "utf-16le") == 0) { return UCS2; - } else if (strcasecmp(*encoding, "binary") == 0) { + } else if (strcasecmp(encoding, "binary") == 0) { return BINARY; - } else if (strcasecmp(*encoding, "buffer") == 0) { + } else if (strcasecmp(encoding, "buffer") == 0) { return BUFFER; - } else if (strcasecmp(*encoding, "hex") == 0) { + } else if (strcasecmp(encoding, "hex") == 0) { return HEX; - } else if (strcasecmp(*encoding, "raw") == 0) { + } else if (strcasecmp(encoding, "raw") == 0) { if (!no_deprecation) { fprintf(stderr, "'raw' (array of integers) has been removed. " "Use 'binary'.\n"); } return BINARY; - } else if (strcasecmp(*encoding, "raws") == 0) { + } else if (strcasecmp(encoding, "raws") == 0) { if (!no_deprecation) { fprintf(stderr, "'raws' encoding has been renamed to 'binary'. " "Please update your code.\n"); @@ -1197,6 +1224,18 @@ enum encoding ParseEncoding(Isolate* isolate, } } + +enum encoding ParseEncoding(Isolate* isolate, + Handle encoding_v, + enum encoding _default) { + if (!encoding_v->IsString()) + return _default; + + node::Utf8Value encoding(isolate, encoding_v); + + return ParseEncoding(*encoding, _default); +} + Local Encode(Isolate* isolate, const char* buf, size_t len, From 38cf6280ba58127d14f4bc1cf7e3384166ff2ffe Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 30 Jan 2015 15:12:48 +0300 Subject: [PATCH 2/5] crypto: use on-stack storage in HashUpdate Boosts speed up to 10% on primitive `createHash().update().digest()` benchmark. --- src/node_crypto.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 3ad4af230ca762..a479f3d6b4b9b7 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -3058,14 +3058,22 @@ void Hash::HashUpdate(const FunctionCallbackInfo& args) { if (!StringBytes::IsValidString(env->isolate(), string, encoding)) return env->ThrowTypeError("Bad input string"); size_t buflen = StringBytes::StorageSize(env->isolate(), string, encoding); - char* buf = new char[buflen]; + char storage[1024]; + char* buf; + + if (buflen <= sizeof(storage)) + buf = storage; + else + buf = new char[buflen]; + size_t written = StringBytes::Write(env->isolate(), buf, buflen, string, encoding); r = hash->HashUpdate(buf, written); - delete[] buf; + if (buf != storage) + delete[] buf; } else { char* buf = Buffer::Data(args[0]); size_t buflen = Buffer::Length(args[0]); From fcbf80cb75864e8552f5affe767e83495a0ab85c Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 30 Jan 2015 15:35:11 +0300 Subject: [PATCH 3/5] string_bytes: introduce InlineDecoder --- src/string_bytes.h | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/string_bytes.h b/src/string_bytes.h index 711e593a0f4f6c..f6cf834e25aec1 100644 --- a/src/string_bytes.h +++ b/src/string_bytes.h @@ -5,6 +5,8 @@ #include "v8.h" #include "node.h" +#include "env.h" +#include "env-inl.h" namespace node { @@ -12,6 +14,51 @@ extern int WRITE_UTF8_FLAGS; class StringBytes { public: + class InlineDecoder { + public: + explicit InlineDecoder(Environment* env) : env_(env), out_(nullptr) { + } + + ~InlineDecoder() { + if (out_ != out_st_) + delete[] out_; + out_ = nullptr; + } + + inline bool Decode(v8::Handle string, + v8::Handle encoding, + enum encoding _default) { + enum encoding enc = ParseEncoding(env_->isolate(), encoding, _default); + if (!StringBytes::IsValidString(env_->isolate(), string, enc)) { + env_->ThrowTypeError("Bad input string"); + return false; + } + + size_t buflen = StringBytes::StorageSize(env_->isolate(), string, enc); + if (buflen > sizeof(out_st_)) + out_ = new char[buflen]; + else + out_ = out_st_; + size_ = StringBytes::Write(env_->isolate(), + out_, + buflen, + string, + enc); + return true; + } + + inline const char* out() const { return out_; } + inline size_t size() const { return size_; } + + private: + static const int kStorageSize = 1024; + + Environment* env_; + char out_st_[kStorageSize]; + char* out_; + size_t size_; + }; + // Does the string match the encoding? Quick but non-exhaustive. // Example: a HEX string must have a length that's a multiple of two. // FIXME(bnoordhuis) IsMaybeValidString()? Naming things is hard... From 077bc99c32571792b60c55f2a4359b202ecb395e Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 30 Jan 2015 15:35:18 +0300 Subject: [PATCH 4/5] crypto: use StringBytes::InlineDecoder --- src/node_crypto.cc | 96 ++++++++++++---------------------------------- 1 file changed, 24 insertions(+), 72 deletions(-) diff --git a/src/node_crypto.cc b/src/node_crypto.cc index a479f3d6b4b9b7..7587e02008a4e5 100644 --- a/src/node_crypto.cc +++ b/src/node_crypto.cc @@ -2750,19 +2750,11 @@ void CipherBase::Update(const FunctionCallbackInfo& args) { // Only copy the data if we have to, because it's a string if (args[0]->IsString()) { - Local string = args[0].As(); - enum encoding encoding = ParseEncoding(env->isolate(), args[1], BINARY); - if (!StringBytes::IsValidString(env->isolate(), string, encoding)) - return env->ThrowTypeError("Bad input string"); - size_t buflen = StringBytes::StorageSize(env->isolate(), string, encoding); - char* buf = new char[buflen]; - size_t written = StringBytes::Write(env->isolate(), - buf, - buflen, - string, - encoding); - r = cipher->Update(buf, written, &out, &out_len); - delete[] buf; + StringBytes::InlineDecoder decoder(env); + + if (!decoder.Decode(args[0].As(), args[1], BINARY)) + return; + r = cipher->Update(decoder.out(), decoder.size(), &out, &out_len); } else { char* buf = Buffer::Data(args[0]); size_t buflen = Buffer::Length(args[0]); @@ -2929,19 +2921,11 @@ void Hmac::HmacUpdate(const FunctionCallbackInfo& args) { // Only copy the data if we have to, because it's a string bool r; if (args[0]->IsString()) { - Local string = args[0].As(); - enum encoding encoding = ParseEncoding(env->isolate(), args[1], BINARY); - if (!StringBytes::IsValidString(env->isolate(), string, encoding)) - return env->ThrowTypeError("Bad input string"); - size_t buflen = StringBytes::StorageSize(env->isolate(), string, encoding); - char* buf = new char[buflen]; - size_t written = StringBytes::Write(env->isolate(), - buf, - buflen, - string, - encoding); - r = hmac->HmacUpdate(buf, written); - delete[] buf; + StringBytes::InlineDecoder decoder(env); + + if (!decoder.Decode(args[0].As(), args[1], BINARY)) + return; + r = hmac->HmacUpdate(decoder.out(), decoder.size()); } else { char* buf = Buffer::Data(args[0]); size_t buflen = Buffer::Length(args[0]); @@ -3053,27 +3037,11 @@ void Hash::HashUpdate(const FunctionCallbackInfo& args) { // Only copy the data if we have to, because it's a string bool r; if (args[0]->IsString()) { - Local string = args[0].As(); - enum encoding encoding = ParseEncoding(env->isolate(), args[1], BINARY); - if (!StringBytes::IsValidString(env->isolate(), string, encoding)) - return env->ThrowTypeError("Bad input string"); - size_t buflen = StringBytes::StorageSize(env->isolate(), string, encoding); - char storage[1024]; - char* buf; - - if (buflen <= sizeof(storage)) - buf = storage; - else - buf = new char[buflen]; + StringBytes::InlineDecoder decoder(env); - size_t written = StringBytes::Write(env->isolate(), - buf, - buflen, - string, - encoding); - r = hash->HashUpdate(buf, written); - if (buf != storage) - delete[] buf; + if (!decoder.Decode(args[0].As(), args[1], BINARY)) + return; + r = hash->HashUpdate(decoder.out(), decoder.size()); } else { char* buf = Buffer::Data(args[0]); size_t buflen = Buffer::Length(args[0]); @@ -3222,19 +3190,11 @@ void Sign::SignUpdate(const FunctionCallbackInfo& args) { // Only copy the data if we have to, because it's a string Error err; if (args[0]->IsString()) { - Local string = args[0].As(); - enum encoding encoding = ParseEncoding(env->isolate(), args[1], BINARY); - if (!StringBytes::IsValidString(env->isolate(), string, encoding)) - return env->ThrowTypeError("Bad input string"); - size_t buflen = StringBytes::StorageSize(env->isolate(), string, encoding); - char* buf = new char[buflen]; - size_t written = StringBytes::Write(env->isolate(), - buf, - buflen, - string, - encoding); - err = sign->SignUpdate(buf, written); - delete[] buf; + StringBytes::InlineDecoder decoder(env); + + if (!decoder.Decode(args[0].As(), args[1], BINARY)) + return; + err = sign->SignUpdate(decoder.out(), decoder.size()); } else { char* buf = Buffer::Data(args[0]); size_t buflen = Buffer::Length(args[0]); @@ -3403,19 +3363,11 @@ void Verify::VerifyUpdate(const FunctionCallbackInfo& args) { // Only copy the data if we have to, because it's a string Error err; if (args[0]->IsString()) { - Local string = args[0].As(); - enum encoding encoding = ParseEncoding(env->isolate(), args[1], BINARY); - if (!StringBytes::IsValidString(env->isolate(), string, encoding)) - return env->ThrowTypeError("Bad input string"); - size_t buflen = StringBytes::StorageSize(env->isolate(), string, encoding); - char* buf = new char[buflen]; - size_t written = StringBytes::Write(env->isolate(), - buf, - buflen, - string, - encoding); - err = verify->VerifyUpdate(buf, written); - delete[] buf; + StringBytes::InlineDecoder decoder(env); + + if (!decoder.Decode(args[0].As(), args[1], BINARY)) + return; + err = verify->VerifyUpdate(decoder.out(), decoder.size()); } else { char* buf = Buffer::Data(args[0]); size_t buflen = Buffer::Length(args[0]); From 7c051320bca4194fab92796e00103ac17b24f96a Mon Sep 17 00:00:00 2001 From: Fedor Indutny Date: Fri, 30 Jan 2015 15:50:12 +0300 Subject: [PATCH 5/5] node: ParseEncoding fix --- src/node.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/node.cc b/src/node.cc index d9270c6320080d..6f993782cde2e6 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1157,7 +1157,7 @@ enum encoding ParseEncoding(const char* encoding, enum encoding _default) { encoding += encoding[3] == '-' ? 4 : 3; if (encoding[0] == '8' && encoding[1] == '\0') return UTF8; - if (strncasecmp(encoding, "16le", 4) == 0) + if (strncmp(encoding, "16le", 4) == 0) return UCS2; // ucs2