Home | History | Annotate | Download | only in bindings
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
      6 #define MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
      7 
      8 #include <functional>
      9 #include <memory>
     10 #include <new>
     11 
     12 #include "base/logging.h"
     13 #include "base/macros.h"
     14 #include "base/optional.h"
     15 #include "mojo/public/cpp/bindings/lib/hash_util.h"
     16 #include "mojo/public/cpp/bindings/type_converter.h"
     17 
     18 namespace mojo {
     19 namespace internal {
     20 
     21 constexpr size_t kHashSeed = 31;
     22 
     23 template <typename Struct>
     24 class StructPtrWTFHelper;
     25 
     26 template <typename Struct>
     27 class InlinedStructPtrWTFHelper;
     28 
     29 }  // namespace internal
     30 
     31 // Smart pointer wrapping a mojom structure with move-only semantics.
     32 template <typename S>
     33 class StructPtr {
     34  public:
     35   using Struct = S;
     36 
     37   StructPtr() = default;
     38   StructPtr(decltype(nullptr)) {}
     39 
     40   ~StructPtr() = default;
     41 
     42   StructPtr& operator=(decltype(nullptr)) {
     43     reset();
     44     return *this;
     45   }
     46 
     47   StructPtr(StructPtr&& other) { Take(&other); }
     48   StructPtr& operator=(StructPtr&& other) {
     49     Take(&other);
     50     return *this;
     51   }
     52 
     53   template <typename... Args>
     54   StructPtr(base::in_place_t, Args&&... args)
     55       : ptr_(new Struct(std::forward<Args>(args)...)) {}
     56 
     57   template <typename U>
     58   U To() const {
     59     return TypeConverter<U, StructPtr>::Convert(*this);
     60   }
     61 
     62   void reset() { ptr_.reset(); }
     63 
     64   bool is_null() const { return !ptr_; }
     65 
     66   Struct& operator*() const {
     67     DCHECK(ptr_);
     68     return *ptr_;
     69   }
     70   Struct* operator->() const {
     71     DCHECK(ptr_);
     72     return ptr_.get();
     73   }
     74   Struct* get() const { return ptr_.get(); }
     75 
     76   void Swap(StructPtr* other) { std::swap(ptr_, other->ptr_); }
     77 
     78   // Please note that calling this method will fail compilation if the value
     79   // type |Struct| doesn't have a Clone() method defined (which usually means
     80   // that it contains Mojo handles).
     81   StructPtr Clone() const { return is_null() ? StructPtr() : ptr_->Clone(); }
     82 
     83   // Compares the pointees (which might both be null).
     84   // TODO(crbug.com/735302): Get rid of Equals in favor of the operator. Same
     85   // for Hash.
     86   bool Equals(const StructPtr& other) const {
     87     if (is_null() || other.is_null())
     88       return is_null() && other.is_null();
     89     return ptr_->Equals(*other.ptr_);
     90   }
     91 
     92   // Hashes based on the pointee (which might be null).
     93   size_t Hash(size_t seed) const {
     94     if (is_null())
     95       return internal::HashCombine(seed, 0);
     96     return ptr_->Hash(seed);
     97   }
     98 
     99   explicit operator bool() const { return !is_null(); }
    100 
    101   bool operator<(const StructPtr& other) const {
    102     return Hash(internal::kHashSeed) < other.Hash(internal::kHashSeed);
    103   }
    104 
    105  private:
    106   friend class internal::StructPtrWTFHelper<Struct>;
    107   void Take(StructPtr* other) {
    108     reset();
    109     Swap(other);
    110   }
    111 
    112   std::unique_ptr<Struct> ptr_;
    113 
    114   DISALLOW_COPY_AND_ASSIGN(StructPtr);
    115 };
    116 
    117 template <typename T>
    118 bool operator==(const StructPtr<T>& lhs, const StructPtr<T>& rhs) {
    119   return lhs.Equals(rhs);
    120 }
    121 template <typename T>
    122 bool operator!=(const StructPtr<T>& lhs, const StructPtr<T>& rhs) {
    123   return !(lhs == rhs);
    124 }
    125 
    126 // Designed to be used when Struct is small and copyable.
    127 template <typename S>
    128 class InlinedStructPtr {
    129  public:
    130   using Struct = S;
    131 
    132   InlinedStructPtr() : state_(NIL) {}
    133   InlinedStructPtr(decltype(nullptr)) : state_(NIL) {}
    134 
    135   ~InlinedStructPtr() {}
    136 
    137   InlinedStructPtr& operator=(decltype(nullptr)) {
    138     reset();
    139     return *this;
    140   }
    141 
    142   InlinedStructPtr(InlinedStructPtr&& other) : state_(NIL) { Take(&other); }
    143   InlinedStructPtr& operator=(InlinedStructPtr&& other) {
    144     Take(&other);
    145     return *this;
    146   }
    147 
    148   template <typename... Args>
    149   InlinedStructPtr(base::in_place_t, Args&&... args)
    150       : value_(std::forward<Args>(args)...), state_(VALID) {}
    151 
    152   template <typename U>
    153   U To() const {
    154     return TypeConverter<U, InlinedStructPtr>::Convert(*this);
    155   }
    156 
    157   void reset() {
    158     state_ = NIL;
    159     value_. ~Struct();
    160     new (&value_) Struct();
    161   }
    162 
    163   bool is_null() const { return state_ == NIL; }
    164 
    165   Struct& operator*() const {
    166     DCHECK(state_ == VALID);
    167     return value_;
    168   }
    169   Struct* operator->() const {
    170     DCHECK(state_ == VALID);
    171     return &value_;
    172   }
    173   Struct* get() const { return &value_; }
    174 
    175   void Swap(InlinedStructPtr* other) {
    176     std::swap(value_, other->value_);
    177     std::swap(state_, other->state_);
    178   }
    179 
    180   InlinedStructPtr Clone() const {
    181     return is_null() ? InlinedStructPtr() : value_.Clone();
    182   }
    183 
    184   // Compares the pointees (which might both be null).
    185   bool Equals(const InlinedStructPtr& other) const {
    186     if (is_null() || other.is_null())
    187       return is_null() && other.is_null();
    188     return value_.Equals(other.value_);
    189   }
    190 
    191   // Hashes based on the pointee (which might be null).
    192   size_t Hash(size_t seed) const {
    193     if (is_null())
    194       return internal::HashCombine(seed, 0);
    195     return value_.Hash(seed);
    196   }
    197 
    198   explicit operator bool() const { return !is_null(); }
    199 
    200   bool operator<(const InlinedStructPtr& other) const {
    201     return Hash(internal::kHashSeed) < other.Hash(internal::kHashSeed);
    202   }
    203 
    204  private:
    205   friend class internal::InlinedStructPtrWTFHelper<Struct>;
    206   void Take(InlinedStructPtr* other) {
    207     reset();
    208     Swap(other);
    209   }
    210 
    211   enum State {
    212     VALID,
    213     NIL,
    214     DELETED,  // For use in WTF::HashMap only
    215   };
    216 
    217   mutable Struct value_;
    218   State state_;
    219 
    220   DISALLOW_COPY_AND_ASSIGN(InlinedStructPtr);
    221 };
    222 
    223 template <typename T>
    224 bool operator==(const InlinedStructPtr<T>& lhs,
    225                 const InlinedStructPtr<T>& rhs) {
    226   return lhs.Equals(rhs);
    227 }
    228 template <typename T>
    229 bool operator!=(const InlinedStructPtr<T>& lhs,
    230                 const InlinedStructPtr<T>& rhs) {
    231   return !(lhs == rhs);
    232 }
    233 
    234 namespace internal {
    235 
    236 template <typename Struct>
    237 class StructPtrWTFHelper {
    238  public:
    239   static bool IsHashTableDeletedValue(const StructPtr<Struct>& value) {
    240     return value.ptr_.get() == reinterpret_cast<Struct*>(1u);
    241   }
    242 
    243   static void ConstructDeletedValue(mojo::StructPtr<Struct>& slot) {
    244     // |slot| refers to a previous, real value that got deleted and had its
    245     // destructor run, so this is the first time the "deleted value" has its
    246     // constructor called.
    247     //
    248     // Dirty trick: implant an invalid pointer in |ptr_|. Destructor isn't
    249     // called for deleted buckets, so this is okay.
    250     new (&slot) StructPtr<Struct>();
    251     slot.ptr_.reset(reinterpret_cast<Struct*>(1u));
    252   }
    253 };
    254 
    255 template <typename Struct>
    256 class InlinedStructPtrWTFHelper {
    257  public:
    258   static bool IsHashTableDeletedValue(const InlinedStructPtr<Struct>& value) {
    259     return value.state_ == InlinedStructPtr<Struct>::DELETED;
    260   }
    261 
    262   static void ConstructDeletedValue(mojo::InlinedStructPtr<Struct>& slot) {
    263     // |slot| refers to a previous, real value that got deleted and had its
    264     // destructor run, so this is the first time the "deleted value" has its
    265     // constructor called.
    266     new (&slot) InlinedStructPtr<Struct>();
    267     slot.state_ = InlinedStructPtr<Struct>::DELETED;
    268   }
    269 };
    270 
    271 }  // namespace internal
    272 }  // namespace mojo
    273 
    274 namespace std {
    275 
    276 template <typename T>
    277 struct hash<mojo::StructPtr<T>> {
    278   size_t operator()(const mojo::StructPtr<T>& value) const {
    279     return value.Hash(mojo::internal::kHashSeed);
    280   }
    281 };
    282 
    283 template <typename T>
    284 struct hash<mojo::InlinedStructPtr<T>> {
    285   size_t operator()(const mojo::InlinedStructPtr<T>& value) const {
    286     return value.Hash(mojo::internal::kHashSeed);
    287   }
    288 };
    289 
    290 }  // namespace std
    291 
    292 #endif  // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
    293