【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:清理缓冲区



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。