手把手教你实现buffer(三)——接口及自动扩容
文章目录
- 接口
- 公有接口
- 返回数据指针
- 大小和容量
- 写入数据
- 下标操作符号
- 扩容实现
- 内存再分配
- 在写入数据时判断是否需要扩容
- 改变`Buffer`的大小
- `示例`
- `Buffer`使用示例
- buffer.cpp的完整代码
接口
前面说到如果不考虑接口的需求,std::vector<uint8_t>
完全可以当作buffer使用。但是buffer还是有特定的使用场景,也需要一些基本的,通用的,易用的接口。Buffer
类的接口如下。
公有接口
返回数据指针
uint8_t* data()
返回的非const
的指针,也就是可以直接获取到Buffer
内部内存的地址,并且可以直接读写数据。
其实不应该返回非const
指针的接口,因为等于提供了绕过Buffer
内容管理机制的接口,可以直接往内存中读写数据,权限太大。这样很容易破坏内存。
但是有些场景,比如需要更改Buffer
的中内容,就需要提供这样的接口,所以使用时一点要注意不要破坏内存,比如写了大于容量的数据量等。
const uint8_t* data() const
返回指向内部内存的const
指针,只可读,不可写。
大小和容量
size_t size() const
size_t capacity() const
size()
是返回Buffer
中的数据量,capacity()
是返回Buffer
的容量。
写入数据
void SetData(constuint8_t*data,size_tsize)
void AppendData(const uint8_t*data,size_t size)
SetData
是往Buffer
中放入size
大小的数据,AppendData
是往Buffer
中添加size
大小的数据,这两个接口也正是std::vector<uint8_t>
所缺少的接口。
下标操作符号
uint8_t& operator[](size_t index)
uint8_t operator[](size_t index) const
重载了两种不同形式的下标操作符,前面一个是返回index
位置的引用,可以直接更改数据。后面一个返回index
位置的值,只能读取值。
这两个下标操作符的实现是通用写法,适用于如下场景
//b是Buffer对象
//调用的返回引用的下标操作符
b[0] = 18;
//调用的是返回值的下标操作符
int i = b[0];
扩容实现
自动扩容对业务代码来说不感知,调用Buffer
的SetData
和AppendData
写入数据时,如果当前的容量不够,则会再次分配内存,扩大容量。
内存再分配
voidEnsureCapacityWithHeadroom(size_tcapacity, boolextra_headroom)
它是实现内存再分配,代码如下:
void Buffer::EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) {assert(IsConsistent());if (capacity <= _capacity) {return;}//扩大capacitysize_t newCapacity = extra_headroom?std::max(capacity,_capacity + _capacity/2):capacity;std::unique_ptr<uint8_t> newData(new uint8_t[newCapacity]);memcpy(newData.get(),_data.get(),_size);memset(_data.get(),0,_capacity);_data = std::move(newData);_capacity= newCapacity;assert(IsConsistent());}
逻辑挺简单,形参capacity
表示需要的容量,当_capacity
不满足时,则会重新分配内存。
在写入数据时判断是否需要扩容
在AppendData
和SetData
接口中会调用EnsureCapacityWithHeadroom
会先判断是否需要扩容。
voidAppendData(constuint8_t*data,size_tsize)
在Buffer
尾部添加数据。
void Buffer::AppendData(const uint8_t* data,size_t size) {assert(IsConsistent());size_t newSize = _size + size;//判断是否需要扩容EnsureCapacityWithHeadroom(newSize,true);std::memcpy(_data.get()+_size,data,size);_size = newSize;assert(IsConsistent());
}
SetData(constuint8_t*data,size_tsize)
SetData
基于AppendData
实现,在起始位置写入数据。
void Buffer::SetData(const uint8_t* data,size_t size) {assert(IsConsistent());//重置_sizesize_t oldSize = _size;//_size赋值为0,将是从起始位置写入数据_size = 0;//调用AppendData写入数据AppendData(data,size);if (_size < oldSize) {//size缩小了,多出来的空间被设置为0ZeroTrailingData(oldSize - _size);}
}
改变Buffer
的大小
void SetSize(size_tsize)
改变Buffer
的大小,这个接口功能就像vector
的reserve
接口,改变Buffer
的大小和容量。但是size
可以缩小,capacity
不能缩小。
void Buffer::SetSize(size_t size) {size_t oldSize = _size;EnsureCapacityWithHeadroom(size,true);_size = size;if (_size < oldSize) {ZeroTrailingData(oldSize - _size);}
}
示例
下面的示例说明了Buffer
的size
和capacity
的变化。
#include <iostream>
#include "buffer.h"
uint8_t kTestData[] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,
0xb,0xc,0xd,0xe,0xf};
int main() {//size和capacity都是7Buffer buf(kTestData,7);std::cout<<buf.size()<<" "<<buf.capacity()<<std::endl;//变小,size是3,capacity是7buf.SetData(kTestData,3);std::cout<<buf.size()<<" "<<buf.capacity()<<std::endl;//size变小,capacity还是7buf.SetSize(1);std::cout<<"size:"<<buf.size()<<",capacity:"<<buf.capacity()<<std::endl;
}
Buffer
使用示例
示例演示了Buffer
的用法
- 通过一个裸指针构造
Buffer
- 动态改变
Buffer
的size
和capacity
Buffer
的移动语义- 通过
swap
交换Buffer
对象 - 在
Buffer
中放入了POD类型
#include <iostream>
#include "buffer.h"uint8_t kTestData[] = {0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,
0xb,0xc,0xd,0xe,0xf};
using namespace base::lib;
int main(){//size和capacity都是7Buffer buf(kTestData,7);std::cout<<buf.size()<<" "<<buf.capacity()<<std::endl;//变小,size是3,capacity是7buf.SetData(kTestData,3);std::cout<<buf.size()<<" "<<buf.capacity()<<std::endl;//size变小,capacity还是7buf.SetSize(1);std::cout<<"size:"<<buf.size()<<",capacity:"<<buf.capacity()<<std::endl;//index 1的值为0std::cout<<"index 1:"<<static_cast<int>(buf[1])<<std::endl;//变大//capcaity将变大(变成15)buf.SetData(kTestData,15);std::cout<<"size:"<<buf.size()<<",capcaity:"<<buf.capacity()<<std::endl;//size变成20,capcaity变成22(capcaity + capcaity/2)buf.SetSize(20);std::cout<<"size:"<<buf.size()<<",capcaity:"<<buf.capacity()<<std::endl;//index 14是14,index 15的值是0std::cout<<"index 14:"<<static_cast<int>(buf[14])<<",index 15:"<<static_cast<int>(buf[15])<<std::endl;//再缩小,size会变成3,capcaity还是22buf.SetData(kTestData,3);std::cout<<"size:"<<buf.size()<<",capcaity:"<<buf.capacity()<<std::endl;//index 0的值应该是0std::cout<<"index 0:"<<static_cast<int>(buf[0])<<std::endl;//index 1的值应该是1std::cout<<"index 1:"<<static_cast<int>(buf[1])<<std::endl;//index 2的值应该是2std::cout<<"index 2:"<<static_cast<int>(buf[2])<<std::endl;//测试移动构造函数Buffer buf1(kTestData,3,40);const uint8_t* data = buf1.data();Buffer buf2(std::move(buf1));//buf2的size为3,buf2的capcaity为40std::cout<<"buf2 size:"<<buf2.size()<<",buf2 capacity "<<buf2.capacity()<<std::endl;//移动操作本质是指针的移动,所以data与buf2.data()的指向相同std::cout<<(buf2.data() == data)<<std::endl;//测试移动操作Buffer buf11(kTestData,3,40);//const uint8_t* data = buf11.data();Buffer buf12(kTestData,15);buf12 = std::move(buf11);std::cout<<"buf12 size:"<<buf12.size()<<",capacity:"<<buf12.capacity()<<std::endl;std::cout<<"buf11 is empty:"<<buf11.empty()<<",buf1 size "<<buf1.size()<<",capacity "<<buf1.capacity()<<std::endl;std::cout<<"buf11 data is null "<<(buf11.data() == nullptr)<<std::endl;//swapBuffer buf21(kTestData,3);Buffer buf22(kTestData,6,40);uint8_t* data21 = buf21.data();//uint8_t* data22 = buf22.data();std::swap(buf21,buf22);std::cout<<"buff21 size "<<buf21.size()<<",capacity:"<<buf21.capacity()<<std::endl;std::cout<<"buff21 data "<<(data21 == buf22.data())<<","<<(data21 == buf21.data())<<std::endl;//放入结构体struct test {int a=18;int b=118;int c=188;char szInfo[10] = {0};};test t;memcpy(t.szInfo,"mmmmiiii",8);Buffer TestBuf(reinterpret_cast<uint8_t*>(&t),sizeof(test));std::cout<<"test buf size "<<TestBuf.size()<<",capcaity:"<<TestBuf.capacity()<<std::endl;test *t1 = reinterpret_cast<test*>(TestBuf.data());std::cout<<"a "<<t1->a<<",b "<<t1->b<<",c "<<t1->c<<",info:"<<t1->szInfo<<std::endl;
}
buffer.cpp的完整代码
Buffer
定义在buffer.h文件中,在前一篇文章已经贴出,下面是buffer.cpp的代码。
#include "buffer.h"void Buffer::ZeroTrailingData(size_t count) {assert(IsConsistent());memset(_data.get()+_size,0,count);
}void Buffer::SetData(const uint8_t* data,size_t size) {assert(IsConsistent());size_t oldSize = _size;_size = 0;AppendData(data,size);if (_size < oldSize) {ZeroTrailingData(oldSize - _size);}
}void Buffer::AppendData(const uint8_t* data,size_t size) {assert(IsConsistent());size_t newSize = _size + size;EnsureCapacityWithHeadroom(newSize,true);std::memcpy(_data.get()+_size,data,size);_size = newSize;assert(IsConsistent());
}void Buffer::SetSize(size_t size) {size_t oldSize = _size;EnsureCapacityWithHeadroom(size,true);_size = size;if (_size < oldSize) {ZeroTrailingData(oldSize - _size);}
}bool Buffer::IsConsistent() const {return ((_data || _capacity == 0)&&(_capacity >= _size));
}void Buffer::EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom) {assert(IsConsistent());if (capacity <= _capacity) {return;}//扩大capacitysize_t newCapacity = extra_headroom?std::max(capacity,_capacity + _capacity/2):capacity;std::unique_ptr<uint8_t> newData(new uint8_t[newCapacity]);memcpy(newData.get(),_data.get(),_size);memset(_data.get(),0,_capacity);_data = std::move(newData);_capacity= newCapacity;assert(IsConsistent());}
手把手教你实现buffer(三)——接口及自动扩容相关推荐
- Android手把手教你使用阿里云接口实现人脸定位、人脸检测、人脸对比功能。
前言 现如今,人工智能越来越火,以至于我们必须了解和掌握它,今天我们就来结合阿里云的接口来实现人脸定位,人脸检测等功能. 废话不多说,先上效果图: 随便在网上找了三张图片进行检测,检测结果只显示了每一 ...
- 手把手教你打造一个可视化接口自动化测试系统
现如今,接口开发几乎成为一个互联网公司的标配了,无论是web还是app,哪怕是小程序,都离不开接口作为支撑,当然,这里的接口范围很广,从http到websocket,再到rpc,只要能实现数据通信的都 ...
- 手把手教Apereo CAS5.2.3 注册后自动登录
顺手贴上CAS 5.2.X官方文档:https://apereo.github.io/cas/5.2.x/index.html hugeo的CAS系列:https://blog.csdn.net/u0 ...
- python自动化开发简历_手把手教你用Python+Selenium实现简历自动刷新!
(2)安装Python: 等待安装完毕. (3) 安装 selenium pip install selenium –i https: 2.导入浏览器驱动 此处用的是 Chrome 74,其他浏览器请 ...
- 手把手教你RMXP 第一部分(新手完整图文教程)
如果您是纯新手,连游戏制作工具都还没选择,请看<新手入门指南>,先看看截图,然后下载游戏制作工具. 此文字教程,是面向完全不懂RPG Maker XP的新手而撰写.它将从最基本的入门开始, ...
- pve安装黑群晖直通硬盘_蜗牛星际装机教程篇三:手把手教你安装黑群晖NAS
原文作者:范俩仟 蜗牛星际装机教程篇三:手把手教你安装黑群晖NAS 有很多评论说软路由没必要,我想说我就是把这东西当个玩意儿来消遣的,您喜欢看电影,我就喜欢玩电子产品.还有就是我没钱,所以想花最少的钱 ...
- 手把手教你搭建SpringCloud项目(九)集成OpenFeign服务接口调用
Spring Cloud全集文章目录: 零.什么是微服务?一看就会系列! 一.手把手教你搭建SpringCloud项目(一)图文详解,傻瓜式操作 二.手把手教你搭建SpringCloud项目(二)生产 ...
- 手把手教你组装电脑(清晰大图详解电脑组装时各接口线缆细节)
很多朋友对各种接口和线缆的连接方法还不是很清楚,那么这里同样以Intel平台为例,借助两块不同品牌的 主板 ,对各种接口及其连接方法进行一下详细的介绍. 一. 认识主板供电接口 图解安装详细过程 在主 ...
- 2021年大数据Hive(三):手把手教你如何吃透Hive数据库和表操作(学会秒变数仓大佬)
全网最详细的Hive文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 系列历史文章 前言 Hive数据库和表操作 一.数据库操作 1.创建数据库 2.创建 ...
最新文章
- JS引用类型(6)——基本包装类型1
- java php aes加密解密_php aes 加密解密可与java对接
- 利用spring session解决共享Session问题
- 厦门大学2016年c语言程序设计,厦门大学c语言程序设计2016模拟题讲评及课程复习.pptx...
- 解决 mcrypt.h not found
- SpringBoot集成Activiti Explorer
- 2 Django-2的路由层(URLconf)
- 刷新iframe内容
- 每个程序员都应读的30本书
- 大学生JAVA程序员周记,java程序员实习周记.docx
- Python翻译Excel文件
- 建站百科|如何做好网站Banner设计
- radius认证服务器系统,03-Radius认证配置举例
- java开发手机app教程,看完必懂
- 拒做背锅侠!如何利用网站性能优化驱动产品体验提升
- EditPlus格式化xml
- varnish 缓存php,php实现监控varnish缓存服务器的状态,php监控varnish缓存
- hangup_after_bridge
- 成为富人的十大心理特质,你有吗?
- 为什么计算机语言很重要
热门文章
- 【CSDN|每日一练】最长回文串
- 目标检测样本数据分析
- python图形界面化编程GUI(五)坦克大战(一)
- Python listdir NotADirectoryError: [WinError 267] 目录名称无效。: ‘D:\\BaiduSyncdisk\\project\\pygame_demo\
- 融合transformer和对抗学习的多变量时间序列异常检测算法TranAD论文和代码解读...
- trex抓包过程详解
- 安装了显卡驱动但是无法使用nvidia-smi?
- XDC约束技巧——时钟篇
- 图像处理与机器视觉_第一篇
- 深入理解异步消息处理机制Message,handler,MessageQueue,looper