缓冲区实现
buffer.h
#ifndef BUFFER_H
#define BUFFER_H
/*
网上没有找到很好的vector<char>和string区别
姑且认为和Qt的QByteArray和QString的关系一样,
一个是用来读写二进制流,一个是用来操作字符串的
见 陈硕书 7.4.3
Buffer:
缓冲
_writePos //读入数据到Buffer后的下标
_readPos //可读数据的起始下标
*/
#include <atomic>
#include <cassert>
#include <string>
#include <sys/uio.h> //readv
#include <unistd.h> //write
#include <vector>
class Buffer
{
public:
Buffer(int initBuffsize = 1024);
~Buffer() = default;
size_t WritableBytes() const;
size_t ReadableBytes() const;
size_t PrependableBytes() const;
const char* Peek() const;
void EnsureWriteable(size_t len);
void HasWritten(size_t len);
void Retrieve(size_t len);
void RetrieveUntil(const char* end);
void RetrieveAll(const char* end);
std::string RetrieveAllToStr();
const char* BeginWriteConst() const;
char* BeginWrite();
void Append(const std::string& str);
void Append(const char* str, size_t len);
void Append(const void* data, size_t len);
void Append(const Buffer& buff);
ssize_t ReadFd(int fd, int* Errno);
ssize_t WriteFd(int fd, int* Errno);
private:
char* _BeginPtr();
const char* _BeginPtr() const;
void _MakeSpace(size_t len);
std::vector<char> _buffer;
// vector自动增长(会重新分配内存)
// 整数下标,非指向_buffer的指针
std::atomic<std::size_t> _readPos;
std::atomic<size_t> _writePos;
};
#endif // BUFFER_H
buffer.cpp
#include "buffer.h"
Buffer::Buffer(int initBuffSize)
: _buffer(initBuffSize)
, _readPos(0)
, _writePos(0)
{}
size_t
Buffer::ReadableBytes() const
{
//当前数据下标 - 未读起始下标
//返回剩余未读的数据长度
return _writePos - _readPos;
}
size_t
Buffer::WritableBytes() const
{
//剩余可读长度
return _buffer.size() - _writePos;
}
size_t
Buffer::PrependableBytes() const
{
//非吾等凡人看的懂(预留大小)
return _readPos;
}
const char*
Buffer::Peek() const
{
//返回当前文件指针
return _BeginPtr() + _readPos;
}
void
Buffer::Retrieve(size_t len)
{
//设置当前已读位置
assert(len <= ReadableBytes());
_readPos += len;
}
/*
end: 传入的读到的终点位置
brief: 设置新的_readPos(已读的位置)
*/
void
Buffer::RetrieveUntil(const char* end)
{
assert(Peek() <= end);
Retrieve(end - Peek());
}
std::string
Buffer::RetrieveAllToStr()
{
//当前指针位置,剩余可读长度
std::string str(Peek(), ReadableBytes());
return str;
}
void
Buffer::Append(const std::string& str)
{
Append(str.data(), str.length());
}
void
Buffer::Append(const void* data, size_t len)
{
assert(data);
Append(static_cast<const char*>(data), len);
}
void
Buffer::Append(const Buffer& buff)
{
Append(buff.Peek(), buff.ReadableBytes());
}
void
Buffer::Append(const char* str, size_t len)
{
assert(str);
//保证buffer长度够 申请空间
EnsureWriteable(len);
std::copy(str, str + len, BeginWrite());
HasWritten(len); //更新当前可写文件下标
}
void
Buffer::EnsureWriteable(size_t len)
{
//确定可写的空间足够
if (WritableBytes() < len) {
_MakeSpace(len);
}
assert(WritableBytes() >= len);
}
const char*
Buffer::BeginWriteConst() const
{
return _BeginPtr() + _writePos;
}
char*
Buffer::BeginWrite()
{
return _BeginPtr() + _writePos;
}
void
Buffer::HasWritten(size_t len)
{
//设置已写指针下标位置
_writePos += len;
}
//把数据读到(写到)_buff
ssize_t
Buffer::ReadFd(int fd, int* saveErrno)
{
char buff[65535];
struct iovec iov[2];
//剩余可读的长度
const size_t writable = WritableBytes();
/*分散读, 保证数据全部读完*/
//起始位置+已读下标
iov[0].iov_base = _BeginPtr() + _writePos;
//剩余位置
iov[0].iov_len = writable; //把数据读到_buffer剩余长度
iov[1].iov_base = buff;
iov[1].iov_len = sizeof(buff); //读取全部的数据到buff
const ssize_t len = readv(fd, iov, 2);
if (len < 0) {
*saveErrno = errno;
} else if (static_cast<size_t>(len) <= writable)
//读出的字节长度 < 剩余可写长度
{
//修改已读长度
_writePos += len;
} else {
_writePos = _buffer.size();
Append(buff, len - writable); // writable被读到_buff了
}
return len;
}
ssize_t
Buffer::WriteFd(int fd, int* saveErrno)
{
//剩余需要读的大小
size_t readSize = ReadableBytes();
//
ssize_t len = write(fd, Peek(), readSize);
if (len < 0) {
*saveErrno = errno;
return len;
}
_readPos += len;
return len;
}
char*
Buffer::_BeginPtr()
{
return &*_buffer.begin();
}
const char*
Buffer::_BeginPtr() const
{
return &*_buffer.begin();
}
void
Buffer::_MakeSpace(size_t len)
{
//剩余可写写 + 预占的大小 < len
if (WritableBytes() + PrependableBytes() < len) {
_buffer.resize(_writePos + len + 1);
} else {
size_t readable = ReadableBytes(); //_w - _r
//起始 + 已读 ==> 复制到前面已读的空间
//可以不用分配新的内存
std::copy(_BeginPtr() + _readPos, _BeginPtr() + _writePos, _BeginPtr());
_readPos = 0;
//下一个可以写的位置
_writePos = /*_readPos + */ readable;
assert(readable == ReadableBytes());
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗