Teach you to implement buffer (3) - interface and automatic expansion


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. BufferThe interface of the class is as follows.

public interface

return data pointer

  1. uint8_t* data()

The returned non -pointer, that is, the address of the internal memory constcan be obtained directly , and the data can be read and written directly.Buffer

In fact, you should not return a non- constpointer interface, because it is equivalent to providing Bufferan 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 Buffercontent 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.

  1. 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 Bufferthe amount of data in the return and capacity()is the returned Buffercapacity.

data input

void SetData(constuint8_t*data,size_tsize)
void AppendData(const uint8_t*data,size_t size)

SetDataIt is to put data of size Bufferinto it , and to add data of size to it. These two interfaces are also the missing interfaces.sizeAppendDataBuffersizestd::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 indexreference to the return location, which can directly change the data. The value of the latter return indexposition 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 Bufferand SetDatawriting AppendDatadata, 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 capacityindicates the required capacity. When _capacityit is not satisfied, the memory will be reallocated.

Determine whether expansion is required when writing data

It will be called in the interface AppendDataand will first determine whether expansion is required.SetDataEnsureCapacityWithHeadroom

  • voidAppendData(constuint8_t*data,size_tsize)

BufferAdd 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)

SetDataBased on AppendDataimplementation, 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 Buffersize

void SetSize(size_tsize)

Changing Bufferthe size, this interface functions just like vectorthe reserveinterface, changing Bufferthe size and capacity. But sizeit can be reduced, capacitynot 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 Bufferof sizeand 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;
}

BufferExample of use

The example demonstrates Bufferthe usage of

  1. Constructed via a raw pointerBuffer
  2. dynamically Bufferchanging sizeandcapacity
  3. Buffermove semantics
  4. by swapexchanging Bufferobjects
  5. BufferPut 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

BufferThe 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());

}

おすすめ

転載: blog.csdn.net/mo4776/article/details/126221456