How to 'steal' data from QByteArray without copy - qt

Is it possible to take pointer to QByteArray's internal T* data and destroy QByteArray itself so that the pointer remains unreleased? I would like to use it in the something similar to the following scenario:
const char* File::readAllBytes(const String& path, u64& outCount) {
QFile file(*static_cast<QString*>(path.internal()));
outCount = static_cast<u64>(file.size());
if (!file.open(QIODevice::ReadOnly)) gException("Failed to open file");
const QByteArray array = file.readAll();
return array.steal();
}

No, you can't steal QByteArray's pointer unless it has been constructed with QByteArray::fromRawData, which is not the case. However you can create char array manually and read data from file to it using QFile::read(char * data, qint64 maxSize). You will then decide where to pass the pointer and when to delete[] it.
Note that this is not considered good practice. You should use managed allocations whenever you can, and Qt provides enough to cover most of possible use cases. You should not do this unless you're trying to do something really low-level.
Note that many of Qt classes, including QString and QByteArray, use copy-on-write strategy, so you generally should not be afraid of copying them and passing them to another context.

No, but you can easily sidestep the problem by not using QByteArray:
const char* File::readAllBytes(const String& path, u64& outCount) {
QFile file(*static_cast<QString*>(path.internal()));
if (!file.open(QIODevice::ReadOnly)) return nullptr;
auto N = file.bytesAvailable();
char *data = malloc(N);
outCount = file.read(data, N);
return data;
}
The solution above also assumes that the consumer of your data is aware of the need to free the data.
Alas, the manual memory management called for with such an API is a bad idea. If you wish not to use Qt classes in your API, you should be using std::vector<char> instead:
std::vector<char> File::readAllBytes(const String& path) {
std::vector<char> result;
QFile file(*static_cast<QString*>(path.internal()));
if (!file.open(QIODevice::ReadOnly)) return result;
result.resize(file.bytesAvailable());
auto count = file.read(result.data(), result.size());
result.resize(count);
return result;
}
I smell that String is some sort of a framework-independent string wrapper. Perhaps you could settle on std::u16string to carry the same UTF16 data as QString would.

Related

QDataStream not working as well and return error with reusing

I am storing some data in QDataStream and immediately taking the data
bool M_FILEMANAGER::readFromDataFile(QString& fileName,RADARBEAMPATTERN *radbeam)
{
// for reading from file sequence .....
QFile fin(m_folderPath +"/"+ fileName);
if (fin.open(QIODevice::ReadOnly)) {
QDataStream in(&fin);
in.device()->startTransaction();
in >> radbeam->nPoints;
qDebug()<<"nPoints : "<<radbeam->nPoints;
fin.close();
return true;
}else{
return false;
}
}
it works fine for one use but when i reuse this function i get error
segmentation fault.
thanks in advance.
1) Strange use of QIODevice::startTransaction(). Did you mean to use QDataStream:startTransaction()? You shouldn't need that at all, but if you meant to use it to check for "valid" (complete) data in the file, do it properly (although this is typically used with async devices like sockets):
int nPoints; // temp variable to hold data, assuming radbeam->nPoints is an int
QDataStream in(&fin);
in.startTransaction();
in >> nPoints;
if (in.commitTransaction() && radbeam != nullptr)
radbeam->nPoints = nPoints;
fin.close();
2) Segfault is most likely due to radbeam pointer (eg. being null), but possibly if you're trying to read corrupted data directly into the member variable nPoints. Impossible to determine cause w/out MCVE.

How to convert double* data to const char* or QByteArray efficiently

I am trying to use the network programming APIs in Qt in my project. One part of my code requires me to convert double* data to QByteArray or a const char*.
I searched through the stackoverflow questions and could find many people suggesting this code :
QByteArray array(reinterpret_cast<const char*>(data), sizeof(double));
or, for an array of double :
QByteArray::fromRawData(reinterpret_cast<const char*>(data),s*sizeof(double));
When I use them in my function, It does notgive me the desired result. The output seems to be random characters.
Please Suggest an efficient way to implement it in Qt. Thank you very much for your time.
Regards
Alok
If you just need to encode and decode a double into a byte array, this works:
double value = 3.14159275;
// Encode the value into the byte array
QByteArray byteArray(reinterpret_cast<const char*>(&value), sizeof(double));
// Decode the value
double outValue;
// Copy the data from the byte array into the double
memcpy(&outValue, byteArray.data(), sizeof(double));
printf("%f", outValue);
However, that is not the best way to send data over the network, as it will depend on the platform specifics of how the machines encode the double type. I would recommend you look at the QDataStream class, which allows you to do this:
double value = 3.14159275;
// Encode the value into the byte array
QByteArray byteArray;
QDataStream stream(&byteArray, QIODevice::WriteOnly);
stream << value;
// Decode the value
double outValue;
QDataStream readStream(&byteArray, QIODevice::ReadOnly);
readStream >> outValue;
printf("%f", outValue);
This is now platform independent, and the stream operators make it very convenient and easy to read.
Assuming that you want to create a human readable string:
double d = 3.141459;
QString s = QString::number(d); // method has options for format and precision, see docs
or if you need localization where locale is a QLocale object:
s = locale.toString(d); // method has options for format and precision, see docs
You can easily convert the string into a QByteArray using s.toUtf8() or s.toLatin1() if really necessary. If speed is important there also is:
QByteArray ba = QByteArray::number(d); // method has options for format and precision, see docs

Does QBuffer write data in system dependent byte order?

The documentation says that QDataStream writes data in system independent way, but it says nothing about QBuffer. I develop a program that saves data in a file like this:
QByteArray a;
QBuffer b(&a);
b.open(QIODevide::WriteOnly);
quint32 x = 1;
b.write((char*)&x, sizeof(x));
b.close();
QFile f(...);
f.open(QIODevide::WriteOnly);
f.write(a.constData(), a.size());
f.close();
, and i want this file can be read in any other OS (win, linux, Mac OS). Will this code work or i must use QDataStream instead?
The QBuffer documentation says :
The QBuffer class provides a QIODevice interface for a QByteArray.
ie it is only a QByteArray underneath. On the other hand a QByteArray is portable because as long as you see the data as an array of byte and write one byte at a time you are fine. Your code will work:
When you say
I want this file to be read in any other OS
Is your file used by your program only or will it be used by other applications in the system? QDataStream provides nicer functions for I\O and you may be still able to take advantage of it.
It will be platform specific. x representation in memory depend on the endianess.It doesn't occur in the QBuffer, but when you do :
b.write((char*)&x, sizeof(x));
If you are on machines of different endianess, you will obtain different values for the resulting array by doing
char* data = &x;
qDebug()<< data[0];
qDebug()<< data[1];
qDebug()<< data[2];
qDebug()<< data[3];
Take a look at the source code of QDataStream operator
QDataStream &QDataStream::operator<<(qint32 i){
CHECK_STREAM_WRITE_PRECOND(*this)
if (!noswap) {
i = qbswap(i);
}
if (dev->write((char *)&i, sizeof(qint32)) != sizeof(qint32))
q_status = WriteFailed;
return *this;
}

convert std::ostream to some array?

I have a legacy library that takes data from hardware and writes it to ostream.
The method looks like following :
int sensors(ostream*) const;
I am not skilled enough in Ancient Ways. How to convert this data to QByteArray? Or, at least, to char array of known size?
I would have solved it myself, but there is an additional problem: the data in ostream seem to be arbitrary length and have several arbitrary '\0' symbols, so you can't count on it being null-terminated.
I think this is what OrcunC was getting at:
std::stringstream s;
sensors( &s );
QByteArray( s.str().data(), (int) s.str().size() );
... but hopefully more clear :). See also std::stringstream and std::string for information on the classes/member functions used here. By the way, note that I am using str().data(), not str().c_str() -- I'm being really careful to handle those \0 characters, and I'm not assuming NULL termination.
I have not tried it, but you need something like this :
ostream s (ios::out | ios::binary);
//..Populate the stream
//Convert it to string. string can hold \0 values too.
string str = s.str ();
QByteArray ba (str.data (),str.size ());
You can subclass std::ostream and use an object of the subclass to collect the bytes into your QByteArray.
/**
* This helper class collects bytes that are passed to it
* into a QByteArray.
*
* After https://stackoverflow.com/a/19933011/316578
*/
class QByteArrayAppender : public std::ostream, public std::streambuf {
private:
QByteArray &m_byteArray;
public:
QByteArrayAppender(QByteArray &byteArray)
: std::ostream(this),
std::streambuf(),
m_byteArray(byteArray) {
}
int overflow(int c) {
m_byteArray.append(static_cast<char>(c));
return 0;
}
};
This avoids going via an std::string, which is an extra copy of the byte array.

QNetworkAccessManager read outgoingData and keep it in QIODevice

I'm trying to save all outgoing POST data in QtWebKit.
I do it using overriding QNetworkReply *QNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice outgoingData) method and reading an outgoingData that contains outgoing POST data.
The problem is that after reading it, the data become not available in the QIODevice.
How to save an outgoing (PUT, POST) data and keep it available for the future internal Qt operations?
If I need to use another approach to save PUT/POST data - please, let me know.
Code example:
QNetworkReply *MyNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData)
{
QByteArray bArray = outgoingData->readAll();
// save bArray (that contains POST outgoing data) somewhere
// do other things, and outgoingData now has no data anymore, as it was already read to bArray
}
I have tried
QByteArray bArray = outgoingData->readAll();
outgoingData->write(bArray);
qDebug() << bArray;
But in this case I get "QIODevice::write: ReadOnly device" message.
How to save the outgoing POST/PUT data in Qt?
Thanks.
qint64 QIODevice::peek (char * data, qint64 maxSize)
Reads at most maxSize bytes from the
device into data, without side effects
(i.e., if you call read() after
peek(), you will get the same data).
Returns the number of bytes read. If
an error occurs, such as when
attempting to peek a device opened in
WriteOnly mode, this function returns
-1.
0 is returned when no more data is
available for reading.
EDIT
Forget about peak(), it's not good in this situation. You could use it but you would have to do much work to accomplish what you ask for. Instead read Tee is for Tubes, grab code from there and use it.
Link by courtesy of peppe from #qt irc channel on http://irc.freenode.net.
I'd like to thank peppe and thiago who were so kind to discuss this problem on #qt channel with me.
In case one day you want to steal incoming (as opposed to outgoing) data from QNetworkAccessManager you'll find answer and code in How to read data from QNetworkReply being used by QWebPage? question.
Using pos() and seek() does actually not work in that special case. The idea of using peek() instead seems to be much better. But an example would be helpful. So, here an example of how to get data buffer from given QIODevice's outgoing data in function createRequest() without affecting original data.
if (outgoing != NULL)
{
const qint64 delta = 100;
qint64 length = delta;
QByteArray array;
while (true)
{
char *buffer = new char[length];
qint64 count = outgoing->peek(buffer, length);
if (count < length)
{
array = QByteArray(buffer, count);
delete buffer;
break;
}
length += delta;
delete buffer;
}
}
For an optimization you may adjust the value of 'delta'.
Save the IO device marker with QIODevice::pos(). Read data from it. Then restore the marker with QIODevice::seek().
This will only work if the device is a random access one. But I think it covers most of them.

Resources