Program Listing for File string.h

Program Listing for File string.h#

Return to documentation for file (tvm/ffi/string.h)

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 TVM_FFI_STRING_H_
#define TVM_FFI_STRING_H_

#include <tvm/ffi/base_details.h>
#include <tvm/ffi/error.h>
#include <tvm/ffi/memory.h>
#include <tvm/ffi/object.h>
#include <tvm/ffi/type_traits.h>

#include <cstddef>
#include <cstring>
#include <initializer_list>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>

// Note: We place string in tvm/ffi instead of tvm/ffi/container
// because string itself needs special handling and is an inherent
// core component for return string handling.
// The following dependency relation holds
// any -> string -> object

namespace tvm {
namespace ffi {
namespace details {
class BytesObjBase : public Object, public TVMFFIByteArray {};

class BytesObj : public BytesObjBase {
 public:
  static constexpr const uint32_t _type_index = TypeIndex::kTVMFFIBytes;
  static const constexpr bool _type_final = true;
  TVM_FFI_DECLARE_OBJECT_INFO_STATIC(StaticTypeKey::kTVMFFIBytes, BytesObj, Object);
};

class StringObj : public BytesObjBase {
 public:
  static constexpr const uint32_t _type_index = TypeIndex::kTVMFFIStr;
  static const constexpr bool _type_final = true;
  TVM_FFI_DECLARE_OBJECT_INFO_STATIC(StaticTypeKey::kTVMFFIStr, StringObj, Object);
};

// String moved from std::string
// without having to trigger a copy
template <typename Base>
class BytesObjStdImpl : public Base {
 public:
  explicit BytesObjStdImpl(std::string other) : data_{other} {
    this->data = data_.data();
    this->size = data_.size();
  }

 private:
  std::string data_;
};

class BytesBaseCell {
 public:
  BytesBaseCell() {
    // initialize to none
    data_.type_index = TypeIndex::kTVMFFINone;
    data_.zero_padding = 0;
    data_.v_int64 = 0;
  }

  explicit BytesBaseCell(std::nullopt_t) {
    data_.type_index = TypeIndex::kTVMFFINone;
    data_.zero_padding = 0;
    data_.v_int64 = 0;
  }

  BytesBaseCell(const BytesBaseCell& other) : data_(other.data_) {  // NOLINT(*)
    if (data_.type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
      details::ObjectUnsafe::IncRefObjectHandle(data_.v_obj);
    }
  }

  BytesBaseCell(BytesBaseCell&& other) : data_(other.data_) {  // NOLINT(*)
    other.data_.type_index = TypeIndex::kTVMFFINone;
  }

  BytesBaseCell& operator=(const BytesBaseCell& other) {
    BytesBaseCell(other).swap(*this);  // NOLINT(*)
    return *this;
  }

  BytesBaseCell& operator=(BytesBaseCell&& other) {
    BytesBaseCell(std::move(other)).swap(*this);  // NOLINT(*)
    return *this;
  }

  ~BytesBaseCell() {
    if (data_.type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
      details::ObjectUnsafe::DecRefObjectHandle(data_.v_obj);
    }
  }

  bool operator==(std::nullopt_t) const { return data_.type_index == TypeIndex::kTVMFFINone; }

  bool operator!=(std::nullopt_t) const { return data_.type_index != TypeIndex::kTVMFFINone; }

  void swap(BytesBaseCell& other) {  // NOLINT(*)
    std::swap(data_, other.data_);
  }

  const char* data() const noexcept {
    if (data_.type_index < TypeIndex::kTVMFFIStaticObjectBegin) {
      return data_.v_bytes;
    } else {
      return TVMFFIBytesGetByteArrayPtr(data_.v_obj)->data;
    }
  }

  size_t size() const noexcept {
    if (data_.type_index < TypeIndex::kTVMFFIStaticObjectBegin) {
      return data_.small_str_len;
    } else {
      return TVMFFIBytesGetByteArrayPtr(data_.v_obj)->size;
    }
  }

  template <typename LargeObj>
  void InitFromStd(std::string&& other, int32_t large_type_index) {
    // needs to be reset to none first for exception safety
    data_.type_index = TypeIndex::kTVMFFINone;
    data_.zero_padding = 0;
    TVM_FFI_CLEAR_PTR_PADDING_IN_FFI_ANY(&data_);
    ObjectPtr<LargeObj> ptr = make_object<BytesObjStdImpl<LargeObj>>(std::move(other));
    data_.v_obj = details::ObjectUnsafe::MoveObjectPtrToTVMFFIObjectPtr(std::move(ptr));
    data_.type_index = large_type_index;
  }

  template <typename LargeObj>
  char* InitSpaceForSize(size_t size, int32_t small_type_index, int32_t large_type_index) {
    size_t kMaxSmallBytesLen = sizeof(int64_t) - 1;
    // first zero the content, this is important for exception safety
    data_.type_index = small_type_index;
    data_.zero_padding = 0;
    if (size <= kMaxSmallBytesLen) {
      // set up the size accordingly
      data_.small_str_len = static_cast<uint32_t>(size);
      return data_.v_bytes;
    } else {
      // allocate from heap
      ObjectPtr<LargeObj> ptr = make_inplace_array_object<LargeObj, char>(size + 1);
      char* dest_data = reinterpret_cast<char*>(ptr.get()) + sizeof(LargeObj);
      ptr->data = dest_data;
      ptr->size = size;
      TVM_FFI_CLEAR_PTR_PADDING_IN_FFI_ANY(&data_);
      data_.v_obj = details::ObjectUnsafe::MoveObjectPtrToTVMFFIObjectPtr(std::move(ptr));
      // now reset the type index to str
      data_.type_index = large_type_index;
      return dest_data;
    }
  }

  void InitTypeIndex(int32_t type_index) { data_.type_index = type_index; }

  void MoveToAny(TVMFFIAny* result) {
    *result = data_;
    data_.type_index = TypeIndex::kTVMFFINone;
    data_.zero_padding = 0;
    data_.v_int64 = 0;
  }

  TVMFFIAny CopyToTVMFFIAny() const { return data_; }

  static BytesBaseCell CopyFromAnyView(const TVMFFIAny* src) {
    BytesBaseCell result(*src);
    if (result.data_.type_index >= TypeIndex::kTVMFFIStaticObjectBegin) {
      details::ObjectUnsafe::IncRefObjectHandle(result.data_.v_obj);
    }
    return result;
  }

  static BytesBaseCell MoveFromAny(TVMFFIAny* src) {
    BytesBaseCell result(*src);
    src->type_index = TypeIndex::kTVMFFINone;
    src->zero_padding = 0;
    src->v_int64 = 0;
    return result;
  }

 private:
  explicit BytesBaseCell(TVMFFIAny data) : data_(data) {}
  TVMFFIAny data_;
};
}  // namespace details

class Bytes {
 public:
  Bytes() { data_.InitTypeIndex(TypeIndex::kTVMFFISmallBytes); }
  Bytes(const char* data, size_t size) { this->InitData(data, size); }
  Bytes(TVMFFIByteArray bytes) {  // NOLINT(*)
    this->InitData(bytes.data, bytes.size);
  }
  Bytes(const std::string& other) {  // NOLINT(*)
    this->InitData(other.data(), other.size());
  }
  Bytes(std::string&& other) {  // NOLINT(*)
    data_.InitFromStd<details::BytesObj>(std::move(other), TypeIndex::kTVMFFIBytes);
  }
  void swap(Bytes& other) {  // NOLINT(*)
    std::swap(data_, other.data_);
  }

  template <typename T>
  Bytes& operator=(T&& other) {
    // copy-and-swap idiom
    Bytes(std::forward<T>(other)).swap(*this);  // NOLINT(*)
    return *this;
  }
  size_t size() const { return data_.size(); }
  const char* data() const { return data_.data(); }
  operator std::string() const { return std::string{data(), size()}; }

  static int memncmp(const char* lhs, const char* rhs, size_t lhs_count, size_t rhs_count) {
    if (lhs == rhs && lhs_count == rhs_count) return 0;

    for (size_t i = 0; i < lhs_count && i < rhs_count; ++i) {
      if (lhs[i] < rhs[i]) return -1;
      if (lhs[i] > rhs[i]) return 1;
    }
    if (lhs_count < rhs_count) {
      return -1;
    } else if (lhs_count > rhs_count) {
      return 1;
    } else {
      return 0;
    }
  }
  static bool memequal(const void* lhs, const void* rhs, size_t lhs_count, size_t rhs_count) {
    return lhs_count == rhs_count && (lhs == rhs || std::memcmp(lhs, rhs, lhs_count) == 0);
  }

 private:
  template <typename, typename>
  friend struct TypeTraits;
  template <typename, typename>
  friend class Optional;
  // internal backing cell
  details::BytesBaseCell data_;
  // create a new String from TVMFFIAny, must keep private
  explicit Bytes(details::BytesBaseCell data) : data_(data) {}
  char* InitSpaceForSize(size_t size) {
    return data_.InitSpaceForSize<details::BytesObj>(size, TypeIndex::kTVMFFISmallBytes,
                                                     TypeIndex::kTVMFFIBytes);
  }
  void InitData(const char* data, size_t size) {
    char* dest_data = InitSpaceForSize(size);
    std::memcpy(dest_data, data, size);
    // mainly to be compat with string
    dest_data[size] = '\0';
  }
};

class String {
 public:
  String(std::nullptr_t) = delete;  // NOLINT(*)
  String() { data_.InitTypeIndex(TypeIndex::kTVMFFISmallStr); }
  // constructors from Any
  String(const String& other) = default;  // NOLINT(*)
  String(String&& other) = default;  // NOLINT(*)
  String& operator=(const String& other) = default;  // NOLINT(*)
  String& operator=(String&& other) = default;  // NOLINT(*)

  void swap(String& other) noexcept {  // NOLINT(*)
    std::swap(data_, other.data_);
  }

  String& operator=(const std::string& other) {
    String(other).swap(*this);  // NOLINT(*)
    return *this;
  }
  String& operator=(std::string&& other) {
    String(std::move(other)).swap(*this);  // NOLINT(*)
    return *this;
  }

  String& operator=(const char* other) {
    String(other).swap(*this);  // NOLINT(*)
    return *this;
  }

  String(const char* data, size_t size) { this->InitData(data, size); }

  String(const char* other) {  // NOLINT(*)
    this->InitData(other, std::char_traits<char>::length(other));
  }
  String(const std::string& other) {  // NOLINT(*)
    this->InitData(other.data(), other.size());
  }

  String(std::string&& other) {  // NOLINT(*)
    // exception safety, first set to none so if exception is thrown
    // destructor works correctly
    data_.InitFromStd<details::StringObj>(std::move(other), TypeIndex::kTVMFFIStr);
  }

  explicit String(TVMFFIByteArray other) { this->InitData(other.data, other.size); }

  const char* data() const noexcept { return data_.data(); }

  const char* c_str() const noexcept { return data(); }

  size_t size() const noexcept { return data_.size(); }

  int compare(const String& other) const {
    return Bytes::memncmp(data(), other.data(), size(), other.size());
  }

  int compare(const std::string& other) const {
    return Bytes::memncmp(data(), other.data(), size(), other.size());
  }

  int compare(const char* other) const {
    const char* this_data = data();
    size_t this_size = size();
    for (size_t i = 0; i < this_size; ++i) {
      // other is shorter than this
      if (other[i] == '\0') return 1;
      if (this_data[i] < other[i]) return -1;
      if (this_data[i] > other[i]) return 1;
    }
    // other equals this
    if (other[this_size] == '\0') return 0;
    // other longer than this
    return -1;
  }

  int compare(const TVMFFIByteArray& other) const {
    return Bytes::memncmp(data(), other.data, size(), other.size);
  }

  size_t length() const { return size(); }

  bool empty() const { return size() == 0; }

  char at(size_t pos) const {
    if (pos < size()) {
      return data()[pos];
    } else {
      throw std::out_of_range("tvm::String index out of bounds");
    }
  }

  operator std::string() const { return std::string{data(), size()}; }

 private:
  template <typename, typename>
  friend struct TypeTraits;
  template <typename, typename>
  friend class Optional;
  // internal backing cell
  details::BytesBaseCell data_;
  // create a new String from TVMFFIAny, must keep private
  explicit String(details::BytesBaseCell data) : data_(data) {}
  char* InitSpaceForSize(size_t size) {
    return data_.InitSpaceForSize<details::StringObj>(size, TypeIndex::kTVMFFISmallStr,
                                                      TypeIndex::kTVMFFIStr);
  }
  void InitData(const char* data, size_t size) {
    char* dest_data = InitSpaceForSize(size);
    std::memcpy(dest_data, data, size);
    dest_data[size] = '\0';
  }
  static String Concat(const char* lhs, size_t lhs_size, const char* rhs, size_t rhs_size) {
    String ret;
    // disable stringop-overflow and restrict warnings
    // gcc may produce false positive when we enable dest_data returned from small string path
    // Because compiler is not able to detect the condition that the path is only triggered via
    // size < kMaxSmallStrLen and can report it as a overflow case.
#if (__GNUC__) && !(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#pragma GCC diagnostic ignored "-Wrestrict"
#endif
    char* dest_data = ret.InitSpaceForSize(lhs_size + rhs_size);
    std::memcpy(dest_data, lhs, lhs_size);
    std::memcpy(dest_data + lhs_size, rhs, rhs_size);
    dest_data[lhs_size + rhs_size] = '\0';
#if (__GNUC__) && !(__clang__)
#pragma GCC diagnostic pop
#endif
    return ret;
  }
  // Overload + operator
  friend String operator+(const String& lhs, const String& rhs);
  friend String operator+(const String& lhs, const std::string& rhs);
  friend String operator+(const std::string& lhs, const String& rhs);
  friend String operator+(const String& lhs, const char* rhs);
  friend String operator+(const char* lhs, const String& rhs);
};

TVM_FFI_INLINE std::string_view ToStringView(TVMFFIByteArray str) {
  return std::string_view(str.data, str.size);
}

template <>
inline constexpr bool use_default_type_traits_v<Bytes> = false;

// specialize to enable implicit conversion from TVMFFIByteArray*
template <>
struct TypeTraits<Bytes> : public TypeTraitsBase {
  // bytes can be union type of small bytes and object, so keep it as any
  static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIAny;

  TVM_FFI_INLINE static void CopyToAnyView(const Bytes& src, TVMFFIAny* result) {
    *result = src.data_.CopyToTVMFFIAny();
  }

  TVM_FFI_INLINE static void MoveToAny(Bytes src, TVMFFIAny* result) {
    src.data_.MoveToAny(result);
  }

  TVM_FFI_INLINE static bool CheckAnyStrict(const TVMFFIAny* src) {
    return src->type_index == TypeIndex::kTVMFFISmallBytes ||
           src->type_index == TypeIndex::kTVMFFIBytes;
  }

  TVM_FFI_INLINE static Bytes CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
    return Bytes(details::BytesBaseCell::CopyFromAnyView(src));
  }

  TVM_FFI_INLINE static Bytes MoveFromAnyAfterCheck(TVMFFIAny* src) {
    return Bytes(details::BytesBaseCell::MoveFromAny(src));
  }

  TVM_FFI_INLINE static std::optional<Bytes> TryCastFromAnyView(const TVMFFIAny* src) {
    if (src->type_index == TypeIndex::kTVMFFIByteArrayPtr) {
      return Bytes(*static_cast<TVMFFIByteArray*>(src->v_ptr));
    }
    if (src->type_index == TypeIndex::kTVMFFISmallBytes ||
        src->type_index == TypeIndex::kTVMFFIBytes) {
      return Bytes(details::BytesBaseCell::CopyFromAnyView(src));
    }
    return std::nullopt;
  }

  TVM_FFI_INLINE static std::string TypeStr() { return "bytes"; }
};

template <>
inline constexpr bool use_default_type_traits_v<String> = false;

// specialize to enable implicit conversion from const char*
template <>
struct TypeTraits<String> : public TypeTraitsBase {
  // string can be union type of small string and object, so keep it as any
  static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIAny;

  TVM_FFI_INLINE static void CopyToAnyView(const String& src, TVMFFIAny* result) {
    *result = src.data_.CopyToTVMFFIAny();
  }

  TVM_FFI_INLINE static void MoveToAny(String src, TVMFFIAny* result) {
    src.data_.MoveToAny(result);
  }

  TVM_FFI_INLINE static bool CheckAnyStrict(const TVMFFIAny* src) {
    return src->type_index == TypeIndex::kTVMFFISmallStr ||
           src->type_index == TypeIndex::kTVMFFIStr;
  }

  TVM_FFI_INLINE static String CopyFromAnyViewAfterCheck(const TVMFFIAny* src) {
    return String(details::BytesBaseCell::CopyFromAnyView(src));
  }

  TVM_FFI_INLINE static String MoveFromAnyAfterCheck(TVMFFIAny* src) {
    return String(details::BytesBaseCell::MoveFromAny(src));
  }

  TVM_FFI_INLINE static std::optional<String> TryCastFromAnyView(const TVMFFIAny* src) {
    if (src->type_index == TypeIndex::kTVMFFIRawStr) {
      return String(src->v_c_str);
    }
    if (src->type_index == TypeIndex::kTVMFFISmallStr || src->type_index == TypeIndex::kTVMFFIStr) {
      return String(details::BytesBaseCell::CopyFromAnyView(src));
    }
    return std::nullopt;
  }

  TVM_FFI_INLINE static std::string TypeStr() { return "str"; }
};

// const char*, requirement: not nullable, do not retain ownership
template <int N>
struct TypeTraits<char[N]> : public TypeTraitsBase {
  // NOTE: only enable implicit conversion into AnyView
  static constexpr bool storage_enabled = false;

  TVM_FFI_INLINE static void CopyToAnyView(const char src[N], TVMFFIAny* result) {
    result->type_index = TypeIndex::kTVMFFIRawStr;
    result->zero_padding = 0;
    result->v_c_str = src;
  }

  TVM_FFI_INLINE static void MoveToAny(const char src[N], TVMFFIAny* result) {
    // when we need to move to any, convert to owned object first
    TypeTraits<String>::MoveToAny(String(src), result);
  }
};

template <>
struct TypeTraits<const char*> : public TypeTraitsBase {
  static constexpr bool storage_enabled = false;

  TVM_FFI_INLINE static void CopyToAnyView(const char* src, TVMFFIAny* result) {
    TVM_FFI_ICHECK_NOTNULL(src);
    result->type_index = TypeIndex::kTVMFFIRawStr;
    result->zero_padding = 0;
    result->v_c_str = src;
  }

  TVM_FFI_INLINE static void MoveToAny(const char* src, TVMFFIAny* result) {
    // when we need to move to any, convert to owned object first
    TypeTraits<String>::MoveToAny(String(src), result);
  }
  // Do not allow const char* in a container, so we do not need CheckAnyStrict
  TVM_FFI_INLINE static std::optional<const char*> TryCastFromAnyView(const TVMFFIAny* src) {
    if (src->type_index == TypeIndex::kTVMFFIRawStr) {
      return static_cast<const char*>(src->v_c_str);
    }
    return std::nullopt;
  }

  TVM_FFI_INLINE static std::string TypeStr() { return "const char*"; }
};

// TVMFFIByteArray, requirement: not nullable, do not retain ownership
template <>
struct TypeTraits<TVMFFIByteArray*> : public TypeTraitsBase {
  static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIByteArrayPtr;
  static constexpr bool storage_enabled = false;

  TVM_FFI_INLINE static void CopyToAnyView(TVMFFIByteArray* src, TVMFFIAny* result) {
    TVM_FFI_ICHECK_NOTNULL(src);
    result->type_index = TypeIndex::kTVMFFIByteArrayPtr;
    result->zero_padding = 0;
    TVM_FFI_CLEAR_PTR_PADDING_IN_FFI_ANY(result);
    result->v_ptr = src;
  }

  TVM_FFI_INLINE static void MoveToAny(TVMFFIByteArray* src, TVMFFIAny* result) {
    TypeTraits<Bytes>::MoveToAny(Bytes(*src), result);
  }

  TVM_FFI_INLINE static std::optional<TVMFFIByteArray*> TryCastFromAnyView(const TVMFFIAny* src) {
    if (src->type_index == TypeIndex::kTVMFFIByteArrayPtr) {
      return static_cast<TVMFFIByteArray*>(src->v_ptr);
    }
    return std::nullopt;
  }

  TVM_FFI_INLINE static std::string TypeStr() { return StaticTypeKey::kTVMFFIByteArrayPtr; }
};

template <>
inline constexpr bool use_default_type_traits_v<std::string> = false;

template <>
struct TypeTraits<std::string>
    : public FallbackOnlyTraitsBase<std::string, const char*, TVMFFIByteArray*, Bytes, String> {
  TVM_FFI_INLINE static void CopyToAnyView(const std::string& src, TVMFFIAny* result) {
    result->type_index = TypeIndex::kTVMFFIRawStr;
    result->zero_padding = 0;
    result->v_c_str = src.c_str();
  }

  TVM_FFI_INLINE static void MoveToAny(std::string src, TVMFFIAny* result) {
    // when we need to move to any, convert to owned object first
    TypeTraits<String>::MoveToAny(String(std::move(src)), result);
  }

  TVM_FFI_INLINE static std::string TypeStr() { return "std::string"; }

  TVM_FFI_INLINE static std::string ConvertFallbackValue(const char* src) {
    return std::string(src);
  }

  TVM_FFI_INLINE static std::string ConvertFallbackValue(TVMFFIByteArray* src) {
    return std::string(src->data, src->size);
  }

  TVM_FFI_INLINE static std::string ConvertFallbackValue(Bytes src) {
    return src.operator std::string();
  }

  TVM_FFI_INLINE static std::string ConvertFallbackValue(String src) {
    return src.operator std::string();
  }
};

inline String operator+(const String& lhs, const String& rhs) {
  size_t lhs_size = lhs.size();
  size_t rhs_size = rhs.size();
  return String::Concat(lhs.data(), lhs_size, rhs.data(), rhs_size);
}

inline String operator+(const String& lhs, const std::string& rhs) {
  size_t lhs_size = lhs.size();
  size_t rhs_size = rhs.size();
  return String::Concat(lhs.data(), lhs_size, rhs.data(), rhs_size);
}

inline String operator+(const std::string& lhs, const String& rhs) {
  size_t lhs_size = lhs.size();
  size_t rhs_size = rhs.size();
  return String::Concat(lhs.data(), lhs_size, rhs.data(), rhs_size);
}

inline String operator+(const char* lhs, const String& rhs) {
  size_t lhs_size = std::strlen(lhs);
  size_t rhs_size = rhs.size();
  return String::Concat(lhs, lhs_size, rhs.data(), rhs_size);
}

inline String operator+(const String& lhs, const char* rhs) {
  size_t lhs_size = lhs.size();
  size_t rhs_size = std::strlen(rhs);
  return String::Concat(lhs.data(), lhs_size, rhs, rhs_size);
}

// Overload < operator
inline bool operator<(std::nullptr_t, const String& rhs) = delete;
inline bool operator<(const String& lhs, std::nullptr_t) = delete;

inline bool operator<(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) < 0; }

inline bool operator<(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) > 0; }

inline bool operator<(const String& lhs, const String& rhs) { return lhs.compare(rhs) < 0; }

inline bool operator<(const String& lhs, const char* rhs) { return lhs.compare(rhs) < 0; }

inline bool operator<(const char* lhs, const String& rhs) { return rhs.compare(lhs) > 0; }

// Overload > operator
inline bool operator>(std::nullptr_t, const String& rhs) = delete;
inline bool operator>(const String& lhs, std::nullptr_t) = delete;

inline bool operator>(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) > 0; }

inline bool operator>(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) < 0; }

inline bool operator>(const String& lhs, const String& rhs) { return lhs.compare(rhs) > 0; }

inline bool operator>(const String& lhs, const char* rhs) { return lhs.compare(rhs) > 0; }

inline bool operator>(const char* lhs, const String& rhs) { return rhs.compare(lhs) < 0; }

// Overload <= operator
inline bool operator<=(std::nullptr_t, const String& rhs) = delete;
inline bool operator<=(const String& lhs, std::nullptr_t) = delete;

inline bool operator<=(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) <= 0; }

inline bool operator<=(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) >= 0; }

inline bool operator<=(const String& lhs, const String& rhs) { return lhs.compare(rhs) <= 0; }

inline bool operator<=(const String& lhs, const char* rhs) { return lhs.compare(rhs) <= 0; }

inline bool operator<=(const char* lhs, const String& rhs) { return rhs.compare(lhs) >= 0; }

// Overload >= operator
inline bool operator>=(std::nullptr_t, const String& rhs) = delete;
inline bool operator>=(const String& lhs, std::nullptr_t) = delete;

inline bool operator>=(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) >= 0; }

inline bool operator>=(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) <= 0; }

inline bool operator>=(const String& lhs, const String& rhs) { return lhs.compare(rhs) >= 0; }

inline bool operator>=(const String& lhs, const char* rhs) { return lhs.compare(rhs) >= 0; }

inline bool operator>=(const char* lhs, const String& rhs) { return rhs.compare(lhs) <= 0; }

// delete Overload == operator for nullptr
inline bool operator==(const String& lhs, std::nullptr_t) = delete;
inline bool operator==(std::nullptr_t, const String& rhs) = delete;

inline bool operator==(const String& lhs, const std::string& rhs) {
  return Bytes::memequal(lhs.data(), rhs.data(), lhs.size(), rhs.size());
}

inline bool operator==(const std::string& lhs, const String& rhs) {
  return Bytes::memequal(lhs.data(), rhs.data(), lhs.size(), rhs.size());
}

inline bool operator==(const String& lhs, const String& rhs) {
  return Bytes::memequal(lhs.data(), rhs.data(), lhs.size(), rhs.size());
}

inline bool operator==(const String& lhs, const char* rhs) { return lhs.compare(rhs) == 0; }

inline bool operator==(const char* lhs, const String& rhs) { return rhs.compare(lhs) == 0; }

// Overload != operator
inline bool operator!=(const String& lhs, std::nullptr_t) = delete;
inline bool operator!=(std::nullptr_t, const String& rhs) = delete;

inline bool operator!=(const String& lhs, const std::string& rhs) { return lhs.compare(rhs) != 0; }

inline bool operator!=(const std::string& lhs, const String& rhs) { return rhs.compare(lhs) != 0; }

inline bool operator!=(const String& lhs, const String& rhs) { return lhs.compare(rhs) != 0; }

inline bool operator!=(const String& lhs, const char* rhs) { return lhs.compare(rhs) != 0; }

inline bool operator!=(const char* lhs, const String& rhs) { return rhs.compare(lhs) != 0; }

inline std::ostream& operator<<(std::ostream& out, const String& input) {
  out.write(input.data(), input.size());
  return out;
}
}  // namespace ffi
}  // namespace tvm

namespace std {

template <>
struct hash<::tvm::ffi::Bytes> {
  std::size_t operator()(const ::tvm::ffi::Bytes& bytes) const {
    return std::hash<std::string_view>()(std::string_view(bytes.data(), bytes.size()));
  }
};

template <>
struct hash<::tvm::ffi::String> {
  std::size_t operator()(const ::tvm::ffi::String& str) const {
    return std::hash<std::string_view>()(std::string_view(str.data(), str.size()));
  }
};
}  // namespace std
#endif  // TVM_FFI_STRING_H_