【C++】设计用户级缓冲区

目录

缓冲区功能分析

缓冲区空间分配策略分析

数据设计和函数介绍

完整代码

接口介绍


个人主页:东洛的克莱斯韦克-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博客

cpp 复制代码
    std::vector<char> _buff;    //缓冲区
    uint64_t _read_pos;         //读下标
    uint64_t _write_pos;        //写下标

拷贝数据用std::copy,第一个参数:从哪里拷贝的起始地址,第二个参数:从哪里拷贝的末尾地址,第三参数:从拷贝到哪里的起始地址。

查找字符用C语言的strchr,第一个参数:一段空间的起始地址,第二个参数:要查找字符的ASCII,找到了返回字符的地址,失败返回nullptr

完整代码

cpp 复制代码
#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:清理缓冲区

相关推荐
在路上看风景7 分钟前
19. 成员初始化列表和初始化对象
c++
zmzb010316 分钟前
C++课后习题训练记录Day98
开发语言·c++
念风零壹1 小时前
C++ 内存避坑指南:如何用移动语义和智能指针解决“深拷贝”与“内存泄漏”
c++
七夜zippoe1 小时前
CANN Runtime任务描述序列化与持久化源码深度解码
大数据·运维·服务器·cann
孞㐑¥2 小时前
算法——BFS
开发语言·c++·经验分享·笔记·算法
Fcy6482 小时前
Linux下 进程(一)(冯诺依曼体系、操作系统、进程基本概念与基本操作)
linux·运维·服务器·进程
袁袁袁袁满2 小时前
Linux怎么查看最新下载的文件
linux·运维·服务器
代码游侠3 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
Harvey9033 小时前
通过 Helm 部署 Nginx 应用的完整标准化步骤
linux·运维·nginx·k8s
MZ_ZXD0013 小时前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·vue.js·spring boot·python·django·php