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 <QtCore/QIODevice>
23
#include <QtCore/QDataStream>
24
#include <QtCore/QDebug>
25
#include <exception>
26
27
class IOException : public std::exception {
28
public:
29
    const QString msg;
30
    IOException() {}
31
    IOException(const QString& m) :msg(m) {}
32
    ~IOException() throw() {}
33
};
34
35
class IncorrectValueException : public IOException {
36
public:
37
    IncorrectValueException(const QString& msg) :IOException(msg) {}
38
    IncorrectValueException(qint64 /*pos*/, const char* errMsg) :IOException(errMsg) {}
39
    ~IncorrectValueException() throw() {}
40
};
41
42
class EOFException : public IOException {
43
public:
44
    EOFException(const QString& msg = QString()) :IOException(msg) {}
45
    ~EOFException() throw() {}
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
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
    }
72
    void checkForLeftOverBits() const {
73
        if (bitfieldpos >= 0) {
74
            throw IOException("Cannot read this type halfway through a bit operation.");
75
        }
76
    }
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;
92
        Mark(QIODevice* in) :input(in), pos((in) ?in->pos() :0) {} 
93
    public:
94
        Mark() :input(0), pos(0) {} 
95
    };
96
97
    LEInputStream(QIODevice* in) :input(in), data(in) {
98
        maxPosition = 0;
99
        bitfield = 0;
100
        bitfieldpos = -1;
101
        data.setByteOrder(QDataStream::LittleEndian);
102
    }
103
104
    Mark setMark() { return Mark(input); }
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
113
    bool readbit() {
114
        quint8 v = getBits(1) & 1;
115
        return v == 1;
116
    }
117
118
    quint8 readuint2() {
119
        return getBits(2) & 3;
120
    }
121
122
    quint8 readuint3() {
123
        return getBits(3) & 0x7;
124
    }
125
126
    quint8 readuint4() {
127
        return getBits(4) & 0xF;
128
    }
129
130
    quint8 readuint5() {
131
        return getBits(5) & 0x1F;
132
    }
133
134
    quint8 readuint6() {
135
        return getBits(6) & 0x3F;
136
    }
137
138
    quint8 readuint7() {
139
        return getBits(7) & 0x7F;
140
    }
141
142
    quint16 readuint9() {
143
        quint8 a = readuint8();
144
        quint8 b = getBits(1) & 0x1;
145
        return (b << 8) | a;
146
    }
147
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
155
    quint16 readuint13() {
156
        quint8 a = getBits(5) & 0x1F;
157
        quint8 b = readuint8();
158
        return (b << 5) | a;
159
    }
160
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
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
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
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
211
    quint8 readuint8() {
212
        checkForLeftOverBits();
213
        quint8 a;
214
        data >> a;
215
        checkStatus();
216
        return a;
217
    }
218
219
    qint16 readint16() {
220
        checkForLeftOverBits();
221
        qint16 v;
222
        data >> v;
223
        checkStatus();
224
        return v;
225
    }
226
227
    quint16 readuint16() {
228
        checkForLeftOverBits();
229
        quint16 v;
230
        data >> v;
231
        checkStatus();
232
        return v;
233
    }
234
235
    quint32 readuint32() {
236
        checkForLeftOverBits();
237
        quint32 v;
238
        data >> v;
239
        checkStatus();
240
        return v;
241
    }
242
243
    qint32 readint32() {
244
        checkForLeftOverBits();
245
        qint32 v;
246
        data >> v;
247
        checkStatus();
248
        return v;
249
    }
250
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
264
    void skip(int len) {
265
        data.skipRawData(len);
266
    }
267
268
    qint64 getPosition() const { return input->pos(); }
269
270
    qint64 getMaxPosition() const { return qMax(input->pos(), maxPosition); }
271
    qint64 getSize() const { return input->size(); }
272
};
273
274
#endif