LCOV - code coverage report
Current view: top level - core/core/utils - SPRefContainer.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 151 153 98.7 %
Date: 2024-05-12 00:16:13 Functions: 63 69 91.3 %

          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_ */

Generated by: LCOV version 1.14