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