项目-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;}
};

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python中版本,操作系统等问题汇总

1. linux源码部署到windows 1.1ModuleNotFoundError: No module named pwd 这个问题&#xff0c;是因为源码是给linux的。这里在windows中&#xff0c;没有该命令。 解决方法之一&#xff0c;在相应的环境目录中&#xff0c;如图中<MetaGPTenv>虚拟环境中&#xff0c;在…

Linux信号【产生-保存-处理】

目录 前言&#xff1a; 1、进程信号基本概念 1.1、什么是信号&#xff1f; 1.2、信号的作用 2、键盘键入 2.1、ctrlc 终止前台进程 2.1.1、signal 注册执行动作 3、系统调用 3.1、kill 函数 3.2、模拟实现 myKill 3.3、raise 函数 3.4、abort 函数 4、软件条件信号…

消息队列+更新DB极易引发的DB并发修改bug

背景 我们在生产系统中和其他系统进行交互时一般都会通过消息队列来解耦生产者和消费者&#xff0c;然后通过每个使用方消费消息队列的消息的方式来完成消息的消费&#xff0c;并且一般来说我们消费消息后极有可能会操作DB&#xff0c;不过这种方式如果处理不够仔细&#xff0…

【C++从0到王者】第四十六站:图的深度优先与广度优先

文章目录 一、图的遍历二、广度优先遍历1.思想2.算法实现3.六度好友 三、深度优先遍历1.思想2.代码实现 四、其他问题 一、图的遍历 对于图而言&#xff0c;我们的遍历一般是遍历顶点&#xff0c;而不是边&#xff0c;因为边的遍历是比较简单的&#xff0c;就是邻接矩阵或者邻接…

electron+vue3全家桶+vite项目搭建【28】封装窗口工具类【2】窗口组,维护窗口关系

文章目录 引入实现效果思路主进程模块渲染进程模块测试效果 引入 demo项目地址 窗口工具类系列文章&#xff1a; 封装窗口工具类【1】雏形 我们思考一下窗口间的关系&#xff0c;窗口创建和销毁的一些动作&#xff0c;例如父子窗口&#xff0c;窗口组合等等&#xff0c;还有…

无字母数字rce总结(自增、取反、异或、或、临时文件上传)

目录 自增 取反 异或 或 临时文件上传 自增 自 PHP 8.3.0 起&#xff0c;此功能已软弃用 在 PHP 中&#xff0c;可以递增非数字字符串。该字符串必须是字母数字 ASCII 字符串。当到达字母 Z 且递增到下个字母时&#xff0c;将进位到左侧值。例如&#xff0c;$a Z; $a;将…

Day07:基础入门-抓包技术全局协议封包监听网卡模式APP小程序PC应用

目录 非HTTP/HTTPS协议抓包工具 WireShark 科来网络分析系统 WPE封包 思维导图 章节知识点&#xff1a; 应用架构&#xff1a;Web/APP/云应用/三方服务/负载均衡等 安全产品&#xff1a;CDN/WAF/IDS/IPS/蜜罐/防火墙/杀毒等 渗透命令&#xff1a;文件上传下载/端口服务/Sh…

强大而灵活的python装饰器

装饰器&#xff08;Decorators&#xff09; 一、概述 在Python中&#xff0c;装饰器是一种特殊类型的函数&#xff0c;它允许我们修改或增强其他函数的功能&#xff0c;而无需修改其源代码。装饰器在函数定义之后立即调用&#xff0c;并以函数对象作为参数。装饰器返回一个新…

【前端素材】推荐优质在线高端家具电商网页Classi平台模板(附源码)

一、需求分析 1、系统定义 在线高端家具商城是一个专门销售高端家具产品的电子商务平台&#xff0c;旨在为消费者提供购买高品质家具的便捷渠道。 2、功能需求 在线高端家具商城是一个专门销售高端家具产品的电子商务平台&#xff0c;旨在为消费者提供购买高品质家具的便捷…

p18 线性代数,行阶梯型矩阵

行阶梯型矩阵 行最简型矩阵

Golang Redis:构建高效和可扩展的应用程序

利用Redis的闪电般的数据存储和Golang的无缝集成解锁协同效应 在当前的应用程序开发中&#xff0c;高效的数据存储和检索的必要性已经变得至关重要。Redis&#xff0c;作为一个闪电般快速的开源内存数据结构存储方案&#xff0c;为各种应用场景提供了可靠的解决方案。在这份完…

安防视频监控EasyCVR平台使用GB28181协议接入时,如何正确配置端口?

国标GB28181协议EasyCVR安防视频监控平台可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力&#xff0c;平台支持7*24小时实时高清视频监控&#xff0c;能同时播放多路监控视频流…

Docker 第十九章 : 阿里云个人镜像仓使用

Docker 第十九章 : 阿里云个人镜像仓使用 本章知识点: 如何创建镜像库,如何设置密码,如何登录与退出个人镜像仓,如何本地打镜像,如何将本地镜像推送到个人镜像库。 背景 在项目YapiDocker部署中,因读取mongo:latest 版本不一致,导致后续执行步骤的异常。遇到此场景…

深度学习 精选笔记(8)梯度消失和梯度爆炸

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

TP6上传图片到OSS(记录贴)

1&#xff0c;先安装&#xff0c;我使用composer安装 在项目的根目录运行composer require aliyuncs/oss-sdk-php 2,安装成功以后vendor目录下可以看到如图&#xff1a; 3&#xff0c;上传图片代码如下&#xff1a; <?php namespace app\controller;use app\BaseControll…

文件拖放到窗体事件

网上的实现1 实现结果 具体实现代码&#xff1a;注意需要使能允许拖拽 public partial class Form1 : Form {public Form1(){InitializeComponent();this.AllowDrop true; //允许拖拽}private void Form1_DragEnter(object sender, DragEventArgs e){this.Text DateTime.No…

Vue中<style scoped lang=“scss“>的含义

这段代码中的<style scoped lang"scss">是HTML和Vue框架结合使用时常见的一个模式&#xff0c;具体含义如下&#xff1a; scoped&#xff1a;这是一个Vue.js特有的属性&#xff0c;用来指定样式只应用于当前组件的元素。没有这个属性时&#xff0c;样式会全局应…

[unity] c# 扩展知识点其一 【个人复习笔记/有不足之处欢迎斧正/侵删】

.NET 微软的.Net既不是编程语言也不是框架,是类似于互联网时代、次时代、21世纪、信息时代之类的宣传口号,是一整套技术体系的统称&#xff0c;或者说是微软提供的技术平台的代号. 1.跨语言 只要是面向.NET平台的编程语言(C#、VB、 C、 F#等等)&#xff0c;用其中一种语言编写…

电脑休眠之后唤不醒

现象&#xff1a;午休时间电脑休眠了&#xff0c;醒来之后发现在密码输入界面&#xff0c;但鼠标键盘没反应。按重启键或电源机重新开机&#xff0c;结果开不了机。 原因&#xff1a;1、内存条脏了&#xff0c;导致内存条读取失败 2、休眠的时候硬盘休眠了&#xff0c;导致按…

基于java+springboot动物检疫信息管理系统设计和实现

基于java SSM springboot动物检疫信息管理系统设计和实现 博主介绍&#xff1a;多年java开发经验&#xff0c;专注Java开发、定制、远程、文档编写指导等,csdn特邀作者、专注于Java技术领域 作者主页 央顺技术团队 Java毕设项目精品实战案例《1000套》 欢迎点赞 收藏 ⭐留言 文…