1.overwrite old element
2.not thread-safe
3.bitwise instead of modulo operation
#ifndef _CIRCULAR_BUFFER_H_2022_10_02
#define _CIRCULAR_BUFFER_H_2022_10_02
#include <vector>
#include<climits>
#include <iostream>
inline bool is_power_of_2(unsigned int v)
{
return v && (!(v & (v - 1)));
}
inline unsigned int roundup_pow_of_2(unsigned int v)
{
if (v == 0)
return 0;
unsigned int tmp = 1U;
while (v >>= 1)
{
tmp <<= 1;
}
return tmp <<= 1;
}
template <typename Ty_>
class CircularBuffer
{
public:
explicit CircularBuffer(unsigned int max_size = 255)
{
//static_assert(!std::is_pointer<Ty_>::value, "not support pointers");
if (max_size <= 0 || max_size >= (1U << (sizeof(max_size) * CHAR_BIT - 1)))
max_size = 255;
++max_size; //one room for flagging full
if (!is_power_of_2(max_size))
{
m_capacity = roundup_pow_of_2(max_size);
}
else
{
m_capacity = max_size;
}
m_buffer.resize(m_capacity);
}
bool empty() const
{
return m_read_index == m_write_index;
}
bool full() const
{
return ((m_write_index + 1) & (m_capacity - 1)) == (m_read_index & (m_capacity - 1));
}
void push_back(const Ty_& e)
{
m_buffer[m_write_index & (m_capacity - 1)] = e;
if (full())
{
++m_read_index;
}
m_write_index++;
}
void push_back(Ty_&& e)
{
m_buffer[m_write_index & (m_capacity - 1)] = std::move(e);
if (full())
{
++m_read_index;
}
m_write_index++;
}
void pop_front()
{
if (!empty()) ++m_read_index;
}
//UB if empty
Ty_& front()
{
return at(0);
}
const Ty_& front() const
{
return at(0);
}
Ty_& at(unsigned int index)
{
if (index >= size()) throw std::invalid_argument("at out of range");
index += m_read_index;
index &= (m_capacity - 1);
return m_buffer[index];
}
const Ty_& at(unsigned int index) const
{
if (index >= size()) throw std::invalid_argument("at out of range");
index += m_read_index;
index &= (m_capacity - 1);
return m_buffer[index];
}
//std::shared_ptr<Ty_> front(unsigned int index) const
//{
// return at(0);
//}
//std::shared_ptr<Ty_> at(unsigned int index) const
//{
// if (index >= size()) return std::shared_ptr<Ty_>();
// index += m_read_index;
// index &= (m_capacity - 1);
// std::shared_ptr<Ty_> res(std::make_shared<Ty_>(m_buffer[index]));
// return res;
//}
unsigned int size() const
{
return m_write_index - m_read_index;
}
unsigned int capacity() const
{
return m_capacity;
}
private:
std::vector<Ty_> m_buffer;
unsigned int m_read_index{ 0 };
unsigned int m_write_index{ 0 };
unsigned int m_capacity{ 0 };
};
//test case
template<typename Ty_>
std::ostream& operator<<(std::ostream& out, CircularBuffer<Ty_>& cb)
{
unsigned int size = cb.size();
if (0 == size)
{
out << "empty" << std::endl;
return out;
}
unsigned int index = 0;
while (index < size)
{
out << cb.at(index) << " ";
index++;
}
out << std::endl;
return out;
}
void test_circular_buffer()
{
constexpr int max_size = 4;
//not support, fail when compile
//CircularBuffer<int*> cb_pointer;
CircularBuffer<int> cb(max_size);
std::cout << cb;
cb.push_back(1);
cb.push_back(2);
cb.push_back(3);
cb.push_back(4);
std::cout << cb;
cb.front() = 9;
std::cout << cb;
cb.push_back(5);
cb.push_back(6);
std::cout << cb;
std::cout << cb;
cb.pop_front();
std::cout << cb;
cb.pop_front();
cb.pop_front();
cb.pop_front();
cb.pop_front();
std::cout << cb;
cb.pop_front();
std::cout << cb;
}
#endif