LCOV - code coverage report
Current view: top level - core/core/memory - SPMemStorageMemImpl.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 242 266 91.0 %
Date: 2024-05-12 00:16:13 Functions: 2295 2799 82.0 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2017-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_MEMORY_SPMEMSTORAGEMEMIMPL_H_
      25             : #define STAPPLER_CORE_MEMORY_SPMEMSTORAGEMEMIMPL_H_
      26             : 
      27             : #include "SPMemAlloc.h"
      28             : 
      29             : namespace STAPPLER_VERSIONIZED stappler::memory::impl {
      30             : 
      31             : // small object optimization type (based on SSO-23: https://github.com/elliotgoodrich/SSO-23)
      32             : template <typename Type, size_t ByteCount>
      33             : struct mem_small {
      34             :         using self = mem_small<Type, ByteCount>;
      35             :         using pointer = Type *;
      36             :         using const_pointer = const Type *;
      37             :         using size_type = size_t;
      38             :         using allocator = Allocator<Type>;
      39             : 
      40   294569252 :         static constexpr size_type max_capacity() { return (sizeof(Type) < ByteCount) ? ((ByteCount - 1) / sizeof(Type)) : 0; }
      41             : 
      42    16612093 :         void assign(allocator &a, const_pointer ptr, size_type s) {
      43    16612093 :                 const auto current = size();
      44    16612092 :                 a.copy_rewrite(data(), size(), ptr, s);
      45    16612094 :                 if (current > s) {
      46    15997141 :                         a.destroy(data() + s, current - s);
      47             :                 }
      48    16612095 :                 set_size(s);
      49    16612092 :         }
      50             : 
      51     7263198 :         void move_assign(allocator &a, pointer source, size_type count) {
      52     7263198 :                 const auto current = size();
      53     7263200 :                 a.move_rewrite(data(), current, source, count);
      54     7263197 :                 if (current > count) {
      55           0 :                         a.destroy(data() + count, current - count);
      56             :                 }
      57     7263197 :                 set_size(count);
      58     7263195 :         }
      59             : 
      60    15991475 :         void force_clear() {
      61    15991475 :                 set_size(0);
      62    15991532 :         }
      63             : 
      64    40240986 :         void drop_unused() {
      65    40240986 :                 const auto unused = storage[ByteCount - 1];
      66    40241084 :                 if (unused < max_capacity()) {
      67    24249559 :                         const auto s = max_capacity() - unused;
      68             :                         // data is already garbage, bypass -Wclass-memaccess
      69    24249562 :                         memset((void *)(storage.data() + s * sizeof(Type)), 0, unused * sizeof(Type));
      70             :                 }
      71    40241090 :         }
      72             : 
      73    40240995 :         void set_size(size_t s) {
      74    40240995 :                 storage[ByteCount - 1] = uint8_t(max_capacity() - s);
      75    40240996 :                 drop_unused();
      76    40241082 :         }
      77             : 
      78     5921783 :         size_t modify_size(intptr_t diff) {
      79     5921783 :                 storage[ByteCount - 1] = uint8_t(max_capacity() - (size() + diff));
      80     5921951 :                 return size();
      81             :         }
      82             : 
      83   144655328 :         size_t size() const { return max_capacity() - storage[ByteCount - 1]; }
      84             : 
      85    11681222 :         size_t capacity() const { return max_capacity(); }
      86             : 
      87    79769329 :         pointer data() { return (pointer)storage.data(); }
      88    67787095 :         const_pointer data() const { return (const_pointer)storage.data(); }
      89             : 
      90             :         std::array<uint8_t, ByteCount> storage;
      91             : };
      92             : 
      93             : template <typename Type, size_t Extra = 0>
      94             : class mem_large {
      95             : public:
      96             :         using self = mem_large<Type, Extra>;
      97             :         using pointer = Type *;
      98             :         using const_pointer = const Type *;
      99             :         using size_type = size_t;
     100             :         using allocator = Allocator<Type>;
     101             : 
     102    28517909 :         mem_large() = default;
     103             :         mem_large(const self &) = default;
     104             :         self & operator= (const self &other) = default;
     105             : 
     106           0 :         mem_large(self &&other) {
     107           0 :                 _ptr = other._ptr;
     108           0 :                 _used = other._used;
     109           0 :                 _allocated = other._allocated;
     110           0 :                 other._ptr = nullptr;
     111           0 :                 other._used = 0;
     112           0 :                 other._allocated = 0;
     113           0 :         }
     114             : 
     115    29648563 :         self & operator= (self &&other) {
     116    29648563 :                 _ptr = other._ptr;
     117    29648563 :                 _used = other._used;
     118    29648563 :                 _allocated = other._allocated;
     119    29648563 :                 other._ptr = nullptr;
     120    29648563 :                 other._used = 0;
     121    29648563 :                 other._allocated = 0;
     122    29648563 :                 return *this;
     123             :         }
     124             : 
     125     2846868 :         void assign(allocator &a, const_pointer ptr, size_type count) {
     126     2846868 :                 if (_allocated < count) {
     127     2846868 :                         reserve(a, count);
     128             :                 }
     129     2846868 :                 a.copy_rewrite(data(), size(), ptr, count);
     130     2846868 :                 if (_used > count) {
     131           0 :                         a.destroy(data() + count, _used - count);
     132             :                 }
     133     2846868 :                 _used = count;
     134     2846868 :                 drop_unused();
     135     2846868 :         }
     136             : 
     137      436355 :         void move_assign(allocator &a, pointer ptr, size_type count) {
     138      436355 :                 if (_allocated < count) {
     139           0 :                         reserve(a, count);
     140             :                 }
     141      436355 :                 a.move_rewrite(_ptr, _used, ptr, count);
     142      436376 :                 if (_used > count) {
     143           0 :                         a.destroy(data() + count, _used - count);
     144             :                 }
     145      436376 :                 _used = count;
     146      436376 :                 drop_unused();
     147      436393 :         }
     148             : 
     149             :         void assign_weak(pointer ptr, size_type s) {
     150             :                 _ptr = ptr;
     151             :                 _used = s;
     152             :                 _allocated = 0;
     153             :         }
     154             : 
     155       42774 :         void assign_weak(const_pointer ptr, size_type s) {
     156       42774 :                 _ptr = const_cast<pointer>(ptr);
     157       42774 :                 _used = s;
     158       42774 :                 _allocated = 0;
     159       42774 :         }
     160             : 
     161             :         void assign_mem(pointer ptr, size_type s, size_type nalloc) {
     162             :                 _ptr = ptr;
     163             :                 _used = s;
     164             :                 _allocated = nalloc - Extra;
     165             :         }
     166             : 
     167             :         bool is_weak() const noexcept {
     168             :                 return _used > 0 && _allocated == 0;
     169             :         }
     170             : 
     171     5102748 :         void reserve(allocator &a, size_type s) {
     172     5102748 :                 grow_alloc(a, s);
     173     5101856 :                 drop_unused();
     174     5102061 :         }
     175             : 
     176     8580276 :         void clear_dealloc(allocator &a) {
     177     8580276 :                 if (_ptr) {
     178      955165 :                         if (_used) {
     179      884135 :                                 a.destroy(_ptr, _used);
     180             :                         }
     181      955148 :                         if (_allocated) {
     182      912388 :                                 a.deallocate(_ptr, _allocated + Extra);
     183             :                         }
     184             :                 }
     185     8580202 :                 _ptr = nullptr;
     186     8580202 :                 _used = 0;
     187     8580202 :                 _allocated = 0;
     188     8580202 :         }
     189             : 
     190     7777095 :         void force_clear() {
     191     7777095 :                 _ptr = nullptr;
     192     7777095 :                 _used = 0;
     193     7777095 :                 _allocated = 0;
     194     7777095 :         }
     195             : 
     196             :         pointer extract() {
     197             :                 auto ret = _ptr;
     198             :                 force_clear();
     199             :                 return ret;
     200             :         }
     201             : 
     202     8392884 :         void drop_unused() {
     203     8392884 :                 if (_allocated > 0 && _allocated >= _used && _ptr) {
     204             :                         // data is already garbage, bypass -Wclass-memaccess
     205     8389725 :                         memset((void *)(_ptr + _used), 0, (_allocated - _used + Extra) * sizeof(Type));
     206             :                 }
     207     8392884 :         }
     208             : 
     209     5102534 :         void grow_alloc(allocator &a, size_type newsize) {
     210     5102534 :                 size_t alloc_size = newsize + Extra;
     211             : 
     212             :                 // use extra memory if provided by allocator
     213     5102534 :                 size_t allocated = 0; // real memory block size returned
     214     5102534 :                 auto ptr = a.__allocate(alloc_size, allocated);
     215     5101987 :                 alloc_size = allocated / sizeof(Type);
     216             : 
     217     5101987 :                 if (_used > 0 && _ptr) {
     218     1282970 :                         a.move(ptr, _ptr, _used);
     219             :                 }
     220             : 
     221     5101848 :                 if (_ptr && _allocated > 0) {
     222     1282866 :                         a.deallocate(_ptr, _allocated);
     223             :                 }
     224             : 
     225     5101878 :                 _ptr = ptr;
     226     5101878 :                 _allocated = alloc_size - Extra;
     227     5101878 :         }
     228             : 
     229  1038514509 :         size_t modify_size(intptr_t diff) {
     230  1038514509 :                 _used += diff;
     231  1038514509 :                 return _used;
     232             :         }
     233             : 
     234     8444980 :         void set_size(size_t s) {
     235     8444980 :                 if (s < _used) {
     236        3978 :                         _used = s;
     237        3978 :                         drop_unused();
     238             :                 } else {
     239     8441002 :                         _used = s;
     240             :                 }
     241     8444980 :         }
     242             : 
     243  1209182007 :         size_t size() const noexcept { return _used; }
     244   545220531 :         size_t capacity() const noexcept { return _allocated; }
     245             : 
     246  1813316471 :         pointer data() noexcept { return _ptr; }
     247    14905601 :         const_pointer data() const noexcept { return _ptr; }
     248             : 
     249    23112804 :         bool empty() const noexcept { return _ptr == nullptr || _used == 0; }
     250             : 
     251             : protected:
     252             :         pointer _ptr = nullptr;
     253             :         size_type _used = 0; // in elements
     254             :         size_type _allocated = 0; // in elements
     255             : };
     256             : 
     257             : template <typename Type, size_t Extra, bool UseSoo>
     258             : class mem_soo_iface;
     259             : 
     260             : template <typename Type, size_t Extra>
     261             : class mem_soo_iface<Type, Extra, false> : public mem_large<Type, Extra> {
     262             : public:
     263             :         using base = mem_large<Type, Extra>;
     264             :         using pointer = typename base::pointer;
     265             :         using const_pointer = typename base::const_pointer;
     266             :         using size_type = typename base::size_type;
     267             :         using allocator = typename base::allocator;
     268             : 
     269             :         static constexpr size_type get_soo_size() { return 0; }
     270             : 
     271      413664 :         mem_soo_iface(const allocator &alloc) : _allocator(alloc) { }
     272             : 
     273      360406 :         ~mem_soo_iface() noexcept { clear_dealloc(_allocator); }
     274             : 
     275        4750 :         void assign(const_pointer ptr, size_type size) {
     276        4750 :                 reserve(size, false);
     277        4750 :                 _allocator.copy_rewrite(_ptr, _used, ptr, size);
     278        4750 :                 if (_used > size) {
     279           0 :                         _allocator.destroy(data() + size, _used - size);
     280             :                 }
     281        4750 :                 _used = size;
     282        4750 :                 drop_unused();
     283        4750 :         }
     284             : 
     285             :         using base::assign_weak;
     286             :         using base::assign_mem;
     287             :         using base::is_weak;
     288             : 
     289             :         using base::data;
     290             :         using base::size;
     291             :         using base::capacity;
     292             : 
     293             :         // reserve memory block, optimal for realloc/free
     294             :         // useful for small temporary buffers
     295             :         // this memory block can be reused by next temporary buffer of same size
     296             :         // so, no pool memory will be leaked
     297         625 :         pointer reserve_block_optimal() {
     298         625 :                 auto target = mempool::custom::BlockThreshold / sizeof(Type) + 1;
     299         625 :                 return reserve(target);
     300             :         }
     301             : 
     302     2118043 :         pointer reserve(size_type s, bool grow = false) {
     303     2118043 :                 if (s > 0 && s > _allocated) {
     304      236814 :                         auto newmem = (grow ? max(s, _allocated * 2) : s);
     305      236814 :                         base::reserve(_allocator, newmem);
     306             :                 }
     307     2118042 :                 return _ptr;
     308             :         }
     309             : 
     310      843555 :         void clear() {
     311      843555 :                 if (_used > 0 && _allocated > 0) {
     312       29449 :                         if (_ptr) {
     313       29449 :                                 _allocator.destroy(_ptr, _used);
     314             :                         }
     315             :                 } else {
     316      814106 :                         if (_allocated == 0) {
     317       12399 :                                 _ptr = nullptr;
     318             :                         }
     319             :                 }
     320      843555 :                 _used = 0;
     321      843555 :         }
     322             : 
     323             :         using base::force_clear;
     324             :         using base::extract;
     325             : 
     326             : protected:
     327       78875 :         void perform_move(mem_soo_iface &&other) {
     328       78875 :                 *(static_cast<base *>(this)) = std::move(other);
     329       78874 :         }
     330             : 
     331             :         using base::clear_dealloc;
     332             :         using base::modify_size;
     333             :         using base::set_size;
     334             :         using base::drop_unused;
     335             : 
     336             :         using base::_ptr;
     337             :         using base::_allocated;
     338             :         using base::_used;
     339             :         allocator _allocator;
     340             : };
     341             : 
     342             : template <typename Type, size_t Extra>
     343             : class mem_soo_iface<Type, Extra, true> {
     344             : public:
     345             :         using pointer = Type *;
     346             :         using const_pointer = const Type *;
     347             : 
     348             :         using size_type = size_t;
     349             :         using allocator = Allocator<Type>;
     350             : 
     351             :         using large_mem = mem_large<Type, Extra>;
     352             :         using small_mem = mem_small<Type, sizeof(large_mem)>;
     353             : 
     354       53325 :         static constexpr size_type get_soo_size() { return small_mem::max_capacity(); }
     355             : 
     356    28104204 :         mem_soo_iface(const allocator &alloc) : _allocator(alloc) {
     357    28104204 :                 set_large_flag();
     358    28104271 :                 _large = large_mem();
     359    28104664 :         }
     360             : 
     361     9287744 :         ~mem_soo_iface() noexcept {
     362     9287744 :                 if (is_large()) {
     363     7067522 :                         _large.clear_dealloc(_allocator);
     364             :                 }
     365     9287655 :         }
     366             : 
     367    19900011 :         void assign(const_pointer ptr, size_type size) {
     368    19900011 :                 if (!ptr || size == 0) {
     369      441044 :                         clear();
     370    19458967 :                 } else if (size <= small_mem::max_capacity() && (is_small() || empty())) {
     371    16612100 :                         set_small_flag();
     372    16612097 :                         _small.assign(_allocator, ptr, size);
     373     2846867 :                 } else if (size > small_mem::max_capacity() && (is_large() || empty())) {
     374     2846868 :                         set_large_flag();
     375     2846868 :                         _large.assign(_allocator, ptr, size);
     376             :                 } else {
     377           0 :                         if (is_small()) {
     378           0 :                                 large_mem new_large;
     379           0 :                                 new_large.assign(_allocator, ptr, size);
     380           0 :                                 set_large_flag_force();
     381           0 :                                 _large = std::move(new_large);
     382             :                         } else {
     383           0 :                                 large_mem old_large(std::move(_large));
     384           0 :                                 _small.force_clear();
     385           0 :                                 set_small_flag();
     386           0 :                                 _small.assign(_allocator, ptr, size);
     387           0 :                                 old_large.clear_dealloc(_allocator);
     388             :                         }
     389             :                 }
     390    19900009 :         }
     391             : 
     392             :         void assign_weak(pointer ptr, size_type s) {
     393             :                 if (s <= small_mem::max_capacity()) {
     394             :                         assign(ptr, s);
     395             :                 } else {
     396             :                         set_large_flag_force();
     397             :                         _large.assign_weak(ptr, s);
     398             :                 }
     399             :         }
     400             : 
     401       61820 :         void assign_weak(const_pointer ptr, size_type s) {
     402       61820 :                 if (s <= small_mem::max_capacity()) {
     403       19049 :                         assign(ptr, s);
     404             :                 } else {
     405       42773 :                         set_large_flag_force();
     406       42773 :                         _large.assign_weak(ptr, s);
     407             :                 }
     408       61820 :         }
     409             : 
     410             :         void assign_mem(pointer ptr, size_type s, size_type nalloc) {
     411             :                 set_large_flag_force();
     412             :                 _large.assign_mem(ptr, s, nalloc);
     413             :         }
     414             : 
     415             :         // Проверка, владеем ли блоком памяти
     416             :         bool is_weak() const noexcept {
     417             :                 return is_large() && _large.is_weak();
     418             :         }
     419             : 
     420   548995554 :         pointer reserve(size_type s, bool grow = false) {
     421   548995554 :                 const auto _allocated = capacity();
     422   548990967 :                 const auto _used = size();
     423   548995516 :                 if (s > _allocated) {
     424     3483708 :                         if (s <= small_mem::max_capacity() && _used <= small_mem::max_capacity()) {
     425     1465084 :                                 if (_allocated == 0) { // память пустая или CoW
     426     1465091 :                                         set_small_flag();
     427     1465110 :                                         if (_large.data() != nullptr) { // CoW
     428           0 :                                                 _small.move_assign(_allocator, _large.data(), _large.size());
     429             :                                         } else { // empty
     430     1465106 :                                                 _small.force_clear();
     431             :                                         }
     432             :                                 }
     433     1465139 :                                 return _small.data();
     434     2019262 :                         } else if (s > 0) {
     435     2019238 :                                 auto newmem = (grow ? max(s, _allocated * 2) : s);
     436     2019257 :                                 if (is_small() && newmem > small_mem::max_capacity()) {
     437      436519 :                                         large_mem new_large;
     438      436519 :                                         new_large.reserve(_allocator, newmem);
     439      436384 :                                         new_large.move_assign(_allocator, _small.data(), _small.size());
     440      436369 :                                         set_large_flag();
     441      436394 :                                         _large = std::move(new_large);
     442             :                                 } else {
     443     1583087 :                                         _large.reserve(_allocator, newmem);
     444             :                                 }
     445     2018816 :                                 return _large.data();
     446             :                         }
     447             :                 }
     448   545511832 :                 return data();
     449             :         }
     450             : 
     451     7906630 :         void clear() {
     452     7906630 :                 const auto _used = size();
     453     7906621 :                 const auto _allocated = capacity();
     454     7906616 :                 auto _ptr = data();
     455     7906610 :                 if (_used > 0 && _allocated > 0 && _ptr) {
     456        6075 :                         _allocator.destroy(_ptr, _used);
     457             :                 } else {
     458     7900535 :                         if (_allocated == 0 && is_large()) { // проверяем и очищаем CoW
     459      506747 :                                 _large.force_clear();
     460             :                         }
     461             :                 }
     462             : 
     463     7906610 :                 if (is_large()) {
     464      637617 :                         _large.set_size(0);
     465             :                 } else {
     466     7268998 :                         set_large_flag();
     467     7268998 :                         _large.force_clear();
     468             :                 }
     469     7906616 :         }
     470             : 
     471        1350 :         void force_clear() {
     472        1350 :                 if (is_small()) {
     473        1050 :                         set_large_flag();
     474             :                 }
     475        1350 :                 _large.force_clear();
     476        1350 :         }
     477             : 
     478             :         pointer extract() {
     479             :                 if (is_large()) {
     480             :                         return _large.extract();
     481             :                 } else {
     482             :                         auto s = _small.size();
     483             :                         auto ptr = _allocator.allocate(s + Extra);
     484             :                         _allocator.move(ptr, _small.data(), s);
     485             :                         if constexpr (Extra) {
     486             :                                 // zero-terminated, bypass -Wclass-memaccess
     487             :                                 memset((void *)(ptr + s), 0, Extra * sizeof(Type));
     488             :                         }
     489             :                         force_clear();
     490             :                         return ptr;
     491             :                 }
     492             :         }
     493             : 
     494  1746785602 :         pointer data() noexcept { return is_large() ? _large.data() : _small.data(); }
     495    75614137 :         const_pointer data() const noexcept { return is_large() ? _large.data() : _small.data(); }
     496             : 
     497  1244470609 :         size_type size() const noexcept { return is_large() ? _large.size() : _small.size(); }
     498   556890706 :         size_type capacity() const noexcept { return is_large() ? _large.capacity() : _small.capacity(); }
     499             : 
     500    23801973 :         bool empty() const noexcept { return is_large() ? _large.empty() : (_small.size() == 0); }
     501             : 
     502             : protected:
     503     8285832 :         void perform_move(mem_soo_iface &&other) {
     504     8285832 :                 if (other.is_small()) {
     505     7259249 :                         set_small_flag();
     506     7259245 :                         _small.force_clear();
     507     7259246 :                         _small.move_assign(this->_allocator, other.data(), other.size());
     508     7259238 :                         other._small.force_clear();
     509             :                 } else {
     510     1026583 :                         set_large_flag();
     511     1026589 :                         _large = std::move(other._large);
     512             :                 }
     513     8285834 :         }
     514             : 
     515     8325802 :         void clear_dealloc(allocator &a) {
     516     8325802 :                 if (is_large()) {
     517     1065611 :                         _large.clear_dealloc(a);
     518             :                 } else {
     519     7260189 :                         clear();
     520             :                 }
     521     8325803 :         }
     522             : 
     523  1040631553 :         size_type modify_size(intptr_t diff) {
     524  1040631553 :                 return is_large() ? _large.modify_size(diff) : _small.modify_size(diff);
     525             :         }
     526             : 
     527     8181443 :         void set_size(size_type s) {
     528     8181443 :                 if (is_large()) {
     529     7807123 :                         _large.set_size(s);
     530             :                 } else {
     531      374319 :                         _small.set_size(s);
     532             :                 }
     533     8181440 :         }
     534             : 
     535             : protected:
     536             :         allocator _allocator;
     537             : 
     538             : private:
     539    26924408 :         bool is_small() const { return this->_allocator.test(allocator::FirstFlag); }
     540  4721257334 :         bool is_large() const { return !this->_allocator.test(allocator::FirstFlag); }
     541             : 
     542    39729044 :         void set_large_flag() {
     543    39729044 :                 this->_allocator.reset(allocator::FirstFlag);
     544    39729319 :         }
     545       42774 :         void set_large_flag_force() {
     546       42774 :                 clear(); // discard content
     547       42773 :                 set_large_flag();
     548       42773 :         }
     549             : 
     550    25340357 :         void set_small_flag() {
     551    25340357 :                 this->_allocator.set(allocator::FirstFlag);
     552    25340376 :         }
     553             : 
     554             :         union {
     555             :                 large_mem _large;
     556             :                 small_mem _small;
     557             :         };
     558             : };
     559             : 
     560             : }
     561             : 
     562             : #endif /* STAPPLER_CORE_MEMORY_SPMEMSTORAGEMEMIMPL_H_ */

Generated by: LCOV version 1.14