Custom IO Device: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
No edit summary
Line 1: Line 1:
=Writing a Custom I/O Device=
[[Category:Snippets]]


This is a port of the article in [http://doc.qt.nokia.com/qq/qq12-iodevice.html Qt Quarterly 12 about writing a custom <span class="caps">QIOD</span>evice] ''[doc.qt.nokia.com]''
'''English''' | [[Custom_IO_Device_German|German]]


* Note: a more complex example can be found here [[Simple Crypt IO Device|Simple_Crypt_IO_Device]]
[toc align_right=&quot;yes&amp;quot; depth=&quot;2&amp;quot;]


==Usage:==
= Writing a Custom I/O Device =
 
This is a port of the article in &quot;Qt Quarterly 12 about writing a custom QIODevice&amp;quot;: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 [http://www.gitorious.org/qtdevnet-wiki-mvc/qtdevnet-custom-iodevice qtdevnet-wiki-mvc/qtdevnet-custom-iodevice] ''[gitorious.org]'')
<code><br /> QFile file&amp;amp;#40;&quot;output.dat&amp;quot;&amp;#41;;<br /> CryptDevice cryptDevice(&amp;file)<br /> QTextStream out(&amp;cryptDevice);<br /> cryptDevice.open(QIODevice::WriteOnly);<br /> out &lt;&lt; &quot;Hello World&amp;quot;;<br /></code>
 
And on the possible usage (in our example code in git &quot;qtdevnet-wiki-mvc/qtdevnet-custom-iodevice&amp;quot;:http://www.gitorious.org/qtdevnet-wiki-mvc/qtdevnet-custom-iodevice)
 
&amp;#32;
 
=== Encryption ===
 
<code><br /> QByteArray dataArray;
 
QBuffer bufferUsedLikeAFile&amp;amp;#40;&amp;dataArray&amp;amp;#41;;<br /> CryptDevice deviceFilter(&amp;bufferUsedLikeAFile);<br /> deviceFilter.open(QIODevice::WriteOnly);<br /> QTextStream stream(&amp;deviceFilter);<br /> QString szText = rawText-&gt;toPlainText();<br /> stream &lt;&lt; szText;<br /></code>
 
&amp;#32;


===Encryption===
=== Decryption ===


===Decryption===
<code><br /> QBuffer bufferUsedLikeAFile&amp;amp;#40;&amp;dataArray&amp;amp;#41;;<br /> CryptDevice deviceFilter(&amp;bufferUsedLikeAFile);<br /> deviceFilter.open(QIODevice::ReadOnly);<br /> QTextStream stream(&amp;deviceFilter);<br /> QString szText = stream.readAll();<br /> decryptedText-&gt;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 <span class="caps">QIOD</span>evice class in Qt 4 involves inheriting <span class="caps">QIOD</span>evice and then reimplementing a set of virtual functions.
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 <span class="caps">QIOD</span>evice::readData ( char * data, qint64 maxSize )
* qint64 QIODevice::readData ( char * data, qint64 maxSize )
* qint64 <span class="caps">QIOD</span>evice::writeData ( const 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 <span class="caps">QIOD</span>evice.
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


As our device should be sequential, we re-implement isSequential<br />
<code><br />CryptDevice::CryptDevice(QIODevice* deviceToUse, QObject* parent) :<br /> QIODevice(parent),<br /> underlyingDevice(deviceToUse)<br />{<br />}<br /></code>


In <code>open()</code>, we open the underlying device if it’s not already open and set the device state to ''mode''.
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 &lt;code&amp;gt;open()&lt;/code&amp;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-&gt;isOpen())<br /> underlyingOk = (underlyingDevice-&gt;openMode() != mode);<br /> else<br /> underlyingOk = underlyingDevice-&gt;open(mode);
 
if (underlyingOk)<br /> {<br /> setOpenMode(mode);<br /> return true;<br /> }<br /> return false;<br />}<br /></code>


Closing is trivial.
Closing is trivial.


When reading a block, we call <code>read()</code> on the underlying device. At the end, we <span class="caps">XOR</span> each byte read from the device with the magic constant 0×5E.
<code><br />void CryptDevice::close()<br />{<br /> underlyingDevice-&gt;close();<br /> setOpenMode(NotOpen);<br />}<br /></code>
 
When writing a block, we create a temporary buffer with the <span class="caps">XOR</span>’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.


===Categories:===
When reading a block, we call &lt;code&amp;gt;read()&lt;/code&amp;gt; on the underlying device. At the end, we XOR each byte read from the device with the magic constant 0x5E.


* [[:Category:snippets|snippets]]
<code><br />qint64 CryptDevice::readData(char* data, qint64 maxSize)<br />{<br /> qint64 deviceRead = underlyingDevice-&gt;read(data, maxSize);<br /> if (deviceRead == –1)<br /> return <s>1;<br /> for (qint64 i = 0; i &lt; 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 &lt;code&amp;gt;write()&lt;/code&amp;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 &lt; (int)maxSize;''+i)<br /> buffer[i] = data[i] ^ 0x5E;<br /> return underlyingDevice</s>&gt;write(buffer.data(), maxSize);<br />}<br /></code>

Revision as of 09:51, 24 February 2015


English | German

[toc align_right="yes&quot; depth="2&quot;]

Writing a Custom I/O Device

This is a port of the article in "Qt Quarterly 12 about writing a custom QIODevice&quot;:http://doc.qt.nokia.com/qq/qq12-iodevice.html

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;amp;#40;&quot;output.dat&amp;quot;&amp;#41;;<br /> CryptDevice cryptDevice(&amp;file)<br /> QTextStream out(&amp;cryptDevice);<br /> cryptDevice.open(QIODevice::WriteOnly);<br /> out &lt;&lt; &quot;Hello World&amp;quot;;<br />

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

<br /> QByteArray dataArray;

QBuffer bufferUsedLikeAFile&amp;amp;#40;&amp;dataArray&amp;amp;#41;;<br /> CryptDevice deviceFilter(&amp;bufferUsedLikeAFile);<br /> deviceFilter.open(QIODevice::WriteOnly);<br /> QTextStream stream(&amp;deviceFilter);<br /> QString szText = rawText-&gt;toPlainText();<br /> stream &lt;&lt; szText;<br />

&#32;

Decryption

<br /> QBuffer bufferUsedLikeAFile&amp;amp;#40;&amp;dataArray&amp;amp;#41;;<br /> CryptDevice deviceFilter(&amp;bufferUsedLikeAFile);<br /> deviceFilter.open(QIODevice::ReadOnly);<br /> QTextStream stream(&amp;deviceFilter);<br /> QString szText = stream.readAll();<br /> decryptedText-&gt;setPlainText(szText);<br />

Example image of the test app:

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&gt;open()</code&gt;, 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-&gt;isOpen())<br /> underlyingOk = (underlyingDevice-&gt;openMode() != mode);<br /> else<br /> underlyingOk = underlyingDevice-&gt;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-&gt;close();<br /> setOpenMode(NotOpen);<br />}<br />

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.

<br />qint64 CryptDevice::readData(char* data, qint64 maxSize)<br />{<br /> qint64 deviceRead = underlyingDevice-&gt;read(data, maxSize);<br /> if (deviceRead == 1)<br /> return <s>1;<br /> for (qint64 i = 0; i &lt; 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&gt;write()</code&gt; 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 &lt; (int)maxSize;''+i)<br /> buffer[i] = data[i] ^ 0x5E;<br /> return underlyingDevice</s>&gt;write(buffer.data(), maxSize);<br />}<br />