Line data Source code
1 : /**
2 : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #ifndef STAPPLER_CORE_UTILS_SPREFCONTAINER_H_
25 : #define STAPPLER_CORE_UTILS_SPREFCONTAINER_H_
26 :
27 : #include "SPRef.h"
28 :
29 : namespace STAPPLER_VERSIONIZED stappler {
30 :
31 : template <typename Item>
32 : struct RefContainer {
33 : using Interface = typename Item::InterfaceType;
34 :
35 : template <typename T>
36 : using Vector = typename Interface::template VectorType<T>;
37 :
38 : static constexpr size_t ReserveItems = std::max(sizeof(Vector<Item *>) / sizeof(Item *), size_t(4));
39 : static constexpr size_t ContainerSize = std::max(sizeof(Vector<Item *>), sizeof(Item *) * ReserveItems);
40 :
41 : std::array<uint8_t, ContainerSize> _container;
42 : size_t _nitems = 0;
43 :
44 : ~RefContainer();
45 : RefContainer();
46 :
47 : Item *getItemByTag(uint32_t tag) const;
48 :
49 : Item * addItem(Item *);
50 : void removeItem(Item *);
51 :
52 : bool invalidateItemByTag(uint32_t tag);
53 : void invalidateAllItemsByTag(uint32_t tag);
54 :
55 : bool removeItemByTag(uint32_t tag);
56 : void removeAllItemsByTag(uint32_t tag);
57 :
58 : bool cleanup();
59 :
60 : template <typename Callback>
61 : void foreach(const Callback &) const;
62 :
63 : void clear();
64 :
65 : bool empty() const;
66 : size_t size() const;
67 : };
68 :
69 : template <typename Item>
70 3659 : RefContainer<Item>::~RefContainer() {
71 3659 : if (_nitems <= ReserveItems) {
72 3422 : auto target = (Item **)_container.data();
73 3422 : auto end = (Item **)(_container.data()) + _nitems;
74 3715 : while (target != end) {
75 293 : (*target)->release(0);
76 293 : ++ target;
77 : }
78 : } else {
79 237 : auto target = (Vector<Item *> *)_container.data();
80 566 : for (auto &it : *target) {
81 329 : it->release(0);
82 : }
83 237 : target->clear();
84 237 : target->~vector();
85 : }
86 3659 : }
87 :
88 : template <typename Item>
89 3659 : RefContainer<Item>::RefContainer() { }
90 :
91 : template <typename Item>
92 767 : auto RefContainer<Item>::getItemByTag(uint32_t tag) const -> Item * {
93 767 : Item *ret = nullptr;
94 767 : foreach([&] (Item *item) {
95 1698 : if (item->getTag() == tag) {
96 767 : ret = item;
97 767 : return false;
98 : }
99 931 : return true;
100 : });
101 767 : return ret;
102 : }
103 :
104 : template <typename Item>
105 6032 : auto RefContainer<Item>::addItem(Item *item) -> Item * {
106 6032 : if (_nitems < ReserveItems) {
107 5591 : auto target = _container.data() + sizeof(Item *) * _nitems;
108 5591 : item->retain();
109 5591 : memcpy(target, &item, sizeof(Item *));
110 5591 : ++ _nitems;
111 441 : } else if (_nitems > ReserveItems) {
112 204 : auto target = (Vector<Item *> *)_container.data();
113 204 : item->retain();
114 204 : target->emplace_back(item);
115 : } else {
116 : // update to Vector storage
117 237 : Vector<Item *> acts; acts.reserve(_nitems + 1);
118 237 : foreach([&] (Item *a) {
119 948 : acts.emplace_back(a);
120 948 : return true;
121 : });
122 237 : item->retain();
123 237 : acts.emplace_back(item);
124 :
125 237 : new (_container.data()) Vector<Item *>(move(acts));
126 237 : ++ _nitems;
127 237 : }
128 6032 : return item;
129 : }
130 :
131 : template <typename Item>
132 111 : void RefContainer<Item>::removeItem(Item *item) {
133 111 : if (_nitems <= ReserveItems) {
134 30 : auto target = (Item **)_container.data();
135 30 : auto end = (Item **)(_container.data()) + _nitems;
136 30 : auto it = std::remove(target, end, item);
137 30 : if (std::distance(it, end) > 0) {
138 30 : item->release(0);
139 30 : -- _nitems;
140 : }
141 : } else {
142 81 : auto target = (Vector<Item *> *)_container.data();
143 81 : auto it = target->begin();
144 303 : while (it != target->end()) {
145 303 : if (*it == item) {
146 81 : item->release(0);
147 81 : it = target->erase(it);
148 81 : break;
149 : } else {
150 222 : ++ it;
151 : }
152 : }
153 : }
154 111 : }
155 :
156 : template <typename Item>
157 60 : bool RefContainer<Item>::invalidateItemByTag(uint32_t tag) {
158 60 : bool ret = false;
159 60 : foreach([&] (Item *item) {
160 237 : if (item->getTag() == tag) {
161 60 : item->invalidate();
162 60 : ret = true;
163 60 : return false;
164 : }
165 177 : return true;
166 : });
167 60 : return ret;
168 : }
169 :
170 : template <typename Item>
171 60 : void RefContainer<Item>::invalidateAllItemsByTag(uint32_t tag) {
172 60 : foreach([&] (Item *item) {
173 300 : if (item->getTag() == tag) {
174 147 : item->invalidate();
175 : }
176 300 : return true;
177 : });
178 60 : }
179 :
180 : template <typename Item>
181 2175 : bool RefContainer<Item>::removeItemByTag(uint32_t tag) {
182 2175 : if (_nitems <= ReserveItems) {
183 2124 : auto target = (Item **)_container.data();
184 2124 : auto end = (Item **)(_container.data()) + _nitems;
185 2548 : while (target != end) {
186 2337 : if ((*target)->getTag() == tag) {
187 1913 : (*target)->invalidate();
188 1913 : (*target)->release(0);
189 1913 : if (target + 1 != end) {
190 30 : memmove(target, target + 1, (end - (target + 1)) * sizeof(Item *));
191 : }
192 1913 : -- _nitems;
193 1913 : return true;
194 : } else {
195 424 : ++ target;
196 : }
197 : }
198 : } else {
199 51 : auto target = (Vector<Item *> *)_container.data();
200 51 : auto it = target->begin();
201 102 : while (it != target->end()) {
202 102 : if ((*it)->getTag() == tag) {
203 51 : (*it)->invalidate();
204 51 : (*it)->release(0);
205 51 : it = target->erase(it);
206 51 : return true;
207 : } else {
208 51 : ++ it;
209 : }
210 : }
211 : }
212 211 : return false;
213 : }
214 :
215 : #ifdef __clang__
216 : // We ignore std::remove_if result becouse we dont need erase in this usecase
217 : #pragma clang diagnostic push
218 : #pragma clang diagnostic ignored "-Wunused-result"
219 : #endif
220 :
221 : template <typename Item>
222 130 : void RefContainer<Item>::removeAllItemsByTag(uint32_t tag) {
223 130 : if (_nitems <= ReserveItems) {
224 100 : auto target = (Item **)_container.data();
225 100 : auto end = (Item **)(_container.data()) + _nitems;
226 100 : std::remove_if(target, end, [&, this] (Item *a) {
227 211 : if (a->getTag() == tag) {
228 159 : a->invalidate();
229 159 : a->release(0);
230 159 : -- _nitems;
231 159 : return true;
232 : }
233 52 : return false;
234 : });
235 : } else {
236 30 : auto target = (Vector<Item *> *)_container.data();
237 30 : auto it = target->begin();
238 210 : while (it != target->end()) {
239 180 : if ((*it)->getTag() == tag) {
240 88 : (*it)->invalidate();
241 88 : (*it)->release(0);
242 88 : it = target->erase(it);
243 : } else {
244 92 : ++ it;
245 : }
246 : }
247 : }
248 130 : }
249 :
250 : template <typename Item>
251 44179 : bool RefContainer<Item>::cleanup() {
252 44179 : if (_nitems <= ReserveItems) {
253 41787 : auto target = (Item **)_container.data();
254 41787 : auto end = (Item **)(_container.data()) + _nitems;
255 41787 : std::remove_if(target, end, [&, this] (Item *a) {
256 43706 : if (a->isDone()) {
257 2248 : a->release(0);
258 2248 : -- _nitems;
259 2248 : return true;
260 : }
261 41458 : return false;
262 : });
263 41787 : return _nitems == 0;
264 : } else {
265 2392 : auto target = (Vector<Item *> *)_container.data();
266 2392 : auto it = target->begin();
267 9128 : while (it != target->end()) {
268 6736 : if ((*it)->isDone()) {
269 840 : (*it)->release(0);
270 840 : it = target->erase(it);
271 : } else {
272 5896 : ++ it;
273 : }
274 : }
275 2392 : return target->empty();
276 : }
277 : }
278 :
279 : #ifdef __clang__
280 : #pragma clang diagnostic pop
281 : #endif
282 :
283 : template <typename Item>
284 : template <typename Callback>
285 45941 : void RefContainer<Item>::foreach(const Callback &cb) const {
286 45941 : if (_nitems <= ReserveItems) {
287 42946 : auto target = (Item **)_container.data();
288 42946 : auto end = (Item **)(_container.data()) + _nitems;
289 89307 : while (target != end) {
290 46798 : if (!cb(*target)) {
291 437 : return;
292 : }
293 46361 : ++ target;
294 : }
295 : } else {
296 2995 : auto target = (Vector<Item *> *)_container.data();
297 11356 : for (auto &it : *target) {
298 8751 : if (!cb(it)) {
299 390 : return;
300 : }
301 : }
302 : }
303 : }
304 :
305 :
306 : template <typename Item>
307 : void RefContainer<Item>::clear() {
308 : if (_nitems <= ReserveItems) {
309 : auto target = (Item **)_container.data();
310 : auto end = (Item **)(_container.data()) + _nitems;
311 : while (target != end) {
312 : (*target)->release(0);
313 : ++ target;
314 : }
315 : } else {
316 : auto target = (Vector<Item *> *)_container.data();
317 : for (auto &it : *target) {
318 : it->release(0);
319 : }
320 : target->clear();
321 : target->~vector();
322 : }
323 : _nitems = 0;
324 : }
325 :
326 : template <typename Item>
327 : bool RefContainer<Item>::empty() const {
328 : if (_nitems <= ReserveItems) {
329 : return _nitems == 0;
330 : } else {
331 : auto target = (Vector<Item *> *)_container.data();
332 : return target->empty();
333 : }
334 : }
335 :
336 : template <typename Item>
337 21 : size_t RefContainer<Item>::size() const {
338 21 : if (_nitems <= ReserveItems) {
339 21 : return _nitems;
340 : } else {
341 0 : auto target = (Vector<Item *> *)_container.data();
342 0 : return target->size();
343 : }
344 : }
345 :
346 : }
347 :
348 : #endif /* STAPPLER_CORE_UTILS_SPREFCONTAINER_H_ */
|