Webthing-CPP: a modern CPP implementation of the WebThings API


LCOV - code coverage report
Current view: top level - webthing - storage.hpp (source / functions) Coverage Total Hit
Test: filtered_coverage.info Lines: 97.9 % 94 92
Test Date: 2025-03-15 12:45:00 Functions: 100.0 % 50 50

            Line data    Source code
       1              : // Webthing-CPP
       2              : // SPDX-FileCopyrightText: 2023-present Benno Waldhauer
       3              : // SPDX-License-Identifier: MIT
       4              : 
       5              : #pragma once
       6              : 
       7              : #include <cstddef>
       8              : #include <deque>
       9              : #include <memory>
      10              : #include <mutex>
      11              : #include <vector>
      12              : 
      13              : namespace bw::webthing {
      14              : 
      15              : struct StorageConfig
      16              : {
      17              :     size_t max_size = SIZE_MAX;
      18              :     bool write_protected = true;
      19              : };
      20              : 
      21              : // A simple ring buffer that overwrites oldest elements when max_size is reached.
      22              : // This ring buffer can not remove a subset of its elements. 
      23              : template<class T>
      24              : class SimpleRingBuffer
      25              : {
      26              : public:
      27           43 :     SimpleRingBuffer(size_t max_size = SIZE_MAX, bool write_protected = false)
      28           43 :         : max_size(max_size)
      29           43 :         , current_size(0)
      30           43 :         , start_pos(0)
      31              :     {
      32           43 :         if(max_size < SIZE_MAX)
      33           40 :             buffer.reserve(max_size);
      34              : 
      35           43 :         if(write_protected)
      36           37 :             mutex = std::make_unique<std::mutex>();
      37           43 :     }
      38              : 
      39           36 :     SimpleRingBuffer(const StorageConfig& config)
      40           36 :         : SimpleRingBuffer(config.max_size, config.write_protected)
      41           36 :     {}
      42              : 
      43      2000049 :     T& get(size_t index)
      44              :     {
      45      2000049 :         return buffer[resolve_index(index)];
      46              :     }
      47              : 
      48           40 :     const T& get(size_t index) const
      49              :     {
      50           40 :         return buffer[resolve_index(index)];
      51              :     }
      52              : 
      53      2009762 :     void add(T element)
      54              :     {
      55      2009762 :         auto lock = conditional_lock();
      56              : 
      57      2010034 :         if (current_size < max_size)
      58              :         {
      59      2010026 :             buffer.push_back(element);
      60      2010026 :             ++current_size;
      61              :         }
      62              :         else
      63              :         {
      64            8 :             buffer[start_pos] = element;
      65            8 :             start_pos = (start_pos + 1) % max_size;
      66              :         }
      67      2010034 :     }
      68              : 
      69           14 :     size_t size() const
      70              :     {
      71           14 :         return current_size;
      72              :     }
      73              : 
      74            6 :     auto begin() { return Iterator(this, 0); }
      75            6 :     auto end() { return Iterator(this, current_size); }
      76           15 :     auto begin() const { return ConstIterator(this, 0); }
      77           15 :     auto end() const { return ConstIterator(this, current_size); }
      78              : 
      79              : private:
      80              :     std::vector<T> buffer;
      81              :     size_t max_size;
      82              :     size_t current_size;
      83              :     size_t start_pos;
      84              :     std::unique_ptr<std::mutex> mutex;
      85              : 
      86      2000089 :     const size_t resolve_index(size_t index) const
      87              :     {
      88      2000089 :         if (index >= current_size)
      89            8 :             throw std::out_of_range("Index out of range");
      90              : 
      91      2000081 :         return (start_pos + index) % max_size;
      92              :     }
      93              : 
      94      2009759 :     std::unique_ptr<std::scoped_lock<std::mutex>> conditional_lock()
      95              :     {
      96      2009759 :         std::unique_ptr<std::scoped_lock<std::mutex>> lock;
      97      2009759 :         if(mutex)
      98         9685 :             lock = std::make_unique<std::scoped_lock<std::mutex>>(*mutex);
      99      2010034 :         return lock;
     100            0 :     }
     101              : 
     102              :     struct Iterator
     103              :     {
     104           12 :         Iterator(SimpleRingBuffer* buffer, size_t position)
     105           12 :             : buffer(buffer)
     106           12 :             , position(position)
     107           12 :         {}
     108              : 
     109      2000018 :         bool operator!=(const Iterator& other) const
     110              :         {
     111      2000018 :             return position != other.position;
     112              :         }
     113              : 
     114      2000012 :         Iterator& operator++()
     115              :         {
     116      2000012 :             ++position;
     117      2000012 :             return *this;
     118              :         }
     119              : 
     120      2000012 :         T& operator*()
     121              :         {
     122      2000012 :             return buffer->get(position);
     123              :         }
     124              : 
     125              :     private:
     126              :         SimpleRingBuffer* buffer;
     127              :         size_t position;
     128              :     };
     129              : 
     130              :     struct ConstIterator
     131              :     {
     132           30 :         ConstIterator(const SimpleRingBuffer* buffer, size_t position)
     133           30 :             : buffer(buffer)
     134           30 :             , position(position)
     135           30 :         {}
     136              : 
     137           55 :         bool operator!=(const ConstIterator& other) const
     138              :         {
     139           55 :             return position != other.position;
     140              :         }
     141              : 
     142           40 :         ConstIterator& operator++()
     143              :         {
     144           40 :             ++position;
     145           40 :             return *this;
     146              :         }
     147              : 
     148           40 :         const T& operator*() const
     149              :         {
     150           40 :             return buffer->get(position);
     151              :         }
     152              : 
     153              :     private:
     154              :         const SimpleRingBuffer* buffer;
     155              :         size_t position;
     156              :     };
     157              : };
     158              : 
     159              : // A more feature rich ring buffer that overwrites oldest elements when max_size is reached.
     160              : // This ring buffer is able to remove a subset of its elements while maintaining the insertion order.
     161              : template<class T>
     162              : class FlexibleRingBuffer
     163              : {
     164              : public:
     165           34 :     FlexibleRingBuffer(size_t max_size = SIZE_MAX, bool write_protected = false)
     166           34 :         : max_size(max_size)
     167              :     {
     168           34 :         if(write_protected)
     169           16 :             mutex = std::make_unique<std::mutex>();
     170           34 :     }
     171              : 
     172           15 :     FlexibleRingBuffer(const StorageConfig& config)
     173           15 :         : FlexibleRingBuffer(config.max_size, config.write_protected)
     174           15 :     {}
     175              : 
     176           17 :     T& get(size_t index)
     177              :     {
     178           17 :         return buffer.at(index);
     179              :     }
     180              : 
     181              :     const T& get(size_t index) const
     182              :     {
     183              :         return buffer.at(index);
     184              :     }
     185              : 
     186         9801 :     void add(T element)
     187              :     {
     188         9801 :         auto lock = conditional_lock();
     189        10033 :         buffer.push_back(element);
     190        10033 :         if (size() > max_size)
     191            4 :             buffer.pop_front();
     192        10033 :     }
     193              : 
     194            3 :     void remove_if(std::function<bool (const T& element)> predicate)
     195              :     {
     196            3 :         auto lock = conditional_lock();
     197            3 :         buffer.erase(std::remove_if(buffer.begin(), buffer.end(), predicate), buffer.end());
     198            3 :     }
     199              : 
     200        10042 :     size_t size() const
     201              :     {
     202        10042 :         return buffer.size();
     203              :     }
     204            5 :     auto begin() { return buffer.begin(); }
     205            5 :     auto end() { return buffer.end(); }
     206           22 :     auto begin() const { return buffer.begin(); }
     207           22 :     auto end() const { return buffer.end(); }
     208              : 
     209              : private:
     210              :     std::deque<T> buffer;
     211              :     size_t max_size;
     212              :     std::unique_ptr<std::mutex> mutex;
     213              : 
     214         9794 :     std::unique_ptr<std::scoped_lock<std::mutex>> conditional_lock()
     215              :     {
     216         9794 :         std::unique_ptr<std::scoped_lock<std::mutex>> lock;
     217         9794 :         if(mutex)
     218         9731 :             lock = std::make_unique<std::scoped_lock<std::mutex>>(*mutex);
     219        10036 :         return lock;
     220            0 :     }
     221              : };
     222              : 
     223              : } // bw::webthing
        

Generated by: LCOV version 2.0-1