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;
}

342

被折叠的 条评论
为什么被折叠?



