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