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; StructPtr(decltype (nullptr))38 StructPtr(decltype(nullptr)) {} 39 40 ~StructPtr() = default; 41 decltype(nullptr)42 StructPtr& operator=(decltype(nullptr)) { 43 reset(); 44 return *this; 45 } 46 StructPtr(StructPtr && other)47 StructPtr(StructPtr&& other) { Take(&other); } 48 StructPtr& operator=(StructPtr&& other) { 49 Take(&other); 50 return *this; 51 } 52 53 template <typename... Args> StructPtr(base::in_place_t,Args &&...args)54 StructPtr(base::in_place_t, Args&&... args) 55 : ptr_(new Struct(std::forward<Args>(args)...)) {} 56 57 template <typename U> To()58 U To() const { 59 return TypeConverter<U, StructPtr>::Convert(*this); 60 } 61 reset()62 void reset() { ptr_.reset(); } 63 is_null()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 } get()74 Struct* get() const { return ptr_.get(); } 75 Swap(StructPtr * other)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). Clone()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. Equals(const StructPtr & other)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). Hash(size_t seed)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>; Take(StructPtr * other)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 InlinedStructPtr()127 InlinedStructPtr() : state_(NIL) {} InlinedStructPtr(decltype (nullptr))128 InlinedStructPtr(decltype(nullptr)) : state_(NIL) {} 129 ~InlinedStructPtr()130 ~InlinedStructPtr() {} 131 decltype(nullptr)132 InlinedStructPtr& operator=(decltype(nullptr)) { 133 reset(); 134 return *this; 135 } 136 InlinedStructPtr(InlinedStructPtr && other)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> InlinedStructPtr(base::in_place_t,Args &&...args)144 InlinedStructPtr(base::in_place_t, Args&&... args) 145 : value_(std::forward<Args>(args)...), state_(VALID) {} 146 147 template <typename U> To()148 U To() const { 149 return TypeConverter<U, InlinedStructPtr>::Convert(*this); 150 } 151 reset()152 void reset() { 153 state_ = NIL; 154 value_. ~Struct(); 155 new (&value_) Struct(); 156 } 157 is_null()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 } get()168 Struct* get() const { return &value_; } 169 Swap(InlinedStructPtr * other)170 void Swap(InlinedStructPtr* other) { 171 std::swap(value_, other->value_); 172 std::swap(state_, other->state_); 173 } 174 Clone()175 InlinedStructPtr Clone() const { 176 return is_null() ? InlinedStructPtr() : value_.Clone(); 177 } 178 179 // Compares the pointees (which might both be null). Equals(const InlinedStructPtr & other)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). Hash(size_t seed)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>; Take(InlinedStructPtr * other)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: IsHashTableDeletedValue(const StructPtr<Struct> & value)230 static bool IsHashTableDeletedValue(const StructPtr<Struct>& value) { 231 return value.ptr_.get() == reinterpret_cast<Struct*>(1u); 232 } 233 ConstructDeletedValue(mojo::StructPtr<Struct> & slot)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: IsHashTableDeletedValue(const InlinedStructPtr<Struct> & value)249 static bool IsHashTableDeletedValue(const InlinedStructPtr<Struct>& value) { 250 return value.state_ == InlinedStructPtr<Struct>::DELETED; 251 } 252 ConstructDeletedValue(mojo::InlinedStructPtr<Struct> & slot)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