LCOV - code coverage report
Current view: top level - model - Map.hpp (source / functions) Coverage Total Hit
Test: final-coverage.info Lines: 100.0 % 63 63
Test Date: 2025-12-16 15:55:14 Functions: 91.2 % 68 62

            Line data    Source code
       1              : /*
       2              :  ___________________________________________
       3              : |    _     ___                        _     |
       4              : |   | |   |__ \                      | |    |
       5              : |   | |__    ) |__ _  __ _  ___ _ __ | |_   |
       6              : |   | '_ \  / // _` |/ _` |/ _ \ '_ \| __|  |  HTTP/2 AGENT FOR MOCK TESTING
       7              : |   | | | |/ /| (_| | (_| |  __/ | | | |_   |  Version 0.0.z
       8              : |   |_| |_|____\__,_|\__, |\___|_| |_|\__|  |  https://github.com/testillano/h2agent
       9              : |                     __/ |                 |
      10              : |                    |___/                  |
      11              : |___________________________________________|
      12              : 
      13              : Licensed under the MIT License <http://opensource.org/licenses/MIT>.
      14              : SPDX-License-Identifier: MIT
      15              : Copyright (c) 2021 Eduardo Ramos
      16              : 
      17              : Permission is hereby  granted, free of charge, to any  person obtaining a copy
      18              : of this software and associated  documentation files (the "Software"), to deal
      19              : in the Software  without restriction, including without  limitation the rights
      20              : to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
      21              : copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
      22              : furnished to do so, subject to the following conditions:
      23              : 
      24              : The above copyright notice and this permission notice shall be included in all
      25              : copies or substantial portions of the Software.
      26              : 
      27              : THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
      28              : IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
      29              : FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
      30              : AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
      31              : LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      32              : OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
      33              : SOFTWARE.
      34              : */
      35              : 
      36              : #pragma once
      37              : 
      38              : // Better unordered_map than map:
      39              : // Slighly more memory consumption (not significative in load tests) due to the hash map.
      40              : // But order is not important for us, and the size is not very big (prune is normally
      41              : //  applied in load test provisions), so the cache is not used.
      42              : // As insertion and deletion are equally fast for both containers, we focus on search
      43              : //  (O(log2(n)) for map as binary tree, O(1) constant as average (O(n) in worst case)
      44              : //  for unordered map as hash table), so for our case, unordered_map seems to be the best choice.
      45              : #include <unordered_map>
      46              : 
      47              : #include <common.hpp>
      48              : 
      49              : #include <nlohmann/json.hpp>
      50              : 
      51              : 
      52              : namespace h2agent
      53              : {
      54              : namespace model
      55              : {
      56              : 
      57              : template<typename Key, typename Value>
      58              : class Map {
      59              : 
      60              :     typedef typename std::unordered_map<Key, Value> map_t;
      61              :     using IterationCallback = std::function<void(const Key&, const Value&)>;
      62              : 
      63              :     mutable mutex_t mutex_{};
      64              :     map_t map_{};
      65              : 
      66              : protected:
      67           30 :     bool clear_unsafe() noexcept {
      68           30 :         bool result = (map_.size() != 0); // same !
      69           30 :         map_.clear();
      70           30 :         return result;
      71              :     }
      72              : 
      73              : public:
      74              : 
      75         1428 :     Map() {};
      76              : 
      77              :     /** copy constructor */
      78              :     Map(const Map& other) : map_{}
      79              :     {
      80              :         read_guard_t guard(other.mutex_);
      81              :         this->map_ = other.map_;
      82              :     }
      83              : 
      84         1347 :     ~Map() = default;
      85              : 
      86              :     // getters
      87              : 
      88          117 :     bool exists(const Key& key) const
      89              :     {
      90          117 :         read_guard_t guard(mutex_);
      91          234 :         return (map_.find(key) != map_.end());
      92          117 :     }
      93              : 
      94              :     /**
      95              :      * Searchs map key
      96              :      *
      97              :      * @param key key to find
      98              :      * @param exits written by reference
      99              :      * @return Value for provided key, or initizalized Value when key is missing
     100              :      */
     101          477 :     Value get(const Key& key, bool &exists) const
     102              :     {
     103          477 :         read_guard_t guard(mutex_);
     104          477 :         auto it = map_.find(key);
     105          477 :         exists = (it != map_.end());
     106          954 :         return (exists ? it->second : Value{}); // return copy
     107          477 :     }
     108              : 
     109              :     /**
     110              :      * Getter which avoid copy of empty value and is more readable than get()
     111              :      */
     112          134 :     bool tryGet(const Key& key, Value& out_value) const {
     113          134 :         read_guard_t guard(mutex_);
     114          134 :         auto it = map_.find(key);
     115          134 :         if (it != map_.end()) {
     116           15 :             out_value = it->second;
     117           15 :             return true;
     118              :         }
     119          119 :         return false;
     120          134 :     }
     121              : 
     122              :     //// Lvalue
     123              :     //bool insert_if_not_exists(const Key& key, const Value& value) {
     124              :     //    write_guard_t guard(mutex_);
     125              :     //    // try_emplace is the atomic equivalent to insert_if_not_exists
     126              :     //    return map_.try_emplace(key, value).second;
     127              :     //}
     128              : 
     129              :     //// Rvalue (max performance)
     130              :     //bool insert_if_not_exists(const Key& key, Value&& value) {
     131              :     //    write_guard_t guard(mutex_);
     132              :     //    return map_.try_emplace(key, std::move(value)).second;
     133              :     //}
     134              : 
     135              :     //template <typename... Args>
     136              :     //bool emplace(const Key& key, Args&&... args) {
     137              :     //    write_guard_t guard(mutex_);
     138              :     //    return map_.try_emplace(key, std::forward<Args>(args)...).second;
     139              :     //}
     140              : 
     141              :     /** map size */
     142          234 :     size_t size() const
     143              :     {
     144          234 :         read_guard_t guard(mutex_);
     145          468 :         return map_.size();
     146          234 :     }
     147              : 
     148           13 :     bool empty() const
     149              :     {
     150           13 :         read_guard_t guard(mutex_);
     151           26 :         return (map_.size() == 0);
     152           13 :     }
     153              : 
     154              :     /**
     155              :      * @brief Iterates safely over all elements in the map.
     156              :      * * This method provides **read access** to the map's elements in a **thread-safe** manner
     157              :      * by applying a user-defined callback function to each key-value pair. The entire
     158              :      * iteration is performed atomically and under a read lock.
     159              :      *
     160              :      * @attention This method acquires a \c std::shared_lock (read lock) for its full duration.
     161              :      * To ensure high concurrency, avoid placing long-running operations (such as file I/O or
     162              :      * thread sleeping) inside the callback function.
     163              :      *
     164              :      * @tparam Key The type of the map's key (e.g., std::string).
     165              :      * @tparam Value The type of the map's value (e.g., std::shared_ptr<T>).
     166              :      *
     167              :      * @param callback A function (lambda or functor) that is applied to every key-value pair.
     168              :      * The signature must be compatible with:
     169              :      * \code
     170              :      * void(const Key&, const Value&)
     171              :      * \endcode
     172              :      *
     173              :      * @note This function uses the callback pattern to prevent the exposure of unsafe iterators
     174              :      * (\c dangling iterators) to external threads.
     175              :      */
     176           30 :     void forEach(const IterationCallback& callback) const {
     177           30 :         read_guard_t guard(mutex_);
     178           63 :         for (const auto& pair : map_) {
     179           33 :             callback(pair.first, pair.second);
     180              :         }
     181           30 :     }
     182              : 
     183              :     /**
     184              :      * @brief Safely converts the internal map content into a JSON object.
     185              :      * * This method provides a thread-safe way to serialize the data stored in the
     186              :      * map by acquiring a read lock for the entire duration of the conversion.
     187              :      * * @details The method acquires a \c std::shared_lock on the map's mutex. While
     188              :      * the lock is held, the internal \c std::unordered_map is copied and cast
     189              :      * into a \c nlohmann::json object. This ensures that the map cannot be
     190              :      * modified (added to or removed from) by writer threads during serialization.
     191              :      * * @tparam Key The type of the map's key.
     192              :      * @tparam Value The type of the map's value.
     193              :      * * @note This implementation relies on \c nlohmann::json's built-in support
     194              :      * for converting \c std::unordered_map. If \c Value is a complex type (e.g.,
     195              :      * a smart pointer or a custom struct), ensure the appropriate \c to_json()
     196              :      * function is implemented for automatic serialization.
     197              :      *
     198              :      * @return nlohmann::json A new, thread-safe copy of the map's content
     199              :      * as a JSON object.
     200              :      */
     201            3 :     nlohmann::json getJson() const {
     202            3 :         read_guard_t guard(mutex_);
     203            3 :         nlohmann::json j(map_);
     204            6 :         return j;  // return copy
     205            3 :     }
     206              : 
     207              :     // setters
     208              : 
     209              :     /**
     210              :      * Adds a new value to the map
     211              :      * Lvalue variant
     212              :      *
     213              :      * @param key key to add
     214              :      * @param value stored
     215              :      */
     216          474 :     void add(const Key& key, const Value &value) {
     217          474 :         write_guard_t guard(mutex_);
     218          474 :         map_.insert_or_assign(key, value);
     219          474 :     }
     220              : 
     221              :     // Rvalue variant (std::move)
     222          320 :     void add(const Key& key, Value&& value) {
     223          320 :         write_guard_t guard(mutex_);
     224          320 :         map_.insert_or_assign(key, std::move(value));
     225          320 :     }
     226              : 
     227              :     /**
     228              :      * Adds another map of same kind to the map
     229              :      *
     230              :      * @param map map to add
     231              :      */
     232            5 :     void add(const map_t& m)
     233              :     {
     234            5 :         write_guard_t guard(mutex_);
     235           13 :         for (const auto& kv : m)
     236            8 :             map_.insert_or_assign(kv.first, kv.second);
     237            5 :     }
     238              : 
     239              :     /**
     240              :      * Removes key
     241              :      *
     242              :      * @param key key to remove
     243              :      */
     244            6 :     void remove(const Key& key, bool &exists)
     245              :     {
     246            6 :         write_guard_t guard(mutex_);
     247            6 :         exists = (map_.erase(key) > 0);
     248            6 :     }
     249              : 
     250              :     /** Clear map */
     251              :     // return if something was deleted
     252           20 :     bool clear()
     253              :     {
     254           20 :         write_guard_t guard(mutex_);
     255           40 :         return clear_unsafe(); // don't call Map::size() to avoid mutex deadlocks (this one uses map_.size())
     256           20 :     }
     257              : };
     258              : 
     259              : }
     260              : }
     261              : 
        

Generated by: LCOV version 2.0-1