
#ifndef SRC_NODE_UNION_BYTES_H_
#define SRC_NODE_UNION_BYTES_H_

#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

// A union of const uint8_t* or const uint16_t* data that can be
// turned into external v8::String when given an isolate.

#include "env.h"
#include "v8.h"

namespace node {

class NonOwningExternalOneByteResource
    : public v8::String::ExternalOneByteStringResource {
 public:
  explicit NonOwningExternalOneByteResource(const uint8_t* data, size_t length)
      : data_(data), length_(length) {}
  ~NonOwningExternalOneByteResource() override = default;

  const char* data() const override {
    return reinterpret_cast<const char*>(data_);
  }
  size_t length() const override { return length_; }

 private:
  const uint8_t* data_;
  size_t length_;
  DISALLOW_COPY_AND_ASSIGN(NonOwningExternalOneByteResource);
};

class NonOwningExternalTwoByteResource
    : public v8::String::ExternalStringResource {
 public:
  explicit NonOwningExternalTwoByteResource(const uint16_t* data, size_t length)
      : data_(data), length_(length) {}
  ~NonOwningExternalTwoByteResource() override = default;

  const uint16_t* data() const override { return data_; }
  size_t length() const override { return length_; }

 private:
  const uint16_t* data_;
  size_t length_;
  DISALLOW_COPY_AND_ASSIGN(NonOwningExternalTwoByteResource);
};

// Similar to a v8::String, but it's independent from Isolates
// and can be materialized in Isolates as external Strings
// via ToStringChecked. The data pointers are owned by the caller.
class UnionBytes {
 public:
  UnionBytes(const uint16_t* data, size_t length)
      : is_one_byte_(false), two_bytes_(data), length_(length) {}
  UnionBytes(const uint8_t* data, size_t length)
      : is_one_byte_(true), one_bytes_(data), length_(length) {}
  bool is_one_byte() const { return is_one_byte_; }
  const uint16_t* two_bytes_data() const {
    CHECK(!is_one_byte_);
    return two_bytes_;
  }
  const uint8_t* one_bytes_data() const {
    CHECK(is_one_byte_);
    return one_bytes_;
  }
  v8::Local<v8::String> ToStringChecked(v8::Isolate* isolate) const {
    if (is_one_byte_) {
      NonOwningExternalOneByteResource* source =
          new NonOwningExternalOneByteResource(one_bytes_, length_);
      return v8::String::NewExternalOneByte(isolate, source).ToLocalChecked();
    } else {
      NonOwningExternalTwoByteResource* source =
          new NonOwningExternalTwoByteResource(two_bytes_, length_);
      return v8::String::NewExternalTwoByte(isolate, source).ToLocalChecked();
    }
  }
  size_t length() { return length_; }

 private:
  bool is_one_byte_;
  union {
    const uint8_t* one_bytes_;
    const uint16_t* two_bytes_;
  };
  size_t length_;
};

}  // namespace node

#endif  // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

#endif  // SRC_NODE_UNION_BYTES_H_
