You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

203 lines
4.7 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#pragma once
#include <vector>
#include <cstddef>
#include <utility>
#include <cassert>
/**
* @brief 固定容量的环形缓冲区
*
* 容量在编译期固定且必须为 2 的幂head/tail 为无符号整数,下标通过 % Capacity 取模。
* 满时继续 push 会覆盖最旧元素。接口风格与 std::deque 类似front/back/pop_front
*
* @tparam T 元素类型
* @tparam Capacity 缓冲区容量,必须大于 0 且为 2 的幂
*/
template<typename T, size_t Capacity>
class RingBuffer
{
static_assert(Capacity > 0 && (Capacity & (Capacity - 1)) == 0, "RingBuffer capacity must be a power of 2");
public:
/** @brief 默认构造,内部缓冲区预分配 Capacity 个元素。 */
RingBuffer() : m_data(Capacity) {}
/**
* @brief 在尾部写入一个元素(拷贝)
* @param value 要写入的值
* @note 若已满则覆盖最旧项,不报错。
*/
void push(const T& value)
{
if (m_size == Capacity)
{
m_head = (m_head + 1) % Capacity;
}
else
{
++m_size;
}
m_data[m_tail] = value;
m_tail = (m_tail + 1) % Capacity;
}
/**
* @brief 在尾部写入一个元素(移动)
* @param value 要写入的值(右值引用,调用后处于未指定状态)
* @note 若已满则覆盖最旧项,不报错。
*/
void push(T&& value)
{
if (m_size == Capacity)
{
m_head = (m_head + 1) % Capacity;
}
else
{
++m_size;
}
m_data[m_tail] = std::move(value);
m_tail = (m_tail + 1) % Capacity;
}
/**
* @brief 取队头(最旧元素)的常量引用
* @return 队头元素的 const 引用
* @pre 调用前须 !empty(),空时断言失败
*/
const T& front() const
{
assert(!empty() && "RingBuffer::front() on empty buffer");
return m_data[m_head];
}
/**
* @brief 取队头(最旧元素)的可写引用
* @return 队头元素的非 const 引用
* @pre 调用前须 !empty(),空时断言失败
*/
T& front()
{
assert(!empty() && "RingBuffer::front() on empty buffer");
return m_data[m_head];
}
/**
* @brief 取队尾(最新元素)的常量引用
* @return 队尾元素的 const 引用
* @pre 调用前须 !empty(),空时断言失败
*/
const T& back() const
{
assert(!empty() && "RingBuffer::back() on empty buffer");
return m_data[(m_tail + Capacity - 1) % Capacity];
}
/**
* @brief 取队尾(最新元素)的可写引用
* @return 队尾元素的非 const 引用
* @pre 调用前须 !empty(),空时断言失败
*/
T& back()
{
assert(!empty() && "RingBuffer::back() on empty buffer");
return m_data[(m_tail + Capacity - 1) % Capacity];
}
/**
* @brief 按逻辑下标取元素(只读)
* @param i 逻辑下标0 为最旧size()-1 为最新
* @return 下标 i 处元素的 const 引用
* @pre 调用前须 i < size(),越界时断言失败
*/
const T& operator[](size_t i) const
{
assert(i < m_size && "RingBuffer::operator[] index out of range");
return m_data[(m_head + i) % Capacity];
}
/**
* @brief 按逻辑下标取元素(可写)
* @param i 逻辑下标0 为最旧size()-1 为最新
* @return 下标 i 处元素的非 const 引用
* @pre 调用前须 i < size(),越界时断言失败
*/
T& operator[](size_t i)
{
assert(i < m_size && "RingBuffer::operator[] index out of range");
return m_data[(m_head + i) % Capacity];
}
/**
* @brief 弹出队头(最旧元素)
* @note 空时无操作,不修改状态、不断言。
*/
void pop_front()
{
if (empty())
return;
m_head = (m_head + 1) % Capacity;
--m_size;
}
/**
* @brief 清空缓冲区,移除所有元素;容量不变,可继续 push。
*/
void clear()
{
m_head = 0;
m_tail = 0;
m_size = 0;
}
/**
* @brief 是否存在等于 value 的元素
* @param value 要查找的值(要求 T 支持 operator==
* @return 存在则 true否则 false
* @note 线性扫描,时间复杂度 O(size())
*/
bool contains(const T& value) const
{
for (size_t i = 0; i < m_size; ++i)
{
size_t idx = (m_head + i) % Capacity;
if (m_data[idx] == value)
{
return true;
}
}
return false;
}
/** @brief 当前元素个数 */
size_t size() const
{
return m_size;
}
/** @brief 缓冲区容量(编译期常量)。 */
size_t capacity() const
{
return Capacity;
}
/** @brief 是否为空(无元素)。 */
bool empty() const
{
return m_size == 0;
}
/** @brief 是否已满(元素个数等于容量)。 */
bool full() const
{
return m_size == Capacity;
}
private:
std::vector<T> m_data; /**< 底层存储,大小为 Capacity */
size_t m_head = 0; /**< 最旧元素的下标 */
size_t m_tail = 0; /**< 下一个写入位置(尾后) */
size_t m_size = 0; /**< 当前元素个数 */
};