Working with Raw Data

Qt’s powerful and complex data types are one of the large assets available to all developers using Qt. For instance, even the basic QString class provides UNICODE support, implicit sharing as well as a quite pleasant programmer’s interface to text strings. In some circumstances, this is not what you want. Sometimes you find yourself needing to control individual bits and bytes.

Working with Bytes

The QByteArray class provides a container for generic bytes. It can be modified in many ways – be populated and read from. You can even compress and decompress these arrays of bytes using the qCompress and qUncompress functions.

For the little C programmer that lives inside of every C++ developer, you can use the qstrcmp, qstrcpy, qstrdup and friends in combination with QByteArrays. In order to access the data of the array as a char*, use the QByteArray::constData and QByteArray::data functions. As always when dealing with this type of memory accesses and modification – make sure to take great care not to mess things up. One mistake can lead to segfaults or worse (buffer overflow attacks and other things that will keep you up at night).

For a safer way to read and write data stored in QByteArrays, you can use a QBuffer object. A QBuffer object basically exposes a QByteArray as a QIODevice. This makes it possible to access the byte array using QDataStream and QTextStream. I.e. you get cross platform compatibility, while still having access to the raw bytes.

Working with Bits

A QBitArray represents an array of individual bits. You can test and modify each bit individually using the operator[], thus avoiding having to use bit shifts and complex masks combined with logical operators. The bit array can also contain an arbitrary number of bits (limited by the fact that the size function returns an int), which makes it quite convenient compared to accessing individual bits in an array of bytes.

Converting Bits to Bytes (and back again)

One inconvenient part about the QBitArray class is that it is hard to convert from and to it. It can be stored and retreived from QDataStreams, but it is harder to get the bytes out of the bits and vice versa.

However, it does not have to be that hard. With some bit-shifting and a logical AND operation, it is easy to convert bytes into bits. The snippet below converts the QByteArray bytes into the QBitArray bits.

  1. QByteArray bytes = ...;
  2.  
  3. // Create a bit array of the appropriate size
  4. QBitArray bits(bytes.count()*8);
  5.  
  6. // Convert from QByteArray to QBitArray
  7. for(int i=0; i<bytes.count(); ++i)
  8.     for(int b=0; b<8; ++b)
  9.         bits.setBit(i*8+b, bytes.at(i)&(1<<(7-b)));

Converting bits back into bytes is a similar operation. However, you must take into account that the number of bits must be a multiple of eight to complete the last byte. Not having a multiple of eight, simply means that the last byte will not be completely filled with bits, regardless of the contents of the QBitArray. In certain situations it might be valuable to fill the remains of the last byte with the value of the last bit, but I digress.

The code snippet below converts the QBitArray bits into the QByteArray bytes. The inner loop is not quite optimal, as it relies on dividing the iteration variable, b, by eight. Modern compilers handle this well, but it could also have been acheived using bit-shifts for optimum performance on all platforms. However, this is left as an exercise for the curious, as it would hamper the readability of the code.

  1. QBitArray bits = ...;
  2.  
  3. // Resulting byte array
  4. QByteArray bytes;
  5.  
  6. // Convert from QBitArray to QByteArray
  7. for(int b=0; b<bits.count(); ++b)
  8.     bytes[b/8] = (bytes.at(b/8) | ((bits[b]?1:0)<<(b%8)));

Categories: