| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // 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 <functional> |
| #include <memory> |
| #include <new> |
| |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/optional.h" |
| #include "mojo/public/cpp/bindings/lib/hash_util.h" |
| #include "mojo/public/cpp/bindings/type_converter.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; |
| |
| StructPtr() = default; |
| StructPtr(decltype(nullptr)) {} |
| |
| ~StructPtr() = default; |
| |
| StructPtr& operator=(decltype(nullptr)) { |
| reset(); |
| return *this; |
| } |
| |
| StructPtr(StructPtr&& other) { Take(&other); } |
| StructPtr& operator=(StructPtr&& other) { |
| Take(&other); |
| return *this; |
| } |
| |
| template <typename... Args> |
| StructPtr(base::in_place_t, Args&&... args) |
| : ptr_(new Struct(std::forward<Args>(args)...)) {} |
| |
| template <typename U> |
| U To() const { |
| return TypeConverter<U, StructPtr>::Convert(*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/735302): 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(); } |
| |
| bool operator<(const StructPtr& other) const { |
| return Hash(internal::kHashSeed) < other.Hash(internal::kHashSeed); |
| } |
| |
| private: |
| friend class internal::StructPtrWTFHelper<Struct>; |
| void Take(StructPtr* other) { |
| reset(); |
| Swap(other); |
| } |
| |
| std::unique_ptr<Struct> ptr_; |
| |
| DISALLOW_COPY_AND_ASSIGN(StructPtr); |
| }; |
| |
| template <typename T> |
| bool operator==(const StructPtr<T>& lhs, const StructPtr<T>& rhs) { |
| return lhs.Equals(rhs); |
| } |
| template <typename T> |
| bool operator!=(const StructPtr<T>& lhs, const StructPtr<T>& rhs) { |
| return !(lhs == rhs); |
| } |
| |
| // Designed to be used when Struct is small and copyable. |
| template <typename S> |
| class InlinedStructPtr { |
| public: |
| using Struct = S; |
| |
| InlinedStructPtr() : state_(NIL) {} |
| InlinedStructPtr(decltype(nullptr)) : state_(NIL) {} |
| |
| ~InlinedStructPtr() {} |
| |
| InlinedStructPtr& operator=(decltype(nullptr)) { |
| reset(); |
| return *this; |
| } |
| |
| InlinedStructPtr(InlinedStructPtr&& other) : state_(NIL) { Take(&other); } |
| InlinedStructPtr& operator=(InlinedStructPtr&& other) { |
| Take(&other); |
| return *this; |
| } |
| |
| template <typename... Args> |
| InlinedStructPtr(base::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 { 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(); } |
| |
| bool operator<(const InlinedStructPtr& other) const { |
| return Hash(internal::kHashSeed) < other.Hash(internal::kHashSeed); |
| } |
| |
| private: |
| friend class internal::InlinedStructPtrWTFHelper<Struct>; |
| void Take(InlinedStructPtr* other) { |
| reset(); |
| Swap(other); |
| } |
| |
| enum State { |
| VALID, |
| NIL, |
| DELETED, // For use in WTF::HashMap only |
| }; |
| |
| mutable Struct value_; |
| State state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InlinedStructPtr); |
| }; |
| |
| template <typename T> |
| bool operator==(const InlinedStructPtr<T>& lhs, |
| const InlinedStructPtr<T>& rhs) { |
| return lhs.Equals(rhs); |
| } |
| template <typename T> |
| bool operator!=(const InlinedStructPtr<T>& lhs, |
| const InlinedStructPtr<T>& rhs) { |
| return !(lhs == rhs); |
| } |
| |
| 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; |
| } |
| }; |
| |
| } // namespace internal |
| } // 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_ |