buffer: construct Uint8Array in JS
authorTrevor Norris <trev.norris@gmail.com>
Mon, 14 Sep 2015 22:31:10 +0000 (16:31 -0600)
committerJeremiah Senkpiel <fishrock123@rocketmail.com>
Wed, 16 Sep 2015 15:41:00 +0000 (11:41 -0400)
Overall construction time of Typed Arrays is faster in JS, but the
problem with using it normally is zero-fill of memory. Get around this
by using a flag in the ArrayBuffer::Allocator to trigger when memory
should or shouldn't be zero-filled.

Remove Buffer::Create() as it is no longer called.

The creation of the Uint8Array() was done at each callsite because at
the time of this patch there was a performance penalty for centralizing
the call in a single function.

PR-URL: https://github.com/nodejs/node/pull/2866
Reviewed-By: Fedor Indutny <fedor@indutny.com>
lib/buffer.js
src/env-inl.h
src/env.h
src/node.cc
src/node_buffer.cc
src/node_internals.h

index 37ed0f9..26b51a8 100644 (file)
@@ -3,6 +3,7 @@
 
 const binding = process.binding('buffer');
 const internalUtil = require('internal/util');
+const bindingObj = {};
 
 exports.Buffer = Buffer;
 exports.SlowBuffer = SlowBuffer;
@@ -14,11 +15,19 @@ Buffer.poolSize = 8 * 1024;
 var poolSize, poolOffset, allocPool;
 
 
+binding.setupBufferJS(Buffer.prototype, bindingObj);
+const flags = bindingObj.flags;
+const kNoZeroFill = 0;
+
+
 function createPool() {
   poolSize = Buffer.poolSize;
-  allocPool = binding.create(poolSize);
+  flags[kNoZeroFill] = 1;
+  allocPool = new Uint8Array(poolSize);
+  Object.setPrototypeOf(allocPool, Buffer.prototype);
   poolOffset = 0;
 }
+createPool();
 
 
 function alignPool() {
@@ -46,22 +55,20 @@ function Buffer(arg) {
 
   // Unusual.
   return fromObject(arg);
-};
+}
 
 Buffer.prototype.__proto__ = Uint8Array.prototype;
 Buffer.__proto__ = Uint8Array;
 
 
-binding.setupBufferJS(Buffer.prototype);
-// Buffer prototype must be past before creating our first pool.
-createPool();
-
-
 function SlowBuffer(length) {
   if (+length != length)
     length = 0;
-  return binding.create(+length);
-};
+  flags[kNoZeroFill] = 1;
+  const ui8 = new Uint8Array(+length);
+  Object.setPrototypeOf(ui8, Buffer.prototype);
+  return ui8;
+}
 
 SlowBuffer.prototype.__proto__ = Buffer.prototype;
 SlowBuffer.__proto__ = Buffer;
@@ -69,7 +76,7 @@ SlowBuffer.__proto__ = Buffer;
 
 function allocate(size) {
   if (size === 0)
-    return binding.create(0);
+    return SlowBuffer(0);
   if (size < (Buffer.poolSize >>> 1)) {
     if (size > (poolSize - poolOffset))
       createPool();
@@ -78,7 +85,10 @@ function allocate(size) {
     alignPool();
     return b;
   } else {
-    return binding.create(size);
+    flags[kNoZeroFill] = 1;
+    const ui8 = new Uint8Array(size);
+    Object.setPrototypeOf(ui8, Buffer.prototype);
+    return ui8;
   }
 }
 
index cbc8c4f..cc9dd9e 100644 (file)
@@ -131,6 +131,27 @@ inline void Environment::TickInfo::set_last_threw(bool value) {
   last_threw_ = value;
 }
 
+inline Environment::ArrayBufferAllocatorInfo::ArrayBufferAllocatorInfo() {
+  for (int i = 0; i < kFieldsCount; ++i)
+    fields_[i] = 0;
+}
+
+inline uint32_t* Environment::ArrayBufferAllocatorInfo::fields() {
+  return fields_;
+}
+
+inline int Environment::ArrayBufferAllocatorInfo::fields_count() const {
+  return kFieldsCount;
+}
+
+inline bool Environment::ArrayBufferAllocatorInfo::no_zero_fill() const {
+  return fields_[kNoZeroFill] != 0;
+}
+
+inline void Environment::ArrayBufferAllocatorInfo::reset_fill_flag() {
+  fields_[kNoZeroFill] = 0;
+}
+
 inline Environment* Environment::New(v8::Local<v8::Context> context,
                                      uv_loop_t* loop) {
   Environment* env = new Environment(context, loop);
@@ -290,6 +311,11 @@ inline Environment::TickInfo* Environment::tick_info() {
   return &tick_info_;
 }
 
+inline Environment::ArrayBufferAllocatorInfo*
+    Environment::array_buffer_allocator_info() {
+  return &array_buffer_allocator_info_;
+}
+
 inline uint64_t Environment::timer_base() const {
   return timer_base_;
 }
index ce972d5..1107dcb 100644 (file)
--- a/src/env.h
+++ b/src/env.h
@@ -339,6 +339,27 @@ class Environment {
     DISALLOW_COPY_AND_ASSIGN(TickInfo);
   };
 
+  class ArrayBufferAllocatorInfo {
+   public:
+    inline uint32_t* fields();
+    inline int fields_count() const;
+    inline bool no_zero_fill() const;
+    inline void reset_fill_flag();
+
+   private:
+    friend class Environment;  // So we can call the constructor.
+    inline ArrayBufferAllocatorInfo();
+
+    enum Fields {
+      kNoZeroFill,
+      kFieldsCount
+    };
+
+    uint32_t fields_[kFieldsCount];
+
+    DISALLOW_COPY_AND_ASSIGN(ArrayBufferAllocatorInfo);
+  };
+
   typedef void (*HandleCleanupCb)(Environment* env,
                                   uv_handle_t* handle,
                                   void* arg);
@@ -401,6 +422,7 @@ class Environment {
   inline AsyncHooks* async_hooks();
   inline DomainFlag* domain_flag();
   inline TickInfo* tick_info();
+  inline ArrayBufferAllocatorInfo* array_buffer_allocator_info();
   inline uint64_t timer_base() const;
 
   static inline Environment* from_cares_timer_handle(uv_timer_t* handle);
@@ -510,6 +532,7 @@ class Environment {
   AsyncHooks async_hooks_;
   DomainFlag domain_flag_;
   TickInfo tick_info_;
+  ArrayBufferAllocatorInfo array_buffer_allocator_info_;
   const uint64_t timer_base_;
   uv_timer_t cares_timer_handle_;
   ares_channel cares_channel_;
index a6f66d4..b0ba02a 100644 (file)
@@ -894,6 +894,14 @@ Local<Value> WinapiErrnoException(Isolate* isolate,
 #endif
 
 
+void* ArrayBufferAllocator::Allocate(size_t size) {
+  if (env_ == nullptr || !env_->array_buffer_allocator_info()->no_zero_fill())
+    return calloc(size, 1);
+  env_->array_buffer_allocator_info()->reset_fill_flag();
+  return malloc(size);
+}
+
+
 void SetupDomainUse(const FunctionCallbackInfo<Value>& args) {
   Environment* env = Environment::GetCurrent(args);
 
@@ -3879,8 +3887,8 @@ Environment* CreateEnvironment(Isolate* isolate,
 static void StartNodeInstance(void* arg) {
   NodeInstanceData* instance_data = static_cast<NodeInstanceData*>(arg);
   Isolate::CreateParams params;
-  ArrayBufferAllocator array_buffer_allocator;
-  params.array_buffer_allocator = &array_buffer_allocator;
+  ArrayBufferAllocator* array_buffer_allocator = new ArrayBufferAllocator();
+  params.array_buffer_allocator = array_buffer_allocator;
   Isolate* isolate = Isolate::New(params);
   if (track_heap_objects) {
     isolate->GetHeapProfiler()->StartTrackingHeapObjects(true);
@@ -3896,6 +3904,7 @@ static void StartNodeInstance(void* arg) {
     HandleScope handle_scope(isolate);
     Local<Context> context = Context::New(isolate);
     Environment* env = CreateEnvironment(isolate, context, instance_data);
+    array_buffer_allocator->set_env(env);
     Context::Scope context_scope(context);
     if (instance_data->is_main())
       env->set_using_abort_on_uncaught_exc(abort_on_uncaught_exception);
@@ -3942,6 +3951,7 @@ static void StartNodeInstance(void* arg) {
     __lsan_do_leak_check();
 #endif
 
+    array_buffer_allocator->set_env(nullptr);
     env->Dispose();
     env = nullptr;
   }
@@ -3949,6 +3959,7 @@ static void StartNodeInstance(void* arg) {
   CHECK_NE(isolate, nullptr);
   isolate->Dispose();
   isolate = nullptr;
+  delete array_buffer_allocator;
   if (instance_data->is_main())
     node_isolate = nullptr;
 }
index dc0c742..5e0de52 100644 (file)
@@ -68,6 +68,7 @@ using v8::Object;
 using v8::Persistent;
 using v8::String;
 using v8::Uint32;
+using v8::Uint32Array;
 using v8::Uint8Array;
 using v8::Value;
 using v8::WeakCallbackData;
@@ -392,43 +393,6 @@ MaybeLocal<Object> New(Environment* env, char* data, size_t length) {
 }
 
 
-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 (length < 0 || length > kMaxLength) {
-    return env->ThrowRangeError("invalid Buffer length");
-  }
-
-  void* data;
-  if (length > 0) {
-    data = malloc(length);
-    if (data == nullptr) {
-      return env->ThrowRangeError(
-          "Buffer allocation failed - process out of memory");
-    }
-  } else {
-    data = nullptr;
-  }
-
-  Local<ArrayBuffer> ab =
-      ArrayBuffer::New(isolate,
-                       data,
-                       length,
-                       ArrayBufferCreationMode::kInternalized);
-  Local<Uint8Array> ui = Uint8Array::New(ab, 0, length);
-  Maybe<bool> mb =
-      ui->SetPrototype(env->context(), env->buffer_prototype_object());
-  if (!mb.FromMaybe(false))
-    return env->ThrowError("Unable to set Object prototype");
-  args.GetReturnValue().Set(ui);
-}
-
-
 void CreateFromString(const FunctionCallbackInfo<Value>& args) {
   CHECK(args[0]->IsString());
   CHECK(args[1]->IsString());
@@ -966,6 +930,19 @@ void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
   env->SetMethod(proto, "utf8Write", Utf8Write);
 
   env->SetMethod(proto, "copy", Copy);
+
+  CHECK(args[1]->IsObject());
+  Local<Object> bObj = args[1].As<Object>();
+
+  uint32_t* const fields = env->array_buffer_allocator_info()->fields();
+  uint32_t const fields_count =
+      env->array_buffer_allocator_info()->fields_count();
+
+  Local<ArrayBuffer> array_buffer =
+      ArrayBuffer::New(env->isolate(), fields, sizeof(*fields) * fields_count);
+
+  bObj->Set(String::NewFromUtf8(env->isolate(), "flags"),
+            Uint32Array::New(array_buffer, 0, fields_count));
 }
 
 
@@ -975,7 +952,6 @@ void Initialize(Local<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, "createFromArrayBuffer", CreateFromArrayBuffer);
 
index f4d8c1b..5cc439c 100644 (file)
@@ -228,18 +228,18 @@ NODE_DEPRECATED("Use ThrowUVException(isolate)",
   return ThrowUVException(isolate, errorno, syscall, message, path);
 })
 
-struct ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
-  virtual void* Allocate(size_t size) {
-    return calloc(size, 1);
-  }
+class ArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
+ public:
+  ArrayBufferAllocator() { }
 
-  virtual void* AllocateUninitialized(size_t size) {
-    return malloc(size);
-  }
+  inline void set_env(Environment* env) { env_ = env; }
 
-  virtual void Free(void* data, size_t) {
-    free(data);
-  }
+  virtual void* Allocate(size_t size);  // Defined in src/node.cc
+  virtual void* AllocateUninitialized(size_t size) { return malloc(size); }
+  virtual void Free(void* data, size_t) { free(data); }
+
+ private:
+  Environment* env_;
 };
 
 enum NodeInstanceType { MAIN, WORKER };