服务器设计细节 之 【缓冲区模块】(memXXX接口、std::cpoy)
编译期检查,无需手动计算字节数,自动推导类型大小。
·
目录
1.模块概述
Buffer 模块是一个基于线性内存空间的缓冲区组件,用于临时存储数据和按序取出数据
它通过读写偏移量机制,避免了频繁的数据搬移,同时能够自动处理空间不足的情况
2.实现思想
2.1.内存模型
采用 std::vector<char> 作为底层内存空间,其内部是一块连续的线性内存
string不能够处理\0,所以不采用
2.2.缓冲区核心要素
| 要素 | 说明 |
|---|---|
| 默认空间大小 | 缓冲区初始容量,默认为 1024 字节 |
读偏移量 _reader_idx |
标记当前读取位置的相对偏移量 |
写偏移量 _writer_idx |
标记当前写入位置的相对偏移量 |
偏移量是相对于缓冲区起始地址的相对值,而非绝对内存地址
2.3.数据操作思想
(1)写入数据流程
- 从当前写偏移位置开始写入
- 检查尾部空闲空间是否足够:足够,直接写入;不足,检查总空闲空间(尾部 + 头部)是否足够(足够:将现有数据搬移到缓冲区起始位置,整理碎片空间;不足:对缓冲区进行扩容)
- 写入成功后,写偏移量向后移动
(2)读取数据流程
- 从当前读偏移位置开始读取
- 可读数据大小 = 写偏移量 - 读偏移量
- 读取后,读偏移量向后移动
2.4.简单实现
#include <vector>
#include <cstdint>
#include <cassert>
#include <algorithm>
#include <string>
#include <cstring>
#define BUFFER_DEFAULT_SIZE 1024
class Buffer
{
private:
std::vector<char> _buffer;//缓冲区
uint64_t _reader_idx; //读偏移
uint64_t _writer_idx; //写偏移
public:
Buffer():_reader_idx(0), _writer_idx(0), _buffer(BUFFER_DEFAULT_SIZE){}
char* Begin(){ return &*_buffer.begin(); }
//获取当前写入起始地址
char* WritePosition() { return Begin() + _writer_idx; }
//获取当前读取起始地址
char* ReadPosition() { return Begin() + _reader_idx; }
//获取末尾空间大小---写偏移之后的空间大小
uint64_t TailIdleSize() { return _buffer.size() - _writer_idx; }
//获取头部空间大小---读偏移之前的空间大小
uint64_t FrontIdleSize() { return _reader_idx; }
//获取可读数据大小 前闭后开
uint64_t ReadableSize() { return _writer_idx - _reader_idx; }
//读偏移向后移动
void MoveReadOffset(uint64_t len)
{
assert(len <= ReadableSize());
_reader_idx += len;
}
//写偏移向后移动
void MoveWriteOffset(uint64_t len)
{
assert(len <= TailIdleSize());
_writer_idx += len;
}
//扩容逻辑(确保写空间足够)
void EnsureWriteSpace(uint64_t len)
{
// 情况1:尾部空间足够,直接返回
if (len <= TailIdleSize()) {
return;
}
uint64_t readable = ReadableSize();
// 情况2:尾部空间不足,但总空闲空间足够(需要整理碎片)
if (len <= TailIdleSize() + FrontIdleSize()) {
// 将可读数据移动到缓冲区起始位置
std::memmove(Begin(), ReadPosition(), readable);
_reader_idx = 0;
_writer_idx = readable;
}
// 情况3:总空闲空间也不够,需要扩容
else {
// 先整理碎片到起始位置,再扩容
if (_reader_idx > 0) {
std::memmove(Begin(), ReadPosition(), readable);
_reader_idx = 0;
_writer_idx = readable;
}
// 扩容到至少能容纳 (readable + len) 字节
_buffer.resize(readable + len);
}
}
//写入数据
void Write(const void* data, uint64_t len)
{
EnsureWriteSpace(len);
const char* d = (const char*)data;
std::copy(d, d + len, WritePosition());
}
//移动偏移量
void WriteAndPush(const void* buf, uint64_t len)
{
Write(buf, len);
MoveWriteOffset(len);
}
//复用逻辑
void WriteString(const std::string& data) { Write(data.c_str(), data.size()); }
void WriteStringAndPush(const std::string& data)
{
WriteString(data);
MoveWriteOffset(data.size());
}
void WriteBuffer(Buffer& data) { Write(data.ReadPosition(), data.ReadableSize()); }
void WriteBufferAndPush(Buffer& data)
{
WriteBuffer(data);
MoveWriteOffset(data.ReadableSize());
}
//读取数据
void Read(void* buffer, uint64_t len)
{
assert(len <= ReadableSize());
std::copy(ReadPosition(), ReadPosition() + len, (char*)buffer);
}
//移动偏移量
void ReadAndPop(void* buf, uint64_t len)
{
Read(buf, len);
MoveReadOffset(len);
}
//强调复用逻辑
std::string ReadAsString(uint64_t len)
{
assert(len <= ReadableSize());
return std::string(ReadPosition(), len);
}
std::string ReadAsStringAndPop(uint64_t len)
{
std::string ret = ReadAsString(len);
MoveReadOffset(len);
return ret;
}
//查找换行符
char* FindCRLF()
{
char* res = (char*)memchr(ReadPosition(), '\n', ReadableSize());
return res;
}
//针对ASCII字符有效
std::string Getline()
{
char* pos = FindCRLF();
if(pos == nullptr) return "";
return ReadAsString(pos - ReadPosition() + 1);
}
std::string GetlineAndPop()
{
std::string ret = Getline();
MoveReadOffset(ret.size());
return ret;
}
//清空缓冲区
void Clear() { _writer_idx = _reader_idx = 0; }
};
3.memXXX接口
3.1.memcpy — 内存拷贝
| 项目 | 说明 |
|---|---|
| 函数原型 | void* memcpy(void* dest, const void* src, size_t n); |
| 头文件 | <cstring> |
| 功能 | 从 src 复制 n 个字节到 dest |
| 参数 | dest: 目标地址src: 源地址n: 拷贝字节数 |
| 返回值 | 返回 dest 指针 |
| 安全性 | 不安全。若 dest 和 src 内存重叠,行为未定义 |
| 适用场景 | 两块不重叠内存的拷贝,例如缓冲区读写、结构体复制 |
| 性能 | 极快,编译器通常做极致优化 |
| 示例 | memcpy(write_pos, data, len); |
3.2.memmove — 内存移动
| 项目 | 说明 |
|---|---|
| 函数原型 | void* memmove(void* dest, const void* src, size_t n); |
| 头文件 | <cstring> |
| 功能 | 从 src 复制 n 个字节到 dest |
| 参数 | dest: 目标地址src: 源地址n: 拷贝字节数 |
| 返回值 | 返回 dest 指针 |
| 安全性 | 安全。允许 dest 和 src 内存重叠,内部会判断重叠方向并选择合适拷贝顺序 |
| 适用场景 | 同一块内存内数据的搬移,例如整理碎片、删除中间元素 |
| 性能 | 略慢于 memcpy(因为有重叠判断),但差距极小 |
| 示例 | memmove(buf, buf + reader_idx, readable); |
3.3.memset — 内存填充
| 项目 | 说明 |
|---|---|
| 函数原型 | void* memset(void* str, int c, size_t n); |
| 头文件 | <cstring> |
| 功能 | 将 str 指向内存的前 n 个字节设置为值 c |
| 参数 | str: 目标地址c: 要设置的值(以 int 传入,实际转为 unsigned char)n: 填充字节数 |
| 返回值 | 返回 str 指针 |
| 安全性 | 安全。但需确保 n 不超出有效内存范围 |
| 适用场景 | 初始化内存为 0、填充固定值、清除敏感数据 |
| 性能 | 极快,通常会使用 CPU 特殊指令优化 |
| 示例 | memset(&obj, 0, sizeof(obj)); |
3.4.memchr — 内存查找
| 项目 | 说明 |
|---|---|
| 函数原型 | void* memchr(const void* str, int c, size_t n); |
| 头文件 | <cstring> |
| 功能 | 在 str 指向的前 n 个字节中查找字符 c 的首次出现 |
| 参数 | str: 搜索起始地址c: 要查找的字符n: 搜索范围(字节数) |
| 返回值 | 找到:返回指向该字节的指针 未找到:返回 nullptr |
| 安全性 | 安全。不会越界访问超出 n 的内存 |
| 适用场景 | 查找分隔符(如 \n)、定位特定标记字节 |
| 性能 | 线性扫描,中等速度。查找大块数据时性能不如专有算法 |
| 示例 | memchr(buf + read_idx, '\n', readable_size); |
3.5.memcmp — 内存比较
| 项目 | 说明 |
|---|---|
| 函数原型 | int memcmp(const void* ptr1, const void* ptr2, size_t n); |
| 头文件 | <cstring> |
| 功能 | 比较 ptr1 和 ptr2 指向的前 n 个字节 |
| 参数 | ptr1: 第一块内存ptr2: 第二块内存n: 比较字节数 |
| 返回值 | <0:ptr1 小于 ptr20:相等>0:ptr1 大于 ptr2 |
| 安全性 | 安全。比较前 n 个字节,不会越界 |
| 适用场景 | 二进制数据比较、检查数据是否相同 |
| 性能 | 逐字节比较,大块数据可考虑先比较首字节或哈希 |
| 示例 | memcmp(buf1, buf2, 16); |
memcpy 本身不会检查边界,如果长度不正确,就会发生越界,导致未定义行为
4.std::copy -- 范围拷贝

| 项目 | 说明 |
|---|---|
| 函数原型 | template<class InputIt, class OutputIt> OutputIt std::copy(InputIt first, InputIt last, OutputIt d_first); |
| 头文件 | <algorithm> |
| 功能 | 将 [first, last) 范围内的元素拷贝到以 d_first 为起点的目标区域 |
| 参数 | first: 源范围起始迭代器last: 源范围结束迭代器(不包含)d_first: 目标范围起始迭代器 |
| 返回值 | 指向目标范围最后一个被拷贝元素的下一个位置的迭代器 |
| 底层实现 | 对平凡可复制类型(如 char),编译器通常会优化为 memmove |
| 安全性 | 若目标范围与源范围重叠且 d_first 在 [first, last) 内,行为未定义 |
| 类型安全 | 编译期检查,无需手动计算字节数,自动推导类型大小 |
| 泛型支持 | 适用于任何满足输入/输出迭代器概念的类型,不仅限于 char* |
| 适用场景 | C++ 风格代码,泛型编程,非平凡类型对象的拷贝 |
| 性能 | 平凡类型:与 memmove 相当;非平凡类型:触发赋值运算符,可能较慢 |
| 示例 | std::copy(d, d + len, WritePosition()); |
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)