diff options
Diffstat (limited to 'src/node_buffer.cc')
-rw-r--r-- | src/node_buffer.cc | 395 |
1 files changed, 321 insertions, 74 deletions
diff --git a/src/node_buffer.cc b/src/node_buffer.cc index 3f429817e13..d3398075d06 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -5,6 +5,8 @@ #include "env-inl.h" #include "smalloc.h" #include "string_bytes.h" +#include "util.h" +#include "util-inl.h" #include "v8-profiler.h" #include "v8.h" @@ -28,6 +30,16 @@ size_t name##_length; \ char* name##_data; +#define SPREAD_ARG(val, name) \ + CHECK((val)->IsUint8Array()); \ + Local<Uint8Array> name = (val).As<Uint8Array>(); \ + ArrayBuffer::Contents name##_c = name->Buffer()->GetContents(); \ + size_t name##_offset = name->ByteOffset(); \ + name##_length = name->ByteLength(); \ + name##_data = static_cast<char*>(name##_c.Data()) + name##_offset; \ + if (name##_length > 0) \ + CHECK_NE(name##_data, nullptr); + #define ARGS_THIS(argT, name) \ Local<Object> name = argT; \ name##_length = name->GetIndexedPropertiesExternalArrayDataLength(); \ @@ -48,6 +60,8 @@ namespace node { namespace Buffer { +using v8::ArrayBuffer; +using v8::ArrayBufferCreationMode; using v8::Context; using v8::EscapableHandleScope; using v8::Function; @@ -61,6 +75,7 @@ using v8::Number; using v8::Object; using v8::String; using v8::Uint32; +using v8::Uint8Array; using v8::Value; @@ -70,10 +85,19 @@ bool HasInstance(Handle<Value> val) { bool HasInstance(Handle<Object> obj) { - if (!obj->HasIndexedPropertiesInExternalArrayData()) + if (using_old_buffer) { + if (!obj->HasIndexedPropertiesInExternalArrayData()) + return false; + v8::ExternalArrayType type = + obj->GetIndexedPropertiesExternalArrayDataType(); + return type == v8::kExternalUint8Array; + } + + if (!obj->IsUint8Array()) return false; - v8::ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType(); - return type == v8::kExternalUint8Array; + Local<Uint8Array> array = obj.As<Uint8Array>(); + Environment* env = Environment::GetCurrent(array->GetIsolate()); + return array->GetPrototype()->StrictEquals(env->buffer_prototype_object()); } @@ -86,8 +110,15 @@ char* Data(Handle<Value> val) { char* Data(Handle<Object> obj) { - CHECK(obj->HasIndexedPropertiesInExternalArrayData()); - return static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData()); + if (using_old_buffer) { + CHECK(obj->HasIndexedPropertiesInExternalArrayData()); + return static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData()); + } + + CHECK(obj->IsUint8Array()); + Local<Uint8Array> ui = obj.As<Uint8Array>(); + ArrayBuffer::Contents ab_c = ui->Buffer()->GetContents(); + return static_cast<char*>(ab_c.Data()) + ui->ByteOffset(); } @@ -98,8 +129,14 @@ size_t Length(Handle<Value> val) { size_t Length(Handle<Object> obj) { - CHECK(obj->HasIndexedPropertiesInExternalArrayData()); - return obj->GetIndexedPropertiesExternalArrayDataLength(); + if (using_old_buffer) { + CHECK(obj->HasIndexedPropertiesInExternalArrayData()); + return obj->GetIndexedPropertiesExternalArrayDataLength(); + } + + CHECK(obj->IsUint8Array()); + Local<Uint8Array> ui = obj.As<Uint8Array>(); + return ui->ByteLength(); } @@ -107,11 +144,20 @@ Local<Object> New(Isolate* isolate, Handle<String> string, enum encoding enc) { EscapableHandleScope scope(isolate); size_t length = StringBytes::Size(isolate, string, enc); + char* data = static_cast<char*>(malloc(length)); - Local<Object> buf = New(isolate, length); - char* data = Buffer::Data(buf); - StringBytes::Write(isolate, data, length, string, enc); + if (data == nullptr) + return Local<Object>(); + size_t actual = StringBytes::Write(isolate, data, length, string, enc); + CHECK(actual <= length); + + if (actual < length) { + data = static_cast<char*>(realloc(data, actual)); + CHECK_NE(data, nullptr); + } + + Local<Object> buf = Use(isolate, data, actual); return scope.Escape(buf); } @@ -123,19 +169,44 @@ Local<Object> New(Isolate* isolate, size_t length) { } -// TODO(trevnorris): these have a flaw by needing to call the Buffer inst then -// Alloc. continue to look for a better architecture. Local<Object> New(Environment* env, size_t length) { EscapableHandleScope scope(env->isolate()); - CHECK_LE(length, kMaxLength); + if (using_old_buffer) { + CHECK_LE(length, kMaxLength); - Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg); + Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local<Object> obj = + env->buffer_constructor_function()->NewInstance(1, &arg); - smalloc::Alloc(env, obj, length); + smalloc::Alloc(env, obj, length); - return scope.Escape(obj); + return scope.Escape(obj); + } + + // V8 currently only allows a maximum Typed Array index of max Smi. + if (!IsValidSmi(length)) { + return Local<Object>(); + } + + void* data; + if (length > 0) { + data = malloc(length); + // NOTE: API change. Must check .IsEmpty() on the return object to see if + // the data was able to be allocated. + if (data == nullptr) + return Local<Object>(); + } else { + data = nullptr; + } + Local<ArrayBuffer> ab = + ArrayBuffer::New(env->isolate(), + data, + length, + ArrayBufferCreationMode::kInternalized); + Local<Uint8Array> ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + return scope.Escape(ui); } @@ -147,33 +218,59 @@ Local<Object> New(Isolate* isolate, const char* data, size_t length) { } -// TODO(trevnorris): for backwards compatibility this is left to copy the data, -// but for consistency w/ the other should use data. And a copy version renamed -// to something else. +// Make a copy of "data". Why this isn't called "Copy", we'll never know. Local<Object> New(Environment* env, const char* data, size_t length) { EscapableHandleScope scope(env->isolate()); - CHECK_LE(length, kMaxLength); + if (using_old_buffer) { + CHECK_LE(length, kMaxLength); + + Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local<Object> obj = + env->buffer_constructor_function()->NewInstance(1, &arg); + + char* new_data; + if (length > 0) { + new_data = static_cast<char*>(malloc(length)); + if (new_data == nullptr) + FatalError("node::Buffer::New(const char*, size_t)", "Out Of Memory"); + memcpy(new_data, data, length); + } else { + new_data = nullptr; + } - Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg); + smalloc::Alloc(env, obj, new_data, length); - // TODO(trevnorris): done like this to handle HasInstance since only checks - // if external array data has been set, but would like to use a better - // approach if v8 provided one. - char* new_data; + return scope.Escape(obj); + } + + // V8 currently only allows a maximum Typed Array index of max Smi. + if (!IsValidSmi(length)) { + return Local<Object>(); + } + + void* new_data; if (length > 0) { - new_data = static_cast<char*>(malloc(length)); + CHECK_NE(data, nullptr); + new_data = malloc(length); + // NOTE: API change. Must check .IsEmpty() on the return object to see if + // the data was able to be allocated. if (new_data == nullptr) - FatalError("node::Buffer::New(const char*, size_t)", "Out Of Memory"); + return Local<Object>(); memcpy(new_data, data, length); } else { new_data = nullptr; } - smalloc::Alloc(env, obj, new_data, length); + Local<ArrayBuffer> ab = + ArrayBuffer::New(env->isolate(), + new_data, + length, + ArrayBufferCreationMode::kInternalized); + Local<Uint8Array> ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); - return scope.Escape(obj); + return scope.Escape(ui); } @@ -196,6 +293,7 @@ Local<Object> New(Environment* env, void* hint) { EscapableHandleScope scope(env->isolate()); + // TODO(trevnorris): IMPLEMENT CHECK_LE(length, kMaxLength); Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); @@ -207,7 +305,7 @@ Local<Object> New(Environment* env, } -Local<Object> Use(Isolate* isolate, char* data, uint32_t length) { +Local<Object> Use(Isolate* isolate, char* data, size_t length) { Environment* env = Environment::GetCurrent(isolate); EscapableHandleScope handle_scope(env->isolate()); Local<Object> obj = Buffer::Use(env, data, length); @@ -215,17 +313,98 @@ Local<Object> Use(Isolate* isolate, char* data, uint32_t length) { } -Local<Object> Use(Environment* env, char* data, uint32_t length) { +Local<Object> Use(Environment* env, char* data, size_t length) { EscapableHandleScope scope(env->isolate()); - CHECK_LE(length, kMaxLength); + if (using_old_buffer) { + CHECK_LE(length, kMaxLength); - Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); - Local<Object> obj = env->buffer_constructor_function()->NewInstance(1, &arg); + Local<Value> arg = Uint32::NewFromUnsigned(env->isolate(), length); + Local<Object> obj = + env->buffer_constructor_function()->NewInstance(1, &arg); - smalloc::Alloc(env, obj, data, length); + smalloc::Alloc(env, obj, data, length); - return scope.Escape(obj); + return scope.Escape(obj); + } + + if (length > 0) { + CHECK_NE(data, nullptr); + CHECK(IsValidSmi(length)); + } + + Local<ArrayBuffer> ab = + ArrayBuffer::New(env->isolate(), + data, + length, + ArrayBufferCreationMode::kInternalized); + Local<Uint8Array> ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + return scope.Escape(ui); +} + + +void Create(const FunctionCallbackInfo<Value>& args) { + Isolate* isolate = args.GetIsolate(); + Environment* env = Environment::GetCurrent(args); + + CHECK(args[0]->IsNumber()); + + int64_t length = args[0]->IntegerValue(); + + if (!IsValidSmi(length)) { + return env->ThrowRangeError("invalid Buffer length"); + } + + void* data; + if (length > 0) { + data = malloc(length); + if (data == nullptr) + return env->ThrowRangeError("invalid Buffer length"); + } else { + data = nullptr; + } + + Local<ArrayBuffer> ab = + ArrayBuffer::New(isolate, + data, + length, + ArrayBufferCreationMode::kInternalized); + Local<Uint8Array> ui = Uint8Array::New(ab, 0, length); + ui->SetPrototype(env->buffer_prototype_object()); + args.GetReturnValue().Set(ui); +} + + +void CreateFromString(const FunctionCallbackInfo<Value>& args) { + CHECK(args[0]->IsString()); + CHECK(args[1]->IsString()); + + enum encoding enc = ParseEncoding(args.GetIsolate(), + args[1].As<String>(), + UTF8); + Local<Object> buf = New(args.GetIsolate(), args[0].As<String>(), enc); + args.GetReturnValue().Set(buf); +} + + +void Slice(const FunctionCallbackInfo<Value>& args) { + CHECK(args[0]->IsUint8Array()); + CHECK(args[1]->IsNumber()); + CHECK(args[2]->IsNumber()); + Environment* env = Environment::GetCurrent(args); + Local<Uint8Array> ab_ui = args[0].As<Uint8Array>(); + Local<ArrayBuffer> ab = ab_ui->Buffer(); + ArrayBuffer::Contents ab_c = ab->GetContents(); + size_t offset = ab_ui->ByteOffset(); + size_t start = args[1]->NumberValue() + offset; + size_t end = args[2]->NumberValue() + offset; + CHECK_GE(end, start); + size_t size = end - start; + CHECK_GE(ab_c.ByteLength(), start + size); + Local<Uint8Array> ui = Uint8Array::New(ab, start, size); + ui->SetPrototype(env->buffer_prototype_object()); + args.GetReturnValue().Set(ui); } @@ -237,7 +416,12 @@ void StringSlice(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args.This(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + } else { + SPREAD_ARG(args.This(), ts_obj); + } if (ts_obj_length == 0) return args.GetReturnValue().SetEmptyString(); @@ -252,10 +436,16 @@ void StringSlice(const FunctionCallbackInfo<Value>& args) { template <> void StringSlice<UCS2>(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); - ARGS_THIS_DEC(ts_obj); THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); - ARGS_THIS(args.This(), ts_obj); + + ARGS_THIS_DEC(ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + } else { + SPREAD_ARG(args.This(), ts_obj); + } if (ts_obj_length == 0) return args.GetReturnValue().SetEmptyString(); @@ -329,11 +519,22 @@ void Copy(const FunctionCallbackInfo<Value> &args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); - Local<Object> target_obj = args[0].As<Object>(); - size_t target_length = target->GetIndexedPropertiesExternalArrayDataLength(); - char* target_data = static_cast<char*>( - target->GetIndexedPropertiesExternalArrayData()); + Local<Object> target_obj = args[0]->ToObject(env->isolate()); + + ARGS_THIS_DEC(ts_obj); + ARGS_THIS_DEC(target); + + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + target_length = target_obj->GetIndexedPropertiesExternalArrayDataLength(); + target_data = static_cast<char*>( + target_obj->GetIndexedPropertiesExternalArrayData()); + } else { + SPREAD_ARG(args.This(), ts_obj); + SPREAD_ARG(target_obj, target); + } + size_t target_start; size_t source_start; size_t source_end; @@ -364,7 +565,12 @@ void Copy(const FunctionCallbackInfo<Value> &args) { void Fill(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } size_t start = args[2]->Uint32Value(); size_t end = args[3]->Uint32Value(); @@ -409,7 +615,12 @@ void StringWrite(const FunctionCallbackInfo<Value>& args) { ARGS_THIS_DEC(ts_obj); THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); - ARGS_THIS(args.This(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args.This(), ts_obj); + } else { + SPREAD_ARG(args.This(), ts_obj); + } if (!args[0]->IsString()) return env->ThrowTypeError("Argument must be a string"); @@ -487,7 +698,12 @@ template <typename T, enum Endianness endianness> void ReadFloatGeneric(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } uint32_t offset = args[1]->Uint32Value(); CHECK_LE(offset + sizeof(T), ts_obj_length); @@ -530,7 +746,12 @@ void ReadDoubleBE(const FunctionCallbackInfo<Value>& args) { template <typename T, enum Endianness endianness> uint32_t WriteFloatGeneric(const FunctionCallbackInfo<Value>& args) { ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } T val = args[1]->NumberValue(); uint32_t offset = args[2]->Uint32Value(); @@ -588,26 +809,27 @@ void Compare(const FunctionCallbackInfo<Value> &args) { THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]); THROW_AND_RETURN_UNLESS_BUFFER(env, args[1]); - Local<Object> obj_a = args[0].As<Object>(); - char* obj_a_data = - static_cast<char*>(obj_a->GetIndexedPropertiesExternalArrayData()); - size_t obj_a_len = obj_a->GetIndexedPropertiesExternalArrayDataLength(); + ARGS_THIS_DEC(obj_a); + ARGS_THIS_DEC(obj_b); - Local<Object> obj_b = args[1].As<Object>(); - char* obj_b_data = - static_cast<char*>(obj_b->GetIndexedPropertiesExternalArrayData()); - size_t obj_b_len = obj_b->GetIndexedPropertiesExternalArrayDataLength(); + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), obj_a); + ARGS_THIS(args[1].As<Object>(), obj_b); + } else { + SPREAD_ARG(args[0], obj_a); + SPREAD_ARG(args[1], obj_b); + } - size_t cmp_length = MIN(obj_a_len, obj_b_len); + size_t cmp_length = MIN(obj_a_length, obj_b_length); int32_t val = memcmp(obj_a_data, obj_b_data, cmp_length); // Normalize val to be an integer in the range of [1, -1] since // implementations of memcmp() can vary by platform. if (val == 0) { - if (obj_a_len > obj_b_len) + if (obj_a_length > obj_b_length) val = 1; - else if (obj_a_len < obj_b_len) + else if (obj_a_length < obj_b_length) val = -1; } else { if (val > 0) @@ -642,7 +864,12 @@ void IndexOfString(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } node::Utf8Value str(args.GetIsolate(), args[1]); int32_t offset_i32 = args[2]->Int32Value(); @@ -675,7 +902,12 @@ void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } Local<Object> buf = args[1].As<Object>(); int32_t offset_i32 = args[2]->Int32Value(); @@ -714,7 +946,12 @@ void IndexOfNumber(const FunctionCallbackInfo<Value>& args) { THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]); ARGS_THIS_DEC(ts_obj); - ARGS_THIS(args[0].As<Object>(), ts_obj); + + if (using_old_buffer) { + ARGS_THIS(args[0].As<Object>(), ts_obj); + } else { + SPREAD_ARG(args[0], ts_obj); + } uint32_t needle = args[1]->Uint32Value(); int32_t offset_i32 = args[2]->Int32Value(); @@ -743,15 +980,20 @@ void IndexOfNumber(const FunctionCallbackInfo<Value>& args) { void SetupBufferJS(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); - CHECK(args[0]->IsFunction()); - - Local<Function> bv = args[0].As<Function>(); - env->set_buffer_constructor_function(bv); - Local<Value> proto_v = bv->Get(env->prototype_string()); - - CHECK(proto_v->IsObject()); + Local<Object> proto; - Local<Object> proto = proto_v.As<Object>(); + if (using_old_buffer) { + CHECK(args[0]->IsFunction()); + Local<Function> bv = args[0].As<Function>(); + env->set_buffer_constructor_function(bv); + Local<Value> proto_v = bv->Get(env->prototype_string()); + CHECK(proto_v->IsObject()); + proto = proto_v.As<Object>(); + } else { + CHECK(args[0]->IsObject()); + proto = args[0].As<Object>(); + env->set_buffer_prototype_object(proto); + } env->SetMethod(proto, "asciiSlice", AsciiSlice); env->SetMethod(proto, "base64Slice", Base64Slice); @@ -770,9 +1012,11 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) { env->SetMethod(proto, "copy", Copy); // for backwards compatibility - proto->ForceSet(env->offset_string(), - Uint32::New(env->isolate(), 0), - v8::ReadOnly); + if (using_old_buffer) { + proto->ForceSet(env->offset_string(), + Uint32::New(env->isolate(), 0), + v8::ReadOnly); + } } @@ -782,7 +1026,10 @@ void Initialize(Handle<Object> target, Environment* env = Environment::GetCurrent(context); env->SetMethod(target, "setupBufferJS", SetupBufferJS); + env->SetMethod(target, "create", Create); + env->SetMethod(target, "createFromString", CreateFromString); + env->SetMethod(target, "slice", Slice); env->SetMethod(target, "byteLengthUtf8", ByteLengthUtf8); env->SetMethod(target, "compare", Compare); env->SetMethod(target, "fill", Fill); |