1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial |
3 | |
4 | #pragma once |
5 | |
6 | #include <cstddef> |
7 | #include <new> |
8 | #include <algorithm> |
9 | #include <optional> |
10 | #include <atomic> |
11 | |
12 | #ifdef __APPLE__ |
13 | # include <AvailabilityMacros.h> |
14 | #endif |
15 | |
16 | namespace vtable { |
17 | |
18 | template<typename T> |
19 | struct VRefMut |
20 | { |
21 | const T *vtable; |
22 | void *instance; |
23 | }; |
24 | |
25 | struct Layout |
26 | { |
27 | std::size_t size; |
28 | std::size_t align; |
29 | }; |
30 | |
31 | // For the C++'s purpose, they are all the same |
32 | template<typename T> |
33 | using VRef = VRefMut<T>; |
34 | |
35 | template<typename T> |
36 | using Pin = T; |
37 | |
38 | template<typename T> |
39 | struct VBox |
40 | { |
41 | const T *vtable = nullptr; |
42 | void *instance = nullptr; |
43 | VBox(const VBox &) = delete; |
44 | VBox() = default; |
45 | VBox &operator=(const VBox &) = delete; |
46 | ~VBox() |
47 | { |
48 | if (vtable && instance) { |
49 | vtable->drop({ vtable, instance }); |
50 | } |
51 | } |
52 | }; |
53 | |
54 | struct AllowPin; |
55 | |
56 | template<typename Base, typename T, typename Flag = void> |
57 | struct VOffset |
58 | { |
59 | const T *vtable; |
60 | std::uintptr_t offset; |
61 | }; |
62 | |
63 | template<typename VTable, typename X> |
64 | struct VRcInner |
65 | { |
66 | template<typename VTable_, typename X_> |
67 | friend class VRc; |
68 | template<typename VTable_, typename X_> |
69 | friend class VWeak; |
70 | |
71 | private: |
72 | VRcInner() : layout {} { } |
73 | const VTable *vtable = &X::static_vtable; |
74 | std::atomic<int> strong_ref = 1; |
75 | std::atomic<int> weak_ref = 1; |
76 | std::uint16_t data_offset = offsetof(VRcInner, data); |
77 | union { |
78 | X data; |
79 | Layout layout; |
80 | }; |
81 | |
82 | void *data_ptr() { return reinterpret_cast<char *>(this) + data_offset; } |
83 | ~VRcInner() = delete; |
84 | }; |
85 | |
86 | struct Dyn |
87 | { |
88 | }; |
89 | |
90 | template<typename VTable, typename X = Dyn> |
91 | class VRc |
92 | { |
93 | VRcInner<VTable, X> *inner; |
94 | VRc(VRcInner<VTable, X> *inner) : inner(inner) { } |
95 | template<typename VTable_, typename X_> |
96 | friend class VWeak; |
97 | |
98 | public: |
99 | ~VRc() |
100 | { |
101 | if (!--inner->strong_ref) { |
102 | Layout layout = inner->vtable->drop_in_place({ inner->vtable, &inner->data }); |
103 | layout.size += inner->data_offset; |
104 | layout.align = std::max<size_t>(a: layout.align, b: alignof(VRcInner<VTable, Dyn>)); |
105 | inner->layout = layout; |
106 | if (!--inner->weak_ref) { |
107 | inner->vtable->dealloc(inner->vtable, reinterpret_cast<uint8_t *>(inner), layout); |
108 | } |
109 | } |
110 | } |
111 | VRc(const VRc &other) : inner(other.inner) { inner->strong_ref++; } |
112 | VRc &operator=(const VRc &other) |
113 | { |
114 | if (inner == other.inner) |
115 | return *this; |
116 | this->~VRc(); |
117 | new (this) VRc(other); |
118 | return *this; |
119 | } |
120 | /// Construct a new VRc holding an X. |
121 | /// |
122 | /// The type X must have a static member `static_vtable` of type VTable |
123 | template<typename... Args> |
124 | static VRc make(Args... args) |
125 | { |
126 | #if !defined(__APPLE__) || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14 |
127 | auto mem = ::operator new(sizeof(VRcInner<VTable, X>), |
128 | static_cast<std::align_val_t>(alignof(VRcInner<VTable, X>))); |
129 | #else |
130 | auto mem = ::operator new(sizeof(VRcInner<VTable, X>)); |
131 | #endif |
132 | auto inner = new (mem) VRcInner<VTable, X>; |
133 | new (&inner->data) X(args...); |
134 | return VRc(inner); |
135 | } |
136 | |
137 | const X *operator->() const |
138 | { |
139 | return &inner->data; |
140 | } |
141 | const X &operator*() const |
142 | { |
143 | return inner->data; |
144 | } |
145 | X *operator->() |
146 | { |
147 | return &inner->data; |
148 | } |
149 | X &operator*() |
150 | { |
151 | return inner->data; |
152 | } |
153 | |
154 | const VRc<VTable, Dyn> &into_dyn() const |
155 | { |
156 | return *reinterpret_cast<const VRc<VTable, Dyn> *>(this); |
157 | } |
158 | |
159 | VRef<VTable> borrow() const |
160 | { |
161 | return { inner->vtable, inner->data_ptr() }; |
162 | } |
163 | |
164 | friend bool operator==(const VRc &a, const VRc &b) |
165 | { |
166 | return a.inner == b.inner; |
167 | } |
168 | friend bool operator!=(const VRc &a, const VRc &b) |
169 | { |
170 | return a.inner != b.inner; |
171 | } |
172 | const VTable *vtable() const |
173 | { |
174 | return inner->vtable; |
175 | } |
176 | }; |
177 | |
178 | template<typename VTable, typename X = Dyn> |
179 | class VWeak |
180 | { |
181 | VRcInner<VTable, X> *inner = nullptr; |
182 | |
183 | public: |
184 | VWeak() = default; |
185 | ~VWeak() |
186 | { |
187 | if (inner && !--inner->weak_ref) { |
188 | inner->vtable->dealloc(inner->vtable, reinterpret_cast<uint8_t *>(inner), |
189 | inner->layout); |
190 | } |
191 | } |
192 | VWeak(const VWeak &other) : inner(other.inner) { inner && inner->weak_ref++; } |
193 | VWeak(const VRc<VTable, X> &other) : inner(other.inner) { inner && inner->weak_ref++; } |
194 | VWeak &operator=(const VWeak &other) |
195 | { |
196 | if (inner == other.inner) |
197 | return *this; |
198 | this->~VWeak(); |
199 | new (this) VWeak(other); |
200 | return *this; |
201 | } |
202 | |
203 | std::optional<VRc<VTable, X>> lock() const |
204 | { |
205 | if (!inner || inner->strong_ref == 0) |
206 | return {}; |
207 | inner->strong_ref++; |
208 | return { VRc<VTable, X>(inner) }; |
209 | } |
210 | |
211 | const VWeak<VTable, Dyn> &into_dyn() const |
212 | { |
213 | return *reinterpret_cast<const VWeak<VTable, Dyn> *>(this); |
214 | } |
215 | |
216 | friend bool operator==(const VWeak &a, const VWeak &b) { return a.inner == b.inner; } |
217 | friend bool operator!=(const VWeak &a, const VWeak &b) { return a.inner != b.inner; } |
218 | const VTable *vtable() const { return inner ? inner->vtable : nullptr; } |
219 | }; |
220 | |
221 | } // namespace vtable |
222 | |