LCOV - code coverage report
Current view: top level - include/utils - CircularBuffer.h (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 32 38 84.2 %
Date: 2025-07-17 01:28:37 Functions: 6 8 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #pragma once
      11             : 
      12             : #include "Buffer.h"
      13             : 
      14             : namespace MooseUtils
      15             : {
      16             : 
      17             : /**
      18             :  * An optimized circular buffer.
      19             :  *
      20             :  * This also always ensures that begin() < end().
      21             :  * That means that if the end of the buffer capacity is reached, an O(N) operation
      22             :  * will be used to move data from the end of the capacity to the beginning
      23             :  *
      24             :  * This is done so that begin()/end() iterators can be used in OpenMP loops
      25             :  * Also means that operator[] works sequentially between 0 and size()
      26             :  *
      27             :  * It will also automatically grow larger if capacity is reached
      28             :  *
      29             :  * NOTE: This buffer will not properly wrap around as a standard circular buffer
      30             :  * does. Once the end of the internal storage has been reached, the data
      31             :  * will be moved to the beginning of the internal storage via a copy. This is
      32             :  * needed to ensure that the access through the begin()/end() iterators
      33             :  * is contiguous.
      34             :  */
      35             : template <typename T>
      36             : class CircularBuffer : public Buffer<T>
      37             : {
      38             : public:
      39             :   CircularBuffer();
      40             : 
      41             :   CircularBuffer(const std::size_t capacity);
      42             : 
      43             :   virtual void erase(const std::size_t num) override;
      44             :   virtual void eraseChunk(const std::size_t chunk_size) override;
      45             : 
      46             :   virtual typename Buffer<T>::iterator beginChunk(const std::size_t chunk_size) override;
      47             :   virtual typename Buffer<T>::const_iterator
      48             :   beginChunk(const std::size_t chunk_size) const override;
      49             :   virtual typename Buffer<T>::iterator endChunk(const std::size_t chunk_size) override;
      50             :   virtual typename Buffer<T>::const_iterator endChunk(const std::size_t chunk_size) const override;
      51             : 
      52             : protected:
      53             :   virtual std::size_t newEnd(const std::size_t new_end) override;
      54             : };
      55             : 
      56             : template <typename T>
      57             : CircularBuffer<T>::CircularBuffer() : Buffer<T>()
      58             : {
      59             : }
      60             : 
      61             : template <typename T>
      62           1 : CircularBuffer<T>::CircularBuffer(const std::size_t capacity) : Buffer<T>(capacity)
      63             : {
      64           1 : }
      65             : 
      66             : template <typename T>
      67             : void
      68          11 : CircularBuffer<T>::erase(const std::size_t num)
      69             : {
      70             :   mooseAssert(num <= this->size(), "Cannot erase past the last entry");
      71             : 
      72          11 :   this->_begin_pos += num;
      73             : 
      74             :   // If there's nothing in the buffer - let's reset the positions
      75          11 :   if (this->_begin_pos == this->_end_pos)
      76           2 :     this->clear();
      77          11 : }
      78             : 
      79             : template <typename T>
      80             : void
      81           5 : CircularBuffer<T>::eraseChunk(const std::size_t chunk_size)
      82             : {
      83           5 :   if (chunk_size > this->size())
      84           1 :     this->erase(this->size());
      85             :   else
      86           4 :     this->erase(chunk_size);
      87           5 : }
      88             : 
      89             : template <typename T>
      90             : typename Buffer<T>::iterator
      91           5 : CircularBuffer<T>::beginChunk(const std::size_t /* chunk_size */)
      92             : {
      93           5 :   return this->begin();
      94             : }
      95             : 
      96             : template <typename T>
      97             : typename Buffer<T>::const_iterator
      98           0 : CircularBuffer<T>::beginChunk(const std::size_t /* chunk_size */) const
      99             : {
     100           0 :   return this->begin();
     101             : }
     102             : 
     103             : template <typename T>
     104             : typename Buffer<T>::iterator
     105           5 : CircularBuffer<T>::endChunk(const std::size_t chunk_size)
     106             : {
     107           5 :   if (chunk_size > this->size())
     108           1 :     return this->end();
     109             :   else
     110           4 :     return this->begin() + chunk_size;
     111             : }
     112             : 
     113             : template <typename T>
     114             : typename Buffer<T>::const_iterator
     115           0 : CircularBuffer<T>::endChunk(const std::size_t chunk_size) const
     116             : {
     117           0 :   if (chunk_size > this->size())
     118           0 :     return this->end();
     119             :   else
     120           0 :     return this->begin() + chunk_size;
     121             : }
     122             : 
     123             : template <typename T>
     124             : std::size_t
     125          38 : CircularBuffer<T>::newEnd(const std::size_t new_end)
     126             : {
     127          38 :   auto actual_new_end = new_end;
     128             : 
     129          38 :   if (new_end > this->_data.size())
     130             :   {
     131           4 :     const auto new_size = new_end - this->_begin_pos;
     132             : 
     133             :     // See if we need to grow our capacity
     134           4 :     if (this->_begin_pos == 0) // If we're already using the beginning - just resize
     135           1 :       this->_data.resize(2 * new_size);
     136             :     else
     137             :     {
     138             :       // Move everything to the beginning
     139           3 :       auto to_it = this->_data.begin();
     140          14 :       for (auto from_it = this->begin(); from_it < this->end(); ++from_it)
     141          11 :         *to_it++ = std::move(*from_it);
     142             : 
     143           3 :       this->_begin_pos = 0;
     144           3 :       actual_new_end = new_size;
     145             : 
     146             :       // If there still isn't room... add space
     147           3 :       if (actual_new_end > this->_data.size())
     148           1 :         this->_data.resize(2 * new_size);
     149             :     }
     150             :   }
     151             : 
     152          38 :   return actual_new_end;
     153             : }
     154             : 
     155             : }

Generated by: LCOV version 1.14