diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/node_buffer.cc | 79 | ||||
-rw-r--r-- | src/util.cc | 23 | ||||
-rw-r--r-- | src/util.h | 27 |
3 files changed, 114 insertions, 15 deletions
diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 988e41dbc9..026a04028a 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -570,42 +570,91 @@ void Copy(const FunctionCallbackInfo<Value> &args) { void Fill(const FunctionCallbackInfo<Value>& args) { - THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); + Environment* env = Environment::GetCurrent(args); + + THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); SPREAD_ARG(args[0], ts_obj); size_t start = args[2]->Uint32Value(); size_t end = args[3]->Uint32Value(); - size_t length = end - start; - CHECK(length + start <= ts_obj_length); + size_t fill_length = end - start; + Local<String> str_obj; + size_t str_length; + enum encoding enc; + CHECK(fill_length + start <= ts_obj_length); + + // First check if Buffer has been passed. + if (Buffer::HasInstance(args[1])) { + SPREAD_ARG(args[1], fill_obj); + str_length = fill_obj_length; + memcpy(ts_obj_data + start, fill_obj_data, MIN(str_length, fill_length)); + goto start_fill; + } - if (args[1]->IsNumber()) { + // Then coerce everything that's not a string. + if (!args[1]->IsString()) { int value = args[1]->Uint32Value() & 255; - memset(ts_obj_data + start, value, length); + memset(ts_obj_data + start, value, fill_length); return; } - node::Utf8Value str(args.GetIsolate(), args[1]); - size_t str_length = str.length(); - size_t in_there = str_length; - char* ptr = ts_obj_data + start + str_length; + str_obj = args[1]->ToString(env->isolate()); + enc = ParseEncoding(env->isolate(), args[4], UTF8); + str_length = + enc == UTF8 ? str_obj->Utf8Length() : + enc == UCS2 ? str_obj->Length() * sizeof(uint16_t) : str_obj->Length(); + + if (enc == HEX && str_length % 2 != 0) + return env->ThrowTypeError("Invalid hex string"); if (str_length == 0) return; - memcpy(ts_obj_data + start, *str, MIN(str_length, length)); + // Can't use StringBytes::Write() in all cases. For example if attempting + // to write a two byte character into a one byte Buffer. + if (enc == UTF8) { + node::Utf8Value str(env->isolate(), args[1]); + memcpy(ts_obj_data + start, *str, MIN(str_length, fill_length)); - if (str_length >= length) + } else if (enc == UCS2) { + node::TwoByteValue str(env->isolate(), args[1]); + memcpy(ts_obj_data + start, *str, MIN(str_length, fill_length)); + + } else { + // Write initial String to Buffer, then use that memory to copy remainder + // of string. Correct the string length for cases like HEX where less than + // the total string length is written. + str_length = StringBytes::Write(env->isolate(), + ts_obj_data + start, + fill_length, + str_obj, + enc, + nullptr); + // This check is also needed in case Write() returns that no bytes could + // be written. + // TODO(trevnorris): Should this throw? Because of the string length was + // greater than 0 but couldn't be written then the string was invalid. + if (str_length == 0) + return; + } + + start_fill: + + if (str_length >= fill_length) return; - while (in_there < length - in_there) { + + size_t in_there = str_length; + char* ptr = ts_obj_data + start + str_length; + + while (in_there < fill_length - in_there) { memcpy(ptr, ts_obj_data + start, in_there); ptr += in_there; in_there *= 2; } - if (in_there < length) { - memcpy(ptr, ts_obj_data + start, length - in_there); - in_there = length; + if (in_there < fill_length) { + memcpy(ptr, ts_obj_data + start, fill_length - in_there); } } diff --git a/src/util.cc b/src/util.cc index 903fbbba13..095e5582db 100644 --- a/src/util.cc +++ b/src/util.cc @@ -25,4 +25,27 @@ Utf8Value::Utf8Value(v8::Isolate* isolate, v8::Local<v8::Value> value) str_[length_] = '\0'; } + +TwoByteValue::TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value) + : length_(0), str_(str_st_) { + if (value.IsEmpty()) + return; + + v8::Local<v8::String> string = value->ToString(isolate); + if (string.IsEmpty()) + return; + + // Allocate enough space to include the null terminator + size_t len = StringBytes::StorageSize(isolate, string, UCS2) + 1; + if (len > sizeof(str_st_)) { + str_ = static_cast<uint16_t*>(malloc(len)); + CHECK_NE(str_, nullptr); + } + + const int flags = + v8::String::NO_NULL_TERMINATION | v8::String::REPLACE_INVALID_UTF8; + length_ = string->Write(str_, 0, len, flags); + str_[length_] = '\0'; +} + } // namespace node diff --git a/src/util.h b/src/util.h index 7b2bc0f1a2..84d0b5a170 100644 --- a/src/util.h +++ b/src/util.h @@ -205,6 +205,33 @@ class Utf8Value { char str_st_[1024]; }; +class TwoByteValue { + public: + explicit TwoByteValue(v8::Isolate* isolate, v8::Local<v8::Value> value); + + ~TwoByteValue() { + if (str_ != str_st_) + free(str_); + } + + uint16_t* operator*() { + return str_; + }; + + const uint16_t* operator*() const { + return str_; + }; + + size_t length() const { + return length_; + }; + + private: + size_t length_; + uint16_t* str_; + uint16_t str_st_[1024]; +}; + } // namespace node #endif // SRC_UTIL_H_ |