Skip to content

File CircularBuffer.h

File List > src > utils > CircularBuffer.h

Go to the documentation of this file

//https://github.com/vinitjames

#ifndef CIRCULAR_BUFFER_H
#define CIRCULAR_BUFFER_H

#include <algorithm>
#include <iterator>
#include <mutex>
#include <memory>
#include <stdexcept>
#include <utility>


template<typename T>
class CircularBuffer {
private:

    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    template <bool isConst> struct BufferIterator;


public:
    typedef T value_type;

    explicit CircularBuffer(size_t size)
        :_buff{std::unique_ptr<T[]>(new value_type[size])}, _max_size{size}{}

    CircularBuffer(const CircularBuffer& other)
        :_buff{std::unique_ptr<T[]>(new value_type[other._max_size])},
         _max_size{other._max_size},
         _size{other._size},
         _head{other._head},
         _tail{other._tail}{
             std::copy(other.data(), other.data() + _max_size, _buff.get());
         }


    CircularBuffer& operator=(const CircularBuffer& other){
        if ( this != &other){
            _buff.reset(new value_type[other._max_size]);
            _max_size = other._max_size;
            _size = other._size;
            _head = other._head;
            _tail = other._tail;
            std::copy(other.data(), other.data() + _max_size, _buff.get());
        }
        return *this;
    }

    CircularBuffer(CircularBuffer&& other) noexcept
        :_buff{std::move(other._buff)},
         _max_size{other._max_size},
         _size{other._size},
         _head{other._head},
         _tail{other._tail}{

        other._buff = nullptr;
        other._max_size = 0;
        other._size = 0;
        other._head = 0;
        other._tail = 0;
    }


    CircularBuffer& operator=(CircularBuffer&& other) noexcept{
        if ( this != &other){
            _buff = std::move(other._buff);
            _max_size = other._max_size;
            _size = other._size;
            _head = other._head;
            _tail = other._tail;

            other._buff = nullptr;
            other._max_size = 0;
            other._size = 0;
            other._head = 0;
            other._tail = 0;            
        }
        return *this;
    }

    void push_back(const value_type& data);
    void push_back(value_type&& data) noexcept;
    void pop_front();
    reference front();
    reference back(); 
    const_reference front() const; 
    const_reference back() const;
    void clear();
    bool empty() const ;
    bool full() const ;
    size_type capacity() const ;
    size_type size() const;
    size_type buffer_size() const {return sizeof(value_type)*_max_size;};
    const_pointer data() const { return _buff.get(); }

    const_reference operator[](size_type index) const;
    reference operator[](size_type index);
    const_reference at(size_type index) const;
    reference at(size_type index);

    typedef BufferIterator<false> iterator;
    typedef BufferIterator<true> const_iterator;

    iterator begin();
    const_iterator begin() const;
    iterator end();
    const_iterator end() const;
    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;
    iterator rbegin() noexcept;
    const_iterator rbegin() const noexcept;
    iterator rend() noexcept;
    const_iterator rend() const noexcept;


private:
    void _increment_bufferstate();
    void _decrement_bufferstate();
    mutable std::mutex _mtx;
    std::unique_ptr<value_type[]> _buff;
    size_type _head = 0;
    size_type _tail = 0;
    size_type _size = 0;
    size_type _max_size = 0;

    template<bool isConst = false>
    struct  BufferIterator{
    public:
        friend class CircularBuffer<T>;
        typedef std::random_access_iterator_tag iterator_category;
        typedef ptrdiff_t difference_type;
        typedef T value_type;
        typedef typename std::conditional<isConst, const value_type&, value_type&>::type reference;
        typedef typename std::conditional<isConst, const value_type*, value_type*>::type pointer;
        typedef typename std::conditional<isConst, const CircularBuffer<value_type>*,
                                          CircularBuffer<value_type>*>::type cbuf_pointer;
    private:
        cbuf_pointer _ptrToBuffer;
        size_type _offset;
        size_type _index;
        bool _reverse;

        bool _comparable(const BufferIterator<isConst>& other) const{
            return (_ptrToBuffer == other._ptrToBuffer)&&(_reverse == other._reverse);
        }

    public:
        BufferIterator()
            :_ptrToBuffer{nullptr}, _offset{0}, _index{0}, _reverse{false}{}

        BufferIterator(const BufferIterator<false>& it)
            :_ptrToBuffer{it._ptrToBuffer},
             _offset{it._offset},
             _index{it._index},
             _reverse{it._reverse}{}

        reference operator*(){
            if(_reverse)
                return (*_ptrToBuffer)[(_ptrToBuffer->size() - _index - 1)];
            return (*_ptrToBuffer)[_index];
        }

        pointer  operator->() { return &(operator*()); }

        reference operator[](size_type index){
            BufferIterator iter = *this;
            iter._index += index;
            return *iter;
        }

        BufferIterator& operator++(){
            ++_index;
            return *this;
        }

        BufferIterator operator++(int){
            BufferIterator iter = *this;
            ++_index;
            return iter;
        }

        BufferIterator& operator--(){
            --_index;
            return *this;
        }

        BufferIterator operator--(int){
            BufferIterator iter = *this;
            --_index;
            return iter;
        }   

        friend BufferIterator operator+(BufferIterator lhsiter, difference_type n){
            lhsiter._index += n;
            return lhsiter;
        }

        friend BufferIterator operator+(difference_type n, BufferIterator rhsiter){
            rhsiter._index += n;
            return rhsiter;
        }


        BufferIterator& operator+=(difference_type n){
            _index += n;
            return *this;
        }

        friend BufferIterator operator-(BufferIterator lhsiter, difference_type n){
            lhsiter._index -= n;
            return lhsiter;
        }

        friend difference_type operator-(const BufferIterator& lhsiter, const BufferIterator& rhsiter){

            return lhsiter._index - rhsiter._index;
        }

        BufferIterator& operator-=(difference_type n){
            _index -= n;
            return *this;
        }

        bool operator==(const BufferIterator& other) const{
            if (!_comparable(other))
                return false;
            return ((_index == other._index)&&(_offset == other._offset));
        }

        bool operator!=(const BufferIterator& other) const{
            if (!_comparable(other))
                return true;
            return ((_index != other._index)||(_offset != other._offset));
        }

        bool operator<(const BufferIterator& other) const {
            if (!_comparable(other))
                return false;
            return ((_index + _offset)<(other._index+other._offset));
        }

        bool operator>(const BufferIterator& other) const{
            if (!_comparable(other))
                return false;
            return ((_index + _offset)>(other._index+other._offset));
        }

        bool operator<=(const BufferIterator& other) const {
            if (!_comparable(other))
                return false;
            return ((_index + _offset)<=(other._index+other._offset));
        }

        bool operator>=(const BufferIterator& other) const {
            if (!_comparable(other))
                return false;
            return ((_index + _offset)>=(other._index+other._offset));
        }
    };
};

template<typename T>
inline 
bool CircularBuffer<T>::full() const{
    return _size == _max_size;
}

template<typename T>
inline 
bool CircularBuffer<T>::empty() const{
    return _size == 0;
}

template<typename T>
inline 
typename CircularBuffer<T>::size_type CircularBuffer<T>::capacity() const{
    return _max_size;
}

template<typename T>
inline 
void  CircularBuffer<T>::clear(){
    std::lock_guard<std::mutex> _lck(_mtx);
    _head = _tail = _size = 0;
}

template<typename T>
inline 
typename CircularBuffer<T>::size_type CircularBuffer<T>::size() const{
    std::lock_guard<std::mutex> _lck(_mtx);
    return _size;
    }

template<typename T>
inline
typename CircularBuffer<T>::reference CircularBuffer<T>::front() {
    std::lock_guard<std::mutex> _lck(_mtx);
    if(empty())
        throw std::length_error("front function called on empty buffer");
    return _buff[_tail];
}

template<typename T>
inline
typename CircularBuffer<T>::reference CircularBuffer<T>::back() {
    std::lock_guard<std::mutex> _lck(_mtx);
    if(empty())
        throw std::length_error("back function called on empty buffer");
    return _head == 0 ? _buff[_max_size - 1] : _buff[_head - 1];
}

template<typename T>
inline
typename CircularBuffer<T>::const_reference CircularBuffer<T>::front() const{
    std::lock_guard<std::mutex> _lck(_mtx);
    if(empty())
        throw std::length_error("front function called on empty buffer");
    return _buff[_tail];
}

template<typename T>
inline
typename CircularBuffer<T>::const_reference CircularBuffer<T>::back() const{
    std::lock_guard<std::mutex> _lck(_mtx);
    if(empty())
        throw std::length_error("back function called on empty buffer");
    return _head == 0 ? _buff[_max_size - 1] : _buff[_head - 1];
}

template<typename T>
inline
void CircularBuffer<T>::push_back(const T& data){
    std::lock_guard<std::mutex> _lck(_mtx);
    //if(full())
    //  _buff[_tail].~T();
    _buff[_head] = data;
    _increment_bufferstate();
}

template<typename T>
inline
void CircularBuffer<T>::push_back(T&& data) noexcept{
    std::lock_guard<std::mutex> _lck(_mtx);
    _buff[_head] = std::move(data);
    _increment_bufferstate();
}


template<typename T>
inline 
void CircularBuffer<T>::_increment_bufferstate(){
    if(full())
        _tail = (_tail + 1)%_max_size;
    else
        ++_size;
    _head = (_head + 1)%_max_size;  
}

template<typename T>
inline 
void CircularBuffer<T>::pop_front(){
    std::lock_guard<std::mutex> _lck(_mtx);
    if(empty())
        throw std::length_error("pop_front called on empty buffer");
    _decrement_bufferstate();
}

template<typename T>
inline 
void CircularBuffer<T>::_decrement_bufferstate(){
    --_size;
    _tail = (_tail + 1)%_max_size;
}

template<typename T>
inline 
typename CircularBuffer<T>::reference CircularBuffer<T>::operator[](size_t index) {
    std::lock_guard<std::mutex> _lck(_mtx);
    if((index<0)||(index>=_size))
        throw std::out_of_range("Index is out of Range of buffer size");
    index += _tail;
    index %= _max_size;
    return _buff[index];
}

template<typename T>
inline 
typename CircularBuffer<T>::const_reference CircularBuffer<T>::operator[](size_t index) const {
    std::lock_guard<std::mutex> _lck(_mtx);
    if((index<0)||(index>=_size))
        throw std::out_of_range("Index is out of Range of buffer size");
    index += _tail;
    index %= _max_size;
    return _buff[index];
}

template<typename T>
inline 
typename CircularBuffer<T>::reference CircularBuffer<T>::at(size_t index) {
    std::lock_guard<std::mutex> _lck(_mtx);
    if((index<0)||(index>=_size))
        throw std::out_of_range("Index is out of Range of buffer size");
    index += _tail;
    index %= _max_size;
    return _buff[index];
}

template<typename T>
inline 
typename CircularBuffer<T>::const_reference CircularBuffer<T>::at(size_t index) const {
    std::lock_guard<std::mutex> _lck(_mtx);
    if((index<0)||(index>=_size))
        throw std::out_of_range("Index is out of Range of buffer size");
    index += _tail;
    index %= _max_size;
    return _buff[index];
}

template<typename T>
inline 
typename CircularBuffer<T>::iterator CircularBuffer<T>::begin() {
    std::lock_guard<std::mutex> _lck(_mtx);
    iterator iter;
    iter._ptrToBuffer = this;
    iter._offset = _tail;
    iter._index = 0;
    iter._reverse = false;
    return iter;
}

template<typename T>
inline 
typename CircularBuffer<T>::const_iterator CircularBuffer<T>::begin() const{
    std::lock_guard<std::mutex> _lck(_mtx);
    const_iterator iter;
    iter._ptrToBuffer = this;
    iter._offset = _tail;
    iter._index = 0;
    iter._reverse = false;
    return iter;
}

template<typename T>
inline 
typename CircularBuffer<T>::iterator CircularBuffer<T>::end() {
    std::lock_guard<std::mutex> _lck(_mtx);
    iterator iter;
    iter._ptrToBuffer = this;
    iter._offset = _tail;
    iter._index = _size;
    iter._reverse = false;
    return iter;
}

template<typename T>
inline 
typename CircularBuffer<T>::const_iterator CircularBuffer<T>::end() const{
    std::lock_guard<std::mutex> _lck(_mtx);
    const_iterator iter;
    iter._ptrToBuffer = this;
    iter._offset = _tail;
    iter._index = _size;
    iter._reverse = false;
    return iter;
}

template<typename T>
inline 
typename CircularBuffer<T>::const_iterator CircularBuffer<T>::cbegin() const noexcept{
    std::lock_guard<std::mutex> _lck(_mtx);
    const_iterator iter;
    iter._ptrToBuffer = this;
    iter._offset = _tail;
    iter._index = 0;
    iter._reverse = false;
    return iter;
}

template<typename T>
inline 
typename CircularBuffer<T>::const_iterator CircularBuffer<T>::cend() const noexcept{
    std::lock_guard<std::mutex> _lck(_mtx);
    const_iterator iter;
    iter._ptrToBuffer = this;
    iter._offset = _tail;
    iter._index = _size;
    iter._reverse = false;
    return iter;
}

template<typename T>
inline 
typename CircularBuffer<T>::iterator CircularBuffer<T>::rbegin() noexcept{
    std::lock_guard<std::mutex> _lck(_mtx);
    iterator iter;
    iter._ptrToBuffer = this;
    iter._offset = _tail;
    iter._index = 0;
    iter._reverse = true;
    return iter;
}

template<typename T>
inline 
typename CircularBuffer<T>::const_iterator CircularBuffer<T>::rbegin() const noexcept{
    std::lock_guard<std::mutex> _lck(_mtx);
    const_iterator iter;
    iter._ptrToBuffer = this;
    iter._offset = _tail;
    iter._index = 0;
    iter._reverse = true;
    return iter;
}

template<typename T>
inline 
typename CircularBuffer<T>::iterator CircularBuffer<T>::rend()  noexcept{
    std::lock_guard<std::mutex> _lck(_mtx);
    iterator iter;
    iter._ptrToBuffer = this;
    iter._offset = _tail;
    iter._index = _size;
    iter._reverse = true;
    return iter;
}

template<typename T>
inline 
typename CircularBuffer<T>::const_iterator CircularBuffer<T>::rend() const noexcept{
    std::lock_guard<std::mutex> _lck(_mtx);
    const_iterator iter;
    iter._ptrToBuffer = this;
    iter._offset = _tail;
    iter._index = _size;
    iter._reverse = true;
    return iter;
}

#endif /* CIRCULAR_BUFFER_H */