文章目录

  • 接口
    • 公有接口
      • 返回数据指针
      • 大小和容量
      • 写入数据
      • 下标操作符号
    • 扩容实现
      • 内存再分配
      • 在写入数据时判断是否需要扩容
      • 改变`Buffer`的大小
        • `示例`
  • `Buffer`使用示例
  • buffer.cpp的完整代码

接口

前面说到如果不考虑接口的需求,std::vector<uint8_t>完全可以当作buffer使用。但是buffer还是有特定的使用场景,也需要一些基本的,通用的,易用的接口。Buffer类的接口如下。

公有接口

返回数据指针

  1. uint8_t* data()

返回的非const的指针,也就是可以直接获取到Buffer内部内存的地址,并且可以直接读写数据。

其实不应该返回非const指针的接口,因为等于提供了绕过Buffer内容管理机制的接口,可以直接往内存中读写数据,权限太大。这样很容易破坏内存。

但是有些场景,比如需要更改Buffer的中内容,就需要提供这样的接口,所以使用时一点要注意不要破坏内存,比如写了大于容量的数据量等。

  1. 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];

扩容实现

自动扩容对业务代码来说不感知,调用BufferSetDataAppendData写入数据时,如果当前的容量不够,则会再次分配内存,扩大容量。

内存再分配

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不满足时,则会重新分配内存。

在写入数据时判断是否需要扩容

AppendDataSetData接口中会调用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的大小,这个接口功能就像vectorreserve接口,改变Buffer的大小和容量。但是size可以缩小,capacity不能缩小。

void Buffer::SetSize(size_t size) {size_t oldSize = _size;EnsureCapacityWithHeadroom(size,true);_size = size;if (_size < oldSize) {ZeroTrailingData(oldSize - _size);}
}
示例

下面的示例说明了Buffersizecapacity的变化。

#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的用法

  1. 通过一个裸指针构造Buffer
  2. 动态改变Buffersizecapacity
  3. Buffer的移动语义
  4. 通过swap交换Buffer对象
  5. 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(三)——接口及自动扩容相关推荐

  1. Android手把手教你使用阿里云接口实现人脸定位、人脸检测、人脸对比功能。

    前言 现如今,人工智能越来越火,以至于我们必须了解和掌握它,今天我们就来结合阿里云的接口来实现人脸定位,人脸检测等功能. 废话不多说,先上效果图: 随便在网上找了三张图片进行检测,检测结果只显示了每一 ...

  2. 手把手教你打造一个可视化接口自动化测试系统

    现如今,接口开发几乎成为一个互联网公司的标配了,无论是web还是app,哪怕是小程序,都离不开接口作为支撑,当然,这里的接口范围很广,从http到websocket,再到rpc,只要能实现数据通信的都 ...

  3. 手把手教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 ...

  4. python自动化开发简历_手把手教你用Python+Selenium实现简历自动刷新!

    (2)安装Python: 等待安装完毕. (3) 安装 selenium pip install selenium –i https: 2.导入浏览器驱动 此处用的是 Chrome 74,其他浏览器请 ...

  5. 手把手教你RMXP 第一部分(新手完整图文教程)

    如果您是纯新手,连游戏制作工具都还没选择,请看<新手入门指南>,先看看截图,然后下载游戏制作工具. 此文字教程,是面向完全不懂RPG Maker XP的新手而撰写.它将从最基本的入门开始, ...

  6. pve安装黑群晖直通硬盘_蜗牛星际装机教程篇三:手把手教你安装黑群晖NAS

    原文作者:范俩仟 蜗牛星际装机教程篇三:手把手教你安装黑群晖NAS 有很多评论说软路由没必要,我想说我就是把这东西当个玩意儿来消遣的,您喜欢看电影,我就喜欢玩电子产品.还有就是我没钱,所以想花最少的钱 ...

  7. 手把手教你搭建SpringCloud项目(九)集成OpenFeign服务接口调用

    Spring Cloud全集文章目录: 零.什么是微服务?一看就会系列! 一.手把手教你搭建SpringCloud项目(一)图文详解,傻瓜式操作 二.手把手教你搭建SpringCloud项目(二)生产 ...

  8. 手把手教你组装电脑(清晰大图详解电脑组装时各接口线缆细节)

    很多朋友对各种接口和线缆的连接方法还不是很清楚,那么这里同样以Intel平台为例,借助两块不同品牌的 主板 ,对各种接口及其连接方法进行一下详细的介绍. 一. 认识主板供电接口 图解安装详细过程 在主 ...

  9. 2021年大数据Hive(三):手把手教你如何吃透Hive数据库和表操作(学会秒变数仓大佬)

    全网最详细的Hive文章系列,强烈建议收藏加关注! 后面更新文章都会列出历史文章目录,帮助大家回顾知识重点. 目录 系列历史文章 前言 Hive数据库和表操作 一.数据库操作 1.创建数据库 2.创建 ...

最新文章

  1. JS引用类型(6)——基本包装类型1
  2. java php aes加密解密_php aes 加密解密可与java对接
  3. 利用spring session解决共享Session问题
  4. 厦门大学2016年c语言程序设计,厦门大学c语言程序设计2016模拟题讲评及课程复习.pptx...
  5. 解决 mcrypt.h not found
  6. SpringBoot集成Activiti Explorer
  7. 2 Django-2的路由层(URLconf)
  8. 刷新iframe内容
  9. 每个程序员都应读的30本书
  10. 大学生JAVA程序员周记,java程序员实习周记.docx
  11. Python翻译Excel文件
  12. 建站百科|如何做好网站Banner设计
  13. radius认证服务器系统,03-Radius认证配置举例
  14. java开发手机app教程,看完必懂
  15. 拒做背锅侠!如何利用网站性能优化驱动产品体验提升
  16. EditPlus格式化xml
  17. varnish 缓存php,php实现监控varnish缓存服务器的状态,php监控varnish缓存
  18. hangup_after_bridge
  19. 成为富人的十大心理特质,你有吗?
  20. 为什么计算机语言很重要

热门文章

  1. 【CSDN|每日一练】最长回文串
  2. 目标检测样本数据分析
  3. python图形界面化编程GUI(五)坦克大战(一)
  4. Python listdir NotADirectoryError: [WinError 267] 目录名称无效。: ‘D:\\BaiduSyncdisk\\project\\pygame_demo\
  5. 融合transformer和对抗学习的多变量时间序列异常检测算法TranAD论文和代码解读...
  6. trex抓包过程详解
  7. 安装了显卡驱动但是无法使用nvidia-smi?
  8. XDC约束技巧——时钟篇
  9. 图像处理与机器视觉_第一篇
  10. 深入理解异步消息处理机制Message,handler,MessageQueue,looper