Johannes Schaub - litb
Johannes Schaub - litb

Reputation: 507095

Ensure a QByteArray owns its memory (QByteArray::fromRawData)

Say we have a function store

void store(const QByteArray& data);

This function's job is to take data and store it away. Unfortunately, it is not safe to do that if the argument was created with QByteArray::fromRawData(ptr, size), because it and all its copies require that ptr remains valid.

Therefore store has no option to forbid its callers to pass in such an array, to treat data as if it was a const char* on stereoids or to force a deep-copy with detach. All of this is not satisfying and especially the latter hurts performance because if data was COW-copied before being passed to store, we will be doing an unnecessary deep-copy.

QByteArray has a private function nulTerminated whose implementation seems to do just what I want: If it doesn't own the memory, it deep copies. And if it owns the memory, it does nothing but return *this.

Two questions really

Upvotes: 5

Views: 3385

Answers (2)

d.Candela
d.Candela

Reputation: 153

this sounds more like a conceptual problem, rather than technical.

store() tries to take absolute ownership, but "taking ownership" on a QByteArray does not make much sense conceptually. A copy might share its memory with another, or it might not, or it might be created from raw data, so there's a hidden ownership juggle going on under the hood and the whole class is designed to hide this from you. The whole implicit sharing thing doesn't mesh well with modern explicit ownership transfer paradigm in general, and QByteArray complicates matters further by providing the raw data facility.

If possible, accepting a non-implicitly-shared container that holds absolute ownership over their content (such as std::string, which is easily generated with QByteArray::toStdString()) by rvalue, forcing callers to copy, would be the best option.

Upvotes: 0

hyde
hyde

Reputation: 62838

Looking at the source (for example here), ba.right(ba.size()); is actually plain shallow copy, almost a no-op, so that's not a solution for you. And in any case, relying on any behaviour not backed by documentation is a bit unsafe, it could be changed without notification in future Qt versions.

That being said, QByteArray::detach() is undocumented but public. It will perform deep copy on arrays created with fromRawData(), but not on already unshared data, and I think this is unlikely to change. Demonstration:

QByteArray ba1 = QByteArray::fromRawData("foo", 4);
QByteArray ba2("foo");
qDebug() << (void*)ba1.constData() << (void*)ba2.constData();
ba1.detach(); ba2.detach();
qDebug() << (void*)ba1.constData() << (void*)ba2.constData();

Output of above for example:

0x804b960 0x93ebfd8
0x93d2170 0x93ebfd8

Looking at the source, IS_RAW_DATA macro is in the qbytearray.cpp file, and I did not spot any way that you could take advantage of it using public interface. So, to do what you want seems to not be possible, and detach() is the closest you can get, even QByteArray::squeeze() doesn't detach raw data.

Upvotes: 2

Related Questions