1 // This file is part of Desktop App Toolkit, 2 // a set of libraries for developing nice desktop applications. 3 // 4 // For license and copyright information please follow this link: 5 // https://github.com/desktop-app/legal/blob/master/LEGAL 6 // 7 #pragma once 8 9 template <typename Base> 10 class RuntimeComposer; 11 12 class RuntimeComposerBase; 13 typedef void(*RuntimeComponentConstruct)(void *location, RuntimeComposerBase *composer); 14 typedef void(*RuntimeComponentDestruct)(void *location); 15 typedef void(*RuntimeComponentMove)(void *location, void *waslocation); 16 17 struct RuntimeComponentWrapStruct { 18 // Don't init any fields, because it is only created in 19 // global scope, so it will be filled by zeros from the start. 20 RuntimeComponentWrapStruct() = default; RuntimeComponentWrapStructRuntimeComponentWrapStruct21 RuntimeComponentWrapStruct(std::size_t size, std::size_t align, RuntimeComponentConstruct construct, RuntimeComponentDestruct destruct, RuntimeComponentMove move) 22 : Size(size) 23 , Align(align) 24 , Construct(construct) 25 , Destruct(destruct) 26 , Move(move) { 27 } 28 std::size_t Size; 29 std::size_t Align; 30 RuntimeComponentConstruct Construct; 31 RuntimeComponentDestruct Destruct; 32 RuntimeComponentMove Move; 33 }; 34 35 template <int Value, int Denominator> 36 struct CeilDivideMinimumOne { 37 static constexpr int Result = ((Value / Denominator) + ((!Value || (Value % Denominator)) ? 1 : 0)); 38 }; 39 40 extern RuntimeComponentWrapStruct RuntimeComponentWraps[64]; 41 extern QAtomicInt RuntimeComponentIndexLast; 42 43 template <typename Type, typename Base> 44 struct RuntimeComponent { 45 using RuntimeComponentBase = Base; 46 RuntimeComponentRuntimeComponent47 RuntimeComponent() { 48 // While there is no std::aligned_alloc(). 49 static_assert(alignof(Type) <= alignof(std::max_align_t), "Components should align to std::max_align_t!"); 50 } 51 RuntimeComponent(const RuntimeComponent &other) = delete; 52 RuntimeComponent &operator=(const RuntimeComponent &other) = delete; 53 RuntimeComponent(RuntimeComponent &&other) = delete; 54 RuntimeComponent &operator=(RuntimeComponent &&other) = default; 55 IndexRuntimeComponent56 static int Index() { 57 static QAtomicInt MyIndex(0); 58 if (auto index = MyIndex.loadAcquire()) { 59 return index - 1; 60 } 61 while (true) { 62 auto last = RuntimeComponentIndexLast.loadAcquire(); 63 if (RuntimeComponentIndexLast.testAndSetOrdered(last, last + 1)) { 64 Assert(last < 64); 65 if (MyIndex.testAndSetOrdered(0, last + 1)) { 66 RuntimeComponentWraps[last] = RuntimeComponentWrapStruct( 67 sizeof(Type), 68 alignof(Type), 69 Type::RuntimeComponentConstruct, 70 Type::RuntimeComponentDestruct, 71 Type::RuntimeComponentMove); 72 } 73 break; 74 } 75 } 76 return MyIndex.loadAcquire() - 1; 77 } BitRuntimeComponent78 static uint64 Bit() { 79 return (1ULL << Index()); 80 } 81 82 protected: RuntimeComponentConstructRuntimeComponent83 static void RuntimeComponentConstruct(void *location, RuntimeComposerBase *composer) { 84 new (location) Type(); 85 } RuntimeComponentDestructRuntimeComponent86 static void RuntimeComponentDestruct(void *location) { 87 ((Type*)location)->~Type(); 88 } RuntimeComponentMoveRuntimeComponent89 static void RuntimeComponentMove(void *location, void *waslocation) { 90 *(Type*)location = std::move(*(Type*)waslocation); 91 } 92 93 }; 94 95 class RuntimeComposerMetadata { 96 public: RuntimeComposerMetadata(uint64 mask)97 RuntimeComposerMetadata(uint64 mask) : _mask(mask) { 98 for (int i = 0; i != 64; ++i) { 99 auto componentBit = (1ULL << i); 100 if (_mask & componentBit) { 101 auto componentSize = RuntimeComponentWraps[i].Size; 102 if (componentSize) { 103 auto componentAlign = RuntimeComponentWraps[i].Align; 104 if (auto badAlign = (size % componentAlign)) { 105 size += (componentAlign - badAlign); 106 } 107 offsets[i] = size; 108 size += componentSize; 109 accumulate_max(align, componentAlign); 110 } 111 } else if (_mask < componentBit) { 112 last = i; 113 break; 114 } 115 } 116 } 117 118 // Meta pointer in the start. 119 std::size_t size = sizeof(const RuntimeComposerMetadata*); 120 std::size_t align = alignof(const RuntimeComposerMetadata*); 121 std::size_t offsets[64] = { 0 }; 122 int last = 64; 123 equals(uint64 mask)124 bool equals(uint64 mask) const { 125 return _mask == mask; 126 } maskadd(uint64 mask)127 uint64 maskadd(uint64 mask) const { 128 return _mask | mask; 129 } maskremove(uint64 mask)130 uint64 maskremove(uint64 mask) const { 131 return _mask & (~mask); 132 } 133 134 private: 135 uint64 _mask; 136 137 }; 138 139 const RuntimeComposerMetadata *GetRuntimeComposerMetadata(uint64 mask); 140 141 class RuntimeComposerBase { 142 public: _data(zerodata ())143 RuntimeComposerBase(uint64 mask = 0) : _data(zerodata()) { 144 if (mask) { 145 auto meta = GetRuntimeComposerMetadata(mask); 146 147 auto data = operator new(meta->size); 148 Assert(data != nullptr); 149 150 _data = data; 151 _meta() = meta; 152 for (int i = 0; i < meta->last; ++i) { 153 auto offset = meta->offsets[i]; 154 if (offset >= sizeof(_meta())) { 155 try { 156 auto constructAt = _dataptrunsafe(offset); 157 auto space = RuntimeComponentWraps[i].Size; 158 auto alignedAt = constructAt; 159 std::align(RuntimeComponentWraps[i].Align, space, alignedAt, space); 160 Assert(alignedAt == constructAt); 161 RuntimeComponentWraps[i].Construct(constructAt, this); 162 } catch (...) { 163 while (i > 0) { 164 --i; 165 offset = meta->offsets[--i]; 166 if (offset >= sizeof(_meta())) { 167 RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset)); 168 } 169 } 170 throw; 171 } 172 } 173 } 174 } 175 } 176 RuntimeComposerBase(const RuntimeComposerBase &other) = delete; 177 RuntimeComposerBase &operator=(const RuntimeComposerBase &other) = delete; ~RuntimeComposerBase()178 ~RuntimeComposerBase() { 179 if (_data != zerodata()) { 180 auto meta = _meta(); 181 for (int i = 0; i < meta->last; ++i) { 182 auto offset = meta->offsets[i]; 183 if (offset >= sizeof(_meta())) { 184 RuntimeComponentWraps[i].Destruct(_dataptrunsafe(offset)); 185 } 186 } 187 operator delete(_data); 188 } 189 } 190 191 protected: 192 bool UpdateComponents(uint64 mask = 0) { 193 if (_meta()->equals(mask)) { 194 return false; 195 } 196 RuntimeComposerBase result(mask); 197 result.swap(*this); 198 if (_data != zerodata() && result._data != zerodata()) { 199 const auto meta = _meta(); 200 const auto wasmeta = result._meta(); 201 for (auto i = 0; i != meta->last; ++i) { 202 const auto offset = meta->offsets[i]; 203 const auto wasoffset = wasmeta->offsets[i]; 204 if (offset >= sizeof(_meta()) 205 && wasoffset >= sizeof(_meta())) { 206 RuntimeComponentWraps[i].Move( 207 _dataptrunsafe(offset), 208 result._dataptrunsafe(wasoffset)); 209 } 210 } 211 } 212 return true; 213 } 214 bool AddComponents(uint64 mask = 0) { 215 return UpdateComponents(_meta()->maskadd(mask)); 216 } 217 bool RemoveComponents(uint64 mask = 0) { 218 return UpdateComponents(_meta()->maskremove(mask)); 219 } 220 221 private: 222 template <typename Base> 223 friend class RuntimeComposer; 224 225 static const RuntimeComposerMetadata *ZeroRuntimeComposerMetadata; zerodata()226 static void *zerodata() { 227 return &ZeroRuntimeComposerMetadata; 228 } 229 _dataptrunsafe(int skip)230 void *_dataptrunsafe(int skip) const { 231 return (char*)_data + skip; 232 } _dataptr(int skip)233 void *_dataptr(int skip) const { 234 return (skip >= sizeof(_meta())) ? _dataptrunsafe(skip) : nullptr; 235 } _meta()236 const RuntimeComposerMetadata *&_meta() const { 237 return *static_cast<const RuntimeComposerMetadata**>(_data); 238 } 239 void *_data = nullptr; 240 swap(RuntimeComposerBase & other)241 void swap(RuntimeComposerBase &other) { 242 std::swap(_data, other._data); 243 } 244 245 }; 246 247 template <typename Base> 248 class RuntimeComposer : public RuntimeComposerBase { 249 public: 250 using RuntimeComposerBase::RuntimeComposerBase; 251 252 template < 253 typename Type, 254 typename = std::enable_if_t<std::is_same_v< 255 typename Type::RuntimeComponentBase, 256 Base>>> Has()257 bool Has() const { 258 return (_meta()->offsets[Type::Index()] >= sizeof(_meta())); 259 } 260 261 template < 262 typename Type, 263 typename = std::enable_if_t<std::is_same_v< 264 typename Type::RuntimeComponentBase, 265 Base>>> Get()266 Type *Get() { 267 return static_cast<Type*>(_dataptr(_meta()->offsets[Type::Index()])); 268 } 269 template < 270 typename Type, 271 typename = std::enable_if_t<std::is_same_v< 272 typename Type::RuntimeComponentBase, 273 Base>>> Get()274 const Type *Get() const { 275 return static_cast<const Type*>(_dataptr(_meta()->offsets[Type::Index()])); 276 } 277 278 }; 279