项目-SERVER模块-Buffer模块

Buffer模块

  • 一、Buffer模块是什么?实现思想是什么?
  • 二、代码实现
    • 如何设计:
    • 1.成员变量:
    • 2.构造函数:
    • 3.获取地址和空间大小
    • 4.读写偏移向后移动
    • 5.扩容函数
    • 6.写入函数
    • 7.读取函数
    • 8.获取地址和空间大小
    • 9.获取地址和空间大小
    • 10.获取地址和空间大小
    • 11.测试代码
    • 12.整体源代码


一、Buffer模块是什么?实现思想是什么?

Buffer模块:缓冲区模块
提供的功能:存储数据,取出数据
在这里插入图片描述
实现思想:

  1. 实现缓冲区得有一块内存空间,采用vector,vertor底层其实使用的就是一个线性的内存空间
  2. 要素:a.默认空间大小;b.当前的读取数据位置;c.当前的写入数据位置;
  3. 操作
    a.写入数据:
    当前写入位置指向哪里,就从哪里开始写入,如果后续剩余空闲空间不够了
    考虑整体缓冲区空闲空间是否足够(因为读位置也会向后偏移,前边有可能会有空闲空间)
    足够,将数据移动到起始位置即可
    不够,扩容,从当前写位置开始扩容足够大小
    数据一旦写入成功,当前写位置,就要向后偏移
    b.读取数据
    当前的读取位置指向哪里,就从哪里开始读取,前提是有数据可读,可读数据大小:当前写入位置,减去当前读取位置

二、代码实现

如何设计:

class Buffer{
private:
       std::vector<char> buffer:
/*位置,是一个相对偏移量,而不是绝对地址*/
       uint64_t_read_idx; // 相对读偏移量
       uint64_t_write_idx;//相对写偏移量
public:
       1.获取当前写位置地址
       2.确保可写空间足够(移动+扩容)
       3.获取前沿空闲空间大小
       4.获取后沿空闲空间大小
       5.将写位置向后移动指定长度
       6.获取当前读位置地址
       7.获取可读数据大小
       8.将读位置向后移动指定长度
       9.清理功能
       };

1.成员变量:

#define BUFFER_DEFAULT_SIZE 1024
private:
    std::vector<char> _buffer;// 使用vector进行内存空间管理
    //位置,是相对偏移量,而不是一个绝对地址
    uint64_t _read_idx; // 相对读偏移:此处开始读取
    uint64_t _write_idx;// 相对写偏移:此处开始写入

这段代码片段是一个简单的类定义,其中包含了三个私有成员变量:

  1. _buffer:这是一个使用std::vector<char>类型的变量,用于管理内存空间。std::vector是C++标准库提供的动态数组容器,可以动态增长和缩小。在这里,它被用来管理一块内存缓冲区。

  2. _read_idx:这是一个uint64_t类型的变量,表示相对读偏移量,用于指示从缓冲区中哪里开始读取数据。

  3. _write_idx:这是另一个uint64_t类型的变量,表示相对写偏移量,用于指示在缓冲区中哪里开始写入数据。

通过这些成员变量,这个类可能用于实现一个简单的缓冲区管理器,用于读取和写入数据到内存缓冲区中。


2.构造函数:

public:
Buffer() : _read_idx(0), _write_idx(0), _buffer(BUFFER_DEFAULT_SIZE) {}

这段代码是一个构造函数的定义,它使用了成员初始化列表来初始化类的成员变量。在这个构造函数中:

  1. _read_idx(0):对_read_idx进行了初始化,将其初始值设为0。
  2. _write_idx(0):对_write_idx进行了初始化,将其初始值设为0。
  3. _buffer(BUFFER_DEFAULT_SIZE):对_buffer进行了初始化,使用了BUFFER_DEFAULT_SIZE作为初始大小来创建了一个std::vector<char>类型的缓冲区。

综合起来,这段代码表示了在实例化Buffer类时,会自动将_read_idx_write_idx初始化为0,并创建一个具有默认大小的缓冲区。

3.获取地址和空间大小

//获取起始地址
char* Begin() { return &*_buffer.begin(); }
//获取当前写入起始地址, _buffer的空间起始地址,加上写偏移量
char* WritePosition() { return Begin() + _write_idx; }
//获取当前读取起始地址
char* ReadPosition() { return Begin() + _read_idx; }
//获取缓冲区末尾空闲空间大小--写偏移之后的空闲空间, 总体空间大小减去写偏移
uint64_t TailIdleSize() { return _buffer.size() - _write_idx; }
//获取缓冲区起始空闲空间大小--读偏移之前的空闲空间
uint64_t HeadIdleSize() { return _read_idx; }
//获取可读数据大小 = 写偏移 - 读偏移
uint64_t ReadAbleSize() { return _write_idx - _read_idx; }

4.读写偏移向后移动

//将读偏移向后移动
void MoveReadOffset(uint64_t len) {
    if (len == 0) return;
    //向后移动的大小,必须小于可读数据大小
    assert(len <= ReadAbleSize());
    _read_idx += len;
}
//将写偏移向后移动 
void MoveWriteOffset(uint64_t len) {
    //向后移动的大小,必须小于当前后边的空闲空间大小
    assert(len <= TailIdleSize());
    _write_idx += len;
}

5.扩容函数

 //确保可写空间足够(整体空闲空间够了就移动数据,否则就扩容)
    void EnsureWriteSpace(uint64_t len) {
        //如果末尾空闲空间大小足够,直接返回
        if (TailIdleSize() >= len) { return; }
        //末尾空闲空间不够,则判断加上起始位置的空闲空间大小是否足够, 够了就将数据移动到起始位置
        if (len <= TailIdleSize() + HeadIdleSize()) {
            //将数据移动到起始位置
            uint64_t rsz = ReadAbleSize();//把当前数据大小先保存起来
            std::copy(ReadPosition(), ReadPosition() + rsz, Begin());//把可读数据拷贝到起始位置
            _read_idx = 0;    //将读偏移归0
            _write_idx = rsz;  //将写位置置为可读数据大小, 因为当前的可读数据大小就是写偏移量
        }
        else {
            //总体空间不够,则需要扩容,不移动数据,直接给写偏移之后扩容足够空间即可
            _buffer.resize(_write_idx + len);
        }
    }

6.写入函数

  //写入数据
    void Write(const void* data, uint64_t len) {
        //1. 保证有足够空间,
        if (len == 0) return;
        EnsureWriteSpace(len);
        //2. 拷贝数据进去
        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());
    }

7.读取函数

 //读取数据
    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;
    }

8.获取地址和空间大小

//查找换行字符
    char* FindCRLF() {
        //找到'\n'的位置
        char* res = (char*)memchr(ReadPosition(), '\n', ReadAbleSize());
        return res;
    }

9.获取地址和空间大小

/*通常获取一行数据,这种情况针对是*/
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;
}

10.获取地址和空间大小

//清空缓冲区
void Clear() {
    //只需要将偏移量归0即可
    _read_idx = 0;
    _write_idx = 0;
}

11.测试代码

(1)这段代码使用了一个名为Buffer的类来进行一些操作。这段代码的大致意思:

  1. 首先,创建了一个名为bufBuffer对象。
  2. 然后,创建了一个字符串str并赋值为"hello!!"。
  3. 接着,调用buf对象的WriteStringAndPush方法,将字符串写入缓冲区,并将其推入缓冲区。
  4. 然后,创建了另一个名为buf1Buffer对象。
  5. 接着,调用buf1对象的WriteBufferAndPush方法,将buf对象的内容写入buf1的缓冲区,并将其推入buf1的缓冲区。
  6. 紧接着,定义了一个名为tmp的字符串。
  7. 然后,调用buf1对象的ReadAbleSize方法获取可读取的大小,并将这个大小作为参数传递给ReadAsStringAndPop方法,将读取的内容转换为字符串并将其存储在tmp中。
  8. 最后,使用std::cout输出了tmpbufbuf1的可读取大小。

总的来说,这段代码应该是在测试一个缓冲区类的读写功能。通过这些操作,可以实现数据的写入、推入、读取和弹出操作,并输出相应的结果。

Buffer buf;
std::string str = "hello!!";
buf.WriteStringAndPush(str); 

Buffer buf1;
buf1.WriteBufferAndPush(buf);

std::string tmp;
tmp = buf1.ReadAsStringAndPop(buf1.ReadAbleSize());

std::cout<< tmp<< std::endl;
std::cout<< buf.ReadAbleSize()<< std::endl;
std::cout<< buf1.ReadAbleSize()<< std::endl;

输出结果:
在这里插入图片描述

(2)这段代码创建了一个名为bufBuffer对象,并通过循环向该缓冲区中写入300个带有编号的字符串。然后,从缓冲区中读取可读取的大小,并将其转换为字符串后存储在tmp中,最后将tmp输出到标准输出流。

在循环中,每次迭代都会创建一个新的std::string,其内容为"hello"加上当前循环变量i的值,再加上换行符\n。这样就生成了类似"hello0\n"、“hello1\n”、"hello2\n"等格式的字符串,然后调用buf对象的WriteStringAndPush方法将这些字符串写入缓冲区并推入缓冲区。

最后,通过调用buf对象的ReadAbleSize方法获取可读取的大小,并将这个大小作为参数传递给ReadAsStringAndPop方法,将读取的内容转换为字符串并将其存储在tmp中。最终,将tmp输出到标准输出流中。

这段代码的作用是往缓冲区中写入一系列带编号的字符串,然后读取整个缓冲区中的内容并输出到控制台。

 Buffer buf;
 for (int i = 0; i < 300; i++) {
     std::string str = "hello" + std::to_string(i) + '\n';
     buf.WriteStringAndPush(str);
 }
 std::string tmp;
 tmp = buf.ReadAsStringAndPop(buf.ReadAbleSize());
 std::cout << tmp << std::endl;

输出结果:hello0-hello299
在这里插入图片描述

12.整体源代码

// Buffer //
#include <iostream>
#include <vector>
#include <string>
#include <cassert>
#include <cstring>
#include <ctime>
#define BUFFER_DEFAULT_SIZE 1024
class Buffer
{
private:
    std::vector<char> _buffer;// 使用vector进行内存空间管理
    //位置,是相对偏移量,而不是一个绝对地址
    uint64_t _read_idx; // 相对读偏移:此处开始读取
    uint64_t _write_idx;// 相对写偏移:此处开始写入
public:
    Buffer() : _read_idx(0), _write_idx(0), _buffer(BUFFER_DEFAULT_SIZE) {}
    //获取起始地址
    char* Begin() { return &*_buffer.begin(); }
    //获取当前写入起始地址, _buffer的空间起始地址,加上写偏移量
    char* WritePosition() { return Begin() + _write_idx; }
    //获取当前读取起始地址
    char* ReadPosition() { return Begin() + _read_idx; }
    //获取缓冲区末尾空闲空间大小--写偏移之后的空闲空间, 总体空间大小减去写偏移
    uint64_t TailIdleSize() { return _buffer.size() - _write_idx; }
    //获取缓冲区起始空闲空间大小--读偏移之前的空闲空间
    uint64_t HeadIdleSize() { return _read_idx; }
    //获取可读数据大小 = 写偏移 - 读偏移
    uint64_t ReadAbleSize() { return _write_idx - _read_idx; }
    //将读偏移向后移动
    void MoveReadOffset(uint64_t len) {
        if (len == 0) return;
        //向后移动的大小,必须小于可读数据大小
        assert(len <= ReadAbleSize());
        _read_idx += len;
    }
    //将写偏移向后移动 
    void MoveWriteOffset(uint64_t len) {
        //向后移动的大小,必须小于当前后边的空闲空间大小
        assert(len <= TailIdleSize());
        _write_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());//把可读数据拷贝到起始位置
            _read_idx = 0;    //将读偏移归0
            _write_idx = rsz;  //将写位置置为可读数据大小, 因为当前的可读数据大小就是写偏移量
        }
        else {
            //总体空间不够,则需要扩容,不移动数据,直接给写偏移之后扩容足够空间即可
            _buffer.resize(_write_idx + len);
        }
    }
    //写入数据
    void Write(const void* data, uint64_t len) {
        //1. 保证有足够空间,
        if (len == 0) return;
        EnsureWriteSpace(len);
        //2. 拷贝数据进去
        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() {
        //找到'\n'的位置
        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即可
        _read_idx = 0;
        _write_idx = 0;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/420185.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot自写插件封包

在Spring Boot中自写插件或封包&#xff08;通常指的是创建自定义的starter&#xff09;是一种常见的做法&#xff0c;用于将一系列相关的配置和组件打包成一个独立的模块&#xff0c;从而简化依赖管理和配置过程。以下是一个简单的步骤&#xff0c;指导你如何创建一个自定义的…

如何高效管理项目:从规划到执行

导语&#xff1a;项目管理是一项复杂而重要的工作&#xff0c;涉及多个方面&#xff0c;包括规划、执行、监控和收尾。本文将介绍如何高效管理项目&#xff0c;从规划到执行&#xff0c;帮助您成功完成项目目标。 一、项目规划 项目规划是项目成功的关键。在规划阶段&#xff0…

JVM原理-基础篇

Java虚拟机&#xff08;JVM, Java Virtual Machine&#xff09;是运行Java应用程序的核心组件&#xff0c;它是一个抽象化的计算机系统模型&#xff0c;为Java字节码提供运行环境。JVM的主要功能包括&#xff1a;类加载机制、内存管理、垃圾回收、指令解释与执行、异常处理与安…

[每周一更]-(第89期):开源许可证介绍

开源代码本就是一种共享精神&#xff0c;一种大无畏行为&#xff0c;为了发扬代码的魅力&#xff0c;创造更多的价值&#xff0c;让爱传递四方&#xff0c;让知识惠及更多人&#xff1b; 写文章也是一种共享精神&#xff0c;让知识传播出去。 介绍下开源中不同许可证的内容限…

【leetcode】随机链表的复制

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家刷题&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 点击查看题目 思路: struct Node* copyRandomList(struct Node* head) {struct Node* curhead;//1.copy原链…

【矩阵】【方向】【素数】3044 出现频率最高的素数

作者推荐 动态规划的时间复杂度优化 本文涉及知识点 素数 矩阵 方向 LeetCode 3044 出现频率最高的素数 给你一个大小为 m x n 、下标从 0 开始的二维矩阵 mat 。在每个单元格&#xff0c;你可以按以下方式生成数字&#xff1a; 最多有 8 条路径可以选择&#xff1a;东&am…

Nginx基础知识

文章目录 简介安装Ubuntu安装CentOS安装windows 常用命令配置文件 nginx.confnginx -V 反向代理 & 负载均衡 简介 Web服务器&#xff0c;高性能&#xff0c;Http与反向代理的服务器。启动后浏览器输入 http://localhost/ 显示欢迎页面就是启动成功了。在nginx安装目录下cm…

【AntDesign】解决嵌套section或layout中,h1字体比h2小问题

问题&#xff1a;以下情况均会导致h1比h2小&#xff0c;具体原因是浏览器默认样式里面&#xff0c;对h1不同层级设置了特殊的样式&#xff0c; <section class"css-dev-only-do-not-override-12q8zf4 ant-layout"><section class"css-dev-only-do-not…

THINKPHP 跨域报错解决方案

报错&#xff1a;has been blocked by CORS policy: Response to preflight request doesnt pass access control check: No Access-Control-Allow-Origin header is present on the requested resource. 环境&#xff1a;thinkphp6 nginx 今天和VUE配合调用接口的时候发现跨…

Maven-私服(黑马学习笔记)

前面我们在讲解多模块开发的时候&#xff0c;我们讲到我们所拆分的模块是可以在同一个公司各个项目组之间进行资源共享的。这个模块的资源共享&#xff0c;就需要通过我们接下来所讲解的Maven的私服来实现。 首先我们先介绍一下什么是私服&#xff0c;以及它的作用是什么。再来…

瑞吉苍穹外卖如何拓展?已经经过不同公司多轮面试。项目中会问到哪些问题?以及问题如何解决?

别催了&#xff0c;别催了&#xff0c;先收藏吧。 作者大大正在加班加点完成。 文章会尽快发布&#xff0c;关注收藏&#xff0c;尽请期待。 想要加入并查阅作者的知识库可以联系作者 不要白嫖&#xff0c;通过后&#xff0c;附上关注和收藏截图。 已有众多小伙伴加入 目前…

mysql读写分离方案

什么是读写分离&#xff1f; 读写分离就是将对数据库的读操作和写操作分散到不同的数据库节点上 如何实现读写分离&#xff1f; 因为更多的读多写少&#xff0c;所以为了缓解主库的读能力从而引入了从库&#xff0c;这样就可以减少主库的负担&#xff0c;从而解决了应用的并发…

【教程】移动互联网时代的APP上架流程和要点

目录 摘要 引言 正文 一、应用商店注册 二、准备APP材料 三、打包上传App 摘要 本文将介绍移动应用程序上架的基本流程和要点&#xff0c;包括应用商店注册、APP材料准备、打包上传App、APP审核以及发布APP的详细步骤。此外&#xff0c;还会提到利用appuploder工具简化i…

【JavaScript】面试手撕节流

引入 上篇我们讲了防抖&#xff0c;这篇我们就谈谈防抖的好兄弟 – 节流。这里在老生常谈般的提一下他们两者之间的区别,顺带给读者巩固下。 PS: 开源节流中节流与这个技术上的节流&#xff0c;个人认为本质上是一样的。 开源节流的节流指的是节省公司的金钱开支。前端技术上的…

Windows的Docker-Desktop安装与问题总结

目录 Docker-Desktop安装步骤 环境配置 Docker-Desktop安装问题总结 问题1&#xff1a;docker-desktop setting界面一直加载转圈 问题2&#xff1a;docker镜像的存储位置变更&#xff08;防止C盘空间不足&#xff09; 参考文献&#xff1a; Docker-Desktop安装步骤 环境…

Unity(第十八部)物理力学,碰撞,触发、关节和材质

1、重力 刚体组件 英文中文描述RigidBody刚体组件physics->rigidbody &#xff0c;刚体组件使一个物体有了质量&#xff0c;重力等。&#xff0c;use gravity 勾选后&#xff0c;物体才会受到重力&#xff0c;会自动下落&#xff0c;取消勾选就不会。&#xff0c;&#xf…

计算机网络物理层知识点总结

本篇博客是基于谢希仁编写的《计算机网络》和王道考研视频总结出来的知识点&#xff0c;本篇总结的主要知识点是第二章的物理层。上一章的传送门&#xff1a;计算机网络体系结构-CSDN博客 通信基础 物理层概念 物理层解决如何在连接各种计算机的传输媒体上传输数据比特流&am…

ElasticSearch搜索引擎使用指南

一、ES数据基础类型 1、数据类型 字符串 主要包括: text和keyword两种类型&#xff0c;keyword代表精确值不会参与分词&#xff0c;text类型的字符串会参与分词处理 数值 包括: long, integer, short, byte, double, float 布尔值 boolean 时间 date 数组 数组类型不…

汽车三元催化器的废品项目详解,三元催化再生项目的回收技术教学

一、教程描述 这是一个收废品项目&#xff0c;就收那些别人不懂的&#xff0c;三元催化器的附加值高&#xff0c;只要掌握技术&#xff0c;怎么玩都行的&#xff0c;只是要放得下你的面子。三元催化器&#xff0c;是安装在汽车排气系统中最重要的机外净化装置&#xff0c;它可…

CodeWhisperer安装教导--一步到位!以及本人使用Whisperer的初体验。

CodeWhisperer是亚马逊出品的一款基于机器学习的通用代码生成器&#xff0c;可实时提供代码建议。类似 Cursor 和Github AWS CodeWhisperer 亚马逊科技的CodeWhisperer是Amazon于2021年12月推出的一款代码补全工具&#xff0c;与GitHub Copilot类似。主要的功能有:代码补全注释…