QByteArray vs reserve () internal redistribution behavior

I just tried to optimize some exchange stack. I am using Qt 5.3.2 / VS2013.

The QByteArray buffer is used on the stack. I intended to use the capacity() and reserve() methods to reduce unnecessary internal buffer reallocations while the data size is growing. However, the behavior of QByteArray was inconsistent. Reserved space sometimes seems compressed implicitly.

I could extract the following demo using the addition of a line, the purpose of a line, and the character added to the three buffers. These single operations seem to preserve the size of the internal buffers (obtained with capacity() ). However, when each of these three operations is applied to the same QByteArray, the size of the reserved size changes. The behavior looks random for me:

 QByteArray x1; x1.reserve(1000); x1.append("test"); qDebug() << "x1" << x1.capacity() << x1; QByteArray x2; x2.reserve(1000); x2 = "test"; qDebug() << "x2" << x2.capacity() << x2; QByteArray x3; x3.reserve(1000); x3.append('t'); qDebug() << "x3" << x3.capacity() << x3; QByteArray x4; x4.reserve(1000); x4.append("test"); x4.append('t'); x4 = "test"; qDebug() << "x4" << x4.capacity() << x4; 

Expected Result:

 x1 1000 "test" x2 1000 "test" x3 1000 "t" x4 1000 "test" 

But the actual conclusion:

 x1 1000 "test" x2 1000 "test" x3 1000 "t" x4 4 "test" 

Does anyone have an explanation for this strange behavior?

UPDATE: It appears clear() also discards the reservation.

+6
source share
3 answers

Ok I think I need information.

Obviously, redundancy is not supported outside of all methods. Especially clear() and operator=() seem to cancel the reservation. In the case of operator=() it will actually be impossible to maintain the reservation due to the implicit data sharing that operator=(QByteArray) uses.

It also means that the QByteArray backup engine has been created for another use case. An attempt to make a reservation is maintained throughout the life of the QByteArray.

In my use case, it seems to use a workaround using truncate(0) instead of clear() or operator=() :

 QByteArray buffer; buffer.reserve(1000); buffer.append("foo"); qDebug() << "buffer" << buffer.capacity() << buffer; buffer.truncate(0); buffer.append("bar"); qDebug() << "buffer" << buffer.capacity() << buffer; 

Fingerprints:

 buffer 1000 "foo" buffer 1000 "bar" 

(Thanks to Alejandro)

However, a more robust approach would be to make a reserve() call before each data collection / upload sequence. This does not reduce the redistribution to one in a lifetime QByteArray, but at least it uses exactly one redistribution per data sequence, where otherwise many redistributions would be required. I think this is an acceptable solution.

In any case, before using reserve() in the Qt container, you need to test the behavior in detail, because otherwise it may happen that the container behaves much differently than expected. This is also important because these basic implementation details are not documented and are subject to change without further notice in future versions of Qt.

+2
source

operator= in QByteArray contains the following code

 int len = qstrlen(str); if (d->ref != 1 || len > d->alloc || (len < d->size && len < d->alloc >> 1)) realloc(len); 

This is reallocated memory if the new data length is more allocated or if the new data length is less than the current size and less (allocated → 1)

+2
source

There is no easy way to use the reserve () method in the QByteArray class. I tried to do below operations:

 QByteArray buffer; buffer.reserve(1000); buffer.append("foo"); cout << "Capacity " << buffer.capacity() << " Buffer " << buffer; buffer.truncate(0); buffer.append("bar"); cout << "Capacity " << buffer.capacity() << " Buffer " << buffer; 

And the result:

 Capacity 1000 Buffer foo Capacity 8 Buffer bar 

This is because calling truncate on buffer calls the "resize" method, which actually frees the reserved memory. To make it work, there is no easy way to use the documented QByteArray APIs. I could find a way to try.

 QByteArray buffer; buffer.reserve(1000); buffer.append("foo"); cout << "Capacity " << buffer.capacity() << " Buffer " << buffer; buffer.fill('\0'); buffer.data_ptr()->size = 0; buffer.append("bar"); cout << "Capacity " << buffer.capacity() << " Buffer " << buffer; 

And the result:

 Capacity 1000 Buffer foo Capacity 1000 Buffer bar 

Line:

 buffer.fill('\0'); 

Sets the contents of an existing buffer to NULL. It is optional to use.

Line:

 buffer.data_ptr()->size = 0; 
Method

data_ptr () returns a pointer to the internal data structure, and then, changing its size variable to 0, makes the buffer empty. Therefore, further calls for additions are legitimate.

+1
source

Source: https://habr.com/ru/post/977927/


All Articles