Custom IO Device: Difference between revisions
No edit summary |
No edit summary |
||
Line 1: | Line 1: | ||
[[Category:Snippets]] | |||
'''English''' | [[Custom_IO_Device_German|German]] | |||
[toc align_right="yes" depth="2"] | |||
==Usage:== | = Writing a Custom I/O Device = | ||
This is a port of the article in "Qt Quarterly 12 about writing a custom QIODevice":http://doc.qt.nokia.com/qq/qq12-iodevice.html | |||
* 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 | <code><br /> QFile file&amp;#40;"output.dat&quot;&#41;;<br /> CryptDevice cryptDevice(&file)<br /> QTextStream out(&cryptDevice);<br /> cryptDevice.open(QIODevice::WriteOnly);<br /> out << "Hello World&quot;;<br /></code> | ||
And on the possible usage (in our example code in git "qtdevnet-wiki-mvc/qtdevnet-custom-iodevice&quot;:http://www.gitorious.org/qtdevnet-wiki-mvc/qtdevnet-custom-iodevice) | |||
&#32; | |||
=== Encryption === | |||
<code><br /> QByteArray dataArray; | |||
QBuffer bufferUsedLikeAFile&amp;#40;&dataArray&amp;#41;;<br /> CryptDevice deviceFilter(&bufferUsedLikeAFile);<br /> deviceFilter.open(QIODevice::WriteOnly);<br /> QTextStream stream(&deviceFilter);<br /> QString szText = rawText->toPlainText();<br /> stream << szText;<br /></code> | |||
&#32; | |||
=== | === Decryption === | ||
= | <code><br /> QBuffer bufferUsedLikeAFile&amp;#40;&dataArray&amp;#41;;<br /> CryptDevice deviceFilter(&bufferUsedLikeAFile);<br /> deviceFilter.open(QIODevice::ReadOnly);<br /> QTextStream stream(&deviceFilter);<br /> QString szText = stream.readAll();<br /> decryptedText->setPlainText(szText);<br /></code> | ||
Example image of the test app: | Example image of the test app: | ||
[[Image:CustomIoDevice.jpg|Test app]] | [[Image:https://lh3.googleusercontent.com/_m1PNLlZctqY/TYpctkjGvTI/AAAAAAAAAFI/BjZnvJhatRY/s800/CustomIoDevice.jpg|Test app]] | ||
==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><br />class CryptDevice : public QIODevice<br />{<br /> Q_OBJECT<br />public:<br /> CryptDevice(QIODevice* deviceToUse, QObject* parent = 0);<br /> bool open(OpenMode mode);<br /> void close();<br /> bool isSequential() const;<br />protected:<br /> qint64 readData(char* data, qint64 maxSize);<br /> qint64 writeData(const char* data, qint64 maxSize);<br />private:<br /> QIODevice* underlyingDevice;<br /> Q_DISABLE_COPY(CryptDevice)<br />};<br /></code> | |||
The constructor definition is pretty straightforward | The constructor definition is pretty straightforward | ||
<code><br />CryptDevice::CryptDevice(QIODevice* deviceToUse, QObject* parent) :<br /> QIODevice(parent),<br /> underlyingDevice(deviceToUse)<br />{<br />}<br /></code> | |||
As our device should be sequential, we re-implement isSequential<br /><code><br />bool CryptDevice::isSequential() const<br />{<br /> return true;<br />}<br /></code> | |||
In <code&gt;open()</code&gt;, we open the underlying device if it's not already open and set the device state to ''mode''. | |||
<code><br />bool CryptDevice::open(OpenMode mode)<br />{<br /> bool underlyingOk;<br /> if (underlyingDevice->isOpen())<br /> underlyingOk = (underlyingDevice->openMode() != mode);<br /> else<br /> underlyingOk = underlyingDevice->open(mode); | |||
if (underlyingOk)<br /> {<br /> setOpenMode(mode);<br /> return true;<br /> }<br /> return false;<br />}<br /></code> | |||
Closing is trivial. | Closing is trivial. | ||
<code><br />void CryptDevice::close()<br />{<br /> underlyingDevice->close();<br /> setOpenMode(NotOpen);<br />}<br /></code> | |||
When reading a block, we call <code&gt;read()</code&gt; on the underlying device. At the end, we XOR each byte read from the device with the magic constant 0x5E. | |||
* [[: | <code><br />qint64 CryptDevice::readData(char* data, qint64 maxSize)<br />{<br /> qint64 deviceRead = underlyingDevice->read(data, maxSize);<br /> if (deviceRead == –1)<br /> return <s>1;<br /> for (qint64 i = 0; i < deviceRead; +''i)<br /> data[i] = data[i] ^ 0x5E; | ||
<br /> return deviceRead;<br />}<br /></code> | |||
<br />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&gt;write()</code&gt; multiple times if size is larger than the buffer. | |||
<br /><code><br />qint64 CryptDevice::writeData(const char* data, qint64 maxSize)<br />{<br /> QByteArray buffer((int)maxSize, 0);<br /> for (int i = 0; i < (int)maxSize;''+i)<br /> buffer[i] = data[i] ^ 0x5E;<br /> return underlyingDevice</s>>write(buffer.data(), maxSize);<br />}<br /></code> |
Revision as of 09:51, 24 February 2015
English | German
[toc align_right="yes" depth="2"]
Writing a Custom I/O Device
This is a port of the article in "Qt Quarterly 12 about writing a custom QIODevice":http://doc.qt.nokia.com/qq/qq12-iodevice.html
- 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:
<br /> QFile file&amp;#40;"output.dat&quot;&#41;;<br /> CryptDevice cryptDevice(&file)<br /> QTextStream out(&cryptDevice);<br /> cryptDevice.open(QIODevice::WriteOnly);<br /> out << "Hello World&quot;;<br />
And on the possible usage (in our example code in git "qtdevnet-wiki-mvc/qtdevnet-custom-iodevice":http://www.gitorious.org/qtdevnet-wiki-mvc/qtdevnet-custom-iodevice)
 
Encryption
<br /> QByteArray dataArray;
QBuffer bufferUsedLikeAFile&amp;#40;&dataArray&amp;#41;;<br /> CryptDevice deviceFilter(&bufferUsedLikeAFile);<br /> deviceFilter.open(QIODevice::WriteOnly);<br /> QTextStream stream(&deviceFilter);<br /> QString szText = rawText->toPlainText();<br /> stream << szText;<br />
 
Decryption
<br /> QBuffer bufferUsedLikeAFile&amp;#40;&dataArray&amp;#41;;<br /> CryptDevice deviceFilter(&bufferUsedLikeAFile);<br /> deviceFilter.open(QIODevice::ReadOnly);<br /> QTextStream stream(&deviceFilter);<br /> QString szText = stream.readAll();<br /> decryptedText->setPlainText(szText);<br />
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:
<br />class CryptDevice : public QIODevice<br />{<br /> Q_OBJECT<br />public:<br /> CryptDevice(QIODevice* deviceToUse, QObject* parent = 0);<br /> bool open(OpenMode mode);<br /> void close();<br /> bool isSequential() const;<br />protected:<br /> qint64 readData(char* data, qint64 maxSize);<br /> qint64 writeData(const char* data, qint64 maxSize);<br />private:<br /> QIODevice* underlyingDevice;<br /> Q_DISABLE_COPY(CryptDevice)<br />};<br />
The constructor definition is pretty straightforward
<br />CryptDevice::CryptDevice(QIODevice* deviceToUse, QObject* parent) :<br /> QIODevice(parent),<br /> underlyingDevice(deviceToUse)<br />{<br />}<br />
As our device should be sequential, we re-implement isSequential
<br />bool CryptDevice::isSequential() const<br />{<br /> return true;<br />}<br />
In <code>open()</code>, we open the underlying device if it's not already open and set the device state to mode.
<br />bool CryptDevice::open(OpenMode mode)<br />{<br /> bool underlyingOk;<br /> if (underlyingDevice->isOpen())<br /> underlyingOk = (underlyingDevice->openMode() != mode);<br /> else<br /> underlyingOk = underlyingDevice->open(mode);
if (underlyingOk)<br /> {<br /> setOpenMode(mode);<br /> return true;<br /> }<br /> return false;<br />}<br />
Closing is trivial.
<br />void CryptDevice::close()<br />{<br /> underlyingDevice->close();<br /> setOpenMode(NotOpen);<br />}<br />
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.
<br />qint64 CryptDevice::readData(char* data, qint64 maxSize)<br />{<br /> qint64 deviceRead = underlyingDevice->read(data, maxSize);<br /> if (deviceRead == –1)<br /> return <s>1;<br /> for (qint64 i = 0; i < deviceRead; +''i)<br /> data[i] = data[i] ^ 0x5E;
<br /> return deviceRead;<br />}<br />
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.
<br />qint64 CryptDevice::writeData(const char* data, qint64 maxSize)<br />{<br /> QByteArray buffer((int)maxSize, 0);<br /> for (int i = 0; i < (int)maxSize;''+i)<br /> buffer[i] = data[i] ^ 0x5E;<br /> return underlyingDevice</s>>write(buffer.data(), maxSize);<br />}<br />