#ifndef MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
#define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
#include <cstddef>
#include <functional>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
#include "base/check.h"
#include "mojo/public/cpp/bindings/lib/hash_util.h"
#include "mojo/public/cpp/bindings/type_converter.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value_forward.h"
namespace mojo {
namespace internal {
constexpr size_t kHashSeed = 31;
template <typename Struct>
class StructPtrWTFHelper;
template <typename Struct>
class InlinedStructPtrWTFHelper;
}
template <typename S>
class StructPtr {
public:
using Struct = S;
using element_type = S;
StructPtr() = default;
StructPtr(std::nullptr_t) {}
StructPtr(const StructPtr&) = delete;
StructPtr& operator=(const StructPtr&) = delete;
~StructPtr() = default;
StructPtr& operator=(std::nullptr_t) {
reset();
return *this;
}
StructPtr(StructPtr&& other) { Take(&other); }
StructPtr& operator=(StructPtr&& other) {
Take(&other);
return *this;
}
template <typename... Args>
StructPtr(std::in_place_t, Args&&... args)
: ptr_(new Struct(std::forward<Args>(args)...)) {}
template <typename U>
U To() const& {
return TypeConverter<U, StructPtr>::Convert(*this);
}
template <typename U>
U To() && {
return TypeConverter<U, StructPtr>::Convert(std::move(*this));
}
void reset() { ptr_.reset(); }
bool is_null() const { return !ptr_; }
Struct& operator*() const {
DCHECK(ptr_);
return *ptr_;
}
Struct* operator->() const {
DCHECK(ptr_);
return ptr_.get();
}
Struct* get() const { return ptr_.get(); }
void Swap(StructPtr* other) { std::swap(ptr_, other->ptr_); }
StructPtr Clone() const { return is_null() ? StructPtr() : ptr_->Clone(); }
bool Equals(const StructPtr& other) const {
if (is_null() || other.is_null())
return is_null() && other.is_null();
return ptr_->Equals(*other.ptr_);
}
size_t Hash(size_t seed) const {
if (is_null())
return internal::HashCombine(seed, 0);
return ptr_->Hash(seed);
}
explicit operator bool() const { return !is_null(); }
template <class U = S>
typename perfetto::check_traced_value_support<U>::type WriteIntoTrace(
perfetto::TracedValue&& context) const {
perfetto::WriteIntoTracedValue(std::move(context), ptr_);
}
private:
friend class internal::StructPtrWTFHelper<Struct>;
void Take(StructPtr* other) {
reset();
Swap(other);
}
std::unique_ptr<Struct> ptr_;
};
template <typename S>
class InlinedStructPtr {
public:
using Struct = S;
using element_type = S;
InlinedStructPtr() = default;
InlinedStructPtr(std::nullptr_t) {}
InlinedStructPtr(const InlinedStructPtr&) = delete;
InlinedStructPtr& operator=(const InlinedStructPtr&) = delete;
~InlinedStructPtr()
requires(!std::is_trivially_destructible_v<Struct>)
{
reset();
}
InlinedStructPtr& operator=(std::nullptr_t) {
reset();
return *this;
}
InlinedStructPtr(InlinedStructPtr&& other) noexcept
: storage_(CreateValue(other)), state_(other.state_) {
other.reset();
}
InlinedStructPtr& operator=(InlinedStructPtr&& other) noexcept {
reset();
state_ = other.state_;
if (other.state_ != NIL) {
new (&storage_.value) Struct(std::move(other.storage_.value));
other.reset();
}
return *this;
}
template <typename... Args>
InlinedStructPtr(std::in_place_t, Args&&... args)
: storage_(std::in_place, std::forward<Args>(args)...), state_(VALID) {}
template <typename U>
U To() const {
return TypeConverter<U, InlinedStructPtr>::Convert(*this);
}
void reset() {
if (state_ != NIL) {
state_ = NIL;
storage_.value.~Struct();
}
}
bool is_null() const { return state_ != VALID; }
Struct& operator*() const {
CHECK(state_ == VALID);
return storage_.value;
}
Struct* operator->() const {
CHECK(state_ == VALID);
return &storage_.value;
}
Struct* get() const {
if (state_ == VALID) {
return &storage_.value;
}
return nullptr;
}
void Swap(InlinedStructPtr* other) {
InlinedStructPtr tmp = std::move(*this);
*this = std::move(*other);
*other = std::move(tmp);
}
InlinedStructPtr Clone() const {
return is_null() ? InlinedStructPtr() : storage_.value.Clone();
}
bool Equals(const InlinedStructPtr& other) const {
if (is_null() || other.is_null())
return is_null() && other.is_null();
return storage_.value.Equals(other.storage_.value);
}
size_t Hash(size_t seed) const {
if (is_null())
return internal::HashCombine(seed, 0);
return storage_.value.Hash(seed);
}
explicit operator bool() const { return !is_null(); }
template <class U = S>
typename perfetto::check_traced_value_support<U>::type WriteIntoTrace(
perfetto::TracedValue&& context) const {
perfetto::WriteIntoTracedValue(std::move(context), get());
}
private:
friend class internal::InlinedStructPtrWTFHelper<Struct>;
enum State {
NIL,
VALID,
DELETED,
};
union Storage {
Storage() {}
~Storage() {}
template <typename... Args>
explicit Storage(std::in_place_t, Args&&... args)
: value(std::forward<Args>(args)...) {}
mutable Struct value;
};
struct DeletedValue {};
explicit InlinedStructPtr(DeletedValue) : state_(DELETED) {}
Storage CreateValue(InlinedStructPtr& other) {
if (other.state_ != NIL) {
return Storage(std::in_place, std::move(other.storage_.value));
} else {
return Storage();
}
}
Storage storage_;
State state_ = NIL;
};
namespace internal {
template <typename Struct>
class StructPtrWTFHelper {
public:
static bool IsHashTableDeletedValue(const StructPtr<Struct>& value) {
return value.ptr_.get() == reinterpret_cast<Struct*>(1u);
}
static void ConstructDeletedValue(mojo::StructPtr<Struct>& slot) {
new (&slot) StructPtr<Struct>();
slot.ptr_.reset(reinterpret_cast<Struct*>(1u));
}
};
template <typename Struct>
class InlinedStructPtrWTFHelper {
public:
static bool IsHashTableDeletedValue(const InlinedStructPtr<Struct>& value) {
return value.state_ == InlinedStructPtr<Struct>::DELETED;
}
static void ConstructDeletedValue(mojo::InlinedStructPtr<Struct>& slot) {
new (&slot) InlinedStructPtr<Struct>(
typename InlinedStructPtr<Struct>::DeletedValue());
}
};
template <typename T>
struct IsStructPtrImpl : std::false_type {};
template <typename S>
struct IsStructPtrImpl<StructPtr<S>> : std::true_type {};
template <typename S>
struct IsStructPtrImpl<InlinedStructPtr<S>> : std::true_type {};
}
template <typename T>
constexpr bool IsStructPtrV = internal::IsStructPtrImpl<std::decay_t<T>>::value;
template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr>
bool operator==(const Ptr& lhs, const Ptr& rhs) {
return lhs.Equals(rhs);
}
template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr>
bool operator!=(const Ptr& lhs, const Ptr& rhs) {
return !(lhs == rhs);
}
template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr>
bool operator<(const Ptr& lhs, const Ptr& rhs) {
if (!lhs || !rhs)
return bool{lhs} < bool{rhs};
return *lhs < *rhs;
}
template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr>
bool operator<=(const Ptr& lhs, const Ptr& rhs) {
return !(rhs < lhs);
}
template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr>
bool operator>(const Ptr& lhs, const Ptr& rhs) {
return rhs < lhs;
}
template <typename Ptr, std::enable_if_t<IsStructPtrV<Ptr>>* = nullptr>
bool operator>=(const Ptr& lhs, const Ptr& rhs) {
return !(lhs < rhs);
}
}
namespace std {
template <typename T>
struct hash<mojo::StructPtr<T>> {
size_t operator()(const mojo::StructPtr<T>& value) const {
return value.Hash(mojo::internal::kHashSeed);
}
};
template <typename T>
struct hash<mojo::InlinedStructPtr<T>> {
size_t operator()(const mojo::InlinedStructPtr<T>& value) const {
return value.Hash(mojo::internal::kHashSeed);
}
};
}
#endif