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

相关推荐
嘟嘟Listing10 分钟前
设置jenkins时区记录
运维·jenkins
嘟嘟Listing11 分钟前
jenkins docker记录
java·运维·jenkins
伴野星辰12 分钟前
小乌龟TortoiseGit 安装和语言包选择
linux·运维·服务器
zaim118 分钟前
计算机的错误计算(一百六十三)
java·c++·python·matlab·错数·等价算式
枫叶丹418 分钟前
【在Linux世界中追寻伟大的One Piece】多线程(一)
java·linux·运维
残念ing20 分钟前
【Linux】—简单实现一个shell(myshell)
linux·运维·服务器
敲键盘的老乡23 分钟前
堆优化版本的Prim
数据结构·c++·算法·图论·最小生成树
码农多耕地呗26 分钟前
trie树-acwing
数据结构·c++·算法
明月心95227 分钟前
linux mount nfs开机自动挂载远程目录
linux·运维·服务器
库库的里昂1 小时前
Linux系统Docker部署开源在线协作笔记Trilium Notes与远程访问详细教程
linux·运维·docker·开源