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(tibell): Get rid of Equals in favor of the operator. Same for Hash.
     85   bool Equals(const StructPtr& other) const {
     86     if (is_null() || other.is_null())
     87       return is_null() && other.is_null();
     88     return ptr_->Equals(*other.ptr_);
     89   }
     90 
     91   // Hashes based on the pointee (which might be null).
     92   size_t Hash(size_t seed) const {
     93     if (is_null())
     94       return internal::HashCombine(seed, 0);
     95     return ptr_->Hash(seed);
     96   }
     97 
     98   explicit operator bool() const { return !is_null(); }
     99 
    100  private:
    101   friend class internal::StructPtrWTFHelper<Struct>;
    102   void Take(StructPtr* other) {
    103     reset();
    104     Swap(other);
    105   }
    106 
    107   std::unique_ptr<Struct> ptr_;
    108 
    109   DISALLOW_COPY_AND_ASSIGN(StructPtr);
    110 };
    111 
    112 template <typename T>
    113 bool operator==(const StructPtr<T>& lhs, const StructPtr<T>& rhs) {
    114   return lhs.Equals(rhs);
    115 }
    116 template <typename T>
    117 bool operator!=(const StructPtr<T>& lhs, const StructPtr<T>& rhs) {
    118   return !(lhs == rhs);
    119 }
    120 
    121 // Designed to be used when Struct is small and copyable.
    122 template <typename S>
    123 class InlinedStructPtr {
    124  public:
    125   using Struct = S;
    126 
    127   InlinedStructPtr() : state_(NIL) {}
    128   InlinedStructPtr(decltype(nullptr)) : state_(NIL) {}
    129 
    130   ~InlinedStructPtr() {}
    131 
    132   InlinedStructPtr& operator=(decltype(nullptr)) {
    133     reset();
    134     return *this;
    135   }
    136 
    137   InlinedStructPtr(InlinedStructPtr&& other) : state_(NIL) { Take(&other); }
    138   InlinedStructPtr& operator=(InlinedStructPtr&& other) {
    139     Take(&other);
    140     return *this;
    141   }
    142 
    143   template <typename... Args>
    144   InlinedStructPtr(base::in_place_t, Args&&... args)
    145       : value_(std::forward<Args>(args)...), state_(VALID) {}
    146 
    147   template <typename U>
    148   U To() const {
    149     return TypeConverter<U, InlinedStructPtr>::Convert(*this);
    150   }
    151 
    152   void reset() {
    153     state_ = NIL;
    154     value_. ~Struct();
    155     new (&value_) Struct();
    156   }
    157 
    158   bool is_null() const { return state_ == NIL; }
    159 
    160   Struct& operator*() const {
    161     DCHECK(state_ == VALID);
    162     return value_;
    163   }
    164   Struct* operator->() const {
    165     DCHECK(state_ == VALID);
    166     return &value_;
    167   }
    168   Struct* get() const { return &value_; }
    169 
    170   void Swap(InlinedStructPtr* other) {
    171     std::swap(value_, other->value_);
    172     std::swap(state_, other->state_);
    173   }
    174 
    175   InlinedStructPtr Clone() const {
    176     return is_null() ? InlinedStructPtr() : value_.Clone();
    177   }
    178 
    179   // Compares the pointees (which might both be null).
    180   bool Equals(const InlinedStructPtr& other) const {
    181     if (is_null() || other.is_null())
    182       return is_null() && other.is_null();
    183     return value_.Equals(other.value_);
    184   }
    185 
    186   // Hashes based on the pointee (which might be null).
    187   size_t Hash(size_t seed) const {
    188     if (is_null())
    189       return internal::HashCombine(seed, 0);
    190     return value_.Hash(seed);
    191   }
    192 
    193   explicit operator bool() const { return !is_null(); }
    194 
    195  private:
    196   friend class internal::InlinedStructPtrWTFHelper<Struct>;
    197   void Take(InlinedStructPtr* other) {
    198     reset();
    199     Swap(other);
    200   }
    201 
    202   enum State {
    203     VALID,
    204     NIL,
    205     DELETED,  // For use in WTF::HashMap only
    206   };
    207 
    208   mutable Struct value_;
    209   State state_;
    210 
    211   DISALLOW_COPY_AND_ASSIGN(InlinedStructPtr);
    212 };
    213 
    214 template <typename T>
    215 bool operator==(const InlinedStructPtr<T>& lhs,
    216                 const InlinedStructPtr<T>& rhs) {
    217   return lhs.Equals(rhs);
    218 }
    219 template <typename T>
    220 bool operator!=(const InlinedStructPtr<T>& lhs,
    221                 const InlinedStructPtr<T>& rhs) {
    222   return !(lhs == rhs);
    223 }
    224 
    225 namespace internal {
    226 
    227 template <typename Struct>
    228 class StructPtrWTFHelper {
    229  public:
    230   static bool IsHashTableDeletedValue(const StructPtr<Struct>& value) {
    231     return value.ptr_.get() == reinterpret_cast<Struct*>(1u);
    232   }
    233 
    234   static void ConstructDeletedValue(mojo::StructPtr<Struct>& slot) {
    235     // |slot| refers to a previous, real value that got deleted and had its
    236     // destructor run, so this is the first time the "deleted value" has its
    237     // constructor called.
    238     //
    239     // Dirty trick: implant an invalid pointer in |ptr_|. Destructor isn't
    240     // called for deleted buckets, so this is okay.
    241     new (&slot) StructPtr<Struct>();
    242     slot.ptr_.reset(reinterpret_cast<Struct*>(1u));
    243   }
    244 };
    245 
    246 template <typename Struct>
    247 class InlinedStructPtrWTFHelper {
    248  public:
    249   static bool IsHashTableDeletedValue(const InlinedStructPtr<Struct>& value) {
    250     return value.state_ == InlinedStructPtr<Struct>::DELETED;
    251   }
    252 
    253   static void ConstructDeletedValue(mojo::InlinedStructPtr<Struct>& slot) {
    254     // |slot| refers to a previous, real value that got deleted and had its
    255     // destructor run, so this is the first time the "deleted value" has its
    256     // constructor called.
    257     new (&slot) InlinedStructPtr<Struct>();
    258     slot.state_ = InlinedStructPtr<Struct>::DELETED;
    259   }
    260 };
    261 
    262 }  // namespace internal
    263 }  // namespace mojo
    264 
    265 namespace std {
    266 
    267 template <typename T>
    268 struct hash<mojo::StructPtr<T>> {
    269   size_t operator()(const mojo::StructPtr<T>& value) const {
    270     return value.Hash(mojo::internal::kHashSeed);
    271   }
    272 };
    273 
    274 template <typename T>
    275 struct hash<mojo::InlinedStructPtr<T>> {
    276   size_t operator()(const mojo::InlinedStructPtr<T>& value) const {
    277     return value.Hash(mojo::internal::kHashSeed);
    278   }
    279 };
    280 
    281 }  // namespace std
    282 
    283 #endif  // MOJO_PUBLIC_CPP_BINDINGS_STRUCT_PTR_H_
    284