仿Muduo高并发服务器之Buffer模块

Buffer模块是⼀个缓冲区模块,用于实现通信中用户态的接收缓冲区和发送缓冲区功能。

设计方式:

|-------------------------------|
| * 数据存储在vector中,通过读写指针管理 |
| * 智能空间管理,通过移动数据和扩容的方式来是数据安全存储 |

优点:

|-------------------|
| * 自动处理数据移动和空间扩展 |
| * 避免了环形缓冲区的复杂边界判断 |
| * 一次读取尽可能多数据 |

主要设计步骤:

通过一个数组加两个指针偏移量来维护,数组表示存储数据的缓存区,读写偏移量来记录读写缓存区的起始位置,当需要读取数据时只需要从读偏移量开始位置读,自定义读取字节数,然后存入传入指针;写数据和读数据方式类似,先检查缓冲区写空间的大小,缓存区扩容空间规则是优先使用尾部空余空间,如果空间不够,就去检查前后加起来的空间够不够,够的话把数据往前移,不够则从新开辟空间,然后有充足的空间之后再来写入数据。

下面是具体代码实现:

cpp 复制代码
//通过一个数组加两个指针偏移量来维护,数组表示存储数据的缓存区,读写偏移量来记录读写缓存区的最后一个位置
#define BUFFER_DEFAULT_SIZE 1024 // 定义缓冲区默认大小为 1024 字节
class Buffer {
    private:
        std::vector<char> _buffer; //使用vector进行内存空间管理
        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(); }
        //获取当前写入起始地址, _buffer的空间起始地址,加上写偏移量
        char *WritePosition() { return Begin() + _writer_idx; }
        //获取当前读取起始地址
        char *ReadPosition() { return Begin() + _reader_idx; }
        //获取缓冲区末尾空闲空间大小--写偏移之后的空闲空间, 总体空间大小减去写偏移
        uint64_t TailIdleSize() { return _buffer.size() - _writer_idx; }
        //获取缓冲区起始空闲空间大小--读偏移之前的空闲空间
        uint64_t HeadIdleSize() { return _reader_idx; }
        //获取可读数据大小 = 写偏移 - 读偏移
        uint64_t ReadAbleSize() { return _writer_idx - _reader_idx; }
        //将读偏移向后移动
        void MoveReadOffset(uint64_t len) { 
            if (len == 0) return; 
            //向后移动的大小,必须小于可读数据大小
            assert(len <= ReadAbleSize());
            _reader_idx += len;
        }
        //将写偏移向后移动 
        void MoveWriteOffset(uint64_t len) {
            //向后移动的大小,必须小于当前后边的空闲空间大小
            assert(len <= TailIdleSize());
            _writer_idx += len;
        }
        //确保可写空间足够(整体空闲空间够了就移动数据,否则就扩容)
        //这里的逻辑是:优先使用尾部空余空间,如果空间不够,就去检查前后加起来的空间够不够,够的话把数据往前移,不够则从新开辟空间
        //读取数据规则
        void EnsureWriteSpace(uint64_t len) {
            //如果末尾空闲空间大小足够,直接返回
            if (TailIdleSize() >= len) { return; }
            //末尾空闲空间不够,则判断加上起始位置的空闲空间大小是否足够, 够了就将数据移动到起始位置
            if (len <= TailIdleSize() + HeadIdleSize()) {
                //将数据移动到起始位置
                uint64_t rsz = ReadAbleSize();//把当前数据大小先保存起来
                std::copy(ReadPosition(), ReadPosition() + rsz, Begin());//把可读数据拷贝到起始位置
                _reader_idx = 0;    //将读偏移归0
                _writer_idx = rsz;  //将写位置置为可读数据大小, 因为当前的可读数据大小就是写偏移量
            }else {
                //总体空间不够,则需要扩容,不移动数据,直接给写偏移之后扩容足够空间即可
                DBG_LOG("RESIZE %ld", _writer_idx + len);
                _buffer.resize(_writer_idx + len);
            }
        } 
        //写入数据
        void Write(const void *data, uint64_t len) {
            //1. 保证有足够空间,2. 拷贝数据进去
            if (len == 0) return;
            EnsureWriteSpace(len);
            const char *d = (const char *)data;
            std::copy(d, d + len, WritePosition());
        }
        void WriteAndPush(const void *data, uint64_t len) {
            Write(data, len);
            MoveWriteOffset(len);
        }
        void WriteString(const std::string &data) {
            return Write(data.c_str(), data.size());
        }
        void WriteStringAndPush(const std::string &data) {
            WriteString(data);
            MoveWriteOffset(data.size());
        }
        void WriteBuffer(Buffer &data) {
            return Write(data.ReadPosition(), data.ReadAbleSize());
        }
        void WriteBufferAndPush(Buffer &data) { 
             WriteBuffer(data);
             MoveWriteOffset(data.ReadAbleSize());
        }
        //读取数据
        void Read(void *buf, uint64_t len) {
            //要求要获取的数据大小必须小于可读数据大小
            assert(len <= ReadAbleSize());
            std::copy(ReadPosition(), ReadPosition() + len, (char*)buf);
        }
        void ReadAndPop(void *buf, uint64_t len) {
            Read(buf, len);
            MoveReadOffset(len);
        }
        std::string ReadAsString(uint64_t len) {
            //要求要获取的数据大小必须小于可读数据大小
            assert(len <= ReadAbleSize());
            std::string str;
            str.resize(len);
            Read(&str[0], len);
            return str;
        }
        std::string ReadAsStringAndPop(uint64_t len) {
            assert(len <= ReadAbleSize());
            std::string str = ReadAsString(len);
            MoveReadOffset(len);
            return str;
        }
        char *FindCRLF() {
            char *res = (char*)memchr(ReadPosition(), '\n', ReadAbleSize());
            return res;
        }
        /*通常获取一行数据,这种情况针对是*/
        std::string GetLine() {
            char *pos = FindCRLF();
            if (pos == NULL) {
                return "";
            }
            // +1是为了把换行字符也取出来。
            return ReadAsString(pos - ReadPosition() + 1);
        }
        std::string GetLineAndPop() {
            std::string str = GetLine();
            MoveReadOffset(str.size());
            return str;
        }
        //清空缓冲区
        void Clear() {
            //只需要将偏移量归0即可
            _reader_idx = 0;
            _writer_idx = 0;
        }
};
相关推荐
旖旎夜光4 小时前
LeetCode 525:连续数组(前缀和) —— 题解
数据结构·c++·算法·leetcode·前缀和
fpcc4 小时前
c++编程实践—如何理解生命周期
c++
小灰灰搞电子4 小时前
Rust std::vec::Vec<T>动态数组:所有相关 API 全面介绍
开发语言·rust
学习星球4 小时前
CodeWhale 深度剖析:从 DeepSeek-TUI 到 40.9K Star 的 Rust 终端编程 Agent
开发语言·后端·rust
zander2585 小时前
LeetCode 32. 最长有效括号
算法
lvwangshu5 小时前
AC 自动机
算法·字符串
曹牧5 小时前
C#:线程间操作无效,不是从创建控件线程访问
开发语言·c#
承渊政道7 小时前
【Python编程—从入门到实践】(Python条件判断完全入门:从布尔表达式到列表中的if实战)
开发语言·python·pycharm·条件判断·布尔表达式
小灰灰搞电子7 小时前
Rust 相关容器(集合)详解
开发语言·容器·rust
萧瑟余晖10 小时前
Java深入解析篇三十三之密封类与接口(Sealed Classes)详解
java·开发语言