【C++】设计用户级缓冲区
东洛的克莱斯韦克 2024-10-04 08:35:01 阅读 51
目录
缓冲区功能分析
缓冲区空间分配策略分析
数据设计和函数介绍
完整代码
接口介绍
个人主页:东洛的克莱斯韦克-CSDN博客
缓冲区功能分析
1.可以向缓冲区写入数据
2.可用从缓冲区读取数据
3.可用窥探数据——把数据拷贝给上层,但缓冲区数据不减少
4.可以读取一行——以\n \r\n \r 结尾算一行
5.可以获取缓冲区可读数据的大小
6.可以清空缓冲区,数据
缓冲区空间分配策略分析
空间划分:
缓冲区初始值为1024个字节,用两个下标(分别是读下标和写下标)把空间分为三段。
第一段:0下标到读下标(左闭右开),这段为剩余空间
第二段:读下标到写下标(左闭右开),这段为可读数据
第三段:写下标到空间末尾(左闭右开),这段为剩余空间
扩容策略:
1.写入数据的大小小于等于第三段空间,直接写入
2.写入的数据的大小小于等于第一段空间大小加第三段空间大小,把可读数据拷贝到空间的开头,在写入数据
3,写入的数据大小大于等于第一段空间大小加第三段空间大小,直接扩容,然后在第三段写入数据。
上述策略中的第二点,是为了不让空间一直扩容,扩容很大概率都是异地扩容,也需要拷贝,这是有代价的。
数据设计和函数介绍
底层的容器用std::vector<char>类型。两个下标用uint64_t类型
【C++】详解STL容器之一的 vector_c++ vector扩容缩容-CSDN博客
<code> std::vector<char> _buff; //缓冲区
uint64_t _read_pos; //读下标
uint64_t _write_pos; //写下标
拷贝数据用std::copy,第一个参数:从哪里拷贝的起始地址,第二个参数:从哪里拷贝的末尾地址,第三参数:从拷贝到哪里的起始地址。
查找字符用C语言的strchr,第一个参数:一段空间的起始地址,第二个参数:要查找字符的ASCII,找到了返回字符的地址,失败返回nullptr
完整代码
#include <vector>
#include <cstdint>
#include <assert.h>
#include <algorithm>
#include <string>
#include <iostream>
#include <cstring>
#define BUFF_MAX_SIZE 1024 //容器大小的初始值
class Buff{
private:
std::vector<char> _buff; //缓冲区
uint64_t _read_pos; //读下标
uint64_t _write_pos; //写下标
private:
//获取容器起始地址
char* GetInitPos(){ return &( *_buff.begin()); }
//获取读位置地址
char* GetReadPos(){ return GetInitPos() + _read_pos; }
//获取写位置地址
char* GetWritePos(){ return GetInitPos() + _write_pos; }
//缓冲区读位置之后有多少空闲空间,把空间分为了三段,第一段
uint64_t FirstParagraphV(){ return _read_pos; }
//可读数据大小,第二段
uint64_t SecondParagraphV(){ return _write_pos - _read_pos; }
//缓冲区写位置之前有多少空闲空间,第三段
uint64_t ThirdParagraphV(){ return _buff.capacity() - _write_pos; }
//读偏移移动
void MoveReadPos(uint64_t len){
assert(len <= SecondParagraphV());
_read_pos += len;
}
//写偏移移动
void MoveWritePos(uint64_t len){
assert(len <= ThirdParagraphV());
_write_pos += len;
}
//确保足够空间
void EnsureSpaceEnough(uint64_t len){
if (len <= ThirdParagraphV()){
return;
}
else if ( len <= FirstParagraphV() + ThirdParagraphV()){
std::copy(GetReadPos(), GetWritePos(), GetInitPos());
_write_pos = SecondParagraphV();
_read_pos = 0;
}
else{ _buff.reserve(_buff.capacity() + len); }
}
public:
//构造
Buff():_read_pos(0), _write_pos(0), _buff(BUFF_MAX_SIZE){ }
//可读数据大小]
uint64_t ReadSize(){ return SecondParagraphV(); }
//写入数据
void Write(const char* date, uint64_t len){
EnsureSpaceEnough(len); //确保空间足够
std::copy(date, date + len, GetWritePos()); //写入数据
MoveWritePos(len); //移动下标
}
void Write(const std::string& date, uint64_t len){ Write(&date[0], len); }
void Write(Buff& date, uint64_t len){ Write(date.GetReadPos(), len); }
//窥探数据
void pry(char* buf, uint64_t len){
assert(len <= SecondParagraphV());
std::copy(GetReadPos(), GetReadPos() + len, buf);
}
void pry(std::string& buf, uint64_t len){ pry(&(*buf.begin()), len); }
void pry(Buff& buf, uint64_t len){ pry(buf.GetWritePos(), len); }
//读取数据
void Read(char* buf, uint64_t len){
assert(len <= SecondParagraphV());
std::copy(GetReadPos(), GetReadPos() + len, buf);
MoveReadPos(len);
}
void Read(std::string& buf, uint64_t len){ Read(&(*buf.begin()), len); }
void Read(Buff& buf, uint64_t len){ Read(buf.GetWritePos(), len); }
//读取一行
bool ReadLen(std::string& str){
//\n \r\n /r
char c = '\n';
char* pos_ptr = strchr(GetInitPos(), c);
if (nullptr == pos_ptr){
c = '\r';
pos_ptr = strchr(GetInitPos(), c);
if (nullptr == pos_ptr){
return false;
}
else{
std::copy(GetInitPos(), pos_ptr, &(*str.begin()));
MoveReadPos(pos_ptr - GetInitPos());
return true;
}
return false;
}
else{
std::copy(GetInitPos(), pos_ptr, &(*str.begin()));
MoveReadPos(pos_ptr - GetInitPos());
return true;
}
}
//清空缓冲区
void Clear(){
_read_pos = 0;
_write_pos = 0;
}
};
接口介绍
ReadSize:获取缓冲区可读数据的大小
Write:向缓冲区写入数据,第一个参数是从哪里写入,可以是char* std::string Buff,第二个参数是写入数据的大小
pry:从缓冲区窥探数据,第一个参数是把数据读到哪里,可以是char* std::string Buff,第二个参数是写入数据的大小
Read:从缓冲区读取数据,第一个参数是把数据读到哪里,可以是char* std::string Buff,第二个参数是写入数据的大小
ReadLen:从缓冲区读取一行
Clear:清理缓冲区
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。