LCOV - code coverage report
Current view: top level - conffwk/src - ConfigurationImpl.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 37.7 % 297 112
Test Date: 2025-12-21 13:07:08 Functions: 38.6 % 57 22

            Line data    Source code
       1              : #include <stdlib.h>
       2              : 
       3              : #include "conffwk/Configuration.hpp"
       4              : #include "conffwk/ConfigurationImpl.hpp"
       5              : #include "conffwk/Schema.hpp"
       6              : 
       7              : namespace dunedaq {
       8              :   namespace conffwk {
       9              : 
      10            0 :     const char * bool2str(bool value) { return (value ? "yes" : "no"); }
      11              : 
      12        14933 :     attribute_t::attribute_t(
      13              :       const std::string& name, type_t type, const std::string& range,
      14              :       int_format_t int_format, bool is_not_null, bool is_multi_value,
      15              :       const std::string& default_value, const std::string& description
      16        14933 :     ) :
      17        14933 :       p_name             (name),
      18        14933 :       p_type             (type),
      19        14933 :       p_range            (range),
      20        14933 :       p_int_format       (int_format),
      21        14933 :       p_is_not_null      (is_not_null),
      22        14933 :       p_is_multi_value   (is_multi_value),
      23        14933 :       p_default_value    (default_value),
      24        14933 :       p_description      (description)
      25        14933 :     { ; }
      26              : 
      27            0 :     const char * attribute_t::type2str(type_t type)
      28              :     {
      29            0 :       switch(type) {
      30              :         case bool_type:   return "boolean";
      31            0 :         case s8_type:     return "8-bits signed integer";
      32            0 :         case u8_type:     return "8-bits unsigned integer";
      33            0 :         case s16_type:    return "16-bits signed integer";
      34            0 :         case u16_type:    return "16-bits unsigned integer";
      35            0 :         case s32_type:    return "32-bits signed integer";
      36            0 :         case u32_type:    return "32-bits unsigned integer";
      37            0 :         case s64_type:    return "64-bits signed integer";
      38            0 :         case u64_type:    return "64-bits unsigned integer";
      39            0 :         case float_type:  return "float";
      40            0 :         case double_type: return "double";
      41            0 :         case date_type:   return "date";
      42            0 :         case time_type:   return "time";
      43            0 :         case string_type: return "string";
      44            0 :         case enum_type:   return "enumeration";
      45            0 :         case class_type:  return "class reference";
      46            0 :         default:          return "unknown";
      47              :       }
      48              :     }
      49              : 
      50            0 :     const char * attribute_t::type(type_t type)
      51              :     {
      52            0 :       switch(type) {
      53              :         case bool_type:   return "bool";
      54            0 :         case s8_type:     return "s8";
      55            0 :         case u8_type:     return "u8";
      56            0 :         case s16_type:    return "s16";
      57            0 :         case u16_type:    return "u16";
      58            0 :         case s32_type:    return "s32";
      59            0 :         case u32_type:    return "u32";
      60            0 :         case s64_type:    return "s64";
      61            0 :         case u64_type:    return "u64";
      62            0 :         case float_type:  return "float";
      63            0 :         case double_type: return "double";
      64            0 :         case date_type:   return "date";
      65            0 :         case time_type:   return "time";
      66            0 :         case string_type: return "string";
      67            0 :         case enum_type:   return "enum";
      68            0 :         case class_type:  return "class";
      69            0 :         default:          return "unknown";
      70              :       }
      71              :     }
      72              : 
      73            0 :     const char * attribute_t::format2str(int_format_t type)
      74              :     {
      75            0 :       switch(type) {
      76              :         case oct_int_format:   return "octal";
      77            0 :         case dec_int_format:   return "decimal";
      78            0 :         case hex_int_format:   return "hexadecimal";
      79            0 :         default:               return "not applicable";
      80              :       }
      81              :     }
      82              : 
      83            0 :     void attribute_t::print(std::ostream& out, const std::string& prefix) const
      84              :     {
      85            0 :       out
      86            0 :         << prefix << "attribute \'" << p_name << "\'\n"
      87            0 :         << prefix << "  type: \'" << type2str(p_type) << "\'\n"
      88            0 :         << prefix << "  range: \'" << p_range << "\'\n";
      89              : 
      90            0 :       if(p_int_format != na_int_format) {
      91            0 :         out << prefix << "  integer format: \'" << format2str(p_int_format) << "\'\n";
      92              :       }
      93              : 
      94            0 :       out
      95            0 :         << prefix << "  is not null: " << bool2str(p_is_not_null) << '\n'
      96            0 :         << prefix << "  is multi-value: " << bool2str(p_is_multi_value) << '\n'
      97            0 :         << prefix << "  default value: \'" << p_default_value << "\'\n"
      98            0 :         << prefix << "  description: \'" << p_description << '\'';
      99            0 :     }
     100              :     
     101            0 :     std::ostream& operator<<(std::ostream& out, const attribute_t& a)
     102              :     {
     103            0 :       a.print(out);
     104            0 :       return out;
     105              :     }
     106              : 
     107              : 
     108         9998 :     relationship_t::relationship_t(
     109              :       const std::string& name, const std::string& type, bool can_be_null,
     110              :       bool is_multi_value, bool is_aggregation, const std::string& description
     111         9998 :     ) :
     112         9998 :       p_name             (name),
     113         9998 :       p_type             (type),
     114         9998 :       p_cardinality      (
     115         9998 :                             (can_be_null  && !is_multi_value) ? zero_or_one  :
     116         9033 :                             (can_be_null  && is_multi_value ) ? zero_or_many :
     117         3520 :                             (!can_be_null && is_multi_value ) ? one_or_many  :
     118              :                             only_one
     119              :                          ),
     120         9998 :       p_is_aggregation   (is_aggregation),
     121         9998 :       p_description      (description)
     122         9998 :     { ; }
     123              :     
     124            0 :     const char * relationship_t::card2str(cardinality_t cardinality)
     125              :     {
     126            0 :       switch(cardinality) {
     127              :         case zero_or_one:    return "zero or one";
     128            0 :         case zero_or_many:   return "zero or many";
     129            0 :         case only_one:       return "one";
     130            0 :         case one_or_many:    return "one or many";
     131            0 :         default:             return "unknown";
     132              :       }
     133              :     }
     134              : 
     135            0 :     void relationship_t::print(std::ostream& out, const std::string& prefix) const
     136              :     {
     137            0 :       out
     138            0 :         << prefix << "relationship \'" << p_name << "\'\n"
     139            0 :         << prefix << "  class type: \'" << p_type << "\'\n"
     140            0 :         << prefix << "  cardinality: \'" << card2str(p_cardinality) << "\'\n"
     141            0 :         << prefix << "  is aggregation: \'" << bool2str(p_is_aggregation) << "\'\n"
     142            0 :         << prefix << "  description: \'" << p_description << '\'';
     143            0 :     }
     144              : 
     145            0 :     std::ostream& operator<<(std::ostream& out, const relationship_t& r)
     146              :     {
     147            0 :       r.print(out);
     148            0 :       return out;
     149              :     }
     150              : 
     151         5558 :     class_t::class_t(
     152              :       const std::string& name,
     153              :       const std::string& description,
     154              :       const std::string& schema_path,
     155              :       bool is_abstract
     156         5558 :     ) :
     157         5558 :     p_name             (name),
     158         5558 :     p_description      (description),
     159         5558 :     p_schema_path      (schema_path),
     160         5558 :     p_abstract         (is_abstract)
     161         5558 :     { ; }
     162              : 
     163            0 :     void class_t::print(std::ostream& out, const std::string& prefix) const
     164              :     {
     165            0 :       out
     166            0 :         << prefix << "class \'" << p_name << "\'\n"
     167            0 :         << prefix << "  is abstract: \'" << bool2str(p_abstract) << "\'\n"
     168            0 :         << prefix << "  description: \'" << p_description << "\'\n"
     169            0 :         << prefix << "  path: \'" << p_schema_path << "\'\n";
     170              : 
     171            0 :       if(p_superclasses.empty()) {
     172            0 :         out << prefix << "  there are no superclasses\n";
     173              :       }
     174              :       else {
     175            0 :         out << prefix << "  " << p_superclasses.size() << " superclass(es):\n";
     176            0 :         for(std::vector<std::string>::const_iterator i = p_superclasses.begin(); i != p_superclasses.end(); ++i) {
     177            0 :           out << prefix << "    \'" << *i << "\'\n";
     178              :         }
     179              :       }
     180              : 
     181            0 :       if(p_subclasses.empty()) {
     182            0 :         out << prefix << "  there are no subclasses\n";
     183              :       }
     184              :       else {
     185            0 :         out << prefix << "  " << p_subclasses.size() << " subclass(es):\n";
     186            0 :         for(std::vector<std::string>::const_iterator i = p_subclasses.begin(); i != p_subclasses.end(); ++i) {
     187            0 :           out << prefix << "    \'" << *i << "\'\n";
     188              :         }
     189              :       }
     190              : 
     191            0 :       std::string new_prefix(prefix);
     192            0 :       new_prefix += "    ";
     193              : 
     194            0 :       if(p_attributes.empty()) {
     195            0 :         out << prefix << "  there are no attributes\n";
     196              :       }
     197              :       else {
     198            0 :         out << prefix << "  " << p_attributes.size() << " attribute(s):\n";
     199            0 :         for(std::vector<attribute_t>::const_iterator i = p_attributes.begin(); i != p_attributes.end(); ++i) {
     200            0 :           (*i).print(out, new_prefix.c_str());
     201            0 :           out << std::endl;
     202              :         }
     203              :       }
     204              : 
     205            0 :       if(p_relationships.empty()) {
     206            0 :         out << prefix << "  there are no relationships\n";
     207              :       }
     208              :       else {
     209            0 :         out << prefix << "  " << p_relationships.size() << " relationship(s):\n";
     210            0 :         for(std::vector<relationship_t>::const_iterator i = p_relationships.begin(); i != p_relationships.end(); ++i) {
     211            0 :           (*i).print(out, new_prefix.c_str());
     212            0 :           out << std::endl;
     213              :         }
     214              :       }
     215            0 :     }
     216              : 
     217            0 :     std::ostream& operator<<(std::ostream& out, const class_t& c)
     218              :     {
     219            0 :       c.print(out);
     220            0 :       return out;
     221              :     }
     222              : 
     223              : 
     224           80 : ConfigurationImpl::ConfigurationImpl() noexcept :
     225           80 :   p_number_of_cache_hits  (0),
     226           80 :   p_number_of_object_read (0),
     227           80 :   m_conf                  (0)
     228              : {
     229           80 : }
     230              : 
     231           80 : ConfigurationImpl::~ConfigurationImpl()
     232              : {
     233           80 :   clean();
     234           80 : }
     235              : 
     236              : void
     237            0 : ConfigurationImpl::print_cache_info() noexcept
     238              : {
     239            0 :   std::cout <<
     240              :     "Configuration implementation profiler report:\n"
     241            0 :     "  number of read objects: " << p_number_of_object_read << "\n"
     242            0 :     "  number of cache hits: " << p_number_of_cache_hits << std::endl;
     243            0 : }
     244              : 
     245              : ConfigObjectImpl *
     246         1286 : ConfigurationImpl::get_impl_object(const std::string& name, const std::string& id) const noexcept
     247              : {
     248              : 
     249         1286 :   conffwk::pmap<conffwk::map<ConfigObjectImpl *> *>::const_iterator i = m_impl_objects.find(&name);
     250              : 
     251         1286 :   const std::string * class_name = nullptr;
     252              : 
     253         1286 :   if(i != m_impl_objects.end()) {
     254          606 :     conffwk::map<ConfigObjectImpl *>::const_iterator j = i->second->find(id);
     255              : 
     256          606 :     if(j != i->second->end()) {
     257          141 :       p_number_of_cache_hits++;
     258          141 :       TLOG_DEBUG(4) << "\n  * found the object with id = \'" << id << "\' in class \'" << name << '\'' ;
     259          141 :       return j->second;
     260              :     }
     261              : 
     262          465 :     class_name = i->first;
     263              : 
     264              : 
     265              :       // prepare and print out debug message
     266              : 
     267          465 :     if(ers::debug_level() >= 4) {
     268            8 :       TLOG_DEBUG(40) << " * there is no object with id = \'" << id << "\' found in the class \'" << name
     269            8 :                      << "\' that has " << i->second->size() << " objects in cache: ";
     270           27 :       for(j=i->second->begin(); j != i->second->end();++j) {
     271           19 :         TLOG_DEBUG(40) << '\'' << j->first << '\'';
     272              :       }
     273              :     }
     274              : 
     275              :   }
     276              :   else {
     277          680 :     class_name = &DalFactory::instance().get_known_class_name_ref(name);
     278          680 :     TLOG_DEBUG(40) << "  * there is no object with id = \'" << id << "\' found in the class \'" << name
     279          680 :                    << "\' that has no objects in cache";
     280              :   }
     281              : 
     282              :     // check implementation objects of subclasses
     283              : 
     284         1145 :   if(m_conf) {
     285         1145 :     conffwk::fmap<conffwk::fset>::const_iterator subclasses = m_conf->subclasses().find(class_name);
     286              : 
     287         1145 :     if(subclasses != m_conf->subclasses().end()) {
     288          402 :       for(conffwk::fset::const_iterator k = subclasses->second.begin(); k != subclasses->second.end(); ++k) {
     289          277 :         i = m_impl_objects.find(*k);
     290          277 :         if(i != m_impl_objects.end()) {
     291            5 :           conffwk::map<ConfigObjectImpl *>::const_iterator j = i->second->find(id);
     292              : 
     293            5 :           if(j != i->second->end()) {
     294            0 :             p_number_of_cache_hits++;
     295            0 :             TLOG_DEBUG(40) << "  * found the object with id = \'" << id << "\' in class \'" << *k << '\'';
     296              : 
     297            0 :             return j->second;
     298              :           }
     299              : 
     300              : 
     301              :             // prepare and print out debug message
     302              : 
     303            5 :           else if(ers::debug_level() >= 4) {
     304            0 :             TLOG_DEBUG(40) << "  * there is no object with id = \'" << id << "\' found in the class \'" << *k
     305            0 :                            << "\' that has " << i->second->size() << " objects in cache: ";
     306            0 :             for(j=i->second->begin(); j != i->second->end();++j) {
     307            0 :               TLOG_DEBUG(40) << '\'' << j->first << '\'';
     308              :             }
     309              :           }
     310              : 
     311              : 
     312              :         }
     313              :         else {
     314          272 :           TLOG_DEBUG(40) << "  * there is no object with id = \'" << id << "\' found in the class \'" << *k
     315          272 :                          << "\' that has no objects in cache\n";
     316              :         }
     317              : 
     318              :       }
     319              :     }
     320              : 
     321         1145 :     TLOG_DEBUG(40) << "  * there is no object \'" << id << "\' in class \'" << name
     322         1145 :                    << "\' and it's subclasses, returning NULL ...";
     323              :   }
     324              :   else {
     325            0 :     TLOG_DEBUG(40) << "  * there is no object \'" << id << "\' in class \'" << name << "\', returning NULL ...";
     326              :   }
     327              : 
     328              :   return nullptr;
     329              : }
     330              : 
     331              : 
     332              : void
     333         1145 : ConfigurationImpl::put_impl_object(const std::string& name, const std::string& id, ConfigObjectImpl * obj) noexcept
     334              : {
     335         1145 :   p_number_of_object_read++;
     336              : 
     337         1145 :   conffwk::pmap<conffwk::map<ConfigObjectImpl *> * >::iterator i = m_impl_objects.find(&name);
     338              : 
     339         1145 :   if(i != m_impl_objects.end()) {
     340          465 :     (*i->second)[id] = obj;
     341          465 :     obj->m_class_name = i->first;
     342              :   }
     343              :   else {
     344          680 :     conffwk::map<ConfigObjectImpl *> * m = new conffwk::map<ConfigObjectImpl *>();
     345          680 :     obj->m_class_name = &DalFactory::instance().get_known_class_name_ref(name);
     346          680 :     m_impl_objects[obj->m_class_name] = m;
     347          680 :     (*m)[id] = obj;
     348              :   }
     349         1145 : }
     350              : 
     351              : void
     352            0 : ConfigurationImpl::rename_impl_object(const std::string * class_name, const std::string& old_id, const std::string& new_id) noexcept
     353              : {
     354            0 :   conffwk::pmap<conffwk::map<ConfigObjectImpl *> *>::iterator i = m_impl_objects.find(class_name);
     355              : 
     356            0 :   if (i != m_impl_objects.end())
     357              :     {
     358            0 :       conffwk::map<ConfigObjectImpl *>::iterator j = i->second->find(old_id);
     359              : 
     360            0 :       if (j != i->second->end())
     361              :         {
     362            0 :           ConfigObjectImpl*& obj = (*i->second)[new_id];
     363              : 
     364            0 :           if (obj != nullptr)
     365              :             {
     366            0 :               obj->m_state = dunedaq::conffwk::Unknown;
     367            0 :               m_tangled_objects.push_back(obj);
     368              :             }
     369              : 
     370            0 :           obj = j->second;
     371              : 
     372            0 :           TLOG_DEBUG(2) << "rename implementation " << (void *)j->second << " of object \'" << old_id << '@' << *class_name << "\' to \'" << new_id << '\'';
     373            0 :           i->second->erase(j);
     374              :         }
     375              :     }
     376            0 : }
     377              : 
     378              : void
     379          240 : ConfigurationImpl::clean() noexcept
     380              : {
     381          920 :   for (auto& i : m_impl_objects)
     382              :     {
     383         1825 :       for (auto& j : *i.second)
     384         1145 :         delete j.second;
     385              : 
     386          680 :       delete i.second;
     387              :     }
     388              : 
     389          240 :   m_impl_objects.clear();
     390              : 
     391          240 :   for (auto& x : m_tangled_objects)
     392            0 :     delete x;
     393              : 
     394          240 :   m_tangled_objects.clear();
     395          240 : }
     396              : 
     397              : std::mutex&
     398            0 : ConfigurationImpl::get_conf_impl_mutex() const
     399              : {
     400            0 :   return m_conf->m_impl_mutex;
     401              : }
     402              : 
     403              : void
     404           70 : ConfigObjectImpl::convert(bool& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     405              : {
     406           70 :   m_impl->m_conf->convert(value, obj, attr_name);
     407           70 : }
     408              : 
     409              : void
     410            5 : ConfigObjectImpl::convert(uint8_t& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     411              : {
     412            5 :   m_impl->m_conf->convert(value, obj, attr_name);
     413            5 : }
     414              : 
     415              : void
     416            0 : ConfigObjectImpl::convert(int8_t& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     417              : {
     418            0 :   m_impl->m_conf->convert(value, obj, attr_name);
     419            0 : }
     420              : 
     421              : void
     422           37 : ConfigObjectImpl::convert(uint16_t& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     423              : {
     424           37 :   m_impl->m_conf->convert(value, obj, attr_name);
     425           37 : }
     426              : 
     427              : void
     428            0 : ConfigObjectImpl::convert(int16_t& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     429              : {
     430            0 :   m_impl->m_conf->convert(value, obj, attr_name);
     431            0 : }
     432              : 
     433              : void
     434         1206 : ConfigObjectImpl::convert(uint32_t& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     435              : {
     436         1206 :   m_impl->m_conf->convert(value, obj, attr_name);
     437         1206 : }
     438              : 
     439              : void
     440           64 : ConfigObjectImpl::convert(int32_t& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     441              : {
     442           64 :   m_impl->m_conf->convert(value, obj, attr_name);
     443           64 : }
     444              : 
     445              : void
     446            5 : ConfigObjectImpl::convert(uint64_t& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     447              : {
     448            5 :   m_impl->m_conf->convert(value, obj, attr_name);
     449            5 : }
     450              : 
     451              : void
     452            0 : ConfigObjectImpl::convert(int64_t& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     453              : {
     454            0 :   m_impl->m_conf->convert(value, obj, attr_name);
     455            0 : }
     456              : 
     457              : void
     458            0 : ConfigObjectImpl::convert(float& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     459              : {
     460            0 :   m_impl->m_conf->convert(value, obj, attr_name);
     461            0 : }
     462              : 
     463              : void
     464            0 : ConfigObjectImpl::convert(double& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     465              : {
     466            0 :   m_impl->m_conf->convert(value, obj, attr_name);
     467            0 : }
     468              : 
     469              : void
     470         1041 : ConfigObjectImpl::convert(std::string& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     471              : {
     472         1041 :   m_impl->m_conf->convert(value, obj, attr_name);
     473         1041 : }
     474              : 
     475              : 
     476              : void
     477            0 : ConfigObjectImpl::convert(std::vector<bool>& /*value*/, const ConfigObject& /*obj*/, const std::string& /*attr_name*/) noexcept
     478              : {
     479              :   //m_impl->m_conf->convert2(value, obj, attr_name);
     480            0 : }
     481              : 
     482              : void
     483            0 : ConfigObjectImpl::convert(std::vector<uint8_t>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     484              : {
     485            0 :   m_impl->m_conf->convert2(value, obj, attr_name);
     486            0 : }
     487              : 
     488              : void
     489            0 : ConfigObjectImpl::convert(std::vector<int8_t>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     490              : {
     491            0 :   m_impl->m_conf->convert2(value, obj, attr_name);
     492            0 : }
     493              : 
     494              : void
     495            0 : ConfigObjectImpl::convert(std::vector<uint16_t>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     496              : {
     497            0 :   m_impl->m_conf->convert2(value, obj, attr_name);
     498            0 : }
     499              : 
     500              : void
     501            0 : ConfigObjectImpl::convert(std::vector<int16_t>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     502              : {
     503            0 :   m_impl->m_conf->convert2(value, obj, attr_name);
     504            0 : }
     505              : 
     506              : void
     507            0 : ConfigObjectImpl::convert(std::vector<uint32_t>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     508              : {
     509            0 :   m_impl->m_conf->convert2(value, obj, attr_name);
     510            0 : }
     511              : 
     512              : void
     513            0 : ConfigObjectImpl::convert(std::vector<int32_t>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     514              : {
     515            0 :   m_impl->m_conf->convert2(value, obj, attr_name);
     516            0 : }
     517              : 
     518              : void
     519            0 : ConfigObjectImpl::convert(std::vector<uint64_t>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     520              : {
     521            0 :   m_impl->m_conf->convert2(value, obj, attr_name);
     522            0 : }
     523              : 
     524              : void
     525            0 : ConfigObjectImpl::convert(std::vector<int64_t>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     526              : {
     527            0 :   m_impl->m_conf->convert2(value, obj, attr_name);
     528            0 : }
     529              : 
     530              : void
     531            0 : ConfigObjectImpl::convert(std::vector<float>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     532              : {
     533            0 :   m_impl->m_conf->convert2(value, obj, attr_name);
     534            0 : }
     535              : 
     536              : void
     537            0 : ConfigObjectImpl::convert(std::vector<double>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     538              : {
     539            0 :   m_impl->m_conf->convert2(value, obj, attr_name);
     540            0 : }
     541              : 
     542              : void
     543           60 : ConfigObjectImpl::convert(std::vector<std::string>& value, const ConfigObject& obj, const std::string& attr_name) noexcept
     544              : {
     545           60 :   m_impl->m_conf->convert2(value, obj, attr_name);
     546           60 : }
     547              : 
     548              : } // namespace conffwk
     549              : } // namespace dunedaq
        

Generated by: LCOV version 2.0-1