1 /* This file is part of the KDE project
2    Copyright (C) 2009,2010 KO GmbH <jos.van.den.oever@kogmbh.com>
3 
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public
6    License as published by the Free Software Foundation; either
7    version 2 of the License, or (at your option) any later version.
8 
9    This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13 
14    You should have received a copy of the GNU Library General Public License
15    along with this library; see the file COPYING.LIB.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18 */
19 #ifndef LEINPUTSTREAM_H
20 #define LEINPUTSTREAM_H
21 
22 #include <QIODevice>
23 #include <QDataStream>
24 #include <QDebug>
25 #include <exception>
26 
27 class IOException : public std::exception {
28 public:
29     const QString msg;
IOException()30     IOException() {}
IOException(const QString & m)31     explicit IOException(const QString &m) :msg(m) {}
throw()32     ~IOException() throw() override {}
33 };
34 
35 class IncorrectValueException : public IOException {
36 public:
IncorrectValueException(const QString & msg)37     explicit IncorrectValueException(const QString &msg) :IOException(msg) {}
IncorrectValueException(qint64,const char * errMsg)38     IncorrectValueException(qint64 /*pos*/, const char* errMsg) :IOException(errMsg) {}
throw()39     ~IncorrectValueException() throw() override {}
40 };
41 
42 class EOFException : public IOException {
43 public:
IOException(msg)44     explicit EOFException(const QString &msg = QString()) :IOException(msg) {}
throw()45     ~EOFException() throw() override {}
46 };
47 
48 class LEInputStream {
49 private:
50     QIODevice* input;
51     QDataStream data;
52 
53     qint64 maxPosition;
54 
55     qint8 bitfieldpos;
56     quint8 bitfield;
57 
getBits(quint8 n)58     quint8 getBits(quint8 n) {
59         if (bitfieldpos < 0) {
60             bitfield = readuint8();
61             bitfieldpos = 0;
62         }
63         quint8 v = bitfield >> bitfieldpos;
64         bitfieldpos += n;
65         if (bitfieldpos == 8) {
66             bitfieldpos = -1;
67         } else if (bitfieldpos > 8) {
68             throw IOException("Bitfield does not have enough bits left.");
69         }
70         return v;
71     }
checkForLeftOverBits()72     void checkForLeftOverBits() const {
73         if (bitfieldpos >= 0) {
74             throw IOException("Cannot read this type halfway through a bit operation.");
75         }
76     }
checkStatus()77     void checkStatus() const {
78         if (data.status() != QDataStream::Ok) {
79             if (data.status() == QDataStream::ReadPastEnd) {
80                 throw EOFException("Stream claims to be at the end at position: " + QString::number(input->pos()) + "." );
81             }
82             throw IOException("Error reading data at position " + QString::number(input->pos()) + ".");
83         }
84     }
85 
86 public:
87     class Mark {
88     friend class LEInputStream;
89     private:
90         QIODevice* input;
91         qint64 pos;
Mark(QIODevice * in)92         explicit Mark(QIODevice *in) :input(in), pos((in) ?in->pos() :0) {}
93     public:
Mark()94         Mark() :input(0), pos(0) {}
95     };
96 
LEInputStream(QIODevice * in)97     LEInputStream(QIODevice* in) :input(in), data(in) {
98         maxPosition = 0;
99         bitfield = 0;
100         bitfieldpos = -1;
101         data.setByteOrder(QDataStream::LittleEndian);
102     }
103 
setMark()104     Mark setMark() { return Mark(input); }
rewind(const Mark & m)105     void rewind(const Mark& m) {
106         maxPosition = qMax(input->pos(), maxPosition);
107         if (!m.input || !m.input->seek(m.pos)) {
108             throw IOException("Cannot rewind.");
109         }
110         data.resetStatus();
111     }
112 
readbit()113     bool readbit() {
114         quint8 v = getBits(1) & 1;
115         return v == 1;
116     }
117 
readuint2()118     quint8 readuint2() {
119         return getBits(2) & 3;
120     }
121 
readuint3()122     quint8 readuint3() {
123         return getBits(3) & 0x7;
124     }
125 
readuint4()126     quint8 readuint4() {
127         return getBits(4) & 0xF;
128     }
129 
readuint5()130     quint8 readuint5() {
131         return getBits(5) & 0x1F;
132     }
133 
readuint6()134     quint8 readuint6() {
135         return getBits(6) & 0x3F;
136     }
137 
readuint7()138     quint8 readuint7() {
139         return getBits(7) & 0x7F;
140     }
141 
readuint9()142     quint16 readuint9() {
143         quint8 a = readuint8();
144         quint8 b = getBits(1) & 0x1;
145         return (b << 8) | a;
146     }
147 
readuint12()148     quint16 readuint12() {
149         // we assume there are 4 bits left
150         quint8 a = getBits(4) & 0xF;
151         quint8 b = readuint8();
152         return (b << 4) | a;
153     }
154 
readuint13()155     quint16 readuint13() {
156         quint8 a = getBits(5) & 0x1F;
157         quint8 b = readuint8();
158         return (b << 5) | a;
159     }
160 
readuint14()161     quint16 readuint14() {
162         quint16 v;
163         if (bitfieldpos < 0) {
164             quint8 a = readuint8();
165             quint8 b = getBits(6) & 0x3F;
166             v = (b << 8) | a;
167         } else if (bitfieldpos == 2) {
168             quint8 a = getBits(6) & 0x3F;
169             quint8 b = readuint8();
170             v = (b << 6) | a;
171         } else {
172             throw IOException("Cannot read this type halfway through a bit operation.");
173         }
174         return v;
175     }
176 
readuint15()177     quint16 readuint15() {
178         // we assume there are 7 bits left
179         quint8 a = getBits(7) & 0x7F;
180         quint8 b = readuint8();
181         return (b << 7) | a;
182     }
183 
readuint20()184     quint32 readuint20() {
185         quint32 v;
186         if (bitfieldpos < 0) {
187             quint8 a = readuint8();
188             quint8 b = readuint8();
189             quint8 c = getBits(4) & 0xF;
190             v = (c << 16) | (b << 8) | a;
191         } else if (bitfieldpos == 4) {
192             quint8 a = getBits(4) & 0xF;
193             quint8 b = readuint8();
194             quint8 c = readuint8();
195             v = (c << 12) | (b << 4) | a;
196         } else {
197             throw IOException("Cannot read this type halfway through a bit operation.");
198         }
199         return v;
200     }
201 
readuint30()202     quint32 readuint30() {
203         checkForLeftOverBits();
204         quint8 a = readuint8();
205         quint8 b = readuint8();
206         quint8 c = readuint8();
207         quint8 d = getBits(6) & 0x3F;
208         return (d << 24) | (c << 16) | (b << 8) | a;
209     }
210 
readuint8()211     quint8 readuint8() {
212         checkForLeftOverBits();
213         quint8 a;
214         data >> a;
215         checkStatus();
216         return a;
217     }
218 
readint16()219     qint16 readint16() {
220         checkForLeftOverBits();
221         qint16 v;
222         data >> v;
223         checkStatus();
224         return v;
225     }
226 
readuint16()227     quint16 readuint16() {
228         checkForLeftOverBits();
229         quint16 v;
230         data >> v;
231         checkStatus();
232         return v;
233     }
234 
readuint32()235     quint32 readuint32() {
236         checkForLeftOverBits();
237         quint32 v;
238         data >> v;
239         checkStatus();
240         return v;
241     }
242 
readint32()243     qint32 readint32() {
244         checkForLeftOverBits();
245         qint32 v;
246         data >> v;
247         checkStatus();
248         return v;
249     }
250 
readBytes(QByteArray & b)251     void readBytes(QByteArray& b) {
252         int offset = 0;
253         int todo = b.size();
254         while (todo > 0) { // do not enter loop if array size is 0
255             int nread = data.readRawData(b.data() + offset, todo);
256             if (nread == -1 || nread == 0) {
257                 throw EOFException();// TODO: differentiate
258             }
259             todo -= nread;
260             offset += nread;
261         }
262     }
263 
skip(int len)264     void skip(int len) {
265         data.skipRawData(len);
266     }
267 
getPosition()268     qint64 getPosition() const { return input->pos(); }
269 
getMaxPosition()270     qint64 getMaxPosition() const { return qMax(input->pos(), maxPosition); }
getSize()271     qint64 getSize() const { return input->size(); }
272 };
273 
274 #endif
275