In my project I'm working with QByteArrays appending data to them as the program goes. Most of the time, a simple quint8 gets appended just fine using QByteArray::append(). But when a quint16 gets appended, only 1 byte gets appended instead of 2.
QByteArray ba = QByteArray::fromHex("010203");
quint number(300);//300 in hex is 012c
ba.append(number);//What should be appended instead of just number?
//the current incorrect result is
ba.toHex() == "0102032c"
//the desired result is
ba.toHex() == "010203012c"
I've already tried this, but it just inserts the value as a string (4 bytes):
ba.append(QByteArray::number(number, 16));
What should I append to the QByteArray so both bytes of "number" get appended instead of just one byte? Also, the fastest method possible is preferred since this program needs to have great performance times. So absolutely no converting to QStrings.
Thanks for your time.
On its own, QByteArray only supports appending bytes; to append a big-endian representation of fixed-size integer types you can build your own operator<< (or what you prefer) overloads using the appropriate bit shifts:
QByteArray &operator<<(QByteArray &l, quint8 r)
{
l.append(r);
return l;
}
QByteArray &operator<<(QByteArray &l, quint16 r)
{
return l<<quint8(r>>8)<<quint8(r);
}
QByteArray &operator<<(QByteArray &l, quint32 r)
{
return l<<quint16(r>>16)<<quint16(r);
}
This allows you to write code like:
QByteArray b;
b<<quint16(300); // appends 0x01 0x2c
b<<quint8(4); // appends 0x04
b<<quint16(4); // appends 0x00 0x04
b<<quint32(123456); // appends 0x00 0x01 0xe2 0x40
b<<quint8(1)<<quin16(2)<<quint32(3); // appends 0x01 0x00 0x02 0x00 0x00 0x00 0x03
You should probably avoid writing
QByteArray b;
b<<1;
because in theory the output depends on the size of the current platform integer (although AFAIK on all platforms supported by Qt int is 32 bit).
Related
I'm going to receive length delimited frame from server with QTcpSocket in this format:-
+----------+--------------------------------+
| len: u32 | frame payload |
+----------+--------------------------------+
where u32 is in 32 bit unsigned integer encoded in Big-Endian format. depending on config, It can also be Little Endian But I will know about endianness beforehand on client side.
How can I convert first 4 bytes of this char* array or QByteArray to 32 bit unsigned int ?
After reading the frame I will need to send message in same format. So If I have message of length 20 bytes, How can I write 20 as 32 bit unsigned int to QByteArray or char* in Big/Little endian format ?
You can use Qt's QtEndian utility functions to accomplish that.
For example, if you have a quint32 (or uint32_t) and want to write that value in little-endian format to the first four bytes of a char array:
const quint32 myValue = 12345;
char frameBuffer[256];
qToLittleEndian(myValue, frameBuffer);
... or, alternatively, to read the first four bytes of a char array in as a little-endian quint32 (or uint32_t):
const char frameBuffer[256] = {0x01, 0x00, 0x00, 0x00};
quint32 myValue = qFromLittleEndian<quint32>(frameBuffer);
Big-endian reads/writes work the same, just replace the substring Little with Big in the function-names.
the way to do is:
bytearray -> stream ->setorder -> serialize to variable
QByteArray newData = ....;
QDataStream stream(newData);
stream.setByteOrder(QDataStream::LittleEndian); // or BigEndian
stream >> variable;
So I'm working on a large program that manipulates binary data stored in QByteArrays. Today, I realized that when I pass a QByteArray to a function by value, e.g.
void myFunc(QByteArray data)
{
// Do stuff
}
that if my QByteArray has a 0x0 somewhere in the middle of the binary stream, like 0xA5 0x20 0x04 0x00 0x52. The 0x52 gets chopped off when the QByteArray is passed into the function.
I imagine that I could simply pass the QByteArray by value into the function, but there are some cases where I need a copy of the array (for data persistence or adding to a queue, etc) and unfortunately, I have many, many lines of code where such functions are passing around copied QByteArrays.
So I'm wondering if there is any way to overload the assignment operator (or use some other method) to copy the data between two QByteArrays without treating the 0x00 items as null-terminated string characters. I'm trying to find the simplest approach to this problem without having to completely gut my program and redo all the functions. Is there a better alternative datatype I can use (where i can just do a quick Find/replace, or some function I can wrap them in?
UPDATE: Added stripped down example to demo problem
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QByteArray data = QByteArrayLiteral("\xA5\x20\x04\x00\x52");
doStuff(data);
return a.exec();
}
void doStuff(QByteArray theData)
{
// theData will contain only 0xA5 0x20 0x04 0x00 if you look from in here
}
UPDATE 2:
OK, apparently, after testing the above example. it actually DOES work. However, the real scenario I'm working with in a thread and I need to test more. Will update once I narrow the problem down more...
I dont think it has anything todo with passing the QByteArray by value but more like how you initialize your arrays. Consider this example:
QByteArray data("\xA5\x20\x04\x00\x52");
qDebug( data.toHex() );
QByteArray data2;
data2.append( static_cast<char>( 0xA5 ) );
data2.append( static_cast<char>( 0x20 ) );
data2.append( static_cast<char>( 0x04 ) );
data2.append( static_cast<char>( 0x00 ) );
data2.append( static_cast<char>( 0x52 ) );
qDebug( data2.toHex() );
The output is:
a52004
a520040052
Initializing it with a string it chops of at the 0-termination. This also holds true for QByteArray::toStdString().
EDIT:
As Frank pointed out:
QByteArray data("\xA5\x20\x04\x00\x52", 5);
qDebug( data.toHex() );
will indeed put out what is expected:
a520040052
I have an unsigned long long variable which I want to write to a binary file. However, I need to neglect all leading bytes which are zero.
This means
unsigned long long toWrite = 4;
should write 0x04 and not 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x04 to the stream.
#include <fstream>
int main(){
std::ofstream out("test.txt", std::ios::binary);
unsigned long long toWrite = 4;
out << cutoffZeroBytes(toWrite);
out.close();
return 1;
}
I was thinking about making cutoffZeroBytes a function which returns a char*. But if there are zero bytes in the middle (e.g. 0x03 0x00 0xf1), then I think I couldn't write it to the stream, since 0x00 determines the end of an char array.
I'm a little clueless here and need some help.
One way is using write for this aim.
So, just change:
out << cutoffZeroBytes(toWrite);
to:
out.write((char*)&toWrite, sizeof(toWrite));
And if you want to cut this number:
char* start = (char*)&toWrite;
int pi = sizeof(toWrite);
for (; pi > 0; pi--, start++)
if (*start)
break;
out.write(start, pi);
I am trying to save a raw byte array into a file:
mDataStream.writeRawData( ( (const char *)&testPacket), 188);
The test packet is just an array of unsigned char, the packet is copied in the right size, but the bytes are reordered. ie: 0x47 0x00 0x10 0x20 ... becomes 0x00 0x47 ox20 0x10.
This looks like an endianness problem, but i've tried setting the byte order to little endian with unsuccessful results.
I've to read data from binary file.
This binary data format is:
0x00 0x00 0x01 - is delimiter
after this delimiter there is raw data byte array.
So, to sum up, my binary file looks like:
0x00 0x00 0x01 (here is raw data byte)
0x00 0x00 0x01 (here is another block
of raw data bytes) 0x00 0x00 0x01 ....
So i've wrote such code to parse my file (I'm not very familiar with C)
ifstream inp("myfile.bin",ios::binary);
char b1, b2, b3;
while (!inp.eof())
{
inp.read(&b1,sizeof(b1));
inp.read(&b2,sizeof(b2));
inp.read(&b3,sizeof(b3));
//finding first delimiter (data starts from delimiter)
while (!((0==b1)&&(0==b2)&&(1==b3)))
{
b1=b2;
b2=b3;
if (inp.eof())
break;
inp.read(&b3,sizeof(b3));
}
if (inp.eof())
break;
char* raw=new char[65535];
int rawSize=0;
inp.read(&b1,sizeof(b1));
inp.read(&b2,sizeof(b2));
inp.read(&b3,sizeof(b3));
raw[rawSize++]=b1;
raw[rawSize++]=b2;
if (inp.eof())
break;
//reading raw data until delimiter is found
while (!((0==b1)&&(0==b2)&&(1==b3)))
{
raw[rawSize++]=b3;
b1=b2;
b2=b3;
if (inp.eof())
break;
inp.read(&b3,sizeof(b3));
}
rawSize-=2; //because of two bytes of delimiter (0x00 0x00) would be added to raw
//Do something with raw data
if (inp.eof())
break;
inp.putback(1);
inp.putback(0);
inp.putback(0);
delete []raw;
}
But sometimes this code falls into infinite loop.
Could you advice me something?
Thanks
I think the problem there is that putback fails. As far as i recall, putback is guaranteed to work only once; second invocation will fail if the internal read buffer is aligned (that is, very rarely; seems like your situation).
To fix, get rid of putback. First of all, move the loop commented as "finding first delimiter" out of the outer while loop: the comment suggests that this code should only run once. After you do it, pay attention that at the beginning of the outer while loop, the sequence 0x00 0x00 0x01 has just been found, so the code doesn't have to use putback and look for it again.
You're using feof() wrong, it's only valid after a read has been attempted and failed.
How do you know that your magic byte sequence 0 0 1 doesn't appear inside the data? If the data is just a "binary array" that doesn't sound like it provides much of a guarantee ...