c++自增长缓冲区实现(一)

1. 引言

在 Qt 框架中,`QByteArray` 是一个极其重要的类,专门用于处理原始字节数据(二进制数据)和传统的 8 位字符串。与 `QString` 专注于 Unicode 文本不同,`QByteArray` 更侧重于底层数据操作,是网络通信、文件 I/O、协议解析等场景中的核心工具。

QByteArray确实非常好用,为我们解决了很多问题。但在一些场景中,我不想引入Qt作为三方库,它太大了。例如在板卡上开发,存储容量有限。我为了使用QByteArray要下载整个Qt,虽然我最终只使用了QtCore。

2. 自增长缓冲区

我们自己写一套缓冲区,实现类似QByteArray的功能。在保留QByteArray的零拷贝,写时复制的基础上,提供自增长的能力。所谓自增长的目的,是为了减少内存申请的消耗。例如我一开始就为缓冲区分配1024长度,在前100次使用中,都只用到了200长度,那么除了第一次,后面199次就没有做内存申请。第101次要用到1025长度,我会重新申请2048个长度。缓冲区根据外部使用内存长度自动增长,保证实际长度是某次使用的最大长度,就是传说中的空间换时间。

3. 实现细节

//头文件

class  ZeroCopyByteArray
{
public:
    ZeroCopyByteArray();
    ZeroCopyByteArray(int size);
    ~ZeroCopyByteArray();
    ZeroCopyByteArray(const ZeroCopyByteArray& other);
    ZeroCopyByteArray(ZeroCopyByteArray&& other) noexcept;

    ZeroCopyByteArray& operator=(ZeroCopyByteArray& other);
    ZeroCopyByteArray& operator=(ZeroCopyByteArray&& other) noexcept;

public:
    void append(char* data, int len);
    void moveHead(int len);
    void moveTail(int len);
    int size();
    char* data();
    void reset();
    ZeroCopyByteArray clone();

private:
    struct ByteArrayData 
    {
        char*  data;
        unsigned int capacity;
        unsigned int totalSize;
        unsigned int headOffset;
        unsigned int tailOffset;
        std::atomic<int> refCount;

        explicit ByteArrayData(unsigned int cap)
            : data(new char[cap]()), capacity(cap), totalSize(0),
            headOffset(0), tailOffset(0), refCount(1) {}

        ~ByteArrayData() 
        {
            delete[] data;
        }
    };

    ByteArrayData* m_data;

    // 写时复制
    void copyOnWrite();
};

//cpp

#include "ZeroCopyByteArray.h"
#include <cstdlib>
#include <cstring>
#include <stdexcept>

ZeroCopyByteArray::ZeroCopyByteArray()
{
    m_data = new ByteArrayData(0);
}
ZeroCopyByteArray::ZeroCopyByteArray(int size)
{
    m_data = new ByteArrayData(size);
}
ZeroCopyByteArray::~ZeroCopyByteArray()
{
    if (m_data && m_data->refCount.fetch_sub(1, std::memory_order_acq_rel) == 1)
        delete m_data;
}
ZeroCopyByteArray::ZeroCopyByteArray(const ZeroCopyByteArray& other)
{
    if (&other == this)
        return;

    m_data = other.m_data;
    if (m_data)
        m_data->refCount.fetch_add(1, std::memory_order_relaxed);
}
ZeroCopyByteArray::ZeroCopyByteArray(ZeroCopyByteArray&& other) noexcept
{
    m_data = other.m_data;
    other.m_data = 0;
}
ZeroCopyByteArray& ZeroCopyByteArray::operator=(ZeroCopyByteArray& other)
{
    if (&other == this)
        return *this;

    if (m_data && m_data->refCount.fetch_sub(1, std::memory_order_acq_rel) == 1)
        delete m_data;

    m_data = other.m_data;
    if (m_data)
        m_data->refCount.fetch_add(1, std::memory_order_relaxed);

    return *this;
}
ZeroCopyByteArray& ZeroCopyByteArray::operator=(ZeroCopyByteArray&& other) noexcept
{
    if (this == &other) 
        return *this;

    if (m_data && m_data->refCount.fetch_sub(1, std::memory_order_acq_rel) == 1)
        delete m_data;

    m_data = other.m_data;
    other.m_data = 0;

    return *this;
}
void ZeroCopyByteArray::copyOnWrite()
{
    if (m_data->refCount.load(std::memory_order_acquire) == 1)
        return; // 只有一个引用者,无需复制

    // 否则需要复制一份新数据
    ByteArrayData* newData = new ByteArrayData(m_data->capacity);
    memcpy(newData->data, m_data->data, m_data->totalSize);
    newData->totalSize = m_data->totalSize;
    newData->headOffset = m_data->headOffset;
    newData->tailOffset = m_data->tailOffset;

    // 减少旧数据引用计数
    if (m_data->refCount.fetch_sub(1, std::memory_order_acq_rel) == 1)
        delete m_data;

    m_data = newData;
}
void ZeroCopyByteArray::append(char* data, int len)
{
    if (!data || len == 0) 
        return;

    copyOnWrite(); // 如果有多个引用者,先复制

    if (m_data->totalSize + len > m_data->capacity) {
        size_t newCap = m_data->capacity + len + 1024;
        char* newData = new char[newCap];
        memcpy(newData, m_data->data, m_data->totalSize);
        delete[] m_data->data;

        m_data->data = newData;
        m_data->capacity = newCap;
    }

    memcpy(m_data->data + m_data->totalSize, data, len);
    m_data->totalSize += len;
}
void ZeroCopyByteArray::moveHead(int len)
{
    if (len > size())
        throw std::out_of_range("moveHead: offset out of range");

    m_data->headOffset += len;
}
void ZeroCopyByteArray::moveTail(int len)
{
    if (len > size())
        throw std::out_of_range("moveTail: offset out of range");
    
    m_data->tailOffset += len;
}
int ZeroCopyByteArray::size()
{
    return m_data->totalSize - m_data->headOffset - m_data->tailOffset;
}
char* ZeroCopyByteArray::data()
{
    return m_data->data + m_data->headOffset;
}
void ZeroCopyByteArray::reset()
{
    m_data->headOffset = 0;
    m_data->tailOffset = 0;
    m_data->totalSize = 0;
}
ZeroCopyByteArray ZeroCopyByteArray::clone()
{
    ZeroCopyByteArray copy(size());
    memcpy(copy.m_data->data, m_data->data + m_data->headOffset, size());
    copy.m_data->totalSize = size();
    return copy;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值