Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/node_buffer.cc')
-rw-r--r--src/node_buffer.cc395
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);