diff --git a/src/node.cc b/src/node.cc index 72ddbe178e37b9..6f993782cde2e6 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 (strncmp(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, diff --git a/src/node_crypto.cc b/src/node_crypto.cc index 3ad4af230ca762..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,19 +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* buf = new char[buflen]; - size_t written = StringBytes::Write(env->isolate(), - buf, - buflen, - string, - encoding); - r = hash->HashUpdate(buf, written); - delete[] buf; + StringBytes::InlineDecoder decoder(env); + + 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]); @@ -3214,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]); @@ -3395,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]); 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...