Files
CPP_Core/Core/Base/RingBuffer.hpp
T
2026-06-16 10:56:40 +08:00

324 lines
11 KiB
C++

#pragma once
#include "../system/export.h"
#include "Core/Base/global_include.h"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <mutex>
#include <string>
#include <string_view>
#include <vector>
namespace Psc {
// -------------------- utils --------------------
template <typename Size_T>
bool is_pow2(Size_T x) { return x && ((x & (x - 1)) == 0); }
template <typename Size_T>
Size_T ceil_to_pow2(Size_T x) {
if (x <= 1) return 1;
--x;
for (Size_T i = 1; i < sizeof(Size_T) * 8; i <<= 1) x |= x >> i;
return x + 1;
}
// -------------------- Base RingBuffer --------------------
// - capacity 必须为 2^n
// - mask = capacity - 1
// - used = (tail - head) & mask
// - free = (capacity - 1) - used (浪费 1 字节)
template <typename MutexT, typename Size_T>
class Base_RingBuffer {
static_assert(std::is_unsigned_v<Size_T>, "Size_T must be unsigned");
public:
Base_RingBuffer() = default;
void init(Size_T min_capacity) {
// 防御:min_capacity + 1 溢出
if (min_capacity >= std::numeric_limits<Size_T>::max()) {
fail_fast("Ring buffer size is too large" + Psc::to_string(min_capacity));
}
// +1 维持“浪费一个字节”的策略
const Size_T cap = ceil_to_pow2(min_capacity + 1);
capacity = cap;
head = 0;
tail = 0;
mask_ = cap - 1;
buf_.assign(cap, 0);
if (!is_pow2(capacity)) {
std::terminate();
}
}
[[nodiscard]] bool empty() const { return head == tail; }
[[nodiscard]] bool full() const { return ((tail + 1) & mask_) == head; }
[[nodiscard]] std::string state_str() {
std::lock_guard<MutexT> lock(mutex_);
auto free_space = free_space_unsafe();
auto used_space = used_space_unsafe();
return VAR_STR_5(free_space, used_space, head, tail, capacity);
}
protected:
// used_space / free_space 与共享内存版一致
[[nodiscard]] Size_T used_space_unsafe() const {
return (tail - head) & mask_;
}
[[nodiscard]] Size_T free_space_unsafe() const {
return (capacity - 1) - used_space_unsafe();
}
void rollback_unsafe(Size_T n) { head = (head - n) & mask_; }
void write_bytes_unsafe(const void* data, Size_T len) {
const Size_T end = std::min(len, capacity - tail);
std::memcpy(buf_.data() + tail, data, end);
std::memcpy(buf_.data(), static_cast<const std::uint8_t*>(data) + end, len - end);
tail = (tail + len) & mask_;
}
void read_bytes_unsafe(void* out, Size_T len) {
const Size_T end = std::min(len, capacity - head);
std::memcpy(out, buf_.data() + head, end);
std::memcpy(static_cast<std::uint8_t*>(out) + end, buf_.data(), len - end);
head = (head + len) & mask_;
}
void peek_bytes_unsafe(void* out, Size_T len) const {
const Size_T end = std::min(len, capacity - head);
std::memcpy(out, buf_.data() + head, end);
std::memcpy(static_cast<std::uint8_t*>(out) + end, buf_.data(), len - end);
}
protected:
MutexT mutex_{};
Size_T capacity = 0;
Size_T head = 0;
Size_T tail = 0;
std::vector<uint8_t> buf_{};
Size_T mask_ = 0;
};
// -------------------- Packet mode (length-prefixed) --------------------
template <class MutexT, typename Size_T>
class RingBuffer : public Base_RingBuffer<MutexT, Size_T> {
public:
static constexpr Size_T HEADER_SIZE = sizeof(Size_T);
// 包模式:空间不够则整体失败(不拆包)
bool write(const void* src, Size_T len) {
std::lock_guard<MutexT> lock(this->mutex_);
if (len > (this->capacity - 1 - HEADER_SIZE)) return false;
const Size_T need = HEADER_SIZE + len;
if (this->free_space_unsafe() < need) return false;
this->write_bytes_unsafe(reinterpret_cast<const void*>(&len), HEADER_SIZE);
if (len) this->write_bytes_unsafe(src, len);
return true;
}
// out_len: in 代表 out 缓冲最大容量;out_len: out 代表实际读到的长度
bool read(void* out, Size_T& out_len) {
std::lock_guard<MutexT> lock(this->mutex_);
if (this->empty()) return false;
Size_T msg_len = 0;
this->peek_bytes_unsafe(reinterpret_cast<void*>(&msg_len), HEADER_SIZE);
const Size_T need = HEADER_SIZE + static_cast<Size_T>(msg_len);
if (this->used_space_unsafe() < need) return false; // 半包
if (out_len < msg_len) return false; // 调用者提供的 out 不够大
Size_T dummy = 0;
this->read_bytes_unsafe(reinterpret_cast<void*>(&dummy), HEADER_SIZE);
if (msg_len) this->read_bytes_unsafe(out, msg_len);
out_len = msg_len;
return true;
}
// 只 peek 包头,不消费;成功返回 true,并写出 msg_len
bool peek_len(Size_T& msg_len) {
std::lock_guard<MutexT> lock(this->mutex_);
if (this->empty()) return false;
this->peek_bytes_unsafe(reinterpret_cast<void*>(&msg_len), HEADER_SIZE);
// 防御:坏包头(避免队列被毒化)
if (msg_len > (this->capacity - 1 - HEADER_SIZE)) return false;
const Size_T need = HEADER_SIZE + msg_len;
if (this->used_space_unsafe() < need) return false; // 半包
return true;
}
// peek 下一条消息内容(不消费)
// out_len: in 为 out 缓冲容量;out_len: out 为消息实际长度
bool peek(void* out, Size_T& out_len) {
std::lock_guard<MutexT> lock(this->mutex_);
if (this->empty()) return false;
Size_T msg_len = 0;
this->peek_bytes_unsafe(reinterpret_cast<void*>(&msg_len), HEADER_SIZE);
if (msg_len > (this->capacity - 1 - HEADER_SIZE)) return false;
const Size_T need = HEADER_SIZE + msg_len;
if (this->used_space_unsafe() < need) return false; // 半包
if (out_len < msg_len) return false;
// 先临时把 head 向前偏移 HEADER_SIZE,再 peek msg_len 字节
// 不修改 head:用局部 off 来计算
Size_T off = (this->head + HEADER_SIZE) & this->mask_;
// 复用 copy 逻辑:这里写一个局部的“从任意 off 读”版本(不改 head)
// 由于 Base 里没有该函数,这里直接展开一份(与 peek_bytes_unsafe 类似)
const Size_T cap = this->capacity;
const Size_T end = std::min(msg_len, cap - off);
std::memcpy(out, this->buf_.data() + off, end);
std::memcpy(static_cast<std::uint8_t*>(out) + end, this->buf_.data(), msg_len - end);
out_len = msg_len;
return true;
}
// 仅丢弃/消费下一条消息(不拷贝内容)
// 成功返回 true;若空/半包/坏包头则 false
bool skip_one() {
std::lock_guard<MutexT> lock(this->mutex_);
if (this->empty()) return false;
Size_T msg_len = 0;
this->peek_bytes_unsafe(reinterpret_cast<void*>(&msg_len), HEADER_SIZE);
// 防御:坏包头,避免队列被毒化
if (msg_len > (this->capacity - 1 - HEADER_SIZE)) return false;
const Size_T need = HEADER_SIZE + msg_len;
if (this->used_space_unsafe() < need) return false; // 半包
// 直接前移 head(不实际读数据)
this->head = (this->head + need) & this->mask_;
return true;
}
// 仅丢弃/消费下一条消息的 payload 的前 n 字节(不拷贝)
// 用于“读一条大消息但分段处理”的场景:你先 peek_len/peek 拿到长度,再分段 consume
// 注意:它不会跳过 header;它假设你已经消费过 header 或者你自定义协议
// 如果你只需要 skip 整包,用 skip_one()
bool skip_bytes(Size_T n) {
std::lock_guard<MutexT> lock(this->mutex_);
if (n == 0) return true;
if (this->used_space_unsafe() < n) return false;
this->head = (this->head + n) & this->mask_;
return true;
}
};
// -------------------- Stream mode --------------------
template <class MutexT, typename Size_T>
class Stream_RingBuffer : public Base_RingBuffer<MutexT, Size_T> {
public:
bool write(const void* src, Size_T len) {
if (len == 0) return true;
std::lock_guard<MutexT> lock(this->mutex_);
if (this->free_space_unsafe() < len) return false;
this->write_bytes_unsafe(src, len);
return true;
}
// out_len 代表要读多少;成功则读满 out_len
bool read(void* out, Size_T& out_len) {
if (out_len == 0) return true;
std::lock_guard<MutexT> lock(this->mutex_);
if (this->used_space_unsafe() < out_len) {
out_len = 0;
return false;
}
this->read_bytes_unsafe(out, out_len);
return true;
}
// peek 最多 out_len 字节(不消费);返回实际 peek 到的字节数
Size_T peek_best_effort(void* out, Size_T max_len) {
if (max_len == 0) return 0;
std::lock_guard<MutexT> lock(this->mutex_);
const Size_T can_read = this->used_space_unsafe();
const Size_T actual = std::min(max_len, can_read);
if (actual == 0) return 0;
// 等价于从 head 开始 peek actual 字节
const Size_T end = std::min(actual, this->capacity - this->head);
std::memcpy(out, this->buf_.data() + this->head, end);
std::memcpy(static_cast<std::uint8_t*>(out) + end, this->buf_.data(), actual - end);
return actual;
}
// peek 指定 out_len 字节(不消费);成功则返回 true 并保持 out_len 不变
bool peek(void* out, Size_T out_len) {
if (out_len == 0) return true;
std::lock_guard<MutexT> lock(this->mutex_);
if (this->used_space_unsafe() < out_len) return false;
const Size_T end = std::min(out_len, this->capacity - this->head);
std::memcpy(out, this->buf_.data() + this->head, end);
std::memcpy(static_cast<std::uint8_t*>(out) + end, this->buf_.data(), out_len - end);
return true;
}
// best effort:尽可能写入,返回实际写入字节数
Size_T write_best_effort(const void* src, Size_T len) {
if (len == 0) return 0;
std::lock_guard<MutexT> lock(this->mutex_);
const Size_T can_write = this->free_space_unsafe();
const Size_T actual = std::min(len, can_write);
if (actual) this->write_bytes_unsafe(src, actual);
return actual;
}
// best effort:尽可能读取,返回实际读取字节数
Size_T read_best_effort(void* out, Size_T max_len) {
if (max_len == 0) return 0;
std::lock_guard<MutexT> lock(this->mutex_);
const Size_T can_read = this->used_space_unsafe();
const Size_T actual = std::min(max_len, can_read);
if (actual) this->read_bytes_unsafe(out, actual);
return actual;
}
// 丢弃/消费 n 字节(不拷贝);成功则返回 true
bool skip(Size_T n) {
if (n == 0) return true;
std::lock_guard<MutexT> lock(this->mutex_);
if (this->used_space_unsafe() < n) return false;
this->head = (this->head + n) & this->mask_;
return true;
}
// best effort:尽可能丢弃,返回实际丢弃字节数
Size_T skip_best_effort(Size_T n) {
if (n == 0) return 0;
std::lock_guard<MutexT> lock(this->mutex_);
const Size_T can = this->used_space_unsafe();
const Size_T actual = std::min(n, can);
if (actual) this->head = (this->head + actual) & this->mask_;
return actual;
}
};
// -------------------- Convenient aliases --------------------
using RingBuffer_ST = RingBuffer<Empty_Lock, std::size_t>;
using StreamRingBuffer_ST = Stream_RingBuffer<Empty_Lock, std::size_t>;
using RingBuffer_MT = RingBuffer<std::mutex, std::size_t>;
using StreamRingBuffer_MT = Stream_RingBuffer<std::mutex, std::size_t>;
} // namespace Psc