前面说到如果不考虑接口的需求,std::vector完全可以当作buffer使用。但是buffer还是有特定的使用场景,也需要一些基本的,通用的,易用的接口。Buffer类的接口如下。
uint8_t* data()返回的非const的指针,也就是可以直接获取到Buffer内部内存的地址,并且可以直接读写数据。
其实不应该返回非const指针的接口,因为等于提供了绕过Buffer内容管理机制的接口,可以直接往内存中读写数据,权限太大。这样很容易破坏内存。
但是有些场景,比如需要更改Buffer的中内容,就需要提供这样的接口,所以使用时一点要注意不要破坏内存,比如写了大于容量的数据量等。
const uint8_t* data() const返回指向内部内存的const指针,只可读,不可写。
size_t size() constsize_t capacity() constsize()是返回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& 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;
}
//扩大capacity
size_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());
//重置_size
size_t oldSize = _size;
//_size赋值为0,将是从起始位置写入数据
_size = 0;
//调用AppendData写入数据
AppendData(data,size);
if (_size < oldSize) {
//size缩小了,多出来的空间被设置为0
ZeroTrailingData(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
#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都是7
Buffer buf(kTestData,7);
std::cout<<buf.size()<<" "<<buf.capacity()<<std::endl;
//变小,size是3,capacity是7
buf.SetData(kTestData,3);
std::cout<<buf.size()<<" "<<buf.capacity()<<std::endl;
//size变小,capacity还是7
buf.SetSize(1);
std::cout<<"size:"<<buf.size()<<",capacity:"<<buf.capacity()<<std::endl;
}
Buffer使用示例示例演示了Buffer的用法
BufferBuffer的size和capacityBuffer的移动语义swap交换Buffer对象Buffer中放入了POD类型#include
#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都是7
Buffer buf(kTestData,7);
std::cout<<buf.size()<<" "<<buf.capacity()<<std::endl;
//变小,size是3,capacity是7
buf.SetData(kTestData,3);
std::cout<<buf.size()<<" "<<buf.capacity()<<std::endl;
//size变小,capacity还是7
buf.SetSize(1);
std::cout<<"size:"<<buf.size()<<",capacity:"<<buf.capacity()<<std::endl;
//index 1的值为0
std::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的值是0
std::cout<<"index 14:"<<static_cast<int>(buf[14])<<",index 15:"<<static_cast<int>(buf[15])<<std::endl;
//再缩小,size会变成3,capcaity还是22
buf.SetData(kTestData,3);
std::cout<<"size:"<<buf.size()<<",capcaity:"<<buf.capacity()<<std::endl;
//index 0的值应该是0
std::cout<<"index 0:"<<static_cast<int>(buf[0])<<std::endl;
//index 1的值应该是1
std::cout<<"index 1:"<<static_cast<int>(buf[1])<<std::endl;
//index 2的值应该是2
std::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为40
std::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;
//swap
Buffer 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定义在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;
}
//扩大capacity
size_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());
}