text: /*
   * Authored by Alex Hultman, 2018-2021.
   * Intellectual property of third-party.
  
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
  
   *     http://www.apache.org/licenses/LICENSE-2.0
  
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #ifndef ADDON_UTILITIES_H
  #define ADDON_UTILITIES_H
  
  #include <v8.h>
  using namespace v8;
  
  /* Unfortunately we _have_ to depend on Node.js crap */
  #include <node.h>
  
  MaybeLocal<Value> CallJS(Isolate *isolate, Local<Function> f, int argc, Local<Value> *argv) {
      /* Slow path */
      return node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), f, argc, argv, {0, 0});
  }
  
  Local<v8::ArrayBuffer> ArrayBuffer_New(Isolate *isolate, void *data, size_t length) {
      std::unique_ptr<BackingStore> backingStore = ArrayBuffer::NewBackingStore(data, length, [](void* data, size_t length, void* deleter_data) {}, nullptr);
      return ArrayBuffer::New(isolate, std::shared_ptr<BackingStore>(backingStore.release()));
  }
  
  Local<v8::ArrayBuffer> ArrayBuffer_NewCopy(Isolate *isolate, void *data, size_t length) {
      Local<ArrayBuffer> ab = ArrayBuffer::New(isolate, length);
      memcpy(ab->GetBackingStore()->Data(), data, length);
      return ab;
  }
  
  struct PerSocketData {
      UniquePersistent<Object> socketPf;
  };
  
  struct PerContextData {
      Isolate *isolate;
      UniquePersistent<Object> reqTemplate;
      UniquePersistent<Object> resTemplate[3];
      UniquePersistent<Object> wsTemplate[2];
  
      /* We hold all apps until free */
      std::vector<std::unique_ptr<uWS::App>> apps;
      std::vector<std::unique_ptr<uWS::SSLApp>> sslApps;
  };
  
  template <class APP>
  static constexpr int getAppTypeIndex() {
      /* Returns 1 for SSLApp and 0 for App */
      //return std::is_same<APP, uWS::SSLApp>::value;
  
      /* Returns 2 for H3App */
  
      if constexpr (std::is_same<APP, uWS::App>::value) {
          return 0;
      } else if constexpr (std::is_same<APP, uWS::SSLApp>::value) {
          return 1;
      } else if constexpr (std::is_same<APP, uWS::H3App>::value) {
          return 2;
      } else {
          // why does this fail?
          //static_assert(false);
      }
  }
  
  static inline bool missingArguments(int length, const FunctionCallbackInfo<Value> &args) {
      if (args.Length() < length) {
          std::string message = "Function requires at least ";
          message += std::to_string(length);
          message += " arguments.";
          args.GetReturnValue().Set(args.GetIsolate()->ThrowException(v8::Exception::Error(String::NewFromUtf8(args.GetIsolate(), message.c_str(), NewStringType::kNormal).ToLocalChecked())));
          return true;
      }
      return false;
  }
  
  struct Callback {
      bool invalid = false;
      UniquePersistent<Function> f;
      Callback(Isolate *isolate, const Local<Value> &value) {
  
          if (!value->IsFunction()) {
              invalid = true;
              return;
          }
  
          f.Reset(isolate, Local<Function>::Cast(value));
      }
  
      bool isInvalid(const FunctionCallbackInfo<Value> &args) {
          if (invalid) {
              args.GetReturnValue().Set(args.GetIsolate()->ThrowException(v8::Exception::Error(String::NewFromUtf8(args.GetIsolate(), "Passed callback is not a valid function.", NewStringType::kNormal).ToLocalChecked())));
          }
          return invalid;
      }
  
      UniquePersistent<Function> &&getFunction() {
          return std::move(f);
      }
  };
  
  class NativeString {
      char *data;
      size_t length;
      char utf8ValueMemory[sizeof(String::Utf8Value)];
      String::Utf8Value *utf8Value = nullptr;
      bool invalid = false;
  public:
      NativeString(Isolate *isolate, const Local<Value> &value) {
          if (value->IsUndefined()) {
              data = nullptr;
              length = 0;
          } else if (value->IsString()) {
              utf8Value = new (utf8ValueMemory) String::Utf8Value(isolate, value);
              data = (**utf8Value);
              length = utf8Value->length();
          } else if (value->IsTypedArray()) {
              Local<ArrayBufferView> arrayBufferView = Local<ArrayBufferView>::Cast(value);
              auto contents = arrayBufferView->Buffer()->GetBackingStore();
              length = arrayBufferView->ByteLength();
              data = (char *) contents->Data() + arrayBufferView->ByteOffset();
          } else if (value->IsArrayBuffer()) {
              Local<ArrayBuffer> arrayBuffer = Local<ArrayBuffer>::Cast(value);
              auto contents = arrayBuffer->GetBackingStore();
              length = contents->ByteLength();
              data = (char *) contents->Data();
          } else if (value->IsSharedArrayBuffer()) {
              Local<SharedArrayBuffer> arrayBuffer = Local<SharedArrayBuffer>::Cast(value);
              auto contents = arrayBuffer->GetBackingStore();
              length = contents->ByteLength();
              data = (char *) contents->Data();
          } else {
              invalid = true;
          }
      }
  
      bool isInvalid(const FunctionCallbackInfo<Value> &args) {
          if (invalid) {
              args.GetReturnValue().Set(args.GetIsolate()->ThrowException(v8::Exception::Error(String::NewFromUtf8(args.GetIsolate(), "Text and data can only be passed by String, ArrayBuffer or TypedArray.", NewStringType::kNormal).ToLocalChecked())));
          }
          return invalid;
      }
  
      std::string_view getString() {
          return {data, length};
      }
  
      ~NativeString() {
          if (utf8Value) {
              utf8Value->~Utf8Value();
          }
      }
  };
  
  #endif
  