From 33e7f6e9535bffe999d7d98de08150bfcd949787 Mon Sep 17 00:00:00 2001
From: Refael Ackermann <refack@gmail.com>
Date: Sun, 21 Oct 2018 14:11:50 -0400
Subject: [PATCH] src: add AliasedBuffer::reserve

refactor grow_async_ids_stack

PR-URL: https://github.com/nodejs/node/pull/23808
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Matheus Marchini <mat@mmarchini.me>
---
 src/aliased_buffer.h | 48 +++++++++++++++++++++++++++++++++++++++-----
 src/env.cc           |  9 +--------
 2 files changed, 44 insertions(+), 13 deletions(-)

diff --git a/src/aliased_buffer.h b/src/aliased_buffer.h
index 2642b2b2a744f0..cdfd90765fcd86 100644
--- a/src/aliased_buffer.h
+++ b/src/aliased_buffer.h
@@ -4,7 +4,7 @@
 #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
 
 #include "v8.h"
-#include "util-inl.h"
+#include "util.h"
 
 namespace node {
 
@@ -22,7 +22,9 @@ namespace node {
  * The encapsulation herein provides a placeholder where such writes can be
  * observed. Any notification APIs will be left as a future exercise.
  */
-template <class NativeT, class V8T>
+template <class NativeT, class V8T,
+          // SFINAE NativeT to be scalar
+          typename = std::enable_if_t<std::is_scalar<NativeT>::value>>
 class AliasedBuffer {
  public:
   AliasedBuffer(v8::Isolate* isolate, const size_t count)
@@ -33,14 +35,14 @@ class AliasedBuffer {
     CHECK_GT(count, 0);
     const v8::HandleScope handle_scope(isolate_);
 
-    const size_t sizeInBytes = sizeof(NativeT) * count;
+    const size_t size_in_bytes = sizeof(NativeT) * count;
 
     // allocate native buffer
     buffer_ = Calloc<NativeT>(count);
 
     // allocate v8 ArrayBuffer
     v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
-        isolate_, buffer_, sizeInBytes);
+        isolate_, buffer_, size_in_bytes);
 
     // allocate v8 TypedArray
     v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, count);
@@ -55,6 +57,7 @@ class AliasedBuffer {
    *
    *  Note that byte_offset must by aligned by sizeof(NativeT).
    */
+  // TODO(refack): refactor into a non-owning `AliasedBufferView`
   AliasedBuffer(v8::Isolate* isolate,
                 const size_t byte_offset,
                 const size_t count,
@@ -96,7 +99,7 @@ class AliasedBuffer {
     js_array_.Reset();
   }
 
-  AliasedBuffer& operator=(AliasedBuffer&& that) {
+  AliasedBuffer& operator=(AliasedBuffer&& that) noexcept {
     this->~AliasedBuffer();
     isolate_ = that.isolate_;
     count_ = that.count_;
@@ -226,6 +229,41 @@ class AliasedBuffer {
     return count_;
   }
 
+  // Should only be used to extend the array.
+  // Should only be used on an owning array, not one created as a sub array of
+  // an owning `AliasedBuffer`.
+  void reserve(size_t new_capacity) {
+#if defined(DEBUG) && DEBUG
+    CHECK_GE(new_capacity, count_);
+    CHECK_EQ(byte_offset_, 0);
+    CHECK(free_buffer_);
+#endif
+    const v8::HandleScope handle_scope(isolate_);
+
+    const size_t old_size_in_bytes = sizeof(NativeT) * count_;
+    const size_t new_size_in_bytes = sizeof(NativeT) * new_capacity;
+
+    // allocate new native buffer
+    NativeT* new_buffer = Calloc<NativeT>(new_capacity);
+    // copy old content
+    memcpy(new_buffer, buffer_, old_size_in_bytes);
+
+    // allocate v8 new ArrayBuffer
+    v8::Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(
+        isolate_, new_buffer, new_size_in_bytes);
+
+    // allocate v8 TypedArray
+    v8::Local<V8T> js_array = V8T::New(ab, byte_offset_, new_capacity);
+
+    // move over old v8 TypedArray
+    js_array_ = std::move(v8::Global<V8T>(isolate_, js_array));
+
+    // Free old buffer and set new values
+    free(buffer_);
+    buffer_ = new_buffer;
+    count_ = new_capacity;
+  }
+
  private:
   v8::Isolate* isolate_;
   size_t count_;
diff --git a/src/env.cc b/src/env.cc
index a1b24a2f15b9fe..4fdfcafaa142b6 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -828,14 +828,7 @@ void Environment::CollectUVExceptionInfo(v8::Local<v8::Value> object,
 
 
 void Environment::AsyncHooks::grow_async_ids_stack() {
-  const uint32_t old_capacity = async_ids_stack_.Length() / 2;
-  const uint32_t new_capacity = old_capacity * 1.5;
-  AliasedBuffer<double, v8::Float64Array> new_buffer(
-      env()->isolate(), new_capacity * 2);
-
-  for (uint32_t i = 0; i < old_capacity * 2; ++i)
-    new_buffer[i] = async_ids_stack_[i];
-  async_ids_stack_ = std::move(new_buffer);
+  async_ids_stack_.reserve(async_ids_stack_.Length() * 3);
 
   env()->async_hooks_binding()->Set(
       env()->context(),