| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #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 "base/check.h" |
| #include "base/template_util.h" |
| #include "mojo/public/cpp/bindings/lib/hash_util.h" |
| #include "mojo/public/cpp/bindings/type_converter.h" |
| #include "third_party/abseil-cpp/absl/utility/utility.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; |
| |
| } // namespace internal |
| |
| // Smart pointer wrapping a mojom structure with move-only semantics. |
| template <typename S> |
| class StructPtr { |
| public: |
| using Struct = S; |
| |
| // Exposing StructPtr<S>::element_type allows gmock's Pointee matcher to |
| // dereference StructPtr'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_); } |
| |
| // Please note that calling this method will fail compilation if the value |
| // type |Struct| doesn't have a Clone() method defined (which usually means |
| // that it contains Mojo handles). |
| StructPtr Clone() const { return is_null() ? StructPtr() : ptr_->Clone(); } |
| |
| // Compares the pointees (which might both be null). |
| // TODO(crbug.com/41326459): Get rid of Equals in favor of the operator. Same |
| // for Hash. |
| bool Equals(const StructPtr& other) const { |
| if (is_null() || other.is_null()) |
| return is_null() && other.is_null(); |
| return ptr_->Equals(*other.ptr_); |
| } |
| |
| // Hashes based on the pointee (which might be null). |
| 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(); } |
| |
| // If T is serialisable into trace, StructPtr<T> is also serialisable. |
| 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_; |
| }; |
| |
| // Designed to be used when Struct is small and copyable. |
| template <typename S> |
| class InlinedStructPtr { |
| public: |
| using Struct = S; |
| |
| // Exposing InlinedStructPtr<S>::element_type allows gmock's Pointee matcher |
| // to dereference InlinedStructPtr's. |
| using element_type = S; |
| |
| InlinedStructPtr() = default; |
| InlinedStructPtr(std::nullptr_t) {} |
| |
| InlinedStructPtr(const InlinedStructPtr&) = delete; |
| InlinedStructPtr& operator=(const InlinedStructPtr&) = delete; |
| |
| ~InlinedStructPtr() = default; |
| |
| InlinedStructPtr& operator=(std::nullptr_t) { |
| reset(); |
| return *this; |
| } |
| |
| InlinedStructPtr(InlinedStructPtr&& other) noexcept { Take(&other); } |
| InlinedStructPtr& operator=(InlinedStructPtr&& other) noexcept { |
| Take(&other); |
| return *this; |
| } |
| |
| template <typename... Args> |
| InlinedStructPtr(std::in_place_t, Args&&... args) |
| : value_(std::forward<Args>(args)...), state_(VALID) {} |
| |
| template <typename U> |
| U To() const { |
| return TypeConverter<U, InlinedStructPtr>::Convert(*this); |
| } |
| |
| void reset() { |
| state_ = NIL; |
| value_. ~Struct(); |
| new (&value_) Struct(); |
| } |
| |
| bool is_null() const { return state_ == NIL; } |
| |
| Struct& operator*() const { |
| DCHECK(state_ == VALID); |
| return value_; |
| } |
| Struct* operator->() const { |
| DCHECK(state_ == VALID); |
| return &value_; |
| } |
| Struct* get() const { |
| if (state_ == NIL) |
| return nullptr; |
| return &value_; |
| } |
| |
| void Swap(InlinedStructPtr* other) { |
| std::swap(value_, other->value_); |
| std::swap(state_, other->state_); |
| } |
| |
| InlinedStructPtr Clone() const { |
| return is_null() ? InlinedStructPtr() : value_.Clone(); |
| } |
| |
| // Compares the pointees (which might both be null). |
| bool Equals(const InlinedStructPtr& other) const { |
| if (is_null() || other.is_null()) |
| return is_null() && other.is_null(); |
| return value_.Equals(other.value_); |
| } |
| |
| // Hashes based on the pointee (which might be null). |
| size_t Hash(size_t seed) const { |
| if (is_null()) |
| return internal::HashCombine(seed, 0); |
| return value_.Hash(seed); |
| } |
| |
| explicit operator bool() const { return !is_null(); } |
| |
| // If T is serialisable into trace, StructPtr<T> is also serialisable. |
| 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>; |
| void Take(InlinedStructPtr* other) { |
| reset(); |
| Swap(other); |
| } |
| |
| enum State { |
| NIL, |
| VALID, |
| DELETED, // For use in WTF::HashMap only |
| }; |
| |
| mutable Struct value_; |
| 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) { |
| // |slot| refers to a previous, real value that got deleted and had its |
| // destructor run, so this is the first time the "deleted value" has its |
| // constructor called. |
| // |
| // Dirty trick: implant an invalid pointer in |ptr_|. Destructor isn't |
| // called for deleted buckets, so this is okay. |
| 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) { |
| // |slot| refers to a previous, real value that got deleted and had its |
| // destructor run, so this is the first time the "deleted value" has its |
| // constructor called. |
| new (&slot) InlinedStructPtr<Struct>(); |
| slot.state_ = InlinedStructPtr<Struct>::DELETED; |
| } |
| }; |
| |
| // Convenience type trait so that we can get away with defining the comparison |
| // operators only once. |
| 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 {}; |
| |
| } // namespace internal |
| |
| 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); |
| } |
| |
| // Perform a deep comparison if possible. Otherwise treat null pointers less |
| // than valid pointers. |
| 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 mojo |
| |
| 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); |
| } |
| }; |
| |
| } // namespace std |
| |
| #endif // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_ |