Custom IO Device: Difference between revisions
No edit summary |
Henri Vikki (talk | contribs) No edit summary |
||
(6 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
[[Category:Snippets]] | |||
==Usage:== | '''English''' | [[Custom_IO_Device_German|German]] | ||
= Writing a Custom I/O Device = | |||
This is a port of the article in [http://doc.qt.io/qt-5/qiodevice.html Qt Quarterly 12 about writing a custom QIODevice] | |||
* Note: a more complex example can be found here [[Simple_Crypt_IO_Device]] | |||
== Usage: == | |||
The following code snippet shows how we would use the custom I/O device to encrypt data and store the result in a file: | The following code snippet shows how we would use the custom I/O device to encrypt data and store the result in a file: | ||
And on the possible usage (in our example code in git [http:// | <code> | ||
QFile file("output.dat"); | |||
CryptDevice cryptDevice(&file) | |||
QTextStream out(&cryptDevice); | |||
cryptDevice.open(QIODevice::WriteOnly); | |||
out << "Hello World"; | |||
</code> | |||
And on the possible usage (in our example code in git [http://code.qt.io/cgit/%7bnon-gerrit%7d/qt-labs/devnet-examples.git/tree/]) | |||
=== Encryption === | |||
<code> | |||
QByteArray dataArray; | |||
= | QBuffer bufferUsedLikeAFile(&dataArray); | ||
CryptDevice deviceFilter(&bufferUsedLikeAFile); | |||
deviceFilter.open(QIODevice::WriteOnly); | |||
QTextStream stream(&deviceFilter); | |||
QString szText = rawText->toPlainText(); | |||
stream << szText; | |||
</code> | |||
===Decryption=== | |||
=== Decryption === | |||
<code> | |||
QBuffer bufferUsedLikeAFile(&dataArray); | |||
CryptDevice deviceFilter(&bufferUsedLikeAFile); | |||
deviceFilter.open(QIODevice::ReadOnly); | |||
QTextStream stream(&deviceFilter); | |||
QString szText = stream.readAll(); | |||
decryptedText->setPlainText(szText); | |||
</code> | |||
Example image of the test app: | Example image of the test app: | ||
https://lh3.googleusercontent.com/_m1PNLlZctqY/TYpctkjGvTI/AAAAAAAAAFI/BjZnvJhatRY/s800/CustomIoDevice.jpg | |||
==The Custom I/O Device== | == The Custom I/O Device == | ||
Writing a custom | Writing a custom QIODevice class in Qt 4 involves inheriting QIODevice and then reimplementing a set of virtual functions. | ||
There is a big difference regarding writing a custom IO device compared to Qt 3: you only have to rewrite 2 functions: | There is a big difference regarding writing a custom IO device compared to Qt 3: you only have to rewrite 2 functions: | ||
* qint64 | * qint64 QIODevice::readData ( char * data, qint64 maxSize ) | ||
* qint64 | * qint64 QIODevice::writeData ( const char * data, qint64 maxSize ) | ||
Our CryptDevice class will be a sequential I/O device. Whether | Our CryptDevice class will be a sequential I/O device. Whether it's synchronous or asynchronous depends on the underlying QIODevice. | ||
==Source Code== | == Source Code == | ||
The class definition looks like this: | The class definition looks like this: | ||
<code> | |||
class CryptDevice : public QIODevice | |||
{ | |||
Q_OBJECT | |||
public: | |||
CryptDevice(QIODevice* deviceToUse, QObject* parent = 0); | |||
bool open(OpenMode mode); | |||
void close(); | |||
bool isSequential() const; | |||
protected: | |||
qint64 readData(char* data, qint64 maxSize); | |||
qint64 writeData(const char* data, qint64 maxSize); | |||
private: | |||
QIODevice* underlyingDevice; | |||
Q_DISABLE_COPY(CryptDevice) | |||
}; | |||
</code> | |||
The constructor definition is pretty straightforward | The constructor definition is pretty straightforward | ||
<code> | |||
CryptDevice::CryptDevice(QIODevice* deviceToUse, QObject* parent) : | |||
QIODevice(parent), | |||
underlyingDevice(deviceToUse) | |||
{ | |||
} | |||
</code> | |||
In <code>open()</code>, we open the underlying device if | As our device should be sequential, we re-implement isSequential | ||
<code> | |||
bool CryptDevice::isSequential() const | |||
{ | |||
return true; | |||
} | |||
</code> | |||
In <code>open()</code>, we open the underlying device if it's not already open and set the device state to ''mode''. | |||
<code> | |||
bool CryptDevice::open(OpenMode mode) | |||
{ | |||
bool underlyingOk; | |||
if (underlyingDevice->isOpen()) | |||
underlyingOk = (underlyingDevice->openMode() != mode); | |||
else | |||
underlyingOk = underlyingDevice->open(mode); | |||
if (underlyingOk) | |||
{ | |||
setOpenMode(mode); | |||
return true; | |||
} | |||
return false; | |||
} | |||
</code> | |||
Closing is trivial. | Closing is trivial. | ||
When reading a block, we call <code>read()</code> on the underlying device. At the end, we | <code> | ||
void CryptDevice::close() | |||
{ | |||
underlyingDevice->close(); | |||
setOpenMode(NotOpen); | |||
} | |||
</code> | |||
When reading a block, we call <code>read()</code> on the underlying device. At the end, we XOR each byte read from the device with the magic constant 0x5E. | |||
<code> | |||
qint64 CryptDevice::readData(char* data, qint64 maxSize) | |||
{ | |||
qint64 deviceRead = underlyingDevice->read(data, maxSize); | |||
if (deviceRead == –1) | |||
return -1; | |||
for (qint64 i = 0; i < deviceRead; +''i) | |||
data[i] = data[i] ^ 0x5E; | |||
return deviceRead; | |||
} | |||
</code> | |||
When writing a block, we create a temporary buffer with the XOR'd data. A more efficient implementation would use a 4096-byte buffer on the stack and call <code>write()</code> multiple times if size is larger than the buffer. | |||
* [[ | <code> | ||
qint64 CryptDevice::writeData(const char* data, qint64 maxSize) | |||
{ | |||
QByteArray buffer((int)maxSize, 0); | |||
for (int i = 0; i < (int)maxSize;''+i) | |||
buffer[i] = data[i] ^ 0x5E; | |||
return underlyingDevice->write(buffer.data(), maxSize); | |||
} | |||
</code> |
Latest revision as of 12:10, 30 March 2015
English | German
Writing a Custom I/O Device
This is a port of the article in Qt Quarterly 12 about writing a custom QIODevice
- Note: a more complex example can be found here Simple_Crypt_IO_Device
Usage:
The following code snippet shows how we would use the custom I/O device to encrypt data and store the result in a file:
QFile file("output.dat");
CryptDevice cryptDevice(&file)
QTextStream out(&cryptDevice);
cryptDevice.open(QIODevice::WriteOnly);
out << "Hello World";
And on the possible usage (in our example code in git [1])
Encryption
QByteArray dataArray;
QBuffer bufferUsedLikeAFile(&dataArray);
CryptDevice deviceFilter(&bufferUsedLikeAFile);
deviceFilter.open(QIODevice::WriteOnly);
QTextStream stream(&deviceFilter);
QString szText = rawText->toPlainText();
stream << szText;
Decryption
QBuffer bufferUsedLikeAFile(&dataArray);
CryptDevice deviceFilter(&bufferUsedLikeAFile);
deviceFilter.open(QIODevice::ReadOnly);
QTextStream stream(&deviceFilter);
QString szText = stream.readAll();
decryptedText->setPlainText(szText);
Example image of the test app:
The Custom I/O Device
Writing a custom QIODevice class in Qt 4 involves inheriting QIODevice and then reimplementing a set of virtual functions.
There is a big difference regarding writing a custom IO device compared to Qt 3: you only have to rewrite 2 functions:
- qint64 QIODevice::readData ( char * data, qint64 maxSize )
- qint64 QIODevice::writeData ( const char * data, qint64 maxSize )
Our CryptDevice class will be a sequential I/O device. Whether it's synchronous or asynchronous depends on the underlying QIODevice.
Source Code
The class definition looks like this:
class CryptDevice : public QIODevice
{
Q_OBJECT
public:
CryptDevice(QIODevice* deviceToUse, QObject* parent = 0);
bool open(OpenMode mode);
void close();
bool isSequential() const;
protected:
qint64 readData(char* data, qint64 maxSize);
qint64 writeData(const char* data, qint64 maxSize);
private:
QIODevice* underlyingDevice;
Q_DISABLE_COPY(CryptDevice)
};
The constructor definition is pretty straightforward
CryptDevice::CryptDevice(QIODevice* deviceToUse, QObject* parent) :
QIODevice(parent),
underlyingDevice(deviceToUse)
{
}
As our device should be sequential, we re-implement isSequential
bool CryptDevice::isSequential() const
{
return true;
}
In
open()
, we open the underlying device if it's not already open and set the device state to mode.
bool CryptDevice::open(OpenMode mode)
{
bool underlyingOk;
if (underlyingDevice->isOpen())
underlyingOk = (underlyingDevice->openMode() != mode);
else
underlyingOk = underlyingDevice->open(mode);
if (underlyingOk)
{
setOpenMode(mode);
return true;
}
return false;
}
Closing is trivial.
void CryptDevice::close()
{
underlyingDevice->close();
setOpenMode(NotOpen);
}
When reading a block, we call
read()
on the underlying device. At the end, we XOR each byte read from the device with the magic constant 0x5E.
qint64 CryptDevice::readData(char* data, qint64 maxSize)
{
qint64 deviceRead = underlyingDevice->read(data, maxSize);
if (deviceRead == –1)
return -1;
for (qint64 i = 0; i < deviceRead; +''i)
data[i] = data[i] ^ 0x5E;
return deviceRead;
}
When writing a block, we create a temporary buffer with the XOR'd data. A more efficient implementation would use a 4096-byte buffer on the stack and call
write()
multiple times if size is larger than the buffer.
qint64 CryptDevice::writeData(const char* data, qint64 maxSize)
{
QByteArray buffer((int)maxSize, 0);
for (int i = 0; i < (int)maxSize;''+i)
buffer[i] = data[i] ^ 0x5E;
return underlyingDevice->write(buffer.data(), maxSize);
}