File indexing completed on 2024-11-24 04:52:54

0001 /* Copyright (C) 2006 - 2014 Jan Kundrát <jkt@flaska.net>
0002 
0003    This file is part of the Trojita Qt IMAP e-mail client,
0004    http://trojita.flaska.net/
0005 
0006    This program is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU General Public License as
0008    published by the Free Software Foundation; either version 2 of
0009    the License or (at your option) version 3 or any later version
0010    accepted by the membership of KDE e.V. (or its successor approved
0011    by the membership of KDE e.V.), which shall act as a proxy
0012    defined in Section 14 of version 3 of the license.
0013 
0014    This program is distributed in the hope that it will be useful,
0015    but WITHOUT ANY WARRANTY; without even the implied warranty of
0016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0017    GNU General Public License for more details.
0018 
0019    You should have received a copy of the GNU General Public License
0020    along with this program.  If not, see <http://www.gnu.org/licenses/>.
0021 */
0022 #ifndef TROJITA_RINGBUFFER_H
0023 #define TROJITA_RINGBUFFER_H
0024 
0025 #include <QVector>
0026 
0027 namespace Common
0028 {
0029 
0030 /** @short Circular buffer holding a number of items
0031 
0032 This class holds a fixed number of items. Use the append() function to put stuff into it. When you want to retrieve them,
0033 obtain an iterator by calling begin(). The returned iterator will cease to be valid immediately after modifying the ring
0034 buffer. Iteration has to be performed by comparing the current value of an iterator for equivalence against the
0035 container's end(), and incrementing the iterator. Any other form is not supported.
0036 
0037 Invalidated iterators will likely not get caught.
0038 */
0039 template<typename T>
0040 class RingBuffer
0041 {
0042 public:
0043     /** @short Constat iterator fro visiting the items after each other, starting at the oldest one
0044 
0045     The iterator itself is linear, ie. it won't wrap. It will however
0046     */
0047     class const_iterator
0048     {
0049         const RingBuffer<T> *container_;
0050         int offset_;
0051     public:
0052         /** @short Dereference the iterator */
0053         const T &operator*() const
0054         {
0055             // It has to point to a correct offset
0056             Q_ASSERT(offset_ >= 0 && offset_ < container_->buf_.size());
0057             int pos = container_->wrapped_ ?
0058                       // It got wrapped, so we have to get wrapped past the end, too, and start at the oldest one
0059                       (container_->appendPos_ + offset_) % container_->buf_.size() :
0060                       // It isn't full yet
0061                       offset_;
0062             return container_->buf_[pos];
0063         }
0064 
0065         const T *operator->() const
0066         {
0067             return &operator*();
0068         }
0069 
0070         /** @short Increment the iterator */
0071         const_iterator &operator++()
0072         {
0073             ++offset_;
0074             // Allow incrementing to the end, ie. one past the last item
0075             Q_ASSERT(offset_ <= container_->buf_.size());
0076             return *this;
0077         }
0078 
0079         /** @short Compare two iterators from the same container for equality */
0080         bool operator==(const const_iterator &other) const
0081         {
0082             Q_ASSERT(container_ == other.container_);
0083             return offset_ == other.offset_;
0084         }
0085 
0086         /** @short Compare two iterators from the same container for inqeuality */
0087         bool operator!=(const const_iterator &other) const
0088         {
0089             return !(*this == other);
0090         }
0091     private:
0092         friend class RingBuffer<T>;
0093         const_iterator(const RingBuffer<T> *container, int offset): container_(container), offset_(offset)
0094         {
0095         }
0096     };
0097 
0098     /** @short Instantiate a ring buffer holding size elements */
0099     RingBuffer(const int size): buf_(size), appendPos_(0), wrapped_(false), skipped_(0)
0100     {
0101         Q_ASSERT(size >= 1);
0102     }
0103 
0104     /** @short Return an interator pointing to the oldest item in the container */
0105     const_iterator begin() const
0106     {
0107         return const_iterator(this, 0);
0108     }
0109 
0110     /** @short Return an interator pointing to one item past the recent addition */
0111     const_iterator end() const
0112     {
0113         return const_iterator(this, wrapped_ ?  buf_.size() : appendPos_);
0114     }
0115 
0116     /** @short Append an item to the container. Oldest item could get overwritten. */
0117     void append(const T &what)
0118     {
0119         if (appendPos_ == buf_.size()) {
0120             wrapped_ = true;
0121             appendPos_ = 0;
0122         }
0123         if (wrapped_)
0124             ++skipped_;
0125         buf_[appendPos_] = what;
0126         ++appendPos_;
0127     }
0128 
0129     /** @short Remove all items from the container */
0130     void clear()
0131     {
0132         buf_ = QVector<T>(buf_.size());
0133         wrapped_ = false;
0134         appendPos_ = 0;
0135         skipped_ = 0;
0136     }
0137 
0138     /** @short How many items were overwritten */
0139     uint skippedCount() const
0140     {
0141         return skipped_;
0142     }
0143 
0144 private:
0145     QVector<T> buf_;
0146     int appendPos_;
0147     bool wrapped_;
0148     uint skipped_;
0149 };
0150 
0151 }
0152 
0153 #endif // TROJITA_RINGBUFFER_H