Does QVariant expect a null-terminated QByteArray? - qt

I need to construct a QVariant object from a QByteArray. Does the QVariant constructor QVariant(const QByteArray & val) expect the raw bytes in the QByteArray to be null-terminated?
Background info:
My QByteArray object was constructed with QByteArray::fromRawData(const char * data, int size) and is not null-terminated.
My objective is to copy the raw bytes in my QByteArray object into a database with QSqlQuery::bindValue() which expects a Qvariant object as one of its arguments.
My guess is the the bytes in QByteArray must be null-terminated. I don't see any other way how QVariant(const QByteArray & val) constructor could be implemented.
Many thanks!

The bytes added in QByteArray may or may not be 0-terminated. If you convert QByteArray to QVariant, it will contain all bytes that have been added to the array regardless of their values. It's possible because QByteArray stores bytes count. When you have char* pointer and byte count, you can safely operate with bytes without knowing if there are 0s there or not.
Actually, when you call data() to get char* pointer, the data is always null-terminated. But it really is behind the scenes. If you use data() and pass that pointer to some function without size, 0-termination will protect you from segfault. But it is not important in your question.
I'll try to illustrate that using example.
Raw data: 1 2 3 4 ....
calling fromRawData(pointer, 4)
QByteArray contents: 1 2 3 4
Underlying QByteArray's buffer contents: 1 2 3 4 0 ...
Raw data: 1 2 3 0 ....
calling fromRawData(pointer, 4)
QByteArray contents: 1 2 3 0
Underlying QByteArray's buffer contents: 1 2 3 0 ...
Raw data: 1 0 2 3
calling fromRawData(pointer, 4)
QByteArray contents: 1 0 2 3
Underlying QByteArray's buffer contents: 1 0 2 3 0 ...
And QVariant will contain the same bytes as "QByteArray contents".

Edited for newcomers. Look at comments.
From docs:
QByteArray can be used to store both raw bytes (including '\0's) and
traditional 8-bit '\0'-terminated strings. Using QByteArray is much
more convenient than using const char *. Behind the scenes, it always
ensures that the data is followed by a '\0' terminator
It means, even if you dont make it null terminated, QByteArray will ensure it. But only when it creates a deep copy... Check size()
int QByteArray::size() const
Returns the number of bytes in this byte
array. The last byte in the byte array is at position size() - 1. In
addition, QByteArray ensures that the byte at position size() is
always '\0', so that you can use the return value of data() and
constData() as arguments to functions that expect '\0'-terminated
strings. If the QByteArray object was created from a raw data that
didn't include the trailing null-termination character then QByteArray
doesn't add it automaticall unless the deep copy is created.
As prolly QVariant will use the constData() method, you cant be sure it will be null terminated. Look at this gist

Related

How can a 1 byte int conversion of a QByteArray fail?

So here is the thing, I'm receiving 1 byte from Bluetooth transmission. When using QDebug I get this message:
The array with error has "\x06"
The line that fails is this:
bool ok = true;
int v = value.toInt(&ok,0);
Because ok has false. But I'm trying to wrap my head around the fact that, How can the conversion fail in the first place if the data represented in that byte (as a sequence of zeros and ones) will always have a valid integer representation. (one byte can always be represented as a int between -127 and 128). So I'm left with the question, how can the conversion fail?
Reading the documentation does not provide many clues as it does not say how the byte array will be interpreted.
QByteArray::toInt converts a string representation in the default C locale to an integer. That means to successfully convert the value in your example, your byte array must contain the string "0x06", which consists of 4 bytes.
To convert a single byte to an int, just extract it:
int i = value[0];
Type promotion will widen the char to an int

Does it make sense to store byte values in Map or it will still use 4 bytes?

In Java in-memory there is no difference between byte or int - both will be represented as 4 bytes.
Does for Chronicle Map the difference exist, i.e. does Chronicle Map store byte values as 8 bits or still use 32?
Same question if byte is an object property.
In primitive map implementations (fastutil, koloboke, gs, hppc) byte values are implemented as a separate byte[] array, so they actually take only 1 byte. If a byte is a field of another on-heap Java object (which is a Map value), indeed, the object size is rounded up to 8-byte boundary, so a single byte field could "take" 8 bytes. But more often, it "takes" 0 bytes, because the field is placed in the already existing alignment holes.
For Chronicle Map, a value could freely be 1 byte in size. (And even 0 bytes, this is how ChronicleSet is currently implmeneted -- a ChronicleMap with 0-byte dummy values.) This is true for all Chronicle Map versions (2, 3).
Edit -- answer to the comment.
If you have a constantly sized structure e. g. 6 byte fields, easiest and efficient way - to use data value generation mechanishm:
interface MyValue {
byte getA(); void setA(byte a);
byte getB(); void setB(byte b);
byte getC(); void setC(byte c);
byte getD(); void setD(byte d);
byte getE(); void setE(byte e);
byte getF(); void setF(byte f);
}
map = ChronicleMapBuilder.of(Key.class, MyValue.class).entries(1000).create();
// Chronicle Map 2 syntax
MyValue value = DataValueClasses.newDirectReference(MyValue.class);
try (Closeable handle = map.getUsingLocked(key, value)) {
// access the value here
System.out.println(value);
}
// Chronicle Map 3 syntax
try (ExternalMapQueryContext<Key, MyValue, ?> q = map.queryContext(key)) {
// if not sure the key is present in the map, check q.entry() != null
MyValue value = q.entry().value().get();
// access the value here
System.out.println(value);
}
It will take exactly 6 bytes per value.
I think I know the response. At least at the version 2.3.8 offheap value will be 1 byte for a byte (work done in SerializationBuilder class).

Qt QByteArray issue

I've tried the following:
qDebug() << QByteArray("\x00\x10\x00\x00").size();
and i get 0 instead of 4 witch i would espect.
What would be a good data type to hold this 4 bytes of data as i need to later write them to a socket so they must remain exactly like you see them above?
The constructor QByteArray(const char* str) uses qstrlen on the argument. Since your string starts with a 0x00 byte, qstrlen returns 0, thus the resulting QByteArray is 0 bytes long.
To avoid the qstrlen check, use the QByteArray(const char* str, int size) constructor:
qDebug() << QByteArray("\x00\x10\x00\x00", 4).size();
will print 4 as you expect.

Is there a way to receive data as unsigned char over UDP on Qt?

I need to send floating point numbers using a UDP connection to a Qt application. Now in Qt the only function available is
qint64 readDatagram ( char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0 )
which accepts data in the form of signed character buffer. I can convert my float into a string and send it but it will obviously not be very efficient converting a 4 byte float into a much longer sized character buffer.
I got hold of these 2 functions to convert a 4 byte float into an unsinged 32 bit integer to transfer over network which works fine for a simple C++ UDP program but for Qt I need to receive the data as unsigned char.
Is it possible to avoid converting the floatinf point data into a string and then sending it?
uint32_t htonf(float f)
{
uint32_t p;
uint32_t sign;
if (f < 0) { sign = 1; f = -f; }
else { sign = 0; }
p = ((((uint32_t)f)&0x7fff)<<16) | (sign<<31); // Whole part and sign.
p |= (uint32_t)(((f - (int)f) * 65536.0f))&0xffff; // Fraction.
return p;
}
float ntohf(uint32_t p)
{
float f = ((p>>16)&0x7fff); // Whole part.
f += (p&0xffff) / 65536.0f; // Fraction.
if (((p>>31)&0x1) == 0x1) { f = -f; } // Sign bit set.
return f;
}
Have you tried using readDatagram? Or converting the data to a QByteArray after reading? In many cases a char* is really just a byte array. This is one of those cases. Note that the writeDatagram can take a QByteArray.
Generally every thing sent across sockets is in bytes not strings, layers on either end do the conversions. Take a look here, especially the Broadcaster examples. They show how to create a QByteArray for broadcast and receive.
Not sure why the downvote, since the question is vague in requirements.
A 4-byte float is simply a 4 character buffer, if cast as one. If the systems are homogenous, the float can be sent as a signed char *, and bit for bit it'll be the same read into the signed char * on the receiver directly, no conversion needed. If the systems are heterogenous, then this won't work and you need to convert it to a portable format, anyway. IEEE format is often used, but my question is still, what are the requirements, is the float format the same between systems?
If I read it correctly, your primary question seems to be how to receive data of type unsigned char with QT's readDatagram function which uses a pointer to a buffer of type char.
The short answer is use a cast along these lines:
const size_t MAXSIZE = 1024;
unsigned char* data = malloc(MAXSIZE);
readDatagram ( (unsigned char *)data, MAXSIZE, address, port )
I'm going to assume you have multiple machines which use the same IEEE floating point format but some of which are big endian and some of which are little endian. See this SO post for a good discussion of this issue.
In that case you could do something a bit simpler like this:
const size_t FCOUNT = 256;
float* data = malloc(FCOUNT * sizeof(*data));
readDatagram ( (char *)data, FCOUNT * sizeof(*data), address, port )
for (int i = 0; i != FCOUNT; ++i)
data[i] = ntohf(*((uint32_t*)&data[i]));
The thing to remember is that as far as networking functions like readDatagram are concerned, the data is just a bunch of bits and it doesn't care what type those bits are interpreted as.
If both ends of your UDP connection use Qt, I would suggest looking at QDataStream. You can create this from a QByteArray each time you read a datagram, and then read whatever values you require - floats, maps, lists, QVariants, and of course string.
Similarly, on the sending side, you'd create a data stream, push data into it, then send the resulting QByteArray over writeDatagram.
Obviously this only works if both ends use Qt - the data encoding is well-defined, but non-trivial to generate by hand.
(If you want stream orientated behaviour, you could use the fact that QUDPSocket is a QIODevice with a data-stream, but it sounds as if you want per-datagram behaviour)

QByteArray to integer

As you may have figured out from the title, I'm having problems converting a QByteArray to an integer.
QByteArray buffer = server->read(8192);
QByteArray q_size = buffer.mid(0, 2);
int size = q_size.toInt();
However, size is 0. The buffer doesn't receive any ASCII character and I believe the toInt() function won't work if it's not an ASCII character. The int size should be 37 (0x25), but - as I have said - it's 0.
The q_size is 0x2500 (or the other endianness order - 0x0025).
What's the problem here ? I'm pretty sure q_size holds the data I need.
Something like this should work, using a data stream to read from the buffer:
QDataStream ds(buffer);
short size; // Since the size you're trying to read appears to be 2 bytes
ds >> size;
// You can continue reading more data from the stream here
The toInt method parses a int if the QByteArray contains a string with digits. You want to interpret the raw bits as an integer. I don't think there is a method for that in QByteArray, so you'll have to construct the value yourself from the single bytes. Probably something like this will work:
int size = (static_cast<unsigned int>(q_size[0]) & 0xFF) << 8
+ (static_cast<unsigned int>(q_size[1]) & 0xFF);
(Or the other way around, depending on Endianness)
I haven't tried this myself to see if it works but it looks from the Qt docs like you want a QDataStream. This supports extracting all the basic C++ types and can be created wth a QByteArray as input.
bool ok;
q_size.toHex().toInt(&ok, 16);
works for me
I had great problems in converting serial data (QByteArray) to integer which was meant to be used as the value for a Progress Bar, but solved it in a very simple way:
QByteArray data = serial->readall();
QString data2 = tr(data); //converted the byte array to a string
ui->QProgressBar->setValue(data2.toUInt()); //converted the string to an unmarked integer..
This works for me:
QByteArray array2;
array2.reserve(4);
array2[0] = data[1];
array2[1] = data[2];
array2[2] = data[3];
array2[3] = data[4];
memcpy(&blockSize, array2, sizeof(int));
data is a qbytearray, from index = 1 to 4 are array integer.
Create a QDataStream that operates on your QByteArray. Documentation is here
Try toInt(bool *ok = Q_NULLPTR, int base = 10) const method of QByteArray Class.
QByteArray Documentatio: http://doc.qt.io/qt-5/QByteArray.html

Resources