Line data Source code
1 : /** 2 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev> 3 : 4 : Permission is hereby granted, free of charge, to any person obtaining a copy 5 : of this software and associated documentation files (the "Software"), to deal 6 : in the Software without restriction, including without limitation the rights 7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 : copies of the Software, and to permit persons to whom the Software is 9 : furnished to do so, subject to the following conditions: 10 : 11 : The above copyright notice and this permission notice shall be included in 12 : all copies or substantial portions of the Software. 13 : 14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 : THE SOFTWARE. 21 : **/ 22 : 23 : #ifndef STAPPLER_CORE_UTILS_SPSUBSCRIPTION_H_ 24 : #define STAPPLER_CORE_UTILS_SPSUBSCRIPTION_H_ 25 : 26 : #include "SPRef.h" 27 : 28 : namespace STAPPLER_VERSIONIZED stappler { 29 : 30 : using SubscriptionId = ValueWrapper<uint64_t, class SubscriptionIdClassFlag>; 31 : 32 : struct SubscriptionFlags : public ValueWrapper<uint64_t, class SubscriptionFlagsClassFlag> { 33 : using Super = ValueWrapper<uint64_t, class SubscriptionFlagsClassFlag>; 34 : 35 : inline constexpr explicit SubscriptionFlags(const Type &val) : Super(val) { } 36 49091 : inline constexpr explicit SubscriptionFlags(Type &&val) : Super(std::move(val)) { } 37 : 38 50689 : inline constexpr SubscriptionFlags(const SubscriptionFlags &other) { value = other.value; } 39 : inline constexpr SubscriptionFlags &operator=(const SubscriptionFlags &other) { value = other.value; return *this; } 40 50094 : inline constexpr SubscriptionFlags(SubscriptionFlags &&other) { value = std::move(other.value); } 41 48941 : inline constexpr SubscriptionFlags &operator=(SubscriptionFlags &&other) { value = std::move(other.value); return *this; } 42 : 43 : inline constexpr SubscriptionFlags(const Super &other) { value = other.value; } 44 : inline constexpr SubscriptionFlags &operator=(const Super &other) { value = other.value; return *this; } 45 : inline constexpr SubscriptionFlags(Super &&other) { value = std::move(other.value); } 46 : inline constexpr SubscriptionFlags &operator=(Super &&other) { value = std::move(other.value); return *this; } 47 : 48 : template <typename T> 49 : inline constexpr bool hasFlag(T f) const { 50 : return (toInt(f) & value) != 0; 51 : } 52 : 53 : inline constexpr bool initial() const { 54 : return (value & 1) != 0; 55 : } 56 : }; 57 : 58 : /* Subscription is a refcounted class, that allow 59 : * provider object (derived from Subscription) to post some update flags, 60 : * and subscribers (any other objects) to receive it via `check` call 61 : * 62 : * Every registered subscriber has it's own id, that used to check for updates 63 : * since last call of `check` 64 : */ 65 : template <typename _Interface> 66 : class SubscriptionTemplate : public RefBase<_Interface> { 67 : public: 68 : using Interface = _Interface; 69 : using Id = SubscriptionId; 70 : using Flags = SubscriptionFlags; 71 : using FlagsMap = typename Interface::template MapType<Id, Flags>; 72 : 73 : // get unique subscription id 74 : static Id getNextId(); 75 : 76 : // initial flags value, every new subscriber receive this on first call of 'check' 77 : static constexpr Flags Initial = Flags(1); 78 : 79 558 : virtual ~SubscriptionTemplate() { 80 558 : setForwardedSubscription(nullptr); 81 558 : } 82 : 83 : // safe way to get Flag with specific bit set, prevents UB 84 : template <class T> 85 : static Flags _Flag(T idx) { return ((uint8_t)idx == 0 || (uint8_t)idx > (sizeof(Flags) * 8))?Flags(0):Flags(1 << (uint8_t)idx); } 86 : 87 : inline static Flags Flag() { return Flags(0); } 88 : 89 : // Variadic way to get specific bits set via multiple arguments of different integral types 90 : template <class T, class... Args> 91 : static Flags Flag(T val, Args&&... args) { return _Flag(val) | Flag(args...); } 92 : 93 : // Set subscription dirty flags 94 : void setDirty(Flags flags = Initial, bool forwardedOnly = false); 95 : 96 : // subscribe with specific object id 97 : bool subscribe(Id); 98 : 99 : // unsubscribe object by id 100 : bool unsubscribe(Id); 101 : 102 : // check wich flags has been set dirty since last check 103 : Flags check(Id); 104 : 105 : // subscription can actually be a reference to other subscription, in this case 106 : // current subscription works as bidirectional forwarder for reference subscription 107 : void setForwardedSubscription(SubscriptionTemplate *); 108 : 109 : protected: 110 : Rc<SubscriptionTemplate> _forwarded; 111 : FlagsMap *_forwardedFlags = nullptr; 112 : FlagsMap _flags; 113 : }; 114 : 115 : /** Binding is an interface or slot for subscription, that handles 116 : * - unique id acquisition 117 : * - proper refcounting for binded subscription 118 : * - easy access and not-null check for binded subscription 119 : * 120 : */ 121 : template <class T> 122 : class Binding { 123 : public: 124 : using Id = SubscriptionId; 125 : using Flags = SubscriptionFlags; 126 : 127 : Binding(); 128 : Binding(T *); 129 : 130 : ~Binding(); 131 : 132 : Binding(const Binding<T> &); 133 : Binding &operator= (const Binding<T> &); 134 : 135 : Binding(Binding<T> &&); 136 : Binding &operator= (Binding<T> &&); 137 : 138 18074 : inline operator T * () { return get(); } 139 : inline operator T * () const { return get(); } 140 : inline explicit operator bool () const { return _subscription; } 141 : 142 : inline T * operator->() { return get(); } 143 : inline const T * operator->() const { return get(); } 144 : 145 : Flags check(); 146 : 147 : void set(T *); 148 : T *get() const; 149 : 150 : protected: 151 : Id _id; 152 : Rc<T> _subscription; 153 : }; 154 : 155 : template <typename Interface> 156 570 : void SubscriptionTemplate<Interface>::setDirty(Flags flags, bool forwardedOnly) { 157 570 : if (_forwardedFlags) { 158 75 : for (auto &it : (*_forwardedFlags)) { 159 50 : if (forwardedOnly) { 160 50 : if (_flags.find(it.first) != _flags.end()) { 161 25 : it.second |= flags; 162 : } 163 : } else { 164 0 : it.second |= flags; 165 : } 166 : } 167 : } else { 168 657 : for (auto &it : _flags) { 169 112 : it.second |= flags; 170 : } 171 : } 172 570 : } 173 : 174 : template <typename Interface> 175 583 : bool SubscriptionTemplate<Interface>::subscribe(Id id) { 176 583 : if (_forwardedFlags) { 177 25 : auto it = _forwardedFlags->find(id); 178 25 : if (it == _forwardedFlags->end()) { 179 25 : _forwardedFlags->insert(std::make_pair(id, Initial)); 180 25 : _flags.insert(std::make_pair(id, Initial)); 181 25 : return true; 182 : } 183 : } else { 184 558 : auto it = _flags.find(id); 185 558 : if (it == _flags.end()) { 186 558 : _flags.insert(std::make_pair(id, Initial)); 187 558 : return true; 188 : } 189 : } 190 0 : return false; 191 : } 192 : 193 : template <typename Interface> 194 583 : bool SubscriptionTemplate<Interface>::unsubscribe(Id id) { 195 583 : if (_forwardedFlags) { 196 25 : auto it = _forwardedFlags->find(id); 197 25 : if (it == _forwardedFlags->end()) { 198 0 : _forwardedFlags->erase(id); 199 0 : _flags.erase(id); 200 0 : return true; 201 : } 202 : } else { 203 558 : auto it = _flags.find(id); 204 558 : if (it != _flags.end()) { 205 558 : _flags.erase(id); 206 558 : return true; 207 : } 208 : } 209 25 : return false; 210 : } 211 : 212 : template <typename Interface> 213 48941 : SubscriptionFlags SubscriptionTemplate<Interface>::check(Id id) { 214 48941 : if (_forwardedFlags) { 215 25 : auto it = _forwardedFlags->find(id); 216 25 : if (it != _forwardedFlags->end()) { 217 25 : auto val = it->second; 218 25 : it->second = Flags(0); 219 25 : return val; 220 : } 221 : } else { 222 48916 : auto it = _flags.find(id); 223 48916 : if (it != _flags.end()) { 224 48916 : auto val = it->second; 225 48916 : it->second = Flags(0); 226 48916 : return val; 227 : } 228 : } 229 0 : return Flags(0); 230 : } 231 : 232 : template <typename Interface> 233 583 : void SubscriptionTemplate<Interface>::setForwardedSubscription(SubscriptionTemplate *sub) { 234 583 : if (_forwardedFlags) { 235 : // erase forwarded items from sub 236 50 : for (auto &it : _flags) { 237 25 : _forwardedFlags->erase(it.first); 238 : } 239 : } 240 583 : _forwardedFlags = nullptr; 241 583 : _forwarded = sub; 242 583 : _flags.clear(); 243 583 : if (sub) { 244 25 : _forwardedFlags = sub->_forwardedFlags ? sub->_forwardedFlags : &sub->_flags; 245 : } 246 583 : } 247 : 248 : template <class T> 249 240 : Binding<T>::Binding() : _id(T::getNextId()) { } 250 : 251 : template <class T> 252 200 : Binding<T>::Binding(T *sub) : _id(T::getNextId()), _subscription(sub) { 253 200 : if (_subscription) { 254 200 : _subscription->subscribe(_id); 255 : } 256 200 : } 257 : 258 : template <class T> 259 440 : Binding<T>::~Binding() { 260 440 : if (_subscription) { 261 210 : _subscription->unsubscribe(_id); 262 : } 263 440 : } 264 : 265 : template <class T> 266 : Binding<T>::Binding(const Binding<T> &other) : _id(T::getNextId()), _subscription(other._subscription) { 267 : if (_subscription) { 268 : _subscription->subscribe(_id); 269 : } 270 : } 271 : 272 : template <class T> 273 : Binding<T>& Binding<T>::operator= (const Binding<T> &other) { 274 : if (_subscription) { 275 : _subscription->unsubscribe(_id); 276 : } 277 : _subscription = other._subscription; 278 : if (_subscription) { 279 : _subscription->subscribe(_id); 280 : } 281 : return *this; 282 : } 283 : 284 : template <class T> 285 : Binding<T>::Binding(Binding &&other) { 286 : _id = other._id; 287 : _subscription = std::move(other._subscription); 288 : } 289 : 290 : template <class T> 291 200 : Binding<T> &Binding<T>::operator= (Binding<T> &&other) { 292 200 : if (_subscription) { 293 0 : _subscription->unsubscribe(_id); 294 : } 295 200 : _subscription = std::move(other._subscription); 296 200 : _id = other._id; 297 200 : return *this; 298 : } 299 : 300 : template <class T> 301 17864 : SubscriptionFlags Binding<T>::check() { 302 17864 : if (_subscription) { 303 17864 : return _subscription->check(_id); 304 : } 305 0 : return Flags(0); 306 : } 307 : 308 : template <class T> 309 240 : void Binding<T>::set(T *sub) { 310 240 : if (_subscription) { 311 0 : _subscription->unsubscribe(_id); 312 : } 313 240 : _subscription = sub; 314 240 : if (_subscription) { 315 10 : _subscription->subscribe(_id); 316 : } 317 240 : } 318 : 319 : template <class T> 320 19830 : T *Binding<T>::get() const { 321 19830 : return _subscription ? _subscription.get() : nullptr; 322 : } 323 : 324 : } 325 : 326 : namespace STAPPLER_VERSIONIZED stappler::mem_std { 327 : 328 : using Subscription = stappler::SubscriptionTemplate<memory::StandartInterface>; 329 : 330 : } 331 : 332 : namespace STAPPLER_VERSIONIZED stappler::mem_pool { 333 : 334 : using Subscription = stappler::SubscriptionTemplate<memory::PoolInterface>; 335 : 336 : } 337 : 338 : #endif /* STAPPLER_CORE_UTILS_SPSUBSCRIPTION_H_ */