Article directory
interface
As mentioned earlier, if the interface requirements are not considered, std::vector<uint8_t>
it can be used as a buffer. But buffer still has specific usage scenarios, and also needs some basic, general, and easy-to-use interfaces. Buffer
The interface of the class is as follows.
public interface
return data pointer
uint8_t* data()
The returned non -pointer, that is, the address of the internal memory const
can be obtained directly , and the data can be read and written directly.Buffer
In fact, you should not return a non- const
pointer interface, because it is equivalent to providing Buffer
an interface that bypasses the content management mechanism, and you can directly read and write data into the memory, and the permissions are too large. This can easily corrupt memory.
However, in some scenarios, such as Buffer
content that needs to be changed, such an interface needs to be provided, so when using it, one must be careful not to damage the memory, such as writing data that is larger than the capacity.
const uint8_t* data() const
Returns a pointer to internal memory const
, readable only, not writable.
size and capacity
size_t size() const
size_t capacity() const
size()
is Buffer
the amount of data in the return and capacity()
is the returned Buffer
capacity.
data input
void SetData(constuint8_t*data,size_tsize)
void AppendData(const uint8_t*data,size_t size)
SetData
It is to put data of size Buffer
into it , and to add data of size to it. These two interfaces are also the missing interfaces.size
AppendData
Buffer
size
std::vector<uint8_t>
subscript operator
uint8_t& operator[](size_t index)
uint8_t operator[](size_t index) const
Two different forms of subscript operators are overloaded. The former is a index
reference to the return location, which can directly change the data. The value of the latter return index
position can only read the value.
The implementation of these two subscript operators is a common way of writing, which is suitable for the following scenarios
//b是Buffer对象
//调用的返回引用的下标操作符
b[0] = 18;
//调用的是返回值的下标操作符
int i = b[0];
Expansion to achieve
Automatic expansion is not aware of business code. When calling Buffer
and SetData
writing AppendData
data, if the current capacity is not enough, memory will be allocated again to expand the capacity.
memory reallocation
voidEnsureCapacityWithHeadroom(size_tcapacity, boolextra_headroom)
It is to achieve memory reallocation, the code is as follows:
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());
}
The logic is quite simple. The formal parameter capacity
indicates the required capacity. When _capacity
it is not satisfied, the memory will be reallocated.
Determine whether expansion is required when writing data
It will be called in the interface AppendData
and will first determine whether expansion is required.SetData
EnsureCapacityWithHeadroom
voidAppendData(constuint8_t*data,size_tsize)
Buffer
Add data at the end.
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
Based on AppendData
implementation, data is written at the beginning.
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);
}
}
change Buffer
size
void SetSize(size_tsize)
Changing Buffer
the size, this interface functions just like vector
the reserve
interface, changing Buffer
the size and capacity. But size
it can be reduced, capacity
not reduced.
void Buffer::SetSize(size_t size) {
size_t oldSize = _size;
EnsureCapacityWithHeadroom(size,true);
_size = size;
if (_size < oldSize) {
ZeroTrailingData(oldSize - _size);
}
}
示例
The following example illustrates the variation Buffer
of size
and 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都是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
Example of use
The example demonstrates Buffer
the usage of
- Constructed via a raw pointer
Buffer
- dynamically
Buffer
changingsize
andcapacity
Buffer
move semantics- by
swap
exchangingBuffer
objects Buffer
Put the POD type in
#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都是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;
}
The complete code of buffer.cpp
Buffer
The definition is in the buffer.h file, which has been posted in the previous article. The following is the code of 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());
}